mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +00:00
Preserve the order of multipart form parts (#2524)
FormFields and FormFiles were std::multimaps, which sort by field name. RFC 7578 5.2 says a form processor "SHOULD send back results in order" and that "Intermediaries MUST NOT reorder the results", so a handler walking req.form.fields saw the parts alphabetised rather than as they were sent, and a body received for forwarding could not be reproduced. Point both at the insertion-ordered container #2523 generalized, with std::equal_to since field names are case-sensitive. Entries sharing a name already kept their relative order under std::multimap; what is recovered here is the order across different names. Server::read_content() keeps a FormFields::iterator alive across the content callbacks that fill the part it points at, which is the one thing this container could have broken: it is vector-backed, so a later emplace can reallocate and leave an older iterator dangling. The code is safe because the iterator is reassigned by the same emplace that could reallocate, and is only read while the flag set alongside it says so. ContentSurvivesContainerGrowth pins that down with 64 parts, enough to grow the vector through seven reallocations; reverting the reassignment makes it abort under ASan rather than fail quietly. Growth also never copies a part's payload: the parser inserts the entry with an empty content and appends the body bytes afterwards, and both mapped types are nothrow-move-constructible, so a reallocation steals the string buffers rather than deep-copying them.
This commit is contained in:
1 parent
7963c382d6
commit
23f67f25c2
2 files changed
+143
-2
No files matched your search
@@ -1362,9 +1362,16 @@ struct FormField {
|
||||
std::string content;
|
||||
Headers headers;
|
||||
};
|
||||
using FormFields = std::multimap<std::string, FormField>;
|
||||
// RFC 7578 5.2: a form processor "SHOULD send back results in order" and
|
||||
// "Intermediaries MUST NOT reorder the results", so a handler walking these
|
||||
// should see the parts as they were sent. A std::multimap sorts by field name
|
||||
// and loses that. Field names are case-sensitive, hence std::equal_to rather
|
||||
// than the case-insensitive predicate Headers uses.
|
||||
using FormFields =
|
||||
detail::insertion_ordered_multimap<FormField, std::equal_to<std::string>>;
|
||||
|
||||
using FormFiles = std::multimap<std::string, FormData>;
|
||||
using FormFiles =
|
||||
detail::insertion_ordered_multimap<FormData, std::equal_to<std::string>>;
|
||||
|
||||
struct MultipartFormData {
|
||||
FormFields fields; // Text fields from multipart
|
||||
|
||||
Reference in new issue
Block a user