@ -34,6 +34,7 @@
* this will serve out files to HTTP clients .
* this will serve out files to HTTP clients .
*/
*/
# define INCLUDE_FROM_HTTPSERVERAPP_C
# include "HTTPServerApp.h"
# include "HTTPServerApp.h"
/** HTTP server response header, for transmission before the page contents. This indicates to the host that a page exists at the
/** HTTP server response header, for transmission before the page contents. This indicates to the host that a page exists at the
@ -93,49 +94,84 @@ void WebserverApp_Init(void)
void WebserverApp_Callback ( void )
void WebserverApp_Callback ( void )
{
{
uip_tcp_appstate_t * const AppState = & uip_conn - > appstate ;
uip_tcp_appstate_t * const AppState = & uip_conn - > appstate ;
char * AppData = ( char * ) uip_appdata ;
uint16_t AppDataSize = 0 ;
if ( uip_aborted ( ) | | uip_timedout ( ) | | uip_closed ( ) )
if ( uip_aborted ( ) | | uip_timedout ( ) | | uip_closed ( ) )
{
{
/* C heck if the open file needs to be closed */
/* C onnection is being terminated for some reason - close file handle if open */
if ( AppState - > FileOpen )
if ( AppState - > FileOpen )
{
{
f_close ( & AppState - > FileHandle ) ;
f_close ( & AppState - > FileHandle ) ;
AppState - > FileOpen = false ;
AppState - > FileOpen = false ;
}
}
AppState - > PrevState = WEBSERVER_STATE_Closed ;
/* Lock to the closed state so that no further processing will occur on the connection */
AppState - > CurrentState = WEBSERVER_STATE_Closed ;
AppState - > CurrentState = WEBSERVER_STATE_Closed ;
AppState - > NextState = WEBSERVER_STATE_Closed ;
return ;
}
}
else if ( uip_connected ( ) )
if ( uip_connected ( ) )
{
{
/* New connection - initialize connection state values */
/* New connection - initialize connection state values */
AppState - > PrevState = WEBSERVER_STATE_OpenRequestedFile ;
AppState - > CurrentState = WEBSERVER_STATE_OpenRequestedFile ;
AppState - > CurrentState = WEBSERVER_STATE_OpenRequestedFile ;
AppState - > NextState = WEBSERVER_STATE_OpenRequestedFile ;
AppState - > FileOpen = false ;
AppState - > FileOpen = false ;
AppState - > ACKedFilePos = 0 ;
AppState - > SentChunkSize = 0 ;
}
}
else if ( uip_rexmit ( ) )
if ( uip_acked ( ) )
{
{
/* Re-try last state */
/* Add the amount of ACKed file data to the total sent file bytes counter */
AppState - > CurrentState = AppState - > PrevState ;
AppState - > ACKedFilePos + = AppState - > SentChunkSize ;
/* Progress to the next state once the current state's data has been ACKed */
AppState - > CurrentState = AppState - > NextState ;
}
}
if ( uip_rexmit ( ) | | uip_newdata ( ) | | uip_acked ( ) | | uip_connected ( ) | | uip_poll ( ) )
{
switch ( AppState - > CurrentState )
switch ( AppState - > CurrentState )
{
{
case WEBSERVER_STATE_OpenRequestedFile :
case WEBSERVER_STATE_OpenRequestedFile :
/* Wait for the packet containing the request header */
Webserver_OpenRequestedFile ( ) ;
if ( uip_newdata ( ) )
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 ;
}
}
}
/** 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 , " " ) ;
char * RequestToken = strtok ( AppData , " " ) ;
/* Must be a GET request, abort otherwise */
/* Must be a GET request, abort otherwise */
if ( strcmp ( RequestToken , " GET " ) ! = 0 )
if ( strcmp ( RequestToken , " GET " ) ! = 0 )
{
{
uip_abort ( ) ;
uip_abort ( ) ;
break ;
return ;
}
}
char * RequestedFileName = strtok ( NULL , " " ) ;
char * RequestedFileName = strtok ( NULL , " " ) ;
@ -151,34 +187,50 @@ void WebserverApp_Callback(void)
/* Try to open the file from the Dataflash disk */
/* 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 - > FileOpen = ( f_open ( & AppState - > FileHandle , AppState - > FileName , FA_OPEN_EXISTING | FA_READ ) = = FR_OK ) ;
AppState - > CurrentFilePos = 0 ;
AppState - > PrevState = WEBSERVER_STATE_OpenRequestedFile ;
/* Lock to the SendResponseHeader state until connection terminated */
AppState - > CurrentState = WEBSERVER_STATE_SendResponseHeader ;
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 ;
break ;
case WEBSERVER_STATE_SendResponseHeader :
/* Determine what HTTP header should be sent to the client */
/* Determine what HTTP header should be sent to the client */
if ( AppState - > FileOpen )
if ( AppState - > FileOpen )
{
{
AppDataSize = strlen_P ( HTTP200Header ) ;
HeaderToSend = HTTP200Header ;
strncpy_P ( AppData , HTTP200Header , AppDataSize ) ;
AppState - > NextState = WEBSERVER_STATE_SendMIMETypeHeader ;
}
}
else
else
{
{
AppDataSize = strlen_P ( HTTP404Header ) ;
HeaderToSend = HTTP404Header ;
strncpy_P ( AppData , HTTP404Header , AppDataSize ) ;
AppState - > NextState = WEBSERVER_STATE_Closing ;
}
}
AppState - > PrevState = WEBSERVER_STATE_SendResponseHeader ;
HeaderLength = strlen_P ( HeaderToSend ) ;
AppState - > CurrentState = WEBSERVER_STATE_SendMIMETypeHeader ;
strncpy_P ( AppData , HeaderToSend , HeaderLength ) ;
break ;
uip_send ( AppData , HeaderLength ) ;
case WEBSERVER_STATE_SendMIMETypeHeader :
}
/* File must have been found and opened for MIME header to be sent */
if ( AppState - > FileOpen )
/** 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 , " . " ) ;
char * Extension = strpbrk ( AppState - > FileName , " . " ) ;
uint16_t MIMEHeaderLength = 0 ;
/* Check to see if a file extension was found for the requested filename */
/* Check to see if a file extension was found for the requested filename */
if ( Extension ! = NULL )
if ( Extension ! = NULL )
@ -188,63 +240,52 @@ void WebserverApp_Callback(void)
{
{
if ( strcmp_P ( & Extension [ 1 ] , MIMETypes [ i ] . Extension ) = = 0 )
if ( strcmp_P ( & Extension [ 1 ] , MIMETypes [ i ] . Extension ) = = 0 )
{
{
AppDataSize = strlen_P ( MIMETypes [ i ] . MIMEType ) ;
MIMEHeaderLength = strlen_P ( MIMETypes [ i ] . MIMEType ) ;
strncpy_P ( AppData , MIMETypes [ i ] . MIMEType , AppDataSize ) ;
strncpy_P ( AppData , MIMETypes [ i ] . MIMEType , MIMEHeaderLength ) ;
break ;
break ;
}
}
}
}
}
}
/* Check if a MIME type was found and copied to the output buffer */
/* Check if a MIME type was found and copied to the output buffer */
if ( ! ( AppDataSize ) )
if ( ! ( MIMEHeaderLength ) )
{
{
/* MIME type not found - copy over the default MIME type */
/* MIME type not found - copy over the default MIME type */
AppDataSize = strlen_P ( DefaultMIMEType ) ;
MIMEHeaderLength = strlen_P ( DefaultMIMEType ) ;
strncpy_P ( AppData , DefaultMIMEType , AppDataSize) ;
strncpy_P ( AppData , DefaultMIMEType , MIMEHeaderLength) ;
}
}
/* Add the end-of line terminator and end-of-headers terminator after the MIME type */
/* 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 " ) ) ;
strncpy ( & AppData [ MIMEHeaderLength ] , " \r \n \r \n " , sizeof ( " \r \n \r \n " ) ) ;
AppDataSize + = ( sizeof ( " \r \n \r \n " ) - 1 ) ;
MIMEHeaderLength + = ( sizeof ( " \r \n \r \n " ) - 1 ) ;
}
AppState - > PrevState = WEBSERVER_STATE_SendMIMETypeHeader ;
/* Send the MIME header to the receiving client */
AppState - > CurrentState = WEBSERVER_STATE_SendData ;
uip_send ( AppData , MIMEHeaderLength ) ;
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 ( ) ;
AppState - > PrevState = WEBSERVER_STATE_Closed ;
/* When the MIME header is ACKed, progress to the data send stage */
AppState - > CurrentState = WEBSERVER_STATE_Closed ;
AppState - > NextState = WEBSERVER_STATE_SendData ;
break ;
}
}
/** 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 ;
uint16_t MaxSegSize = uip_mss ( ) ;
/* 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 */
/* Return file pointer to the last ACKed position */
f_lseek ( & AppState - > FileHandle , AppState - > CurrentFilePos ) ;
f_lseek ( & AppState - > FileHandle , AppState - > ACKed FilePos) ;
/* Read the next chunk of data from the open file */
/* Read the next chunk of data from the open file */
f_read ( & AppState - > FileHandle , AppData , MaxSeg Size, & AppData Size) ;
f_read ( & AppState - > FileHandle , AppData , MaxSeg mentSize, & AppState - > SentChunk Size) ;
/* If we are not re-transmitting a lost segment, advance file position */
/* Send the next file chunk to the receiving client */
if ( uip_acked ( ) & & ! ( uip_rexmit ( ) ) )
uip_send ( AppData , AppState - > SentChunkSize ) ;
{
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 */
/* Check if we are at the last chunk of the file, if so next ACK should close the connection */
if ( AppDataSize )
AppState - > NextState = ( MaxSegmentSize ! = AppState - > SentChunkSize ) ? WEBSERVER_STATE_Closing : WEBSERVER_STATE_SendData ;
uip_send ( AppData , AppDataSize ) ;
}
}