diff --git a/html/index.tpl b/html/index.tpl
index 80e05d1..0fbef91 100644
--- a/html/index.tpl
+++ b/html/index.tpl
@@ -15,6 +15,7 @@ been loaded %counter% times.
can upgrade the firmware of this module.
You can download the raw contents of the SPI flash rom
And because I can, here's a link to my website
diff --git a/html/test/index.html b/html/test/index.html
new file mode 100644
index 0000000..01f4121
--- /dev/null
+++ b/html/test/index.html
@@ -0,0 +1,9 @@
+Webserver test
+
+
+
+
+
+
Initializing test...
+
+
\ No newline at end of file
diff --git a/html/test/test.cgi b/html/test/test.cgi
new file mode 100644
index 0000000..ed00813
Binary files /dev/null and b/html/test/test.cgi differ
diff --git a/html/test/test.js b/html/test/test.js
new file mode 100644
index 0000000..03c0cd4
--- /dev/null
+++ b/html/test/test.js
@@ -0,0 +1,205 @@
+
+/*
+Code to test the webserver. This depends on:
+- The cat images being available, for concurrent espfs testing
+- the test.cgi script available, for generic data mangling tests
+
+
+This test does a max of 4 requests in parallel. The nonos SDK supports a max of
+5 connections; the default libesphttpd setting is 4 sockets at a time. Unfortunately,
+the nonos sdk just closes all sockets opened after the available sockets are opened,
+instead of queueing them until a socket frees up.
+*/
+
+
+function log(line) {
+ $("#log").insertAdjacentHTML('beforeend', line+' ');
+}
+
+
+//Load an image multiple times in parallel
+function testParLdImg(url, ct, doneFn) {
+ var im=[];
+ var state={"loaded":0, "count":ct, "doneFn": doneFn, "error":false};
+ for (var x=0; x=200 && xhr.status<300) {
+ if (xhr.response.length==this.len) {
+ log("Downloaded "+this.len+" bytes successfully.");
+ this.doneFn(true);
+ } else {
+ log("Downloaded "+xhr.response.length+" bytes successfully, but needed "+this.len+"!");
+ this.doneFn(false);
+ }
+ } else if (xhr.readyState==4) {
+ log("Failed! Error "+xhr.status);
+ this.doneFn(false);
+ }
+ }.bind(state);
+ //If the webbrowser enables it, show progress.
+ if (typeof xhr.onprogress != 'undefined') {
+ xhr.onprogress=function(e) {
+ if (Date.now()>this.ts+2000) {
+ log("..."+Math.floor(e.loaded*100/this.len).toString()+"%");
+ this.ts=Date.now();
+ }
+ }.bind(state);
+ }
+ xhr.send();
+}
+
+
+function testUploadCgi(len, doneFn) {
+ var xhr=j();
+ var state={"len":len, "doneFn":doneFn, "ts": Date.now()};
+ var data="";
+ for (var x=0; x=200 && xhr.status<300) {
+ var ulen=parseInt(xhr.responseText);
+ if (ulen==this.len) {
+ log("Uploaded "+this.len+" bytes successfully.");
+ this.doneFn(true);
+ } else {
+ log("Webserver received "+ulen+" bytes successfully, but sent "+this.len+"!");
+ this.doneFn(false);
+ }
+ } else if (xhr.readyState==4) {
+ log("Failed! Error "+xhr.status);
+ this.doneFn(false);
+ }
+ }.bind(state);
+ //If the webbrowser enables it, show progress.
+ if (typeof xhr.upload.onprogress != 'undefined') {
+ xhr.upload.onprogress=function(e) {
+ if (Date.now()>this.ts+2000) {
+ log("..."+Math.floor(e.loaded*100/e.total).toString()+"%");
+ this.ts=Date.now();
+ }
+ }.bind(state);
+ }
+ //Upload the file
+ xhr.send(data);
+}
+
+function hammerNext(state, xhr) {
+ if (state.done==state.count) {
+ state.doneFn(!state.error);
+ }
+ if (state.started==state.count) return;
+ xhr.open("GET", "test.cgi?len="+state.len+"&nocache="+Math.floor(Math.random()*100000).toString());
+ xhr.onreadystatechange=function(xhr) {
+ if (xhr.readyState==4 && xhr.status>=200 && xhr.status<300) {
+ if (xhr.response.length==this.len) {
+ state.done++;
+ hammerNext(this, xhr);
+ } else {
+ log("Downloaded "+xhr.response.length+" bytes successfully, but needed "+this.len+"!");
+ state.done++;
+ hammerNext(this, xhr);
+ }
+ } else if (xhr.readyState==4) {
+ log("Failed! Error "+xhr.status);
+ state.done++;
+ hammerNext(this, xhr);
+ }
+ }.bind(state, xhr);
+ //If the webbrowser enables it, show progress.
+ if (typeof xhr.onprogress != 'undefined') {
+ xhr.onprogress=function(e) {
+ if (Date.now()>this.ts+2000) {
+ log("..."+state.done+"/"+state.count);
+ this.ts=Date.now();
+ }
+ }.bind(state);
+ }
+ state.started++;
+ xhr.send();
+}
+
+function testHammer(count, par, len, doneFn) {
+ var state={"count":count, "started":0, "done":0, "par":par, "len":len, "doneFn":doneFn, "ts": Date.now(), "error":false};
+ var xhr=[];
+ for (var i=0; iSuccess!");
+ successCnt++;
+ } else {
+ log("Test failed!");
+ }
+ }
+ tstState++;
+ if (tstState==1) {
+ log("Testing parallel load of espfs files...");
+ testParLdImg("../cats/kitten-loves-toy.jpg", 3, nextTest);
+ } else if (tstState==2) {
+ log("Testing GET request of 32K...");
+ testDownloadCgi(32*1024, nextTest);
+ } else if (tstState==3) {
+ log("Testing GET request of 128K...");
+ testDownloadCgi(128*1024, nextTest);
+ } else if (tstState==4) {
+ log("Testing GET request of 512K...");
+ testDownloadCgi(512*1024, nextTest);
+ } else if (tstState==5) {
+ log("Testing POST request of 512 bytes...");
+ testUploadCgi(512, nextTest);
+ } else if (tstState==6) {
+ log("Testing POST request of 16K bytes...");
+ testUploadCgi(16*1024, nextTest);
+ } else if (tstState==7) {
+ log("Testing POST request of 512K bytes...");
+ testUploadCgi(512*1024, nextTest);
+ } else if (tstState==8) {
+ log("Hammering webserver with 500 requests of size 512...");
+ testHammer(500, 3, 512, nextTest);
+ } else if (tstState==9) {
+ log("Hammering webserver with 500 requests of size 2048...");
+ testHammer(500, 3, 2048, nextTest);
+ } else {
+ log("Tests done! "+successCnt+" out of "+(tstState-1)+" tests were successful.");
+ }
+}
+
+
+
+window.onload=function(e) {
+ log("Starting tests.");
+ nextTest(false);
+}
+
+
+
diff --git a/libesphttpd b/libesphttpd
index cdc6316..b1808d2 160000
--- a/libesphttpd
+++ b/libesphttpd
@@ -1 +1 @@
-Subproject commit cdc6316b126ea92723743da37945fb92cfa7c924
+Subproject commit b1808d27b08bb8915fcfd6d21784fa6069a56c37
diff --git a/user/cgi-test.c b/user/cgi-test.c
new file mode 100644
index 0000000..a7e3ef5
--- /dev/null
+++ b/user/cgi-test.c
@@ -0,0 +1,86 @@
+/*
+Cgi routines as used by the tests in the html/test subdirectory.
+*/
+
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * Jeroen Domburg 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.
+ * ----------------------------------------------------------------------------
+ */
+
+
+#include
+#include "cgi-test.h"
+
+
+typedef struct {
+ int len;
+ int sendPos;
+} TestbedState;
+
+
+int ICACHE_FLASH_ATTR cgiTestbed(HttpdConnData *connData) {
+ char buff[1024];
+ int first=0;
+ int l, x;
+ TestbedState *state=(TestbedState*)connData->cgiData;
+
+ if (connData->conn==NULL) {
+ //Connection aborted. Clean up.
+ if (state) free(state);
+ return HTTPD_CGI_DONE;
+ }
+
+ if (state==NULL) {
+ //First call
+ state=malloc(sizeof(TestbedState));
+ memset(state, 0, sizeof(state));
+ connData->cgiData=state;
+ first=1;
+ }
+
+ if (connData->requestType==HTTPD_METHOD_GET) {
+ if (first) {
+ httpdStartResponse(connData, 200);
+ httpdHeader(connData, "content-type", "application/data");
+ httpdEndHeaders(connData);
+ l=httpdFindArg(connData->getArgs, "len", buff, sizeof(buff));
+ state->len=1024;
+ if (l!=-1) state->len=atoi(buff);
+ state->sendPos=0;
+ return HTTPD_CGI_MORE;
+ } else {
+ l=sizeof(buff);
+ if (l>(state->len-state->sendPos)) l=(state->len-state->sendPos);
+ //Fill with semi-random data
+ for (x=0; xsendPos>>10))&0x1F)+'0';
+ httpdSend(connData, buff, l);
+ state->sendPos+=l;
+ printf("Test: Uploaded %d/%d bytes\n", state->sendPos, state->len);
+ if (state->len<=state->sendPos) {
+ if (state) free(state);
+ return HTTPD_CGI_DONE;
+ } else {
+ return HTTPD_CGI_MORE;
+ }
+ }
+ }
+ if (connData->requestType==HTTPD_METHOD_POST) {
+ if (connData->post->len!=connData->post->received) {
+ //Still receiving data. Ignore this.
+ printf("Test: got %d/%d bytes\n", connData->post->received, connData->post->len);
+ return HTTPD_CGI_MORE;
+ } else {
+ httpdStartResponse(connData, 200);
+ httpdHeader(connData, "content-type", "text/plain");
+ httpdEndHeaders(connData);
+ l=sprintf(buff, "%d", connData->post->received);
+ httpdSend(connData, buff, l);
+ return HTTPD_CGI_DONE;
+ }
+ }
+ return HTTPD_CGI_DONE;
+}
diff --git a/user/cgi-test.h b/user/cgi-test.h
new file mode 100644
index 0000000..ba78edc
--- /dev/null
+++ b/user/cgi-test.h
@@ -0,0 +1,8 @@
+#ifndef CGI_TEST_H
+#define CGI_TEST_H
+
+#include "httpd.h"
+
+int cgiTestbed(HttpdConnData *connData);
+
+#endif
\ No newline at end of file
diff --git a/user/user_main.c b/user/user_main.c
index c61235b..e22a345 100644
--- a/user/user_main.c
+++ b/user/user_main.c
@@ -26,6 +26,7 @@ some pictures of cats.
#include "captdns.h"
#include "webpages-espfs.h"
#include "cgiwebsocket.h"
+#include "cgi-test.h"
//The example can print out the heap use every 3 seconds. You can use this to catch memory leaks.
//#define SHOW_HEAP_USE
@@ -147,6 +148,10 @@ HttpdBuiltInUrl builtInUrls[]={
{"/websocket/ws.cgi", cgiWebsocket, myWebsocketConnect},
{"/websocket/echo.cgi", cgiWebsocket, myEchoWebsocketConnect},
+ {"/test", cgiRedirect, "/test/index.html"},
+ {"/test/", cgiRedirect, "/test/index.html"},
+ {"/test/test.cgi", cgiTestbed, NULL},
+
{"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem
{NULL, NULL, NULL}
};