/* Copyright 2000 (c) by Reto Gaehler, Toni Kaufmann, Swiss Federal Institute of Technology, Computer Engineering and Networks Laboratory. TOPSY -- A Teachable Operating System. Implementation of a tiny and simple micro kernel for teaching purposes. For further information, please visit http://www.tik.ee.ethz.ch/~topsy This software is provided under the terms of the GNU General Public Licence. A full copy of the GNU GPL is provided in the file COPYING found in the development root of Topsy. */ /* File: $Source: /usr/drwho/vault/cvs/topsy/Topsy/Net/TCP/nettcpHTTPServer.c,v $ Author(s): Affiliation: ETH Zuerich, TIK Version: $Revision: 1.2 $ Creation Date: Last Date of Change: $Date: 2000/04/03 17:45:30 $ by: $Author: gfa $ $Log: nettcpHTTPServer.c,v $ Revision 1.2 2000/04/03 17:45:30 gfa *** empty log message *** Revision 1.1 2000/03/31 17:50:38 gfa Merged with /Net from several term projects */ #include "nettcpSocket.h" #include "NetDebug.h" #include "nettcpHTTPServer.h" #include "UserSupport.h" Boolean gbTerminate; //************************************************************************************************** // the TCP echo server //************************************************************************************************** #ifdef MacOS pascal void *tcpHTTPServerThread(ThreadArg arg) #else void tcpHTTPServerThread(ThreadArg arg) #endif { SocketPtr pSocket = (SocketPtr)arg; int nError; void *pTmp; int state; int header_length, content_length; int totreqlen; int nCount; int bytesReceived; char ch; char tbuf[HTTP_TBUF_LEN]; char response_header[HTTP_TBUF_LEN], response_message[HTTP_TBUF_LEN]; char *tptr; /* * Read a complete HTTP request - header and body */ for (nCount=0;nCount 0) { // Put the character into the buffer. tbuf[header_length] = ch; header_length += 1; tbuf[header_length] = '\0'; // Check that we still have space to include our null terminator. if (header_length >= HTTP_TBUF_LEN) { nError = -1; } // Do the magic state machine. Note the use of // hardwired numbers for CR and LF. This is correct // because the Internet standards say that these // numbers can't change. I don't use \n and \r // because these values change between various C // compilers on the Mac. switch (ch) { case 13: switch (state) { case kWorkerWaitingForCR1: state = kWorkerWaitingForLF1; break; case kWorkerWaitingForCR2: state = kWorkerWaitingForLF2; break; default: state = kWorkerWaitingForCR1; break; } break; case 10: switch (state) { case kWorkerWaitingForLF1: state = kWorkerWaitingForCR2; break; case kWorkerWaitingForLF2: state = kWorkerDone; break; default: state = kWorkerWaitingForCR1; break; } break; default: state = kWorkerWaitingForCR1; break; } } else { nError = bytesReceived; } } while ( nError == TCP_NO_ERR && state != kWorkerDone ); content_length = 0; totreqlen = header_length; if (nError == TCP_NO_ERR) { /* Find eventual request body */ content_length=getContentLen(tbuf,header_length); if (content_length>0) { do { bytesReceived = tcpRead(pSocket, &ch, sizeof(char)); if ((bytesReceived == TCP_URGENTMODE) || (bytesReceived == TCP_NORMALMODE)) continue; if (bytesReceived > 0) { tbuf[totreqlen] = ch; totreqlen += 1; tbuf[totreqlen] = '\0'; } else { nError = bytesReceived; } } while (nError == TCP_NO_ERR && totreqlen < header_length+content_length); } /* Interpretate HTTP requests */ /******************************/ /* * Root object "/" */ if (nError != TCP_NO_ERR) { tcpClose(pSocket); return; } if (stringNCompare("GET / ", tbuf, 6) == EQUAL || stringNCompare("GET /\n", tbuf, 6) == EQUAL || stringNCompare("GET /\r", tbuf, 6) == EQUAL) { /* Create the response message */ sprintf(response_message, "

Topsy - Tiny high performance webserver

\n"); sprintf(&response_message[stringLength(response_message)], "A HTTP test server, listening at port %d.\n", 80); sprintf(&response_message[stringLength(response_message)], "

Log data encapsulated by HTML - /logdump\n"); sprintf(&response_message[stringLength(response_message)], "
Log data readable by Excel - /logdump.xls\n"); sprintf(&response_message[stringLength(response_message)], "

\n"); sprintf(&response_message[stringLength(response_message)], "\t\n"); sprintf(&response_message[stringLength(response_message)], "\t\n"); sprintf(&response_message[stringLength(response_message)], "
\n"); /* Create response header */ sprintf(response_header, "HTTP/1.0 200 OK\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-type: text/html\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message)); sprintf(&response_header[stringLength(response_header)], "\r\n"); } else /* * Log object "/logdump" in HTML */ if (stringNCompare("GET /logdump ", tbuf, 13) == EQUAL || stringNCompare("GET /logdump\n", tbuf, 13) == EQUAL || stringNCompare("GET /logdump\r", tbuf, 13) == EQUAL) { /* Create the response message */ sprintf(response_message, "

Dump of log data

\n"); sprintf(&response_message[stringLength(response_message)], "Donald = 0x4711\n"); sprintf(&response_message[stringLength(response_message)], "
\n"); sprintf(&response_message[stringLength(response_message)], "Duck = 17\n"); /* Create response header */ sprintf(response_header, "HTTP/1.0 200 OK\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-type: text/html\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message)); sprintf(&response_header[stringLength(response_header)], "\r\n"); } else /* * Log object "/logdump.xls" to EXCEL */ if (stringNCompare("GET /logdump.xls ", tbuf, 17) == EQUAL || stringNCompare("GET /logdump.xls\n", tbuf, 17) == EQUAL || stringNCompare("GET /logdump.xls\r", tbuf, 17) == EQUAL) { /* Create the response message */ sprintf(response_message, "Dump of log data \t Values\n"); sprintf(&response_message[stringLength(response_message)], "Donald \t 0x4711\n"); sprintf(&response_message[stringLength(response_message)], "Duck \t 17\n"); /* Create response header */ sprintf(response_header, "HTTP/1.0 200 OK\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-type: application/vnd.ms-excel\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message)); sprintf(&response_header[stringLength(response_header)], "\r\n"); } else /* * Parameter object "/parameters" in HTML */ if (stringNCompare("POST /parameters ", tbuf, 17) == EQUAL || stringNCompare("POST /parameters\n", tbuf, 17) == EQUAL || stringNCompare("POST /parameters\r", tbuf, 17) == EQUAL) { /* Create the response message */ sprintf(response_message, "

Parameters

\n"); if (header_length > 0) sprintf(&response_message[stringLength(response_message)], "%s\n", &tbuf[header_length]); else sprintf(&response_message[stringLength(response_message)], "No parameters available by means of POST\n"); /* Create response header */ sprintf(response_header, "HTTP/1.0 200 OK\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-type: text/html\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message)); sprintf(&response_header[stringLength(response_header)], "\r\n"); //if ((tptr = strstr(tbuf, "kill")) != NULL) //{ // gbTerminate=TRUE; //} } else { /* * Unknown object requested */ /* Create the response message */ sprintf(response_message, "

Error

\n"); sprintf(&response_message[stringLength(response_message)], "The requested object does not exist:\n"); sprintf(&response_message[stringLength(response_message)], "\n"); sprintf(&response_message[stringLength(response_message)], "
%s\n
\n", tbuf); /* Create response header */ sprintf(response_header, "HTTP/1.0 404 Not found\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-type: text/html\r\n"); sprintf(&response_header[stringLength(response_header)], "Content-length: %d\r\n", stringLength(response_message)); sprintf(&response_header[stringLength(response_header)], "\r\n"); } /* * Send HTTP response */ nError = tcpWrite(pSocket,(void *)response_header,stringLength(response_header),FALSE); nError = tcpWrite(pSocket,(void *)response_message,stringLength(response_message),FALSE); } /* * Close connection to client process */ tcpClose(pSocket); netdbgPrintf(NETDEBUG_TCP, "Terminate HTTP Server Thread\n"); } //************************************************************************************************** // the TCP echo server //************************************************************************************************** #ifdef MacOS pascal void *tcpHTTPDeamon(ThreadArg arg) #else void tcpHTTPDeamon(ThreadArg arg) #endif { IPAddressUon uonSrcAddr; IPAddressUon uonDstAddr; int nError; SocketPtr pServerSocket; SocketPtr pSocket; ThreadId echoID; void *pTmp; char cChar; uonSrcAddr.u32Addr = 0xC0A80202; // 192.168.2.2 uonDstAddr.u32Addr = 0xC0A80201; // 192.168.2.1 nError = tcpOpen(&uonSrcAddr,&uonDstAddr,80,TCP_ANY_FOREIGN_PORT,&pServerSocket); if (nError == TCP_NO_ERR) { nError = tcpControl(pServerSocket,TCP_CONTROL_LISTEN_QUEUE,(void*)10,NULL,&pTmp); if (nError == TCP_NO_ERR) { netdbgPrintf(NETDEBUG_TCP, "httpd is started\n"); while (TRUE) { nError = tcpControl(pServerSocket,TCP_CONTROL_ACCEPT,NULL,NULL,&pSocket); if ((nError != TCP_NO_ERR) && (pSocket != NULL)) { netdbgDisplay(NETDEBUG_TCP, "httpd failed\n"); break; } else netdbgDisplay(NETDEBUG_TCP,"http client connected successfully\n"); tcpControl(pSocket,TCP_CONTROL_SET_USER_OPTION,TCP_TCB_BUFFER,(void*)0,(void*)0); if (tmStart(&echoID,tcpHTTPServerThread,(ThreadArg)pSocket,"tcpHTTPServer") != TM_STARTOK) netdbgDisplay(NETDEBUG_TCP,"httpd error\n"); } } else netdbgDisplay(NETDEBUG_TCP,"httpd error tcpControl\n"); } else netdbgDisplay(NETDEBUG_TCP,"httpd error tcpOpen\n"); } //************************************************************************************************** // getNum //************************************************************************************************** int getNum(unsigned char *pu8Buffer,unsigned short u16Len) { unsigned char u8NumBuf[12]; int nPos = 0; int nDigits = 0; int nValue = -1; while ((*pu8Buffer == 32) || (*pu8Buffer == 9)) { pu8Buffer++; u16Len--; } while ((*pu8Buffer >= '0') && (*pu8Buffer <= '9') && (nPos++ < u16Len)) { u8NumBuf[nDigits++] = *pu8Buffer++; } u8NumBuf[nDigits] = 0; if (nDigits > 0) atoi(&nValue,u8NumBuf); return nValue; } //************************************************************************************************** // getContentLen //************************************************************************************************** int getContentLen(unsigned char *pu8Buffer,unsigned short u16Len) { unsigned char u8Pattern1[] = "Content-Length:"; unsigned char u8Pattern2[] = "Content-length:"; int nPos = 0; int nRes; while (nPos < u16Len-sizeof(u8Pattern1)+1) { if (*pu8Buffer == 'C') { nRes = stringNCompare((const char*)pu8Buffer,(const char*)u8Pattern1,sizeof(u8Pattern1)-1); if (nRes == EQUAL) return getNum(pu8Buffer+sizeof(u8Pattern1)-1,u16Len-nPos); else { nRes = stringNCompare((const char*)pu8Buffer,(const char*)u8Pattern2,sizeof(u8Pattern2)-1); if (nRes == EQUAL) return getNum(pu8Buffer+sizeof(u8Pattern2)-1,u16Len-nPos); } } nPos++; pu8Buffer++; } } //************************************************************************************************** // tcpHTTPServerTest //************************************************************************************************** int tcpHTTPServerTest(void) { long serverThreadId; int nResult; gbTerminate=FALSE; if (tmStart(&serverThreadId,tcpHTTPDeamon,(ThreadArg)0,"HTTPDeamon") != TM_STARTOK) return TCP_GENERAL_ERROR; netdbgDisplay(NETDEBUG_TCP,"http-server started\n"); do { tmYield(); } while (gbTerminate==FALSE); netdbgDisplay(NETDEBUG_TCP,"exit http-server\n"); return 0; }