forked from mfulz_github/qmk_firmware
Clean up HTTP webserver code in the Webserver project, so that it follows the uIP application structure guidelines and uses cleaner state machine based code.
This commit is contained in:
parent
331e8dece6
commit
bb1a036f09
|
@ -86,7 +86,7 @@ void DHCPApp_Callback(void)
|
|||
RequiredOptionList);
|
||||
|
||||
/* Send the DHCP DISCOVER packet */
|
||||
uip_send(AppData, AppDataSize);
|
||||
uip_udp_send(AppDataSize);
|
||||
|
||||
/* Reset the timeout timer, progress to next state */
|
||||
timer_reset(&DHCPTimer);
|
||||
|
@ -132,7 +132,7 @@ void DHCPApp_Callback(void)
|
|||
&AppState->DHCPOffer_Data.ServerIP);
|
||||
|
||||
/* Send the DHCP REQUEST packet */
|
||||
uip_send(AppData, AppDataSize);
|
||||
uip_udp_send(AppDataSize);
|
||||
|
||||
/* Reset the timeout timer, progress to next state */
|
||||
timer_reset(&DHCPTimer);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
* this will serve out files to HTTP clients.
|
||||
*/
|
||||
|
||||
#define INCLUDE_FROM_HTTPSERVERAPP_C
|
||||
#include "HTTPServerApp.h"
|
||||
|
||||
/** HTTP server response header, for transmission before the page contents. This indicates to the host that a page exists at the
|
||||
|
@ -92,159 +93,199 @@ void WebserverApp_Init(void)
|
|||
*/
|
||||
void WebserverApp_Callback(void)
|
||||
{
|
||||
uip_tcp_appstate_t* const AppState = &uip_conn->appstate;
|
||||
char* AppData = (char*)uip_appdata;
|
||||
uint16_t AppDataSize = 0;
|
||||
uip_tcp_appstate_t* const AppState = &uip_conn->appstate;
|
||||
|
||||
if (uip_aborted() || uip_timedout() || uip_closed())
|
||||
{
|
||||
/* Check if the open file needs to be closed */
|
||||
/* Connection is being terminated for some reason - close file handle if open */
|
||||
if (AppState->FileOpen)
|
||||
{
|
||||
f_close(&AppState->FileHandle);
|
||||
AppState->FileOpen = false;
|
||||
}
|
||||
|
||||
AppState->PrevState = WEBSERVER_STATE_Closed;
|
||||
AppState->CurrentState = WEBSERVER_STATE_Closed;
|
||||
|
||||
return;
|
||||
|
||||
/* Lock to the closed state so that no further processing will occur on the connection */
|
||||
AppState->CurrentState = WEBSERVER_STATE_Closed;
|
||||
AppState->NextState = WEBSERVER_STATE_Closed;
|
||||
}
|
||||
else if (uip_connected())
|
||||
|
||||
if (uip_connected())
|
||||
{
|
||||
/* New connection - initialize connection state values */
|
||||
AppState->PrevState = WEBSERVER_STATE_OpenRequestedFile;
|
||||
AppState->CurrentState = WEBSERVER_STATE_OpenRequestedFile;
|
||||
AppState->FileOpen = false;
|
||||
AppState->CurrentState = WEBSERVER_STATE_OpenRequestedFile;
|
||||
AppState->NextState = WEBSERVER_STATE_OpenRequestedFile;
|
||||
AppState->FileOpen = false;
|
||||
AppState->ACKedFilePos = 0;
|
||||
AppState->SentChunkSize = 0;
|
||||
}
|
||||
else if (uip_rexmit())
|
||||
|
||||
if (uip_acked())
|
||||
{
|
||||
/* Re-try last state */
|
||||
AppState->CurrentState = AppState->PrevState;
|
||||
/* Add the amount of ACKed file data to the total sent file bytes counter */
|
||||
AppState->ACKedFilePos += AppState->SentChunkSize;
|
||||
|
||||
/* Progress to the next state once the current state's data has been ACKed */
|
||||
AppState->CurrentState = AppState->NextState;
|
||||
}
|
||||
|
||||
switch (AppState->CurrentState)
|
||||
|
||||
if (uip_rexmit() || uip_newdata() || uip_acked() || uip_connected() || uip_poll())
|
||||
{
|
||||
case WEBSERVER_STATE_OpenRequestedFile:
|
||||
/* Wait for the packet containing the request header */
|
||||
if (uip_newdata())
|
||||
{
|
||||
char* RequestToken = strtok(AppData, " ");
|
||||
|
||||
/* Must be a GET request, abort otherwise */
|
||||
if (strcmp(RequestToken, "GET") != 0)
|
||||
{
|
||||
uip_abort();
|
||||
break;
|
||||
}
|
||||
|
||||
char* RequestedFileName = strtok(NULL, " ");
|
||||
|
||||
/* If the requested filename has more that just the leading '/' path in it, copy it over */
|
||||
if (strlen(RequestedFileName) > 1)
|
||||
strncpy(AppState->FileName, &RequestedFileName[1], (sizeof(AppState->FileName) - 1));
|
||||
else
|
||||
strcpy(AppState->FileName, "index.htm");
|
||||
|
||||
/* Ensure filename is null-terminated */
|
||||
AppState->FileName[(sizeof(AppState->FileName) - 1)] = 0x00;
|
||||
|
||||
/* Try to open the file from the Dataflash disk */
|
||||
AppState->FileOpen = (f_open(&AppState->FileHandle, AppState->FileName, FA_OPEN_EXISTING | FA_READ) == FR_OK);
|
||||
AppState->CurrentFilePos = 0;
|
||||
|
||||
AppState->PrevState = WEBSERVER_STATE_OpenRequestedFile;
|
||||
AppState->CurrentState = WEBSERVER_STATE_SendResponseHeader;
|
||||
}
|
||||
|
||||
break;
|
||||
case WEBSERVER_STATE_SendResponseHeader:
|
||||
/* Determine what HTTP header should be sent to the client */
|
||||
if (AppState->FileOpen)
|
||||
{
|
||||
AppDataSize = strlen_P(HTTP200Header);
|
||||
strncpy_P(AppData, HTTP200Header, AppDataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppDataSize = strlen_P(HTTP404Header);
|
||||
strncpy_P(AppData, HTTP404Header, AppDataSize);
|
||||
}
|
||||
|
||||
AppState->PrevState = WEBSERVER_STATE_SendResponseHeader;
|
||||
AppState->CurrentState = WEBSERVER_STATE_SendMIMETypeHeader;
|
||||
break;
|
||||
case WEBSERVER_STATE_SendMIMETypeHeader:
|
||||
/* File must have been found and opened for MIME header to be sent */
|
||||
if (AppState->FileOpen)
|
||||
{
|
||||
char* Extension = strpbrk(AppState->FileName, ".");
|
||||
|
||||
/* Check to see if a file extension was found for the requested filename */
|
||||
if (Extension != NULL)
|
||||
{
|
||||
/* Look through the MIME type list, copy over the required MIME type if found */
|
||||
for (int i = 0; i < (sizeof(MIMETypes) / sizeof(MIMETypes[0])); i++)
|
||||
{
|
||||
if (strcmp_P(&Extension[1], MIMETypes[i].Extension) == 0)
|
||||
{
|
||||
AppDataSize = strlen_P(MIMETypes[i].MIMEType);
|
||||
strncpy_P(AppData, MIMETypes[i].MIMEType, AppDataSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if a MIME type was found and copied to the output buffer */
|
||||
if (!(AppDataSize))
|
||||
{
|
||||
/* MIME type not found - copy over the default MIME type */
|
||||
AppDataSize = strlen_P(DefaultMIMEType);
|
||||
strncpy_P(AppData, DefaultMIMEType, AppDataSize);
|
||||
}
|
||||
|
||||
/* Add the end-of line terminator and end-of-headers terminator after the MIME type */
|
||||
strncpy(&AppData[AppDataSize], "\r\n\r\n", sizeof("\r\n\r\n"));
|
||||
AppDataSize += (sizeof("\r\n\r\n") - 1);
|
||||
}
|
||||
|
||||
AppState->PrevState = WEBSERVER_STATE_SendMIMETypeHeader;
|
||||
AppState->CurrentState = WEBSERVER_STATE_SendData;
|
||||
break;
|
||||
case WEBSERVER_STATE_SendData:
|
||||
/* If end of file/file not open, progress to the close state */
|
||||
if (!(AppState->FileOpen) && !(uip_rexmit()))
|
||||
{
|
||||
f_close(&AppState->FileHandle);
|
||||
switch (AppState->CurrentState)
|
||||
{
|
||||
case WEBSERVER_STATE_OpenRequestedFile:
|
||||
Webserver_OpenRequestedFile();
|
||||
break;
|
||||
case WEBSERVER_STATE_SendResponseHeader:
|
||||
Webserver_SendResponseHeader();
|
||||
break;
|
||||
case WEBSERVER_STATE_SendMIMETypeHeader:
|
||||
Webserver_SendMIMETypeHeader();
|
||||
break;
|
||||
case WEBSERVER_STATE_SendData:
|
||||
Webserver_SendData();
|
||||
break;
|
||||
case WEBSERVER_STATE_Closing:
|
||||
uip_close();
|
||||
|
||||
AppState->NextState = WEBSERVER_STATE_Closed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppState->PrevState = WEBSERVER_STATE_Closed;
|
||||
AppState->CurrentState = WEBSERVER_STATE_Closed;
|
||||
/** HTTP Server State handler for the Request Process state. This state manages the processing of incomming HTTP
|
||||
* GET requests to the server from the receiving HTTP client.
|
||||
*/
|
||||
static void Webserver_OpenRequestedFile(void)
|
||||
{
|
||||
uip_tcp_appstate_t* const AppState = &uip_conn->appstate;
|
||||
char* AppData = (char*)uip_appdata;
|
||||
|
||||
/* No HTTP header received from the client, abort processing */
|
||||
if (!(uip_newdata()))
|
||||
return;
|
||||
|
||||
char* RequestToken = strtok(AppData, " ");
|
||||
|
||||
/* Must be a GET request, abort otherwise */
|
||||
if (strcmp(RequestToken, "GET") != 0)
|
||||
{
|
||||
uip_abort();
|
||||
return;
|
||||
}
|
||||
|
||||
char* RequestedFileName = strtok(NULL, " ");
|
||||
|
||||
/* If the requested filename has more that just the leading '/' path in it, copy it over */
|
||||
if (strlen(RequestedFileName) > 1)
|
||||
strncpy(AppState->FileName, &RequestedFileName[1], (sizeof(AppState->FileName) - 1));
|
||||
else
|
||||
strcpy(AppState->FileName, "index.htm");
|
||||
|
||||
/* Ensure filename is null-terminated */
|
||||
AppState->FileName[(sizeof(AppState->FileName) - 1)] = 0x00;
|
||||
|
||||
/* Try to open the file from the Dataflash disk */
|
||||
AppState->FileOpen = (f_open(&AppState->FileHandle, AppState->FileName, FA_OPEN_EXISTING | FA_READ) == FR_OK);
|
||||
|
||||
/* Lock to the SendResponseHeader state until connection terminated */
|
||||
AppState->CurrentState = WEBSERVER_STATE_SendResponseHeader;
|
||||
AppState->NextState = WEBSERVER_STATE_SendResponseHeader;
|
||||
}
|
||||
|
||||
/** HTTP Server State handler for the HTTP Response Header Send state. This state manages the transmission of
|
||||
* the HTTP response header to the receiving HTTP client.
|
||||
*/
|
||||
static void Webserver_SendResponseHeader(void)
|
||||
{
|
||||
uip_tcp_appstate_t* const AppState = &uip_conn->appstate;
|
||||
char* AppData = (char*)uip_appdata;
|
||||
|
||||
char* HeaderToSend;
|
||||
uint16_t HeaderLength;
|
||||
|
||||
/* Determine what HTTP header should be sent to the client */
|
||||
if (AppState->FileOpen)
|
||||
{
|
||||
HeaderToSend = HTTP200Header;
|
||||
AppState->NextState = WEBSERVER_STATE_SendMIMETypeHeader;
|
||||
}
|
||||
else
|
||||
{
|
||||
HeaderToSend = HTTP404Header;
|
||||
AppState->NextState = WEBSERVER_STATE_Closing;
|
||||
}
|
||||
|
||||
HeaderLength = strlen_P(HeaderToSend);
|
||||
strncpy_P(AppData, HeaderToSend, HeaderLength);
|
||||
uip_send(AppData, HeaderLength);
|
||||
}
|
||||
|
||||
/** HTTP Server State handler for the MIME Header Send state. This state manages the transmission of the file
|
||||
* MIME type header for the requested file to the receiving HTTP client.
|
||||
*/
|
||||
static void Webserver_SendMIMETypeHeader(void)
|
||||
{
|
||||
uip_tcp_appstate_t* const AppState = &uip_conn->appstate;
|
||||
char* AppData = (char*)uip_appdata;
|
||||
|
||||
char* Extension = strpbrk(AppState->FileName, ".");
|
||||
uint16_t MIMEHeaderLength = 0;
|
||||
|
||||
/* Check to see if a file extension was found for the requested filename */
|
||||
if (Extension != NULL)
|
||||
{
|
||||
/* Look through the MIME type list, copy over the required MIME type if found */
|
||||
for (int i = 0; i < (sizeof(MIMETypes) / sizeof(MIMETypes[0])); i++)
|
||||
{
|
||||
if (strcmp_P(&Extension[1], MIMETypes[i].Extension) == 0)
|
||||
{
|
||||
MIMEHeaderLength = strlen_P(MIMETypes[i].MIMEType);
|
||||
strncpy_P(AppData, MIMETypes[i].MIMEType, MIMEHeaderLength);
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t MaxSegSize = uip_mss();
|
||||
|
||||
/* Return file pointer to the last ACKed position */
|
||||
f_lseek(&AppState->FileHandle, AppState->CurrentFilePos);
|
||||
|
||||
/* Read the next chunk of data from the open file */
|
||||
f_read(&AppState->FileHandle, AppData, MaxSegSize, &AppDataSize);
|
||||
|
||||
/* If we are not re-transmitting a lost segment, advance file position */
|
||||
if (uip_acked() && !(uip_rexmit()))
|
||||
{
|
||||
AppState->FileOpen = (AppDataSize > 0);
|
||||
AppState->CurrentFilePos += AppDataSize;
|
||||
}
|
||||
|
||||
/* Stay in the SendData state if retransmission is required until all data sent */
|
||||
AppState->PrevState = WEBSERVER_STATE_SendData;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If data has been loaded into the application buffer by the server, send it to the client */
|
||||
if (AppDataSize)
|
||||
uip_send(AppData, AppDataSize);
|
||||
/* Check if a MIME type was found and copied to the output buffer */
|
||||
if (!(MIMEHeaderLength))
|
||||
{
|
||||
/* MIME type not found - copy over the default MIME type */
|
||||
MIMEHeaderLength = strlen_P(DefaultMIMEType);
|
||||
strncpy_P(AppData, DefaultMIMEType, MIMEHeaderLength);
|
||||
}
|
||||
|
||||
/* Add the end-of line terminator and end-of-headers terminator after the MIME type */
|
||||
strncpy(&AppData[MIMEHeaderLength], "\r\n\r\n", sizeof("\r\n\r\n"));
|
||||
MIMEHeaderLength += (sizeof("\r\n\r\n") - 1);
|
||||
|
||||
/* Send the MIME header to the receiving client */
|
||||
uip_send(AppData, MIMEHeaderLength);
|
||||
|
||||
/* When the MIME header is ACKed, progress to the data send stage */
|
||||
AppState->NextState = WEBSERVER_STATE_SendData;
|
||||
}
|
||||
|
||||
/** HTTP Server State handler for the Data Send state. This state manages the transmission of file chunks
|
||||
* to the receiving HTTP client.
|
||||
*/
|
||||
static void Webserver_SendData(void)
|
||||
{
|
||||
uip_tcp_appstate_t* const AppState = &uip_conn->appstate;
|
||||
char* AppData = (char*)uip_appdata;
|
||||
|
||||
/* Must determine the maximum segment size to determine maximum file chunk size */
|
||||
uint16_t MaxSegmentSize = uip_mss();
|
||||
|
||||
/* Return file pointer to the last ACKed position */
|
||||
f_lseek(&AppState->FileHandle, AppState->ACKedFilePos);
|
||||
|
||||
/* Read the next chunk of data from the open file */
|
||||
f_read(&AppState->FileHandle, AppData, MaxSegmentSize, &AppState->SentChunkSize);
|
||||
|
||||
/* Send the next file chunk to the receiving client */
|
||||
uip_send(AppData, AppState->SentChunkSize);
|
||||
|
||||
/* Check if we are at the last chunk of the file, if so next ACK should close the connection */
|
||||
AppState->NextState = (MaxSegmentSize != AppState->SentChunkSize) ? WEBSERVER_STATE_Closing : WEBSERVER_STATE_SendData;
|
||||
}
|
||||
|
|
|
@ -50,8 +50,9 @@
|
|||
WEBSERVER_STATE_OpenRequestedFile, /** Currently opening requested file */
|
||||
WEBSERVER_STATE_SendResponseHeader, /**< Currently sending HTTP response headers to the client */
|
||||
WEBSERVER_STATE_SendMIMETypeHeader, /**< Currently sending HTTP MIME type header to the client */
|
||||
WEBSERVER_STATE_SendData, /**< Currently sending HTTP page data to the client */
|
||||
WEBSERVER_STATE_Closed, /**< Connection closed after all data sent */
|
||||
WEBSERVER_STATE_SendData, /**< Currently sending HTTP page data to the client */
|
||||
WEBSERVER_STATE_Closing, /**< Ready to close the connection to the client */
|
||||
WEBSERVER_STATE_Closed, /**< Connection closed after all data sent */
|
||||
};
|
||||
|
||||
/* Type Defines: */
|
||||
|
@ -70,4 +71,11 @@
|
|||
void WebserverApp_Init(void);
|
||||
void WebserverApp_Callback(void);
|
||||
|
||||
#if defined(INCLUDE_FROM_HTTPSERVERAPP_C)
|
||||
static void Webserver_OpenRequestedFile(void);
|
||||
static void Webserver_SendResponseHeader(void);
|
||||
static void Webserver_SendMIMETypeHeader(void);
|
||||
static void Webserver_SendData(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -52,7 +52,7 @@ void uIPManagement_Init(void)
|
|||
{
|
||||
/* uIP Timing Initialization */
|
||||
clock_init();
|
||||
timer_set(&ConnectionTimer, CLOCK_SECOND / 2);
|
||||
timer_set(&ConnectionTimer, CLOCK_SECOND / 10);
|
||||
timer_set(&ARPTimer, CLOCK_SECOND * 10);
|
||||
|
||||
/* uIP Stack Initialization */
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t PrevState;
|
||||
uint8_t CurrentState;
|
||||
uint8_t CurrentState;
|
||||
uint8_t NextState;
|
||||
|
||||
FIL FileHandle;
|
||||
char FileName[50];
|
||||
bool FileOpen;
|
||||
uint32_t CurrentFilePos;
|
||||
char FileName[30];
|
||||
FIL FileHandle;
|
||||
bool FileOpen;
|
||||
uint32_t ACKedFilePos;
|
||||
uint16_t SentChunkSize;
|
||||
} uip_tcp_appstate_t;
|
||||
|
||||
typedef struct
|
||||
|
|
Loading…
Reference in New Issue