From 80826f0c14d91edc6c369fcd23c9a2b858e24091 Mon Sep 17 00:00:00 2001 From: leecoder Date: Thu, 26 Jun 2014 14:29:36 +0900 Subject: [PATCH] enhancing response incremental parser --- .../protocol/http/parser/incremental.hpp | 47 ++++-- libs/network/test/http/CMakeLists.txt | 1 + .../http/response_incremental_parser_test.cpp | 151 +++++++++++++++++- 3 files changed, 178 insertions(+), 21 deletions(-) diff --git a/boost/network/protocol/http/parser/incremental.hpp b/boost/network/protocol/http/parser/incremental.hpp index 44c4c2a2e..c64449ac3 100644 --- a/boost/network/protocol/http/parser/incremental.hpp +++ b/boost/network/protocol/http/parser/incremental.hpp @@ -168,26 +168,35 @@ namespace boost { namespace network { namespace http { } else if (*current == ' ') { state_ = http_status_done; ++current; + } else if (*current == '\r' || *current == '\n') { + state_ = http_status_done; } else { parsed_ok = false; } break; case http_status_done: - if (algorithm::is_alnum()(*current)) { - state_ = http_status_message_char; + if (*current == ' ') { + ++current; + } else if (*current == '\r') { + state_ = http_status_message_cr; + ++current; + } else if (*current == '\n') { + state_ = http_status_message_done; ++current; } else { - parsed_ok = false; + state_ = http_status_message_char; + ++current; } break; case http_status_message_char: - if (algorithm::is_alnum()(*current) || algorithm::is_punct()(*current) || (*current == ' ')) { - ++current; - } else if (*current == '\r') { + if (*current == '\r') { state_ = http_status_message_cr; ++current; + } else if (*current == '\n') { + state_ = http_status_message_done; + ++current; } else { - parsed_ok = false; + ++current; } break; case http_status_message_cr: @@ -200,12 +209,17 @@ namespace boost { namespace network { namespace http { break; case http_status_message_done: case http_header_line_done: - if (algorithm::is_alnum()(*current)) { + if (*current == ' ') { + ++current; + } else if (algorithm::is_alnum()(*current) || algorithm::is_punct()(*current)) { state_ = http_header_name_char; ++current; } else if (*current == '\r') { state_ = http_headers_end_cr; ++current; + } else if (*current == '\n') { + state_ = http_headers_done; + ++current; } else { parsed_ok = false; } @@ -221,21 +235,26 @@ namespace boost { namespace network { namespace http { } break; case http_header_colon: - if (algorithm::is_space()(*current)) { + if (*current == '\r') { + state_ = http_header_line_cr; ++current; - } else if (algorithm::is_alnum()(*current) || algorithm::is_punct()(*current)) { - state_ = http_header_value_char; + } else if (*current == '\n') { + state_ = http_header_line_done; + ++current; + } else if (algorithm::is_space()(*current)) { ++current; } else { - parsed_ok = false; + state_ = http_header_value_char; + ++current; } break; case http_header_value_char: if (*current == '\r') { state_ = http_header_line_cr; ++current; - } else if (algorithm::is_cntrl()(*current)) { - parsed_ok = false; + } else if (*current == '\n') { + state_ = http_header_line_done; + ++current; } else { ++current; } diff --git a/libs/network/test/http/CMakeLists.txt b/libs/network/test/http/CMakeLists.txt index cb953f867..ffacadf49 100644 --- a/libs/network/test/http/CMakeLists.txt +++ b/libs/network/test/http/CMakeLists.txt @@ -13,6 +13,7 @@ endif() if (Boost_FOUND) set ( TESTS + response_incremental_parser_test request_incremental_parser_test request_linearize_test ) diff --git a/libs/network/test/http/response_incremental_parser_test.cpp b/libs/network/test/http/response_incremental_parser_test.cpp index 9026ec0bc..34cf9307c 100644 --- a/libs/network/test/http/response_incremental_parser_test.cpp +++ b/libs/network/test/http/response_incremental_parser_test.cpp @@ -53,6 +53,17 @@ namespace logic = boost::logic; namespace fusion = boost::fusion; using namespace boost::network::http; +struct crlf { + static const std::string literal; +}; +const std::string crlf::literal = "\r\n"; +struct lf { + static const std::string literal; +}; +const std::string lf::literal = "\n"; +typedef boost::mpl::vector eol_types; + + BOOST_AUTO_TEST_CASE(incremental_parser_constructor) { response_parser p; // default constructible } @@ -114,7 +125,7 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_http_version) { * the parser doesn't do any conversions from string to integer * and outsource that part to the user of the parser. */ -BOOST_AUTO_TEST_CASE(incremental_parser_parse_status) { +BOOST_AUTO_TEST_CASE_TEMPLATE(incremental_parser_parse_status, eol, eol_types) { typedef response_parser response_parser_type; typedef boost::iterator_range range_type; // We want to create a parser that has been initialized to a specific @@ -140,17 +151,25 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_status) { BOOST_CHECK_EQUAL(parsed_ok, false); parsed = std::string(boost::begin(result_range), boost::end(result_range)); std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl; + + valid_status = "200" + eol::literal; + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_status_done, + valid_status); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl; } /** In this test then we get the rest of the first line of the HTTP * Response, and treat it as the status message. */ -BOOST_AUTO_TEST_CASE(incremental_parser_parse_status_message) { +BOOST_AUTO_TEST_CASE_TEMPLATE(incremental_parser_parse_status_message, eol, eol_types) { typedef response_parser response_parser_type; typedef boost::iterator_range range_type; response_parser_type p(response_parser_type::http_status_done); - std::string valid_status_message = "OK\r\nServer: Foo"; + std::string valid_status_message = "OK" + eol::literal + "Server: Foo"; logic::tribool parsed_ok; range_type result_range; fusion::tie(parsed_ok, result_range) = p.parse_until( @@ -161,7 +180,25 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_status_message) { std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl; p.reset(response_parser_type::http_status_done); - valid_status_message = "OK\r\n"; + valid_status_message = "OK" + eol::literal; + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_status_message_done, + valid_status_message); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl; + + p.reset(response_parser_type::http_status_done); + valid_status_message = "Internal Server Error" + eol::literal; + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_status_message_done, + valid_status_message); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl; + + p.reset(response_parser_type::http_status_done); + valid_status_message = eol::literal; fusion::tie(parsed_ok, result_range) = p.parse_until( response_parser_type::http_status_message_done, valid_status_message); @@ -170,7 +207,7 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_status_message) { std::cout << "PARSED: " << parsed << " state=" << p.state() << std::endl; p.reset(response_parser_type::http_status_done); - valid_status_message = "Internal Server Error\r\n"; + valid_status_message = "한글메시지" + eol::literal; fusion::tie(parsed_ok, result_range) = p.parse_until( response_parser_type::http_status_message_done, valid_status_message); @@ -181,12 +218,12 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_status_message) { /** This test specifices how one-line-per-header parsing happens incrementally. */ -BOOST_AUTO_TEST_CASE(incremental_parser_parse_header_lines) { +BOOST_AUTO_TEST_CASE_TEMPLATE(incremental_parser_parse_header_lines, eol, eol_types) { typedef response_parser response_parser_type; typedef boost::iterator_range range_type; response_parser_type p(response_parser_type::http_status_message_done); - std::string valid_headers = "Server: Foo\r\nContent-Type: application/json\r\n\r\n"; + std::string valid_headers = "Server: Foo" + eol::literal + "Content-Type: application/json" + eol::literal + eol::literal; logic::tribool parsed_ok; range_type result_range; fusion::tie(parsed_ok, result_range) = p.parse_until( @@ -211,5 +248,105 @@ BOOST_AUTO_TEST_CASE(incremental_parser_parse_header_lines) { valid_headers); BOOST_CHECK_EQUAL(parsed_ok, true); BOOST_CHECK(parsed1 != parsed2); + + p.reset(response_parser_type::http_status_message_done); + valid_headers = " Server: Foo" + eol::literal + " Content-Type: application/json" + eol::literal + eol::literal; + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_header_line_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed1 = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed1 << " state=" << p.state() << std::endl; + p.reset(response_parser_type::http_status_message_done); + end = valid_headers.end(); + valid_headers.assign(boost::end(result_range), end); + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_header_line_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed2 = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed2 << " state=" << p.state() << std::endl; + valid_headers.assign(boost::end(result_range), end); + p.reset(response_parser_type::http_status_message_done); + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_headers_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + BOOST_CHECK(parsed1 != parsed2); + + p.reset(response_parser_type::http_status_message_done); + valid_headers = "_Server: Foo" + eol::literal + "_Content-Type: application/json" + eol::literal + eol::literal; + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_header_line_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed1 = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed1 << " state=" << p.state() << std::endl; + p.reset(response_parser_type::http_status_message_done); + end = valid_headers.end(); + valid_headers.assign(boost::end(result_range), end); + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_header_line_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed2 = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed2 << " state=" << p.state() << std::endl; + valid_headers.assign(boost::end(result_range), end); + p.reset(response_parser_type::http_status_message_done); + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_headers_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + BOOST_CHECK(parsed1 != parsed2); + + p.reset(response_parser_type::http_status_message_done); + valid_headers = "Server: " + eol::literal + "Content-Type: application/json" + eol::literal + eol::literal; + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_header_line_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed1 = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed1 << " state=" << p.state() << std::endl; + p.reset(response_parser_type::http_status_message_done); + end = valid_headers.end(); + valid_headers.assign(boost::end(result_range), end); + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_header_line_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed2 = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed2 << " state=" << p.state() << std::endl; + valid_headers.assign(boost::end(result_range), end); + p.reset(response_parser_type::http_status_message_done); + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_headers_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + BOOST_CHECK(parsed1 != parsed2); + + p.reset(response_parser_type::http_status_message_done); + valid_headers = "Server: 서버" + eol::literal + "Content-Type: application/json" + eol::literal + eol::literal; + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_header_line_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed1 = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed1 << " state=" << p.state() << std::endl; + p.reset(response_parser_type::http_status_message_done); + end = valid_headers.end(); + valid_headers.assign(boost::end(result_range), end); + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_header_line_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + parsed2 = std::string(boost::begin(result_range), boost::end(result_range)); + std::cout << "PARSED: " << parsed2 << " state=" << p.state() << std::endl; + valid_headers.assign(boost::end(result_range), end); + p.reset(response_parser_type::http_status_message_done); + fusion::tie(parsed_ok, result_range) = p.parse_until( + response_parser_type::http_headers_done, + valid_headers); + BOOST_CHECK_EQUAL(parsed_ok, true); + BOOST_CHECK(parsed1 != parsed2); }