• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 __sgi
26 #  include <string.h>
27 #  define errx(exitcode, format, args...)                                      \
28     {                                                                          \
29       warnx(format, ##args);                                                   \
30       exit(exitcode);                                                          \
31     }
32 #  define warnx(format, args...) fprintf(stderr, format "\n", ##args)
33 char *strndup(const char *s, size_t size);
34 #endif
35 
36 #ifdef HAVE_CONFIG_H
37 #  include <config.h>
38 #endif /* HAVE_CONFIG_H */
39 
40 #include <sys/types.h>
41 #ifdef HAVE_UNISTD_H
42 #  include <unistd.h>
43 #endif /* HAVE_UNISTD_H */
44 #ifdef HAVE_SYS_SOCKET_H
45 #  include <sys/socket.h>
46 #endif /* HAVE_SYS_SOCKET_H */
47 #ifdef HAVE_NETINET_IN_H
48 #  include <netinet/in.h>
49 #endif /* HAVE_NETINET_IN_H */
50 #include <netinet/tcp.h>
51 #ifndef __sgi
52 #  include <err.h>
53 #endif
54 #include <signal.h>
55 #include <string.h>
56 
57 #include <openssl/ssl.h>
58 #include <openssl/err.h>
59 #include <openssl/conf.h>
60 
61 #include <event.h>
62 #include <event2/event.h>
63 #include <event2/bufferevent_ssl.h>
64 #include <event2/dns.h>
65 
66 #define NGHTTP2_NO_SSIZE_T
67 #include <nghttp2/nghttp2.h>
68 
69 #include "url-parser/url_parser.h"
70 
71 #define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
72 
73 typedef struct {
74   /* The NULL-terminated URI string to retrieve. */
75   const char *uri;
76   /* Parsed result of the |uri| */
77   struct http_parser_url *u;
78   /* The authority portion of the |uri|, not NULL-terminated */
79   char *authority;
80   /* The path portion of the |uri|, including query, not
81      NULL-terminated */
82   char *path;
83   /* The length of the |authority| */
84   size_t authoritylen;
85   /* The length of the |path| */
86   size_t pathlen;
87   /* The stream ID of this stream */
88   int32_t stream_id;
89 } http2_stream_data;
90 
91 typedef struct {
92   nghttp2_session *session;
93   struct evdns_base *dnsbase;
94   struct bufferevent *bev;
95   http2_stream_data *stream_data;
96 } http2_session_data;
97 
create_http2_stream_data(const char * uri,struct http_parser_url * u)98 static http2_stream_data *create_http2_stream_data(const char *uri,
99                                                    struct http_parser_url *u) {
100   /* MAX 5 digits (max 65535) + 1 ':' + 1 NULL (because of snprintf) */
101   size_t extra = 7;
102   http2_stream_data *stream_data = malloc(sizeof(http2_stream_data));
103 
104   stream_data->uri = uri;
105   stream_data->u = u;
106   stream_data->stream_id = -1;
107 
108   stream_data->authoritylen = u->field_data[UF_HOST].len;
109   stream_data->authority = malloc(stream_data->authoritylen + extra);
110   memcpy(stream_data->authority, &uri[u->field_data[UF_HOST].off],
111          u->field_data[UF_HOST].len);
112   if (u->field_set & (1 << UF_PORT)) {
113     stream_data->authoritylen +=
114       (size_t)snprintf(stream_data->authority + u->field_data[UF_HOST].len,
115                        extra, ":%u", u->port);
116   }
117 
118   /* If we don't have path in URI, we use "/" as path. */
119   stream_data->pathlen = 1;
120   if (u->field_set & (1 << UF_PATH)) {
121     stream_data->pathlen = u->field_data[UF_PATH].len;
122   }
123   if (u->field_set & (1 << UF_QUERY)) {
124     /* +1 for '?' character */
125     stream_data->pathlen += (size_t)(u->field_data[UF_QUERY].len + 1);
126   }
127 
128   stream_data->path = malloc(stream_data->pathlen);
129   if (u->field_set & (1 << UF_PATH)) {
130     memcpy(stream_data->path, &uri[u->field_data[UF_PATH].off],
131            u->field_data[UF_PATH].len);
132   } else {
133     stream_data->path[0] = '/';
134   }
135   if (u->field_set & (1 << UF_QUERY)) {
136     stream_data->path[stream_data->pathlen - u->field_data[UF_QUERY].len - 1] =
137       '?';
138     memcpy(stream_data->path + stream_data->pathlen -
139              u->field_data[UF_QUERY].len,
140            &uri[u->field_data[UF_QUERY].off], u->field_data[UF_QUERY].len);
141   }
142 
143   return stream_data;
144 }
145 
delete_http2_stream_data(http2_stream_data * stream_data)146 static void delete_http2_stream_data(http2_stream_data *stream_data) {
147   free(stream_data->path);
148   free(stream_data->authority);
149   free(stream_data);
150 }
151 
152 /* Initializes |session_data| */
153 static http2_session_data *
create_http2_session_data(struct event_base * evbase)154 create_http2_session_data(struct event_base *evbase) {
155   http2_session_data *session_data = malloc(sizeof(http2_session_data));
156 
157   memset(session_data, 0, sizeof(http2_session_data));
158   session_data->dnsbase = evdns_base_new(evbase, 1);
159   return session_data;
160 }
161 
delete_http2_session_data(http2_session_data * session_data)162 static void delete_http2_session_data(http2_session_data *session_data) {
163   SSL *ssl = bufferevent_openssl_get_ssl(session_data->bev);
164 
165   if (ssl) {
166     SSL_shutdown(ssl);
167   }
168   bufferevent_free(session_data->bev);
169   session_data->bev = NULL;
170   evdns_base_free(session_data->dnsbase, 1);
171   session_data->dnsbase = NULL;
172   nghttp2_session_del(session_data->session);
173   session_data->session = NULL;
174   if (session_data->stream_data) {
175     delete_http2_stream_data(session_data->stream_data);
176     session_data->stream_data = NULL;
177   }
178   free(session_data);
179 }
180 
print_header(FILE * f,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen)181 static void print_header(FILE *f, const uint8_t *name, size_t namelen,
182                          const uint8_t *value, size_t valuelen) {
183   fwrite(name, 1, namelen, f);
184   fprintf(f, ": ");
185   fwrite(value, 1, valuelen, f);
186   fprintf(f, "\n");
187 }
188 
189 /* Print HTTP headers to |f|. Please note that this function does not
190    take into account that header name and value are sequence of
191    octets, therefore they may contain non-printable characters. */
print_headers(FILE * f,nghttp2_nv * nva,size_t nvlen)192 static void print_headers(FILE *f, nghttp2_nv *nva, size_t nvlen) {
193   size_t i;
194   for (i = 0; i < nvlen; ++i) {
195     print_header(f, nva[i].name, nva[i].namelen, nva[i].value, nva[i].valuelen);
196   }
197   fprintf(f, "\n");
198 }
199 
200 /* nghttp2_send_callback2. Here we transmit the |data|, |length|
201    bytes, to the network. Because we are using libevent bufferevent,
202    we just write those bytes into bufferevent buffer. */
send_callback(nghttp2_session * session,const uint8_t * data,size_t length,int flags,void * user_data)203 static nghttp2_ssize send_callback(nghttp2_session *session,
204                                    const uint8_t *data, size_t length,
205                                    int flags, void *user_data) {
206   http2_session_data *session_data = (http2_session_data *)user_data;
207   struct bufferevent *bev = session_data->bev;
208   (void)session;
209   (void)flags;
210 
211   bufferevent_write(bev, data, length);
212   return (nghttp2_ssize)length;
213 }
214 
215 /* nghttp2_on_header_callback: Called when nghttp2 library emits
216    single header name/value pair. */
on_header_callback(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * user_data)217 static int on_header_callback(nghttp2_session *session,
218                               const nghttp2_frame *frame, const uint8_t *name,
219                               size_t namelen, const uint8_t *value,
220                               size_t valuelen, uint8_t flags, void *user_data) {
221   http2_session_data *session_data = (http2_session_data *)user_data;
222   (void)session;
223   (void)flags;
224 
225   switch (frame->hd.type) {
226   case NGHTTP2_HEADERS:
227     if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
228         session_data->stream_data->stream_id == frame->hd.stream_id) {
229       /* Print response headers for the initiated request. */
230       print_header(stderr, name, namelen, value, valuelen);
231       break;
232     }
233   }
234   return 0;
235 }
236 
237 /* nghttp2_on_begin_headers_callback: Called when nghttp2 library gets
238    started to receive header block. */
on_begin_headers_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)239 static int on_begin_headers_callback(nghttp2_session *session,
240                                      const nghttp2_frame *frame,
241                                      void *user_data) {
242   http2_session_data *session_data = (http2_session_data *)user_data;
243   (void)session;
244 
245   switch (frame->hd.type) {
246   case NGHTTP2_HEADERS:
247     if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
248         session_data->stream_data->stream_id == frame->hd.stream_id) {
249       fprintf(stderr, "Response headers for stream ID=%d:\n",
250               frame->hd.stream_id);
251     }
252     break;
253   }
254   return 0;
255 }
256 
257 /* nghttp2_on_frame_recv_callback: Called when nghttp2 library
258    received a complete frame from the remote peer. */
on_frame_recv_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)259 static int on_frame_recv_callback(nghttp2_session *session,
260                                   const nghttp2_frame *frame, void *user_data) {
261   http2_session_data *session_data = (http2_session_data *)user_data;
262   (void)session;
263 
264   switch (frame->hd.type) {
265   case NGHTTP2_HEADERS:
266     if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE &&
267         session_data->stream_data->stream_id == frame->hd.stream_id) {
268       fprintf(stderr, "All headers received\n");
269     }
270     break;
271   }
272   return 0;
273 }
274 
275 /* nghttp2_on_data_chunk_recv_callback: Called when DATA frame is
276    received from the remote peer. In this implementation, if the frame
277    is meant to the stream we initiated, print the received data in
278    stdout, so that the user can redirect its output to the file
279    easily. */
on_data_chunk_recv_callback(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)280 static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
281                                        int32_t stream_id, const uint8_t *data,
282                                        size_t len, void *user_data) {
283   http2_session_data *session_data = (http2_session_data *)user_data;
284   (void)session;
285   (void)flags;
286 
287   if (session_data->stream_data->stream_id == stream_id) {
288     fwrite(data, 1, len, stdout);
289   }
290   return 0;
291 }
292 
293 /* nghttp2_on_stream_close_callback: Called when a stream is about to
294    closed. This example program only deals with 1 HTTP request (1
295    stream), if it is closed, we send GOAWAY and tear down the
296    session */
on_stream_close_callback(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * user_data)297 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
298                                     uint32_t error_code, void *user_data) {
299   http2_session_data *session_data = (http2_session_data *)user_data;
300   int rv;
301 
302   if (session_data->stream_data->stream_id == stream_id) {
303     fprintf(stderr, "Stream %d closed with error_code=%u\n", stream_id,
304             error_code);
305     rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
306     if (rv != 0) {
307       return NGHTTP2_ERR_CALLBACK_FAILURE;
308     }
309   }
310   return 0;
311 }
312 
313 /* Create SSL_CTX. */
create_ssl_ctx(void)314 static SSL_CTX *create_ssl_ctx(void) {
315   SSL_CTX *ssl_ctx;
316   ssl_ctx = SSL_CTX_new(TLS_client_method());
317   if (!ssl_ctx) {
318     errx(1, "Could not create SSL/TLS context: %s",
319          ERR_error_string(ERR_get_error(), NULL));
320   }
321   SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
322                                  SSL_OP_NO_COMPRESSION |
323                                  SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
324 
325   SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3);
326 
327   return ssl_ctx;
328 }
329 
330 /* Create SSL object */
create_ssl(SSL_CTX * ssl_ctx)331 static SSL *create_ssl(SSL_CTX *ssl_ctx) {
332   SSL *ssl;
333   ssl = SSL_new(ssl_ctx);
334   if (!ssl) {
335     errx(1, "Could not create SSL/TLS session object: %s",
336          ERR_error_string(ERR_get_error(), NULL));
337   }
338   return ssl;
339 }
340 
initialize_nghttp2_session(http2_session_data * session_data)341 static void initialize_nghttp2_session(http2_session_data *session_data) {
342   nghttp2_session_callbacks *callbacks;
343 
344   nghttp2_session_callbacks_new(&callbacks);
345 
346   nghttp2_session_callbacks_set_send_callback2(callbacks, send_callback);
347 
348   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
349                                                        on_frame_recv_callback);
350 
351   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
352     callbacks, on_data_chunk_recv_callback);
353 
354   nghttp2_session_callbacks_set_on_stream_close_callback(
355     callbacks, on_stream_close_callback);
356 
357   nghttp2_session_callbacks_set_on_header_callback(callbacks,
358                                                    on_header_callback);
359 
360   nghttp2_session_callbacks_set_on_begin_headers_callback(
361     callbacks, on_begin_headers_callback);
362 
363   nghttp2_session_client_new(&session_data->session, callbacks, session_data);
364 
365   nghttp2_session_callbacks_del(callbacks);
366 }
367 
send_client_connection_header(http2_session_data * session_data)368 static void send_client_connection_header(http2_session_data *session_data) {
369   nghttp2_settings_entry iv[1] = {
370     {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
371   int rv;
372 
373   /* client 24 bytes magic string will be sent by nghttp2 library */
374   rv = nghttp2_submit_settings(session_data->session, NGHTTP2_FLAG_NONE, iv,
375                                ARRLEN(iv));
376   if (rv != 0) {
377     errx(1, "Could not submit SETTINGS: %s", nghttp2_strerror(rv));
378   }
379 }
380 
381 #define MAKE_NV(NAME, VALUE, VALUELEN)                                         \
382   {                                                                            \
383     (uint8_t *)NAME, (uint8_t *)VALUE,     sizeof(NAME) - 1,                   \
384     VALUELEN,        NGHTTP2_NV_FLAG_NONE,                                     \
385   }
386 
387 #define MAKE_NV2(NAME, VALUE)                                                  \
388   {                                                                            \
389     (uint8_t *)NAME,   (uint8_t *)VALUE,     sizeof(NAME) - 1,                 \
390     sizeof(VALUE) - 1, NGHTTP2_NV_FLAG_NONE,                                   \
391   }
392 
393 /* Send HTTP request to the remote peer */
submit_request(http2_session_data * session_data)394 static void submit_request(http2_session_data *session_data) {
395   int32_t stream_id;
396   http2_stream_data *stream_data = session_data->stream_data;
397   const char *uri = stream_data->uri;
398   const struct http_parser_url *u = stream_data->u;
399   nghttp2_nv hdrs[] = {
400     MAKE_NV2(":method", "GET"),
401     MAKE_NV(":scheme", &uri[u->field_data[UF_SCHEMA].off],
402             u->field_data[UF_SCHEMA].len),
403     MAKE_NV(":authority", stream_data->authority, stream_data->authoritylen),
404     MAKE_NV(":path", stream_data->path, stream_data->pathlen)};
405   fprintf(stderr, "Request headers:\n");
406   print_headers(stderr, hdrs, ARRLEN(hdrs));
407   stream_id = nghttp2_submit_request2(session_data->session, NULL, hdrs,
408                                       ARRLEN(hdrs), NULL, stream_data);
409   if (stream_id < 0) {
410     errx(1, "Could not submit HTTP request: %s", nghttp2_strerror(stream_id));
411   }
412 
413   stream_data->stream_id = stream_id;
414 }
415 
416 /* Serialize the frame and send (or buffer) the data to
417    bufferevent. */
session_send(http2_session_data * session_data)418 static int session_send(http2_session_data *session_data) {
419   int rv;
420 
421   rv = nghttp2_session_send(session_data->session);
422   if (rv != 0) {
423     warnx("Fatal error: %s", nghttp2_strerror(rv));
424     return -1;
425   }
426   return 0;
427 }
428 
429 /* readcb for bufferevent. Here we get the data from the input buffer
430    of bufferevent and feed them to nghttp2 library. This may invoke
431    nghttp2 callbacks. It may also queues the frame in nghttp2 session
432    context. To send them, we call session_send() in the end. */
readcb(struct bufferevent * bev,void * ptr)433 static void readcb(struct bufferevent *bev, void *ptr) {
434   http2_session_data *session_data = (http2_session_data *)ptr;
435   nghttp2_ssize readlen;
436   struct evbuffer *input = bufferevent_get_input(bev);
437   size_t datalen = evbuffer_get_length(input);
438   unsigned char *data = evbuffer_pullup(input, -1);
439 
440   readlen = nghttp2_session_mem_recv2(session_data->session, data, datalen);
441   if (readlen < 0) {
442     warnx("Fatal error: %s", nghttp2_strerror((int)readlen));
443     delete_http2_session_data(session_data);
444     return;
445   }
446   if (evbuffer_drain(input, (size_t)readlen) != 0) {
447     warnx("Fatal error: evbuffer_drain failed");
448     delete_http2_session_data(session_data);
449     return;
450   }
451   if (session_send(session_data) != 0) {
452     delete_http2_session_data(session_data);
453     return;
454   }
455 }
456 
457 /* writecb for bufferevent. To greaceful shutdown after sending or
458    receiving GOAWAY, we check the some conditions on the nghttp2
459    library and output buffer of bufferevent. If it indicates we have
460    no business to this session, tear down the connection. */
writecb(struct bufferevent * bev,void * ptr)461 static void writecb(struct bufferevent *bev, void *ptr) {
462   http2_session_data *session_data = (http2_session_data *)ptr;
463   (void)bev;
464 
465   if (nghttp2_session_want_read(session_data->session) == 0 &&
466       nghttp2_session_want_write(session_data->session) == 0 &&
467       evbuffer_get_length(bufferevent_get_output(session_data->bev)) == 0) {
468     delete_http2_session_data(session_data);
469   }
470 }
471 
472 /* eventcb for bufferevent. For the purpose of simplicity and
473    readability of the example program, we omitted the certificate and
474    peer verification. After SSL/TLS handshake is over, initialize
475    nghttp2 library session, and send client connection header. Then
476    send HTTP request. */
eventcb(struct bufferevent * bev,short events,void * ptr)477 static void eventcb(struct bufferevent *bev, short events, void *ptr) {
478   http2_session_data *session_data = (http2_session_data *)ptr;
479   if (events & BEV_EVENT_CONNECTED) {
480     int fd = bufferevent_getfd(bev);
481     int val = 1;
482     const unsigned char *alpn = NULL;
483     unsigned int alpnlen = 0;
484     SSL *ssl;
485 
486     fprintf(stderr, "Connected\n");
487 
488     ssl = bufferevent_openssl_get_ssl(session_data->bev);
489 
490     if (alpn == NULL) {
491       SSL_get0_alpn_selected(ssl, &alpn, &alpnlen);
492     }
493 
494     if (alpn == NULL || alpnlen != 2 || memcmp("h2", alpn, 2) != 0) {
495       fprintf(stderr, "h2 is not negotiated\n");
496       delete_http2_session_data(session_data);
497       return;
498     }
499 
500     setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val, sizeof(val));
501     initialize_nghttp2_session(session_data);
502     send_client_connection_header(session_data);
503     submit_request(session_data);
504     if (session_send(session_data) != 0) {
505       delete_http2_session_data(session_data);
506     }
507     return;
508   }
509   if (events & BEV_EVENT_EOF) {
510     warnx("Disconnected from the remote host");
511   } else if (events & BEV_EVENT_ERROR) {
512     warnx("Network error");
513   } else if (events & BEV_EVENT_TIMEOUT) {
514     warnx("Timeout");
515   }
516   delete_http2_session_data(session_data);
517 }
518 
519 /* Start connecting to the remote peer |host:port| */
initiate_connection(struct event_base * evbase,SSL_CTX * ssl_ctx,const char * host,uint16_t port,http2_session_data * session_data)520 static void initiate_connection(struct event_base *evbase, SSL_CTX *ssl_ctx,
521                                 const char *host, uint16_t port,
522                                 http2_session_data *session_data) {
523   int rv;
524   struct bufferevent *bev;
525   SSL *ssl;
526 
527   ssl = create_ssl(ssl_ctx);
528   bev = bufferevent_openssl_socket_new(
529     evbase, -1, ssl, BUFFEREVENT_SSL_CONNECTING,
530     BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE);
531   bufferevent_enable(bev, EV_READ | EV_WRITE);
532   bufferevent_setcb(bev, readcb, writecb, eventcb, session_data);
533   rv = bufferevent_socket_connect_hostname(bev, session_data->dnsbase,
534                                            AF_UNSPEC, host, port);
535 
536   if (rv != 0) {
537     errx(1, "Could not connect to the remote host %s", host);
538   }
539   session_data->bev = bev;
540 }
541 
542 /* Get resource denoted by the |uri|. The debug and error messages are
543    printed in stderr, while the response body is printed in stdout. */
run(const char * uri)544 static void run(const char *uri) {
545   struct http_parser_url u;
546   char *host;
547   uint16_t port;
548   int rv;
549   SSL_CTX *ssl_ctx;
550   struct event_base *evbase;
551   http2_session_data *session_data;
552 
553   /* Parse the |uri| and stores its components in |u| */
554   rv = http_parser_parse_url(uri, strlen(uri), 0, &u);
555   if (rv != 0) {
556     errx(1, "Could not parse URI %s", uri);
557   }
558   host = strndup(&uri[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len);
559   if (!(u.field_set & (1 << UF_PORT))) {
560     port = 443;
561   } else {
562     port = u.port;
563   }
564 
565   ssl_ctx = create_ssl_ctx();
566 
567   evbase = event_base_new();
568 
569   session_data = create_http2_session_data(evbase);
570   session_data->stream_data = create_http2_stream_data(uri, &u);
571 
572   initiate_connection(evbase, ssl_ctx, host, port, session_data);
573   free(host);
574   host = NULL;
575 
576   event_base_loop(evbase, 0);
577 
578   event_base_free(evbase);
579   SSL_CTX_free(ssl_ctx);
580 }
581 
main(int argc,char ** argv)582 int main(int argc, char **argv) {
583   struct sigaction act;
584 
585   if (argc < 2) {
586     fprintf(stderr, "Usage: libevent-client HTTPS_URI\n");
587     exit(EXIT_FAILURE);
588   }
589 
590   memset(&act, 0, sizeof(struct sigaction));
591   act.sa_handler = SIG_IGN;
592   sigaction(SIGPIPE, &act, NULL);
593 
594   run(argv[1]);
595   return 0;
596 }
597