2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2014 Tatsuhiro Tsujikawa
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:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
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.
26 * This program is intended to measure library performance, avoiding
27 * overhead of underlying I/O library (e.g., libevent, Boost ASIO).
33 #endif /* !HAVE_CONFIG_H */
35 #include <sys/types.h>
36 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <netinet/tcp.h>
50 #include <sys/epoll.h>
51 #include <sys/timerfd.h>
53 #include <nghttp2/nghttp2.h>
55 #define SERVER_NAME "tiny-nghttpd nghttp2/" NGHTTP2_VERSION
57 #define MAKE_NV(name, value) \
59 (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, \
60 sizeof((value)) - 1, NGHTTP2_NV_FLAG_NONE \
63 #define MAKE_NV2(name, value, valuelen) \
65 (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, (valuelen), \
66 NGHTTP2_NV_FLAG_NONE \
69 #define array_size(a) (sizeof((a)) / sizeof((a)[0]))
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)
77 /* beginning of buffer */
79 /* one byte beyond the end of buffer */
81 /* next read/write position of buffer */
83 /* one byte beyond last data of buffer */
92 typedef struct stream {
93 struct stream *prev, *next;
94 /* mandatory header fields */
100 /* region of response body in rawscrbuf */
101 uint8_t *res_begin, *res_end;
102 /* io_buf wrapping rawscrbuf */
105 /* length of mandatory header fields */
111 /* stream ID of this stream */
113 /* fd for reading file */
115 /* scratch buffer for this stream */
116 uint8_t rawscrbuf[4096];
119 typedef struct { int (*handler)(io_loop *, uint32_t, void *); } evhandle;
123 nghttp2_session *session;
126 /* pending library output */
127 const uint8_t *cache;
128 /* io_buf wrapping rawoutbuf */
130 /* length of cache */
135 uint8_t rawoutbuf[65536];
152 /* length of docroot */
155 nghttp2_session_callbacks *shared_callbacks;
156 nghttp2_option *shared_option;
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);
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;
167 static void io_buf_add(io_buf *buf, const void *src, size_t len) {
168 memcpy(buf->last, src, len);
172 static char *io_buf_add_str(io_buf *buf, const void *src, size_t len) {
173 uint8_t *start = buf->last;
175 memcpy(buf->last, src, len);
179 return (char *)start;
182 static int memseq(const uint8_t *a, size_t alen, const char *b) {
183 const uint8_t *last = a + alen;
185 for (; a != last && *b && *a == *b; ++a, ++b)
188 return a == last && *b == 0;
191 static char *cpydig(char *buf, int n, size_t len) {
196 *p-- = (n % 10) + '0';
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"};
208 static size_t http_date(char *buf, time_t t) {
212 if (gmtime_r(&t, &tms) == NULL) {
216 /* Sat, 27 Sep 2014 06:31:15 GMT */
218 memcpy(p, DAY_OF_WEEK[tms.tm_wday], 3);
222 p = cpydig(p, tms.tm_mday, 2);
224 memcpy(p, MONTH[tms.tm_mon], 3);
227 p = cpydig(p, tms.tm_year + 1900, 4);
229 p = cpydig(p, tms.tm_hour, 2);
231 p = cpydig(p, tms.tm_min, 2);
233 p = cpydig(p, tms.tm_sec, 2);
234 memcpy(p, " GMT", 4);
240 static char date[29];
243 static void update_date() { datelen = http_date(date, time(NULL)); }
245 static size_t utos(char *buf, size_t len, uint64_t n) {
258 for (; t; t /= 10, ++nwrite)
267 *buf-- = (n % 10) + '0';
274 static void print_errno(const char *prefix, int errnum) {
278 errmsg = strerror_r(errnum, buf, sizeof(buf));
280 fprintf(stderr, "%s: %s\n", prefix, errmsg);
283 #define list_insert(head, elem) \
285 (elem)->prev = (head); \
286 (elem)->next = (head)->next; \
288 if ((head)->next) { \
289 (head)->next->prev = (elem); \
291 (head)->next = (elem); \
294 #define list_remove(elem) \
296 (elem)->prev->next = (elem)->next; \
297 if ((elem)->next) { \
298 (elem)->next->prev = (elem)->prev; \
302 static stream *stream_new(int32_t stream_id, connection *conn) {
305 strm = malloc(sizeof(stream));
307 strm->prev = strm->next = NULL;
310 strm->authority = NULL;
313 strm->res_begin = NULL;
314 strm->res_end = NULL;
317 strm->authoritylen = 0;
320 strm->stream_id = stream_id;
324 list_insert(&conn->strm_head, strm);
326 io_buf_init(&strm->scrbuf, strm->rawscrbuf, sizeof(strm->rawscrbuf));
331 static void stream_del(stream *strm) {
334 if (strm->filefd != -1) {
341 static connection *connection_new(int fd) {
345 conn = malloc(sizeof(connection));
347 rv = nghttp2_session_server_new2(&conn->session, shared_callbacks, conn,
357 io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf));
358 conn->evhn.handler = handle_connection;
359 conn->strm_head.next = NULL;
368 static void connection_del(connection *conn) {
371 nghttp2_session_del(conn->session);
372 shutdown(conn->fd, SHUT_WR);
375 strm = conn->strm_head.next;
377 stream *next_strm = strm->next;
386 static int connection_start(connection *conn) {
387 nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
390 rv = nghttp2_submit_settings(conn->session, NGHTTP2_FLAG_NONE, &iv, 1);
399 static int server_init(server *serv, const char *node, const char *service) {
401 struct addrinfo hints;
402 struct addrinfo *res, *rp;
405 socklen_t optlen = sizeof(on);
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;
413 rv = getaddrinfo(node, service, &hints, &res);
416 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
420 for (rp = res; rp; rp = rp->ai_next) {
422 socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol);
428 rv = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, optlen);
431 print_errno("setsockopt", errno);
434 if (bind(fd, rp->ai_addr, rp->ai_addrlen) != 0) {
439 if (listen(fd, 65536) != 0) {
450 fprintf(stderr, "No address to bind\n");
455 serv->evhn.handler = handle_accept;
460 static int timer_init(timer *tmr) {
462 struct itimerspec timerval = {{1, 0}, {1, 0}};
464 fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
466 print_errno("timerfd_create", errno);
470 if (timerfd_settime(fd, 0, &timerval, NULL) != 0) {
471 print_errno("timerfd_settime", errno);
476 tmr->evhn.handler = handle_timer;
481 static int io_loop_init(io_loop *loop) {
484 epfd = epoll_create1(0);
487 print_errno("epoll_create", errno);
496 static int io_loop_ctl(io_loop *loop, int op, int fd, uint32_t events,
499 struct epoll_event ev;
504 rv = epoll_ctl(loop->epfd, op, fd, &ev);
507 print_errno("epoll_ctl", errno);
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);
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);
522 static int io_loop_run(io_loop *loop, server *serv _U_) {
523 #define NUM_EVENTS 1024
524 struct epoll_event events[NUM_EVENTS];
529 struct epoll_event *ev, *end;
531 while ((nev = epoll_wait(loop->epfd, events, NUM_EVENTS, -1)) == -1 &&
536 print_errno("epoll_wait", errno);
540 for (ev = events, end = events + nev; ev != end; ++ev) {
542 evhn->handler(loop, ev->events, ev->data.ptr);
547 static int handle_timer(io_loop *loop _U_, uint32_t events _U_, void *ptr) {
552 while ((nread = read(tmr->fd, &buf, sizeof(buf))) == -1 && errno == EINTR)
555 assert(nread == sizeof(buf));
562 static int handle_accept(io_loop *loop, uint32_t events _U_, void *ptr) {
566 socklen_t optlen = sizeof(on);
572 while ((acfd = accept4(serv->fd, NULL, NULL, SOCK_NONBLOCK)) == -1 &&
591 rv = setsockopt(acfd, IPPROTO_TCP, TCP_NODELAY, &on, optlen);
594 print_errno("setsockopt", errno);
597 conn = connection_new(acfd);
604 if (connection_start(conn) != 0 ||
605 io_loop_add(loop, acfd, EPOLLIN | EPOLLOUT, conn) != 0) {
606 connection_del(conn);
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,
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;
625 while ((nread = read(strm->filefd, buf, length)) == -1 && errno == EINTR)
628 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
630 strm->fileleft -= nread;
631 if (nread == 0 || strm->fileleft == 0) {
632 if (strm->fileleft != 0) {
633 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
635 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
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;
649 memcpy(buf, strm->res_begin, nwrite);
650 strm->res_begin += nwrite;
652 if (strm->res_begin == strm->res_end) {
653 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
659 static int hex_digit(char c) {
660 return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') ||
661 ('a' <= c && c <= 'f');
664 static unsigned int hex_to_uint(char c) {
676 static void percent_decode(io_buf *buf, const char *s) {
678 if (*s == '?' || *s == '#') {
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));
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);
699 static int make_path(io_buf *pathbuf, const char *req, size_t reqlen _U_) {
706 if (docrootlen + strlen(req) + sizeof("index.html") >
707 (size_t)io_buf_left(pathbuf)) {
711 io_buf_add(pathbuf, docroot, docrootlen);
715 percent_decode(pathbuf, req);
717 if (*(pathbuf->last - 1) == '/') {
718 io_buf_add(pathbuf, "index.html", sizeof("index.html") - 1);
721 *pathbuf->last++ = '\0';
723 if (!check_path((const char *)p, pathbuf->last - 1 - p)) {
731 static int status_response(stream *strm, connection *conn,
732 const char *status_code) {
734 size_t status_codelen = strlen(status_code);
735 char contentlength[19];
736 size_t contentlengthlen;
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", ""),
745 nva[0].value = (uint8_t *)status_code;
746 nva[0].valuelen = strlen(status_code);
748 #define BODY1 "<html><head><title>"
749 #define BODY2 "</title></head><body><h1>"
750 #define BODY3 "</h1></body></html>"
752 reslen = sizeof(BODY1) - 1 + sizeof(BODY2) - 1 + sizeof(BODY3) - 1 +
755 if ((size_t)io_buf_left(&strm->scrbuf) < reslen) {
756 contentlength[0] = '0';
757 contentlengthlen = 1;
760 contentlengthlen = utos(contentlength, sizeof(contentlength), reslen);
762 strm->res_begin = strm->scrbuf.last;
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);
770 strm->res_end = strm->scrbuf.last;
774 nva[nvlen].value = (uint8_t *)contentlength;
775 nva[nvlen].valuelen = contentlengthlen;
779 prd.source.ptr = strm;
780 prd.read_callback = resbuf_read_callback;
782 rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen,
791 static int redirect_response(stream *strm, connection *conn) {
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", ""),
800 /* + 1 for trailing '/' */
801 locationlen = strm->schemelen + 3 + strm->hostlen + strm->pathlen + 1;
802 if ((size_t)io_buf_left(&strm->scrbuf) < locationlen) {
806 nva[4].value = strm->scrbuf.last;
807 nva[4].valuelen = locationlen;
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++ = '/';
815 rv = nghttp2_submit_response(conn->session, strm->stream_id, nva,
816 array_size(nva), NULL);
825 static int process_request(stream *strm, connection *conn) {
829 nghttp2_data_provider prd;
832 char contentlength[19];
833 size_t contentlengthlen;
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", ""),
842 io_buf_init(&pathbuf, (uint8_t *)path, sizeof(path));
844 rv = make_path(&pathbuf, strm->path, strm->pathlen);
847 return status_response(strm, conn, "400");
850 fd = open(path, O_RDONLY);
853 return status_response(strm, conn, "404");
858 rv = fstat(fd, &stbuf);
861 return status_response(strm, conn, "404");
864 if (stbuf.st_mode & S_IFDIR) {
865 return redirect_response(strm, conn);
868 prd.source.ptr = strm;
869 prd.read_callback = fd_read_callback;
871 strm->fileleft = stbuf.st_size;
873 lastmodlen = http_date(lastmod, stbuf.st_mtim.tv_sec);
874 contentlengthlen = utos(contentlength, sizeof(contentlength), stbuf.st_size);
876 nva[nvlen].value = (uint8_t *)contentlength;
877 nva[nvlen].valuelen = contentlengthlen;
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;
892 nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, &prd);
900 static int on_begin_headers_callback(nghttp2_session *session,
901 const nghttp2_frame *frame,
903 connection *conn = user_data;
906 if (frame->hd.type != NGHTTP2_HEADERS ||
907 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
911 strm = stream_new(frame->hd.stream_id, conn);
914 nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
915 NGHTTP2_INTERNAL_ERROR);
919 nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, strm);
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_,
929 connection *conn = user_data;
932 if (frame->hd.type != NGHTTP2_HEADERS ||
933 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
937 strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
943 if (!nghttp2_check_header_name(name, namelen) ||
944 !nghttp2_check_header_value(value, valuelen)) {
945 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
948 if (memseq(name, namelen, ":method")) {
950 stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
951 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
953 strm->method = io_buf_add_str(&strm->scrbuf, value, valuelen);
955 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
957 strm->methodlen = valuelen;
961 if (memseq(name, namelen, ":scheme")) {
963 stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
964 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
966 strm->scheme = io_buf_add_str(&strm->scrbuf, value, valuelen);
968 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
970 strm->schemelen = valuelen;
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;
979 strm->authority = io_buf_add_str(&strm->scrbuf, value, valuelen);
980 if (!strm->authority) {
981 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
983 strm->authoritylen = valuelen;
987 if (memseq(name, namelen, ":path")) {
989 stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
990 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
992 strm->path = io_buf_add_str(&strm->scrbuf, value, valuelen);
994 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
996 strm->pathlen = valuelen;
1000 if (name[0] == ':') {
1001 stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
1002 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1005 if (memseq(name, namelen, "host") && !strm->host) {
1006 strm->host = io_buf_add_str(&strm->scrbuf, value, valuelen);
1008 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1010 strm->hostlen = valuelen;
1016 static int on_frame_recv_callback(nghttp2_session *session,
1017 const nghttp2_frame *frame, void *user_data) {
1018 connection *conn = user_data;
1021 if (frame->hd.type != NGHTTP2_HEADERS ||
1022 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
1026 strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
1032 if (!strm->method || !strm->scheme || !strm->path ||
1033 (!strm->authority && !strm->host)) {
1034 stream_error(conn, strm->stream_id, NGHTTP2_PROTOCOL_ERROR);
1039 strm->host = strm->authority;
1040 strm->hostlen = strm->authoritylen;
1043 if (process_request(strm, conn) != 0) {
1044 stream_error(conn, strm->stream_id, NGHTTP2_INTERNAL_ERROR);
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_) {
1056 strm = nghttp2_session_get_stream_user_data(session, stream_id);
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;
1072 if (frame->hd.type != NGHTTP2_HEADERS) {
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);
1083 static int do_read(connection *conn) {
1090 while ((nread = read(conn->fd, buf, sizeof(buf))) == -1 && errno == EINTR)
1093 if (errno == EAGAIN || errno == EWOULDBLOCK) {
1104 nproc = nghttp2_session_mem_recv(conn->session, buf, nread);
1112 static int do_write(connection *conn) {
1114 if (io_buf_len(&conn->buf)) {
1116 while ((nwrite = write(conn->fd, conn->buf.pos,
1117 io_buf_len(&conn->buf))) == -1 &&
1121 if (errno == EAGAIN || errno == EWOULDBLOCK) {
1127 conn->buf.pos += nwrite;
1129 if (io_buf_len(&conn->buf)) {
1133 io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf));
1137 io_buf_add(&conn->buf, conn->cache, conn->cachelen);
1146 n = nghttp2_session_mem_send(conn->session, &b);
1153 if (io_buf_len(&conn->buf) == 0) {
1159 if (io_buf_left(&conn->buf) < n) {
1165 io_buf_add(&conn->buf, b, n);
1170 static int handle_connection(io_loop *loop, uint32_t events, void *ptr) {
1171 connection *conn = ptr;
1173 uint32_t nextev = 0;
1175 if (events & (EPOLLHUP | EPOLLERR)) {
1179 if (events & EPOLLIN) {
1187 rv = do_write(conn);
1193 if (nghttp2_session_want_read(conn->session)) {
1197 if (io_buf_len(&conn->buf) || nghttp2_session_want_write(conn->session)) {
1205 io_loop_mod(loop, conn->fd, nextev, conn);
1210 connection_del(conn);
1215 int main(int argc, char **argv) {
1220 struct sigaction act;
1221 const char *address;
1222 const char *service;
1225 fprintf(stderr, "Usage: tiny-nghttpd <address> <port> <doc-root>\n");
1232 docrootlen = strlen(docroot);
1234 memset(&act, 0, sizeof(act));
1235 act.sa_handler = SIG_IGN;
1236 sigaction(SIGPIPE, &act, NULL);
1238 rv = server_init(&serv, address, service);
1244 rv = timer_init(&tmr);
1250 rv = io_loop_init(&loop);
1256 rv = nghttp2_session_callbacks_new(&shared_callbacks);
1258 fprintf(stderr, "nghttp2_session_callbacks_new: %s", nghttp2_strerror(rv));
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);
1273 rv = nghttp2_option_new(&shared_option);
1275 fprintf(stderr, "nghttp2_option_new: %s", nghttp2_strerror(rv));
1279 nghttp2_option_set_recv_client_preface(shared_option, 1);
1281 rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv);
1287 rv = io_loop_add(&loop, tmr.fd, EPOLLIN, &tmr);
1295 io_loop_run(&loop, &serv);
1297 nghttp2_option_del(shared_option);
1298 nghttp2_session_callbacks_del(shared_callbacks);