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