diff --git a/server_http.hpp b/server_http.hpp index fcf8a01..8e46875 100644 --- a/server_http.hpp +++ b/server_http.hpp @@ -107,13 +107,39 @@ namespace SimpleWeb { Content content; std::unordered_multimap header; - std::unordered_multimap query_string; REGEX_NS::smatch path_match; std::string remote_endpoint_address; unsigned short remote_endpoint_port; + /// Returns query keys with percent-decoded values. + std::unordered_multimap parse_query_string() { + std::unordered_multimap result; + auto qs_start_pos = path.find('?'); + if (qs_start_pos != std::string::npos && qs_start_pos + 1 < path.size()) { + ++qs_start_pos; + static REGEX_NS::regex pattern("([\\w+%]+)=?([^&]*)"); + int submatches[] = {1, 2}; + auto it_begin = REGEX_NS::sregex_token_iterator(path.begin() + qs_start_pos, path.end(), pattern, submatches); + auto it_end = REGEX_NS::sregex_token_iterator(); + for (auto it = it_begin; it != it_end; ++it) { + auto query_it = result.emplace(it->str(), (++it)->str()); + auto &value = query_it->second; + for (size_t c = 0; c < value.size(); ++c) { + if (value[c] == '+') + value[c] = ' '; + else if (value[c] == '%' && c + 2 < value.size()) { + auto hex = value.substr(c + 1, 2); + auto chr = static_cast(std::strtol(hex.c_str(), nullptr, 16)); + value.replace(c, 3, &chr, 1); + } + } + } + } + return result; + } + private: Request(const socket_type &socket): content(streambuf) { try { @@ -321,25 +347,6 @@ namespace SimpleWeb { request->method=line.substr(0, method_end); request->path=line.substr(method_end+1, path_end-method_end-1); - //search and populte query_string - size_t qs_start; - if ((qs_start = request->path.find('?')) != std::string::npos) - { - std::string qs = request->path.substr(qs_start, request->path.size() - qs_start - 1); - REGEX_NS::regex pattern("([\\w+%]+)=?([^&]*)"); - int submatches[] = { 1, 2 }; - auto qs_begin = REGEX_NS::sregex_token_iterator(qs.begin(), qs.end(), pattern, submatches); - auto qs_end = REGEX_NS::sregex_token_iterator(); - - for (auto it = qs_begin; it != qs_end; it++) - { - std::string key = it->str(); - std::string value = (++it)->str(); - request->query_string.emplace(std::make_pair(key, value)); - } - request->path = request->path.substr(0, qs_start); - } - size_t protocol_end; if((protocol_end=line.find('/', path_end+1))!=std::string::npos) { if(line.compare(path_end+1, protocol_end-path_end-1, "HTTP")!=0) diff --git a/tests/parse_test.cpp b/tests/parse_test.cpp index 785d305..76ff348 100644 --- a/tests/parse_test.cpp +++ b/tests/parse_test.cpp @@ -117,4 +117,43 @@ int main() { clientTest2.constructor_parse_test2(); clientTest2.parse_response_header_test(); + + + boost::asio::io_service io_service; + boost::asio::ip::tcp::socket socket(io_service); + SimpleWeb::Server::Request request(socket); + { + request.path = "/?"; + auto queries = request.parse_query_string(); + assert(queries.empty()); + } + { + request.path = "/"; + auto queries = request.parse_query_string(); + assert(queries.empty()); + } + { + request.path = "/?a=1%202%20%203&b=3+4&c&d=æ%25ø%26å%3F"; + auto queries = request.parse_query_string(); + { + auto range = queries.equal_range("a"); + assert(range.first != range.second); + assert(range.first->second == "1 2 3"); + } + { + auto range = queries.equal_range("b"); + assert(range.first != range.second); + assert(range.first->second == "3 4"); + } + { + auto range = queries.equal_range("c"); + assert(range.first != range.second); + assert(range.first->second == ""); + } + { + auto range = queries.equal_range("d"); + assert(range.first != range.second); + assert(range.first->second == "æ%ø&å?"); + } + } }