improved template to support multipart substitution
This commit is contained in:
+23
-11
@@ -5,9 +5,9 @@ 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.
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@@ -92,7 +92,7 @@ const char ICACHE_FLASH_ATTR *httpdGetMimetype(char *url) {
|
||||
char *ext=url+(strlen(url)-1);
|
||||
while (ext!=url && *ext!='.') ext--;
|
||||
if (*ext=='.') ext++;
|
||||
|
||||
|
||||
//ToDo: strcmp is case sensitive; we may want to do case-intensive matching here...
|
||||
while (mimeTypes[i].ext!=NULL && strcmp(ext, mimeTypes[i].ext)!=0) i++;
|
||||
return mimeTypes[i].mimetype;
|
||||
@@ -172,7 +172,7 @@ int ICACHE_FLASH_ATTR httpdUrlDecode(char *val, int valLen, char *ret, int retLe
|
||||
//Find a specific arg in a string of get- or post-data.
|
||||
//Line is the string of post/get-data, arg is the name of the value to find. The
|
||||
//zero-terminated result is written in buff, with at most buffLen bytes used. The
|
||||
//function returns the length of the result, or -1 if the value wasn't found. The
|
||||
//function returns the length of the result, or -1 if the value wasn't found. The
|
||||
//returned string will be urldecoded already.
|
||||
int ICACHE_FLASH_ATTR httpdFindArg(char *line, char *arg, char *buff, int buffLen) {
|
||||
char *p, *e;
|
||||
@@ -233,9 +233,9 @@ void ICACHE_FLASH_ATTR httpdDisableTransferEncoding(HttpdConnData *conn) {
|
||||
void ICACHE_FLASH_ATTR httpdStartResponse(HttpdConnData *conn, int code) {
|
||||
char buff[256];
|
||||
int l;
|
||||
l=sprintf(buff, "HTTP/1.%d %d OK\r\nServer: esp8266-httpd/"HTTPDVER"\r\n%s\r\n",
|
||||
(conn->priv->flags&HFL_HTTP11)?1:0,
|
||||
code,
|
||||
l=sprintf(buff, "HTTP/1.%d %d OK\r\nServer: esp8266-httpd/"HTTPDVER"\r\n%s\r\n",
|
||||
(conn->priv->flags&HFL_HTTP11)?1:0,
|
||||
code,
|
||||
(conn->priv->flags&HFL_CHUNKED)?"Transfer-Encoding: chunked":"Connection: close");
|
||||
httpdSend(conn, buff, l);
|
||||
}
|
||||
@@ -524,7 +524,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
|
||||
httpd_printf("%s not found. 404!\n", conn->url);
|
||||
conn->cgi=cgiNotFound;
|
||||
}
|
||||
|
||||
|
||||
//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);
|
||||
@@ -553,7 +553,7 @@ static void ICACHE_FLASH_ATTR httpdProcessRequest(HttpdConnData *conn) {
|
||||
static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
|
||||
int i;
|
||||
char firstLine=0;
|
||||
|
||||
|
||||
if (strncmp(h, "GET ", 4)==0) {
|
||||
conn->requestType = HTTPD_METHOD_GET;
|
||||
firstLine=1;
|
||||
@@ -568,7 +568,7 @@ static void ICACHE_FLASH_ATTR httpdParseHeader(char *h, HttpdConnData *conn) {
|
||||
|
||||
if (firstLine) {
|
||||
char *e;
|
||||
|
||||
|
||||
//Skip past the space after POST/GET
|
||||
i=0;
|
||||
while (h[i]!=' ') i++;
|
||||
@@ -737,6 +737,18 @@ int ICACHE_FLASH_ATTR httpdConnectCb(ConnTypePtr conn, char *remIp, int remPort)
|
||||
httpd_printf("Conn req from %d.%d.%d.%d:%d, using pool slot %d\n", remIp[0]&0xff, remIp[1]&0xff, remIp[2]&0xff, remIp[3]&0xff, remPort, i);
|
||||
if (i==HTTPD_MAX_CONNECTIONS) {
|
||||
httpd_printf("Aiee, conn pool overflow!\n");
|
||||
|
||||
|
||||
// added by mightypork
|
||||
if (system_get_free_heap_size() < 29000) {
|
||||
// this probably means we got flooded by someone spamming F5
|
||||
// better restart
|
||||
|
||||
httpd_printf("\x1b[31;1mLow heap & con pool overflow, GOING FOR RESTART.\x1b[0m\n");
|
||||
system_restart();
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
connData[i]=malloc(sizeof(HttpdConnData));
|
||||
|
||||
+132
-55
@@ -18,24 +18,32 @@ Connector to let httpd use the espfs filesystem to serve the files in it.
|
||||
|
||||
// The static files marked with FLAG_GZIP are compressed and will be served with GZIP compression.
|
||||
// If the client does not advertise that he accepts GZIP send following warning message (telnet users for e.g.)
|
||||
static const char *gzipNonSupportedMessage = "HTTP/1.0 501 Not implemented\r\nServer: esp8266-httpd/"HTTPDVER"\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: 52\r\n\r\nYour browser does not accept gzip-compressed data.\r\n";
|
||||
static const char *gzipNonSupportedMessage = "HTTP/1.0 501 Not implemented\r\n"
|
||||
"Server: esp8266-httpd/"HTTPDVER"\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: 52\r\n"
|
||||
"\r\n"
|
||||
"Your browser does not accept gzip-compressed data.\r\n";
|
||||
|
||||
|
||||
EspFsFile *tryOpenIndex(char *buff, const char *path) {
|
||||
EspFsFile *tryOpenIndex(const char *path)
|
||||
{
|
||||
// Try appending index.tpl
|
||||
char fname[100];
|
||||
|
||||
size_t url_len = strlen(path);
|
||||
strcpy(buff, path); // add current path
|
||||
strcpy(fname, path); // add current path
|
||||
|
||||
// add slash if not already ending with slash
|
||||
if (path[url_len-1] != '/') {
|
||||
buff[url_len++] = '/';
|
||||
if (path[url_len - 1] != '/') {
|
||||
fname[url_len++] = '/';
|
||||
}
|
||||
|
||||
// add index
|
||||
strcpy(buff + url_len, "index.tpl");
|
||||
strcpy(fname + url_len, "index.tpl");
|
||||
|
||||
return espFsOpen(buff);
|
||||
return espFsOpen(fname);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,28 +51,29 @@ EspFsFile *tryOpenIndex(char *buff, const char *path) {
|
||||
//This is a catch-all cgi function. It takes the url passed to it, looks up the corresponding
|
||||
//path in the filesystem and if it exists, passes the file through. This simulates what a normal
|
||||
//webserver would do with static files.
|
||||
int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) {
|
||||
EspFsFile *file=connData->cgiData;
|
||||
int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData)
|
||||
{
|
||||
EspFsFile *file = connData->cgiData;
|
||||
int len;
|
||||
char buff[1024];
|
||||
char acceptEncodingBuffer[64];
|
||||
int isGzip;
|
||||
|
||||
if (connData->conn==NULL) {
|
||||
if (connData->conn == NULL) {
|
||||
//Connection aborted. Clean up.
|
||||
espFsClose(file);
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
if (file==NULL) {
|
||||
if (file == NULL) {
|
||||
//First call to this cgi. Open the file so we can read it.
|
||||
file=espFsOpen(connData->url);
|
||||
if (file==NULL) {
|
||||
file = espFsOpen(connData->url);
|
||||
if (file == NULL) {
|
||||
|
||||
// file not found
|
||||
file = tryOpenIndex(buff, connData->url);
|
||||
file = tryOpenIndex(connData->url);
|
||||
|
||||
if (file==NULL) {
|
||||
if (file == NULL) {
|
||||
return HTTPD_CGI_NOTFOUND;
|
||||
}
|
||||
|
||||
@@ -89,7 +98,7 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) {
|
||||
}
|
||||
}
|
||||
|
||||
connData->cgiData=file;
|
||||
connData->cgiData = file;
|
||||
httpdStartResponse(connData, 200);
|
||||
httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url));
|
||||
if (isGzip) {
|
||||
@@ -100,9 +109,9 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) {
|
||||
return HTTPD_CGI_MORE;
|
||||
}
|
||||
|
||||
len=espFsRead(file, buff, 1024);
|
||||
if (len>0) httpdSend(connData, buff, len);
|
||||
if (len!=1024) {
|
||||
len = espFsRead(file, buff, 1024);
|
||||
if (len > 0) httpdSend(connData, buff, len);
|
||||
if (len != 1024) {
|
||||
//We're done.
|
||||
espFsClose(file);
|
||||
return HTTPD_CGI_DONE;
|
||||
@@ -115,23 +124,33 @@ int ICACHE_FLASH_ATTR cgiEspFsHook(HttpdConnData *connData) {
|
||||
|
||||
//cgiEspFsTemplate can be used as a template.
|
||||
|
||||
#define TEMPLATE_CHUNK 1024
|
||||
|
||||
typedef struct {
|
||||
EspFsFile *file;
|
||||
void *tplArg;
|
||||
char token[64];
|
||||
int tokenPos;
|
||||
|
||||
char buff[TEMPLATE_CHUNK + 1];
|
||||
|
||||
bool chunk_resume;
|
||||
int buff_len;
|
||||
int buff_x;
|
||||
int buff_sp;
|
||||
char *buff_e;
|
||||
} TplData;
|
||||
|
||||
typedef void (* TplCallback)(HttpdConnData *connData, char *token, void **arg);
|
||||
typedef int (* TplCallback)(HttpdConnData *connData, char *token, void **arg);
|
||||
|
||||
int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData) {
|
||||
TplData *tpd=connData->cgiData;
|
||||
int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData)
|
||||
{
|
||||
TplData *tpd = connData->cgiData;
|
||||
int len;
|
||||
int x, sp=0;
|
||||
char *e=NULL;
|
||||
char buff[1025];
|
||||
int x, sp = 0;
|
||||
char *e = NULL;
|
||||
|
||||
if (connData->conn==NULL) {
|
||||
if (connData->conn == NULL) {
|
||||
//Connection aborted. Clean up.
|
||||
((TplCallback)(connData->cgiArg))(connData, NULL, &tpd->tplArg);
|
||||
espFsClose(tpd->file);
|
||||
@@ -139,77 +158,135 @@ int ICACHE_FLASH_ATTR cgiEspFsTemplate(HttpdConnData *connData) {
|
||||
return HTTPD_CGI_DONE;
|
||||
}
|
||||
|
||||
if (tpd==NULL) {
|
||||
if (tpd == NULL) {
|
||||
//First call to this cgi. Open the file so we can read it.
|
||||
tpd=(TplData *)malloc(sizeof(TplData));
|
||||
tpd->file=espFsOpen(connData->url);
|
||||
if (tpd->file==NULL) {
|
||||
tpd = (TplData *)malloc(sizeof(TplData));
|
||||
|
||||
tpd->chunk_resume = false;
|
||||
|
||||
tpd->file = espFsOpen(connData->url);
|
||||
if (tpd->file == NULL) {
|
||||
|
||||
// file not found
|
||||
tpd->file = tryOpenIndex(buff, connData->url);
|
||||
tpd->file = tryOpenIndex(connData->url);
|
||||
|
||||
if (tpd->file==NULL) {
|
||||
if (tpd->file == NULL) {
|
||||
espFsClose(tpd->file);
|
||||
free(tpd);
|
||||
return HTTPD_CGI_NOTFOUND;
|
||||
}
|
||||
}
|
||||
tpd->tplArg=NULL;
|
||||
tpd->tokenPos=-1;
|
||||
tpd->tplArg = NULL;
|
||||
tpd->tokenPos = -1;
|
||||
if (espFsFlags(tpd->file) & FLAG_GZIP) {
|
||||
httpd_printf("cgiEspFsTemplate: Trying to use gzip-compressed file %s as template!\n", connData->url);
|
||||
espFsClose(tpd->file);
|
||||
free(tpd);
|
||||
return HTTPD_CGI_NOTFOUND;
|
||||
}
|
||||
connData->cgiData=tpd;
|
||||
connData->cgiData = tpd;
|
||||
httpdStartResponse(connData, 200);
|
||||
httpdHeader(connData, "Content-Type", httpdGetMimetype(connData->url));
|
||||
httpdEndHeaders(connData);
|
||||
return HTTPD_CGI_MORE;
|
||||
}
|
||||
|
||||
len=espFsRead(tpd->file, buff, 1024);
|
||||
if (len>0) {
|
||||
sp=0;
|
||||
e=buff;
|
||||
for (x=0; x<len; x++) {
|
||||
if (tpd->tokenPos==-1) {
|
||||
char *buff = tpd->buff;
|
||||
|
||||
|
||||
// resume the parser state from the last token,
|
||||
// if subst. func wants more data to be sent.
|
||||
if (tpd->chunk_resume) {
|
||||
printf("\nResuming chunk...\n");
|
||||
|
||||
len = tpd->buff_len;
|
||||
e = tpd->buff_e;
|
||||
sp = tpd->buff_sp;
|
||||
x = tpd->buff_x;
|
||||
|
||||
} else {
|
||||
len = espFsRead(tpd->file, buff, TEMPLATE_CHUNK);
|
||||
tpd->buff_len = len;
|
||||
|
||||
e = buff;
|
||||
sp = 0;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
printf("*a");
|
||||
for (; x < len; x++) {
|
||||
printf("*b");
|
||||
if (tpd->tokenPos == -1) {
|
||||
//Inside ordinary text.
|
||||
if (buff[x]=='%') {
|
||||
if (buff[x] == '%') {
|
||||
//Send raw data up to now
|
||||
if (sp!=0) httpdSend(connData, e, sp);
|
||||
sp=0;
|
||||
if (sp != 0) httpdSend(connData, e, sp);
|
||||
sp = 0;
|
||||
//Go collect token chars.
|
||||
tpd->tokenPos=0;
|
||||
tpd->tokenPos = 0;
|
||||
} else {
|
||||
sp++;
|
||||
}
|
||||
} else {
|
||||
if (buff[x]=='%') {
|
||||
if (tpd->tokenPos==0) {
|
||||
printf("*c");
|
||||
if (buff[x] == '%') {
|
||||
printf("*d");
|
||||
if (tpd->tokenPos == 0) {
|
||||
//This is the second % of a %% escape string.
|
||||
//Send a single % and resume with the normal program flow.
|
||||
httpdSend(connData, "%", 1);
|
||||
} else {
|
||||
//This is an actual token.
|
||||
tpd->token[tpd->tokenPos++]=0; //zero-terminate token
|
||||
((TplCallback)(connData->cgiArg))(connData, tpd->token, &tpd->tplArg);
|
||||
printf("*e");
|
||||
|
||||
if (!tpd->chunk_resume) {
|
||||
//This is an actual token.
|
||||
tpd->token[tpd->tokenPos++] = 0; //zero-terminate token
|
||||
} else {
|
||||
printf("*f");
|
||||
}
|
||||
|
||||
tpd->chunk_resume = false;
|
||||
|
||||
int status = ((TplCallback)(connData->cgiArg))(connData, tpd->token, &tpd->tplArg);
|
||||
|
||||
if (status == HTTPD_CGI_MORE) {
|
||||
printf("\nSaving template pos & continuing in next run..\n");
|
||||
|
||||
// wants to send more in this token's place.....
|
||||
tpd->chunk_resume = true;
|
||||
|
||||
tpd->buff_len = len;
|
||||
tpd->buff_e = e;
|
||||
tpd->buff_sp = sp;
|
||||
tpd->buff_x = x;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Go collect normal chars again.
|
||||
e=&buff[x+1];
|
||||
tpd->tokenPos=-1;
|
||||
e = &buff[x + 1];
|
||||
tpd->tokenPos = -1;
|
||||
} else {
|
||||
if (tpd->tokenPos<(sizeof(tpd->token)-1)) tpd->token[tpd->tokenPos++]=buff[x];
|
||||
if (tpd->tokenPos < (sizeof(tpd->token) - 1)) tpd->token[tpd->tokenPos++] = buff[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tpd->chunk_resume) {
|
||||
return HTTPD_CGI_MORE;
|
||||
}
|
||||
|
||||
//Send remaining bit.
|
||||
if (sp!=0) httpdSend(connData, e, sp);
|
||||
if (len!=1024) {
|
||||
if (sp != 0) httpdSend(connData, e, sp);
|
||||
if (len != TEMPLATE_CHUNK) {
|
||||
|
||||
//We're done.
|
||||
|
||||
// let the cgi func clean it's stuff
|
||||
((TplCallback)(connData->cgiArg))(connData, NULL, &tpd->tplArg);
|
||||
|
||||
espFsClose(tpd->file);
|
||||
free(tpd);
|
||||
return HTTPD_CGI_DONE;
|
||||
|
||||
Reference in New Issue
Block a user