12c593315Sopenharmony_ci/* 22c593315Sopenharmony_ci * nghttp2 - HTTP/2 C Library 32c593315Sopenharmony_ci * 42c593315Sopenharmony_ci * Copyright (c) 2013 Tatsuhiro Tsujikawa 52c593315Sopenharmony_ci * 62c593315Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining 72c593315Sopenharmony_ci * a copy of this software and associated documentation files (the 82c593315Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 92c593315Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 102c593315Sopenharmony_ci * distribute, sublicense, and/or sell copies of the Software, and to 112c593315Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 122c593315Sopenharmony_ci * the following conditions: 132c593315Sopenharmony_ci * 142c593315Sopenharmony_ci * The above copyright notice and this permission notice shall be 152c593315Sopenharmony_ci * included in all copies or substantial portions of the Software. 162c593315Sopenharmony_ci * 172c593315Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 182c593315Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 192c593315Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 202c593315Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 212c593315Sopenharmony_ci * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 222c593315Sopenharmony_ci * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 232c593315Sopenharmony_ci * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 242c593315Sopenharmony_ci */ 252c593315Sopenharmony_ci/* 262c593315Sopenharmony_ci * This program is written to show how to use nghttp2 API in C and 272c593315Sopenharmony_ci * intentionally made simple. 282c593315Sopenharmony_ci */ 292c593315Sopenharmony_ci#ifdef HAVE_CONFIG_H 302c593315Sopenharmony_ci# include <config.h> 312c593315Sopenharmony_ci#endif /* HAVE_CONFIG_H */ 322c593315Sopenharmony_ci 332c593315Sopenharmony_ci#include <inttypes.h> 342c593315Sopenharmony_ci#include <stdlib.h> 352c593315Sopenharmony_ci#ifdef HAVE_UNISTD_H 362c593315Sopenharmony_ci# include <unistd.h> 372c593315Sopenharmony_ci#endif /* HAVE_UNISTD_H */ 382c593315Sopenharmony_ci#ifdef HAVE_FCNTL_H 392c593315Sopenharmony_ci# include <fcntl.h> 402c593315Sopenharmony_ci#endif /* HAVE_FCNTL_H */ 412c593315Sopenharmony_ci#include <sys/types.h> 422c593315Sopenharmony_ci#ifdef HAVE_SYS_SOCKET_H 432c593315Sopenharmony_ci# include <sys/socket.h> 442c593315Sopenharmony_ci#endif /* HAVE_SYS_SOCKET_H */ 452c593315Sopenharmony_ci#ifdef HAVE_NETDB_H 462c593315Sopenharmony_ci# include <netdb.h> 472c593315Sopenharmony_ci#endif /* HAVE_NETDB_H */ 482c593315Sopenharmony_ci#ifdef HAVE_NETINET_IN_H 492c593315Sopenharmony_ci# include <netinet/in.h> 502c593315Sopenharmony_ci#endif /* HAVE_NETINET_IN_H */ 512c593315Sopenharmony_ci#include <netinet/tcp.h> 522c593315Sopenharmony_ci#include <poll.h> 532c593315Sopenharmony_ci#include <signal.h> 542c593315Sopenharmony_ci#include <stdio.h> 552c593315Sopenharmony_ci#include <assert.h> 562c593315Sopenharmony_ci#include <string.h> 572c593315Sopenharmony_ci#include <errno.h> 582c593315Sopenharmony_ci 592c593315Sopenharmony_ci#include <nghttp2/nghttp2.h> 602c593315Sopenharmony_ci 612c593315Sopenharmony_ci#include <openssl/ssl.h> 622c593315Sopenharmony_ci#include <openssl/err.h> 632c593315Sopenharmony_ci#include <openssl/conf.h> 642c593315Sopenharmony_ci 652c593315Sopenharmony_cienum { IO_NONE, WANT_READ, WANT_WRITE }; 662c593315Sopenharmony_ci 672c593315Sopenharmony_ci#define MAKE_NV(NAME, VALUE) \ 682c593315Sopenharmony_ci { \ 692c593315Sopenharmony_ci (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1, \ 702c593315Sopenharmony_ci NGHTTP2_NV_FLAG_NONE \ 712c593315Sopenharmony_ci } 722c593315Sopenharmony_ci 732c593315Sopenharmony_ci#define MAKE_NV_CS(NAME, VALUE) \ 742c593315Sopenharmony_ci { \ 752c593315Sopenharmony_ci (uint8_t *)NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE), \ 762c593315Sopenharmony_ci NGHTTP2_NV_FLAG_NONE \ 772c593315Sopenharmony_ci } 782c593315Sopenharmony_ci 792c593315Sopenharmony_cistruct Connection { 802c593315Sopenharmony_ci SSL *ssl; 812c593315Sopenharmony_ci nghttp2_session *session; 822c593315Sopenharmony_ci /* WANT_READ if SSL/TLS connection needs more input; or WANT_WRITE 832c593315Sopenharmony_ci if it needs more output; or IO_NONE. This is necessary because 842c593315Sopenharmony_ci SSL/TLS re-negotiation is possible at any time. nghttp2 API 852c593315Sopenharmony_ci offers similar functions like nghttp2_session_want_read() and 862c593315Sopenharmony_ci nghttp2_session_want_write() but they do not take into account 872c593315Sopenharmony_ci SSL/TSL connection. */ 882c593315Sopenharmony_ci int want_io; 892c593315Sopenharmony_ci}; 902c593315Sopenharmony_ci 912c593315Sopenharmony_cistruct Request { 922c593315Sopenharmony_ci char *host; 932c593315Sopenharmony_ci /* In this program, path contains query component as well. */ 942c593315Sopenharmony_ci char *path; 952c593315Sopenharmony_ci /* This is the concatenation of host and port with ":" in 962c593315Sopenharmony_ci between. */ 972c593315Sopenharmony_ci char *hostport; 982c593315Sopenharmony_ci /* Stream ID for this request. */ 992c593315Sopenharmony_ci int32_t stream_id; 1002c593315Sopenharmony_ci uint16_t port; 1012c593315Sopenharmony_ci}; 1022c593315Sopenharmony_ci 1032c593315Sopenharmony_cistruct URI { 1042c593315Sopenharmony_ci const char *host; 1052c593315Sopenharmony_ci /* In this program, path contains query component as well. */ 1062c593315Sopenharmony_ci const char *path; 1072c593315Sopenharmony_ci size_t pathlen; 1082c593315Sopenharmony_ci const char *hostport; 1092c593315Sopenharmony_ci size_t hostlen; 1102c593315Sopenharmony_ci size_t hostportlen; 1112c593315Sopenharmony_ci uint16_t port; 1122c593315Sopenharmony_ci}; 1132c593315Sopenharmony_ci 1142c593315Sopenharmony_ci/* 1152c593315Sopenharmony_ci * Returns copy of string |s| with the length |len|. The returned 1162c593315Sopenharmony_ci * string is NULL-terminated. 1172c593315Sopenharmony_ci */ 1182c593315Sopenharmony_cistatic char *strcopy(const char *s, size_t len) { 1192c593315Sopenharmony_ci char *dst; 1202c593315Sopenharmony_ci dst = malloc(len + 1); 1212c593315Sopenharmony_ci memcpy(dst, s, len); 1222c593315Sopenharmony_ci dst[len] = '\0'; 1232c593315Sopenharmony_ci return dst; 1242c593315Sopenharmony_ci} 1252c593315Sopenharmony_ci 1262c593315Sopenharmony_ci/* 1272c593315Sopenharmony_ci * Prints error message |msg| and exit. 1282c593315Sopenharmony_ci */ 1292c593315Sopenharmony_ciNGHTTP2_NORETURN 1302c593315Sopenharmony_cistatic void die(const char *msg) { 1312c593315Sopenharmony_ci fprintf(stderr, "FATAL: %s\n", msg); 1322c593315Sopenharmony_ci exit(EXIT_FAILURE); 1332c593315Sopenharmony_ci} 1342c593315Sopenharmony_ci 1352c593315Sopenharmony_ci/* 1362c593315Sopenharmony_ci * Prints error containing the function name |func| and message |msg| 1372c593315Sopenharmony_ci * and exit. 1382c593315Sopenharmony_ci */ 1392c593315Sopenharmony_ciNGHTTP2_NORETURN 1402c593315Sopenharmony_cistatic void dief(const char *func, const char *msg) { 1412c593315Sopenharmony_ci fprintf(stderr, "FATAL: %s: %s\n", func, msg); 1422c593315Sopenharmony_ci exit(EXIT_FAILURE); 1432c593315Sopenharmony_ci} 1442c593315Sopenharmony_ci 1452c593315Sopenharmony_ci/* 1462c593315Sopenharmony_ci * Prints error containing the function name |func| and error code 1472c593315Sopenharmony_ci * |error_code| and exit. 1482c593315Sopenharmony_ci */ 1492c593315Sopenharmony_ciNGHTTP2_NORETURN 1502c593315Sopenharmony_cistatic void diec(const char *func, int error_code) { 1512c593315Sopenharmony_ci fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, 1522c593315Sopenharmony_ci nghttp2_strerror(error_code)); 1532c593315Sopenharmony_ci exit(EXIT_FAILURE); 1542c593315Sopenharmony_ci} 1552c593315Sopenharmony_ci 1562c593315Sopenharmony_ci/* 1572c593315Sopenharmony_ci * The implementation of nghttp2_send_callback type. Here we write 1582c593315Sopenharmony_ci * |data| with size |length| to the network and return the number of 1592c593315Sopenharmony_ci * bytes actually written. See the documentation of 1602c593315Sopenharmony_ci * nghttp2_send_callback for the details. 1612c593315Sopenharmony_ci */ 1622c593315Sopenharmony_cistatic ssize_t send_callback(nghttp2_session *session, const uint8_t *data, 1632c593315Sopenharmony_ci size_t length, int flags, void *user_data) { 1642c593315Sopenharmony_ci struct Connection *connection; 1652c593315Sopenharmony_ci int rv; 1662c593315Sopenharmony_ci (void)session; 1672c593315Sopenharmony_ci (void)flags; 1682c593315Sopenharmony_ci 1692c593315Sopenharmony_ci connection = (struct Connection *)user_data; 1702c593315Sopenharmony_ci connection->want_io = IO_NONE; 1712c593315Sopenharmony_ci ERR_clear_error(); 1722c593315Sopenharmony_ci rv = SSL_write(connection->ssl, data, (int)length); 1732c593315Sopenharmony_ci if (rv <= 0) { 1742c593315Sopenharmony_ci int err = SSL_get_error(connection->ssl, rv); 1752c593315Sopenharmony_ci if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { 1762c593315Sopenharmony_ci connection->want_io = 1772c593315Sopenharmony_ci (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); 1782c593315Sopenharmony_ci rv = NGHTTP2_ERR_WOULDBLOCK; 1792c593315Sopenharmony_ci } else { 1802c593315Sopenharmony_ci rv = NGHTTP2_ERR_CALLBACK_FAILURE; 1812c593315Sopenharmony_ci } 1822c593315Sopenharmony_ci } 1832c593315Sopenharmony_ci return rv; 1842c593315Sopenharmony_ci} 1852c593315Sopenharmony_ci 1862c593315Sopenharmony_ci/* 1872c593315Sopenharmony_ci * The implementation of nghttp2_recv_callback type. Here we read data 1882c593315Sopenharmony_ci * from the network and write them in |buf|. The capacity of |buf| is 1892c593315Sopenharmony_ci * |length| bytes. Returns the number of bytes stored in |buf|. See 1902c593315Sopenharmony_ci * the documentation of nghttp2_recv_callback for the details. 1912c593315Sopenharmony_ci */ 1922c593315Sopenharmony_cistatic ssize_t recv_callback(nghttp2_session *session, uint8_t *buf, 1932c593315Sopenharmony_ci size_t length, int flags, void *user_data) { 1942c593315Sopenharmony_ci struct Connection *connection; 1952c593315Sopenharmony_ci int rv; 1962c593315Sopenharmony_ci (void)session; 1972c593315Sopenharmony_ci (void)flags; 1982c593315Sopenharmony_ci 1992c593315Sopenharmony_ci connection = (struct Connection *)user_data; 2002c593315Sopenharmony_ci connection->want_io = IO_NONE; 2012c593315Sopenharmony_ci ERR_clear_error(); 2022c593315Sopenharmony_ci rv = SSL_read(connection->ssl, buf, (int)length); 2032c593315Sopenharmony_ci if (rv < 0) { 2042c593315Sopenharmony_ci int err = SSL_get_error(connection->ssl, rv); 2052c593315Sopenharmony_ci if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { 2062c593315Sopenharmony_ci connection->want_io = 2072c593315Sopenharmony_ci (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); 2082c593315Sopenharmony_ci rv = NGHTTP2_ERR_WOULDBLOCK; 2092c593315Sopenharmony_ci } else { 2102c593315Sopenharmony_ci rv = NGHTTP2_ERR_CALLBACK_FAILURE; 2112c593315Sopenharmony_ci } 2122c593315Sopenharmony_ci } else if (rv == 0) { 2132c593315Sopenharmony_ci rv = NGHTTP2_ERR_EOF; 2142c593315Sopenharmony_ci } 2152c593315Sopenharmony_ci return rv; 2162c593315Sopenharmony_ci} 2172c593315Sopenharmony_ci 2182c593315Sopenharmony_cistatic int on_frame_send_callback(nghttp2_session *session, 2192c593315Sopenharmony_ci const nghttp2_frame *frame, void *user_data) { 2202c593315Sopenharmony_ci size_t i; 2212c593315Sopenharmony_ci (void)user_data; 2222c593315Sopenharmony_ci 2232c593315Sopenharmony_ci switch (frame->hd.type) { 2242c593315Sopenharmony_ci case NGHTTP2_HEADERS: 2252c593315Sopenharmony_ci if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) { 2262c593315Sopenharmony_ci const nghttp2_nv *nva = frame->headers.nva; 2272c593315Sopenharmony_ci printf("[INFO] C ----------------------------> S (HEADERS)\n"); 2282c593315Sopenharmony_ci for (i = 0; i < frame->headers.nvlen; ++i) { 2292c593315Sopenharmony_ci fwrite(nva[i].name, 1, nva[i].namelen, stdout); 2302c593315Sopenharmony_ci printf(": "); 2312c593315Sopenharmony_ci fwrite(nva[i].value, 1, nva[i].valuelen, stdout); 2322c593315Sopenharmony_ci printf("\n"); 2332c593315Sopenharmony_ci } 2342c593315Sopenharmony_ci } 2352c593315Sopenharmony_ci break; 2362c593315Sopenharmony_ci case NGHTTP2_RST_STREAM: 2372c593315Sopenharmony_ci printf("[INFO] C ----------------------------> S (RST_STREAM)\n"); 2382c593315Sopenharmony_ci break; 2392c593315Sopenharmony_ci case NGHTTP2_GOAWAY: 2402c593315Sopenharmony_ci printf("[INFO] C ----------------------------> S (GOAWAY)\n"); 2412c593315Sopenharmony_ci break; 2422c593315Sopenharmony_ci } 2432c593315Sopenharmony_ci return 0; 2442c593315Sopenharmony_ci} 2452c593315Sopenharmony_ci 2462c593315Sopenharmony_cistatic int on_frame_recv_callback(nghttp2_session *session, 2472c593315Sopenharmony_ci const nghttp2_frame *frame, void *user_data) { 2482c593315Sopenharmony_ci size_t i; 2492c593315Sopenharmony_ci (void)user_data; 2502c593315Sopenharmony_ci 2512c593315Sopenharmony_ci switch (frame->hd.type) { 2522c593315Sopenharmony_ci case NGHTTP2_HEADERS: 2532c593315Sopenharmony_ci if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) { 2542c593315Sopenharmony_ci const nghttp2_nv *nva = frame->headers.nva; 2552c593315Sopenharmony_ci struct Request *req; 2562c593315Sopenharmony_ci req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); 2572c593315Sopenharmony_ci if (req) { 2582c593315Sopenharmony_ci printf("[INFO] C <---------------------------- S (HEADERS)\n"); 2592c593315Sopenharmony_ci for (i = 0; i < frame->headers.nvlen; ++i) { 2602c593315Sopenharmony_ci fwrite(nva[i].name, 1, nva[i].namelen, stdout); 2612c593315Sopenharmony_ci printf(": "); 2622c593315Sopenharmony_ci fwrite(nva[i].value, 1, nva[i].valuelen, stdout); 2632c593315Sopenharmony_ci printf("\n"); 2642c593315Sopenharmony_ci } 2652c593315Sopenharmony_ci } 2662c593315Sopenharmony_ci } 2672c593315Sopenharmony_ci break; 2682c593315Sopenharmony_ci case NGHTTP2_RST_STREAM: 2692c593315Sopenharmony_ci printf("[INFO] C <---------------------------- S (RST_STREAM)\n"); 2702c593315Sopenharmony_ci break; 2712c593315Sopenharmony_ci case NGHTTP2_GOAWAY: 2722c593315Sopenharmony_ci printf("[INFO] C <---------------------------- S (GOAWAY)\n"); 2732c593315Sopenharmony_ci break; 2742c593315Sopenharmony_ci } 2752c593315Sopenharmony_ci return 0; 2762c593315Sopenharmony_ci} 2772c593315Sopenharmony_ci 2782c593315Sopenharmony_ci/* 2792c593315Sopenharmony_ci * The implementation of nghttp2_on_stream_close_callback type. We use 2802c593315Sopenharmony_ci * this function to know the response is fully received. Since we just 2812c593315Sopenharmony_ci * fetch 1 resource in this program, after reception of the response, 2822c593315Sopenharmony_ci * we submit GOAWAY and close the session. 2832c593315Sopenharmony_ci */ 2842c593315Sopenharmony_cistatic int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, 2852c593315Sopenharmony_ci uint32_t error_code, void *user_data) { 2862c593315Sopenharmony_ci struct Request *req; 2872c593315Sopenharmony_ci (void)error_code; 2882c593315Sopenharmony_ci (void)user_data; 2892c593315Sopenharmony_ci 2902c593315Sopenharmony_ci req = nghttp2_session_get_stream_user_data(session, stream_id); 2912c593315Sopenharmony_ci if (req) { 2922c593315Sopenharmony_ci int rv; 2932c593315Sopenharmony_ci rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); 2942c593315Sopenharmony_ci 2952c593315Sopenharmony_ci if (rv != 0) { 2962c593315Sopenharmony_ci diec("nghttp2_session_terminate_session", rv); 2972c593315Sopenharmony_ci } 2982c593315Sopenharmony_ci } 2992c593315Sopenharmony_ci return 0; 3002c593315Sopenharmony_ci} 3012c593315Sopenharmony_ci 3022c593315Sopenharmony_ci/* 3032c593315Sopenharmony_ci * The implementation of nghttp2_on_data_chunk_recv_callback type. We 3042c593315Sopenharmony_ci * use this function to print the received response body. 3052c593315Sopenharmony_ci */ 3062c593315Sopenharmony_cistatic int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, 3072c593315Sopenharmony_ci int32_t stream_id, const uint8_t *data, 3082c593315Sopenharmony_ci size_t len, void *user_data) { 3092c593315Sopenharmony_ci struct Request *req; 3102c593315Sopenharmony_ci (void)flags; 3112c593315Sopenharmony_ci (void)user_data; 3122c593315Sopenharmony_ci 3132c593315Sopenharmony_ci req = nghttp2_session_get_stream_user_data(session, stream_id); 3142c593315Sopenharmony_ci if (req) { 3152c593315Sopenharmony_ci printf("[INFO] C <---------------------------- S (DATA chunk)\n" 3162c593315Sopenharmony_ci "%lu bytes\n", 3172c593315Sopenharmony_ci (unsigned long int)len); 3182c593315Sopenharmony_ci fwrite(data, 1, len, stdout); 3192c593315Sopenharmony_ci printf("\n"); 3202c593315Sopenharmony_ci } 3212c593315Sopenharmony_ci return 0; 3222c593315Sopenharmony_ci} 3232c593315Sopenharmony_ci 3242c593315Sopenharmony_ci/* 3252c593315Sopenharmony_ci * Setup callback functions. nghttp2 API offers many callback 3262c593315Sopenharmony_ci * functions, but most of them are optional. The send_callback is 3272c593315Sopenharmony_ci * always required. Since we use nghttp2_session_recv(), the 3282c593315Sopenharmony_ci * recv_callback is also required. 3292c593315Sopenharmony_ci */ 3302c593315Sopenharmony_cistatic void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) { 3312c593315Sopenharmony_ci nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); 3322c593315Sopenharmony_ci 3332c593315Sopenharmony_ci nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback); 3342c593315Sopenharmony_ci 3352c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, 3362c593315Sopenharmony_ci on_frame_send_callback); 3372c593315Sopenharmony_ci 3382c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks, 3392c593315Sopenharmony_ci on_frame_recv_callback); 3402c593315Sopenharmony_ci 3412c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_stream_close_callback( 3422c593315Sopenharmony_ci callbacks, on_stream_close_callback); 3432c593315Sopenharmony_ci 3442c593315Sopenharmony_ci nghttp2_session_callbacks_set_on_data_chunk_recv_callback( 3452c593315Sopenharmony_ci callbacks, on_data_chunk_recv_callback); 3462c593315Sopenharmony_ci} 3472c593315Sopenharmony_ci 3482c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG 3492c593315Sopenharmony_ci/* 3502c593315Sopenharmony_ci * Callback function for TLS NPN. Since this program only supports 3512c593315Sopenharmony_ci * HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2 3522c593315Sopenharmony_ci * library supports, we terminate program. 3532c593315Sopenharmony_ci */ 3542c593315Sopenharmony_cistatic int select_next_proto_cb(SSL *ssl, unsigned char **out, 3552c593315Sopenharmony_ci unsigned char *outlen, const unsigned char *in, 3562c593315Sopenharmony_ci unsigned int inlen, void *arg) { 3572c593315Sopenharmony_ci int rv; 3582c593315Sopenharmony_ci (void)ssl; 3592c593315Sopenharmony_ci (void)arg; 3602c593315Sopenharmony_ci 3612c593315Sopenharmony_ci /* nghttp2_select_next_protocol() selects HTTP/2 protocol the 3622c593315Sopenharmony_ci nghttp2 library supports. */ 3632c593315Sopenharmony_ci rv = nghttp2_select_next_protocol(out, outlen, in, inlen); 3642c593315Sopenharmony_ci if (rv <= 0) { 3652c593315Sopenharmony_ci die("Server did not advertise HTTP/2 protocol"); 3662c593315Sopenharmony_ci } 3672c593315Sopenharmony_ci return SSL_TLSEXT_ERR_OK; 3682c593315Sopenharmony_ci} 3692c593315Sopenharmony_ci#endif /* !OPENSSL_NO_NEXTPROTONEG */ 3702c593315Sopenharmony_ci 3712c593315Sopenharmony_ci/* 3722c593315Sopenharmony_ci * Setup SSL/TLS context. 3732c593315Sopenharmony_ci */ 3742c593315Sopenharmony_cistatic void init_ssl_ctx(SSL_CTX *ssl_ctx) { 3752c593315Sopenharmony_ci /* Disable SSLv2 and enable all workarounds for buggy servers */ 3762c593315Sopenharmony_ci SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); 3772c593315Sopenharmony_ci SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); 3782c593315Sopenharmony_ci SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); 3792c593315Sopenharmony_ci /* Set NPN callback */ 3802c593315Sopenharmony_ci#ifndef OPENSSL_NO_NEXTPROTONEG 3812c593315Sopenharmony_ci SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL); 3822c593315Sopenharmony_ci#endif /* !OPENSSL_NO_NEXTPROTONEG */ 3832c593315Sopenharmony_ci 3842c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x10002000L 3852c593315Sopenharmony_ci SSL_CTX_set_alpn_protos(ssl_ctx, (const unsigned char *)"\x02h2", 3); 3862c593315Sopenharmony_ci#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ 3872c593315Sopenharmony_ci} 3882c593315Sopenharmony_ci 3892c593315Sopenharmony_cistatic void ssl_handshake(SSL *ssl, int fd) { 3902c593315Sopenharmony_ci int rv; 3912c593315Sopenharmony_ci if (SSL_set_fd(ssl, fd) == 0) { 3922c593315Sopenharmony_ci dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); 3932c593315Sopenharmony_ci } 3942c593315Sopenharmony_ci ERR_clear_error(); 3952c593315Sopenharmony_ci rv = SSL_connect(ssl); 3962c593315Sopenharmony_ci if (rv <= 0) { 3972c593315Sopenharmony_ci dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL)); 3982c593315Sopenharmony_ci } 3992c593315Sopenharmony_ci} 4002c593315Sopenharmony_ci 4012c593315Sopenharmony_ci/* 4022c593315Sopenharmony_ci * Connects to the host |host| and port |port|. This function returns 4032c593315Sopenharmony_ci * the file descriptor of the client socket. 4042c593315Sopenharmony_ci */ 4052c593315Sopenharmony_cistatic int connect_to(const char *host, uint16_t port) { 4062c593315Sopenharmony_ci struct addrinfo hints; 4072c593315Sopenharmony_ci int fd = -1; 4082c593315Sopenharmony_ci int rv; 4092c593315Sopenharmony_ci char service[NI_MAXSERV]; 4102c593315Sopenharmony_ci struct addrinfo *res, *rp; 4112c593315Sopenharmony_ci snprintf(service, sizeof(service), "%u", port); 4122c593315Sopenharmony_ci memset(&hints, 0, sizeof(struct addrinfo)); 4132c593315Sopenharmony_ci hints.ai_family = AF_UNSPEC; 4142c593315Sopenharmony_ci hints.ai_socktype = SOCK_STREAM; 4152c593315Sopenharmony_ci rv = getaddrinfo(host, service, &hints, &res); 4162c593315Sopenharmony_ci if (rv != 0) { 4172c593315Sopenharmony_ci dief("getaddrinfo", gai_strerror(rv)); 4182c593315Sopenharmony_ci } 4192c593315Sopenharmony_ci for (rp = res; rp; rp = rp->ai_next) { 4202c593315Sopenharmony_ci fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 4212c593315Sopenharmony_ci if (fd == -1) { 4222c593315Sopenharmony_ci continue; 4232c593315Sopenharmony_ci } 4242c593315Sopenharmony_ci while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && 4252c593315Sopenharmony_ci errno == EINTR) 4262c593315Sopenharmony_ci ; 4272c593315Sopenharmony_ci if (rv == 0) { 4282c593315Sopenharmony_ci break; 4292c593315Sopenharmony_ci } 4302c593315Sopenharmony_ci close(fd); 4312c593315Sopenharmony_ci fd = -1; 4322c593315Sopenharmony_ci } 4332c593315Sopenharmony_ci freeaddrinfo(res); 4342c593315Sopenharmony_ci return fd; 4352c593315Sopenharmony_ci} 4362c593315Sopenharmony_ci 4372c593315Sopenharmony_cistatic void make_non_block(int fd) { 4382c593315Sopenharmony_ci int flags, rv; 4392c593315Sopenharmony_ci while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR) 4402c593315Sopenharmony_ci ; 4412c593315Sopenharmony_ci if (flags == -1) { 4422c593315Sopenharmony_ci dief("fcntl", strerror(errno)); 4432c593315Sopenharmony_ci } 4442c593315Sopenharmony_ci while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR) 4452c593315Sopenharmony_ci ; 4462c593315Sopenharmony_ci if (rv == -1) { 4472c593315Sopenharmony_ci dief("fcntl", strerror(errno)); 4482c593315Sopenharmony_ci } 4492c593315Sopenharmony_ci} 4502c593315Sopenharmony_ci 4512c593315Sopenharmony_cistatic void set_tcp_nodelay(int fd) { 4522c593315Sopenharmony_ci int val = 1; 4532c593315Sopenharmony_ci int rv; 4542c593315Sopenharmony_ci rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); 4552c593315Sopenharmony_ci if (rv == -1) { 4562c593315Sopenharmony_ci dief("setsockopt", strerror(errno)); 4572c593315Sopenharmony_ci } 4582c593315Sopenharmony_ci} 4592c593315Sopenharmony_ci 4602c593315Sopenharmony_ci/* 4612c593315Sopenharmony_ci * Update |pollfd| based on the state of |connection|. 4622c593315Sopenharmony_ci */ 4632c593315Sopenharmony_cistatic void ctl_poll(struct pollfd *pollfd, struct Connection *connection) { 4642c593315Sopenharmony_ci pollfd->events = 0; 4652c593315Sopenharmony_ci if (nghttp2_session_want_read(connection->session) || 4662c593315Sopenharmony_ci connection->want_io == WANT_READ) { 4672c593315Sopenharmony_ci pollfd->events |= POLLIN; 4682c593315Sopenharmony_ci } 4692c593315Sopenharmony_ci if (nghttp2_session_want_write(connection->session) || 4702c593315Sopenharmony_ci connection->want_io == WANT_WRITE) { 4712c593315Sopenharmony_ci pollfd->events |= POLLOUT; 4722c593315Sopenharmony_ci } 4732c593315Sopenharmony_ci} 4742c593315Sopenharmony_ci 4752c593315Sopenharmony_ci/* 4762c593315Sopenharmony_ci * Submits the request |req| to the connection |connection|. This 4772c593315Sopenharmony_ci * function does not send packets; just append the request to the 4782c593315Sopenharmony_ci * internal queue in |connection->session|. 4792c593315Sopenharmony_ci */ 4802c593315Sopenharmony_cistatic void submit_request(struct Connection *connection, struct Request *req) { 4812c593315Sopenharmony_ci int32_t stream_id; 4822c593315Sopenharmony_ci /* Make sure that the last item is NULL */ 4832c593315Sopenharmony_ci const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"), 4842c593315Sopenharmony_ci MAKE_NV_CS(":path", req->path), 4852c593315Sopenharmony_ci MAKE_NV(":scheme", "https"), 4862c593315Sopenharmony_ci MAKE_NV_CS(":authority", req->hostport), 4872c593315Sopenharmony_ci MAKE_NV("accept", "*/*"), 4882c593315Sopenharmony_ci MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)}; 4892c593315Sopenharmony_ci 4902c593315Sopenharmony_ci stream_id = nghttp2_submit_request(connection->session, NULL, nva, 4912c593315Sopenharmony_ci sizeof(nva) / sizeof(nva[0]), NULL, req); 4922c593315Sopenharmony_ci 4932c593315Sopenharmony_ci if (stream_id < 0) { 4942c593315Sopenharmony_ci diec("nghttp2_submit_request", stream_id); 4952c593315Sopenharmony_ci } 4962c593315Sopenharmony_ci 4972c593315Sopenharmony_ci req->stream_id = stream_id; 4982c593315Sopenharmony_ci printf("[INFO] Stream ID = %d\n", stream_id); 4992c593315Sopenharmony_ci} 5002c593315Sopenharmony_ci 5012c593315Sopenharmony_ci/* 5022c593315Sopenharmony_ci * Performs the network I/O. 5032c593315Sopenharmony_ci */ 5042c593315Sopenharmony_cistatic void exec_io(struct Connection *connection) { 5052c593315Sopenharmony_ci int rv; 5062c593315Sopenharmony_ci rv = nghttp2_session_recv(connection->session); 5072c593315Sopenharmony_ci if (rv != 0) { 5082c593315Sopenharmony_ci diec("nghttp2_session_recv", rv); 5092c593315Sopenharmony_ci } 5102c593315Sopenharmony_ci rv = nghttp2_session_send(connection->session); 5112c593315Sopenharmony_ci if (rv != 0) { 5122c593315Sopenharmony_ci diec("nghttp2_session_send", rv); 5132c593315Sopenharmony_ci } 5142c593315Sopenharmony_ci} 5152c593315Sopenharmony_ci 5162c593315Sopenharmony_cistatic void request_init(struct Request *req, const struct URI *uri) { 5172c593315Sopenharmony_ci req->host = strcopy(uri->host, uri->hostlen); 5182c593315Sopenharmony_ci req->port = uri->port; 5192c593315Sopenharmony_ci req->path = strcopy(uri->path, uri->pathlen); 5202c593315Sopenharmony_ci req->hostport = strcopy(uri->hostport, uri->hostportlen); 5212c593315Sopenharmony_ci req->stream_id = -1; 5222c593315Sopenharmony_ci} 5232c593315Sopenharmony_ci 5242c593315Sopenharmony_cistatic void request_free(struct Request *req) { 5252c593315Sopenharmony_ci free(req->host); 5262c593315Sopenharmony_ci free(req->path); 5272c593315Sopenharmony_ci free(req->hostport); 5282c593315Sopenharmony_ci} 5292c593315Sopenharmony_ci 5302c593315Sopenharmony_ci/* 5312c593315Sopenharmony_ci * Fetches the resource denoted by |uri|. 5322c593315Sopenharmony_ci */ 5332c593315Sopenharmony_cistatic void fetch_uri(const struct URI *uri) { 5342c593315Sopenharmony_ci nghttp2_session_callbacks *callbacks; 5352c593315Sopenharmony_ci int fd; 5362c593315Sopenharmony_ci SSL_CTX *ssl_ctx; 5372c593315Sopenharmony_ci SSL *ssl; 5382c593315Sopenharmony_ci struct Request req; 5392c593315Sopenharmony_ci struct Connection connection; 5402c593315Sopenharmony_ci int rv; 5412c593315Sopenharmony_ci nfds_t npollfds = 1; 5422c593315Sopenharmony_ci struct pollfd pollfds[1]; 5432c593315Sopenharmony_ci 5442c593315Sopenharmony_ci request_init(&req, uri); 5452c593315Sopenharmony_ci 5462c593315Sopenharmony_ci /* Establish connection and setup SSL */ 5472c593315Sopenharmony_ci fd = connect_to(req.host, req.port); 5482c593315Sopenharmony_ci if (fd == -1) { 5492c593315Sopenharmony_ci die("Could not open file descriptor"); 5502c593315Sopenharmony_ci } 5512c593315Sopenharmony_ci ssl_ctx = SSL_CTX_new(TLS_client_method()); 5522c593315Sopenharmony_ci if (ssl_ctx == NULL) { 5532c593315Sopenharmony_ci dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL)); 5542c593315Sopenharmony_ci } 5552c593315Sopenharmony_ci init_ssl_ctx(ssl_ctx); 5562c593315Sopenharmony_ci ssl = SSL_new(ssl_ctx); 5572c593315Sopenharmony_ci if (ssl == NULL) { 5582c593315Sopenharmony_ci dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); 5592c593315Sopenharmony_ci } 5602c593315Sopenharmony_ci /* To simplify the program, we perform SSL/TLS handshake in blocking 5612c593315Sopenharmony_ci I/O. */ 5622c593315Sopenharmony_ci ssl_handshake(ssl, fd); 5632c593315Sopenharmony_ci 5642c593315Sopenharmony_ci connection.ssl = ssl; 5652c593315Sopenharmony_ci connection.want_io = IO_NONE; 5662c593315Sopenharmony_ci 5672c593315Sopenharmony_ci /* Here make file descriptor non-block */ 5682c593315Sopenharmony_ci make_non_block(fd); 5692c593315Sopenharmony_ci set_tcp_nodelay(fd); 5702c593315Sopenharmony_ci 5712c593315Sopenharmony_ci printf("[INFO] SSL/TLS handshake completed\n"); 5722c593315Sopenharmony_ci 5732c593315Sopenharmony_ci rv = nghttp2_session_callbacks_new(&callbacks); 5742c593315Sopenharmony_ci 5752c593315Sopenharmony_ci if (rv != 0) { 5762c593315Sopenharmony_ci diec("nghttp2_session_callbacks_new", rv); 5772c593315Sopenharmony_ci } 5782c593315Sopenharmony_ci 5792c593315Sopenharmony_ci setup_nghttp2_callbacks(callbacks); 5802c593315Sopenharmony_ci 5812c593315Sopenharmony_ci rv = nghttp2_session_client_new(&connection.session, callbacks, &connection); 5822c593315Sopenharmony_ci 5832c593315Sopenharmony_ci nghttp2_session_callbacks_del(callbacks); 5842c593315Sopenharmony_ci 5852c593315Sopenharmony_ci if (rv != 0) { 5862c593315Sopenharmony_ci diec("nghttp2_session_client_new", rv); 5872c593315Sopenharmony_ci } 5882c593315Sopenharmony_ci 5892c593315Sopenharmony_ci rv = nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0); 5902c593315Sopenharmony_ci 5912c593315Sopenharmony_ci if (rv != 0) { 5922c593315Sopenharmony_ci diec("nghttp2_submit_settings", rv); 5932c593315Sopenharmony_ci } 5942c593315Sopenharmony_ci 5952c593315Sopenharmony_ci /* Submit the HTTP request to the outbound queue. */ 5962c593315Sopenharmony_ci submit_request(&connection, &req); 5972c593315Sopenharmony_ci 5982c593315Sopenharmony_ci pollfds[0].fd = fd; 5992c593315Sopenharmony_ci ctl_poll(pollfds, &connection); 6002c593315Sopenharmony_ci 6012c593315Sopenharmony_ci /* Event loop */ 6022c593315Sopenharmony_ci while (nghttp2_session_want_read(connection.session) || 6032c593315Sopenharmony_ci nghttp2_session_want_write(connection.session)) { 6042c593315Sopenharmony_ci int nfds = poll(pollfds, npollfds, -1); 6052c593315Sopenharmony_ci if (nfds == -1) { 6062c593315Sopenharmony_ci dief("poll", strerror(errno)); 6072c593315Sopenharmony_ci } 6082c593315Sopenharmony_ci if (pollfds[0].revents & (POLLIN | POLLOUT)) { 6092c593315Sopenharmony_ci exec_io(&connection); 6102c593315Sopenharmony_ci } 6112c593315Sopenharmony_ci if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) { 6122c593315Sopenharmony_ci die("Connection error"); 6132c593315Sopenharmony_ci } 6142c593315Sopenharmony_ci ctl_poll(pollfds, &connection); 6152c593315Sopenharmony_ci } 6162c593315Sopenharmony_ci 6172c593315Sopenharmony_ci /* Resource cleanup */ 6182c593315Sopenharmony_ci nghttp2_session_del(connection.session); 6192c593315Sopenharmony_ci SSL_shutdown(ssl); 6202c593315Sopenharmony_ci SSL_free(ssl); 6212c593315Sopenharmony_ci SSL_CTX_free(ssl_ctx); 6222c593315Sopenharmony_ci shutdown(fd, SHUT_WR); 6232c593315Sopenharmony_ci close(fd); 6242c593315Sopenharmony_ci request_free(&req); 6252c593315Sopenharmony_ci} 6262c593315Sopenharmony_ci 6272c593315Sopenharmony_cistatic int parse_uri(struct URI *res, const char *uri) { 6282c593315Sopenharmony_ci /* We only interested in https */ 6292c593315Sopenharmony_ci size_t len, i, offset; 6302c593315Sopenharmony_ci int ipv6addr = 0; 6312c593315Sopenharmony_ci memset(res, 0, sizeof(struct URI)); 6322c593315Sopenharmony_ci len = strlen(uri); 6332c593315Sopenharmony_ci if (len < 9 || memcmp("https://", uri, 8) != 0) { 6342c593315Sopenharmony_ci return -1; 6352c593315Sopenharmony_ci } 6362c593315Sopenharmony_ci offset = 8; 6372c593315Sopenharmony_ci res->host = res->hostport = &uri[offset]; 6382c593315Sopenharmony_ci res->hostlen = 0; 6392c593315Sopenharmony_ci if (uri[offset] == '[') { 6402c593315Sopenharmony_ci /* IPv6 literal address */ 6412c593315Sopenharmony_ci ++offset; 6422c593315Sopenharmony_ci ++res->host; 6432c593315Sopenharmony_ci ipv6addr = 1; 6442c593315Sopenharmony_ci for (i = offset; i < len; ++i) { 6452c593315Sopenharmony_ci if (uri[i] == ']') { 6462c593315Sopenharmony_ci res->hostlen = i - offset; 6472c593315Sopenharmony_ci offset = i + 1; 6482c593315Sopenharmony_ci break; 6492c593315Sopenharmony_ci } 6502c593315Sopenharmony_ci } 6512c593315Sopenharmony_ci } else { 6522c593315Sopenharmony_ci const char delims[] = ":/?#"; 6532c593315Sopenharmony_ci for (i = offset; i < len; ++i) { 6542c593315Sopenharmony_ci if (strchr(delims, uri[i]) != NULL) { 6552c593315Sopenharmony_ci break; 6562c593315Sopenharmony_ci } 6572c593315Sopenharmony_ci } 6582c593315Sopenharmony_ci res->hostlen = i - offset; 6592c593315Sopenharmony_ci offset = i; 6602c593315Sopenharmony_ci } 6612c593315Sopenharmony_ci if (res->hostlen == 0) { 6622c593315Sopenharmony_ci return -1; 6632c593315Sopenharmony_ci } 6642c593315Sopenharmony_ci /* Assuming https */ 6652c593315Sopenharmony_ci res->port = 443; 6662c593315Sopenharmony_ci if (offset < len) { 6672c593315Sopenharmony_ci if (uri[offset] == ':') { 6682c593315Sopenharmony_ci /* port */ 6692c593315Sopenharmony_ci const char delims[] = "/?#"; 6702c593315Sopenharmony_ci int port = 0; 6712c593315Sopenharmony_ci ++offset; 6722c593315Sopenharmony_ci for (i = offset; i < len; ++i) { 6732c593315Sopenharmony_ci if (strchr(delims, uri[i]) != NULL) { 6742c593315Sopenharmony_ci break; 6752c593315Sopenharmony_ci } 6762c593315Sopenharmony_ci if ('0' <= uri[i] && uri[i] <= '9') { 6772c593315Sopenharmony_ci port *= 10; 6782c593315Sopenharmony_ci port += uri[i] - '0'; 6792c593315Sopenharmony_ci if (port > 65535) { 6802c593315Sopenharmony_ci return -1; 6812c593315Sopenharmony_ci } 6822c593315Sopenharmony_ci } else { 6832c593315Sopenharmony_ci return -1; 6842c593315Sopenharmony_ci } 6852c593315Sopenharmony_ci } 6862c593315Sopenharmony_ci if (port == 0) { 6872c593315Sopenharmony_ci return -1; 6882c593315Sopenharmony_ci } 6892c593315Sopenharmony_ci offset = i; 6902c593315Sopenharmony_ci res->port = (uint16_t)port; 6912c593315Sopenharmony_ci } 6922c593315Sopenharmony_ci } 6932c593315Sopenharmony_ci res->hostportlen = (size_t)(uri + offset + ipv6addr - res->host); 6942c593315Sopenharmony_ci for (i = offset; i < len; ++i) { 6952c593315Sopenharmony_ci if (uri[i] == '#') { 6962c593315Sopenharmony_ci break; 6972c593315Sopenharmony_ci } 6982c593315Sopenharmony_ci } 6992c593315Sopenharmony_ci if (i - offset == 0) { 7002c593315Sopenharmony_ci res->path = "/"; 7012c593315Sopenharmony_ci res->pathlen = 1; 7022c593315Sopenharmony_ci } else { 7032c593315Sopenharmony_ci res->path = &uri[offset]; 7042c593315Sopenharmony_ci res->pathlen = i - offset; 7052c593315Sopenharmony_ci } 7062c593315Sopenharmony_ci return 0; 7072c593315Sopenharmony_ci} 7082c593315Sopenharmony_ci 7092c593315Sopenharmony_ciint main(int argc, char **argv) { 7102c593315Sopenharmony_ci struct URI uri; 7112c593315Sopenharmony_ci struct sigaction act; 7122c593315Sopenharmony_ci int rv; 7132c593315Sopenharmony_ci 7142c593315Sopenharmony_ci if (argc < 2) { 7152c593315Sopenharmony_ci die("Specify a https URI"); 7162c593315Sopenharmony_ci } 7172c593315Sopenharmony_ci 7182c593315Sopenharmony_ci memset(&act, 0, sizeof(struct sigaction)); 7192c593315Sopenharmony_ci act.sa_handler = SIG_IGN; 7202c593315Sopenharmony_ci sigaction(SIGPIPE, &act, 0); 7212c593315Sopenharmony_ci 7222c593315Sopenharmony_ci#if OPENSSL_VERSION_NUMBER >= 0x1010000fL 7232c593315Sopenharmony_ci /* No explicit initialization is required. */ 7242c593315Sopenharmony_ci#elif defined(OPENSSL_IS_BORINGSSL) 7252c593315Sopenharmony_ci CRYPTO_library_init(); 7262c593315Sopenharmony_ci#else /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \ 7272c593315Sopenharmony_ci !defined(OPENSSL_IS_BORINGSSL) */ 7282c593315Sopenharmony_ci OPENSSL_config(NULL); 7292c593315Sopenharmony_ci SSL_load_error_strings(); 7302c593315Sopenharmony_ci SSL_library_init(); 7312c593315Sopenharmony_ci OpenSSL_add_all_algorithms(); 7322c593315Sopenharmony_ci#endif /* !(OPENSSL_VERSION_NUMBER >= 0x1010000fL) && \ 7332c593315Sopenharmony_ci !defined(OPENSSL_IS_BORINGSSL) */ 7342c593315Sopenharmony_ci 7352c593315Sopenharmony_ci rv = parse_uri(&uri, argv[1]); 7362c593315Sopenharmony_ci if (rv != 0) { 7372c593315Sopenharmony_ci die("parse_uri failed"); 7382c593315Sopenharmony_ci } 7392c593315Sopenharmony_ci fetch_uri(&uri); 7402c593315Sopenharmony_ci return EXIT_SUCCESS; 7412c593315Sopenharmony_ci} 742