66
77#include " crow/http_request.h"
88#include " crow/returnable.h"
9+ #include " crow/ci_map.h"
910
1011namespace crow
1112{
13+
1214 // / Encapsulates anything related to processing and organizing `multipart/xyz` messages
1315 namespace multipart
1416 {
17+
1518 const std::string dd = " --" ;
1619
1720 // / The first part in a section, contains metadata about the part
1821 struct header
1922 {
20- std::pair<std:: string, std::string> value; // /< The first part of the header, usually `Content-Type` or `Content-Disposition`
23+ std::string value; // /< The first part of the header, usually `Content-Type` or `Content-Disposition`
2124 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
2228 };
2329
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+
2452 // /One part of the multipart message
2553
2654 // /
2755 // / It is usually separated from other sections by a `boundary`
2856 struct part
2957 {
30- // TODO(EDev): restructure this to an `unordered_map<string, header>` with string being `header::value.first`
31- std::vector<header> headers; // /< (optional) The first part before the data, Contains information regarding the type of data and encoding
32- std::string body; // /< The actual data in the part
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+ {
66+ return multipart::get_header_object (headers, key);
67+ }
3368 };
3469
70+ // / Multipart map (key is the name parameter).
71+ using mp_map = std::unordered_multimap<std::string, part, ci_hash, ci_key_eq>;
72+
3573 // / The parsed multipart request/response
3674 struct message : public returnable
3775 {
3876 ci_map headers; // /< The request/response headers
3977 std::string boundary; // /< The text boundary that separates different `parts`
4078 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
4180
4281 const std::string& get_header_value (const std::string& key) const
4382 {
4483 return crow::get_header_value (headers, key);
4584 }
4685
86+ part get_part_by_name (const std::string& name)
87+ {
88+ return part_map.find (name)->second ;
89+ }
90+
4791 // / Represent all parts as a string (**does not include message headers**)
4892 std::string dump () const override
4993 {
@@ -64,10 +108,10 @@ namespace crow
64108 {
65109 std::stringstream str;
66110 part item = parts[part_];
67- for (header item_h : item.headers )
111+ for (auto & item_h : item.headers )
68112 {
69- str << item_h.value . first << " : " << item_h.value . second ;
70- for (auto & it : item_h.params )
113+ str << item_h.first << " : " << item_h.second . value ;
114+ for (auto & it : item_h.second . params )
71115 {
72116 str << " ; " << it.first << ' =' << pad (it.second );
73117 }
@@ -80,15 +124,28 @@ namespace crow
80124
81125 // / Default constructor using default values
82126 message (const ci_map& headers, const std::string& boundary, const std::vector<part>& sections):
83- returnable (" multipart/form-data" ), headers(headers), boundary(boundary), parts(sections) {}
127+ returnable (" multipart/form-data; boundary=CROW-BOUNDARY" ), headers(headers), boundary(boundary), parts(sections)
128+ {
129+ if (!boundary.empty ())
130+ content_type = " multipart/form-data; boundary=" + boundary;
131+ for (auto & item : parts)
132+ {
133+ part_map.emplace (
134+ (get_header_object (item.headers , " Content-Disposition" ).params .find (" name" )->second ),
135+ item);
136+ }
137+ }
84138
85139 // / Create a multipart message from a request data
86140 message (const request& req):
87- returnable (" multipart/form-data" ),
141+ returnable (" multipart/form-data; boundary=CROW-BOUNDARY " ),
88142 headers (req.headers),
89- boundary (get_boundary(get_header_value(" Content-Type" ))),
90- parts (parse_body(req.body))
91- {}
143+ boundary (get_boundary(get_header_value(" Content-Type" )))
144+ {
145+ if (!boundary.empty ())
146+ content_type = " multipart/form-data; boundary=" + boundary;
147+ parse_body (req.body , parts, part_map);
148+ }
92149
93150 private:
94151 std::string get_boundary (const std::string& header) const
@@ -107,13 +164,12 @@ namespace crow
107164 return std::string ();
108165 }
109166
110- std::vector<part> parse_body (std::string body)
167+ void parse_body (std::string body, std::vector<part>& sections, mp_map& part_map )
111168 {
112169
113- std::vector<part> sections;
114-
115170 std::string delimiter = dd + boundary;
116171
172+ // TODO(EDev): Exit on error
117173 while (body != (crlf))
118174 {
119175 size_t found = body.find (delimiter);
@@ -124,10 +180,13 @@ namespace crow
124180 body.erase (0 , found + delimiter.length () + 2 );
125181 if (!section.empty ())
126182 {
127- sections.emplace_back (parse_section (section));
183+ part parsed_section (parse_section (section));
184+ part_map.emplace (
185+ (get_header_object (parsed_section.headers , " Content-Disposition" ).params .find (" name" )->second ),
186+ parsed_section);
187+ sections.push_back (std::move (parsed_section));
128188 }
129189 }
130- return sections;
131190 }
132191
133192 part parse_section (std::string& section)
@@ -151,6 +210,7 @@ namespace crow
151210
152211 size_t found = lines.find (crlf);
153212 std::string line = lines.substr (0 , found);
213+ std::string key;
154214 lines.erase (0 , found + 2 );
155215 // Add the header if available
156216 if (!line.empty ())
@@ -163,8 +223,9 @@ namespace crow
163223 line = std::string ();
164224
165225 size_t header_split = header.find (" : " );
226+ key = header.substr (0 , header_split);
166227
167- to_add.value = std::pair<std::string, std::string>( header.substr (0 , header_split), header. substr (header_split + 2 ) );
228+ to_add.value = header.substr (header_split + 2 );
168229 }
169230
170231 // Add the parameters
@@ -183,7 +244,7 @@ namespace crow
183244
184245 to_add.params .emplace (param.substr (0 , param_split), trim (value));
185246 }
186- part.headers .emplace_back ( to_add);
247+ part.headers .emplace (key, to_add);
187248 }
188249 }
189250
0 commit comments