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