1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, 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 http://curl.haxx.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 ***************************************************************************/
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 /* somewhat unix-specific */
26 #include <sys/time.h>
27 #include <unistd.h>
28
29 /* curl stuff */
30 #include <curl/curl.h>
31
32 #ifndef CURLPIPE_MULTIPLEX
33 /* This little trick will just make sure that we don't enable pipelining for
34 libcurls old enough to not have this symbol. It is _not_ defined to zero in
35 a recent libcurl header. */
36 #define CURLPIPE_MULTIPLEX 0
37 #endif
38
39 #define NUM_HANDLES 1000
40
41 void *curl_hnd[NUM_HANDLES];
42 int num_transfers;
43
44 /* a handle to number lookup, highly ineffective when we do many
45 transfers... */
hnd2num(CURL * hnd)46 static int hnd2num(CURL *hnd)
47 {
48 int i;
49 for(i=0; i< num_transfers; i++) {
50 if(curl_hnd[i] == hnd)
51 return i;
52 }
53 return 0; /* weird, but just a fail-safe */
54 }
55
56 static
dump(const char * text,int num,unsigned char * ptr,size_t size,char nohex)57 void dump(const char *text, int num, unsigned char *ptr, size_t size,
58 char nohex)
59 {
60 size_t i;
61 size_t c;
62
63 unsigned int width=0x10;
64
65 if(nohex)
66 /* without the hex output, we can fit more on screen */
67 width = 0x40;
68
69 fprintf(stderr, "%d %s, %ld bytes (0x%lx)\n",
70 num, text, (long)size, (long)size);
71
72 for(i=0; i<size; i+= width) {
73
74 fprintf(stderr, "%4.4lx: ", (long)i);
75
76 if(!nohex) {
77 /* hex not disabled, show it */
78 for(c = 0; c < width; c++)
79 if(i+c < size)
80 fprintf(stderr, "%02x ", ptr[i+c]);
81 else
82 fputs(" ", stderr);
83 }
84
85 for(c = 0; (c < width) && (i+c < size); c++) {
86 /* check for 0D0A; if found, skip past and start a new line of output */
87 if (nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
88 i+=(c+2-width);
89 break;
90 }
91 fprintf(stderr, "%c",
92 (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
93 /* check again for 0D0A, to avoid an extra \n if it's at width */
94 if (nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
95 i+=(c+3-width);
96 break;
97 }
98 }
99 fputc('\n', stderr); /* newline */
100 }
101 }
102
103 static
my_trace(CURL * handle,curl_infotype type,char * data,size_t size,void * userp)104 int my_trace(CURL *handle, curl_infotype type,
105 char *data, size_t size,
106 void *userp)
107 {
108 const char *text;
109 int num = hnd2num(handle);
110 (void)handle; /* prevent compiler warning */
111 (void)userp;
112 switch (type) {
113 case CURLINFO_TEXT:
114 fprintf(stderr, "== %d Info: %s", num, data);
115 default: /* in case a new one is introduced to shock us */
116 return 0;
117
118 case CURLINFO_HEADER_OUT:
119 text = "=> Send header";
120 break;
121 case CURLINFO_DATA_OUT:
122 text = "=> Send data";
123 break;
124 case CURLINFO_SSL_DATA_OUT:
125 text = "=> Send SSL data";
126 break;
127 case CURLINFO_HEADER_IN:
128 text = "<= Recv header";
129 break;
130 case CURLINFO_DATA_IN:
131 text = "<= Recv data";
132 break;
133 case CURLINFO_SSL_DATA_IN:
134 text = "<= Recv SSL data";
135 break;
136 }
137
138 dump(text, num, (unsigned char *)data, size, 1);
139 return 0;
140 }
141
setup(CURL * hnd,int num)142 static void setup(CURL *hnd, int num)
143 {
144 FILE *out;
145 char filename[128];
146
147 sprintf(filename, "dl-%d", num);
148
149 out = fopen(filename, "wb");
150
151 /* write to this file */
152 curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out);
153
154 /* set the same URL */
155 curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html");
156
157 /* send it verbose for max debuggaility */
158 curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
159 curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace);
160
161 /* HTTP/2 please */
162 curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
163
164 /* we use a self-signed test server, skip verification during debugging */
165 curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
166 curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
167
168 #if (CURLPIPE_MULTIPLEX > 0)
169 /* wait for pipe connection to confirm */
170 curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
171 #endif
172
173 curl_hnd[num] = hnd;
174 }
175
176 /*
177 * Simply download two files over HTTP/2, using the same physical connection!
178 */
main(int argc,char ** argv)179 int main(int argc, char **argv)
180 {
181 CURL *easy[NUM_HANDLES];
182 CURLM *multi_handle;
183 int i;
184 int still_running; /* keep number of running handles */
185
186 if(argc > 1)
187 /* if given a number, do that many transfers */
188 num_transfers = atoi(argv[1]);
189
190 if(!num_transfers || (num_transfers > NUM_HANDLES))
191 num_transfers = 3; /* a suitable low default */
192
193 /* init a multi stack */
194 multi_handle = curl_multi_init();
195
196 for(i=0; i<num_transfers; i++) {
197 easy[i] = curl_easy_init();
198 /* set options */
199 setup(easy[i], i);
200
201 /* add the individual transfer */
202 curl_multi_add_handle(multi_handle, easy[i]);
203 }
204
205 curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
206
207 /* we start some action by calling perform right away */
208 curl_multi_perform(multi_handle, &still_running);
209
210 do {
211 struct timeval timeout;
212 int rc; /* select() return code */
213 CURLMcode mc; /* curl_multi_fdset() return code */
214
215 fd_set fdread;
216 fd_set fdwrite;
217 fd_set fdexcep;
218 int maxfd = -1;
219
220 long curl_timeo = -1;
221
222 FD_ZERO(&fdread);
223 FD_ZERO(&fdwrite);
224 FD_ZERO(&fdexcep);
225
226 /* set a suitable timeout to play around with */
227 timeout.tv_sec = 1;
228 timeout.tv_usec = 0;
229
230 curl_multi_timeout(multi_handle, &curl_timeo);
231 if(curl_timeo >= 0) {
232 timeout.tv_sec = curl_timeo / 1000;
233 if(timeout.tv_sec > 1)
234 timeout.tv_sec = 1;
235 else
236 timeout.tv_usec = (curl_timeo % 1000) * 1000;
237 }
238
239 /* get file descriptors from the transfers */
240 mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
241
242 if(mc != CURLM_OK)
243 {
244 fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc);
245 break;
246 }
247
248 /* On success the value of maxfd is guaranteed to be >= -1. We call
249 select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
250 no fds ready yet so we call select(0, ...) --or Sleep() on Windows--
251 to sleep 100ms, which is the minimum suggested value in the
252 curl_multi_fdset() doc. */
253
254 if(maxfd == -1) {
255 #ifdef _WIN32
256 Sleep(100);
257 rc = 0;
258 #else
259 /* Portable sleep for platforms other than Windows. */
260 struct timeval wait = { 0, 100 * 1000 }; /* 100ms */
261 rc = select(0, NULL, NULL, NULL, &wait);
262 #endif
263 }
264 else {
265 /* Note that on some platforms 'timeout' may be modified by select().
266 If you need access to the original value save a copy beforehand. */
267 rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
268 }
269
270 switch(rc) {
271 case -1:
272 /* select error */
273 break;
274 case 0:
275 default:
276 /* timeout or readable/writable sockets */
277 curl_multi_perform(multi_handle, &still_running);
278 break;
279 }
280 } while(still_running);
281
282 curl_multi_cleanup(multi_handle);
283
284 for(i=0; i<num_transfers; i++)
285 curl_easy_cleanup(easy[i]);
286
287 return 0;
288 }
289