Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/tests/http/clients/hx-upload.c
2066 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
/* <DESC>
25
* HTTP upload tests and tweaks
26
* </DESC>
27
*/
28
/* curl stuff */
29
#include <curl/curl.h>
30
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
35
#ifndef _MSC_VER
36
/* somewhat Unix-specific */
37
#include <unistd.h> /* getopt() */
38
#endif
39
40
#ifndef CURLPIPE_MULTIPLEX
41
#error "too old libcurl"
42
#endif
43
44
#ifndef _MSC_VER
45
static int verbose = 1;
46
47
static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type)
48
{
49
/*
50
* This is the trace look that is similar to what libcurl makes on its
51
* own.
52
*/
53
static const char * const s_infotype[] = {
54
"* ", "< ", "> ", "{ ", "} ", "{ ", "} "
55
};
56
if(idsbuf && *idsbuf)
57
fprintf(log, "%s%s", idsbuf, s_infotype[type]);
58
else
59
fputs(s_infotype[type], log);
60
}
61
62
#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] "
63
#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \
64
CURL_FORMAT_CURL_OFF_T "] "
65
/*
66
** callback for CURLOPT_DEBUGFUNCTION
67
*/
68
static int debug_cb(CURL *handle, curl_infotype type,
69
char *data, size_t size,
70
void *userdata)
71
{
72
FILE *output = stderr;
73
static int newl = 0;
74
static int traced_data = 0;
75
char idsbuf[60];
76
curl_off_t xfer_id, conn_id;
77
78
(void)handle; /* not used */
79
(void)userdata;
80
81
if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) {
82
if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) &&
83
conn_id >= 0) {
84
curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, xfer_id,
85
conn_id);
86
}
87
else {
88
curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id);
89
}
90
}
91
else
92
idsbuf[0] = 0;
93
94
switch(type) {
95
case CURLINFO_HEADER_OUT:
96
if(size > 0) {
97
size_t st = 0;
98
size_t i;
99
for(i = 0; i < size - 1; i++) {
100
if(data[i] == '\n') { /* LF */
101
if(!newl) {
102
log_line_start(output, idsbuf, type);
103
}
104
(void)fwrite(data + st, i - st + 1, 1, output);
105
st = i + 1;
106
newl = 0;
107
}
108
}
109
if(!newl)
110
log_line_start(output, idsbuf, type);
111
(void)fwrite(data + st, i - st + 1, 1, output);
112
}
113
newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
114
traced_data = 0;
115
break;
116
case CURLINFO_TEXT:
117
case CURLINFO_HEADER_IN:
118
if(!newl)
119
log_line_start(output, idsbuf, type);
120
(void)fwrite(data, size, 1, output);
121
newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
122
traced_data = 0;
123
break;
124
case CURLINFO_DATA_OUT:
125
case CURLINFO_DATA_IN:
126
case CURLINFO_SSL_DATA_IN:
127
case CURLINFO_SSL_DATA_OUT:
128
if(!traced_data) {
129
if(!newl)
130
log_line_start(output, idsbuf, type);
131
fprintf(output, "[%ld bytes data]\n", (long)size);
132
newl = 0;
133
traced_data = 1;
134
}
135
break;
136
default: /* nada */
137
newl = 0;
138
traced_data = 1;
139
break;
140
}
141
142
return 0;
143
}
144
145
struct transfer {
146
int idx;
147
CURL *easy;
148
const char *method;
149
char filename[128];
150
FILE *out;
151
curl_off_t send_total;
152
curl_off_t recv_size;
153
curl_off_t send_size;
154
curl_off_t fail_at;
155
curl_off_t pause_at;
156
curl_off_t abort_at;
157
int started;
158
int paused;
159
int resumed;
160
int done;
161
};
162
163
static size_t transfer_count = 1;
164
static struct transfer *transfers;
165
static int forbid_reuse = 0;
166
167
static struct transfer *get_transfer_for_easy(CURL *easy)
168
{
169
size_t i;
170
for(i = 0; i < transfer_count; ++i) {
171
if(easy == transfers[i].easy)
172
return &transfers[i];
173
}
174
return NULL;
175
}
176
177
static size_t my_write_cb(char *buf, size_t nitems, size_t buflen,
178
void *userdata)
179
{
180
struct transfer *t = userdata;
181
size_t blen = (nitems * buflen);
182
size_t nwritten;
183
184
fprintf(stderr, "[t-%d] RECV %ld bytes, total=%ld, pause_at=%ld\n",
185
t->idx, (long)blen, (long)t->recv_size, (long)t->pause_at);
186
if(!t->out) {
187
curl_msnprintf(t->filename, sizeof(t->filename)-1, "download_%u.data",
188
t->idx);
189
t->out = fopen(t->filename, "wb");
190
if(!t->out)
191
return 0;
192
}
193
194
nwritten = fwrite(buf, nitems, buflen, t->out);
195
if(nwritten < blen) {
196
fprintf(stderr, "[t-%d] write failure\n", t->idx);
197
return 0;
198
}
199
t->recv_size += (curl_off_t)nwritten;
200
return (size_t)nwritten;
201
}
202
203
static size_t my_read_cb(char *buf, size_t nitems, size_t buflen,
204
void *userdata)
205
{
206
struct transfer *t = userdata;
207
size_t blen = (nitems * buflen);
208
size_t nread;
209
210
if(t->send_total <= t->send_size)
211
nread = 0;
212
else if((t->send_total - t->send_size) < (curl_off_t)blen)
213
nread = (size_t)(t->send_total - t->send_size);
214
else
215
nread = blen;
216
217
fprintf(stderr, "[t-%d] SEND %ld bytes, total=%ld, pause_at=%ld\n",
218
t->idx, (long)nread, (long)t->send_total, (long)t->pause_at);
219
220
if(!t->resumed &&
221
t->send_size < t->pause_at &&
222
((t->send_size + (curl_off_t)blen) >= t->pause_at)) {
223
fprintf(stderr, "[t-%d] PAUSE\n", t->idx);
224
t->paused = 1;
225
return CURL_READFUNC_PAUSE;
226
}
227
228
memset(buf, 'x', nread);
229
t->send_size += (curl_off_t)nread;
230
if(t->fail_at > 0 && t->send_size >= t->fail_at) {
231
fprintf(stderr, "[t-%d] ABORT by read callback at %ld bytes\n",
232
t->idx, (long)t->send_size);
233
return CURL_READFUNC_ABORT;
234
}
235
return (size_t)nread;
236
}
237
238
static int my_progress_cb(void *userdata,
239
curl_off_t dltotal, curl_off_t dlnow,
240
curl_off_t ultotal, curl_off_t ulnow)
241
{
242
struct transfer *t = userdata;
243
(void)ultotal;
244
(void)dlnow;
245
(void)dltotal;
246
if(t->abort_at > 0 && ulnow >= t->abort_at) {
247
fprintf(stderr, "[t-%d] ABORT by progress_cb at %ld bytes sent\n",
248
t->idx, (long)ulnow);
249
return 1;
250
}
251
return 0;
252
}
253
254
static int setup(CURL *hnd, const char *url, struct transfer *t,
255
long http_version, struct curl_slist *host,
256
CURLSH *share, int use_earlydata, int announce_length)
257
{
258
curl_easy_setopt(hnd, CURLOPT_SHARE, share);
259
curl_easy_setopt(hnd, CURLOPT_URL, url);
260
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, http_version);
261
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
262
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
263
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, (long)(128 * 1024));
264
curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE);
265
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, my_write_cb);
266
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, t);
267
if(use_earlydata)
268
curl_easy_setopt(hnd, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_EARLYDATA);
269
270
if(!t->method || !strcmp("PUT", t->method))
271
curl_easy_setopt(hnd, CURLOPT_UPLOAD, 1L);
272
else if(!strcmp("POST", t->method))
273
curl_easy_setopt(hnd, CURLOPT_POST, 1L);
274
else {
275
fprintf(stderr, "unsupported method '%s'\n", t->method);
276
return 1;
277
}
278
curl_easy_setopt(hnd, CURLOPT_READFUNCTION, my_read_cb);
279
curl_easy_setopt(hnd, CURLOPT_READDATA, t);
280
if(announce_length)
281
curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE, t->send_total);
282
283
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 0L);
284
curl_easy_setopt(hnd, CURLOPT_XFERINFOFUNCTION, my_progress_cb);
285
curl_easy_setopt(hnd, CURLOPT_XFERINFODATA, t);
286
if(forbid_reuse)
287
curl_easy_setopt(hnd, CURLOPT_FORBID_REUSE, 1L);
288
if(host)
289
curl_easy_setopt(hnd, CURLOPT_RESOLVE, host);
290
291
/* please be verbose */
292
if(verbose) {
293
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
294
curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, debug_cb);
295
}
296
297
#if (CURLPIPE_MULTIPLEX > 0)
298
/* wait for pipe connection to confirm */
299
curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
300
#endif
301
return 0; /* all is good */
302
}
303
304
static void usage(const char *msg)
305
{
306
if(msg)
307
fprintf(stderr, "%s\n", msg);
308
fprintf(stderr,
309
"usage: [options] url\n"
310
" upload to a url with following options:\n"
311
" -a abort paused transfer\n"
312
" -e use TLS earlydata\n"
313
" -m number max parallel uploads\n"
314
" -n number total uploads\n"
315
" -A number abort transfer after `number` request body bytes\n"
316
" -F number fail reading request body after `number` of bytes\n"
317
" -P number pause transfer after `number` request body bytes\n"
318
" -r <host>:<port>:<addr> resolve information\n"
319
" -S number size to upload\n"
320
" -V http_version (http/1.1, h2, h3) http version to use\n"
321
);
322
}
323
#endif /* !_MSC_VER */
324
325
/*
326
* Download a file over HTTP/2, take care of server push.
327
*/
328
int main(int argc, char *argv[])
329
{
330
#ifndef _MSC_VER
331
CURLM *multi_handle;
332
CURLSH *share;
333
const char *url;
334
const char *method = "PUT";
335
size_t i, n, max_parallel = 1;
336
size_t active_transfers;
337
size_t pause_offset = 0;
338
size_t abort_offset = 0;
339
size_t fail_offset = 0;
340
size_t send_total = (128 * 1024);
341
int abort_paused = 0;
342
int reuse_easy = 0;
343
int use_earlydata = 0;
344
int announce_length = 0;
345
struct transfer *t;
346
int http_version = CURL_HTTP_VERSION_2_0;
347
struct curl_slist *host = NULL;
348
const char *resolve = NULL;
349
int ch;
350
351
while((ch = getopt(argc, argv, "aefhlm:n:A:F:M:P:r:RS:V:")) != -1) {
352
switch(ch) {
353
case 'h':
354
usage(NULL);
355
return 2;
356
case 'a':
357
abort_paused = 1;
358
break;
359
case 'e':
360
use_earlydata = 1;
361
break;
362
case 'f':
363
forbid_reuse = 1;
364
break;
365
case 'l':
366
announce_length = 1;
367
break;
368
case 'm':
369
max_parallel = (size_t)strtol(optarg, NULL, 10);
370
break;
371
case 'n':
372
transfer_count = (size_t)strtol(optarg, NULL, 10);
373
break;
374
case 'A':
375
abort_offset = (size_t)strtol(optarg, NULL, 10);
376
break;
377
case 'F':
378
fail_offset = (size_t)strtol(optarg, NULL, 10);
379
break;
380
case 'M':
381
method = optarg;
382
break;
383
case 'P':
384
pause_offset = (size_t)strtol(optarg, NULL, 10);
385
break;
386
case 'r':
387
resolve = optarg;
388
break;
389
case 'R':
390
reuse_easy = 1;
391
break;
392
case 'S':
393
send_total = (size_t)strtol(optarg, NULL, 10);
394
break;
395
case 'V': {
396
if(!strcmp("http/1.1", optarg))
397
http_version = CURL_HTTP_VERSION_1_1;
398
else if(!strcmp("h2", optarg))
399
http_version = CURL_HTTP_VERSION_2_0;
400
else if(!strcmp("h3", optarg))
401
http_version = CURL_HTTP_VERSION_3ONLY;
402
else {
403
usage("invalid http version");
404
return 1;
405
}
406
break;
407
}
408
default:
409
usage("invalid option");
410
return 1;
411
}
412
}
413
argc -= optind;
414
argv += optind;
415
416
if(max_parallel > 1 && reuse_easy) {
417
usage("cannot mix -R and -P");
418
return 2;
419
}
420
421
curl_global_init(CURL_GLOBAL_DEFAULT);
422
curl_global_trace("ids,time,http/2,http/3");
423
424
if(argc != 1) {
425
usage("not enough arguments");
426
return 2;
427
}
428
url = argv[0];
429
430
if(resolve)
431
host = curl_slist_append(NULL, resolve);
432
433
share = curl_share_init();
434
if(!share) {
435
fprintf(stderr, "error allocating share\n");
436
return 1;
437
}
438
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
439
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
440
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
441
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
442
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
443
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
444
445
transfers = calloc(transfer_count, sizeof(*transfers));
446
if(!transfers) {
447
fprintf(stderr, "error allocating transfer structs\n");
448
return 1;
449
}
450
451
active_transfers = 0;
452
for(i = 0; i < transfer_count; ++i) {
453
t = &transfers[i];
454
t->idx = (int)i;
455
t->method = method;
456
t->send_total = (curl_off_t)send_total;
457
t->abort_at = (curl_off_t)abort_offset;
458
t->fail_at = (curl_off_t)fail_offset;
459
t->pause_at = (curl_off_t)pause_offset;
460
}
461
462
if(reuse_easy) {
463
CURL *easy = curl_easy_init();
464
CURLcode rc = CURLE_OK;
465
if(!easy) {
466
fprintf(stderr, "failed to init easy handle\n");
467
return 1;
468
}
469
for(i = 0; i < transfer_count; ++i) {
470
t = &transfers[i];
471
t->easy = easy;
472
if(setup(t->easy, url, t, http_version, host, share, use_earlydata,
473
announce_length)) {
474
fprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
475
return 1;
476
}
477
478
fprintf(stderr, "[t-%d] STARTING\n", t->idx);
479
rc = curl_easy_perform(easy);
480
fprintf(stderr, "[t-%d] DONE -> %d\n", t->idx, rc);
481
t->easy = NULL;
482
curl_easy_reset(easy);
483
}
484
curl_easy_cleanup(easy);
485
}
486
else {
487
multi_handle = curl_multi_init();
488
curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
489
490
n = (max_parallel < transfer_count) ? max_parallel : transfer_count;
491
for(i = 0; i < n; ++i) {
492
t = &transfers[i];
493
t->easy = curl_easy_init();
494
if(!t->easy || setup(t->easy, url, t, http_version, host, share,
495
use_earlydata, announce_length)) {
496
fprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
497
return 1;
498
}
499
curl_multi_add_handle(multi_handle, t->easy);
500
t->started = 1;
501
++active_transfers;
502
fprintf(stderr, "[t-%d] STARTED\n", t->idx);
503
}
504
505
do {
506
int still_running; /* keep number of running handles */
507
CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
508
struct CURLMsg *m;
509
510
if(still_running) {
511
/* wait for activity, timeout or "nothing" */
512
mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
513
}
514
515
if(mc)
516
break;
517
518
do {
519
int msgq = 0;
520
m = curl_multi_info_read(multi_handle, &msgq);
521
if(m && (m->msg == CURLMSG_DONE)) {
522
CURL *e = m->easy_handle;
523
--active_transfers;
524
curl_multi_remove_handle(multi_handle, e);
525
t = get_transfer_for_easy(e);
526
if(t) {
527
long res_status;
528
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &res_status);
529
t->done = 1;
530
fprintf(stderr, "[t-%d] FINISHED, result=%d, response=%ld\n",
531
t->idx, m->data.result, res_status);
532
if(use_earlydata) {
533
curl_off_t sent;
534
curl_easy_getinfo(e, CURLINFO_EARLYDATA_SENT_T, &sent);
535
fprintf(stderr, "[t-%d] EarlyData: %ld\n", t->idx, (long)sent);
536
}
537
}
538
else {
539
curl_easy_cleanup(e);
540
fprintf(stderr, "unknown FINISHED???\n");
541
}
542
}
543
544
545
/* nothing happening, maintenance */
546
if(abort_paused) {
547
/* abort paused transfers */
548
for(i = 0; i < transfer_count; ++i) {
549
t = &transfers[i];
550
if(!t->done && t->paused && t->easy) {
551
curl_multi_remove_handle(multi_handle, t->easy);
552
t->done = 1;
553
active_transfers--;
554
fprintf(stderr, "[t-%d] ABORTED\n", t->idx);
555
}
556
}
557
}
558
else {
559
/* resume one paused transfer */
560
for(i = 0; i < transfer_count; ++i) {
561
t = &transfers[i];
562
if(!t->done && t->paused) {
563
t->resumed = 1;
564
t->paused = 0;
565
curl_easy_pause(t->easy, CURLPAUSE_CONT);
566
fprintf(stderr, "[t-%d] RESUMED\n", t->idx);
567
break;
568
}
569
}
570
}
571
572
while(active_transfers < max_parallel) {
573
for(i = 0; i < transfer_count; ++i) {
574
t = &transfers[i];
575
if(!t->started) {
576
t->easy = curl_easy_init();
577
if(!t->easy || setup(t->easy, url, t, http_version, host,
578
share, use_earlydata, announce_length)) {
579
fprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
580
return 1;
581
}
582
curl_multi_add_handle(multi_handle, t->easy);
583
t->started = 1;
584
++active_transfers;
585
fprintf(stderr, "[t-%d] STARTED\n", t->idx);
586
break;
587
}
588
}
589
/* all started */
590
if(i == transfer_count)
591
break;
592
}
593
} while(m);
594
595
} while(active_transfers); /* as long as we have transfers going */
596
597
curl_multi_cleanup(multi_handle);
598
}
599
600
for(i = 0; i < transfer_count; ++i) {
601
t = &transfers[i];
602
if(t->out) {
603
fclose(t->out);
604
t->out = NULL;
605
}
606
if(t->easy) {
607
curl_easy_cleanup(t->easy);
608
t->easy = NULL;
609
}
610
}
611
free(transfers);
612
curl_share_cleanup(share);
613
614
return 0;
615
#else
616
(void)argc;
617
(void)argv;
618
fprintf(stderr, "Not supported with this compiler.\n");
619
return 1;
620
#endif /* !_MSC_VER */
621
}
622
623