Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/tests/http/clients/h2-serverpush.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 server push
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 CURLPIPE_MULTIPLEX
36
#error "too old libcurl, cannot do HTTP/2 server push!"
37
#endif
38
39
static
40
void dump(const char *text, unsigned char *ptr, size_t size,
41
char nohex)
42
{
43
size_t i;
44
size_t c;
45
46
unsigned int width = 0x10;
47
48
if(nohex)
49
/* without the hex output, we can fit more on screen */
50
width = 0x40;
51
52
fprintf(stderr, "%s, %lu bytes (0x%lx)\n",
53
text, (unsigned long)size, (unsigned long)size);
54
55
for(i = 0; i < size; i += width) {
56
57
fprintf(stderr, "%4.4lx: ", (unsigned long)i);
58
59
if(!nohex) {
60
/* hex not disabled, show it */
61
for(c = 0; c < width; c++)
62
if(i + c < size)
63
fprintf(stderr, "%02x ", ptr[i + c]);
64
else
65
fputs(" ", stderr);
66
}
67
68
for(c = 0; (c < width) && (i + c < size); c++) {
69
/* check for 0D0A; if found, skip past and start a new line of output */
70
if(nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D &&
71
ptr[i + c + 1] == 0x0A) {
72
i += (c + 2 - width);
73
break;
74
}
75
fprintf(stderr, "%c",
76
(ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.');
77
/* check again for 0D0A, to avoid an extra \n if it's at width */
78
if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D &&
79
ptr[i + c + 2] == 0x0A) {
80
i += (c + 3 - width);
81
break;
82
}
83
}
84
fputc('\n', stderr); /* newline */
85
}
86
}
87
88
static
89
int my_trace(CURL *handle, curl_infotype type,
90
char *data, size_t size,
91
void *userp)
92
{
93
const char *text;
94
(void)handle; /* prevent compiler warning */
95
(void)userp;
96
switch(type) {
97
case CURLINFO_TEXT:
98
fprintf(stderr, "== Info: %s", data);
99
return 0;
100
case CURLINFO_HEADER_OUT:
101
text = "=> Send header";
102
break;
103
case CURLINFO_DATA_OUT:
104
text = "=> Send data";
105
break;
106
case CURLINFO_SSL_DATA_OUT:
107
text = "=> Send SSL data";
108
break;
109
case CURLINFO_HEADER_IN:
110
text = "<= Recv header";
111
break;
112
case CURLINFO_DATA_IN:
113
text = "<= Recv data";
114
break;
115
case CURLINFO_SSL_DATA_IN:
116
text = "<= Recv SSL data";
117
break;
118
default: /* in case a new one is introduced to shock us */
119
return 0;
120
}
121
122
dump(text, (unsigned char *)data, size, 1);
123
return 0;
124
}
125
126
#define OUTPUTFILE "download_0.data"
127
128
static int setup(CURL *hnd, const char *url)
129
{
130
FILE *out = fopen(OUTPUTFILE, "wb");
131
if(!out)
132
/* failed */
133
return 1;
134
135
curl_easy_setopt(hnd, CURLOPT_URL, url);
136
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
137
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
138
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
139
140
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out);
141
142
/* please be verbose */
143
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
144
curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace);
145
146
#if (CURLPIPE_MULTIPLEX > 0)
147
/* wait for pipe connection to confirm */
148
curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
149
#endif
150
return 0; /* all is good */
151
}
152
153
/* called when there's an incoming push */
154
static int server_push_callback(CURL *parent,
155
CURL *easy,
156
size_t num_headers,
157
struct curl_pushheaders *headers,
158
void *userp)
159
{
160
char *headp;
161
size_t i;
162
int *transfers = (int *)userp;
163
char filename[128];
164
FILE *out;
165
static unsigned int count = 0;
166
int rv;
167
168
(void)parent; /* we have no use for this */
169
curl_msnprintf(filename, sizeof(filename)-1, "push%u", count++);
170
171
/* here's a new stream, save it in a new file for each new push */
172
out = fopen(filename, "wb");
173
if(!out) {
174
/* if we cannot save it, deny it */
175
fprintf(stderr, "Failed to create output file for push\n");
176
rv = CURL_PUSH_DENY;
177
goto out;
178
}
179
180
/* write to this file */
181
curl_easy_setopt(easy, CURLOPT_WRITEDATA, out);
182
183
fprintf(stderr, "**** push callback approves stream %u, got %lu headers!\n",
184
count, (unsigned long)num_headers);
185
186
for(i = 0; i < num_headers; i++) {
187
headp = curl_pushheader_bynum(headers, i);
188
fprintf(stderr, "**** header %lu: %s\n", (unsigned long)i, headp);
189
}
190
191
headp = curl_pushheader_byname(headers, ":path");
192
if(headp) {
193
fprintf(stderr, "**** The PATH is %s\n", headp /* skip :path + colon */);
194
}
195
196
(*transfers)++; /* one more */
197
rv = CURL_PUSH_OK;
198
199
out:
200
return rv;
201
}
202
203
204
/*
205
* Download a file over HTTP/2, take care of server push.
206
*/
207
int main(int argc, char *argv[])
208
{
209
CURL *easy;
210
CURLM *multi_handle;
211
int transfers = 1; /* we start with one */
212
struct CURLMsg *m;
213
const char *url;
214
215
if(argc != 2) {
216
fprintf(stderr, "need URL as argument\n");
217
return 2;
218
}
219
url = argv[1];
220
221
multi_handle = curl_multi_init();
222
curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
223
curl_multi_setopt(multi_handle, CURLMOPT_PUSHFUNCTION, server_push_callback);
224
curl_multi_setopt(multi_handle, CURLMOPT_PUSHDATA, &transfers);
225
226
easy = curl_easy_init();
227
if(setup(easy, url)) {
228
fprintf(stderr, "failed\n");
229
return 1;
230
}
231
232
curl_multi_add_handle(multi_handle, easy);
233
do {
234
int still_running; /* keep number of running handles */
235
CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
236
237
if(still_running)
238
/* wait for activity, timeout or "nothing" */
239
mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
240
241
if(mc)
242
break;
243
244
/*
245
* A little caution when doing server push is that libcurl itself has
246
* created and added one or more easy handles but we need to clean them up
247
* when we are done.
248
*/
249
do {
250
int msgq = 0;
251
m = curl_multi_info_read(multi_handle, &msgq);
252
if(m && (m->msg == CURLMSG_DONE)) {
253
CURL *e = m->easy_handle;
254
transfers--;
255
curl_multi_remove_handle(multi_handle, e);
256
curl_easy_cleanup(e);
257
}
258
} while(m);
259
260
} while(transfers); /* as long as we have transfers going */
261
262
curl_multi_cleanup(multi_handle);
263
264
return 0;
265
}
266
267