HTTPStream.cpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #include "HTTPStream.h"
  2. #include <memory>
  3. #include <vector>
  4. #include <string>
  5. #include <iostream>
  6. #include <ctype.h>
  7. #include <cstring>
  8. #include <stdlib.h>
  9. #include <sys/types.h>
  10. #include <sys/socket.h>
  11. #include <netdb.h>
  12. #include <netinet/in.h>
  13. #include <unistd.h>
  14. #include <sstream>
  15. #include <fstream>
  16. #include <netinet/tcp.h>
  17. bell::HTTPStream::HTTPStream()
  18. {
  19. }
  20. bell::HTTPStream::~HTTPStream()
  21. {
  22. close();
  23. }
  24. void bell::HTTPStream::close()
  25. {
  26. if (status != StreamStatus::CLOSED)
  27. {
  28. status = StreamStatus::CLOSED;
  29. BELL_LOG(info, "httpStream", "Closing socket");
  30. socket->close();
  31. BELL_LOG(info, "httpStream", "Closed socket");
  32. }
  33. }
  34. void bell::HTTPStream::connectToUrl(std::string url, bool disableSSL)
  35. {
  36. std::string portString;
  37. // check if url contains "https"
  38. if (url.find("https") != std::string::npos && !disableSSL)
  39. {
  40. socket = std::make_unique<bell::TLSSocket>();
  41. portString = "443";
  42. }
  43. else
  44. {
  45. socket = std::make_unique<bell::TCPSocket>();
  46. portString = "80";
  47. }
  48. socket->open(url);
  49. // remove https or http from url
  50. url.erase(0, url.find("://") + 3);
  51. // split by first "/" in url
  52. std::string hostUrl = url.substr(0, url.find('/'));
  53. std::string pathUrl = url.substr(url.find('/'));
  54. // check if hostUrl contains ':'
  55. if (hostUrl.find(':') != std::string::npos)
  56. {
  57. // split by ':'
  58. std::string host = hostUrl.substr(0, hostUrl.find(':'));
  59. portString = hostUrl.substr(hostUrl.find(':') + 1);
  60. hostUrl = host;
  61. }
  62. // Prepare HTTP get header
  63. std::stringstream ss;
  64. ss << "GET " << pathUrl << " HTTP/1.1\r\n"
  65. << "Host: " << hostUrl << ":" << portString << "\r\n"
  66. << "Accept: */*\r\n"
  67. << "\r\n\r\n";
  68. std::string request = ss.str();
  69. // Send the request
  70. if (socket->write((uint8_t*)request.c_str(), request.length()) != (int)request.length())
  71. {
  72. close();
  73. BELL_LOG(error, "http", "Can't send request");
  74. throw std::runtime_error("Resolve failed");
  75. }
  76. status = StreamStatus::READING_HEADERS;
  77. auto buffer = std::vector<uint8_t>(128);
  78. auto currentLine = std::string();
  79. auto statusOkay = false;
  80. auto readingData = false;
  81. // Read data on socket sockFd line after line
  82. int nbytes;
  83. while (status == StreamStatus::READING_HEADERS)
  84. {
  85. nbytes = socket->read(&buffer[0], buffer.size());
  86. if (nbytes < 0)
  87. {
  88. BELL_LOG(error, "http", "Error reading from client");
  89. perror("recv");
  90. exit(EXIT_FAILURE);
  91. }
  92. else if (nbytes == 0)
  93. {
  94. BELL_LOG(error, "http", "Client disconnected");
  95. close();
  96. }
  97. else
  98. {
  99. currentLine += std::string(buffer.data(), buffer.data() + nbytes);
  100. while (currentLine.find("\r\n") != std::string::npos)
  101. {
  102. auto line = currentLine.substr(0, currentLine.find("\r\n"));
  103. currentLine = currentLine.substr(currentLine.find("\r\n") + 2, currentLine.size());
  104. BELL_LOG(info, "http", "Line: %s", line.c_str());
  105. // handle redirects:
  106. if (line.find("Location:") != std::string::npos)
  107. {
  108. auto newUrl = line.substr(10);
  109. BELL_LOG(info, "http", "Redirecting to %s", newUrl.c_str());
  110. close();
  111. return connectToUrl(newUrl);
  112. }
  113. // handle content-length
  114. if (line.find("Content-Length:") != std::string::npos)
  115. {
  116. auto contentLengthStr = line.substr(16);
  117. BELL_LOG(info, "http", "Content size %s", contentLengthStr.c_str());
  118. // convert contentLengthStr to size_t
  119. this->contentLength = std::stoi(contentLengthStr);
  120. hasFixedSize = true;
  121. }
  122. else if (line.find("200 OK") != std::string::npos)
  123. {
  124. statusOkay = true;
  125. }
  126. else if (line.size() == 0 && statusOkay)
  127. {
  128. BELL_LOG(info, "http", "Ready to receive data!");
  129. status = StreamStatus::READING_DATA;
  130. }
  131. }
  132. }
  133. }
  134. }
  135. size_t bell::HTTPStream::read(uint8_t *buf, size_t nbytes)
  136. {
  137. if (status != StreamStatus::READING_DATA)
  138. {
  139. BELL_LOG(error, "http", "Not ready to read data");
  140. exit(0);
  141. return 0;
  142. }
  143. int nread = socket->read(buf, nbytes);
  144. if (nread < 0)
  145. {
  146. BELL_LOG(error, "http", "Error reading from client");
  147. close();
  148. perror("recv");
  149. exit(EXIT_FAILURE);
  150. }
  151. if (this->hasFixedSize)
  152. {
  153. this->currentPos += nread;
  154. }
  155. if (nread < nbytes)
  156. {
  157. return nread + read(buf + nread, nbytes - nread);
  158. }
  159. return nread;
  160. }
  161. size_t bell::HTTPStream::skip(size_t nbytes)
  162. {
  163. return 0;
  164. }