ESPTerm - ESP8266 terminal emulator. Branches: [master] patches, [work] next release
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
espterm-firmware/user/httpd.c

335 lines
8.9 KiB

#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 postLen;
int postPos;
char *postBuff;
};
//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; 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 void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) {
if (conn->priv->postBuff) os_free(conn->priv->postBuff);
conn->cgi=NULL;
conn->conn=NULL;
}
//Find a specific arg in a string of get- or post-data.
static char *ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg) {
char *p;
int al;
if (line==NULL) return NULL;
p=line;
al=os_strlen(arg);
os_printf("Finding %s in %s\n", arg, line);
while (p[0]!=0) {
if (os_strncmp(p, arg, al)==0 && p[al]=='=') {
//Gotcha.
return &p[al+1];
} else {
//Wrong arg. Advance to start of next arg.
p+=os_strlen(p)+1;
}
}
os_printf("Finding %s in %s: Not found :/\n", arg, line);
return NULL; //not found
}
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) {
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; //Shouldn't happen; we haven't had a chance to send the headers yet
return;
}
}
i++;
}
os_printf("%s not found. 404!\n", conn->url);
//Can't find :/
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);
l=os_strlen(conn->getArgs);
for (x=0; x<l; x++) if (conn->getArgs[x]=='&') conn->getArgs[x]=0;
//End with double-zero
conn->getArgs[l]=0;
conn->getArgs[l+1]=0;
} else {
conn->getArgs=NULL;
}
} else if (os_strncmp(h, "Content-Length: ", 16)==0) {
i=0;
while (h[i]!=' ') i++;
conn->priv->postLen=atoi(h+i+1);
if (conn->priv->postLen>MAX_POST) conn->priv->postLen=MAX_POST;
os_printf("Mallocced buffer for %d bytes of post data.\n", conn->priv->postLen);
conn->priv->postBuff=(char*)os_malloc(conn->priv->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;
os_printf("RECV\n");
//Receive post-data if needed
if (conn->priv->postLen!=0 && conn->priv->postPos<conn->priv->postLen) {
l=conn->priv->postLen-conn->priv->postPos;
if (l>len) l=len;
for (x=0; x<l; x++) conn->priv->postBuff[conn->priv->postPos++]=data[x];
if (conn->priv->postPos>=conn->priv->postLen) {
//Received post stuff.
conn->priv->postBuff[conn->priv->postPos]=0; //zero-terminate
conn->priv->postPos=-1;
os_printf("Post data: %s\n", conn->priv->postBuff);
//Send the response.
httpdSendResp(conn);
return;
}
}
if (conn->priv->headPos==-1) return; //we don't accept data anymore
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
if ((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->priv->postLen==0) {
httpdSendResp(conn);
}
}
}
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.
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; 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
httpdRetireConn(&connData[i]);
connData[i].cgi=NULL;
}
}
}
}
static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) {
struct espconn *conn=arg;
HttpdConnData *conndata;
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;
connData[i].priv->postBuff=NULL;
connData[i].priv->postPos=0;
connData[i].priv->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<MAX_CONN; i++) {
connData[i].conn=NULL;
}
httpdConn.type=ESPCONN_TCP;
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);
}