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}