Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/tests/libtest/cli_h2_pausing.c
2649 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
/* This is based on the PoC client of issue #11982
25
*/
26
#include "first.h"
27
28
#include "testtrace.h"
29
#include "memdebug.h"
30
31
static void usage_h2_pausing(const char *msg)
32
{
33
if(msg)
34
curl_mfprintf(stderr, "%s\n", msg);
35
curl_mfprintf(stderr,
36
"usage: [options] url\n"
37
" pause downloads with following options:\n"
38
" -V http_version (http/1.1, h2, h3) http version to use\n"
39
);
40
}
41
42
struct handle
43
{
44
size_t idx;
45
int paused;
46
int resumed;
47
int errored;
48
int fail_write;
49
CURL *curl;
50
};
51
52
static size_t cb(char *data, size_t size, size_t nmemb, void *clientp)
53
{
54
size_t realsize = size * nmemb;
55
struct handle *handle = (struct handle *) clientp;
56
curl_off_t totalsize;
57
58
(void)data;
59
if(curl_easy_getinfo(handle->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T,
60
&totalsize) == CURLE_OK)
61
curl_mfprintf(stderr, "INFO: [%zu] write, "
62
"Content-Length %" CURL_FORMAT_CURL_OFF_T "\n",
63
handle->idx, totalsize);
64
65
if(!handle->resumed) {
66
++handle->paused;
67
curl_mfprintf(stderr, "INFO: [%zu] write, PAUSING %d time on %zu bytes\n",
68
handle->idx, handle->paused, realsize);
69
assert(handle->paused == 1);
70
return CURL_WRITEFUNC_PAUSE;
71
}
72
if(handle->fail_write) {
73
++handle->errored;
74
curl_mfprintf(stderr, "INFO: [%zu] FAIL write of %zu bytes, %d time\n",
75
handle->idx, realsize, handle->errored);
76
return CURL_WRITEFUNC_ERROR;
77
}
78
curl_mfprintf(stderr, "INFO: [%zu] write, accepting %zu bytes\n",
79
handle->idx, realsize);
80
return realsize;
81
}
82
83
static CURLcode test_cli_h2_pausing(const char *URL)
84
{
85
struct handle handles[2];
86
CURLM *multi = NULL;
87
int still_running = 1, msgs_left, numfds;
88
size_t i;
89
CURLMsg *msg;
90
int rounds = 0;
91
CURLcode result = CURLE_OK;
92
CURLU *cu;
93
struct curl_slist *resolve = NULL;
94
char resolve_buf[1024];
95
const char *url;
96
char *host = NULL, *port = NULL;
97
int all_paused = 0;
98
int resume_round = -1;
99
long http_version = CURL_HTTP_VERSION_2_0;
100
int ch;
101
102
(void)URL;
103
104
while((ch = cgetopt(test_argc, test_argv, "hV:")) != -1) {
105
switch(ch) {
106
case 'h':
107
usage_h2_pausing(NULL);
108
return (CURLcode)2;
109
case 'V': {
110
if(!strcmp("http/1.1", coptarg))
111
http_version = CURL_HTTP_VERSION_1_1;
112
else if(!strcmp("h2", coptarg))
113
http_version = CURL_HTTP_VERSION_2_0;
114
else if(!strcmp("h3", coptarg))
115
http_version = CURL_HTTP_VERSION_3ONLY;
116
else {
117
usage_h2_pausing("invalid http version");
118
return (CURLcode)1;
119
}
120
break;
121
}
122
default:
123
usage_h2_pausing("invalid option");
124
return (CURLcode)1;
125
}
126
}
127
test_argc -= coptind;
128
test_argv += coptind;
129
130
if(test_argc != 1) {
131
curl_mfprintf(stderr, "ERROR: need URL as argument\n");
132
return (CURLcode)2;
133
}
134
url = test_argv[0];
135
136
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
137
curl_mfprintf(stderr, "curl_global_init() failed\n");
138
return (CURLcode)3;
139
}
140
141
curl_global_trace("ids,time,http/2,http/3");
142
143
memset(handles, 0, sizeof(handles));
144
145
cu = curl_url();
146
if(!cu) {
147
curl_mfprintf(stderr, "out of memory\n");
148
result = (CURLcode)1;
149
goto cleanup;
150
}
151
if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
152
curl_mfprintf(stderr, "not a URL: '%s'\n", url);
153
result = (CURLcode)1;
154
goto cleanup;
155
}
156
if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
157
curl_mfprintf(stderr, "could not get host of '%s'\n", url);
158
result = (CURLcode)1;
159
goto cleanup;
160
}
161
if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
162
curl_mfprintf(stderr, "could not get port of '%s'\n", url);
163
result = (CURLcode)1;
164
goto cleanup;
165
}
166
memset(&resolve, 0, sizeof(resolve));
167
curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, "%s:%s:127.0.0.1",
168
host, port);
169
resolve = curl_slist_append(resolve, resolve_buf);
170
171
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
172
handles[i].idx = i;
173
handles[i].paused = 0;
174
handles[i].resumed = 0;
175
handles[i].errored = 0;
176
handles[i].fail_write = 1;
177
handles[i].curl = curl_easy_init();
178
if(!handles[i].curl ||
179
curl_easy_setopt(handles[i].curl, CURLOPT_WRITEFUNCTION, cb)
180
!= CURLE_OK ||
181
curl_easy_setopt(handles[i].curl, CURLOPT_WRITEDATA, &handles[i])
182
!= CURLE_OK ||
183
curl_easy_setopt(handles[i].curl, CURLOPT_FOLLOWLOCATION, 1L)
184
!= CURLE_OK ||
185
curl_easy_setopt(handles[i].curl, CURLOPT_VERBOSE, 1L) != CURLE_OK ||
186
curl_easy_setopt(handles[i].curl, CURLOPT_DEBUGFUNCTION, cli_debug_cb)
187
!= CURLE_OK ||
188
curl_easy_setopt(handles[i].curl, CURLOPT_SSL_VERIFYPEER, 0L)
189
!= CURLE_OK ||
190
curl_easy_setopt(handles[i].curl, CURLOPT_RESOLVE, resolve)
191
!= CURLE_OK ||
192
curl_easy_setopt(handles[i].curl, CURLOPT_PIPEWAIT, 1L) != CURLE_OK ||
193
curl_easy_setopt(handles[i].curl, CURLOPT_URL, url) != CURLE_OK) {
194
curl_mfprintf(stderr, "failed configuring easy handle - bailing out\n");
195
result = (CURLcode)2;
196
goto cleanup;
197
}
198
curl_easy_setopt(handles[i].curl, CURLOPT_HTTP_VERSION, http_version);
199
}
200
201
multi = curl_multi_init();
202
if(!multi) {
203
curl_mfprintf(stderr, "curl_multi_init() failed - bailing out\n");
204
result = (CURLcode)2;
205
goto cleanup;
206
}
207
208
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
209
if(curl_multi_add_handle(multi, handles[i].curl) != CURLM_OK) {
210
curl_mfprintf(stderr, "curl_multi_add_handle() failed - bailing out\n");
211
result = (CURLcode)2;
212
goto cleanup;
213
}
214
}
215
216
for(rounds = 0;; rounds++) {
217
curl_mfprintf(stderr, "INFO: multi_perform round %d\n", rounds);
218
if(curl_multi_perform(multi, &still_running) != CURLM_OK) {
219
curl_mfprintf(stderr, "curl_multi_perform() failed - bailing out\n");
220
result = (CURLcode)2;
221
goto cleanup;
222
}
223
224
if(!still_running) {
225
int as_expected = 1;
226
curl_mfprintf(stderr, "INFO: no more handles running\n");
227
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
228
if(!handles[i].paused) {
229
curl_mfprintf(stderr, "ERROR: [%zu] NOT PAUSED\n", i);
230
as_expected = 0;
231
}
232
else if(handles[i].paused != 1) {
233
curl_mfprintf(stderr, "ERROR: [%zu] PAUSED %d times!\n",
234
i, handles[i].paused);
235
as_expected = 0;
236
}
237
else if(!handles[i].resumed) {
238
curl_mfprintf(stderr, "ERROR: [%zu] NOT resumed!\n", i);
239
as_expected = 0;
240
}
241
else if(handles[i].errored != 1) {
242
curl_mfprintf(stderr, "ERROR: [%zu] NOT errored once, %d instead!\n",
243
i, handles[i].errored);
244
as_expected = 0;
245
}
246
}
247
if(!as_expected) {
248
curl_mfprintf(stderr, "ERROR: handles not in expected state "
249
"after %d rounds\n", rounds);
250
result = (CURLcode)1;
251
}
252
break;
253
}
254
255
if(curl_multi_poll(multi, NULL, 0, 100, &numfds) != CURLM_OK) {
256
curl_mfprintf(stderr, "curl_multi_poll() failed - bailing out\n");
257
result = (CURLcode)2;
258
goto cleanup;
259
}
260
261
/* !checksrc! disable EQUALSNULL 1 */
262
while((msg = curl_multi_info_read(multi, &msgs_left)) != NULL) {
263
if(msg->msg == CURLMSG_DONE) {
264
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
265
if(msg->easy_handle == handles[i].curl) {
266
if(handles[i].paused != 1 || !handles[i].resumed) {
267
curl_mfprintf(stderr, "ERROR: [%zu] done, paused=%d, "
268
"resumed=%d, result %d - wtf?\n", i,
269
handles[i].paused,
270
handles[i].resumed, msg->data.result);
271
result = (CURLcode)1;
272
goto cleanup;
273
}
274
}
275
}
276
}
277
}
278
279
/* Successfully paused? */
280
if(!all_paused) {
281
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
282
if(!handles[i].paused) {
283
break;
284
}
285
}
286
all_paused = (i == CURL_ARRAYSIZE(handles));
287
if(all_paused) {
288
curl_mfprintf(stderr, "INFO: all transfers paused\n");
289
/* give transfer some rounds to mess things up */
290
resume_round = rounds + 2;
291
}
292
}
293
if(resume_round > 0 && rounds == resume_round) {
294
/* time to resume */
295
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
296
curl_mfprintf(stderr, "INFO: [%zu] resumed\n", i);
297
handles[i].resumed = 1;
298
curl_easy_pause(handles[i].curl, CURLPAUSE_CONT);
299
}
300
}
301
}
302
303
cleanup:
304
305
for(i = 0; i < CURL_ARRAYSIZE(handles); i++) {
306
curl_multi_remove_handle(multi, handles[i].curl);
307
curl_easy_cleanup(handles[i].curl);
308
}
309
310
curl_slist_free_all(resolve);
311
curl_free(host);
312
curl_free(port);
313
curl_url_cleanup(cu);
314
curl_multi_cleanup(multi);
315
curl_global_cleanup();
316
317
return result;
318
}
319
320