Crow  1.1
A C++ microframework for the web
 
Loading...
Searching...
No Matches
multipart.h
1#pragma once
2
3#include <string>
4#include <vector>
5#include <sstream>
6
7#include "crow/http_request.h"
8#include "crow/returnable.h"
9#include "crow/ci_map.h"
10
11namespace crow
12{
13
14 /// Encapsulates anything related to processing and organizing `multipart/xyz` messages
15 namespace multipart
16 {
17
18 const std::string dd = "--";
19
20 /// The first part in a section, contains metadata about the part
21 struct header
22 {
23 std::string value; ///< The first part of the header, usually `Content-Type` or `Content-Disposition`
24 std::unordered_map<std::string, std::string> params; ///< The parameters of the header, come after the `value`
25
26 operator int() const { return std::stoi(value); } ///< Returns \ref value as integer
27 operator double() const { return std::stod(value); } ///< Returns \ref value as double
28 };
29
30 /// Multipart header map (key is header key).
31 using mph_map = std::unordered_multimap<std::string, header, ci_hash, ci_key_eq>;
32
33 /// Find and return the value object associated with the key. (returns an empty class if nothing is found)
34 template<typename O, typename T>
35 inline const O& get_header_value_object(const T& headers, const std::string& key)
36 {
37 if (headers.count(key))
38 {
39 return headers.find(key)->second;
40 }
41 static O empty;
42 return empty;
43 }
44
45 /// Same as \ref get_header_value_object() but for \ref multipart.header
46 template<typename T>
47 inline const header& get_header_object(const T& headers, const std::string& key)
48 {
49 return get_header_value_object<header>(headers, key);
50 }
51
52 ///One part of the multipart message
53
54 ///
55 /// It is usually separated from other sections by a `boundary`
56 struct part
57 {
58 mph_map headers; ///< (optional) The first part before the data, Contains information regarding the type of data and encoding
59 std::string body; ///< The actual data in the part
60
61 operator int() const { return std::stoi(body); } ///< Returns \ref body as integer
62 operator double() const { return std::stod(body); } ///< Returns \ref body as double
63
64 const header& get_header_object(const std::string& key) const
65 {
67 }
68 };
69
70 /// Multipart map (key is the name parameter).
71 using mp_map = std::unordered_multimap<std::string, part, ci_hash, ci_key_eq>;
72
73 /// The parsed multipart request/response
74 struct message : public returnable
75 {
76 ci_map headers; ///< The request/response headers
77 std::string boundary; ///< The text boundary that separates different `parts`
78 std::vector<part> parts; ///< The individual parts of the message
79 mp_map part_map; ///< The individual parts of the message, organized in a map with the `name` header parameter being the key
80
81 const std::string& get_header_value(const std::string& key) const
82 {
84 }
85
86 part get_part_by_name(const std::string& name)
87 {
88 mp_map::iterator result = part_map.find(name);
89 if (result != part_map.end())
90 return result->second;
91 else
92 return {};
93 }
94
95 /// Represent all parts as a string (**does not include message headers**)
96 std::string dump() const override
97 {
98 std::stringstream str;
99 std::string delimiter = dd + boundary;
100
101 for (unsigned i = 0; i < parts.size(); i++)
102 {
103 str << delimiter << crlf;
104 str << dump(i);
105 }
106 str << delimiter << dd << crlf;
107 return str.str();
108 }
109
110 /// Represent an individual part as a string
111 std::string dump(int part_) const
112 {
113 std::stringstream str;
114 part item = parts[part_];
115 for (auto& item_h : item.headers)
116 {
117 str << item_h.first << ": " << item_h.second.value;
118 for (auto& it : item_h.second.params)
119 {
120 str << "; " << it.first << '=' << pad(it.second);
121 }
122 str << crlf;
123 }
124 str << crlf;
125 str << item.body << crlf;
126 return str.str();
127 }
128
129 /// Default constructor using default values
130 message(const ci_map& headers, const std::string& boundary, const std::vector<part>& sections):
131 returnable("multipart/form-data; boundary=CROW-BOUNDARY"), headers(headers), boundary(boundary), parts(sections)
132 {
133 if (!boundary.empty())
134 content_type = "multipart/form-data; boundary=" + boundary;
135 for (auto& item : parts)
136 {
137 part_map.emplace(
138 (get_header_object(item.headers, "Content-Disposition").params.find("name")->second),
139 item);
140 }
141 }
142
143 /// Create a multipart message from a request data
144 message(const request& req):
145 returnable("multipart/form-data; boundary=CROW-BOUNDARY"),
146 headers(req.headers),
147 boundary(get_boundary(get_header_value("Content-Type")))
148 {
149 if (!boundary.empty())
150 content_type = "multipart/form-data; boundary=" + boundary;
151 parse_body(req.body, parts, part_map);
152 }
153
154 private:
155 std::string get_boundary(const std::string& header) const
156 {
157 constexpr char boundary_text[] = "boundary=";
158 size_t found = header.find(boundary_text);
159 if (found != std::string::npos)
160 {
161 std::string to_return(header.substr(found + strlen(boundary_text)));
162 if (to_return[0] == '\"')
163 {
164 to_return = to_return.substr(1, to_return.length() - 2);
165 }
166 return to_return;
167 }
168 return std::string();
169 }
170
171 void parse_body(std::string body, std::vector<part>& sections, mp_map& part_map)
172 {
173
174 std::string delimiter = dd + boundary;
175
176 // TODO(EDev): Exit on error
177 while (body != (crlf))
178 {
179 size_t found = body.find(delimiter);
180 if (found == std::string::npos)
181 {
182 // did not find delimiter; probably an ill-formed body; ignore the rest
183 break;
184 }
185 std::string section = body.substr(0, found);
186
187 // +2 is the CRLF.
188 // We don't check it and delete it so that the same delimiter can be used for The last delimiter (--delimiter--CRLF).
189 body.erase(0, found + delimiter.length() + 2);
190 if (!section.empty())
191 {
192 part parsed_section(parse_section(section));
193 part_map.emplace(
194 (get_header_object(parsed_section.headers, "Content-Disposition").params.find("name")->second),
195 parsed_section);
196 sections.push_back(std::move(parsed_section));
197 }
198 }
199 }
200
201 part parse_section(std::string& section)
202 {
203 struct part to_return;
204
205 size_t found = section.find(crlf + crlf);
206 std::string head_line = section.substr(0, found + 2);
207 section.erase(0, found + 4);
208
209 parse_section_head(head_line, to_return);
210 to_return.body = section.substr(0, section.length() - 2);
211 return to_return;
212 }
213
214 void parse_section_head(std::string& lines, part& part)
215 {
216 while (!lines.empty())
217 {
218 header to_add;
219
220 size_t found = lines.find(crlf);
221 std::string line = lines.substr(0, found);
222 std::string key;
223 lines.erase(0, found + 2);
224 // Add the header if available
225 if (!line.empty())
226 {
227 size_t found = line.find("; ");
228 std::string header = line.substr(0, found);
229 if (found != std::string::npos)
230 line.erase(0, found + 2);
231 else
232 line = std::string();
233
234 size_t header_split = header.find(": ");
235 key = header.substr(0, header_split);
236
237 to_add.value = header.substr(header_split + 2);
238 }
239
240 // Add the parameters
241 while (!line.empty())
242 {
243 size_t found = line.find("; ");
244 std::string param = line.substr(0, found);
245 if (found != std::string::npos)
246 line.erase(0, found + 2);
247 else
248 line = std::string();
249
250 size_t param_split = param.find('=');
251
252 std::string value = param.substr(param_split + 1);
253
254 to_add.params.emplace(param.substr(0, param_split), trim(value));
255 }
256 part.headers.emplace(key, to_add);
257 }
258 }
259
260 inline std::string trim(std::string& string, const char& excess = '"') const
261 {
262 if (string.length() > 1 && string[0] == excess && string[string.length() - 1] == excess)
263 return string.substr(1, string.length() - 2);
264 return string;
265 }
266
267 inline std::string pad(std::string& string, const char& padding = '"') const
268 {
269 return (padding + string + padding);
270 }
271 };
272 } // namespace multipart
273} // namespace crow
const O & get_header_value_object(const T &headers, const std::string &key)
Find and return the value object associated with the key. (returns an empty class if nothing is found...
Definition multipart.h:35
std::unordered_multimap< std::string, part, ci_hash, ci_key_eq > mp_map
Multipart map (key is the name parameter).
Definition multipart.h:71
const header & get_header_object(const T &headers, const std::string &key)
Same as get_header_value_object() but for multipart::header.
Definition multipart.h:47
std::unordered_multimap< std::string, header, ci_hash, ci_key_eq > mph_map
Multipart header map (key is header key).
Definition multipart.h:31
The main namespace of the library. In this namespace is defined the most important classes and functi...
const std::string & get_header_value(const T &headers, const std::string &key)
Find and return the value associated with the key. (returns an empty string if nothing is found)
Definition http_request.h:24
The first part in a section, contains metadata about the part.
Definition multipart.h:22
std::unordered_map< std::string, std::string > params
The parameters of the header, come after the value
Definition multipart.h:24
std::string value
The first part of the header, usually Content-Type or Content-Disposition
Definition multipart.h:23
The parsed multipart request/response.
Definition multipart.h:75
message(const ci_map &headers, const std::string &boundary, const std::vector< part > &sections)
Default constructor using default values.
Definition multipart.h:130
std::vector< part > parts
The individual parts of the message.
Definition multipart.h:78
message(const request &req)
Create a multipart message from a request data.
Definition multipart.h:144
std::string boundary
The text boundary that separates different parts
Definition multipart.h:77
mp_map part_map
The individual parts of the message, organized in a map with the name header parameter being the key.
Definition multipart.h:79
std::string dump(int part_) const
Represent an individual part as a string.
Definition multipart.h:111
std::string dump() const override
Represent all parts as a string (does not include message headers)
Definition multipart.h:96
ci_map headers
The request/response headers.
Definition multipart.h:76
One part of the multipart message.
Definition multipart.h:57
mph_map headers
(optional) The first part before the data, Contains information regarding the type of data and encodi...
Definition multipart.h:58
std::string body
The actual data in the part.
Definition multipart.h:59
An HTTP request.
Definition http_request.h:36
An abstract class that allows any other class to be returned by a handler.
Definition returnable.h:9