123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- #include "HTTPClient.h"
- #include <string.h> // for memcpy
- #include <algorithm> // for transform
- #include <cassert> // for assert
- #include <cctype> // for tolower
- #include <ostream> // for operator<<, basic_ostream
- #include <stdexcept> // for runtime_error
- #include "BellSocket.h" // for bell
- using namespace bell;
- void HTTPClient::Response::connect(const std::string& url) {
- urlParser = bell::URLParser::parse(url);
- // Open socket of type
- this->socketStream.open(urlParser.host, urlParser.port,
- urlParser.schema == "https");
- }
- HTTPClient::Response::~Response() {
- if (this->socketStream.isOpen()) {
- this->socketStream.close();
- }
- }
- void HTTPClient::Response::rawRequest(const std::string& url,
- const std::string& method,
- const std::vector<uint8_t>& content,
- Headers& headers) {
- urlParser = bell::URLParser::parse(url);
- // Prepare a request
- const char* reqEnd = "\r\n";
- socketStream << method << " " << urlParser.path << " HTTP/1.1" << reqEnd;
- socketStream << "Host: " << urlParser.host << ":" << urlParser.port << reqEnd;
- socketStream << "Connection: keep-alive" << reqEnd;
- socketStream << "Accept: */*" << reqEnd;
- // Write content
- if (content.size() > 0) {
- socketStream << "Content-Length: " << content.size() << reqEnd;
- }
- // Write headers
- for (auto& header : headers) {
- socketStream << header.first << ": " << header.second << reqEnd;
- }
- socketStream << reqEnd;
- // Write request body
- if (content.size() > 0) {
- socketStream.write((const char*)content.data(), content.size());
- }
- socketStream.flush();
- // Parse response
- readResponseHeaders();
- }
- void HTTPClient::Response::readResponseHeaders() {
- char *method, *path;
- const char* msgPointer;
- size_t msgLen;
- int pret, minorVersion, status;
- size_t prevbuflen = 0, numHeaders;
- this->httpBufferAvailable = 0;
- while (1) {
- socketStream.getline((char*)httpBuffer.data() + httpBufferAvailable,
- httpBuffer.size() - httpBufferAvailable);
- prevbuflen = httpBufferAvailable;
- httpBufferAvailable += socketStream.gcount();
- // Restore delimiters
- memcpy(httpBuffer.data() + httpBufferAvailable - 2, "\r\n", 2);
- // Parse the request
- numHeaders = sizeof(phResponseHeaders) / sizeof(phResponseHeaders[0]);
- pret =
- phr_parse_response((const char*)httpBuffer.data(), httpBufferAvailable,
- &minorVersion, &status, &msgPointer, &msgLen,
- phResponseHeaders, &numHeaders, prevbuflen);
- if (pret > 0) {
- break; /* successfully parsed the request */
- } else if (pret == -1)
- throw std::runtime_error("Cannot parse http response");
- /* request is incomplete, continue the loop */
- assert(pret == -2);
- if (httpBufferAvailable == httpBuffer.size())
- throw std::runtime_error("Response too large");
- }
- this->responseHeaders = {};
- // Headers have benen read
- for (int headerIndex = 0; headerIndex < numHeaders; headerIndex++) {
- this->responseHeaders.push_back(
- ValueHeader{std::string(phResponseHeaders[headerIndex].name,
- phResponseHeaders[headerIndex].name_len),
- std::string(phResponseHeaders[headerIndex].value,
- phResponseHeaders[headerIndex].value_len)});
- }
- std::string contentLengthValue = std::string(header("content-length"));
- if (contentLengthValue.size() > 0) {
- this->hasContentSize = true;
- this->contentSize = std::stoi(contentLengthValue);
- }
- }
- void HTTPClient::Response::get(const std::string& url, Headers headers) {
- std::string method = "GET";
- return this->rawRequest(url, method, {}, headers);
- }
- void HTTPClient::Response::post(const std::string& url, Headers headers,
- const std::vector<uint8_t>& body) {
- std::string method = "POST";
- return this->rawRequest(url, method, body, headers);
- }
- size_t HTTPClient::Response::contentLength() {
- return contentSize;
- }
- std::string_view HTTPClient::Response::header(const std::string& headerName) {
- for (auto& header : this->responseHeaders) {
- std::string headerValue = header.first;
- std::transform(headerValue.begin(), headerValue.end(), headerValue.begin(),
- [](unsigned char c) { return std::tolower(c); });
- if (headerName == headerValue) {
- return header.second;
- }
- }
- return "";
- }
- size_t HTTPClient::Response::totalLength() {
- auto rangeHeader = header("content-range");
- if (rangeHeader.find("/") != std::string::npos) {
- return std::stoi(
- std::string(rangeHeader.substr(rangeHeader.find("/") + 1)));
- }
- return this->contentLength();
- }
- void HTTPClient::Response::readRawBody() {
- if (contentSize > 0 && rawBody.size() == 0) {
- rawBody = std::vector<uint8_t>(contentSize);
- socketStream.read((char*)rawBody.data(), contentSize);
- }
- }
- std::string_view HTTPClient::Response::body() {
- readRawBody();
- return std::string_view((char*)rawBody.data(), rawBody.size());
- }
- std::vector<uint8_t> HTTPClient::Response::bytes() {
- readRawBody();
- return rawBody;
- }
|