• 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 "nodejs_parser.hpp"
11 
12 #include "test/beast/http/message_fuzz.hpp"
13 
14 #include <boost/beast/http.hpp>
15 #include <boost/beast/core/buffer_traits.hpp>
16 #include <boost/beast/core/buffers_suffix.hpp>
17 #include <boost/beast/core/buffers_to_string.hpp>
18 #include <boost/beast/core/ostream.hpp>
19 #include <boost/beast/core/flat_buffer.hpp>
20 #include <boost/beast/core/multi_buffer.hpp>
21 #include <boost/beast/_experimental/unit_test/suite.hpp>
22 #include <chrono>
23 #include <iostream>
24 #include <vector>
25 
26 namespace boost {
27 namespace beast {
28 namespace http {
29 
30 class parser_test : public beast::unit_test::suite
31 {
32 public:
33     static std::size_t constexpr N = 2000;
34 
35     //using corpus = std::vector<multi_buffer>;
36     using corpus = std::vector<flat_buffer>;
37 
38     corpus creq_;
39     corpus cres_;
40     std::size_t size_ = 0;
41 
42     corpus
build_corpus(std::size_t n,std::true_type)43     build_corpus(std::size_t n, std::true_type)
44     {
45         corpus v;
46         v.resize(n);
47         message_fuzz mg;
48         for(std::size_t i = 0; i < n; ++i)
49         {
50             mg.request(v[i]);
51             size_ += v[i].size();
52             BEAST_EXPECT(v[i].size() > 0);
53         }
54         return v;
55     }
56 
57     corpus
build_corpus(std::size_t n,std::false_type)58     build_corpus(std::size_t n, std::false_type)
59     {
60         corpus v;
61         v.resize(n);
62         message_fuzz mg;
63         for(std::size_t i = 0; i < n; ++i)
64         {
65             mg.response(v[i]);
66             size_ += v[i].size();
67             BEAST_EXPECT(v[i].size() > 0);
68         }
69         return v;
70     }
71 
72     template<class ConstBufferSequence,
73         bool isRequest>
74     static
75     std::size_t
feed(ConstBufferSequence const & buffers,basic_parser<isRequest> & parser,error_code & ec)76     feed(ConstBufferSequence const& buffers,
77         basic_parser<isRequest>& parser,
78             error_code& ec)
79     {
80         beast::buffers_suffix<
81             ConstBufferSequence> cb{buffers};
82         std::size_t used = 0;
83         for(;;)
84         {
85             auto const n =
86                 parser.put(cb, ec);
87             if(ec)
88                 return 0;
89             if(n == 0)
90                 break;
91             cb.consume(n);
92             used += n;
93             if(parser.is_done())
94                 break;
95             if(buffer_bytes(cb) == 0)
96                 break;
97         }
98         return used;
99     }
100 
101     template<class Parser>
102     void
testParser1(std::size_t repeat,corpus const & v)103     testParser1(std::size_t repeat, corpus const& v)
104     {
105         while(repeat--)
106             for(auto const& b : v)
107             {
108                 Parser p;
109                 error_code ec;
110                 p.write(b.data(), ec);
111                 if(! BEAST_EXPECTS(! ec, ec.message()))
112                     log << buffers_to_string(b.data()) << std::endl;
113             }
114     }
115 
116     template<class Parser>
117     void
testParser2(std::size_t repeat,corpus const & v)118     testParser2(std::size_t repeat, corpus const& v)
119     {
120         while(repeat--)
121             for(auto const& b : v)
122             {
123                 Parser p;
124                 p.header_limit((std::numeric_limits<std::uint32_t>::max)());
125                 error_code ec;
126                 feed(b.data(), p, ec);
127                 if(! BEAST_EXPECTS(! ec, ec.message()))
128                     log << buffers_to_string(b.data()) << std::endl;
129             }
130     }
131 
132     template<class Function>
133     void
timedTest(std::size_t repeat,std::string const & name,Function && f)134     timedTest(std::size_t repeat, std::string const& name, Function&& f)
135     {
136         using namespace std::chrono;
137         using clock_type = std::chrono::high_resolution_clock;
138         log << name << std::endl;
139         for(std::size_t trial = 1; trial <= repeat; ++trial)
140         {
141             auto const t0 = clock_type::now();
142             f();
143             auto const elapsed = clock_type::now() - t0;
144             log <<
145                 "Trial " << trial << ": " <<
146                 duration_cast<milliseconds>(elapsed).count() << " ms" << std::endl;
147         }
148     }
149 
150     template<bool isRequest>
151     struct null_parser :
152         basic_parser<isRequest>
153     {
154         void
on_request_implboost::beast::http::parser_test::null_parser155         on_request_impl(
156             verb, string_view, string_view,
157             int, error_code&) override
158         {
159         }
160 
161         void
on_response_implboost::beast::http::parser_test::null_parser162         on_response_impl(
163             int, string_view, int,
164             error_code&) override
165         {
166         }
167 
168         void
on_field_implboost::beast::http::parser_test::null_parser169         on_field_impl(
170             field, string_view, string_view,
171             error_code&) override
172         {
173         }
174 
175         void
on_header_implboost::beast::http::parser_test::null_parser176         on_header_impl(error_code&) override
177         {
178         }
179 
180         void
on_body_init_implboost::beast::http::parser_test::null_parser181         on_body_init_impl(
182             boost::optional<std::uint64_t> const&,
183             error_code&) override
184         {
185         }
186 
187         std::size_t
on_body_implboost::beast::http::parser_test::null_parser188         on_body_impl(
189             string_view,
190             error_code&) override
191         {
192             return 0;
193         }
194 
195         void
on_chunk_header_implboost::beast::http::parser_test::null_parser196         on_chunk_header_impl(
197             std::uint64_t,
198             string_view,
199             error_code&) override
200         {
201         }
202 
203         std::size_t
on_chunk_body_implboost::beast::http::parser_test::null_parser204         on_chunk_body_impl(
205             std::uint64_t,
206             string_view,
207             error_code&) override
208         {
209             return 0;
210         }
211 
212         void
on_finish_implboost::beast::http::parser_test::null_parser213         on_finish_impl(error_code&) override
214         {
215         }
216     };
217 
218     template<bool isRequest, class Body, class Fields>
219     struct bench_parser : basic_parser<isRequest>
220     {
221         using mutable_buffers_type =
222             net::mutable_buffer;
223 
224         void
on_request_implboost::beast::http::parser_test::bench_parser225         on_request_impl(verb, string_view,
226             string_view, int, error_code&) override
227         {
228         }
229 
230         void
on_response_implboost::beast::http::parser_test::bench_parser231         on_response_impl(int,
232             string_view, int, error_code&) override
233         {
234         }
235 
236         void
on_field_implboost::beast::http::parser_test::bench_parser237         on_field_impl(field,
238             string_view, string_view, error_code&) override
239         {
240         }
241 
242         void
on_header_implboost::beast::http::parser_test::bench_parser243         on_header_impl(error_code&) override
244         {
245         }
246 
247         void
on_body_init_implboost::beast::http::parser_test::bench_parser248         on_body_init_impl(
249             boost::optional<std::uint64_t> const&,
250             error_code&) override
251         {
252         }
253 
254         std::size_t
on_body_implboost::beast::http::parser_test::bench_parser255         on_body_impl(
256             string_view s, error_code&) override
257         {
258             return s.size();
259         }
260 
261         void
on_chunk_header_implboost::beast::http::parser_test::bench_parser262         on_chunk_header_impl(std::uint64_t,
263             string_view, error_code&) override
264         {
265         }
266 
267         std::size_t
on_chunk_body_implboost::beast::http::parser_test::bench_parser268         on_chunk_body_impl(std::uint64_t,
269             string_view s, error_code&) override
270         {
271             return s.size();
272         }
273 
274         void
on_finish_implboost::beast::http::parser_test::bench_parser275         on_finish_impl(error_code&) override
276         {
277         }
278     };
279 
280     void
testSpeed()281     testSpeed()
282     {
283         static std::size_t constexpr Trials = 5;
284         static std::size_t constexpr Repeat = 500;
285 
286         creq_ = build_corpus(N/2, std::true_type{});
287         cres_ = build_corpus(N/2, std::false_type{});
288 
289         log << "sizeof(request parser)  == " <<
290             sizeof(null_parser<true>) << '\n';
291 
292         log << "sizeof(response parser) == " <<
293             sizeof(null_parser<false>)<< '\n';
294 
295         testcase << "Parser speed test, " <<
296             ((Repeat * size_ + 512) / 1024) << "KB in " <<
297                 (Repeat * (creq_.size() + cres_.size())) << " messages";
298 
299 #if 0
300         timedTest(Trials, "http::parser",
301             [&]
302             {
303                 testParser2<request_parser<dynamic_body>>(Repeat, creq_);
304                 testParser2<response_parser<dynamic_body>>(Repeat, cres_);
305             });
306 #endif
307 #if 1
308         timedTest(Trials, "http::basic_parser",
309             [&]
310             {
311                 testParser2<bench_parser<
312                     true, dynamic_body, fields> >(
313                         Repeat, creq_);
314                 testParser2<bench_parser<
315                     false, dynamic_body, fields>>(
316                         Repeat, cres_);
317             });
318 #if 1
319         timedTest(Trials, "nodejs_parser",
320             [&]
321             {
322                 testParser1<nodejs_parser<
323                     true, dynamic_body, fields>>(
324                         Repeat, creq_);
325                 testParser1<nodejs_parser<
326                     false, dynamic_body, fields>>(
327                         Repeat, cres_);
328             });
329 #endif
330 #endif
331         pass();
332     }
333 
run()334     void run() override
335     {
336         pass();
337         testSpeed();
338     }
339 };
340 
341 BEAST_DEFINE_TESTSUITE(beast,benchmarks,parser);
342 
343 } // http
344 } // beast
345 } // boost
346 
347