/*
LUFA Library
Copyright ( C ) Dean Camera , 2010.
dean [ at ] fourwalledcubicle [ dot ] com
www . fourwalledcubicle . com
*/
/*
Copyright 2010 Dean Camera ( dean [ at ] fourwalledcubicle [ dot ] com )
Permission to use , copy , modify , distribute , and sell this
software and its documentation for any purpose is hereby granted
without fee , provided that the above copyright notice appear in
all copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation , and that the name of the author not be used in
advertising or publicity pertaining to distribution of the
software without specific , written prior permission .
The author disclaim all warranties with regard to this
software , including all implied warranties of merchantability
and fitness . In no event shall the author be liable for any
special , indirect or consequential damages or any damages
whatsoever resulting from loss of use , data or profits , whether
in an action of contract , negligence or other tortious action ,
arising out of or in connection with the use or performance of
this software .
*/
/** \file
*
* Simple HTTP Webserver Application . When connected to the uIP stack ,
* this will serve out files to HTTP clients on port 80.
*/
# 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
* given location , and gives extra connection information .
*/
const char PROGMEM HTTP200Header [ ] = " HTTP/1.1 200 OK \r \n "
" Server: LUFA " LUFA_VERSION_STRING " \r \n "
" Connection: close \r \n "
" MIME-version: 1.0 \r \n "
" Content-Type: " ;
/** HTTP server response header, for transmission before a resource not found error. This indicates to the host that the given
* given URL is invalid , and gives extra error information .
*/
const char PROGMEM HTTP404Header [ ] = " HTTP/1.1 404 Not Found \r \n "
" Server: LUFA " LUFA_VERSION_STRING " \r \n "
" Connection: close \r \n "
" MIME-version: 1.0 \r \n "
" Content-Type: text/plain \r \n \r \n "
" Error 404: File Not Found: / " ;
/** Default filename to fetch when a directory is requested */
const char PROGMEM DefaultDirFileName [ ] = " index.htm " ;
/** Default MIME type sent if no other MIME type can be determined. */
const char PROGMEM DefaultMIMEType [ ] = " text/plain " ;
/** List of MIME types for each supported file extension. */
const MIME_Type_t MIMETypes [ ] =
{
{ . Extension = " htm " , . MIMEType = " text/html " } ,
{ . Extension = " jpg " , . MIMEType = " image/jpeg " } ,
{ . Extension = " gif " , . MIMEType = " image/gif " } ,
{ . Extension = " bmp " , . MIMEType = " image/bmp " } ,
{ . Extension = " png " , . MIMEType = " image/png " } ,
{ . Extension = " ico " , . MIMEType = " image/x-icon " } ,
{ . Extension = " exe " , . MIMEType = " application/octet-stream " } ,
{ . Extension = " gz " , . MIMEType = " application/x-gzip " } ,
{ . Extension = " zip " , . MIMEType = " application/zip " } ,
{ . Extension = " pdf " , . MIMEType = " application/pdf " } ,
} ;
/** FATFs structure to hold the internal state of the FAT driver for the dataflash contents. */
FATFS DiskFATState ;
/** Initialization function for the simple HTTP webserver. */
void HTTPServerApp_Init ( void )
{
/* Listen on port 80 for HTTP connections from hosts */
uip_listen ( HTONS ( HTTP_SERVER_PORT ) ) ;
/* Mount the dataflash disk via FatFS */
f_mount ( 0 , & DiskFATState ) ;
}
/** uIP stack application callback for the simple HTTP webserver. This function must be called each time the
* TCP / IP stack needs a TCP packet to be processed .
*/
void HTTPServerApp_Callback ( void )
{
uip_tcp_appstate_t * const AppState = & uip_conn - > appstate ;
if ( uip_aborted ( ) | | uip_timedout ( ) | | uip_closed ( ) )
{
/* Lock to the closed state so that no further processing will occur on the connection */
AppState - > HTTPServer . CurrentState = WEBSERVER_STATE_Closing ;
AppState - > HTTPServer . NextState = WEBSERVER_STATE_Closing ;
}
if ( uip_connected ( ) )
{
/* New connection - initialize connection state values */
AppState - > HTTPServer . CurrentState = WEBSERVER_STATE_OpenRequestedFile ;
AppState - > HTTPServer . NextState = WEBSERVER_STATE_OpenRequestedFile ;
AppState - > HTTPServer . FileOpen = false ;
AppState - > HTTPServer . ACKedFilePos = 0 ;
AppState - > HTTPServer . SentChunkSize = 0 ;
}
if ( uip_acked ( ) )
{
/* Add the amount of ACKed file data to the total sent file bytes counter */
AppState - > HTTPServer . ACKedFilePos + = AppState - > HTTPServer . SentChunkSize ;
/* Progress to the next state once the current state's data has been ACKed */
AppState - > HTTPServer . CurrentState = AppState - > HTTPServer . NextState ;
}
if ( uip_rexmit ( ) )
{
/* Return file pointer to the last ACKed position */
f_lseek ( & AppState - > HTTPServer . FileHandle , AppState - > HTTPServer . ACKedFilePos ) ;
}
if ( uip_rexmit ( ) | | uip_acked ( ) | | uip_newdata ( ) | | uip_connected ( ) | | uip_poll ( ) )
{
switch ( AppState - > HTTPServer . CurrentState )
{
case WEBSERVER_STATE_OpenRequestedFile :
HTTPServerApp_OpenRequestedFile ( ) ;
break ;
case WEBSERVER_STATE_SendResponseHeader :
HTTPServerApp_SendResponseHeader ( ) ;
break ;
case WEBSERVER_STATE_SendData :
HTTPServerApp_SendData ( ) ;
break ;
case WEBSERVER_STATE_Closing :
/* Connection is being terminated for some reason - close file handle */
f_close ( & AppState - > HTTPServer . FileHandle ) ;
AppState - > HTTPServer . FileOpen = false ;
/* If connection is not already closed, close it */
uip_close ( ) ;
AppState - > HTTPServer . CurrentState = WEBSERVER_STATE_Closed ;
AppState - > HTTPServer . NextState = WEBSERVER_STATE_Closed ;
break ;
}
}
}
/** HTTP Server State handler for the Request Process state. This state manages the processing of incoming HTTP
* GET requests to the server from the receiving HTTP client .
*/
static void HTTPServerApp_OpenRequestedFile ( void )
{
uip_tcp_appstate_t * const AppState = & uip_conn - > appstate ;
char * const AppData = ( char * ) uip_appdata ;
/* No HTTP header received from the client, abort processing */
if ( ! ( uip_newdata ( ) ) )
return ;
char * RequestToken = strtok ( AppData , " " ) ;
char * RequestedFileName = strtok ( NULL , " " ) ;
/* Must be a GET request, abort otherwise */
if ( strcmp_P ( RequestToken , PSTR ( " GET " ) ) ! = 0 )
{
uip_abort ( ) ;
return ;
}
/* Copy over the requested filename */
strncpy ( AppState - > HTTPServer . FileName , & RequestedFileName [ 1 ] , ( sizeof ( AppState - > HTTPServer . FileName ) - 1 ) ) ;
/* Ensure filename is null-terminated */
AppState - > HTTPServer . FileName [ sizeof ( AppState - > HTTPServer . FileName ) - 1 ] = 0x00 ;
/* Determine the length of the URI so that it can be checked to see if it is a directory */
uint8_t FileNameLen = strlen ( AppState - > HTTPServer . FileName ) ;
/* If the URI is a directory, append the default filename */
if ( AppState - > HTTPServer . FileName [ FileNameLen - 1 ] = = ' / ' )
{
strncpy_P ( & AppState - > HTTPServer . FileName [ FileNameLen ] , DefaultDirFileName ,
( sizeof ( AppState - > HTTPServer . FileName ) - FileNameLen ) ) ;
/* Ensure altered filename is still null-terminated */
AppState - > HTTPServer . FileName [ sizeof ( AppState - > HTTPServer . FileName ) - 1 ] = 0x00 ;
}
/* Try to open the file from the Dataflash disk */
AppState - > HTTPServer . FileOpen = ( f_open ( & AppState - > HTTPServer . FileHandle , AppState - > HTTPServer . FileName ,
( FA_OPEN_EXISTING | FA_READ ) ) = = FR_OK ) ;
/* Lock to the SendResponseHeader state until connection terminated */
AppState - > HTTPServer . CurrentState = WEBSERVER_STATE_SendResponseHeader ;
AppState - > HTTPServer . 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 HTTPServerApp_SendResponseHeader ( void )
{
uip_tcp_appstate_t * const AppState = & uip_conn - > appstate ;
char * const AppData = ( char * ) uip_appdata ;
char * Extension = strpbrk ( AppState - > HTTPServer . FileName , " . " ) ;
bool FoundMIMEType = false ;
/* If the file isn't already open, it wasn't found - send back a 404 error response and abort */
if ( ! ( AppState - > HTTPServer . FileOpen ) )
{
/* Copy over the HTTP 404 response header and send it to the receiving client */
strcpy_P ( AppData , HTTP404Header ) ;
strcpy ( & AppData [ strlen ( AppData ) ] , AppState - > HTTPServer . FileName ) ;
uip_send ( AppData , strlen ( AppData ) ) ;
AppState - > HTTPServer . NextState = WEBSERVER_STATE_Closing ;
return ;
}
/* Copy over the HTTP 200 response header and send it to the receiving client */
strcpy_P ( AppData , HTTP200Header ) ;
/* Check to see if a MIME type for the requested file's extension was found */
if ( Extension ! = NULL )
{
/* Look through the MIME type list, copy over the required MIME type if found */
for ( uint8_t i = 0 ; i < ( sizeof ( MIMETypes ) / sizeof ( MIMETypes [ 0 ] ) ) ; i + + )
{
if ( strcmp ( & Extension [ 1 ] , MIMETypes [ i ] . Extension ) = = 0 )
{
strcpy ( & AppData [ strlen ( AppData ) ] , MIMETypes [ i ] . MIMEType ) ;
FoundMIMEType = true ;
break ;
}
}
}
/* Check if a MIME type was found and copied to the output buffer */
if ( ! ( FoundMIMEType ) )
{
/* MIME type not found - copy over the default MIME type */
strcpy_P ( & AppData [ strlen ( AppData ) ] , DefaultMIMEType ) ;
}
/* Add the end-of-line terminator and end-of-headers terminator after the MIME type */
strcpy_P ( & AppData [ strlen ( AppData ) ] , PSTR ( " \r \n \r \n " ) ) ;
/* Send the MIME header to the receiving client */
uip_send ( AppData , strlen ( AppData ) ) ;
/* When the MIME header is ACKed, progress to the data send stage */
AppState - > HTTPServer . 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 HTTPServerApp_SendData ( void )
{
uip_tcp_appstate_t * const AppState = & uip_conn - > appstate ;
char * const AppData = ( char * ) uip_appdata ;
/* Get the maximum segment size for the current packet */
uint16_t MaxChunkSize = uip_mss ( ) ;
/* Read the next chunk of data from the open file */
f_read ( & AppState - > HTTPServer . FileHandle , AppData , MaxChunkSize , & AppState - > HTTPServer . SentChunkSize ) ;
/* Send the next file chunk to the receiving client */
uip_send ( AppData , AppState - > HTTPServer . SentChunkSize ) ;
/* Check if we are at the last chunk of the file, if so next ACK should close the connection */
if ( MaxChunkSize ! = AppState - > HTTPServer . SentChunkSize )
AppState - > HTTPServer . NextState = WEBSERVER_STATE_Closing ;
}