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