diff --git a/src/AudioFileSourceHTTPStream.cpp b/src/AudioFileSourceHTTPStream.cpp index c5d0b833..04e814a7 100644 --- a/src/AudioFileSourceHTTPStream.cpp +++ b/src/AudioFileSourceHTTPStream.cpp @@ -27,13 +27,44 @@ AudioFileSourceHTTPStream::AudioFileSourceHTTPStream() pos = 0; reconnectTries = 0; saveURL[0] = 0; + next_chunk = 0; } AudioFileSourceHTTPStream::AudioFileSourceHTTPStream(const char *url) { saveURL[0] = 0; reconnectTries = 0; + next_chunk = 0; open(url); + +} + +bool AudioFileSourceHTTPStream::verifyCrlf() +{ + + uint8_t crlf[3]; + + client.read(crlf, 2); + crlf[2] = 0; + + return !strncmp("\r\n", reinterpret_cast(crlf), 2); +} + +int AudioFileSourceHTTPStream::getChunkSize() +{ + String length = client.readStringUntil('\r'); + String lf = client.readStringUntil('\n'); + + unsigned int val = 0; + auto ret = sscanf(length.c_str(), "%x", &val); + if(ret) + { + return val; + } + else + { + return -1; + } } bool AudioFileSourceHTTPStream::open(const char *url) @@ -44,12 +75,37 @@ bool AudioFileSourceHTTPStream::open(const char *url) #ifndef ESP32 http.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS); #endif + const char* headers[] = { "Transfer-Encoding" }; + http.collectHeaders( headers, 1 ); int code = http.GET(); if (code != HTTP_CODE_OK) { http.end(); cb.st(STATUS_HTTPFAIL, PSTR("Can't open HTTP request")); return false; } + if (http.hasHeader("Transfer-Encoding")) { + audioLogger->printf_P(PSTR("Transfer-Encoding: %s\n"), http.header("Transfer-Encoding").c_str()); + if(http.header("Transfer-Encoding") == String(PSTR("chunked"))) { + + next_chunk = getChunkSize(); + if(-1 == next_chunk) + { + return false; + } + is_chunked = true; + readImpl = &AudioFileSourceHTTPStream::readChunked; + } else { + is_chunked = false; + readImpl = &AudioFileSourceHTTPStream::readRegular; + } + + } else { + readImpl = &AudioFileSourceHTTPStream::readRegular; + audioLogger->printf_P(PSTR("No Transfer-Encoding\n")); + is_chunked = false; + } + + size = http.getSize(); strncpy(saveURL, url, sizeof(saveURL)); saveURL[sizeof(saveURL)-1] = 0; @@ -61,13 +117,57 @@ AudioFileSourceHTTPStream::~AudioFileSourceHTTPStream() http.end(); } +uint32_t AudioFileSourceHTTPStream::readRegular(void *data, uint32_t len, bool nonBlock) +{ + return readInternal(data, len, nonBlock); +} + +uint32_t AudioFileSourceHTTPStream::readChunked(void *data, uint32_t len, bool nonBlock) +{ + uint32_t bytesRead = 0; + uint32_t pos = 0; + + while(len > 0) + { + if(len >= next_chunk) + { + while (next_chunk) + { + bytesRead = readInternal(data + pos, next_chunk, nonBlock); + next_chunk -= bytesRead; + pos += bytesRead; + } + len -= pos; + if(!verifyCrlf()) + { + audioLogger->printf(PSTR("Couldn't read CRLF after chunk, something is wrong !!\n")); + return 0; + } + next_chunk = getChunkSize(); + } + else + { + bytesRead = readInternal(data + pos, len, nonBlock); + next_chunk -= bytesRead; + len -= bytesRead; + pos += bytesRead; + } + + } + + + return pos; +} + uint32_t AudioFileSourceHTTPStream::read(void *data, uint32_t len) { if (data==NULL) { audioLogger->printf_P(PSTR("ERROR! AudioFileSourceHTTPStream::read passed NULL data\n")); return 0; } - return readInternal(data, len, false); + + return (this->*readImpl)(data, len, false); + } uint32_t AudioFileSourceHTTPStream::readNonBlock(void *data, uint32_t len) @@ -76,7 +176,8 @@ uint32_t AudioFileSourceHTTPStream::readNonBlock(void *data, uint32_t len) audioLogger->printf_P(PSTR("ERROR! AudioFileSourceHTTPStream::readNonBlock passed NULL data\n")); return 0; } - return readInternal(data, len, true); + return (this->*readImpl)(data, len, true); + } uint32_t AudioFileSourceHTTPStream::readInternal(void *data, uint32_t len, bool nonBlock) diff --git a/src/AudioFileSourceHTTPStream.h b/src/AudioFileSourceHTTPStream.h index 34e54663..c3eaeb53 100644 --- a/src/AudioFileSourceHTTPStream.h +++ b/src/AudioFileSourceHTTPStream.h @@ -21,6 +21,8 @@ #if defined(ESP32) || defined(ESP8266) #pragma once +#include // std::map ascii_to_hex; + #include #ifdef ESP32 #include @@ -29,6 +31,8 @@ #endif #include "AudioFileSource.h" + + class AudioFileSourceHTTPStream : public AudioFileSource { friend class AudioFileSourceICYStream; @@ -52,7 +56,8 @@ class AudioFileSourceHTTPStream : public AudioFileSource enum { STATUS_HTTPFAIL=2, STATUS_DISCONNECTED, STATUS_RECONNECTING, STATUS_RECONNECTED, STATUS_NODATA }; private: - virtual uint32_t readInternal(void *data, uint32_t len, bool nonBlock); + bool is_chunked; + std::size_t next_chunk; WiFiClient client; HTTPClient http; int pos; @@ -60,6 +65,14 @@ class AudioFileSourceHTTPStream : public AudioFileSource int reconnectTries; int reconnectDelayMs; char saveURL[128]; + uint32_t (AudioFileSourceHTTPStream::*readImpl)(void *data, uint32_t len, bool nonBlock); + + virtual uint32_t readInternal(void *data, uint32_t len, bool nonBlock); + uint32_t readChunked(void *data, uint32_t len, bool nonBlock); + uint32_t readRegular(void *data, uint32_t len, bool nonBlock); + bool verifyCrlf(); + int getChunkSize(); + };