BlinkenArea - GitList
Repositories
Blog
Wiki
flaneth
Code
Commits
Branches
Tags
Search
Tree:
e8658d5
Branches
Tags
master
flaneth
firmware
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 version 0.2 date 2008-11-08 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 "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_HDR_END 2 // receiving header until end #define HTTP_RCV_DONE 3 // receiving completed // knwon HTTP commands #define HTTP_CMD_UNKNOWN 0 // unknown HTTP command #define HTTP_CMD_GET 1 // GET 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_ON_ALL 2 // turn on all link #define HTTP_FILE_ON_SINGLE 3 // turn on single links (+0..+7) #define HTTP_FILE_OFF_ALL 11 // turn off all link #define HTTP_FILE_OFF_SINGLE 12 // turn off single links (+0..+7) #define HTTP_FILE_IMG1 20 // image: on #define HTTP_FILE_IMG0 21 // image: off // 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[16]; // 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 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 } 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; // 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; // turn on all link else if (pConn->RcvBufLen == 3 && strncasecmp((const char *)pConn->RcvBuf, "/on", 3) == 0) pConn->File = HTTP_FILE_ON_ALL; // turn on single link else if (pConn->RcvBufLen == 4 && strncasecmp((const char *)pConn->RcvBuf, "/on", 3) == 0 && pConn->RcvBuf[3] >= '1' && pConn->RcvBuf[3] <= '8') pConn->File = HTTP_FILE_ON_SINGLE + pConn->RcvBuf[3] - '1'; // turn off all link else if (pConn->RcvBufLen == 4 && strncasecmp((const char *)pConn->RcvBuf, "/off", 4) == 0) pConn->File = HTTP_FILE_OFF_ALL; // turn off single link else if (pConn->RcvBufLen == 5 && strncasecmp((const char *)pConn->RcvBuf, "/off", 4) == 0 && pConn->RcvBuf[4] >= '1' && pConn->RcvBuf[4] <= '8') pConn->File = HTTP_FILE_OFF_SINGLE + pConn->RcvBuf[4] - '1'; // image: on else if (pConn->RcvBufLen == 5 && strncasecmp((const char *)pConn->RcvBuf, "/img1", 5) == 0) pConn->File = HTTP_FILE_IMG1; // image: off else if (pConn->RcvBufLen == 5 && strncasecmp((const char *)pConn->RcvBuf, "/img0", 5) == 0) pConn->File = HTTP_FILE_IMG0; // 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: // different actions switch (pConn->File) { case HTTP_FILE_ON_ALL: pConn->File = HTTP_FILE_INDEX; // "symlink" to index file break; case HTTP_FILE_ON_SINGLE + 0: case HTTP_FILE_ON_SINGLE + 1: case HTTP_FILE_ON_SINGLE + 2: case HTTP_FILE_ON_SINGLE + 3: case HTTP_FILE_ON_SINGLE + 4: case HTTP_FILE_ON_SINGLE + 5: case HTTP_FILE_ON_SINGLE + 6: case HTTP_FILE_ON_SINGLE + 7: pConn->File = HTTP_FILE_INDEX; // "symlink" to index file break; case HTTP_FILE_OFF_ALL: pConn->File = HTTP_FILE_INDEX; // "symlink" to index file break; case HTTP_FILE_OFF_SINGLE + 0: case HTTP_FILE_OFF_SINGLE + 1: case HTTP_FILE_OFF_SINGLE + 2: case HTTP_FILE_OFF_SINGLE + 3: case HTTP_FILE_OFF_SINGLE + 4: case HTTP_FILE_OFF_SINGLE + 5: case HTTP_FILE_OFF_SINGLE + 6: case HTTP_FILE_OFF_SINGLE + 7: pConn->File = HTTP_FILE_INDEX; // "symlink" to index file break; } // switch( pConn->File ) // 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 = 1; break; case HTTP_FILE_IMG1: if (pConn->PreHttp1) { pConn->pSndData = (uint16_t) HttpImg1 + HttpImg1HeaderSize; pConn->SndDataLen = sizeof(HttpImg1) - 1 - HttpImg1HeaderSize; } else { pConn->pSndData = (uint16_t) HttpImg1; pConn->SndDataLen = sizeof(HttpImg1) - 1; } pConn->Vars = 0; break; case HTTP_FILE_IMG0: if (pConn->PreHttp1) { pConn->pSndData = (uint16_t) HttpImg0 + HttpImg0HeaderSize; pConn->SndDataLen = sizeof(HttpImg0) - 1 - HttpImg0HeaderSize; } else { pConn->pSndData = (uint16_t) HttpImg0; pConn->SndDataLen = sizeof(HttpImg0) - 1; } pConn->Vars = 0; 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 ) } // get a variable // returns progmem pointer to variable and the length of the variable static void HttpGetVariable(unsigned char VarNo, uint16_t * ppVar, unsigned char *pVarLen) { switch (VarNo) { // current state of output 1..8 ("ON" or "off") case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: *ppVar = (uint16_t) HttpVarOff; *pVarLen = sizeof(HttpVarOff); break; // current state of output 1..8 ("1" or "0") case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: *ppVar = (uint16_t) HttpVar0; *pVarLen = sizeof(HttpVar0); break; // unknown variable default: *ppVar = (uint16_t) HttpVarUnknown; *pVarLen = sizeof(HttpVarUnknown); break; } // switch( VarNo ); } // 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, var, VarLen, VarPos; struct HttpConnection *pConn; unsigned short len, j; uint16_t src, srcTmp, pVar; // 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 // 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; // no variable in variable buffer var = 0; VarLen = 0; VarPos = 0; // if part that should be sent starts with variable src = pConn->pSndData + (uint16_t) Pos; chr = (char)pgm_read_byte_near(src); // read first character if (pConn->Vars && (unsigned char)chr >= 0x80) { // get variable var = chr; HttpGetVariable(var, &pVar, &VarLen); // get position in variable for (VarPos = 0, srcTmp = src - 1; srcTmp > pConn->pSndData; VarPos++, srcTmp--) if ((char)pgm_read_byte_near(srcTmp) != var) // if normal // character or other // variable, we found // the begin of the // variable break; } // copy data to buffer 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) { // new variable if (var != chr) { // get variable var = chr; HttpGetVariable(var, &pVar, &VarLen); VarPos = 0; } // copy next character of variable if (VarPos < VarLen) // get next character of variable *dest = (char)pgm_read_byte_near(pVar + VarPos++); else *dest = ' '; // fill rest of variable with spaces } // normal character else { var = 0; // not a variable *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("get+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("get no=%u", pConn->ConnNo); 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 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 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) { 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 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; }