Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/tests/libtest/cli_hx_download.c
2653 views
1
/***************************************************************************
2
* _ _ ____ _
3
* Project ___| | | | _ \| |
4
* / __| | | | |_) | |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
7
*
8
* Copyright (C) Daniel Stenberg, <[email protected]>, 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
#include "first.h"
25
26
#include "testtrace.h"
27
28
#include "curl_mem_undef.h"
29
30
#if defined(USE_QUICHE) || defined(USE_OPENSSL)
31
#include <openssl/ssl.h>
32
#endif
33
#ifdef USE_WOLFSSL
34
#include <wolfssl/options.h>
35
#include <wolfssl/version.h>
36
#include <wolfssl/ssl.h>
37
#endif
38
#ifdef USE_GNUTLS
39
#include <gnutls/gnutls.h>
40
#endif
41
#ifdef USE_MBEDTLS
42
#include <mbedtls/ssl.h>
43
#endif
44
#ifdef USE_RUSTLS
45
#include <rustls.h>
46
#endif
47
48
#include "memdebug.h"
49
50
static int verbose_d = 1;
51
52
struct transfer_d {
53
size_t idx;
54
CURL *curl;
55
char filename[128];
56
FILE *out;
57
curl_off_t recv_size;
58
curl_off_t fail_at;
59
curl_off_t pause_at;
60
curl_off_t abort_at;
61
int started;
62
int paused;
63
int resumed;
64
int done;
65
int checked_ssl;
66
CURLcode result;
67
};
68
69
static size_t transfer_count_d = 1;
70
static struct transfer_d *transfer_d;
71
static int forbid_reuse_d = 0;
72
73
static struct transfer_d *get_transfer_for_easy_d(CURL *curl)
74
{
75
size_t i;
76
for(i = 0; i < transfer_count_d; ++i) {
77
if(curl == transfer_d[i].curl)
78
return &transfer_d[i];
79
}
80
return NULL;
81
}
82
83
static size_t my_write_d_cb(char *buf, size_t nitems, size_t buflen,
84
void *userdata)
85
{
86
struct transfer_d *t = userdata;
87
size_t blen = (nitems * buflen);
88
size_t nwritten;
89
90
curl_mfprintf(stderr, "[t-%zu] RECV %zu bytes, "
91
"total=%" CURL_FORMAT_CURL_OFF_T ", "
92
"pause_at=%" CURL_FORMAT_CURL_OFF_T "\n",
93
t->idx, blen, t->recv_size, t->pause_at);
94
if(!t->out) {
95
curl_msnprintf(t->filename, sizeof(t->filename)-1, "download_%zu.data",
96
t->idx);
97
t->out = curlx_fopen(t->filename, "wb");
98
if(!t->out)
99
return 0;
100
}
101
102
if(!t->resumed &&
103
t->recv_size < t->pause_at &&
104
((t->recv_size + (curl_off_t)blen) >= t->pause_at)) {
105
curl_mfprintf(stderr, "[t-%zu] PAUSE\n", t->idx);
106
t->paused = 1;
107
return CURL_WRITEFUNC_PAUSE;
108
}
109
110
nwritten = fwrite(buf, nitems, buflen, t->out);
111
if(nwritten < blen) {
112
curl_mfprintf(stderr, "[t-%zu] write failure\n", t->idx);
113
return 0;
114
}
115
t->recv_size += (curl_off_t)nwritten;
116
if(t->fail_at > 0 && t->recv_size >= t->fail_at) {
117
curl_mfprintf(stderr, "[t-%zu] FAIL by write callback at "
118
"%" CURL_FORMAT_CURL_OFF_T " bytes\n", t->idx, t->recv_size);
119
return CURL_WRITEFUNC_ERROR;
120
}
121
122
return (size_t)nwritten;
123
}
124
125
static int my_progress_d_cb(void *userdata,
126
curl_off_t dltotal, curl_off_t dlnow,
127
curl_off_t ultotal, curl_off_t ulnow)
128
{
129
struct transfer_d *t = userdata;
130
(void)ultotal;
131
(void)ulnow;
132
(void)dltotal;
133
if(t->abort_at > 0 && dlnow >= t->abort_at) {
134
curl_mfprintf(stderr, "[t-%zu] ABORT by progress_cb at "
135
"%" CURL_FORMAT_CURL_OFF_T " bytes\n", t->idx, dlnow);
136
return 1;
137
}
138
139
#if defined(USE_QUICHE) || defined(USE_OPENSSL) || defined(USE_WOLFSSL) || \
140
defined(USE_GNUTLS) || defined(USE_MBEDTLS) || defined(USE_RUSTLS)
141
if(!t->checked_ssl && dlnow > 0) {
142
struct curl_tlssessioninfo *tls;
143
CURLcode res;
144
145
t->checked_ssl = TRUE;
146
res = curl_easy_getinfo(t->curl, CURLINFO_TLS_SSL_PTR, &tls);
147
if(res) {
148
curl_mfprintf(stderr, "[t-%zu] info CURLINFO_TLS_SSL_PTR failed: %d\n",
149
t->idx, res);
150
assert(0);
151
}
152
else {
153
switch(tls->backend) {
154
#if defined(USE_QUICHE) || defined(USE_OPENSSL)
155
case CURLSSLBACKEND_OPENSSL: {
156
const char *version = SSL_get_version((SSL*)tls->internals);
157
assert(version);
158
assert(strcmp(version, "unknown"));
159
curl_mfprintf(stderr, "[t-%zu] info OpenSSL using %s\n",
160
t->idx, version);
161
break;
162
}
163
#endif
164
#ifdef USE_WOLFSSL
165
case CURLSSLBACKEND_WOLFSSL: {
166
const char *version = wolfSSL_get_version((WOLFSSL*)tls->internals);
167
assert(version);
168
assert(strcmp(version, "unknown"));
169
curl_mfprintf(stderr, "[t-%zu] info wolfSSL using %s\n",
170
t->idx, version);
171
break;
172
}
173
#endif
174
#ifdef USE_GNUTLS
175
case CURLSSLBACKEND_GNUTLS: {
176
int v = gnutls_protocol_get_version((gnutls_session_t)tls->internals);
177
assert(v);
178
curl_mfprintf(stderr, "[t-%zu] info GnuTLS using %s\n",
179
t->idx, gnutls_protocol_get_name(v));
180
break;
181
}
182
#endif
183
#ifdef USE_MBEDTLS
184
case CURLSSLBACKEND_MBEDTLS: {
185
const char *version = mbedtls_ssl_get_version(
186
(mbedtls_ssl_context*)tls->internals);
187
assert(version);
188
assert(strcmp(version, "unknown"));
189
curl_mfprintf(stderr, "[t-%zu] info mbedTLS using %s\n",
190
t->idx, version);
191
break;
192
}
193
#endif
194
#ifdef USE_RUSTLS
195
case CURLSSLBACKEND_RUSTLS: {
196
int v = rustls_connection_get_protocol_version(
197
(struct rustls_connection*)tls->internals);
198
assert(v);
199
curl_mfprintf(stderr, "[t-%zu] info rustls TLS version 0x%x\n",
200
t->idx, v);
201
break;
202
}
203
#endif
204
default:
205
curl_mfprintf(stderr, "[t-%zu] info SSL_PTR backend=%d, ptr=%p\n",
206
t->idx, tls->backend, (void *)tls->internals);
207
break;
208
}
209
}
210
}
211
#endif
212
return 0;
213
}
214
215
static int setup_hx_download(CURL *curl, const char *url, struct transfer_d *t,
216
long http_version, struct curl_slist *host,
217
CURLSH *share, int use_earlydata,
218
int fresh_connect)
219
{
220
curl_easy_setopt(curl, CURLOPT_SHARE, share);
221
curl_easy_setopt(curl, CURLOPT_URL, url);
222
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, http_version);
223
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
224
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
225
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
226
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, (long)(128 * 1024));
227
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_write_d_cb);
228
curl_easy_setopt(curl, CURLOPT_WRITEDATA, t);
229
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
230
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, my_progress_d_cb);
231
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, t);
232
if(use_earlydata)
233
curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_EARLYDATA);
234
if(forbid_reuse_d)
235
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1L);
236
if(host)
237
curl_easy_setopt(curl, CURLOPT_RESOLVE, host);
238
if(fresh_connect)
239
curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1L);
240
241
/* please be verbose */
242
if(verbose_d) {
243
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
244
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, cli_debug_cb);
245
}
246
247
/* wait for pipe connection to confirm */
248
curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L);
249
250
return 0; /* all is good */
251
}
252
253
static void usage_hx_download(const char *msg)
254
{
255
if(msg)
256
curl_mfprintf(stderr, "%s\n", msg);
257
curl_mfprintf(stderr,
258
"usage: [options] url\n"
259
" download a url with following options:\n"
260
" -a abort paused transfer\n"
261
" -m number max parallel downloads\n"
262
" -e use TLS early data when possible\n"
263
" -f forbid connection reuse\n"
264
" -n number total downloads\n");
265
curl_mfprintf(stderr,
266
" -A number abort transfer after `number` response bytes\n"
267
" -F number fail writing response after `number` response bytes\n"
268
" -M number max concurrent connections to a host\n"
269
" -P number pause transfer after `number` response bytes\n"
270
" -r <host>:<port>:<addr> resolve information\n"
271
" -T number max concurrent connections total\n"
272
" -V http_version (http/1.1, h2, h3) http version to use\n"
273
);
274
}
275
276
/*
277
* Download a file over HTTP/2, take care of server push.
278
*/
279
static CURLcode test_cli_hx_download(const char *URL)
280
{
281
CURLM *multi = NULL;
282
struct CURLMsg *m;
283
CURLSH *share = NULL;
284
const char *url;
285
size_t i, n, max_parallel = 1;
286
size_t active_transfers;
287
size_t pause_offset = 0;
288
size_t abort_offset = 0;
289
size_t fail_offset = 0;
290
int abort_paused = 0, use_earlydata = 0;
291
struct transfer_d *t = NULL;
292
long http_version = CURL_HTTP_VERSION_2_0;
293
int ch;
294
struct curl_slist *host = NULL;
295
char *resolve = NULL;
296
size_t max_host_conns = 0;
297
size_t max_total_conns = 0;
298
int fresh_connect = 0;
299
CURLcode result = CURLE_OK;
300
301
(void)URL;
302
303
while((ch = cgetopt(test_argc, test_argv, "aefhm:n:xA:F:M:P:r:T:V:"))
304
!= -1) {
305
switch(ch) {
306
case 'h':
307
usage_hx_download(NULL);
308
result = (CURLcode)2;
309
goto optcleanup;
310
case 'a':
311
abort_paused = 1;
312
break;
313
case 'e':
314
use_earlydata = 1;
315
break;
316
case 'f':
317
forbid_reuse_d = 1;
318
break;
319
case 'm':
320
max_parallel = (size_t)atol(coptarg);
321
break;
322
case 'n':
323
transfer_count_d = (size_t)atol(coptarg);
324
break;
325
case 'x':
326
fresh_connect = 1;
327
break;
328
case 'A':
329
abort_offset = (size_t)atol(coptarg);
330
break;
331
case 'F':
332
fail_offset = (size_t)atol(coptarg);
333
break;
334
case 'M':
335
max_host_conns = (size_t)atol(coptarg);
336
break;
337
case 'P':
338
pause_offset = (size_t)atol(coptarg);
339
break;
340
case 'r':
341
free(resolve);
342
resolve = strdup(coptarg);
343
break;
344
case 'T':
345
max_total_conns = (size_t)atol(coptarg);
346
break;
347
case 'V': {
348
if(!strcmp("http/1.1", coptarg))
349
http_version = CURL_HTTP_VERSION_1_1;
350
else if(!strcmp("h2", coptarg))
351
http_version = CURL_HTTP_VERSION_2_0;
352
else if(!strcmp("h3", coptarg))
353
http_version = CURL_HTTP_VERSION_3ONLY;
354
else {
355
usage_hx_download("invalid http version");
356
result = (CURLcode)1;
357
goto optcleanup;
358
}
359
break;
360
}
361
default:
362
usage_hx_download("invalid option");
363
result = (CURLcode)1;
364
goto optcleanup;
365
}
366
}
367
test_argc -= coptind;
368
test_argv += coptind;
369
370
curl_global_trace("ids,time,http/2,http/3");
371
372
if(test_argc != 1) {
373
usage_hx_download("not enough arguments");
374
result = (CURLcode)2;
375
goto optcleanup;
376
}
377
url = test_argv[0];
378
379
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
380
curl_mfprintf(stderr, "curl_global_init() failed\n");
381
result = (CURLcode)3;
382
goto optcleanup;
383
}
384
385
if(resolve)
386
host = curl_slist_append(NULL, resolve);
387
388
share = curl_share_init();
389
if(!share) {
390
curl_mfprintf(stderr, "error allocating share\n");
391
result = (CURLcode)1;
392
goto cleanup;
393
}
394
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
395
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
396
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
397
/* curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); */
398
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
399
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
400
401
transfer_d = calloc(transfer_count_d, sizeof(*transfer_d));
402
if(!transfer_d) {
403
curl_mfprintf(stderr, "error allocating transfer structs\n");
404
result = (CURLcode)1;
405
goto cleanup;
406
}
407
408
multi = curl_multi_init();
409
curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
410
curl_multi_setopt(multi, CURLMOPT_MAX_TOTAL_CONNECTIONS,
411
(long)max_total_conns);
412
curl_multi_setopt(multi, CURLMOPT_MAX_HOST_CONNECTIONS,
413
(long)max_host_conns);
414
415
active_transfers = 0;
416
for(i = 0; i < transfer_count_d; ++i) {
417
t = &transfer_d[i];
418
t->idx = i;
419
t->abort_at = (curl_off_t)abort_offset;
420
t->fail_at = (curl_off_t)fail_offset;
421
t->pause_at = (curl_off_t)pause_offset;
422
}
423
424
n = (max_parallel < transfer_count_d) ? max_parallel : transfer_count_d;
425
for(i = 0; i < n; ++i) {
426
t = &transfer_d[i];
427
t->curl = curl_easy_init();
428
if(!t->curl ||
429
setup_hx_download(t->curl, url, t, http_version, host, share,
430
use_earlydata, fresh_connect)) {
431
curl_mfprintf(stderr, "[t-%zu] FAILED setup\n", i);
432
result = (CURLcode)1;
433
goto cleanup;
434
}
435
curl_multi_add_handle(multi, t->curl);
436
t->started = 1;
437
++active_transfers;
438
curl_mfprintf(stderr, "[t-%zu] STARTED\n", t->idx);
439
}
440
441
do {
442
int still_running; /* keep number of running handles */
443
CURLMcode mc = curl_multi_perform(multi, &still_running);
444
445
if(still_running) {
446
/* wait for activity, timeout or "nothing" */
447
mc = curl_multi_poll(multi, NULL, 0, 1000, NULL);
448
}
449
450
if(mc)
451
break;
452
453
do {
454
int msgq = 0;
455
m = curl_multi_info_read(multi, &msgq);
456
if(m && (m->msg == CURLMSG_DONE)) {
457
CURL *easy = m->easy_handle;
458
--active_transfers;
459
curl_multi_remove_handle(multi, easy);
460
t = get_transfer_for_easy_d(easy);
461
if(t) {
462
t->done = 1;
463
t->result = m->data.result;
464
curl_mfprintf(stderr, "[t-%zu] FINISHED with result %d\n",
465
t->idx, t->result);
466
if(use_earlydata) {
467
curl_off_t sent;
468
curl_easy_getinfo(easy, CURLINFO_EARLYDATA_SENT_T, &sent);
469
curl_mfprintf(stderr, "[t-%zu] EarlyData: "
470
"%" CURL_FORMAT_CURL_OFF_T "\n", t->idx, sent);
471
}
472
}
473
else {
474
curl_easy_cleanup(easy);
475
curl_mfprintf(stderr, "unknown FINISHED???\n");
476
}
477
}
478
479
/* nothing happening, maintenance */
480
if(abort_paused) {
481
/* abort paused transfers */
482
for(i = 0; i < transfer_count_d; ++i) {
483
t = &transfer_d[i];
484
if(!t->done && t->paused && t->curl) {
485
curl_multi_remove_handle(multi, t->curl);
486
t->done = 1;
487
active_transfers--;
488
curl_mfprintf(stderr, "[t-%zu] ABORTED\n", t->idx);
489
}
490
}
491
}
492
else {
493
/* resume one paused transfer */
494
for(i = 0; i < transfer_count_d; ++i) {
495
t = &transfer_d[i];
496
if(!t->done && t->paused) {
497
t->resumed = 1;
498
t->paused = 0;
499
curl_easy_pause(t->curl, CURLPAUSE_CONT);
500
curl_mfprintf(stderr, "[t-%zu] RESUMED\n", t->idx);
501
break;
502
}
503
}
504
}
505
506
while(active_transfers < max_parallel) {
507
for(i = 0; i < transfer_count_d; ++i) {
508
t = &transfer_d[i];
509
if(!t->started) {
510
t->curl = curl_easy_init();
511
if(!t->curl ||
512
setup_hx_download(t->curl, url, t, http_version, host, share,
513
use_earlydata, fresh_connect)) {
514
curl_mfprintf(stderr, "[t-%zu] FAILED setup\n", i);
515
result = (CURLcode)1;
516
goto cleanup;
517
}
518
curl_multi_add_handle(multi, t->curl);
519
t->started = 1;
520
++active_transfers;
521
curl_mfprintf(stderr, "[t-%zu] STARTED\n", t->idx);
522
break;
523
}
524
}
525
/* all started */
526
if(i == transfer_count_d)
527
break;
528
}
529
} while(m);
530
531
} while(active_transfers); /* as long as we have transfers going */
532
533
cleanup:
534
535
curl_multi_cleanup(multi);
536
537
if(transfer_d) {
538
for(i = 0; i < transfer_count_d; ++i) {
539
t = &transfer_d[i];
540
if(t->out) {
541
curlx_fclose(t->out);
542
t->out = NULL;
543
}
544
if(t->curl) {
545
curl_easy_cleanup(t->curl);
546
t->curl = NULL;
547
}
548
if(t->result)
549
result = t->result;
550
else /* on success we expect ssl to have been checked */
551
assert(t->checked_ssl);
552
}
553
free(transfer_d);
554
}
555
556
curl_share_cleanup(share);
557
curl_slist_free_all(host);
558
559
curl_global_cleanup();
560
561
optcleanup:
562
563
free(resolve);
564
565
return result;
566
}
567
568