Browse code

Fix clientblock and bufblock management. Fix wk_clientservicereadheader to consume input. Set keep-alive as default for HTTP/1.1 or greater connections

Dario Rodriguez authored on 02/07/2014 12:12:46
Showing 2 changed files
... ...
@@ -111,6 +111,37 @@ static int wk_postset(_wk *web, wk_client *client,char *varname, char *data, int
111 111
 static char *wk_postget(_wk *web, wk_client *client,char *varname, int *isfile);
112 112
 static int wk_postfree(_wk *web, wk_client *client);
113 113
 
114
+#ifdef WK_DEBUG_IO
115
+#define DEBUG_IN_PRE { int sbp=sbuf_count(in);
116
+#define DEBUG_IN_POST wk_debug_io("READ",fd,sbuf_ptr(in)+sbp,sbuf_count(in)-sbp); }
117
+#define DEBUG_OUT_PRE { int sbu=out->got;
118
+#define DEBUG_OUT_POST wk_debug_io("WRITTEN",fd,out->buf+sbu,out->got-sbu); }
119
+
120
+static void
121
+wk_debug_io(char *iotype, int fd, char *ptr, long int size)
122
+{
123
+        int n,c;
124
+        fprintf(stderr,"%s %li bytes on %i:",iotype,size,fd);
125
+        for(n=0;n<size;n++) {
126
+                c=((unsigned char *)ptr)[n];
127
+                if(c=='\r' || c=='\n')
128
+                        fprintf(stderr,"\\%c",(c=='\r')?'r':'n');
129
+                else if(c<' ' || c>'~')
130
+                        fprintf(stderr,"\\x%02X",c);
131
+                else
132
+                        fprintf(stderr,"%c",c);
133
+        }
134
+        fprintf(stderr,"\n");
135
+}
136
+
137
+#else
138
+#define DEBUG_IN_PRE
139
+#define DEBUG_IN_POST
140
+#define DEBUG_OUT_PRE
141
+#define DEBUG_OUT_POST
142
+#endif
143
+
144
+
114 145
 wk *
115 146
 wk_init(int port, sselect *ssel, void (*callback_event)(/*wk *web,int connid, wk_event event, void *userptr*/), wk_action (*callback_http)(/*wk *web,int connid, wk_uri *uri, void *userptr*/), wk_action (*callback_post)(/*wk *web,int connid, wk_uri *uri, void *userptr*/), wk_action (*callback_continuation)(/*wk *web,int connid, wk_uri *uri, void *userptr*/), void *userptr)
116 147
 {
... ...
@@ -236,13 +267,16 @@ wk_service(wk *paramweb)
236 267
                 for(i=0;i<n;i++) {
237 268
                         fd=fds[i];
238 269
                         if(fd==web->serverfd) {
239
-                                /* accept new connection */
240
-                                if((client=wk_accept(web))!=NULL && web->callback_event!=NULL)
241
-                                        web->callback_event((wk *)web,client->connid,wke_connected,web->userptr);
270
+                                do {
271
+                                        /* accept new connection */
272
+                                        if((client=wk_accept(web))!=NULL && web->callback_event!=NULL)
273
+                                                web->callback_event((wk *)web,client->connid,wke_connected,web->userptr);
274
+                                } while(sock_readable(fd)==0);
242 275
                                 continue; /* all done here */
243 276
                         }
244 277
                         if((client=(wk_client *)sselect_getuserptr(web->ssel,fd))==NULL || client->web!=(wk *)web) {
245
-                                wk_close((wk *)web,client->connid);
278
+                                sselect_delread(web->ssel,fd);
279
+                                close(fd),fd=-1;
246 280
                                 continue; /* internal error */
247 281
                         }
248 282
                         if((in=wk_sbufget((wk *) web,client->inbufid))==NULL) {
... ...
@@ -255,11 +289,13 @@ wk_service(wk *paramweb)
255 289
                                 sselect_delread(web->ssel,fd);
256 290
                                 continue;
257 291
                         }
292
+                        DEBUG_IN_PRE;
258 293
                         if(sbuf_fill(in,fd,sock_queued(fd))==0) {
259 294
                                 /* client has closed connection */
260 295
                                 wk_close((wk *) web, client->connid);
261 296
                                 continue;
262 297
                         }
298
+                        DEBUG_IN_POST;
263 299
                         if(client->status==wkc_header) {
264 300
                                 if(wk_clientservicereadheader(web,client)==-1) {
265 301
                                         /* internal error, protocol error or no enough memory */
... ...
@@ -288,7 +324,9 @@ wk_service(wk *paramweb)
288 324
                                 wk_close((wk *)web,client->connid);
289 325
                                 continue; /* internal error */
290 326
                         }
327
+                        DEBUG_OUT_PRE;
291 328
                         sbuf_send(out,fd,sbuf_count(out));
329
+                        DEBUG_OUT_POST;
292 330
                         /* if we are serving from file, load the next chunk */
293 331
                         if(client->fdtoserve!=-1 && client->usedoutbufids==1) {
294 332
                                 sbuf_discard(out);
... ...
@@ -314,8 +352,8 @@ wk_service(wk *paramweb)
314 352
                                                 client->continuationactive=web->callback_continuation((wk *)web,client->connid,&(client->uri),web->userptr);
315 353
                                         } else {
316 354
                                                 client->uriready=0; /* we have finished servicing this one */
317
-                                                if(!client->keepalive)
318
-                                                        wk_close((wk *)web, fd); /* all sent */
355
+                                                if(client->keepalive==0)
356
+                                                        wk_close((wk *)web, client->connid); /* all sent */
319 357
                                         }
320 358
                                 }
321 359
                         }
... ...
@@ -335,7 +373,7 @@ wk_serve_buffer_as_file(wk *paramweb, int connid, void *data, int datalen, const
335 373
                 return(-1);
336 374
         if((client=wk_clientget(web,connid))==NULL)
337 375
                 return(-1);
338
-        wk_writestr((wk *)web,connid,"HTTP/1.1 200 OK\r\n");
376
+        wk_writestr((wk *)web,connid,"HTTP/1.0 200 OK\r\n");
339 377
         sprintf(buf,"Content-Length: %i\r\n",datalen);
340 378
         wk_writestr((wk *)web,connid,buf);
341 379
         if(mime!=NULL && strlen(mime)<(sizeof(buf)-sizeof(strcontenttype)-3))
... ...
@@ -744,7 +782,7 @@ wk_sbufrelease(wk *paramweb, int sbufid)
744 782
                 return(-1);
745 783
         numblock=sbufid/BUFBLOCK;
746 784
         j=sbufid%BUFBLOCK;
747
-        bit=0x1<<(j&3);
785
+        bit=0x1<<(j&7);
748 786
         if((buf=wk_sbufget(web,sbufid))==NULL)
749 787
                 return(-1);
750 788
         web->bufblocks[numblock].usedbufs--;
... ...
@@ -763,7 +801,7 @@ wk_sbufget(wk *paramweb, int sbufid)
763 801
                 return(NULL);
764 802
         numblock=sbufid/BUFBLOCK;
765 803
         j=sbufid%BUFBLOCK;
766
-        bit=0x1<<(j&3);
804
+        bit=0x1<<(j&7);
767 805
         if(web==NULL ||
768 806
           web->bufblocks==NULL ||
769 807
           web->sizebufblocks<=numblock ||
... ...
@@ -871,7 +909,7 @@ wk_clientrelease(_wk *web, int connid)
871 909
         int w;
872 910
         numblock=connid/CLIENTBLOCK;
873 911
         j=connid%CLIENTBLOCK;
874
-        bit=0x1<<(j&3);
912
+        bit=0x1<<(j&7);
875 913
         if((client=wk_clientget(web,connid))==NULL)
876 914
                 return(-1);
877 915
         web->clientblocks[numblock].usedclients--;
... ...
@@ -893,7 +931,7 @@ wk_clientget(_wk *web, int connid)
893 931
         int numblock,j,bit;
894 932
         numblock=connid/CLIENTBLOCK;
895 933
         j=connid%CLIENTBLOCK;
896
-        bit=0x1<<(j&3);
934
+        bit=0x1<<(j&7);
897 935
         if(web==NULL ||
898 936
           web->clientblocks==NULL ||
899 937
           web->sizeclientblocks<=numblock ||
... ...
@@ -974,7 +1012,10 @@ wk_clientservicereadheader(_wk *web, wk_client *client)
974 1012
         /* headers */
975 1013
         *end='\0';
976 1014
         uri->headers=sbuf_ptrunused(hbuf);
977
-        client->keepalive=0;
1015
+        if(strcmp(uri->protocol,"HTTP/0.9")==0 || strcmp(uri->protocol,"HTTP/1.0")==0)
1016
+                client->keepalive=0;
1017
+        else
1018
+                client->keepalive=1; /* default for http/1.1 */
978 1019
         while(ptr<end) {
979 1020
                 if(*ptr!='\n')
980 1021
                         return(-1); /* line is not ended with \r\n */
... ...
@@ -994,6 +1035,8 @@ wk_clientservicereadheader(_wk *web, wk_client *client)
994 1035
         }
995 1036
         /* add header terminator */
996 1037
         sbuf_add(hbuf,"",1);
1038
+        /* mark data as used */
1039
+        sbuf_getbytes(in,end-sbuf_ptr(in)+4);
997 1040
         /* call the http method */
998 1041
         client->uriready=1;
999 1042
         client->continuationactive=0;
... ...
@@ -30,6 +30,7 @@ char *socklib_connect(test_action action);
30 30
 char *socklib_sselect(test_action action);
31 31
 char *sbuf_memory(test_action action);
32 32
 char *webkernel_basic(test_action action);
33
+char *webkernel_dual(test_action action);
33 34
 
34 35
 
35 36
 struct {
... ...
@@ -39,6 +40,7 @@ struct {
39 40
            {socklib_sselect},
40 41
            {sbuf_memory},
41 42
            {webkernel_basic},
43
+           {webkernel_dual},
42 44
           };
43 45
 
44 46
 int
... ...
@@ -48,8 +50,10 @@ main(int argc, char *argv[])
48 50
         int flagall;
49 51
         char *resstr;
50 52
         int total,totalfail;
51
-        if(argc==1 || strcmp(argv[argc-1],"--help")==0 || argc!=2) {
52
-                printf("Syntax:\n\t%s { --help | --list | test_name | all }\nNOTE: test_name is one of the tests listed in --list\n",argv[0]);
53
+        int n;
54
+        char *test;
55
+        if(argc==1 || strcmp(argv[argc-1],"--help")==0 || argc<2) {
56
+                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]);
53 57
                 return(0);
54 58
         }
55 59
         if(strcmp(argv[argc-1],"--list")==0) {
... ...
@@ -58,8 +62,12 @@ main(int argc, char *argv[])
58 62
                 return(0);
59 63
         }
60 64
         flagall=(strcmp(argv[argc-1],"all")==0)?1:0;
61
-        for(total=totalfail=0,i=0;i<(sizeof(tests)/sizeof(tests[0]));i++) {
62
-                if(!flagall && strcmp(tests[i].test(test_name),argv[argc-1])!=0)
65
+        total=totalfail=0;
66
+        for(n=(flagall)?0:1;(flagall==0 && n<argc) || (flagall!=0 && n<(sizeof(tests)/sizeof(tests[0])));n++) {
67
+                test=(flagall!=0)?tests[n].test(test_name):argv[n];
68
+                for(i=0;i<(sizeof(tests)/sizeof(tests[0])) && strcmp(tests[i].test(test_name),test)!=0;i++)
69
+                        ;
70
+                if(i>=(sizeof(tests)/sizeof(tests[0])))
63 71
                         continue;
64 72
                 printf("%20s...",tests[i].test(test_name));
65 73
                 fflush(stdout);
... ...
@@ -69,7 +77,7 @@ main(int argc, char *argv[])
69 77
                 printf("%s\n",resstr);
70 78
 
71 79
         }
72
-        if(!flagall && i>=(sizeof(tests)/sizeof(tests[0]))) {
80
+        if(total==0) {
73 81
                 printf("ERROR: test not found\n");
74 82
                 return(1);
75 83
         }
... ...
@@ -353,12 +361,17 @@ wk_action
353 361
 webkernel_basichttp(wk *web, int connid, wk_uri *uri, void *userptr)
354 362
 {
355 363
 
356
-        *((int *)userptr)=0;
357 364
         char reply_ok[]={"Ok"};
365
+#if 1
366
+        printf("CALLBACK! (check is %i, 0x%p), uri->path:%s\n",(*((int *)userptr)),(void *)userptr,uri->path);
367
+#endif
358 368
         if(strcmp(uri->path,"/test.cgi")==0) {
359
-                *((int *)userptr)=1;
369
+                (*((int *)userptr))=(*((int *)userptr))+1;
360 370
                 wk_serve_buffer_as_file(web,connid,reply_ok,sizeof(reply_ok)-1,mime_getdefault("test.txt",NULL));
361 371
         }
372
+#if 1
373
+        printf("check is now %i\n",(*((int *)userptr)));
374
+#endif
362 375
         return(wkact_finished);
363 376
 }
364 377
 
... ...
@@ -452,7 +465,7 @@ Connection: keep-alive\r\n\
452 465
         /* reply_in_wk -> socket */
453 466
         if(sselect_wait(ssel,timeout)!=1) {
454 467
                 webkernel_basicclose(&client,&ssel,&host,&web);
455
-                return(STRING_FAIL ": timeout on sselect_wait (after client request)\n");
468
+                return(STRING_FAIL ": (2) timeout on sselect_wait (after client request)\n");
456 469
         }
457 470
         wk_service(web);
458 471
         /* client read availability */
... ...
@@ -483,3 +496,170 @@ Connection: keep-alive\r\n\
483 496
         return(STRING_OK);
484 497
 }
485 498
 
499
+void
500
+webkernel_dualclose(int *clients,int sizeclients,sselect **ssel, char **host, wk **web)
501
+{
502
+        int c;
503
+        for(c=0;c<sizeclients;c++) {
504
+                if(clients[c]!=-1)
505
+                        close(clients[c]),clients[c]=-1;
506
+        }
507
+        if(*host!=NULL)
508
+                free(*host),*host=NULL;
509
+        if(*web!=NULL)
510
+                wk_free(*web),*web=NULL;
511
+        if(*ssel!=NULL)
512
+                sselect_free(*ssel),*ssel=NULL;
513
+}
514
+
515
+
516
+char *
517
+webkernel_dual(test_action action)
518
+{
519
+        wk *web;
520
+        int port;
521
+        int off;
522
+        int check;
523
+        sselect *ssel;
524
+        int clients[2];
525
+        int c;
526
+        char *host;
527
+        long hostsize;
528
+        int timeout;
529
+        int fds[2];
530
+        char buf[128];
531
+        int usedbuf;
532
+        char test_request[]={"\
533
+GET /test.cgi HTTP/1.0\r\n\
534
+Host: localhost\r\n\
535
+Connection: keep-alive\r\n\
536
+\r\n\
537
+"};
538
+        if(action==test_name)
539
+                return("webkernel_dual");
540
+        else if(action==test_description)
541
+                return("webkernel multiple simultaneous connections");
542
+        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++)
543
+                clients[c]=-1;
544
+        ssel=NULL;
545
+        host=NULL;
546
+        web=NULL;
547
+        timeout=100;
548
+        check=0;
549
+#if 1
550
+printf("\n");
551
+#endif
552
+        if((ssel=sselect_init())==NULL)
553
+                return(STRING_FAIL ": couln't alloc a sselect structure");
554
+        for(port=19747,off=0;(web=wk_init(port,ssel,NULL,webkernel_basichttp,NULL,NULL,&check))==NULL;off++,port++)
555
+                ;
556
+        if(web==NULL) {
557
+                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
558
+                return(STRING_FAIL ": couln't find a free port to init webkernel");
559
+        }
560
+        if((host=ipv4_genip("localhost",&hostsize))==NULL) {
561
+                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
562
+                return(STRING_FAIL ": couldn't resove localhost");
563
+        }
564
+        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) {
565
+                if((clients[c]=ipv4_preconnect(host,hostsize,port))==-1) {
566
+                        webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
567
+                        return(STRING_FAIL ": couldn't connect to server\n");
568
+                }
569
+                if(ipv4_connect(clients[c],timeout)==-1) {
570
+                        webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
571
+                                return(STRING_FAIL ": timeout on connect\n");
572
+                }
573
+                ipv4_postconnect(clients[c]);
574
+        }
575
+        /* accept the connection */
576
+#if 1
577
+printf("0. fd[0]=%i, fd[1]=%i\n",clients[0],clients[1]);
578
+#endif
579
+#if 1
580
+printf("1. sselect_wait\n");
581
+#endif
582
+        if(sselect_wait(ssel,timeout)!=1) {
583
+                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
584
+                return(STRING_FAIL ": timeout on sselect_wait\n");
585
+        }
586
+#if 1
587
+printf("2. wk_service\n");
588
+#endif
589
+        wk_service(web);
590
+#if 1
591
+printf("3. sselect_getread\n");
592
+#endif
593
+        if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))==1) {
594
+                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
595
+                return(STRING_FAIL ": wk_service hasn't serviced the server connection\n");
596
+        }
597
+        /* client does request into socket */
598
+#if 1
599
+printf("4. write\n");
600
+#endif
601
+        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++)
602
+                write(clients[c],test_request,sizeof(test_request)-1);
603
+        /* socket-> callback_http -> reply_in_wk */
604
+#if 1
605
+printf("5. sselect_wait\n");
606
+#endif
607
+        if(sselect_wait(ssel,timeout)!=2) {
608
+                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
609
+                return(STRING_FAIL ": timeout on sselect_wait (after client request)\n");
610
+        }
611
+#if 1
612
+printf("6. wk_service\n");
613
+#endif
614
+        wk_service(web);
615
+#if 1
616
+printf("check is %i\n",check);
617
+#endif
618
+        if(check!=2) {
619
+                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
620
+                return(STRING_FAIL ": http callback was not called\n");
621
+        }
622
+        /* reply_in_wk -> socket */
623
+#if 1
624
+printf("7. sselect_wait\n");
625
+#endif
626
+        if(sselect_wait(ssel,timeout)!=2) {
627
+                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
628
+                return(STRING_FAIL ": (2) timeout on sselect_wait (after client request)\n");
629
+        }
630
+#if 1
631
+printf("8. wk_service\n");
632
+#endif
633
+        wk_service(web);
634
+        /* client read availability */
635
+        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++)
636
+                sselect_addread(ssel,clients[c],NULL);
637
+        if(sselect_wait(ssel,timeout)!=2) {
638
+                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
639
+                return(STRING_FAIL ": timeout on sselect_wait (to read client reply)\n");
640
+        }
641
+        wk_service(web);
642
+        if(sselect_getread(ssel,fds,sizeof(fds)/sizeof(fds[0]))!=2) {
643
+                webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
644
+                return(STRING_FAIL ": client read not detected\n");
645
+        }
646
+        /* read data */
647
+        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++) {
648
+                if((usedbuf=read(clients[c],buf,sizeof(buf)-1))<0) {
649
+                        webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
650
+                        return(STRING_FAIL ": couldn't read reply\n");
651
+                }
652
+                buf[usedbuf]='\0';
653
+                if(usedbuf<2 || memcmp(buf+usedbuf-2,"Ok",2)!=0) {
654
+                        webkernel_dualclose(clients,sizeof(clients)/sizeof(clients[0]),&ssel,&host,&web);
655
+                        return(STRING_FAIL ": corrupted data in read on client\n");
656
+                }
657
+        }
658
+        for(c=0;c<(sizeof(clients)/sizeof(clients[0]));c++)
659
+                close(clients[c]),clients[c]=-1;
660
+        wk_free(web),web=NULL;
661
+        sselect_free(ssel),ssel=NULL;
662
+        free(host),host=NULL;
663
+        return(STRING_OK);
664
+}
665
+