2 // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
8 #ifndef BEAST_HTTP_NODEJS_PARSER_HPP
9 #define BEAST_HTTP_NODEJS_PARSER_HPP
11 #include "nodejs-parser/http_parser.h"
13 #include <beast/http/message.hpp>
14 #include <beast/http/rfc7230.hpp>
15 #include <beast/core/buffer_concepts.hpp>
16 #include <beast/core/error.hpp>
17 #include <boost/asio/buffer.hpp>
18 #include <boost/system/error_code.hpp>
21 #include <type_traits>
29 class nodejs_message_category
30 : public boost::system::error_category
34 name() const noexcept override
36 return "nodejs-http-error";
40 message(int ev) const override
42 return http_errno_description(
43 static_cast<http_errno>(ev));
46 boost::system::error_condition
47 default_error_condition(int ev) const noexcept override
49 return boost::system::error_condition{ev, *this};
54 boost::system::error_condition const& condition
55 ) const noexcept override
57 return condition.value() == ev &&
58 &condition.category() == this;
62 equivalent(boost::system::error_code const& error,
63 int ev) const noexcept override
65 return error.value() == ev &&
66 &error.category() == this;
70 template<class = void>
71 boost::system::error_code
72 make_nodejs_error(int http_errno)
74 static nodejs_message_category const mc{};
75 return boost::system::error_code{http_errno, mc};
80 method_to_string(unsigned method)
82 using namespace beast;
83 switch(static_cast<http_method>(method))
85 case HTTP_DELETE: return "DELETE";
86 case HTTP_GET: return "GET";
87 case HTTP_HEAD: return "HEAD";
88 case HTTP_POST: return "POST";
89 case HTTP_PUT: return "PUT";
92 case HTTP_CONNECT: return "CONNECT";
93 case HTTP_OPTIONS: return "OPTIONS";
94 case HTTP_TRACE: return "TRACE";
97 case HTTP_COPY: return "COPY";
98 case HTTP_LOCK: return "LOCK";
99 case HTTP_MKCOL: return "MKCOL";
100 case HTTP_MOVE: return "MOVE";
101 case HTTP_PROPFIND: return "PROPFIND";
102 case HTTP_PROPPATCH: return "PROPPATCH";
103 case HTTP_SEARCH: return "SEARCH";
104 case HTTP_UNLOCK: return "UNLOCK";
105 case HTTP_BIND: return "BIND";
106 case HTTP_REBIND: return "REBIND";
107 case HTTP_UNBIND: return "UNBIND";
108 case HTTP_ACL: return "ACL";
111 case HTTP_REPORT: return "REPORT";
112 case HTTP_MKACTIVITY: return "MKACTIVITY";
113 case HTTP_CHECKOUT: return "CHECKOUT";
114 case HTTP_MERGE: return "MERGE";
117 case HTTP_MSEARCH: return "MSEARCH";
118 case HTTP_NOTIFY: return "NOTIFY";
119 case HTTP_SUBSCRIBE: return "SUBSCRIBE";
120 case HTTP_UNSUBSCRIBE: return "UNSUBSCRIBE";
123 case HTTP_PATCH: return "PATCH";
124 case HTTP_PURGE: return "PURGE";
127 case HTTP_MKCALENDAR: return "MKCALENDAR";
129 // RFC-2068, section 19.6.1.2
130 case HTTP_LINK: return "LINK";
131 case HTTP_UNLINK: return "UNLINK";
139 template<class Derived>
140 class nodejs_basic_parser
143 boost::system::error_code* ec_;
144 bool complete_ = false;
151 using error_code = boost::system::error_code;
153 nodejs_basic_parser(nodejs_basic_parser&& other);
156 operator=(nodejs_basic_parser&& other);
158 nodejs_basic_parser(nodejs_basic_parser const& other);
160 nodejs_basic_parser& operator=(nodejs_basic_parser const& other);
163 nodejs_basic_parser(bool request) noexcept;
166 complete() const noexcept
172 write(void const* data, std::size_t size)
175 auto const used = write(data, size, ec);
177 throw system_error{ec};
182 write(void const* data, std::size_t size,
185 template<class ConstBufferSequence>
187 write(ConstBufferSequence const& buffers)
190 auto const used = write(buffers, ec);
192 throw system_error{ec};
196 template<class ConstBufferSequence>
198 write(ConstBufferSequence const& buffers,
207 throw system_error{ec};
211 write_eof(error_code& ec);
217 return *static_cast<Derived*>(this);
223 template<class T, class R =
224 decltype(std::declval<T>().on_start(), std::true_type{})>
227 static std::false_type check(...);
228 using type = decltype(check<C>(0));
230 static bool const value = type::value;
234 std::integral_constant<bool, has_on_start_t<C>::value>;
237 call_on_start(std::true_type)
243 call_on_start(std::false_type)
250 template<class T, class R =
251 decltype(std::declval<T>().on_field(
252 std::declval<std::string const&>(),
253 std::declval<std::string const&>()),
257 static std::false_type check(...);
258 using type = decltype(check<C>(0));
260 static bool const value = type::value;
264 std::integral_constant<bool, has_on_field_t<C>::value>;
267 call_on_field(std::string const& field,
268 std::string const& value, std::true_type)
270 impl().on_field(field, value);
274 call_on_field(std::string const&, std::string const&,
280 class has_on_headers_complete_t
282 template<class T, class R =
283 decltype(std::declval<T>().on_headers_complete(
284 std::declval<error_code&>()), std::true_type{})>
287 static std::false_type check(...);
288 using type = decltype(check<C>(0));
290 static bool const value = type::value;
293 using has_on_headers_complete =
294 std::integral_constant<bool, has_on_headers_complete_t<C>::value>;
297 call_on_headers_complete(error_code& ec, std::true_type)
299 impl().on_headers_complete(ec);
303 call_on_headers_complete(error_code&, std::false_type)
308 class has_on_request_t
310 template<class T, class R =
311 decltype(std::declval<T>().on_request(
312 std::declval<unsigned>(), std::declval<std::string>(),
313 std::declval<int>(), std::declval<int>(),
314 std::declval<bool>(), std::declval<bool>()),
318 static std::false_type check(...);
319 using type = decltype(check<C>(0));
321 static bool const value = type::value;
324 using has_on_request =
325 std::integral_constant<bool, has_on_request_t<C>::value>;
328 call_on_request(unsigned method, std::string url,
329 int major, int minor, bool keep_alive, bool upgrade,
333 method, url, major, minor, keep_alive, upgrade);
337 call_on_request(unsigned, std::string, int, int, bool, bool,
343 class has_on_response_t
345 template<class T, class R =
346 decltype(std::declval<T>().on_response(
347 std::declval<int>(), std::declval<std::string>,
348 std::declval<int>(), std::declval<int>(),
349 std::declval<bool>(), std::declval<bool>()),
353 static std::false_type check(...);
355 using type = decltype(check<C>(0));
357 // VFALCO Trait seems broken for http::parser
358 using type = std::true_type;
361 static bool const value = type::value;
364 using has_on_response =
365 std::integral_constant<bool, has_on_response_t<C>::value>;
368 call_on_response(int status, std::string text,
369 int major, int minor, bool keep_alive, bool upgrade,
372 return impl().on_response(
373 status, text, major, minor, keep_alive, upgrade);
377 call_on_response(int, std::string, int, int, bool, bool,
380 // VFALCO Certainly incorrect
387 template<class T, class R =
388 decltype(std::declval<T>().on_body(
389 std::declval<void const*>(), std::declval<std::size_t>(),
390 std::declval<error_code&>()), std::true_type{})>
393 static std::false_type check(...);
394 using type = decltype(check<C>(0));
396 static bool const value = type::value;
400 std::integral_constant<bool, has_on_body_t<C>::value>;
403 call_on_body(void const* data, std::size_t bytes,
404 error_code& ec, std::true_type)
406 impl().on_body(data, bytes, ec);
410 call_on_body(void const*, std::size_t,
411 error_code&, std::false_type)
416 class has_on_complete_t
418 template<class T, class R =
419 decltype(std::declval<T>().on_complete(), std::true_type{})>
422 static std::false_type check(...);
423 using type = decltype(check<C>(0));
425 static bool const value = type::value;
428 using has_on_complete =
429 std::integral_constant<bool, has_on_complete_t<C>::value>;
432 call_on_complete(std::true_type)
434 impl().on_complete();
438 call_on_complete(std::false_type)
445 static int cb_message_start(http_parser*);
446 static int cb_url(http_parser*, char const*, std::size_t);
447 static int cb_status(http_parser*, char const*, std::size_t);
448 static int cb_header_field(http_parser*, char const*, std::size_t);
449 static int cb_header_value(http_parser*, char const*, std::size_t);
450 static int cb_headers_complete(http_parser*);
451 static int cb_body(http_parser*, char const*, std::size_t);
452 static int cb_message_complete(http_parser*);
453 static int cb_chunk_header(http_parser*);
454 static int cb_chunk_complete(http_parser*);
456 struct hooks_t : http_parser_settings
460 http_parser_settings_init(this);
461 on_message_begin = &nodejs_basic_parser::cb_message_start;
462 on_url = &nodejs_basic_parser::cb_url;
463 on_status = &nodejs_basic_parser::cb_status;
464 on_header_field = &nodejs_basic_parser::cb_header_field;
465 on_header_value = &nodejs_basic_parser::cb_header_value;
466 on_headers_complete = &nodejs_basic_parser::cb_headers_complete;
467 on_body = &nodejs_basic_parser::cb_body;
468 on_message_complete = &nodejs_basic_parser::cb_message_complete;
469 on_chunk_header = &nodejs_basic_parser::cb_chunk_header;
470 on_chunk_complete = &nodejs_basic_parser::cb_chunk_complete;
475 http_parser_settings const*
479 template<class Derived>
480 template<class ConstBufferSequence>
482 nodejs_basic_parser<Derived>::write(
483 ConstBufferSequence const& buffers, error_code& ec)
485 static_assert(beast::is_ConstBufferSequence<
486 ConstBufferSequence>::value,
487 "ConstBufferSequence requirements not met");
488 using boost::asio::buffer_cast;
489 using boost::asio::buffer_size;
490 std::size_t bytes_used = 0;
491 for(auto const& buffer : buffers)
493 auto const n = write(
494 buffer_cast<void const*>(buffer),
495 buffer_size(buffer), ec);
505 template<class Derived>
506 http_parser_settings const*
507 nodejs_basic_parser<Derived>::hooks()
509 static hooks_t const h;
513 template<class Derived>
514 nodejs_basic_parser<Derived>::
515 nodejs_basic_parser(nodejs_basic_parser&& other)
517 state_ = other.state_;
519 complete_ = other.complete_;
520 url_ = std::move(other.url_);
521 status_ = std::move(other.status_);
522 field_ = std::move(other.field_);
523 value_ = std::move(other.value_);
526 template<class Derived>
528 nodejs_basic_parser<Derived>::operator=(nodejs_basic_parser&& other) ->
531 state_ = other.state_;
533 complete_ = other.complete_;
534 url_ = std::move(other.url_);
535 status_ = std::move(other.status_);
536 field_ = std::move(other.field_);
537 value_ = std::move(other.value_);
541 template<class Derived>
542 nodejs_basic_parser<Derived>::
543 nodejs_basic_parser(nodejs_basic_parser const& other)
545 state_ = other.state_;
547 complete_ = other.complete_;
549 status_ = other.status_;
550 field_ = other.field_;
551 value_ = other.value_;
554 template<class Derived>
556 nodejs_basic_parser<Derived>::
557 operator=(nodejs_basic_parser const& other) ->
560 state_ = other.state_;
562 complete_ = other.complete_;
564 status_ = other.status_;
565 field_ = other.field_;
566 value_ = other.value_;
570 template<class Derived>
571 nodejs_basic_parser<Derived>::nodejs_basic_parser(bool request) noexcept
574 http_parser_init(&state_, request
575 ? http_parser_type::HTTP_REQUEST
576 : http_parser_type::HTTP_RESPONSE);
579 template<class Derived>
581 nodejs_basic_parser<Derived>::write(void const* data,
582 std::size_t size, error_code& ec)
585 auto const n = http_parser_execute(
587 static_cast<const char*>(data), size);
589 ec = detail::make_nodejs_error(
590 static_cast<int>(state_.http_errno));
596 template<class Derived>
598 nodejs_basic_parser<Derived>::write_eof(error_code& ec)
602 &state_, hooks(), nullptr, 0);
604 ec = detail::make_nodejs_error(
605 static_cast<int>(state_.http_errno));
608 template<class Derived>
610 nodejs_basic_parser<Derived>::check_header()
614 //detail::trim(value_);
615 call_on_field(field_, value_,
616 has_on_field<Derived>{});
622 template<class Derived>
624 nodejs_basic_parser<Derived>::cb_message_start(http_parser* p)
626 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
632 t.call_on_start(has_on_start<Derived>{});
636 template<class Derived>
638 nodejs_basic_parser<Derived>::cb_url(http_parser* p,
639 char const* in, std::size_t bytes)
641 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
642 t.url_.append(in, bytes);
646 template<class Derived>
648 nodejs_basic_parser<Derived>::cb_status(http_parser* p,
649 char const* in, std::size_t bytes)
651 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
652 t.status_.append(in, bytes);
656 template<class Derived>
658 nodejs_basic_parser<Derived>::cb_header_field(http_parser* p,
659 char const* in, std::size_t bytes)
661 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
663 t.field_.append(in, bytes);
667 template<class Derived>
669 nodejs_basic_parser<Derived>::cb_header_value(http_parser* p,
670 char const* in, std::size_t bytes)
672 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
673 t.value_.append(in, bytes);
677 template<class Derived>
679 nodejs_basic_parser<Derived>::cb_headers_complete(http_parser* p)
681 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
683 t.call_on_headers_complete(*t.ec_,
684 has_on_headers_complete<Derived>{});
687 bool const keep_alive =
688 http_should_keep_alive(p) != 0;
689 if(p->type == http_parser_type::HTTP_REQUEST)
691 t.call_on_request(p->method, t.url_,
692 p->http_major, p->http_minor, keep_alive,
693 p->upgrade, has_on_request<Derived>{});
696 return t.call_on_response(p->status_code, t.status_,
697 p->http_major, p->http_minor, keep_alive,
698 p->upgrade, has_on_response<Derived>{}) ? 0 : 1;
701 template<class Derived>
703 nodejs_basic_parser<Derived>::cb_body(http_parser* p,
704 char const* in, std::size_t bytes)
706 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
707 t.call_on_body(in, bytes, *t.ec_, has_on_body<Derived>{});
708 return *t.ec_ ? 1 : 0;
711 template<class Derived>
713 nodejs_basic_parser<Derived>::cb_message_complete(http_parser* p)
715 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
717 t.call_on_complete(has_on_complete<Derived>{});
721 template<class Derived>
723 nodejs_basic_parser<Derived>::cb_chunk_header(http_parser*)
728 template<class Derived>
730 nodejs_basic_parser<Derived>::cb_chunk_complete(http_parser*)
737 The parser may only be used once.
739 template<bool isRequest, class Body, class Fields>
741 : public nodejs_basic_parser<nodejs_parser<isRequest, Body, Fields>>
743 bool started_ = false;
746 nodejs_parser(nodejs_parser&&) = default;
749 : http::nodejs_basic_parser<nodejs_parser>(isRequest)
753 /// Returns `true` if at least one byte has been processed
761 friend class http::nodejs_basic_parser<nodejs_parser>;
770 on_field(std::string const& field, std::string const& value)
775 on_headers_complete(error_code&)
777 // vFALCO TODO Decode the Content-Length and
778 // Transfer-Encoding, see if we can reserve the buffer.
780 // r_.reserve(content_length)
784 on_request(unsigned method, std::string const& url,
785 int major, int minor, bool /*keep_alive*/, bool /*upgrade*/,
792 on_request(unsigned, std::string const&,
793 int, int, bool, bool,
800 on_request(unsigned method, std::string const& url,
801 int major, int minor, bool keep_alive, bool upgrade)
803 return on_request(method, url,
804 major, minor, keep_alive, upgrade,
805 std::integral_constant<
810 on_response(int status, std::string const& reason,
811 int major, int minor, bool keep_alive, bool upgrade,
814 beast::detail::ignore_unused(keep_alive, upgrade);
819 on_response(int, std::string const&, int, int, bool, bool,
826 on_response(int status, std::string const& reason,
827 int major, int minor, bool keep_alive, bool upgrade)
830 status, reason, major, minor, keep_alive, upgrade,
831 std::integral_constant<bool, ! isRequest>{});
835 on_body(void const* data,
836 std::size_t size, error_code& ec)