#include "http_client_wrapper.h" #include HTTPClientWrapper::HTTPClientWrapper() { _buffer = new uint8_t[512]; _buffer_size = 512; } HTTPClientWrapper::~HTTPClientWrapper() { if (_http) { _http->end(); delete _http; } delete _buffer; } void HTTPClientWrapper::close() { _http->end(); _connected = false; } bool HTTPClientWrapper::get(String url, uint32_t offset, uint8_t redirection_count) { return _request("GET", url, offset, redirection_count); } bool HTTPClientWrapper::head(String url, uint32_t offset, uint8_t redirection_count) { return _request("HEAD", url, offset, redirection_count); } bool HTTPClientWrapper::_request(String method, String url, uint32_t offset, uint8_t redirection_count) { if (redirection_count>=5) return false; //if (_http) delete _http; DEBUG("%s %s\n", method.c_str(), url.c_str()); _http = new HTTPClient(); _http->setUserAgent("PodBox/0.1"); if (offset) { String s = "bytes="; s += offset; s += "-"; _http->addHeader("Range: ", s); } const char* headers[] = {"Location", "Content-Type", "Transfer-Encoding"}; _http->collectHeaders(headers, 3); bool result; /*if (url.startsWith("https:")) { BearSSL::WiFiClientSecure* client = new BearSSL::WiFiClientSecure(); client->setInsecure(); result = _http->begin(*client, url); } else { result = _http->begin(url); }*/ result = _http->begin(url); TRACE("HTTP->begin result: %d\n", result); if (!result) return false; int status = _http->sendRequest(method.c_str()); TRACE("HTTP Status code: %d\n", status); if (status == HTTP_CODE_FOUND || status==HTTP_CODE_MOVED_PERMANENTLY || status==HTTP_CODE_TEMPORARY_REDIRECT) { if (_http->hasHeader("Location")) { url = _http->header("Location"); _http->end(); delete _http; _http = NULL; return _request(method, url, offset, redirection_count+1); } else { ERROR("Got redirection HTTP code, but no Location header.\n"); delete _http; _http = NULL; return false; } } else if (status != HTTP_CODE_OK) { DEBUG("Unexpected HTTP return code %d. Cancelling.\n", status); return false; } _buffer_position = 0; _buffer_length = 0; _connected = true; _length = _http->getSize() + offset; if (_http->hasHeader("Content-Type")) { _content_type = _http->header("Content-Type"); } else { _content_type = ""; } _is_chunked = (_http->hasHeader("Transfer-Encoding")) && (_http->header("Transfer-Encoding").indexOf("chunked")!=-1); _stream = _http->getStreamPtr(); if (_is_chunked) { _read_next_chunk_header(true); } return true; } void HTTPClientWrapper::_read_next_chunk_header(bool first) { if (!_connected) { _chunk_length = 0; return; } if (!first) { // read() returns an error if no bytes is available right at this moment. // So we wait until 2 bytes are available or the connection times out. while (_stream->connected() && !_stream->available()) { delay(1); } int c1 = _stream->read(); while (_stream->connected() && !_stream->available()) { delay(1); } int c2 = _stream->read(); if (c1==-1 || c2==-1) { ERROR("Connection timeout.\n"); DEBUG("_stream.connected() returns %d\n", _stream->connected()); _chunk_length = 0; _connected = false; return; } else if (c1!='\r' || c2!='\n') { ERROR("Invalid chunk border found. Found: 0x%02X 0x%02X\n", c1, c2); _chunk_length = 0; _connected = false; return; } } String chunk_header = _stream->readStringUntil('\n'); chunk_header.trim(); _chunk_length = strtol(chunk_header.c_str(), NULL, 16); if (_chunk_length == 0) { _connected = false; TRACE("Empty chunk found -> EOF reached.\n"); } else { TRACE("Chunk found. Length: %d\n", _chunk_length); } } uint16_t HTTPClientWrapper::_fill_buffer() { if (!_connected) { _buffer_position = 0; _buffer_length = 0; return 0; } uint16_t bytes_to_fill = _buffer_size; uint16_t bytes_filled = 0; while (bytes_to_fill > 0) { uint16_t bytes_to_request = bytes_to_fill; if (_is_chunked && _chunk_length < bytes_to_fill) bytes_to_request = _chunk_length; TRACE("fill_buffer loop. _is_chunked: %d, _chunk_length: %d, _buffer_size: %d, bytes_filled: %d, bytes_to_fill: %d, bytes_to_request: %d", _is_chunked, _chunk_length, _buffer_size, bytes_filled, bytes_to_fill, bytes_to_request); uint16_t result = _stream->readBytes(_buffer + bytes_filled, bytes_to_request); TRACE(", result: %d\n", result); bytes_filled += result; bytes_to_fill -= result; if (_is_chunked) { _chunk_length -= result; if (_chunk_length == 0) _read_next_chunk_header(false); } if (result == 0) { _connected = false; break; } } _buffer_position = 0; _buffer_length = bytes_filled; TRACE("Buffer filled. _buffer_length: %d\n", _buffer_length); return bytes_filled; } String HTTPClientWrapper::getContentType() { return _content_type; } int HTTPClientWrapper::read() { if (_buffer_position >= _buffer_length) _fill_buffer(); if (_buffer_position >= _buffer_length) return -1; return _buffer[_buffer_position++]; } uint32_t HTTPClientWrapper::read(uint8_t* dst, uint32_t len) { TRACE("Reading %d bytes...\n", len); uint32_t bytes_filled = 0; while (1) { if (_buffer_position >= _buffer_length) _fill_buffer(); if (_buffer_position >= _buffer_length) break; uint32_t bytes_to_fill = len; if (bytes_to_fill > _buffer_length - _buffer_position) bytes_to_fill = _buffer_length - _buffer_position; TRACE("read_loop: _buffer_length=%d, _buffer_position=%d, len=%d, bytes_to_fill=%d\n", _buffer_length, _buffer_position, len, bytes_to_fill); memcpy(dst + bytes_filled, _buffer + _buffer_position, bytes_to_fill); _buffer_position += bytes_to_fill; bytes_filled += bytes_to_fill; len -= bytes_to_fill; if (bytes_to_fill==0 || len==0) break; } return bytes_filled; } uint32_t HTTPClientWrapper::getSize() {return _length; } String HTTPClientWrapper::readUntil(String sep) { String result = ""; while(true) { int i = read(); if (i==-1) break; char c = i; if (sep.indexOf(c)!=-1) { // separator if (result.length()>0) break; } else { result.concat(c); } } return result; } String HTTPClientWrapper::readLine() { return readUntil("\n\r"); }