tizen 2.4 release
[external/nghttp2.git] / examples / libevent-server.c
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2013 Tatsuhiro Tsujikawa
5  *
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:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
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.
24  */
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif /* !HAVE_CONFIG_H */
28
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netdb.h>
32 #include <signal.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #include <ctype.h>
37 #include <netinet/in.h>
38 #include <netinet/tcp.h>
39 #include <err.h>
40
41 #include <openssl/ssl.h>
42 #include <openssl/err.h>
43 #include <openssl/conf.h>
44
45 #include <event.h>
46 #include <event2/event.h>
47 #include <event2/bufferevent_ssl.h>
48 #include <event2/listener.h>
49
50 #include <nghttp2/nghttp2.h>
51
52 #define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
53
54 #define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
55
56 #define MAKE_NV(NAME, VALUE)                                                   \
57   {                                                                            \
58     (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1,   \
59         NGHTTP2_NV_FLAG_NONE                                                   \
60   }
61
62 struct app_context;
63 typedef struct app_context app_context;
64
65 typedef struct http2_stream_data {
66   struct http2_stream_data *prev, *next;
67   char *request_path;
68   int32_t stream_id;
69   int fd;
70 } http2_stream_data;
71
72 typedef struct http2_session_data {
73   struct http2_stream_data root;
74   struct bufferevent *bev;
75   app_context *app_ctx;
76   nghttp2_session *session;
77   char *client_addr;
78 } http2_session_data;
79
80 struct app_context {
81   SSL_CTX *ssl_ctx;
82   struct event_base *evbase;
83 };
84
85 static unsigned char next_proto_list[256];
86 static size_t next_proto_list_len;
87
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;
93 }
94
95 /* Create SSL_CTX. */
96 static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
97   SSL_CTX *ssl_ctx;
98   EC_KEY *ecdh;
99
100   ssl_ctx = SSL_CTX_new(SSLv23_server_method());
101   if (!ssl_ctx) {
102     errx(1, "Could not create SSL/TLS context: %s",
103          ERR_error_string(ERR_get_error(), NULL));
104   }
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);
109
110   ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
111   if (!ecdh) {
112     errx(1, "EC_KEY_new_by_curv_name failed: %s",
113          ERR_error_string(ERR_get_error(), NULL));
114   }
115   SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
116   EC_KEY_free(ecdh);
117
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);
120   }
121   if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
122     errx(1, "Could not read certificate file %s", cert_file);
123   }
124
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;
129
130   SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
131   return ssl_ctx;
132 }
133
134 /* Create SSL object */
135 static SSL *create_ssl(SSL_CTX *ssl_ctx) {
136   SSL *ssl;
137   ssl = SSL_new(ssl_ctx);
138   if (!ssl) {
139     errx(1, "Could not create SSL/TLS session object: %s",
140          ERR_error_string(ERR_get_error(), NULL));
141   }
142   return ssl;
143 }
144
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;
152   }
153 }
154
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;
160   }
161 }
162
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;
170
171   add_stream(session_data, stream_data);
172   return stream_data;
173 }
174
175 static void delete_http2_stream_data(http2_stream_data *stream_data) {
176   if (stream_data->fd != -1) {
177     close(stream_data->fd);
178   }
179   free(stream_data->request_path);
180   free(stream_data);
181 }
182
183 static http2_session_data *create_http2_session_data(app_context *app_ctx,
184                                                      int fd,
185                                                      struct sockaddr *addr,
186                                                      int addrlen) {
187   int rv;
188   http2_session_data *session_data;
189   SSL *ssl;
190   char host[NI_MAXHOST];
191   int val = 1;
192
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);
202   if (rv != 0) {
203     session_data->client_addr = strdup("(unknown)");
204   } else {
205     session_data->client_addr = strdup(host);
206   }
207
208   return session_data;
209 }
210
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);
215   if (ssl) {
216     SSL_shutdown(ssl);
217   }
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);
223     stream_data = next;
224   }
225   free(session_data->client_addr);
226   free(session_data);
227 }
228
229 /* Serialize the frame and send (or buffer) the data to
230    bufferevent. */
231 static int session_send(http2_session_data *session_data) {
232   int rv;
233   rv = nghttp2_session_send(session_data->session);
234   if (rv != 0) {
235     warnx("Fatal error: %s", nghttp2_strerror(rv));
236     return -1;
237   }
238   return 0;
239 }
240
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
244    function. */
245 static int session_recv(http2_session_data *session_data) {
246   ssize_t readlen;
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);
250
251   readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
252   if (readlen < 0) {
253     warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
254     return -1;
255   }
256   if (evbuffer_drain(input, readlen) != 0) {
257     warnx("Fatal error: evbuffer_drain failed");
258     return -1;
259   }
260   if (session_send(session_data) != 0) {
261     return -1;
262   }
263   return 0;
264 }
265
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;
274   }
275   bufferevent_write(bev, data, length);
276   return length;
277 }
278
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);
283   if (slen < sublen) {
284     return 0;
285   }
286   return memcmp(s + slen - sublen, sub, sublen) == 0;
287 }
288
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') {
292     return c - '0';
293   }
294   if ('A' <= c && c <= 'F') {
295     return c - 'A' + 10;
296   }
297   if ('a' <= c && c <= 'f') {
298     return c - 'a' + 10;
299   }
300   return 0;
301 }
302
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
306    string. */
307 static char *percent_decode(const uint8_t *value, size_t valuelen) {
308   char *res;
309
310   res = malloc(valuelen + 1);
311   if (valuelen > 3) {
312     size_t i, j;
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++];
317         continue;
318       }
319       res[j++] = (hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]);
320       i += 3;
321     }
322     memcpy(&res[j], &value[i], 2);
323     res[j + 2] = '\0';
324   } else {
325     memcpy(res, value, valuelen);
326     res[valuelen] = '\0';
327   }
328   return res;
329 }
330
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_) {
336   int fd = source->fd;
337   ssize_t r;
338   while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
339     ;
340   if (r == -1) {
341     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
342   }
343   if (r == 0) {
344     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
345   }
346   return r;
347 }
348
349 static int send_response(nghttp2_session *session, int32_t stream_id,
350                          nghttp2_nv *nva, size_t nvlen, int fd) {
351   int rv;
352   nghttp2_data_provider data_prd;
353   data_prd.source.fd = fd;
354   data_prd.read_callback = file_read_callback;
355
356   rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
357   if (rv != 0) {
358     warnx("Fatal error: %s", nghttp2_strerror(rv));
359     return -1;
360   }
361   return 0;
362 }
363
364 const char ERROR_HTML[] = "<html><head><title>404</title></head>"
365                           "<body><h1>404 Not Found</h1></body></html>";
366
367 static int error_reply(nghttp2_session *session,
368                        http2_stream_data *stream_data) {
369   int rv;
370   ssize_t writelen;
371   int pipefd[2];
372   nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
373
374   rv = pipe(pipefd);
375   if (rv != 0) {
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);
380     if (rv != 0) {
381       warnx("Fatal error: %s", nghttp2_strerror(rv));
382       return -1;
383     }
384     return 0;
385   }
386
387   writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
388   close(pipefd[1]);
389
390   if (writelen != sizeof(ERROR_HTML) - 1) {
391     close(pipefd[0]);
392     return -1;
393   }
394
395   stream_data->fd = pipefd[0];
396
397   if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
398                     pipefd[0]) != 0) {
399     close(pipefd[0]);
400     return -1;
401   }
402   return 0;
403 }
404
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) {
417       break;
418     }
419     stream_data =
420         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
421     if (!stream_data || stream_data->request_path) {
422       break;
423     }
424     if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
425       size_t j;
426       for (j = 0; j < valuelen && value[j] != '?'; ++j)
427         ;
428       stream_data->request_path = percent_decode(value, j);
429     }
430     break;
431   }
432   return 0;
433 }
434
435 static int on_begin_headers_callback(nghttp2_session *session,
436                                      const nghttp2_frame *frame,
437                                      void *user_data) {
438   http2_session_data *session_data = (http2_session_data *)user_data;
439   http2_stream_data *stream_data;
440
441   if (frame->hd.type != NGHTTP2_HEADERS ||
442       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
443     return 0;
444   }
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,
447                                        stream_data);
448   return 0;
449 }
450
451 /* Minimum check for directory traversal. Returns nonzero if it is
452    safe. */
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, "/.");
458 }
459
460 static int on_request_recv(nghttp2_session *session,
461                            http2_session_data *session_data,
462                            http2_stream_data *stream_data) {
463   int fd;
464   nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
465   char *rel_path;
466
467   if (!stream_data->request_path) {
468     if (error_reply(session, stream_data) != 0) {
469       return NGHTTP2_ERR_CALLBACK_FAILURE;
470     }
471     return 0;
472   }
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;
478     }
479     return 0;
480   }
481   for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
482     ;
483   fd = open(rel_path, O_RDONLY);
484   if (fd == -1) {
485     if (error_reply(session, stream_data) != 0) {
486       return NGHTTP2_ERR_CALLBACK_FAILURE;
487     }
488     return 0;
489   }
490   stream_data->fd = fd;
491
492   if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
493       0) {
494     close(fd);
495     return NGHTTP2_ERR_CALLBACK_FAILURE;
496   }
497   return 0;
498 }
499
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) {
505   case NGHTTP2_DATA:
506   case NGHTTP2_HEADERS:
507     /* Check that the client request has finished */
508     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
509       stream_data =
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. */
513       if (!stream_data) {
514         return 0;
515       }
516       return on_request_recv(session, session_data, stream_data);
517     }
518     break;
519   default:
520     break;
521   }
522   return 0;
523 }
524
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;
529
530   stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
531   if (!stream_data) {
532     return 0;
533   }
534   remove_stream(session_data, stream_data);
535   delete_http2_stream_data(stream_data);
536   return 0;
537 }
538
539 static void initialize_nghttp2_session(http2_session_data *session_data) {
540   nghttp2_option *option;
541   nghttp2_session_callbacks *callbacks;
542
543   nghttp2_option_new(&option);
544
545   /* Tells nghttp2_session object that it handles client connection
546      preface */
547   nghttp2_option_set_recv_client_preface(option, 1);
548
549   nghttp2_session_callbacks_new(&callbacks);
550
551   nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
552
553   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
554                                                        on_frame_recv_callback);
555
556   nghttp2_session_callbacks_set_on_stream_close_callback(
557       callbacks, on_stream_close_callback);
558
559   nghttp2_session_callbacks_set_on_header_callback(callbacks,
560                                                    on_header_callback);
561
562   nghttp2_session_callbacks_set_on_begin_headers_callback(
563       callbacks, on_begin_headers_callback);
564
565   nghttp2_session_server_new2(&session_data->session, callbacks, session_data,
566                               option);
567
568   nghttp2_session_callbacks_del(callbacks);
569   nghttp2_option_del(option);
570 }
571
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}};
577   int rv;
578
579   rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
580                                ARRLEN(iv));
581   if (rv != 0) {
582     warnx("Fatal error: %s", nghttp2_strerror(rv));
583     return -1;
584   }
585   return 0;
586 }
587
588 /* readcb for bufferevent after client connection header was
589    checked. */
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);
594     return;
595   }
596 }
597
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) {
609     return;
610   }
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);
614     return;
615   }
616   if (session_send(session_data) != 0) {
617     delete_http2_session_data(session_data);
618     return;
619   }
620 }
621
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);
627
628     initialize_nghttp2_session(session_data);
629
630     if (send_server_connection_header(session_data) != 0) {
631       delete_http2_session_data(session_data);
632       return;
633     }
634
635     return;
636   }
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);
643   }
644   delete_http2_session_data(session_data);
645 }
646
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;
652
653   session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
654
655   bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
656 }
657
658 static void start_listen(struct event_base *evbase, const char *service,
659                          app_context *app_ctx) {
660   int rv;
661   struct addrinfo hints;
662   struct addrinfo *res, *rp;
663
664   memset(&hints, 0, sizeof(hints));
665   hints.ai_family = AF_UNSPEC;
666   hints.ai_socktype = SOCK_STREAM;
667   hints.ai_flags = AI_PASSIVE;
668 #ifdef AI_ADDRCONFIG
669   hints.ai_flags |= AI_ADDRCONFIG;
670 #endif /* AI_ADDRCONFIG */
671
672   rv = getaddrinfo(NULL, service, &hints, &res);
673   if (rv != 0) {
674     errx(1, NULL);
675   }
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);
681     if (listener) {
682       freeaddrinfo(res);
683
684       return;
685     }
686   }
687   errx(1, "Could not start listener");
688 }
689
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;
695 }
696
697 static void run(const char *service, const char *key_file,
698                 const char *cert_file) {
699   SSL_CTX *ssl_ctx;
700   app_context app_ctx;
701   struct event_base *evbase;
702
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);
707
708   event_base_loop(evbase, 0);
709
710   event_base_free(evbase);
711   SSL_CTX_free(ssl_ctx);
712 }
713
714 int main(int argc, char **argv) {
715   struct sigaction act;
716
717   if (argc < 4) {
718     fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
719     exit(EXIT_FAILURE);
720   }
721
722   memset(&act, 0, sizeof(struct sigaction));
723   act.sa_handler = SIG_IGN;
724   sigaction(SIGPIPE, &act, NULL);
725
726   OPENSSL_config(NULL);
727   OpenSSL_add_all_algorithms();
728   SSL_load_error_strings();
729   SSL_library_init();
730
731   run(argv[1], argv[2], argv[3]);
732   return 0;
733 }