Browse code

webkernel: completed initial implementation

Dario Rodriguez authored on 17/06/2014 20:32:33
Showing 4 changed files
... ...
@@ -136,7 +136,7 @@ sbuf_ptr(sbuf *buf)
136 136
 }
137 137
 
138 138
 long
139
-sbuf_add(sbuf *buf, char *data, long datasize)
139
+sbuf_add(sbuf *buf, const char *data, long datasize)
140 140
 {
141 141
         long added;
142 142
         if(buf==NULL || data==NULL || buf->used==buf->size)
... ...
@@ -148,7 +148,7 @@ sbuf_add(sbuf *buf, char *data, long datasize)
148 148
 }
149 149
 
150 150
 long
151
-sbuf_addstr(sbuf *buf, char *str)
151
+sbuf_addstr(sbuf *buf, const char *str)
152 152
 {
153 153
         if(buf==NULL || str==NULL)
154 154
                 return(0);
... ...
@@ -41,8 +41,8 @@ void sbuf_wipe(sbuf *buf);
41 41
 char *sbuf_getbytes(sbuf *buf, long numbytes);
42 42
 long sbuf_count(sbuf *buf);
43 43
 char *sbuf_ptr(sbuf *buf);
44
-long sbuf_add(sbuf *buf, char *data, long datasize);
45
-long sbuf_addstr(sbuf *buf, char *str);
44
+long sbuf_add(sbuf *buf, const char *data, long datasize);
45
+long sbuf_addstr(sbuf *buf, const char *str);
46 46
 long sbuf_unused(sbuf *buf);
47 47
 char *sbuf_ptrunused(sbuf *buf);
48 48
 long sbuf_addfromunused(sbuf *buf, long numbytes);
... ...
@@ -12,6 +12,9 @@
12 12
 #include <stdlib.h>
13 13
 #include <unistd.h>
14 14
 #include <string.h>
15
+#include <sys/types.h>
16
+#include <sys/stat.h>
17
+#include <fcntl.h>
15 18
 #include "sbuf.h"
16 19
 #include "socklib.h"
17 20
 #include "webkernel.h"
... ...
@@ -34,8 +37,10 @@ typedef struct wk_client {
34 37
         int usedoutbufids;
35 38
         int outbufids[MAXOUTBUF];
36 39
         int serviced;
37
-        wk_uri uri;
38 40
         int continuationactive;
41
+        int fdtoserve;
42
+        wk_uri uri;
43
+        int headerbufid;
39 44
         int keepalive;
40 45
 } wk_client;
41 46
 
... ...
@@ -78,7 +83,7 @@ static wk_client *wk_accept(_wk *web);
78 83
 static wk_client *wk_clientacquire(_wk *web);
79 84
 static int wk_clientrelease(_wk *web, int connid);
80 85
 static wk_client *wk_clientget(_wk *web, int connid);
81
-static void wk_clientserviceread(_wk *web, wk_client *client);
86
+static int wk_clientserviceread(_wk *web, wk_client *client);
82 87
 
83 88
 wk *
84 89
 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_continuation)(/*wk *web,int connid, wk_uri *uri, void *userptr*/), void *userptr)
... ...
@@ -144,6 +149,8 @@ wk_free(wk *paramweb)
144 149
                                         FD_CLR(client->fd,&(web->fdset));
145 150
                                         close(client->fd),client->fd=-1;
146 151
                                 }
152
+                                if(client->fdtoserve!=-1)
153
+                                        close(client->fdtoserve),client->fdtoserve=-1;
147 154
                         }
148 155
                 }
149 156
                 cb->usedclients=0;
... ...
@@ -190,7 +197,7 @@ wk_service(wk *paramweb)
190 197
 {
191 198
         int fds[FDBLOCK];
192 199
         int fd;
193
-        int n,i,k;
200
+        int n,i;
194 201
         sbuf *in;
195 202
         sbuf *out;
196 203
         _wk *web=(_wk *)paramweb;
... ...
@@ -221,10 +228,14 @@ wk_service(wk *paramweb)
221 228
                         }
222 229
                         if(sbuf_fill(in,fd,sock_queued(fd))==0) {
223 230
                                 /* client has closed connection */
224
-                                wk_close((wk *) web, fd);
231
+                                wk_close((wk *) web, client->connid);
232
+                                continue;
233
+                        }
234
+                        if(wk_clientserviceread(web,client)==-1) {
235
+                                /* internal error, protocol error or no enough memory */
236
+                                wk_close((wk *) web, client->connid);
225 237
                                 continue;
226 238
                         }
227
-                        wk_clientserviceread(web,client);
228 239
                 }
229 240
         }
230 241
         while((n=sselect_getwritefiltered(web,&(web->fdset),fds,sizeof(fds)/sizeof(fds[0])))>0) {
... ...
@@ -232,21 +243,30 @@ wk_service(wk *paramweb)
232 243
                         fd=fds[i];
233 244
                         if((client=(wk_client *)sselect_getuserptr(web->ssel,fd))==NULL || client->web!=(wk *)web)
234 245
                                 continue; /* internal error */
235
-                        for(out=NULL,k=client->usedoutbufids-1;k>=0;k--) {
236
-                                if((out=wk_sbufget((wk *) web,client->outbufids[k]))==NULL) {
237
-                                        k=-1;
238
-                                        break; /* internal error */
239
-                                }
240
-                        }
241
-                        if(k==-1 || out==NULL) {
246
+                        if((out=wk_sbufget((wk *) web,client->outbufids[0]))==NULL) {
242 247
                                 wk_close((wk *)web,client->connid);
243 248
                                 continue; /* internal error */
244 249
                         }
245 250
                         sbuf_send(out,fd,sbuf_count(out));
251
+                        /* if we are serving from file, load the next chunk */
252
+                        if(client->fdtoserve!=-1 && client->usedoutbufids==1) {
253
+                                sbuf_discard(out);
254
+                                if(sbuf_unused(out)>0) {
255
+                                        int n;
256
+                                        n=sbuf_fill(out,client->fdtoserve,sbuf_unused(out));
257
+                                        if(n<=0)
258
+                                                close(client->fdtoserve),client->fdtoserve=-1;
259
+                                }
260
+                        }
261
+                        /* free unused bufs, detect finished sending */
246 262
                         if(sbuf_count(out)==0) {
247
-                                if(k>0) {
263
+                                if(client->usedoutbufids>1) {
264
+                                        int sbufid;
265
+                                        sbufid=client->outbufids[0];
248 266
                                         client->usedoutbufids--;
249
-                                        wk_sbufrelease((wk *)web,client->outbufids[k]),client->outbufids[k]=-1;
267
+                                        memmove(client->outbufids,client->outbufids+1,sizeof(int)*(MAXOUTBUF-1));
268
+                                        client->outbufids[MAXOUTBUF-1]=-1;
269
+                                        wk_sbufrelease((wk *)web,sbufid),sbufid=-1;
250 270
                                 } else {
251 271
                                         sselect_delwrite(web->ssel,client->fd);
252 272
                                         if(client->continuationactive && web->callback_continuation!=NULL) {
... ...
@@ -266,29 +286,117 @@ wk_service(wk *paramweb)
266 286
 int
267 287
 wk_serve_buffer_as_file(wk *paramweb, int connid, void *data, int datalen, const char *mime)
268 288
 {
269
-
289
+        static const char *strcontenttype={"Content-Type: "};
290
+        _wk *web=(_wk *)paramweb;
291
+        wk_client *client;
292
+        char buf[256];
293
+        if(web==NULL)
294
+                return(-1);
295
+        if((client=wk_clientget(web,connid))==NULL)
296
+                return(-1);
297
+        wk_writestr((wk *)web,connid,"HTTP/1.1 200 OK\r\n");
298
+        sprintf(buf,"Content-Length: %i\r\n",datalen);
299
+        wk_writestr((wk *)web,connid,buf);
300
+        if(mime!=NULL && strlen(mime)<(sizeof(buf)-sizeof(strcontenttype)-3))
301
+                sprintf(buf,"%s%s\r\n",strcontenttype,mime);
302
+        else
303
+                sprintf(buf,"%s%s\r\n",strcontenttype,"application/octet-stream");
304
+        wk_writestr((wk *)web,connid,buf);
305
+        if(client->keepalive)
306
+                wk_writestr((wk *)web,connid,"Connection: keep-alive\r\n");
307
+        wk_writestr((wk *)web,connid,"\r\n");
308
+        return(wk_write((wk *)web,connid,data,datalen));
270 309
 }
271 310
 
272 311
 int
273 312
 wk_serve_file(wk *paramweb, int connid, char *filename, const char *mime)
274 313
 {
275
-
314
+        static const char *strcontenttype={"Content-Type: "};
315
+        _wk *web=(_wk *)paramweb;
316
+        wk_client *client;
317
+        char buf[256];
318
+        struct stat st;
319
+        if(web==NULL || filename==NULL)
320
+                return(-1);
321
+        if((client=wk_clientget(web,connid))==NULL)
322
+                return(-1);
323
+        if(client->fdtoserve!=-1)
324
+                close(client->fdtoserve),client->fdtoserve=-1;
325
+        if((client->fdtoserve=open(filename,O_RDONLY))==-1) {
326
+                wk_serve_error((wk *)web,connid,wkerr_notfound);
327
+                return(-1);
328
+        }
329
+        wk_writestr((wk *)web,connid,"HTTP/1.1 200 OK\r\n");
330
+        if(fstat(client->fdtoserve,&st)==0) {
331
+                sprintf(buf,"Content-Length: %lld\r\n",(long long)st.st_size);
332
+                wk_writestr((wk *)web,connid,buf);
333
+        } else
334
+                client->keepalive=0;
335
+        if(mime!=NULL && strlen(mime)<(sizeof(buf)-sizeof(strcontenttype)-3))
336
+                sprintf(buf,"%s%s\r\n",strcontenttype,mime);
337
+        else
338
+                sprintf(buf,"%s%s\r\n",strcontenttype,"application/octet-stream");
339
+        wk_writestr((wk *)web,connid,buf);
340
+        if(client->keepalive)
341
+                wk_writestr((wk *)web,connid,"Connection: keep-alive\r\n");
342
+        wk_writestr((wk *)web,connid,"\r\n");
343
+        return(0);
276 344
 }
277 345
 
278 346
 int
279 347
 wk_serve_error(wk *paramweb, int connid, wk_error wkerror)
280 348
 {
281
-
349
+        static const char strnotfound[]={"\
350
+HTTP/1.1 404 Not found\r\n\
351
+Content-Type: text/html\r\n\
352
+Content-Length: 113\r\n\
353
+\r\n\
354
+<title>404 Not Found</title>\r\n\
355
+<h1>404 Not Found</h1>\r\n\
356
+The requested resource could not be found on this server.\r\n\
357
+"};
358
+        static const char strinternal[]={"\
359
+HTTP/1.1 500 Internal Server Error\r\n\
360
+Content-Type: text/html\r\n\
361
+Content-Length: 118\r\n\
362
+\r\n\
363
+<title>500 Internal Server Error</title>\r\n\
364
+<h1>500 Internal Server Error</h1>\r\n\
365
+Internal error processing the request.\r\n\
366
+"};
367
+        static const char strnotimplemented[]={"\
368
+HTTP/1.1 501 Not Implemented\r\n\
369
+Content-Type: text/html\r\n\
370
+Content-Length: 132\r\n\
371
+\r\n\
372
+<title>501 Not Implemented</title>\r\n\
373
+<h1>501 Not Implemented</h1>\r\n\
374
+The request was not understood or is not allowed by this server.\r\n\
375
+"};
376
+        int res;
377
+        _wk *web=(_wk *)paramweb;
378
+        wk_client *client;
379
+        if(web==NULL)
380
+                return(-1);
381
+        if((client=wk_clientget(web,connid))==NULL)
382
+                return(-1);
383
+        if(wkerror==wkerr_notfound)
384
+                res=wk_write((wk *)web,connid,strnotfound,sizeof(strnotfound)-1);
385
+        else if(wkerror==wkerr_notimplemented)
386
+                res=wk_write((wk *)web,connid,strnotimplemented,sizeof(strnotimplemented)-1);
387
+        else
388
+                res=wk_write((wk *)web,connid,strinternal,sizeof(strinternal)-1);
389
+        return(res);
282 390
 }
283 391
 
284 392
 int
285
-wk_writestr(wk *paramweb, int connid, char *str)
393
+wk_writestr(wk *paramweb, int connid, const char *str)
286 394
 {
287 395
         return(wk_write(paramweb,connid,str,strlen(str)));
288 396
 }
289 397
 
290 398
 int
291
-wk_write(wk *paramweb, int connid, void *data, int datalen)
399
+wk_write(wk *paramweb, int connid, const void *data, int datalen)
292 400
 {
293 401
         int k;
294 402
         _wk *web=(_wk *)paramweb;
... ...
@@ -317,6 +425,8 @@ wk_write(wk *paramweb, int connid, void *data, int datalen)
317 425
                 datalen-=added;
318 426
                 total+=added;
319 427
         }
428
+        if(total>0)
429
+                sselect_addwrite(web->ssel,client->fd,(void *)client);
320 430
         return(total);
321 431
 }
322 432
 
... ...
@@ -335,6 +445,8 @@ wk_close(wk *paramweb, int connid)
335 445
                 FD_CLR(client->fd,&(web->fdset));
336 446
                 close(client->fd),client->fd=-1;
337 447
         }
448
+        if(client->fdtoserve!=-1)
449
+                close(client->fdtoserve),client->fdtoserve=-1;
338 450
         wk_clientrelease(web,connid);
339 451
         if(web->callback_event!=NULL)
340 452
                 web->callback_event((wk *)web,connid,wke_closed,web->userptr);
... ...
@@ -342,15 +454,32 @@ wk_close(wk *paramweb, int connid)
342 454
 }
343 455
 
344 456
 char *
345
-wk_uri_getheader(wk_uri *wkuri, char *header, char *defaultvalue)
457
+wk_uri_getheader(wk_uri *uri, char *header, char *defaultvalue)
346 458
 {
347
-
459
+        int n,len;
460
+        char *ptr;
461
+        if(uri==NULL || uri->headers==NULL || header==NULL)
462
+                return(NULL);
463
+        len=strlen(header);
464
+        for(n=0,ptr=uri->headers;*ptr!='\0';ptr++,n++) {
465
+                if(memcmp(ptr,header,len)==0 && ptr[len]==':' && ptr[len+1]==' ')
466
+                        return(ptr+len+2);
467
+        }
468
+        return(defaultvalue);
348 469
 }
349 470
 
350 471
 char *
351
-wk_uri_getheaderbynum(wk_uri *wkuri, int num)
472
+wk_uri_getheaderbynum(wk_uri *uri, int num)
352 473
 {
353
-
474
+        int n;
475
+        char *ptr;
476
+        if(uri==NULL || uri->headers==NULL)
477
+                return(NULL);
478
+        for(n=0,ptr=uri->headers;*ptr!='\0';ptr++,n++) {
479
+                if(n==num)
480
+                        return(ptr);
481
+        }
482
+        return(NULL);
354 483
 }
355 484
 
356 485
 const char *
... ...
@@ -584,6 +713,8 @@ wk_clientacquire(_wk *web)
584 713
                 return(NULL); /* insufficient memory */
585 714
         }
586 715
         client->usedoutbufids=1;
716
+        client->fdtoserve=-1;
717
+        client->headerbufid=-1;
587 718
         return(client);
588 719
 }
589 720
 
... ...
@@ -604,6 +735,8 @@ wk_clientrelease(_wk *web, int connid)
604 735
                 wk_sbufrelease((wk *)web,client->inbufid),client->inbufid=-1;
605 736
         for(w=0;w<client->usedoutbufids;w++)
606 737
                 wk_sbufrelease((wk *)web,client->outbufids[w]),client->outbufids[w]=-1;
738
+        if(client->headerbufid!=-1)
739
+                wk_sbufrelease((wk *)web,client->headerbufid),client->headerbufid=-1;
607 740
         memset(client,0,sizeof(wk_client));
608 741
         return(0);
609 742
 }
... ...
@@ -624,9 +757,98 @@ wk_clientget(_wk *web, int connid)
624 757
         return(web->clientblocks[numblock].clients+j);
625 758
 }
626 759
 
627
-static void
760
+static char *
761
+str_findfirstempty(char *ptr, int size)
762
+{
763
+        int i;
764
+        for(i=0;i<(size-3);i++) {
765
+                if(ptr[0]=='\r' && ptr[1]=='\n' && ptr[2]=='\r' && ptr[3]=='\n')
766
+                        return(ptr);
767
+        }
768
+        return(NULL);
769
+}
770
+
771
+static int
628 772
 wk_clientserviceread(_wk *web, wk_client *client)
629 773
 {
630
-#warning TODO
774
+        sbuf *in,*hbuf;
775
+        char *end;
776
+        char *ptr;
777
+        char *lineend;
778
+        char *sep;
779
+        wk_uri *uri;
780
+        /* check if we have all the headers */
781
+        if((in=wk_sbufget((wk *)web, client->inbufid))==NULL)
782
+                return(-1); /* internal error */
783
+        sbuf_discard(in);
784
+        if((end=str_findfirstempty(sbuf_ptr(in),sbuf_count(in)))==NULL) {
785
+                if(sbuf_unused(in)==0)
786
+                        return(-1); /* header part too long */
787
+                /* uncomplete headers, have to wait for more data */
788
+                return(0);
789
+        }
790
+        /* get memory for the uri data */
791
+        if(client->headerbufid!=-1)
792
+                wk_sbufrelease((wk *)web,client->headerbufid),client->headerbufid=-1;
793
+        if((client->headerbufid=wk_sbufacquire((wk *)web))==-1)
794
+                return(-1); /* insufficient memory */
795
+        if((hbuf=wk_sbufget((wk *)web,client->headerbufid))==NULL)
796
+                return(-1); /* internal error */
797
+        /* prepare to fill the uri struct */
798
+        uri=&(client->uri);
799
+        memset(uri,0,sizeof(wk_uri));
800
+        /* check that the method is supported */
801
+        ptr=sbuf_ptr(in);
802
+        if(memcmp(ptr,"GET ",4)!=0 && memcmp(ptr,"PUT ",4)!=0 && memcmp(ptr,"POST ",5)!=0)
803
+                return(-1); /* unknown method */
804
+        if((lineend=strchr(ptr,'\r'))!=NULL)
805
+                *lineend='\0';
806
+        else
807
+                lineend=end;
808
+        /* method */
809
+        sep=strchr(ptr,' ');
810
+        *sep='\0';
811
+        uri->method=sbuf_ptrunused(hbuf);
812
+        sbuf_add(hbuf,ptr,strlen(ptr)+1);
813
+        ptr+=strlen(ptr)+1;
814
+        /* path */
815
+        if((sep=strchr(ptr,' '))==NULL)
816
+                return(-1); /* no separator between path and protocol */
817
+        *sep='\0';
818
+        uri->path=sbuf_ptrunused(hbuf);
819
+        sbuf_add(hbuf,ptr,strlen(ptr)+1);
820
+        ptr+=strlen(ptr)+1;
821
+        /* protocol */
822
+        uri->path=sbuf_ptrunused(hbuf);
823
+        sbuf_add(hbuf,ptr,strlen(ptr)+1);
824
+        ptr+=strlen(ptr)+1;
825
+        /* headers */
826
+        *end='\0';
827
+        uri->headers=sbuf_ptrunused(hbuf);
828
+        client->keepalive=0;
829
+        while(ptr<end) {
830
+                if(*ptr!='\n')
831
+                        return(-1); /* line is not ended with \r\n */
832
+                ptr++;
833
+                if((lineend=strchr(ptr,'\r'))!=NULL)
834
+                        *lineend='\0';
835
+                else
836
+                        lineend=end;
837
+                if((sep=strchr(ptr,':'))==NULL || sep[1]!=' ')
838
+                        return(-1); /* header not in format "name: value" */
839
+                /* check for keepalive header */
840
+                if(memcmp(ptr,"Connection: ",12)==0 &&
841
+                  (strcmp(ptr+12,"keep-alive")==0 || strcmp(ptr+12,"Keep-Alive")==0))
842
+                        client->keepalive=1;
843
+                sbuf_add(hbuf,ptr,strlen(ptr)+1);
844
+                ptr+=strlen(ptr)+1;
845
+        }
846
+        /* add header terminator */
847
+        sbuf_add(hbuf,"",1);
848
+        /* call the http method */
849
+        client->serviced=1;
850
+        client->continuationactive=0;
851
+        client->continuationactive=web->callback_http((wk *)web, client->connid, uri, web->userptr);
852
+        return(0);
631 853
 }
632 854
 
... ...
@@ -53,8 +53,8 @@ int wk_service(wk *web); /* To be called after sselelect_wait() but before ssele
53 53
 int wk_serve_buffer_as_file(wk *web, int connid, void *data, int datalen, const char *mime);
54 54
 int wk_serve_file(wk *web, int connid, char *filename, const char *mime);
55 55
 int wk_serve_error(wk *web, int connid, wk_error wkerror);
56
-int wk_writestr(wk *web, int connid, char *str);
57
-int wk_write(wk *web, int connid, void *data, int datalen);
56
+int wk_writestr(wk *web, int connid, const char *str);
57
+int wk_write(wk *web, int connid, const void *data, int datalen);
58 58
 int wk_close(wk *web, int connid);
59 59
 
60 60
 char *wk_uri_getheader(wk_uri *wkuri, char *header, char *defaultvalue);