Toolbox snapshot
The Reactive C++ Toolbox
Loading...
Searching...
No Matches
Parser.ut.cpp
Go to the documentation of this file.
1// The Reactive C++ Toolbox.
2// Copyright (C) 2013-2019 Swirly Cloud Limited
3// Copyright (C) 2021 Reactive Markets Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#include "Parser.hpp"
18
19#include <toolbox/http/Url.hpp>
21
22#include <boost/test/unit_test.hpp>
23
24#include <iostream>
25#include <vector>
26
27namespace std {
28template <typename T, typename U>
29ostream& operator<<(ostream& os, const pair<T, U>& p)
30{
31 return os << '(' << p.first << ',' << p.second << ')';
32}
33} // namespace std
34
35using namespace std;
36using namespace toolbox;
37
38namespace {
39
40class Parser
41: public BasicParser<Parser>
42, public BasicUrl<Parser> {
43 friend class BasicParser<Parser>;
44 friend class BasicUrl<Parser>;
45
46 public:
47 using BasicParser<Parser>::BasicParser;
48 ~Parser() = default;
49
50 const auto& url() const noexcept { return url_; }
51 const auto& status() const noexcept { return status_; }
52 const auto& headers() const noexcept { return headers_; }
53 const auto& body() const noexcept { return body_; }
54
55 void clear() noexcept
56 {
57 url_.clear();
58 status_.clear();
59 headers_.clear();
60 body_.clear();
61 }
62 using BasicParser<Parser>::parse;
63
64 private:
65 bool on_http_message_begin(CyclTime /*now*/) noexcept
66 {
68 clear();
69 return true;
70 }
71 bool on_http_url(CyclTime /*now*/, string_view sv) noexcept
72 {
73 url_.append(sv.data(), sv.size());
74 return true;
75 }
76 bool on_http_status(CyclTime /*now*/, string_view sv) noexcept
77 {
78 status_.append(sv.data(), sv.size());
79 return true;
80 }
81 bool on_http_header_field(CyclTime /*now*/, string_view sv, First first) noexcept
82 {
83 if (first == First::Yes) {
84 headers_.emplace_back(string{sv.data(), sv.size()}, "");
85 } else {
86 headers_.back().first.append(sv.data(), sv.size());
87 }
88 return true;
89 }
90 bool on_http_header_value(CyclTime /*now*/, string_view sv, First /*first*/) noexcept
91 {
92 headers_.back().second.append(sv.data(), sv.size());
93 return true;
94 }
95 bool on_http_headers_end(CyclTime /*now*/) noexcept { return true; }
96 bool on_http_body(CyclTime /*now*/, string_view sv) noexcept
97 {
98 body_.append(sv.data(), sv.size());
99 return true;
100 }
101 bool on_http_message_end(CyclTime /*now*/) noexcept
102 {
103 bool ret{false};
104 try {
105 if (!url_.empty()) {
107 }
108 ret = true;
109 } catch (const std::exception& e) {
110 cerr << "exception: " << e.what() << endl;
111 }
112 return ret;
113 }
114 bool on_http_chunk_header(CyclTime /*now*/, size_t /*len*/) noexcept { return true; }
115 bool on_http_chunk_end(CyclTime /*now*/) noexcept { return true; }
116
117 string url_;
118 string status_;
120 string body_;
121};
122
123} // namespace
124
126
128{
129 constexpr auto Message = //
130 "GET /path/to/file/index.html HTTP/1.0\r\n" //
131 "\r\n"sv;
132
133 Parser h{Type::Request};
134 const auto now = CyclTime::now();
135 BOOST_CHECK_EQUAL(h.parse(now, {Message.data(), Message.size()}), Message.size());
136 BOOST_CHECK(!h.should_keep_alive());
137 BOOST_CHECK_EQUAL(h.http_major(), 1);
138 BOOST_CHECK_EQUAL(h.http_minor(), 0);
139
140 BOOST_CHECK_EQUAL(h.method(), Method::Get);
141 BOOST_CHECK_EQUAL(h.url(), "/path/to/file/index.html"s);
142
143 BOOST_CHECK(h.headers().empty());
144 BOOST_CHECK(h.body().empty());
145}
146
148{
149 constexpr auto Message = //
150 "HTTP/1.0 404 Not Found\r\n" //
151 "\r\n"sv;
152
153 Parser h{Type::Response};
154 const auto now = CyclTime::now();
155 BOOST_CHECK_EQUAL(h.parse(now, {Message.data(), Message.size()}), Message.size());
156 BOOST_CHECK(!h.should_keep_alive());
157 BOOST_CHECK_EQUAL(h.http_major(), 1);
158 BOOST_CHECK_EQUAL(h.http_minor(), 0);
159
160 BOOST_CHECK_EQUAL(h.status_code(), 404);
161 BOOST_CHECK_EQUAL(h.status(), "Not Found"s);
162
163 BOOST_CHECK(h.headers().empty());
164 BOOST_CHECK(h.body().empty());
165}
166
168{
169 constexpr auto Message = //
170 "GET /path/file.html HTTP/1.0\r\n" //
171 "From: someuser@reactivemarkets.com\r\n" //
172 "User-Agent: HTTPTool/1.0\r\n" //
173 "\r\n"sv;
174
175 Parser h{Type::Request};
176 const auto now = CyclTime::now();
177 BOOST_CHECK_EQUAL(h.parse(now, {Message.data(), Message.size()}), Message.size());
178 BOOST_CHECK(!h.should_keep_alive());
179 BOOST_CHECK_EQUAL(h.http_major(), 1);
180 BOOST_CHECK_EQUAL(h.http_minor(), 0);
181
182 BOOST_CHECK_EQUAL(h.method(), Method::Get);
183 BOOST_CHECK_EQUAL(h.url(), "/path/file.html"s);
184
185 BOOST_CHECK_EQUAL(h.headers().size(), 2U);
186 BOOST_CHECK_EQUAL(h.headers()[0], make_pair("From"s, "someuser@reactivemarkets.com"s));
187 BOOST_CHECK_EQUAL(h.headers()[1], make_pair("User-Agent"s, "HTTPTool/1.0"s));
188
189 BOOST_CHECK(h.body().empty());
190}
191
193{
194 constexpr auto Message = //
195 "HTTP/1.0 200 OK\r\n" //
196 "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" //
197 "Content-Type: text/plain\r\n" //
198 "Content-Length: 13\r\n" //
199 "\r\n" //
200 "Hello, World!"sv;
201
202 Parser h{Type::Response};
203 const auto now = CyclTime::now();
204 BOOST_CHECK_EQUAL(h.parse(now, {Message.data(), Message.size()}), Message.size());
205 BOOST_CHECK(!h.should_keep_alive());
206 BOOST_CHECK_EQUAL(h.http_major(), 1);
207 BOOST_CHECK_EQUAL(h.http_minor(), 0);
208
209 BOOST_CHECK_EQUAL(h.status_code(), 200);
210 BOOST_CHECK_EQUAL(h.status(), "OK"s);
211
212 BOOST_CHECK_EQUAL(h.headers().size(), 3U);
213 BOOST_CHECK_EQUAL(h.headers()[0], make_pair("Date"s, "Fri, 31 Dec 1999 23:59:59 GMT"s));
214 BOOST_CHECK_EQUAL(h.headers()[1], make_pair("Content-Type"s, "text/plain"s));
215 BOOST_CHECK_EQUAL(h.headers()[2], make_pair("Content-Length"s, "13"s));
216
217 BOOST_CHECK_EQUAL(h.body(), "Hello, World!"s);
218}
219
221{
222 constexpr auto Message = //
223 "POST /path/script.cgi HTTP/1.0\r\n" //
224 "From: frog@reactivemarkets.com\r\n" //
225 "User-Agent: HTTPTool/1.0\r\n" //
226 "Content-Type: application/x-www-form-urlencoded\r\n" //
227 "Content-Length: 32\r\n" //
228 "\r\n" //
229 "home=Cosby&favorite+flavor=flies"sv;
230
231 Parser h{Type::Request};
232 const auto now = CyclTime::now();
233 BOOST_CHECK_EQUAL(h.parse(now, {Message.data(), Message.size()}), Message.size());
234 BOOST_CHECK(!h.should_keep_alive());
235 BOOST_CHECK_EQUAL(h.http_major(), 1);
236 BOOST_CHECK_EQUAL(h.http_minor(), 0);
237
238 BOOST_CHECK_EQUAL(h.method(), Method::Post);
239 BOOST_CHECK_EQUAL(h.url(), "/path/script.cgi"s);
240
241 BOOST_CHECK_EQUAL(h.headers().size(), 4U);
242 BOOST_CHECK_EQUAL(h.headers()[0], make_pair("From"s, "frog@reactivemarkets.com"s));
243 BOOST_CHECK_EQUAL(h.headers()[1], make_pair("User-Agent"s, "HTTPTool/1.0"s));
244 BOOST_CHECK_EQUAL(h.headers()[2],
245 make_pair("Content-Type"s, "application/x-www-form-urlencoded"s));
246 BOOST_CHECK_EQUAL(h.headers()[3], make_pair("Content-Length"s, "32"s));
247
248 BOOST_CHECK_EQUAL(h.body(), "home=Cosby&favorite+flavor=flies"s);
249}
250
252{
253 constexpr auto Message = //
254 "GET /path/file.html HTTP/1.1\r\n" //
255 "Host: www.host1.com:80\r\n" //
256 "\r\n"sv;
257
258 Parser h{Type::Request};
259 const auto now = CyclTime::now();
260 BOOST_CHECK_EQUAL(h.parse(now, {Message.data(), Message.size()}), Message.size());
261 BOOST_CHECK(h.should_keep_alive());
262 BOOST_CHECK_EQUAL(h.http_major(), 1);
263 BOOST_CHECK_EQUAL(h.http_minor(), 1);
264
265 BOOST_CHECK_EQUAL(h.method(), Method::Get);
266 BOOST_CHECK_EQUAL(h.url(), "/path/file.html"s);
267
268 BOOST_CHECK_EQUAL(h.headers().size(), 1U);
269 BOOST_CHECK_EQUAL(h.headers()[0], make_pair("Host"s, "www.host1.com:80"s));
270
271 BOOST_CHECK(h.body().empty());
272}
273
275{
276 constexpr auto Message = //
277 "HTTP/1.1 200 OK\r\n" //
278 "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" //
279 "Content-Type: text/plain\r\n" //
280 "Transfer-Encoding: chunked\r\n" //
281 "\r\n" //
282 "1a; ignore-stuff-here\r\n" //
283 "abcdefghijklmnopqrstuvwxyz\r\n" //
284 "10\r\n" //
285 "1234567890abcdef\r\n" //
286 "0\r\n" //
287 "some-footer: some-value\r\n" //
288 "another-footer: another-value\r\n" //
289 "\r\n"sv;
290
291 Parser h{Type::Response};
292 const auto now = CyclTime::now();
293 BOOST_CHECK_EQUAL(h.parse(now, {Message.data(), Message.size()}), Message.size());
294 BOOST_CHECK(h.should_keep_alive());
295 BOOST_CHECK_EQUAL(h.http_major(), 1);
296 BOOST_CHECK_EQUAL(h.http_minor(), 1);
297
298 BOOST_CHECK_EQUAL(h.status_code(), 200);
299 BOOST_CHECK_EQUAL(h.status(), "OK"s);
300
301 BOOST_CHECK_EQUAL(h.headers().size(), 5U);
302 BOOST_CHECK_EQUAL(h.headers()[0], make_pair("Date"s, "Fri, 31 Dec 1999 23:59:59 GMT"s));
303 BOOST_CHECK_EQUAL(h.headers()[1], make_pair("Content-Type"s, "text/plain"s));
304 BOOST_CHECK_EQUAL(h.headers()[2], make_pair("Transfer-Encoding"s, "chunked"s));
305 BOOST_CHECK_EQUAL(h.headers()[3], make_pair("some-footer"s, "some-value"s));
306 BOOST_CHECK_EQUAL(h.headers()[4], make_pair("another-footer"s, "another-value"s));
307
308 BOOST_CHECK_EQUAL(h.body(), "abcdefghijklmnopqrstuvwxyz1234567890abcdef"s);
309}
310
312{
313 constexpr auto Message = //
314 "POST /path/script.cgi HTTP/1.1\r\n" //
315 "Content-Type: text/plain\r\n" //
316 "Content-Length: 5\r\n" //
317 "\r\n" //
318 "first" //
319 "POST /path/script.cgi HTTP/1.1\r\n" //
320 "Content-Type: text/plain\r\n" //
321 "Content-Length: 6\r\n" //
322 "\r\n" //
323 "second"sv;
324
325 Parser h{Type::Request};
326 const auto now = CyclTime::now();
327 BOOST_CHECK_EQUAL(h.parse(now, {Message.data(), Message.size()}), Message.size());
328 BOOST_CHECK(h.should_keep_alive());
329 BOOST_CHECK_EQUAL(h.http_major(), 1);
330 BOOST_CHECK_EQUAL(h.http_minor(), 1);
331
332 BOOST_CHECK_EQUAL(h.method(), Method::Post);
333 BOOST_CHECK_EQUAL(h.url(), "/path/script.cgi"s);
334
335 BOOST_CHECK_EQUAL(h.headers().size(), 2U);
336 BOOST_CHECK_EQUAL(h.headers()[0], make_pair("Content-Type"s, "text/plain"s));
337 BOOST_CHECK_EQUAL(h.headers()[1], make_pair("Content-Length"s, "6"s));
338
339 BOOST_CHECK_EQUAL(h.body(), "second"s);
340}
341
BOOST_CHECK_EQUAL(v.size(), 10U)
BOOST_AUTO_TEST_CASE(InitialRequestLineCase)
STL namespace.
ostream & operator<<(ostream &os, const pair< T, U > &p)
Definition Parser.ut.cpp:29
void clear(timeval &tv) noexcept
Definition Time.hpp:294
std::string_view sv
Definition Tokeniser.hpp:26
constexpr auto bind() noexcept
Definition Slot.hpp:92
BOOST_CHECK(isnan(stod(""sv, numeric_limits< double >::quiet_NaN())))