From bb1a036f097602a70ce219915db28dea616b76a8 Mon Sep 17 00:00:00 2001
From: Dean Camera <dean@fourwalledcubicle.com>
Date: Sun, 31 Jan 2010 14:18:03 +0000
Subject: [PATCH] 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.

---
 Projects/Webserver/Lib/DHCPApp.c            |   4 +-
 Projects/Webserver/Lib/HTTPServerApp.c      | 297 +++++++++++---------
 Projects/Webserver/Lib/HTTPServerApp.h      |  12 +-
 Projects/Webserver/Lib/uIPManagement.c      |   2 +-
 Projects/Webserver/Lib/uip/conf/apps-conf.h |  13 +-
 5 files changed, 189 insertions(+), 139 deletions(-)

diff --git a/Projects/Webserver/Lib/DHCPApp.c b/Projects/Webserver/Lib/DHCPApp.c
index fe6c6f457..a687586ad 100644
--- a/Projects/Webserver/Lib/DHCPApp.c
+++ b/Projects/Webserver/Lib/DHCPApp.c
@@ -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);
diff --git a/Projects/Webserver/Lib/HTTPServerApp.c b/Projects/Webserver/Lib/HTTPServerApp.c
index 5f0b2cfcd..7d29272b8 100644
--- a/Projects/Webserver/Lib/HTTPServerApp.c
+++ b/Projects/Webserver/Lib/HTTPServerApp.c
@@ -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;
 }
diff --git a/Projects/Webserver/Lib/HTTPServerApp.h b/Projects/Webserver/Lib/HTTPServerApp.h
index 1ab64b323..91956ce07 100644
--- a/Projects/Webserver/Lib/HTTPServerApp.h
+++ b/Projects/Webserver/Lib/HTTPServerApp.h
@@ -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
diff --git a/Projects/Webserver/Lib/uIPManagement.c b/Projects/Webserver/Lib/uIPManagement.c
index c816dea6c..44da609dd 100644
--- a/Projects/Webserver/Lib/uIPManagement.c
+++ b/Projects/Webserver/Lib/uIPManagement.c
@@ -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 */
diff --git a/Projects/Webserver/Lib/uip/conf/apps-conf.h b/Projects/Webserver/Lib/uip/conf/apps-conf.h
index 540fc33a8..0060f1f4c 100644
--- a/Projects/Webserver/Lib/uip/conf/apps-conf.h
+++ b/Projects/Webserver/Lib/uip/conf/apps-conf.h
@@ -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
-- 
GitLab