Return to http2-serverpush.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / docs / examples |
1.1 ! misho 1: /*************************************************************************** ! 2: * _ _ ____ _ ! 3: * Project ___| | | | _ \| | ! 4: * / __| | | | |_) | | ! 5: * | (__| |_| | _ <| |___ ! 6: * \___|\___/|_| \_\_____| ! 7: * ! 8: * Copyright (C) 1998 - 2019, 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, %lu bytes (0x%lx)\n", ! 55: text, (unsigned long)size, (unsigned long)size); ! 56: ! 57: for(i = 0; i<size; i += width) { ! 58: ! 59: fprintf(stderr, "%4.4lx: ", (unsigned 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; ! 111: case CURLINFO_SSL_DATA_OUT: ! 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; ! 120: case CURLINFO_SSL_DATA_IN: ! 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 int setup(CURL *hnd) ! 132: { ! 133: FILE *out = fopen(OUTPUTFILE, "wb"); ! 134: if(!out) ! 135: /* failed */ ! 136: return 1; ! 137: ! 138: /* write to this file */ ! 139: curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out); ! 140: ! 141: /* set the same URL */ ! 142: curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html"); ! 143: ! 144: /* please be verbose */ ! 145: curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); ! 146: curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace); ! 147: ! 148: /* HTTP/2 please */ ! 149: curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); ! 150: ! 151: /* we use a self-signed test server, skip verification during debugging */ ! 152: curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); ! 153: curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L); ! 154: ! 155: #if (CURLPIPE_MULTIPLEX > 0) ! 156: /* wait for pipe connection to confirm */ ! 157: curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); ! 158: #endif ! 159: return 0; /* all is good */ ! 160: } ! 161: ! 162: /* called when there's an incoming push */ ! 163: static int server_push_callback(CURL *parent, ! 164: CURL *easy, ! 165: size_t num_headers, ! 166: struct curl_pushheaders *headers, ! 167: void *userp) ! 168: { ! 169: char *headp; ! 170: size_t i; ! 171: int *transfers = (int *)userp; ! 172: char filename[128]; ! 173: FILE *out; ! 174: static unsigned int count = 0; ! 175: ! 176: (void)parent; /* we have no use for this */ ! 177: ! 178: snprintf(filename, 128, "push%u", count++); ! 179: ! 180: /* here's a new stream, save it in a new file for each new push */ ! 181: out = fopen(filename, "wb"); ! 182: if(!out) { ! 183: /* if we can't save it, deny it */ ! 184: fprintf(stderr, "Failed to create output file for push\n"); ! 185: return CURL_PUSH_DENY; ! 186: } ! 187: ! 188: /* write to this file */ ! 189: curl_easy_setopt(easy, CURLOPT_WRITEDATA, out); ! 190: ! 191: fprintf(stderr, "**** push callback approves stream %u, got %lu headers!\n", ! 192: count, (unsigned long)num_headers); ! 193: ! 194: for(i = 0; i<num_headers; i++) { ! 195: headp = curl_pushheader_bynum(headers, i); ! 196: fprintf(stderr, "**** header %lu: %s\n", (unsigned long)i, headp); ! 197: } ! 198: ! 199: headp = curl_pushheader_byname(headers, ":path"); ! 200: if(headp) { ! 201: fprintf(stderr, "**** The PATH is %s\n", headp /* skip :path + colon */); ! 202: } ! 203: ! 204: (*transfers)++; /* one more */ ! 205: return CURL_PUSH_OK; ! 206: } ! 207: ! 208: ! 209: /* ! 210: * Download a file over HTTP/2, take care of server push. ! 211: */ ! 212: int main(void) ! 213: { ! 214: CURL *easy; ! 215: CURLM *multi_handle; ! 216: int still_running; /* keep number of running handles */ ! 217: int transfers = 1; /* we start with one */ ! 218: struct CURLMsg *m; ! 219: ! 220: /* init a multi stack */ ! 221: multi_handle = curl_multi_init(); ! 222: ! 223: easy = curl_easy_init(); ! 224: ! 225: /* set options */ ! 226: if(setup(easy)) { ! 227: fprintf(stderr, "failed\n"); ! 228: return 1; ! 229: } ! 230: ! 231: /* add the easy transfer */ ! 232: curl_multi_add_handle(multi_handle, easy); ! 233: ! 234: curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); ! 235: curl_multi_setopt(multi_handle, CURLMOPT_PUSHFUNCTION, server_push_callback); ! 236: curl_multi_setopt(multi_handle, CURLMOPT_PUSHDATA, &transfers); ! 237: ! 238: /* we start some action by calling perform right away */ ! 239: curl_multi_perform(multi_handle, &still_running); ! 240: ! 241: do { ! 242: struct timeval timeout; ! 243: int rc; /* select() return code */ ! 244: CURLMcode mc; /* curl_multi_fdset() return code */ ! 245: ! 246: fd_set fdread; ! 247: fd_set fdwrite; ! 248: fd_set fdexcep; ! 249: int maxfd = -1; ! 250: ! 251: long curl_timeo = -1; ! 252: ! 253: FD_ZERO(&fdread); ! 254: FD_ZERO(&fdwrite); ! 255: FD_ZERO(&fdexcep); ! 256: ! 257: /* set a suitable timeout to play around with */ ! 258: timeout.tv_sec = 1; ! 259: timeout.tv_usec = 0; ! 260: ! 261: curl_multi_timeout(multi_handle, &curl_timeo); ! 262: if(curl_timeo >= 0) { ! 263: timeout.tv_sec = curl_timeo / 1000; ! 264: if(timeout.tv_sec > 1) ! 265: timeout.tv_sec = 1; ! 266: else ! 267: timeout.tv_usec = (curl_timeo % 1000) * 1000; ! 268: } ! 269: ! 270: /* get file descriptors from the transfers */ ! 271: mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); ! 272: ! 273: if(mc != CURLM_OK) { ! 274: fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc); ! 275: break; ! 276: } ! 277: ! 278: /* On success the value of maxfd is guaranteed to be >= -1. We call ! 279: select(maxfd + 1, ...); specially in case of (maxfd == -1) there are ! 280: no fds ready yet so we call select(0, ...) --or Sleep() on Windows-- ! 281: to sleep 100ms, which is the minimum suggested value in the ! 282: curl_multi_fdset() doc. */ ! 283: ! 284: if(maxfd == -1) { ! 285: #ifdef _WIN32 ! 286: Sleep(100); ! 287: rc = 0; ! 288: #else ! 289: /* Portable sleep for platforms other than Windows. */ ! 290: struct timeval wait = { 0, 100 * 1000 }; /* 100ms */ ! 291: rc = select(0, NULL, NULL, NULL, &wait); ! 292: #endif ! 293: } ! 294: else { ! 295: /* Note that on some platforms 'timeout' may be modified by select(). ! 296: If you need access to the original value save a copy beforehand. */ ! 297: rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); ! 298: } ! 299: ! 300: switch(rc) { ! 301: case -1: ! 302: /* select error */ ! 303: break; ! 304: case 0: ! 305: default: ! 306: /* timeout or readable/writable sockets */ ! 307: curl_multi_perform(multi_handle, &still_running); ! 308: break; ! 309: } ! 310: ! 311: /* ! 312: * A little caution when doing server push is that libcurl itself has ! 313: * created and added one or more easy handles but we need to clean them up ! 314: * when we are done. ! 315: */ ! 316: ! 317: do { ! 318: int msgq = 0;; ! 319: m = curl_multi_info_read(multi_handle, &msgq); ! 320: if(m && (m->msg == CURLMSG_DONE)) { ! 321: CURL *e = m->easy_handle; ! 322: transfers--; ! 323: curl_multi_remove_handle(multi_handle, e); ! 324: curl_easy_cleanup(e); ! 325: } ! 326: } while(m); ! 327: ! 328: } while(transfers); /* as long as we have transfers going */ ! 329: ! 330: curl_multi_cleanup(multi_handle); ! 331: ! 332: ! 333: return 0; ! 334: }