|
|
|
@ -3,15 +3,15 @@ Esp8266 http server - core routines |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ---------------------------------------------------------------------------- |
|
|
|
|
* "THE BEER-WARE LICENSE" (Revision 42): |
|
|
|
|
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain |
|
|
|
|
* this notice you can do whatever you want with this stuff. If we meet some day, |
|
|
|
|
* and you think this stuff is worth it, you can buy me a beer in return. |
|
|
|
|
* ---------------------------------------------------------------------------- |
|
|
|
|
* Modified and enhanced by Thorsten von Eicken in 2015 |
|
|
|
|
* ---------------------------------------------------------------------------- |
|
|
|
|
*/ |
|
|
|
|
* ---------------------------------------------------------------------------- |
|
|
|
|
* "THE BEER-WARE LICENSE" (Revision 42): |
|
|
|
|
* Jeroen Domburg <jeroen@spritesmods.com> wrote this file. As long as you retain |
|
|
|
|
* this notice you can do whatever you want with this stuff. If we meet some day, |
|
|
|
|
* and you think this stuff is worth it, you can buy me a beer in return. |
|
|
|
|
* ---------------------------------------------------------------------------- |
|
|
|
|
* Modified and enhanced by Thorsten von Eicken in 2015 |
|
|
|
|
* ---------------------------------------------------------------------------- |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <esp8266.h> |
|
|
|
@ -56,29 +56,29 @@ typedef struct { |
|
|
|
|
|
|
|
|
|
//The mappings from file extensions to mime types. If you need an extra mime type,
|
|
|
|
|
//add it here.
|
|
|
|
|
static const MimeMap mimeTypes[]={ |
|
|
|
|
{"htm", "text/htm"}, |
|
|
|
|
{"html", "text/html; charset=UTF-8"}, |
|
|
|
|
{"css", "text/css"}, |
|
|
|
|
{"js", "text/javascript"}, |
|
|
|
|
{"txt", "text/plain"}, |
|
|
|
|
{"jpg", "image/jpeg"}, |
|
|
|
|
{"jpeg", "image/jpeg"}, |
|
|
|
|
{"png", "image/png"}, |
|
|
|
|
{"tpl", "text/html; charset=UTF-8"}, |
|
|
|
|
{NULL, "text/html"}, //default value
|
|
|
|
|
static const MimeMap mimeTypes[] = { |
|
|
|
|
{ "htm", "text/htm" }, |
|
|
|
|
{ "html", "text/html; charset=UTF-8" }, |
|
|
|
|
{ "css", "text/css" }, |
|
|
|
|
{ "js", "text/javascript" }, |
|
|
|
|
{ "txt", "text/plain" }, |
|
|
|
|
{ "jpg", "image/jpeg" }, |
|
|
|
|
{ "jpeg", "image/jpeg" }, |
|
|
|
|
{ "png", "image/png" }, |
|
|
|
|
{ "tpl", "text/html; charset=UTF-8" }, |
|
|
|
|
{ NULL, "text/html" }, //default value
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
//Returns a static char* to a mime type for a given url to a file.
|
|
|
|
|
const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) { |
|
|
|
|
int i=0; |
|
|
|
|
int i = 0; |
|
|
|
|
//Go find the extension
|
|
|
|
|
char *ext=url+(strlen(url)-1); |
|
|
|
|
while (ext!=url && *ext!='.') ext--; |
|
|
|
|
if (*ext=='.') ext++; |
|
|
|
|
char *ext = url + (strlen(url) - 1); |
|
|
|
|
while (ext != url && *ext != '.') ext--; |
|
|
|
|
if (*ext == '.') ext++; |
|
|
|
|
|
|
|
|
|
//ToDo: os_strcmp is case sensitive; we may want to do case-intensive matching here...
|
|
|
|
|
while (mimeTypes[i].ext!=NULL && os_strcmp(ext, mimeTypes[i].ext)!=0) i++; |
|
|
|
|
while (mimeTypes[i].ext != NULL && os_strcmp(ext, mimeTypes[i].ext) != 0) i++; |
|
|
|
|
return mimeTypes[i].mimetype; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -98,21 +98,25 @@ static void debugConn(void *arg, char *what) { |
|
|
|
|
//Looks up the connData info for a specific esp connection
|
|
|
|
|
static HttpdConnData ICACHE_FLASH_ATTR *httpdFindConnData(void *arg) { |
|
|
|
|
struct espconn *espconn = arg; |
|
|
|
|
for (int i=0; i<MAX_CONN; i++) { |
|
|
|
|
for (int i = 0; i<MAX_CONN; i++) { |
|
|
|
|
if (connData[i].remote_port == espconn->proto.tcp->remote_port && |
|
|
|
|
os_memcmp(connData[i].remote_ip, espconn->proto.tcp->remote_ip, 4) == 0) |
|
|
|
|
{ |
|
|
|
|
#if 0 |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("FindConn: 0x%p->0x%p", arg, &connData[i]); |
|
|
|
|
if (arg == connData[i].conn) os_printf("\n"); |
|
|
|
|
else os_printf(" *** was 0x%p\n", connData[i].conn); |
|
|
|
|
#endif |
|
|
|
|
#endif |
|
|
|
|
if (arg != connData[i].conn) connData[i].conn = arg; // yes, this happens!?
|
|
|
|
|
return &connData[i]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
//Shouldn't happen.
|
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s *** Unknown connection 0x%p\n", connStr, arg); |
|
|
|
|
#endif |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -123,22 +127,24 @@ static void ICACHE_FLASH_ATTR httpdRetireConn(HttpdConnData *conn) { |
|
|
|
|
if (conn->post->buff != NULL) { |
|
|
|
|
os_free(conn->post->buff); |
|
|
|
|
} |
|
|
|
|
conn->cgi=NULL; |
|
|
|
|
conn->post->buff=NULL; |
|
|
|
|
conn->cgi = NULL; |
|
|
|
|
conn->post->buff = NULL; |
|
|
|
|
conn->remote_port = 0; |
|
|
|
|
conn->remote_ip[0] = 0; |
|
|
|
|
|
|
|
|
|
uint32 dt = conn->startTime; |
|
|
|
|
if (dt > 0) dt = (system_get_time() - dt)/1000; |
|
|
|
|
if (dt > 0) dt = (system_get_time() - dt) / 1000; |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s Closed, %ums, heap=%ld\n", connStr, dt, |
|
|
|
|
(unsigned long)system_get_free_heap_size()); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//Stupid li'l helper function that returns the value of a hex char.
|
|
|
|
|
static int httpdHexVal(char c) { |
|
|
|
|
if (c>='0' && c<='9') return c-'0'; |
|
|
|
|
if (c>='A' && c<='F') return c-'A'+10; |
|
|
|
|
if (c>='a' && c<='f') return c-'a'+10; |
|
|
|
|
if (c >= '0' && c <= '9') return c - '0'; |
|
|
|
|
if (c >= 'A' && c <= 'F') return c - 'A' + 10; |
|
|
|
|
if (c >= 'a' && c <= 'f') return c - 'a' + 10; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -147,26 +153,30 @@ static int httpdHexVal(char c) { |
|
|
|
|
//are stored in the ret buffer. Returns the actual amount of bytes used in ret. Also
|
|
|
|
|
//zero-terminates the ret buffer.
|
|
|
|
|
int httpdUrlDecode(char *val, int valLen, char *ret, int retLen) { |
|
|
|
|
int s=0, d=0; |
|
|
|
|
int esced=0, escVal=0; |
|
|
|
|
int s = 0, d = 0; |
|
|
|
|
int esced = 0, escVal = 0; |
|
|
|
|
while (s<valLen && d<retLen) { |
|
|
|
|
if (esced==1) { |
|
|
|
|
escVal=httpdHexVal(val[s])<<4; |
|
|
|
|
esced=2; |
|
|
|
|
} else if (esced==2) { |
|
|
|
|
escVal+=httpdHexVal(val[s]); |
|
|
|
|
ret[d++]=escVal; |
|
|
|
|
esced=0; |
|
|
|
|
} else if (val[s]=='%') { |
|
|
|
|
esced=1; |
|
|
|
|
} else if (val[s]=='+') { |
|
|
|
|
ret[d++]=' '; |
|
|
|
|
} else { |
|
|
|
|
ret[d++]=val[s]; |
|
|
|
|
if (esced == 1) { |
|
|
|
|
escVal = httpdHexVal(val[s]) << 4; |
|
|
|
|
esced = 2; |
|
|
|
|
} |
|
|
|
|
else if (esced == 2) { |
|
|
|
|
escVal += httpdHexVal(val[s]); |
|
|
|
|
ret[d++] = escVal; |
|
|
|
|
esced = 0; |
|
|
|
|
} |
|
|
|
|
else if (val[s] == '%') { |
|
|
|
|
esced = 1; |
|
|
|
|
} |
|
|
|
|
else if (val[s] == '+') { |
|
|
|
|
ret[d++] = ' '; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
ret[d++] = val[s]; |
|
|
|
|
} |
|
|
|
|
s++; |
|
|
|
|
} |
|
|
|
|
if (d<retLen) ret[d]=0; |
|
|
|
|
if (d<retLen) ret[d] = 0; |
|
|
|
|
return d; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -177,19 +187,19 @@ int httpdUrlDecode(char *val, int valLen, char *ret, int retLen) { |
|
|
|
|
//returned string will be urldecoded already.
|
|
|
|
|
int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen) { |
|
|
|
|
char *p, *e; |
|
|
|
|
if (line==NULL) return 0; |
|
|
|
|
p=line; |
|
|
|
|
while(p!=NULL && *p!='\n' && *p!='\r' && *p!=0) { |
|
|
|
|
if (line == NULL) return 0; |
|
|
|
|
p = line; |
|
|
|
|
while (p != NULL && *p != '\n' && *p != '\r' && *p != 0) { |
|
|
|
|
//os_printf("findArg: %s\n", p);
|
|
|
|
|
if (os_strncmp(p, arg, os_strlen(arg))==0 && p[strlen(arg)]=='=') { |
|
|
|
|
p+=os_strlen(arg)+1; //move p to start of value
|
|
|
|
|
e=(char*)os_strstr(p, "&"); |
|
|
|
|
if (e==NULL) e=p+os_strlen(p); |
|
|
|
|
if (os_strncmp(p, arg, os_strlen(arg)) == 0 && p[strlen(arg)] == '=') { |
|
|
|
|
p += os_strlen(arg) + 1; //move p to start of value
|
|
|
|
|
e = (char*)os_strstr(p, "&"); |
|
|
|
|
if (e == NULL) e = p + os_strlen(p); |
|
|
|
|
//os_printf("findArg: val %s len %d\n", p, (e-p));
|
|
|
|
|
return httpdUrlDecode(p, (e-p), buff, buffLen); |
|
|
|
|
return httpdUrlDecode(p, (e - p), buff, buffLen); |
|
|
|
|
} |
|
|
|
|
p=(char*)os_strstr(p, "&"); |
|
|
|
|
if (p!=NULL) p+=1; |
|
|
|
|
p = (char*)os_strstr(p, "&"); |
|
|
|
|
if (p != NULL) p += 1; |
|
|
|
|
} |
|
|
|
|
//os_printf("Finding %s in %s: Not found :/\n", arg, line);
|
|
|
|
|
return -1; //not found
|
|
|
|
@ -197,28 +207,28 @@ int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLe |
|
|
|
|
|
|
|
|
|
//Get the value of a certain header in the HTTP client head
|
|
|
|
|
int ICACHE_FLASH_ATTR httpdGetHeader(HttpdConnData *conn, char *header, char *ret, int retLen) { |
|
|
|
|
char *p=conn->priv->head; |
|
|
|
|
p=p+strlen(p)+1; //skip GET/POST part
|
|
|
|
|
p=p+strlen(p)+1; //skip HTTP part
|
|
|
|
|
while (p<(conn->priv->head+conn->priv->headPos)) { |
|
|
|
|
while(*p<=32 && *p!=0) p++; //skip crap at start
|
|
|
|
|
char *p = conn->priv->head; |
|
|
|
|
p = p + strlen(p) + 1; //skip GET/POST part
|
|
|
|
|
p = p + strlen(p) + 1; //skip HTTP part
|
|
|
|
|
while (p<(conn->priv->head + conn->priv->headPos)) { |
|
|
|
|
while (*p <= 32 && *p != 0) p++; //skip crap at start
|
|
|
|
|
//See if this is the header
|
|
|
|
|
if (os_strncmp(p, header, strlen(header))==0 && p[strlen(header)]==':') { |
|
|
|
|
if (os_strncmp(p, header, strlen(header)) == 0 && p[strlen(header)] == ':') { |
|
|
|
|
//Skip 'key:' bit of header line
|
|
|
|
|
p=p+strlen(header)+1; |
|
|
|
|
p = p + strlen(header) + 1; |
|
|
|
|
//Skip past spaces after the colon
|
|
|
|
|
while(*p==' ') p++; |
|
|
|
|
while (*p == ' ') p++; |
|
|
|
|
//Copy from p to end
|
|
|
|
|
while (*p!=0 && *p!='\r' && *p!='\n' && retLen>1) { |
|
|
|
|
*ret++=*p++; |
|
|
|
|
while (*p != 0 && *p != '\r' && *p != '\n' && retLen>1) { |
|
|
|
|
*ret++ = *p++; |
|
|
|
|
retLen--; |
|
|
|
|
} |
|
|
|
|
//Zero-terminate string
|
|
|
|
|
*ret=0; |
|
|
|
|
*ret = 0; |
|
|
|
|
//All done :)
|
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
p+=strlen(p)+1; //Skip past end of string and \0 terminator
|
|
|
|
|
p += strlen(p) + 1; //Skip past end of string and \0 terminator
|
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
@ -237,7 +247,7 @@ void ICACHE_FLASH_ATTR httpdHeader(HttpdConnData *conn, const char *field, const |
|
|
|
|
char buff[256]; |
|
|
|
|
int l; |
|
|
|
|
|
|
|
|
|
l=os_sprintf(buff, "%s: %s\r\n", field, val); |
|
|
|
|
l = os_sprintf(buff, "%s: %s\r\n", field, val); |
|
|
|
|
httpdSend(conn, buff, l); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -251,13 +261,13 @@ void ICACHE_FLASH_ATTR httpdEndHeaders(HttpdConnData *conn) { |
|
|
|
|
void ICACHE_FLASH_ATTR httpdRedirect(HttpdConnData *conn, char *newUrl) { |
|
|
|
|
char buff[1024]; |
|
|
|
|
int l; |
|
|
|
|
l=os_sprintf(buff, "HTTP/1.0 302 Found\r\nServer: esp8266-link\r\nConnection: close\r\nLocation: %s\r\n\r\nRedirecting to %s\r\n", newUrl, newUrl); |
|
|
|
|
l = os_sprintf(buff, "HTTP/1.0 302 Found\r\nServer: esp8266-link\r\nConnection: close\r\nLocation: %s\r\n\r\nRedirecting to %s\r\n", newUrl, newUrl); |
|
|
|
|
httpdSend(conn, buff, l); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//Use this as a cgi function to redirect one url to another.
|
|
|
|
|
int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) { |
|
|
|
|
if (connData->conn==NULL) { |
|
|
|
|
if (connData->conn == NULL) { |
|
|
|
|
//Connection aborted. Clean up.
|
|
|
|
|
return HTTPD_CGI_DONE; |
|
|
|
|
} |
|
|
|
@ -270,25 +280,29 @@ int ICACHE_FLASH_ATTR cgiRedirect(HttpdConnData *connData) { |
|
|
|
|
//the data is seen as a C-string.
|
|
|
|
|
//Returns 1 for success, 0 for out-of-memory.
|
|
|
|
|
int ICACHE_FLASH_ATTR httpdSend(HttpdConnData *conn, const char *data, int len) { |
|
|
|
|
if (len<0) len=strlen(data); |
|
|
|
|
if (conn->priv->sendBuffLen+len>MAX_SENDBUFF_LEN) { |
|
|
|
|
if (len<0) len = strlen(data); |
|
|
|
|
if (conn->priv->sendBuffLen + len>MAX_SENDBUFF_LEN) { |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s ERROR! httpdSend full (%d of %d)\n", |
|
|
|
|
connStr, conn->priv->sendBuffLen, MAX_SENDBUFF_LEN); |
|
|
|
|
#endif |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
os_memcpy(conn->priv->sendBuff+conn->priv->sendBuffLen, data, len); |
|
|
|
|
conn->priv->sendBuffLen+=len; |
|
|
|
|
os_memcpy(conn->priv->sendBuff + conn->priv->sendBuffLen, data, len); |
|
|
|
|
conn->priv->sendBuffLen += len; |
|
|
|
|
return 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//Helper function to send any data in conn->priv->sendBuff
|
|
|
|
|
static void ICACHE_FLASH_ATTR xmitSendBuff(HttpdConnData *conn) { |
|
|
|
|
if (conn->priv->sendBuffLen!=0) { |
|
|
|
|
if (conn->priv->sendBuffLen != 0) { |
|
|
|
|
sint8 status = espconn_sent(conn->conn, (uint8_t*)conn->priv->sendBuff, conn->priv->sendBuffLen); |
|
|
|
|
if (status != 0) { |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s ERROR! espconn_sent returned %d\n", connStr, status); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
conn->priv->sendBuffLen=0; |
|
|
|
|
conn->priv->sendBuffLen = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -296,31 +310,33 @@ static void ICACHE_FLASH_ATTR xmitSendBuff(HttpdConnData *conn) { |
|
|
|
|
static void ICACHE_FLASH_ATTR httpdSentCb(void *arg) { |
|
|
|
|
debugConn(arg, "httpdSentCb"); |
|
|
|
|
int r; |
|
|
|
|
HttpdConnData *conn=httpdFindConnData(arg); |
|
|
|
|
HttpdConnData *conn = httpdFindConnData(arg); |
|
|
|
|
char sendBuff[MAX_SENDBUFF_LEN]; |
|
|
|
|
|
|
|
|
|
if (conn==NULL) return; |
|
|
|
|
conn->priv->sendBuff=sendBuff; |
|
|
|
|
conn->priv->sendBuffLen=0; |
|
|
|
|
if (conn == NULL) return; |
|
|
|
|
conn->priv->sendBuff = sendBuff; |
|
|
|
|
conn->priv->sendBuffLen = 0; |
|
|
|
|
|
|
|
|
|
if (conn->cgi==NULL) { //Marked for destruction?
|
|
|
|
|
if (conn->cgi == NULL) { //Marked for destruction?
|
|
|
|
|
//os_printf("Closing 0x%p/0x%p->0x%p\n", arg, conn->conn, conn);
|
|
|
|
|
espconn_disconnect(conn->conn); // we will get a disconnect callback
|
|
|
|
|
return; //No need to call xmitSendBuff.
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
r=conn->cgi(conn); //Execute cgi fn.
|
|
|
|
|
if (r==HTTPD_CGI_DONE) { |
|
|
|
|
conn->cgi=NULL; //mark for destruction.
|
|
|
|
|
r = conn->cgi(conn); //Execute cgi fn.
|
|
|
|
|
if (r == HTTPD_CGI_DONE) { |
|
|
|
|
conn->cgi = NULL; //mark for destruction.
|
|
|
|
|
} |
|
|
|
|
if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) { |
|
|
|
|
if (r == HTTPD_CGI_NOTFOUND || r == HTTPD_CGI_AUTHENTICATED) { |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s ERROR! Bad CGI code %d\n", connStr, r); |
|
|
|
|
conn->cgi=NULL; //mark for destruction.
|
|
|
|
|
#endif |
|
|
|
|
conn->cgi = NULL; //mark for destruction.
|
|
|
|
|
} |
|
|
|
|
xmitSendBuff(conn); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nNot Found.\r\n"; |
|
|
|
|
static const char *httpNotFoundHeader = "HTTP/1.0 404 Not Found\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nNot Found.\r\n"; |
|
|
|
|
|
|
|
|
|
//This is called when the headers have been received and the connection is ready to send
|
|
|
|
|
//the result headers and data.
|
|
|
|
@ -328,53 +344,59 @@ static const char *httpNotFoundHeader="HTTP/1.0 404 Not Found\r\nConnection: clo |
|
|
|
|
//find the next cgi function, wait till the cgi data is sent or close up the connection.
|
|
|
|
|
static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) { |
|
|
|
|
int r; |
|
|
|
|
int i=0; |
|
|
|
|
if (conn->url==NULL) { |
|
|
|
|
int i = 0; |
|
|
|
|
if (conn->url == NULL) { |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s WtF? url = NULL\n", connStr); |
|
|
|
|
#endif |
|
|
|
|
return; //Shouldn't happen
|
|
|
|
|
} |
|
|
|
|
//See if we can find a CGI that's happy to handle the request.
|
|
|
|
|
while (1) { |
|
|
|
|
//Look up URL in the built-in URL table.
|
|
|
|
|
while (builtInUrls[i].url!=NULL) { |
|
|
|
|
int match=0; |
|
|
|
|
while (builtInUrls[i].url != NULL) { |
|
|
|
|
int match = 0; |
|
|
|
|
//See if there's a literal match
|
|
|
|
|
if (os_strcmp(builtInUrls[i].url, conn->url)==0) match=1; |
|
|
|
|
if (os_strcmp(builtInUrls[i].url, conn->url) == 0) match = 1; |
|
|
|
|
//See if there's a wildcard match
|
|
|
|
|
if (builtInUrls[i].url[os_strlen(builtInUrls[i].url)-1]=='*' && |
|
|
|
|
os_strncmp(builtInUrls[i].url, conn->url, os_strlen(builtInUrls[i].url)-1)==0) match=1; |
|
|
|
|
if (builtInUrls[i].url[os_strlen(builtInUrls[i].url) - 1] == '*' && |
|
|
|
|
os_strncmp(builtInUrls[i].url, conn->url, os_strlen(builtInUrls[i].url) - 1) == 0) match = 1; |
|
|
|
|
if (match) { |
|
|
|
|
//os_printf("Is url index %d\n", i);
|
|
|
|
|
conn->cgiData=NULL; |
|
|
|
|
conn->cgi=builtInUrls[i].cgiCb; |
|
|
|
|
conn->cgiArg=builtInUrls[i].cgiArg; |
|
|
|
|
conn->cgiData = NULL; |
|
|
|
|
conn->cgi = builtInUrls[i].cgiCb; |
|
|
|
|
conn->cgiArg = builtInUrls[i].cgiArg; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
i++; |
|
|
|
|
} |
|
|
|
|
if (builtInUrls[i].url==NULL) { |
|
|
|
|
if (builtInUrls[i].url == NULL) { |
|
|
|
|
//Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just
|
|
|
|
|
//generate a built-in 404 to handle this.
|
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s %s not found. 404!\n", connStr, conn->url); |
|
|
|
|
#endif |
|
|
|
|
httpdSend(conn, httpNotFoundHeader, -1); |
|
|
|
|
xmitSendBuff(conn); |
|
|
|
|
conn->cgi=NULL; //mark for destruction
|
|
|
|
|
conn->cgi = NULL; //mark for destruction
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//Okay, we have a CGI function that matches the URL. See if it wants to handle the
|
|
|
|
|
//particular URL we're supposed to handle.
|
|
|
|
|
r=conn->cgi(conn); |
|
|
|
|
if (r==HTTPD_CGI_MORE) { |
|
|
|
|
r = conn->cgi(conn); |
|
|
|
|
if (r == HTTPD_CGI_MORE) { |
|
|
|
|
//Yep, it's happy to do so and has more data to send.
|
|
|
|
|
xmitSendBuff(conn); |
|
|
|
|
return; |
|
|
|
|
} else if (r==HTTPD_CGI_DONE) { |
|
|
|
|
} |
|
|
|
|
else if (r == HTTPD_CGI_DONE) { |
|
|
|
|
//Yep, it's happy to do so and already is done sending data.
|
|
|
|
|
xmitSendBuff(conn); |
|
|
|
|
conn->cgi=NULL; //mark conn for destruction
|
|
|
|
|
conn->cgi = NULL; //mark conn for destruction
|
|
|
|
|
return; |
|
|
|
|
} else if (r==HTTPD_CGI_NOTFOUND || r==HTTPD_CGI_AUTHENTICATED) { |
|
|
|
|
} |
|
|
|
|
else if (r == HTTPD_CGI_NOTFOUND || r == HTTPD_CGI_AUTHENTICATED) { |
|
|
|
|
//URL doesn't want to handle the request: either the data isn't found or there's no
|
|
|
|
|
//need to generate a login screen.
|
|
|
|
|
i++; //look at next url the next iteration of the loop.
|
|
|
|
@ -387,10 +409,11 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { |
|
|
|
|
int i; |
|
|
|
|
char first_line = false; |
|
|
|
|
|
|
|
|
|
if (os_strncmp(h, "GET ", 4)==0) { |
|
|
|
|
if (os_strncmp(h, "GET ", 4) == 0) { |
|
|
|
|
conn->requestType = HTTPD_METHOD_GET; |
|
|
|
|
first_line = true; |
|
|
|
|
} else if (os_strncmp(h, "POST ", 5)==0) { |
|
|
|
|
} |
|
|
|
|
else if (os_strncmp(h, "POST ", 5) == 0) { |
|
|
|
|
conn->requestType = HTTPD_METHOD_POST; |
|
|
|
|
first_line = true; |
|
|
|
|
} |
|
|
|
@ -399,49 +422,56 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) { |
|
|
|
|
char *e; |
|
|
|
|
|
|
|
|
|
//Skip past the space after POST/GET
|
|
|
|
|
i=0; |
|
|
|
|
while (h[i]!=' ') i++; |
|
|
|
|
conn->url=h+i+1; |
|
|
|
|
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
|
|
|
|
|
e = (char*)os_strstr(conn->url, " "); |
|
|
|
|
if (e == NULL) return; //wtf?
|
|
|
|
|
*e = 0; //terminate url part
|
|
|
|
|
|
|
|
|
|
// Count number of open connections
|
|
|
|
|
int open = 0; |
|
|
|
|
for (int j=0; j<MAX_CONN; j++) if (connData[j].conn != NULL) open++; |
|
|
|
|
|
|
|
|
|
for (int j = 0; j<MAX_CONN; j++) if (connData[j].conn != NULL) open++; |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s %s %s (%d conn open)\n", connStr, |
|
|
|
|
conn->requestType == HTTPD_METHOD_GET ? "GET" : "POST", conn->url, open); |
|
|
|
|
#endif |
|
|
|
|
//Parse out the URL part before the GET parameters.
|
|
|
|
|
conn->getArgs=(char*)os_strstr(conn->url, "?"); |
|
|
|
|
if (conn->getArgs!=0) { |
|
|
|
|
*conn->getArgs=0; |
|
|
|
|
conn->getArgs = (char*)os_strstr(conn->url, "?"); |
|
|
|
|
if (conn->getArgs != 0) { |
|
|
|
|
*conn->getArgs = 0; |
|
|
|
|
conn->getArgs++; |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s args = %s\n", connStr, conn->getArgs); |
|
|
|
|
} else { |
|
|
|
|
conn->getArgs=NULL; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
conn->getArgs = NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else if (os_strncmp(h, "Content-Length:", 15)==0) { |
|
|
|
|
i=15; |
|
|
|
|
} |
|
|
|
|
else if (os_strncmp(h, "Content-Length:", 15) == 0) { |
|
|
|
|
i = 15; |
|
|
|
|
//Skip trailing spaces
|
|
|
|
|
while (h[i]==' ') i++; |
|
|
|
|
while (h[i] == ' ') i++; |
|
|
|
|
//Get POST data length
|
|
|
|
|
conn->post->len=atoi(h+i); |
|
|
|
|
conn->post->len = atoi(h + i); |
|
|
|
|
|
|
|
|
|
// Allocate the buffer
|
|
|
|
|
if (conn->post->len > MAX_POST) { |
|
|
|
|
// we'll stream this in in chunks
|
|
|
|
|
conn->post->buffSize = MAX_POST; |
|
|
|
|
} else { |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
conn->post->buffSize = conn->post->len; |
|
|
|
|
} |
|
|
|
|
//os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize);
|
|
|
|
|
conn->post->buff=(char*)os_malloc(conn->post->buffSize + 1); |
|
|
|
|
conn->post->buffLen=0; |
|
|
|
|
} else if (os_strncmp(h, "Content-Type: ", 14)==0) { |
|
|
|
|
conn->post->buff = (char*)os_malloc(conn->post->buffSize + 1); |
|
|
|
|
conn->post->buffLen = 0; |
|
|
|
|
} |
|
|
|
|
else if (os_strncmp(h, "Content-Type: ", 14) == 0) { |
|
|
|
|
if (os_strstr(h, "multipart/form-data")) { |
|
|
|
|
// It's multipart form data so let's pull out the boundary for future use
|
|
|
|
|
char *b; |
|
|
|
@ -462,10 +492,10 @@ static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short |
|
|
|
|
int x; |
|
|
|
|
char *p, *e; |
|
|
|
|
char sendBuff[MAX_SENDBUFF_LEN]; |
|
|
|
|
HttpdConnData *conn=httpdFindConnData(arg); |
|
|
|
|
if (conn==NULL) return; |
|
|
|
|
conn->priv->sendBuff=sendBuff; |
|
|
|
|
conn->priv->sendBuffLen=0; |
|
|
|
|
HttpdConnData *conn = httpdFindConnData(arg); |
|
|
|
|
if (conn == NULL) return; |
|
|
|
|
conn->priv->sendBuff = sendBuff; |
|
|
|
|
conn->priv->sendBuffLen = 0; |
|
|
|
|
|
|
|
|
|
//This is slightly evil/dirty: we abuse conn->post->len as a state variable for where in the http communications we are:
|
|
|
|
|
//<0 (-1): Post len unknown because we're still receiving headers
|
|
|
|
@ -473,38 +503,39 @@ static void ICACHE_FLASH_ATTR httpdRecvCb(void *arg, char *data, unsigned short |
|
|
|
|
//>0: Need to receive post data
|
|
|
|
|
//ToDo: See if we can use something more elegant for this.
|
|
|
|
|
|
|
|
|
|
for (x=0; x<len; x++) { |
|
|
|
|
for (x = 0; x<len; x++) { |
|
|
|
|
if (conn->post->len<0) { |
|
|
|
|
//This byte is a header byte.
|
|
|
|
|
if (conn->priv->headPos!=MAX_HEAD_LEN) conn->priv->head[conn->priv->headPos++]=data[x]; |
|
|
|
|
conn->priv->head[conn->priv->headPos]=0; |
|
|
|
|
if (conn->priv->headPos != MAX_HEAD_LEN) conn->priv->head[conn->priv->headPos++] = data[x]; |
|
|
|
|
conn->priv->head[conn->priv->headPos] = 0; |
|
|
|
|
//Scan for /r/n/r/n. Receiving this indicate the headers end.
|
|
|
|
|
if (data[x]=='\n' && (char *)os_strstr(conn->priv->head, "\r\n\r\n")!=NULL) { |
|
|
|
|
if (data[x] == '\n' && (char *)os_strstr(conn->priv->head, "\r\n\r\n") != NULL) { |
|
|
|
|
//Indicate we're done with the headers.
|
|
|
|
|
conn->post->len=0; |
|
|
|
|
conn->post->len = 0; |
|
|
|
|
//Reset url data
|
|
|
|
|
conn->url=NULL; |
|
|
|
|
conn->url = NULL; |
|
|
|
|
//Iterate over all received headers and parse them.
|
|
|
|
|
p=conn->priv->head; |
|
|
|
|
while(p<(&conn->priv->head[conn->priv->headPos-4])) { |
|
|
|
|
e=(char *)os_strstr(p, "\r\n"); //Find end of header line
|
|
|
|
|
if (e==NULL) break; //Shouldn't happen.
|
|
|
|
|
e[0]=0; //Zero-terminate header
|
|
|
|
|
p = conn->priv->head; |
|
|
|
|
while (p<(&conn->priv->head[conn->priv->headPos - 4])) { |
|
|
|
|
e = (char *)os_strstr(p, "\r\n"); //Find end of header line
|
|
|
|
|
if (e == NULL) break; //Shouldn't happen.
|
|
|
|
|
e[0] = 0; //Zero-terminate header
|
|
|
|
|
httpdParseHeader(p, conn); //and parse it.
|
|
|
|
|
p=e+2; //Skip /r/n (now /0/n)
|
|
|
|
|
p = e + 2; //Skip /r/n (now /0/n)
|
|
|
|
|
} |
|
|
|
|
//If we don't need to receive post data, we can send the response now.
|
|
|
|
|
if (conn->post->len==0) { |
|
|
|
|
if (conn->post->len == 0) { |
|
|
|
|
httpdProcessRequest(conn); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else if (conn->post->len!=0) { |
|
|
|
|
} |
|
|
|
|
else if (conn->post->len != 0) { |
|
|
|
|
//This byte is a POST byte.
|
|
|
|
|
conn->post->buff[conn->post->buffLen++]=data[x]; |
|
|
|
|
conn->post->buff[conn->post->buffLen++] = data[x]; |
|
|
|
|
conn->post->received++; |
|
|
|
|
if (conn->post->buffLen >= conn->post->buffSize || conn->post->received == conn->post->len) { |
|
|
|
|
//Received a chunk of post data
|
|
|
|
|
conn->post->buff[conn->post->buffLen]=0; //zero-terminate, in case the cgi handler knows it can use strings
|
|
|
|
|
conn->post->buff[conn->post->buffLen] = 0; //zero-terminate, in case the cgi handler knows it can use strings
|
|
|
|
|
//Send the response.
|
|
|
|
|
httpdProcessRequest(conn); |
|
|
|
|
conn->post->buffLen = 0; |
|
|
|
@ -525,7 +556,9 @@ static void ICACHE_FLASH_ATTR httpdDisconCb(void *arg) { |
|
|
|
|
static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) { |
|
|
|
|
debugConn(arg, "httpdReconCb"); |
|
|
|
|
HttpdConnData *conn = httpdFindConnData(arg); |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s ***** reset, err=%d\n", connStr, err); |
|
|
|
|
#endif |
|
|
|
|
if (conn == NULL) return; |
|
|
|
|
httpdRetireConn(conn); |
|
|
|
|
} |
|
|
|
@ -533,33 +566,37 @@ static void ICACHE_FLASH_ATTR httpdReconCb(void *arg, sint8 err) { |
|
|
|
|
|
|
|
|
|
static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) { |
|
|
|
|
debugConn(arg, "httpdConnectCb"); |
|
|
|
|
struct espconn *conn=arg; |
|
|
|
|
struct espconn *conn = arg; |
|
|
|
|
int i; |
|
|
|
|
//Find empty conndata in pool
|
|
|
|
|
for (i=0; i<MAX_CONN; i++) if (connData[i].conn==NULL) break; |
|
|
|
|
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);
|
|
|
|
|
if (i==MAX_CONN) { |
|
|
|
|
if (i == MAX_CONN) { |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s Aiee, conn pool overflow!\n", connStr); |
|
|
|
|
#endif |
|
|
|
|
espconn_disconnect(conn); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if 0 |
|
|
|
|
int num = 0; |
|
|
|
|
for (int j=0; j<MAX_CONN; j++) if (connData[j].conn != NULL) num++; |
|
|
|
|
os_printf("%s Connect (%d open)\n", connStr, num+1); |
|
|
|
|
for (int j = 0; j<MAX_CONN; j++) if (connData[j].conn != NULL) num++; |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("%s Connect (%d open)\n", connStr, num + 1); |
|
|
|
|
#endif |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
connData[i].priv=&connPrivData[i]; |
|
|
|
|
connData[i].conn=conn; |
|
|
|
|
connData[i].priv = &connPrivData[i]; |
|
|
|
|
connData[i].conn = conn; |
|
|
|
|
connData[i].remote_port = conn->proto.tcp->remote_port; |
|
|
|
|
os_memcpy(connData[i].remote_ip, conn->proto.tcp->remote_ip, 4); |
|
|
|
|
connData[i].priv->headPos=0; |
|
|
|
|
connData[i].post=&connPostData[i]; |
|
|
|
|
connData[i].post->buff=NULL; |
|
|
|
|
connData[i].post->buffLen=0; |
|
|
|
|
connData[i].post->received=0; |
|
|
|
|
connData[i].post->len=-1; |
|
|
|
|
connData[i].priv->headPos = 0; |
|
|
|
|
connData[i].post = &connPostData[i]; |
|
|
|
|
connData[i].post->buff = NULL; |
|
|
|
|
connData[i].post->buffLen = 0; |
|
|
|
|
connData[i].post->received = 0; |
|
|
|
|
connData[i].post->len = -1; |
|
|
|
|
connData[i].startTime = system_get_time(); |
|
|
|
|
|
|
|
|
|
espconn_regist_recvcb(conn, httpdRecvCb); |
|
|
|
@ -567,23 +604,24 @@ static void ICACHE_FLASH_ATTR httpdConnectCb(void *arg) { |
|
|
|
|
espconn_regist_disconcb(conn, httpdDisconCb); |
|
|
|
|
espconn_regist_sentcb(conn, httpdSentCb); |
|
|
|
|
|
|
|
|
|
espconn_set_opt(conn, ESPCONN_REUSEADDR|ESPCONN_NODELAY); |
|
|
|
|
espconn_set_opt(conn, ESPCONN_REUSEADDR | ESPCONN_NODELAY); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
//Httpd initialization routine. Call this to kick off webserver functionality.
|
|
|
|
|
void ICACHE_FLASH_ATTR httpdInit(HttpdBuiltInUrl *fixedUrls, int port) { |
|
|
|
|
int i; |
|
|
|
|
|
|
|
|
|
for (i=0; i<MAX_CONN; i++) { |
|
|
|
|
connData[i].conn=NULL; |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
httpdConn.type = ESPCONN_TCP; |
|
|
|
|
httpdConn.state = ESPCONN_NONE; |
|
|
|
|
httpdTcp.local_port = port; |
|
|
|
|
httpdConn.proto.tcp = &httpdTcp; |
|
|
|
|
builtInUrls = fixedUrls; |
|
|
|
|
#ifdef HTTPD_DBG |
|
|
|
|
os_printf("Httpd init, conn=%p\n", &httpdConn); |
|
|
|
|
#endif |
|
|
|
|
espconn_regist_connectcb(&httpdConn, httpdConnectCb); |
|
|
|
|
espconn_accept(&httpdConn); |
|
|
|
|
espconn_tcp_set_max_con_allow(&httpdConn, MAX_CONN); |
|
|
|
|