diff --git a/configure.ac b/configure.ac index 49b66071..dbfe62fc 100644 --- a/configure.ac +++ b/configure.ac @@ -729,13 +729,6 @@ AC_CHECK_DECLS([initgroups], [], [], [[ #include ]]) -# Checks for epoll availability, primarily for examples/tiny-nghttpd -AX_HAVE_EPOLL([have_epoll=yes], [have_epoll=no]) - -AM_CONDITIONAL([ENABLE_TINY_NGHTTPD], - [ test "x${have_epoll}" = "xyes" && - test "x${have_timerfd_create}" = "xyes"]) - save_CFLAGS=$CFLAGS save_CXXFLAGS=$CXXFLAGS diff --git a/examples/Makefile.am b/examples/Makefile.am index a80949c7..776d39d4 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -51,14 +51,6 @@ libevent_server_SOURCES = libevent-server.c deflate_SOURCES = deflate.c -if ENABLE_TINY_NGHTTPD - -noinst_PROGRAMS += tiny-nghttpd - -tiny_nghttpd_SOURCES = tiny-nghttpd.c - -endif # ENABLE_TINY_NGHTTPD - if ENABLE_ASIO_LIB noinst_PROGRAMS += asio-sv asio-sv2 asio-cl asio-cl2 diff --git a/examples/tiny-nghttpd.c b/examples/tiny-nghttpd.c deleted file mode 100644 index 0141663d..00000000 --- a/examples/tiny-nghttpd.c +++ /dev/null @@ -1,1344 +0,0 @@ -/* - * nghttp2 - HTTP/2 C Library - * - * Copyright (c) 2014 Tatsuhiro Tsujikawa - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -/* - * This program is intended to measure library performance, avoiding - * overhead of underlying I/O library (e.g., libevent, Boost ASIO). - */ -#define _GNU_SOURCE - -#ifdef HAVE_CONFIG_H -#include -#endif /* HAVE_CONFIG_H */ - -#include -#ifdef HAVE_SYS_SOCKET_H -#include -#endif /* HAVE_SYS_SOCKET_H */ -#include -#ifdef HAVE_FCNTL_H -#include -#endif /* HAVE_FCNTL_H */ -#ifdef HAVE_NETDB_H -#include -#endif /* HAVE_NETDB_H */ -#ifdef HAVE_NETINET_IN_H -#include -#endif /* HAVE_NETINET_IN_H */ -#include -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ -#include -#ifdef HAVE_TIME_H -#include -#endif /* HAVE_TIME_H */ -#include -#include -#include -#include -#include -#include -#include - -#include - -#define SERVER_NAME "tiny-nghttpd nghttp2/" NGHTTP2_VERSION - -#define MAKE_NV(name, value) \ - { \ - (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, \ - sizeof((value)) - 1, NGHTTP2_NV_FLAG_NONE \ - } - -#define MAKE_NV2(name, value, valuelen) \ - { \ - (uint8_t *)(name), (uint8_t *)(value), sizeof((name)) - 1, (valuelen), \ - NGHTTP2_NV_FLAG_NONE \ - } - -#define array_size(a) (sizeof((a)) / sizeof((a)[0])) - -/* Returns the length of remaning data in buffer */ -#define io_buf_len(iobuf) ((size_t)((iobuf)->last - (iobuf)->pos)) -/* Returns the space buffer can still accept */ -#define io_buf_left(iobuf) ((size_t)((iobuf)->end - (iobuf)->last)) - -typedef struct { - /* beginning of buffer */ - uint8_t *begin; - /* one byte beyond the end of buffer */ - uint8_t *end; - /* next read/write position of buffer */ - uint8_t *pos; - /* one byte beyond last data of buffer */ - uint8_t *last; -} io_buf; - -typedef struct { - /* epoll fd */ - int epfd; -} io_loop; - -typedef struct stream { - struct stream *prev, *next; - /* mandatory header fields */ - char *method; - char *scheme; - char *authority; - char *path; - char *host; - /* region of response body in rawscrbuf */ - uint8_t *res_begin, *res_end; - /* io_buf wrapping rawscrbuf */ - io_buf scrbuf; - int64_t fileleft; - /* length of mandatory header fields */ - size_t methodlen; - size_t schemelen; - size_t authoritylen; - size_t pathlen; - size_t hostlen; - /* stream ID of this stream */ - int32_t stream_id; - /* fd for reading file */ - int filefd; - /* scratch buffer for this stream */ - uint8_t rawscrbuf[4096]; -} stream; - -typedef struct { int (*handler)(io_loop *, uint32_t, void *); } evhandle; - -typedef struct { - evhandle evhn; - nghttp2_session *session; - /* list of stream */ - stream strm_head; - /* pending library output */ - const uint8_t *cache; - /* io_buf wrapping rawoutbuf */ - io_buf buf; - /* length of cache */ - size_t cachelen; - /* client fd */ - int fd; - /* output buffer */ - uint8_t rawoutbuf[65536]; -} connection; - -typedef struct { - evhandle evhn; - /* listening fd */ - int fd; -} server; - -typedef struct { - evhandle evhn; - /* timerfd */ - int fd; -} timer; - -/* document root */ -static const char *docroot; -/* length of docroot */ -static size_t docrootlen; - -static nghttp2_session_callbacks *shared_callbacks; - -static int handle_accept(io_loop *loop, uint32_t events, void *ptr); -static int handle_connection(io_loop *loop, uint32_t events, void *ptr); -static int handle_timer(io_loop *loop, uint32_t events, void *ptr); - -static void io_buf_init(io_buf *buf, uint8_t *underlying, size_t len) { - buf->begin = buf->pos = buf->last = underlying; - buf->end = underlying + len; -} - -static void io_buf_add(io_buf *buf, const void *src, size_t len) { - memcpy(buf->last, src, len); - buf->last += len; -} - -static char *io_buf_add_str(io_buf *buf, const void *src, size_t len) { - uint8_t *start = buf->last; - - memcpy(buf->last, src, len); - buf->last += len; - *buf->last++ = '\0'; - - return (char *)start; -} - -static int memeq(const void *a, const void *b, size_t n) { - return memcmp(a, b, n) == 0; -} - -#define streq(A, B, N) ((sizeof((A)) - 1) == (N) && memeq((A), (B), (N))) - -typedef enum { - NGHTTP2_TOKEN__AUTHORITY, - NGHTTP2_TOKEN__METHOD, - NGHTTP2_TOKEN__PATH, - NGHTTP2_TOKEN__SCHEME, - NGHTTP2_TOKEN_HOST -} nghttp2_token; - -/* Inspired by h2o header lookup. https://github.com/h2o/h2o */ -static int lookup_token(const uint8_t *name, size_t namelen) { - switch (namelen) { - case 5: - switch (name[namelen - 1]) { - case 'h': - if (streq(":pat", name, 4)) { - return NGHTTP2_TOKEN__PATH; - } - break; - } - break; - case 7: - switch (name[namelen - 1]) { - case 'd': - if (streq(":metho", name, 6)) { - return NGHTTP2_TOKEN__METHOD; - } - break; - case 'e': - if (streq(":schem", name, 6)) { - return NGHTTP2_TOKEN__SCHEME; - } - break; - } - break; - case 10: - switch (name[namelen - 1]) { - case 'y': - if (streq(":authorit", name, 9)) { - return NGHTTP2_TOKEN__AUTHORITY; - } - break; - } - break; - } - return -1; -} - -static char *cpydig(char *buf, int n, size_t len) { - char *p; - - p = buf + len - 1; - do { - *p-- = (char)((n % 10) + '0'); - n /= 10; - } while (p >= buf); - - return buf + len; -} - -static const char *MONTH[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; -static const char *DAY_OF_WEEK[] = {"Sun", "Mon", "Tue", "Wed", - "Thu", "Fri", "Sat"}; - -static size_t http_date(char *buf, time_t t) { - struct tm tms; - char *p = buf; - - if (gmtime_r(&t, &tms) == NULL) { - return 0; - } - - /* Sat, 27 Sep 2014 06:31:15 GMT */ - - memcpy(p, DAY_OF_WEEK[tms.tm_wday], 3); - p += 3; - *p++ = ','; - *p++ = ' '; - p = cpydig(p, tms.tm_mday, 2); - *p++ = ' '; - memcpy(p, MONTH[tms.tm_mon], 3); - p += 3; - *p++ = ' '; - p = cpydig(p, tms.tm_year + 1900, 4); - *p++ = ' '; - p = cpydig(p, tms.tm_hour, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_min, 2); - *p++ = ':'; - p = cpydig(p, tms.tm_sec, 2); - memcpy(p, " GMT", 4); - p += 4; - - return (size_t)(p - buf); -} - -static char date[29]; -static size_t datelen; - -static void update_date(void) { datelen = http_date(date, time(NULL)); } - -static size_t utos(char *buf, size_t len, uint64_t n) { - size_t nwrite = 0; - uint64_t t = n; - - if (len == 0) { - return 0; - } - - if (n == 0) { - buf[0] = '0'; - return 1; - } - - for (; t; t /= 10, ++nwrite) - ; - - if (nwrite > len) { - return 0; - } - - buf += nwrite - 1; - do { - *buf-- = (char)((n % 10) + '0'); - n /= 10; - } while (n); - - return nwrite; -} - -static void print_errno(const char *prefix, int errnum) { - char buf[1024]; - char *errmsg; - - errmsg = strerror_r(errnum, buf, sizeof(buf)); - - fprintf(stderr, "%s: %s\n", prefix, errmsg); -} - -#define list_insert(head, elem) \ - do { \ - (elem)->prev = (head); \ - (elem)->next = (head)->next; \ - \ - if ((head)->next) { \ - (head)->next->prev = (elem); \ - } \ - (head)->next = (elem); \ - } while (0) - -#define list_remove(elem) \ - do { \ - (elem)->prev->next = (elem)->next; \ - if ((elem)->next) { \ - (elem)->next->prev = (elem)->prev; \ - } \ - } while (0) - -static stream *stream_new(int32_t stream_id, connection *conn) { - stream *strm; - - strm = malloc(sizeof(stream)); - - strm->prev = strm->next = NULL; - strm->method = NULL; - strm->scheme = NULL; - strm->authority = NULL; - strm->path = NULL; - strm->host = NULL; - strm->res_begin = NULL; - strm->res_end = NULL; - strm->methodlen = 0; - strm->schemelen = 0; - strm->authoritylen = 0; - strm->pathlen = 0; - strm->hostlen = 0; - strm->stream_id = stream_id; - strm->filefd = -1; - strm->fileleft = 0; - - list_insert(&conn->strm_head, strm); - - io_buf_init(&strm->scrbuf, strm->rawscrbuf, sizeof(strm->rawscrbuf)); - - return strm; -} - -static void stream_del(stream *strm) { - list_remove(strm); - - if (strm->filefd != -1) { - close(strm->filefd); - } - - free(strm); -} - -static connection *connection_new(int fd) { - connection *conn; - int rv; - - conn = malloc(sizeof(connection)); - - rv = nghttp2_session_server_new(&conn->session, shared_callbacks, conn); - - if (rv != 0) { - goto cleanup; - } - - conn->fd = fd; - conn->cache = NULL; - conn->cachelen = 0; - io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf)); - conn->evhn.handler = handle_connection; - conn->strm_head.next = NULL; - - return conn; - -cleanup: - free(conn); - return NULL; -} - -static void connection_del(connection *conn) { - stream *strm; - - nghttp2_session_del(conn->session); - shutdown(conn->fd, SHUT_WR); - close(conn->fd); - - strm = conn->strm_head.next; - while (strm) { - stream *next_strm = strm->next; - - stream_del(strm); - strm = next_strm; - } - - free(conn); -} - -static int connection_start(connection *conn) { - nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}; - int rv; - - rv = nghttp2_submit_settings(conn->session, NGHTTP2_FLAG_NONE, &iv, 1); - - if (rv != 0) { - return -1; - } - - return 0; -} - -static int server_init(server *serv, const char *node, const char *service) { - int rv; - struct addrinfo hints; - struct addrinfo *res, *rp; - int fd; - int on = 1; - socklen_t optlen = sizeof(on); - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; - - rv = getaddrinfo(node, service, &hints, &res); - - if (rv != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); - return -1; - } - - for (rp = res; rp; rp = rp->ai_next) { - fd = - socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol); - - if (fd == -1) { - continue; - } - - rv = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, optlen); - - if (rv == -1) { - print_errno("setsockopt", errno); - } - - if (bind(fd, rp->ai_addr, rp->ai_addrlen) != 0) { - close(fd); - continue; - } - - if (listen(fd, 65536) != 0) { - close(fd); - continue; - } - - break; - } - - freeaddrinfo(res); - - if (!rp) { - fprintf(stderr, "No address to bind\n"); - return -1; - } - - serv->fd = fd; - serv->evhn.handler = handle_accept; - - return 0; -} - -static int timer_init(timer *tmr) { - int fd; - struct itimerspec timerval = {{1, 0}, {1, 0}}; - - fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); - if (fd == -1) { - print_errno("timerfd_create", errno); - return -1; - } - - if (timerfd_settime(fd, 0, &timerval, NULL) != 0) { - print_errno("timerfd_settime", errno); - return -1; - } - - tmr->fd = fd; - tmr->evhn.handler = handle_timer; - - return 0; -} - -static int io_loop_init(io_loop *loop) { - int epfd; - - epfd = epoll_create1(0); - - if (epfd == -1) { - print_errno("epoll_create", errno); - return -1; - } - - loop->epfd = epfd; - - return 0; -} - -static int io_loop_ctl(io_loop *loop, int op, int fd, uint32_t events, - void *ptr) { - int rv; - struct epoll_event ev; - - ev.events = events; - ev.data.ptr = ptr; - - rv = epoll_ctl(loop->epfd, op, fd, &ev); - - if (rv != 0) { - print_errno("epoll_ctl", errno); - return -1; - } - - return 0; -} - -static int io_loop_add(io_loop *loop, int fd, uint32_t events, void *ptr) { - return io_loop_ctl(loop, EPOLL_CTL_ADD, fd, events, ptr); -} - -static int io_loop_mod(io_loop *loop, int fd, uint32_t events, void *ptr) { - return io_loop_ctl(loop, EPOLL_CTL_MOD, fd, events, ptr); -} - -static int io_loop_run(io_loop *loop, server *serv _U_) { -#define NUM_EVENTS 1024 - struct epoll_event events[NUM_EVENTS]; - - for (;;) { - int nev; - evhandle *evhn; - struct epoll_event *ev, *end; - - while ((nev = epoll_wait(loop->epfd, events, NUM_EVENTS, -1)) == -1 && - errno == EINTR) - ; - - if (nev == -1) { - print_errno("epoll_wait", errno); - return -1; - } - - for (ev = events, end = events + nev; ev != end; ++ev) { - evhn = ev->data.ptr; - evhn->handler(loop, ev->events, ev->data.ptr); - } - } -} - -static int handle_timer(io_loop *loop _U_, uint32_t events _U_, void *ptr) { - timer *tmr = ptr; - int64_t buf; - ssize_t nread; - - while ((nread = read(tmr->fd, &buf, sizeof(buf))) == -1 && errno == EINTR) - ; - - assert(nread == sizeof(buf)); - - update_date(); - - return 0; -} - -static int handle_accept(io_loop *loop, uint32_t events _U_, void *ptr) { - int acfd; - server *serv = ptr; - int on = 1; - socklen_t optlen = sizeof(on); - int rv; - - for (;;) { - connection *conn; - - while ((acfd = accept4(serv->fd, NULL, NULL, SOCK_NONBLOCK)) == -1 && - errno == EINTR) - ; - - if (acfd == -1) { - switch (errno) { - case ENETDOWN: - case EPROTO: - case ENOPROTOOPT: - case EHOSTDOWN: - case ENONET: - case EHOSTUNREACH: - case EOPNOTSUPP: - case ENETUNREACH: - continue; - } - return 0; - } - - rv = setsockopt(acfd, IPPROTO_TCP, TCP_NODELAY, &on, optlen); - - if (rv == -1) { - print_errno("setsockopt", errno); - } - - conn = connection_new(acfd); - - if (conn == NULL) { - close(acfd); - continue; - } - - if (connection_start(conn) != 0 || - io_loop_add(loop, acfd, EPOLLIN | EPOLLOUT, conn) != 0) { - connection_del(conn); - } - } -} - -static void stream_error(connection *conn, int32_t stream_id, - uint32_t error_code) { - nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE, stream_id, - error_code); -} - -static int send_data_callback(nghttp2_session *session _U_, - nghttp2_frame *frame, const uint8_t *framehd, - size_t length, nghttp2_data_source *source, - void *user_data) { - connection *conn = user_data; - uint8_t *p = conn->buf.last; - stream *strm = source->ptr; - - /* We never use padding in this program */ - assert(frame->data.padlen == 0); - - if ((size_t)io_buf_left(&conn->buf) < 9 + frame->hd.length) { - return NGHTTP2_ERR_WOULDBLOCK; - } - - memcpy(p, framehd, 9); - p += 9; - - while (length) { - ssize_t nread; - while ((nread = read(strm->filefd, p, length)) == -1 && errno == EINTR) - ; - if (nread == -1) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - - length -= (size_t)nread; - p += nread; - } - - conn->buf.last = p; - - return 0; -} - -static ssize_t fd_read_callback(nghttp2_session *session _U_, - int32_t stream_id _U_, uint8_t *buf _U_, - size_t length, uint32_t *data_flags, - nghttp2_data_source *source, - void *user_data _U_) { - stream *strm = source->ptr; - ssize_t nread = - (int64_t)length < strm->fileleft ? (int64_t)length : strm->fileleft; - - *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY; - - strm->fileleft -= nread; - if (nread == 0 || strm->fileleft == 0) { - if (strm->fileleft != 0) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - return nread; -} - -static ssize_t resbuf_read_callback(nghttp2_session *session _U_, - int32_t stream_id _U_, uint8_t *buf, - size_t length, uint32_t *data_flags, - nghttp2_data_source *source, - void *user_data _U_) { - stream *strm = source->ptr; - size_t left = (size_t)(strm->res_end - strm->res_begin); - size_t nwrite = length < left ? length : left; - - memcpy(buf, strm->res_begin, nwrite); - strm->res_begin += nwrite; - - if (strm->res_begin == strm->res_end) { - *data_flags |= NGHTTP2_DATA_FLAG_EOF; - } - - return (ssize_t)nwrite; -} - -static int hex_digit(char c) { - return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || - ('a' <= c && c <= 'f'); -} - -static unsigned int hex_to_uint(char c) { - if (c <= '9') { - return (unsigned int)(c - '0'); - } - - if (c <= 'F') { - return (unsigned int)(c - 'A' + 10); - } - - return (unsigned int)(c - 'a' + 10); -} - -static void percent_decode(io_buf *buf, const char *s) { - for (; *s; ++s) { - if (*s == '?' || *s == '#') { - break; - } - - if (*s == '%' && hex_digit(*(s + 1)) && hex_digit(*(s + 2))) { - *buf->last++ = - (uint8_t)((hex_to_uint(*(s + 1)) << 4) + hex_to_uint(*(s + 2))); - s += 2; - continue; - } - - *buf->last++ = (uint8_t)*s; - } -} - -static int check_path(const char *path, size_t len) { - return path[0] == '/' && strchr(path, '\\') == NULL && - strstr(path, "/../") == NULL && strstr(path, "/./") == NULL && - (len < 3 || memcmp(path + len - 3, "/..", 3) != 0) && - (len < 2 || memcmp(path + len - 2, "/.", 2) != 0); -} - -static int make_path(io_buf *pathbuf, const char *req, size_t reqlen _U_) { - uint8_t *p; - - if (req[0] != '/') { - return -1; - } - - if (docrootlen + strlen(req) + sizeof("index.html") > - (size_t)io_buf_left(pathbuf)) { - return -1; - } - - io_buf_add(pathbuf, docroot, docrootlen); - - p = pathbuf->last; - - percent_decode(pathbuf, req); - - if (*(pathbuf->last - 1) == '/') { - io_buf_add(pathbuf, "index.html", sizeof("index.html") - 1); - } - - *pathbuf->last++ = '\0'; - - if (!check_path((const char *)p, (size_t)(pathbuf->last - 1 - p))) { - - return -1; - } - - return 0; -} - -static int status_response(stream *strm, connection *conn, - const char *status_code) { - int rv; - size_t status_codelen = strlen(status_code); - char contentlength[19]; - size_t contentlengthlen; - size_t reslen; - nghttp2_data_provider prd, *prdptr; - nghttp2_nv nva[5] = { - MAKE_NV(":status", ""), MAKE_NV("server", SERVER_NAME), - MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""), - }; - size_t nvlen = 3; - - nva[0].value = (uint8_t *)status_code; - nva[0].valuelen = strlen(status_code); - -#define BODY1 "" -#define BODY2 "

" -#define BODY3 "

" - - reslen = sizeof(BODY1) - 1 + sizeof(BODY2) - 1 + sizeof(BODY3) - 1 + - status_codelen * 2; - - if ((size_t)io_buf_left(&strm->scrbuf) < reslen) { - contentlength[0] = '0'; - contentlengthlen = 1; - prdptr = NULL; - } else { - contentlengthlen = utos(contentlength, sizeof(contentlength), reslen); - - strm->res_begin = strm->scrbuf.last; - - io_buf_add(&strm->scrbuf, BODY1, sizeof(BODY1) - 1); - io_buf_add(&strm->scrbuf, status_code, strlen(status_code)); - io_buf_add(&strm->scrbuf, BODY2, sizeof(BODY2) - 1); - io_buf_add(&strm->scrbuf, status_code, strlen(status_code)); - io_buf_add(&strm->scrbuf, BODY3, sizeof(BODY3) - 1); - - strm->res_end = strm->scrbuf.last; - prdptr = &prd; - } - - nva[nvlen].value = (uint8_t *)contentlength; - nva[nvlen].valuelen = contentlengthlen; - - ++nvlen; - - prd.source.ptr = strm; - prd.read_callback = resbuf_read_callback; - - rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, - prdptr); - if (rv != 0) { - return -1; - } - - return 0; -} - -static int redirect_response(stream *strm, connection *conn) { - int rv; - size_t locationlen; - nghttp2_nv nva[5] = { - MAKE_NV(":status", "301"), MAKE_NV("server", SERVER_NAME), - MAKE_NV2("date", date, datelen), MAKE_NV("content-length", "0"), - MAKE_NV("location", ""), - }; - - /* + 1 for trailing '/' */ - locationlen = strm->schemelen + 3 + strm->hostlen + strm->pathlen + 1; - if ((size_t)io_buf_left(&strm->scrbuf) < locationlen) { - return -1; - } - - nva[4].value = strm->scrbuf.last; - nva[4].valuelen = locationlen; - - io_buf_add(&strm->scrbuf, strm->scheme, strm->schemelen); - io_buf_add(&strm->scrbuf, "://", 3); - io_buf_add(&strm->scrbuf, strm->host, strm->hostlen); - io_buf_add(&strm->scrbuf, strm->path, strm->pathlen); - *strm->scrbuf.last++ = '/'; - - rv = nghttp2_submit_response(conn->session, strm->stream_id, nva, - array_size(nva), NULL); - - if (rv != 0) { - return -1; - } - - return 0; -} - -static int process_request(stream *strm, connection *conn) { - int fd; - struct stat stbuf; - int rv; - nghttp2_data_provider prd; - char lastmod[32]; - size_t lastmodlen; - char contentlength[19]; - size_t contentlengthlen; - char path[1024]; - io_buf pathbuf; - nghttp2_nv nva[5] = { - MAKE_NV(":status", "200"), MAKE_NV("server", SERVER_NAME), - MAKE_NV2("date", date, datelen), MAKE_NV("content-length", ""), - }; - size_t nvlen = 3; - - io_buf_init(&pathbuf, (uint8_t *)path, sizeof(path)); - - rv = make_path(&pathbuf, strm->path, strm->pathlen); - - if (rv != 0) { - return status_response(strm, conn, "400"); - } - - fd = open(path, O_RDONLY); - - if (fd == -1) { - return status_response(strm, conn, "404"); - } - - strm->filefd = fd; - - rv = fstat(fd, &stbuf); - - if (rv == -1) { - return status_response(strm, conn, "404"); - } - - if (stbuf.st_mode & S_IFDIR) { - return redirect_response(strm, conn); - } - - prd.source.ptr = strm; - prd.read_callback = fd_read_callback; - - strm->fileleft = stbuf.st_size; - - lastmodlen = http_date(lastmod, stbuf.st_mtim.tv_sec); - contentlengthlen = - utos(contentlength, sizeof(contentlength), (uint64_t)stbuf.st_size); - - nva[nvlen].value = (uint8_t *)contentlength; - nva[nvlen].valuelen = contentlengthlen; - - ++nvlen; - - if (lastmodlen) { - nva[nvlen].name = (uint8_t *)"last-modified"; - nva[nvlen].namelen = sizeof("last-modified") - 1; - nva[nvlen].value = (uint8_t *)lastmod; - nva[nvlen].valuelen = lastmodlen; - nva[nvlen].flags = NGHTTP2_NV_FLAG_NONE; - - ++nvlen; - } - - rv = - nghttp2_submit_response(conn->session, strm->stream_id, nva, nvlen, &prd); - if (rv != 0) { - return -1; - } - - return 0; -} - -static int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data) { - connection *conn = user_data; - stream *strm; - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - strm = stream_new(frame->hd.stream_id, conn); - - if (!strm) { - nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id, - NGHTTP2_INTERNAL_ERROR); - return 0; - } - - nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, strm); - - return 0; -} - -static int on_header_callback(nghttp2_session *session, - const nghttp2_frame *frame, const uint8_t *name, - size_t namelen, const uint8_t *value, - size_t valuelen, uint8_t flags _U_, - void *user_data _U_) { - stream *strm; - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - - if (!strm) { - return 0; - } - - switch (lookup_token(name, namelen)) { - case NGHTTP2_TOKEN__METHOD: - strm->method = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->method) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->methodlen = valuelen; - break; - case NGHTTP2_TOKEN__SCHEME: - strm->scheme = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->scheme) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->schemelen = valuelen; - break; - case NGHTTP2_TOKEN__AUTHORITY: - strm->authority = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->authority) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->authoritylen = valuelen; - break; - case NGHTTP2_TOKEN__PATH: - strm->path = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->path) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->pathlen = valuelen; - break; - case NGHTTP2_TOKEN_HOST: - strm->host = io_buf_add_str(&strm->scrbuf, value, valuelen); - if (!strm->host) { - return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; - } - strm->hostlen = valuelen; - break; - } - - return 0; -} - -static int on_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, void *user_data) { - connection *conn = user_data; - stream *strm; - - if (frame->hd.type != NGHTTP2_HEADERS || - frame->headers.cat != NGHTTP2_HCAT_REQUEST) { - return 0; - } - - strm = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); - - if (!strm) { - return 0; - } - - if (!strm->host) { - strm->host = strm->authority; - strm->hostlen = strm->authoritylen; - } - - if (process_request(strm, conn) != 0) { - stream_error(conn, strm->stream_id, NGHTTP2_INTERNAL_ERROR); - return 0; - } - - return 0; -} - -static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code _U_, - void *user_data _U_) { - stream *strm; - - strm = nghttp2_session_get_stream_user_data(session, stream_id); - - if (!strm) { - return 0; - } - - stream_del(strm); - - return 0; -} - -static int on_frame_not_send_callback(nghttp2_session *session _U_, - const nghttp2_frame *frame, - int lib_error_code _U_, void *user_data) { - connection *conn = user_data; - - if (frame->hd.type != NGHTTP2_HEADERS) { - return 0; - } - - /* Issue RST_STREAM so that stream does not hang around. */ - nghttp2_submit_rst_stream(conn->session, NGHTTP2_FLAG_NONE, - frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); - - return 0; -} - -static int do_read(connection *conn) { - uint8_t buf[32768]; - - for (;;) { - ssize_t nread; - ssize_t nproc; - - while ((nread = read(conn->fd, buf, sizeof(buf))) == -1 && errno == EINTR) - ; - if (nread == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return 0; - } - - return -1; - } - - if (nread == 0) { - return -1; - } - - nproc = nghttp2_session_mem_recv(conn->session, buf, (size_t)nread); - - if (nproc < 0) { - return -1; - } - } -} - -static int do_write(connection *conn) { - for (;;) { - if (io_buf_len(&conn->buf)) { - ssize_t nwrite; - while ((nwrite = write(conn->fd, conn->buf.pos, - io_buf_len(&conn->buf))) == -1 && - errno == EINTR) - ; - if (nwrite == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { - return 0; - } - return -1; - } - - conn->buf.pos += nwrite; - - if (io_buf_len(&conn->buf)) { - return 0; - } - - io_buf_init(&conn->buf, conn->rawoutbuf, sizeof(conn->rawoutbuf)); - } - - if (conn->cache) { - io_buf_add(&conn->buf, conn->cache, conn->cachelen); - conn->cache = NULL; - conn->cachelen = 0; - } - - for (;;) { - ssize_t n; - const uint8_t *b; - - n = nghttp2_session_mem_send(conn->session, &b); - - if (n < 0) { - return -1; - } - - if (n == 0) { - if (io_buf_len(&conn->buf) == 0) { - return 0; - } - break; - } - - if (io_buf_left(&conn->buf) < (size_t)n) { - conn->cache = b; - conn->cachelen = (size_t)n; - break; - } - - io_buf_add(&conn->buf, b, (size_t)n); - } - } -} - -static int handle_connection(io_loop *loop, uint32_t events, void *ptr) { - connection *conn = ptr; - int rv; - uint32_t nextev = 0; - - if (events & (EPOLLHUP | EPOLLERR)) { - goto cleanup; - } - - if (events & EPOLLIN) { - rv = do_read(conn); - - if (rv != 0) { - goto cleanup; - } - } - - rv = do_write(conn); - - if (rv != 0) { - goto cleanup; - } - - if (nghttp2_session_want_read(conn->session)) { - nextev |= EPOLLIN; - } - - if (io_buf_len(&conn->buf) || nghttp2_session_want_write(conn->session)) { - nextev |= EPOLLOUT; - } - - if (!nextev) { - goto cleanup; - } - - io_loop_mod(loop, conn->fd, nextev, conn); - - return 0; - -cleanup: - connection_del(conn); - - return 0; -} - -int main(int argc, char **argv) { - int rv; - server serv; - timer tmr; - io_loop loop; - struct sigaction act; - const char *address; - const char *service; - - if (argc < 4) { - fprintf(stderr, "Usage: tiny-nghttpd
\n"); - exit(EXIT_FAILURE); - } - - address = argv[1]; - service = argv[2]; - docroot = argv[3]; - docrootlen = strlen(docroot); - - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, NULL); - - rv = server_init(&serv, address, service); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - rv = timer_init(&tmr); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - rv = io_loop_init(&loop); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - rv = nghttp2_session_callbacks_new(&shared_callbacks); - if (rv != 0) { - fprintf(stderr, "nghttp2_session_callbacks_new: %s", nghttp2_strerror(rv)); - exit(EXIT_FAILURE); - } - - nghttp2_session_callbacks_set_on_begin_headers_callback( - shared_callbacks, on_begin_headers_callback); - nghttp2_session_callbacks_set_on_header_callback(shared_callbacks, - on_header_callback); - nghttp2_session_callbacks_set_on_frame_recv_callback(shared_callbacks, - on_frame_recv_callback); - nghttp2_session_callbacks_set_on_stream_close_callback( - shared_callbacks, on_stream_close_callback); - nghttp2_session_callbacks_set_on_frame_not_send_callback( - shared_callbacks, on_frame_not_send_callback); - nghttp2_session_callbacks_set_send_data_callback(shared_callbacks, - send_data_callback); - - rv = io_loop_add(&loop, serv.fd, EPOLLIN, &serv); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - rv = io_loop_add(&loop, tmr.fd, EPOLLIN, &tmr); - - if (rv != 0) { - exit(EXIT_FAILURE); - } - - update_date(); - - io_loop_run(&loop, &serv); - - nghttp2_session_callbacks_del(shared_callbacks); - - return 0; -} diff --git a/m4/ax_have_epoll.m4 b/m4/ax_have_epoll.m4 deleted file mode 100644 index 07ceb49f..00000000 --- a/m4/ax_have_epoll.m4 +++ /dev/null @@ -1,104 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_have_epoll.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# -# DESCRIPTION -# -# This macro determines whether the system supports the epoll I/O event -# interface. A neat usage example would be: -# -# AX_HAVE_EPOLL( -# [AX_CONFIG_FEATURE_ENABLE(epoll)], -# [AX_CONFIG_FEATURE_DISABLE(epoll)]) -# AX_CONFIG_FEATURE( -# [epoll], [This platform supports epoll(7)], -# [HAVE_EPOLL], [This platform supports epoll(7).]) -# -# The epoll interface was added to the Linux kernel in version 2.5.45, and -# the macro verifies that a kernel newer than this is installed. This -# check is somewhat unreliable if doesn't match the -# running kernel, but it is necessary regardless, because glibc comes with -# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to -# compile and link even if the kernel is too old; the problem would then -# be detected only at runtime. -# -# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to -# epoll_wait(). The availability of that function can be tested with the -# second macro. Generally speaking, it is safe to assume that -# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the -# other way round. -# -# LICENSE -# -# Copyright (c) 2008 Peter Simons -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 10 - -AC_DEFUN([AX_HAVE_EPOLL], [dnl - ax_have_epoll_cppflags="${CPPFLAGS}" - AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) - AC_MSG_CHECKING([for Linux epoll(7) interface]) - AC_CACHE_VAL([ax_cv_have_epoll], [dnl - AC_LINK_IFELSE([dnl - AC_LANG_PROGRAM([dnl -#include -#ifdef HAVE_LINUX_VERSION_H -# include -# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) -# error linux kernel version is too old to have epoll -# endif -#endif -], [dnl -int fd, rc; -struct epoll_event ev; -fd = epoll_create(128); -rc = epoll_wait(fd, &ev, 1, 0);])], - [ax_cv_have_epoll=yes], - [ax_cv_have_epoll=no])]) - CPPFLAGS="${ax_have_epoll_cppflags}" - AS_IF([test "${ax_cv_have_epoll}" = "yes"], - [AC_MSG_RESULT([yes]) -$1],[AC_MSG_RESULT([no]) -$2]) -])dnl - -AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl - ax_have_epoll_cppflags="${CPPFLAGS}" - AC_CHECK_HEADER([linux/version.h], - [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) - AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension]) - AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl - AC_LINK_IFELSE([dnl - AC_LANG_PROGRAM([dnl -#ifdef HAVE_LINUX_VERSION_H -# include -# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -# error linux kernel version is too old to have epoll_pwait -# endif -#endif -#include -#include -], [dnl -int fd, rc; -struct epoll_event ev; -fd = epoll_create(128); -rc = epoll_wait(fd, &ev, 1, 0); -rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])], - [ax_cv_have_epoll_pwait=yes], - [ax_cv_have_epoll_pwait=no])]) - CPPFLAGS="${ax_have_epoll_cppflags}" - AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"], - [AC_MSG_RESULT([yes]) -$1],[AC_MSG_RESULT([no]) -$2]) -])dnl