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

pull/30/head
Jeroen Domburg 10 years ago
parent da0d7df331
commit 2646aff1f3
  1. 2
      Makefile
  2. 5
      html/test2.html
  3. 14
      user/espfs.c
  4. 1
      user/espfs.h
  5. 286
      user/httpd.c
  6. 45
      user/httpd.h
  7. 13
      user/user_main.c

@ -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

@ -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>

@ -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);
} }

@ -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);

@ -10,54 +10,64 @@
#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
//struct UrlData; //This gets set at init time.
typedef struct UrlData UrlData; static HttpdBuiltInUrl *builtInUrls;
typedef struct { //Private data for httpd thing
char *url; struct HttpdPriv {
char *getArgs; char head[MAX_HEAD_LEN];
const UrlData *effUrl; int headPos;
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;
}; };
int cgiSet(struct espconn *conn, GetData *getData); //Connection pool
int cgiGetFlash(struct espconn *conn, GetData *getData); static HttpdPriv connPrivData[MAX_CONN];
static int cgiSendFile(struct espconn *conn, GetData *getData); static HttpdConnData connData[MAX_CONN];
const char htmlIndex[]="<html><head><title>Hello World</title></head> \ static struct espconn httpdConn;
<body><h1>Hello, World!</h1></body> \ static esp_tcp httpdTcp;
</html>";
static const UrlData urls[]={ typedef struct {
{"/", htmlIndex, NULL}, const char *ext;
{"/set", NULL, cgiSet}, const char *mimetype;
{"/flash.bin", NULL, cgiGetFlash}, } MimeMap;
{NULL, NULL, NULL},
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 HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) {
static char recBuff[MAX_HEAD_LEN]; int i;
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 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) {
@ -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 static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nServer: esp8266-thingie/0.1\r\n\r\nNot Found.\r\n";
int ICACHE_FLASH_ATTR cgiSet(struct espconn *conn, GetData *getData) {
char *on; void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code) {
static const char okStr[]="<html><head><title>OK</title></head><body><p>OK</p></body></html>"; char buff[128];
on=httpdFindArg(getData->getArgs, "led"); int l;
os_printf("cgiSet: on=%s\n", on?on:"not found"); //ToDo: Change 'OK' according to code
if (on!=NULL) ioLed(atoi(on)); l=os_sprintf(buff, "HTTP/1.0 %d OK\r\nServer: esp8266-thingie/0.1\r\n", code);
espconn_sent(conn, (uint8 *)okStr, os_strlen(okStr)); espconn_sent(conn->conn, buff, l);
return 1;
} }
int ICACHE_FLASH_ATTR cgiGetFlash(struct espconn *conn, GetData *getData) {
static char *p=(char *)0x40200000; void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const char *val) {
static int t=0; char buff[256];
espconn_sent(conn, (uint8 *)p, 1024); int l;
p+=1024; l=os_sprintf(buff, "%s: %s\r\n", field, val);
t++; espconn_sent(conn->conn, buff, l);
if (t<1024) return 0;
t=0;
p=(char*)0x40200000;
return 1;
} }
static int ICACHE_FLASH_ATTR cgiSendFile(struct espconn *conn, GetData *getData) { void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn) {
int len; espconn_sent(conn->conn, "\r\n", 2);
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 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);
} }

@ -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

@ -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");
} }

Loading…
Cancel
Save