mirror of
https://github.com/idolpx/libMeatloaf.git
synced 2025-12-05 20:28:50 -05:00
576 lines
19 KiB
C++
576 lines
19 KiB
C++
// #include "http.h"
|
|
|
|
// #include <esp_idf_version.h>
|
|
|
|
// #include "../../../include/debug.h"
|
|
// #include "../../../include/global_defines.h"
|
|
|
|
// /********************************************************
|
|
// * File impls
|
|
// ********************************************************/
|
|
|
|
// MeatHttpClient* HttpFile::fromHeader() {
|
|
// if(client == nullptr) {
|
|
// //Debug_printv("Client was not present, creating");
|
|
// client = new MeatHttpClient();
|
|
|
|
// // let's just get the headers so we have some info
|
|
// //Debug_printv("Client requesting head");
|
|
// //Debug_printv("before head url[%s]", url.c_str());
|
|
// client->HEAD(url);
|
|
// //Debug_printv("after head url[%s]", client->url.c_str());
|
|
// resetURL(client->url);
|
|
// }
|
|
// return client;
|
|
// }
|
|
|
|
// bool HttpFile::isDirectory() {
|
|
// if(fromHeader()->m_isDirectory)
|
|
// return true;
|
|
|
|
// if(fromHeader()->m_isWebDAV) {
|
|
// // try webdav PROPFIND to get a listing
|
|
// return true;
|
|
// }
|
|
// else
|
|
// // otherwise return false
|
|
// return false;
|
|
// }
|
|
|
|
// MStream* HttpFile::getSourceStream(std::ios_base::openmode mode) {
|
|
// // has to return OPENED stream
|
|
// //Debug_printv("Input stream requested: [%s]", url.c_str());
|
|
|
|
// // headers have to be supplied here, they might be set on this channel using
|
|
// // i.e. CBM DOS commands like
|
|
// // H+:Accept: */*
|
|
// // H+:Accept-Encoding: gzip, deflate
|
|
// // here you add them all to a map, like this:
|
|
// std::map<std::string, std::string> headers;
|
|
// // headers["Accept"] = "*/*";
|
|
// // headers["Accept-Encoding"] = "gzip, deflate";
|
|
// // etc.
|
|
// MStream* istream = new HttpIStream(url, mode, headers);
|
|
// istream->open();
|
|
|
|
// return istream;
|
|
// }
|
|
|
|
// MStream* HttpFile::getDecodedStream(std::shared_ptr<MStream> is) {
|
|
// return is.get(); // DUMMY return value - we've overriden istreamfunction, so this one won't be used
|
|
// }
|
|
|
|
// time_t HttpFile::getLastWrite() {
|
|
// if(fromHeader()->m_isWebDAV) {
|
|
// return 0;
|
|
// }
|
|
// else
|
|
// // take from webdav PROPFIND or fallback to Last-Modified
|
|
// return 0;
|
|
// }
|
|
|
|
// time_t HttpFile::getCreationTime() {
|
|
// if(fromHeader()->m_isWebDAV) {
|
|
// return 0;
|
|
// }
|
|
// else
|
|
// // take from webdav PROPFIND or fallback to Last-Modified
|
|
// return 0;
|
|
// }
|
|
|
|
// bool HttpFile::exists() {
|
|
// return fromHeader()->_exists;
|
|
// }
|
|
|
|
// uint32_t HttpFile::size() {
|
|
// if(fromHeader()->m_isWebDAV) {
|
|
// // take from webdav PROPFIND
|
|
// return 0;
|
|
// }
|
|
// else
|
|
// // fallback to what we had from the header
|
|
// return fromHeader()->_size;
|
|
// }
|
|
|
|
// bool HttpFile::remove() {
|
|
// if(fromHeader()->m_isWebDAV) {
|
|
// // PROPPATCH allows deletion
|
|
// return false;
|
|
// }
|
|
// return false;
|
|
// }
|
|
|
|
// bool HttpFile::mkDir() {
|
|
// if(fromHeader()->m_isWebDAV) {
|
|
// // MKCOL creates dir
|
|
// return false;
|
|
// }
|
|
// return false;
|
|
// }
|
|
|
|
// bool HttpFile::rewindDirectory() {
|
|
// if(fromHeader()->m_isWebDAV) {
|
|
// // we can try if this is webdav, then
|
|
// // PROPFIND allows listing dir
|
|
// return false;
|
|
// }
|
|
// return false;
|
|
// };
|
|
|
|
// MFile* HttpFile::getNextFileInDir() {
|
|
// Debug_printv("");
|
|
// if(fromHeader()->m_isWebDAV) {
|
|
// // we can try if this is webdav, then
|
|
// // PROPFIND allows listing dir
|
|
// return nullptr;
|
|
// }
|
|
// return nullptr;
|
|
// };
|
|
|
|
|
|
// bool HttpFile::isText() {
|
|
// return fromHeader()->isText;
|
|
// }
|
|
|
|
// /********************************************************
|
|
// * Istream impls
|
|
// ********************************************************/
|
|
// bool HttpIStream::open() {
|
|
// bool r = false;
|
|
// m_http.setHeaders(headers);
|
|
|
|
// if(mode == (std::ios_base::out | std::ios_base::app))
|
|
// r = m_http.PUT(url);
|
|
// else if(mode == std::ios_base::out)
|
|
// r = m_http.POST(url);
|
|
// else
|
|
// r = m_http.GET(url);
|
|
|
|
// if ( r ) {
|
|
// _size = m_http._size;
|
|
// }
|
|
|
|
// return r;
|
|
// }
|
|
|
|
// void HttpIStream::close() {
|
|
// //Debug_printv("CLOSE called explicitly on this HTTP stream!");
|
|
// m_http.close();
|
|
// }
|
|
|
|
// bool HttpIStream::seek(uint32_t pos) {
|
|
// if ( !m_http._is_open )
|
|
// {
|
|
// Debug_printv("error");
|
|
// return false;
|
|
// }
|
|
|
|
// return m_http.seek(pos);
|
|
// }
|
|
|
|
// uint32_t HttpIStream::read(uint8_t* buf, uint32_t size) {
|
|
// uint32_t bytesRead = 0;
|
|
// if ( size > available() )
|
|
// size = available();
|
|
|
|
// if ( size > 0 )
|
|
// {
|
|
// bytesRead = m_http.read(buf, size);
|
|
// _position += bytesRead;
|
|
// }
|
|
|
|
// return bytesRead;
|
|
// };
|
|
|
|
// uint32_t HttpIStream::write(const uint8_t *buf, uint32_t size) {
|
|
// uint32_t bytesWritten = m_http.write(buf, size);
|
|
// _position += bytesWritten;
|
|
// return bytesWritten;
|
|
// }
|
|
|
|
|
|
|
|
// bool HttpIStream::isOpen() {
|
|
// return m_http._is_open;
|
|
// };
|
|
|
|
// // uint32_t HttpIStream::size() {
|
|
// // return m_http._size;
|
|
// // };
|
|
|
|
// // uint32_t HttpIStream::available() {
|
|
// // return m_http.m_bytesAvailable;
|
|
// // };
|
|
|
|
// // uint32_t HttpIStream::position() {
|
|
// // return m_http._position;
|
|
// // }
|
|
|
|
// // size_t HttpIStream::error() {
|
|
// // return m_http.m_error;
|
|
// // }
|
|
|
|
// /********************************************************
|
|
// * Meat HTTP client impls
|
|
// ********************************************************/
|
|
// bool MeatHttpClient::GET(std::string dstUrl) {
|
|
// Debug_printv("GET");
|
|
// return open(dstUrl, HTTP_METHOD_GET);
|
|
// }
|
|
|
|
// bool MeatHttpClient::POST(std::string dstUrl) {
|
|
// Debug_printv("POST");
|
|
// return open(dstUrl, HTTP_METHOD_POST);
|
|
// }
|
|
|
|
// bool MeatHttpClient::PUT(std::string dstUrl) {
|
|
// Debug_printv("PUT");
|
|
// return open(dstUrl, HTTP_METHOD_PUT);
|
|
// }
|
|
|
|
// bool MeatHttpClient::HEAD(std::string dstUrl) {
|
|
// Debug_printv("HEAD");
|
|
// bool rc = open(dstUrl, HTTP_METHOD_HEAD);
|
|
// close();
|
|
// return rc;
|
|
// }
|
|
|
|
// bool MeatHttpClient::processRedirectsAndOpen(int range) {
|
|
// wasRedirected = false;
|
|
// _size = -1;
|
|
|
|
// Debug_printv("reopening url[%s] from position:%d", url.c_str(), range);
|
|
// lastRC = openAndFetchHeaders(lastMethod, range);
|
|
|
|
// while(lastRC == HttpStatus_MovedPermanently || lastRC == HttpStatus_Found || lastRC == 303)
|
|
// {
|
|
// Debug_printv("--- Page moved, doing redirect to [%s]", url.c_str());
|
|
// close();
|
|
// lastRC = openAndFetchHeaders(lastMethod, range);
|
|
// wasRedirected = true;
|
|
// }
|
|
|
|
// if(lastRC != HttpStatus_Ok && lastRC != 301 && lastRC != 206) {
|
|
// Debug_printv("opening stream failed, httpCode=%d", lastRC);
|
|
// close();
|
|
// return false;
|
|
// }
|
|
|
|
// // TODO - set m_isWebDAV somehow
|
|
// _is_open = true;
|
|
// _exists = true;
|
|
// _position = 0;
|
|
|
|
// Debug_printv("length[%d] avail[%d] isFriendlySkipper[%d] isText[%d] httpCode[%d]", _size, available(), isFriendlySkipper, isText, lastRC);
|
|
|
|
// return true;
|
|
// }
|
|
|
|
// bool MeatHttpClient::open(std::string dstUrl, esp_http_client_method_t meth) {
|
|
// url = dstUrl;
|
|
// lastMethod = meth;
|
|
// //m_error = 0;
|
|
|
|
// return processRedirectsAndOpen(0);
|
|
// };
|
|
|
|
// void MeatHttpClient::close() {
|
|
// if(m_http != nullptr) {
|
|
// if ( _is_open ) {
|
|
// esp_http_client_close(m_http);
|
|
// }
|
|
// esp_http_client_cleanup(m_http);
|
|
// //Debug_printv("HTTP Close and Cleanup");
|
|
// m_http = nullptr;
|
|
// }
|
|
// _is_open = false;
|
|
// }
|
|
|
|
// void MeatHttpClient::setOnHeader(const std::function<int(char*, char*)> &lambda) {
|
|
// onHeader = lambda;
|
|
// }
|
|
|
|
// bool MeatHttpClient::seek(uint32_t pos) {
|
|
// if(pos==_position)
|
|
// return true;
|
|
|
|
// if(isFriendlySkipper) {
|
|
// esp_http_client_close(m_http);
|
|
|
|
// bool op = processRedirectsAndOpen(pos);
|
|
|
|
// Debug_printv("SEEK in HttpIStream %s: range request RC=%d", url.c_str(), lastRC);
|
|
|
|
// if(!op)
|
|
// return false;
|
|
|
|
// // 200 = range not supported! according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests
|
|
// if(lastRC == 206){
|
|
// Debug_printv("Seek successful");
|
|
|
|
// _position = pos;
|
|
// return true;
|
|
// }
|
|
// }
|
|
|
|
// if(lastMethod == HTTP_METHOD_GET) {
|
|
// Debug_printv("Server doesn't support resume, reading from start and discarding");
|
|
// // server doesn't support resume, so...
|
|
// if(pos<_position || pos == 0) {
|
|
// // skipping backward let's simply reopen the stream...
|
|
// esp_http_client_close(m_http);
|
|
// bool op = open(url, lastMethod);
|
|
// if(!op)
|
|
// return false;
|
|
|
|
// // and read pos bytes - requires some buffer
|
|
// for(int i = 0; i<pos; i++) {
|
|
// char c;
|
|
// int rc = esp_http_client_read(m_http, &c, 1);
|
|
// if(rc == -1)
|
|
// return false;
|
|
// }
|
|
// }
|
|
// else {
|
|
// auto delta = pos-_position;
|
|
// // skipping forward let's skip a proper amount of bytes - requires some buffer
|
|
// for(int i = 0; i<delta; i++) {
|
|
// char c;
|
|
// int rc = esp_http_client_read(m_http, &c, 1);
|
|
// if(rc == -1)
|
|
// return false;
|
|
// }
|
|
// }
|
|
|
|
// _position = pos;
|
|
// Debug_printv("stream opened[%s]", url.c_str());
|
|
|
|
// return true;
|
|
// }
|
|
// else
|
|
// return false;
|
|
// }
|
|
|
|
// uint32_t MeatHttpClient::read(uint8_t* buf, uint32_t size) {
|
|
// if (_is_open) {
|
|
// auto bytesRead= esp_http_client_read(m_http, (char *)buf, size );
|
|
|
|
// if(bytesRead>0) {
|
|
// _position+=bytesRead;
|
|
// }
|
|
// return bytesRead;
|
|
// }
|
|
// return 0;
|
|
// };
|
|
|
|
// uint32_t MeatHttpClient::write(const uint8_t* buf, uint32_t size) {
|
|
// if (_is_open) {
|
|
// auto bytesWritten= esp_http_client_write(m_http, (char *)buf, size );
|
|
// _position+=bytesWritten;
|
|
// return bytesWritten;
|
|
// }
|
|
// return 0;
|
|
// };
|
|
|
|
// int MeatHttpClient::openAndFetchHeaders(esp_http_client_method_t meth, int resume) {
|
|
|
|
// if ( url.size() < 5)
|
|
// return 0;
|
|
|
|
// mstr::replaceAll(url, " ", "%20");
|
|
// esp_http_client_config_t config = {
|
|
// .url = url.c_str(),
|
|
// .user_agent = USER_AGENT,
|
|
// .method = meth,
|
|
// .timeout_ms = 10000,
|
|
// .max_redirection_count = 10,
|
|
// .event_handler = _http_event_handler,
|
|
// .user_data = this,
|
|
// .keep_alive_enable = true,
|
|
// .keep_alive_idle = 10,
|
|
// .keep_alive_interval = 1
|
|
// };
|
|
|
|
// //Debug_printv("HTTP Init url[%s]", url.c_str());
|
|
// m_http = esp_http_client_init(&config);
|
|
|
|
// for (const auto& pair : headers) {
|
|
// std::cout << pair.first << ": " << pair.second << std::endl;
|
|
// esp_http_client_set_header(m_http, pair.first.c_str(), pair.second.c_str());
|
|
// }
|
|
|
|
// if(resume > 0) {
|
|
// char str[40];
|
|
// snprintf(str, sizeof str, "bytes=%lu-", (unsigned long)resume);
|
|
// esp_http_client_set_header(m_http, "range", str);
|
|
// }
|
|
|
|
// //Debug_printv("--- PRE OPEN");
|
|
|
|
// esp_err_t initOk = esp_http_client_open(m_http, 0); // or open? It's not entirely clear...
|
|
|
|
// if(initOk == ESP_FAIL)
|
|
// return 0;
|
|
|
|
// //Debug_printv("--- PRE FETCH HEADERS");
|
|
|
|
// int lengthResp = esp_http_client_fetch_headers(m_http);
|
|
// if(_size == -1 && lengthResp > 0) {
|
|
// // only if we aren't chunked!
|
|
// _size = lengthResp;
|
|
// _position = 0;
|
|
// }
|
|
|
|
// //Debug_printv("--- PRE GET STATUS CODE");
|
|
|
|
// return esp_http_client_get_status_code(m_http);
|
|
// }
|
|
|
|
// esp_err_t MeatHttpClient::_http_event_handler(esp_http_client_event_t *evt)
|
|
// {
|
|
// MeatHttpClient* meatClient = (MeatHttpClient*)evt->user_data;
|
|
|
|
// switch(evt->event_id) {
|
|
// case HTTP_EVENT_ERROR: // This event occurs when there are any errors during execution
|
|
// Debug_printv("HTTP_EVENT_ERROR");
|
|
// //meatClient->m_error = 1;
|
|
// break;
|
|
|
|
// case HTTP_EVENT_ON_CONNECTED: // Once the HTTP has been connected to the server, no data exchange has been performed
|
|
// // Debug_printv("HTTP_EVENT_ON_CONNECTED");
|
|
// break;
|
|
|
|
// case HTTP_EVENT_HEADER_SENT: // After sending all the headers to the server
|
|
// // Debug_printv("HTTP_EVENT_HEADER_SENT");
|
|
// break;
|
|
|
|
// case HTTP_EVENT_ON_HEADER: // Occurs when receiving each header sent from the server
|
|
// // Does this server support resume?
|
|
// // Accept-Ranges: bytes
|
|
|
|
// if (mstr::equals("Accept-Ranges", evt->header_key, false))
|
|
// {
|
|
// if(meatClient != nullptr) {
|
|
// meatClient->isFriendlySkipper = mstr::equals("bytes", evt->header_value,false);
|
|
// //Debug_printv("* Ranges info present '%s', comparison=%d!",evt->header_value, strcmp("bytes", evt->header_value)==0);
|
|
// }
|
|
// }
|
|
// // what can we do UTF8<->PETSCII on this stream?
|
|
// else if (mstr::equals("Content-Type", evt->header_key, false))
|
|
// {
|
|
// std::string asString = evt->header_value;
|
|
// bool isText = mstr::isText(asString);
|
|
|
|
// if(meatClient != nullptr) {
|
|
// meatClient->isText = isText;
|
|
// //Debug_printv("* Content info present '%s', isText=%d!, type=%s", evt->header_value, isText, asString.c_str());
|
|
// }
|
|
// }
|
|
// else if(mstr::equals("Last-Modified", evt->header_key, false))
|
|
// {
|
|
// // Last-Modified, value=Thu, 03 Dec 1992 08:37:20 - may be used to get file date
|
|
// }
|
|
// else if(mstr::equals("Content-Disposition", evt->header_key, false))
|
|
// {
|
|
// // Content-Disposition, value=attachment; filename*=UTF-8''GeckOS-c64.d64
|
|
// // we can set isText from real file extension, too!
|
|
// std::string value = evt->header_value;
|
|
// if ( mstr::contains( value, (char *)"index.prg" ) )
|
|
// {
|
|
// Debug_printv("HTTP Directory Listing [%s]", meatClient->url.c_str());
|
|
// meatClient->m_isDirectory = true;
|
|
// }
|
|
// }
|
|
// else if(mstr::equals("Content-Length", evt->header_key, false))
|
|
// {
|
|
// //Debug_printv("* Content len present '%s'", evt->header_value);
|
|
// meatClient->_size = std::stoi(evt->header_value);
|
|
// }
|
|
// else if(mstr::equals("Location", evt->header_key, false))
|
|
// {
|
|
// Debug_printv("* This page redirects from '%s' to '%s'", meatClient->url.c_str(), evt->header_value);
|
|
// if ( mstr::startsWith(evt->header_value, (char *)"http") )
|
|
// {
|
|
// //Debug_printv("match");
|
|
// meatClient->url = evt->header_value;
|
|
// }
|
|
// else
|
|
// {
|
|
// //Debug_printv("no match");
|
|
// if ( mstr::startsWith(evt->header_value, (char *)"/") )
|
|
// {
|
|
// // Absolute path redirect
|
|
// PeoplesUrlParser *u = PeoplesUrlParser::parseURL( meatClient->url );
|
|
// meatClient->url = u->root() + evt->header_value;
|
|
// }
|
|
// else
|
|
// {
|
|
// // Relative path redirect
|
|
// meatClient->url += evt->header_value;
|
|
// }
|
|
// }
|
|
|
|
// Debug_printv("new url '%s'", meatClient->url.c_str());
|
|
// }
|
|
|
|
// // Allow override in lambda
|
|
// meatClient->onHeader(evt->header_key, evt->header_value);
|
|
|
|
// break;
|
|
|
|
// #if __cplusplus > 201703L
|
|
// //#if ESP_IDF_VERSION > ESP_IDF_VERSION_VAL(5, 3, 0)
|
|
// case HTTP_EVENT_REDIRECT:
|
|
|
|
// Debug_printv("* This page redirects from '%s' to '%s'", meatClient->url.c_str(), evt->header_value);
|
|
// if ( mstr::startsWith(evt->header_value, (char *)"http") )
|
|
// {
|
|
// //Debug_printv("match");
|
|
// meatClient->url = evt->header_value;
|
|
// }
|
|
// else
|
|
// {
|
|
// //Debug_printv("no match");
|
|
// meatClient->url += evt->header_value;
|
|
// }
|
|
// break;
|
|
// #endif
|
|
|
|
// case HTTP_EVENT_ON_DATA: // Occurs multiple times when receiving body data from the server. MAY BE SKIPPED IF BODY IS EMPTY!
|
|
// //Debug_printv("HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
|
|
// {
|
|
// // int status = esp_http_client_get_status_code(meatClient->m_http);
|
|
|
|
// // if ((status == HttpStatus_Found || status == HttpStatus_MovedPermanently || status == 303) /*&& client->_redirect_count < (client->_max_redirects - 1)*/)
|
|
// // {
|
|
// // //Debug_printv("HTTP_EVENT_ON_DATA: Redirect response body, ignoring");
|
|
// // }
|
|
// // else {
|
|
// // //Debug_printv("HTTP_EVENT_ON_DATA: Got response body");
|
|
// // }
|
|
|
|
|
|
// if (esp_http_client_is_chunked_response(evt->client)) {
|
|
// int len;
|
|
// esp_http_client_get_chunk_length(evt->client, &len);
|
|
// meatClient->_size = len;
|
|
// //Debug_printv("HTTP_EVENT_ON_DATA: Got chunked response, chunklen=%d, contentlen[%d]", len, meatClient->_size);
|
|
// }
|
|
// }
|
|
// break;
|
|
|
|
// case HTTP_EVENT_ON_FINISH:
|
|
// // Occurs when finish a HTTP session
|
|
// // This may get called more than once if esp_http_client decides to retry in order to handle a redirect or auth response
|
|
// //Debug_printv("HTTP_EVENT_ON_FINISH %u\r\n", uxTaskGetStackHighWaterMark(nullptr));
|
|
// // Keep track of how many times we "finish" reading a response from the server
|
|
// //Debug_printv("HTTP_EVENT_ON_FINISH");
|
|
// break;
|
|
// case HTTP_EVENT_DISCONNECTED: // The connection has been disconnected
|
|
// //Debug_printv("HTTP_EVENT_DISCONNECTED");
|
|
// //meatClient->m_bytesAvailable = 0;
|
|
// break;
|
|
// }
|
|
// return ESP_OK;
|
|
// }
|