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}