From 2646aff1f36ab2c3ab9d0b96808da9d5d4f07cee Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 8 Oct 2014 17:34:56 +0200 Subject: [PATCH] Rework httpd into a multi-connection capable thing. Seems to work, yay! --- Makefile | 2 +- html/test2.html | 5 +- user/espfs.c | 14 ++- user/espfs.h | 1 + user/httpd.c | 286 +++++++++++++++++++++++++++-------------------- user/httpd.h | 45 +++++++- user/user_main.c | 13 ++- 7 files changed, 232 insertions(+), 134 deletions(-) diff --git a/Makefile b/Makefile index f3e8197..9c02f4d 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ EXTRA_INCDIR = include ../../esp_iot_sdk_novm_unpacked/usr/xtensa/XtDevTools/ins LIBS = c gcc hal phy net80211 lwip wpa main # compiler flags using during compilation of source files -CFLAGS = -Os -g -O2 -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH +CFLAGS = -Os -ggdb -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH # linker flags used to generate the main object file LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static diff --git a/html/test2.html b/html/test2.html index 990b231..81c3ffd 100644 --- a/html/test2.html +++ b/html/test2.html @@ -3,5 +3,8 @@

Test2!

Here's an image of a cat (hopefully...)
- +
+
+
+

diff --git a/user/espfs.c b/user/espfs.c index 8a6ffc6..9d2caf7 100644 --- a/user/espfs.c +++ b/user/espfs.c @@ -1,3 +1,9 @@ +/* +This is a simple read-only implementation of a file system. It uses a block of data coming from the +mkespfsimg tool, and can use that block to do abstracted operations on the files that are in there. +It's written for use with httpd, but doesn't need to be used as such. +*/ + #include "driver/uart.h" #include "c_types.h" #include "user_interface.h" @@ -33,7 +39,7 @@ a memory exception, crashing the program. //Copies len bytes over from dst to src, but does it using *only* //aligned 32-bit reads. -void memcpyAligned(char *dst, char *src, int len) { +void ICACHE_FLASH_ATTR memcpyAligned(char *dst, char *src, int len) { int x; int w, b; for (x=0; xdecompressor==COMPRESS_NONE) { int toRead; @@ -105,7 +111,7 @@ int espFsRead(EspFsFile *fh, char *buff, int len) { } } -void espFsClose(EspFsFile *fh) { +void ICACHE_FLASH_ATTR espFsClose(EspFsFile *fh) { if (fh==NULL) return; os_free(fh); } diff --git a/user/espfs.h b/user/espfs.h index e479f90..c2c5c4b 100644 --- a/user/espfs.h +++ b/user/espfs.h @@ -4,6 +4,7 @@ //Pos of esp fs in flash #define ESPFS_POS 0x20000 + typedef struct EspFsFile EspFsFile; EspFsFile *espFsOpen(char *fileName); diff --git a/user/httpd.c b/user/httpd.c index ccf695a..6297562 100644 --- a/user/httpd.c +++ b/user/httpd.c @@ -10,54 +10,64 @@ #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 -//struct UrlData; -typedef struct UrlData UrlData; +//This gets set at init time. +static HttpdBuiltInUrl *builtInUrls; -typedef struct { - char *url; - char *getArgs; - const UrlData *effUrl; - char *datPtr; - int datLen; - EspFsFile *file; -} GetData; - -typedef int (* cgiSendCallback)(struct espconn *conn, GetData *getData); - -struct UrlData { - const char *url; - const char *fixedResp; - cgiSendCallback cgiCb; +//Private data for httpd thing +struct HttpdPriv { + char head[MAX_HEAD_LEN]; + int headPos; }; -int cgiSet(struct espconn *conn, GetData *getData); -int cgiGetFlash(struct espconn *conn, GetData *getData); -static int cgiSendFile(struct espconn *conn, GetData *getData); - +//Connection pool +static HttpdPriv connPrivData[MAX_CONN]; +static HttpdConnData connData[MAX_CONN]; -const char htmlIndex[]="Hello World \ -

Hello, World!

\ -"; +static struct espconn httpdConn; +static esp_tcp httpdTcp; -static const UrlData urls[]={ - {"/", htmlIndex, NULL}, - {"/set", NULL, cgiSet}, - {"/flash.bin", NULL, cgiGetFlash}, - {NULL, NULL, NULL}, +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"}, }; -static const UrlData cpioUrlData={"*", NULL, cgiSendFile}; +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 struct espconn conn; -static esp_tcp tcp; -static int recLen; -static char recBuff[MAX_HEAD_LEN]; +static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) { + int i; + for (i=0; igetArgs, "led"); - os_printf("cgiSet: on=%s\n", on?on:"not found"); - if (on!=NULL) ioLed(atoi(on)); - espconn_sent(conn, (uint8 *)okStr, os_strlen(okStr)); - return 1; +static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nServer: esp8266-thingie/0.1\r\n\r\nNot Found.\r\n"; + +void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code) { + char buff[128]; + int l; + //ToDo: Change 'OK' according to code + l=os_sprintf(buff, "HTTP/1.0 %d OK\r\nServer: esp8266-thingie/0.1\r\n", code); + espconn_sent(conn->conn, buff, l); } -int ICACHE_FLASH_ATTR cgiGetFlash(struct espconn *conn, GetData *getData) { - static char *p=(char *)0x40200000; - static int t=0; - espconn_sent(conn, (uint8 *)p, 1024); - p+=1024; - t++; - if (t<1024) return 0; - t=0; - p=(char*)0x40200000; - return 1; + +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); } -static int ICACHE_FLASH_ATTR cgiSendFile(struct espconn *conn, GetData *getData) { - int len; - char buff[1024]; - len=espFsRead(getData->file, buff, 1024); - espconn_sent(conn, (uint8 *)buff, len); - return (len==0); +void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn) { + espconn_sent(conn->conn, "\r\n", 2); } -static const char *httpOkHeader="HTTP/1.0 200 OK\r\nServer: esp8266-thingie/0.1\r\nContent-Type: text/html\r\n\r\n"; -static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nServer: esp8266-thingie/0.1\r\n\r\nNot Found.\r\n"; static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { - struct espconn *conn=arg; - if (getData.effUrl==NULL) { - espconn_disconnect(conn); + 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); + conn->conn=NULL; return; } - if (getData.effUrl->cgiCb!=NULL) { - if (getData.effUrl->cgiCb(conn, &getData)) getData.effUrl=NULL; - } else { - espconn_sent(conn, (uint8 *)getData.effUrl->fixedResp, os_strlen(getData.effUrl->fixedResp)); - getData.effUrl=NULL; + r=conn->cgi(conn); //Execute cgi fn. + if (r==HTTPD_CGI_DONE) { + conn->cgi=NULL; //mark for destruction. } } -static void ICACHE_FLASH_ATTR httpdSendResp(struct espconn *conn) { +static void ICACHE_FLASH_ATTR httpdSendResp(HttpdConnData *conn) { int i=0; - EspFsFile *fdat; + int r; //See if the url is somewhere in our internal url table. - while (urls[i].url!=NULL) { - if (os_strcmp(urls[i].url, getData.url)==0) { - getData.effUrl=&urls[i]; + while (builtInUrls[i].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); - espconn_sent(conn, (uint8 *)httpOkHeader, os_strlen(httpOkHeader)); - return; + 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; //Shouldn't happen; we haven't had a chance to send the headers yet + return; + } } i++; } - //Nope. See if it's in the cpio archive - fdat=espFsOpen(getData.url); - if (fdat!=NULL) { - //Found - getData.file=fdat; - getData.effUrl=&cpioUrlData; - espconn_sent(conn, (uint8 *)httpOkHeader, os_strlen(httpOkHeader)); - return; - } - + os_printf("%s not found. 404!\n", conn->url); //Can't find :/ - espconn_sent(conn, (uint8 *)httpNotFoundHeader, os_strlen(httpNotFoundHeader)); + espconn_sent(conn->conn, (uint8 *)httpNotFoundHeader, os_strlen(httpNotFoundHeader)); + conn->cgi=NULL; //mark for destruction } -static void ICACHE_FLASH_ATTR httpdParseHeader(char *h) { +static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { os_printf("Got header %s\n", h); if (os_strncmp(h, "GET ", 4)==0) { char *e; - getData.url=h+4; - e=(char*)os_strstr(getData.url, " "); + conn->url=h+4; + e=(char*)os_strstr(conn->url, " "); if (e==NULL) return; //wtf? *e=0; //terminate url part - os_printf("URL = %s\n", getData.url); - getData.getArgs=(char*)os_strstr(getData.url, "?"); - if (getData.getArgs!=0) { + os_printf("URL = %s\n", conn->url); + conn->getArgs=(char*)os_strstr(conn->url, "?"); + if (conn->getArgs!=0) { int x,l; - *getData.getArgs=0; - getData.getArgs++; - os_printf("GET args = %s\n", getData.getArgs); - l=os_strlen(getData.getArgs); - for (x=0; xgetArgs=0; + conn->getArgs++; + os_printf("GET args = %s\n", conn->getArgs); + l=os_strlen(conn->getArgs); + for (x=0; xgetArgs[x]=='&') conn->getArgs[x]=0; //End with double-zero - getData.getArgs[l]=0; - getData.getArgs[l+1]=0; + conn->getArgs[l]=0; + conn->getArgs[l+1]=0; } else { - getData.getArgs=NULL; + conn->getArgs=NULL; } } } static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short len) { - struct espconn *conn=arg; int x; char *p, *e; + HttpdConnData *conn=httpdFindConnData(arg); + if (conn==NULL) return; - if (recLen==-1) return; //we don't accept data anymore - for (x=0; xpriv->headPos==-1) return; //we don't accept data anymore + + for (x=0; xpriv->headPos!=MAX_HEAD_LEN; x++) conn->priv->head[conn->priv->headPos++]=data[x]; + conn->priv->head[conn->priv->headPos]=0; //Scan for /r/n/r/n - if ((char *)os_strstr(recBuff, "\r\n\r\n")!=NULL) { + if ((char *)os_strstr(conn->priv->head, "\r\n\r\n")!=NULL) { //Reset url data - getData.url=NULL; + conn->url=NULL; //Find end of next header line - p=recBuff; - while(p<(&recBuff[recLen-4])) { + 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); + httpdParseHeader(p, conn); p=e+2; } httpdSendResp(conn); @@ -211,20 +213,51 @@ static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short } static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) { + HttpdConnData *conn=httpdFindConnData(arg); os_printf("ReconCb\n"); - httpdInit(); + if (conn==NULL) return; + //Yeah... No idea what to do here. ToDo: figure something out. } static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) { - struct espconn *conn=arg; +#if 0 + //Stupid esp sdk passes through wrong arg here, namely the one of the *listening* socket. + 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 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 + connData[i].cgi=NULL; + } + } + } } static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) { struct espconn *conn=arg; - os_printf("Con req, conn=%p\n", conn); - recLen=0; + HttpdConnData *conndata; + int i; + //Find empty conndata in pool + for (i=0; iheadPos=0; + espconn_regist_recvcb(conn, httpdRecvCb); espconn_regist_reconcb(conn, httpdReconCb); espconn_regist_disconcb(conn, httpdDisconCb); @@ -232,12 +265,19 @@ static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) { } -void ICACHE_FLASH_ATTR httpdInit() { - conn.type=ESPCONN_TCP; - conn.state=ESPCONN_NONE; - tcp.local_port=80; - conn.proto.tcp=&tcp; - os_printf("Httpd init, conn=%p\n", conn); - espconn_regist_connectcb(&conn, httpdConnectCb); - espconn_accept(&conn); +void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) { + int i; + + for (i=0; i +#include +#include + +#define HTTPD_CGI_MORE 0 +#define HTTPD_CGI_DONE 1 +#define HTTPD_CGI_NOTFOUND 2 + +typedef struct HttpdPriv HttpdPriv; +typedef struct HttpdConnData HttpdConnData; + +typedef int (* cgiSendCallback)(HttpdConnData *connData); + +//A struct describing a http connection. This gets passed to cgi functions. +struct HttpdConnData { + struct espconn *conn; + char *url; + char *getArgs; + const void *cgiArg; + void *cgiData; + HttpdPriv *priv; + cgiSendCallback cgi; +}; + + + +//A struct describing an url that's not in the filesystem or otherwise available. +//Also the way cgi functions get connected to an URL. +typedef struct { + const char *url; + cgiSendCallback cgiCb; + const void *cgiArg; +} HttpdBuiltInUrl; + + +void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port); +const char *httpdGetMimetype(char *url); +void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code); +void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val); +void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn); + +#endif \ No newline at end of file diff --git a/user/user_main.c b/user/user_main.c index 4005161..58b1b67 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -3,13 +3,18 @@ #include "osapi.h" #include "httpd.h" #include "io.h" +#include "httpdespfs.h" -extern uint8_t at_wifiMode; +HttpdBuiltInUrl builtInUrls[]={ +// {"/", cgiLiteral, "Lalala etc"}, + {"*", cgiEspFsHook, NULL}, + {NULL, NULL, NULL} +}; -void user_init(void) -{ + +void user_init(void) { uart_init(BIT_RATE_115200, BIT_RATE_115200); - httpdInit(); ioInit(); + httpdInit(builtInUrls, 80); os_printf("\nReady\n"); }