c524c55a797618abe0a485574cfcc13445c22e8b jcasper Thu Jul 25 11:52:12 2024 -0700 Adding support for setting FAILONERROR to fakeCurl, refs #33225 diff --git src/hg/lib/fakeCurl.c src/hg/lib/fakeCurl.c index 360e453..0c5868d 100644 --- src/hg/lib/fakeCurl.c +++ src/hg/lib/fakeCurl.c @@ -12,30 +12,31 @@ #include "common.h" #include "udc.h" #include "fakeCurl.h" CURL *curl_easy_init(void) /* Create a new fakeCurl object. Dispose of this with curl_easy_cleanup(). */ { CURL *new = (CURL*) malloc (sizeof(CURL)); new->url = NULL; new->range = NULL; new->writeBuffer = NULL; new->WriteFunction = NULL; new->HeaderFunction = NULL; + new->failonerror = 0; return new; } void curl_easy_cleanup(CURL *curl) /* Free a curl object allocated with curl_easy_init(). Do not attempt * to use the pointer's value after calling this function on it. */ { if (curl != NULL) { // clear the local copies of URL strings if (curl->url) free(curl->url); if (curl->range) free(curl->range); @@ -74,30 +75,33 @@ break; case CURLOPT_URL: if (curl->url) free(curl->url); curl->url = cloneString(va_arg(args,char *)); break; case CURLOPT_FOLLOWLOCATION: // ignored break; case CURLOPT_USERAGENT: // ignored break; case CURLOPT_HEADERFUNCTION: curl->HeaderFunction = va_arg(args, curl_write_callback); break; + case CURLOPT_FAILONERROR: + curl->failonerror = 1; + break; default: errAbort("Unexpected curl option supplied to fakeCurl"); } va_end(args); return CURLE_OK; } CURLcode curl_easy_perform(CURL *curl) /* Perform a fake curl operation via UDC, using the settings establised in the CURL object * via calls to curl_easy_setopt(). The return value will be either CURLE_OK (for success) * or CURLE_NOTOK (for failure). * * As noted in curl_easy_setopt(), the content provided to any supplied header function is * a faked subset of actual header content - just a "Content-Range" string. */ @@ -107,36 +111,57 @@ if (udc == NULL) return CURLE_NOTOK; long fileSize = (long) udcFileSize(curl->url); // Set up the seek offset if there's a range supplied long start = 0; long end = fileSize; if (curl->range != NULL) { start = atol(curl->range); char *end_pos = strrchr(curl->range, '-'); if (end_pos != NULL && *(end_pos+1) != 0) end = atol(end_pos+1); } - // If there's a header function, fake up a Content-Range string for it to parse using the range - // and file size. + if (end >= fileSize) + end = fileSize-1; + + char headerbuf[4096]; + + if (start < 0 || start >= fileSize) + { + // We need to gin up a 416 error response here and abort if FAILONERROR is set. + // Otherwise our next step is udcSeek, which will just attempt an lseek + // and then errAbort when the requested range isn't within the file bounds. + safef(headerbuf, sizeof(headerbuf), + "HTTP/1.1 416 Requested Range Not Satisfiable\nContent-Range: bytes */%ld", fileSize); + if (curl->HeaderFunction != NULL) + { + char buf[4096]; + curl->HeaderFunction(buf, strlen(buf), 1, NULL); + } + if (curl->failonerror) + return CURLE_NOTOK; + return CURLE_OK; + } + + // Fake up a Content-Range string in a header in case there's a header function to call. + safef(headerbuf, sizeof(headerbuf), "Content-Range: bytes %ld-%ld/%ld", start, end, fileSize); if (curl->HeaderFunction != NULL) { char buf[4096]; - safef(buf, sizeof(buf), "Content-Range: bytes %ld-%ld/%ld", start, end, fileSize); curl->HeaderFunction(buf, strlen(buf), 1, NULL); } char *readBuffer = (char*) malloc(end-start); udcSeek(udc, start); long bytesRead = udcRead(udc, readBuffer, end-start); // Technically we should pay attention to the value returned by udcRead, as it might indicate // that fewer bytes than requested were actually read. Worry about that a bit later. // If writefunction is defined, then call that on the buffer of data we got from udc. // Otherwise, put it into the supplied writebuffer (which must exist). if (curl->WriteFunction != NULL) { curl->WriteFunction(readBuffer, bytesRead, 1, curl->writeBuffer); }