Add testsuite to test core libesphttpd functionality
This commit is contained in:
@@ -15,6 +15,7 @@ been loaded <b>%counter%</b> times.
|
|||||||
can <a href="flash/index.html">upgrade the firmware</a> of this module.</li>
|
can <a href="flash/index.html">upgrade the firmware</a> of this module.</li>
|
||||||
<li>You can download the raw <a href="flash.bin">contents</a> of the SPI flash rom</li>
|
<li>You can download the raw <a href="flash.bin">contents</a> of the SPI flash rom</li>
|
||||||
<li>Esphttpd now also supports <a href="websocket/index.html">websockets</a>.</li>
|
<li>Esphttpd now also supports <a href="websocket/index.html">websockets</a>.</li>
|
||||||
|
<li>Test esphttpd using the built-in <a href="test/">test suite</a></li>
|
||||||
<li>And because I can, here's a link to my <a href="http://spritesmods.com/?f=esphttpd">website</a></ul>
|
<li>And because I can, here's a link to my <a href="http://spritesmods.com/?f=esphttpd">website</a></ul>
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<html><head><title>Webserver test</title></head>
|
||||||
|
<link rel="stylesheet" type="text/css" href="../wifi/style.css">
|
||||||
|
<script type="text/javascript" src="../wifi/140medley.min.js"></script>
|
||||||
|
<script type="text/javascript" src="test.js"></script>
|
||||||
|
<body>
|
||||||
|
<div id="main">
|
||||||
|
<div id="log">Initializing test...</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
Binary file not shown.
@@ -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+'<br />');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//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<ct; x++) {
|
||||||
|
im[x]=new Image();
|
||||||
|
im[x].onload=function(no) {
|
||||||
|
log("File "+no+" loaded successfully.");
|
||||||
|
this.loaded++;
|
||||||
|
if (this.loaded==this.count) this.doneFn(!this.error);
|
||||||
|
}.bind(state, x);
|
||||||
|
im[x].onerror=function(no) {
|
||||||
|
log("Error loading image "+no+"!");
|
||||||
|
this.loaded++;
|
||||||
|
this.error++;
|
||||||
|
if (this.loaded==this.count) this.doneFn(!this.error);
|
||||||
|
}.bind(state, x);
|
||||||
|
im[x].src=url+"?"+Math.floor(Math.random()*100000).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testDownloadCgi(len, doneFn) {
|
||||||
|
var xhr=j();
|
||||||
|
var state={"len":len, "doneFn":doneFn, "ts": Date.now()};
|
||||||
|
xhr.open("GET", "test.cgi?len="+len+"&nocache="+Math.floor(Math.random()*100000).toString());
|
||||||
|
xhr.onreadystatechange=function() {
|
||||||
|
if (xhr.readyState==4 && xhr.status>=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<len; x++) data+="X";
|
||||||
|
xhr.open("POST", "test.cgi");
|
||||||
|
xhr.onreadystatechange=function() {
|
||||||
|
if (xhr.readyState==4 && xhr.status>=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; i<par; i++) {
|
||||||
|
xhr[i]=j();
|
||||||
|
hammerNext(state, xhr[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var tstState=0;
|
||||||
|
var successCnt=0;
|
||||||
|
|
||||||
|
function nextTest(lastOk) {
|
||||||
|
if (tstState!=0) {
|
||||||
|
if (lastOk) {
|
||||||
|
log("<b>Success!</b>");
|
||||||
|
successCnt++;
|
||||||
|
} else {
|
||||||
|
log("<b>Test failed!</b>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+1
-1
Submodule libesphttpd updated: cdc6316b12...b1808d27b0
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
Cgi routines as used by the tests in the html/test subdirectory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
* "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.
|
||||||
|
* ----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <esp8266.h>
|
||||||
|
#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; x<l; x++) buff[x]=((x^(state->sendPos>>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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef CGI_TEST_H
|
||||||
|
#define CGI_TEST_H
|
||||||
|
|
||||||
|
#include "httpd.h"
|
||||||
|
|
||||||
|
int cgiTestbed(HttpdConnData *connData);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -26,6 +26,7 @@ some pictures of cats.
|
|||||||
#include "captdns.h"
|
#include "captdns.h"
|
||||||
#include "webpages-espfs.h"
|
#include "webpages-espfs.h"
|
||||||
#include "cgiwebsocket.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.
|
//The example can print out the heap use every 3 seconds. You can use this to catch memory leaks.
|
||||||
//#define SHOW_HEAP_USE
|
//#define SHOW_HEAP_USE
|
||||||
@@ -147,6 +148,10 @@ HttpdBuiltInUrl builtInUrls[]={
|
|||||||
{"/websocket/ws.cgi", cgiWebsocket, myWebsocketConnect},
|
{"/websocket/ws.cgi", cgiWebsocket, myWebsocketConnect},
|
||||||
{"/websocket/echo.cgi", cgiWebsocket, myEchoWebsocketConnect},
|
{"/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
|
{"*", cgiEspFsHook, NULL}, //Catch-all cgi function for the filesystem
|
||||||
{NULL, NULL, NULL}
|
{NULL, NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user