tizen 2.4 release
[external/nghttp2.git] / examples / tiny-nghttpd.c
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2014 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 /*
26  * This program is intended to measure library performance, avoiding
27  * overhead of underlying I/O library (e.g., libevent, Boost ASIO).
28  */
29 #define _GNU_SOURCE
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif /* !HAVE_CONFIG_H */
34
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <netdb.h>
40 #include <netinet/in.h>
41 #include <netinet/tcp.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <time.h>
45 #include <string.h>
46 #include <stdio.h>
47 #include <errno.h>
48 #include <assert.h>
49 #include <signal.h>
50 #include <sys/epoll.h>
51 #include <sys/timerfd.h>
52
53 #include <nghttp2/nghttp2.h>
54
55 #define SERVER_NAME "tiny-nghttpd nghttp2/" NGHTTP2_VERSION
56
57 #define MAKE_NV(name, value)                                                   \
58   {                                                                            \
59     (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1,                 \
60         sizeof((value)) - 1, NGHTTP2_NV_FLAG_NONE                              \
61   }
62
63 #define MAKE_NV2(name, value, valuelen)                                        \
64   {                                                                            \
65     (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, (valuelen),     \
66         NGHTTP2_NV_FLAG_NONE                                                   \
67   }
68
69 #define array_size(a) (sizeof((a)) / sizeof((a)[0]))
70
71 /* Returns the length of remaning data in buffer */
72 #define io_buf_len(iobuf) ((iobuf)->last - (iobuf)->pos)
73 /* Returns the space buffer can still accept */
74 #define io_buf_left(iobuf) ((iobuf)->end - (iobuf)->last)
75
76 typedef struct {
77   /* beginning of buffer */
78   uint8_t *begin;
79   /* one byte beyond the end of buffer */
80   uint8_t *end;
81   /* next read/write position of buffer */
82   uint8_t *pos;
83   /* one byte beyond last data of buffer */
84   uint8_t *last;
85 } io_buf;
86
87 typedef struct {
88   /* epoll fd */
89   int epfd;
90 } io_loop;
91
92 typedef struct stream {
93   struct stream *prev, *next;
94   /* mandatory header fields */
95   char *method;
96   char *scheme;
97   char *authority;
98   char *path;
99   char *host;
100   /* region of response body in rawscrbuf */
101   uint8_t *res_begin, *res_end;
102   /* io_buf wrapping rawscrbuf */
103   io_buf scrbuf;
104   int64_t fileleft;
105   /* length of mandatory header fields */
106   size_t methodlen;
107   size_t schemelen;
108   size_t authoritylen;
109   size_t pathlen;
110   size_t hostlen;
111   /* stream ID of this stream */
112   int32_t stream_id;
113   /* fd for reading file */
114   int filefd;
115   /* scratch buffer for this stream */
116   uint8_t rawscrbuf[4096];
117 } stream;
118
119 typedef struct { int (*handler)(io_loop *, uint32_t, void *); } evhandle;
120
121 typedef struct {
122   evhandle evhn;
123   nghttp2_session *session;
124   /* list of stream */
125   stream strm_head;
126   /* pending library output */
127   const uint8_t *cache;
128   /* io_buf wrapping rawoutbuf */
129   io_buf buf;
130   /* length of cache */
131   size_t cachelen;
132   /* client fd */
133   int fd;
134   /* output buffer */
135   uint8_t rawoutbuf[65536];
136 } connection;
137
138 typedef struct {
139   evhandle evhn;
140   /* listening fd */
141   int fd;
142 } server;
143
144 typedef struct {
145   evhandle evhn;
146   /* timerfd */
147   int fd;
148 } timer;
149
150 /* document root */
151 const char *docroot;
152 /* length of docroot */
153 size_t docrootlen;
154
155 nghttp2_session_callbacks *shared_callbacks;
156 nghttp2_option *shared_option;
157
158 static int handle_accept(io_loop *loop, uint32_t events, void *ptr);
159 static int handle_connection(io_loop *loop, uint32_t events, void *ptr);
160 static int handle_timer(io_loop *loop, uint32_t events, void *ptr);
161
162 static void io_buf_init(io_buf *buf, uint8_t *underlying, size_t len) {
163   buf->begin = buf->pos = buf->last = underlying;
164   buf->end = underlying + len;
165 }
166
167 static void io_buf_add(io_buf *buf, const void *src, size_t len) {
168   memcpy(buf->last, src, len);
169   buf->last += len;
170 }
171
172 static char *io_buf_add_str(io_buf *buf, const void *src, size_t len) {
173   uint8_t *start = buf->last;
174
175   memcpy(buf->last, src, len);
176   buf->last += len;
177   *buf->last++ = '\0';
178
179   return (char *)start;
180 }
181
182 static int memseq(const uint8_t *a, size_t alen, const char *b) {
183   const uint8_t *last = a + alen;
184
185   for (; a != last && *b && *a == *b; ++a, ++b)
186     ;
187
188   return a == last && *b == 0;
189 }
190
191 static char *cpydig(char *buf, int n, size_t len) {
192   char *p;
193
194   p = buf + len - 1;
195   do {
196     *p-- = (n % 10) + '0';
197     n /= 10;
198   } while (p >= buf);
199
200   return buf + len;
201 }
202
203 static const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
204                               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
205 static const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed",
206                                     "Thu", "Fri", "Sat"};
207
208 static size_t http_date(char *buf, time_t t) {
209   struct tm tms;
210   char *p = buf;
211
212   if (gmtime_r(&t, &tms) == NULL) {
213     return 0;
214   }
215
216   /* Sat, 27 Sep 2014 06:31:15 GMT */
217
218   memcpy(p, DAY_OF_WEEK[tms.tm_wday], 3);
219   p += 3;
220   *p++ = ',';
221   *p++ = ' ';
222   p = cpydig(p, tms.tm_mday, 2);
223   *p++ = ' ';
224   memcpy(p, MONTH[tms.tm_mon], 3);
225   p += 3;
226   *p++ = ' ';
227   p = cpydig(p, tms.tm_year + 1900, 4);
228   *p++ = ' ';
229   p = cpydig(p, tms.tm_hour, 2);
230   *p++ = ':';
231   p = cpydig(p, tms.tm_min, 2);
232   *p++ = ':';
233   p = cpydig(p, tms.tm_sec, 2);
234   memcpy(p, " GMT", 4);
235   p += 4;
236
237   return p - buf;
238 }
239
240 static char date[29];
241 static char datelen;
242
243 static void update_date() { datelen = http_date(date, time(NULL)); }
244
245 static size_t utos(char *buf, size_t len, uint64_t n) {
246   size_t nwrite = 0;
247   uint64_t t = n;
248
249   if (len == 0) {
250     return 0;
251   }
252
253   if (n == 0) {
254     buf[0] = '0';
255     return 1;
256   }
257
258   for (; t; t /= 10, ++nwrite)
259     ;
260
261   if (nwrite > len) {
262     return 0;
263   }
264
265   buf += nwrite - 1;
266   do {
267     *buf-- = (n % 10) + '0';
268     n /= 10;
269   } while (n);
270
271   return nwrite;
272 }
273
274 static void print_errno(const char *prefix, int errnum) {
275   char buf[1024];
276   char *errmsg;
277
278   errmsg = strerror_r(errnum, buf, sizeof(buf));
279
280   fprintf(stderr, "%s: %s\n", prefix, errmsg);
281 }
282
283 #define list_insert(head, elem)                                                \
284   do {                                                                         \
285     (elem)->prev = (head);                                                     \
286     (elem)->next = (head)->next;                                               \
287                                                                                \
288     if ((head)->next) {                                                        \
289       (head)->next->prev = (elem);                                             \
290     }                                                                          \
291     (head)->next = (elem);                                                     \
292   } while (0)
293
294 #define list_remove(elem)                                                      \
295   do {                                                                         \
296     (elem)->prev->next = (elem)->next;                                         \
297     if ((elem)->next) {                                                        \
298       (elem)->next->prev = (elem)->prev;                                       \
299     }                                                                          \
300   } while (0)
301
302 static stream *stream_new(int32_t stream_id, connection *conn) {
303   stream *strm;
304
305   strm = malloc(sizeof(stream));
306
307   strm->prev = strm->next = NULL;
308   strm->method = NULL;
309   strm->scheme = NULL;
310   strm->authority = NULL;
311   strm->path = NULL;
312   strm->host = NULL;
313   strm->res_begin = NULL;
314   strm->res_end = NULL;
315   strm->methodlen = 0;
316   strm->schemelen = 0;
317   strm->authoritylen = 0;
318   strm->pathlen = 0;
319   strm->hostlen = 0;
320   strm->stream_id = stream_id;
321   strm->filefd = -1;
322   strm->fileleft = 0;
323
324   list_insert(&conn->strm_head, strm);
325
326   io_buf_init(&strm->scrbuf, strm->rawscrbuf, sizeof(strm->rawscrbuf));
327
328   return strm;
329 }
330
331 static void stream_del(stream *strm) {
332   list_remove(strm);
333
334   if (strm->filefd != -1) {
335     close(strm->filefd);
336   }
337
338   free(strm);
339 }
340
341 static connection *connection_new(int fd) {
342   connection *conn;
343   int rv;
344
345   conn = malloc(sizeof(connection));
346
347   rv = nghttp2_session_server_new2(&conn->session, shared_callbacks, conn,
348                                    shared_option);
349
350   if (rv != 0) {
351     goto cleanup;
352   }
353
354   conn->fd = fd;
355   conn->cache = NULL;
356   conn->cachelen = 0;
357   io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf));
358   conn->evhn.handler = handle_connection;
359   conn->strm_head.next = NULL;
360
361   return conn;
362
363 cleanup:
364   free(conn);
365   return NULL;
366 }
367
368 static void connection_del(connection *conn) {
369   stream *strm;
370
371   nghttp2_session_del(conn->session);
372   shutdown(conn->fd, SHUT_WR);
373   close(conn->fd);
374
375   strm = conn->strm_head.next;
376   while (strm) {
377     stream *next_strm = strm->next;
378
379     stream_del(strm);
380     strm = next_strm;
381   }
382
383   free(conn);
384 }
385
386 static int connection_start(connection *conn) {
387   nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
388   int rv;
389
390   rv = nghttp2_submit_settings(conn->session, NGHTTP2_FLAG_NONE, &iv, 1);
391
392   if (rv != 0) {
393     return -1;
394   }
395
396   return 0;
397 }
398
399 static int server_init(server *serv, const char *node, const char *service) {
400   int rv;
401   struct addrinfo hints;
402   struct addrinfo *res, *rp;
403   int fd;
404   int on = 1;
405   socklen_t optlen = sizeof(on);
406
407   memset(&hints, 0, sizeof(hints));
408   hints.ai_family = AF_UNSPEC;
409   hints.ai_socktype = SOCK_STREAM;
410   hints.ai_protocol = 0;
411   hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
412
413   rv = getaddrinfo(node, service, &hints, &res);
414
415   if (rv != 0) {
416     fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
417     return -1;
418   }
419
420   for (rp = res; rp; rp = rp->ai_next) {
421     fd =
422         socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol);
423
424     if (fd == -1) {
425       continue;
426     }
427
428     rv = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, optlen);
429
430     if (rv == -1) {
431       print_errno("setsockopt", errno);
432     }
433
434     if (bind(fd, rp->ai_addr, rp->ai_addrlen) != 0) {
435       close(fd);
436       continue;
437     }
438
439     if (listen(fd, 65536) != 0) {
440       close(fd);
441       continue;
442     }
443
444     break;
445   }
446
447   freeaddrinfo(res);
448
449   if (!rp) {
450     fprintf(stderr, "No address to bind\n");
451     return -1;
452   }
453
454   serv->fd = fd;
455   serv->evhn.handler = handle_accept;
456
457   return 0;
458 }
459
460 static int timer_init(timer *tmr) {
461   int fd;
462   struct itimerspec timerval = {{1, 0}, {1, 0}};
463
464   fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
465   if (fd == -1) {
466     print_errno("timerfd_create", errno);
467     return -1;
468   }
469
470   if (timerfd_settime(fd, 0, &timerval, NULL) != 0) {
471     print_errno("timerfd_settime", errno);
472     return -1;
473   }
474
475   tmr->fd = fd;
476   tmr->evhn.handler = handle_timer;
477
478   return 0;
479 }
480
481 static int io_loop_init(io_loop *loop) {
482   int epfd;
483
484   epfd = epoll_create1(0);
485
486   if (epfd == -1) {
487     print_errno("epoll_create", errno);
488     return -1;
489   }
490
491   loop->epfd = epfd;
492
493   return 0;
494 }
495
496 static int io_loop_ctl(io_loop *loop, int op, int fd, uint32_t events,
497                        void *ptr) {
498   int rv;
499   struct epoll_event ev;
500
501   ev.events = events;
502   ev.data.ptr = ptr;
503
504   rv = epoll_ctl(loop->epfd, op, fd, &ev);
505
506   if (rv != 0) {
507     print_errno("epoll_ctl", errno);
508     return -1;
509   }
510
511   return 0;
512 }
513
514 static int io_loop_add(io_loop *loop, int fd, uint32_t events, void *ptr) {
515   return io_loop_ctl(loop, EPOLL_CTL_ADD, fd, events, ptr);
516 }
517
518 static int io_loop_mod(io_loop *loop, int fd, uint32_t events, void *ptr) {
519   return io_loop_ctl(loop, EPOLL_CTL_MOD, fd, events, ptr);
520 }
521
522 static int io_loop_run(io_loop *loop, server *serv _U_) {
523 #define NUM_EVENTS 1024
524   struct epoll_event events[NUM_EVENTS];
525
526   for (;;) {
527     int nev;
528     evhandle *evhn;
529     struct epoll_event *ev, *end;
530
531     while ((nev = epoll_wait(loop->epfd, events, NUM_EVENTS, -1)) == -1 &&
532            errno == EINTR)
533       ;
534
535     if (nev == -1) {
536       print_errno("epoll_wait", errno);
537       return -1;
538     }
539
540     for (ev = events, end = events + nev; ev != end; ++ev) {
541       evhn = ev->data.ptr;
542       evhn->handler(loop, ev->events, ev->data.ptr);
543     }
544   }
545 }
546
547 static int handle_timer(io_loop *loop _U_, uint32_t events _U_, void *ptr) {
548   timer *tmr = ptr;
549   int64_t buf;
550   ssize_t nread;
551
552   while ((nread = read(tmr->fd, &buf, sizeof(buf))) == -1 && errno == EINTR)
553     ;
554
555   assert(nread == sizeof(buf));
556
557   update_date();
558
559   return 0;
560 }
561
562 static int handle_accept(io_loop *loop, uint32_t events _U_, void *ptr) {
563   int acfd;
564   server *serv = ptr;
565   int on = 1;
566   socklen_t optlen = sizeof(on);
567   int rv;
568
569   for (;;) {
570     connection *conn;
571
572     while ((acfd = accept4(serv->fd, NULL, NULL, SOCK_NONBLOCK)) == -1 &&
573            errno == EINTR)
574       ;
575
576     if (acfd == -1) {
577       switch (errno) {
578       case ENETDOWN:
579       case EPROTO:
580       case ENOPROTOOPT:
581       case EHOSTDOWN:
582       case ENONET:
583       case EHOSTUNREACH:
584       case EOPNOTSUPP:
585       case ENETUNREACH:
586         continue;
587       }
588       return 0;
589     }
590
591     rv = setsockopt(acfd, IPPROTO_TCP, TCP_NODELAY, &on, optlen);
592
593     if (rv == -1) {
594       print_errno("setsockopt", errno);
595     }
596
597     conn = connection_new(acfd);
598
599     if (conn == NULL) {
600       close(acfd);
601       continue;
602     }
603
604     if (connection_start(conn) != 0 ||
605         io_loop_add(loop, acfd, EPOLLIN | EPOLLOUT, conn) != 0) {
606       connection_del(conn);
607     }
608   }
609 }
610
611 static void stream_error(connection *conn, int32_t stream_id,
612                          uint32_t error_code) {
613   nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE, stream_id,
614                             error_code);
615 }
616
617 static ssize_t fd_read_callback(nghttp2_session *session _U_,
618                                 int32_t stream_id _U_, uint8_t *buf,
619                                 size_t length, uint32_t *data_flags,
620                                 nghttp2_data_source *source,
621                                 void *user_data _U_) {
622   stream *strm = source->ptr;
623   ssize_t nread;
624
625   while ((nread = read(strm->filefd, buf, length)) == -1 && errno == EINTR)
626     ;
627   if (nread == -1) {
628     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
629   }
630   strm->fileleft -= nread;
631   if (nread == 0 || strm->fileleft == 0) {
632     if (strm->fileleft != 0) {
633       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
634     }
635     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
636   }
637   return nread;
638 }
639
640 static ssize_t resbuf_read_callback(nghttp2_session *session _U_,
641                                     int32_t stream_id _U_, uint8_t *buf,
642                                     size_t length, uint32_t *data_flags,
643                                     nghttp2_data_source *source,
644                                     void *user_data _U_) {
645   stream *strm = source->ptr;
646   size_t left = strm->res_end - strm->res_begin;
647   size_t nwrite = length < left ? length : left;
648
649   memcpy(buf, strm->res_begin, nwrite);
650   strm->res_begin += nwrite;
651
652   if (strm->res_begin == strm->res_end) {
653     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
654   }
655
656   return nwrite;
657 }
658
659 static int hex_digit(char c) {
660   return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') ||
661          ('a' <= c && c <= 'f');
662 }
663
664 static unsigned int hex_to_uint(char c) {
665   if (c <= '9') {
666     return c - '0';
667   }
668
669   if (c <= 'F') {
670     return c - 'A' + 10;
671   }
672
673   return c - 'a' + 10;
674 }
675
676 static void percent_decode(io_buf *buf, const char *s) {
677   for (; *s; ++s) {
678     if (*s == '?' || *s == '#') {
679       break;
680     }
681
682     if (*s == '%' && hex_digit(*(s + 1)) && hex_digit(*(s + 2))) {
683       *buf->last++ = (hex_to_uint(*(s + 1)) << 4) + hex_to_uint(*(s + 2));
684       s += 2;
685       continue;
686     }
687
688     *buf->last++ = *s;
689   }
690 }
691
692 static int check_path(const char *path, size_t len) {
693   return path[0] == '/' && strchr(path, '\\') == NULL &&
694          strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
695          (len < 3 || memcmp(path + len - 3, "/..", 3) != 0) &&
696          (len < 2 || memcmp(path + len - 2, "/.", 2) != 0);
697 }
698
699 static int make_path(io_buf *pathbuf, const char *req, size_t reqlen _U_) {
700   uint8_t *p;
701
702   if (req[0] != '/') {
703     return -1;
704   }
705
706   if (docrootlen + strlen(req) + sizeof("index.html") >
707       (size_t)io_buf_left(pathbuf)) {
708     return -1;
709   }
710
711   io_buf_add(pathbuf, docroot, docrootlen);
712
713   p = pathbuf->last;
714
715   percent_decode(pathbuf, req);
716
717   if (*(pathbuf->last - 1) == '/') {
718     io_buf_add(pathbuf, "index.html", sizeof("index.html") - 1);
719   }
720
721   *pathbuf->last++ = '\0';
722
723   if (!check_path((const char *)p, pathbuf->last - 1 - p)) {
724
725     return -1;
726   }
727
728   return 0;
729 }
730
731 static int status_response(stream *strm, connection *conn,
732                            const char *status_code) {
733   int rv;
734   size_t status_codelen = strlen(status_code);
735   char contentlength[19];
736   size_t contentlengthlen;
737   size_t reslen;
738   nghttp2_data_provider prd, *prdptr;
739   nghttp2_nv nva[5] = {
740       MAKE_NV(":status", ""), MAKE_NV("server", SERVER_NAME),
741       MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""),
742   };
743   size_t nvlen = 3;
744
745   nva[0].value = (uint8_t *)status_code;
746   nva[0].valuelen = strlen(status_code);
747
748 #define BODY1 "<html><head><title>"
749 #define BODY2 "</title></head><body><h1>"
750 #define BODY3 "</h1></body></html>"
751
752   reslen = sizeof(BODY1) - 1 + sizeof(BODY2) - 1 + sizeof(BODY3) - 1 +
753            status_codelen * 2;
754
755   if ((size_t)io_buf_left(&strm->scrbuf) < reslen) {
756     contentlength[0] = '0';
757     contentlengthlen = 1;
758     prdptr = NULL;
759   } else {
760     contentlengthlen = utos(contentlength, sizeof(contentlength), reslen);
761
762     strm->res_begin = strm->scrbuf.last;
763
764     io_buf_add(&strm->scrbuf, BODY1, sizeof(BODY1) - 1);
765     io_buf_add(&strm->scrbuf, status_code, strlen(status_code));
766     io_buf_add(&strm->scrbuf, BODY2, sizeof(BODY2) - 1);
767     io_buf_add(&strm->scrbuf, status_code, strlen(status_code));
768     io_buf_add(&strm->scrbuf, BODY3, sizeof(BODY3) - 1);
769
770     strm->res_end = strm->scrbuf.last;
771     prdptr = &prd;
772   }
773
774   nva[nvlen].value = (uint8_t *)contentlength;
775   nva[nvlen].valuelen = contentlengthlen;
776
777   ++nvlen;
778
779   prd.source.ptr = strm;
780   prd.read_callback = resbuf_read_callback;
781
782   rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen,
783                                prdptr);
784   if (rv != 0) {
785     return -1;
786   }
787
788   return 0;
789 }
790
791 static int redirect_response(stream *strm, connection *conn) {
792   int rv;
793   size_t locationlen;
794   nghttp2_nv nva[5] = {
795       MAKE_NV(":status", "301"),       MAKE_NV("server", SERVER_NAME),
796       MAKE_NV2("date", date, datelen), MAKE_NV("content-length", "0"),
797       MAKE_NV("location", ""),
798   };
799
800   /* + 1 for trailing '/' */
801   locationlen = strm->schemelen + 3 + strm->hostlen + strm->pathlen + 1;
802   if ((size_t)io_buf_left(&strm->scrbuf) < locationlen) {
803     return -1;
804   }
805
806   nva[4].value = strm->scrbuf.last;
807   nva[4].valuelen = locationlen;
808
809   io_buf_add(&strm->scrbuf, strm->scheme, strm->schemelen);
810   io_buf_add(&strm->scrbuf, "://", 3);
811   io_buf_add(&strm->scrbuf, strm->host, strm->hostlen);
812   io_buf_add(&strm->scrbuf, strm->path, strm->pathlen);
813   *strm->scrbuf.last++ = '/';
814
815   rv = nghttp2_submit_response(conn->session, strm->stream_id, nva,
816                                array_size(nva), NULL);
817
818   if (rv != 0) {
819     return -1;
820   }
821
822   return 0;
823 }
824
825 static int process_request(stream *strm, connection *conn) {
826   int fd;
827   struct stat stbuf;
828   int rv;
829   nghttp2_data_provider prd;
830   char lastmod[32];
831   size_t lastmodlen;
832   char contentlength[19];
833   size_t contentlengthlen;
834   char path[1024];
835   io_buf pathbuf;
836   nghttp2_nv nva[5] = {
837       MAKE_NV(":status", "200"), MAKE_NV("server", SERVER_NAME),
838       MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""),
839   };
840   size_t nvlen = 3;
841
842   io_buf_init(&pathbuf, (uint8_t *)path, sizeof(path));
843
844   rv = make_path(&pathbuf, strm->path, strm->pathlen);
845
846   if (rv != 0) {
847     return status_response(strm, conn, "400");
848   }
849
850   fd = open(path, O_RDONLY);
851
852   if (fd == -1) {
853     return status_response(strm, conn, "404");
854   }
855
856   strm->filefd = fd;
857
858   rv = fstat(fd, &stbuf);
859
860   if (rv == -1) {
861     return status_response(strm, conn, "404");
862   }
863
864   if (stbuf.st_mode & S_IFDIR) {
865     return redirect_response(strm, conn);
866   }
867
868   prd.source.ptr = strm;
869   prd.read_callback = fd_read_callback;
870
871   strm->fileleft = stbuf.st_size;
872
873   lastmodlen = http_date(lastmod, stbuf.st_mtim.tv_sec);
874   contentlengthlen = utos(contentlength, sizeof(contentlength), stbuf.st_size);
875
876   nva[nvlen].value = (uint8_t *)contentlength;
877   nva[nvlen].valuelen = contentlengthlen;
878
879   ++nvlen;
880
881   if (lastmodlen) {
882     nva[nvlen].name = (uint8_t *)"last-modified";
883     nva[nvlen].namelen = sizeof("last-modified") - 1;
884     nva[nvlen].value = (uint8_t *)lastmod;
885     nva[nvlen].valuelen = lastmodlen;
886     nva[nvlen].flags = NGHTTP2_NV_FLAG_NONE;
887
888     ++nvlen;
889   }
890
891   rv =
892       nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, &prd);
893   if (rv != 0) {
894     return -1;
895   }
896
897   return 0;
898 }
899
900 static int on_begin_headers_callback(nghttp2_session *session,
901                                      const nghttp2_frame *frame,
902                                      void *user_data) {
903   connection *conn = user_data;
904   stream *strm;
905
906   if (frame->hd.type != NGHTTP2_HEADERS ||
907       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
908     return 0;
909   }
910
911   strm = stream_new(frame->hd.stream_id, conn);
912
913   if (!strm) {
914     nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
915                               NGHTTP2_INTERNAL_ERROR);
916     return 0;
917   }
918
919   nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, strm);
920
921   return 0;
922 }
923
924 static int on_header_callback(nghttp2_session *session,
925                               const nghttp2_frame *frame, const uint8_t *name,
926                               size_t namelen, const uint8_t *value,
927                               size_t valuelen, uint8_t flags _U_,
928                               void *user_data) {
929   connection *conn = user_data;
930   stream *strm;
931
932   if (frame->hd.type != NGHTTP2_HEADERS ||
933       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
934     return 0;
935   }
936
937   strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
938
939   if (!strm) {
940     return 0;
941   }
942
943   if (!nghttp2_check_header_name(name, namelen) ||
944       !nghttp2_check_header_value(value, valuelen)) {
945     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
946   }
947
948   if (memseq(name, namelen, ":method")) {
949     if (strm->method) {
950       stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
951       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
952     }
953     strm->method = io_buf_add_str(&strm->scrbuf, value, valuelen);
954     if (!strm->method) {
955       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
956     }
957     strm->methodlen = valuelen;
958     return 0;
959   }
960
961   if (memseq(name, namelen, ":scheme")) {
962     if (strm->scheme) {
963       stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
964       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
965     }
966     strm->scheme = io_buf_add_str(&strm->scrbuf, value, valuelen);
967     if (!strm->scheme) {
968       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
969     }
970     strm->schemelen = valuelen;
971     return 0;
972   }
973
974   if (memseq(name, namelen, ":authority")) {
975     if (strm->authority) {
976       stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
977       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
978     }
979     strm->authority = io_buf_add_str(&strm->scrbuf, value, valuelen);
980     if (!strm->authority) {
981       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
982     }
983     strm->authoritylen = valuelen;
984     return 0;
985   }
986
987   if (memseq(name, namelen, ":path")) {
988     if (strm->path) {
989       stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
990       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
991     }
992     strm->path = io_buf_add_str(&strm->scrbuf, value, valuelen);
993     if (!strm->path) {
994       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
995     }
996     strm->pathlen = valuelen;
997     return 0;
998   }
999
1000   if (name[0] == ':') {
1001     stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
1002     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1003   }
1004
1005   if (memseq(name, namelen, "host") && !strm->host) {
1006     strm->host = io_buf_add_str(&strm->scrbuf, value, valuelen);
1007     if (!strm->host) {
1008       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1009     }
1010     strm->hostlen = valuelen;
1011   }
1012
1013   return 0;
1014 }
1015
1016 static int on_frame_recv_callback(nghttp2_session *session,
1017                                   const nghttp2_frame *frame, void *user_data) {
1018   connection *conn = user_data;
1019   stream *strm;
1020
1021   if (frame->hd.type != NGHTTP2_HEADERS ||
1022       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
1023     return 0;
1024   }
1025
1026   strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
1027
1028   if (!strm) {
1029     return 0;
1030   }
1031
1032   if (!strm->method || !strm->scheme || !strm->path ||
1033       (!strm->authority && !strm->host)) {
1034     stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
1035     return 0;
1036   }
1037
1038   if (!strm->host) {
1039     strm->host = strm->authority;
1040     strm->hostlen = strm->authoritylen;
1041   }
1042
1043   if (process_request(strm, conn) != 0) {
1044     stream_error(conn, strm->stream_id, NGHTTP2_INTERNAL_ERROR);
1045     return 0;
1046   }
1047
1048   return 0;
1049 }
1050
1051 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
1052                                     uint32_t error_code _U_,
1053                                     void *user_data _U_) {
1054   stream *strm;
1055
1056   strm = nghttp2_session_get_stream_user_data(session, stream_id);
1057
1058   if (!strm) {
1059     return 0;
1060   }
1061
1062   stream_del(strm);
1063
1064   return 0;
1065 }
1066
1067 static int on_frame_not_send_callback(nghttp2_session *session _U_,
1068                                       const nghttp2_frame *frame,
1069                                       int lib_error_code _U_, void *user_data) {
1070   connection *conn = user_data;
1071
1072   if (frame->hd.type != NGHTTP2_HEADERS) {
1073     return 0;
1074   }
1075
1076   /* Issue RST_STREAM so that stream does not hang around. */
1077   nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE,
1078                             frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR);
1079
1080   return 0;
1081 }
1082
1083 static int do_read(connection *conn) {
1084   uint8_t buf[32768];
1085
1086   for (;;) {
1087     ssize_t nread;
1088     ssize_t nproc;
1089
1090     while ((nread = read(conn->fd, buf, sizeof(buf))) == -1 && errno == EINTR)
1091       ;
1092     if (nread == -1) {
1093       if (errno == EAGAIN || errno == EWOULDBLOCK) {
1094         return 0;
1095       }
1096
1097       return -1;
1098     }
1099
1100     if (nread == 0) {
1101       return -1;
1102     }
1103
1104     nproc = nghttp2_session_mem_recv(conn->session, buf, nread);
1105
1106     if (nproc < 0) {
1107       return -1;
1108     }
1109   }
1110 }
1111
1112 static int do_write(connection *conn) {
1113   for (;;) {
1114     if (io_buf_len(&conn->buf)) {
1115       ssize_t nwrite;
1116       while ((nwrite = write(conn->fd, conn->buf.pos,
1117                              io_buf_len(&conn->buf))) == -1 &&
1118              errno == EINTR)
1119         ;
1120       if (nwrite == -1) {
1121         if (errno == EAGAIN || errno == EWOULDBLOCK) {
1122           return 0;
1123         }
1124         return -1;
1125       }
1126
1127       conn->buf.pos += nwrite;
1128
1129       if (io_buf_len(&conn->buf)) {
1130         return 0;
1131       }
1132
1133       io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf));
1134     }
1135
1136     if (conn->cache) {
1137       io_buf_add(&conn->buf, conn->cache, conn->cachelen);
1138       conn->cache = NULL;
1139       conn->cachelen = 0;
1140     }
1141
1142     for (;;) {
1143       ssize_t n;
1144       const uint8_t *b;
1145
1146       n = nghttp2_session_mem_send(conn->session, &b);
1147
1148       if (n < 0) {
1149         return -1;
1150       }
1151
1152       if (n == 0) {
1153         if (io_buf_len(&conn->buf) == 0) {
1154           return 0;
1155         }
1156         break;
1157       }
1158
1159       if (io_buf_left(&conn->buf) < n) {
1160         conn->cache = b;
1161         conn->cachelen = n;
1162         break;
1163       }
1164
1165       io_buf_add(&conn->buf, b, n);
1166     }
1167   }
1168 }
1169
1170 static int handle_connection(io_loop *loop, uint32_t events, void *ptr) {
1171   connection *conn = ptr;
1172   int rv;
1173   uint32_t nextev = 0;
1174
1175   if (events & (EPOLLHUP | EPOLLERR)) {
1176     goto cleanup;
1177   }
1178
1179   if (events & EPOLLIN) {
1180     rv = do_read(conn);
1181
1182     if (rv != 0) {
1183       goto cleanup;
1184     }
1185   }
1186
1187   rv = do_write(conn);
1188
1189   if (rv != 0) {
1190     goto cleanup;
1191   }
1192
1193   if (nghttp2_session_want_read(conn->session)) {
1194     nextev |= EPOLLIN;
1195   }
1196
1197   if (io_buf_len(&conn->buf) || nghttp2_session_want_write(conn->session)) {
1198     nextev |= EPOLLOUT;
1199   }
1200
1201   if (!nextev) {
1202     goto cleanup;
1203   }
1204
1205   io_loop_mod(loop, conn->fd, nextev, conn);
1206
1207   return 0;
1208
1209 cleanup:
1210   connection_del(conn);
1211
1212   return 0;
1213 }
1214
1215 int main(int argc, char **argv) {
1216   int rv;
1217   server serv;
1218   timer tmr;
1219   io_loop loop;
1220   struct sigaction act;
1221   const char *address;
1222   const char *service;
1223
1224   if (argc < 4) {
1225     fprintf(stderr, "Usage: tiny-nghttpd <address> <port> <doc-root>\n");
1226     exit(EXIT_FAILURE);
1227   }
1228
1229   address = argv[1];
1230   service = argv[2];
1231   docroot = argv[3];
1232   docrootlen = strlen(docroot);
1233
1234   memset(&act, 0, sizeof(act));
1235   act.sa_handler = SIG_IGN;
1236   sigaction(SIGPIPE, &act, NULL);
1237
1238   rv = server_init(&serv, address, service);
1239
1240   if (rv != 0) {
1241     exit(EXIT_FAILURE);
1242   }
1243
1244   rv = timer_init(&tmr);
1245
1246   if (rv != 0) {
1247     exit(EXIT_FAILURE);
1248   }
1249
1250   rv = io_loop_init(&loop);
1251
1252   if (rv != 0) {
1253     exit(EXIT_FAILURE);
1254   }
1255
1256   rv = nghttp2_session_callbacks_new(&shared_callbacks);
1257   if (rv != 0) {
1258     fprintf(stderr, "nghttp2_session_callbacks_new: %s", nghttp2_strerror(rv));
1259     exit(EXIT_FAILURE);
1260   }
1261
1262   nghttp2_session_callbacks_set_on_begin_headers_callback(
1263       shared_callbacks, on_begin_headers_callback);
1264   nghttp2_session_callbacks_set_on_header_callback(shared_callbacks,
1265                                                    on_header_callback);
1266   nghttp2_session_callbacks_set_on_frame_recv_callback(shared_callbacks,
1267                                                        on_frame_recv_callback);
1268   nghttp2_session_callbacks_set_on_stream_close_callback(
1269       shared_callbacks, on_stream_close_callback);
1270   nghttp2_session_callbacks_set_on_frame_not_send_callback(
1271       shared_callbacks, on_frame_not_send_callback);
1272
1273   rv = nghttp2_option_new(&shared_option);
1274   if (rv != 0) {
1275     fprintf(stderr, "nghttp2_option_new: %s", nghttp2_strerror(rv));
1276     exit(EXIT_FAILURE);
1277   }
1278
1279   nghttp2_option_set_recv_client_preface(shared_option, 1);
1280
1281   rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv);
1282
1283   if (rv != 0) {
1284     exit(EXIT_FAILURE);
1285   }
1286
1287   rv = io_loop_add(&loop, tmr.fd, EPOLLIN, &tmr);
1288
1289   if (rv != 0) {
1290     exit(EXIT_FAILURE);
1291   }
1292
1293   update_date();
1294
1295   io_loop_run(&loop, &serv);
1296
1297   nghttp2_option_del(shared_option);
1298   nghttp2_session_callbacks_del(shared_callbacks);
1299
1300   return 0;
1301 }