Skip to content

Commit

Permalink
Fix for a media server running on an Orange France router
Browse files Browse the repository at this point in the history
  • Loading branch information
yellobyte committed May 12, 2024
1 parent 0d66578 commit 1b97ef3
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 27 deletions.
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "SoapESP32",
"version": "1.3.1",
"version": "1.3.2",
"platforms":
[
"espressif32"
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=SoapESP32
version=1.3.1
version=1.3.2
author=Thomas Jentzsch
maintainer=Thomas Jentzsch <yellobyte@bluewin.ch>
sentence=Enables ESP32 devices to scan the local network for DLNA media servers, browse their content and download files.
Expand Down
105 changes: 82 additions & 23 deletions src/SoapESP32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,20 +182,21 @@ bool SoapESP32::wakeUpServer(const char *macAddress)
//
// helper function, client timed read
//
int SoapESP32::soapClientTimedRead()
int SoapESP32::soapClientTimedRead(unsigned long ms)
{
int c;
unsigned long startMillis = millis();
unsigned long timeout = ms ? ms : SERVER_READ_TIMEOUT,
startMillis = millis();

do {
claimSPI();
c = m_client->read();
releaseSPI();
if(c >= 0) {
if (c >= 0) {
return c;
}
}
while (millis() - startMillis < SERVER_READ_TIMEOUT);
while (millis() - startMillis < timeout);

return -1; // read timeout
}
Expand Down Expand Up @@ -383,15 +384,15 @@ bool SoapESP32::soapReadHttpHeader(uint64_t *contentLength, bool *chunked)
log_v("header line: %s", tmpBuffer);
if (len == 1) break; // End of header: finishing line contains only "\r\n"
if (!ok) {
if ((p = strcasestr(tmpBuffer,HEADER_CONTENT_LENGTH)) != NULL) {
if (sscanf(p+strlen(HEADER_CONTENT_LENGTH),"%llu", contentLength) == 1) {
if ((p = strcasestr(tmpBuffer, HEADER_CONTENT_LENGTH)) != NULL) {
if (sscanf(p+strlen(HEADER_CONTENT_LENGTH), "%llu", contentLength) == 1) {
ok = true;
continue; // continue to read rest of header
}
}
else if (chunked && strcasestr(tmpBuffer,HEADER_TRANS_ENC_CHUNKED)) {
else if (chunked && strcasestr(tmpBuffer, HEADER_TRANS_ENC_CHUNKED)) {
ok = *chunked = true;
m_xmlChunkCount = 0; // telling chunk size comes next
m_ChunkCount = 0; // telling chunk size comes next
continue; // continue to read rest of header
}
}
Expand Down Expand Up @@ -435,7 +436,7 @@ int SoapESP32::soapReadXML(bool chunked, bool replace)
}
else {
// de-chunk XML data
if (m_xmlChunkCount <= 0) {
if (m_ChunkCount <= 0) {
char tmpBuffer[10];

// next line contains chunk size
Expand All @@ -446,11 +447,11 @@ int SoapESP32::soapReadXML(bool chunked, bool replace)
return -2; // we expect at least 1 digit chunk size + '\r'
}
tmpBuffer[len-1] = 0; // replace '\r' with '\0'
if (sscanf(tmpBuffer, "%x", &m_xmlChunkCount) != 1) {
if (sscanf(tmpBuffer, "%x", &m_ChunkCount) != 1) {
return -3;
}
log_d("announced chunk size: 0x%x(%d)", m_xmlChunkCount, m_xmlChunkCount);
if (m_xmlChunkCount <= 0) {
log_d("announced chunk size: 0x%x(%d)", m_ChunkCount, m_ChunkCount);
if (m_ChunkCount <= 0) {
return -4; // not necessarily an error...final chunk size can be 0
}
}
Expand All @@ -459,7 +460,7 @@ int SoapESP32::soapReadXML(bool chunked, bool replace)
}

// check for end of chunk
if (--m_xmlChunkCount == 0) {
if (--m_ChunkCount == 0) {
// skip "\r\n" trailing each chunk
if (soapClientTimedRead() < 0 || soapClientTimedRead() < 0) {
return -6;
Expand Down Expand Up @@ -1218,9 +1219,9 @@ if (strCaps.length()) {
bool SoapESP32::readStart(soapObject_t *object, size_t *size)
{
uint64_t contentSize;
bool chunked;

if (object->isDirectory) return false;
m_clientDataAvailable = 0;

log_i("server ip: %s, port: %d, uri: \"%s\"",
object->downloadIp.toString().c_str(), object->downloadPort, object->uri.c_str());
Expand All @@ -1240,7 +1241,7 @@ bool SoapESP32::readStart(soapObject_t *object, size_t *size)
}

// connection established, read HTTP header
if (!soapReadHttpHeader(&contentSize)) {
if (!soapReadHttpHeader(&contentSize, &chunked)) {
// error returned
log_e("soapReadHttpHeader() was unsuccessful.");
claimSPI();
Expand All @@ -1257,11 +1258,25 @@ bool SoapESP32::readStart(soapObject_t *object, size_t *size)
releaseSPI();
return false;
}

m_clientDataAvailable = 0;
m_clientDataChunked = chunked;
m_ChunkCount = 0;

if (contentSize > 0) {
// file size announced in HTTP header
log_d("media file size taken from http header: %llu", contentSize);
m_clientDataAvailable = (size_t)contentSize;
}
else if (object->size > 0) {
// as an alternative we use file size given in function argument
log_d("media file size taken from argument (media object): %llu", object->size);
m_clientDataAvailable = (size_t)object->size;
}

m_clientDataAvailable = (size_t)contentSize;
if (m_clientDataAvailable == 0) {
// no data available
log_e("announced file size: 0 !");
// no file size given or no data available to read
log_e("unknown file size !");
claimSPI();
m_client->stop();
releaseSPI();
Expand All @@ -1278,7 +1293,7 @@ bool SoapESP32::readStart(soapObject_t *object, size_t *size)

//
// read up to size bytes from server and place them into buf
// returnes number of bytes read or -1 in case nothing was read (default read timeout is 3s)
// returnes number of bytes read, -1 in case of read timeout or -2...-5 in case of other errors
// Remarks:
// - older WiFi library versions & the Ethernet library return -1 if connection is still up but
// momentarily no data available and return 0 in case of EOF. Newer WiFi versions return 0 in
Expand All @@ -1289,16 +1304,58 @@ int SoapESP32::read(uint8_t *buf, size_t size, uint32_t timeout) {

// first some basic checks
if (!buf || !size || !m_clientDataConOpen) return -1; // clearly an error
if (!m_clientDataAvailable) return 0; // most probably EOF
if (!m_clientDataAvailable) return 0; // most probably EOF

int res = -1;
uint32_t start = millis();

while (1) {
//if (m_clientDataAvailable < size) size = m_clientDataAvailable;
claimSPI();
res = m_client->read(buf, size);
releaseSPI();
if (!m_clientDataChunked) {
claimSPI();
res = m_client->read(buf, size);
releaseSPI();
}
else {
// de-chunking of data required
if (m_ChunkCount <= 0) {
char tmpBuffer[10];

// next line contains chunk size
claimSPI();
int len = m_client->readBytesUntil('\n', tmpBuffer, sizeof(tmpBuffer) - 1);
releaseSPI();
if (len < 2) {
log_e("error reading chunk size");
return -2; // we expect at least 1 digit chunk size + '\r'
}
tmpBuffer[len-1] = 0; // clear '\r'
if (sscanf(tmpBuffer, "%x", &m_ChunkCount) != 1) {
log_e("error scanning chunk size");
return -3;
}
log_d("announced chunk size: 0x%x(%d)", m_ChunkCount, m_ChunkCount);
if (m_ChunkCount <= 0) {
return -4; // not necessarily an error...final chunk size can be 0
}
}
// read maximal till end of chunk
if (m_ChunkCount < size) size = m_ChunkCount;
claimSPI();
res = m_client->read(buf, size);
releaseSPI();
if (res > 0) {
m_ChunkCount -= res;
// check for end of chunk
if (m_ChunkCount == 0) {
// skip "\r\n" trailing each chunk
if (soapClientTimedRead(10) < 0 || soapClientTimedRead(10) < 0) {
log_e("error reading chunk trailing CR+LF");
return -5;
}
}
}
}
if (res > 0) {
// got at least 1 byte from server
m_clientDataAvailable -= res;
Expand Down Expand Up @@ -1337,6 +1394,8 @@ void SoapESP32::readStop()
log_d("client data connection to media server closed");
}
m_clientDataAvailable = 0;
m_clientDataChunked = false;
m_ChunkCount = 0;
}

//
Expand Down
5 changes: 3 additions & 2 deletions src/SoapESP32.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,14 @@ class SoapESP32
#endif
bool m_clientDataConOpen; // marker: socket open for reading file
size_t m_clientDataAvailable; // file read count
bool m_clientDataChunked; // some servers deliver chunked data when reading files
soapServerVect_t m_server ; // list of usable media servers in local network
int m_xmlChunkCount; // nr of bytes left of chunk (0 = end of chunk, next line delivers chunk size)
int m_ChunkCount; // nr of bytes left of chunk (0 = end of chunk, next line delivers chunk size)
eXmlReplaceState m_xmlReplaceState; // state machine for replacing XML entities
uint8_t m_xmlReplaceOffset;
char m_xmlReplaceBuffer[15]; // Fits longest string in replaceWith[] array

int soapClientTimedRead(void);
int soapClientTimedRead(unsigned long ms = 0);
bool soapUDPmulticast(unsigned int repeats = 0);
bool soapSSDPquery(std::vector<soapServer_t> *rcvd, int msWait);
bool soapGet(const IPAddress ip, const uint16_t port, const char *uri);
Expand Down

0 comments on commit 1b97ef3

Please sign in to comment.