xref: /curl/tests/http/clients/tls-session-reuse.c (revision 32101010)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 /* <DESC>
25  * TLS session reuse
26  * </DESC>
27  */
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdint.h>
31 #include <string.h>
32 #include <inttypes.h>
33 /* #include <error.h> */
34 #include <errno.h>
35 #include <curl/curl.h>
36 #include <curl/mprintf.h>
37 
38 
log_line_start(FILE * log,const char * idsbuf,curl_infotype type)39 static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type)
40 {
41   /*
42    * This is the trace look that is similar to what libcurl makes on its
43    * own.
44    */
45   static const char * const s_infotype[] = {
46     "* ", "< ", "> ", "{ ", "} ", "{ ", "} "
47   };
48   if(idsbuf && *idsbuf)
49     fprintf(log, "%s%s", idsbuf, s_infotype[type]);
50   else
51     fputs(s_infotype[type], log);
52 }
53 
54 #define TRC_IDS_FORMAT_IDS_1  "[%" CURL_FORMAT_CURL_OFF_T "-x] "
55 #define TRC_IDS_FORMAT_IDS_2  "[%" CURL_FORMAT_CURL_OFF_T "-%" \
56                                    CURL_FORMAT_CURL_OFF_T "] "
57 /*
58 ** callback for CURLOPT_DEBUGFUNCTION
59 */
debug_cb(CURL * handle,curl_infotype type,char * data,size_t size,void * userdata)60 static int debug_cb(CURL *handle, curl_infotype type,
61                     char *data, size_t size,
62                     void *userdata)
63 {
64   FILE *output = stderr;
65   static int newl = 0;
66   static int traced_data = 0;
67   char idsbuf[60];
68   curl_off_t xfer_id, conn_id;
69 
70   (void)handle; /* not used */
71   (void)userdata;
72 
73   if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) {
74     if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) &&
75         conn_id >= 0) {
76       curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2,
77                      xfer_id, conn_id);
78     }
79     else {
80       curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id);
81     }
82   }
83   else
84     idsbuf[0] = 0;
85 
86   switch(type) {
87   case CURLINFO_HEADER_OUT:
88     if(size > 0) {
89       size_t st = 0;
90       size_t i;
91       for(i = 0; i < size - 1; i++) {
92         if(data[i] == '\n') { /* LF */
93           if(!newl) {
94             log_line_start(output, idsbuf, type);
95           }
96           (void)fwrite(data + st, i - st + 1, 1, output);
97           st = i + 1;
98           newl = 0;
99         }
100       }
101       if(!newl)
102         log_line_start(output, idsbuf, type);
103       (void)fwrite(data + st, i - st + 1, 1, output);
104     }
105     newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
106     traced_data = 0;
107     break;
108   case CURLINFO_TEXT:
109   case CURLINFO_HEADER_IN:
110     if(!newl)
111       log_line_start(output, idsbuf, type);
112     (void)fwrite(data, size, 1, output);
113     newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
114     traced_data = 0;
115     break;
116   case CURLINFO_DATA_OUT:
117   case CURLINFO_DATA_IN:
118   case CURLINFO_SSL_DATA_IN:
119   case CURLINFO_SSL_DATA_OUT:
120     if(!traced_data) {
121       if(!newl)
122         log_line_start(output, idsbuf, type);
123       fprintf(output, "[%ld bytes data]\n", (long)size);
124       newl = 0;
125       traced_data = 1;
126     }
127     break;
128   default: /* nada */
129     newl = 0;
130     traced_data = 1;
131     break;
132   }
133 
134   return 0;
135 }
136 
write_cb(char * ptr,size_t size,size_t nmemb,void * opaque)137 static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *opaque)
138 {
139   (void)ptr;
140   (void)opaque;
141   return size * nmemb;
142 }
143 
add_transfer(CURLM * multi,CURLSH * share,struct curl_slist * resolve,const char * url,int http_version)144 static void add_transfer(CURLM *multi, CURLSH *share,
145                          struct curl_slist *resolve,
146                          const char *url, int http_version)
147 {
148   CURL *easy;
149   CURLMcode mc;
150 
151   easy = curl_easy_init();
152   if(!easy) {
153     fprintf(stderr, "curl_easy_init failed\n");
154     exit(1);
155   }
156   curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
157   curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb);
158   curl_easy_setopt(easy, CURLOPT_URL, url);
159   curl_easy_setopt(easy, CURLOPT_SHARE, share);
160   curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L);
161   curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L);
162   curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
163   curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, http_version);
164   curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb);
165   curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
166   curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
167   curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
168   if(resolve)
169     curl_easy_setopt(easy, CURLOPT_RESOLVE, resolve);
170 
171 
172   mc = curl_multi_add_handle(multi, easy);
173   if(mc != CURLM_OK) {
174     fprintf(stderr, "curl_multi_add_handle: %s\n",
175            curl_multi_strerror(mc));
176     exit(1);
177   }
178 }
179 
main(int argc,char * argv[])180 int main(int argc, char *argv[])
181 {
182   const char *url;
183   CURLM *multi;
184   CURLMcode mc;
185   int running_handles = 0, numfds;
186   CURLMsg *msg;
187   CURLSH *share;
188   CURLU *cu;
189   struct curl_slist resolve;
190   char resolve_buf[1024];
191   int msgs_in_queue;
192   int add_more, waits, ongoing = 0;
193   char *host, *port;
194   int http_version = CURL_HTTP_VERSION_1_1;
195 
196   if(argc != 3) {
197     fprintf(stderr, "%s proto URL\n", argv[0]);
198     exit(2);
199   }
200 
201   if(!strcmp("h2", argv[1]))
202     http_version = CURL_HTTP_VERSION_2;
203   else if(!strcmp("h3", argv[1]))
204     http_version = CURL_HTTP_VERSION_3ONLY;
205 
206   url = argv[2];
207   cu = curl_url();
208   if(!cu) {
209     fprintf(stderr, "out of memory\n");
210     exit(1);
211   }
212   if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
213     fprintf(stderr, "not a URL: '%s'\n", url);
214     exit(1);
215   }
216   if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
217     fprintf(stderr, "could not get host of '%s'\n", url);
218     exit(1);
219   }
220   if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
221     fprintf(stderr, "could not get port of '%s'\n", url);
222     exit(1);
223   }
224 
225    memset(&resolve, 0, sizeof(resolve));
226    curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1,
227                   "%s:%s:127.0.0.1", host, port);
228   curl_slist_append(&resolve, resolve_buf);
229 
230   multi = curl_multi_init();
231   if(!multi) {
232     fprintf(stderr, "curl_multi_init failed\n");
233     exit(1);
234   }
235 
236   share = curl_share_init();
237   if(!share) {
238     fprintf(stderr, "curl_share_init failed\n");
239     exit(1);
240   }
241   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
242 
243 
244   add_transfer(multi, share, &resolve, url, http_version);
245   ++ongoing;
246   add_more = 6;
247   waits = 3;
248   do {
249     mc = curl_multi_perform(multi, &running_handles);
250     if(mc != CURLM_OK) {
251       fprintf(stderr, "curl_multi_perform: %s\n",
252              curl_multi_strerror(mc));
253       exit(1);
254     }
255 
256     if(running_handles) {
257       mc = curl_multi_poll(multi, NULL, 0, 1000000, &numfds);
258       if(mc != CURLM_OK) {
259         fprintf(stderr, "curl_multi_poll: %s\n",
260                curl_multi_strerror(mc));
261         exit(1);
262       }
263     }
264 
265     if(waits) {
266       --waits;
267     }
268     else {
269       while(add_more) {
270         add_transfer(multi, share, &resolve, url, http_version);
271         ++ongoing;
272         --add_more;
273       }
274     }
275 
276     /* Check for finished handles and remove. */
277     while((msg = curl_multi_info_read(multi, &msgs_in_queue))) {
278       if(msg->msg == CURLMSG_DONE) {
279         long status = 0;
280         curl_off_t xfer_id;
281         curl_easy_getinfo(msg->easy_handle, CURLINFO_XFER_ID, &xfer_id);
282         curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
283         if(msg->data.result == CURLE_SEND_ERROR ||
284             msg->data.result == CURLE_RECV_ERROR) {
285           /* We get these if the server had a GOAWAY in transit on
286            * re-using a connection */
287         }
288         else if(msg->data.result) {
289           fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
290                   ": failed with %d\n", xfer_id, msg->data.result);
291           exit(1);
292         }
293         else if(status != 200) {
294           fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
295                   ": wrong http status %ld (expected 200)\n", xfer_id, status);
296           exit(1);
297         }
298         curl_multi_remove_handle(multi, msg->easy_handle);
299         curl_easy_cleanup(msg->easy_handle);
300         --ongoing;
301         fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T" retiring "
302                 "(%d now running)\n", xfer_id, running_handles);
303       }
304     }
305 
306     fprintf(stderr, "running_handles=%d, yet_to_start=%d\n",
307             running_handles, add_more);
308 
309   } while(ongoing || add_more);
310 
311   fprintf(stderr, "exiting\n");
312   exit(EXIT_SUCCESS);
313 }
314