#include "driver/uart.h" #include "c_types.h" #include "user_interface.h" #include "espconn.h" #include "mem.h" #include "osapi.h" #include "espconn.h" #include "httpd.h" #include "io.h" #include "espfs.h" //Max length of head (plus POST data) #define MAX_HEAD_LEN 1024 //Max amount of connections #define MAX_CONN 8 //Max post buffer len #define MAX_POST 1024 //This gets set at init time. static HttpdBuiltInUrl *builtInUrls; //Private data for httpd thing struct HttpdPriv { char head[MAX_HEAD_LEN]; int headPos; int postPos; }; //Connection pool static HttpdPriv connPrivData[MAX_CONN]; static HttpdConnData connData[MAX_CONN]; static struct espconn httpdConn; static esp_tcp httpdTcp; typedef struct { const char *ext; const char *mimetype; } MimeMap; static const MimeMap mimeTypes[]={ {"htm", "text/htm"}, {"html", "text/html"}, {"txt", "text/plain"}, {"jpg", "image/jpeg"}, {"jpeg", "image/jpeg"}, {"png", "image/png"}, {NULL, "text/html"}, }; const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { int i=0; //Go find the extension char *ext=url+(strlen(url)-1); while (ext!=url && *ext!='.') ext--; if (*ext=='.') ext++; while (mimeTypes[i].ext!=NULL && os_strcmp(ext, mimeTypes[i].ext)!=0) i++; return mimeTypes[i].mimetype; } static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) { int i; for (i=0; ipostBuff!=NULL) os_free(conn->postBuff); conn->postBuff=NULL; conn->cgi=NULL; conn->conn=NULL; } static int httpdHexVal(char c) { if (c>='0' && c<='9') return c-'0'; if (c>='A' && c<='F') return c-'A'+10; if (c>='a' && c<='f') return c-'a'+10; } //Decode a percent-encoded value int httpdUrlDecode(char *val, int valLen, char *ret, int retLen) { int s=0, d=0; int esced=0, escVal=0; while (sconn, buff, l); } void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val) { char buff[256]; int l; l=os_sprintf(buff, "%s: %s\r\n", field, val); espconn_sent(conn->conn, buff, l); } void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn) { espconn_sent(conn->conn, "\r\n", 2); } //ToDo: sprintf->snprintf everywhere void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl) { char buff[1024]; int l; l=os_sprintf(buff, "HTTP/1.1 302 Found\r\nLocation: %s\r\n\r\nMoved to %s\r\n", newUrl, newUrl); espconn_sent(conn->conn, buff, l); } int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) { int len; char buff[1024]; if (connData->conn==NULL) { //Connection aborted. Clean up. return HTTPD_CGI_DONE; } httpdRedirect(connData, (char*)connData->cgiArg); return HTTPD_CGI_DONE; } static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { int r; HttpdConnData *conn=httpdFindConnData(arg); os_printf("Sent callback on conn %p\n", conn); if (conn==NULL) return; if (conn->cgi==NULL) { //Marked for destruction? os_printf("Conn %p is done. Closing.\n", conn->conn); espconn_disconnect(conn->conn); httpdRetireConn(conn); return; } r=conn->cgi(conn); //Execute cgi fn. if (r==HTTPD_CGI_DONE) { conn->cgi=NULL; //mark for destruction. } } static void ICACHE_FLASH_ATTR httpdSendResp(HttpdConnData *conn) { int i=0; int r; //See if the url is somewhere in our internal url table. while (builtInUrls[i].url!=NULL && conn->url!=NULL) { os_printf("%s == %s?\n", builtInUrls[i].url, conn->url); if (os_strcmp(builtInUrls[i].url, conn->url)==0 || builtInUrls[i].url[0]=='*') { os_printf("Is url index %d\n", i); conn->cgiData=NULL; conn->cgi=builtInUrls[i].cgiCb; conn->cgiArg=builtInUrls[i].cgiArg; r=conn->cgi(conn); if (r!=HTTPD_CGI_NOTFOUND) { if (r==HTTPD_CGI_DONE) conn->cgi=NULL; //If cgi finishes immediately: mark conn for destruction. return; } } i++; } //Can't find :/ os_printf("%s not found. 404!\n", conn->url); espconn_sent(conn->conn, (uint8 *)httpNotFoundHeader, os_strlen(httpNotFoundHeader)); conn->cgi=NULL; //mark for destruction } static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { int i; os_printf("Got header %s\n", h); if (os_strncmp(h, "GET ", 4)==0 || os_strncmp(h, "POST ", 5)==0) { char *e; //Skip past the space after POST/GET i=0; while (h[i]!=' ') i++; conn->url=h+i+1; //Figure out end of url. e=(char*)os_strstr(conn->url, " "); if (e==NULL) return; //wtf? *e=0; //terminate url part os_printf("URL = %s\n", conn->url); conn->getArgs=(char*)os_strstr(conn->url, "?"); if (conn->getArgs!=0) { int x,l; *conn->getArgs=0; conn->getArgs++; os_printf("GET args = %s\n", conn->getArgs); } else { conn->getArgs=NULL; } } else if (os_strncmp(h, "Content-Length: ", 16)==0) { i=0; while (h[i]!=' ') i++; conn->postLen=atoi(h+i+1); if (conn->postLen>MAX_POST) conn->postLen=MAX_POST; os_printf("Mallocced buffer for %d bytes of post data.\n", conn->postLen); conn->postBuff=(char*)os_malloc(conn->postLen+1); conn->priv->postPos=0; } } static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short len) { int x, l; char *p, *e; HttpdConnData *conn=httpdFindConnData(arg); if (conn==NULL) return; for (x=0; xpriv->headPos!=-1) { //This byte is a header byte. if (conn->priv->headPos!=MAX_HEAD_LEN) conn->priv->head[conn->priv->headPos++]=data[x]; conn->priv->head[conn->priv->headPos]=0; //Scan for /r/n/r/n if (data[x]=='\n' && (char *)os_strstr(conn->priv->head, "\r\n\r\n")!=NULL) { //Reset url data conn->url=NULL; //Find end of next header line p=conn->priv->head; while(p<(&conn->priv->head[conn->priv->headPos-4])) { e=(char *)os_strstr(p, "\r\n"); if (e==NULL) break; e[0]=0; httpdParseHeader(p, conn); p=e+2; } //If we don't need to receive post data, we can send the response now. if (conn->postLen==0) { httpdSendResp(conn); } conn->priv->headPos=-1; //Indicate we're done with the headers. } } else if (conn->priv->postPos!=-1 && conn->postLen!=0 && conn->priv->postPos <= conn->postLen) { //This byte is a POST byte. conn->postBuff[conn->priv->postPos++]=data[x]; if (conn->priv->postPos>=conn->postLen) { //Received post stuff. conn->postBuff[conn->priv->postPos]=0; //zero-terminate conn->priv->postPos=-1; os_printf("Post data: %s\n", conn->postBuff); //Send the response. httpdSendResp(conn); return; } } } } static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) { HttpdConnData *conn=httpdFindConnData(arg); os_printf("ReconCb\n"); if (conn==NULL) return; //Yeah... No idea what to do here. ToDo: figure something out. } static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) { #if 0 //Stupid esp sdk passes through wrong arg here, namely the one of the *listening* socket. //If it ever gets fixed, be sure to update the code in this snippet; it's probably out-of-date. HttpdConnData *conn=httpdFindConnData(arg); os_printf("Disconnected, conn=%p\n", conn); if (conn==NULL) return; conn->conn=NULL; if (conn->cgi!=NULL) conn->cgi(conn); //flush cgi data #endif //Just look at all the sockets and kill the slot if needed. int i; for (i=0; istate==ESPCONN_NONE || connData[i].conn->state==ESPCONN_CLOSE) { connData[i].conn=NULL; if (connData[i].cgi!=NULL) connData[i].cgi(&connData[i]); //flush cgi data httpdRetireConn(&connData[i]); } } } } static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) { struct espconn *conn=arg; HttpdConnData *conndata; int i; //Find empty conndata in pool for (i=0; iheadPos=0; connData[i].postBuff=NULL; connData[i].priv->postPos=0; connData[i].postLen=0; espconn_regist_recvcb(conn, httpdRecvCb); espconn_regist_reconcb(conn, httpdReconCb); espconn_regist_disconcb(conn, httpdDisconCb); espconn_regist_sentcb(conn, httpdSentCb); } void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) { int i; for (i=0; i