From: Steinar H. Gunderson Date: Mon, 9 Apr 2018 20:56:24 +0000 (+0200) Subject: Make HTTP header parsing case-insensitive. X-Git-Tag: 1.4.0~17 X-Git-Url: https://git.sesse.net/?p=cubemap;a=commitdiff_plain;h=fa431bfab848624c2490a1134e084e6c1dd0dbfa Make HTTP header parsing case-insensitive. --- diff --git a/httpinput.cpp b/httpinput.cpp index 7967c76..bac6b05 100644 --- a/httpinput.cpp +++ b/httpinput.cpp @@ -249,17 +249,15 @@ bool HTTPInput::parse_response(const string &request) return false; } - unordered_multimap parameters = extract_headers(lines, url); + HTTPHeaderMultimap parameters = extract_headers(lines, url); // Remove “Content-encoding: metacube”. - // TODO: Make case-insensitive. - const auto encoding_it = parameters.find("Content-encoding"); + const auto encoding_it = parameters.find("Content-Encoding"); if (encoding_it != parameters.end() && encoding_it->second == "metacube") { parameters.erase(encoding_it); } // Change “Server: foo” to “Server: metacube/0.1 (reflecting: foo)” - // TODO: Make case-insensitive. // XXX: Use a Via: instead? if (parameters.count("Server") == 0) { parameters.insert(make_pair("Server", SERVER_IDENTIFICATION)); @@ -273,7 +271,6 @@ bool HTTPInput::parse_response(const string &request) } // Erase “Connection: close”; we'll set it on the sending side if needed. - // TODO: Make case-insensitive. parameters.erase("Connection"); // Construct the new HTTP header. diff --git a/parse.cpp b/parse.cpp index bec742b..3f373c4 100644 --- a/parse.cpp +++ b/parse.cpp @@ -56,9 +56,9 @@ vector split_lines(const string &str) return ret; } -unordered_multimap extract_headers(const vector &lines, const string &log_context) +HTTPHeaderMultimap extract_headers(const vector &lines, const string &log_context) { - unordered_multimap parameters; + HTTPHeaderMultimap parameters; for (size_t i = 1; i < lines.size(); ++i) { size_t split = lines[i].find(":"); if (split == string::npos) { diff --git a/parse.h b/parse.h index 1924cc0..a2e3580 100644 --- a/parse.h +++ b/parse.h @@ -5,9 +5,42 @@ #include #include +#include +#include #include #include +// Locale-unaware tolower(); matches RFC 2616 no matter what the locale is set to. +static inline char ascii_tolower(const char ch) +{ + if (ch >= 'A' && ch <= 'Z') { + return ch + 'a' - 'A'; + } else { + return ch; + } +} + +// Case-insensitive header comparison and hashing. +struct HTTPLess { + bool operator() (const std::string &a, const std::string &b) const + { + return std::lexicographical_compare( + begin(a), end(a), begin(b), end(b), + [](char a, char b) { + return ascii_tolower(a) < ascii_tolower(b); + }); + } +}; +struct HTTPHash { + size_t operator() (const std::string &s) const + { + std::string s_low = s; + for (char &ch : s_low) { ch = ascii_tolower(ch); } + return std::hash() (s_low); + } +}; +using HTTPHeaderMultimap = std::unordered_multimap; + // Split a line on whitespace, e.g. "foo bar baz" -> {"foo", "bar", "baz"}. std::vector split_tokens(const std::string &line); @@ -16,8 +49,7 @@ std::vector split_lines(const std::string &str); // Extract HTTP headers from a request or response. Ignores the first line, // where the verb or the return code is. -std::unordered_multimap extract_headers( - const std::vector &lines, const std::string &log_context); +HTTPHeaderMultimap extract_headers(const std::vector &lines, const std::string &log_context); // Add the new data to an existing string, looking for \r\n\r\n // (typical of HTTP requests and/or responses). Will return one diff --git a/server.cpp b/server.cpp index 40f3b48..0e056fb 100644 --- a/server.cpp +++ b/server.cpp @@ -911,8 +911,7 @@ int Server::parse_request(Client *client) } // Parse the headers, for logging purposes. - // TODO: Case-insensitivity. - unordered_multimap headers = extract_headers(lines, client->remote_addr); + HTTPHeaderMultimap headers = extract_headers(lines, client->remote_addr); const auto referer_it = headers.find("Referer"); if (referer_it != headers.end()) { client->referer = referer_it->second;