|
@@ -3619,6 +3619,8 @@ bool parse_range_header(const std::string &s, Ranges &ranges);
|
|
|
bool parse_accept_header(const std::string &s,
|
|
bool parse_accept_header(const std::string &s,
|
|
|
std::vector<std::string> &content_types);
|
|
std::vector<std::string> &content_types);
|
|
|
|
|
|
|
|
|
|
+void parse_disposition_params(const std::string &s, Params ¶ms);
|
|
|
|
|
+
|
|
|
ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
|
|
ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
|
|
|
|
|
|
|
|
ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
|
|
ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
|
|
@@ -5885,6 +5887,55 @@ inline void split(const char *b, const char *e, char d, size_t m,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Same contract as split(), except that a delimiter inside a quoted-string is
|
|
|
|
|
+// not a delimiter. RFC 9110 Section 5.6.6 lets a parameter value be a
|
|
|
|
|
+// quoted-string, and ';' and '=' are legal characters inside one.
|
|
|
|
|
+inline void split_unquoted(const char *b, const char *e, char d, size_t m,
|
|
|
|
|
+ std::function<void(const char *, const char *)> fn) {
|
|
|
|
|
+ size_t i = 0;
|
|
|
|
|
+ size_t beg = 0;
|
|
|
|
|
+ size_t count = 1;
|
|
|
|
|
+ auto in_quotes = false;
|
|
|
|
|
+
|
|
|
|
|
+ while (e ? (b + i < e) : (b[i] != '\0')) {
|
|
|
|
|
+ if (b[i] == '"') {
|
|
|
|
|
+ in_quotes = !in_quotes;
|
|
|
|
|
+ } else if (b[i] == d && !in_quotes && count < m) {
|
|
|
|
|
+ auto r = trim(b, e, beg, i);
|
|
|
|
|
+ if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
|
|
|
|
|
+ beg = i + 1;
|
|
|
|
|
+ count++;
|
|
|
|
|
+ }
|
|
|
|
|
+ i++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (i) {
|
|
|
|
|
+ auto r = trim(b, e, beg, i);
|
|
|
|
|
+ if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+inline void split_unquoted(const char *b, const char *e, char d,
|
|
|
|
|
+ std::function<void(const char *, const char *)> fn) {
|
|
|
|
|
+ return split_unquoted(b, e, d, (std::numeric_limits<size_t>::max)(),
|
|
|
|
|
+ std::move(fn));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Divide a header parameter at its first '='. RFC 9110 Section 5.6.6 makes the
|
|
|
|
|
+// key a token, so the first '=' is the separator even when the value is a
|
|
|
|
|
+// quoted-string carrying more of them.
|
|
|
|
|
+inline void divide_param_pair(const char *b, const char *e, std::string &key,
|
|
|
|
|
+ std::string &val) {
|
|
|
|
|
+ divide(
|
|
|
|
|
+ b, static_cast<std::size_t>(e - b), '=',
|
|
|
|
|
+ [&](const char *kb, std::size_t klen, const char *vb, std::size_t vlen) {
|
|
|
|
|
+ const auto kr = trim(kb, kb + klen, 0, klen);
|
|
|
|
|
+ key.assign(kb + kr.first, kb + kr.second);
|
|
|
|
|
+ const auto vr = trim(vb, vb + vlen, 0, vlen);
|
|
|
|
|
+ val.assign(vb + vr.first, vb + vr.second);
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
inline bool split_find(const char *b, const char *e, char d, size_t m,
|
|
inline bool split_find(const char *b, const char *e, char d, size_t m,
|
|
|
std::function<bool(const char *, const char *)> fn) {
|
|
std::function<bool(const char *, const char *)> fn) {
|
|
|
size_t i = 0;
|
|
size_t i = 0;
|
|
@@ -7303,21 +7354,16 @@ extract_media_type(const std::string &content_type,
|
|
|
|
|
|
|
|
if (params) {
|
|
if (params) {
|
|
|
// Parse parameters: key=value pairs separated by ';'
|
|
// Parse parameters: key=value pairs separated by ';'
|
|
|
- split(param_str.data(), param_str.data() + param_str.size(), ';',
|
|
|
|
|
- [&](const char *b, const char *e) {
|
|
|
|
|
- std::string key;
|
|
|
|
|
- std::string val;
|
|
|
|
|
- split(b, e, '=', [&](const char *b2, const char *e2) {
|
|
|
|
|
- if (key.empty()) {
|
|
|
|
|
- key.assign(b2, e2);
|
|
|
|
|
- } else {
|
|
|
|
|
- val.assign(b2, e2);
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- if (!key.empty()) {
|
|
|
|
|
- params->emplace(trim_copy(key), trim_double_quotes_copy(val));
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ split_unquoted(param_str.data(), param_str.data() + param_str.size(), ';',
|
|
|
|
|
+ [&](const char *b, const char *e) {
|
|
|
|
|
+ std::string key;
|
|
|
|
|
+ std::string val;
|
|
|
|
|
+ divide_param_pair(b, e, key, val);
|
|
|
|
|
+ if (!key.empty()) {
|
|
|
|
|
+ params->emplace(trim_copy(key),
|
|
|
|
|
+ trim_double_quotes_copy(val));
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -8760,26 +8806,21 @@ inline bool parse_multipart_boundary(const std::string &content_type,
|
|
|
|
|
|
|
|
inline void parse_disposition_params(const std::string &s, Params ¶ms) {
|
|
inline void parse_disposition_params(const std::string &s, Params ¶ms) {
|
|
|
std::set<std::string> cache;
|
|
std::set<std::string> cache;
|
|
|
- split(s.data(), s.data() + s.size(), ';', [&](const char *b, const char *e) {
|
|
|
|
|
- std::string kv(b, e);
|
|
|
|
|
- if (cache.find(kv) != cache.end()) { return; }
|
|
|
|
|
- cache.insert(kv);
|
|
|
|
|
-
|
|
|
|
|
- std::string key;
|
|
|
|
|
- std::string val;
|
|
|
|
|
- split(b, e, '=', [&](const char *b2, const char *e2) {
|
|
|
|
|
- if (key.empty()) {
|
|
|
|
|
- key.assign(b2, e2);
|
|
|
|
|
- } else {
|
|
|
|
|
- val.assign(b2, e2);
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- if (!key.empty()) {
|
|
|
|
|
- params.emplace(trim_double_quotes_copy((key)),
|
|
|
|
|
- trim_double_quotes_copy((val)));
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ split_unquoted(s.data(), s.data() + s.size(), ';',
|
|
|
|
|
+ [&](const char *b, const char *e) {
|
|
|
|
|
+ std::string kv(b, e);
|
|
|
|
|
+ if (cache.find(kv) != cache.end()) { return; }
|
|
|
|
|
+ cache.insert(kv);
|
|
|
|
|
+
|
|
|
|
|
+ std::string key;
|
|
|
|
|
+ std::string val;
|
|
|
|
|
+ divide_param_pair(b, e, key, val);
|
|
|
|
|
+
|
|
|
|
|
+ if (!key.empty()) {
|
|
|
|
|
+ params.emplace(trim_double_quotes_copy(key),
|
|
|
|
|
+ trim_double_quotes_copy(val));
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
|
|
#ifdef CPPHTTPLIB_NO_EXCEPTIONS
|