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