HTTPClient.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #include "HTTPClient.h"
  2. using namespace bell;
  3. void HTTPClient::Response::connect(const std::string& url) {
  4. urlParser = bell::URLParser::parse(url);
  5. // Open socket of type
  6. this->socketStream.open(urlParser.host, urlParser.port,
  7. urlParser.schema == "https");
  8. }
  9. HTTPClient::Response::~Response() {
  10. if (this->socketStream.isOpen()) {
  11. this->socketStream.close();
  12. }
  13. }
  14. void HTTPClient::Response::rawRequest(const std::string& url,
  15. const std::string& method,
  16. const std::string& content,
  17. Headers& headers) {
  18. urlParser = bell::URLParser::parse(url);
  19. // Prepare a request
  20. const char* reqEnd = "\r\n";
  21. socketStream << method << " " << urlParser.path << " HTTP/1.1" << reqEnd;
  22. socketStream << "Host: " << urlParser.host << ":" << urlParser.port << reqEnd;
  23. socketStream << "Connection: keep-alive" << reqEnd;
  24. socketStream << "Accept: */*" << reqEnd;
  25. // Write content
  26. if (content.size() > 0) {
  27. socketStream << "Content-Length: " << content.size() << reqEnd;
  28. }
  29. // Write headers
  30. for (auto& header : headers) {
  31. socketStream << header.first << ": " << header.second << reqEnd;
  32. }
  33. socketStream << reqEnd;
  34. socketStream.flush();
  35. // Parse response
  36. readResponseHeaders();
  37. }
  38. void HTTPClient::Response::readResponseHeaders() {
  39. char *method, *path;
  40. const char* msgPointer;
  41. size_t msgLen;
  42. int pret, minorVersion, status;
  43. size_t prevbuflen = 0, numHeaders;
  44. this->httpBufferAvailable = 0;
  45. while (1) {
  46. socketStream.getline((char*)httpBuffer.data() + httpBufferAvailable,
  47. httpBuffer.size() - httpBufferAvailable);
  48. prevbuflen = httpBufferAvailable;
  49. httpBufferAvailable += socketStream.gcount();
  50. // Restore delimiters
  51. memcpy(httpBuffer.data() + httpBufferAvailable - 2, "\r\n", 2);
  52. // Parse the request
  53. numHeaders = sizeof(phResponseHeaders) / sizeof(phResponseHeaders[0]);
  54. pret =
  55. phr_parse_response((const char*)httpBuffer.data(), httpBufferAvailable,
  56. &minorVersion, &status, &msgPointer, &msgLen,
  57. phResponseHeaders, &numHeaders, prevbuflen);
  58. if (pret > 0) {
  59. break; /* successfully parsed the request */
  60. } else if (pret == -1)
  61. throw std::runtime_error("Cannot parse http response");
  62. /* request is incomplete, continue the loop */
  63. assert(pret == -2);
  64. if (httpBufferAvailable == httpBuffer.size())
  65. throw std::runtime_error("Response too large");
  66. }
  67. this->responseHeaders = {};
  68. // Headers have benen read
  69. for (int headerIndex = 0; headerIndex < numHeaders; headerIndex++) {
  70. this->responseHeaders.push_back(
  71. ValueHeader{std::string(phResponseHeaders[headerIndex].name,
  72. phResponseHeaders[headerIndex].name_len),
  73. std::string(phResponseHeaders[headerIndex].value,
  74. phResponseHeaders[headerIndex].value_len)});
  75. }
  76. std::string contentLengthValue = std::string(header("content-length"));
  77. if (contentLengthValue.size() > 0) {
  78. this->hasContentSize = true;
  79. this->contentSize = std::stoi(contentLengthValue);
  80. }
  81. }
  82. void HTTPClient::Response::get(const std::string& url, Headers headers) {
  83. std::string method = "GET";
  84. return this->rawRequest(url, method, "", headers);
  85. }
  86. size_t HTTPClient::Response::contentLength() {
  87. return contentSize;
  88. }
  89. std::string_view HTTPClient::Response::header(const std::string& headerName) {
  90. for (auto& header : this->responseHeaders) {
  91. std::string headerValue = header.first;
  92. std::transform(headerValue.begin(), headerValue.end(), headerValue.begin(),
  93. [](unsigned char c) { return std::tolower(c); });
  94. if (headerName == headerValue) {
  95. return header.second;
  96. }
  97. }
  98. return "";
  99. }
  100. size_t HTTPClient::Response::totalLength() {
  101. auto rangeHeader = header("content-range");
  102. if (rangeHeader.find("/") != std::string::npos) {
  103. return std::stoi(
  104. std::string(rangeHeader.substr(rangeHeader.find("/") + 1)));
  105. }
  106. return this->contentLength();
  107. }
  108. void HTTPClient::Response::readRawBody() {
  109. if (contentSize > 0 && rawBody.size() == 0) {
  110. rawBody = std::vector<uint8_t>(contentSize);
  111. socketStream.read((char*)rawBody.data(), contentSize);
  112. }
  113. }
  114. std::string_view HTTPClient::Response::body() {
  115. readRawBody();
  116. return std::string_view((char*)rawBody.data(), rawBody.size());
  117. }
  118. std::vector<uint8_t> HTTPClient::Response::bytes() {
  119. readRawBody();
  120. return rawBody;
  121. }