]>
git.proxmox.com Git - ceph.git/blob - ceph/src/Beast/test/http/design.cpp
2 // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
8 #include <beast/core/flat_streambuf.hpp>
9 #include <beast/core/prepare_buffers.hpp>
10 #include <beast/http/chunk_encode.hpp>
11 #include <beast/http/read.hpp>
12 #include <beast/http/write.hpp>
13 #include <beast/http/string_body.hpp>
14 #include <beast/core/detail/clamp.hpp>
15 #include <beast/test/string_istream.hpp>
16 #include <beast/test/string_ostream.hpp>
17 #include <beast/test/yield_to.hpp>
18 #include <beast/unit_test/suite.hpp>
19 #include <boost/asio/read.hpp>
20 #include <boost/asio/write.hpp>
26 : public beast::unit_test::suite
27 , public beast::test::enable_yield_to
30 //--------------------------------------------------------------------------
32 Read a message with a direct Reader Body.
36 using value_type
= std::string
;
44 static bool constexpr is_direct
= true;
46 using mutable_buffers_type
=
47 boost::asio::mutable_buffers_1
;
49 template<bool isRequest
, class Fields
>
51 reader(message
<isRequest
, direct_body
, Fields
>& m
)
62 init(std::uint64_t content_length
)
65 (std::numeric_limits
<std::size_t>::max
)())
66 throw std::length_error(
67 "Content-Length max exceeded");
68 body_
.reserve(static_cast<
69 std::size_t>(content_length
));
73 prepare(std::size_t n
)
75 body_
.resize(len_
+ n
);
76 return {&body_
[len_
], n
};
82 if(body_
.size() > len_
+ n
)
83 body_
.resize(len_
+ n
);
100 test::string_istream is
{ios_
,
102 "Content-Length: 1\r\n"
106 message
<true, direct_body
, fields
> m
;
107 flat_streambuf sb
{1024};
109 BEAST_EXPECT(m
.body
== "*");
114 test::string_istream is
{ios_
,
115 "HTTP/1.1 200 OK\r\n"
116 "\r\n" // 19 byte header
119 message
<false, direct_body
, fields
> m
;
120 flat_streambuf sb
{20};
122 BEAST_EXPECT(m
.body
== "*");
127 test::string_istream is
{ios_
,
129 "Transfer-Encoding: chunked\r\n"
135 message
<true, direct_body
, fields
> m
;
136 flat_streambuf sb
{100};
138 BEAST_EXPECT(m
.body
== "*");
142 //--------------------------------------------------------------------------
144 Read a message with an indirect Reader Body.
148 using value_type
= std::string
;
155 static bool constexpr is_direct
= false;
157 using mutable_buffers_type
=
158 boost::asio::null_buffers
;
160 template<bool isRequest
, class Fields
>
162 reader(message
<isRequest
, indirect_body
, Fields
>& m
)
173 init(std::uint64_t content_length
,
179 write(boost::string_ref
const& s
,
182 body_
.append(s
.data(), s
.size());
186 finish(error_code
& ec
)
197 test::string_istream is
{ios_
,
199 "Content-Length: 1\r\n"
203 message
<true, indirect_body
, fields
> m
;
204 flat_streambuf sb
{1024};
206 BEAST_EXPECT(m
.body
== "*");
211 test::string_istream is
{ios_
,
212 "HTTP/1.1 200 OK\r\n"
213 "\r\n" // 19 byte header
216 message
<false, indirect_body
, fields
> m
;
217 flat_streambuf sb
{20};
219 BEAST_EXPECT(m
.body
== "*");
225 test::string_istream is
{ios_
,
227 "Transfer-Encoding: chunked\r\n"
233 message
<true, indirect_body
, fields
> m
;
234 flat_streambuf sb
{1024};
236 BEAST_EXPECT(m
.body
== "*");
240 //--------------------------------------------------------------------------
242 Read a message header and manually read the body.
249 test::string_istream is
{ios_
,
251 "Content-Length: 5\r\n"
252 "\r\n" // 37 byte header
255 header_parser
<true, fields
> p
;
256 flat_streambuf sb
{38};
257 auto const bytes_used
=
258 read_some(is
, sb
, p
);
259 sb
.consume(bytes_used
);
260 BEAST_EXPECT(p
.size() == 5);
261 BEAST_EXPECT(sb
.size() < 5);
262 sb
.commit(boost::asio::read(
263 is
, sb
.prepare(5 - sb
.size())));
264 BEAST_EXPECT(sb
.size() == 5);
269 test::string_istream is
{ios_
,
270 "HTTP/1.1 200 OK\r\n"
271 "\r\n" // 19 byte header
274 header_parser
<false, fields
> p
;
275 flat_streambuf sb
{20};
276 auto const bytes_used
=
277 read_some(is
, sb
, p
);
278 sb
.consume(bytes_used
);
279 BEAST_EXPECT(p
.state() ==
280 parse_state::body_to_eof
);
281 BEAST_EXPECT(sb
.size() < 5);
282 sb
.commit(boost::asio::read(
283 is
, sb
.prepare(5 - sb
.size())));
284 BEAST_EXPECT(sb
.size() == 5);
288 //--------------------------------------------------------------------------
290 Read a header, check for Expect: 100-continue,
291 then conditionally read the body.
294 testExpect100Continue()
297 test::string_istream is
{ios_
,
299 "Expect: 100-continue\r\n"
300 "Content-Length: 5\r\n"
305 header_parser
<true, fields
> p
;
306 flat_streambuf sb
{128};
307 auto const bytes_used
=
308 read_some(is
, sb
, p
);
309 sb
.consume(bytes_used
);
310 BEAST_EXPECT(p
.got_header());
312 p
.get().fields
["Expect"] ==
315 true, string_body
, fields
> p1
{
319 p1
.get().body
== "*****");
323 //--------------------------------------------------------------------------
325 Efficiently relay a message from one stream to another
329 class SyncWriteStream
,
331 class SyncReadStream
>
334 SyncWriteStream
& out
,
338 flat_streambuf buffer
{4096}; // 4K limit
339 header_parser
<isRequest
, fields
> parser
;
343 auto const state0
= parser
.state();
344 auto const bytes_used
=
345 read_some(in
, buffer
, parser
, ec
);
346 BEAST_EXPECTS(! ec
, ec
.message());
349 case parse_state::header
:
351 BEAST_EXPECT(parser
.got_header());
352 write(out
, parser
.get());
356 case parse_state::chunk_header
:
358 // inspect parser.chunk_extension() here
359 if(parser
.is_complete())
360 boost::asio::write(out
,
361 chunk_encode_final());
365 case parse_state::body
:
366 case parse_state::body_to_eof
:
367 case parse_state::chunk_body
:
369 if(! parser
.is_complete())
371 auto const body
= parser
.body();
372 boost::asio::write(out
, chunk_encode(
373 false, boost::asio::buffer(
374 body
.data(), body
.size())));
379 case parse_state::complete
:
382 buffer
.consume(bytes_used
);
384 while(! parser
.is_complete());
392 test::string_istream is
{ios_
,
394 "Content-Length: 5\r\n"
395 "\r\n" // 37 byte header
399 test::string_ostream os
{ios_
};
400 flat_streambuf sb
{16};
401 relay
<true>(os
, sb
, is
);
406 test::string_istream is
{ios_
,
407 "HTTP/1.1 200 OK\r\n"
408 "\r\n" // 19 byte header
412 test::string_ostream os
{ios_
};
413 flat_streambuf sb
{16};
414 relay
<false>(os
, sb
, is
);
419 test::string_istream is
{ios_
,
421 "Transfer-Encoding: chunked\r\n"
423 "5;x;y=1;z=\"-\"\r\n*****\r\n"
429 test::string_ostream os
{ios_
};
430 flat_streambuf sb
{16};
431 relay
<true>(os
, sb
, is
);
435 //--------------------------------------------------------------------------
437 Read the request header, then read the request body content using
438 a fixed-size buffer, i.e. read the body in chunks of 4k for instance.
439 The end of the body should be indicated somehow and chunk-encoding
440 should be decoded by beast.
442 template<bool isRequest
,
443 class SyncReadStream
, class BodyCallback
>
445 doFixedRead(SyncReadStream
& stream
, BodyCallback
const& cb
)
447 flat_streambuf buffer
{4096}; // 4K limit
448 header_parser
<isRequest
, fields
> parser
;
449 std::size_t bytes_used
;
450 bytes_used
= read_some(stream
, buffer
, parser
);
451 BEAST_EXPECT(parser
.got_header());
452 buffer
.consume(bytes_used
);
456 read_some(stream
, buffer
, parser
);
457 if(! parser
.body().empty())
459 buffer
.consume(bytes_used
);
461 while(! parser
.is_complete());
467 operator()(boost::string_ref
const& body
) const
469 // called for each piece of the body,
476 using boost::asio::buffer
;
477 using boost::asio::buffer_cast
;
478 using boost::asio::buffer_size
;
482 test::string_istream is
{ios_
,
484 "Content-Length: 1\r\n"
488 doFixedRead
<true>(is
, bodyHandler
{});
493 test::string_istream is
{ios_
,
494 "HTTP/1.1 200 OK\r\n"
495 "\r\n" // 19 byte header
498 doFixedRead
<false>(is
, bodyHandler
{});
503 test::string_istream is
{ios_
,
505 "Transfer-Encoding: chunked\r\n"
507 "5;x;y=1;z=\"-\"\r\n*****\r\n"
513 doFixedRead
<true>(is
, bodyHandler
{});
517 //--------------------------------------------------------------------------
525 testExpect100Continue();
531 BEAST_DEFINE_TESTSUITE(design
,http
,beast
);