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

Add MIME type handling to the Webserver project, so that files of different...

Add MIME type handling to the Webserver project, so that files of different types (e.g. images) can be served out to HTTP clients.
parent e81a4c95
...@@ -139,7 +139,7 @@ STRIP_FROM_INC_PATH = ...@@ -139,7 +139,7 @@ STRIP_FROM_INC_PATH =
# (but less readable) file names. This can be useful is your file systems # (but less readable) file names. This can be useful is your file systems
# doesn't support long names like on DOS, Mac, or CD-ROM. # doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = YES SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style # will interpret the first line (until the first dot) of a JavaDoc-style
...@@ -496,7 +496,7 @@ SHOW_DIRECTORIES = YES ...@@ -496,7 +496,7 @@ SHOW_DIRECTORIES = YES
# This will remove the Files entry from the Quick Index and from the # This will remove the Files entry from the Quick Index and from the
# Folder Tree View (if specified). The default is YES. # Folder Tree View (if specified). The default is YES.
SHOW_FILES = YES SHOW_FILES = NO
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Set the SHOW_NAMESPACES tag to NO to disable the generation of the
# Namespaces page. # Namespaces page.
......
...@@ -17,10 +17,10 @@ ...@@ -17,10 +17,10 @@
* - Added MIDI event packing support to the MIDI Device and Host mode Class drivers, allowing for multiple MIDI events to * - Added MIDI event packing support to the MIDI Device and Host mode Class drivers, allowing for multiple MIDI events to
* sent or received in packed form in a single USB packet * sent or received in packed form in a single USB packet
* - Added new MIDI send buffer flush routines to the MIDI Device and Host mode Class drivers, to flush packed events * - Added new MIDI send buffer flush routines to the MIDI Device and Host mode Class drivers, to flush packed events
* - Added master mode hardware TWI driver * - Added master mode hardware TWI driver for easy TWI peripheral control
* - Added ADC MUX masks for the standard ADC input channels on all AVR models with an ADC, altered demos to use these masks * - Added ADC MUX masks for the standard ADC input channels on all AVR models with an ADC, altered demos to use these masks
* as on some models, the channel number is not identical to its single-ended ADC MUX mask * as on some models, the channel number is not identical to its single-ended ADC MUX mask
* - New Webserver project, a RNDIS host USB webserver using the open source uIP TCP/IP network stack * - New Webserver project, a RNDIS host USB webserver using the open source uIP TCP/IP network stack and FatFS library
* *
* <b>Changed:</b> * <b>Changed:</b>
* - Slowed down software USART carried PDI programming in the AVRISP project to prevent transmission errors * - Slowed down software USART carried PDI programming in the AVRISP project to prevent transmission errors
......
...@@ -14,9 +14,9 @@ ...@@ -14,9 +14,9 @@
* *
* <b>Non-USB Library Components</b> * <b>Non-USB Library Components</b>
* - Due to some ADC channels not being identical to their ADC MUX selection masks for single-ended conversions on some AVR models, * - Due to some ADC channels not being identical to their ADC MUX selection masks for single-ended conversions on some AVR models,
* the ADC driver now has explicit masks for each of the standard ADC channels. These masks should be used when calling the ADC * the ADC driver now has explicit masks for each of the standard ADC channels (see \ref Group_ADC). These masks should be used
* functions to ensure proper operation across all AVR models. Note that the \ref ADC_SetupChannel() function is an exception, and * when calling the ADC functions to ensure proper operation across all AVR models. Note that the \ref ADC_SetupChannel() function
* should always be called with a channel number rather than a channel mask. * is an exception, and should always be called with a channel number rather than a channel mask.
* *
* <b>Host Mode</b> * <b>Host Mode</b>
* - The MIDI Host Class driver send and receive routines now operate on packed events, where multiple MIDI events may be * - The MIDI Host Class driver send and receive routines now operate on packed events, where multiple MIDI events may be
......
...@@ -51,7 +51,7 @@ clean_list: ...@@ -51,7 +51,7 @@ clean_list:
doxygen: doxygen:
@echo Generating Library Documentation... @echo Generating Library Documentation...
@doxygen Doxygen.conf ( cat Doxygen.conf ; echo "PROJECT_NUMBER=`grep LUFA_VERSION_STRING Version.h | cut -d'"' -f2`" ) | doxygen -
@echo Documentation Generation Complete. @echo Documentation Generation Complete.
clean_doxygen: clean_doxygen:
......
...@@ -41,20 +41,42 @@ ...@@ -41,20 +41,42 @@
*/ */
char PROGMEM HTTP200Header[] = "HTTP/1.1 200 OK\r\n" char PROGMEM HTTP200Header[] = "HTTP/1.1 200 OK\r\n"
"Server: LUFA RNDIS\r\n" "Server: LUFA RNDIS\r\n"
"Content-type: text/html\r\n" "Connection: close\r\n"
"Connection: close\r\n\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 /** 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. * given URL is invalid, and gives extra error information.
*/ */
char PROGMEM HTTP404Header[] = "HTTP/1.1 404 Not Found\r\n" char PROGMEM HTTP404Header[] = "HTTP/1.1 404 Not Found\r\n"
"Server: LUFA RNDIS\r\n" "Server: LUFA RNDIS\r\n"
"Connection: close\r\n\r\n" "Connection: close\r\n"
"The requested file was not found."; "MIME-version: 1.0\r\n"
"Content-Type: text/plain\r\n\r\n"
"Error 404: File Not Found";
/** Default MIME type sent if no other MIME type can be determined */
char PROGMEM DefaultMIMEType[] = "text/plain";
/** List of MIME types for each supported file extension - must be terminated with \ref END_OF_MIME_LIST entry. */
MIME_Type_t PROGMEM 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 = "exe", .MIMEType = "application/octet-stream"},
{.Extension = "gz", .MIMEType = "application/x-gzip"},
{.Extension = "ico", .MIMEType = "image/x-icon"},
{.Extension = "zip", .MIMEType = "application/zip"},
{.Extension = "pdf", .MIMEType = "application/pdf"},
};
/** FAT Fs structure to hold the internal state of the FAT driver for the dataflash contents. */ /** FAT Fs structure to hold the internal state of the FAT driver for the dataflash contents. */
FATFS DiskFATState; FATFS DiskFATState;
/** Initialization function for the simple HTTP webserver. */ /** Initialization function for the simple HTTP webserver. */
void WebserverApp_Init(void) void WebserverApp_Init(void)
{ {
...@@ -85,7 +107,8 @@ void WebserverApp_Callback(void) ...@@ -85,7 +107,8 @@ void WebserverApp_Callback(void)
} }
else if (uip_closed()) else if (uip_closed())
{ {
/* Completed connection, just return */ AppState->CurrentState = WEBSERVER_STATE_Closed;
return; return;
} }
else if (uip_connected()) else if (uip_connected())
...@@ -107,50 +130,86 @@ void WebserverApp_Callback(void) ...@@ -107,50 +130,86 @@ void WebserverApp_Callback(void)
break; break;
} }
char FileName[13]; /* Copy over the requested filename from the GET request as all-lowercase */
for (uint8_t i = 0; i < (sizeof(AppState->FileName) - 1); i++)
/* Copy over the requested filename from the GET request */
for (uint8_t i = 0; i < (sizeof(FileName) - 1); i++)
{ {
FileName[i] = AppData[sizeof("GET ") + i]; AppState->FileName[i] = tolower(AppData[sizeof("GET ") + i]);
if (FileName[i] == ' ') if (AppState->FileName[i] == ' ')
{ {
FileName[i] = 0x00; AppState->FileName[i] = 0x00;
break; break;
} }
} }
/* Ensure requested filename is null-terminated */ /* Ensure requested filename is null-terminated */
FileName[(sizeof(FileName) - 1)] = 0x00; AppState->FileName[(sizeof(AppState->FileName) - 1)] = 0x00;
/* If no filename specified, assume the default of INDEX.HTM */ /* If no filename specified, assume the default of index.htm */
if (FileName[0] == 0x00) if (AppState->FileName[0] == 0x00)
strcpy(FileName, "INDEX.HTM"); strcpy(AppState->FileName, "index.htm");
/* Try to open the file from the Dataflash disk */ /* Try to open the file from the Dataflash disk */
AppState->FileOpen = (f_open(&AppState->FileToSend, FileName, FA_OPEN_EXISTING | FA_READ) == FR_OK); AppState->FileOpen = (f_open(&AppState->FileToSend, AppState->FileName, FA_OPEN_EXISTING | FA_READ) == FR_OK);
AppState->CurrentState = WEBSERVER_STATE_SendHeaders; AppState->CurrentState = WEBSERVER_STATE_SendResponseHeader;
} }
break; break;
case WEBSERVER_STATE_SendHeaders: 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); AppDataSize = strlen_P(HTTP200Header);
strncpy_P(AppData, HTTP200Header, AppDataSize); strncpy_P(AppData, HTTP200Header, AppDataSize);
} }
else else
{ {
AppDataSize = strlen_P(HTTP404Header); AppDataSize = strlen_P(HTTP404Header);
strncpy_P(AppData, HTTP404Header, AppDataSize); strncpy_P(AppData, HTTP404Header, AppDataSize);
} }
uip_send(AppData, AppDataSize); uip_send(AppData, AppDataSize);
AppState->CurrentState = WEBSERVER_STATE_SendData; 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);
uip_send(AppData, AppDataSize);
}
AppState->CurrentState = WEBSERVER_STATE_SendData;
break; break;
case WEBSERVER_STATE_SendData: case WEBSERVER_STATE_SendData:
/* If end of file/file not open, progress to the close state */ /* If end of file/file not open, progress to the close state */
......
...@@ -39,21 +39,31 @@ ...@@ -39,21 +39,31 @@
/* Includes: */ /* Includes: */
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include <LUFA/Version.h> #include <LUFA/Version.h>
#include <uip.h> #include <uip.h>
#include <ff.h> #include <ff.h>
/* Enums: */ /* Enums: */
/** States for each HTTP connection to the webserver. */ /** States for each HTTP connection to the webserver. */
enum Webserver_States_t enum Webserver_States_t
{ {
WEBSERVER_STATE_OpenRequestedFile, /** Currently opening requested file */ WEBSERVER_STATE_OpenRequestedFile, /** Currently opening requested file */
WEBSERVER_STATE_SendHeaders, /**< Currently sending HTTP headers to the client */ 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_SendData, /**< Currently sending HTTP page data to the client */
WEBSERVER_STATE_Closed, /**< Connection closed after all data sent */ WEBSERVER_STATE_Closed, /**< Connection closed after all data sent */
}; };
/* Type Defines: */
/** Type define for a MIME type handler. */
typedef struct
{
char Extension[4]; /**< 3 or less character file extension */
char MIMEType[30]; /**< Appropriate MIME type to send when the extension is encountered */
} MIME_Type_t;
/* Macros: */ /* Macros: */
/** TCP listen port for incomming HTTP traffic */ /** TCP listen port for incomming HTTP traffic */
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
typedef struct typedef struct
{ {
uint8_t CurrentState; uint8_t CurrentState;
char FileName[13];
FIL FileToSend; FIL FileToSend;
bool FileOpen; bool FileOpen;
} uip_tcp_appstate_t; } uip_tcp_appstate_t;
......
Supports Markdown
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