]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/test/beast/http/parser.cpp
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / boost / libs / beast / test / beast / http / parser.cpp
1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9
10 // Test that header file is self-contained.
11 #include <boost/beast/http/parser.hpp>
12
13 #include "test_parser.hpp"
14
15 #include <boost/beast/_experimental/unit_test/suite.hpp>
16 #include <boost/beast/core/buffer_traits.hpp>
17 #include <boost/beast/core/buffers_suffix.hpp>
18 #include <boost/beast/core/flat_buffer.hpp>
19 #include <boost/beast/core/multi_buffer.hpp>
20 #include <boost/beast/core/ostream.hpp>
21 #include <boost/beast/http/read.hpp>
22 #include <boost/beast/http/string_body.hpp>
23 #include <boost/system/system_error.hpp>
24 #include <algorithm>
25
26 namespace boost {
27 namespace beast {
28 namespace http {
29
30 class parser_test
31 : public beast::unit_test::suite
32 {
33 public:
34 template<bool isRequest>
35 using parser_type =
36 parser<isRequest, string_body>;
37
38 static
39 net::const_buffer
40 buf(string_view s)
41 {
42 return {s.data(), s.size()};
43 }
44
45 template<class ConstBufferSequence,
46 bool isRequest>
47 static
48 void
49 put(ConstBufferSequence const& buffers,
50 basic_parser<isRequest>& p,
51 error_code& ec)
52 {
53 buffers_suffix<ConstBufferSequence> cb{buffers};
54 for(;;)
55 {
56 auto const used = p.put(cb, ec);
57 cb.consume(used);
58 if(ec)
59 return;
60 if(p.need_eof() &&
61 buffer_bytes(cb) == 0)
62 {
63 p.put_eof(ec);
64 if(ec)
65 return;
66 }
67 if(p.is_done())
68 break;
69 }
70 }
71
72 template<bool isRequest, class F>
73 void
74 doMatrix(string_view s0, F const& f)
75 {
76 // parse a single buffer
77 {
78 auto s = s0;
79 error_code ec;
80 parser_type<isRequest> p;
81 put(net::buffer(s.data(), s.size()), p, ec);
82 if(! BEAST_EXPECTS(! ec, ec.message()))
83 return;
84 f(p);
85 }
86 // parse two buffers
87 for(auto n = s0.size() - 1; n >= 1; --n)
88 {
89 auto s = s0;
90 error_code ec;
91 parser_type<isRequest> p;
92 p.eager(true);
93 auto used =
94 p.put(net::buffer(s.data(), n), ec);
95 s.remove_prefix(used);
96 if(ec == error::need_more)
97 ec = {};
98 if(! BEAST_EXPECTS(! ec, ec.message()))
99 continue;
100 BEAST_EXPECT(! p.is_done());
101 used = p.put(
102 net::buffer(s.data(), s.size()), ec);
103 s.remove_prefix(used);
104 if(! BEAST_EXPECTS(! ec, ec.message()))
105 continue;
106 BEAST_EXPECT(s.empty());
107 if(p.need_eof())
108 {
109 p.put_eof(ec);
110 if(! BEAST_EXPECTS(! ec, ec.message()))
111 continue;
112 }
113 if(BEAST_EXPECT(p.is_done()))
114 f(p);
115 }
116 }
117
118 void
119 testParse()
120 {
121 doMatrix<false>(
122 "HTTP/1.0 200 OK\r\n"
123 "Server: test\r\n"
124 "\r\n"
125 "Hello, world!",
126 [&](parser_type<false> const& p)
127 {
128 auto const& m = p.get();
129 BEAST_EXPECT(! p.chunked());
130 BEAST_EXPECT(p.need_eof());
131 BEAST_EXPECT(p.content_length() == boost::none);
132 BEAST_EXPECT(m.version() == 10);
133 BEAST_EXPECT(m.result() == status::ok);
134 BEAST_EXPECT(m.reason() == "OK");
135 BEAST_EXPECT(m["Server"] == "test");
136 BEAST_EXPECT(m.body() == "Hello, world!");
137 }
138 );
139 doMatrix<false>(
140 "HTTP/1.1 200 OK\r\n"
141 "Server: test\r\n"
142 "Expect: Expires, MD5-Fingerprint\r\n"
143 "Transfer-Encoding: chunked\r\n"
144 "\r\n"
145 "5\r\n"
146 "*****\r\n"
147 "2;a;b=1;c=\"2\"\r\n"
148 "--\r\n"
149 "0;d;e=3;f=\"4\"\r\n"
150 "Expires: never\r\n"
151 "MD5-Fingerprint: -\r\n"
152 "\r\n",
153 [&](parser_type<false> const& p)
154 {
155 auto const& m = p.get();
156 BEAST_EXPECT(! p.need_eof());
157 BEAST_EXPECT(p.chunked());
158 BEAST_EXPECT(p.content_length() == boost::none);
159 BEAST_EXPECT(m.version() == 11);
160 BEAST_EXPECT(m.result() == status::ok);
161 BEAST_EXPECT(m.reason() == "OK");
162 BEAST_EXPECT(m["Server"] == "test");
163 BEAST_EXPECT(m["Transfer-Encoding"] == "chunked");
164 BEAST_EXPECT(m["Expires"] == "never");
165 BEAST_EXPECT(m["MD5-Fingerprint"] == "-");
166 BEAST_EXPECT(m.body() == "*****--");
167 }
168 );
169 doMatrix<false>(
170 "HTTP/1.0 200 OK\r\n"
171 "Server: test\r\n"
172 "Content-Length: 5\r\n"
173 "\r\n"
174 "*****",
175 [&](parser_type<false> const& p)
176 {
177 auto const& m = p.get();
178 BEAST_EXPECT(m.body() == "*****");
179 }
180 );
181 doMatrix<true>(
182 "GET / HTTP/1.1\r\n"
183 "User-Agent: test\r\n"
184 "\r\n",
185 [&](parser_type<true> const& p)
186 {
187 auto const& m = p.get();
188 BEAST_EXPECT(m.method() == verb::get);
189 BEAST_EXPECT(m.target() == "/");
190 BEAST_EXPECT(m.version() == 11);
191 BEAST_EXPECT(! p.need_eof());
192 BEAST_EXPECT(! p.chunked());
193 BEAST_EXPECT(p.content_length() == boost::none);
194 }
195 );
196 doMatrix<true>(
197 "GET / HTTP/1.1\r\n"
198 "User-Agent: test\r\n"
199 "X: \t x \t \r\n"
200 "\r\n",
201 [&](parser_type<true> const& p)
202 {
203 auto const& m = p.get();
204 BEAST_EXPECT(m["X"] == "x");
205 }
206 );
207
208 // test eager(true)
209 {
210 error_code ec;
211 parser_type<true> p;
212 p.eager(true);
213 p.put(buf(
214 "GET / HTTP/1.1\r\n"
215 "User-Agent: test\r\n"
216 "Content-Length: 1\r\n"
217 "\r\n"
218 "*")
219 , ec);
220 auto const& m = p.get();
221 BEAST_EXPECT(! ec);
222 BEAST_EXPECT(p.is_done());
223 BEAST_EXPECT(p.is_header_done());
224 BEAST_EXPECT(! p.need_eof());
225 BEAST_EXPECT(m.method() == verb::get);
226 BEAST_EXPECT(m.target() == "/");
227 BEAST_EXPECT(m.version() == 11);
228 BEAST_EXPECT(m["User-Agent"] == "test");
229 BEAST_EXPECT(m.body() == "*");
230 }
231 {
232 // test partial parsing of final chunk
233 // parse through the chunk body
234 error_code ec;
235 flat_buffer b;
236 parser_type<true> p;
237 p.eager(true);
238 ostream(b) <<
239 "PUT / HTTP/1.1\r\n"
240 "Transfer-Encoding: chunked\r\n"
241 "\r\n"
242 "1\r\n"
243 "*";
244 auto used = p.put(b.data(), ec);
245 b.consume(used);
246 BEAST_EXPECT(! ec);
247 BEAST_EXPECT(! p.is_done());
248 BEAST_EXPECT(p.get().body() == "*");
249 ostream(b) <<
250 "\r\n"
251 "0;d;e=3;f=\"4\"\r\n"
252 "Expires: never\r\n"
253 "MD5-Fingerprint: -\r\n";
254 // incomplete parse, missing the final crlf
255 used = p.put(b.data(), ec);
256 b.consume(used);
257 BEAST_EXPECT(ec == error::need_more);
258 ec = {};
259 BEAST_EXPECT(! p.is_done());
260 ostream(b) <<
261 "\r\n"; // final crlf to end message
262 used = p.put(b.data(), ec);
263 b.consume(used);
264 BEAST_EXPECTS(! ec, ec.message());
265 BEAST_EXPECT(p.is_done());
266 }
267 // skip body
268 {
269 error_code ec;
270 response_parser<string_body> p;
271 p.skip(true);
272 p.put(buf(
273 "HTTP/1.1 200 OK\r\n"
274 "Content-Length: 5\r\n"
275 "\r\n"
276 "*****")
277 , ec);
278 BEAST_EXPECTS(! ec, ec.message());
279 BEAST_EXPECT(p.is_done());
280 BEAST_EXPECT(p.is_header_done());
281 BEAST_EXPECT(p.content_length() &&
282 *p.content_length() == 5);
283 }
284 }
285
286 //--------------------------------------------------------------------------
287
288 template<class DynamicBuffer>
289 void
290 testNeedMore()
291 {
292 error_code ec;
293 std::size_t used;
294 {
295 DynamicBuffer b;
296 parser_type<true> p;
297 ostream(b) <<
298 "GET / HTTP/1.1\r\n";
299 used = p.put(b.data(), ec);
300 BEAST_EXPECTS(ec == error::need_more, ec.message());
301 b.consume(used);
302 ec = {};
303 ostream(b) <<
304 "User-Agent: test\r\n"
305 "\r\n";
306 used = p.put(b.data(), ec);
307 BEAST_EXPECTS(! ec, ec.message());
308 b.consume(used);
309 BEAST_EXPECT(p.is_done());
310 BEAST_EXPECT(p.is_header_done());
311 }
312 }
313
314 void
315 testGotSome()
316 {
317 error_code ec;
318 parser_type<true> p;
319 auto used = p.put(buf(""), ec);
320 BEAST_EXPECT(ec == error::need_more);
321 BEAST_EXPECT(! p.got_some());
322 BEAST_EXPECT(used == 0);
323 ec = {};
324 used = p.put(buf("G"), ec);
325 BEAST_EXPECT(ec == error::need_more);
326 BEAST_EXPECT(p.got_some());
327 BEAST_EXPECT(used == 0);
328 }
329
330 void
331 testIssue818()
332 {
333 // Make sure that the parser clears pre-existing fields
334 request<string_body> m;
335 m.set(field::accept, "html/text");
336 BEAST_EXPECT(std::distance(m.begin(), m.end()) == 1);
337 request_parser<string_body> p{std::move(m)};
338 BEAST_EXPECT(std::distance(m.begin(), m.end()) == 0);
339 auto& m1 = p.get();
340 BEAST_EXPECT(std::distance(m1.begin(), m1.end()) == 0);
341 }
342
343 void
344 testIssue1187()
345 {
346 // make sure parser finishes on redirect
347 error_code ec;
348 parser_type<false> p;
349 p.eager(true);
350 p.put(buf(
351 "HTTP/1.1 301 Moved Permanently\r\n"
352 "Location: https://www.ebay.com\r\n"
353 "\r\n\r\n"), ec);
354 BEAST_EXPECTS(! ec, ec.message());
355 BEAST_EXPECT(p.is_header_done());
356 BEAST_EXPECT(! p.is_done());
357 BEAST_EXPECT(p.need_eof());
358 }
359
360 void
361 testIssue1880()
362 {
363 // A user raised the issue that multiple Content-Length fields and
364 // values are permissible provided all values are the same.
365 // See rfc7230 section-3.3.2
366 // https://tools.ietf.org/html/rfc7230#section-3.3.2
367 // Credit: Dimitry Bulsunov
368
369 auto checkPass = [&](std::string const& message)
370 {
371 response_parser<string_body> parser;
372 error_code ec;
373 parser.put(net::buffer(message), ec);
374 BEAST_EXPECTS(!ec.failed(), ec.message());
375 };
376
377 auto checkFail = [&](std::string const& message)
378 {
379 response_parser<string_body> parser;
380 error_code ec;
381 parser.put(net::buffer(message), ec);
382 BEAST_EXPECTS(ec == error::bad_content_length, ec.message());
383 };
384
385 // multiple contents lengths the same
386 checkPass(
387 "HTTP/1.1 200 OK\r\n"
388 "Content-Length: 0\r\n"
389 "Content-Length: 0\r\n"
390 "\r\n");
391
392 // multiple contents lengths different
393 checkFail(
394 "HTTP/1.1 200 OK\r\n"
395 "Content-Length: 0\r\n"
396 "Content-Length: 1\r\n"
397 "\r\n");
398
399 // multiple content in same header
400 checkPass(
401 "HTTP/1.1 200 OK\r\n"
402 "Content-Length: 0, 0, 0\r\n"
403 "\r\n");
404
405 // multiple content in same header but mismatch (case 1)
406 checkFail(
407 "HTTP/1.1 200 OK\r\n"
408 "Content-Length: 0, 0, 1\r\n"
409 "\r\n");
410
411 // multiple content in same header but mismatch (case 2)
412 checkFail(
413 "HTTP/1.1 200 OK\r\n"
414 "Content-Length: 0, 0, 0\r\n"
415 "Content-Length: 1\r\n"
416 "\r\n");
417 }
418
419 void
420 run() override
421 {
422 testParse();
423 testNeedMore<flat_buffer>();
424 testNeedMore<multi_buffer>();
425 testGotSome();
426 testIssue818();
427 testIssue1187();
428 testIssue1880();
429 }
430 };
431
432 BEAST_DEFINE_TESTSUITE(beast,http,parser);
433
434 } // http
435 } // beast
436 } // boost