curl_http_client/
http_client.rs

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