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; iOKOK
";
- on=httpdFindArg(getData->getArgs, "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");
}