Instrument Control Lib
Framework to control Oscilloscopes, SMUs, Function Generators and DC Powersupplies via Ethernet.
HTTPRequest.h
Go to the documentation of this file.
1 //
2 // HTTPRequest
3 //
4 
5 #ifndef HTTPREQUEST_HPP
6 #define HTTPREQUEST_HPP
7 
8 #include <cctype>
9 #include <cstddef>
10 #include <cstdint>
11 #include <cstring>
12 #include <algorithm>
13 #include <array>
14 #include <chrono>
15 #include <functional>
16 #include <map>
17 #include <memory>
18 #include <stdexcept>
19 #include <string>
20 #include <system_error>
21 #include <type_traits>
22 #include <vector>
23 
24 #if defined(_WIN32) || defined(__CYGWIN__)
25 # pragma push_macro("WIN32_LEAN_AND_MEAN")
26 # pragma push_macro("NOMINMAX")
27 # ifndef WIN32_LEAN_AND_MEAN
28 # define WIN32_LEAN_AND_MEAN
29 # endif // WIN32_LEAN_AND_MEAN
30 # ifndef NOMINMAX
31 # define NOMINMAX
32 # endif // NOMINMAX
33 # include <winsock2.h>
34 # if _WIN32_WINNT < _WIN32_WINNT_WINXP
35 extern "C" char *_strdup(const char *strSource);
36 # define strdup _strdup
37 # include <wspiapi.h>
38 # endif // _WIN32_WINNT < _WIN32_WINNT_WINXP
39 # include <ws2tcpip.h>
40 # pragma pop_macro("WIN32_LEAN_AND_MEAN")
41 # pragma pop_macro("NOMINMAX")
42 #else
43 # include <errno.h>
44 # include <fcntl.h>
45 # include <netinet/in.h>
46 # include <netdb.h>
47 # include <sys/select.h>
48 # include <sys/socket.h>
49 # include <sys/types.h>
50 # include <unistd.h>
51 #endif // defined(_WIN32) || defined(__CYGWIN__)
52 
53 namespace http
54 {
55  class RequestError final: public std::logic_error
56  {
57  public:
58  using logic_error::logic_error;
59  };
60 
61  class ResponseError final: public std::runtime_error
62  {
63  public:
64  using runtime_error::runtime_error;
65  };
66 
67  enum class InternetProtocol: std::uint8_t
68  {
69  v4,
70  v6
71  };
72 
73  struct Uri final
74  {
75  std::string scheme;
76  std::string user;
77  std::string password;
78  std::string host;
79  std::string port;
80  std::string path;
81  std::string query;
82  std::string fragment;
83  };
84 
85  struct HttpVersion final
86  {
87  uint16_t major;
88  uint16_t minor;
89  };
90 
91  struct Status final
92  {
93  // RFC 7231, 6. Response Status Codes
94  enum Code: std::uint16_t
95  {
96  Continue = 100,
98  Processing = 102,
99  EarlyHints = 103,
100 
101  Ok = 200,
102  Created = 201,
103  Accepted = 202,
105  NoContent = 204,
108  MultiStatus = 207,
110  ImUsed = 226,
111 
114  Found = 302,
115  SeeOther = 303,
116  NotModified = 304,
117  UseProxy = 305,
120 
121  BadRequest = 400,
124  Forbidden = 403,
125  NotFound = 404,
130  Conflict = 409,
131  Gone = 410,
135  UriTooLong = 414,
141  Locked = 423,
143  TooEarly = 425,
149 
152  BadGateway = 502,
159  NotExtended = 510,
161  };
162 
164  std::uint16_t code;
165  std::string reason;
166  };
167 
168  using HeaderField = std::pair<std::string, std::string>;
169  using HeaderFields = std::vector<HeaderField>;
170 
171  struct Response final
172  {
175  std::vector<std::uint8_t> body;
176  };
177 
178  inline namespace detail
179  {
180 #if defined(_WIN32) || defined(__CYGWIN__)
181  class WinSock final
182  {
183  public:
184  WinSock()
185  {
186  WSADATA wsaData;
187  const auto error = WSAStartup(MAKEWORD(2, 2), &wsaData);
188  if (error != 0)
189  throw std::system_error{error, std::system_category(), "WSAStartup failed"};
190 
191  if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
192  {
193  WSACleanup();
194  throw std::runtime_error{"Invalid WinSock version"};
195  }
196 
197  started = true;
198  }
199 
200  ~WinSock()
201  {
202  if (started) WSACleanup();
203  }
204 
205  WinSock(WinSock&& other) noexcept:
206  started{other.started}
207  {
208  other.started = false;
209  }
210 
211  WinSock& operator=(WinSock&& other) noexcept
212  {
213  if (&other == this) return *this;
214  if (started) WSACleanup();
215  started = other.started;
216  other.started = false;
217  return *this;
218  }
219 
220  private:
221  bool started = false;
222  };
223 #endif // defined(_WIN32) || defined(__CYGWIN__)
224 
225  inline int getLastError() noexcept
226  {
227 #if defined(_WIN32) || defined(__CYGWIN__)
228  return WSAGetLastError();
229 #else
230  return errno;
231 #endif // defined(_WIN32) || defined(__CYGWIN__)
232  }
233 
234  constexpr int getAddressFamily(const InternetProtocol internetProtocol)
235  {
236  return (internetProtocol == InternetProtocol::v4) ? AF_INET :
237  (internetProtocol == InternetProtocol::v6) ? AF_INET6 :
238  throw RequestError{"Unsupported protocol"};
239  }
240 
241  class Socket final
242  {
243  public:
244 #if defined(_WIN32) || defined(__CYGWIN__)
245  using Type = SOCKET;
246  static constexpr Type invalid = INVALID_SOCKET;
247 #else
248  using Type = int;
249  static constexpr Type invalid = -1;
250 #endif // defined(_WIN32) || defined(__CYGWIN__)
251 
252  explicit Socket(const InternetProtocol internetProtocol):
253  endpoint{socket(getAddressFamily(internetProtocol), SOCK_STREAM, IPPROTO_TCP)}
254  {
255  if (endpoint == invalid)
256  throw std::system_error{getLastError(), std::system_category(), "Failed to create socket"};
257 
258 #if defined(_WIN32) || defined(__CYGWIN__)
259  ULONG mode = 1;
260  if (ioctlsocket(endpoint, FIONBIO, &mode) != 0)
261  {
262  close();
263  throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to get socket flags"};
264  }
265 #else
266  const auto flags = fcntl(endpoint, F_GETFL);
267  if (flags == -1)
268  {
269  close();
270  throw std::system_error{errno, std::system_category(), "Failed to get socket flags"};
271  }
272 
273  if (fcntl(endpoint, F_SETFL, flags | O_NONBLOCK) == -1)
274  {
275  close();
276  throw std::system_error{errno, std::system_category(), "Failed to set socket flags"};
277  }
278 #endif // defined(_WIN32) || defined(__CYGWIN__)
279 
280 #ifdef __APPLE__
281  const int value = 1;
282  if (setsockopt(endpoint, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)) == -1)
283  {
284  close();
285  throw std::system_error{errno, std::system_category(), "Failed to set socket option"};
286  }
287 #endif // __APPLE__
288  }
289 
291  {
292  if (endpoint != invalid) close();
293  }
294 
295  Socket(Socket&& other) noexcept:
296  endpoint{other.endpoint}
297  {
298  other.endpoint = invalid;
299  }
300 
301  Socket& operator=(Socket&& other) noexcept
302  {
303  if (&other == this) return *this;
304  if (endpoint != invalid) close();
305  endpoint = other.endpoint;
306  other.endpoint = invalid;
307  return *this;
308  }
309 
310  void connect(const struct sockaddr* address, const socklen_t addressSize, const std::int64_t timeout)
311  {
312 #if defined(_WIN32) || defined(__CYGWIN__)
313  auto result = ::connect(endpoint, address, addressSize);
314  while (result == -1 && WSAGetLastError() == WSAEINTR)
315  result = ::connect(endpoint, address, addressSize);
316 
317  if (result == -1)
318  {
319  if (WSAGetLastError() == WSAEWOULDBLOCK)
320  {
321  select(SelectType::write, timeout);
322 
323  char socketErrorPointer[sizeof(int)];
324  socklen_t optionLength = sizeof(socketErrorPointer);
325  if (getsockopt(endpoint, SOL_SOCKET, SO_ERROR, socketErrorPointer, &optionLength) == -1)
326  throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to get socket option"};
327 
328  int socketError;
329  std::memcpy(&socketError, socketErrorPointer, sizeof(socketErrorPointer));
330 
331  if (socketError != 0)
332  throw std::system_error{socketError, std::system_category(), "Failed to connect"};
333  }
334  else
335  throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to connect"};
336  }
337 #else
338  auto result = ::connect(endpoint, address, addressSize);
339  while (result == -1 && errno == EINTR)
340  result = ::connect(endpoint, address, addressSize);
341 
342  if (result == -1)
343  {
344  if (errno == EINPROGRESS)
345  {
346  select(SelectType::write, timeout);
347 
348  int socketError;
349  socklen_t optionLength = sizeof(socketError);
350  if (getsockopt(endpoint, SOL_SOCKET, SO_ERROR, &socketError, &optionLength) == -1)
351  throw std::system_error{errno, std::system_category(), "Failed to get socket option"};
352 
353  if (socketError != 0)
354  throw std::system_error{socketError, std::system_category(), "Failed to connect"};
355  }
356  else
357  throw std::system_error{errno, std::system_category(), "Failed to connect"};
358  }
359 #endif // defined(_WIN32) || defined(__CYGWIN__)
360  }
361 
362  std::size_t send(const void* buffer, const std::size_t length, const std::int64_t timeout)
363  {
364  select(SelectType::write, timeout);
365 #if defined(_WIN32) || defined(__CYGWIN__)
366  auto result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
367  static_cast<int>(length), 0);
368 
369  while (result == -1 && WSAGetLastError() == WSAEINTR)
370  result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
371  static_cast<int>(length), 0);
372 
373  if (result == -1)
374  throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to send data"};
375 #else
376  auto result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
377  length, noSignal);
378 
379  while (result == -1 && errno == EINTR)
380  result = ::send(endpoint, reinterpret_cast<const char*>(buffer),
381  length, noSignal);
382 
383  if (result == -1)
384  throw std::system_error{errno, std::system_category(), "Failed to send data"};
385 #endif // defined(_WIN32) || defined(__CYGWIN__)
386  return static_cast<std::size_t>(result);
387  }
388 
389  std::size_t recv(void* buffer, const std::size_t length, const std::int64_t timeout)
390  {
391  select(SelectType::read, timeout);
392 #if defined(_WIN32) || defined(__CYGWIN__)
393  auto result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
394  static_cast<int>(length), 0);
395 
396  while (result == -1 && WSAGetLastError() == WSAEINTR)
397  result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
398  static_cast<int>(length), 0);
399 
400  if (result == -1)
401  throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to read data"};
402 #else
403  auto result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
404  length, noSignal);
405 
406  while (result == -1 && errno == EINTR)
407  result = ::recv(endpoint, reinterpret_cast<char*>(buffer),
408  length, noSignal);
409 
410  if (result == -1)
411  throw std::system_error{errno, std::system_category(), "Failed to read data"};
412 #endif // defined(_WIN32) || defined(__CYGWIN__)
413  return static_cast<std::size_t>(result);
414  }
415 
416  private:
417  enum class SelectType
418  {
419  read,
420  write
421  };
422 
423  void select(const SelectType type, const std::int64_t timeout)
424  {
425  fd_set descriptorSet;
426  FD_ZERO(&descriptorSet);
427  FD_SET(endpoint, &descriptorSet);
428 
429 #if defined(_WIN32) || defined(__CYGWIN__)
430  TIMEVAL selectTimeout{
431  static_cast<LONG>(timeout / 1000),
432  static_cast<LONG>((timeout % 1000) * 1000)
433  };
434  auto count = ::select(0,
435  (type == SelectType::read) ? &descriptorSet : nullptr,
436  (type == SelectType::write) ? &descriptorSet : nullptr,
437  nullptr,
438  (timeout >= 0) ? &selectTimeout : nullptr);
439 
440  while (count == -1 && WSAGetLastError() == WSAEINTR)
441  count = ::select(0,
442  (type == SelectType::read) ? &descriptorSet : nullptr,
443  (type == SelectType::write) ? &descriptorSet : nullptr,
444  nullptr,
445  (timeout >= 0) ? &selectTimeout : nullptr);
446 
447  if (count == -1)
448  throw std::system_error{WSAGetLastError(), std::system_category(), "Failed to select socket"};
449  else if (count == 0)
450  throw ResponseError{"Request timed out"};
451 #else
452  timeval selectTimeout{
453  static_cast<time_t>(timeout / 1000),
454  static_cast<suseconds_t>((timeout % 1000) * 1000)
455  };
456  auto count = ::select(endpoint + 1,
457  (type == SelectType::read) ? &descriptorSet : nullptr,
458  (type == SelectType::write) ? &descriptorSet : nullptr,
459  nullptr,
460  (timeout >= 0) ? &selectTimeout : nullptr);
461 
462  while (count == -1 && errno == EINTR)
463  count = ::select(endpoint + 1,
464  (type == SelectType::read) ? &descriptorSet : nullptr,
465  (type == SelectType::write) ? &descriptorSet : nullptr,
466  nullptr,
467  (timeout >= 0) ? &selectTimeout : nullptr);
468 
469  if (count == -1)
470  throw std::system_error{errno, std::system_category(), "Failed to select socket"};
471  else if (count == 0)
472  throw ResponseError{"Request timed out"};
473 #endif // defined(_WIN32) || defined(__CYGWIN__)
474  }
475 
476  void close() noexcept
477  {
478 #if defined(_WIN32) || defined(__CYGWIN__)
479  closesocket(endpoint);
480 #else
481  ::close(endpoint);
482 #endif // defined(_WIN32) || defined(__CYGWIN__)
483  }
484 
485 #if defined(__unix__) && !defined(__APPLE__) && !defined(__CYGWIN__)
486  static constexpr int noSignal = MSG_NOSIGNAL;
487 #else
488  static constexpr int noSignal = 0;
489 #endif // defined(__unix__) && !defined(__APPLE__)
490 
491  Type endpoint = invalid;
492  };
493 
494  // RFC 7230, 3.2.3. WhiteSpace
495  template <typename C>
496  constexpr bool isWhiteSpaceChar(const C c) noexcept
497  {
498  return c == 0x20 || c == 0x09; // space or tab
499  };
500 
501  // RFC 5234, Appendix B.1. Core Rules
502  template <typename C>
503  constexpr bool isDigitChar(const C c) noexcept
504  {
505  return c >= 0x30 && c <= 0x39; // 0 - 9
506  }
507 
508  // RFC 5234, Appendix B.1. Core Rules
509  template <typename C>
510  constexpr bool isAlphaChar(const C c) noexcept
511  {
512  return
513  (c >= 0x61 && c <= 0x7A) || // a - z
514  (c >= 0x41 && c <= 0x5A); // A - Z
515  }
516 
517  // RFC 7230, 3.2.6. Field Value Components
518  template <typename C>
519  constexpr bool isTokenChar(const C c) noexcept
520  {
521  return c == 0x21 || // !
522  c == 0x23 || // #
523  c == 0x24 || // $
524  c == 0x25 || // %
525  c == 0x26 || // &
526  c == 0x27 || // '
527  c == 0x2A || // *
528  c == 0x2B || // +
529  c == 0x2D || // -
530  c == 0x2E || // .
531  c == 0x5E || // ^
532  c == 0x5F || // _
533  c == 0x60 || // `
534  c == 0x7C || // |
535  c == 0x7E || // ~
536  isDigitChar(c) ||
537  isAlphaChar(c);
538  };
539 
540  // RFC 5234, Appendix B.1. Core Rules
541  template <typename C>
542  constexpr bool isVisibleChar(const C c) noexcept
543  {
544  return c >= 0x21 && c <= 0x7E;
545  }
546 
547  // RFC 7230, Appendix B. Collected ABNF
548  template <typename C>
549  constexpr bool isObsoleteTextChar(const C c) noexcept
550  {
551  return static_cast<unsigned char>(c) >= 0x80 &&
552  static_cast<unsigned char>(c) <= 0xFF;
553  }
554 
555  template <class Iterator>
556  Iterator skipWhiteSpaces(const Iterator begin, const Iterator end)
557  {
558  auto i = begin;
559  for (i = begin; i != end; ++i)
560  if (!isWhiteSpaceChar(*i))
561  break;
562 
563  return i;
564  }
565 
566  // RFC 5234, Appendix B.1. Core Rules
567  template <typename T, typename C, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
568  constexpr T digitToUint(const C c)
569  {
570  // DIGIT
571  return (c >= 0x30 && c <= 0x39) ? static_cast<T>(c - 0x30) : // 0 - 9
572  throw ResponseError{"Invalid digit"};
573  }
574 
575  // RFC 5234, Appendix B.1. Core Rules
576  template <typename T, typename C, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
577  constexpr T hexDigitToUint(const C c)
578  {
579  // HEXDIG
580  return (c >= 0x30 && c <= 0x39) ? static_cast<T>(c - 0x30) : // 0 - 9
581  (c >= 0x41 && c <= 0x46) ? static_cast<T>(c - 0x41) + T(10) : // A - Z
582  (c >= 0x61 && c <= 0x66) ? static_cast<T>(c - 0x61) + T(10) : // a - z, some services send lower-case hex digits
583  throw ResponseError{"Invalid hex digit"};
584  }
585 
586  // RFC 3986, 3. Syntax Components
587  template <class Iterator>
588  Uri parseUri(const Iterator begin, const Iterator end)
589  {
590  Uri result;
591 
592  // RFC 3986, 3.1. Scheme
593  auto i = begin;
594  if (i == end || !isAlphaChar(*begin))
595  throw RequestError{"Invalid scheme"};
596 
597  result.scheme.push_back(*i++);
598 
599  for (; i != end && (isAlphaChar(*i) || isDigitChar(*i) || *i == '+' || *i == '-' || *i == '.'); ++i)
600  result.scheme.push_back(*i);
601 
602  if (i == end || *i++ != ':')
603  throw RequestError{"Invalid scheme"};
604  if (i == end || *i++ != '/')
605  throw RequestError{"Invalid scheme"};
606  if (i == end || *i++ != '/')
607  throw RequestError{"Invalid scheme"};
608 
609  // RFC 3986, 3.2. Authority
610  std::string authority = std::string(i, end);
611 
612  // RFC 3986, 3.5. Fragment
613  const auto fragmentPosition = authority.find('#');
614  if (fragmentPosition != std::string::npos)
615  {
616  result.fragment = authority.substr(fragmentPosition + 1);
617  authority.resize(fragmentPosition); // remove the fragment part
618  }
619 
620  // RFC 3986, 3.4. Query
621  const auto queryPosition = authority.find('?');
622  if (queryPosition != std::string::npos)
623  {
624  result.query = authority.substr(queryPosition + 1);
625  authority.resize(queryPosition); // remove the query part
626  }
627 
628  // RFC 3986, 3.3. Path
629  const auto pathPosition = authority.find('/');
630  if (pathPosition != std::string::npos)
631  {
632  // RFC 3986, 3.3. Path
633  result.path = authority.substr(pathPosition);
634  authority.resize(pathPosition);
635  }
636  else
637  result.path = "/";
638 
639  // RFC 3986, 3.2.1. User Information
640  std::string userinfo;
641  const auto hostPosition = authority.find('@');
642  if (hostPosition != std::string::npos)
643  {
644  userinfo = authority.substr(0, hostPosition);
645 
646  const auto passwordPosition = userinfo.find(':');
647  if (passwordPosition != std::string::npos)
648  {
649  result.user = userinfo.substr(0, passwordPosition);
650  result.password = userinfo.substr(passwordPosition + 1);
651  }
652  else
653  result.user = userinfo;
654 
655  result.host = authority.substr(hostPosition + 1);
656  }
657  else
658  result.host = authority;
659 
660  // RFC 3986, 3.2.2. Host
661  const auto portPosition = result.host.find(':');
662  if (portPosition != std::string::npos)
663  {
664  // RFC 3986, 3.2.3. Port
665  result.port = result.host.substr(portPosition + 1);
666  result.host.resize(portPosition);
667  }
668 
669  return result;
670  }
671 
672  // RFC 7230, 2.6. Protocol Versioning
673  template <class Iterator>
674  std::pair<Iterator, HttpVersion> parseHttpVersion(const Iterator begin, const Iterator end)
675  {
676  auto i = begin;
677 
678  if (i == end || *i++ != 'H')
679  throw ResponseError{"Invalid HTTP version"};
680  if (i == end || *i++ != 'T')
681  throw ResponseError{"Invalid HTTP version"};
682  if (i == end || *i++ != 'T')
683  throw ResponseError{"Invalid HTTP version"};
684  if (i == end || *i++ != 'P')
685  throw ResponseError{"Invalid HTTP version"};
686  if (i == end || *i++ != '/')
687  throw ResponseError{"Invalid HTTP version"};
688 
689  if (i == end)
690  throw ResponseError{"Invalid HTTP version"};
691 
692  const auto majorVersion = digitToUint<std::uint16_t>(*i++);
693 
694  if (i == end || *i++ != '.')
695  throw ResponseError{"Invalid HTTP version"};
696 
697  if (i == end)
698  throw ResponseError{"Invalid HTTP version"};
699 
700  const auto minorVersion = digitToUint<std::uint16_t>(*i++);
701 
702  return {i, HttpVersion{majorVersion, minorVersion}};
703  }
704 
705  // RFC 7230, 3.1.2. Status Line
706  template <class Iterator>
707  std::pair<Iterator, std::uint16_t> parseStatusCode(const Iterator begin, const Iterator end)
708  {
709  std::uint16_t result = 0;
710 
711  auto i = begin;
712  while (i != end && isDigitChar(*i))
713  result = static_cast<std::uint16_t>(result * 10U) + digitToUint<std::uint16_t>(*i++);
714 
715  if (std::distance(begin, i) != 3)
716  throw ResponseError{"Invalid status code"};
717 
718  return {i, result};
719  }
720 
721  // RFC 7230, 3.1.2. Status Line
722  template <class Iterator>
723  std::pair<Iterator, std::string> parseReasonPhrase(const Iterator begin, const Iterator end)
724  {
725  std::string result;
726 
727  auto i = begin;
728  for (; i != end && (isWhiteSpaceChar(*i) || isVisibleChar(*i) || isObsoleteTextChar(*i)); ++i)
729  result.push_back(static_cast<char>(*i));
730 
731  return {i, std::move(result)};
732  }
733 
734  // RFC 7230, 3.2.6. Field Value Components
735  template <class Iterator>
736  std::pair<Iterator, std::string> parseToken(const Iterator begin, const Iterator end)
737  {
738  std::string result;
739 
740  auto i = begin;
741  for (; i != end && isTokenChar(*i); ++i)
742  result.push_back(static_cast<char>(*i));
743 
744  if (result.empty())
745  throw ResponseError{"Invalid token"};
746 
747  return {i, std::move(result)};
748  }
749 
750  // RFC 7230, 3.2. Header Fields
751  template <class Iterator>
752  std::pair<Iterator, std::string> parseFieldValue(const Iterator begin, const Iterator end)
753  {
754  std::string result;
755 
756  auto i = begin;
757  for (; i != end && (isWhiteSpaceChar(*i) || isVisibleChar(*i) || isObsoleteTextChar(*i)); ++i)
758  result.push_back(static_cast<char>(*i));
759 
760  // trim white spaces
761  result.erase(std::find_if(result.rbegin(), result.rend(), [](const char c) noexcept {
762  return !isWhiteSpaceChar(c);
763  }).base(), result.end());
764 
765  return {i, std::move(result)};
766  }
767 
768  // RFC 7230, 3.2. Header Fields
769  template <class Iterator>
770  std::pair<Iterator, std::string> parseFieldContent(const Iterator begin, const Iterator end)
771  {
772  std::string result;
773 
774  auto i = begin;
775 
776  for (;;)
777  {
778  const auto fieldValueResult = parseFieldValue(i, end);
779  i = fieldValueResult.first;
780  result += fieldValueResult.second;
781 
782  // Handle obsolete fold as per RFC 7230, 3.2.4. Field Parsing
783  // Obsolete folding is known as linear white space (LWS) in RFC 2616, 2.2 Basic Rules
784  auto obsoleteFoldIterator = i;
785  if (obsoleteFoldIterator == end || *obsoleteFoldIterator++ != '\r')
786  break;
787 
788  if (obsoleteFoldIterator == end || *obsoleteFoldIterator++ != '\n')
789  break;
790 
791  if (obsoleteFoldIterator == end || !isWhiteSpaceChar(*obsoleteFoldIterator++))
792  break;
793 
794  result.push_back(' ');
795  i = obsoleteFoldIterator;
796  }
797 
798  return {i, std::move(result)};
799  }
800 
801  // RFC 7230, 3.2. Header Fields
802  template <class Iterator>
803  std::pair<Iterator, HeaderField> parseHeaderField(const Iterator begin, const Iterator end)
804  {
805  auto tokenResult = parseToken(begin, end);
806  auto i = tokenResult.first;
807  auto fieldName = std::move(tokenResult.second);
808 
809  if (i == end || *i++ != ':')
810  throw ResponseError{"Invalid header"};
811 
812  i = skipWhiteSpaces(i, end);
813 
814  auto valueResult = parseFieldContent(i, end);
815  i = valueResult.first;
816  auto fieldValue = std::move(valueResult.second);
817 
818  if (i == end || *i++ != '\r')
819  throw ResponseError{"Invalid header"};
820 
821  if (i == end || *i++ != '\n')
822  throw ResponseError{"Invalid header"};
823 
824  return {i, {std::move(fieldName), std::move(fieldValue)}};
825  }
826 
827  // RFC 7230, 3.1.2. Status Line
828  template <class Iterator>
829  std::pair<Iterator, Status> parseStatusLine(const Iterator begin, const Iterator end)
830  {
831  const auto httpVersionResult = parseHttpVersion(begin, end);
832  auto i = httpVersionResult.first;
833 
834  if (i == end || *i++ != ' ')
835  throw ResponseError{"Invalid status line"};
836 
837  const auto statusCodeResult = parseStatusCode(i, end);
838  i = statusCodeResult.first;
839 
840  if (i == end || *i++ != ' ')
841  throw ResponseError{"Invalid status line"};
842 
843  auto reasonPhraseResult = parseReasonPhrase(i, end);
844  i = reasonPhraseResult.first;
845 
846  if (i == end || *i++ != '\r')
847  throw ResponseError{"Invalid status line"};
848 
849  if (i == end || *i++ != '\n')
850  throw ResponseError{"Invalid status line"};
851 
852  return {i, Status{
853  httpVersionResult.second,
854  statusCodeResult.second,
855  std::move(reasonPhraseResult.second)
856  }};
857  }
858 
859  // RFC 7230, 4.1. Chunked Transfer Coding
860  template <typename T, class Iterator, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
861  T stringToUint(const Iterator begin, const Iterator end)
862  {
863  T result = 0;
864  for (auto i = begin; i != end; ++i)
865  result = T(10U) * result + digitToUint<T>(*i);
866 
867  return result;
868  }
869 
870  template <typename T, class Iterator, typename std::enable_if<std::is_unsigned<T>::value>::type* = nullptr>
871  T hexStringToUint(const Iterator begin, const Iterator end)
872  {
873  T result = 0;
874  for (auto i = begin; i != end; ++i)
875  result = T(16U) * result + hexDigitToUint<T>(*i);
876 
877  return result;
878  }
879 
880  // RFC 7230, 3.1.1. Request Line
881  inline std::string encodeRequestLine(const std::string& method, const std::string& target)
882  {
883  return method + " " + target + " HTTP/1.1\r\n";
884  }
885 
886  // RFC 7230, 3.2. Header Fields
887  inline std::string encodeHeaderFields(const HeaderFields& headerFields)
888  {
889  std::string result;
890  for (const auto& headerField : headerFields)
891  {
892  if (headerField.first.empty())
893  throw RequestError{"Invalid header field name"};
894 
895  for (const auto c : headerField.first)
896  if (!isTokenChar(c))
897  throw RequestError{"Invalid header field name"};
898 
899  for (const auto c : headerField.second)
900  if (!isWhiteSpaceChar(c) && !isVisibleChar(c) && !isObsoleteTextChar(c))
901  throw RequestError{"Invalid header field value"};
902 
903  result += headerField.first + ": " + headerField.second + "\r\n";
904  }
905 
906  return result;
907  }
908 
909  // RFC 4648, 4. Base 64 Encoding
910  template <class Iterator>
911  std::string encodeBase64(const Iterator begin, const Iterator end)
912  {
913  constexpr std::array<char, 64> chars{
914  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
915  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
916  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
917  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
918  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
919  };
920 
921  std::string result;
922  std::size_t c = 0;
923  std::array<std::uint8_t, 3> charArray;
924 
925  for (auto i = begin; i != end; ++i)
926  {
927  charArray[c++] = static_cast<std::uint8_t>(*i);
928  if (c == 3)
929  {
930  result += chars[static_cast<std::uint8_t>((charArray[0] & 0xFC) >> 2)];
931  result += chars[static_cast<std::uint8_t>(((charArray[0] & 0x03) << 4) + ((charArray[1] & 0xF0) >> 4))];
932  result += chars[static_cast<std::uint8_t>(((charArray[1] & 0x0F) << 2) + ((charArray[2] & 0xC0) >> 6))];
933  result += chars[static_cast<std::uint8_t>(charArray[2] & 0x3f)];
934  c = 0;
935  }
936  }
937 
938  if (c)
939  {
940  result += chars[static_cast<std::uint8_t>((charArray[0] & 0xFC) >> 2)];
941 
942  if (c == 1)
943  result += chars[static_cast<std::uint8_t>((charArray[0] & 0x03) << 4)];
944  else // c == 2
945  {
946  result += chars[static_cast<std::uint8_t>(((charArray[0] & 0x03) << 4) + ((charArray[1] & 0xF0) >> 4))];
947  result += chars[static_cast<std::uint8_t>((charArray[1] & 0x0F) << 2)];
948  }
949 
950  while (++c < 4) result += '='; // padding
951  }
952 
953  return result;
954  }
955 
956  inline std::vector<std::uint8_t> encodeHtml(const Uri& uri,
957  const std::string& method,
958  const std::vector<uint8_t>& body,
959  HeaderFields headerFields)
960  {
961  if (uri.scheme != "http")
962  throw RequestError{"Only HTTP scheme is supported"};
963 
964  // RFC 7230, 5.3. Request Target
965  const std::string requestTarget = uri.path + (uri.query.empty() ? "" : '?' + uri.query);
966 
967  // RFC 7230, 5.4. Host
968  headerFields.push_back({"Host", uri.host});
969 
970  // RFC 7230, 3.3.2. Content-Length
971  headerFields.push_back({"Content-Length", std::to_string(body.size())});
972 
973  // RFC 7617, 2. The 'Basic' Authentication Scheme
974  if (!uri.user.empty() || !uri.password.empty())
975  {
976  std::string userinfo = uri.user + ':' + uri.password;
977  headerFields.push_back({"Authorization", "Basic " + encodeBase64(userinfo.begin(), userinfo.end())});
978  }
979 
980  const auto headerData = encodeRequestLine(method, requestTarget) +
981  encodeHeaderFields(headerFields) +
982  "\r\n";
983 
984  std::vector<uint8_t> result(headerData.begin(), headerData.end());
985  result.insert(result.end(), body.begin(), body.end());
986 
987  return result;
988  }
989  }
990 
991  class Request final
992  {
993  public:
994  explicit Request(const std::string& uriString,
995  const InternetProtocol protocol = InternetProtocol::v4):
996  internetProtocol{protocol},
997  uri{parseUri(uriString.begin(), uriString.end())}
998  {
999  }
1000 
1001  Response send(const std::string& method = "GET",
1002  const std::string& body = "",
1003  const HeaderFields& headerFields = {},
1004  const std::chrono::milliseconds timeout = std::chrono::milliseconds{-1})
1005  {
1006  return send(method,
1007  std::vector<uint8_t>(body.begin(), body.end()),
1008  headerFields,
1009  timeout);
1010  }
1011 
1012  Response send(const std::string& method,
1013  const std::vector<uint8_t>& body,
1014  const HeaderFields& headerFields = {},
1015  const std::chrono::milliseconds timeout = std::chrono::milliseconds{-1})
1016  {
1017  const auto stopTime = std::chrono::steady_clock::now() + timeout;
1018 
1019  if (uri.scheme != "http")
1020  throw RequestError{"Only HTTP scheme is supported"};
1021 
1022  addrinfo hints = {};
1023  hints.ai_family = getAddressFamily(internetProtocol);
1024  hints.ai_socktype = SOCK_STREAM;
1025 
1026  const char* port = uri.port.empty() ? "80" : uri.port.c_str();
1027 
1028  addrinfo* info;
1029  if (getaddrinfo(uri.host.c_str(), port, &hints, &info) != 0)
1030  throw std::system_error{getLastError(), std::system_category(), "Failed to get address info of " + uri.host};
1031 
1032  const std::unique_ptr<addrinfo, decltype(&freeaddrinfo)> addressInfo{info, freeaddrinfo};
1033 
1034  const auto requestData = encodeHtml(uri, method, body, headerFields);
1035 
1036  Socket socket{internetProtocol};
1037 
1038  const auto getRemainingMilliseconds = [](const std::chrono::steady_clock::time_point time) noexcept -> std::int64_t {
1039  const auto now = std::chrono::steady_clock::now();
1040  const auto remainingTime = std::chrono::duration_cast<std::chrono::milliseconds>(time - now);
1041  return (remainingTime.count() > 0) ? remainingTime.count() : 0;
1042  };
1043 
1044  // take the first address from the list
1045  socket.connect(addressInfo->ai_addr, static_cast<socklen_t>(addressInfo->ai_addrlen),
1046  (timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
1047 
1048  auto remaining = requestData.size();
1049  auto sendData = requestData.data();
1050 
1051  // send the request
1052  while (remaining > 0)
1053  {
1054  const auto size = socket.send(sendData, remaining,
1055  (timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
1056  remaining -= size;
1057  sendData += size;
1058  }
1059 
1060  std::array<std::uint8_t, 4096> tempBuffer;
1061  constexpr std::array<std::uint8_t, 2> crlf = {'\r', '\n'};
1062  constexpr std::array<std::uint8_t, 4> headerEnd = {'\r', '\n', '\r', '\n'};
1063  Response response;
1064  std::vector<std::uint8_t> responseData;
1065  bool parsingBody = false;
1066  bool contentLengthReceived = false;
1067  std::size_t contentLength = 0U;
1068  bool chunkedResponse = false;
1069  std::size_t expectedChunkSize = 0U;
1070  bool removeCrlfAfterChunk = false;
1071 
1072  // read the response
1073  for (;;)
1074  {
1075  const auto size = socket.recv(tempBuffer.data(), tempBuffer.size(),
1076  (timeout.count() >= 0) ? getRemainingMilliseconds(stopTime) : -1);
1077  if (size == 0) // disconnected
1078  return response;
1079 
1080  responseData.insert(responseData.end(), tempBuffer.begin(), tempBuffer.begin() + size);
1081 
1082  if (!parsingBody)
1083  {
1084  // RFC 7230, 3. Message Format
1085  // Empty line indicates the end of the header section (RFC 7230, 2.1. Client/Server Messaging)
1086  const auto endIterator = std::search(responseData.cbegin(), responseData.cend(),
1087  headerEnd.cbegin(), headerEnd.cend());
1088  if (endIterator == responseData.cend()) break; // two consecutive CRLFs not found
1089 
1090  const auto headerBeginIterator = responseData.cbegin();
1091  const auto headerEndIterator = endIterator + 2;
1092 
1093  auto statusLineResult = parseStatusLine(headerBeginIterator, headerEndIterator);
1094  auto i = statusLineResult.first;
1095 
1096  response.status = std::move(statusLineResult.second);
1097 
1098  for (;;)
1099  {
1100  auto headerFieldResult = parseHeaderField(i, headerEndIterator);
1101  i = headerFieldResult.first;
1102 
1103  auto fieldName = std::move(headerFieldResult.second.first);
1104  const auto toLower = [](const char c) noexcept {
1105  return (c >= 'A' && c <= 'Z') ? c - ('A' - 'a') : c;
1106  };
1107  std::transform(fieldName.begin(), fieldName.end(), fieldName.begin(), toLower);
1108 
1109  auto fieldValue = std::move(headerFieldResult.second.second);
1110 
1111  if (fieldName == "transfer-encoding")
1112  {
1113  // RFC 7230, 3.3.1. Transfer-Encoding
1114  if (fieldValue == "chunked")
1115  chunkedResponse = true;
1116  else
1117  throw ResponseError{"Unsupported transfer encoding: " + fieldValue};
1118  }
1119  else if (fieldName == "content-length")
1120  {
1121  // RFC 7230, 3.3.2. Content-Length
1122  contentLength = stringToUint<std::size_t>(fieldValue.cbegin(), fieldValue.cend());
1123  contentLengthReceived = true;
1124  response.body.reserve(contentLength);
1125  }
1126 
1127  response.headerFields.push_back({std::move(fieldName), std::move(fieldValue)});
1128 
1129  if (i == headerEndIterator)
1130  break;
1131  }
1132 
1133  responseData.erase(responseData.cbegin(), headerEndIterator + 2);
1134  parsingBody = true;
1135  }
1136 
1137  if (parsingBody)
1138  {
1139  // Content-Length must be ignored if Transfer-Encoding is received (RFC 7230, 3.2. Content-Length)
1140  if (chunkedResponse)
1141  {
1142  // RFC 7230, 4.1. Chunked Transfer Coding
1143  for (;;)
1144  {
1145  if (expectedChunkSize > 0)
1146  {
1147  const auto toWrite = (std::min)(expectedChunkSize, responseData.size());
1148  response.body.insert(response.body.end(), responseData.begin(),
1149  responseData.begin() + static_cast<std::ptrdiff_t>(toWrite));
1150  responseData.erase(responseData.begin(),
1151  responseData.begin() + static_cast<std::ptrdiff_t>(toWrite));
1152  expectedChunkSize -= toWrite;
1153 
1154  if (expectedChunkSize == 0) removeCrlfAfterChunk = true;
1155  if (responseData.empty()) break;
1156  }
1157  else
1158  {
1159  if (removeCrlfAfterChunk)
1160  {
1161  if (responseData.size() < 2) break;
1162 
1163  if (!std::equal(crlf.begin(), crlf.end(), responseData.begin()))
1164  throw ResponseError{"Invalid chunk"};
1165 
1166  removeCrlfAfterChunk = false;
1167  responseData.erase(responseData.begin(), responseData.begin() + 2);
1168  }
1169 
1170  const auto i = std::search(responseData.begin(), responseData.end(),
1171  crlf.begin(), crlf.end());
1172 
1173  if (i == responseData.end()) break;
1174 
1175  expectedChunkSize = detail::hexStringToUint<std::size_t>(responseData.begin(), i);
1176  responseData.erase(responseData.begin(), i + 2);
1177 
1178  if (expectedChunkSize == 0)
1179  return response;
1180  }
1181  }
1182  }
1183  else
1184  {
1185  response.body.insert(response.body.end(), responseData.begin(), responseData.end());
1186  responseData.clear();
1187 
1188  // got the whole content
1189  if (contentLengthReceived && response.body.size() >= contentLength)
1190  return response;
1191  }
1192  }
1193  }
1194 
1195  return response;
1196  }
1197 
1198  private:
1199 #if defined(_WIN32) || defined(__CYGWIN__)
1200  WinSock winSock;
1201 #endif // defined(_WIN32) || defined(__CYGWIN__)
1202  InternetProtocol internetProtocol;
1203  Uri uri;
1204  };
1205 }
1206 
1207 #endif // HTTPREQUEST_HPP
Definition: HTTPRequest.h:56
Definition: HTTPRequest.h:992
Response send(const std::string &method, const std::vector< uint8_t > &body, const HeaderFields &headerFields={}, const std::chrono::milliseconds timeout=std::chrono::milliseconds{-1})
Definition: HTTPRequest.h:1012
Request(const std::string &uriString, const InternetProtocol protocol=InternetProtocol::v4)
Definition: HTTPRequest.h:994
Response send(const std::string &method="GET", const std::string &body="", const HeaderFields &headerFields={}, const std::chrono::milliseconds timeout=std::chrono::milliseconds{-1})
Definition: HTTPRequest.h:1001
Definition: HTTPRequest.h:62
Definition: HTTPRequest.h:242
Socket & operator=(Socket &&other) noexcept
Definition: HTTPRequest.h:301
static constexpr Type invalid
Definition: HTTPRequest.h:249
std::size_t recv(void *buffer, const std::size_t length, const std::int64_t timeout)
Definition: HTTPRequest.h:389
Socket(Socket &&other) noexcept
Definition: HTTPRequest.h:295
Socket(const InternetProtocol internetProtocol)
Definition: HTTPRequest.h:252
std::size_t send(const void *buffer, const std::size_t length, const std::int64_t timeout)
Definition: HTTPRequest.h:362
int Type
Definition: HTTPRequest.h:248
void connect(const struct sockaddr *address, const socklen_t addressSize, const std::int64_t timeout)
Definition: HTTPRequest.h:310
~Socket()
Definition: HTTPRequest.h:290
std::string encodeHeaderFields(const HeaderFields &headerFields)
Definition: HTTPRequest.h:887
std::pair< Iterator, std::string > parseToken(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:736
std::pair< Iterator, std::uint16_t > parseStatusCode(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:707
std::vector< std::uint8_t > encodeHtml(const Uri &uri, const std::string &method, const std::vector< uint8_t > &body, HeaderFields headerFields)
Definition: HTTPRequest.h:956
Uri parseUri(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:588
constexpr bool isWhiteSpaceChar(const C c) noexcept
Definition: HTTPRequest.h:496
Iterator skipWhiteSpaces(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:556
constexpr T hexDigitToUint(const C c)
Definition: HTTPRequest.h:577
constexpr bool isTokenChar(const C c) noexcept
Definition: HTTPRequest.h:519
std::pair< Iterator, std::string > parseFieldValue(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:752
std::pair< Iterator, Status > parseStatusLine(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:829
std::pair< Iterator, std::string > parseReasonPhrase(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:723
T stringToUint(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:861
std::pair< Iterator, HttpVersion > parseHttpVersion(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:674
std::string encodeRequestLine(const std::string &method, const std::string &target)
Definition: HTTPRequest.h:881
T hexStringToUint(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:871
constexpr int getAddressFamily(const InternetProtocol internetProtocol)
Definition: HTTPRequest.h:234
constexpr bool isVisibleChar(const C c) noexcept
Definition: HTTPRequest.h:542
constexpr bool isObsoleteTextChar(const C c) noexcept
Definition: HTTPRequest.h:549
constexpr bool isAlphaChar(const C c) noexcept
Definition: HTTPRequest.h:510
constexpr bool isDigitChar(const C c) noexcept
Definition: HTTPRequest.h:503
constexpr T digitToUint(const C c)
Definition: HTTPRequest.h:568
std::string encodeBase64(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:911
std::pair< Iterator, HeaderField > parseHeaderField(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:803
std::pair< Iterator, std::string > parseFieldContent(const Iterator begin, const Iterator end)
Definition: HTTPRequest.h:770
int getLastError() noexcept
Definition: HTTPRequest.h:225
Definition: HTTPRequest.h:54
std::vector< HeaderField > HeaderFields
Definition: HTTPRequest.h:169
std::pair< std::string, std::string > HeaderField
Definition: HTTPRequest.h:168
InternetProtocol
Definition: HTTPRequest.h:68
Definition: HTTPRequest.h:86
uint16_t minor
Definition: HTTPRequest.h:88
uint16_t major
Definition: HTTPRequest.h:87
Definition: HTTPRequest.h:172
HeaderFields headerFields
Definition: HTTPRequest.h:174
std::vector< std::uint8_t > body
Definition: HTTPRequest.h:175
Status status
Definition: HTTPRequest.h:173
Definition: HTTPRequest.h:92
HttpVersion httpVersion
Definition: HTTPRequest.h:163
std::string reason
Definition: HTTPRequest.h:165
std::uint16_t code
Definition: HTTPRequest.h:164
Code
Definition: HTTPRequest.h:95
@ Forbidden
Definition: HTTPRequest.h:124
@ PartialContent
Definition: HTTPRequest.h:107
@ PayloadTooLarge
Definition: HTTPRequest.h:134
@ MovedPermanently
Definition: HTTPRequest.h:113
@ ImUsed
Definition: HTTPRequest.h:110
@ GatewayTimeout
Definition: HTTPRequest.h:154
@ InsufficientStorage
Definition: HTTPRequest.h:157
@ ProxyAuthenticationRequired
Definition: HTTPRequest.h:128
@ Found
Definition: HTTPRequest.h:114
@ TooEarly
Definition: HTTPRequest.h:143
@ Continue
Definition: HTTPRequest.h:96
@ EarlyHints
Definition: HTTPRequest.h:99
@ NotExtended
Definition: HTTPRequest.h:159
@ PreconditionFailed
Definition: HTTPRequest.h:133
@ RequestHeaderFieldsTooLarge
Definition: HTTPRequest.h:147
@ SeeOther
Definition: HTTPRequest.h:115
@ Locked
Definition: HTTPRequest.h:141
@ UriTooLong
Definition: HTTPRequest.h:135
@ LoopDetected
Definition: HTTPRequest.h:158
@ UnsupportedMediaType
Definition: HTTPRequest.h:136
@ NotFound
Definition: HTTPRequest.h:125
@ NotModified
Definition: HTTPRequest.h:116
@ NotAcceptable
Definition: HTTPRequest.h:127
@ BadRequest
Definition: HTTPRequest.h:121
@ Unauthorized
Definition: HTTPRequest.h:122
@ FailedDependency
Definition: HTTPRequest.h:142
@ ServiceUnavailable
Definition: HTTPRequest.h:153
@ TemporaryRedirect
Definition: HTTPRequest.h:118
@ TooManyRequests
Definition: HTTPRequest.h:146
@ MultipleChoice
Definition: HTTPRequest.h:112
@ AlreadyReported
Definition: HTTPRequest.h:109
@ LengthRequired
Definition: HTTPRequest.h:132
@ SwitchingProtocol
Definition: HTTPRequest.h:97
@ Accepted
Definition: HTTPRequest.h:103
@ PermanentRedirect
Definition: HTTPRequest.h:119
@ InternalServerError
Definition: HTTPRequest.h:150
@ NonAuthoritativeInformation
Definition: HTTPRequest.h:104
@ Processing
Definition: HTTPRequest.h:98
@ NetworkAuthenticationRequired
Definition: HTTPRequest.h:160
@ NoContent
Definition: HTTPRequest.h:105
@ BadGateway
Definition: HTTPRequest.h:152
@ PreconditionRequired
Definition: HTTPRequest.h:145
@ Ok
Definition: HTTPRequest.h:101
@ UpgradeRequired
Definition: HTTPRequest.h:144
@ Created
Definition: HTTPRequest.h:102
@ Conflict
Definition: HTTPRequest.h:130
@ UseProxy
Definition: HTTPRequest.h:117
@ MisdirectedRequest
Definition: HTTPRequest.h:139
@ ResetContent
Definition: HTTPRequest.h:106
@ RangeNotSatisfiable
Definition: HTTPRequest.h:137
@ RequestTimeout
Definition: HTTPRequest.h:129
@ VariantAlsoNegotiates
Definition: HTTPRequest.h:156
@ NotImplemented
Definition: HTTPRequest.h:151
@ UnavailableForLegalReasons
Definition: HTTPRequest.h:148
@ ExpectationFailed
Definition: HTTPRequest.h:138
@ Gone
Definition: HTTPRequest.h:131
@ UnprocessableEntity
Definition: HTTPRequest.h:140
@ HttpVersionNotSupported
Definition: HTTPRequest.h:155
@ MultiStatus
Definition: HTTPRequest.h:108
@ PaymentRequired
Definition: HTTPRequest.h:123
@ MethodNotAllowed
Definition: HTTPRequest.h:126
Definition: HTTPRequest.h:74
std::string scheme
Definition: HTTPRequest.h:75
std::string fragment
Definition: HTTPRequest.h:82
std::string host
Definition: HTTPRequest.h:78
std::string user
Definition: HTTPRequest.h:76
std::string path
Definition: HTTPRequest.h:80
std::string password
Definition: HTTPRequest.h:77
std::string port
Definition: HTTPRequest.h:79
std::string query
Definition: HTTPRequest.h:81