BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
e8658d5
Branches
Tags
master
flaneth
firmware.dartboard
http.c
initial commit after making CF identify work
Stefan Schuermans
commited
e8658d5
at 2012-04-15 19:57:57
http.c
Blame
History
Raw
/* flaneth - flash and ethernet - dartboard mod * version 0.1 date 2008-11-09 * Copyright (C) 2007-2008 Stefan Schuermans <stefan@schuermans.info> * Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html * a BlinkenArea project - http://www.blinkenarea.org/ */ #include <string.h> #include <avr/pgmspace.h> #include "dart.h" #include "debug.h" #include "http.h" #include "macros.h" #include "tcp.h" // receive states while parsing HTTP request #define HTTP_RCV_CMD 0 // receiving HTTP command (e.g. GET) #define HTTP_RCV_FILE 1 // receiving filename #define HTTP_RCV_LEN 2 // receiving content length #define HTTP_RCV_HDR_END 3 // receiving header until end #define HTTP_RCV_DATA 4 // receiving post data #define HTTP_RCV_DONE 5 // receiving completed // knwon HTTP commands #define HTTP_CMD_UNKNOWN 0 // unknown HTTP command #define HTTP_CMD_GET 1 // GET command #define HTTP_CMD_POST 2 // POST command // files available using HTTP #define HTTP_FILE_NOT_FOUND 0 // file not found file #define HTTP_FILE_INDEX 1 // index file #define HTTP_FILE_DART 10 // dartboard response, still waiting #define HTTP_FILE_DART_NO 11 // dartboard response, no hit #define HTTP_FILE_DART_1D 12 // dartboard response, 1 digit hit #define HTTP_FILE_DART_2D 13 // dartboard response, 2 digit hit // maximum HTTP/SOAP timeout (must be less than TCP timeout) #define HttpMaxTimeout 45 // content of webpages and strings to fill in for variables #include "http_content.inc" // table with HTTP connections struct HttpConnection { unsigned char ConnNo; // number of the TCP connection, 0xFF is unused unsigned long RcvCnt; // number of received bytes on connection unsigned char RcvState; // what is being received at the moment unsigned char RcvBuf[32]; // receive buffer unsigned char RcvBufLen; // length of data in receive buffer unsigned char Command; // the command the client issued char PreHttp1; // flag if a HTTP version before 1.0 is used unsigned char File; // the file the client requested unsigned long DataLen; // length of the data the client posts unsigned char DataTag; // flag if currently in a tag in HTTP request body unsigned long DataValue; // numeric value posted in HTTP request body uint16_t pSndData; // the data to send (progmem pointer) unsigned short SndDataLen; // the length of the data to send char Vars; // flag if to interpret variables unsigned char DartFactor; // factor hit on dart board to transmit unsigned char DartValue; // value hit on dart board to transmit } HttpConns[4]; // parse HTTP command static void HttpParseCommand( struct HttpConnection * pConn ) { // GET command if( pConn->RcvBufLen == 3 && strncasecmp( (const char *)pConn->RcvBuf, "GET", 3 ) == 0 ) pConn->Command = HTTP_CMD_GET; // POST command else if( pConn->RcvBufLen == 4 && strncasecmp( (const char *)pConn->RcvBuf, "POST", 4 ) == 0 ) { pConn->Command = HTTP_CMD_POST; pConn->DataLen = 0; } // unknown command is the default else pConn->Command = HTTP_CMD_UNKNOWN; } // parse HTTP file static void HttpParseFile( struct HttpConnection * pConn ) { // index file if( (pConn->RcvBufLen == 1 && strncasecmp( (const char *)pConn->RcvBuf, "/", 1 ) == 0) || (pConn->RcvBufLen == 2 && strncasecmp( (const char *)pConn->RcvBuf, "/i", 2 ) == 0) ) pConn->File = HTTP_FILE_INDEX; // dartboard response else if( pConn->RcvBufLen == 5 && strncasecmp( (const char *)pConn->RcvBuf, "/dart", 5 ) == 0 ) pConn->File = HTTP_FILE_DART; // error file is the default else pConn->File = HTTP_FILE_NOT_FOUND; } // process HTTP request static void HttpProcessRequest( struct HttpConnection * pConn ) { // different commands switch( pConn->Command ) { case HTTP_CMD_GET: case HTTP_CMD_POST: // different files switch( pConn->File ) { case HTTP_FILE_INDEX: if( pConn->PreHttp1 ) { pConn->pSndData = (uint16_t)HttpIndex + HttpIndexHeaderSize; pConn->SndDataLen = sizeof( HttpIndex ) - 1 - HttpIndexHeaderSize; } else { pConn->pSndData = (uint16_t)HttpIndex; pConn->SndDataLen = sizeof( HttpIndex ) - 1; } pConn->Vars = 0; break; case HTTP_FILE_DART_NO: if( pConn->PreHttp1 ) { pConn->pSndData = (uint16_t)HttpDartNo + HttpDartNoHeaderSize; pConn->SndDataLen = sizeof( HttpDartNo ) - 1 - HttpDartNoHeaderSize; } else { pConn->pSndData = (uint16_t)HttpDartNo; pConn->SndDataLen = sizeof( HttpDartNo ) - 1; } pConn->Vars = 1; break; case HTTP_FILE_DART_1D: if( pConn->PreHttp1 ) { pConn->pSndData = (uint16_t)HttpDart1d + HttpDart1dHeaderSize; pConn->SndDataLen = sizeof( HttpDart1d ) - 1 - HttpDart1dHeaderSize; } else { pConn->pSndData = (uint16_t)HttpDart1d; pConn->SndDataLen = sizeof( HttpDart1d ) - 1; } pConn->Vars = 1; break; case HTTP_FILE_DART_2D: if( pConn->PreHttp1 ) { pConn->pSndData = (uint16_t)HttpDart2d + HttpDart2dHeaderSize; pConn->SndDataLen = sizeof( HttpDart2d ) - 1 - HttpDart2dHeaderSize; } else { pConn->pSndData = (uint16_t)HttpDart2d; pConn->SndDataLen = sizeof( HttpDart2d ) - 1; } pConn->Vars = 1; break; case HTTP_FILE_NOT_FOUND: default: if( pConn->PreHttp1 ) { pConn->pSndData = (uint16_t)HttpNotFound + HttpNotFoundHeaderSize; pConn->SndDataLen = sizeof( HttpNotFound ) - 1 - HttpNotFoundHeaderSize; } else { pConn->pSndData = (uint16_t)HttpNotFound; pConn->SndDataLen = sizeof( HttpNotFound ) - 1; } pConn->Vars = 0; } // switch( pConn->File ) break; case HTTP_CMD_UNKNOWN: default: if( pConn->PreHttp1 ) { pConn->pSndData = (uint16_t)HttpBadRequest + HttpBadRequestHeaderSize; pConn->SndDataLen = sizeof( HttpBadRequest ) - 1 - HttpBadRequestHeaderSize; } else { pConn->pSndData = (uint16_t)HttpBadRequest; pConn->SndDataLen = sizeof( HttpBadRequest ) - 1; } pConn->Vars = 0; } // switch( pConn->Command ) } // called when connection is established void HttpConnect( unsigned char ConnNo ) { unsigned char i; struct HttpConnection * pConn; // find connection in table (in case TCP calls us twice, this should never happen) for( i = 0; i < count( HttpConns ); i++ ) if( HttpConns[i].ConnNo == ConnNo ) break; // connection not found if( i >= count( HttpConns ) ) { // find a free entry for( i = 0; i < count( HttpConns ); i++ ) if( HttpConns[i].ConnNo == 0xFF ) break; if( i >= count( HttpConns ) ) // no free entry found return; // ignore this connection (will be closed in first call of HttpSend) } // get pointer to connection pConn = &HttpConns[i]; debug_http_printf( "accept no=%u", ConnNo ); // put new connection into table pConn->ConnNo = ConnNo; pConn->RcvCnt = 0; pConn->RcvState = HTTP_RCV_CMD; pConn->RcvBufLen = 0; } // called when connection is closed / reset // (after this, the connection number may not be used any more) void HttpClose( unsigned char ConnNo ) { unsigned char i; struct HttpConnection * pConn; // find connection in table for( i = 0; i < count( HttpConns ); i++ ) if( HttpConns[i].ConnNo == ConnNo ) break; if( i >= count( HttpConns ) ) // connection not found return; // ignore this (now already closed) connection // get pointer to connection pConn = &HttpConns[i]; debug_http_printf( "close no=%u", ConnNo ); // drop connection from table pConn->ConnNo = 0xFF; } // called when sending data is possible // (return length of available data, 0xFFFF to close connection) unsigned short HttpSend( unsigned char ConnNo, unsigned long Pos, unsigned char * pBuffer, unsigned short MaxLen ) { unsigned char i; struct HttpConnection * pConn; unsigned short len, j; uint16_t src; // progmem pointer char chr, * dest; // find connection in table for( i = 0; i < count( HttpConns ); i++ ) if( HttpConns[i].ConnNo == ConnNo ) break; if( i >= count( HttpConns ) ) // connection not found return 0xFFFF; // close connection // get pointer to connection pConn = &HttpConns[i]; // not done with receiving if( pConn->RcvState != HTTP_RCV_DONE ) return 0; // do not send anything yet // still waiting for dartboard if( pConn->File == HTTP_FILE_DART ) return 0; // do not send anything yet // at or behind end of data if( Pos >= (unsigned long)pConn->SndDataLen ) return 0xFFFF; // request to close connection // get number of bytes to send len = min( pConn->SndDataLen - (unsigned short)Pos, MaxLen ); if( len == 0 ) // nothing to send return 0; // copy data to buffer src = pConn->pSndData + (uint16_t)Pos; dest = (char *)pBuffer; for( j = 0; j < len; j++ ) { // read current character chr = (char)pgm_read_byte_near( src ); // variable if( pConn->Vars && (unsigned char)chr >= 0x80 ) { switch( chr ) { case 0x80: chr = pConn->DartFactor + '0'; break; case 0x90: chr = pConn->DartValue % 10 + '0'; break; case 0x91: chr = pConn->DartValue / 10 + '0'; break; default: chr = ' '; break; } } *dest = chr; // copy character // next character src++; dest++; } // for( j ... // return length of data in buffer debug_http_printf( "send no=%u pos=%lu len=%u", ConnNo, Pos, len ); return len; } // called when data was sent and ACKed void HttpSent( unsigned char ConnNo, unsigned long Pos ) { // nothing needs to be done here } // called when data was received // must return new window size (not smaller than curWnd) unsigned short HttpReceived( unsigned char ConnNo, unsigned long Pos, unsigned char * pBuffer, unsigned short Len, unsigned short curWnd ) { unsigned char i; struct HttpConnection * pConn; // find connection in table for( i = 0; i < count( HttpConns ); i++ ) if( HttpConns[i].ConnNo == ConnNo ) break; if( i >= count( HttpConns ) ) // connection not found return max( curWnd, 256 ); // ignore this connection (will be closed in first call of HttpSend) // get pointer to connection pConn = &HttpConns[i]; // received duplicate data or missed some data // (just to be on the safe side, this should never happen) if( pConn->RcvCnt != Pos ) { // close connection TcpClose( pConn->ConnNo ); pConn->ConnNo = 0xFF; return max( curWnd, 256 ); } // process received data for( ; Len > 0; pBuffer++, Len--, pConn->RcvCnt++ ) { // store character in receive buffer (if it fits into it) if( pConn->RcvBufLen < sizeof( pConn->RcvBuf ) ) { pConn->RcvBuf[pConn->RcvBufLen] = *pBuffer; pConn->RcvBufLen++; } // actions according to state switch( pConn->RcvState ) { // receiving HTTP command (e.g GET) case HTTP_RCV_CMD: if( *pBuffer == '\r' || *pBuffer == '\n' ) // newline { pConn->RcvBufLen--; // remove newline from buffer HttpParseCommand( pConn ); // parse command pConn->PreHttp1 = 1; // older than HTTP 1.0 pConn->File = HTTP_FILE_INDEX; // send index file pConn->RcvState = HTTP_RCV_DONE; // receiving completed debug_http_printf( "pre1+end no=%u", pConn->ConnNo ); pConn->RcvBufLen = 0; // empty receive buffer HttpProcessRequest( pConn ); // now process request } else if( *pBuffer == ' ' || *pBuffer == '\t' ) // whitespace { pConn->RcvBufLen--; // remove whitespace from buffer HttpParseCommand( pConn ); // parse command pConn->RcvState = HTTP_RCV_FILE; // now receive filename debug_http_printf( "cmd no=%u cmd=%u", pConn->ConnNo, pConn->Command ); pConn->RcvBufLen = 0; // empty receive buffer } break; // receiving filename case HTTP_RCV_FILE: if( *pBuffer == '\r' || *pBuffer == '\n' ) // newline { pConn->RcvBufLen--; // remove newline from buffer HttpParseFile( pConn ); // parse file pConn->PreHttp1 = 1; // older than HTTP 1.0 pConn->RcvState = HTTP_RCV_DONE; // receiving completed debug_http_printf( "file+end no=%u file=%u", pConn->ConnNo, pConn->File ); pConn->RcvBufLen = 0; // empty receive buffer HttpProcessRequest( pConn ); // now process request } else if( *pBuffer == ' ' || *pBuffer == '\t' ) // whitespace { pConn->RcvBufLen--; // remove whitespace from buffer HttpParseFile( pConn ); // parse file pConn->PreHttp1 = 0; // HTTP 1.0 or newer if( pConn->Command == HTTP_CMD_POST ) pConn->RcvState = HTTP_RCV_LEN; // now receive content length else pConn->RcvState = HTTP_RCV_HDR_END; // now receive header until end debug_http_printf( "file no=%u file=%u", pConn->ConnNo, pConn->File ); pConn->RcvBufLen = 0; // empty receive buffer } break; // receiving content length case HTTP_RCV_LEN: if( *pBuffer == '\n' || *pBuffer == '\r' ) // newline { pConn->RcvBuf[pConn->RcvBufLen-1] = 0; // content length line if( pConn->RcvBufLen >= 15 && memcmp( pConn->RcvBuf, "content-length:", 15 ) == 0 ) { for( i = 15; i < pConn->RcvBufLen; i++ ) if( pConn->RcvBuf[i] >= '0' && pConn->RcvBuf[i] <= '9' ) pConn->DataLen = pConn->DataLen * 10 + pConn->RcvBuf[i] - '0'; debug_http_printf( "data len %lu", pConn->DataLen ); pConn->RcvState = HTTP_RCV_HDR_END; // now receive header until end } // empty receive buffer pConn->RcvBufLen = 0; // re-insert newline if now receiving header until end if( pConn->RcvState == HTTP_RCV_HDR_END ) pConn->RcvBuf[pConn->RcvBufLen++] = *pBuffer; } else if( *pBuffer >= 'A' && *pBuffer <= 'Z' ) // capital letter { pConn->RcvBuf[pConn->RcvBufLen - 1] |= 0x20; // convert to lower case } break; // receiving header until end case HTTP_RCV_HDR_END: if( *pBuffer != '\r' && *pBuffer != '\n' ) // not a newline { pConn->RcvBufLen = 0; // empty receive buffer break; } if( (pConn->RcvBufLen == 2 && memcmp( pConn->RcvBuf, "\r\r", 2 ) == 0) || // CR CR (pConn->RcvBufLen == 2 && memcmp( pConn->RcvBuf, "\n\n", 2 ) == 0) || // LF LF (pConn->RcvBufLen == 3 && memcmp( pConn->RcvBuf, "\r\n\n", 3 ) == 0) || // CR LF LF (pConn->RcvBufLen == 3 && memcmp( pConn->RcvBuf, "\n\r\n", 3 ) == 0) || // LF CR LF pConn->RcvBufLen == 4 ) // CR LF CR LF (or some other combination of 4 times CR or LF) { if( pConn->Command == HTTP_CMD_POST && pConn->DataLen > 0 ) { pConn->RcvState = HTTP_RCV_DATA; // now receive post data pConn->DataTag = 0; // not in a tag pConn->DataValue = 0; // no value yet } else pConn->RcvState = HTTP_RCV_DONE; // receiving completed debug_http_printf( "end no=%u", pConn->ConnNo ); pConn->RcvBufLen = 0; // empty receive buffer HttpProcessRequest( pConn ); // now process request } break; // receiving post data case HTTP_RCV_DATA: pConn->RcvBufLen = 0; // ignore any data in receive buffer // process data byte for byte if( pConn->DataTag ) { // in a tag if( *pBuffer == '>' ) // end of tag pConn->DataTag = 0; } else { // not in a tag if( *pBuffer == '<' ) // start of tag pConn->DataTag = 1; if( *pBuffer >= '0' && *pBuffer <= '9' ) // a digit pConn->DataValue = pConn->DataValue * 10 + *pBuffer - '0'; // collect digits } // count down number of bytes to receive if( pConn->DataLen > 0 ) pConn->DataLen--; if( pConn->DataLen <= 0 ) { debug_http_printf( "data value %lu", pConn->DataValue ); if( pConn->DataValue > HttpMaxTimeout ) // limit timeout (must be less than TCP timeout) pConn->DataValue = HttpMaxTimeout; pConn->DataValue *= 5; // convert from seconds to ticks pConn->RcvState = HTTP_RCV_DONE; // receiving completed } break; // receiving completed case HTTP_RCV_DONE: pConn->RcvBufLen = 0; // ignore any additional data break; } // switch( pConn->RcvState ) } // for( ; Len > 0; pBuffer++, Len--, pConn->RcvCnt++ ) // return at least 256 bytes window size // - we are always able to receive data return max( curWnd, 256 ); } // http notification functions struct TcpNotify HttpNotify = // (extern) { .Connect = HttpConnect, .Close = HttpClose, .Send = HttpSend, .Sent = HttpSent, .Received = HttpReceived, }; // initialize void HttpInit( void ) // (extern) { unsigned char i; // no HTTP connections yet for( i = 0; i < count( HttpConns ); i++ ) HttpConns[i].ConnNo = 0xFF; } // http tick procedure - call every 200ms void HttpTick200( void ) // (extern) { unsigned char i, factor, value; // process all HTTP connections for( i = 0; i < count( HttpConns ); i++ ) { struct HttpConnection * pConn = &HttpConns[i]; // connection in use and waiting for dart if( pConn->ConnNo != 0xFF && pConn->RcvState == HTTP_RCV_DONE && pConn->File == HTTP_FILE_DART ) { // poll dart DartPoll( &factor, &value ); // 2 digit field hit if( factor > 0 && value >= 10 ) { pConn->DartFactor = factor; pConn->DartValue = value; pConn->File = HTTP_FILE_DART_2D; HttpProcessRequest( pConn ); // re-process request TcpSend( pConn->ConnNo ); // data to send is available now } // 1 digit field hit else if( factor > 0 && value > 0 ) { pConn->DartFactor = factor; pConn->DartValue = value; pConn->File = HTTP_FILE_DART_1D; HttpProcessRequest( pConn ); // re-process request TcpSend( pConn->ConnNo ); // data to send is available now } // no field hit else { // no timeout yet ---> count down time if( pConn->DataValue > 0 ) { pConn->DataValue--; } // timeout else { pConn->DartFactor = 0; pConn->DartValue = 0; pConn->File = HTTP_FILE_DART_NO; HttpProcessRequest( pConn ); // re-process request TcpSend( pConn->ConnNo ); // data to send is available now } } } // in use } // for( i ... }