Reworked the way the main send buffer buffer is allocated. This is not done on the stack anymore because it led to a nasty stackoverflow. Changed some debug statements, finished the sendDirectoryListing method which is now working fine. Txt files are now sent as text/plain data.
This commit is contained in:
parent
16cae233bb
commit
6130e8cd71
@ -7,7 +7,7 @@
|
||||
#include "HttpConstants.h"
|
||||
#include "utilities.h"
|
||||
//#define DEBUG_WEBS
|
||||
#define READ_WRITE_BUFFER_SIZE 2000
|
||||
#define READ_WRITE_BUFFER_SIZE 5000
|
||||
|
||||
template <typename T>
|
||||
class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
@ -562,8 +562,7 @@ class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
{
|
||||
File pageToSend;
|
||||
char *filePath(NULL), *header(NULL);
|
||||
uint8_t sendBuffer[READ_WRITE_BUFFER_SIZE];
|
||||
int readBytes(0);
|
||||
size_t readBytes(0);
|
||||
//We check what kind of http verb it is
|
||||
switch(client->_httpRequestData.HRM)
|
||||
{
|
||||
@ -576,8 +575,7 @@ class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
}
|
||||
|
||||
#ifdef DEBUG_WEBS
|
||||
Serial.print("FILE PATH : ");
|
||||
Serial.println(filePath);
|
||||
Serial.printf("File path : #%s#\n",filePath);
|
||||
#endif
|
||||
|
||||
pageToSend = _sdClass->open(filePath);
|
||||
@ -588,7 +586,7 @@ class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
{
|
||||
char *response(NULL);
|
||||
|
||||
response = (char *) malloc(sizeof(char) * (strlen_P((PGM_P)F("Resource : not found on this server")) + strlen(client->_httpRequestData.httpResource) + 1));
|
||||
response = (char *) malloc(sizeof(char) * (36 + strlen(client->_httpRequestData.httpResource) + 1));
|
||||
if(response == NULL)
|
||||
{
|
||||
sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for the response");
|
||||
@ -611,10 +609,28 @@ class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
|
||||
if(pageToSend.isDirectory()) //TODO : List the files present in the directory
|
||||
{
|
||||
//sendDirectoryListing(client, pageToSend); //Sends the content of the directory like what apache does by default. CRASHING FOR NOW, needs to be checked further.
|
||||
pageToSend.close();
|
||||
//If the ressource wasn't terminated by a '/' then we issue a 301 Moved Permanently, else all's good
|
||||
if(client->_httpRequestData.httpResource[strlen(client->_httpRequestData.httpResource)-1] != '/')
|
||||
{
|
||||
uint16_t locationLength(strlen(client->_httpRequestData.httpResource) + 7/*http://*/ + 16/*ip addr + :*/ + 5/*port*/ + 2);
|
||||
char *location = (char *)malloc(locationLength * sizeof(char));
|
||||
char *message = (char *)malloc((27 + locationLength + 1) * sizeof(char));
|
||||
if(!location || !message)
|
||||
{
|
||||
sendInfoResponse(HTTP_CODE::HTTP_CODE_INTERNAL_SERVER_ERROR, client, "Failed to allocate memory for the location or message");
|
||||
pageToSend.close();
|
||||
return false;
|
||||
}
|
||||
sprintf(location, "http://%s:%u%s/",client->_client.localIP().toString().c_str(), TCPServer<T>::getPort(), client->_httpRequestData.httpResource);
|
||||
sprintf(message, "The document has moved to %s.", location);
|
||||
sendInfoResponse(HTTP_CODE::HTTP_CODE_MOVED_PERMANENTLY, client, message, location);
|
||||
|
||||
free(location);free(message);
|
||||
}
|
||||
else
|
||||
sendDirectoryListing(client, pageToSend); //Sends the content of the directory like what apache does by default.
|
||||
|
||||
sendInfoResponse(HTTP_CODE::HTTP_CODE_FORBIDDEN, client, "The file you want to access is a folder");
|
||||
pageToSend.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -667,14 +683,34 @@ class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
|
||||
if(pageToSend.available() && client->_fileSentBytes < client->_rangeData._rangeEnd + 1) // File is done sending once the whole file was sent or the partial part is done sending
|
||||
{
|
||||
readBytes = pageToSend.read(sendBuffer,READ_WRITE_BUFFER_SIZE);
|
||||
size_t mallocAcceptedSize(0);
|
||||
/*
|
||||
The original issue was that a buffer of 2048 bytes was allocated on the stack causing a hard to track down stack overflow.
|
||||
Possible solutions I cam up with :
|
||||
1) Create a statically allocated buffer which would live in the BSS (Block Started by a Symbol) RAM segment.
|
||||
Advantage : buffer is allocated once and ready to be used immediately.
|
||||
Drawback : takes space in RAM (lost space) even if the buffer isn't used.
|
||||
2) Create a dynamically allocated buffer using malloc and friends which would live in the heap RAM segment - SOLUTION I IMPLEMENTED
|
||||
Advantage : buffer is taking RAM only when needed and can be freed afterwards.
|
||||
Drawback : Allocating and deallocating heap memory a lot is costly in MCU time, leads to RAM fragmentation and could potentially fail...
|
||||
*/
|
||||
uint8_t *sendBuffer = (uint8_t*)mallocWithFallback(READ_WRITE_BUFFER_SIZE * sizeof(uint8_t), &mallocAcceptedSize);
|
||||
|
||||
if(!sendBuffer)
|
||||
{
|
||||
pageToSend.close();
|
||||
return false; //Not clean but what else can I do. Should never happen anyway.
|
||||
}
|
||||
|
||||
readBytes = pageToSend.read(sendBuffer,mallocAcceptedSize);
|
||||
client->_client.write(sendBuffer, readBytes);
|
||||
|
||||
|
||||
free(sendBuffer);
|
||||
|
||||
#ifdef DEBUG_WEBS
|
||||
Serial.print("BYTES SENT : ");
|
||||
Serial.println(readBytes);
|
||||
Serial.printf("Bytes sent : %u, got allocated buffer size : %u - free stack : %u\n", readBytes, mallocAcceptedSize, ESP.getFreeContStack());
|
||||
#endif
|
||||
|
||||
|
||||
client->_fileSentBytes += readBytes; //We save the number of bytes sent so that we can reopen the file to this position later on.
|
||||
}
|
||||
else
|
||||
@ -702,7 +738,7 @@ class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
|
||||
void sendDirectoryListing(T *client, File& pageToSend)
|
||||
{
|
||||
/*sendHTTPHeader(&client->_client, HttpConstants::httpMIMETypeToString(HttpConstants::TEXT_HTML));
|
||||
sendHTTPHeader(&client->_client, HttpConstants::httpMIMETypeToString(HttpConstants::TEXT_HTML));
|
||||
client->_client.printf_P(PSTR( "<!DOCTYPE HTML>\r\n\
|
||||
<html>\r\n\
|
||||
<head>\r\n\
|
||||
@ -711,41 +747,66 @@ class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
<body>\r\n\
|
||||
<h1>Index of %s</h1>\r\n\
|
||||
<table>\r\n\
|
||||
<tr><th>Name</th><th>Created</th><th>Last modified</th><th>Size</th></tr>\r\n\
|
||||
<tr><th colspan=\"4\"><hr></th></tr>\r\n")
|
||||
, client->_httpRequestData.httpResource, client->_httpRequestData.httpResource);*/
|
||||
//File nextFile;
|
||||
for (;;)
|
||||
{
|
||||
//if(!(nextFile = pageToSend.openNextFile()))break;
|
||||
File nextFile = pageToSend.openNextFile();
|
||||
if (!nextFile) //No more files in the directory
|
||||
break;
|
||||
//time_t creationTime = nextFile.getCreationTime(), lastModified = nextFile.getLastWrite();
|
||||
<tr><th>Type</th><th>Name</th><th>Created</th><th>Last modified</th><th>Size</th></tr>\r\n\
|
||||
<tr><th colspan=\"5\"><hr></th></tr>\r\n")
|
||||
, client->_httpRequestData.httpResource, client->_httpRequestData.httpResource);
|
||||
|
||||
if(strlen(client->_httpRequestData.httpResource) > 1) //Then we are not at the root of the WEBServer's directory.
|
||||
{
|
||||
char *rsrcCopy = strdup(client->_httpRequestData.httpResource);
|
||||
if(rsrcCopy)
|
||||
{
|
||||
char *lastSlash(strrchr(rsrcCopy, '/'));
|
||||
if(lastSlash)*lastSlash = '\0';
|
||||
lastSlash = strrchr(rsrcCopy, '/');
|
||||
if(lastSlash)*lastSlash = '\0';
|
||||
|
||||
/*client->_client.printf_P(PSTR("<tr><td><a href=\"%s\">%s</a></td><td align=\"right\">2021-07-09 15:37 </td><td align=\"right\">2021-07-09 15:37 </td><td align=\"right\"> %u</td></tr>\r\n"),
|
||||
client->_client.printf_P(PSTR("<tr><td>[DIR]</td><td><a href=\"%s/\">Parent Directory</a></td><td> - </td><td align=\"right\"> - </td><td> </td></tr>\r\n"), rsrcCopy);
|
||||
free(rsrcCopy);
|
||||
}
|
||||
}
|
||||
|
||||
File nextFile;
|
||||
for(;;)
|
||||
{
|
||||
if(!(nextFile = pageToSend.openNextFile()))break;
|
||||
|
||||
char zero_prepended[4][3] = {"","","",""};
|
||||
time_t rawCreationTime(nextFile.getCreationTime()), rawLastModifiedTime(nextFile.getLastWrite());
|
||||
tm creationTime(*localtime(&rawCreationTime)), lastModifiedTime(*localtime(&rawLastModifiedTime));
|
||||
|
||||
client->_client.printf_P(PSTR("<tr><td>%s</td><td><a href=\"%s\">%s</a></td><td align=\"right\">%d-%d-%d %s:%s</td><td align=\"right\">%d-%d-%d %s:%s</td><td align=\"right\">%.1fK</td></tr>\r\n"),
|
||||
nextFile.isDirectory() ? "[DIR]":"[FILE]",
|
||||
nextFile.name(),
|
||||
nextFile.name(),
|
||||
nextFile.size()
|
||||
);*/
|
||||
Serial.printf("%s %u\r\n", nextFile.name(), nextFile.size());
|
||||
creationTime.tm_year + 1900, creationTime.tm_mon + 1, creationTime.tm_mday, dateTimeFormater(zero_prepended[0], creationTime.tm_hour, '0'), dateTimeFormater(zero_prepended[1], creationTime.tm_min, '0'),
|
||||
lastModifiedTime.tm_year + 1900, lastModifiedTime.tm_mon + 1, lastModifiedTime.tm_mday, dateTimeFormater(zero_prepended[2], lastModifiedTime.tm_hour, '0'), dateTimeFormater(zero_prepended[3], lastModifiedTime.tm_min, '0'),
|
||||
nextFile.size() / 1024.0
|
||||
);
|
||||
|
||||
#ifdef DEBUG_WEBS
|
||||
Serial.printf("File name : %s\nFile size : %u\nFree stack : %u\n", nextFile.name(), nextFile.size(), ESP.getFreeContStack());
|
||||
#endif
|
||||
nextFile.close();
|
||||
}
|
||||
|
||||
/*client->_client.printf_P(PSTR( "<tr><th colspan=\"4\"><hr></th></tr>\r\n\
|
||||
client->_client.printf_P(PSTR( "<tr><th colspan=\"5\"><hr></th></tr>\r\n\
|
||||
</table>\r\n\
|
||||
<address>SAB WEBServer, Version %s at %s Port %u</address>\r\n\
|
||||
</body>\r\n\
|
||||
</html>"), "1.0.0", "", TCPServer<T>::getPort()); //TODO get IP*/
|
||||
</html>"), "1.0.0", client->_client.localIP().toString().c_str(), TCPServer<T>::getPort());
|
||||
}
|
||||
|
||||
/*Static helper methods*/
|
||||
|
||||
static void sendInfoResponse(HTTP_CODE http_code, T *client, const char *message)
|
||||
static void sendInfoResponse(HTTP_CODE http_code, T *client, const char *message, const char *location = nullptr)
|
||||
{
|
||||
char codeLiteral[100];
|
||||
switch(http_code)
|
||||
{
|
||||
case HTTP_CODE_MOVED_PERMANENTLY:
|
||||
strcpy_P(codeLiteral,PSTR("Moved Permanently"));
|
||||
break;
|
||||
case HTTP_CODE_BAD_REQUEST:
|
||||
strcpy_P(codeLiteral,PSTR("Bad Request"));
|
||||
break;
|
||||
@ -768,7 +829,13 @@ class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
strcpy_P(codeLiteral,PSTR("Error Not Defined"));
|
||||
break;
|
||||
}
|
||||
client->_client.printf_P(PSTR("HTTP/1.1 %d %s\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n<h1>Error %d</h1><p>%s</p>\r\n</html>"), http_code, codeLiteral, strlen(message) + 56 + (http_code != 0 ? 3:1), http_code , message);
|
||||
client->_client.printf_P(PSTR("HTTP/1.1 %d %s\r\n"), http_code, codeLiteral);
|
||||
|
||||
|
||||
if(http_code == HTTP_CODE_MOVED_PERMANENTLY)
|
||||
client->_client.printf_P(PSTR("Location: %s\r\n"), location);
|
||||
|
||||
client->_client.printf_P(PSTR("Content-Type: text/html\r\nContent-Length: %d\r\n\r\n<!DOCTYPE HTML>\r\n<html>\r\n<h1>Error %d</h1><p>%s</p>\r\n</html>"), strlen(message) + 56 + (http_code != 0 ? 3:1), http_code , message);
|
||||
}
|
||||
|
||||
static HttpRequestMethod getHttpVerbEnumValue(const char *parseBuffer)
|
||||
@ -802,6 +869,7 @@ class WEBServer : public TCPServer<T>, public HttpConstants
|
||||
else if(strcmp(extension, "jpg") == 0) return IMAGE_JPEG;
|
||||
else if(strcmp(extension, "ico") == 0) return IMAGE_X_ICO;
|
||||
else if(strcmp(extension, "mp3") == 0) return AUDIO_MPEG;
|
||||
else if(strcmp(extension, "txt") == 0) return TEXT_PLAIN;
|
||||
else return UNKNOWN_MIME;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user