Return to http2-upload.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: * Multiplexed HTTP/2 uploads over a single connection ! 24: * </DESC> ! 25: */ ! 26: #include <stdio.h> ! 27: #include <stdlib.h> ! 28: #include <string.h> ! 29: #include <fcntl.h> ! 30: #include <sys/stat.h> ! 31: ! 32: /* somewhat unix-specific */ ! 33: #include <sys/time.h> ! 34: #include <unistd.h> ! 35: ! 36: /* curl stuff */ ! 37: #include <curl/curl.h> ! 38: ! 39: #ifndef CURLPIPE_MULTIPLEX ! 40: /* This little trick will just make sure that we don't enable pipelining for ! 41: libcurls old enough to not have this symbol. It is _not_ defined to zero in ! 42: a recent libcurl header. */ ! 43: #define CURLPIPE_MULTIPLEX 0 ! 44: #endif ! 45: ! 46: #define NUM_HANDLES 1000 ! 47: ! 48: struct input { ! 49: FILE *in; ! 50: size_t bytes_read; /* count up */ ! 51: CURL *hnd; ! 52: int num; ! 53: }; ! 54: ! 55: static ! 56: void dump(const char *text, int num, unsigned char *ptr, size_t size, ! 57: char nohex) ! 58: { ! 59: size_t i; ! 60: size_t c; ! 61: unsigned int width = 0x10; ! 62: ! 63: if(nohex) ! 64: /* without the hex output, we can fit more on screen */ ! 65: width = 0x40; ! 66: ! 67: fprintf(stderr, "%d %s, %lu bytes (0x%lx)\n", ! 68: num, text, (unsigned long)size, (unsigned long)size); ! 69: ! 70: for(i = 0; i<size; i += width) { ! 71: ! 72: fprintf(stderr, "%4.4lx: ", (unsigned long)i); ! 73: ! 74: if(!nohex) { ! 75: /* hex not disabled, show it */ ! 76: for(c = 0; c < width; c++) ! 77: if(i + c < size) ! 78: fprintf(stderr, "%02x ", ptr[i + c]); ! 79: else ! 80: fputs(" ", stderr); ! 81: } ! 82: ! 83: for(c = 0; (c < width) && (i + c < size); c++) { ! 84: /* check for 0D0A; if found, skip past and start a new line of output */ ! 85: if(nohex && (i + c + 1 < size) && ptr[i + c] == 0x0D && ! 86: ptr[i + c + 1] == 0x0A) { ! 87: i += (c + 2 - width); ! 88: break; ! 89: } ! 90: fprintf(stderr, "%c", ! 91: (ptr[i + c] >= 0x20) && (ptr[i + c]<0x80)?ptr[i + c]:'.'); ! 92: /* check again for 0D0A, to avoid an extra \n if it's at width */ ! 93: if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D && ! 94: ptr[i + c + 2] == 0x0A) { ! 95: i += (c + 3 - width); ! 96: break; ! 97: } ! 98: } ! 99: fputc('\n', stderr); /* newline */ ! 100: } ! 101: } ! 102: ! 103: static ! 104: int my_trace(CURL *handle, curl_infotype type, ! 105: char *data, size_t size, ! 106: void *userp) ! 107: { ! 108: char timebuf[60]; ! 109: const char *text; ! 110: struct input *i = (struct input *)userp; ! 111: int num = i->num; ! 112: static time_t epoch_offset; ! 113: static int known_offset; ! 114: struct timeval tv; ! 115: time_t secs; ! 116: struct tm *now; ! 117: (void)handle; /* prevent compiler warning */ ! 118: ! 119: gettimeofday(&tv, NULL); ! 120: if(!known_offset) { ! 121: epoch_offset = time(NULL) - tv.tv_sec; ! 122: known_offset = 1; ! 123: } ! 124: secs = epoch_offset + tv.tv_sec; ! 125: now = localtime(&secs); /* not thread safe but we don't care */ ! 126: snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld", ! 127: now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec); ! 128: ! 129: switch(type) { ! 130: case CURLINFO_TEXT: ! 131: fprintf(stderr, "%s [%d] Info: %s", timebuf, num, data); ! 132: /* FALLTHROUGH */ ! 133: default: /* in case a new one is introduced to shock us */ ! 134: return 0; ! 135: ! 136: case CURLINFO_HEADER_OUT: ! 137: text = "=> Send header"; ! 138: break; ! 139: case CURLINFO_DATA_OUT: ! 140: text = "=> Send data"; ! 141: break; ! 142: case CURLINFO_SSL_DATA_OUT: ! 143: text = "=> Send SSL data"; ! 144: break; ! 145: case CURLINFO_HEADER_IN: ! 146: text = "<= Recv header"; ! 147: break; ! 148: case CURLINFO_DATA_IN: ! 149: text = "<= Recv data"; ! 150: break; ! 151: case CURLINFO_SSL_DATA_IN: ! 152: text = "<= Recv SSL data"; ! 153: break; ! 154: } ! 155: ! 156: dump(text, num, (unsigned char *)data, size, 1); ! 157: return 0; ! 158: } ! 159: ! 160: static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp) ! 161: { ! 162: struct input *i = userp; ! 163: size_t retcode = fread(ptr, size, nmemb, i->in); ! 164: i->bytes_read += retcode; ! 165: return retcode; ! 166: } ! 167: ! 168: static void setup(struct input *i, int num, const char *upload) ! 169: { ! 170: FILE *out; ! 171: char url[256]; ! 172: char filename[128]; ! 173: struct stat file_info; ! 174: curl_off_t uploadsize; ! 175: CURL *hnd; ! 176: ! 177: hnd = i->hnd = curl_easy_init(); ! 178: i->num = num; ! 179: snprintf(filename, 128, "dl-%d", num); ! 180: out = fopen(filename, "wb"); ! 181: ! 182: snprintf(url, 256, "https://localhost:8443/upload-%d", num); ! 183: ! 184: /* get the file size of the local file */ ! 185: stat(upload, &file_info); ! 186: uploadsize = file_info.st_size; ! 187: ! 188: i->in = fopen(upload, "rb"); ! 189: ! 190: /* write to this file */ ! 191: curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out); ! 192: ! 193: /* we want to use our own read function */ ! 194: curl_easy_setopt(hnd, CURLOPT_READFUNCTION, read_callback); ! 195: /* read from this file */ ! 196: curl_easy_setopt(hnd, CURLOPT_READDATA, i); ! 197: /* provide the size of the upload */ ! 198: curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE, uploadsize); ! 199: ! 200: /* send in the URL to store the upload as */ ! 201: curl_easy_setopt(hnd, CURLOPT_URL, url); ! 202: ! 203: /* upload please */ ! 204: curl_easy_setopt(hnd, CURLOPT_UPLOAD, 1L); ! 205: ! 206: /* please be verbose */ ! 207: curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); ! 208: curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace); ! 209: curl_easy_setopt(hnd, CURLOPT_DEBUGDATA, i); ! 210: ! 211: /* HTTP/2 please */ ! 212: curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); ! 213: ! 214: /* we use a self-signed test server, skip verification during debugging */ ! 215: curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); ! 216: curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L); ! 217: ! 218: #if (CURLPIPE_MULTIPLEX > 0) ! 219: /* wait for pipe connection to confirm */ ! 220: curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); ! 221: #endif ! 222: } ! 223: ! 224: /* ! 225: * Upload all files over HTTP/2, using the same physical connection! ! 226: */ ! 227: int main(int argc, char **argv) ! 228: { ! 229: struct input trans[NUM_HANDLES]; ! 230: CURLM *multi_handle; ! 231: int i; ! 232: int still_running = 0; /* keep number of running handles */ ! 233: const char *filename = "index.html"; ! 234: int num_transfers; ! 235: ! 236: if(argc > 1) { ! 237: /* if given a number, do that many transfers */ ! 238: num_transfers = atoi(argv[1]); ! 239: ! 240: if(!num_transfers || (num_transfers > NUM_HANDLES)) ! 241: num_transfers = 3; /* a suitable low default */ ! 242: ! 243: if(argc > 2) ! 244: /* if given a file name, upload this! */ ! 245: filename = argv[2]; ! 246: } ! 247: else ! 248: num_transfers = 3; ! 249: ! 250: /* init a multi stack */ ! 251: multi_handle = curl_multi_init(); ! 252: ! 253: for(i = 0; i<num_transfers; i++) { ! 254: setup(&trans[i], i, filename); ! 255: ! 256: /* add the individual transfer */ ! 257: curl_multi_add_handle(multi_handle, trans[i].hnd); ! 258: } ! 259: ! 260: curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); ! 261: ! 262: /* We do HTTP/2 so let's stick to one connection per host */ ! 263: curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, 1L); ! 264: ! 265: /* we start some action by calling perform right away */ ! 266: curl_multi_perform(multi_handle, &still_running); ! 267: ! 268: while(still_running) { ! 269: struct timeval timeout; ! 270: int rc; /* select() return code */ ! 271: CURLMcode mc; /* curl_multi_fdset() return code */ ! 272: ! 273: fd_set fdread; ! 274: fd_set fdwrite; ! 275: fd_set fdexcep; ! 276: int maxfd = -1; ! 277: ! 278: long curl_timeo = -1; ! 279: ! 280: FD_ZERO(&fdread); ! 281: FD_ZERO(&fdwrite); ! 282: FD_ZERO(&fdexcep); ! 283: ! 284: /* set a suitable timeout to play around with */ ! 285: timeout.tv_sec = 1; ! 286: timeout.tv_usec = 0; ! 287: ! 288: curl_multi_timeout(multi_handle, &curl_timeo); ! 289: if(curl_timeo >= 0) { ! 290: timeout.tv_sec = curl_timeo / 1000; ! 291: if(timeout.tv_sec > 1) ! 292: timeout.tv_sec = 1; ! 293: else ! 294: timeout.tv_usec = (curl_timeo % 1000) * 1000; ! 295: } ! 296: ! 297: /* get file descriptors from the transfers */ ! 298: mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); ! 299: ! 300: if(mc != CURLM_OK) { ! 301: fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc); ! 302: break; ! 303: } ! 304: ! 305: /* On success the value of maxfd is guaranteed to be >= -1. We call ! 306: select(maxfd + 1, ...); specially in case of (maxfd == -1) there are ! 307: no fds ready yet so we call select(0, ...) --or Sleep() on Windows-- ! 308: to sleep 100ms, which is the minimum suggested value in the ! 309: curl_multi_fdset() doc. */ ! 310: ! 311: if(maxfd == -1) { ! 312: #ifdef _WIN32 ! 313: Sleep(100); ! 314: rc = 0; ! 315: #else ! 316: /* Portable sleep for platforms other than Windows. */ ! 317: struct timeval wait = { 0, 100 * 1000 }; /* 100ms */ ! 318: rc = select(0, NULL, NULL, NULL, &wait); ! 319: #endif ! 320: } ! 321: else { ! 322: /* Note that on some platforms 'timeout' may be modified by select(). ! 323: If you need access to the original value save a copy beforehand. */ ! 324: rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); ! 325: } ! 326: ! 327: switch(rc) { ! 328: case -1: ! 329: /* select error */ ! 330: break; ! 331: case 0: ! 332: default: ! 333: /* timeout or readable/writable sockets */ ! 334: curl_multi_perform(multi_handle, &still_running); ! 335: break; ! 336: } ! 337: } ! 338: ! 339: curl_multi_cleanup(multi_handle); ! 340: ! 341: for(i = 0; i<num_transfers; i++) { ! 342: curl_multi_remove_handle(multi_handle, trans[i].hnd); ! 343: curl_easy_cleanup(trans[i].hnd); ! 344: } ! 345: ! 346: return 0; ! 347: }