Crow  1.1
A C++ microframework for the web
parser.h
1 #pragma once
2 
3 #include <string>
4 #include <unordered_map>
5 #include <algorithm>
6 
7 #include "crow/http_request.h"
8 #include "crow/http_parser_merged.h"
9 
10 namespace crow
11 {
12  /// A wrapper for `nodejs/http-parser`.
13 
14  ///
15  /// Used to generate a \ref crow.request from the TCP socket buffer.
16  template<typename Handler>
17  struct HTTPParser : public http_parser
18  {
19  static int on_message_begin(http_parser*)
20  {
21  return 0;
22  }
23  static int on_method(http_parser* self_)
24  {
25  HTTPParser* self = static_cast<HTTPParser*>(self_);
26  self->req.method = static_cast<HTTPMethod>(self->method);
27 
28  return 0;
29  }
30  static int on_url(http_parser* self_, const char* at, size_t length)
31  {
32  HTTPParser* self = static_cast<HTTPParser*>(self_);
33  self->req.raw_url.insert(self->req.raw_url.end(), at, at + length);
34  self->req.url_params = query_string(self->req.raw_url);
35  self->req.url = self->req.raw_url.substr(0, self->qs_point != 0 ? self->qs_point : std::string::npos);
36 
37  self->process_url();
38 
39  return 0;
40  }
41  static int on_header_field(http_parser* self_, const char* at, size_t length)
42  {
43  HTTPParser* self = static_cast<HTTPParser*>(self_);
44  switch (self->header_building_state)
45  {
46  case 0:
47  if (!self->header_value.empty())
48  {
49  self->req.headers.emplace(std::move(self->header_field), std::move(self->header_value));
50  }
51  self->header_field.assign(at, at + length);
52  self->header_building_state = 1;
53  break;
54  case 1:
55  self->header_field.insert(self->header_field.end(), at, at + length);
56  break;
57  }
58  return 0;
59  }
60  static int on_header_value(http_parser* self_, const char* at, size_t length)
61  {
62  HTTPParser* self = static_cast<HTTPParser*>(self_);
63  switch (self->header_building_state)
64  {
65  case 0:
66  self->header_value.insert(self->header_value.end(), at, at + length);
67  break;
68  case 1:
69  self->header_building_state = 0;
70  self->header_value.assign(at, at + length);
71  break;
72  }
73  return 0;
74  }
75  static int on_headers_complete(http_parser* self_)
76  {
77  HTTPParser* self = static_cast<HTTPParser*>(self_);
78  if (!self->header_field.empty())
79  {
80  self->req.headers.emplace(std::move(self->header_field), std::move(self->header_value));
81  }
82 
83  self->set_connection_parameters();
84 
85  self->process_header();
86  return 0;
87  }
88  static int on_body(http_parser* self_, const char* at, size_t length)
89  {
90  HTTPParser* self = static_cast<HTTPParser*>(self_);
91  self->req.body.insert(self->req.body.end(), at, at + length);
92  return 0;
93  }
94  static int on_message_complete(http_parser* self_)
95  {
96  HTTPParser* self = static_cast<HTTPParser*>(self_);
97 
98  self->message_complete = true;
99  self->process_message();
100  return 0;
101  }
102  HTTPParser(Handler* handler):
103  http_parser(),
104  handler_(handler)
105  {
106  http_parser_init(this);
107  }
108 
109  // return false on error
110  /// Parse a buffer into the different sections of an HTTP request.
111  bool feed(const char* buffer, int length)
112  {
113  if (message_complete)
114  return true;
115 
116  const static http_parser_settings settings_{
117  on_message_begin,
118  on_method,
119  on_url,
120  on_header_field,
121  on_header_value,
122  on_headers_complete,
123  on_body,
124  on_message_complete,
125  };
126 
127  int nparsed = http_parser_execute(this, &settings_, buffer, length);
128  if (http_errno != CHPE_OK)
129  {
130  return false;
131  }
132  return nparsed == length;
133  }
134 
135  bool done()
136  {
137  return feed(nullptr, 0);
138  }
139 
140  void clear()
141  {
142  req = crow::request();
143  header_field.clear();
144  header_value.clear();
145  header_building_state = 0;
146  qs_point = 0;
147  message_complete = false;
148  state = CROW_NEW_MESSAGE();
149  }
150 
151  inline void process_url()
152  {
153  handler_->handle_url();
154  }
155 
156  inline void process_header()
157  {
158  handler_->handle_header();
159  }
160 
161  inline void process_message()
162  {
163  handler_->handle();
164  }
165 
166  inline void set_connection_parameters()
167  {
168  req.http_ver_major = http_major;
169  req.http_ver_minor = http_minor;
170 
171  //NOTE(EDev): it seems that the problem is with crow's policy on closing the connection for HTTP_VERSION < 1.0, the behaviour for that in crow is "don't close the connection, but don't send a keep-alive either"
172 
173  // HTTP1.1 = always send keep_alive, HTTP1.0 = only send if header exists, HTTP?.? = never send
174  req.keep_alive = (http_major == 1 && http_minor == 0) ?
175  ((flags & F_CONNECTION_KEEP_ALIVE) ? true : false) :
176  ((http_major == 1 && http_minor == 1) ? true : false);
177 
178  // HTTP1.1 = only close if close header exists, HTTP1.0 = always close unless keep_alive header exists, HTTP?.?= never close
179  req.close_connection = (http_major == 1 && http_minor == 0) ?
180  ((flags & F_CONNECTION_KEEP_ALIVE) ? false : true) :
181  ((http_major == 1 && http_minor == 1) ? ((flags & F_CONNECTION_CLOSE) ? true : false) : false);
182  req.upgrade = static_cast<bool>(upgrade);
183  }
184 
185  /// The final request that this parser outputs.
186  ///
187  /// Data parsed is put directly into this object as soon as the related callback returns. (e.g. the request will have the cooorect method as soon as on_method() returns)
189 
190  private:
191  int header_building_state = 0;
192  bool message_complete = false;
193  std::string header_field;
194  std::string header_value;
195 
196  Handler* handler_; ///< This is currently an HTTP connection object (\ref crow.Connection).
197  };
198 } // namespace crow
199 
200 #undef CROW_NEW_MESSAGE
201 #undef CROW_start_state
A class to represent any data coming after the ? in the request URL into key-value pairs.
Definition: query_string.h:294
The main namespace of the library. In this namespace is defined the most important classes and functi...
A wrapper for nodejs/http-parser.
Definition: parser.h:18
request req
Definition: parser.h:188
bool feed(const char *buffer, int length)
Parse a buffer into the different sections of an HTTP request.
Definition: parser.h:111
Definition: http_parser_merged.h:181
Definition: http_parser_merged.h:148
unsigned char http_major
Definition: http_parser_merged.h:163
unsigned int flags
Definition: http_parser_merged.h:150
An HTTP request.
Definition: http_request.h:36
bool close_connection
Whether or not the server should shut down the TCP connection once a response is sent.
Definition: http_request.h:46
bool keep_alive
Whether or not the server should send a connection: Keep-Alive header to the client.
Definition: http_request.h:45
std::string raw_url
The full URL containing the ? and URL parameters.
Definition: http_request.h:38
bool upgrade
Whether or noth the server should change the HTTP connection to a different connection.
Definition: http_request.h:47