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  handler_(handler)
104  {
105  http_parser_init(this);
106  }
107 
108  // return false on error
109  /// Parse a buffer into the different sections of an HTTP request.
110  bool feed(const char* buffer, int length)
111  {
112  if (message_complete)
113  return true;
114 
115  const static http_parser_settings settings_{
116  on_message_begin,
117  on_method,
118  on_url,
119  on_header_field,
120  on_header_value,
121  on_headers_complete,
122  on_body,
123  on_message_complete,
124  };
125 
126  int nparsed = http_parser_execute(this, &settings_, buffer, length);
127  if (http_errno != CHPE_OK)
128  {
129  return false;
130  }
131  return nparsed == length;
132  }
133 
134  bool done()
135  {
136  return feed(nullptr, 0);
137  }
138 
139  void clear()
140  {
141  req = crow::request();
142  header_field.clear();
143  header_value.clear();
144  header_building_state = 0;
145  qs_point = 0;
146  message_complete = false;
147  state = CROW_NEW_MESSAGE();
148  }
149 
150  inline void process_url()
151  {
152  handler_->handle_url();
153  }
154 
155  inline void process_header()
156  {
157  handler_->handle_header();
158  }
159 
160  inline void process_message()
161  {
162  handler_->handle();
163  }
164 
165  inline void set_connection_parameters()
166  {
167  req.http_ver_major = http_major;
168  req.http_ver_minor = http_minor;
169 
170  //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"
171 
172  // HTTP1.1 = always send keep_alive, HTTP1.0 = only send if header exists, HTTP?.? = never send
173  req.keep_alive = (http_major == 1 && http_minor == 0) ?
174  ((flags & F_CONNECTION_KEEP_ALIVE) ? true : false) :
175  ((http_major == 1 && http_minor == 1) ? true : false);
176 
177  // HTTP1.1 = only close if close header exists, HTTP1.0 = always close unless keep_alive header exists, HTTP?.?= never close
178  req.close_connection = (http_major == 1 && http_minor == 0) ?
179  ((flags & F_CONNECTION_KEEP_ALIVE) ? false : true) :
180  ((http_major == 1 && http_minor == 1) ? ((flags & F_CONNECTION_CLOSE) ? true : false) : false);
181  req.upgrade = static_cast<bool>(upgrade);
182  }
183 
184  /// The final request that this parser outputs.
185  ///
186  /// 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)
188 
189  private:
190  int header_building_state = 0;
191  bool message_complete = false;
192  std::string header_field;
193  std::string header_value;
194 
195  Handler* handler_; ///< This is currently an HTTP connection object (\ref crow.Connection).
196  };
197 } // namespace crow
198 
199 #undef CROW_NEW_MESSAGE
200 #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
A wrapper for nodejs/http-parser.
Definition: parser.h:18
request req
Definition: parser.h:187
bool feed(const char *buffer, int length)
Parse a buffer into the different sections of an HTTP request.
Definition: parser.h:110
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