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