• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
31 namespace boost {
32 namespace beast {
33 namespace http {
34 
35 class examples_test
36     : public beast::unit_test::suite
37     , public beast::test::enable_yield_to
38 {
39 public:
40     // two threads, for some examples
examples_test()41     examples_test()
42         : enable_yield_to(2)
43     {
44     }
45 
46     template<bool isRequest, class Body, class Fields>
47     static
48     std::string
to_string(message<isRequest,Body,Fields> const & m)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
equal_body(string_view sv,string_view body)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
doExpect100Continue()77     doExpect100Continue()
78     {
79         test::stream ts{ioc_}, tr{ioc_};
80         ts.connect(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
doCgiResponse()109     doCgiResponse()
110     {
111         std::string const s = "Hello, world!";
112         test::stream t0{ioc_, s};
113         t0.read_size(3);
114         t0.close_remote();
115         test::stream t1{ioc_}, t1r{ioc_};
116         t1.connect(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
doRelay()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(dsr);
136         dsr.read_size(3);
137         test::stream us{ioc_}, usr{ioc_};
138         us.connect(usr);
139         us.write_size(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("Content-Length");
152                 h.set("Transfer-Encoding", "chunked");
153             });
154         BEAST_EXPECTS(! ec, ec.message());
155         BEAST_EXPECT(equal_body<true>(
156             usr.str(), req.body()));
157     }
158 
159     void
doReadStdStream()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
doWriteStdStream()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
doHEAD()192     doHEAD()
193     {
194         test::stream ts{ioc_}, tr{ioc_};
195         ts.connect(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
operator ()boost::beast::http::examples_test::handler219         operator()(request<Body>&&)
220         {
221         }
222 
223         void
operator ()boost::beast::http::examples_test::handler224         operator()(request<string_body>&& req)
225         {
226             body = req.body();
227         }
228     };
229 
230     void
doDeferredBody()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
doIncrementalRead()250     doIncrementalRead()
251     {
252         test::stream ts{ioc_};
253         std::string s(2048, '*');
254         ostream(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
doExplicitChunkSerialize()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(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(ts,
294             make_chunk(buf("First")), ec);
295 
296         exts.insert("quality", "1.0");
297         net::write(ts,
298             make_chunk(buf("Hello, world!"), exts), ec);
299 
300         exts.clear();
301         exts.insert("file", "abc.txt");
302         exts.insert("quality", "0.7");
303         net::write(ts,
304             make_chunk(buf("The Next Chunk"), std::move(exts)), ec);
305 
306         exts.clear();
307         exts.insert("last");
308         net::write(ts,
309             make_chunk(buf("Last one"), std::move(exts),
310                 std::allocator<double>{}), ec);
311 
312         fields trailers;
313         trailers.set(field::expires, "never");
314         trailers.set(field::content_md5, "f4a5c16584f03d90");
315 
316         net::write(ts,
317             make_chunk_last(
318                 trailers,
319                 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
doExplicitChunkParse()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
run()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 
403 BEAST_DEFINE_TESTSUITE(beast,http,examples);
404 
405 } // http
406 } // beast
407 } // boost
408