/** \file\r
*\r
* Simple HTTP Webserver Application. When connected to the uIP stack,\r
- * this will serve out files to HTTP clients.\r
+ * this will serve out files to HTTP clients on port 80.\r
*/\r
\r
#define INCLUDE_FROM_HTTPSERVERAPP_C\r
/** HTTP server response header, for transmission before the page contents. This indicates to the host that a page exists at the\r
* given location, and gives extra connection information.\r
*/\r
-char PROGMEM HTTP200Header[] = "HTTP/1.1 200 OK\r\n"\r
- "Server: LUFA RNDIS\r\n"\r
- "Connection: close\r\n"\r
- "MIME-version: 1.0\r\n"\r
- "Content-Type: ";\r
+const char PROGMEM HTTP200Header[] = "HTTP/1.1 200 OK\r\n"\r
+ "Server: LUFA " LUFA_VERSION_STRING "\r\n"\r
+ "Connection: close\r\n"\r
+ "MIME-version: 1.0\r\n"\r
+ "Content-Type: ";\r
\r
/** HTTP server response header, for transmission before a resource not found error. This indicates to the host that the given\r
* given URL is invalid, and gives extra error information.\r
*/\r
-char PROGMEM HTTP404Header[] = "HTTP/1.1 404 Not Found\r\n"\r
- "Server: LUFA RNDIS\r\n"\r
- "Connection: close\r\n"\r
- "MIME-version: 1.0\r\n"\r
- "Content-Type: text/plain\r\n\r\n"\r
- "Error 404: File Not Found";\r
+const char PROGMEM HTTP404Header[] = "HTTP/1.1 404 Not Found\r\n"\r
+ "Server: LUFA " LUFA_VERSION_STRING "\r\n"\r
+ "Connection: close\r\n"\r
+ "MIME-version: 1.0\r\n"\r
+ "Content-Type: text/plain\r\n\r\n"\r
+ "Error 404: File Not Found: /";\r
\r
-/** Default MIME type sent if no other MIME type can be determined */\r
-char PROGMEM DefaultMIMEType[] = "text/plain";\r
+/** Default filename to fetch when a directory is requested */\r
+const char PROGMEM DefaultDirFileName[] = "index.htm";\r
+\r
+/** Default MIME type sent if no other MIME type can be determined. */\r
+const char PROGMEM DefaultMIMEType[] = "text/plain";\r
\r
/** List of MIME types for each supported file extension. */\r
-MIME_Type_t PROGMEM MIMETypes[] =\r
+const MIME_Type_t MIMETypes[] =\r
{\r
{.Extension = "htm", .MIMEType = "text/html"},\r
{.Extension = "jpg", .MIMEType = "image/jpeg"},\r
{.Extension = "pdf", .MIMEType = "application/pdf"},\r
};\r
\r
-/** FAT Fs structure to hold the internal state of the FAT driver for the dataflash contents. */\r
+/** FATFs structure to hold the internal state of the FAT driver for the dataflash contents. */\r
FATFS DiskFATState;\r
\r
\r
\r
if (uip_aborted() || uip_timedout() || uip_closed())\r
{\r
- /* Connection is being terminated for some reason - close file handle */\r
- f_close(&AppState->HTTPServer.FileHandle);\r
- AppState->HTTPServer.FileOpen = false;\r
- \r
/* Lock to the closed state so that no further processing will occur on the connection */\r
- AppState->HTTPServer.CurrentState = WEBSERVER_STATE_Closed;\r
- AppState->HTTPServer.NextState = WEBSERVER_STATE_Closed;\r
+ AppState->HTTPServer.CurrentState = WEBSERVER_STATE_Closing;\r
+ AppState->HTTPServer.NextState = WEBSERVER_STATE_Closing;\r
}\r
\r
if (uip_connected())\r
AppState->HTTPServer.CurrentState = AppState->HTTPServer.NextState;\r
}\r
\r
+ if (uip_rexmit())\r
+ {\r
+ /* Return file pointer to the last ACKed position */\r
+ f_lseek(&AppState->HTTPServer.FileHandle, AppState->HTTPServer.ACKedFilePos); \r
+ }\r
+\r
if (uip_rexmit() || uip_acked() || uip_newdata() || uip_connected() || uip_poll())\r
{\r
switch (AppState->HTTPServer.CurrentState)\r
case WEBSERVER_STATE_SendResponseHeader:\r
HTTPServerApp_SendResponseHeader();\r
break;\r
- case WEBSERVER_STATE_SendMIMETypeHeader:\r
- HTTPServerApp_SendMIMETypeHeader(); \r
- break;\r
case WEBSERVER_STATE_SendData:\r
HTTPServerApp_SendData();\r
break;\r
case WEBSERVER_STATE_Closing:\r
+ /* Connection is being terminated for some reason - close file handle */\r
+ f_close(&AppState->HTTPServer.FileHandle);\r
+ AppState->HTTPServer.FileOpen = false;\r
+ \r
+ /* If connection is not already closed, close it */\r
uip_close();\r
\r
- AppState->HTTPServer.NextState = WEBSERVER_STATE_Closed;\r
+ AppState->HTTPServer.CurrentState = WEBSERVER_STATE_Closed;\r
+ AppState->HTTPServer.NextState = WEBSERVER_STATE_Closed;\r
break;\r
} \r
} \r
if (!(uip_newdata()))\r
return;\r
\r
- char* RequestToken = strtok(AppData, " ");\r
+ char* RequestToken = strtok(AppData, " ");\r
+ char* RequestedFileName = strtok(NULL, " ");\r
\r
/* Must be a GET request, abort otherwise */\r
- if (strcmp(RequestToken, "GET") != 0)\r
+ if (strcmp_P(RequestToken, PSTR("GET")) != 0)\r
{\r
uip_abort();\r
return;\r
}\r
-\r
- char* RequestedFileName = strtok(NULL, " ");\r
\r
- /* If the requested filename has more that just the leading '/' path in it, copy it over */\r
- if (strlen(RequestedFileName) > 1)\r
- strncpy(AppState->HTTPServer.FileName, &RequestedFileName[1], (sizeof(AppState->HTTPServer.FileName) - 1));\r
- else\r
- strcpy(AppState->HTTPServer.FileName, "index.htm");\r
-\r
+ /* Copy over the requested filename */\r
+ strncpy(AppState->HTTPServer.FileName, &RequestedFileName[1], (sizeof(AppState->HTTPServer.FileName) - 1));\r
+ \r
/* Ensure filename is null-terminated */\r
- AppState->HTTPServer.FileName[(sizeof(AppState->HTTPServer.FileName) - 1)] = 0x00;\r
+ AppState->HTTPServer.FileName[sizeof(AppState->HTTPServer.FileName) - 1] = 0x00;\r
+ \r
+ /* Determine the length of the URI so that it can be checked to see if it is a directory */\r
+ uint8_t FileNameLen = strlen(AppState->HTTPServer.FileName);\r
+\r
+ /* If the URI is a directory, append the default filename */\r
+ if (AppState->HTTPServer.FileName[FileNameLen - 1] == '/')\r
+ {\r
+ strncpy_P(&AppState->HTTPServer.FileName[FileNameLen], DefaultDirFileName,\r
+ (sizeof(AppState->HTTPServer.FileName) - FileNameLen));\r
+\r
+ /* Ensure altered filename is still null-terminated */\r
+ AppState->HTTPServer.FileName[sizeof(AppState->HTTPServer.FileName) - 1] = 0x00;\r
+ }\r
\r
/* Try to open the file from the Dataflash disk */\r
- AppState->HTTPServer.FileOpen = (f_open(&AppState->HTTPServer.FileHandle, AppState->HTTPServer.FileName, FA_OPEN_EXISTING | FA_READ) == FR_OK);\r
+ AppState->HTTPServer.FileOpen = (f_open(&AppState->HTTPServer.FileHandle, AppState->HTTPServer.FileName,\r
+ (FA_OPEN_EXISTING | FA_READ)) == FR_OK);\r
\r
/* Lock to the SendResponseHeader state until connection terminated */\r
AppState->HTTPServer.CurrentState = WEBSERVER_STATE_SendResponseHeader;\r
uip_tcp_appstate_t* const AppState = &uip_conn->appstate;\r
char* const AppData = (char*)uip_appdata;\r
\r
- char* HeaderToSend;\r
+ char* Extension = strpbrk(AppState->HTTPServer.FileName, ".");\r
+ bool FoundMIMEType = false;\r
\r
- /* Determine which HTTP header should be sent to the client */\r
- if (AppState->HTTPServer.FileOpen)\r
+ /* If the file isn't already open, it wasn't found - send back a 404 error response and abort */\r
+ if (!(AppState->HTTPServer.FileOpen))\r
{\r
- HeaderToSend = HTTP200Header;\r
- AppState->HTTPServer.NextState = WEBSERVER_STATE_SendMIMETypeHeader;\r
- }\r
- else\r
- {\r
- HeaderToSend = HTTP404Header;\r
+ /* Copy over the HTTP 404 response header and send it to the receiving client */\r
+ strcpy_P(AppData, HTTP404Header);\r
+ strcpy(&AppData[strlen(AppData)], AppState->HTTPServer.FileName); \r
+ uip_send(AppData, strlen(AppData));\r
+ \r
AppState->HTTPServer.NextState = WEBSERVER_STATE_Closing;\r
+ return;\r
}\r
+ \r
+ /* Copy over the HTTP 200 response header and send it to the receiving client */\r
+ strcpy_P(AppData, HTTP200Header);\r
\r
- /* Copy over the HTTP response header and send it to the receiving client */\r
- strcpy_P(AppData, HeaderToSend);\r
- uip_send(AppData, strlen(AppData));\r
-}\r
-\r
-/** HTTP Server State handler for the MIME Header Send state. This state manages the transmission of the file\r
- * MIME type header for the requested file to the receiving HTTP client.\r
- */\r
-static void HTTPServerApp_SendMIMETypeHeader(void)\r
-{\r
- uip_tcp_appstate_t* const AppState = &uip_conn->appstate;\r
- char* const AppData = (char*)uip_appdata;\r
-\r
- char* Extension = strpbrk(AppState->HTTPServer.FileName, ".");\r
- uint16_t MIMEHeaderLength = 0;\r
-\r
- /* Check to see if a file extension was found for the requested filename */\r
+ /* Check to see if a MIME type for the requested file's extension was found */\r
if (Extension != NULL)\r
{\r
/* Look through the MIME type list, copy over the required MIME type if found */\r
- for (int i = 0; i < (sizeof(MIMETypes) / sizeof(MIMETypes[0])); i++)\r
+ for (uint8_t i = 0; i < (sizeof(MIMETypes) / sizeof(MIMETypes[0])); i++)\r
{\r
- if (strcmp_P(&Extension[1], MIMETypes[i].Extension) == 0)\r
+ if (strcmp(&Extension[1], MIMETypes[i].Extension) == 0)\r
{\r
- MIMEHeaderLength = strlen_P(MIMETypes[i].MIMEType);\r
- strncpy_P(AppData, MIMETypes[i].MIMEType, MIMEHeaderLength); \r
+ strcpy(&AppData[strlen(AppData)], MIMETypes[i].MIMEType); \r
+ FoundMIMEType = true;\r
break;\r
}\r
} \r
}\r
\r
/* Check if a MIME type was found and copied to the output buffer */\r
- if (!(MIMEHeaderLength))\r
+ if (!(FoundMIMEType))\r
{\r
/* MIME type not found - copy over the default MIME type */\r
- MIMEHeaderLength = strlen_P(DefaultMIMEType);\r
- strncpy_P(AppData, DefaultMIMEType, MIMEHeaderLength);\r
+ strcpy_P(&AppData[strlen(AppData)], DefaultMIMEType);\r
}\r
\r
- /* Add the end-of line terminator and end-of-headers terminator after the MIME type */\r
- strncpy(&AppData[MIMEHeaderLength], "\r\n\r\n", sizeof("\r\n\r\n"));\r
- MIMEHeaderLength += (sizeof("\r\n\r\n") - 1);\r
+ /* Add the end-of-line terminator and end-of-headers terminator after the MIME type */\r
+ strcpy_P(&AppData[strlen(AppData)], PSTR("\r\n\r\n"));\r
\r
/* Send the MIME header to the receiving client */\r
- uip_send(AppData, MIMEHeaderLength);\r
+ uip_send(AppData, strlen(AppData));\r
\r
/* When the MIME header is ACKed, progress to the data send stage */\r
AppState->HTTPServer.NextState = WEBSERVER_STATE_SendData;\r
uip_tcp_appstate_t* const AppState = &uip_conn->appstate;\r
char* const AppData = (char*)uip_appdata;\r
\r
- /* Must determine the maximum segment size to determine maximum file chunk size */\r
- uint16_t MaxSegmentSize = uip_mss();\r
+ /* Get the maximum segment size for the current packet */\r
+ uint16_t MaxChunkSize = uip_mss();\r
\r
- /* Return file pointer to the last ACKed position */\r
- f_lseek(&AppState->HTTPServer.FileHandle, AppState->HTTPServer.ACKedFilePos);\r
- \r
/* Read the next chunk of data from the open file */\r
- f_read(&AppState->HTTPServer.FileHandle, AppData, MaxSegmentSize, &AppState->HTTPServer.SentChunkSize);\r
+ f_read(&AppState->HTTPServer.FileHandle, AppData, MaxChunkSize, &AppState->HTTPServer.SentChunkSize);\r
\r
/* Send the next file chunk to the receiving client */\r
uip_send(AppData, AppState->HTTPServer.SentChunkSize);\r
\r
/* Check if we are at the last chunk of the file, if so next ACK should close the connection */\r
- AppState->HTTPServer.NextState = (MaxSegmentSize != AppState->HTTPServer.SentChunkSize) ? WEBSERVER_STATE_Closing : WEBSERVER_STATE_SendData;\r
+ if (MaxChunkSize != AppState->HTTPServer.SentChunkSize)\r
+ AppState->HTTPServer.NextState = WEBSERVER_STATE_Closing;\r
}\r