http2-serverpush.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 /* <DESC>
00023  * HTTP/2 server push
00024  * </DESC>
00025  */
00026 #include <stdio.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 
00030 /* somewhat unix-specific */
00031 #include <sys/time.h>
00032 #include <unistd.h>
00033 
00034 /* curl stuff */
00035 #include <curl/curl.h>
00036 
00037 #ifndef CURLPIPE_MULTIPLEX
00038 #error "too old libcurl, can't do HTTP/2 server push!"
00039 #endif
00040 
00041 static
00042 void dump(const char *text, unsigned char *ptr, size_t size,
00043           char nohex)
00044 {
00045   size_t i;
00046   size_t c;
00047 
00048   unsigned int width=0x10;
00049 
00050   if(nohex)
00051     /* without the hex output, we can fit more on screen */
00052     width = 0x40;
00053 
00054   fprintf(stderr, "%s, %ld bytes (0x%lx)\n",
00055           text, (long)size, (long)size);
00056 
00057   for(i=0; i<size; i+= width) {
00058 
00059     fprintf(stderr, "%4.4lx: ", (long)i);
00060 
00061     if(!nohex) {
00062       /* hex not disabled, show it */
00063       for(c = 0; c < width; c++)
00064         if(i+c < size)
00065           fprintf(stderr, "%02x ", ptr[i+c]);
00066         else
00067           fputs("   ", stderr);
00068     }
00069 
00070     for(c = 0; (c < width) && (i+c < size); c++) {
00071       /* check for 0D0A; if found, skip past and start a new line of output */
00072       if(nohex && (i+c+1 < size) && ptr[i+c]==0x0D && ptr[i+c+1]==0x0A) {
00073         i+=(c+2-width);
00074         break;
00075       }
00076       fprintf(stderr, "%c",
00077               (ptr[i+c]>=0x20) && (ptr[i+c]<0x80)?ptr[i+c]:'.');
00078       /* check again for 0D0A, to avoid an extra \n if it's at width */
00079       if(nohex && (i+c+2 < size) && ptr[i+c+1]==0x0D && ptr[i+c+2]==0x0A) {
00080         i+=(c+3-width);
00081         break;
00082       }
00083     }
00084     fputc('\n', stderr); /* newline */
00085   }
00086 }
00087 
00088 static
00089 int my_trace(CURL *handle, curl_infotype type,
00090              char *data, size_t size,
00091              void *userp)
00092 {
00093   const char *text;
00094   (void)handle; /* prevent compiler warning */
00095   (void)userp;
00096   switch(type) {
00097   case CURLINFO_TEXT:
00098     fprintf(stderr, "== Info: %s", data);
00099   default: /* in case a new one is introduced to shock us */
00100     return 0;
00101 
00102   case CURLINFO_HEADER_OUT:
00103     text = "=> Send header";
00104     break;
00105   case CURLINFO_DATA_OUT:
00106     text = "=> Send data";
00107     break;
00108   case CURLINFO_SSL_DATA_OUT:
00109     text = "=> Send SSL data";
00110     break;
00111   case CURLINFO_HEADER_IN:
00112     text = "<= Recv header";
00113     break;
00114   case CURLINFO_DATA_IN:
00115     text = "<= Recv data";
00116     break;
00117   case CURLINFO_SSL_DATA_IN:
00118     text = "<= Recv SSL data";
00119     break;
00120   }
00121 
00122   dump(text, (unsigned char *)data, size, 1);
00123   return 0;
00124 }
00125 
00126 #define OUTPUTFILE "dl"
00127 
00128 static void setup(CURL *hnd)
00129 {
00130   FILE *out = fopen(OUTPUTFILE, "wb");
00131 
00132   /* write to this file */
00133   curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out);
00134 
00135   /* set the same URL */
00136   curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html");
00137 
00138   /* send it verbose for max debuggaility */
00139   curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
00140   curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace);
00141 
00142   /* HTTP/2 please */
00143   curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
00144 
00145   /* we use a self-signed test server, skip verification during debugging */
00146   curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
00147   curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
00148 
00149 #if (CURLPIPE_MULTIPLEX > 0)
00150   /* wait for pipe connection to confirm */
00151   curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
00152 #endif
00153 
00154 }
00155 
00156 /* called when there's an incoming push */
00157 static int server_push_callback(CURL *parent,
00158                                 CURL *easy,
00159                                 size_t num_headers,
00160                                 struct curl_pushheaders *headers,
00161                                 void *userp)
00162 {
00163   char *headp;
00164   size_t i;
00165   int *transfers = (int *)userp;
00166   char filename[128];
00167   FILE *out;
00168   static unsigned int count = 0;
00169 
00170   (void)parent; /* we have no use for this */
00171 
00172   snprintf(filename, 128, "push%u", count++);
00173 
00174   /* here's a new stream, save it in a new file for each new push */
00175   out = fopen(filename, "wb");
00176 
00177   /* write to this file */
00178   curl_easy_setopt(easy, CURLOPT_WRITEDATA, out);
00179 
00180   fprintf(stderr, "**** push callback approves stream %u, got %d headers!\n",
00181           count, (int)num_headers);
00182 
00183   for(i=0; i<num_headers; i++) {
00184     headp = curl_pushheader_bynum(headers, i);
00185     fprintf(stderr, "**** header %u: %s\n", (int)i, headp);
00186   }
00187 
00188   headp = curl_pushheader_byname(headers, ":path");
00189   if(headp) {
00190     fprintf(stderr, "**** The PATH is %s\n", headp /* skip :path + colon */);
00191   }
00192 
00193   (*transfers)++; /* one more */
00194   return CURL_PUSH_OK;
00195 }
00196 
00197 
00198 /*
00199  * Download a file over HTTP/2, take care of server push.
00200  */
00201 int main(void)
00202 {
00203   CURL *easy;
00204   CURLM *multi_handle;
00205   int still_running; /* keep number of running handles */
00206   int transfers=1; /* we start with one */
00207   struct CURLMsg *m;
00208 
00209   /* init a multi stack */
00210   multi_handle = curl_multi_init();
00211 
00212   easy = curl_easy_init();
00213 
00214   /* set options */
00215   setup(easy);
00216 
00217   /* add the easy transfer */
00218   curl_multi_add_handle(multi_handle, easy);
00219 
00220   curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
00221   curl_multi_setopt(multi_handle, CURLMOPT_PUSHFUNCTION, server_push_callback);
00222   curl_multi_setopt(multi_handle, CURLMOPT_PUSHDATA, &transfers);
00223 
00224   /* we start some action by calling perform right away */
00225   curl_multi_perform(multi_handle, &still_running);
00226 
00227   do {
00228     struct timeval timeout;
00229     int rc; /* select() return code */
00230     CURLMcode mc; /* curl_multi_fdset() return code */
00231 
00232     fd_set fdread;
00233     fd_set fdwrite;
00234     fd_set fdexcep;
00235     int maxfd = -1;
00236 
00237     long curl_timeo = -1;
00238 
00239     FD_ZERO(&fdread);
00240     FD_ZERO(&fdwrite);
00241     FD_ZERO(&fdexcep);
00242 
00243     /* set a suitable timeout to play around with */
00244     timeout.tv_sec = 1;
00245     timeout.tv_usec = 0;
00246 
00247     curl_multi_timeout(multi_handle, &curl_timeo);
00248     if(curl_timeo >= 0) {
00249       timeout.tv_sec = curl_timeo / 1000;
00250       if(timeout.tv_sec > 1)
00251         timeout.tv_sec = 1;
00252       else
00253         timeout.tv_usec = (curl_timeo % 1000) * 1000;
00254     }
00255 
00256     /* get file descriptors from the transfers */
00257     mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
00258 
00259     if(mc != CURLM_OK) {
00260       fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc);
00261       break;
00262     }
00263 
00264     /* On success the value of maxfd is guaranteed to be >= -1. We call
00265        select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
00266        no fds ready yet so we call select(0, ...) --or Sleep() on Windows--
00267        to sleep 100ms, which is the minimum suggested value in the
00268        curl_multi_fdset() doc. */
00269 
00270     if(maxfd == -1) {
00271 #ifdef _WIN32
00272       Sleep(100);
00273       rc = 0;
00274 #else
00275       /* Portable sleep for platforms other than Windows. */
00276       struct timeval wait = { 0, 100 * 1000 }; /* 100ms */
00277       rc = select(0, NULL, NULL, NULL, &wait);
00278 #endif
00279     }
00280     else {
00281       /* Note that on some platforms 'timeout' may be modified by select().
00282          If you need access to the original value save a copy beforehand. */
00283       rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
00284     }
00285 
00286     switch(rc) {
00287     case -1:
00288       /* select error */
00289       break;
00290     case 0:
00291     default:
00292       /* timeout or readable/writable sockets */
00293       curl_multi_perform(multi_handle, &still_running);
00294       break;
00295     }
00296 
00297     /*
00298      * A little caution when doing server push is that libcurl itself has
00299      * created and added one or more easy handles but we need to clean them up
00300      * when we are done.
00301      */
00302 
00303     do {
00304       int msgq = 0;;
00305       m = curl_multi_info_read(multi_handle, &msgq);
00306       if(m && (m->msg == CURLMSG_DONE)) {
00307         CURL *e = m->easy_handle;
00308         transfers--;
00309         curl_multi_remove_handle(multi_handle, e);
00310         curl_easy_cleanup(e);
00311       }
00312     } while(m);
00313 
00314   } while(transfers); /* as long as we have transfers going */
00315 
00316   curl_multi_cleanup(multi_handle);
00317 
00318 
00319   return 0;
00320 }


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:04