From 549bc646bb70e60341f0bda9d8be3a61ce04d3fa Mon Sep 17 00:00:00 2001 From: eidheim Date: Sat, 31 Dec 2016 10:34:03 +0100 Subject: [PATCH] Case insensitive header cleanup. Also cleanup and additions to parse_test --- client_http.hpp | 39 +++++++------- server_http.hpp | 41 ++++++++------- tests/parse_test.cpp | 119 ++++++++++++++++++++----------------------- 3 files changed, 98 insertions(+), 101 deletions(-) diff --git a/client_http.hpp b/client_http.hpp index c294e99..2f17b87 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -12,6 +12,26 @@ #include #include +#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH +#define CASE_INSENSITIVE_EQUALS_AND_HASH +//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html +class case_insensitive_equals { +public: + bool operator()(const std::string &key1, const std::string &key2) const { + return boost::algorithm::iequals(key1, key2); + } +}; +class case_insensitive_hash { +public: + size_t operator()(const std::string &key) const { + std::size_t seed=0; + for(auto &c: key) + boost::hash_combine(seed, std::tolower(c)); + return seed; + } +}; +#endif + namespace SimpleWeb { template class Client; @@ -24,29 +44,12 @@ namespace SimpleWeb { class Response { friend class ClientBase; friend class Client; - - //Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html - class iequal_to { - public: - bool operator()(const std::string &key1, const std::string &key2) const { - return boost::algorithm::iequals(key1, key2); - } - }; - class ihash { - public: - size_t operator()(const std::string &key) const { - std::size_t seed=0; - for(auto &c: key) - boost::hash_combine(seed, std::tolower(c)); - return seed; - } - }; public: std::string http_version, status_code; std::istream content; - std::unordered_multimap header; + std::unordered_multimap header; private: boost::asio::streambuf content_buffer; diff --git a/server_http.hpp b/server_http.hpp index e5fae19..9ab8760 100644 --- a/server_http.hpp +++ b/server_http.hpp @@ -11,6 +11,26 @@ #include #include +#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH +#define CASE_INSENSITIVE_EQUALS_AND_HASH +//Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html +class case_insensitive_equals { +public: + bool operator()(const std::string &key1, const std::string &key2) const { + return boost::algorithm::iequals(key1, key2); + } +}; +class case_insensitive_hash { +public: + size_t operator()(const std::string &key) const { + std::size_t seed=0; + for(auto &c: key) + boost::hash_combine(seed, std::tolower(c)); + return seed; + } +}; +#endif + // Late 2017 TODO: remove the following checks and always use std::regex #ifdef USE_BOOST_REGEX #include @@ -74,29 +94,12 @@ namespace SimpleWeb { class Request { friend class ServerBase; friend class Server; - - //Based on http://www.boost.org/doc/libs/1_60_0/doc/html/unordered/hash_equality.html - class iequal_to { - public: - bool operator()(const std::string &key1, const std::string &key2) const { - return boost::algorithm::iequals(key1, key2); - } - }; - class ihash { - public: - size_t operator()(const std::string &key) const { - std::size_t seed=0; - for(auto &c: key) - boost::hash_combine(seed, std::tolower(c)); - return seed; - } - }; public: std::string method, path, http_version; Content content; - std::unordered_multimap header; + std::unordered_multimap header; REGEX_NS::smatch path_match; @@ -338,7 +341,7 @@ namespace SimpleWeb { if(line[value_start]==' ') value_start++; if(value_startheader.insert(std::make_pair(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1))); + request->header.emplace(line.substr(0, param_end), line.substr(value_start, line.size()-value_start-1)); } getline(request->content, line); diff --git a/tests/parse_test.cpp b/tests/parse_test.cpp index 502db0b..785d305 100644 --- a/tests/parse_test.cpp +++ b/tests/parse_test.cpp @@ -1,18 +1,18 @@ #include "server_http.hpp" #include "client_http.hpp" #include +#include using namespace std; using namespace SimpleWeb; class ServerTest : public ServerBase { public: - ServerTest() : - ServerBase::ServerBase(8080) {} + ServerTest() : ServerBase::ServerBase(8080) {} void accept() {} - bool parse_request_test() { + void parse_request_test() { HTTP socket(*io_service); std::shared_ptr request(new Request(socket)); @@ -20,28 +20,34 @@ public: stream << "GET /test/ HTTP/1.1\r\n"; stream << "TestHeader: test\r\n"; stream << "TestHeader2:test2\r\n"; + stream << "TestHeader3:test3a\r\n"; + stream << "TestHeader3:test3b\r\n"; stream << "\r\n"; - if(!parse_request(request)) - return 0; + assert(parse_request(request)); - if(request->method!="GET") - return 0; - if(request->path!="/test/") - return 0; - if(request->http_version!="1.1") - return 0; + assert(request->method=="GET"); + assert(request->path=="/test/"); + assert(request->http_version=="1.1"); - if(request->header.size()!=2) - return 0; + assert(request->header.size()==4); auto header_it=request->header.find("TestHeader"); - if(header_it==request->header.end() || header_it->second!="test") - return 0; + assert(header_it!=request->header.end() && header_it->second=="test"); header_it=request->header.find("TestHeader2"); - if(header_it==request->header.end() || header_it->second!="test2") - return 0; + assert(header_it!=request->header.end() && header_it->second=="test2"); - return 1; + header_it=request->header.find("testheader"); + assert(header_it!=request->header.end() && header_it->second=="test"); + header_it=request->header.find("testheader2"); + assert(header_it!=request->header.end() && header_it->second=="test2"); + + auto range=request->header.equal_range("testheader3"); + auto first=range.first; + auto second=first; + ++second; + assert(range.first!=request->header.end() && range.second!=request->header.end() && + ((first->second=="test3a" && second->second=="test3b") || + (first->second=="test3b" && second->second=="test3a"))); } }; @@ -51,51 +57,50 @@ public: void connect() {} - bool constructor_parse_test1() { - if(host!="test.org") - return 0; - if(port!=8080) - return 0; - - return 1; + void constructor_parse_test1() { + assert(host=="test.org"); + assert(port==8080); } - bool constructor_parse_test2() { - if(host!="test.org") - return 0; - if(port!=80) - return 0; - - return 1; + void constructor_parse_test2() { + assert(host=="test.org"); + assert(port==80); } - bool parse_response_header_test() { + void parse_response_header_test() { std::shared_ptr response(new Response()); ostream stream(&response->content_buffer); stream << "HTTP/1.1 200 OK\r\n"; stream << "TestHeader: test\r\n"; stream << "TestHeader2:test2\r\n"; + stream << "TestHeader3:test3a\r\n"; + stream << "TestHeader3:test3b\r\n"; stream << "\r\n"; parse_response_header(response); - if(response->http_version!="1.1") - return 0; - if(response->status_code!="200 OK") - return 0; + assert(response->http_version=="1.1"); + assert(response->status_code=="200 OK"); - if(response->header.size()!=2) - return 0; - + assert(response->header.size()==4); auto header_it=response->header.find("TestHeader"); - if(header_it==response->header.end() || header_it->second!="test") - return 0; + assert(header_it!=response->header.end() && header_it->second=="test"); header_it=response->header.find("TestHeader2"); - if(header_it==response->header.end() || header_it->second!="test2") - return 0; + assert(header_it!=response->header.end() && header_it->second=="test2"); - return 1; + header_it=response->header.find("testheader"); + assert(header_it!=response->header.end() && header_it->second=="test"); + header_it=response->header.find("testheader2"); + assert(header_it!=response->header.end() && header_it->second=="test2"); + + auto range=response->header.equal_range("testheader3"); + auto first=range.first; + auto second=first; + ++second; + assert(range.first!=response->header.end() && range.second!=response->header.end() && + ((first->second=="test3a" && second->second=="test3b") || + (first->second=="test3b" && second->second=="test3a"))); } }; @@ -103,27 +108,13 @@ int main() { ServerTest serverTest; serverTest.io_service=std::make_shared(); - if(!serverTest.parse_request_test()) { - cerr << "FAIL Server::parse_request" << endl; - return 1; - } + serverTest.parse_request_test(); ClientTest clientTest("test.org:8080"); - if(!clientTest.constructor_parse_test1()) { - cerr << "FAIL Client::Client" << endl; - return 1; - } + clientTest.constructor_parse_test1(); ClientTest clientTest2("test.org"); - if(!clientTest2.constructor_parse_test2()) { - cerr << "FAIL Client::Client" << endl; - return 1; - } + clientTest2.constructor_parse_test2(); - if(!clientTest2.parse_response_header_test()) { - cerr << "FAIL Client::parse_response_header" << endl; - return 1; - } - - return 0; -} \ No newline at end of file + clientTest2.parse_response_header_test(); +}