curl_http_client/
http_client.rs

1use std::{fmt::Debug, path::Path, time::Duration};
2
3use async_curl::Actor;
4use curl::easy::{Auth, Easy2, Handler, HttpVersion, List, ProxyType, SslVersion, TimeCondition};
5use derive_deref_rs::Deref;
6use http::{
7    header::{CONTENT_LENGTH, CONTENT_TYPE},
8    HeaderMap, HeaderValue, Method, Request, Response,
9};
10use log::trace;
11
12use crate::{Error, ExtendedHandler};
13
14/// The HttpClient struct's job is to wrap and build curl Easy2.
15pub struct HttpClient<C>
16where
17    C: Handler + Debug + Send + 'static,
18{
19    easy: Easy2<C>,
20}
21
22impl<C> HttpClient<C>
23where
24    C: ExtendedHandler + Debug + Send + 'static,
25{
26    /// Creates a new HTTP Client.
27    ///
28    /// The C is a generic type to be able to implement a custom HTTP response collector whoever uses this crate.
29    /// There is a built-in [`Collector`](https://docs.rs/curl-http-client/latest/curl_http_client/collector/enum.Collector.html) in this crate that can be used store HTTP response body into memory or in a File.
30    pub fn new(collector: C) -> Self {
31        Self {
32            easy: Easy2::new(collector),
33        }
34    }
35
36    /// This marks the end of the curl builder to be able to do asynchronous operation during perform.
37    ///
38    /// The parameter trait [`Actor<C>`](https://docs.rs/async-curl/latest/async_curl/actor/trait.Actor.html) is any custom Actor implemented by the user that
39    /// must implement a send_request that is non-blocking.
40    ///
41    /// There is a built-in [`CurlActor`](https://docs.rs/async-curl/latest/async_curl/actor/struct.CurlActor.html) that implements the
42    /// [`Actor<C>`](https://docs.rs/async-curl/latest/async_curl/actor/trait.Actor.html) trait that can be cloned
43    /// to be able to handle multiple request sender and a single consumer that is spawned in the background to be able to achieve
44    /// non-blocking I/O during curl perform.
45    pub fn nonblocking<A: Actor<C>>(self, actor: A) -> AsyncPerform<C, A> {
46        AsyncPerform::<C, A> {
47            actor,
48            easy: self.easy,
49        }
50    }
51
52    /// This marks the end of the curl builder to be able to do synchronous operation during perform.
53    pub fn blocking(self) -> SyncPerform<C> {
54        SyncPerform::<C> { easy: self.easy }
55    }
56
57    /// Add some headers to this HTTP request.
58    ///
59    /// If you add a header that is otherwise used internally, the value here
60    /// takes precedence. If a header is added with no content (like `Accept:`)
61    /// the internally the header will get disabled. To add a header with no
62    /// content, use the form `MyHeader;` (not the trailing semicolon).
63    ///
64    /// Headers must not be CRLF terminated. Many replaced headers have common
65    /// shortcuts which should be prefered.
66    pub fn http_headers(mut self, list: List) -> Result<Self, Error<C>> {
67        self.easy.http_headers(list).map_err(Error::Curl)?;
68        Ok(self)
69    }
70
71    /// Configures the data that will be uploaded as part of a POST.
72    ///
73    /// Note that the data is copied into this handle and if that's not desired
74    /// then the read callbacks can be used instead.
75    ///
76    /// By default this option is not set and corresponds to
77    /// `CURLOPT_COPYPOSTFIELDS`.
78    pub fn post_fields_copy(mut self, bytes: &[u8]) -> Result<Self, Error<C>> {
79        self.easy.post_fields_copy(bytes).map_err(Error::Curl)?;
80        Ok(self)
81    }
82
83    /// Sets the HTTP request.
84    ///
85    /// The HttpRequest can be customized by the caller by setting the Url, Method Type,
86    /// Headers and the Body.
87    pub fn request<B: CurlBodyRequest>(mut self, request: Request<B>) -> Result<Self, Error<C>> {
88        self.easy
89            .url(request.uri().to_string().as_str())
90            .map_err(|e| {
91                trace!("{:?}", e);
92                Error::Curl(e)
93            })?;
94
95        let mut headers = curl::easy::List::new();
96
97        request.headers().iter().try_for_each(|(name, value)| {
98            headers
99                .append(&format!(
100                    "{}: {}",
101                    name,
102                    value.to_str().map_err(|_| Error::Other(format!(
103                        "invalid {} header value {:?}",
104                        name,
105                        value.as_bytes()
106                    )))?
107                ))
108                .map_err(|e| {
109                    trace!("{:?}", e);
110                    Error::Curl(e)
111                })
112        })?;
113
114        self.easy.http_headers(headers).map_err(|e| {
115            trace!("{:?}", e);
116            Error::Curl(e)
117        })?;
118
119        match *request.method() {
120            Method::POST => {
121                self.easy.post(true).map_err(Error::Curl)?;
122
123                if let Some(body) = request.body().get_bytes() {
124                    self.easy.post_field_size(body.len() as u64).map_err(|e| {
125                        trace!("{:?}", e);
126                        Error::Curl(e)
127                    })?;
128                    self.easy.post_fields_copy(body).map_err(|e| {
129                        trace!("{:?}", e);
130                        Error::Curl(e)
131                    })?;
132                }
133            }
134            Method::GET => {
135                self.easy.get(true).map_err(Error::Curl)?;
136            }
137            Method::PUT => {
138                self.easy.upload(true).map_err(Error::Curl)?;
139            }
140            _ => {
141                // TODO: For Future improvements to handle other Methods
142                unimplemented!();
143            }
144        }
145        Ok(self)
146    }
147
148    /// Set a point to resume transfer from
149    ///
150    /// Specify the offset in bytes you want the transfer to start from.
151    ///
152    /// By default this option is 0 and corresponds to
153    /// `CURLOPT_RESUME_FROM_LARGE`.
154    pub fn resume_from(mut self, offset: BytesOffset) -> Result<Self, Error<C>> {
155        self.easy.resume_from(*offset as u64).map_err(Error::Curl)?;
156        Ok(self)
157    }
158
159    /// Rate limit data download speed
160    ///
161    /// If a download exceeds this speed (counted in bytes per second) on
162    /// cumulative average during the transfer, the transfer will pause to keep
163    /// the average rate less than or equal to the parameter value.
164    ///
165    /// By default this option is not set (unlimited speed) and corresponds to
166    /// `CURLOPT_MAX_RECV_SPEED_LARGE`.
167    pub fn download_speed(mut self, speed: Bps) -> Result<Self, Error<C>> {
168        self.easy.max_recv_speed(*speed).map_err(Error::Curl)?;
169        Ok(self)
170    }
171
172    /// Set the size of the input file to send off.
173    ///
174    /// By default this option is not set and corresponds to
175    /// `CURLOPT_INFILESIZE_LARGE`.
176    pub fn upload_file_size(mut self, size: FileSize) -> Result<Self, Error<C>> {
177        self.easy.in_filesize(*size as u64).map_err(Error::Curl)?;
178        Ok(self)
179    }
180
181    /// Rate limit data upload speed
182    ///
183    /// If an upload exceeds this speed (counted in bytes per second) on
184    /// cumulative average during the transfer, the transfer will pause to keep
185    /// the average rate less than or equal to the parameter value.
186    ///
187    /// By default this option is not set (unlimited speed) and corresponds to
188    /// `CURLOPT_MAX_SEND_SPEED_LARGE`.
189    pub fn upload_speed(mut self, speed: Bps) -> Result<Self, Error<C>> {
190        self.easy.max_send_speed(*speed).map_err(Error::Curl)?;
191        Ok(self)
192    }
193
194    // =========================================================================
195    // Names and passwords
196
197    /// Configures the username to pass as authentication for this connection.
198    ///
199    /// By default this value is not set and corresponds to `CURLOPT_USERNAME`.
200    pub fn username(mut self, user: &str) -> Result<Self, Error<C>> {
201        self.easy.username(user).map_err(Error::Curl)?;
202        Ok(self)
203    }
204
205    /// Configures the password to pass as authentication for this connection.
206    ///
207    /// By default this value is not set and corresponds to `CURLOPT_PASSWORD`.
208    pub fn password(mut self, pass: &str) -> Result<Self, Error<C>> {
209        self.easy.password(pass).map_err(Error::Curl)?;
210        Ok(self)
211    }
212
213    /// Set HTTP server authentication methods to try
214    ///
215    /// If more than one method is set, libcurl will first query the site to see
216    /// which authentication methods it supports and then pick the best one you
217    /// allow it to use. For some methods, this will induce an extra network
218    /// round-trip. Set the actual name and password with the `password` and
219    /// `username` methods.
220    ///
221    /// For authentication with a proxy, see `proxy_auth`.
222    ///
223    /// By default this value is basic and corresponds to `CURLOPT_HTTPAUTH`.
224    pub fn http_auth(mut self, auth: &Auth) -> Result<Self, Error<C>> {
225        self.easy.http_auth(auth).map_err(Error::Curl)?;
226        Ok(self)
227    }
228
229    /// Configures the port number to connect to, instead of the one specified
230    /// in the URL or the default of the protocol.
231    pub fn port(mut self, port: u16) -> Result<Self, Error<C>> {
232        self.easy.port(port).map_err(Error::Curl)?;
233        Ok(self)
234    }
235
236    // /// Verify the certificate's status.
237    // ///
238    // /// This option determines whether libcurl verifies the status of the server
239    // /// cert using the "Certificate Status Request" TLS extension (aka. OCSP
240    // /// stapling).
241    // ///
242    // /// By default this option is set to `false` and corresponds to
243    // /// `CURLOPT_SSL_VERIFYSTATUS`.
244    // pub fn ssl_verify_status(&mut self, verify: bool) -> Result<(), Error<C>> {
245    //     self.setopt_long(curl_sys::CURLOPT_SSL_VERIFYSTATUS, verify as c_long)
246    // }
247
248    /// Specify the path to Certificate Authority (CA) bundle
249    ///
250    /// The file referenced should hold one or more certificates to verify the
251    /// peer with.
252    ///
253    /// This option is by default set to the system path where libcurl's cacert
254    /// bundle is assumed to be stored, as established at build time.
255    ///
256    /// If curl is built against the NSS SSL library, the NSS PEM PKCS#11 module
257    /// (libnsspem.so) needs to be available for this option to work properly.
258    ///
259    /// By default this option is the system defaults, and corresponds to
260    /// `CURLOPT_CAINFO`.
261    pub fn cainfo<P: AsRef<Path>>(mut self, path: P) -> Result<Self, Error<C>> {
262        self.easy.cainfo(path).map_err(Error::Curl)?;
263        Ok(self)
264    }
265
266    /// Specify directory holding CA certificates
267    ///
268    /// Names a directory holding multiple CA certificates to verify the peer
269    /// with. If libcurl is built against OpenSSL, the certificate directory
270    /// must be prepared using the openssl c_rehash utility. This makes sense
271    /// only when used in combination with the `ssl_verify_peer` option.
272    ///
273    /// By default this option is not set and corresponds to `CURLOPT_CAPATH`.
274    pub fn capath<P: AsRef<Path>>(mut self, path: P) -> Result<Self, Error<C>> {
275        self.easy.capath(path).map_err(Error::Curl)?;
276        Ok(self)
277    }
278
279    /// Configures the proxy username to pass as authentication for this
280    /// connection.
281    ///
282    /// By default this value is not set and corresponds to
283    /// `CURLOPT_PROXYUSERNAME`.
284    pub fn proxy_username(mut self, user: &str) -> Result<Self, Error<C>> {
285        self.easy.proxy_username(user).map_err(Error::Curl)?;
286        Ok(self)
287    }
288
289    /// Configures the proxy password to pass as authentication for this
290    /// connection.
291    ///
292    /// By default this value is not set and corresponds to
293    /// `CURLOPT_PROXYPASSWORD`.
294    pub fn proxy_password(mut self, pass: &str) -> Result<Self, Error<C>> {
295        self.easy.proxy_password(pass).map_err(Error::Curl)?;
296        Ok(self)
297    }
298
299    /// Set HTTP proxy authentication methods to try
300    ///
301    /// If more than one method is set, libcurl will first query the site to see
302    /// which authentication methods it supports and then pick the best one you
303    /// allow it to use. For some methods, this will induce an extra network
304    /// round-trip. Set the actual name and password with the `proxy_password`
305    /// and `proxy_username` methods.
306    ///
307    /// By default this value is basic and corresponds to `CURLOPT_PROXYAUTH`.
308    pub fn proxy_auth(mut self, auth: &Auth) -> Result<Self, Error<C>> {
309        self.easy.proxy_auth(auth).map_err(Error::Curl)?;
310        Ok(self)
311    }
312
313    /// Provide the URL of a proxy to use.
314    ///
315    /// By default this option is not set and corresponds to `CURLOPT_PROXY`.
316    pub fn proxy(mut self, url: &str) -> Result<Self, Error<C>> {
317        self.easy.proxy(url).map_err(Error::Curl)?;
318        Ok(self)
319    }
320
321    /// Provide port number the proxy is listening on.
322    ///
323    /// By default this option is not set (the default port for the proxy
324    /// protocol is used) and corresponds to `CURLOPT_PROXYPORT`.
325    pub fn proxy_port(mut self, port: u16) -> Result<Self, Error<C>> {
326        self.easy.proxy_port(port).map_err(Error::Curl)?;
327        Ok(self)
328    }
329
330    /// Set CA certificate to verify peer against for proxy.
331    ///
332    /// By default this value is not set and corresponds to
333    /// `CURLOPT_PROXY_CAINFO`.
334    pub fn proxy_cainfo(mut self, cainfo: &str) -> Result<Self, Error<C>> {
335        self.easy.proxy_cainfo(cainfo).map_err(Error::Curl)?;
336        Ok(self)
337    }
338
339    /// Specify a directory holding CA certificates for proxy.
340    ///
341    /// The specified directory should hold multiple CA certificates to verify
342    /// the HTTPS proxy with. If libcurl is built against OpenSSL, the
343    /// certificate directory must be prepared using the OpenSSL `c_rehash`
344    /// utility.
345    ///
346    /// By default this value is not set and corresponds to
347    /// `CURLOPT_PROXY_CAPATH`.
348    pub fn proxy_capath<P: AsRef<Path>>(mut self, path: P) -> Result<Self, Error<C>> {
349        self.easy.proxy_capath(path).map_err(Error::Curl)?;
350        Ok(self)
351    }
352
353    /// Set client certificate for proxy.
354    ///
355    /// By default this value is not set and corresponds to
356    /// `CURLOPT_PROXY_SSLCERT`.
357    pub fn proxy_sslcert(mut self, sslcert: &str) -> Result<Self, Error<C>> {
358        self.easy.proxy_sslcert(sslcert).map_err(Error::Curl)?;
359        Ok(self)
360    }
361
362    /// Specify type of the client SSL certificate for HTTPS proxy.
363    ///
364    /// The string should be the format of your certificate. Supported formats
365    /// are "PEM" and "DER", except with Secure Transport. OpenSSL (versions
366    /// 0.9.3 and later) and Secure Transport (on iOS 5 or later, or OS X 10.7
367    /// or later) also support "P12" for PKCS#12-encoded files.
368    ///
369    /// By default this option is "PEM" and corresponds to
370    /// `CURLOPT_PROXY_SSLCERTTYPE`.
371    pub fn proxy_sslcert_type(mut self, kind: &str) -> Result<Self, Error<C>> {
372        self.easy.proxy_sslcert_type(kind).map_err(Error::Curl)?;
373        Ok(self)
374    }
375
376    /// Set the client certificate for the proxy using an in-memory blob.
377    ///
378    /// The specified byte buffer should contain the binary content of the
379    /// certificate, which will be copied into the handle.
380    ///
381    /// By default this option is not set and corresponds to
382    /// `CURLOPT_PROXY_SSLCERT_BLOB`.
383    pub fn proxy_sslcert_blob(mut self, blob: &[u8]) -> Result<Self, Error<C>> {
384        self.easy.proxy_sslcert_blob(blob).map_err(Error::Curl)?;
385        Ok(self)
386    }
387
388    /// Set private key for HTTPS proxy.
389    ///
390    /// By default this value is not set and corresponds to
391    /// `CURLOPT_PROXY_SSLKEY`.
392    pub fn proxy_sslkey(mut self, sslkey: &str) -> Result<Self, Error<C>> {
393        self.easy.proxy_sslkey(sslkey).map_err(Error::Curl)?;
394        Ok(self)
395    }
396
397    /// Set type of the private key file for HTTPS proxy.
398    ///
399    /// The string should be the format of your private key. Supported formats
400    /// are "PEM", "DER" and "ENG".
401    ///
402    /// The format "ENG" enables you to load the private key from a crypto
403    /// engine. In this case `ssl_key` is used as an identifier passed to
404    /// the engine. You have to set the crypto engine with `ssl_engine`.
405    /// "DER" format key file currently does not work because of a bug in
406    /// OpenSSL.
407    ///
408    /// By default this option is "PEM" and corresponds to
409    /// `CURLOPT_PROXY_SSLKEYTYPE`.
410    pub fn proxy_sslkey_type(mut self, kind: &str) -> Result<Self, Error<C>> {
411        self.easy.proxy_sslkey_type(kind).map_err(Error::Curl)?;
412        Ok(self)
413    }
414
415    /// Set the private key for the proxy using an in-memory blob.
416    ///
417    /// The specified byte buffer should contain the binary content of the
418    /// private key, which will be copied into the handle.
419    ///
420    /// By default this option is not set and corresponds to
421    /// `CURLOPT_PROXY_SSLKEY_BLOB`.
422    pub fn proxy_sslkey_blob(mut self, blob: &[u8]) -> Result<Self, Error<C>> {
423        self.easy.proxy_sslkey_blob(blob).map_err(Error::Curl)?;
424        Ok(self)
425    }
426
427    /// Set passphrase to private key for HTTPS proxy.
428    ///
429    /// This will be used as the password required to use the `ssl_key`.
430    /// You never needed a pass phrase to load a certificate but you need one to
431    /// load your private key.
432    ///
433    /// By default this option is not set and corresponds to
434    /// `CURLOPT_PROXY_KEYPASSWD`.
435    pub fn proxy_key_password(mut self, password: &str) -> Result<Self, Error<C>> {
436        self.easy
437            .proxy_key_password(password)
438            .map_err(Error::Curl)?;
439        Ok(self)
440    }
441
442    /// Indicates the type of proxy being used.
443    ///
444    /// By default this option is `ProxyType::Http` and corresponds to
445    /// `CURLOPT_PROXYTYPE`.
446    pub fn proxy_type(mut self, kind: ProxyType) -> Result<Self, Error<C>> {
447        self.easy.proxy_type(kind).map_err(Error::Curl)?;
448        Ok(self)
449    }
450
451    /// Provide a list of hosts that should not be proxied to.
452    ///
453    /// This string is a comma-separated list of hosts which should not use the
454    /// proxy specified for connections. A single `*` character is also accepted
455    /// as a wildcard for all hosts.
456    ///
457    /// By default this option is not set and corresponds to
458    /// `CURLOPT_NOPROXY`.
459    pub fn noproxy(mut self, skip: &str) -> Result<Self, Error<C>> {
460        self.easy.noproxy(skip).map_err(Error::Curl)?;
461        Ok(self)
462    }
463
464    /// Inform curl whether it should tunnel all operations through the proxy.
465    ///
466    /// This essentially means that a `CONNECT` is sent to the proxy for all
467    /// outbound requests.
468    ///
469    /// By default this option is `false` and corresponds to
470    /// `CURLOPT_HTTPPROXYTUNNEL`.
471    pub fn http_proxy_tunnel(mut self, tunnel: bool) -> Result<Self, Error<C>> {
472        self.easy.http_proxy_tunnel(tunnel).map_err(Error::Curl)?;
473        Ok(self)
474    }
475
476    /// Follow HTTP 3xx redirects.
477    ///
478    /// Indicates whether any `Location` headers in the response should get
479    /// followed.
480    ///
481    /// By default this option is `false` and corresponds to
482    /// `CURLOPT_FOLLOWLOCATION`.
483    pub fn follow_location(mut self, enable: bool) -> Result<Self, Error<C>> {
484        self.easy.follow_location(enable).map_err(Error::Curl)?;
485        Ok(self)
486    }
487
488    /// Force a new connection to be used.
489    ///
490    /// Makes the next transfer use a new (fresh) connection by force instead of
491    /// trying to re-use an existing one. This option should be used with
492    /// caution and only if you understand what it does as it may seriously
493    /// impact performance.
494    ///
495    /// By default this option is `false` and corresponds to
496    /// `CURLOPT_FRESH_CONNECT`.
497    pub fn fresh_connect(mut self, enable: bool) -> Result<Self, Error<C>> {
498        self.easy.fresh_connect(enable).map_err(Error::Curl)?;
499        Ok(self)
500    }
501
502    /// Make connection get closed at once after use.
503    ///
504    /// Makes libcurl explicitly close the connection when done with the
505    /// transfer. Normally, libcurl keeps all connections alive when done with
506    /// one transfer in case a succeeding one follows that can re-use them.
507    /// This option should be used with caution and only if you understand what
508    /// it does as it can seriously impact performance.
509    ///
510    /// By default this option is `false` and corresponds to
511    /// `CURLOPT_FORBID_REUSE`.
512    pub fn forbid_reuse(mut self, enable: bool) -> Result<Self, Error<C>> {
513        self.easy.forbid_reuse(enable).map_err(Error::Curl)?;
514        Ok(self)
515    }
516
517    /// Timeout for the connect phase
518    ///
519    /// This is the maximum time that you allow the connection phase to the
520    /// server to take. This only limits the connection phase, it has no impact
521    /// once it has connected.
522    ///
523    /// By default this value is 300 seconds and corresponds to
524    /// `CURLOPT_CONNECTTIMEOUT_MS`.
525    pub fn connect_timeout(mut self, timeout: Duration) -> Result<Self, Error<C>> {
526        self.easy.connect_timeout(timeout).map_err(Error::Curl)?;
527        Ok(self)
528    }
529
530    // =========================================================================
531    // Connection Options
532
533    /// Set maximum time the request is allowed to take.
534    ///
535    /// Normally, name lookups can take a considerable time and limiting
536    /// operations to less than a few minutes risk aborting perfectly normal
537    /// operations.
538    ///
539    /// If libcurl is built to use the standard system name resolver, that
540    /// portion of the transfer will still use full-second resolution for
541    /// timeouts with a minimum timeout allowed of one second.
542    ///
543    /// In unix-like systems, this might cause signals to be used unless
544    /// `nosignal` is set.
545    ///
546    /// Since this puts a hard limit for how long a request is allowed to
547    /// take, it has limited use in dynamic use cases with varying transfer
548    /// times. You are then advised to explore `low_speed_limit`,
549    /// `low_speed_time` or using `progress_function` to implement your own
550    /// timeout logic.
551    ///
552    /// By default this option is not set and corresponds to
553    /// `CURLOPT_TIMEOUT_MS`.
554    pub fn timeout(mut self, timeout: Duration) -> Result<Self, Error<C>> {
555        self.easy.timeout(timeout).map_err(Error::Curl)?;
556        Ok(self)
557    }
558
559    /// Set preferred HTTP version.
560    ///
561    /// By default this option is not set and corresponds to
562    /// `CURLOPT_HTTP_VERSION`.
563    pub fn http_version(mut self, version: HttpVersion) -> Result<Self, Error<C>> {
564        self.easy.http_version(version).map_err(Error::Curl)?;
565        Ok(self)
566    }
567
568    /// Set preferred TLS/SSL version.
569    ///
570    /// By default this option is not set and corresponds to
571    /// `CURLOPT_SSLVERSION`.
572    pub fn ssl_version(mut self, version: SslVersion) -> Result<Self, Error<C>> {
573        self.easy.ssl_version(version).map_err(Error::Curl)?;
574        Ok(self)
575    }
576
577    // =========================================================================
578    // Behavior options
579
580    /// Configures this handle to have verbose output to help debug protocol
581    /// information.
582    ///
583    /// By default output goes to stderr, but the `stderr` function on this type
584    /// can configure that. You can also use the `debug_function` method to get
585    /// all protocol data sent and received.
586    ///
587    /// By default, this option is `false`.
588    pub fn verbose(mut self, verbose: bool) -> Result<Self, Error<C>> {
589        self.easy.verbose(verbose).map_err(Error::Curl)?;
590        Ok(self)
591    }
592
593    /// Indicates whether header information is streamed to the output body of
594    /// this request.
595    ///
596    /// This option is only relevant for protocols which have header metadata
597    /// (like http or ftp). It's not generally possible to extract headers
598    /// from the body if using this method, that use case should be intended for
599    /// the `header_function` method.
600    ///
601    /// To set HTTP headers, use the `http_header` method.
602    ///
603    /// By default, this option is `false` and corresponds to
604    /// `CURLOPT_HEADER`.
605    pub fn show_header(mut self, show: bool) -> Result<Self, Error<C>> {
606        self.easy.show_header(show).map_err(Error::Curl)?;
607        Ok(self)
608    }
609
610    /// Indicates whether a progress meter will be shown for requests done with
611    /// this handle.
612    ///
613    /// This will also prevent the `progress_function` from being called.
614    ///
615    /// By default this option is `false` and corresponds to
616    /// `CURLOPT_NOPROGRESS`.
617    pub fn progress(mut self, progress: bool) -> Result<Self, Error<C>> {
618        self.easy.progress(progress).map_err(Error::Curl)?;
619        Ok(self)
620    }
621
622    /// Specify the preferred receive buffer size, in bytes.
623    ///
624    /// This is treated as a request, not an order, and the main point of this
625    /// is that the write callback may get called more often with smaller
626    /// chunks.
627    ///
628    /// By default this option is the maximum write size and corresopnds to
629    /// `CURLOPT_BUFFERSIZE`.
630    pub fn download_buffer_size(mut self, size: usize) -> Result<Self, Error<C>> {
631        self.easy.buffer_size(size).map_err(Error::Curl)?;
632        Ok(self)
633    }
634
635    /// Specify the preferred send buffer size, in bytes.
636    ///
637    /// This is treated as a request, not an order, and the main point of this
638    /// is that the read callback may get called more often with smaller
639    /// chunks.
640    ///
641    /// The upload buffer size is by default 64 kilobytes.
642    pub fn upload_buffer_size(mut self, size: usize) -> Result<Self, Error<C>> {
643        self.easy.upload_buffer_size(size).map_err(Error::Curl)?;
644        Ok(self)
645    }
646
647    /// Specify the preferred receive buffer size, in bytes.
648    ///
649    /// This is treated as a request, not an order, and the main point of this
650    /// is that the write callback may get called more often with smaller
651    /// chunks.
652    ///
653    /// By default this option is the maximum write size and corresopnds to
654    /// `CURLOPT_BUFFERSIZE`.
655    pub fn buffer_size(mut self, size: usize) -> Result<Self, Error<C>> {
656        self.easy.buffer_size(size).map_err(Error::Curl)?;
657        Ok(self)
658    }
659
660    /// Re-initializes this handle to the default values.
661    ///
662    /// This puts the handle to the same state as it was in when it was just
663    /// created. This does, however, keep live connections, the session id
664    /// cache, the dns cache, and cookies.
665    pub fn reset(&mut self) {
666        self.easy.reset()
667    }
668
669    /// Provides the URL which this handle will work with.
670    ///
671    /// The string provided must be URL-encoded with the format:
672    ///
673    /// ```text
674    /// scheme://host:port/path
675    /// ```
676    ///
677    /// The syntax is not validated as part of this function and that is
678    /// deferred until later.
679    ///
680    /// By default this option is not set and `perform` will not work until it
681    /// is set. This option corresponds to `CURLOPT_URL`.
682    pub fn url(mut self, url: &str) -> Result<Self, Error<C>> {
683        self.easy.url(url).map_err(Error::Curl)?;
684        Ok(self)
685    }
686
687    /// Set a custom request string
688    ///
689    /// Specifies that a custom request will be made (e.g. a custom HTTP
690    /// method). This does not change how libcurl performs internally, just
691    /// changes the string sent to the server.
692    ///
693    /// By default this option is not set and corresponds to
694    /// `CURLOPT_CUSTOMREQUEST`.
695    pub fn custom_request(mut self, request: &str) -> Result<Self, Error<C>> {
696        self.easy.custom_request(request).map_err(Error::Curl)?;
697        Ok(self)
698    }
699
700    /// Get the modification time of the remote resource
701    ///
702    /// If true, libcurl will attempt to get the modification time of the
703    /// remote document in this operation. This requires that the remote server
704    /// sends the time or replies to a time querying command. The `filetime`
705    /// function can be used after a transfer to extract the received time (if
706    /// any).
707    ///
708    /// By default this option is `false` and corresponds to `CURLOPT_FILETIME`
709    pub fn fetch_filetime(mut self, fetch: bool) -> Result<Self, Error<C>> {
710        self.easy.fetch_filetime(fetch).map_err(Error::Curl)?;
711        Ok(self)
712    }
713
714    /// Indicate whether to download the request without getting the body
715    ///
716    /// This is useful, for example, for doing a HEAD request.
717    ///
718    /// By default this option is `false` and corresponds to `CURLOPT_NOBODY`.
719    pub fn nobody(mut self, enable: bool) -> Result<Self, Error<C>> {
720        self.easy.nobody(enable).map_err(Error::Curl)?;
721        Ok(self)
722    }
723
724    /// Set the size of the input file to send off.
725    ///
726    /// By default this option is not set and corresponds to
727    /// `CURLOPT_INFILESIZE_LARGE`.
728    pub fn in_filesize(mut self, size: u64) -> Result<Self, Error<C>> {
729        self.easy.in_filesize(size).map_err(Error::Curl)?;
730        Ok(self)
731    }
732
733    /// Enable or disable data upload.
734    ///
735    /// This means that a PUT request will be made for HTTP and probably wants
736    /// to be combined with the read callback as well as the `in_filesize`
737    /// method.
738    ///
739    /// By default this option is `false` and corresponds to `CURLOPT_UPLOAD`.
740    pub fn upload(mut self, enable: bool) -> Result<Self, Error<C>> {
741        self.easy.upload(enable).map_err(Error::Curl)?;
742        Ok(self)
743    }
744
745    /// Configure the maximum file size to download.
746    ///
747    /// By default this option is not set and corresponds to
748    /// `CURLOPT_MAXFILESIZE_LARGE`.
749    pub fn max_filesize(mut self, size: u64) -> Result<Self, Error<C>> {
750        self.easy.max_filesize(size).map_err(Error::Curl)?;
751        Ok(self)
752    }
753
754    /// Selects a condition for a time request.
755    ///
756    /// This value indicates how the `time_value` option is interpreted.
757    ///
758    /// By default this option is not set and corresponds to
759    /// `CURLOPT_TIMECONDITION`.
760    pub fn time_condition(mut self, cond: TimeCondition) -> Result<Self, Error<C>> {
761        self.easy.time_condition(cond).map_err(Error::Curl)?;
762        Ok(self)
763    }
764
765    /// Sets the time value for a conditional request.
766    ///
767    /// The value here should be the number of seconds elapsed since January 1,
768    /// 1970. To pass how to interpret this value, use `time_condition`.
769    ///
770    /// By default this option is not set and corresponds to
771    /// `CURLOPT_TIMEVALUE`.
772    pub fn time_value(mut self, val: i64) -> Result<Self, Error<C>> {
773        self.easy.time_value(val).map_err(Error::Curl)?;
774        Ok(self)
775    }
776
777    /// Start a new cookie session
778    ///
779    /// Marks this as a new cookie "session". It will force libcurl to ignore
780    /// all cookies it is about to load that are "session cookies" from the
781    /// previous session. By default, libcurl always stores and loads all
782    /// cookies, independent if they are session cookies or not. Session cookies
783    /// are cookies without expiry date and they are meant to be alive and
784    /// existing for this "session" only.
785    ///
786    /// By default this option is `false` and corresponds to
787    /// `CURLOPT_COOKIESESSION`.
788    pub fn cookie_session(mut self, session: bool) -> Result<Self, Error<C>> {
789        self.easy.cookie_session(session).map_err(Error::Curl)?;
790        Ok(self)
791    }
792
793    /// Ask for a HTTP GET request.
794    ///
795    /// By default this option is `false` and corresponds to `CURLOPT_HTTPGET`.
796    pub fn get(mut self, enable: bool) -> Result<Self, Error<C>> {
797        self.easy.get(enable).map_err(Error::Curl)?;
798        Ok(self)
799    }
800
801    /// Make an HTTP POST request.
802    ///
803    /// This will also make the library use the
804    /// `Content-Type: application/x-www-form-urlencoded` header.
805    ///
806    /// POST data can be specified through `post_fields` or by specifying a read
807    /// function.
808    ///
809    /// By default this option is `false` and corresponds to `CURLOPT_POST`.
810    pub fn post(mut self, enable: bool) -> Result<Self, Error<C>> {
811        self.easy.post(enable).map_err(Error::Curl)?;
812        Ok(self)
813    }
814
815    /// Verify the certificate's name against host.
816    ///
817    /// This should be disabled with great caution! It basically disables the
818    /// security features of SSL if it is disabled.
819    ///
820    /// By default this option is set to `true` and corresponds to
821    /// `CURLOPT_SSL_VERIFYHOST`.
822    pub fn ssl_verify_host(mut self, enable: bool) -> Result<Self, Error<C>> {
823        self.easy.ssl_verify_host(enable).map_err(Error::Curl)?;
824        Ok(self)
825    }
826
827    /// Verify the peer's SSL certificate.
828    ///
829    /// This should be disabled with great caution! It basically disables the
830    /// security features of SSL if it is disabled.
831    ///
832    /// By default this option is set to `true` and corresponds to
833    /// `CURLOPT_SSL_VERIFYPEER`.
834    pub fn ssl_verify_peer(mut self, enable: bool) -> Result<Self, Error<C>> {
835        self.easy.ssl_verify_peer(enable).map_err(Error::Curl)?;
836        Ok(self)
837    }
838}
839
840/// The AsyncPerform struct is the result when calling nonblocking() function to signify the end of the builder.
841/// The main job of this is to perform the Curl in nonblocking fashion.
842pub struct AsyncPerform<C, A>
843where
844    C: Handler + Debug + Send + 'static,
845    A: Actor<C>,
846{
847    /// This is the the actor handler that can be cloned to be able to handle multiple request sender
848    /// and a single consumer that is spawned in the background upon creation of this object to be able to achieve
849    /// non-blocking I/O during curl perform.
850    actor: A,
851    /// The `Easy2<C>` is the Easy2 from curl-rust crate wrapped in this struct to be able to do
852    /// asynchronous task during perform operation.
853    easy: Easy2<C>,
854}
855
856impl<C, A> AsyncPerform<C, A>
857where
858    C: ExtendedHandler + Debug + Send,
859    A: Actor<C>,
860{
861    /// This will send the request asynchronously,
862    /// and return the underlying [`Easy2<C>`](https://docs.rs/curl/latest/curl/easy/struct.Easy2.html) useful if you
863    /// want to decide how to transform the response yourself.
864    ///
865    /// This becomes a non-blocking I/O since the actual perform operation is done
866    /// at the actor side using Curl-Multi.
867    pub async fn send_request(self) -> Result<Easy2<C>, Error<C>> {
868        self.actor.send_request(self.easy).await.map_err(|e| {
869            trace!("{:?}", e);
870            Error::Perform(e)
871        })
872    }
873
874    /// This will perform the curl operation asynchronously.
875    pub async fn perform(self) -> Result<Response<Option<Vec<u8>>>, Error<C>> {
876        let easy = self.send_request().await?;
877
878        let (data, headers) = easy.get_ref().get_response_body_and_headers();
879        let status_code = easy.response_code().map_err(|e| {
880            trace!("{:?}", e);
881            Error::Curl(e)
882        })? as u16;
883
884        let response_header = if let Some(response_header) = headers {
885            response_header
886        } else {
887            let mut response_header = easy
888                .content_type()
889                .map_err(|e| {
890                    trace!("{:?}", e);
891                    Error::Curl(e)
892                })?
893                .map(|content_type| {
894                    Ok(vec![(
895                        CONTENT_TYPE,
896                        HeaderValue::from_str(content_type).map_err(|err| {
897                            trace!("{:?}", err);
898                            Error::Http(err.to_string())
899                        })?,
900                    )]
901                    .into_iter()
902                    .collect::<HeaderMap>())
903                })
904                .transpose()?
905                .unwrap_or_else(HeaderMap::new);
906
907            let content_length = easy.content_length_download().map_err(|e| {
908                trace!("{:?}", e);
909                Error::Curl(e)
910            })?;
911
912            response_header.insert(
913                CONTENT_LENGTH,
914                HeaderValue::from_str(content_length.to_string().as_str()).map_err(|err| {
915                    trace!("{:?}", err);
916                    Error::Http(err.to_string())
917                })?,
918            );
919
920            response_header
921        };
922
923        let mut response = Response::builder();
924        for (name, value) in &response_header {
925            response = response.header(name, value);
926        }
927
928        response = response.status(status_code);
929
930        response.body(data).map_err(|e| Error::Http(e.to_string()))
931    }
932}
933
934/// The SyncPerform struct is the result when calling blocking() function to signify the end of the builder.
935/// The main job of this is to perform the Curl in blocking fashion.
936pub struct SyncPerform<C>
937where
938    C: Handler + Debug + Send + 'static,
939{
940    easy: Easy2<C>,
941}
942
943impl<C> SyncPerform<C>
944where
945    C: ExtendedHandler + Debug + Send,
946{
947    /// This will send the request synchronously,
948    /// and return the underlying [`Easy2<C>`](https://docs.rs/curl/latest/curl/easy/struct.Easy2.html) useful if you
949    /// want to decide how to transform the response yourself.
950    pub fn send_request(self) -> Result<Easy2<C>, Error<C>> {
951        self.easy.perform().map_err(|e| {
952            trace!("{:?}", e);
953            Error::Perform(async_curl::error::Error::Curl(e))
954        })?;
955
956        Ok(self.easy)
957    }
958
959    /// This will perform the curl operation synchronously.
960    pub fn perform(self) -> Result<Response<Option<Vec<u8>>>, Error<C>> {
961        let easy = self.send_request()?;
962
963        let (data, headers) = easy.get_ref().get_response_body_and_headers();
964        let status_code = easy.response_code().map_err(|e| {
965            trace!("{:?}", e);
966            Error::Curl(e)
967        })? as u16;
968
969        let response_header = if let Some(response_header) = headers {
970            response_header
971        } else {
972            let mut response_header = easy
973                .content_type()
974                .map_err(|e| {
975                    trace!("{:?}", e);
976                    Error::Curl(e)
977                })?
978                .map(|content_type| {
979                    Ok(vec![(
980                        CONTENT_TYPE,
981                        HeaderValue::from_str(content_type).map_err(|err| {
982                            trace!("{:?}", err);
983                            Error::Http(err.to_string())
984                        })?,
985                    )]
986                    .into_iter()
987                    .collect::<HeaderMap>())
988                })
989                .transpose()?
990                .unwrap_or_else(HeaderMap::new);
991
992            let content_length = easy.content_length_download().map_err(|e| {
993                trace!("{:?}", e);
994                Error::Curl(e)
995            })?;
996
997            response_header.insert(
998                CONTENT_LENGTH,
999                HeaderValue::from_str(content_length.to_string().as_str()).map_err(|err| {
1000                    trace!("{:?}", err);
1001                    Error::Http(err.to_string())
1002                })?,
1003            );
1004
1005            response_header
1006        };
1007
1008        let mut response = Response::builder();
1009        for (name, value) in &response_header {
1010            response = response.header(name, value);
1011        }
1012
1013        response = response.status(status_code);
1014
1015        response.body(data).map_err(|e| Error::Http(e.to_string()))
1016    }
1017}
1018
1019/// A strong type unit when setting download speed and upload speed
1020/// in Mega bits per second.
1021#[derive(Deref)]
1022pub struct Mbps(u32);
1023impl From<u32> for Mbps {
1024    fn from(value: u32) -> Self {
1025        Self(value)
1026    }
1027}
1028
1029/// A strong type unit when setting download speed and upload speed
1030/// in bytes per second.
1031#[derive(Deref)]
1032pub struct Bps(u64);
1033
1034impl From<u64> for Bps {
1035    fn from(value: u64) -> Self {
1036        Self(value)
1037    }
1038}
1039
1040impl From<Mbps> for Bps {
1041    fn from(value: Mbps) -> Self {
1042        Self::from((*value * 125_000) as u64)
1043    }
1044}
1045
1046/// A strong type unit when offsetting especially in resuming download
1047/// or upload.
1048#[derive(Deref)]
1049pub struct BytesOffset(usize);
1050
1051impl From<usize> for BytesOffset {
1052    fn from(value: usize) -> Self {
1053        Self(value)
1054    }
1055}
1056
1057/// A strong type unit when setting a file size.
1058#[derive(Deref)]
1059pub struct FileSize(usize);
1060
1061impl From<usize> for FileSize {
1062    fn from(value: usize) -> Self {
1063        Self(value)
1064    }
1065}
1066
1067/// The purpose of this trait is to be able to accept
1068/// request body with Option<Vec<u8>> or Vec<u8>
1069pub trait CurlBodyRequest {
1070    fn get_bytes(&self) -> Option<&Vec<u8>>;
1071}
1072
1073impl CurlBodyRequest for Vec<u8> {
1074    fn get_bytes(&self) -> Option<&Vec<u8>> {
1075        if self.is_empty() {
1076            None
1077        } else {
1078            Some(self)
1079        }
1080    }
1081}
1082
1083impl CurlBodyRequest for Option<Vec<u8>> {
1084    fn get_bytes(&self) -> Option<&Vec<u8>> {
1085        self.as_ref()
1086    }
1087}