http2-serverpush.c
Go to the documentation of this file.
1 /***************************************************************************
2  * _ _ ____ _
3  * Project ___| | | | _ \| |
4  * / __| | | | |_) | |
5  * | (__| |_| | _ <| |___
6  * \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2017, 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 https://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 /* <DESC>
23  * HTTP/2 server push
24  * </DESC>
25  */
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 /* somewhat unix-specific */
31 #include <sys/time.h>
32 #include <unistd.h>
33 
34 /* curl stuff */
35 #include <curl/curl.h>
36 
37 #ifndef CURLPIPE_MULTIPLEX
38 #error "too old libcurl, can't do HTTP/2 server push!"
39 #endif
40 
41 static
42 void dump(const char *text, unsigned char *ptr, size_t size,
43  char nohex)
44 {
45  size_t i;
46  size_t c;
47 
48  unsigned int width = 0x10;
49 
50  if(nohex)
51  /* without the hex output, we can fit more on screen */
52  width = 0x40;
53 
54  fprintf(stderr, "%s, %ld bytes (0x%lx)\n",
55  text, (long)size, (long)size);
56 
57  for(i = 0; i<size; i += width) {
58 
59  fprintf(stderr, "%4.4lx: ", (long)i);
60 
61  if(!nohex) {
62  /* hex not disabled, show it */
63  for(c = 0; c < width; c++)
64  if(i + c < size)
65  fprintf(stderr, "%02x ", ptr[i + c]);
66  else
67  fputs(" ", stderr);
68  }
69 
70  for(c = 0; (c < width) && (i + c < size); c++) {
71  /* check for 0D0A; if found, skip past and start a new line of output */
72  if(nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D &&
73  ptr[i + c + 1] == 0x0A) {
74  i += (c + 2 - width);
75  break;
76  }
77  fprintf(stderr, "%c",
78  (ptr[i + c] >= 0x20) && (ptr[i + c]<0x80)?ptr[i + c]:'.');
79  /* check again for 0D0A, to avoid an extra \n if it's at width */
80  if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D &&
81  ptr[i + c + 2] == 0x0A) {
82  i += (c + 3 - width);
83  break;
84  }
85  }
86  fputc('\n', stderr); /* newline */
87  }
88 }
89 
90 static
91 int my_trace(CURL *handle, curl_infotype type,
92  char *data, size_t size,
93  void *userp)
94 {
95  const char *text;
96  (void)handle; /* prevent compiler warning */
97  (void)userp;
98  switch(type) {
99  case CURLINFO_TEXT:
100  fprintf(stderr, "== Info: %s", data);
101  /* FALLTHROUGH */
102  default: /* in case a new one is introduced to shock us */
103  return 0;
104 
105  case CURLINFO_HEADER_OUT:
106  text = "=> Send header";
107  break;
108  case CURLINFO_DATA_OUT:
109  text = "=> Send data";
110  break;
112  text = "=> Send SSL data";
113  break;
114  case CURLINFO_HEADER_IN:
115  text = "<= Recv header";
116  break;
117  case CURLINFO_DATA_IN:
118  text = "<= Recv data";
119  break;
121  text = "<= Recv SSL data";
122  break;
123  }
124 
125  dump(text, (unsigned char *)data, size, 1);
126  return 0;
127 }
128 
129 #define OUTPUTFILE "dl"
130 
131 static void setup(CURL *hnd)
132 {
133  FILE *out = fopen(OUTPUTFILE, "wb");
134 
135  /* write to this file */
136  curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out);
137 
138  /* set the same URL */
139  curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html");
140 
141  /* please be verbose */
142  curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
143  curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace);
144 
145  /* HTTP/2 please */
146  curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
147 
148  /* we use a self-signed test server, skip verification during debugging */
149  curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
150  curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
151 
152 #if (CURLPIPE_MULTIPLEX > 0)
153  /* wait for pipe connection to confirm */
154  curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
155 #endif
156 
157 }
158 
159 /* called when there's an incoming push */
160 static int server_push_callback(CURL *parent,
161  CURL *easy,
162  size_t num_headers,
163  struct curl_pushheaders *headers,
164  void *userp)
165 {
166  char *headp;
167  size_t i;
168  int *transfers = (int *)userp;
169  char filename[128];
170  FILE *out;
171  static unsigned int count = 0;
172 
173  (void)parent; /* we have no use for this */
174 
175  snprintf(filename, 128, "push%u", count++);
176 
177  /* here's a new stream, save it in a new file for each new push */
178  out = fopen(filename, "wb");
179 
180  /* write to this file */
181  curl_easy_setopt(easy, CURLOPT_WRITEDATA, out);
182 
183  fprintf(stderr, "**** push callback approves stream %u, got %d headers!\n",
184  count, (int)num_headers);
185 
186  for(i = 0; i<num_headers; i++) {
187  headp = curl_pushheader_bynum(headers, i);
188  fprintf(stderr, "**** header %u: %s\n", (int)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  return CURL_PUSH_OK;
198 }
199 
200 
201 /*
202  * Download a file over HTTP/2, take care of server push.
203  */
204 int main(void)
205 {
206  CURL *easy;
208  int still_running; /* keep number of running handles */
209  int transfers = 1; /* we start with one */
210  struct CURLMsg *m;
211 
212  /* init a multi stack */
213  multi_handle = curl_multi_init();
214 
215  easy = curl_easy_init();
216 
217  /* set options */
218  setup(easy);
219 
220  /* add the easy transfer */
221  curl_multi_add_handle(multi_handle, easy);
222 
223  curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
224  curl_multi_setopt(multi_handle, CURLMOPT_PUSHFUNCTION, server_push_callback);
225  curl_multi_setopt(multi_handle, CURLMOPT_PUSHDATA, &transfers);
226 
227  /* we start some action by calling perform right away */
228  curl_multi_perform(multi_handle, &still_running);
229 
230  do {
231  struct timeval timeout;
232  int rc; /* select() return code */
233  CURLMcode mc; /* curl_multi_fdset() return code */
234 
235  fd_set fdread;
236  fd_set fdwrite;
237  fd_set fdexcep;
238  int maxfd = -1;
239 
240  long curl_timeo = -1;
241 
242  FD_ZERO(&fdread);
243  FD_ZERO(&fdwrite);
244  FD_ZERO(&fdexcep);
245 
246  /* set a suitable timeout to play around with */
247  timeout.tv_sec = 1;
248  timeout.tv_usec = 0;
249 
250  curl_multi_timeout(multi_handle, &curl_timeo);
251  if(curl_timeo >= 0) {
252  timeout.tv_sec = curl_timeo / 1000;
253  if(timeout.tv_sec > 1)
254  timeout.tv_sec = 1;
255  else
256  timeout.tv_usec = (curl_timeo % 1000) * 1000;
257  }
258 
259  /* get file descriptors from the transfers */
260  mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
261 
262  if(mc != CURLM_OK) {
263  fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc);
264  break;
265  }
266 
267  /* On success the value of maxfd is guaranteed to be >= -1. We call
268  select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
269  no fds ready yet so we call select(0, ...) --or Sleep() on Windows--
270  to sleep 100ms, which is the minimum suggested value in the
271  curl_multi_fdset() doc. */
272 
273  if(maxfd == -1) {
274 #ifdef _WIN32
275  Sleep(100);
276  rc = 0;
277 #else
278  /* Portable sleep for platforms other than Windows. */
279  struct timeval wait = { 0, 100 * 1000 }; /* 100ms */
280  rc = select(0, NULL, NULL, NULL, &wait);
281 #endif
282  }
283  else {
284  /* Note that on some platforms 'timeout' may be modified by select().
285  If you need access to the original value save a copy beforehand. */
286  rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
287  }
288 
289  switch(rc) {
290  case -1:
291  /* select error */
292  break;
293  case 0:
294  default:
295  /* timeout or readable/writable sockets */
296  curl_multi_perform(multi_handle, &still_running);
297  break;
298  }
299 
300  /*
301  * A little caution when doing server push is that libcurl itself has
302  * created and added one or more easy handles but we need to clean them up
303  * when we are done.
304  */
305 
306  do {
307  int msgq = 0;;
308  m = curl_multi_info_read(multi_handle, &msgq);
309  if(m && (m->msg == CURLMSG_DONE)) {
310  CURL *e = m->easy_handle;
311  transfers--;
312  curl_multi_remove_handle(multi_handle, e);
314  }
315  } while(m);
316 
317  } while(transfers); /* as long as we have transfers going */
318 
319  curl_multi_cleanup(multi_handle);
320 
321 
322  return 0;
323 }
CURL_EXTERN char * curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
Definition: http2.c:2273
filename
CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd)
CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *curl_handle)
int main(void)
#define CURL_PUSH_OK
Definition: multi.h:419
CURL * easy_handle
Definition: multi.h:95
CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles)
CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, long *milliseconds)
UNITTEST_START char * ptr
Definition: unit1330.c:38
Definition: multi.h:93
#define curl_easy_setopt(handle, option, value)
Definition: typecheck-gcc.h:41
unsigned int i
Definition: unit1303.c:79
CURL_EXTERN CURLM * curl_multi_init(void)
Definition: multi.c:355
static void setup(CURL *hnd)
#define OUTPUTFILE
CURL_EXTERN char * curl_pushheader_byname(struct curl_pushheaders *h, const char *name)
Definition: http2.c:2280
static CURLM * multi_handle
Definition: fopen.c:91
#define curl_multi_setopt(handle, opt, param)
UNITTEST_START int rc
Definition: unit1301.c:31
CURL_EXTERN CURL * curl_easy_init(void)
Definition: easy.c:343
CURLMSG msg
Definition: multi.h:94
CURL_EXTERN void curl_easy_cleanup(CURL *curl)
#define CURLPIPE_MULTIPLEX
int width
Definition: unit1398.c:34
CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, CURL *curl_handle)
CURLMcode
Definition: multi.h:61
void CURLM
Definition: multi.h:58
static CURL * hnd
Definition: unit1396.c:24
void CURL
Definition: curl.h:102
CURL_EXTERN CURLMsg * curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
Definition: multi.h:64
size_t size
Definition: unit1302.c:52
#define fprintf
Definition: curl_printf.h:41
#define snprintf
Definition: curl_printf.h:42
static CURL * easy[MAX_EASY_HANDLES]
static void dump(const char *text, unsigned char *ptr, size_t size, char nohex)
curl_infotype
Definition: curl.h:429
static int my_trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp)
static int server_push_callback(CURL *parent, CURL *easy, size_t num_headers, struct curl_pushheaders *headers, void *userp)
Definition: debug.c:29
CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle)


rc_tagdetect_client
Author(s): Monika Florek-Jasinska , Raphael Schaller
autogenerated on Sat Feb 13 2021 03:42:15