2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2013 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.
27 #endif /* HAVE_CONFIG_H */
29 #include <sys/types.h>
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
32 #endif /* HAVE_SYS_SOCKET_H */
35 #endif /* HAVE_NETDB_H */
39 #endif /* HAVE_UNISTD_H */
43 #endif /* HAVE_FCNTL_H */
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif /* HAVE_NETINET_IN_H */
48 #include <netinet/tcp.h>
51 #include <openssl/ssl.h>
52 #include <openssl/err.h>
53 #include <openssl/conf.h>
56 #include <event2/event.h>
57 #include <event2/bufferevent_ssl.h>
58 #include <event2/listener.h>
60 #include <nghttp2/nghttp2.h>
62 #define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
64 #define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
66 #define MAKE_NV(NAME, VALUE) \
68 (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
69 NGHTTP2_NV_FLAG_NONE \
73 typedef struct app_context app_context;
75 typedef struct http2_stream_data {
76 struct http2_stream_data *prev, *next;
82 typedef struct http2_session_data {
83 struct http2_stream_data root;
84 struct bufferevent *bev;
86 nghttp2_session *session;
92 struct event_base *evbase;
95 static unsigned char next_proto_list[256];
96 static size_t next_proto_list_len;
98 static int next_proto_cb(SSL *s _U_, const unsigned char **data,
99 unsigned int *len, void *arg _U_) {
100 *data = next_proto_list;
101 *len = (unsigned int)next_proto_list_len;
102 return SSL_TLSEXT_ERR_OK;
105 /* Create SSL_CTX. */
106 static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
110 ssl_ctx = SSL_CTX_new(SSLv23_server_method());
112 errx(1, "Could not create SSL/TLS context: %s",
113 ERR_error_string(ERR_get_error(), NULL));
115 SSL_CTX_set_options(ssl_ctx,
116 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
117 SSL_OP_NO_COMPRESSION |
118 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
120 ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
122 errx(1, "EC_KEY_new_by_curv_name failed: %s",
123 ERR_error_string(ERR_get_error(), NULL));
125 SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
128 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
129 errx(1, "Could not read private key file %s", key_file);
131 if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
132 errx(1, "Could not read certificate file %s", cert_file);
135 next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
136 memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
137 NGHTTP2_PROTO_VERSION_ID_LEN);
138 next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
140 SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
144 /* Create SSL object */
145 static SSL *create_ssl(SSL_CTX *ssl_ctx) {
147 ssl = SSL_new(ssl_ctx);
149 errx(1, "Could not create SSL/TLS session object: %s",
150 ERR_error_string(ERR_get_error(), NULL));
155 static void add_stream(http2_session_data *session_data,
156 http2_stream_data *stream_data) {
157 stream_data->next = session_data->root.next;
158 session_data->root.next = stream_data;
159 stream_data->prev = &session_data->root;
160 if (stream_data->next) {
161 stream_data->next->prev = stream_data;
165 static void remove_stream(http2_session_data *session_data _U_,
166 http2_stream_data *stream_data) {
167 stream_data->prev->next = stream_data->next;
168 if (stream_data->next) {
169 stream_data->next->prev = stream_data->prev;
173 static http2_stream_data *
174 create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
175 http2_stream_data *stream_data;
176 stream_data = malloc(sizeof(http2_stream_data));
177 memset(stream_data, 0, sizeof(http2_stream_data));
178 stream_data->stream_id = stream_id;
179 stream_data->fd = -1;
181 add_stream(session_data, stream_data);
185 static void delete_http2_stream_data(http2_stream_data *stream_data) {
186 if (stream_data->fd != -1) {
187 close(stream_data->fd);
189 free(stream_data->request_path);
193 static http2_session_data *create_http2_session_data(app_context *app_ctx,
195 struct sockaddr *addr,
198 http2_session_data *session_data;
200 char host[NI_MAXHOST];
203 ssl = create_ssl(app_ctx->ssl_ctx);
204 session_data = malloc(sizeof(http2_session_data));
205 memset(session_data, 0, sizeof(http2_session_data));
206 session_data->app_ctx = app_ctx;
207 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
208 session_data->bev = bufferevent_openssl_socket_new(
209 app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
210 BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
211 rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
213 session_data->client_addr = strdup("(unknown)");
215 session_data->client_addr = strdup(host);
221 static void delete_http2_session_data(http2_session_data *session_data) {
222 http2_stream_data *stream_data;
223 SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
224 fprintf(stderr, "%s disconnected\n", session_data->client_addr);
228 bufferevent_free(session_data->bev);
229 nghttp2_session_del(session_data->session);
230 for (stream_data = session_data->root.next; stream_data;) {
231 http2_stream_data *next = stream_data->next;
232 delete_http2_stream_data(stream_data);
235 free(session_data->client_addr);
239 /* Serialize the frame and send (or buffer) the data to
241 static int session_send(http2_session_data *session_data) {
243 rv = nghttp2_session_send(session_data->session);
245 warnx("Fatal error: %s", nghttp2_strerror(rv));
251 /* Read the data in the bufferevent and feed them into nghttp2 library
252 function. Invocation of nghttp2_session_mem_recv() may make
253 additional pending frames, so call session_send() at the end of the
255 static int session_recv(http2_session_data *session_data) {
257 struct evbuffer *input = bufferevent_get_input(session_data->bev);
258 size_t datalen = evbuffer_get_length(input);
259 unsigned char *data = evbuffer_pullup(input, -1);
261 readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
263 warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
266 if (evbuffer_drain(input, readlen) != 0) {
267 warnx("Fatal error: evbuffer_drain failed");
270 if (session_send(session_data) != 0) {
276 static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
277 size_t length, int flags _U_, void *user_data) {
278 http2_session_data *session_data = (http2_session_data *)user_data;
279 struct bufferevent *bev = session_data->bev;
280 /* Avoid excessive buffering in server side. */
281 if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
282 OUTPUT_WOULDBLOCK_THRESHOLD) {
283 return NGHTTP2_ERR_WOULDBLOCK;
285 bufferevent_write(bev, data, length);
289 /* Returns nonzero if the string |s| ends with the substring |sub| */
290 static int ends_with(const char *s, const char *sub) {
291 size_t slen = strlen(s);
292 size_t sublen = strlen(sub);
296 return memcmp(s + slen - sublen, sub, sublen) == 0;
299 /* Returns int value of hex string character |c| */
300 static uint8_t hex_to_uint(uint8_t c) {
301 if ('0' <= c && c <= '9') {
304 if ('A' <= c && c <= 'F') {
307 if ('a' <= c && c <= 'f') {
313 /* Decodes percent-encoded byte string |value| with length |valuelen|
314 and returns the decoded byte string in allocated buffer. The return
315 value is NULL terminated. The caller must free the returned
317 static char *percent_decode(const uint8_t *value, size_t valuelen) {
320 res = malloc(valuelen + 1);
323 for (i = 0, j = 0; i < valuelen - 2;) {
324 if (value[i] != '%' || !isxdigit(value[i + 1]) ||
325 !isxdigit(value[i + 2])) {
326 res[j++] = value[i++];
329 res[j++] = (hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]);
332 memcpy(&res[j], &value[i], 2);
335 memcpy(res, value, valuelen);
336 res[valuelen] = '\0';
341 static ssize_t file_read_callback(nghttp2_session *session _U_,
342 int32_t stream_id _U_, uint8_t *buf,
343 size_t length, uint32_t *data_flags,
344 nghttp2_data_source *source,
345 void *user_data _U_) {
348 while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
351 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
354 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
359 static int send_response(nghttp2_session *session, int32_t stream_id,
360 nghttp2_nv *nva, size_t nvlen, int fd) {
362 nghttp2_data_provider data_prd;
363 data_prd.source.fd = fd;
364 data_prd.read_callback = file_read_callback;
366 rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
368 warnx("Fatal error: %s", nghttp2_strerror(rv));
374 const char ERROR_HTML[] = "<html><head><title>404</title></head>"
375 "<body><h1>404 Not Found</h1></body></html>";
377 static int error_reply(nghttp2_session *session,
378 http2_stream_data *stream_data) {
382 nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
386 warn("Could not create pipe");
387 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
388 stream_data->stream_id,
389 NGHTTP2_INTERNAL_ERROR);
391 warnx("Fatal error: %s", nghttp2_strerror(rv));
397 writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
400 if (writelen != sizeof(ERROR_HTML) - 1) {
405 stream_data->fd = pipefd[0];
407 if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
415 /* nghttp2_on_header_callback: Called when nghttp2 library emits
416 single header name/value pair. */
417 static int on_header_callback(nghttp2_session *session,
418 const nghttp2_frame *frame, const uint8_t *name,
419 size_t namelen, const uint8_t *value,
420 size_t valuelen, uint8_t flags _U_,
421 void *user_data _U_) {
422 http2_stream_data *stream_data;
423 const char PATH[] = ":path";
424 switch (frame->hd.type) {
425 case NGHTTP2_HEADERS:
426 if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
430 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
431 if (!stream_data || stream_data->request_path) {
434 if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
436 for (j = 0; j < valuelen && value[j] != '?'; ++j)
438 stream_data->request_path = percent_decode(value, j);
445 static int on_begin_headers_callback(nghttp2_session *session,
446 const nghttp2_frame *frame,
448 http2_session_data *session_data = (http2_session_data *)user_data;
449 http2_stream_data *stream_data;
451 if (frame->hd.type != NGHTTP2_HEADERS ||
452 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
455 stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
456 nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
461 /* Minimum check for directory traversal. Returns nonzero if it is
463 static int check_path(const char *path) {
464 /* We don't like '\' in url. */
465 return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
466 strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
467 !ends_with(path, "/..") && !ends_with(path, "/.");
470 static int on_request_recv(nghttp2_session *session,
471 http2_session_data *session_data,
472 http2_stream_data *stream_data) {
474 nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
477 if (!stream_data->request_path) {
478 if (error_reply(session, stream_data) != 0) {
479 return NGHTTP2_ERR_CALLBACK_FAILURE;
483 fprintf(stderr, "%s GET %s\n", session_data->client_addr,
484 stream_data->request_path);
485 if (!check_path(stream_data->request_path)) {
486 if (error_reply(session, stream_data) != 0) {
487 return NGHTTP2_ERR_CALLBACK_FAILURE;
491 for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
493 fd = open(rel_path, O_RDONLY);
495 if (error_reply(session, stream_data) != 0) {
496 return NGHTTP2_ERR_CALLBACK_FAILURE;
500 stream_data->fd = fd;
502 if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
505 return NGHTTP2_ERR_CALLBACK_FAILURE;
510 static int on_frame_recv_callback(nghttp2_session *session,
511 const nghttp2_frame *frame, void *user_data) {
512 http2_session_data *session_data = (http2_session_data *)user_data;
513 http2_stream_data *stream_data;
514 switch (frame->hd.type) {
516 case NGHTTP2_HEADERS:
517 /* Check that the client request has finished */
518 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
520 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
521 /* For DATA and HEADERS frame, this callback may be called after
522 on_stream_close_callback. Check that stream still alive. */
526 return on_request_recv(session, session_data, stream_data);
535 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
536 uint32_t error_code _U_, void *user_data) {
537 http2_session_data *session_data = (http2_session_data *)user_data;
538 http2_stream_data *stream_data;
540 stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
544 remove_stream(session_data, stream_data);
545 delete_http2_stream_data(stream_data);
549 static void initialize_nghttp2_session(http2_session_data *session_data) {
550 nghttp2_session_callbacks *callbacks;
552 nghttp2_session_callbacks_new(&callbacks);
554 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
556 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
557 on_frame_recv_callback);
559 nghttp2_session_callbacks_set_on_stream_close_callback(
560 callbacks, on_stream_close_callback);
562 nghttp2_session_callbacks_set_on_header_callback(callbacks,
565 nghttp2_session_callbacks_set_on_begin_headers_callback(
566 callbacks, on_begin_headers_callback);
568 nghttp2_session_server_new(&session_data->session, callbacks, session_data);
570 nghttp2_session_callbacks_del(callbacks);
573 /* Send HTTP/2 client connection header, which includes 24 bytes
574 magic octets and SETTINGS frame */
575 static int send_server_connection_header(http2_session_data *session_data) {
576 nghttp2_settings_entry iv[1] = {
577 {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
580 rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
583 warnx("Fatal error: %s", nghttp2_strerror(rv));
589 /* readcb for bufferevent after client connection header was
591 static void readcb(struct bufferevent *bev _U_, void *ptr) {
592 http2_session_data *session_data = (http2_session_data *)ptr;
593 if (session_recv(session_data) != 0) {
594 delete_http2_session_data(session_data);
599 /* writecb for bufferevent. To greaceful shutdown after sending or
600 receiving GOAWAY, we check the some conditions on the nghttp2
601 library and output buffer of bufferevent. If it indicates we have
602 no business to this session, tear down the connection. If the
603 connection is not going to shutdown, we call session_send() to
604 process pending data in the output buffer. This is necessary
605 because we have a threshold on the buffer size to avoid too much
606 buffering. See send_callback(). */
607 static void writecb(struct bufferevent *bev, void *ptr) {
608 http2_session_data *session_data = (http2_session_data *)ptr;
609 if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
612 if (nghttp2_session_want_read(session_data->session) == 0 &&
613 nghttp2_session_want_write(session_data->session) == 0) {
614 delete_http2_session_data(session_data);
617 if (session_send(session_data) != 0) {
618 delete_http2_session_data(session_data);
623 /* eventcb for bufferevent */
624 static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
625 http2_session_data *session_data = (http2_session_data *)ptr;
626 if (events & BEV_EVENT_CONNECTED) {
627 fprintf(stderr, "%s connected\n", session_data->client_addr);
629 initialize_nghttp2_session(session_data);
631 if (send_server_connection_header(session_data) != 0) {
632 delete_http2_session_data(session_data);
638 if (events & BEV_EVENT_EOF) {
639 fprintf(stderr, "%s EOF\n", session_data->client_addr);
640 } else if (events & BEV_EVENT_ERROR) {
641 fprintf(stderr, "%s network error\n", session_data->client_addr);
642 } else if (events & BEV_EVENT_TIMEOUT) {
643 fprintf(stderr, "%s timeout\n", session_data->client_addr);
645 delete_http2_session_data(session_data);
648 /* callback for evconnlistener */
649 static void acceptcb(struct evconnlistener *listener _U_, int fd,
650 struct sockaddr *addr, int addrlen, void *arg) {
651 app_context *app_ctx = (app_context *)arg;
652 http2_session_data *session_data;
654 session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
656 bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
659 static void start_listen(struct event_base *evbase, const char *service,
660 app_context *app_ctx) {
662 struct addrinfo hints;
663 struct addrinfo *res, *rp;
665 memset(&hints, 0, sizeof(hints));
666 hints.ai_family = AF_UNSPEC;
667 hints.ai_socktype = SOCK_STREAM;
668 hints.ai_flags = AI_PASSIVE;
670 hints.ai_flags |= AI_ADDRCONFIG;
671 #endif /* AI_ADDRCONFIG */
673 rv = getaddrinfo(NULL, service, &hints, &res);
677 for (rp = res; rp; rp = rp->ai_next) {
678 struct evconnlistener *listener;
679 listener = evconnlistener_new_bind(
680 evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
681 16, rp->ai_addr, rp->ai_addrlen);
688 errx(1, "Could not start listener");
691 static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
692 struct event_base *evbase) {
693 memset(app_ctx, 0, sizeof(app_context));
694 app_ctx->ssl_ctx = ssl_ctx;
695 app_ctx->evbase = evbase;
698 static void run(const char *service, const char *key_file,
699 const char *cert_file) {
702 struct event_base *evbase;
704 ssl_ctx = create_ssl_ctx(key_file, cert_file);
705 evbase = event_base_new();
706 initialize_app_context(&app_ctx, ssl_ctx, evbase);
707 start_listen(evbase, service, &app_ctx);
709 event_base_loop(evbase, 0);
711 event_base_free(evbase);
712 SSL_CTX_free(ssl_ctx);
715 int main(int argc, char **argv) {
716 struct sigaction act;
719 fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
723 memset(&act, 0, sizeof(struct sigaction));
724 act.sa_handler = SIG_IGN;
725 sigaction(SIGPIPE, &act, NULL);
727 OPENSSL_config(NULL);
728 OpenSSL_add_all_algorithms();
729 SSL_load_error_strings();
732 run(argv[1], argv[2], argv[3]);