Annotation of embedaddon/curl/tests/libtest/lib582.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 2020, 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: #include "test.h"
! 23:
! 24: #include <fcntl.h>
! 25:
! 26: #include "testutil.h"
! 27: #include "warnless.h"
! 28: #include "memdebug.h"
! 29:
! 30: #define TEST_HANG_TIMEOUT 60 * 1000
! 31:
! 32: struct Sockets
! 33: {
! 34: curl_socket_t *sockets;
! 35: int count; /* number of sockets actually stored in array */
! 36: int max_count; /* max number of sockets that fit in allocated array */
! 37: };
! 38:
! 39: struct ReadWriteSockets
! 40: {
! 41: struct Sockets read, write;
! 42: };
! 43:
! 44: /**
! 45: * Remove a file descriptor from a sockets array.
! 46: */
! 47: static void removeFd(struct Sockets* sockets, curl_socket_t fd, int mention)
! 48: {
! 49: int i;
! 50:
! 51: if(mention)
! 52: fprintf(stderr, "Remove socket fd %d\n", (int) fd);
! 53:
! 54: for(i = 0; i < sockets->count; ++i) {
! 55: if(sockets->sockets[i] == fd) {
! 56: if(i < sockets->count - 1)
! 57: memmove(&sockets->sockets[i], &sockets->sockets[i + 1],
! 58: sizeof(curl_socket_t) * (sockets->count - (i + 1)));
! 59: --sockets->count;
! 60: }
! 61: }
! 62: }
! 63:
! 64: /**
! 65: * Add a file descriptor to a sockets array.
! 66: */
! 67: static void addFd(struct Sockets* sockets, curl_socket_t fd, const char *what)
! 68: {
! 69: /**
! 70: * To ensure we only have each file descriptor once, we remove it then add
! 71: * it again.
! 72: */
! 73: fprintf(stderr, "Add socket fd %d for %s\n", (int) fd, what);
! 74: removeFd(sockets, fd, 0);
! 75: /*
! 76: * Allocate array storage when required.
! 77: */
! 78: if(!sockets->sockets) {
! 79: sockets->sockets = malloc(sizeof(curl_socket_t) * 20U);
! 80: if(!sockets->sockets)
! 81: return;
! 82: sockets->max_count = 20;
! 83: }
! 84: else if(sockets->count + 1 > sockets->max_count) {
! 85: curl_socket_t *oldptr = sockets->sockets;
! 86: sockets->sockets = realloc(oldptr, sizeof(curl_socket_t) *
! 87: (sockets->max_count + 20));
! 88: if(!sockets->sockets) {
! 89: /* cleanup in test_cleanup */
! 90: sockets->sockets = oldptr;
! 91: return;
! 92: }
! 93: sockets->max_count += 20;
! 94: }
! 95: /*
! 96: * Add file descriptor to array.
! 97: */
! 98: sockets->sockets[sockets->count] = fd;
! 99: ++sockets->count;
! 100: }
! 101:
! 102: /**
! 103: * Callback invoked by curl to poll reading / writing of a socket.
! 104: */
! 105: static int curlSocketCallback(CURL *easy, curl_socket_t s, int action,
! 106: void *userp, void *socketp)
! 107: {
! 108: struct ReadWriteSockets* sockets = userp;
! 109:
! 110: (void)easy; /* unused */
! 111: (void)socketp; /* unused */
! 112:
! 113: if(action == CURL_POLL_IN || action == CURL_POLL_INOUT)
! 114: addFd(&sockets->read, s, "read");
! 115:
! 116: if(action == CURL_POLL_OUT || action == CURL_POLL_INOUT)
! 117: addFd(&sockets->write, s, "write");
! 118:
! 119: if(action == CURL_POLL_REMOVE) {
! 120: removeFd(&sockets->read, s, 1);
! 121: removeFd(&sockets->write, s, 0);
! 122: }
! 123:
! 124: return 0;
! 125: }
! 126:
! 127: /**
! 128: * Callback invoked by curl to set a timeout.
! 129: */
! 130: static int curlTimerCallback(CURLM *multi, long timeout_ms, void *userp)
! 131: {
! 132: struct timeval* timeout = userp;
! 133:
! 134: (void)multi; /* unused */
! 135: if(timeout_ms != -1) {
! 136: *timeout = tutil_tvnow();
! 137: timeout->tv_usec += timeout_ms * 1000;
! 138: }
! 139: else {
! 140: timeout->tv_sec = -1;
! 141: }
! 142: return 0;
! 143: }
! 144:
! 145: /**
! 146: * Check for curl completion.
! 147: */
! 148: static int checkForCompletion(CURLM *curl, int *success)
! 149: {
! 150: int numMessages;
! 151: CURLMsg *message;
! 152: int result = 0;
! 153: *success = 0;
! 154: while((message = curl_multi_info_read(curl, &numMessages)) != NULL) {
! 155: if(message->msg == CURLMSG_DONE) {
! 156: result = 1;
! 157: if(message->data.result == CURLE_OK)
! 158: *success = 1;
! 159: else
! 160: *success = 0;
! 161: }
! 162: else {
! 163: fprintf(stderr, "Got an unexpected message from curl: %i\n",
! 164: (int)message->msg);
! 165: result = 1;
! 166: *success = 0;
! 167: }
! 168: }
! 169: return result;
! 170: }
! 171:
! 172: static int getMicroSecondTimeout(struct timeval* timeout)
! 173: {
! 174: struct timeval now;
! 175: ssize_t result;
! 176: now = tutil_tvnow();
! 177: result = (ssize_t)((timeout->tv_sec - now.tv_sec) * 1000000 +
! 178: timeout->tv_usec - now.tv_usec);
! 179: if(result < 0)
! 180: result = 0;
! 181:
! 182: return curlx_sztosi(result);
! 183: }
! 184:
! 185: /**
! 186: * Update a fd_set with all of the sockets in use.
! 187: */
! 188: static void updateFdSet(struct Sockets* sockets, fd_set* fdset,
! 189: curl_socket_t *maxFd)
! 190: {
! 191: int i;
! 192: for(i = 0; i < sockets->count; ++i) {
! 193: FD_SET(sockets->sockets[i], fdset);
! 194: if(*maxFd < sockets->sockets[i] + 1) {
! 195: *maxFd = sockets->sockets[i] + 1;
! 196: }
! 197: }
! 198: }
! 199:
! 200: static void notifyCurl(CURLM *curl, curl_socket_t s, int evBitmask,
! 201: const char *info)
! 202: {
! 203: int numhandles = 0;
! 204: CURLMcode result = curl_multi_socket_action(curl, s, evBitmask, &numhandles);
! 205: if(result != CURLM_OK) {
! 206: fprintf(stderr, "Curl error on %s: %i (%s)\n",
! 207: info, result, curl_multi_strerror(result));
! 208: }
! 209: }
! 210:
! 211: /**
! 212: * Invoke curl when a file descriptor is set.
! 213: */
! 214: static void checkFdSet(CURLM *curl, struct Sockets *sockets, fd_set *fdset,
! 215: int evBitmask, const char *name)
! 216: {
! 217: int i;
! 218: for(i = 0; i < sockets->count; ++i) {
! 219: if(FD_ISSET(sockets->sockets[i], fdset)) {
! 220: notifyCurl(curl, sockets->sockets[i], evBitmask, name);
! 221: }
! 222: }
! 223: }
! 224:
! 225: int test(char *URL)
! 226: {
! 227: int res = 0;
! 228: CURL *curl = NULL;
! 229: FILE *hd_src = NULL;
! 230: int hd;
! 231: struct_stat file_info;
! 232: CURLM *m = NULL;
! 233: struct ReadWriteSockets sockets = {{NULL, 0, 0}, {NULL, 0, 0}};
! 234: struct timeval timeout = {-1, 0};
! 235: int success = 0;
! 236:
! 237: start_test_timing();
! 238:
! 239: if(!libtest_arg3) {
! 240: fprintf(stderr, "Usage: lib582 [url] [filename] [username]\n");
! 241: return TEST_ERR_USAGE;
! 242: }
! 243:
! 244: hd_src = fopen(libtest_arg2, "rb");
! 245: if(NULL == hd_src) {
! 246: fprintf(stderr, "fopen() failed with error: %d (%s)\n",
! 247: errno, strerror(errno));
! 248: fprintf(stderr, "Error opening file: (%s)\n", libtest_arg2);
! 249: return TEST_ERR_FOPEN;
! 250: }
! 251:
! 252: /* get the file size of the local file */
! 253: hd = fstat(fileno(hd_src), &file_info);
! 254: if(hd == -1) {
! 255: /* can't open file, bail out */
! 256: fprintf(stderr, "fstat() failed with error: %d (%s)\n",
! 257: errno, strerror(errno));
! 258: fprintf(stderr, "ERROR: cannot open file (%s)\n", libtest_arg2);
! 259: fclose(hd_src);
! 260: return TEST_ERR_FSTAT;
! 261: }
! 262: fprintf(stderr, "Set to upload %d bytes\n", (int)file_info.st_size);
! 263:
! 264: res_global_init(CURL_GLOBAL_ALL);
! 265: if(res) {
! 266: fclose(hd_src);
! 267: return res;
! 268: }
! 269:
! 270: easy_init(curl);
! 271:
! 272: /* enable uploading */
! 273: easy_setopt(curl, CURLOPT_UPLOAD, 1L);
! 274:
! 275: /* specify target */
! 276: easy_setopt(curl, CURLOPT_URL, URL);
! 277:
! 278: /* go verbose */
! 279: easy_setopt(curl, CURLOPT_VERBOSE, 1L);
! 280:
! 281: /* now specify which file to upload */
! 282: easy_setopt(curl, CURLOPT_READDATA, hd_src);
! 283:
! 284: easy_setopt(curl, CURLOPT_USERPWD, libtest_arg3);
! 285: easy_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, "curl_client_key.pub");
! 286: easy_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, "curl_client_key");
! 287: easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
! 288:
! 289: easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size);
! 290:
! 291: multi_init(m);
! 292:
! 293: multi_setopt(m, CURLMOPT_SOCKETFUNCTION, curlSocketCallback);
! 294: multi_setopt(m, CURLMOPT_SOCKETDATA, &sockets);
! 295:
! 296: multi_setopt(m, CURLMOPT_TIMERFUNCTION, curlTimerCallback);
! 297: multi_setopt(m, CURLMOPT_TIMERDATA, &timeout);
! 298:
! 299: multi_add_handle(m, curl);
! 300:
! 301: while(!checkForCompletion(m, &success)) {
! 302: fd_set readSet, writeSet;
! 303: curl_socket_t maxFd = 0;
! 304: struct timeval tv = {10, 0};
! 305:
! 306: FD_ZERO(&readSet);
! 307: FD_ZERO(&writeSet);
! 308: updateFdSet(&sockets.read, &readSet, &maxFd);
! 309: updateFdSet(&sockets.write, &writeSet, &maxFd);
! 310:
! 311: if(timeout.tv_sec != -1) {
! 312: int usTimeout = getMicroSecondTimeout(&timeout);
! 313: tv.tv_sec = usTimeout / 1000000;
! 314: tv.tv_usec = usTimeout % 1000000;
! 315: }
! 316: else if(maxFd <= 0) {
! 317: tv.tv_sec = 0;
! 318: tv.tv_usec = 100000;
! 319: }
! 320:
! 321: select_test((int)maxFd, &readSet, &writeSet, NULL, &tv);
! 322:
! 323: /* Check the sockets for reading / writing */
! 324: checkFdSet(m, &sockets.read, &readSet, CURL_CSELECT_IN, "read");
! 325: checkFdSet(m, &sockets.write, &writeSet, CURL_CSELECT_OUT, "write");
! 326:
! 327: if(timeout.tv_sec != -1 && getMicroSecondTimeout(&timeout) == 0) {
! 328: /* Curl's timer has elapsed. */
! 329: notifyCurl(m, CURL_SOCKET_TIMEOUT, 0, "timeout");
! 330: }
! 331:
! 332: abort_on_test_timeout();
! 333: }
! 334:
! 335: if(!success) {
! 336: fprintf(stderr, "Error uploading file.\n");
! 337: res = TEST_ERR_MAJOR_BAD;
! 338: }
! 339:
! 340: test_cleanup:
! 341:
! 342: /* proper cleanup sequence - type PB */
! 343:
! 344: curl_multi_remove_handle(m, curl);
! 345: curl_easy_cleanup(curl);
! 346: curl_multi_cleanup(m);
! 347: curl_global_cleanup();
! 348:
! 349: /* close the local file */
! 350: fclose(hd_src);
! 351:
! 352: /* free local memory */
! 353: free(sockets.read.sockets);
! 354: free(sockets.write.sockets);
! 355:
! 356: return res;
! 357: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>