CppServer  1.0.4.0
C++ Server Library
http_request.cpp
Go to the documentation of this file.
1 
10 
11 #include "string/string_utils.h"
12 #include "utility/countof.h"
13 
14 #include <cassert>
15 
16 namespace CppServer {
17 namespace HTTP {
18 
19 std::tuple<std::string_view, std::string_view> HTTPRequest::header(size_t i) const noexcept
20 {
21  assert((i < _headers.size()) && "Index out of bounds!");
22  if (i >= _headers.size())
23  return std::make_tuple(std::string_view(), std::string_view());
24 
25  auto item = _headers[i];
26 
27  return std::make_tuple(std::string_view(_cache.data() + std::get<0>(item), std::get<1>(item)), std::string_view(_cache.data() + std::get<2>(item), std::get<3>(item)));
28 }
29 
30 std::tuple<std::string_view, std::string_view> HTTPRequest::cookie(size_t i) const noexcept
31 {
32  assert((i < _cookies.size()) && "Index out of bounds!");
33  if (i >= _cookies.size())
34  return std::make_tuple(std::string_view(), std::string_view());
35 
36  auto item = _cookies[i];
37 
38  return std::make_tuple(std::string_view(_cache.data() + std::get<0>(item), std::get<1>(item)), std::string_view(_cache.data() + std::get<2>(item), std::get<3>(item)));
39 }
40 
42 {
43  _error = false;
44  _method_index = 0;
45  _method_size = 0;
46  _url_index = 0;
47  _url_size = 0;
48  _protocol_index = 0;
49  _protocol_size = 0;
50  _headers.clear();
51  _cookies.clear();
52  _body_index = 0;
53  _body_size = 0;
54  _body_length = 0;
55  _body_length_provided = false;
56 
57  _cache.clear();
58  _cache_size = 0;
59  return *this;
60 }
61 
62 HTTPRequest& HTTPRequest::SetBegin(std::string_view method, std::string_view url, std::string_view protocol)
63 {
64  // Clear the HTTP request cache
65  Clear();
66 
67  size_t index = 0;
68 
69  // Append the HTTP request method
70  _cache.append(method);
71  _method_index = index;
72  _method_size = method.size();
73 
74  _cache.append(" ");
75  index = _cache.size();
76 
77  // Append the HTTP request URL
78  _cache.append(url);
79  _url_index = index;
80  _url_size = url.size();
81 
82  _cache.append(" ");
83  index = _cache.size();
84 
85  // Append the HTTP request protocol version
86  _cache.append(protocol);
87  _protocol_index = index;
88  _protocol_size = protocol.size();
89 
90  _cache.append("\r\n");
91  return *this;
92 }
93 
94 HTTPRequest& HTTPRequest::SetHeader(std::string_view key, std::string_view value)
95 {
96  size_t index = _cache.size();
97 
98  // Append the HTTP request header's key
99  _cache.append(key);
100  size_t key_index = index;
101  size_t key_size = key.size();
102 
103  _cache.append(": ");
104  index = _cache.size();
105 
106  // Append the HTTP request header's value
107  _cache.append(value);
108  size_t value_index = index;
109  size_t value_size = value.size();
110 
111  _cache.append("\r\n");
112 
113  // Add the header to the corresponding collection
114  _headers.emplace_back(key_index, key_size, value_index, value_size);
115  return *this;
116 }
117 
118 HTTPRequest& HTTPRequest::SetCookie(std::string_view name, std::string_view value)
119 {
120  size_t index = _cache.size();
121 
122  // Append the HTTP request header's key
123  _cache.append("Cookie");
124  size_t key_index = index;
125  size_t key_size = 6;
126 
127  _cache.append(": ");
128  index = _cache.size();
129 
130  // Append the HTTP request header's value
131  size_t value_index = index;
132 
133  // Append Cookie
134  index = _cache.size();
135  _cache.append(name);
136  size_t name_index = index;
137  size_t name_size = name.size();
138  _cache.append("=");
139  index = _cache.size();
140  _cache.append(value);
141  size_t cookie_index = index;
142  size_t cookie_size = value.size();
143 
144  size_t value_size = _cache.size() - value_index;
145 
146  _cache.append("\r\n");
147 
148  // Add the header to the corresponding collection
149  _headers.emplace_back(key_index, key_size, value_index, value_size);
150  // Add the cookie to the corresponding collection
151  _cookies.emplace_back(name_index, name_size, cookie_index, cookie_size);
152  return *this;
153 }
154 
155 HTTPRequest& HTTPRequest::AddCookie(std::string_view name, std::string_view value)
156 {
157  // Append Cookie
158  _cache.append("; ");
159  size_t index = _cache.size();
160  _cache.append(name);
161  size_t name_index = index;
162  size_t name_size = name.size();
163  _cache.append("=");
164  index = _cache.size();
165  _cache.append(value);
166  size_t cookie_index = index;
167  size_t cookie_size = value.size();
168 
169  // Add the cookie to the corresponding collection
170  _cookies.emplace_back(name_index, name_size, cookie_index, cookie_size);
171  return *this;
172 }
173 
174 HTTPRequest& HTTPRequest::SetBody(std::string_view body)
175 {
176  // Append content length header
177  char buffer[32];
178  SetHeader("Content-Length", FastConvert(body.size(), buffer, CppCommon::countof(buffer)));
179 
180  _cache.append("\r\n");
181 
182  size_t index = _cache.size();
183 
184  // Append the HTTP request body
185  _cache.append(body);
186  _body_index = index;
187  _body_size = body.size();
188  _body_length = body.size();
189  _body_length_provided = true;
190  return *this;
191 }
192 
194 {
195  // Append content length header
196  char buffer[32];
197  SetHeader("Content-Length", FastConvert(length, buffer, CppCommon::countof(buffer)));
198 
199  _cache.append("\r\n");
200 
201  size_t index = _cache.size();
202 
203  // Clear the HTTP request body
204  _body_index = index;
205  _body_size = 0;
206  _body_length = length;
207  _body_length_provided = true;
208  return *this;
209 }
210 
212 {
213  Clear();
214  SetBegin("HEAD", url);
215  SetBody();
216  return *this;
217 }
218 
220 {
221  Clear();
222  SetBegin("GET", url);
223  SetBody();
224  return *this;
225 }
226 
227 HTTPRequest& HTTPRequest::MakePostRequest(std::string_view url, std::string_view content, std::string_view content_type)
228 {
229  Clear();
230  SetBegin("POST", url);
231  if (!content_type.empty())
232  SetHeader("Content-Type", content_type);
233  SetBody(content);
234  return *this;
235 }
236 
237 HTTPRequest& HTTPRequest::MakePutRequest(std::string_view url, std::string_view content, std::string_view content_type)
238 {
239  Clear();
240  SetBegin("PUT", url);
241  if (!content_type.empty())
242  SetHeader("Content-Type", content_type);
243  SetBody(content);
244  return *this;
245 }
246 
248 {
249  Clear();
250  SetBegin("DELETE", url);
251  SetBody();
252  return *this;
253 }
254 
256 {
257  Clear();
258  SetBegin("OPTIONS", url);
259  SetBody();
260  return *this;
261 }
262 
264 {
265  Clear();
266  SetBegin("TRACE", url);
267  SetBody();
268  return *this;
269 }
270 
271 bool HTTPRequest::IsPendingHeader() const
272 {
273  return (!_error && (_body_index == 0));
274 }
275 
276 bool HTTPRequest::IsPendingBody() const
277 {
278  return (!_error && (_body_index > 0) && (_body_size > 0));
279 }
280 
281 bool HTTPRequest::ReceiveHeader(const void* buffer, size_t size)
282 {
283  // Update the request cache
284  _cache.insert(_cache.end(), (const char*)buffer, (const char*)buffer + size);
285 
286  // Try to seek for HTTP header separator
287  for (size_t i = _cache_size; i < _cache.size(); ++i)
288  {
289  // Check for the request cache out of bounds
290  if ((i + 3) >= _cache.size())
291  break;
292 
293  // Check for the header separator
294  if ((_cache[i + 0] == '\r') && (_cache[i + 1] == '\n') && (_cache[i + 2] == '\r') && (_cache[i + 3] == '\n'))
295  {
296  size_t index = 0;
297 
298  // Set the error flag for a while...
299  _error = true;
300 
301  // Parse method
302  _method_index = index;
303  _method_size = 0;
304  while (_cache[index] != ' ')
305  {
306  ++_method_size;
307  ++index;
308  if (index >= _cache.size())
309  return false;
310  }
311  ++index;
312  if (index >= _cache.size())
313  return false;
314 
315  // Parse URL
316  _url_index = index;
317  _url_size = 0;
318  while (_cache[index] != ' ')
319  {
320  ++_url_size;
321  ++index;
322  if (index >= _cache.size())
323  return false;
324  }
325  ++index;
326  if (index >= _cache.size())
327  return false;
328 
329  // Parse protocol version
330  _protocol_index = index;
331  _protocol_size = 0;
332  while (_cache[index] != '\r')
333  {
334  ++_protocol_size;
335  ++index;
336  if (index >= _cache.size())
337  return false;
338  }
339  ++index;
340  if ((index >= _cache.size()) || (_cache[index] != '\n'))
341  return false;
342  ++index;
343  if (index >= _cache.size())
344  return false;
345 
346  // Parse headers
347  while ((index < _cache.size()) && (index < i))
348  {
349  // Parse header name
350  size_t header_name_index = index;
351  size_t header_name_size = 0;
352  while (_cache[index] != ':')
353  {
354  ++header_name_size;
355  ++index;
356  if (index >= i)
357  break;
358  if (index >= _cache.size())
359  return false;
360  }
361  ++index;
362  if (index >= i)
363  break;
364  if (index >= _cache.size())
365  return false;
366 
367  // Skip all prefix space characters
368  while (std::isspace(_cache[index]))
369  {
370  ++index;
371  if (index >= i)
372  break;
373  if (index >= _cache.size())
374  return false;
375  }
376 
377  // Parse header value
378  size_t header_value_index = index;
379  size_t header_value_size = 0;
380  while (_cache[index] != '\r')
381  {
382  ++header_value_size;
383  ++index;
384  if (index >= i)
385  break;
386  if (index >= _cache.size())
387  return false;
388  }
389  ++index;
390  if ((index >= _cache.size()) || (_cache[index] != '\n'))
391  return false;
392  ++index;
393  if (index >= _cache.size())
394  return false;
395 
396  // Validate header name and value (sometimes value can be empty)
397  if (header_name_size == 0)
398  return false;
399 
400  // Add a new header
401  _headers.emplace_back(header_name_index, header_name_size, header_value_index, header_value_size);
402 
403  // Try to find the body content length
404  if (CppCommon::StringUtils::CompareNoCase(std::string_view(_cache.data() + header_name_index, header_name_size), "Content-Length"))
405  {
406  _body_length = 0;
407  for (size_t j = header_value_index; j < (header_value_index + header_value_size); ++j)
408  {
409  if ((_cache[j] < '0') || (_cache[j] > '9'))
410  return false;
411  _body_length *= 10;
412  _body_length += _cache[j] - '0';
413  _body_length_provided = true;
414  }
415  }
416 
417  // Try to find Cookies
418  if (CppCommon::StringUtils::CompareNoCase(std::string_view(_cache.data() + header_name_index, header_name_size), "Cookie"))
419  {
420  bool name = true;
421  bool token = false;
422  size_t current = header_value_index;
423  size_t name_index = index;
424  size_t name_size = 0;
425  size_t cookie_index = index;
426  size_t cookie_size = 0;
427  for (size_t j = header_value_index; j < (header_value_index + header_value_size); ++j)
428  {
429  if (_cache[j] == ' ')
430  {
431  if (token)
432  {
433  if (name)
434  {
435  name_index = current;
436  name_size = j - current;
437  }
438  else
439  {
440  cookie_index = current;
441  cookie_size = j - current;
442  }
443  }
444  token = false;
445  continue;
446  }
447  if (_cache[j] == '=')
448  {
449  if (token)
450  {
451  if (name)
452  {
453  name_index = current;
454  name_size = j - current;
455  }
456  else
457  {
458  cookie_index = current;
459  cookie_size = j - current;
460  }
461  }
462  token = false;
463  name = false;
464  continue;
465  }
466  if (_cache[j] == ';')
467  {
468  if (token)
469  {
470  if (name)
471  {
472  name_index = current;
473  name_size = j - current;
474  }
475  else
476  {
477  cookie_index = current;
478  cookie_size = j - current;
479  }
480 
481  // Validate the cookie
482  if ((name_size > 0) && (cookie_size > 0))
483  {
484  // Add the cookie to the corresponding collection
485  _cookies.emplace_back(name_index, name_size, cookie_index, cookie_size);
486 
487  // Resset the current cookie values
488  name_index = j;
489  name_size = 0;
490  cookie_index = j;
491  cookie_size = 0;
492  }
493  }
494  token = false;
495  name = true;
496  continue;
497  }
498  if (!token)
499  {
500  current = j;
501  token = true;
502  }
503  }
504 
505  // Process the last cookie
506  if (token)
507  {
508  if (name)
509  {
510  name_index = current;
511  name_size = header_value_index + header_value_size - current;
512  }
513  else
514  {
515  cookie_index = current;
516  cookie_size = header_value_index + header_value_size - current;
517  }
518 
519  // Validate the cookie
520  if ((name_size > 0) && (cookie_size > 0))
521  {
522  // Add the cookie to the corresponding collection
523  _cookies.emplace_back(name_index, name_size, cookie_index, cookie_size);
524  }
525  }
526  }
527  }
528 
529  // Reset the error flag
530  _error = false;
531 
532  // Update the body index and size
533  _body_index = i + 4;
534  _body_size = _cache.size() - i - 4;
535 
536  // Update the parsed cache size
537  _cache_size = _cache.size();
538 
539  return true;
540  }
541  }
542 
543  // Update the parsed cache size
544  _cache_size = (_cache.size() >= 3) ? (_cache.size() - 3) : 0;
545 
546  return false;
547 }
548 
549 bool HTTPRequest::ReceiveBody(const void* buffer, size_t size)
550 {
551  // Update HTTP request cache
552  _cache.insert(_cache.end(), (const char*)buffer, (const char*)buffer + size);
553 
554  // Update the parsed cache size
555  _cache_size = _cache.size();
556 
557  // Update body size
558  _body_size += size;
559 
560  // Check if the body length was provided
561  if (_body_length_provided)
562  {
563  // Was the body fully received?
564  if (_body_size >= _body_length)
565  {
566  _body_size = _body_length;
567  return true;
568  }
569  }
570  else
571  {
572  // HEAD/GET/DELETE/OPTIONS/TRACE request might have no body
573  if ((method() == "HEAD") || (method() == "GET") || (method() == "DELETE") || (method() == "OPTIONS") || (method() == "TRACE"))
574  {
575  _body_length = 0;
576  _body_size = 0;
577  return true;
578  }
579 
580  // Check the body content to find the request body end
581  if (_body_size >= 4)
582  {
583  size_t index = _body_index + _body_size - 4;
584 
585  // Was the body fully received?
586  if ((_cache[index + 0] == '\r') && (_cache[index + 1] == '\n') && (_cache[index + 2] == '\r') && (_cache[index + 3] == '\n'))
587  {
588  _body_length = _body_size;
589  return true;
590  }
591  }
592  }
593 
594  // Body was received partially...
595  return false;
596 }
597 
598 std::string_view HTTPRequest::FastConvert(size_t value, char* buffer, size_t size)
599 {
600  size_t index = size;
601  do
602  {
603  buffer[--index] = '0' + (value % 10);
604  value /= 10;
605  }
606  while (value > 0);
607  return std::string_view(buffer + index, size - index);
608 }
609 
610 std::ostream& operator<<(std::ostream& os, const HTTPRequest& request)
611 {
612  os << "Request method: " << request.method() << std::endl;
613  os << "Request URL: " << request.url() << std::endl;
614  os << "Request protocol: " << request.protocol() << std::endl;
615  os << "Request headers: " << request.headers() << std::endl;
616  for (size_t i = 0; i < request.headers(); ++i)
617  {
618  auto header = request.header(i);
619  os << std::get<0>(header) << ": " << std::get<1>(header) << std::endl;
620  }
621  os << "Request body:" << request.body_length() << std::endl;
622  os << request.body() << std::endl;
623  return os;
624 }
625 
626 void HTTPRequest::swap(HTTPRequest& request) noexcept
627 {
628  using std::swap;
629  swap(_error, request._error);
630  swap(_method_index, request._method_index);
631  swap(_method_size, request._method_size);
632  swap(_url_index, request._url_index);
633  swap(_url_size, request._url_size);
634  swap(_protocol_index, request._protocol_index);
635  swap(_protocol_size, request._protocol_size);
636  swap(_headers, request._headers);
637  swap(_cookies, request._cookies);
638  swap(_body_index, request._body_index);
639  swap(_body_size, request._body_size);
640  swap(_body_length, request._body_length);
641  swap(_body_length_provided, request._body_length_provided);
642  swap(_cache, request._cache);
643  swap(_cache_size, request._cache_size);
644 }
645 
646 } // namespace HTTP
647 } // namespace CppServer
size_t body_length() const noexcept
Get the HTTP request body length.
Definition: http_request.h:74
HTTPRequest & SetBody(std::string_view body="")
Set the HTTP request body.
HTTPRequest & MakePutRequest(std::string_view url, std::string_view content, std::string_view content_type="text/plain; charset=UTF-8")
Make PUT request.
void swap(HTTPRequest &request) noexcept
Swap two instances.
HTTPRequest & MakeHeadRequest(std::string_view url)
Make HEAD request.
HTTPRequest & Clear()
Clear the HTTP request cache.
std::tuple< std::string_view, std::string_view > cookie(size_t i) const noexcept
Get the HTTP request cookie by index.
HTTPRequest & MakeDeleteRequest(std::string_view url)
Make DELETE request.
HTTPRequest & MakePostRequest(std::string_view url, std::string_view content, std::string_view content_type="text/plain; charset=UTF-8")
Make POST request.
HTTPRequest & AddCookie(std::string_view name, std::string_view value)
Add the HTTP request cookie.
HTTPRequest & MakeOptionsRequest(std::string_view url)
Make OPTIONS request.
HTTPRequest & SetBodyLength(size_t length)
Set the HTTP request body length.
HTTPRequest & SetBegin(std::string_view method, std::string_view url, std::string_view protocol="HTTP/1.1")
Set the HTTP request begin with a given method, URL and protocol.
HTTPRequest & MakeGetRequest(std::string_view url)
Make GET request.
size_t headers() const noexcept
Get the HTTP request headers count.
Definition: http_request.h:64
HTTPRequest & SetHeader(std::string_view key, std::string_view value)
Set the HTTP request header.
std::string_view method() const noexcept
Get the HTTP request method.
Definition: http_request.h:58
std::tuple< std::string_view, std::string_view > header(size_t i) const noexcept
Get the HTTP request header by index.
HTTPRequest & SetCookie(std::string_view name, std::string_view value)
Set the HTTP request cookie.
HTTPRequest & MakeTraceRequest(std::string_view url)
Make TRACE request.
std::string_view url() const noexcept
Get the HTTP request URL.
Definition: http_request.h:60
std::string_view body() const noexcept
Get the HTTP request body.
Definition: http_request.h:72
std::string_view protocol() const noexcept
Get the HTTP request protocol version.
Definition: http_request.h:62
HTTP request definition.
std::ostream & operator<<(std::ostream &os, const HTTPRequest &request)
C++ Server project definitions.
Definition: asio.h:56