Skip to content

Commit eb926e6

Browse files
author
pfeatherstone
committed
parse block by block
1 parent b508582 commit eb926e6

File tree

7 files changed

+422
-256
lines changed

7 files changed

+422
-256
lines changed

examples/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ add_executable_coro(client_ws_coro ${CMAKE_CURRENT_SOURCE_DIR}/client_ws_coro
4646
add_executable(tests
4747
unit_tests/main.cpp
4848
unit_tests/base64.cpp
49-
unit_tests/sha1.cpp)
49+
unit_tests/sha1.cpp
50+
unit_tests/message.cpp)
5051
target_link_options(tests PRIVATE $<$<CONFIG:Release>:-s>)
5152
target_include_directories(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/extra)
5253
target_link_libraries(tests PRIVATE http)

examples/client_http.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ awaitable_strand http_session(std::string_view host)
6565
// Prepare request
6666
req.verb = http::GET;
6767
req.uri = "/get";
68-
req.http_version_major = req.http_version_minor = 1;
68+
req.http_version_minor = 1;
6969
req.add_header(http::host, host); // mandatory in HTTP/1.1 in request messages
7070
req.add_header(http::user_agent, "Boost::asio " + std::to_string(BOOST_ASIO_VERSION)); // optional header
7171

examples/server.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -355,16 +355,15 @@ awaitable_strand http_session (
355355
}
356356

357357
// Reset response, set http version and keep alive status of response
358+
const bool keep_alive = req.keep_alive();
358359
resp.clear();
359-
resp.http_version_major = req.http_version_major;
360360
resp.http_version_minor = req.http_version_minor;
361-
resp.keep_alive(req.keep_alive());
361+
resp.keep_alive(keep_alive);
362362

363363
// Handle request and set response
364364
handle_request(options.docroot, options.username, options.password, options.http_handlers, req, resp);
365365

366366
// Write response
367-
const bool keep_alive = req.keep_alive();
368367
res = co_await async_http_write(sock, resp, buf);
369368

370369
// Shutdown if necessary

examples/unit_tests/message.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#include <http.h>
2+
#include "doctest.h"
3+
4+
TEST_SUITE("[MESSAGE]")
5+
{
6+
TEST_CASE("serialise & parse bad requests")
7+
{
8+
http::request req;
9+
10+
SUBCASE("empty")
11+
{
12+
}
13+
14+
SUBCASE("missing uri")
15+
{
16+
req.verb = http::GET;
17+
}
18+
19+
SUBCASE("missing http version")
20+
{
21+
req.verb = http::GET;
22+
req.uri = "/index";
23+
}
24+
25+
SUBCASE("missing host")
26+
{
27+
req.verb = http::GET;
28+
req.uri = "/index";
29+
req.http_version_minor = 1;
30+
}
31+
32+
SUBCASE("bad http version")
33+
{
34+
req.verb = http::GET;
35+
req.uri = "/index";
36+
req.http_version_minor = 100;
37+
req.add_header(http::host, "www.example.com");
38+
}
39+
40+
std::error_code ec{};
41+
std::string buf;
42+
http::serialize_header(req, buf, ec);
43+
REQUIRE(bool(ec));
44+
}
45+
46+
TEST_CASE("serialize & parse good request")
47+
{
48+
http::request req0;
49+
req0.verb = http::GET;
50+
req0.uri = "/path/to/resource/with%20spaces?q=search+term&empty=&weird=%26%25%3F";
51+
req0.http_version_minor = 1;
52+
req0.add_header(http::host, "www.example.com:8080");
53+
req0.add_header(http::user_agent, "CustomTestAgent/7.4.2 (compatible; FancyBot/1.0; +https://example.com/bot)");
54+
req0.add_header(http::accept, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
55+
req0.add_header(http::accept_language, "en-US,en;q=0.5");
56+
req0.add_header(http::accept_encoding, "gzip, deflate, br");
57+
req0.add_header(http::connection, "keep-alive, Upgrade");
58+
req0.add_header(http::upgrade, "websocket");
59+
req0.add_header(http::sec_websocket_key, "x3JJHMbDL1EzLkh9GBhXDw==");
60+
req0.add_header(http::sec_websocket_version, "13");
61+
req0.add_header(http::cache_control, "no-cache, no-store, must-revalidate");
62+
req0.add_header(http::pragma, "no-cache");
63+
req0.add_header(http::content_type, "application/json; charset=\"utf-8\"");
64+
req0.content = "{\"message\": \"This is a test body with some content.\"}";
65+
REQUIRE(req0.keep_alive());
66+
REQUIRE(req0.is_websocket_req());
67+
68+
// Serialize
69+
std::error_code ec{};
70+
std::string buf;
71+
http::serialize_header(req0, buf, ec);
72+
buf.append(req0.content);
73+
REQUIRE(!bool(ec));
74+
75+
http::request req1;
76+
77+
SUBCASE("parse entire message")
78+
{
79+
const bool finished = http::parser<http::request>{}.parse(req1, buf, ec);
80+
REQUIRE(!bool(ec));
81+
REQUIRE(finished);
82+
}
83+
84+
SUBCASE("parse block by block")
85+
{
86+
http::parser<http::request> parser;
87+
88+
size_t blocksize{};
89+
90+
SUBCASE("") { blocksize = 1;}
91+
SUBCASE("") { blocksize = 10;}
92+
SUBCASE("") { blocksize = 99;}
93+
SUBCASE("") { blocksize = 128;}
94+
SUBCASE("") { blocksize = 1024;}
95+
96+
size_t nblocks = (buf.size() + blocksize - 1) / blocksize;
97+
std::string block;
98+
99+
for (size_t i = 0 ; i < nblocks ; ++i)
100+
{
101+
const size_t len = std::min(blocksize, buf.size());
102+
block.append(&buf[0], len);
103+
buf.erase(begin(buf), begin(buf) + len);
104+
const bool finished = parser.parse(req1, block, ec);
105+
REQUIRE(!bool(ec));
106+
REQUIRE(finished == (i == (nblocks-1)));
107+
}
108+
REQUIRE(block.empty());
109+
}
110+
111+
REQUIRE(buf.empty());
112+
REQUIRE(req0.verb == req1.verb);
113+
REQUIRE(req0.http_version_minor == req1.http_version_minor);
114+
REQUIRE(req0.uri == req1.uri);
115+
REQUIRE(req0.headers.size() == req1.headers.size());
116+
for (size_t i = 0 ; i < req0.headers.size() ; ++i)
117+
{
118+
REQUIRE(req0.headers[i].key == req1.headers[i].key);
119+
REQUIRE(req0.headers[i].value == req1.headers[i].value);
120+
}
121+
REQUIRE(req0.content == req1.content);
122+
}
123+
}

0 commit comments

Comments
 (0)