2 // Copyright (c) 2016-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)
7 // Official repository: https://github.com/boostorg/beast
10 #ifndef BOOST_BEAST_HTTP_NODEJS_PARSER_HPP
11 #define BOOST_BEAST_HTTP_NODEJS_PARSER_HPP
13 #include "nodejs-parser/http_parser.h"
15 #include <boost/beast/http/message.hpp>
16 #include <boost/beast/http/rfc7230.hpp>
17 #include <boost/beast/core/error.hpp>
18 #include <boost/beast/core/type_traits.hpp>
19 #include <boost/asio/buffer.hpp>
20 #include <boost/system/error_code.hpp>
21 #include <boost/throw_exception.hpp>
24 #include <type_traits>
33 class nodejs_message_category
34 : public boost::system::error_category
38 name() const noexcept override
40 return "nodejs-http-error";
44 message(int ev) const override
46 return http_errno_description(
47 static_cast<http_errno>(ev));
50 boost::system::error_condition
51 default_error_condition(int ev) const noexcept override
53 return boost::system::error_condition{ev, *this};
58 boost::system::error_condition const& condition
59 ) const noexcept override
61 return condition.value() == ev &&
62 &condition.category() == this;
66 equivalent(boost::system::error_code const& error,
67 int ev) const noexcept override
69 return error.value() == ev &&
70 &error.category() == this;
74 template<class = void>
75 boost::system::error_code
76 make_nodejs_error(int http_errno)
78 static nodejs_message_category const mc{};
79 return boost::system::error_code{http_errno, mc};
84 method_to_string(unsigned method)
86 using namespace beast;
87 switch(static_cast<http_method>(method))
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";
96 case HTTP_CONNECT: return "CONNECT";
97 case HTTP_OPTIONS: return "OPTIONS";
98 case HTTP_TRACE: return "TRACE";
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";
115 case HTTP_REPORT: return "REPORT";
116 case HTTP_MKACTIVITY: return "MKACTIVITY";
117 case HTTP_CHECKOUT: return "CHECKOUT";
118 case HTTP_MERGE: return "MERGE";
121 case HTTP_MSEARCH: return "MSEARCH";
122 case HTTP_NOTIFY: return "NOTIFY";
123 case HTTP_SUBSCRIBE: return "SUBSCRIBE";
124 case HTTP_UNSUBSCRIBE: return "UNSUBSCRIBE";
127 case HTTP_PATCH: return "PATCH";
128 case HTTP_PURGE: return "PURGE";
131 case HTTP_MKCALENDAR: return "MKCALENDAR";
133 // RFC-2068, section 19.6.1.2
134 case HTTP_LINK: return "LINK";
135 case HTTP_UNLINK: return "UNLINK";
143 template<class Derived>
144 class nodejs_basic_parser
147 boost::system::error_code* ec_;
148 bool complete_ = false;
155 using error_code = boost::system::error_code;
157 nodejs_basic_parser(nodejs_basic_parser&& other);
160 operator=(nodejs_basic_parser&& other);
162 nodejs_basic_parser(nodejs_basic_parser const& other);
164 nodejs_basic_parser& operator=(nodejs_basic_parser const& other);
167 nodejs_basic_parser(bool request) noexcept;
170 complete() const noexcept
176 write(void const* data, std::size_t size)
179 auto const used = write(data, size, ec);
181 BOOST_THROW_EXCEPTION(system_error{ec});
186 write(void const* data, std::size_t size,
189 template<class ConstBufferSequence>
191 write(ConstBufferSequence const& buffers)
194 auto const used = write(buffers, ec);
196 BOOST_THROW_EXCEPTION(system_error{ec});
200 template<class ConstBufferSequence>
202 write(ConstBufferSequence const& buffers,
211 BOOST_THROW_EXCEPTION(system_error{ec});
215 write_eof(error_code& ec);
224 return *static_cast<Derived*>(this);
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*);
238 struct hooks_t : http_parser_settings
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;
257 http_parser_settings const*
261 template<class Derived>
262 template<class ConstBufferSequence>
264 nodejs_basic_parser<Derived>::write(
265 ConstBufferSequence const& buffers, error_code& ec)
267 static_assert(boost::asio::is_const_buffer_sequence<
268 ConstBufferSequence>::value,
269 "ConstBufferSequence requirements not met");
270 std::size_t bytes_used = 0;
271 for(auto buffer : beast::detail::buffers_range(buffers))
273 auto const n = write(
274 reinterpret_cast<void const*>(buffer.data()),
285 template<class Derived>
286 http_parser_settings const*
287 nodejs_basic_parser<Derived>::hooks()
289 static hooks_t const h;
293 template<class Derived>
294 nodejs_basic_parser<Derived>::
295 nodejs_basic_parser(nodejs_basic_parser&& other)
297 state_ = other.state_;
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_);
306 template<class Derived>
308 nodejs_basic_parser<Derived>::
309 operator=(nodejs_basic_parser&& other) ->
312 state_ = other.state_;
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_);
322 template<class Derived>
323 nodejs_basic_parser<Derived>::
324 nodejs_basic_parser(nodejs_basic_parser const& other)
326 state_ = other.state_;
328 complete_ = other.complete_;
330 status_ = other.status_;
331 field_ = other.field_;
332 value_ = other.value_;
335 template<class Derived>
337 nodejs_basic_parser<Derived>::
338 operator=(nodejs_basic_parser const& other) ->
341 state_ = other.state_;
343 complete_ = other.complete_;
345 status_ = other.status_;
346 field_ = other.field_;
347 value_ = other.value_;
351 template<class Derived>
352 nodejs_basic_parser<Derived>::
353 nodejs_basic_parser(bool request) noexcept
356 http_parser_init(&state_, request
357 ? http_parser_type::HTTP_REQUEST
358 : http_parser_type::HTTP_RESPONSE);
361 template<class Derived>
363 nodejs_basic_parser<Derived>::
364 write(void const* data,
365 std::size_t size, error_code& ec)
368 auto const n = http_parser_execute(
370 static_cast<const char*>(data), size);
372 ec = detail::make_nodejs_error(
373 static_cast<int>(state_.http_errno));
379 template<class Derived>
381 nodejs_basic_parser<Derived>::
382 write_eof(error_code& ec)
385 http_parser_execute(&state_, hooks(), nullptr, 0);
387 ec = detail::make_nodejs_error(
388 static_cast<int>(state_.http_errno));
391 template<class Derived>
393 nodejs_basic_parser<Derived>::
398 impl().on_field(field_, value_);
404 template<class Derived>
406 nodejs_basic_parser<Derived>::
407 cb_message_start(http_parser* p)
409 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
419 template<class Derived>
421 nodejs_basic_parser<Derived>::
422 cb_url(http_parser* p,
423 char const* in, std::size_t bytes)
425 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
426 t.url_.append(in, bytes);
430 template<class Derived>
432 nodejs_basic_parser<Derived>::
433 cb_status(http_parser* p,
434 char const* in, std::size_t bytes)
436 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
437 t.status_.append(in, bytes);
441 template<class Derived>
443 nodejs_basic_parser<Derived>::
444 cb_header_field(http_parser* p,
445 char const* in, std::size_t bytes)
447 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
449 t.field_.append(in, bytes);
453 template<class Derived>
455 nodejs_basic_parser<Derived>::
456 cb_header_value(http_parser* p,
457 char const* in, std::size_t bytes)
459 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
460 t.value_.append(in, bytes);
464 template<class Derived>
466 nodejs_basic_parser<Derived>::
467 cb_headers_complete(http_parser* p)
469 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
471 t.impl().on_headers_complete(*t.ec_);
474 bool const keep_alive =
475 http_should_keep_alive(p) != 0;
476 if(p->type == http_parser_type::HTTP_REQUEST)
478 t.impl().on_request(p->method, t.url_,
479 p->http_major, p->http_minor, keep_alive,
483 return t.impl().on_response(p->status_code, t.status_,
484 p->http_major, p->http_minor, keep_alive,
488 template<class Derived>
490 nodejs_basic_parser<Derived>::
491 cb_body(http_parser* p,
492 char const* in, std::size_t bytes)
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;
499 template<class Derived>
501 nodejs_basic_parser<Derived>::
502 cb_message_complete(http_parser* p)
504 auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
506 t.impl().on_complete();
510 template<class Derived>
512 nodejs_basic_parser<Derived>::
513 cb_chunk_header(http_parser*)
518 template<class Derived>
520 nodejs_basic_parser<Derived>::
521 cb_chunk_complete(http_parser*)
526 //------------------------------------------------------------------------------
530 The parser may only be used once.
532 template<bool isRequest, class Body, class Fields>
534 : public nodejs_basic_parser<nodejs_parser<isRequest, Body, Fields>>
536 bool started_ = false;
539 nodejs_parser(nodejs_parser&&) = default;
542 : http::nodejs_basic_parser<nodejs_parser>(isRequest)
546 /// Returns `true` if at least one byte has been processed
554 friend class http::nodejs_basic_parser<nodejs_parser>;
563 on_field(std::string const&, std::string const&)
568 on_headers_complete(error_code& ec)
570 // vFALCO TODO Decode the Content-Length and
571 // Transfer-Encoding, see if we can reserve the buffer.
573 // r_.reserve(content_length)
574 ec.assign(0, ec.category());
578 on_request(unsigned, std::string const&,
579 int, int, bool, bool, std::true_type)
585 on_request(unsigned, std::string const&,
586 int, int, bool, bool, std::false_type)
592 on_request(unsigned method, std::string const& url,
593 int major, int minor, bool keep_alive, bool upgrade)
595 return on_request(method, url,
596 major, minor, keep_alive, upgrade,
597 std::integral_constant<
602 on_response(int, std::string const&,
603 int, int, bool, bool, std::true_type)
609 on_response(int, std::string const&, int, int, bool, bool,
616 on_response(int status, std::string const& reason,
617 int major, int minor, bool keep_alive, bool upgrade)
620 status, reason, major, minor, keep_alive, upgrade,
621 std::integral_constant<bool, ! isRequest>{});
625 on_body(void const*, std::size_t, error_code& ec)
627 ec.assign(0, ec.category());