tftpclient: converting to net sockets
[barrelfish] / usr / webserver / http_server.c
1 /**
2  * \file
3  * \brief HTTP server
4  *
5  * \bug All calls to tcp_write currently use TCP_WRITE_FLAG_COPY, causing
6  *   the data to be copied to LWIP's internal memory pool. This is necessary,
7  *   because we lack the VM support necessary to do a reverse mapping for
8  *   arbitrary memory regions.
9  */
10
11 /*
12  * Copyright (c) 2008, 2009, ETH Zurich.
13  * All rights reserved.
14  *
15  * This file is distributed under the terms in the attached LICENSE file.
16  * If you do not find this file, copies can be found by writing to:
17  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
18  */
19
20 #include <stdio.h>
21 #include <sys/param.h>
22 #include <barrelfish/barrelfish.h>
23 #include <netinet/in.h>
24 #include <net_sockets/net_sockets.h>
25 #include <netbench/netbench.h>
26
27 #define LWIP_IPV4
28 #include <lwip/ip_addr.h>
29
30 #include "http_cache.h"
31 #include "webserver_network.h"
32 #include "webserver_debug.h"
33 #include "webserver_session.h"
34
35 #define HTTP_PORT       80
36
37 #define CRLF "\r\n"
38 #define HTTP_HEADER_COMMON "Server: Barrelfish" CRLF
39 #define HTTP_HEADER_200 "HTTP/1.0 200 OK" CRLF HTTP_HEADER_COMMON
40 #define HTTP_HEADER_404 "HTTP/1.0 404 Not Found" CRLF HTTP_HEADER_COMMON
41 #define HTTP_HEADER_500 "HTTP/1.0 500 Internal Server Error" CRLF HTTP_HEADER_COMMON
42
43 #define HTTP_MIME_HTML  "Content-type: text/html; charset=utf-8" CRLF
44 #define HTTP_MIME_GIF   "Content-type: image/gif" CRLF
45 #define HTTP_MIME_JPG   "Content-type: image/jpeg" CRLF
46 #define HTTP_MIME_PDF   "Content-type: application/pdf" CRLF
47 #define HTTP_MIME_TAR   "Content-type: application/x-tar" CRLF
48 #define HTTP_MIME_GZIP  "Content-type: application/x-gzip" CRLF
49 #define HTTP_MIME_BZIP2 "Content-type: application/x-bzip2" CRLF
50 #define HTTP_MIME_OCTET "Content-type: application/octet-stream" CRLF
51
52
53 static const char notfound_reply[] =
54     HTTP_HEADER_404 HTTP_MIME_HTML CRLF
55     "<html>" CRLF
56     "<body>" CRLF
57     "<h1>404 Not Found</h1>" CRLF
58     "<p>The requested URL was not found.</p>" CRLF
59     "</body>" CRLF
60     "</html>" CRLF;
61
62 static const char error_reply[] =
63     HTTP_HEADER_500 HTTP_MIME_HTML CRLF
64     "<html>" CRLF
65     "<body>" CRLF
66     "<h1>500 Internal Server Error</h1>" CRLF
67     "<p>Bad stuff happened. Damn.</p>" CRLF
68     "</body>" CRLF
69     "</html>" CRLF;
70
71 static const char header_html[] = HTTP_HEADER_200 HTTP_MIME_HTML CRLF;
72 static const char header_gif[] = HTTP_HEADER_200 HTTP_MIME_GIF CRLF;
73 static const char header_jpg[] = HTTP_HEADER_200 HTTP_MIME_JPG CRLF;
74 static const char header_pdf[] = HTTP_HEADER_200 HTTP_MIME_PDF CRLF;
75 static const char header_bz2[] = HTTP_HEADER_200 HTTP_MIME_BZIP2 CRLF;
76 static const char header_gz[] = HTTP_HEADER_200 HTTP_MIME_GZIP CRLF;
77 static const char header_octet[] = HTTP_HEADER_200 HTTP_MIME_OCTET CRLF;
78
79 #if 0
80
81 #define MAX_DURATIONS   1000
82
83 #define INST_BEGIN \
84     static uint64_t _dursum = 0, _dur = 0;      \
85     uint64_t _begin = rdtsc();
86
87 #define INST_END \
88     _dursum += rdtsc() - _begin;                \
89     _dur++;                                                     \
90     if(_dur == MAX_DURATIONS) {                                 \
91         DEBUGPRINT("%s: %lu\n", __func__, _dursum / MAX_DURATIONS);     \
92         _dur = 0; _dursum = 0;                                      \
93     }
94
95 #else
96
97 #define INST_BEGIN
98 #define INST_END
99
100 #endif
101
102 /* GLOBAL STATE */
103 static int parallel_connections = 0; /* number of connections alive at moment */
104 static int request_counter = 0;  /* Total no. of requests received till now */
105 /* above both are for debugging purpose only */
106
107
108
109 static struct http_conn *http_conn_new(void)
110 {
111     struct http_conn *newconn = malloc(sizeof(struct http_conn));
112     assert (newconn != NULL);
113     memset (newconn, 0, sizeof(struct http_conn));
114
115     newconn->state = HTTP_STATE_NEW;
116     newconn->request_no = request_counter++;
117
118     DEBUGPRINT ("%d: http_conn created [p %d] %lu %lu\n", newconn->request_no,
119         parallel_connections, newconn->header_pos, newconn->header_length);
120     ++parallel_connections;
121     return newconn;
122 }
123
124
125 static void http_conn_free(struct http_conn *conn)
126 {
127     DEBUGPRINT ("%d: http_conn_free freeing [p %d]\n", conn->request_no,
128         parallel_connections);
129
130     if(conn->request != NULL) {
131         free(conn->request);
132     }
133     /* decrementing the reference to buff_holder */
134     decrement_buff_holder_ref (conn->hbuff);
135     free(conn);
136     --parallel_connections;
137 }
138
139 /* increments the reference counter and returns the incremented value */
140 long increment_http_conn_reference (struct http_conn *cs)
141 {
142     ++cs->ref_count;
143     return (cs->ref_count);
144 } /* end function: increment_http_conn_reference  */
145
146 /* This function decrements the references to http_conn
147  * and if references reach 0, the memory of struct is released. */
148 long decrement_http_conn_reference (struct http_conn *cs)
149 {
150     --cs->ref_count;
151     if (cs->mark_invalid) {
152         /* connection is no longer valid */
153         if (cs->ref_count <= 0) {
154             /* no one is using the connection */
155             /* so, free up the the memory */
156             http_conn_free(cs);
157             return 0;
158         }
159     } /* end if : invalid http_conn */
160     return cs->ref_count;
161 } /* end function: decrement_reference */
162
163
164 static void http_conn_invalidate (struct http_conn *conn)
165 {
166     DEBUGPRINT ("%d: http_conn_invalidate\n", conn->request_no);
167     conn->mark_invalid = 1;
168     decrement_http_conn_reference (conn);
169 }
170
171
172 // static void http_server_err(void *arg, errval_t err)
173 // {
174 //     struct http_conn *conn = arg;
175 //
176 //     DEBUGPRINT("http_server_err! %p %d\n", arg, err);
177 //     if(conn != NULL) {
178 //         DEBUGPRINT("%d: http_server_err! %p %d\n", conn->request_no, arg, err);
179 //         http_conn_invalidate (conn);
180 //     } else {
181 //         DEBUGPRINT("http_server_err! %p %d\n", arg, err);
182 //     }
183 // }
184 //
185 //
186 static void http_server_close(struct net_socket *tpcb, struct http_conn *cs)
187 {
188 /*
189     printf("%s %s %s %hu.%hu.%hu.%hu in %"PU"\n",
190             cs->hbuff->data ? "200" : "404", cs->request, cs->filename,
191            ip4_addr1(&cs->pcb->remote_ip), ip4_addr2(&cs->pcb->remote_ip),
192            ip4_addr3(&cs->pcb->remote_ip), ip4_addr4(&cs->pcb->remote_ip),
193             in_seconds(get_time_delta(&cs->start_ts)));
194 */
195     DEBUGPRINT("%d: http_server_close freeing the connection\n",
196         cs->request_no);
197
198     // replace TCP callbacks with NULL
199     if (cs != NULL) {
200         http_conn_invalidate (cs);
201     }
202     net_close(tpcb);
203 }
204
205 static errval_t trysend(struct net_socket *t, const void *data, size_t *len, bool
206 more)
207 {
208     size_t sendlen;
209     errval_t err;
210
211     for (sendlen = 0; sendlen < *len;) {
212         void *buffer;
213         size_t s = *len - sendlen;
214         s = s > 16000 ? 16000: s;
215
216         buffer = net_alloc(s);
217         if (!buffer)
218             break;
219         memcpy(buffer, data + sendlen, s);
220         err = net_send(t, buffer, s);
221         assert(err_is_ok(err));
222         sendlen += s;
223     }
224     *len = sendlen;
225     return SYS_ERR_OK;
226 }
227
228 static void http_send_data(struct net_socket *tpcb, struct http_conn *conn)
229 {
230     errval_t err;
231     const void *data;
232     size_t len;
233
234     switch (conn->state) {
235     case HTTP_STATE_SENDHEADER:
236         DEBUGPRINT ("%d: http_send_data: header_pos %lu < header_len %lu\n",
237             conn->request_no, conn->header_pos, conn->header_length);
238         assert(conn->header_pos < conn->header_length);
239         data = &conn->header[conn->header_pos];
240         len = conn->header_length - conn->header_pos;
241         err = trysend(tpcb, data, &len, (conn->hbuff->data != NULL));
242         if (err != SYS_ERR_OK) {
243             DEBUGPRINT("http_send_data(): Error %d sending header\n", err);
244             return; // will retry
245         }
246
247         conn->header_pos += len;
248         DEBUGPRINT ("%d: http_send_data incr: hdr_pos %lu < hdr_len %lu\n",
249                 conn->request_no, conn->header_pos, conn->header_length);
250         if (conn->header_pos == conn->header_length) {
251             conn->state = HTTP_STATE_SENDFILE; // fall through below
252             conn->reply_pos = 0;
253             conn->reply_sent = 0;
254         } else {
255             break;
256         }
257
258     case HTTP_STATE_SENDFILE:
259         if (conn->hbuff->data == NULL) {
260             conn->state = HTTP_STATE_CLOSING;
261             break;
262         }
263         data = conn->hbuff->data + conn->reply_pos; /* pointer arithmatic */
264         len = conn->hbuff->len - conn->reply_pos;
265         size_t maxlen = 16000 - (conn->reply_pos - conn->reply_sent);
266         // debug_printf("%s: %zd %zd\n", __func__, len, maxlen);
267         if (len > maxlen)
268             len = maxlen;
269         err = trysend(tpcb, data, &len, false);
270         if (err != SYS_ERR_OK) {
271             DEBUGPRINT("http_send_data(): Error %d sending payload\n", err);
272             return; // will retry
273         }
274         conn->reply_pos += len;
275         if (conn->reply_pos == conn->hbuff->len) {
276             conn->state = HTTP_STATE_CLOSING;
277         }
278         break;
279
280     default:
281         DEBUGPRINT ("http_send_data(): Wrong state! (%d)\n", conn->state);
282         break;
283     }
284 }
285
286 /* This function is called periodically from TCP.
287  * and is also responsible for taking care of stale connections.
288 **/
289 // static errval_t http_poll(void *arg, struct net_socket *tpcb)
290 // {
291 //     struct http_conn *conn = arg;
292 //
293 //     if (conn == NULL && tpcb->state == ESTABLISHED) {
294 //         tcp_abort(tpcb);
295 //         return ERR_ABRT;
296 //     } else if (conn != NULL && (conn->state == HTTP_STATE_SENDHEADER
297 //                                 || conn->state == HTTP_STATE_SENDFILE)) {
298 //         if (++conn->retries == 4) {
299 //             DEBUGPRINT ("connection closed, tried too hard\n");
300 //             http_conn_invalidate (conn);
301 //             net_delete_socket(tpcb);
302 //             return ERR_ABRT;
303 //         }
304 //         http_send_data(tpcb, conn);
305 //         if (conn->state == HTTP_STATE_CLOSING) {
306 //             DEBUGPRINT ("%d: http_poll closing the connection\n",
307 //                     conn->request_no);
308 //             http_server_close(tpcb, conn);
309 //         } else {
310 //             // tcp_output(tpcb);
311 //         }
312 //     } else if (conn != NULL && (conn->state == HTTP_STATE_NEW
313 //                                 || conn->state == HTTP_STATE_REQUEST)) {
314 //         /* abort connections that sit open for too long without sending a
315 // request */
316 //         if (++conn->retries == 60) {
317 //             DEBUGPRINT("connection in state %d too long, aborted\n",
318 //                          conn->state);
319 //             DEBUGPRINT("connection in state %d too long, aborted\n",
320 //                         conn->state);
321 //
322 //             http_conn_invalidate (conn);
323 //             net_delete_socket(tpcb);
324 //             return ERR_ABRT;
325 //         }
326 //     }
327 //     return SYS_ERR_OK;
328 // } /* end function: http_poll */
329
330 /* called when data is successfully sent */
331 static void http_server_sent(void *arg, struct net_socket *socket, void *buffer, size_t size)
332 {
333     struct http_conn *conn = arg;
334
335     assert(conn);
336     net_free(buffer);
337
338     // debug_printf("%s: %zd  %zd:%zd  %zd:%zd\n", __func__, size, conn->header_pos, conn->header_sent, conn->reply_pos, conn->reply_sent);
339     if (conn->header_sent < conn->header_pos)
340         conn->header_sent += size;
341     else
342         conn->reply_sent += size;
343
344     switch(conn->state) {
345     case HTTP_STATE_SENDHEADER:
346     case HTTP_STATE_SENDFILE:
347         // Need to send more data?
348         http_send_data(socket, conn);
349         if (conn->state != HTTP_STATE_CLOSING) {
350             // tcp_output(tpcb);
351             break;
352         }
353
354     case HTTP_STATE_CLOSING:
355         DEBUGPRINT("%d: http_server_sent closing the connection\n",
356                     conn->request_no);
357 // debug_printf("%s.%d: %zd\n", __func__, __LINE__, size);
358         if (conn->header_pos == conn->header_sent && conn->reply_pos == conn->reply_sent)
359             http_server_close(socket, conn);
360         break;
361
362     default:
363         break;
364     }
365 }
366
367 static const void *make_header(const char *uri, size_t *retlen)
368 {
369     /* FIXME: hack to guess MIME type */
370     size_t urilen = strlen(uri);
371     if (strcmp(uri + urilen - 5, ".html") == 0) {
372         *retlen = sizeof(header_html) - 1; // -1 for '\0'
373         return header_html;
374     } else if (strcmp(uri + urilen - 4, ".gif") == 0) {
375         *retlen = sizeof(header_gif) - 1;
376         return header_gif;
377     } else if (strcmp(uri + urilen - 4, ".jpg") == 0) {
378         *retlen = sizeof(header_jpg) - 1;
379         return header_jpg;
380     } else if (strcmp(uri + urilen - 4, ".pdf") == 0) {
381         *retlen = sizeof(header_pdf) - 1;
382         return header_pdf;
383     } else if (strcmp(uri + urilen - 4, ".bz2") == 0) {
384         *retlen = sizeof(header_bz2) - 1;
385         return header_bz2;
386     } else if (strcmp(uri + urilen - 3, ".gz") == 0) {
387         *retlen = sizeof(header_gz) - 1;
388         return header_gz;
389     } else {
390         *retlen = sizeof(header_octet) - 1;
391         return header_octet;
392     }
393 }
394
395 /* callback function to fetch file
396     This function is responsible for sending the fetched file */
397 static void send_response(struct http_conn *cs)
398 {
399
400     if (cs->error) {
401         DEBUGPRINT ("%d: BIGERROR Sending the response back of size %lu\n",
402                                         cs->request_no, cs->reply_pos);
403         DEBUGPRINT("%s %s %s %hu.%hu.%hu.%hu\n", "500",
404                cs->request, cs->filename,
405                ip4_addr1(&cs->pcb->remote_ip), ip4_addr2(&cs->pcb->remote_ip),
406                ip4_addr3(&cs->pcb->remote_ip), ip4_addr4(&cs->pcb->remote_ip));
407
408         cs->header = error_reply;
409         cs->header_length = sizeof(error_reply) - 1;
410         cs->header_sent = 0;
411     } else {
412         DEBUGPRINT ("%d: Sending the response back of size %lu\n",
413                 cs->request_no, cs->reply_pos);
414         DEBUGPRINT("%s %s %s %hu.%hu.%hu.%hu\n", cs->hbuff->data ?
415                 "200" : "404", cs->request, cs->filename,
416                ip4_addr1(&cs->pcb->remote_ip), ip4_addr2(&cs->pcb->remote_ip),
417                ip4_addr3(&cs->pcb->remote_ip), ip4_addr4(&cs->pcb->remote_ip));
418
419         if (cs->hbuff->data == NULL) {
420             /* not found, send 404 */
421             DEBUGPRINT ("%d: making 404 case\n",cs->request_no);
422             DEBUGPRINT ("witness: header_pos %lu < header_len %lu\n",
423                 cs->header_pos, cs->header_length);
424
425             cs->header = notfound_reply;
426             cs->header_length = sizeof(notfound_reply) - 1;
427             cs->header_sent = 0;
428         } else {
429             /* found, send static header */
430             cs->header = make_header(cs->filename, &cs->header_length);
431             cs->header_sent = 0;
432         }
433     } /* end else: internal error */
434
435     /* send data */
436     cs->state = HTTP_STATE_SENDHEADER;
437     cs->retries = 0;
438     http_send_data(cs->pcb, cs);
439
440     /* did we send the whole page? */
441     if (cs->state == HTTP_STATE_CLOSING) {
442         DEBUGPRINT("%d: send_response closing the connection\n",
443                 cs->request_no);
444 // debug_printf("%s.%d:\n", __func__, __LINE__);
445                                 // http_server_close(cs->pcb, cs);
446     } else {
447         // tcp_output(cs->pcb);
448     }
449 } /* end function: send_response */
450
451 // static errval_t http_server_recv(void *arg, struct net_socket *tpcb, struct pbuf *p,
452 //                               errval_t err);
453 //
454 static void http_server_recv(void *arg, struct net_socket *tpcb, void *data, size_t size, struct in_addr ip_address, uint16_t port)
455 {
456     struct http_conn *conn = arg;
457
458     DEBUGPRINT("%d, http_server_recv called\n", conn->request_no);
459     // debug_printf("%s(%d): %ld\n", __func__, tpcb->descriptor, size);
460
461     // check if connection closed
462     assert(conn);
463     if (size == 0) {
464         DEBUGPRINT("%d, closing from http_server_recv\n", conn->request_no);
465 // debug_printf("%s.%d:\n", __func__, __LINE__);
466         http_server_close(tpcb, conn);
467         return;
468     }
469
470     switch(conn->state) {
471     case HTTP_STATE_NEW:
472         conn->state = HTTP_STATE_REQUEST;
473         // Fall through...
474
475     case HTTP_STATE_REQUEST:
476         /* don't send an immediate ack here, do it later with the data */
477         // tpcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
478
479         /* accumulate the request data */
480         conn->request_length += size;
481         conn->request = realloc(conn->request, conn->request_length + 1);
482         char *d = conn->request + conn->request_length - size;
483         memcpy(d, data, size);
484         d += size;
485         *d = '\0';
486
487         // pbuf_free(p);
488
489         // have we seen the end of the request yet?
490         if (strstr(conn->request, CRLF CRLF) == NULL) {
491             break;
492         }
493
494         // ignore everything after the first line
495         char *cp = strstr(conn->request, CRLF);
496         assert(cp != NULL);
497         *cp = '\0';
498
499         // Parse request: break into method and URI
500         cp = strchr(conn->request, ' ');
501         if (cp == NULL) {
502             goto invalid;
503         }
504         *cp = '\0';
505         const char *uri = cp + 1;
506         cp = strrchr(uri, ' ');
507         if (cp == NULL) {
508             goto invalid;
509         }
510         *cp = '\0';
511
512         if (strcmp(conn->request, "GET") != 0) {
513             goto invalid;
514         }
515
516         // drop a leading /
517         if (uri[0] == '/') {
518             uri++;
519         }
520
521         // if URI is now empty, look for index.html
522         if (uri[0] == '\0') {
523             uri = "index.html";
524         }
525
526         conn->filename = (char *)uri;
527         conn->callback = send_response;
528         conn->pcb = tpcb;
529         conn->start_ts = rdtsc();
530         /* for callback execution */
531         errval_t e = http_cache_lookup(uri, conn);
532         if (e != SYS_ERR_OK) {
533             conn->error = 1;
534             send_response(conn);
535         }
536         break;
537
538     default:
539         DEBUGPRINT("http_server_recv(): data received in wrong state (%d)!\n",
540                      conn->state);
541         conn->error = 1;
542         send_response(conn);
543         break;
544     }
545     return;
546
547 invalid:
548     DEBUGPRINT("invalid request: %s\n", conn->request);
549     DEBUGPRINT("%d: invalid request: %s\n",conn->request_no, conn->request);
550     conn->state = HTTP_STATE_CLOSING;
551 // debug_printf("%s.%d:\n", __func__, __LINE__);
552     http_server_close(tpcb, conn);
553     return;
554 }
555
556 static void http_server_accept(void *arg, struct net_socket *tpcb)
557 {
558 // #if TCP_LISTEN_BACKLOG
559 //     /* Decrease the listen backlog counter */
560 //     struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen*)arg;
561 // #endif
562     // debug_printf("%s(%d):\n", __func__, tpcb->descriptor);
563     struct http_conn *conn = http_conn_new();
564     DEBUGPRINT("accpet called: %s\n", conn->request);
565     increment_http_conn_reference (conn);
566     /* NOTE: This initial increment marks the basic assess and it will be
567         decremented by http_server_invalidate */
568
569     net_set_user_state(tpcb, conn);
570     net_recv(tpcb, http_server_recv);
571     net_set_sent(tpcb, http_server_sent);
572
573     // tcp_err(tpcb, http_server_err);
574     // tcp_poll(tpcb, http_poll, 4);
575 }
576
577
578 static void realinit(void)
579 {
580     uint64_t ts = rdtsc();
581     struct net_socket *pcb = net_tcp_socket();
582 //    err_t e = tcp_bind(pcb, IP_ADDR_ANY, (HTTP_PORT + disp_get_core_id()));
583     errval_t e = net_bind(pcb, (struct in_addr){(INADDR_ANY)}, HTTP_PORT);
584     assert(e == SYS_ERR_OK);
585
586     e = net_listen(pcb, 100);
587     assert(e == SYS_ERR_OK);
588
589     net_accept(pcb, http_server_accept);
590     printf("HTTP setup time %"PU"\n", in_seconds(get_time_delta(&ts)));
591     printf("#######################################################\n");
592     printf("Starting webserver\n");
593     printf("#######################################################\n");
594
595 }
596
597 void http_server_init(struct in_addr server, const char *path)
598 {
599     http_cache_init(server, path, realinit);
600 }
601
602
603 uint64_t get_time_delta(uint64_t *l_ts)
604 {
605     uint64_t ct = rdtsc();
606     uint64_t delta = ct - *l_ts;
607     *l_ts = ct;
608     return delta;
609     //  return delta / (2800 * 1000);
610 } // end function: get_time_delta