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 #ifndef BOOST_BEAST_HTTP_NODEJS_PARSER_HPP
11 #define BOOST_BEAST_HTTP_NODEJS_PARSER_HPP
12
13 #include "nodejs-parser/http_parser.h"
14
15 #include <boost/beast/http/message.hpp>
16 #include <boost/beast/http/rfc7230.hpp>
17 #include <boost/beast/core/buffers_range.hpp>
18 #include <boost/beast/core/error.hpp>
19 #include <boost/asio/buffer.hpp>
20 #include <boost/system/error_code.hpp>
21 #include <boost/throw_exception.hpp>
22 #include <cstdint>
23 #include <string>
24 #include <type_traits>
25 #include <utility>
26
27 namespace boost {
28 namespace beast {
29 namespace http {
30
31 namespace detail {
32
33 class nodejs_message_category final
34 : public boost::system::error_category
35 {
36 public:
37 const char*
name() const38 name() const noexcept override
39 {
40 return "nodejs-http-error";
41 }
42
43 std::string
message(int ev) const44 message(int ev) const override
45 {
46 return http_errno_description(
47 static_cast<http_errno>(ev));
48 }
49
50 boost::system::error_condition
default_error_condition(int ev) const51 default_error_condition(int ev) const noexcept override
52 {
53 return boost::system::error_condition{ev, *this};
54 }
55
56 bool
equivalent(int ev,boost::system::error_condition const & condition) const57 equivalent(int ev,
58 boost::system::error_condition const& condition
59 ) const noexcept override
60 {
61 return condition.value() == ev &&
62 &condition.category() == this;
63 }
64
65 bool
equivalent(boost::system::error_code const & error,int ev) const66 equivalent(boost::system::error_code const& error,
67 int ev) const noexcept override
68 {
69 return error.value() == ev &&
70 &error.category() == this;
71 }
72 };
73
74 template<class = void>
75 boost::system::error_code
make_nodejs_error(int http_errno)76 make_nodejs_error(int http_errno)
77 {
78 static nodejs_message_category const mc{};
79 return boost::system::error_code{http_errno, mc};
80 }
81
82 inline
83 char const*
method_to_string(unsigned method)84 method_to_string(unsigned method)
85 {
86 using namespace beast;
87 switch(static_cast<http_method>(method))
88 {
89 case HTTP_DELETE: return "DELETE";
90 case HTTP_GET: return "GET";
91 case HTTP_HEAD: return "HEAD";
92 case HTTP_POST: return "POST";
93 case HTTP_PUT: return "PUT";
94
95 // pathological
96 case HTTP_CONNECT: return "CONNECT";
97 case HTTP_OPTIONS: return "OPTIONS";
98 case HTTP_TRACE: return "TRACE";
99
100 // webdav
101 case HTTP_COPY: return "COPY";
102 case HTTP_LOCK: return "LOCK";
103 case HTTP_MKCOL: return "MKCOL";
104 case HTTP_MOVE: return "MOVE";
105 case HTTP_PROPFIND: return "PROPFIND";
106 case HTTP_PROPPATCH: return "PROPPATCH";
107 case HTTP_SEARCH: return "SEARCH";
108 case HTTP_UNLOCK: return "UNLOCK";
109 case HTTP_BIND: return "BIND";
110 case HTTP_REBIND: return "REBIND";
111 case HTTP_UNBIND: return "UNBIND";
112 case HTTP_ACL: return "ACL";
113
114 // subversion
115 case HTTP_REPORT: return "REPORT";
116 case HTTP_MKACTIVITY: return "MKACTIVITY";
117 case HTTP_CHECKOUT: return "CHECKOUT";
118 case HTTP_MERGE: return "MERGE";
119
120 // upnp
121 case HTTP_MSEARCH: return "MSEARCH";
122 case HTTP_NOTIFY: return "NOTIFY";
123 case HTTP_SUBSCRIBE: return "SUBSCRIBE";
124 case HTTP_UNSUBSCRIBE: return "UNSUBSCRIBE";
125
126 // RFC-5789
127 case HTTP_PATCH: return "PATCH";
128 case HTTP_PURGE: return "PURGE";
129
130 // CalDav
131 case HTTP_MKCALENDAR: return "MKCALENDAR";
132
133 // RFC-2068, section 19.6.1.2
134 case HTTP_LINK: return "LINK";
135 case HTTP_UNLINK: return "UNLINK";
136 };
137
138 return "<unknown>";
139 }
140
141 } // detail
142
143 template<class Derived>
144 class nodejs_basic_parser
145 {
146 http_parser state_;
147 boost::system::error_code* ec_;
148 bool complete_ = false;
149 std::string url_;
150 std::string status_;
151 std::string field_;
152 std::string value_;
153
154 public:
155 using error_code = boost::system::error_code;
156
157 nodejs_basic_parser(nodejs_basic_parser&& other);
158
159 nodejs_basic_parser&
160 operator=(nodejs_basic_parser&& other);
161
162 nodejs_basic_parser(nodejs_basic_parser const& other);
163
164 nodejs_basic_parser& operator=(nodejs_basic_parser const& other);
165
166 explicit
167 nodejs_basic_parser(bool request) noexcept;
168
169 bool
complete() const170 complete() const noexcept
171 {
172 return complete_;
173 }
174
175 std::size_t
write(void const * data,std::size_t size)176 write(void const* data, std::size_t size)
177 {
178 error_code ec;
179 auto const used = write(data, size, ec);
180 if(ec)
181 BOOST_THROW_EXCEPTION(system_error{ec});
182 return used;
183 }
184
185 std::size_t
186 write(void const* data, std::size_t size,
187 error_code& ec);
188
189 template<class ConstBufferSequence>
190 std::size_t
write(ConstBufferSequence const & buffers)191 write(ConstBufferSequence const& buffers)
192 {
193 error_code ec;
194 auto const used = write(buffers, ec);
195 if(ec)
196 BOOST_THROW_EXCEPTION(system_error{ec});
197 return used;
198 }
199
200 template<class ConstBufferSequence>
201 std::size_t
202 write(ConstBufferSequence const& buffers,
203 error_code& ec);
204
205 void
write_eof()206 write_eof()
207 {
208 error_code ec;
209 write_eof(ec);
210 if(ec)
211 BOOST_THROW_EXCEPTION(system_error{ec});
212 }
213
214 void
215 write_eof(error_code& ec);
216
217 void
218 check_header();
219
220 private:
221 Derived&
impl()222 impl()
223 {
224 return *static_cast<Derived*>(this);
225 }
226
227 static int cb_message_start(http_parser*);
228 static int cb_url(http_parser*, char const*, std::size_t);
229 static int cb_status(http_parser*, char const*, std::size_t);
230 static int cb_header_field(http_parser*, char const*, std::size_t);
231 static int cb_header_value(http_parser*, char const*, std::size_t);
232 static int cb_headers_complete(http_parser*);
233 static int cb_body(http_parser*, char const*, std::size_t);
234 static int cb_message_complete(http_parser*);
235 static int cb_chunk_header(http_parser*);
236 static int cb_chunk_complete(http_parser*);
237
238 struct hooks_t : http_parser_settings
239 {
hooks_tboost::beast::http::nodejs_basic_parser::hooks_t240 hooks_t()
241 {
242 http_parser_settings_init(this);
243 on_message_begin = &nodejs_basic_parser::cb_message_start;
244 on_url = &nodejs_basic_parser::cb_url;
245 on_status = &nodejs_basic_parser::cb_status;
246 on_header_field = &nodejs_basic_parser::cb_header_field;
247 on_header_value = &nodejs_basic_parser::cb_header_value;
248 on_headers_complete = &nodejs_basic_parser::cb_headers_complete;
249 on_body = &nodejs_basic_parser::cb_body;
250 on_message_complete = &nodejs_basic_parser::cb_message_complete;
251 on_chunk_header = &nodejs_basic_parser::cb_chunk_header;
252 on_chunk_complete = &nodejs_basic_parser::cb_chunk_complete;
253 }
254 };
255
256 static
257 http_parser_settings const*
258 hooks();
259 };
260
261 template<class Derived>
262 template<class ConstBufferSequence>
263 std::size_t
write(ConstBufferSequence const & buffers,error_code & ec)264 nodejs_basic_parser<Derived>::write(
265 ConstBufferSequence const& buffers, error_code& ec)
266 {
267 static_assert(net::is_const_buffer_sequence<
268 ConstBufferSequence>::value,
269 "ConstBufferSequence type requirements not met");
270 std::size_t bytes_used = 0;
271 for(auto buffer : beast::buffers_range_ref(buffers))
272 {
273 auto const n = write(
274 static_cast<void const*>(buffer.data()),
275 buffer.size(), ec);
276 if(ec)
277 return 0;
278 bytes_used += n;
279 if(complete())
280 break;
281 }
282 return bytes_used;
283 }
284
285 template<class Derived>
286 http_parser_settings const*
hooks()287 nodejs_basic_parser<Derived>::hooks()
288 {
289 static hooks_t const h;
290 return &h;
291 }
292
293 template<class Derived>
294 nodejs_basic_parser<Derived>::
nodejs_basic_parser(nodejs_basic_parser && other)295 nodejs_basic_parser(nodejs_basic_parser&& other)
296 {
297 state_ = other.state_;
298 state_.data = this;
299 complete_ = other.complete_;
300 url_ = std::move(other.url_);
301 status_ = std::move(other.status_);
302 field_ = std::move(other.field_);
303 value_ = std::move(other.value_);
304 }
305
306 template<class Derived>
307 auto
308 nodejs_basic_parser<Derived>::
operator =(nodejs_basic_parser && other)309 operator=(nodejs_basic_parser&& other) ->
310 nodejs_basic_parser&
311 {
312 state_ = other.state_;
313 state_.data = this;
314 complete_ = other.complete_;
315 url_ = std::move(other.url_);
316 status_ = std::move(other.status_);
317 field_ = std::move(other.field_);
318 value_ = std::move(other.value_);
319 return *this;
320 }
321
322 template<class Derived>
323 nodejs_basic_parser<Derived>::
nodejs_basic_parser(nodejs_basic_parser const & other)324 nodejs_basic_parser(nodejs_basic_parser const& other)
325 {
326 state_ = other.state_;
327 state_.data = this;
328 complete_ = other.complete_;
329 url_ = other.url_;
330 status_ = other.status_;
331 field_ = other.field_;
332 value_ = other.value_;
333 }
334
335 template<class Derived>
336 auto
337 nodejs_basic_parser<Derived>::
operator =(nodejs_basic_parser const & other)338 operator=(nodejs_basic_parser const& other) ->
339 nodejs_basic_parser&
340 {
341 state_ = other.state_;
342 state_.data = this;
343 complete_ = other.complete_;
344 url_ = other.url_;
345 status_ = other.status_;
346 field_ = other.field_;
347 value_ = other.value_;
348 return *this;
349 }
350
351 template<class Derived>
352 nodejs_basic_parser<Derived>::
nodejs_basic_parser(bool request)353 nodejs_basic_parser(bool request) noexcept
354 {
355 state_.data = this;
356 http_parser_init(&state_, request
357 ? http_parser_type::HTTP_REQUEST
358 : http_parser_type::HTTP_RESPONSE);
359 }
360
361 template<class Derived>
362 std::size_t
363 nodejs_basic_parser<Derived>::
write(void const * data,std::size_t size,error_code & ec)364 write(void const* data,
365 std::size_t size, error_code& ec)
366 {
367 ec_ = &ec;
368 auto const n = http_parser_execute(
369 &state_, hooks(),
370 static_cast<const char*>(data), size);
371 if(! ec)
372 ec = detail::make_nodejs_error(
373 static_cast<int>(state_.http_errno));
374 if(ec)
375 return 0;
376 return n;
377 }
378
379 template<class Derived>
380 void
381 nodejs_basic_parser<Derived>::
write_eof(error_code & ec)382 write_eof(error_code& ec)
383 {
384 ec_ = &ec;
385 http_parser_execute(&state_, hooks(), nullptr, 0);
386 if(! ec)
387 ec = detail::make_nodejs_error(
388 static_cast<int>(state_.http_errno));
389 }
390
391 template<class Derived>
392 void
393 nodejs_basic_parser<Derived>::
check_header()394 check_header()
395 {
396 if(! value_.empty())
397 {
398 impl().on_field(field_, value_);
399 field_.clear();
400 value_.clear();
401 }
402 }
403
404 template<class Derived>
405 int
406 nodejs_basic_parser<Derived>::
cb_message_start(http_parser * p)407 cb_message_start(http_parser* p)
408 {
409 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
410 t.complete_ = false;
411 t.url_.clear();
412 t.status_.clear();
413 t.field_.clear();
414 t.value_.clear();
415 t.impl().on_start();
416 return 0;
417 }
418
419 template<class Derived>
420 int
421 nodejs_basic_parser<Derived>::
cb_url(http_parser * p,char const * in,std::size_t bytes)422 cb_url(http_parser* p,
423 char const* in, std::size_t bytes)
424 {
425 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
426 t.url_.append(in, bytes);
427 return 0;
428 }
429
430 template<class Derived>
431 int
432 nodejs_basic_parser<Derived>::
cb_status(http_parser * p,char const * in,std::size_t bytes)433 cb_status(http_parser* p,
434 char const* in, std::size_t bytes)
435 {
436 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
437 t.status_.append(in, bytes);
438 return 0;
439 }
440
441 template<class Derived>
442 int
443 nodejs_basic_parser<Derived>::
cb_header_field(http_parser * p,char const * in,std::size_t bytes)444 cb_header_field(http_parser* p,
445 char const* in, std::size_t bytes)
446 {
447 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
448 t.check_header();
449 t.field_.append(in, bytes);
450 return 0;
451 }
452
453 template<class Derived>
454 int
455 nodejs_basic_parser<Derived>::
cb_header_value(http_parser * p,char const * in,std::size_t bytes)456 cb_header_value(http_parser* p,
457 char const* in, std::size_t bytes)
458 {
459 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
460 t.value_.append(in, bytes);
461 return 0;
462 }
463
464 template<class Derived>
465 int
466 nodejs_basic_parser<Derived>::
cb_headers_complete(http_parser * p)467 cb_headers_complete(http_parser* p)
468 {
469 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
470 t.check_header();
471 t.impl().on_headers_complete(*t.ec_);
472 if(*t.ec_)
473 return 1;
474 bool const keep_alive =
475 http_should_keep_alive(p) != 0;
476 if(p->type == http_parser_type::HTTP_REQUEST)
477 {
478 t.impl().on_request(p->method, t.url_,
479 p->http_major, p->http_minor, keep_alive,
480 p->upgrade);
481 return 0;
482 }
483 return t.impl().on_response(p->status_code, t.status_,
484 p->http_major, p->http_minor, keep_alive,
485 p->upgrade) ? 0 : 1;
486 }
487
488 template<class Derived>
489 int
490 nodejs_basic_parser<Derived>::
cb_body(http_parser * p,char const * in,std::size_t bytes)491 cb_body(http_parser* p,
492 char const* in, std::size_t bytes)
493 {
494 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
495 t.impl().on_body(in, bytes, *t.ec_);
496 return *t.ec_ ? 1 : 0;
497 }
498
499 template<class Derived>
500 int
501 nodejs_basic_parser<Derived>::
cb_message_complete(http_parser * p)502 cb_message_complete(http_parser* p)
503 {
504 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
505 t.complete_ = true;
506 t.impl().on_complete();
507 return 0;
508 }
509
510 template<class Derived>
511 int
512 nodejs_basic_parser<Derived>::
cb_chunk_header(http_parser *)513 cb_chunk_header(http_parser*)
514 {
515 return 0;
516 }
517
518 template<class Derived>
519 int
520 nodejs_basic_parser<Derived>::
cb_chunk_complete(http_parser *)521 cb_chunk_complete(http_parser*)
522 {
523 return 0;
524 }
525
526 //------------------------------------------------------------------------------
527
528 /** A HTTP parser.
529
530 The parser may only be used once.
531 */
532 template<bool isRequest, class Body, class Fields>
533 class nodejs_parser
534 : public nodejs_basic_parser<nodejs_parser<isRequest, Body, Fields>>
535 {
536 bool started_ = false;
537
538 public:
539 nodejs_parser(nodejs_parser&&) = default;
540
nodejs_parser()541 nodejs_parser()
542 : http::nodejs_basic_parser<nodejs_parser>(isRequest)
543 {
544 }
545
546 /// Returns `true` if at least one byte has been processed
547 bool
started()548 started()
549 {
550 return started_;
551 }
552
553 private:
554 friend class http::nodejs_basic_parser<nodejs_parser>;
555
556 void
on_start()557 on_start()
558 {
559 started_ = true;
560 }
561
562 void
on_field(std::string const &,std::string const &)563 on_field(std::string const&, std::string const&)
564 {
565 }
566
567 void
on_headers_complete(error_code & ec)568 on_headers_complete(error_code& ec)
569 {
570 // vFALCO TODO Decode the Content-Length and
571 // Transfer-Encoding, see if we can reserve the buffer.
572 //
573 // r_.reserve(content_length)
574 ec = {};
575 }
576
577 bool
on_request(unsigned,std::string const &,int,int,bool,bool,std::true_type)578 on_request(unsigned, std::string const&,
579 int, int, bool, bool, std::true_type)
580 {
581 return true;
582 }
583
584 bool
on_request(unsigned,std::string const &,int,int,bool,bool,std::false_type)585 on_request(unsigned, std::string const&,
586 int, int, bool, bool, std::false_type)
587 {
588 return true;
589 }
590
591 bool
on_request(unsigned method,std::string const & url,int major,int minor,bool keep_alive,bool upgrade)592 on_request(unsigned method, std::string const& url,
593 int major, int minor, bool keep_alive, bool upgrade)
594 {
595 return on_request(method, url,
596 major, minor, keep_alive, upgrade,
597 std::integral_constant<
598 bool, isRequest>{});
599 }
600
601 bool
on_response(int,std::string const &,int,int,bool,bool,std::true_type)602 on_response(int, std::string const&,
603 int, int, bool, bool, std::true_type)
604 {
605 return true;
606 }
607
608 bool
on_response(int,std::string const &,int,int,bool,bool,std::false_type)609 on_response(int, std::string const&, int, int, bool, bool,
610 std::false_type)
611 {
612 return true;
613 }
614
615 bool
on_response(int status,std::string const & reason,int major,int minor,bool keep_alive,bool upgrade)616 on_response(int status, std::string const& reason,
617 int major, int minor, bool keep_alive, bool upgrade)
618 {
619 return on_response(
620 status, reason, major, minor, keep_alive, upgrade,
621 std::integral_constant<bool, ! isRequest>{});
622 }
623
624 void
on_body(void const *,std::size_t,error_code & ec)625 on_body(void const*, std::size_t, error_code& ec)
626 {
627 ec = {};
628 }
629
630 void
on_complete()631 on_complete()
632 {
633 }
634 };
635
636 } // http
637 } // beast
638 } // boost
639
640 #endif
641