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)144 static void add_transfer(CURLM *multi, CURLSH *share,
145 struct curl_slist *resolve, const char *url)
146 {
147 CURL *easy;
148 CURLMcode mc;
149
150 easy = curl_easy_init();
151 if(!easy) {
152 fprintf(stderr, "curl_easy_init failed\n");
153 exit(1);
154 }
155 curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
156 curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb);
157 curl_easy_setopt(easy, CURLOPT_URL, url);
158 curl_easy_setopt(easy, CURLOPT_SHARE, share);
159 curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L);
160 curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L);
161 curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
162 curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
163 curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb);
164 curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
165 curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
166 curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
167 if(resolve)
168 curl_easy_setopt(easy, CURLOPT_RESOLVE, resolve);
169
170
171 mc = curl_multi_add_handle(multi, easy);
172 if(mc != CURLM_OK) {
173 fprintf(stderr, "curl_multi_add_handle: %s\n",
174 curl_multi_strerror(mc));
175 exit(1);
176 }
177 }
178
main(int argc, char *argv[])179 int main(int argc, char *argv[])
180 {
181 const char *url;
182 CURLM *multi;
183 CURLMcode mc;
184 int running_handles = 0, numfds;
185 CURLMsg *msg;
186 CURLSH *share;
187 CURLU *cu;
188 struct curl_slist resolve;
189 char resolve_buf[1024];
190 int msgs_in_queue;
191 int add_more, waits, ongoing = 0;
192 char *host, *port;
193
194 if(argc != 2) {
195 fprintf(stderr, "%s URL\n", argv[0]);
196 exit(2);
197 }
198
199 url = argv[1];
200 cu = curl_url();
201 if(!cu) {
202 fprintf(stderr, "out of memory\n");
203 exit(1);
204 }
205 if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
206 fprintf(stderr, "not a URL: '%s'\n", url);
207 exit(1);
208 }
209 if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
210 fprintf(stderr, "could not get host of '%s'\n", url);
211 exit(1);
212 }
213 if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
214 fprintf(stderr, "could not get port of '%s'\n", url);
215 exit(1);
216 }
217
218 memset(&resolve, 0, sizeof(resolve));
219 curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1,
220 "%s:%s:127.0.0.1", host, port);
221 curl_slist_append(&resolve, resolve_buf);
222
223 multi = curl_multi_init();
224 if(!multi) {
225 fprintf(stderr, "curl_multi_init failed\n");
226 exit(1);
227 }
228
229 share = curl_share_init();
230 if(!share) {
231 fprintf(stderr, "curl_share_init failed\n");
232 exit(1);
233 }
234 curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
235
236
237 add_transfer(multi, share, &resolve, url);
238 ++ongoing;
239 add_more = 6;
240 waits = 3;
241 do {
242 mc = curl_multi_perform(multi, &running_handles);
243 if(mc != CURLM_OK) {
244 fprintf(stderr, "curl_multi_perform: %s\n",
245 curl_multi_strerror(mc));
246 exit(1);
247 }
248
249 if(running_handles) {
250 mc = curl_multi_poll(multi, NULL, 0, 1000000, &numfds);
251 if(mc != CURLM_OK) {
252 fprintf(stderr, "curl_multi_poll: %s\n",
253 curl_multi_strerror(mc));
254 exit(1);
255 }
256 }
257
258 if(waits) {
259 --waits;
260 }
261 else {
262 while(add_more) {
263 add_transfer(multi, share, &resolve, url);
264 ++ongoing;
265 --add_more;
266 }
267 }
268
269 /* Check for finished handles and remove. */
270 while((msg = curl_multi_info_read(multi, &msgs_in_queue))) {
271 if(msg->msg == CURLMSG_DONE) {
272 long status = 0;
273 curl_off_t xfer_id;
274 curl_easy_getinfo(msg->easy_handle, CURLINFO_XFER_ID, &xfer_id);
275 curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &status);
276 if(msg->data.result == CURLE_SEND_ERROR ||
277 msg->data.result == CURLE_RECV_ERROR) {
278 /* We get these if the server had a GOAWAY in transit on
279 * re-using a connection */
280 }
281 else if(msg->data.result) {
282 fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
283 ": failed with %d\n", xfer_id, msg->data.result);
284 exit(1);
285 }
286 else if(status != 200) {
287 fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T
288 ": wrong http status %ld (expected 200)\n", xfer_id, status);
289 exit(1);
290 }
291 curl_multi_remove_handle(multi, msg->easy_handle);
292 curl_easy_cleanup(msg->easy_handle);
293 --ongoing;
294 fprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T" retiring "
295 "(%d now running)\n", xfer_id, running_handles);
296 }
297 }
298
299 fprintf(stderr, "running_handles=%d, yet_to_start=%d\n",
300 running_handles, add_more);
301
302 } while(ongoing || add_more);
303
304 fprintf(stderr, "exiting\n");
305 exit(EXIT_SUCCESS);
306 }
307