4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * To get a libcurl handle, call get_handle(). When you hold the
36 * handle, it is yours exclusively to use. After you have finished
37 * with the handle, put it back into the pool by calling put_handle().
51 #include <curl/curl.h>
53 #include <nbdkit-plugin.h>
55 #include "ascii-ctype.h"
56 #include "ascii-string.h"
62 /* Use '-D curl.pool=1' to debug handle pool. */
63 NBDKIT_DLL_PUBLIC
int curl_debug_pool
= 0;
65 /* Translate CURLcode to nbdkit_error. */
66 #define display_curl_error(ch, r, fs, ...) \
68 nbdkit_error ((fs ": %s: %s"), ## __VA_ARGS__, \
69 curl_easy_strerror ((r)), (ch)->errbuf); \
72 static struct curl_handle
*allocate_handle (void);
73 static void free_handle (struct curl_handle
*);
74 static int debug_cb (CURL
*handle
, curl_infotype type
,
75 const char *data
, size_t size
, void *);
76 static size_t header_cb (void *ptr
, size_t size
, size_t nmemb
, void *opaque
);
77 static size_t write_cb (char *ptr
, size_t size
, size_t nmemb
, void *opaque
);
78 static size_t read_cb (void *ptr
, size_t size
, size_t nmemb
, void *opaque
);
80 /* This lock protects access to the curl_handles vector below. */
81 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
83 /* List of curl handles. This is allocated dynamically as more
84 * handles are requested. Currently it does not shrink. It may grow
85 * up to 'connections' in length.
87 DEFINE_VECTOR_TYPE (curl_handle_list
, struct curl_handle
*);
88 static curl_handle_list curl_handles
= empty_vector
;
90 /* The condition is used when the curl handles vector is full and
91 * we're waiting for a thread to put_handle.
93 static pthread_cond_t cond
= PTHREAD_COND_INITIALIZER
;
94 static size_t in_use
= 0, waiting
= 0;
96 /* Close and free all handles in the pool. */
98 free_all_handles (void)
103 nbdkit_debug ("free_all_handles: number of curl handles allocated: %zu",
106 for (i
= 0; i
< curl_handles
.len
; ++i
)
107 free_handle (curl_handles
.ptr
[i
]);
108 curl_handle_list_reset (&curl_handles
);
111 /* Get a handle from the pool.
113 * It is owned exclusively by the caller until they call put_handle.
118 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock
);
120 struct curl_handle
*ch
;
123 /* Look for a handle which is not in_use. */
124 for (i
= 0; i
< curl_handles
.len
; ++i
) {
125 ch
= curl_handles
.ptr
[i
];
130 nbdkit_debug ("get_handle: %zu", ch
->i
);
135 /* If more connections are allowed, then allocate a new handle. */
136 if (curl_handles
.len
< connections
) {
137 ch
= allocate_handle ();
140 if (curl_handle_list_append (&curl_handles
, ch
) == -1) {
144 ch
->i
= curl_handles
.len
- 1;
148 nbdkit_debug ("get_handle: %zu", ch
->i
);
152 /* Otherwise we have run out of connections so we must wait until
153 * another thread calls put_handle.
155 assert (in_use
== connections
);
157 while (in_use
== connections
)
158 pthread_cond_wait (&cond
, &lock
);
164 /* Return the handle to the pool. */
166 put_handle (struct curl_handle
*ch
)
168 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock
);
171 nbdkit_debug ("put_handle: %zu", ch
->i
);
176 /* Signal the next thread which is waiting. */
178 pthread_cond_signal (&cond
);
181 /* Allocate and initialize a new libcurl handle. */
182 static struct curl_handle
*
183 allocate_handle (void)
185 struct curl_handle
*ch
;
187 #ifdef HAVE_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
193 ch
= calloc (1, sizeof *ch
);
195 nbdkit_error ("calloc: %m");
200 ch
->c
= curl_easy_init ();
202 nbdkit_error ("curl_easy_init: failed: %m");
206 if (curl_debug_verbose
) {
207 /* NB: Constants must be explicitly long because the parameter is
210 curl_easy_setopt (ch
->c
, CURLOPT_VERBOSE
, 1L);
211 curl_easy_setopt (ch
->c
, CURLOPT_DEBUGFUNCTION
, debug_cb
);
214 curl_easy_setopt (ch
->c
, CURLOPT_ERRORBUFFER
, ch
->errbuf
);
217 if (unix_socket_path
) {
218 #if HAVE_CURLOPT_UNIX_SOCKET_PATH
219 r
= curl_easy_setopt (ch
->c
, CURLOPT_UNIX_SOCKET_PATH
, unix_socket_path
);
221 r
= CURLE_UNKNOWN_OPTION
;
225 display_curl_error (ch
, r
, "curl_easy_setopt: CURLOPT_UNIX_SOCKET_PATH");
230 r
= curl_easy_setopt (ch
->c
, CURLOPT_URL
, url
);
232 display_curl_error (ch
, r
, "curl_easy_setopt: CURLOPT_URL [%s]", url
);
236 /* Various options we always set.
238 * NB: Both here and below constants must be explicitly long because
239 * the parameter is varargs.
241 * For use of CURLOPT_NOSIGNAL see:
242 * https://curl.se/libcurl/c/CURLOPT_NOSIGNAL.html
244 curl_easy_setopt (ch
->c
, CURLOPT_NOSIGNAL
, 1L);
245 curl_easy_setopt (ch
->c
, CURLOPT_AUTOREFERER
, 1L);
247 curl_easy_setopt (ch
->c
, CURLOPT_FOLLOWLOCATION
, 1L);
248 curl_easy_setopt (ch
->c
, CURLOPT_FAILONERROR
, 1L);
252 if (strlen (cainfo
) == 0)
253 curl_easy_setopt (ch
->c
, CURLOPT_CAINFO
, NULL
);
255 curl_easy_setopt (ch
->c
, CURLOPT_CAINFO
, cainfo
);
258 curl_easy_setopt (ch
->c
, CURLOPT_CAPATH
, capath
);
260 curl_easy_setopt (ch
->c
, CURLOPT_COOKIE
, cookie
);
262 curl_easy_setopt (ch
->c
, CURLOPT_COOKIEFILE
, cookiefile
);
264 curl_easy_setopt (ch
->c
, CURLOPT_COOKIEJAR
, cookiejar
);
266 curl_easy_setopt (ch
->c
, CURLOPT_HTTPHEADER
, headers
);
267 if (http_version
!= CURL_HTTP_VERSION_NONE
)
268 curl_easy_setopt (ch
->c
, CURLOPT_HTTP_VERSION
, (long) http_version
);
271 curl_easy_setopt (ch
->c
, CURLOPT_PASSWORD
, password
);
272 #ifndef HAVE_CURLOPT_PROTOCOLS_STR
273 if (protocols
!= CURLPROTO_ALL
) {
274 curl_easy_setopt (ch
->c
, CURLOPT_PROTOCOLS
, protocols
);
275 curl_easy_setopt (ch
->c
, CURLOPT_REDIR_PROTOCOLS
, protocols
);
277 #else /* HAVE_CURLOPT_PROTOCOLS_STR (new in 7.85.0) */
279 curl_easy_setopt (ch
->c
, CURLOPT_PROTOCOLS_STR
, protocols
);
280 curl_easy_setopt (ch
->c
, CURLOPT_REDIR_PROTOCOLS_STR
, protocols
);
282 #endif /* HAVE_CURLOPT_PROTOCOLS_STR */
284 curl_easy_setopt (ch
->c
, CURLOPT_PROXY
, proxy
);
286 curl_easy_setopt (ch
->c
, CURLOPT_PROXYPASSWORD
, proxy_password
);
288 curl_easy_setopt (ch
->c
, CURLOPT_PROXYUSERNAME
, proxy_user
);
290 curl_easy_setopt (ch
->c
, CURLOPT_SSL_VERIFYPEER
, 0L);
291 curl_easy_setopt (ch
->c
, CURLOPT_SSL_VERIFYHOST
, 0L);
293 if (ssl_version
!= CURL_SSLVERSION_DEFAULT
)
294 curl_easy_setopt (ch
->c
, CURLOPT_SSLVERSION
, (long) ssl_version
);
296 curl_easy_setopt (ch
->c
, CURLOPT_SSL_CIPHER_LIST
, ssl_cipher_list
);
298 #if (LIBCURL_VERSION_MAJOR > 7) || \
299 (LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 61)
300 curl_easy_setopt (ch
->c
, CURLOPT_TLS13_CIPHERS
, tls13_ciphers
);
302 /* This is not available before curl-7.61 */
303 nbdkit_error ("tls13-ciphers is not supported in this build of "
304 "nbdkit-curl-plugin");
309 curl_easy_setopt (ch
->c
, CURLOPT_TCP_KEEPALIVE
, 1L);
311 curl_easy_setopt (ch
->c
, CURLOPT_TCP_NODELAY
, 0L);
313 curl_easy_setopt (ch
->c
, CURLOPT_TIMEOUT
, (long) timeout
);
315 curl_easy_setopt (ch
->c
, CURLOPT_USERNAME
, user
);
317 curl_easy_setopt (ch
->c
, CURLOPT_USERAGENT
, user_agent
);
319 /* Get the file size and also whether the remote HTTP server
320 * supports byte ranges.
322 * We must run the scripts if necessary and set headers in the
325 if (do_scripts (ch
) == -1) goto err
;
326 ch
->accept_range
= false;
327 curl_easy_setopt (ch
->c
, CURLOPT_NOBODY
, 1L); /* No Body, not nobody! */
328 curl_easy_setopt (ch
->c
, CURLOPT_HEADERFUNCTION
, header_cb
);
329 curl_easy_setopt (ch
->c
, CURLOPT_HEADERDATA
, ch
);
330 r
= curl_easy_perform (ch
->c
);
332 display_curl_error (ch
, r
,
333 "problem doing HEAD request to fetch size of URL [%s]",
338 #ifdef HAVE_CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
339 r
= curl_easy_getinfo (ch
->c
, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T
, &o
);
341 display_curl_error (ch
, r
,
342 "could not get length of remote file [%s]", url
);
347 nbdkit_error ("could not get length of remote file [%s], "
348 "is the URL correct?", url
);
354 r
= curl_easy_getinfo (ch
->c
, CURLINFO_CONTENT_LENGTH_DOWNLOAD
, &d
);
356 display_curl_error (ch
, r
,
357 "could not get length of remote file [%s]", url
);
362 nbdkit_error ("could not get length of remote file [%s], "
363 "is the URL correct?", url
);
369 nbdkit_debug ("content length: %" PRIi64
, ch
->exportsize
);
371 if (ascii_strncasecmp (url
, "http://", strlen ("http://")) == 0 ||
372 ascii_strncasecmp (url
, "https://", strlen ("https://")) == 0) {
373 if (!ch
->accept_range
) {
374 nbdkit_error ("server does not support 'range' (byte range) requests");
378 nbdkit_debug ("accept range supported (for HTTP/HTTPS)");
381 /* Get set up for reading and writing. */
382 curl_easy_setopt (ch
->c
, CURLOPT_HEADERFUNCTION
, NULL
);
383 curl_easy_setopt (ch
->c
, CURLOPT_HEADERDATA
, NULL
);
384 curl_easy_setopt (ch
->c
, CURLOPT_WRITEFUNCTION
, write_cb
);
385 curl_easy_setopt (ch
->c
, CURLOPT_WRITEDATA
, ch
);
386 /* These are only used if !readonly but we always register them. */
387 curl_easy_setopt (ch
->c
, CURLOPT_READFUNCTION
, read_cb
);
388 curl_easy_setopt (ch
->c
, CURLOPT_READDATA
, ch
);
394 curl_easy_cleanup (ch
->c
);
399 /* When using CURLOPT_VERBOSE, this callback is used to redirect
400 * messages to nbdkit_debug (instead of stderr).
403 debug_cb (CURL
*handle
, curl_infotype type
,
404 const char *data
, size_t size
, void *opaque
)
406 size_t origsize
= size
;
407 CLEANUP_FREE
char *str
;
409 /* The data parameter passed is NOT \0-terminated, but also it may
410 * have \n or \r\n line endings. The only sane way to deal with
411 * this is to copy the string. (The data strings may also be
412 * multi-line, but we don't deal with that here).
414 str
= malloc (size
+ 1);
417 memcpy (str
, data
, size
);
420 while (size
> 0 && (str
[size
-1] == '\n' || str
[size
-1] == '\r')) {
427 nbdkit_debug ("%s", str
);
429 case CURLINFO_HEADER_IN
:
430 nbdkit_debug ("S: %s", str
);
432 case CURLINFO_HEADER_OUT
:
433 nbdkit_debug ("C: %s", str
);
436 /* Assume everything else is binary data that we cannot print. */
437 nbdkit_debug ("<data with size=%zu>", origsize
);
445 header_cb (void *ptr
, size_t size
, size_t nmemb
, void *opaque
)
447 struct curl_handle
*ch
= opaque
;
448 size_t realsize
= size
* nmemb
;
449 const char *header
= ptr
;
450 const char *end
= header
+ realsize
;
451 const char *accept_ranges
= "accept-ranges:";
452 const char *bytes
= "bytes";
454 if (realsize
>= strlen (accept_ranges
) &&
455 ascii_strncasecmp (header
, accept_ranges
, strlen (accept_ranges
)) == 0) {
456 const char *p
= strchr (header
, ':') + 1;
458 /* Skip whitespace between the header name and value. */
459 while (p
< end
&& *p
&& ascii_isspace (*p
))
462 if (end
- p
>= strlen (bytes
)
463 && strncmp (p
, bytes
, strlen (bytes
)) == 0) {
464 /* Check that there is nothing but whitespace after the value. */
466 while (p
< end
&& *p
&& ascii_isspace (*p
))
470 ch
->accept_range
= true;
477 /* NB: The terminology used by libcurl is confusing!
479 * WRITEFUNCTION / write_cb is used when reading from the remote server
480 * READFUNCTION / read_cb is used when writing to the remote server.
482 * We use the same terminology as libcurl here.
486 write_cb (char *ptr
, size_t size
, size_t nmemb
, void *opaque
)
488 struct curl_handle
*ch
= opaque
;
489 size_t orig_realsize
= size
* nmemb
;
490 size_t realsize
= orig_realsize
;
492 assert (ch
->write_buf
);
494 /* Don't read more than the requested amount of data, even if the
495 * server or libcurl sends more.
497 if (realsize
> ch
->write_count
)
498 realsize
= ch
->write_count
;
500 memcpy (ch
->write_buf
, ptr
, realsize
);
502 ch
->write_count
-= realsize
;
503 ch
->write_buf
+= realsize
;
505 return orig_realsize
;
509 read_cb (void *ptr
, size_t size
, size_t nmemb
, void *opaque
)
511 struct curl_handle
*ch
= opaque
;
512 size_t realsize
= size
* nmemb
;
514 assert (ch
->read_buf
);
515 if (realsize
> ch
->read_count
)
516 realsize
= ch
->read_count
;
518 memcpy (ptr
, ch
->read_buf
, realsize
);
520 ch
->read_count
-= realsize
;
521 ch
->read_buf
+= realsize
;
527 free_handle (struct curl_handle
*ch
)
529 curl_easy_cleanup (ch
->c
);
530 if (ch
->headers_copy
)
531 curl_slist_free_all (ch
->headers_copy
);