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.
26 # define errx(exitcode, format, args...) \
28 warnx(format, ##args); \
31 # define warn(format, args...) warnx(format ": %s", ##args, strerror(errno))
32 # define warnx(format, args...) fprintf(stderr, format "\n", ##args)
37 #endif /* HAVE_CONFIG_H */
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
42 #endif /* HAVE_SYS_SOCKET_H */
45 #endif /* HAVE_NETDB_H */
49 #endif /* HAVE_UNISTD_H */
53 #endif /* HAVE_FCNTL_H */
55 #ifdef HAVE_NETINET_IN_H
56 # include <netinet/in.h>
57 #endif /* HAVE_NETINET_IN_H */
58 #include <netinet/tcp.h>
65 #include <openssl/ssl.h>
66 #include <openssl/err.h>
67 #include <openssl/conf.h>
70 #include <event2/event.h>
71 #include <event2/bufferevent_ssl.h>
72 #include <event2/listener.h>
74 #include <nghttp2/nghttp2.h>
76 #define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
78 #define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
80 #define MAKE_NV(NAME, VALUE) \
82 (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
83 NGHTTP2_NV_FLAG_NONE \
87 typedef struct app_context app_context;
89 typedef struct http2_stream_data {
90 struct http2_stream_data *prev, *next;
96 typedef struct http2_session_data {
97 struct http2_stream_data root;
98 struct bufferevent *bev;
100 nghttp2_session *session;
102 } http2_session_data;
106 struct event_base *evbase;
109 static unsigned char next_proto_list[256];
110 static size_t next_proto_list_len;
112 #ifndef OPENSSL_NO_NEXTPROTONEG
113 static int next_proto_cb(SSL *ssl, const unsigned char **data,
114 unsigned int *len, void *arg) {
118 *data = next_proto_list;
119 *len = (unsigned int)next_proto_list_len;
120 return SSL_TLSEXT_ERR_OK;
122 #endif /* !OPENSSL_NO_NEXTPROTONEG */
124 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
125 static int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
126 unsigned char *outlen, const unsigned char *in,
127 unsigned int inlen, void *arg) {
132 rv = nghttp2_select_next_protocol((unsigned char **)out, outlen, in, inlen);
135 return SSL_TLSEXT_ERR_NOACK;
138 return SSL_TLSEXT_ERR_OK;
140 #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
142 /* Create SSL_CTX. */
143 static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
146 ssl_ctx = SSL_CTX_new(TLS_server_method());
148 errx(1, "Could not create SSL/TLS context: %s",
149 ERR_error_string(ERR_get_error(), NULL));
151 SSL_CTX_set_options(ssl_ctx,
152 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
153 SSL_OP_NO_COMPRESSION |
154 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
155 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
156 if (SSL_CTX_set1_curves_list(ssl_ctx, "P-256") != 1) {
157 errx(1, "SSL_CTX_set1_curves_list failed: %s",
158 ERR_error_string(ERR_get_error(), NULL));
160 #else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
163 ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
165 errx(1, "EC_KEY_new_by_curv_name failed: %s",
166 ERR_error_string(ERR_get_error(), NULL));
168 SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
171 #endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
173 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) != 1) {
174 errx(1, "Could not read private key file %s", key_file);
176 if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
177 errx(1, "Could not read certificate file %s", cert_file);
180 next_proto_list[0] = NGHTTP2_PROTO_VERSION_ID_LEN;
181 memcpy(&next_proto_list[1], NGHTTP2_PROTO_VERSION_ID,
182 NGHTTP2_PROTO_VERSION_ID_LEN);
183 next_proto_list_len = 1 + NGHTTP2_PROTO_VERSION_ID_LEN;
185 #ifndef OPENSSL_NO_NEXTPROTONEG
186 SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
187 #endif /* !OPENSSL_NO_NEXTPROTONEG */
189 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
190 SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, NULL);
191 #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
196 /* Create SSL object */
197 static SSL *create_ssl(SSL_CTX *ssl_ctx) {
199 ssl = SSL_new(ssl_ctx);
201 errx(1, "Could not create SSL/TLS session object: %s",
202 ERR_error_string(ERR_get_error(), NULL));
207 static void add_stream(http2_session_data *session_data,
208 http2_stream_data *stream_data) {
209 stream_data->next = session_data->root.next;
210 session_data->root.next = stream_data;
211 stream_data->prev = &session_data->root;
212 if (stream_data->next) {
213 stream_data->next->prev = stream_data;
217 static void remove_stream(http2_session_data *session_data,
218 http2_stream_data *stream_data) {
221 stream_data->prev->next = stream_data->next;
222 if (stream_data->next) {
223 stream_data->next->prev = stream_data->prev;
227 static http2_stream_data *
228 create_http2_stream_data(http2_session_data *session_data, int32_t stream_id) {
229 http2_stream_data *stream_data;
230 stream_data = malloc(sizeof(http2_stream_data));
231 memset(stream_data, 0, sizeof(http2_stream_data));
232 stream_data->stream_id = stream_id;
233 stream_data->fd = -1;
235 add_stream(session_data, stream_data);
239 static void delete_http2_stream_data(http2_stream_data *stream_data) {
240 if (stream_data->fd != -1) {
241 close(stream_data->fd);
243 free(stream_data->request_path);
247 static http2_session_data *create_http2_session_data(app_context *app_ctx,
249 struct sockaddr *addr,
252 http2_session_data *session_data;
254 char host[NI_MAXHOST];
257 ssl = create_ssl(app_ctx->ssl_ctx);
258 session_data = malloc(sizeof(http2_session_data));
259 memset(session_data, 0, sizeof(http2_session_data));
260 session_data->app_ctx = app_ctx;
261 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
262 session_data->bev = bufferevent_openssl_socket_new(
263 app_ctx->evbase, fd, ssl, BUFFEREVENT_SSL_ACCEPTING,
264 BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
265 bufferevent_enable(session_data->bev, EV_READ | EV_WRITE);
266 rv = getnameinfo(addr, (socklen_t)addrlen, host, sizeof(host), NULL, 0,
269 session_data->client_addr = strdup("(unknown)");
271 session_data->client_addr = strdup(host);
277 static void delete_http2_session_data(http2_session_data *session_data) {
278 http2_stream_data *stream_data;
279 SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
280 fprintf(stderr, "%s disconnected\n", session_data->client_addr);
284 bufferevent_free(session_data->bev);
285 nghttp2_session_del(session_data->session);
286 for (stream_data = session_data->root.next; stream_data;) {
287 http2_stream_data *next = stream_data->next;
288 delete_http2_stream_data(stream_data);
291 free(session_data->client_addr);
295 /* Serialize the frame and send (or buffer) the data to
297 static int session_send(http2_session_data *session_data) {
299 rv = nghttp2_session_send(session_data->session);
301 warnx("Fatal error: %s", nghttp2_strerror(rv));
307 /* Read the data in the bufferevent and feed them into nghttp2 library
308 function. Invocation of nghttp2_session_mem_recv() may make
309 additional pending frames, so call session_send() at the end of the
311 static int session_recv(http2_session_data *session_data) {
313 struct evbuffer *input = bufferevent_get_input(session_data->bev);
314 size_t datalen = evbuffer_get_length(input);
315 unsigned char *data = evbuffer_pullup(input, -1);
317 readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
319 warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
322 if (evbuffer_drain(input, (size_t)readlen) != 0) {
323 warnx("Fatal error: evbuffer_drain failed");
326 if (session_send(session_data) != 0) {
332 static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
333 size_t length, int flags, void *user_data) {
334 http2_session_data *session_data = (http2_session_data *)user_data;
335 struct bufferevent *bev = session_data->bev;
339 /* Avoid excessive buffering in server side. */
340 if (evbuffer_get_length(bufferevent_get_output(session_data->bev)) >=
341 OUTPUT_WOULDBLOCK_THRESHOLD) {
342 return NGHTTP2_ERR_WOULDBLOCK;
344 bufferevent_write(bev, data, length);
345 return (ssize_t)length;
348 /* Returns nonzero if the string |s| ends with the substring |sub| */
349 static int ends_with(const char *s, const char *sub) {
350 size_t slen = strlen(s);
351 size_t sublen = strlen(sub);
355 return memcmp(s + slen - sublen, sub, sublen) == 0;
358 /* Returns int value of hex string character |c| */
359 static uint8_t hex_to_uint(uint8_t c) {
360 if ('0' <= c && c <= '9') {
361 return (uint8_t)(c - '0');
363 if ('A' <= c && c <= 'F') {
364 return (uint8_t)(c - 'A' + 10);
366 if ('a' <= c && c <= 'f') {
367 return (uint8_t)(c - 'a' + 10);
372 /* Decodes percent-encoded byte string |value| with length |valuelen|
373 and returns the decoded byte string in allocated buffer. The return
374 value is NULL terminated. The caller must free the returned
376 static char *percent_decode(const uint8_t *value, size_t valuelen) {
379 res = malloc(valuelen + 1);
382 for (i = 0, j = 0; i < valuelen - 2;) {
383 if (value[i] != '%' || !isxdigit(value[i + 1]) ||
384 !isxdigit(value[i + 2])) {
385 res[j++] = (char)value[i++];
389 (char)((hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]));
392 memcpy(&res[j], &value[i], 2);
395 memcpy(res, value, valuelen);
396 res[valuelen] = '\0';
401 static ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
402 uint8_t *buf, size_t length,
403 uint32_t *data_flags,
404 nghttp2_data_source *source,
412 while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
415 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
418 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
423 static int send_response(nghttp2_session *session, int32_t stream_id,
424 nghttp2_nv *nva, size_t nvlen, int fd) {
426 nghttp2_data_provider data_prd;
427 data_prd.source.fd = fd;
428 data_prd.read_callback = file_read_callback;
430 rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
432 warnx("Fatal error: %s", nghttp2_strerror(rv));
438 static const char ERROR_HTML[] = "<html><head><title>404</title></head>"
439 "<body><h1>404 Not Found</h1></body></html>";
441 static int error_reply(nghttp2_session *session,
442 http2_stream_data *stream_data) {
446 nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
450 warn("Could not create pipe");
451 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
452 stream_data->stream_id,
453 NGHTTP2_INTERNAL_ERROR);
455 warnx("Fatal error: %s", nghttp2_strerror(rv));
461 writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
464 if (writelen != sizeof(ERROR_HTML) - 1) {
469 stream_data->fd = pipefd[0];
471 if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
479 /* nghttp2_on_header_callback: Called when nghttp2 library emits
480 single header name/value pair. */
481 static int on_header_callback(nghttp2_session *session,
482 const nghttp2_frame *frame, const uint8_t *name,
483 size_t namelen, const uint8_t *value,
484 size_t valuelen, uint8_t flags, void *user_data) {
485 http2_stream_data *stream_data;
486 const char PATH[] = ":path";
490 switch (frame->hd.type) {
491 case NGHTTP2_HEADERS:
492 if (frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
496 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
497 if (!stream_data || stream_data->request_path) {
500 if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
502 for (j = 0; j < valuelen && value[j] != '?'; ++j)
504 stream_data->request_path = percent_decode(value, j);
511 static int on_begin_headers_callback(nghttp2_session *session,
512 const nghttp2_frame *frame,
514 http2_session_data *session_data = (http2_session_data *)user_data;
515 http2_stream_data *stream_data;
517 if (frame->hd.type != NGHTTP2_HEADERS ||
518 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
521 stream_data = create_http2_stream_data(session_data, frame->hd.stream_id);
522 nghttp2_session_set_stream_user_data(session, frame->hd.stream_id,
527 /* Minimum check for directory traversal. Returns nonzero if it is
529 static int check_path(const char *path) {
530 /* We don't like '\' in url. */
531 return path[0] && path[0] == '/' && strchr(path, '\\') == NULL &&
532 strstr(path, "/../") == NULL && strstr(path, "/./") == NULL &&
533 !ends_with(path, "/..") && !ends_with(path, "/.");
536 static int on_request_recv(nghttp2_session *session,
537 http2_session_data *session_data,
538 http2_stream_data *stream_data) {
540 nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
543 if (!stream_data->request_path) {
544 if (error_reply(session, stream_data) != 0) {
545 return NGHTTP2_ERR_CALLBACK_FAILURE;
549 fprintf(stderr, "%s GET %s\n", session_data->client_addr,
550 stream_data->request_path);
551 if (!check_path(stream_data->request_path)) {
552 if (error_reply(session, stream_data) != 0) {
553 return NGHTTP2_ERR_CALLBACK_FAILURE;
557 for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
559 fd = open(rel_path, O_RDONLY);
561 if (error_reply(session, stream_data) != 0) {
562 return NGHTTP2_ERR_CALLBACK_FAILURE;
566 stream_data->fd = fd;
568 if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
571 return NGHTTP2_ERR_CALLBACK_FAILURE;
576 static int on_frame_recv_callback(nghttp2_session *session,
577 const nghttp2_frame *frame, void *user_data) {
578 http2_session_data *session_data = (http2_session_data *)user_data;
579 http2_stream_data *stream_data;
580 switch (frame->hd.type) {
582 case NGHTTP2_HEADERS:
583 /* Check that the client request has finished */
584 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
586 nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
587 /* For DATA and HEADERS frame, this callback may be called after
588 on_stream_close_callback. Check that stream still alive. */
592 return on_request_recv(session, session_data, stream_data);
601 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
602 uint32_t error_code, void *user_data) {
603 http2_session_data *session_data = (http2_session_data *)user_data;
604 http2_stream_data *stream_data;
607 stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
611 remove_stream(session_data, stream_data);
612 delete_http2_stream_data(stream_data);
616 static void initialize_nghttp2_session(http2_session_data *session_data) {
617 nghttp2_session_callbacks *callbacks;
619 nghttp2_session_callbacks_new(&callbacks);
621 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
623 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
624 on_frame_recv_callback);
626 nghttp2_session_callbacks_set_on_stream_close_callback(
627 callbacks, on_stream_close_callback);
629 nghttp2_session_callbacks_set_on_header_callback(callbacks,
632 nghttp2_session_callbacks_set_on_begin_headers_callback(
633 callbacks, on_begin_headers_callback);
635 nghttp2_session_server_new(&session_data->session, callbacks, session_data);
637 nghttp2_session_callbacks_del(callbacks);
640 /* Send HTTP/2 client connection header, which includes 24 bytes
641 magic octets and SETTINGS frame */
642 static int send_server_connection_header(http2_session_data *session_data) {
643 nghttp2_settings_entry iv[1] = {
644 {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
647 rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
650 warnx("Fatal error: %s", nghttp2_strerror(rv));
656 /* readcb for bufferevent after client connection header was
658 static void readcb(struct bufferevent *bev, void *ptr) {
659 http2_session_data *session_data = (http2_session_data *)ptr;
662 if (session_recv(session_data) != 0) {
663 delete_http2_session_data(session_data);
668 /* writecb for bufferevent. To greaceful shutdown after sending or
669 receiving GOAWAY, we check the some conditions on the nghttp2
670 library and output buffer of bufferevent. If it indicates we have
671 no business to this session, tear down the connection. If the
672 connection is not going to shutdown, we call session_send() to
673 process pending data in the output buffer. This is necessary
674 because we have a threshold on the buffer size to avoid too much
675 buffering. See send_callback(). */
676 static void writecb(struct bufferevent *bev, void *ptr) {
677 http2_session_data *session_data = (http2_session_data *)ptr;
678 if (evbuffer_get_length(bufferevent_get_output(bev)) > 0) {
681 if (nghttp2_session_want_read(session_data->session) == 0 &&
682 nghttp2_session_want_write(session_data->session) == 0) {
683 delete_http2_session_data(session_data);
686 if (session_send(session_data) != 0) {
687 delete_http2_session_data(session_data);
692 /* eventcb for bufferevent */
693 static void eventcb(struct bufferevent *bev, short events, void *ptr) {
694 http2_session_data *session_data = (http2_session_data *)ptr;
695 if (events & BEV_EVENT_CONNECTED) {
696 const unsigned char *alpn = NULL;
697 unsigned int alpnlen = 0;
701 fprintf(stderr, "%s connected\n", session_data->client_addr);
703 ssl = bufferevent_openssl_get_ssl(session_data->bev);
705 #ifndef OPENSSL_NO_NEXTPROTONEG
706 SSL_get0_next_proto_negotiated(ssl, &alpn, &alpnlen);
707 #endif /* !OPENSSL_NO_NEXTPROTONEG */
708 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
710 SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
712 #endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
714 if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
715 fprintf(stderr, "%s h2 is not negotiated\n", session_data->client_addr);
716 delete_http2_session_data(session_data);
720 initialize_nghttp2_session(session_data);
722 if (send_server_connection_header(session_data) != 0 ||
723 session_send(session_data) != 0) {
724 delete_http2_session_data(session_data);
730 if (events & BEV_EVENT_EOF) {
731 fprintf(stderr, "%s EOF\n", session_data->client_addr);
732 } else if (events & BEV_EVENT_ERROR) {
733 fprintf(stderr, "%s network error\n", session_data->client_addr);
734 } else if (events & BEV_EVENT_TIMEOUT) {
735 fprintf(stderr, "%s timeout\n", session_data->client_addr);
737 delete_http2_session_data(session_data);
740 /* callback for evconnlistener */
741 static void acceptcb(struct evconnlistener *listener, int fd,
742 struct sockaddr *addr, int addrlen, void *arg) {
743 app_context *app_ctx = (app_context *)arg;
744 http2_session_data *session_data;
747 session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
749 bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
752 static void start_listen(struct event_base *evbase, const char *service,
753 app_context *app_ctx) {
755 struct addrinfo hints;
756 struct addrinfo *res, *rp;
758 memset(&hints, 0, sizeof(hints));
759 hints.ai_family = AF_UNSPEC;
760 hints.ai_socktype = SOCK_STREAM;
761 hints.ai_flags = AI_PASSIVE;
763 hints.ai_flags |= AI_ADDRCONFIG;
764 #endif /* AI_ADDRCONFIG */
766 rv = getaddrinfo(NULL, service, &hints, &res);
768 errx(1, "Could not resolve server address");
770 for (rp = res; rp; rp = rp->ai_next) {
771 struct evconnlistener *listener;
772 listener = evconnlistener_new_bind(
773 evbase, acceptcb, app_ctx, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
774 16, rp->ai_addr, (int)rp->ai_addrlen);
781 errx(1, "Could not start listener");
784 static void initialize_app_context(app_context *app_ctx, SSL_CTX *ssl_ctx,
785 struct event_base *evbase) {
786 memset(app_ctx, 0, sizeof(app_context));
787 app_ctx->ssl_ctx = ssl_ctx;
788 app_ctx->evbase = evbase;
791 static void run(const char *service, const char *key_file,
792 const char *cert_file) {
795 struct event_base *evbase;
797 ssl_ctx = create_ssl_ctx(key_file, cert_file);
798 evbase = event_base_new();
799 initialize_app_context(&app_ctx, ssl_ctx, evbase);
800 start_listen(evbase, service, &app_ctx);
802 event_base_loop(evbase, 0);
804 event_base_free(evbase);
805 SSL_CTX_free(ssl_ctx);
808 int main(int argc, char **argv) {
809 struct sigaction act;
812 fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
816 memset(&act, 0, sizeof(struct sigaction));
817 act.sa_handler = SIG_IGN;
818 sigaction(SIGPIPE, &act, NULL);
820 #if OPENSSL_VERSION_NUMBER >= 0x1010000fL
821 /* No explicit initialization is required. */
822 #elif defined(OPENSSL_IS_BORINGSSL)
823 CRYPTO_library_init();
824 #else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
825 !defined(OPENSSL_IS_BORINGSSL) */
826 OPENSSL_config(NULL);
827 SSL_load_error_strings();
829 OpenSSL_add_all_algorithms();
830 #endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \
831 !defined(OPENSSL_IS_BORINGSSL) */
833 run(argv[1], argv[2], argv[3]);