Commit bb1a036f authored by Dean Camera's avatar Dean Camera
Browse files

Clean up HTTP webserver code in the Webserver project, so that it follows the...

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.
parent 331e8dec
......@@ -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;
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();
/* 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;
}
AppState->NextState = WEBSERVER_STATE_Closed;
break;
}
}
}
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);
}
/** 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, " ");
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);
uip_close();
/* Must be a GET request, abort otherwise */
if (strcmp(RequestToken, "GET") != 0)
{
uip_abort();
return;
}
AppState->PrevState = WEBSERVER_STATE_Closed;
AppState->CurrentState = WEBSERVER_STATE_Closed;
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");
uint16_t MaxSegSize = uip_mss();
/* Return file pointer to the last ACKed position */
f_lseek(&AppState->FileHandle, AppState->CurrentFilePos);
/* 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;
/* Read the next chunk of data from the open file */
f_read(&AppState->FileHandle, AppData, MaxSegSize, &AppDataSize);
char* Extension = strpbrk(AppState->FileName, ".");
uint16_t MIMEHeaderLength = 0;
/* If we are not re-transmitting a lost segment, advance file position */
if (uip_acked() && !(uip_rexmit()))
/* 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)
{
AppState->FileOpen = (AppDataSize > 0);
AppState->CurrentFilePos += AppDataSize;
MIMEHeaderLength = strlen_P(MIMETypes[i].MIMEType);
strncpy_P(AppData, MIMETypes[i].MIMEType, MIMEHeaderLength);
break;
}
/* Stay in the SendData state if retransmission is required until all data sent */
AppState->PrevState = WEBSERVER_STATE_SendData;
}
}
break;
/* 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;
}
/* If data has been loaded into the application buffer by the server, send it to the client */
if (AppDataSize)
uip_send(AppData, AppDataSize);
/** 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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment