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#include "example/doc/http_examples.hpp"
11
12#include <boost/beast/core/flat_buffer.hpp>
13#include <boost/beast/core/read_size.hpp>
14#include <boost/beast/core/ostream.hpp>
15#include <boost/beast/core/detail/clamp.hpp>
16#include <boost/beast/core/detail/type_traits.hpp>
17#include <boost/beast/http/chunk_encode.hpp>
18#include <boost/beast/http/parser.hpp>
19#include <boost/beast/http/read.hpp>
20#include <boost/beast/http/write.hpp>
21#include <boost/beast/_experimental/test/stream.hpp>
22#include <boost/beast/test/yield_to.hpp>
23#include <boost/beast/_experimental/unit_test/suite.hpp>
24#include <sstream>
25#include <array>
26#include <limits>
27#include <list>
28#include <sstream>
29#include <vector>
30
31namespace boost {
32namespace beast {
33namespace http {
34
35class examples_test
36 : public beast::unit_test::suite
37 , public beast::test::enable_yield_to
38{
39public:
40 // two threads, for some examples
41 examples_test()
42 : enable_yield_to(2)
43 {
44 }
45
46 template<bool isRequest, class Body, class Fields>
47 static
48 std::string
49 to_string(message<isRequest, Body, Fields> const& m)
50 {
51 std::stringstream ss;
52 ss << m;
53 return ss.str();
54 }
55
56 template<bool isRequest>
57 bool
58 equal_body(string_view sv, string_view body)
59 {
60 test::stream ts{ioc_, sv};
61 message<isRequest, string_body, fields> m;
62 multi_buffer b;
63 ts.close_remote();
64 try
65 {
66 read(ts, b, m);
67 return m.body() == body;
68 }
69 catch(std::exception const& e)
70 {
71 log << "equal_body: " << e.what() << std::endl;
72 return false;
73 }
74 }
75
76 void
77 doExpect100Continue()
78 {
79 test::stream ts{ioc_}, tr{ioc_};
80 ts.connect(remote&: tr);
81 yield_to(
82 [&](yield_context)
83 {
84 error_code ec;
85 flat_buffer buffer;
86 receive_expect_100_continue(
87 tr, buffer, ec);
88 BEAST_EXPECTS(! ec, ec.message());
89 },
90 [&](yield_context)
91 {
92 flat_buffer buffer;
93 request<string_body> req;
94 req.version(11);
95 req.method_string("POST");
96 req.target("/");
97 req.insert(field::user_agent, "test");
98 req.body() = "Hello, world!";
99 req.prepare_payload();
100
101 error_code ec;
102 send_expect_100_continue(
103 ts, buffer, req, ec);
104 BEAST_EXPECTS(! ec, ec.message());
105 });
106 }
107
108 void
109 doCgiResponse()
110 {
111 std::string const s = "Hello, world!";
112 test::stream t0{ioc_, s};
113 t0.read_size(n: 3);
114 t0.close_remote();
115 test::stream t1{ioc_}, t1r{ioc_};
116 t1.connect(remote&: t1r);
117 error_code ec;
118 send_cgi_response(t0, t1, ec);
119 BEAST_EXPECTS(! ec, ec.message());
120 BEAST_EXPECT(equal_body<false>(t1r.str(), s));
121 }
122
123 void
124 doRelay()
125 {
126 request<string_body> req;
127 req.version(11);
128 req.method_string("POST");
129 req.target("/");
130 req.insert(field::user_agent, "test");
131 req.body() = "Hello, world!";
132 req.prepare_payload();
133
134 test::stream ds{ioc_}, dsr{ioc_};
135 ds.connect(remote&: dsr);
136 dsr.read_size(n: 3);
137 test::stream us{ioc_}, usr{ioc_};
138 us.connect(remote&: usr);
139 us.write_size(n: 3);
140
141 error_code ec;
142 write(ds, req);
143 BEAST_EXPECTS(! ec, ec.message());
144 ds.close();
145
146 flat_buffer buffer;
147 relay<true>(us, dsr, buffer, ec,
148 [&](header<true, fields>& h, error_code& ev)
149 {
150 ev = {};
151 h.erase(name: "Content-Length");
152 h.set(sname: "Transfer-Encoding", value: "chunked");
153 });
154 BEAST_EXPECTS(! ec, ec.message());
155 BEAST_EXPECT(equal_body<true>(
156 usr.str(), req.body()));
157 }
158
159 void
160 doReadStdStream()
161 {
162 std::string const s =
163 "HTTP/1.0 200 OK\r\n"
164 "User-Agent: test\r\n"
165 "\r\n"
166 "Hello, world!";
167 std::istringstream is(s);
168 error_code ec;
169 flat_buffer buffer;
170 response<string_body> res;
171 read_istream(is, buffer, res, ec);
172 BEAST_EXPECTS(! ec, ec.message());
173 BEAST_EXPECT(to_string(res) == s);
174 }
175
176 void
177 doWriteStdStream()
178 {
179 std::ostringstream os;
180 request<string_body> req;
181 req.version(11);
182 req.method(verb::get);
183 req.target("/");
184 req.insert(field::user_agent, "test");
185 error_code ec;
186 write_ostream(os, req, ec);
187 BEAST_EXPECTS(! ec, ec.message());
188 BEAST_EXPECT(to_string(req) == os.str());
189 }
190
191 void
192 doHEAD()
193 {
194 test::stream ts{ioc_}, tr{ioc_};
195 ts.connect(remote&: tr);
196 yield_to(
197 [&](yield_context)
198 {
199 error_code ec;
200 flat_buffer buffer;
201 do_server_head(tr, buffer, ec);
202 BEAST_EXPECTS(! ec, ec.message());
203 },
204 [&](yield_context)
205 {
206 error_code ec;
207 flat_buffer buffer;
208 auto res = do_head_request(ts, buffer, "/", ec);
209 BEAST_EXPECTS(! ec, ec.message());
210 });
211 }
212
213 struct handler
214 {
215 std::string body;
216
217 template<class Body>
218 void
219 operator()(request<Body>&&)
220 {
221 }
222
223 void
224 operator()(request<string_body>&& req)
225 {
226 body = req.body();
227 }
228 };
229
230 void
231 doDeferredBody()
232 {
233 test::stream ts(ioc_,
234 "POST / HTTP/1.1\r\n"
235 "User-Agent: test\r\n"
236 "Content-Type: multipart/form-data\r\n"
237 "Content-Length: 13\r\n"
238 "\r\n"
239 "Hello, world!");
240
241 handler h;
242 flat_buffer buffer;
243 do_form_request(ts, buffer, h);
244 BEAST_EXPECT(h.body == "Hello, world!");
245 }
246
247 //--------------------------------------------------------------------------
248
249 void
250 doIncrementalRead()
251 {
252 test::stream ts{ioc_};
253 std::string s(2048, '*');
254 ostream(buffer&: ts.buffer()) <<
255 "HTTP/1.1 200 OK\r\n"
256 "Content-Length: 2048\r\n"
257 "Server: test\r\n"
258 "\r\n" <<
259 s;
260 error_code ec;
261 flat_buffer b;
262 std::stringstream ss;
263 read_and_print_body<false>(ss, ts, b, ec);
264 if(BEAST_EXPECTS(! ec, ec.message()))
265 BEAST_EXPECT(ss.str() == s);
266 }
267
268 //--------------------------------------------------------------------------
269
270 void
271 doExplicitChunkSerialize()
272 {
273 auto const buf =
274 [](string_view s)
275 {
276 return net::const_buffer{
277 s.data(), s.size()};
278 };
279 test::stream ts{ioc_}, tr{ioc_};
280 ts.connect(remote&: tr);
281
282 response<empty_body> res{status::ok, 11};
283 res.set(field::server, "test");
284 res.set(field::accept, "Expires, Content-MD5");
285 res.chunked(true);
286
287 error_code ec;
288 response_serializer<empty_body> sr{res};
289 write_header(ts, sr, ec);
290
291 chunk_extensions exts;
292
293 net::write(s&: ts,
294 buffers: make_chunk(buffers: buf("First")), ec);
295
296 exts.insert(name: "quality", value: "1.0");
297 net::write(s&: ts,
298 buffers: make_chunk(buffers: buf("Hello, world!"), args&: exts), ec);
299
300 exts.clear();
301 exts.insert(name: "file", value: "abc.txt");
302 exts.insert(name: "quality", value: "0.7");
303 net::write(s&: ts,
304 buffers: make_chunk(buffers: buf("The Next Chunk"), args: std::move(exts)), ec);
305
306 exts.clear();
307 exts.insert(name: "last");
308 net::write(s&: ts,
309 buffers: make_chunk(buffers: buf("Last one"), args: std::move(exts),
310 args: std::allocator<double>{}), ec);
311
312 fields trailers;
313 trailers.set(name: field::expires, value: "never");
314 trailers.set(name: field::content_md5, value: "f4a5c16584f03d90");
315
316 net::write(s&: ts,
317 buffers: make_chunk_last(
318 trailer: trailers,
319 args: std::allocator<double>{}
320 ), ec);
321 BEAST_EXPECT(
322 buffers_to_string(tr.buffer().data()) ==
323 "HTTP/1.1 200 OK\r\n"
324 "Server: test\r\n"
325 "Accept: Expires, Content-MD5\r\n"
326 "Transfer-Encoding: chunked\r\n"
327 "\r\n"
328 "5\r\n"
329 "First\r\n"
330 "d;quality=1.0\r\n"
331 "Hello, world!\r\n"
332 "e;file=abc.txt;quality=0.7\r\n"
333 "The Next Chunk\r\n"
334 "8;last\r\n"
335 "Last one\r\n"
336 "0\r\n"
337 "Expires: never\r\n"
338 "Content-MD5: f4a5c16584f03d90\r\n"
339 "\r\n");
340 }
341
342 //--------------------------------------------------------------------------
343
344 void
345 doExplicitChunkParse()
346 {
347 test::stream ts(ioc_,
348 "HTTP/1.1 200 OK\r\n"
349 "Server: test\r\n"
350 "Trailer: Expires, Content-MD5\r\n"
351 "Transfer-Encoding: chunked\r\n"
352 "\r\n"
353 "5\r\n"
354 "First\r\n"
355 "d;quality=1.0\r\n"
356 "Hello, world!\r\n"
357 "e;file=abc.txt;quality=0.7\r\n"
358 "The Next Chunk\r\n"
359 "8;last\r\n"
360 "Last one\r\n"
361 "0\r\n"
362 "Expires: never\r\n"
363 "Content-MD5: f4a5c16584f03d90\r\n"
364 "\r\n");
365
366
367 error_code ec;
368 flat_buffer b;
369 std::stringstream ss;
370 print_chunked_body<false>(ss, ts, b, ec);
371 BEAST_EXPECTS(! ec, ec.message());
372 BEAST_EXPECT(ss.str() ==
373 "Chunk Body: First\n"
374 "Extension: quality = 1.0\n"
375 "Chunk Body: Hello, world!\n"
376 "Extension: file = abc.txt\n"
377 "Extension: quality = 0.7\n"
378 "Chunk Body: The Next Chunk\n"
379 "Extension: last\n"
380 "Chunk Body: Last one\n"
381 "Expires: never\n"
382 "Content-MD5: f4a5c16584f03d90\n");
383 }
384
385 //--------------------------------------------------------------------------
386
387 void
388 run()
389 {
390 doExpect100Continue();
391 doCgiResponse();
392 doRelay();
393 doReadStdStream();
394 doWriteStdStream();
395 doHEAD();
396 doDeferredBody();
397 doIncrementalRead();
398 doExplicitChunkSerialize();
399 doExplicitChunkParse();
400 }
401};
402
403BEAST_DEFINE_TESTSUITE(beast,http,examples);
404
405} // http
406} // beast
407} // boost
408

source code of boost/libs/beast/test/doc/http_examples.cpp