/* * webkernel_test.c * * Tests to stress the webkernel API. * * Author: Dario Rodriguez dario@softhome.net * This program is dual licensed: MIT and in "the public domain". */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include "sbuf.h" #include "socklib.h" #include "webkernel.h" #define STRING_OK "ok" #define STRING_FAIL "fail" typedef enum test_action { test_name=0, test_description, test_run } test_action; char *test1(test_action action); char *socklib_connect(test_action action); char *socklib_sselect(test_action action); char *sbuf_memory(test_action action); char *webkernel_basic(test_action action); char *webkernel_dual(test_action action); char *webkernel_urldecode(test_action action); struct { char *(*test)(/* test_action action */); } tests[]={{test1}, {socklib_connect}, {socklib_sselect}, {sbuf_memory}, {webkernel_basic}, {webkernel_dual}, {webkernel_urldecode}, }; int main(int argc, char *argv[]) { int i; int flagall; char *resstr; int total,totalfail; int n; char *test; if(argc==1 || strcmp(argv[argc-1],"--help")==0 || argc<2) { printf("Syntax:\n\t%s { --help | --list | all | test_name [ test_name [...]] }\nNOTE: test_name is one of the tests listed in --list\n",argv[0]); return(0); } if(strcmp(argv[argc-1],"--list")==0) { for(i=0;i<(sizeof(tests)/sizeof(tests[0]));i++) printf("%s\n\t%s\n",tests[i].test(test_name),tests[i].test(test_description)); return(0); } flagall=(strcmp(argv[argc-1],"all")==0)?1:0; total=totalfail=0; for(n=(flagall)?0:1;(flagall==0 && n<argc) || (flagall!=0 && n<(sizeof(tests)/sizeof(tests[0])));n++) { test=(flagall!=0)?tests[n].test(test_name):argv[n]; for(i=0;i<(sizeof(tests)/sizeof(tests[0])) && strcmp(tests[i].test(test_name),test)!=0;i++) ; if(i>=(sizeof(tests)/sizeof(tests[0]))) continue; printf("%20s...",tests[i].test(test_name)); fflush(stdout); resstr=tests[i].test(test_run); total++; totalfail+=((memcmp(resstr,STRING_OK,strlen(STRING_OK))==0)?0:1); printf("%s\n",resstr); } if(total==0) { printf("ERROR: test not found\n"); return(1); } if(totalfail!=0) printf("Failed %i of %i tests.\n",totalfail,total); else printf("Passed %i of %i tests.\n",total,total); return((totalfail!=0)?1:0); } /* test1 */ char * test1(test_action action) { if(action==test_name) return("test1"); else if(action==test_description) return("test the testing framework"); /* run test */ return("ok"); } /* socklib_connect */ char * socklib_connect(test_action action) { int server,client; char *host; long hostsize; int port; int off; int timeout=100; if(action==test_name) return("socklib_connect"); if(action==test_description) return("listen and connect"); if((host=ipv4_genip("localhost",&hostsize))==NULL) return(STRING_FAIL ": couldn't resove localhost"); for(server=-1,off=0,port=19747;off<1024;off++,port++) { if((server=ipv4_serverbinded(host,hostsize,port))!=-1) break; } if(server==-1) { free(host),host=NULL; return(STRING_FAIL ": couldn't find empty port for server\n"); } sock_setunsafe(server); if((client=ipv4_preconnect(host,hostsize,port))==-1) { close(server),server=-1; free(host),host=NULL; return(STRING_FAIL ": couldn't connect to server\n"); } if(ipv4_connect(client,timeout)==-1) { close(client),client=-1; close(server),server=-1; free(host),host=NULL; return(STRING_FAIL ": timeout on connect\n"); } ipv4_postconnect(client); close(client),client=-1; close(server),server=-1; free(host),host=NULL; return(STRING_OK); } /* socklib_sselect */ char * test_socketsinit(int *server, int *client, char **host, long *hostsize, int *port) { int timeout=100; int off; *server=*client=*port=-1; *hostsize=0; *host=NULL; *port=-1; if((*host=ipv4_genip("localhost",hostsize))==NULL) return(STRING_FAIL ": couldn't resove localhost"); for(*server=-1,off=0,*port=19747;off<1024;off++,(*port)++) { if((*server=ipv4_serverbinded(*host,*hostsize,*port))!=-1) break; } if(*server==-1) { free(*host),*host=NULL; return(STRING_FAIL ": couldn't find empty port for server\n"); } sock_setunsafe(*server); if((*client=ipv4_preconnect(*host,*hostsize,*port))==-1) { close(*server),*server=-1; free(*host),*host=NULL; return(STRING_FAIL ": couldn't connect to server\n"); } if(ipv4_connect(*client,timeout)==-1) { close(*client),*client=-1; close(*server),*server=-1; free(*host),*host=NULL; return(STRING_FAIL ": timeout on connect\n"); } ipv4_postconnect(*client); return(NULL); } void test_socketsfini(int *server, int *client, char **host, long *hostsize, int *port) { if(*server!=-1) close(*server),*server=-1; if(*client!=-1) close(*client),*client=-1; if(*host!=NULL) free(*host),*host=NULL; *hostsize=0; *port=-1; } char * socklib_sselect(test_action action) { int server,client; char *host; long hostsize; int port; char *result; char buf[128]; int readyfds[16]; sselect *ssel; int timeout=100; int workfd; if(action==test_name) return("socklib_sselect"); if(action==test_description) return("select over sockets"); if((result=test_socketsinit(&server,&client,&host,&hostsize,&port))!=NULL) { test_socketsfini(&server,&client,&host,&hostsize,&port); return(result); } if((ssel=sselect_init())==NULL) { test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": couldn't init sselect struct"); } if(sselect_reset(ssel)!=0) { sselect_free(ssel),ssel=NULL; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": couldn't test the sselect reset function"); } if(sselect_wait(ssel,timeout)!=0) { sselect_free(ssel),ssel=NULL; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": (1) was expecting a timeout, something else happened"); } if(sselect_addread(ssel,server,buf)!=0) { sselect_free(ssel),ssel=NULL; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": couldn't add 'read serverfd' to the sselect"); } if(sselect_wait(ssel,timeout)!=1) { sselect_free(ssel),ssel=NULL; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": (2) was expecting a ready-to-read, something else happened"); } if((workfd=sock_accept(server))==-1) { sselect_free(ssel),ssel=NULL; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": couldn't accept connection"); } write(client,".",1); if(sselect_wait(ssel,timeout)!=0) { sselect_free(ssel),ssel=NULL; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": (2) was expecting a timeout, something else happened"); } if(sselect_addread(ssel,workfd,&workfd)!=0) { sselect_free(ssel),ssel=NULL; close(workfd),workfd=-1; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": couldn't add 'read workfd' to the sselect"); } if(sselect_wait(ssel,timeout)!=1) { sselect_free(ssel),ssel=NULL; close(workfd),workfd=-1; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": was expecting a something-to-read, something else happened"); } if((sselect_getread(ssel,readyfds,sizeof(readyfds)/sizeof(readyfds[0])))!=1 || *readyfds!=workfd) { sselect_free(ssel),ssel=NULL; close(workfd),workfd=-1; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": the list of ready-to-read fds is wrong"); } if(sselect_getuserptr(ssel,workfd)!=&workfd) { sselect_free(ssel),ssel=NULL; close(workfd),workfd=-1; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": Couldn't recover the userptr of workfd"); } read(workfd,buf,1); if(sselect_addwrite(ssel,client,buf)!=0) { sselect_free(ssel),ssel=NULL; close(workfd),workfd=-1; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": couldn't add 'write clientfd' to the sselect"); } if(sselect_wait(ssel,timeout)!=1) { sselect_free(ssel),ssel=NULL; close(workfd),workfd=-1; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_FAIL ": was expecting an ok-to-write, something else happened"); } sselect_free(ssel),ssel=NULL; close(workfd),workfd=-1; test_socketsfini(&server,&client,&host,&hostsize,&port); return(STRING_OK); } /* sbuf_memory */ char * sbuf_memory(test_action action) { int bufsize=2048; const unsigned char data[]={0,2,4,1,2,4,1,76,91,147,135,253,121,56,5,9}; unsigned char moredata[16]; unsigned char *ptr; const char *line,lines[]={"one\nand two\nthree"}; int i; sbuf *buf; if(action==test_name) return("sbuf_memory"); else if(action==test_description) return("memory operations with sbuf"); if((buf=sbuf_init(bufsize))==NULL) return(STRING_FAIL ": couldn't alloc new sbuf"); if(sbuf_add(buf,(char *)data,sizeof(data))!=sizeof(data)) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": couldn't push data"); } for(i=0;i<sizeof(moredata);i++) moredata[i]=i; if(sbuf_add(buf,(char *)moredata,sizeof(moredata))!=sizeof(moredata)) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": (2) couldn't push data"); } if(sbuf_count(buf)!=(sizeof(data)+sizeof(moredata))) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": bad count"); } for(i=0;i<sizeof(data);i++) { if((ptr=(unsigned char *)sbuf_getbytes(buf,1))==NULL || *ptr!=data[i]) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": bad retrieval"); } } sbuf_discard(buf); if((ptr=(unsigned char *)sbuf_getbytes(buf,sizeof(moredata)))==NULL || memcmp(ptr,moredata,sizeof(moredata))!=0) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": (2) bad retrieval"); } if(sbuf_getbytes(buf,1)!=NULL) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": (3) bad retrieval"); } sbuf_discard(buf); if(sbuf_addstr(buf,lines)!=(sizeof(lines)-1)) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": (3) couldn't push data"); } if((line=sbuf_getline(buf))==NULL || strcmp(line,"one")!=0) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": bad line retrieval"); } if((line=sbuf_getline(buf))==NULL || strcmp(line,"and two")!=0) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": (2) bad line retrieval"); } if(sbuf_getline(buf)!=NULL) { sbuf_free(buf),buf=NULL; return(STRING_FAIL ": (3) bad line retrieval"); } sbuf_free(buf),buf=NULL; return(STRING_OK); } /* webkernel_basic */ wk_action webkernel_basichttp(wk *web, int connid, wk_uri *uri, void *userptr) { char reply_ok[]={"Ok"}; if(strcmp(uri->path,"/test.cgi")==0) { (*((int *)userptr))=(*((int *)userptr))+1; wk_serve_buffer_as_file(web,connid,reply_ok,sizeof(reply_ok)-1,mime_getdefault("test.txt",NULL)); } return(wkact_finished); } void webkernel_basicclose(int *client,sselect **ssel, char **host, wk **web) { if(*client!=-1) close(*client),*client=-1; if(*host!=NULL) free(*host),*host=NULL; if(*web!=NULL) wk_free(*web),*web=NULL; if(*ssel!=NULL) sselect_free(*ssel),*ssel=NULL; } char * webkernel_basic(test_action action) { wk *web; int port; int off; int check; sselect *ssel; int client; char *host; long hostsize; int timeout; int fds[2]; char buf[128]; int usedbuf; int serverfd; char test_request[]={"\ GET /test.cgi HTTP/1.0\r\n\ Host: localhost\r\n\ Connection: keep-alive\r\n\ \r\n\ "}; if(action==test_name) return("webkernel_basic"); else if(action==test_description) return("webkernel basic usage"); client=-1; ssel=NULL; host=NULL; web=NULL; timeout=100; check=0; if((ssel=sselect_init())==NULL) return(STRING_FAIL ": couln't alloc a sselect structure"); for(port=19747,off=0,check=0;(serverfd=ipv4_server(port))==-1;off++,port++) ; sock_setunsafe(serverfd); if((web=wk_init(serverfd,ssel,NULL,webkernel_basichttp,NULL,NULL,&check))==NULL) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": couln't find a free port to init webkernel"); } if((host=ipv4_genip("localhost",&hostsize))==NULL) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": couldn't resove localhost"); } if((client=ipv4_preconnect(host,hostsize,port))==-1) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": couldn't connect to server\n"); } if(ipv4_connect(client,timeout)==-1) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": timeout on connect\n"); } ipv4_postconnect(client); /* accept the connection */ if(sselect_wait(ssel,timeout)!=1) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": timeout on sselect_wait\n"); } wk_service(web); if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))==1) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": wk_service hasn't serviced the server connection\n"); } /* client does request into socket */ write(client,test_request,sizeof(test_request)-1); /* socket-> callback_http -> reply_in_wk */ if(sselect_wait(ssel,timeout)!=1) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": timeout on sselect_wait (after client request)\n"); } wk_service(web); if(check!=1) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": http callback was not called\n"); } /* reply_in_wk -> socket */ if(sselect_wait(ssel,timeout)!=1) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": (2) timeout on sselect_wait (after client request)\n"); } wk_service(web); /* client read availability */ sselect_addread(ssel,client,NULL); if(sselect_wait(ssel,timeout)!=1) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": timeout on sselect_wait (to read client reply)\n"); } wk_service(web); if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))!=1 || *fds!=client) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": client read not detected\n"); } /* read data */ if((usedbuf=read(client,buf,sizeof(buf)-1))<0) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": couldn't read reply\n"); } buf[usedbuf]='\0'; if(usedbuf<2 || memcmp(buf+usedbuf-2,"Ok",2)!=0) { webkernel_basicclose(&client,&ssel,&host,&web); return(STRING_FAIL ": corrupted data in read on client\n"); } close(client),client=-1; wk_free(web),web=NULL; close(serverfd),serverfd=-1; sselect_free(ssel),ssel=NULL; free(host),host=NULL; return(STRING_OK); } /* webkernel_dual */ void webkernel_dualclose(int *clients,int sizeclients,sselect **ssel, char **host, wk **web) { int c; for(c=0;c<sizeclients;c++) { if(clients[c]!=-1) close(clients[c]),clients[c]=-1; } if(*host!=NULL) free(*host),*host=NULL; if(*web!=NULL) wk_free(*web),*web=NULL; if(*ssel!=NULL) sselect_free(*ssel),*ssel=NULL; } char * webkernel_dual(test_action action) { wk *web; int port; int off; int check; sselect *ssel; int serverfd; int clients[2]; int c; char *host; long hostsize; int timeout; int fds[2]; char buf[128]; int usedbuf; char test_request[]={"\ GET /test.cgi HTTP/1.0\r\n\ Host: localhost\r\n\ Connection: keep-alive\r\n\ \r\n\ "}; if(action==test_name) return("webkernel_dual"); else if(action==test_description) return("webkernel multiple simultaneous connections"); for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) clients[c]=-1; ssel=NULL; host=NULL; web=NULL; timeout=100; check=0; if((ssel=sselect_init())==NULL) return(STRING_FAIL ": couln't alloc a sselect structure"); for(port=19747,off=0;(serverfd=ipv4_server(port))==-1;off++,port++) ; sock_setunsafe(serverfd); if((web=wk_init(serverfd,ssel,NULL,webkernel_basichttp,NULL,NULL,&check))==NULL) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": couln't find a free port to init webkernel"); } if((host=ipv4_genip("localhost",&hostsize))==NULL) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": couldn't resove localhost"); } for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) { if((clients[c]=ipv4_preconnect(host,hostsize,port))==-1) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": couldn't connect to server\n"); } if(ipv4_connect(clients[c],timeout)==-1) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": timeout on connect\n"); } ipv4_postconnect(clients[c]); } /* accept the connection */ if(sselect_wait(ssel,timeout)!=1) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": timeout on sselect_wait\n"); } wk_service(web); if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))==1) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": wk_service hasn't serviced the server connection\n"); } /* client does request into socket */ for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) write(clients[c],test_request,sizeof(test_request)-1); /* socket-> callback_http -> reply_in_wk */ if(sselect_wait(ssel,timeout)!=2) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": timeout on sselect_wait (after client request)\n"); } wk_service(web); if(check!=2) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": http callback was not called\n"); } /* reply_in_wk -> socket */ if(sselect_wait(ssel,timeout)!=2) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": (2) timeout on sselect_wait (after client request)\n"); } wk_service(web); /* client read availability */ for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) sselect_addread(ssel,clients[c],NULL); if(sselect_wait(ssel,timeout)!=2) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": timeout on sselect_wait (to read client reply)\n"); } wk_service(web); if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))!=2) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": client read not detected\n"); } /* read data */ for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) { if((usedbuf=read(clients[c],buf,sizeof(buf)-1))<0) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": couldn't read reply\n"); } buf[usedbuf]='\0'; if(usedbuf<2 || memcmp(buf+usedbuf-2,"Ok",2)!=0) { webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web); return(STRING_FAIL ": corrupted data in read on client\n"); } } for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) close(clients[c]),clients[c]=-1; wk_free(web),web=NULL; close(serverfd),serverfd=-1; sselect_free(ssel),ssel=NULL; free(host),host=NULL; return(STRING_OK); } /* webkernel_urldecode */ char * webkernel_urldecode(test_action action) { static struct { char *encoded; char *decoded; } strs[]={{"my%40emailservice.com","my@emailservice.com"}}; int i; char buf[1024]; static char myerror[1024]; if(action==test_name) return("webkernel_urldecode"); else if(action==test_description) return("check some strings against uri_urldecode"); /* run test */ for(i=0;i<(sizeof(strs)/sizeof(strs[0]));i++) { strncpy(buf,strs[i].encoded,sizeof(buf)); buf[sizeof(buf)-1]='\0'; uri_urldecode(buf); if(strcmp(buf,strs[i].decoded)!=0) { snprintf(myerror,sizeof(myerror),"%s: incorrect decoding of string \"%s\" (\"%s\"!=\"%s\")\n",STRING_FAIL,strs[i].encoded,buf,strs[i].decoded); myerror[sizeof(myerror)-1]='\0'; return(myerror); } } return(STRING_OK); }