Rework httpd into a multi-connection capable thing. Seems to work, yay!

This commit is contained in:
Jeroen Domburg
2014-10-08 17:34:56 +02:00
parent da0d7df331
commit 2646aff1f3
7 changed files with 240 additions and 142 deletions
+1 -1
View File
@@ -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 LIBS = c gcc hal phy net80211 lwip wpa main
# compiler flags using during compilation of source files # 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 # linker flags used to generate the main object file
LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static LDFLAGS = -nostdlib -Wl,--no-check-sections -u call_user_start -Wl,-static
+4 -1
View File
@@ -3,5 +3,8 @@
<body> <body>
<h1>Test2!</h1> <h1>Test2!</h1>
<p>Here's an image of a cat (hopefully...)<br /> <p>Here's an image of a cat (hopefully...)<br />
<img src="cat.jpeg"> <img src="cat.jpeg"><br />
<img src="ceilingcat.jpg"><br />
<img src="disapprove.jpg"><br />
<img src="invisiblepogostick.jpg"><br />
</p> </p>
+10 -4
View File
@@ -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 "driver/uart.h"
#include "c_types.h" #include "c_types.h"
#include "user_interface.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* //Copies len bytes over from dst to src, but does it using *only*
//aligned 32-bit reads. //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 x;
int w, b; int w, b;
for (x=0; x<len; x++) { for (x=0; x<len; x++) {
@@ -49,7 +55,7 @@ void memcpyAligned(char *dst, char *src, int len) {
EspFsFile *espFsOpen(char *fileName) { EspFsFile ICACHE_FLASH_ATTR *espFsOpen(char *fileName) {
char *p=(char *)(ESPFS_POS+0x40200000); char *p=(char *)(ESPFS_POS+0x40200000);
char *hpos; char *hpos;
char namebuf[256]; char namebuf[256];
@@ -90,7 +96,7 @@ EspFsFile *espFsOpen(char *fileName) {
} }
int espFsRead(EspFsFile *fh, char *buff, int len) { int ICACHE_FLASH_ATTR espFsRead(EspFsFile *fh, char *buff, int len) {
if (fh==NULL) return 0; if (fh==NULL) return 0;
if (fh->decompressor==COMPRESS_NONE) { if (fh->decompressor==COMPRESS_NONE) {
int toRead; 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; if (fh==NULL) return;
os_free(fh); os_free(fh);
} }
+1
View File
@@ -4,6 +4,7 @@
//Pos of esp fs in flash //Pos of esp fs in flash
#define ESPFS_POS 0x20000 #define ESPFS_POS 0x20000
typedef struct EspFsFile EspFsFile; typedef struct EspFsFile EspFsFile;
EspFsFile *espFsOpen(char *fileName); EspFsFile *espFsOpen(char *fileName);
+171 -131
View File
@@ -10,55 +10,65 @@
#include "io.h" #include "io.h"
#include "espfs.h" #include "espfs.h"
//Max length of head (plus POST data)
#define MAX_HEAD_LEN 1024 #define MAX_HEAD_LEN 1024
//Max amount of connections
#define MAX_CONN 8
//This gets set at init time.
static HttpdBuiltInUrl *builtInUrls;
//Private data for httpd thing
struct HttpdPriv {
char head[MAX_HEAD_LEN];
int headPos;
};
//Connection pool
static HttpdPriv connPrivData[MAX_CONN];
static HttpdConnData connData[MAX_CONN];
static struct espconn httpdConn;
static esp_tcp httpdTcp;
//struct UrlData;
typedef struct UrlData UrlData;
typedef struct { typedef struct {
char *url; const char *ext;
char *getArgs; const char *mimetype;
const UrlData *effUrl; } MimeMap;
char *datPtr;
int datLen;
EspFsFile *file;
} GetData;
typedef int (* cgiSendCallback)(struct espconn *conn, GetData *getData); static const MimeMap mimeTypes[]={
{"htm", "text/htm"},
struct UrlData { {"html", "text/html"},
const char *url; {"txt", "text/plain"},
const char *fixedResp; {"jpg", "image/jpeg"},
cgiSendCallback cgiCb; {"jpeg", "image/jpeg"},
{"png", "image/png"},
{NULL, "text/html"},
}; };
int cgiSet(struct espconn *conn, GetData *getData); const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) {
int cgiGetFlash(struct espconn *conn, GetData *getData); int i=0;
static int cgiSendFile(struct espconn *conn, GetData *getData); //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;
}
const char htmlIndex[]="<html><head><title>Hello World</title></head> \ static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) {
<body><h1>Hello, World!</h1></body> \ int i;
</html>"; for (i=0; i<MAX_CONN; i++) {
if (connData[i].conn==(struct espconn *)arg) return &connData[i];
}
os_printf("FindConnData: Huh? Couldn't find connection for %p\n", arg);
return NULL; //WtF?
}
static const UrlData urls[]={
{"/", htmlIndex, NULL},
{"/set", NULL, cgiSet},
{"/flash.bin", NULL, cgiGetFlash},
{NULL, NULL, NULL},
};
static const UrlData cpioUrlData={"*", NULL, cgiSendFile};
static struct espconn conn;
static esp_tcp tcp;
static int recLen;
static char recBuff[MAX_HEAD_LEN];
static GetData getData;
//Find a specific arg in a string of get- or post-data. //Find a specific arg in a string of get- or post-data.
static char *ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg) { static char *ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg) {
char *p; char *p;
@@ -82,128 +92,120 @@ static char *ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg) {
return NULL; //not found return NULL; //not found
} }
//ToDo: Move cgi functions to somewhere else
int ICACHE_FLASH_ATTR cgiSet(struct espconn *conn, GetData *getData) {
char *on;
static const char okStr[]="<html><head><title>OK</title></head><body><p>OK</p></body></html>";
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;
}
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;
}
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);
}
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 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);
}
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);
}
static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) {
struct espconn *conn=arg; int r;
if (getData.effUrl==NULL) { HttpdConnData *conn=httpdFindConnData(arg);
espconn_disconnect(conn); 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; return;
} }
if (getData.effUrl->cgiCb!=NULL) { r=conn->cgi(conn); //Execute cgi fn.
if (getData.effUrl->cgiCb(conn, &getData)) getData.effUrl=NULL; if (r==HTTPD_CGI_DONE) {
} else { conn->cgi=NULL; //mark for destruction.
espconn_sent(conn, (uint8 *)getData.effUrl->fixedResp, os_strlen(getData.effUrl->fixedResp));
getData.effUrl=NULL;
} }
} }
static void ICACHE_FLASH_ATTR httpdSendResp(struct espconn *conn) { static void ICACHE_FLASH_ATTR httpdSendResp(HttpdConnData *conn) {
int i=0; int i=0;
EspFsFile *fdat; int r;
//See if the url is somewhere in our internal url table. //See if the url is somewhere in our internal url table.
while (urls[i].url!=NULL) { while (builtInUrls[i].url!=NULL) {
if (os_strcmp(urls[i].url, getData.url)==0) { os_printf("%s == %s?\n", builtInUrls[i].url, conn->url);
getData.effUrl=&urls[i]; if (os_strcmp(builtInUrls[i].url, conn->url)==0 || builtInUrls[i].url[0]=='*') {
os_printf("Is url index %d\n", i); os_printf("Is url index %d\n", i);
espconn_sent(conn, (uint8 *)httpOkHeader, os_strlen(httpOkHeader)); conn->cgiData=NULL;
return; 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++; i++;
} }
//Nope. See if it's in the cpio archive os_printf("%s not found. 404!\n", conn->url);
fdat=espFsOpen(getData.url);
if (fdat!=NULL) {
//Found
getData.file=fdat;
getData.effUrl=&cpioUrlData;
espconn_sent(conn, (uint8 *)httpOkHeader, os_strlen(httpOkHeader));
return;
}
//Can't find :/ //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); os_printf("Got header %s\n", h);
if (os_strncmp(h, "GET ", 4)==0) { if (os_strncmp(h, "GET ", 4)==0) {
char *e; char *e;
getData.url=h+4; conn->url=h+4;
e=(char*)os_strstr(getData.url, " "); e=(char*)os_strstr(conn->url, " ");
if (e==NULL) return; //wtf? if (e==NULL) return; //wtf?
*e=0; //terminate url part *e=0; //terminate url part
os_printf("URL = %s\n", getData.url); os_printf("URL = %s\n", conn->url);
getData.getArgs=(char*)os_strstr(getData.url, "?"); conn->getArgs=(char*)os_strstr(conn->url, "?");
if (getData.getArgs!=0) { if (conn->getArgs!=0) {
int x,l; int x,l;
*getData.getArgs=0; *conn->getArgs=0;
getData.getArgs++; conn->getArgs++;
os_printf("GET args = %s\n", getData.getArgs); os_printf("GET args = %s\n", conn->getArgs);
l=os_strlen(getData.getArgs); l=os_strlen(conn->getArgs);
for (x=0; x<l; x++) if (getData.getArgs[x]=='&') getData.getArgs[x]=0; for (x=0; x<l; x++) if (conn->getArgs[x]=='&') conn->getArgs[x]=0;
//End with double-zero //End with double-zero
getData.getArgs[l]=0; conn->getArgs[l]=0;
getData.getArgs[l+1]=0; conn->getArgs[l+1]=0;
} else { } else {
getData.getArgs=NULL; conn->getArgs=NULL;
} }
} }
} }
static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short len) { static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short len) {
struct espconn *conn=arg;
int x; int x;
char *p, *e; char *p, *e;
HttpdConnData *conn=httpdFindConnData(arg);
if (conn==NULL) return;
if (recLen==-1) return; //we don't accept data anymore if (conn->priv->headPos==-1) return; //we don't accept data anymore
for (x=0; x<len && recLen!=MAX_HEAD_LEN; x++) recBuff[recLen++]=data[x];
recBuff[recLen]=0; for (x=0; x<len && conn->priv->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 //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 //Reset url data
getData.url=NULL; conn->url=NULL;
//Find end of next header line //Find end of next header line
p=recBuff; p=conn->priv->head;
while(p<(&recBuff[recLen-4])) { while(p<(&conn->priv->head[conn->priv->headPos-4])) {
e=(char *)os_strstr(p, "\r\n"); e=(char *)os_strstr(p, "\r\n");
if (e==NULL) break; if (e==NULL) break;
e[0]=0; e[0]=0;
httpdParseHeader(p); httpdParseHeader(p, conn);
p=e+2; p=e+2;
} }
httpdSendResp(conn); 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) { static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) {
HttpdConnData *conn=httpdFindConnData(arg);
os_printf("ReconCb\n"); 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) { 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); 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; i<MAX_CONN; i++) {
if (connData[i].conn!=NULL) {
if (connData[i].conn->state==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) { static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) {
struct espconn *conn=arg; struct espconn *conn=arg;
os_printf("Con req, conn=%p\n", conn); HttpdConnData *conndata;
recLen=0; int i;
//Find empty conndata in pool
for (i=0; i<MAX_CONN; i++) if (connData[i].conn==NULL) break;
os_printf("Con req, conn=%p, pool slot %d\n", conn, i);
connData[i].priv=&connPrivData[i];
if (i==MAX_CONN) {
os_printf("Aiee, conn pool overflow!\n");
espconn_disconnect(conn);
return;
}
connData[i].conn=conn;
connData[i].priv->headPos=0;
espconn_regist_recvcb(conn, httpdRecvCb); espconn_regist_recvcb(conn, httpdRecvCb);
espconn_regist_reconcb(conn, httpdReconCb); espconn_regist_reconcb(conn, httpdReconCb);
espconn_regist_disconcb(conn, httpdDisconCb); espconn_regist_disconcb(conn, httpdDisconCb);
@@ -232,12 +265,19 @@ static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) {
} }
void ICACHE_FLASH_ATTR httpdInit() { void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) {
conn.type=ESPCONN_TCP; int i;
conn.state=ESPCONN_NONE;
tcp.local_port=80; for (i=0; i<MAX_CONN; i++) {
conn.proto.tcp=&tcp; connData[i].conn=NULL;
os_printf("Httpd init, conn=%p\n", conn); }
espconn_regist_connectcb(&conn, httpdConnectCb); httpdConn.type=ESPCONN_TCP;
espconn_accept(&conn); httpdConn.state=ESPCONN_NONE;
httpdTcp.local_port=port;
httpdConn.proto.tcp=&httpdTcp;
builtInUrls=fixedUrls;
os_printf("Httpd init, conn=%p\n", &httpdConn);
espconn_regist_connectcb(&httpdConn, httpdConnectCb);
espconn_accept(&httpdConn);
} }
+44 -1
View File
@@ -1 +1,44 @@
void ICACHE_FLASH_ATTR httpdInit(); #ifndef HTTPD_H
#define HTTPD_H
#include <ip_addr.h>
#include <c_types.h>
#include <espconn.h>
#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
+9 -4
View File
@@ -3,13 +3,18 @@
#include "osapi.h" #include "osapi.h"
#include "httpd.h" #include "httpd.h"
#include "io.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); uart_init(BIT_RATE_115200, BIT_RATE_115200);
httpdInit();
ioInit(); ioInit();
httpdInit(builtInUrls, 80);
os_printf("\nReady\n"); os_printf("\nReady\n");
} }