2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2013 Tatsuhiro Tsujikawa
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:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
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.
26 * This program is written to show how to use nghttp2 API in C and
27 * intentionally made simple.
31 #endif /* HAVE_CONFIG_H */
37 #endif /* HAVE_UNISTD_H */
40 #endif /* HAVE_FCNTL_H */
41 #include <sys/types.h>
42 #ifdef HAVE_SYS_SOCKET_H
43 #include <sys/socket.h>
44 #endif /* HAVE_SYS_SOCKET_H */
47 #endif /* HAVE_NETDB_H */
48 #ifdef HAVE_NETINET_IN_H
49 #include <netinet/in.h>
50 #endif /* HAVE_NETINET_IN_H */
51 #include <netinet/tcp.h>
57 #include <nghttp2/nghttp2.h>
59 #include <openssl/ssl.h>
60 #include <openssl/err.h>
61 #include <openssl/conf.h>
63 enum { IO_NONE, WANT_READ, WANT_WRITE };
65 #define MAKE_NV(NAME, VALUE) \
67 (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \
68 NGHTTP2_NV_FLAG_NONE \
71 #define MAKE_NV_CS(NAME, VALUE) \
73 (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \
74 NGHTTP2_NV_FLAG_NONE \
79 nghttp2_session *session;
80 /* WANT_READ if SSL/TLS connection needs more input; or WANT_WRITE
81 if it needs more output; or IO_NONE. This is necessary because
82 SSL/TLS re-negotiation is possible at any time. nghttp2 API
83 offers similar functions like nghttp2_session_want_read() and
84 nghttp2_session_want_write() but they do not take into account
85 SSL/TSL connection. */
91 /* In this program, path contains query component as well. */
93 /* This is the concatenation of host and port with ":" in
96 /* Stream ID for this request. */
103 /* In this program, path contains query component as well. */
106 const char *hostport;
113 * Returns copy of string |s| with the length |len|. The returned
114 * string is NULL-terminated.
116 static char *strcopy(const char *s, size_t len) {
118 dst = malloc(len + 1);
125 * Prints error message |msg| and exit.
127 static void die(const char *msg) {
128 fprintf(stderr, "FATAL: %s\n", msg);
133 * Prints error containing the function name |func| and message |msg|
136 static void dief(const char *func, const char *msg) {
137 fprintf(stderr, "FATAL: %s: %s\n", func, msg);
142 * Prints error containing the function name |func| and error code
143 * |error_code| and exit.
145 static void diec(const char *func, int error_code) {
146 fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
147 nghttp2_strerror(error_code));
152 * The implementation of nghttp2_send_callback type. Here we write
153 * |data| with size |length| to the network and return the number of
154 * bytes actually written. See the documentation of
155 * nghttp2_send_callback for the details.
157 static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
158 size_t length, int flags _U_, void *user_data) {
159 struct Connection *connection;
161 connection = (struct Connection *)user_data;
162 connection->want_io = IO_NONE;
164 rv = SSL_write(connection->ssl, data, (int)length);
166 int err = SSL_get_error(connection->ssl, rv);
167 if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
168 connection->want_io =
169 (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
170 rv = NGHTTP2_ERR_WOULDBLOCK;
172 rv = NGHTTP2_ERR_CALLBACK_FAILURE;
179 * The implementation of nghttp2_recv_callback type. Here we read data
180 * from the network and write them in |buf|. The capacity of |buf| is
181 * |length| bytes. Returns the number of bytes stored in |buf|. See
182 * the documentation of nghttp2_recv_callback for the details.
184 static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf,
185 size_t length, int flags _U_, void *user_data) {
186 struct Connection *connection;
188 connection = (struct Connection *)user_data;
189 connection->want_io = IO_NONE;
191 rv = SSL_read(connection->ssl, buf, (int)length);
193 int err = SSL_get_error(connection->ssl, rv);
194 if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
195 connection->want_io =
196 (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
197 rv = NGHTTP2_ERR_WOULDBLOCK;
199 rv = NGHTTP2_ERR_CALLBACK_FAILURE;
201 } else if (rv == 0) {
202 rv = NGHTTP2_ERR_EOF;
207 static int on_frame_send_callback(nghttp2_session *session,
208 const nghttp2_frame *frame,
209 void *user_data _U_) {
211 switch (frame->hd.type) {
212 case NGHTTP2_HEADERS:
213 if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
214 const nghttp2_nv *nva = frame->headers.nva;
215 printf("[INFO] C ----------------------------> S (HEADERS)\n");
216 for (i = 0; i < frame->headers.nvlen; ++i) {
217 fwrite(nva[i].name, nva[i].namelen, 1, stdout);
219 fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
224 case NGHTTP2_RST_STREAM:
225 printf("[INFO] C ----------------------------> S (RST_STREAM)\n");
228 printf("[INFO] C ----------------------------> S (GOAWAY)\n");
234 static int on_frame_recv_callback(nghttp2_session *session,
235 const nghttp2_frame *frame,
236 void *user_data _U_) {
238 switch (frame->hd.type) {
239 case NGHTTP2_HEADERS:
240 if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
241 const nghttp2_nv *nva = frame->headers.nva;
243 req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
245 printf("[INFO] C <---------------------------- S (HEADERS)\n");
246 for (i = 0; i < frame->headers.nvlen; ++i) {
247 fwrite(nva[i].name, nva[i].namelen, 1, stdout);
249 fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
255 case NGHTTP2_RST_STREAM:
256 printf("[INFO] C <---------------------------- S (RST_STREAM)\n");
259 printf("[INFO] C <---------------------------- S (GOAWAY)\n");
266 * The implementation of nghttp2_on_stream_close_callback type. We use
267 * this function to know the response is fully received. Since we just
268 * fetch 1 resource in this program, after reception of the response,
269 * we submit GOAWAY and close the session.
271 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
272 uint32_t error_code _U_,
273 void *user_data _U_) {
275 req = nghttp2_session_get_stream_user_data(session, stream_id);
278 rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
281 diec("nghttp2_session_terminate_session", rv);
287 #define MAX_OUTLEN 4096
290 * The implementation of nghttp2_on_data_chunk_recv_callback type. We
291 * use this function to print the received response body.
293 static int on_data_chunk_recv_callback(nghttp2_session *session,
294 uint8_t flags _U_, int32_t stream_id,
295 const uint8_t *data, size_t len,
296 void *user_data _U_) {
298 req = nghttp2_session_get_stream_user_data(session, stream_id);
300 printf("[INFO] C <---------------------------- S (DATA chunk)\n"
302 (unsigned long int)len);
303 fwrite(data, 1, len, stdout);
310 * Setup callback functions. nghttp2 API offers many callback
311 * functions, but most of them are optional. The send_callback is
312 * always required. Since we use nghttp2_session_recv(), the
313 * recv_callback is also required.
315 static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
316 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
318 nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
320 nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
321 on_frame_send_callback);
323 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
324 on_frame_recv_callback);
326 nghttp2_session_callbacks_set_on_stream_close_callback(
327 callbacks, on_stream_close_callback);
329 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
330 callbacks, on_data_chunk_recv_callback);
334 * Callback function for TLS NPN. Since this program only supports
335 * HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
336 * library supports, we terminate program.
338 static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
339 unsigned char *outlen, const unsigned char *in,
340 unsigned int inlen, void *arg _U_) {
342 /* nghttp2_select_next_protocol() selects HTTP/2 protocol the
343 nghttp2 library supports. */
344 rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
346 die("Server did not advertise HTTP/2 protocol");
348 return SSL_TLSEXT_ERR_OK;
352 * Setup SSL/TLS context.
354 static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
355 /* Disable SSLv2 and enable all workarounds for buggy servers */
356 SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
357 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
358 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
359 /* Set NPN callback */
360 SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
363 static void ssl_handshake(SSL *ssl, int fd) {
365 if (SSL_set_fd(ssl, fd) == 0) {
366 dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
369 rv = SSL_connect(ssl);
371 dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
376 * Connects to the host |host| and port |port|. This function returns
377 * the file descriptor of the client socket.
379 static int connect_to(const char *host, uint16_t port) {
380 struct addrinfo hints;
383 char service[NI_MAXSERV];
384 struct addrinfo *res, *rp;
385 snprintf(service, sizeof(service), "%u", port);
386 memset(&hints, 0, sizeof(struct addrinfo));
387 hints.ai_family = AF_UNSPEC;
388 hints.ai_socktype = SOCK_STREAM;
389 rv = getaddrinfo(host, service, &hints, &res);
391 dief("getaddrinfo", gai_strerror(rv));
393 for (rp = res; rp; rp = rp->ai_next) {
394 fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
398 while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
411 static void make_non_block(int fd) {
413 while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
416 dief("fcntl", strerror(errno));
418 while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
421 dief("fcntl", strerror(errno));
425 static void set_tcp_nodelay(int fd) {
428 rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
430 dief("setsockopt", strerror(errno));
435 * Update |pollfd| based on the state of |connection|.
437 static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
439 if (nghttp2_session_want_read(connection->session) ||
440 connection->want_io == WANT_READ) {
441 pollfd->events |= POLLIN;
443 if (nghttp2_session_want_write(connection->session) ||
444 connection->want_io == WANT_WRITE) {
445 pollfd->events |= POLLOUT;
450 * Submits the request |req| to the connection |connection|. This
451 * function does not send packets; just append the request to the
452 * internal queue in |connection->session|.
454 static void submit_request(struct Connection *connection, struct Request *req) {
456 /* Make sure that the last item is NULL */
457 const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"),
458 MAKE_NV_CS(":path", req->path),
459 MAKE_NV(":scheme", "https"),
460 MAKE_NV_CS(":authority", req->hostport),
461 MAKE_NV("accept", "*/*"),
462 MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
464 stream_id = nghttp2_submit_request(connection->session, NULL, nva,
465 sizeof(nva) / sizeof(nva[0]), NULL, req);
468 diec("nghttp2_submit_request", stream_id);
471 req->stream_id = stream_id;
472 printf("[INFO] Stream ID = %d\n", stream_id);
476 * Performs the network I/O.
478 static void exec_io(struct Connection *connection) {
480 rv = nghttp2_session_recv(connection->session);
482 diec("nghttp2_session_recv", rv);
484 rv = nghttp2_session_send(connection->session);
486 diec("nghttp2_session_send", rv);
490 static void request_init(struct Request *req, const struct URI *uri) {
491 req->host = strcopy(uri->host, uri->hostlen);
492 req->port = uri->port;
493 req->path = strcopy(uri->path, uri->pathlen);
494 req->hostport = strcopy(uri->hostport, uri->hostportlen);
498 static void request_free(struct Request *req) {
505 * Fetches the resource denoted by |uri|.
507 static void fetch_uri(const struct URI *uri) {
508 nghttp2_session_callbacks *callbacks;
513 struct Connection connection;
516 struct pollfd pollfds[1];
518 request_init(&req, uri);
520 /* Establish connection and setup SSL */
521 fd = connect_to(req.host, req.port);
523 die("Could not open file descriptor");
525 ssl_ctx = SSL_CTX_new(SSLv23_client_method());
526 if (ssl_ctx == NULL) {
527 dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
529 init_ssl_ctx(ssl_ctx);
530 ssl = SSL_new(ssl_ctx);
532 dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
534 /* To simplify the program, we perform SSL/TLS handshake in blocking
536 ssl_handshake(ssl, fd);
538 connection.ssl = ssl;
539 connection.want_io = IO_NONE;
541 /* Here make file descriptor non-block */
545 printf("[INFO] SSL/TLS handshake completed\n");
547 rv = nghttp2_session_callbacks_new(&callbacks);
550 diec("nghttp2_session_callbacks_new", rv);
553 setup_nghttp2_callbacks(callbacks);
555 rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);
557 nghttp2_session_callbacks_del(callbacks);
560 diec("nghttp2_session_client_new", rv);
563 nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
565 /* Submit the HTTP request to the outbound queue. */
566 submit_request(&connection, &req);
569 ctl_poll(pollfds, &connection);
572 while (nghttp2_session_want_read(connection.session) ||
573 nghttp2_session_want_write(connection.session)) {
574 int nfds = poll(pollfds, npollfds, -1);
576 dief("poll", strerror(errno));
578 if (pollfds[0].revents & (POLLIN | POLLOUT)) {
579 exec_io(&connection);
581 if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
582 die("Connection error");
584 ctl_poll(pollfds, &connection);
587 /* Resource cleanup */
588 nghttp2_session_del(connection.session);
591 SSL_CTX_free(ssl_ctx);
592 shutdown(fd, SHUT_WR);
597 static int parse_uri(struct URI *res, const char *uri) {
598 /* We only interested in https */
599 size_t len, i, offset;
601 memset(res, 0, sizeof(struct URI));
603 if (len < 9 || memcmp("https://", uri, 8) != 0) {
607 res->host = res->hostport = &uri[offset];
609 if (uri[offset] == '[') {
610 /* IPv6 literal address */
614 for (i = offset; i < len; ++i) {
616 res->hostlen = i - offset;
622 const char delims[] = ":/?#";
623 for (i = offset; i < len; ++i) {
624 if (strchr(delims, uri[i]) != NULL) {
628 res->hostlen = i - offset;
631 if (res->hostlen == 0) {
637 if (uri[offset] == ':') {
639 const char delims[] = "/?#";
642 for (i = offset; i < len; ++i) {
643 if (strchr(delims, uri[i]) != NULL) {
646 if ('0' <= uri[i] && uri[i] <= '9') {
648 port += uri[i] - '0';
663 res->hostportlen = uri + offset + ipv6addr - res->host;
664 for (i = offset; i < len; ++i) {
669 if (i - offset == 0) {
673 res->path = &uri[offset];
674 res->pathlen = i - offset;
679 int main(int argc, char **argv) {
681 struct sigaction act;
685 die("Specify a https URI");
688 memset(&act, 0, sizeof(struct sigaction));
689 act.sa_handler = SIG_IGN;
690 sigaction(SIGPIPE, &act, 0);
692 OPENSSL_config(NULL);
693 OpenSSL_add_all_algorithms();
694 SSL_load_error_strings();
697 rv = parse_uri(&uri, argv[1]);
699 die("parse_uri failed");