113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci/* <DESC> 2513498266Sopenharmony_ci * HTTP/2 server push. Receive all data in memory. 2613498266Sopenharmony_ci * </DESC> 2713498266Sopenharmony_ci */ 2813498266Sopenharmony_ci#include <stdio.h> 2913498266Sopenharmony_ci#include <stdlib.h> 3013498266Sopenharmony_ci#include <string.h> 3113498266Sopenharmony_ci 3213498266Sopenharmony_ci/* somewhat unix-specific */ 3313498266Sopenharmony_ci#include <sys/time.h> 3413498266Sopenharmony_ci#include <unistd.h> 3513498266Sopenharmony_ci 3613498266Sopenharmony_ci/* curl stuff */ 3713498266Sopenharmony_ci#include <curl/curl.h> 3813498266Sopenharmony_ci 3913498266Sopenharmony_cistruct Memory { 4013498266Sopenharmony_ci char *memory; 4113498266Sopenharmony_ci size_t size; 4213498266Sopenharmony_ci}; 4313498266Sopenharmony_ci 4413498266Sopenharmony_cistatic size_t 4513498266Sopenharmony_ciwrite_cb(void *contents, size_t size, size_t nmemb, void *userp) 4613498266Sopenharmony_ci{ 4713498266Sopenharmony_ci size_t realsize = size * nmemb; 4813498266Sopenharmony_ci struct Memory *mem = (struct Memory *)userp; 4913498266Sopenharmony_ci char *ptr = realloc(mem->memory, mem->size + realsize + 1); 5013498266Sopenharmony_ci if(!ptr) { 5113498266Sopenharmony_ci /* out of memory! */ 5213498266Sopenharmony_ci printf("not enough memory (realloc returned NULL)\n"); 5313498266Sopenharmony_ci return 0; 5413498266Sopenharmony_ci } 5513498266Sopenharmony_ci 5613498266Sopenharmony_ci mem->memory = ptr; 5713498266Sopenharmony_ci memcpy(&(mem->memory[mem->size]), contents, realsize); 5813498266Sopenharmony_ci mem->size += realsize; 5913498266Sopenharmony_ci mem->memory[mem->size] = 0; 6013498266Sopenharmony_ci 6113498266Sopenharmony_ci return realsize; 6213498266Sopenharmony_ci} 6313498266Sopenharmony_ci 6413498266Sopenharmony_ci#define MAX_FILES 10 6513498266Sopenharmony_cistatic struct Memory files[MAX_FILES]; 6613498266Sopenharmony_cistatic int pushindex = 1; 6713498266Sopenharmony_ci 6813498266Sopenharmony_cistatic void init_memory(struct Memory *chunk) 6913498266Sopenharmony_ci{ 7013498266Sopenharmony_ci chunk->memory = malloc(1); /* grown as needed with realloc */ 7113498266Sopenharmony_ci chunk->size = 0; /* no data at this point */ 7213498266Sopenharmony_ci} 7313498266Sopenharmony_ci 7413498266Sopenharmony_cistatic void setup(CURL *hnd) 7513498266Sopenharmony_ci{ 7613498266Sopenharmony_ci /* set the same URL */ 7713498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html"); 7813498266Sopenharmony_ci 7913498266Sopenharmony_ci /* HTTP/2 please */ 8013498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); 8113498266Sopenharmony_ci 8213498266Sopenharmony_ci /* we use a self-signed test server, skip verification during debugging */ 8313498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); 8413498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L); 8513498266Sopenharmony_ci 8613498266Sopenharmony_ci /* write data to a struct */ 8713498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, write_cb); 8813498266Sopenharmony_ci init_memory(&files[0]); 8913498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &files[0]); 9013498266Sopenharmony_ci 9113498266Sopenharmony_ci /* wait for pipe connection to confirm */ 9213498266Sopenharmony_ci curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); 9313498266Sopenharmony_ci} 9413498266Sopenharmony_ci 9513498266Sopenharmony_ci/* called when there is an incoming push */ 9613498266Sopenharmony_cistatic int server_push_callback(CURL *parent, 9713498266Sopenharmony_ci CURL *easy, 9813498266Sopenharmony_ci size_t num_headers, 9913498266Sopenharmony_ci struct curl_pushheaders *headers, 10013498266Sopenharmony_ci void *userp) 10113498266Sopenharmony_ci{ 10213498266Sopenharmony_ci char *headp; 10313498266Sopenharmony_ci int *transfers = (int *)userp; 10413498266Sopenharmony_ci (void)parent; /* we have no use for this */ 10513498266Sopenharmony_ci (void)num_headers; /* unused */ 10613498266Sopenharmony_ci 10713498266Sopenharmony_ci if(pushindex == MAX_FILES) 10813498266Sopenharmony_ci /* cannot fit anymore */ 10913498266Sopenharmony_ci return CURL_PUSH_DENY; 11013498266Sopenharmony_ci 11113498266Sopenharmony_ci /* write to this buffer */ 11213498266Sopenharmony_ci init_memory(&files[pushindex]); 11313498266Sopenharmony_ci curl_easy_setopt(easy, CURLOPT_WRITEDATA, &files[pushindex]); 11413498266Sopenharmony_ci pushindex++; 11513498266Sopenharmony_ci 11613498266Sopenharmony_ci headp = curl_pushheader_byname(headers, ":path"); 11713498266Sopenharmony_ci if(headp) 11813498266Sopenharmony_ci fprintf(stderr, "* Pushed :path '%s'\n", headp /* skip :path + colon */); 11913498266Sopenharmony_ci 12013498266Sopenharmony_ci (*transfers)++; /* one more */ 12113498266Sopenharmony_ci return CURL_PUSH_OK; 12213498266Sopenharmony_ci} 12313498266Sopenharmony_ci 12413498266Sopenharmony_ci 12513498266Sopenharmony_ci/* 12613498266Sopenharmony_ci * Download a file over HTTP/2, take care of server push. 12713498266Sopenharmony_ci */ 12813498266Sopenharmony_ciint main(void) 12913498266Sopenharmony_ci{ 13013498266Sopenharmony_ci CURL *easy; 13113498266Sopenharmony_ci CURLM *multi; 13213498266Sopenharmony_ci int still_running; /* keep number of running handles */ 13313498266Sopenharmony_ci int transfers = 1; /* we start with one */ 13413498266Sopenharmony_ci int i; 13513498266Sopenharmony_ci struct CURLMsg *m; 13613498266Sopenharmony_ci 13713498266Sopenharmony_ci /* init a multi stack */ 13813498266Sopenharmony_ci multi = curl_multi_init(); 13913498266Sopenharmony_ci 14013498266Sopenharmony_ci easy = curl_easy_init(); 14113498266Sopenharmony_ci 14213498266Sopenharmony_ci /* set options */ 14313498266Sopenharmony_ci setup(easy); 14413498266Sopenharmony_ci 14513498266Sopenharmony_ci /* add the easy transfer */ 14613498266Sopenharmony_ci curl_multi_add_handle(multi, easy); 14713498266Sopenharmony_ci 14813498266Sopenharmony_ci curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); 14913498266Sopenharmony_ci curl_multi_setopt(multi, CURLMOPT_PUSHFUNCTION, server_push_callback); 15013498266Sopenharmony_ci curl_multi_setopt(multi, CURLMOPT_PUSHDATA, &transfers); 15113498266Sopenharmony_ci 15213498266Sopenharmony_ci while(transfers) { 15313498266Sopenharmony_ci int rc; 15413498266Sopenharmony_ci CURLMcode mcode = curl_multi_perform(multi, &still_running); 15513498266Sopenharmony_ci if(mcode) 15613498266Sopenharmony_ci break; 15713498266Sopenharmony_ci 15813498266Sopenharmony_ci mcode = curl_multi_wait(multi, NULL, 0, 1000, &rc); 15913498266Sopenharmony_ci if(mcode) 16013498266Sopenharmony_ci break; 16113498266Sopenharmony_ci 16213498266Sopenharmony_ci 16313498266Sopenharmony_ci /* 16413498266Sopenharmony_ci * When doing server push, libcurl itself created and added one or more 16513498266Sopenharmony_ci * easy handles but *we* need to clean them up when they are done. 16613498266Sopenharmony_ci */ 16713498266Sopenharmony_ci do { 16813498266Sopenharmony_ci int msgq = 0; 16913498266Sopenharmony_ci m = curl_multi_info_read(multi, &msgq); 17013498266Sopenharmony_ci if(m && (m->msg == CURLMSG_DONE)) { 17113498266Sopenharmony_ci CURL *e = m->easy_handle; 17213498266Sopenharmony_ci transfers--; 17313498266Sopenharmony_ci curl_multi_remove_handle(multi, e); 17413498266Sopenharmony_ci curl_easy_cleanup(e); 17513498266Sopenharmony_ci } 17613498266Sopenharmony_ci } while(m); 17713498266Sopenharmony_ci 17813498266Sopenharmony_ci } 17913498266Sopenharmony_ci 18013498266Sopenharmony_ci 18113498266Sopenharmony_ci curl_multi_cleanup(multi); 18213498266Sopenharmony_ci 18313498266Sopenharmony_ci /* 'pushindex' is now the number of received transfers */ 18413498266Sopenharmony_ci for(i = 0; i < pushindex; i++) { 18513498266Sopenharmony_ci /* do something fun with the data, and then free it when done */ 18613498266Sopenharmony_ci free(files[i].memory); 18713498266Sopenharmony_ci } 18813498266Sopenharmony_ci 18913498266Sopenharmony_ci return 0; 19013498266Sopenharmony_ci} 191