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(SSLv23_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 SSL_load_error_strings();
621 SSL_library_init();
622
623 run(argv[1]);
624 return 0;
625 }
626