diff --git a/changelog.md b/changelog.md index 085a6cb3f5..2b1d38a88d 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,8 @@ HEAD - Allows changing the listen backlog queue length. - Fix handler allocation crash with multithreaded io_service. - Split tcp init into pre and post init. +- Fixes incorrect whitespace handling in header parsing. #301 Thank you Wolfram + Schroers for reporting - Adds URI method to extract query string from URI. Thank you Banaan for code. #298 - Numerous performance improvements. Including: tuned default buffer sizes based diff --git a/test/http/parser.cpp b/test/http/parser.cpp index 19a4909e79..a5c13ea3bd 100644 --- a/test/http/parser.cpp +++ b/test/http/parser.cpp @@ -355,6 +355,24 @@ BOOST_AUTO_TEST_CASE( extract_parameters ) { BOOST_CHECK_EQUAL( a.find("bar")->second, "a \"b\" c" ); } +BOOST_AUTO_TEST_CASE( strip_lws ) { + std::string test1 = "foo"; + std::string test2 = " foo "; + std::string test3 = "foo "; + std::string test4 = " foo"; + std::string test5 = " foo "; + std::string test6 = " \r\n foo "; + std::string test7 = " \t foo "; + + BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test1), "foo" ); + BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test2), "foo" ); + BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test3), "foo" ); + BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test4), "foo" ); + BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test5), "foo" ); + BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test6), "foo" ); + BOOST_CHECK_EQUAL( websocketpp::http::parser::strip_lws(test7), "foo" ); +} + BOOST_AUTO_TEST_CASE( case_insensitive_headers ) { websocketpp::http::parser::parser r; @@ -655,12 +673,12 @@ BOOST_AUTO_TEST_CASE( old_http_version ) { } BOOST_CHECK( exception == false ); - BOOST_CHECK( pos == 41 ); + BOOST_CHECK_EQUAL( pos, 41 ); BOOST_CHECK( r.ready() == true ); - BOOST_CHECK( r.get_version() == "HTTP/1.0" ); - BOOST_CHECK( r.get_method() == "GET" ); - BOOST_CHECK( r.get_uri() == "/" ); - BOOST_CHECK( r.get_header("Host") == "www.example.com" ); + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.0" ); + BOOST_CHECK_EQUAL( r.get_method(), "GET" ); + BOOST_CHECK_EQUAL( r.get_uri(), "/" ); + BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" ); } BOOST_AUTO_TEST_CASE( new_http_version1 ) { @@ -678,12 +696,12 @@ BOOST_AUTO_TEST_CASE( new_http_version1 ) { } BOOST_CHECK( exception == false ); - BOOST_CHECK( pos == 42 ); + BOOST_CHECK_EQUAL( pos, 42 ); BOOST_CHECK( r.ready() == true ); - BOOST_CHECK( r.get_version() == "HTTP/1.12" ); - BOOST_CHECK( r.get_method() == "GET" ); - BOOST_CHECK( r.get_uri() == "/" ); - BOOST_CHECK( r.get_header("Host") == "www.example.com" ); + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.12" ); + BOOST_CHECK_EQUAL( r.get_method(), "GET" ); + BOOST_CHECK_EQUAL( r.get_uri(), "/" ); + BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" ); } BOOST_AUTO_TEST_CASE( new_http_version2 ) { @@ -701,12 +719,12 @@ BOOST_AUTO_TEST_CASE( new_http_version2 ) { } BOOST_CHECK( exception == false ); - BOOST_CHECK( pos == 43 ); + BOOST_CHECK_EQUAL( pos, 43 ); BOOST_CHECK( r.ready() == true ); - BOOST_CHECK( r.get_version() == "HTTP/12.12" ); - BOOST_CHECK( r.get_method() == "GET" ); - BOOST_CHECK( r.get_uri() == "/" ); - BOOST_CHECK( r.get_header("Host") == "www.example.com" ); + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/12.12" ); + BOOST_CHECK_EQUAL( r.get_method(), "GET" ); + BOOST_CHECK_EQUAL( r.get_uri(), "/" ); + BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" ); } /* commented out due to not being implemented yet @@ -726,7 +744,7 @@ BOOST_AUTO_TEST_CASE( new_http_version3 ) { } BOOST_CHECK( exception == true ); -} +}*/ BOOST_AUTO_TEST_CASE( header_whitespace1 ) { websocketpp::http::parser::request r; @@ -743,12 +761,12 @@ BOOST_AUTO_TEST_CASE( header_whitespace1 ) { } BOOST_CHECK( exception == false ); - BOOST_CHECK( pos == 43 ); + BOOST_CHECK_EQUAL( pos, 43 ); BOOST_CHECK( r.ready() == true ); - BOOST_CHECK( r.get_version() == "HTTP/1.1" ); - BOOST_CHECK( r.get_method() == "GET" ); - BOOST_CHECK( r.get_uri() == "/" ); - BOOST_CHECK( r.get_header("Host") == "www.example.com" ); + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); + BOOST_CHECK_EQUAL( r.get_method(), "GET" ); + BOOST_CHECK_EQUAL( r.get_uri(), "/" ); + BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" ); } BOOST_AUTO_TEST_CASE( header_whitespace2 ) { @@ -766,13 +784,13 @@ BOOST_AUTO_TEST_CASE( header_whitespace2 ) { } BOOST_CHECK( exception == false ); - BOOST_CHECK( pos == 40 ); + BOOST_CHECK_EQUAL( pos, 40 ); BOOST_CHECK( r.ready() == true ); - BOOST_CHECK( r.get_version() == "HTTP/1.1" ); - BOOST_CHECK( r.get_method() == "GET" ); - BOOST_CHECK( r.get_uri() == "/" ); - BOOST_CHECK( r.get_header("Host") == "www.example.com" ); -}*/ + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); + BOOST_CHECK_EQUAL( r.get_method(), "GET" ); + BOOST_CHECK_EQUAL( r.get_uri(), "/" ); + BOOST_CHECK_EQUAL( r.get_header("Host"), "www.example.com" ); +} BOOST_AUTO_TEST_CASE( header_aggregation ) { websocketpp::http::parser::request r; @@ -789,12 +807,12 @@ BOOST_AUTO_TEST_CASE( header_aggregation ) { } BOOST_CHECK( exception == false ); - BOOST_CHECK( pos == 61 ); + BOOST_CHECK_EQUAL( pos, 61 ); BOOST_CHECK( r.ready() == true ); - BOOST_CHECK( r.get_version() == "HTTP/1.1" ); - BOOST_CHECK( r.get_method() == "GET" ); - BOOST_CHECK( r.get_uri() == "/" ); - BOOST_CHECK( r.get_header("Foo") == "bar, bat" ); + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); + BOOST_CHECK_EQUAL( r.get_method(), "GET" ); + BOOST_CHECK_EQUAL( r.get_uri(), "/" ); + BOOST_CHECK_EQUAL( r.get_header("Foo"), "bar, bat" ); } BOOST_AUTO_TEST_CASE( wikipedia_example_response ) { @@ -813,15 +831,42 @@ BOOST_AUTO_TEST_CASE( wikipedia_example_response ) { } BOOST_CHECK( exception == false ); - BOOST_CHECK( pos == 159 ); + BOOST_CHECK_EQUAL( pos, 159 ); BOOST_CHECK( r.headers_ready() == true ); - BOOST_CHECK( r.get_version() == "HTTP/1.1" ); - BOOST_CHECK( r.get_status_code() == websocketpp::http::status_code::switching_protocols ); - BOOST_CHECK( r.get_status_msg() == "Switching Protocols" ); - BOOST_CHECK( r.get_header("Upgrade") == "websocket" ); - BOOST_CHECK( r.get_header("Connection") == "Upgrade" ); - BOOST_CHECK( r.get_header("Sec-WebSocket-Accept") == "HSmrc0sMlYUkAGmm5OPpG2HaGWk=" ); - BOOST_CHECK( r.get_header("Sec-WebSocket-Protocol") == "chat" ); + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); + BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); + BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); + BOOST_CHECK_EQUAL( r.get_header("Upgrade"), "websocket" ); + BOOST_CHECK_EQUAL( r.get_header("Connection"), "Upgrade" ); + BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Accept"), "HSmrc0sMlYUkAGmm5OPpG2HaGWk=" ); + BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Protocol"), "chat" ); +} + +BOOST_AUTO_TEST_CASE( response_with_non_standard_lws ) { + websocketpp::http::parser::response r; + + std::string raw = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept:HSmrc0sMlYUkAGmm5OPpG2HaGWk=\r\nSec-WebSocket-Protocol: chat\r\n\r\n"; + + bool exception = false; + size_t pos = 0; + + try { + pos += r.consume(raw.c_str(),raw.size()); + } catch (std::exception &e) { + exception = true; + std::cout << e.what() << std::endl; + } + + BOOST_CHECK( exception == false ); + BOOST_CHECK_EQUAL( pos, 158 ); + BOOST_CHECK( r.headers_ready() ); + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); + BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::switching_protocols ); + BOOST_CHECK_EQUAL( r.get_status_msg(), "Switching Protocols" ); + BOOST_CHECK_EQUAL( r.get_header("Upgrade"), "websocket" ); + BOOST_CHECK_EQUAL( r.get_header("Connection"), "Upgrade" ); + BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Accept"), "HSmrc0sMlYUkAGmm5OPpG2HaGWk=" ); + BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Protocol"), "chat" ); } BOOST_AUTO_TEST_CASE( plain_http_response ) { @@ -840,21 +885,21 @@ BOOST_AUTO_TEST_CASE( plain_http_response ) { } BOOST_CHECK( exception == false ); - BOOST_CHECK( pos == 405 ); + BOOST_CHECK_EQUAL( pos, 405 ); BOOST_CHECK( r.headers_ready() == true ); BOOST_CHECK( r.ready() == true ); - BOOST_CHECK( r.get_version() == "HTTP/1.1" ); - BOOST_CHECK( r.get_status_code() == websocketpp::http::status_code::ok ); - BOOST_CHECK( r.get_status_msg() == "OK" ); - BOOST_CHECK( r.get_header("Date") == "Thu, 10 May 2012 11:59:25 GMT" ); - BOOST_CHECK( r.get_header("Server") == "Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8r DAV/2 PHP/5.3.8 with Suhosin-Patch" ); - BOOST_CHECK( r.get_header("Last-Modified") == "Tue, 30 Mar 2010 17:41:28 GMT" ); - BOOST_CHECK( r.get_header("ETag") == "\"16799d-55-4830823a78200\"" ); - BOOST_CHECK( r.get_header("Accept-Ranges") == "bytes" ); - BOOST_CHECK( r.get_header("Content-Length") == "85" ); - BOOST_CHECK( r.get_header("Vary") == "Accept-Encoding" ); - BOOST_CHECK( r.get_header("Content-Type") == "text/html" ); - BOOST_CHECK( r.get_body() == "\n\n
\nThor
\n" ); + BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1" ); + BOOST_CHECK_EQUAL( r.get_status_code(), websocketpp::http::status_code::ok ); + BOOST_CHECK_EQUAL( r.get_status_msg(), "OK" ); + BOOST_CHECK_EQUAL( r.get_header("Date"), "Thu, 10 May 2012 11:59:25 GMT" ); + BOOST_CHECK_EQUAL( r.get_header("Server"), "Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8r DAV/2 PHP/5.3.8 with Suhosin-Patch" ); + BOOST_CHECK_EQUAL( r.get_header("Last-Modified"), "Tue, 30 Mar 2010 17:41:28 GMT" ); + BOOST_CHECK_EQUAL( r.get_header("ETag"), "\"16799d-55-4830823a78200\"" ); + BOOST_CHECK_EQUAL( r.get_header("Accept-Ranges"), "bytes" ); + BOOST_CHECK_EQUAL( r.get_header("Content-Length"), "85" ); + BOOST_CHECK_EQUAL( r.get_header("Vary"), "Accept-Encoding" ); + BOOST_CHECK_EQUAL( r.get_header("Content-Type"), "text/html" ); + BOOST_CHECK_EQUAL( r.get_body(), "\n\n\nThor
\n" ); } BOOST_AUTO_TEST_CASE( parse_istream ) { @@ -889,7 +934,7 @@ BOOST_AUTO_TEST_CASE( write_request_basic ) { r.set_method("GET"); r.set_uri("/"); - BOOST_CHECK( r.raw() == raw ); + BOOST_CHECK_EQUAL( r.raw(), raw ); } BOOST_AUTO_TEST_CASE( write_request_with_header ) { @@ -902,7 +947,7 @@ BOOST_AUTO_TEST_CASE( write_request_with_header ) { r.set_uri("/"); r.replace_header("Host","http://example.com"); - BOOST_CHECK( r.raw() == raw ); + BOOST_CHECK_EQUAL( r.raw(), raw ); } BOOST_AUTO_TEST_CASE( write_request_with_body ) { @@ -917,5 +962,5 @@ BOOST_AUTO_TEST_CASE( write_request_with_body ) { r.replace_header("Content-Type","application/x-www-form-urlencoded"); r.set_body("licenseID=string&content=string¶msXML=string"); - BOOST_CHECK( r.raw() == raw ); + BOOST_CHECK_EQUAL( r.raw(), raw ); } diff --git a/websocketpp/http/constants.hpp b/websocketpp/http/constants.hpp index 3edc3eb0fc..be3006aabd 100644 --- a/websocketpp/http/constants.hpp +++ b/websocketpp/http/constants.hpp @@ -53,7 +53,7 @@ namespace http { static char const header_delimiter[] = "\r\n"; /// Literal value of the HTTP header separator - static char const header_separator[] = ": "; + static char const header_separator[] = ":"; /// Literal value of an empty header static std::string const empty_header = ""; diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp index 0ceac2ed57..d1d908135d 100644 --- a/websocketpp/http/impl/parser.hpp +++ b/websocketpp/http/impl/parser.hpp @@ -147,8 +147,8 @@ inline void parser::process_header(std::string::iterator begin, throw exception("Invalid header line",status_code::bad_request); } - append_header(std::string(begin,cursor), - std::string(cursor+sizeof(header_separator)-1,end)); + append_header(strip_lws(std::string(begin,cursor)), + strip_lws(std::string(cursor+sizeof(header_separator)-1,end))); } inline std::string parser::raw_headers() const { diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp index 0bb52c2b6c..771c53971e 100644 --- a/websocketpp/http/parser.hpp +++ b/websocketpp/http/parser.hpp @@ -367,6 +367,13 @@ InputIterator extract_parameters(InputIterator begin, InputIterator end, return cursor; } +inline std::string strip_lws(std::string const & input) { + std::string::const_iterator begin = extract_all_lws(input.begin(),input.end()); + std::string::const_reverse_iterator end = extract_all_lws(input.rbegin(),input.rend()); + + return std::string(begin,end.base()); +} + /// Base HTTP parser /** * Includes methods and data elements common to all types of HTTP messages such