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