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