• 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 #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