214 lines
6.1 KiB
C++
214 lines
6.1 KiB
C++
#include "http_client_wrapper.h"
|
|
#include <WiFiClientSecure.h>
|
|
|
|
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");
|
|
}
|