Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/tests/http/clients/h2-pausing.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/2 download pausing
26
* </DESC>
27
*/
28
/* This is based on the PoC client of issue #11982
29
*/
30
#include <curl/curl.h>
31
32
#include <assert.h>
33
#include <stdio.h>
34
#include <string.h>
35
#include <stdlib.h>
36
37
#ifndef _MSC_VER
38
/* somewhat Unix-specific */
39
#include <unistd.h> /* getopt() */
40
#endif
41
42
#ifndef _MSC_VER
43
#define HANDLECOUNT 2
44
45
static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type)
46
{
47
/*
48
* This is the trace look that is similar to what libcurl makes on its
49
* own.
50
*/
51
static const char * const s_infotype[] = {
52
"* ", "< ", "> ", "{ ", "} ", "{ ", "} "
53
};
54
if(idsbuf && *idsbuf)
55
fprintf(log, "%s%s", idsbuf, s_infotype[type]);
56
else
57
fputs(s_infotype[type], log);
58
}
59
60
#define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] "
61
#define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \
62
CURL_FORMAT_CURL_OFF_T "] "
63
/*
64
** callback for CURLOPT_DEBUGFUNCTION
65
*/
66
static int debug_cb(CURL *handle, curl_infotype type,
67
char *data, size_t size,
68
void *userdata)
69
{
70
FILE *output = stderr;
71
static int newl = 0;
72
static int traced_data = 0;
73
char idsbuf[60];
74
curl_off_t xfer_id, conn_id;
75
76
(void)handle; /* not used */
77
(void)userdata;
78
79
if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) {
80
if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) &&
81
conn_id >= 0) {
82
curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, xfer_id,
83
conn_id);
84
}
85
else {
86
curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id);
87
}
88
}
89
else
90
idsbuf[0] = 0;
91
92
switch(type) {
93
case CURLINFO_HEADER_OUT:
94
if(size > 0) {
95
size_t st = 0;
96
size_t i;
97
for(i = 0; i < size - 1; i++) {
98
if(data[i] == '\n') { /* LF */
99
if(!newl) {
100
log_line_start(output, idsbuf, type);
101
}
102
(void)fwrite(data + st, i - st + 1, 1, output);
103
st = i + 1;
104
newl = 0;
105
}
106
}
107
if(!newl)
108
log_line_start(output, idsbuf, type);
109
(void)fwrite(data + st, i - st + 1, 1, output);
110
}
111
newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
112
traced_data = 0;
113
break;
114
case CURLINFO_TEXT:
115
case CURLINFO_HEADER_IN:
116
if(!newl)
117
log_line_start(output, idsbuf, type);
118
(void)fwrite(data, size, 1, output);
119
newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
120
traced_data = 0;
121
break;
122
case CURLINFO_DATA_OUT:
123
case CURLINFO_DATA_IN:
124
case CURLINFO_SSL_DATA_IN:
125
case CURLINFO_SSL_DATA_OUT:
126
if(!traced_data) {
127
if(!newl)
128
log_line_start(output, idsbuf, type);
129
fprintf(output, "[%ld bytes data]\n", (long)size);
130
newl = 0;
131
traced_data = 1;
132
}
133
break;
134
default: /* nada */
135
newl = 0;
136
traced_data = 1;
137
break;
138
}
139
140
return 0;
141
}
142
143
static void usage(const char *msg)
144
{
145
if(msg)
146
fprintf(stderr, "%s\n", msg);
147
fprintf(stderr,
148
"usage: [options] url\n"
149
" pause downloads with following options:\n"
150
" -V http_version (http/1.1, h2, h3) http version to use\n"
151
);
152
}
153
154
struct handle
155
{
156
int idx;
157
int paused;
158
int resumed;
159
int errored;
160
int fail_write;
161
CURL *h;
162
};
163
164
static size_t cb(char *data, size_t size, size_t nmemb, void *clientp)
165
{
166
size_t realsize = size * nmemb;
167
struct handle *handle = (struct handle *) clientp;
168
curl_off_t totalsize;
169
170
(void)data;
171
if(curl_easy_getinfo(handle->h, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,
172
&totalsize) == CURLE_OK)
173
fprintf(stderr, "INFO: [%d] write, Content-Length %"CURL_FORMAT_CURL_OFF_T
174
"\n", handle->idx, totalsize);
175
176
if(!handle->resumed) {
177
++handle->paused;
178
fprintf(stderr, "INFO: [%d] write, PAUSING %d time on %lu bytes\n",
179
handle->idx, handle->paused, (long)realsize);
180
assert(handle->paused == 1);
181
return CURL_WRITEFUNC_PAUSE;
182
}
183
if(handle->fail_write) {
184
++handle->errored;
185
fprintf(stderr, "INFO: [%d] FAIL write of %lu bytes, %d time\n",
186
handle->idx, (long)realsize, handle->errored);
187
return CURL_WRITEFUNC_ERROR;
188
}
189
fprintf(stderr, "INFO: [%d] write, accepting %lu bytes\n",
190
handle->idx, (long)realsize);
191
return realsize;
192
}
193
194
#define ERR() \
195
do { \
196
fprintf(stderr, "something unexpected went wrong - bailing out!\n"); \
197
return 2; \
198
} while(0)
199
200
#endif /* !_MSC_VER */
201
202
int main(int argc, char *argv[])
203
{
204
#ifndef _MSC_VER
205
struct handle handles[HANDLECOUNT];
206
CURLM *multi_handle;
207
int i, still_running = 1, msgs_left, numfds;
208
CURLMsg *msg;
209
int rounds = 0;
210
int rc = 0;
211
CURLU *cu;
212
struct curl_slist *resolve = NULL;
213
char resolve_buf[1024];
214
char *url, *host = NULL, *port = NULL;
215
int all_paused = 0;
216
int resume_round = -1;
217
int http_version = CURL_HTTP_VERSION_2_0;
218
int ch;
219
220
while((ch = getopt(argc, argv, "hV:")) != -1) {
221
switch(ch) {
222
case 'h':
223
usage(NULL);
224
return 2;
225
case 'V': {
226
if(!strcmp("http/1.1", optarg))
227
http_version = CURL_HTTP_VERSION_1_1;
228
else if(!strcmp("h2", optarg))
229
http_version = CURL_HTTP_VERSION_2_0;
230
else if(!strcmp("h3", optarg))
231
http_version = CURL_HTTP_VERSION_3ONLY;
232
else {
233
usage("invalid http version");
234
return 1;
235
}
236
break;
237
}
238
default:
239
usage("invalid option");
240
return 1;
241
}
242
}
243
argc -= optind;
244
argv += optind;
245
246
if(argc != 1) {
247
fprintf(stderr, "ERROR: need URL as argument\n");
248
return 2;
249
}
250
url = argv[0];
251
252
curl_global_init(CURL_GLOBAL_DEFAULT);
253
curl_global_trace("ids,time,http/2,http/3");
254
255
cu = curl_url();
256
if(!cu) {
257
fprintf(stderr, "out of memory\n");
258
return 1;
259
}
260
if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
261
fprintf(stderr, "not a URL: '%s'\n", url);
262
return 1;
263
}
264
if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
265
fprintf(stderr, "could not get host of '%s'\n", url);
266
return 1;
267
}
268
if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
269
fprintf(stderr, "could not get port of '%s'\n", url);
270
return 1;
271
}
272
memset(&resolve, 0, sizeof(resolve));
273
curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, "%s:%s:127.0.0.1",
274
host, port);
275
resolve = curl_slist_append(resolve, resolve_buf);
276
277
for(i = 0; i < HANDLECOUNT; i++) {
278
handles[i].idx = i;
279
handles[i].paused = 0;
280
handles[i].resumed = 0;
281
handles[i].errored = 0;
282
handles[i].fail_write = 1;
283
handles[i].h = curl_easy_init();
284
if(!handles[i].h ||
285
curl_easy_setopt(handles[i].h, CURLOPT_WRITEFUNCTION, cb) != CURLE_OK ||
286
curl_easy_setopt(handles[i].h, CURLOPT_WRITEDATA, &handles[i])
287
!= CURLE_OK ||
288
curl_easy_setopt(handles[i].h, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK ||
289
curl_easy_setopt(handles[i].h, CURLOPT_VERBOSE, 1L) != CURLE_OK ||
290
curl_easy_setopt(handles[i].h, CURLOPT_DEBUGFUNCTION, debug_cb)
291
!= CURLE_OK ||
292
curl_easy_setopt(handles[i].h, CURLOPT_SSL_VERIFYPEER, 0L) != CURLE_OK ||
293
curl_easy_setopt(handles[i].h, CURLOPT_RESOLVE, resolve) != CURLE_OK ||
294
curl_easy_setopt(handles[i].h, CURLOPT_PIPEWAIT, 1L) ||
295
curl_easy_setopt(handles[i].h, CURLOPT_URL, url) != CURLE_OK) {
296
ERR();
297
}
298
curl_easy_setopt(handles[i].h, CURLOPT_HTTP_VERSION, (long)http_version);
299
}
300
301
multi_handle = curl_multi_init();
302
if(!multi_handle)
303
ERR();
304
305
for(i = 0; i < HANDLECOUNT; i++) {
306
if(curl_multi_add_handle(multi_handle, handles[i].h) != CURLM_OK)
307
ERR();
308
}
309
310
for(rounds = 0;; rounds++) {
311
fprintf(stderr, "INFO: multi_perform round %d\n", rounds);
312
if(curl_multi_perform(multi_handle, &still_running) != CURLM_OK)
313
ERR();
314
315
if(!still_running) {
316
int as_expected = 1;
317
fprintf(stderr, "INFO: no more handles running\n");
318
for(i = 0; i < HANDLECOUNT; i++) {
319
if(!handles[i].paused) {
320
fprintf(stderr, "ERROR: [%d] NOT PAUSED\n", i);
321
as_expected = 0;
322
}
323
else if(handles[i].paused != 1) {
324
fprintf(stderr, "ERROR: [%d] PAUSED %d times!\n",
325
i, handles[i].paused);
326
as_expected = 0;
327
}
328
else if(!handles[i].resumed) {
329
fprintf(stderr, "ERROR: [%d] NOT resumed!\n", i);
330
as_expected = 0;
331
}
332
else if(handles[i].errored != 1) {
333
fprintf(stderr, "ERROR: [%d] NOT errored once, %d instead!\n",
334
i, handles[i].errored);
335
as_expected = 0;
336
}
337
}
338
if(!as_expected) {
339
fprintf(stderr, "ERROR: handles not in expected state "
340
"after %d rounds\n", rounds);
341
rc = 1;
342
}
343
break;
344
}
345
346
if(curl_multi_poll(multi_handle, NULL, 0, 100, &numfds) != CURLM_OK)
347
ERR();
348
349
/* !checksrc! disable EQUALSNULL 1 */
350
while((msg = curl_multi_info_read(multi_handle, &msgs_left)) != NULL) {
351
if(msg->msg == CURLMSG_DONE) {
352
for(i = 0; i < HANDLECOUNT; i++) {
353
if(msg->easy_handle == handles[i].h) {
354
if(handles[i].paused != 1 || !handles[i].resumed) {
355
fprintf(stderr, "ERROR: [%d] done, pauses=%d, resumed=%d, "
356
"result %d - wtf?\n", i, handles[i].paused,
357
handles[i].resumed, msg->data.result);
358
rc = 1;
359
goto out;
360
}
361
}
362
}
363
}
364
}
365
366
/* Successfully paused? */
367
if(!all_paused) {
368
for(i = 0; i < HANDLECOUNT; i++) {
369
if(!handles[i].paused) {
370
break;
371
}
372
}
373
all_paused = (i == HANDLECOUNT);
374
if(all_paused) {
375
fprintf(stderr, "INFO: all transfers paused\n");
376
/* give transfer some rounds to mess things up */
377
resume_round = rounds + 2;
378
}
379
}
380
if(resume_round > 0 && rounds == resume_round) {
381
/* time to resume */
382
for(i = 0; i < HANDLECOUNT; i++) {
383
fprintf(stderr, "INFO: [%d] resumed\n", i);
384
handles[i].resumed = 1;
385
curl_easy_pause(handles[i].h, CURLPAUSE_CONT);
386
}
387
}
388
}
389
390
out:
391
for(i = 0; i < HANDLECOUNT; i++) {
392
curl_multi_remove_handle(multi_handle, handles[i].h);
393
curl_easy_cleanup(handles[i].h);
394
}
395
396
397
curl_slist_free_all(resolve);
398
curl_free(host);
399
curl_free(port);
400
curl_url_cleanup(cu);
401
curl_multi_cleanup(multi_handle);
402
curl_global_cleanup();
403
404
return rc;
405
#else
406
(void)argc;
407
(void)argv;
408
fprintf(stderr, "Not supported with this compiler.\n");
409
return 1;
410
#endif /* !_MSC_VER */
411
}
412
413