tizen 2.4 release
[external/nghttp2.git] / examples / 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 /*
26  * This program is written to show how to use nghttp2 API in C and
27  * intentionally made simple.
28  */
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif /* !HAVE_CONFIG_H */
32
33 #include <stdint.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netdb.h>
40 #include <netinet/in.h>
41 #include <netinet/tcp.h>
42 #include <poll.h>
43 #include <signal.h>
44 #include <stdio.h>
45 #include <assert.h>
46
47 #include <nghttp2/nghttp2.h>
48
49 #include <openssl/ssl.h>
50 #include <openssl/err.h>
51 #include <openssl/conf.h>
52
53 enum { IO_NONE, WANT_READ, WANT_WRITE };
54
55 #define MAKE_NV(NAME, VALUE)                                                   \
56   {                                                                            \
57     (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1,   \
58         NGHTTP2_NV_FLAG_NONE                                                   \
59   }
60
61 #define MAKE_NV_CS(NAME, VALUE)                                                \
62   {                                                                            \
63     (uint8_t *) NAME, (uint8_t *)VALUE, sizeof(NAME) - 1, strlen(VALUE),       \
64         NGHTTP2_NV_FLAG_NONE                                                   \
65   }
66
67 struct Connection {
68   SSL *ssl;
69   nghttp2_session *session;
70   /* WANT_READ if SSL/TLS connection needs more input; or WANT_WRITE
71      if it needs more output; or IO_NONE. This is necessary because
72      SSL/TLS re-negotiation is possible at any time. nghttp2 API
73      offers similar functions like nghttp2_session_want_read() and
74      nghttp2_session_want_write() but they do not take into account
75      SSL/TSL connection. */
76   int want_io;
77 };
78
79 struct Request {
80   char *host;
81   /* In this program, path contains query component as well. */
82   char *path;
83   /* This is the concatenation of host and port with ":" in
84      between. */
85   char *hostport;
86   /* Stream ID for this request. */
87   int32_t stream_id;
88   uint16_t port;
89 };
90
91 struct URI {
92   const char *host;
93   /* In this program, path contains query component as well. */
94   const char *path;
95   size_t pathlen;
96   const char *hostport;
97   size_t hostlen;
98   size_t hostportlen;
99   uint16_t port;
100 };
101
102 /*
103  * Returns copy of string |s| with the length |len|. The returned
104  * string is NULL-terminated.
105  */
106 static char *strcopy(const char *s, size_t len) {
107   char *dst;
108   dst = malloc(len + 1);
109   memcpy(dst, s, len);
110   dst[len] = '\0';
111   return dst;
112 }
113
114 /*
115  * Prints error message |msg| and exit.
116  */
117 static void die(const char *msg) {
118   fprintf(stderr, "FATAL: %s\n", msg);
119   exit(EXIT_FAILURE);
120 }
121
122 /*
123  * Prints error containing the function name |func| and message |msg|
124  * and exit.
125  */
126 static void dief(const char *func, const char *msg) {
127   fprintf(stderr, "FATAL: %s: %s\n", func, msg);
128   exit(EXIT_FAILURE);
129 }
130
131 /*
132  * Prints error containing the function name |func| and error code
133  * |error_code| and exit.
134  */
135 static void diec(const char *func, int error_code) {
136   fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code,
137           nghttp2_strerror(error_code));
138   exit(EXIT_FAILURE);
139 }
140
141 /*
142  * The implementation of nghttp2_send_callback type. Here we write
143  * |data| with size |length| to the network and return the number of
144  * bytes actually written. See the documentation of
145  * nghttp2_send_callback for the details.
146  */
147 static ssize_t send_callback(nghttp2_session *session _U_, const uint8_t *data,
148                              size_t length, int flags _U_, void *user_data) {
149   struct Connection *connection;
150   int rv;
151   connection = (struct Connection *)user_data;
152   connection->want_io = IO_NONE;
153   ERR_clear_error();
154   rv = SSL_write(connection->ssl, data, (int)length);
155   if (rv <= 0) {
156     int err = SSL_get_error(connection->ssl, rv);
157     if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
158       connection->want_io =
159           (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
160       rv = NGHTTP2_ERR_WOULDBLOCK;
161     } else {
162       rv = NGHTTP2_ERR_CALLBACK_FAILURE;
163     }
164   }
165   return rv;
166 }
167
168 /*
169  * The implementation of nghttp2_recv_callback type. Here we read data
170  * from the network and write them in |buf|. The capacity of |buf| is
171  * |length| bytes. Returns the number of bytes stored in |buf|. See
172  * the documentation of nghttp2_recv_callback for the details.
173  */
174 static ssize_t recv_callback(nghttp2_session *session _U_, uint8_t *buf,
175                              size_t length, int flags _U_, void *user_data) {
176   struct Connection *connection;
177   int rv;
178   connection = (struct Connection *)user_data;
179   connection->want_io = IO_NONE;
180   ERR_clear_error();
181   rv = SSL_read(connection->ssl, buf, (int)length);
182   if (rv < 0) {
183     int err = SSL_get_error(connection->ssl, rv);
184     if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
185       connection->want_io =
186           (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE);
187       rv = NGHTTP2_ERR_WOULDBLOCK;
188     } else {
189       rv = NGHTTP2_ERR_CALLBACK_FAILURE;
190     }
191   } else if (rv == 0) {
192     rv = NGHTTP2_ERR_EOF;
193   }
194   return rv;
195 }
196
197 static int on_frame_send_callback(nghttp2_session *session,
198                                   const nghttp2_frame *frame,
199                                   void *user_data _U_) {
200   size_t i;
201   switch (frame->hd.type) {
202   case NGHTTP2_HEADERS:
203     if (nghttp2_session_get_stream_user_data(session, frame->hd.stream_id)) {
204       const nghttp2_nv *nva = frame->headers.nva;
205       printf("[INFO] C ----------------------------> S (HEADERS)\n");
206       for (i = 0; i < frame->headers.nvlen; ++i) {
207         fwrite(nva[i].name, nva[i].namelen, 1, stdout);
208         printf(": ");
209         fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
210         printf("\n");
211       }
212     }
213     break;
214   case NGHTTP2_RST_STREAM:
215     printf("[INFO] C ----------------------------> S (RST_STREAM)\n");
216     break;
217   case NGHTTP2_GOAWAY:
218     printf("[INFO] C ----------------------------> S (GOAWAY)\n");
219     break;
220   }
221   return 0;
222 }
223
224 static int on_frame_recv_callback(nghttp2_session *session,
225                                   const nghttp2_frame *frame,
226                                   void *user_data _U_) {
227   size_t i;
228   switch (frame->hd.type) {
229   case NGHTTP2_HEADERS:
230     if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
231       const nghttp2_nv *nva = frame->headers.nva;
232       struct Request *req;
233       req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
234       if (req) {
235         printf("[INFO] C <---------------------------- S (HEADERS)\n");
236         for (i = 0; i < frame->headers.nvlen; ++i) {
237           fwrite(nva[i].name, nva[i].namelen, 1, stdout);
238           printf(": ");
239           fwrite(nva[i].value, nva[i].valuelen, 1, stdout);
240           printf("\n");
241         }
242       }
243     }
244     break;
245   case NGHTTP2_RST_STREAM:
246     printf("[INFO] C <---------------------------- S (RST_STREAM)\n");
247     break;
248   case NGHTTP2_GOAWAY:
249     printf("[INFO] C <---------------------------- S (GOAWAY)\n");
250     break;
251   }
252   return 0;
253 }
254
255 /*
256  * The implementation of nghttp2_on_stream_close_callback type. We use
257  * this function to know the response is fully received. Since we just
258  * fetch 1 resource in this program, after reception of the response,
259  * we submit GOAWAY and close the session.
260  */
261 static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
262                                     uint32_t error_code _U_,
263                                     void *user_data _U_) {
264   struct Request *req;
265   req = nghttp2_session_get_stream_user_data(session, stream_id);
266   if (req) {
267     int rv;
268     rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
269
270     if (rv != 0) {
271       diec("nghttp2_session_terminate_session", rv);
272     }
273   }
274   return 0;
275 }
276
277 #define MAX_OUTLEN 4096
278
279 /*
280  * The implementation of nghttp2_on_data_chunk_recv_callback type. We
281  * use this function to print the received response body.
282  */
283 static int on_data_chunk_recv_callback(nghttp2_session *session,
284                                        uint8_t flags _U_, int32_t stream_id,
285                                        const uint8_t *data, size_t len,
286                                        void *user_data _U_) {
287   struct Request *req;
288   req = nghttp2_session_get_stream_user_data(session, stream_id);
289   if (req) {
290     printf("[INFO] C <---------------------------- S (DATA chunk)\n"
291            "%lu bytes\n",
292            (unsigned long int)len);
293     fwrite(data, 1, len, stdout);
294     printf("\n");
295   }
296   return 0;
297 }
298
299 /*
300  * Setup callback functions. nghttp2 API offers many callback
301  * functions, but most of them are optional. The send_callback is
302  * always required. Since we use nghttp2_session_recv(), the
303  * recv_callback is also required.
304  */
305 static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks) {
306   nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
307
308   nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
309
310   nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
311                                                        on_frame_send_callback);
312
313   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
314                                                        on_frame_recv_callback);
315
316   nghttp2_session_callbacks_set_on_stream_close_callback(
317       callbacks, on_stream_close_callback);
318
319   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
320       callbacks, on_data_chunk_recv_callback);
321 }
322
323 /*
324  * Callback function for TLS NPN. Since this program only supports
325  * HTTP/2 protocol, if server does not offer HTTP/2 the nghttp2
326  * library supports, we terminate program.
327  */
328 static int select_next_proto_cb(SSL *ssl _U_, unsigned char **out,
329                                 unsigned char *outlen, const unsigned char *in,
330                                 unsigned int inlen, void *arg _U_) {
331   int rv;
332   /* nghttp2_select_next_protocol() selects HTTP/2 protocol the
333      nghttp2 library supports. */
334   rv = nghttp2_select_next_protocol(out, outlen, in, inlen);
335   if (rv <= 0) {
336     die("Server did not advertise HTTP/2 protocol");
337   }
338   return SSL_TLSEXT_ERR_OK;
339 }
340
341 /*
342  * Setup SSL/TLS context.
343  */
344 static void init_ssl_ctx(SSL_CTX *ssl_ctx) {
345   /* Disable SSLv2 and enable all workarounds for buggy servers */
346   SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
347   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
348   SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
349   /* Set NPN callback */
350   SSL_CTX_set_next_proto_select_cb(ssl_ctx, select_next_proto_cb, NULL);
351 }
352
353 static void ssl_handshake(SSL *ssl, int fd) {
354   int rv;
355   if (SSL_set_fd(ssl, fd) == 0) {
356     dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL));
357   }
358   ERR_clear_error();
359   rv = SSL_connect(ssl);
360   if (rv <= 0) {
361     dief("SSL_connect", ERR_error_string(ERR_get_error(), NULL));
362   }
363 }
364
365 /*
366  * Connects to the host |host| and port |port|.  This function returns
367  * the file descriptor of the client socket.
368  */
369 static int connect_to(const char *host, uint16_t port) {
370   struct addrinfo hints;
371   int fd = -1;
372   int rv;
373   char service[NI_MAXSERV];
374   struct addrinfo *res, *rp;
375   snprintf(service, sizeof(service), "%u", port);
376   memset(&hints, 0, sizeof(struct addrinfo));
377   hints.ai_family = AF_UNSPEC;
378   hints.ai_socktype = SOCK_STREAM;
379   rv = getaddrinfo(host, service, &hints, &res);
380   if (rv != 0) {
381     dief("getaddrinfo", gai_strerror(rv));
382   }
383   for (rp = res; rp; rp = rp->ai_next) {
384     fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
385     if (fd == -1) {
386       continue;
387     }
388     while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 &&
389            errno == EINTR)
390       ;
391     if (rv == 0) {
392       break;
393     }
394     close(fd);
395     fd = -1;
396   }
397   freeaddrinfo(res);
398   return fd;
399 }
400
401 static void make_non_block(int fd) {
402   int flags, rv;
403   while ((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR)
404     ;
405   if (flags == -1) {
406     dief("fcntl", strerror(errno));
407   }
408   while ((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR)
409     ;
410   if (rv == -1) {
411     dief("fcntl", strerror(errno));
412   }
413 }
414
415 static void set_tcp_nodelay(int fd) {
416   int val = 1;
417   int rv;
418   rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val));
419   if (rv == -1) {
420     dief("setsockopt", strerror(errno));
421   }
422 }
423
424 /*
425  * Update |pollfd| based on the state of |connection|.
426  */
427 static void ctl_poll(struct pollfd *pollfd, struct Connection *connection) {
428   pollfd->events = 0;
429   if (nghttp2_session_want_read(connection->session) ||
430       connection->want_io == WANT_READ) {
431     pollfd->events |= POLLIN;
432   }
433   if (nghttp2_session_want_write(connection->session) ||
434       connection->want_io == WANT_WRITE) {
435     pollfd->events |= POLLOUT;
436   }
437 }
438
439 /*
440  * Submits the request |req| to the connection |connection|.  This
441  * function does not send packets; just append the request to the
442  * internal queue in |connection->session|.
443  */
444 static void submit_request(struct Connection *connection, struct Request *req) {
445   int32_t stream_id;
446   /* Make sure that the last item is NULL */
447   const nghttp2_nv nva[] = {MAKE_NV(":method", "GET"),
448                             MAKE_NV_CS(":path", req->path),
449                             MAKE_NV(":scheme", "https"),
450                             MAKE_NV_CS(":authority", req->hostport),
451                             MAKE_NV("accept", "*/*"),
452                             MAKE_NV("user-agent", "nghttp2/" NGHTTP2_VERSION)};
453
454   stream_id = nghttp2_submit_request(connection->session, NULL, nva,
455                                      sizeof(nva) / sizeof(nva[0]), NULL, req);
456
457   if (stream_id < 0) {
458     diec("nghttp2_submit_request", stream_id);
459   }
460
461   req->stream_id = stream_id;
462   printf("[INFO] Stream ID = %d\n", stream_id);
463 }
464
465 /*
466  * Performs the network I/O.
467  */
468 static void exec_io(struct Connection *connection) {
469   int rv;
470   rv = nghttp2_session_recv(connection->session);
471   if (rv != 0) {
472     diec("nghttp2_session_recv", rv);
473   }
474   rv = nghttp2_session_send(connection->session);
475   if (rv != 0) {
476     diec("nghttp2_session_send", rv);
477   }
478 }
479
480 static void request_init(struct Request *req, const struct URI *uri) {
481   req->host = strcopy(uri->host, uri->hostlen);
482   req->port = uri->port;
483   req->path = strcopy(uri->path, uri->pathlen);
484   req->hostport = strcopy(uri->hostport, uri->hostportlen);
485   req->stream_id = -1;
486 }
487
488 static void request_free(struct Request *req) {
489   free(req->host);
490   free(req->path);
491   free(req->hostport);
492 }
493
494 /*
495  * Fetches the resource denoted by |uri|.
496  */
497 static void fetch_uri(const struct URI *uri) {
498   nghttp2_session_callbacks *callbacks;
499   int fd;
500   SSL_CTX *ssl_ctx;
501   SSL *ssl;
502   struct Request req;
503   struct Connection connection;
504   int rv;
505   nfds_t npollfds = 1;
506   struct pollfd pollfds[1];
507
508   request_init(&req, uri);
509
510   /* Establish connection and setup SSL */
511   fd = connect_to(req.host, req.port);
512   if (fd == -1) {
513     die("Could not open file descriptor");
514   }
515   ssl_ctx = SSL_CTX_new(SSLv23_client_method());
516   if (ssl_ctx == NULL) {
517     dief("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
518   }
519   init_ssl_ctx(ssl_ctx);
520   ssl = SSL_new(ssl_ctx);
521   if (ssl == NULL) {
522     dief("SSL_new", ERR_error_string(ERR_get_error(), NULL));
523   }
524   /* To simplify the program, we perform SSL/TLS handshake in blocking
525      I/O. */
526   ssl_handshake(ssl, fd);
527
528   connection.ssl = ssl;
529   connection.want_io = IO_NONE;
530
531   /* Send connection header in blocking I/O mode */
532   rv = SSL_write(ssl, NGHTTP2_CLIENT_CONNECTION_PREFACE,
533                  NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN);
534   if (rv <= 0) {
535     dief("SSL_write failed: could not send connection preface",
536          ERR_error_string(ERR_get_error(), NULL));
537   }
538
539   /* Here make file descriptor non-block */
540   make_non_block(fd);
541   set_tcp_nodelay(fd);
542
543   printf("[INFO] SSL/TLS handshake completed\n");
544
545   rv = nghttp2_session_callbacks_new(&callbacks);
546
547   if (rv != 0) {
548     diec("nghttp2_session_callbacks_new", rv);
549   }
550
551   setup_nghttp2_callbacks(callbacks);
552
553   rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);
554
555   nghttp2_session_callbacks_del(callbacks);
556
557   if (rv != 0) {
558     diec("nghttp2_session_client_new", rv);
559   }
560
561   nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
562
563   /* Submit the HTTP request to the outbound queue. */
564   submit_request(&connection, &req);
565
566   pollfds[0].fd = fd;
567   ctl_poll(pollfds, &connection);
568
569   /* Event loop */
570   while (nghttp2_session_want_read(connection.session) ||
571          nghttp2_session_want_write(connection.session)) {
572     int nfds = poll(pollfds, npollfds, -1);
573     if (nfds == -1) {
574       dief("poll", strerror(errno));
575     }
576     if (pollfds[0].revents & (POLLIN | POLLOUT)) {
577       exec_io(&connection);
578     }
579     if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
580       die("Connection error");
581     }
582     ctl_poll(pollfds, &connection);
583   }
584
585   /* Resource cleanup */
586   nghttp2_session_del(connection.session);
587   SSL_shutdown(ssl);
588   SSL_free(ssl);
589   SSL_CTX_free(ssl_ctx);
590   shutdown(fd, SHUT_WR);
591   close(fd);
592   request_free(&req);
593 }
594
595 static int parse_uri(struct URI *res, const char *uri) {
596   /* We only interested in https */
597   size_t len, i, offset;
598   int ipv6addr = 0;
599   memset(res, 0, sizeof(struct URI));
600   len = strlen(uri);
601   if (len < 9 || memcmp("https://", uri, 8) != 0) {
602     return -1;
603   }
604   offset = 8;
605   res->host = res->hostport = &uri[offset];
606   res->hostlen = 0;
607   if (uri[offset] == '[') {
608     /* IPv6 literal address */
609     ++offset;
610     ++res->host;
611     ipv6addr = 1;
612     for (i = offset; i < len; ++i) {
613       if (uri[i] == ']') {
614         res->hostlen = i - offset;
615         offset = i + 1;
616         break;
617       }
618     }
619   } else {
620     const char delims[] = ":/?#";
621     for (i = offset; i < len; ++i) {
622       if (strchr(delims, uri[i]) != NULL) {
623         break;
624       }
625     }
626     res->hostlen = i - offset;
627     offset = i;
628   }
629   if (res->hostlen == 0) {
630     return -1;
631   }
632   /* Assuming https */
633   res->port = 443;
634   if (offset < len) {
635     if (uri[offset] == ':') {
636       /* port */
637       const char delims[] = "/?#";
638       int port = 0;
639       ++offset;
640       for (i = offset; i < len; ++i) {
641         if (strchr(delims, uri[i]) != NULL) {
642           break;
643         }
644         if ('0' <= uri[i] && uri[i] <= '9') {
645           port *= 10;
646           port += uri[i] - '0';
647           if (port > 65535) {
648             return -1;
649           }
650         } else {
651           return -1;
652         }
653       }
654       if (port == 0) {
655         return -1;
656       }
657       offset = i;
658       res->port = port;
659     }
660   }
661   res->hostportlen = uri + offset + ipv6addr - res->host;
662   for (i = offset; i < len; ++i) {
663     if (uri[i] == '#') {
664       break;
665     }
666   }
667   if (i - offset == 0) {
668     res->path = "/";
669     res->pathlen = 1;
670   } else {
671     res->path = &uri[offset];
672     res->pathlen = i - offset;
673   }
674   return 0;
675 }
676
677 int main(int argc, char **argv) {
678   struct URI uri;
679   struct sigaction act;
680   int rv;
681
682   if (argc < 2) {
683     die("Specify a https URI");
684   }
685
686   memset(&act, 0, sizeof(struct sigaction));
687   act.sa_handler = SIG_IGN;
688   sigaction(SIGPIPE, &act, 0);
689
690   OPENSSL_config(NULL);
691   OpenSSL_add_all_algorithms();
692   SSL_load_error_strings();
693   SSL_library_init();
694
695   rv = parse_uri(&uri, argv[1]);
696   if (rv != 0) {
697     die("parse_uri failed");
698   }
699   fetch_uri(&uri);
700   return EXIT_SUCCESS;
701 }