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 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <netinet/tcp.h>
41 #include <openssl/ssl.h>
42 #include <openssl/err.h>
43 #include <openssl/conf.h>
46 #include <event2/event.h>
47 #include <event2/bufferevent_ssl.h>
48 #include <event2/listener.h>
50 #include <nghttp2/nghttp2.h>
52 #define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
54 #define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
56 #define MAKE_NV(NAME, VALUE) \
58 (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
59 NGHTTP2_NV_FLAG_NONE \
63 typedef struct app_context app_context;
65 typedef struct http2_stream_data {
66 struct http2_stream_data *prev, *next;
72 typedef struct http2_session_data {
73 struct http2_stream_data root;
74 struct bufferevent *bev;
76 nghttp2_session *session;
82 struct event_base *evbase;
85 static unsigned char next_proto_list[256];
86 static size_t next_proto_list_len;
88 static int next_proto_cb(SSL *s _U_, const unsigned char **data,
89 unsigned int *len, void *arg _U_) {
90 *data = next_proto_list;
91 *len = (unsigned int)next_proto_list_len;
92 return SSL_TLSEXT_ERR_OK;
96 static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
100 ssl_ctx = SSL_CTX_new(SSLv23_server_method());
102 errx(1, "Could not create SSL/TLS context: %s",
103 ERR_error_string(ERR_get_error(), NULL));
105 SSL_CTX_set_options(ssl_ctx,
106 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
107 SSL_OP_NO_COMPRESSION |
108 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
110 ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
112 errx(1, "EC_KEY_new_by_curv_name failed: %s",
113 ERR_error_string(ERR_get_error(), NULL));
115 SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
118 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
119 errx(1, "Could not read private key file %s", key_file);
121 if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
122 errx(1, "Could not read certificate file %s", cert_file);
125 next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
126 memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
127 NGHTTP2_PROTO_VERSION_ID_LEN);
128 next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
130 SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
134 /* Create SSL object */
135 static SSL *create_ssl(SSL_CTX *ssl_ctx) {
137 ssl = SSL_new(ssl_ctx);
139 errx(1, "Could not create SSL/TLS session object: %s",
140 ERR_error_string(ERR_get_error(), NULL));
145 static void add_stream(http2_session_data *session_data,
146 http2_stream_data *stream_data) {
147 stream_data->next = session_data->root.next;
148 session_data->root.next = stream_data;
149 stream_data->prev = &session_data->root;
150 if (stream_data->next) {
151 stream_data->next->prev = stream_data;
155 static void remove_stream(http2_session_data *session_data _U_,
156 http2_stream_data *stream_data) {
157 stream_data->prev->next = stream_data->next;
158 if (stream_data->next) {
159 stream_data->next->prev = stream_data->prev;
163 static http2_stream_data *
164 create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
165 http2_stream_data *stream_data;
166 stream_data = malloc(sizeof(http2_stream_data));
167 memset(stream_data, 0, sizeof(http2_stream_data));
168 stream_data->stream_id = stream_id;
169 stream_data->fd = -1;
171 add_stream(session_data, stream_data);
175 static void delete_http2_stream_data(http2_stream_data *stream_data) {
176 if (stream_data->fd != -1) {
177 close(stream_data->fd);
179 free(stream_data->request_path);
183 static http2_session_data *create_http2_session_data(app_context *app_ctx,
185 struct sockaddr *addr,
188 http2_session_data *session_data;
190 char host[NI_MAXHOST];
193 ssl = create_ssl(app_ctx->ssl_ctx);
194 session_data = malloc(sizeof(http2_session_data));
195 memset(session_data, 0, sizeof(http2_session_data));
196 session_data->app_ctx = app_ctx;
197 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
198 session_data->bev = bufferevent_openssl_socket_new(
199 app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
200 BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
201 rv = getnameinfo(addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
203 session_data->client_addr = strdup("(unknown)");
205 session_data->client_addr = strdup(host);
211 static void delete_http2_session_data(http2_session_data *session_data) {
212 http2_stream_data *stream_data;
213 SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
214 fprintf(stderr, "%s disconnected\n", session_data->client_addr);
218 bufferevent_free(session_data->bev);
219 nghttp2_session_del(session_data->session);
220 for (stream_data = session_data->root.next; stream_data;) {
221 http2_stream_data *next = stream_data->next;
222 delete_http2_stream_data(stream_data);
225 free(session_data->client_addr);
229 /* Serialize the frame and send (or buffer) the data to
231 static int session_send(http2_session_data *session_data) {
233 rv = nghttp2_session_send(session_data->session);
235 warnx("Fatal error: %s", nghttp2_strerror(rv));
241 /* Read the data in the bufferevent and feed them into nghttp2 library
242 function. Invocation of nghttp2_session_mem_recv() may make
243 additional pending frames, so call session_send() at the end of the
245 static int session_recv(http2_session_data *session_data) {
247 struct evbuffer *input = bufferevent_get_input(session_data->bev);
248 size_t datalen = evbuffer_get_length(input);
249 unsigned char *data = evbuffer_pullup(input, -1);
251 readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
253 warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
256 if (evbuffer_drain(input, readlen) != 0) {
257 warnx("Fatal error: evbuffer_drain failed");
260 if (session_send(session_data) != 0) {
266 static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
267 size_t length, int flags _U_, void *user_data) {
268 http2_session_data *session_data = (http2_session_data *)user_data;
269 struct bufferevent *bev = session_data->bev;
270 /* Avoid excessive buffering in server side. */
271 if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
272 OUTPUT_WOULDBLOCK_THRESHOLD) {
273 return NGHTTP2_ERR_WOULDBLOCK;
275 bufferevent_write(bev, data, length);
279 /* Returns nonzero if the string |s| ends with the substring |sub| */
280 static int ends_with(const char *s, const char *sub) {
281 size_t slen = strlen(s);
282 size_t sublen = strlen(sub);
286 return memcmp(s + slen - sublen, sub, sublen) == 0;
289 /* Returns int value of hex string character |c| */
290 static uint8_t hex_to_uint(uint8_t c) {
291 if ('0' <= c && c <= '9') {
294 if ('A' <= c && c <= 'F') {
297 if ('a' <= c && c <= 'f') {
303 /* Decodes percent-encoded byte string |value| with length |valuelen|
304 and returns the decoded byte string in allocated buffer. The return
305 value is NULL terminated. The caller must free the returned
307 static char *percent_decode(const uint8_t *value, size_t valuelen) {
310 res = malloc(valuelen + 1);
313 for (i = 0, j = 0; i < valuelen - 2;) {
314 if (value[i] != '%' || !isxdigit(value[i + 1]) ||
315 !isxdigit(value[i + 2])) {
316 res[j++] = value[i++];
319 res[j++] = (hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]);
322 memcpy(&res[j], &value[i], 2);
325 memcpy(res, value, valuelen);
326 res[valuelen] = '\0';
331 static ssize_t file_read_callback(nghttp2_session *session _U_,
332 int32_t stream_id _U_, uint8_t *buf,
333 size_t length, uint32_t *data_flags,
334 nghttp2_data_source *source,
335 void *user_data _U_) {
338 while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
341 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
344 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
349 static int send_response(nghttp2_session *session, int32_t stream_id,
350 nghttp2_nv *nva, size_t nvlen, int fd) {
352 nghttp2_data_provider data_prd;
353 data_prd.source.fd = fd;
354 data_prd.read_callback = file_read_callback;
356 rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
358 warnx("Fatal error: %s", nghttp2_strerror(rv));
364 const char ERROR_HTML[] = "<html><head><title>404</title></head>"
365 "<body><h1>404 Not Found</h1></body></html>";
367 static int error_reply(nghttp2_session *session,
368 http2_stream_data *stream_data) {
372 nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
376 warn("Could not create pipe");
377 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
378 stream_data->stream_id,
379 NGHTTP2_INTERNAL_ERROR);
381 warnx("Fatal error: %s", nghttp2_strerror(rv));
387 writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
390 if (writelen != sizeof(ERROR_HTML) - 1) {
395 stream_data->fd = pipefd[0];
397 if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
405 /* nghttp2_on_header_callback: Called when nghttp2 library emits
406 single header name/value pair. */
407 static int on_header_callback(nghttp2_session *session,
408 const nghttp2_frame *frame, const uint8_t *name,
409 size_t namelen, const uint8_t *value,
410 size_t valuelen, uint8_t flags _U_,
411 void *user_data _U_) {
412 http2_stream_data *stream_data;
413 const char PATH[] = ":path";
414 switch (frame->hd.type) {
415 case NGHTTP2_HEADERS:
416 if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
420 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
421 if (!stream_data || stream_data->request_path) {
424 if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
426 for (j = 0; j < valuelen && value[j] != '?'; ++j)
428 stream_data->request_path = percent_decode(value, j);
435 static int on_begin_headers_callback(nghttp2_session *session,
436 const nghttp2_frame *frame,
438 http2_session_data *session_data = (http2_session_data *)user_data;
439 http2_stream_data *stream_data;
441 if (frame->hd.type != NGHTTP2_HEADERS ||
442 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
445 stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
446 nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
451 /* Minimum check for directory traversal. Returns nonzero if it is
453 static int check_path(const char *path) {
454 /* We don't like '\' in url. */
455 return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
456 strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
457 !ends_with(path, "/..") && !ends_with(path, "/.");
460 static int on_request_recv(nghttp2_session *session,
461 http2_session_data *session_data,
462 http2_stream_data *stream_data) {
464 nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
467 if (!stream_data->request_path) {
468 if (error_reply(session, stream_data) != 0) {
469 return NGHTTP2_ERR_CALLBACK_FAILURE;
473 fprintf(stderr, "%s GET %s\n", session_data->client_addr,
474 stream_data->request_path);
475 if (!check_path(stream_data->request_path)) {
476 if (error_reply(session, stream_data) != 0) {
477 return NGHTTP2_ERR_CALLBACK_FAILURE;
481 for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
483 fd = open(rel_path, O_RDONLY);
485 if (error_reply(session, stream_data) != 0) {
486 return NGHTTP2_ERR_CALLBACK_FAILURE;
490 stream_data->fd = fd;
492 if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
495 return NGHTTP2_ERR_CALLBACK_FAILURE;
500 static int on_frame_recv_callback(nghttp2_session *session,
501 const nghttp2_frame *frame, void *user_data) {
502 http2_session_data *session_data = (http2_session_data *)user_data;
503 http2_stream_data *stream_data;
504 switch (frame->hd.type) {
506 case NGHTTP2_HEADERS:
507 /* Check that the client request has finished */
508 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
510 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
511 /* For DATA and HEADERS frame, this callback may be called after
512 on_stream_close_callback. Check that stream still alive. */
516 return on_request_recv(session, session_data, stream_data);
525 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
526 uint32_t error_code _U_, void *user_data) {
527 http2_session_data *session_data = (http2_session_data *)user_data;
528 http2_stream_data *stream_data;
530 stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
534 remove_stream(session_data, stream_data);
535 delete_http2_stream_data(stream_data);
539 static void initialize_nghttp2_session(http2_session_data *session_data) {
540 nghttp2_option *option;
541 nghttp2_session_callbacks *callbacks;
543 nghttp2_option_new(&option);
545 /* Tells nghttp2_session object that it handles client connection
547 nghttp2_option_set_recv_client_preface(option, 1);
549 nghttp2_session_callbacks_new(&callbacks);
551 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
553 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
554 on_frame_recv_callback);
556 nghttp2_session_callbacks_set_on_stream_close_callback(
557 callbacks, on_stream_close_callback);
559 nghttp2_session_callbacks_set_on_header_callback(callbacks,
562 nghttp2_session_callbacks_set_on_begin_headers_callback(
563 callbacks, on_begin_headers_callback);
565 nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
568 nghttp2_session_callbacks_del(callbacks);
569 nghttp2_option_del(option);
572 /* Send HTTP/2 client connection header, which includes 24 bytes
573 magic octets and SETTINGS frame */
574 static int send_server_connection_header(http2_session_data *session_data) {
575 nghttp2_settings_entry iv[1] = {
576 {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
579 rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
582 warnx("Fatal error: %s", nghttp2_strerror(rv));
588 /* readcb for bufferevent after client connection header was
590 static void readcb(struct bufferevent *bev _U_, void *ptr) {
591 http2_session_data *session_data = (http2_session_data *)ptr;
592 if (session_recv(session_data) != 0) {
593 delete_http2_session_data(session_data);
598 /* writecb for bufferevent. To greaceful shutdown after sending or
599 receiving GOAWAY, we check the some conditions on the nghttp2
600 library and output buffer of bufferevent. If it indicates we have
601 no business to this session, tear down the connection. If the
602 connection is not going to shutdown, we call session_send() to
603 process pending data in the output buffer. This is necessary
604 because we have a threshold on the buffer size to avoid too much
605 buffering. See send_callback(). */
606 static void writecb(struct bufferevent *bev, void *ptr) {
607 http2_session_data *session_data = (http2_session_data *)ptr;
608 if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
611 if (nghttp2_session_want_read(session_data->session) == 0 &&
612 nghttp2_session_want_write(session_data->session) == 0) {
613 delete_http2_session_data(session_data);
616 if (session_send(session_data) != 0) {
617 delete_http2_session_data(session_data);
622 /* eventcb for bufferevent */
623 static void eventcb(struct bufferevent *bev _U_, short events, void *ptr) {
624 http2_session_data *session_data = (http2_session_data *)ptr;
625 if (events & BEV_EVENT_CONNECTED) {
626 fprintf(stderr, "%s connected\n", session_data->client_addr);
628 initialize_nghttp2_session(session_data);
630 if (send_server_connection_header(session_data) != 0) {
631 delete_http2_session_data(session_data);
637 if (events & BEV_EVENT_EOF) {
638 fprintf(stderr, "%s EOF\n", session_data->client_addr);
639 } else if (events & BEV_EVENT_ERROR) {
640 fprintf(stderr, "%s network error\n", session_data->client_addr);
641 } else if (events & BEV_EVENT_TIMEOUT) {
642 fprintf(stderr, "%s timeout\n", session_data->client_addr);
644 delete_http2_session_data(session_data);
647 /* callback for evconnlistener */
648 static void acceptcb(struct evconnlistener *listener _U_, int fd,
649 struct sockaddr *addr, int addrlen, void *arg) {
650 app_context *app_ctx = (app_context *)arg;
651 http2_session_data *session_data;
653 session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
655 bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
658 static void start_listen(struct event_base *evbase, const char *service,
659 app_context *app_ctx) {
661 struct addrinfo hints;
662 struct addrinfo *res, *rp;
664 memset(&hints, 0, sizeof(hints));
665 hints.ai_family = AF_UNSPEC;
666 hints.ai_socktype = SOCK_STREAM;
667 hints.ai_flags = AI_PASSIVE;
669 hints.ai_flags |= AI_ADDRCONFIG;
670 #endif /* AI_ADDRCONFIG */
672 rv = getaddrinfo(NULL, service, &hints, &res);
676 for (rp = res; rp; rp = rp->ai_next) {
677 struct evconnlistener *listener;
678 listener = evconnlistener_new_bind(
679 evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
680 16, rp->ai_addr, rp->ai_addrlen);
687 errx(1, "Could not start listener");
690 static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
691 struct event_base *evbase) {
692 memset(app_ctx, 0, sizeof(app_context));
693 app_ctx->ssl_ctx = ssl_ctx;
694 app_ctx->evbase = evbase;
697 static void run(const char *service, const char *key_file,
698 const char *cert_file) {
701 struct event_base *evbase;
703 ssl_ctx = create_ssl_ctx(key_file, cert_file);
704 evbase = event_base_new();
705 initialize_app_context(&app_ctx, ssl_ctx, evbase);
706 start_listen(evbase, service, &app_ctx);
708 event_base_loop(evbase, 0);
710 event_base_free(evbase);
711 SSL_CTX_free(ssl_ctx);
714 int main(int argc, char **argv) {
715 struct sigaction act;
718 fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
722 memset(&act, 0, sizeof(struct sigaction));
723 act.sa_handler = SIG_IGN;
724 sigaction(SIGPIPE, &act, NULL);
726 OPENSSL_config(NULL);
727 OpenSSL_add_all_algorithms();
728 SSL_load_error_strings();
731 run(argv[1], argv[2], argv[3]);