http2-pushinmemory.c (5092B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 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.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. Receive all data in memory. 26 * </DESC> 27 */ 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 32 /* curl stuff */ 33 #include <curl/curl.h> 34 35 struct Memory { 36 char *memory; 37 size_t size; 38 }; 39 40 static size_t 41 write_cb(void *contents, size_t size, size_t nmemb, void *userp) 42 { 43 size_t realsize = size * nmemb; 44 struct Memory *mem = (struct Memory *)userp; 45 char *ptr = realloc(mem->memory, mem->size + realsize + 1); 46 if(!ptr) { 47 /* out of memory! */ 48 printf("not enough memory (realloc returned NULL)\n"); 49 return 0; 50 } 51 52 mem->memory = ptr; 53 memcpy(&(mem->memory[mem->size]), contents, realsize); 54 mem->size += realsize; 55 mem->memory[mem->size] = 0; 56 57 return realsize; 58 } 59 60 #define MAX_FILES 10 61 static struct Memory files[MAX_FILES]; 62 static int pushindex = 1; 63 64 static void init_memory(struct Memory *chunk) 65 { 66 chunk->memory = malloc(1); /* grown as needed with realloc */ 67 chunk->size = 0; /* no data at this point */ 68 } 69 70 static void setup(CURL *hnd) 71 { 72 /* set the same URL */ 73 curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html"); 74 75 /* HTTP/2 please */ 76 curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); 77 78 /* we use a self-signed test server, skip verification during debugging */ 79 curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); 80 curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L); 81 82 /* write data to a struct */ 83 curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, write_cb); 84 init_memory(&files[0]); 85 curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &files[0]); 86 87 /* wait for pipe connection to confirm */ 88 curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); 89 } 90 91 /* called when there is an incoming push */ 92 static int server_push_callback(CURL *parent, 93 CURL *easy, 94 size_t num_headers, 95 struct curl_pushheaders *headers, 96 void *userp) 97 { 98 char *headp; 99 int *transfers = (int *)userp; 100 (void)parent; /* we have no use for this */ 101 (void)num_headers; /* unused */ 102 103 if(pushindex == MAX_FILES) 104 /* cannot fit anymore */ 105 return CURL_PUSH_DENY; 106 107 /* write to this buffer */ 108 init_memory(&files[pushindex]); 109 curl_easy_setopt(easy, CURLOPT_WRITEDATA, &files[pushindex]); 110 pushindex++; 111 112 headp = curl_pushheader_byname(headers, ":path"); 113 if(headp) 114 fprintf(stderr, "* Pushed :path '%s'\n", headp /* skip :path + colon */); 115 116 (*transfers)++; /* one more */ 117 return CURL_PUSH_OK; 118 } 119 120 121 /* 122 * Download a file over HTTP/2, take care of server push. 123 */ 124 int main(void) 125 { 126 CURL *easy; 127 CURLM *multi; 128 int still_running; /* keep number of running handles */ 129 int transfers = 1; /* we start with one */ 130 int i; 131 struct CURLMsg *m; 132 133 /* init a multi stack */ 134 multi = curl_multi_init(); 135 136 easy = curl_easy_init(); 137 138 /* set options */ 139 setup(easy); 140 141 /* add the easy transfer */ 142 curl_multi_add_handle(multi, easy); 143 144 curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); 145 curl_multi_setopt(multi, CURLMOPT_PUSHFUNCTION, server_push_callback); 146 curl_multi_setopt(multi, CURLMOPT_PUSHDATA, &transfers); 147 148 while(transfers) { 149 int rc; 150 CURLMcode mcode = curl_multi_perform(multi, &still_running); 151 if(mcode) 152 break; 153 154 mcode = curl_multi_wait(multi, NULL, 0, 1000, &rc); 155 if(mcode) 156 break; 157 158 159 /* 160 * When doing server push, libcurl itself created and added one or more 161 * easy handles but *we* need to clean them up when they are done. 162 */ 163 do { 164 int msgq = 0; 165 m = curl_multi_info_read(multi, &msgq); 166 if(m && (m->msg == CURLMSG_DONE)) { 167 CURL *e = m->easy_handle; 168 transfers--; 169 curl_multi_remove_handle(multi, e); 170 curl_easy_cleanup(e); 171 } 172 } while(m); 173 174 } 175 176 177 curl_multi_cleanup(multi); 178 179 /* 'pushindex' is now the number of received transfers */ 180 for(i = 0; i < pushindex; i++) { 181 /* do something fun with the data, and then free it when done */ 182 free(files[i].memory); 183 } 184 185 return 0; 186 }