Imported Upstream version 1.0.0
[platform/upstream/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 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
32 #endif /* HAVE_SYS_SOCKET_H */
33 #ifdef HAVE_NETDB_H
34 #include <netdb.h>
35 #endif /* HAVE_NETDB_H */
36 #include <signal.h>
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif /* HAVE_UNISTD_H */
40 #include <sys/stat.h>
41 #ifdef HAVE_FCNTL_H
42 #include <fcntl.h>
43 #endif /* HAVE_FCNTL_H */
44 #include <ctype.h>
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
47 #endif /* HAVE_NETINET_IN_H */
48 #include <netinet/tcp.h>
49 #include <err.h>
50
51 #include <openssl/ssl.h>
52 #include <openssl/err.h>
53 #include <openssl/conf.h>
54
55 #include <event.h>
56 #include <event2/event.h>
57 #include <event2/bufferevent_ssl.h>
58 #include <event2/listener.h>
59
60 #include <nghttp2/nghttp2.h>
61
62 #define OUTPUT_WOULDBLOCK_THRESHOLD (1 << 16)
63
64 #define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
65
66 #define MAKE_NV(NAME, VALUE)                                                   \
67   {                                                                            \
68     (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1,   \
69         NGHTTP2_NV_FLAG_NONE                                                   \
70   }
71
72 struct app_context;
73 typedef struct app_context app_context;
74
75 typedef struct http2_stream_data {
76   struct http2_stream_data *prev, *next;
77   char *request_path;
78   int32_t stream_id;
79   int fd;
80 } http2_stream_data;
81
82 typedef struct http2_session_data {
83   struct http2_stream_data root;
84   struct bufferevent *bev;
85   app_context *app_ctx;
86   nghttp2_session *session;
87   char *client_addr;
88 } http2_session_data;
89
90 struct app_context {
91   SSL_CTX *ssl_ctx;
92   struct event_base *evbase;
93 };
94
95 static unsigned char next_proto_list[256];
96 static size_t next_proto_list_len;
97
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;
103 }
104
105 /* Create SSL_CTX. */
106 static SSL_CTX *create_ssl_ctx(const char *key_file, const char *cert_file) {
107   SSL_CTX *ssl_ctx;
108   EC_KEY *ecdh;
109
110   ssl_ctx = SSL_CTX_new(SSLv23_server_method());
111   if (!ssl_ctx) {
112     errx(1, "Could not create SSL/TLS context: %s",
113          ERR_error_string(ERR_get_error(), NULL));
114   }
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);
119
120   ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
121   if (!ecdh) {
122     errx(1, "EC_KEY_new_by_curv_name failed: %s",
123          ERR_error_string(ERR_get_error(), NULL));
124   }
125   SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
126   EC_KEY_free(ecdh);
127
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);
130   }
131   if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cert_file) != 1) {
132     errx(1, "Could not read certificate file %s", cert_file);
133   }
134
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;
139
140   SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, NULL);
141   return ssl_ctx;
142 }
143
144 /* Create SSL object */
145 static SSL *create_ssl(SSL_CTX *ssl_ctx) {
146   SSL *ssl;
147   ssl = SSL_new(ssl_ctx);
148   if (!ssl) {
149     errx(1, "Could not create SSL/TLS session object: %s",
150          ERR_error_string(ERR_get_error(), NULL));
151   }
152   return ssl;
153 }
154
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;
162   }
163 }
164
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;
170   }
171 }
172
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;
180
181   add_stream(session_data, stream_data);
182   return stream_data;
183 }
184
185 static void delete_http2_stream_data(http2_stream_data *stream_data) {
186   if (stream_data->fd != -1) {
187     close(stream_data->fd);
188   }
189   free(stream_data->request_path);
190   free(stream_data);
191 }
192
193 static http2_session_data *create_http2_session_data(app_context *app_ctx,
194                                                      int fd,
195                                                      struct sockaddr *addr,
196                                                      int addrlen) {
197   int rv;
198   http2_session_data *session_data;
199   SSL *ssl;
200   char host[NI_MAXHOST];
201   int val = 1;
202
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);
212   if (rv != 0) {
213     session_data->client_addr = strdup("(unknown)");
214   } else {
215     session_data->client_addr = strdup(host);
216   }
217
218   return session_data;
219 }
220
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);
225   if (ssl) {
226     SSL_shutdown(ssl);
227   }
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);
233     stream_data = next;
234   }
235   free(session_data->client_addr);
236   free(session_data);
237 }
238
239 /* Serialize the frame and send (or buffer) the data to
240    bufferevent. */
241 static int session_send(http2_session_data *session_data) {
242   int rv;
243   rv = nghttp2_session_send(session_data->session);
244   if (rv != 0) {
245     warnx("Fatal error: %s", nghttp2_strerror(rv));
246     return -1;
247   }
248   return 0;
249 }
250
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
254    function. */
255 static int session_recv(http2_session_data *session_data) {
256   ssize_t readlen;
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);
260
261   readlen = nghttp2_session_mem_recv(session_data->session, data, datalen);
262   if (readlen < 0) {
263     warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
264     return -1;
265   }
266   if (evbuffer_drain(input, readlen) != 0) {
267     warnx("Fatal error: evbuffer_drain failed");
268     return -1;
269   }
270   if (session_send(session_data) != 0) {
271     return -1;
272   }
273   return 0;
274 }
275
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;
284   }
285   bufferevent_write(bev, data, length);
286   return length;
287 }
288
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);
293   if (slen < sublen) {
294     return 0;
295   }
296   return memcmp(s + slen - sublen, sub, sublen) == 0;
297 }
298
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') {
302     return c - '0';
303   }
304   if ('A' <= c && c <= 'F') {
305     return c - 'A' + 10;
306   }
307   if ('a' <= c && c <= 'f') {
308     return c - 'a' + 10;
309   }
310   return 0;
311 }
312
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
316    string. */
317 static char *percent_decode(const uint8_t *value, size_t valuelen) {
318   char *res;
319
320   res = malloc(valuelen + 1);
321   if (valuelen > 3) {
322     size_t i, j;
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++];
327         continue;
328       }
329       res[j++] = (hex_to_uint(value[i + 1]) << 4) + hex_to_uint(value[i + 2]);
330       i += 3;
331     }
332     memcpy(&res[j], &value[i], 2);
333     res[j + 2] = '\0';
334   } else {
335     memcpy(res, value, valuelen);
336     res[valuelen] = '\0';
337   }
338   return res;
339 }
340
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_) {
346   int fd = source->fd;
347   ssize_t r;
348   while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
349     ;
350   if (r == -1) {
351     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
352   }
353   if (r == 0) {
354     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
355   }
356   return r;
357 }
358
359 static int send_response(nghttp2_session *session, int32_t stream_id,
360                          nghttp2_nv *nva, size_t nvlen, int fd) {
361   int rv;
362   nghttp2_data_provider data_prd;
363   data_prd.source.fd = fd;
364   data_prd.read_callback = file_read_callback;
365
366   rv = nghttp2_submit_response(session, stream_id, nva, nvlen, &data_prd);
367   if (rv != 0) {
368     warnx("Fatal error: %s", nghttp2_strerror(rv));
369     return -1;
370   }
371   return 0;
372 }
373
374 const char ERROR_HTML[] = "<html><head><title>404</title></head>"
375                           "<body><h1>404 Not Found</h1></body></html>";
376
377 static int error_reply(nghttp2_session *session,
378                        http2_stream_data *stream_data) {
379   int rv;
380   ssize_t writelen;
381   int pipefd[2];
382   nghttp2_nv hdrs[] = {MAKE_NV(":status", "404")};
383
384   rv = pipe(pipefd);
385   if (rv != 0) {
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);
390     if (rv != 0) {
391       warnx("Fatal error: %s", nghttp2_strerror(rv));
392       return -1;
393     }
394     return 0;
395   }
396
397   writelen = write(pipefd[1], ERROR_HTML, sizeof(ERROR_HTML) - 1);
398   close(pipefd[1]);
399
400   if (writelen != sizeof(ERROR_HTML) - 1) {
401     close(pipefd[0]);
402     return -1;
403   }
404
405   stream_data->fd = pipefd[0];
406
407   if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs),
408                     pipefd[0]) != 0) {
409     close(pipefd[0]);
410     return -1;
411   }
412   return 0;
413 }
414
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) {
427       break;
428     }
429     stream_data =
430         nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
431     if (!stream_data || stream_data->request_path) {
432       break;
433     }
434     if (namelen == sizeof(PATH) - 1 && memcmp(PATH, name, namelen) == 0) {
435       size_t j;
436       for (j = 0; j < valuelen && value[j] != '?'; ++j)
437         ;
438       stream_data->request_path = percent_decode(value, j);
439     }
440     break;
441   }
442   return 0;
443 }
444
445 static int on_begin_headers_callback(nghttp2_session *session,
446                                      const nghttp2_frame *frame,
447                                      void *user_data) {
448   http2_session_data *session_data = (http2_session_data *)user_data;
449   http2_stream_data *stream_data;
450
451   if (frame->hd.type != NGHTTP2_HEADERS ||
452       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
453     return 0;
454   }
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,
457                                        stream_data);
458   return 0;
459 }
460
461 /* Minimum check for directory traversal. Returns nonzero if it is
462    safe. */
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, "/.");
468 }
469
470 static int on_request_recv(nghttp2_session *session,
471                            http2_session_data *session_data,
472                            http2_stream_data *stream_data) {
473   int fd;
474   nghttp2_nv hdrs[] = {MAKE_NV(":status", "200")};
475   char *rel_path;
476
477   if (!stream_data->request_path) {
478     if (error_reply(session, stream_data) != 0) {
479       return NGHTTP2_ERR_CALLBACK_FAILURE;
480     }
481     return 0;
482   }
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;
488     }
489     return 0;
490   }
491   for (rel_path = stream_data->request_path; *rel_path == '/'; ++rel_path)
492     ;
493   fd = open(rel_path, O_RDONLY);
494   if (fd == -1) {
495     if (error_reply(session, stream_data) != 0) {
496       return NGHTTP2_ERR_CALLBACK_FAILURE;
497     }
498     return 0;
499   }
500   stream_data->fd = fd;
501
502   if (send_response(session, stream_data->stream_id, hdrs, ARRLEN(hdrs), fd) !=
503       0) {
504     close(fd);
505     return NGHTTP2_ERR_CALLBACK_FAILURE;
506   }
507   return 0;
508 }
509
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) {
515   case NGHTTP2_DATA:
516   case NGHTTP2_HEADERS:
517     /* Check that the client request has finished */
518     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
519       stream_data =
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. */
523       if (!stream_data) {
524         return 0;
525       }
526       return on_request_recv(session, session_data, stream_data);
527     }
528     break;
529   default:
530     break;
531   }
532   return 0;
533 }
534
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;
539
540   stream_data = nghttp2_session_get_stream_user_data(session, stream_id);
541   if (!stream_data) {
542     return 0;
543   }
544   remove_stream(session_data, stream_data);
545   delete_http2_stream_data(stream_data);
546   return 0;
547 }
548
549 static void initialize_nghttp2_session(http2_session_data *session_data) {
550   nghttp2_session_callbacks *callbacks;
551
552   nghttp2_session_callbacks_new(&callbacks);
553
554   nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
555
556   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
557                                                        on_frame_recv_callback);
558
559   nghttp2_session_callbacks_set_on_stream_close_callback(
560       callbacks, on_stream_close_callback);
561
562   nghttp2_session_callbacks_set_on_header_callback(callbacks,
563                                                    on_header_callback);
564
565   nghttp2_session_callbacks_set_on_begin_headers_callback(
566       callbacks, on_begin_headers_callback);
567
568   nghttp2_session_server_new(&session_data->session, callbacks, session_data);
569
570   nghttp2_session_callbacks_del(callbacks);
571 }
572
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}};
578   int rv;
579
580   rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
581                                ARRLEN(iv));
582   if (rv != 0) {
583     warnx("Fatal error: %s", nghttp2_strerror(rv));
584     return -1;
585   }
586   return 0;
587 }
588
589 /* readcb for bufferevent after client connection header was
590    checked. */
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);
595     return;
596   }
597 }
598
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) {
610     return;
611   }
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);
615     return;
616   }
617   if (session_send(session_data) != 0) {
618     delete_http2_session_data(session_data);
619     return;
620   }
621 }
622
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);
628
629     initialize_nghttp2_session(session_data);
630
631     if (send_server_connection_header(session_data) != 0) {
632       delete_http2_session_data(session_data);
633       return;
634     }
635
636     return;
637   }
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);
644   }
645   delete_http2_session_data(session_data);
646 }
647
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;
653
654   session_data = create_http2_session_data(app_ctx, fd, addr, addrlen);
655
656   bufferevent_setcb(session_data->bev, readcb, writecb, eventcb, session_data);
657 }
658
659 static void start_listen(struct event_base *evbase, const char *service,
660                          app_context *app_ctx) {
661   int rv;
662   struct addrinfo hints;
663   struct addrinfo *res, *rp;
664
665   memset(&hints, 0, sizeof(hints));
666   hints.ai_family = AF_UNSPEC;
667   hints.ai_socktype = SOCK_STREAM;
668   hints.ai_flags = AI_PASSIVE;
669 #ifdef AI_ADDRCONFIG
670   hints.ai_flags |= AI_ADDRCONFIG;
671 #endif /* AI_ADDRCONFIG */
672
673   rv = getaddrinfo(NULL, service, &hints, &res);
674   if (rv != 0) {
675     errx(1, NULL);
676   }
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);
682     if (listener) {
683       freeaddrinfo(res);
684
685       return;
686     }
687   }
688   errx(1, "Could not start listener");
689 }
690
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;
696 }
697
698 static void run(const char *service, const char *key_file,
699                 const char *cert_file) {
700   SSL_CTX *ssl_ctx;
701   app_context app_ctx;
702   struct event_base *evbase;
703
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);
708
709   event_base_loop(evbase, 0);
710
711   event_base_free(evbase);
712   SSL_CTX_free(ssl_ctx);
713 }
714
715 int main(int argc, char **argv) {
716   struct sigaction act;
717
718   if (argc < 4) {
719     fprintf(stderr, "Usage: libevent-server PORT KEY_FILE CERT_FILE\n");
720     exit(EXIT_FAILURE);
721   }
722
723   memset(&act, 0, sizeof(struct sigaction));
724   act.sa_handler = SIG_IGN;
725   sigaction(SIGPIPE, &act, NULL);
726
727   OPENSSL_config(NULL);
728   OpenSSL_add_all_algorithms();
729   SSL_load_error_strings();
730   SSL_library_init();
731
732   run(argv[1], argv[2], argv[3]);
733   return 0;
734 }