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 /// Verify the certificate's name against host.
790 ///
791 /// This should be disabled with great caution! It basically disables the
792 /// security features of SSL if it is disabled.
793 ///
794 /// By default this option is set to `true` and corresponds to
795 /// `CURLOPT_SSL_VERIFYHOST`.
796 pub fn ssl_verify_host(mut self, enable: bool) -> Result<Self, Error<C>> {
797 self.easy.ssl_verify_host(enable).map_err(Error::Curl)?;
798 Ok(self)
799 }
800
801 /// Verify the peer's SSL certificate.
802 ///
803 /// This should be disabled with great caution! It basically disables the
804 /// security features of SSL if it is disabled.
805 ///
806 /// By default this option is set to `true` and corresponds to
807 /// `CURLOPT_SSL_VERIFYPEER`.
808 pub fn ssl_verify_peer(mut self, enable: bool) -> Result<Self, Error<C>> {
809 self.easy.ssl_verify_peer(enable).map_err(Error::Curl)?;
810 Ok(self)
811 }
812}
813
814/// The AsyncPerform struct is the result when calling nonblocking() function to signify the end of the builder.
815/// The main job of this is to perform the Curl in nonblocking fashion.
816pub struct AsyncPerform<C, A>
817where
818 C: Handler + Debug + Send + 'static,
819 A: Actor<C>,
820{
821 /// This is the the actor handler that can be cloned to be able to handle multiple request sender
822 /// and a single consumer that is spawned in the background upon creation of this object to be able to achieve
823 /// non-blocking I/O during curl perform.
824 actor: A,
825 /// The `Easy2<C>` is the Easy2 from curl-rust crate wrapped in this struct to be able to do
826 /// asynchronous task during perform operation.
827 easy: Easy2<C>,
828}
829
830impl<C, A> AsyncPerform<C, A>
831where
832 C: ExtendedHandler + Debug + Send,
833 A: Actor<C>,
834{
835 /// This will send the request asynchronously,
836 /// and return the underlying [`Easy2<C>`](https://docs.rs/curl/latest/curl/easy/struct.Easy2.html) useful if you
837 /// want to decide how to transform the response yourself.
838 ///
839 /// This becomes a non-blocking I/O since the actual perform operation is done
840 /// at the actor side using Curl-Multi.
841 pub async fn send_request(self) -> Result<Easy2<C>, Error<C>> {
842 self.actor.send_request(self.easy).await.map_err(|e| {
843 trace!("{:?}", e);
844 Error::Perform(e)
845 })
846 }
847
848 /// This will perform the curl operation asynchronously.
849 pub async fn perform(self) -> Result<Response<Option<Vec<u8>>>, Error<C>> {
850 let easy = self.send_request().await?;
851
852 let (data, headers) = easy.get_ref().get_response_body_and_headers();
853 let status_code = easy.response_code().map_err(|e| {
854 trace!("{:?}", e);
855 Error::Curl(e)
856 })? as u16;
857
858 let response_header = if let Some(response_header) = headers {
859 response_header
860 } else {
861 let mut response_header = easy
862 .content_type()
863 .map_err(|e| {
864 trace!("{:?}", e);
865 Error::Curl(e)
866 })?
867 .map(|content_type| {
868 Ok(vec![(
869 CONTENT_TYPE,
870 HeaderValue::from_str(content_type).map_err(|err| {
871 trace!("{:?}", err);
872 Error::Http(err.to_string())
873 })?,
874 )]
875 .into_iter()
876 .collect::<HeaderMap>())
877 })
878 .transpose()?
879 .unwrap_or_else(HeaderMap::new);
880
881 let content_length = easy.content_length_download().map_err(|e| {
882 trace!("{:?}", e);
883 Error::Curl(e)
884 })?;
885
886 response_header.insert(
887 CONTENT_LENGTH,
888 HeaderValue::from_str(content_length.to_string().as_str()).map_err(|err| {
889 trace!("{:?}", err);
890 Error::Http(err.to_string())
891 })?,
892 );
893
894 response_header
895 };
896
897 let mut response = Response::builder();
898 for (name, value) in &response_header {
899 response = response.header(name, value);
900 }
901
902 response = response.status(status_code);
903
904 response.body(data).map_err(|e| Error::Http(e.to_string()))
905 }
906}
907
908/// The SyncPerform struct is the result when calling blocking() function to signify the end of the builder.
909/// The main job of this is to perform the Curl in blocking fashion.
910pub struct SyncPerform<C>
911where
912 C: Handler + Debug + Send + 'static,
913{
914 easy: Easy2<C>,
915}
916
917impl<C> SyncPerform<C>
918where
919 C: ExtendedHandler + Debug + Send,
920{
921 /// This will send the request synchronously,
922 /// and return the underlying [`Easy2<C>`](https://docs.rs/curl/latest/curl/easy/struct.Easy2.html) useful if you
923 /// want to decide how to transform the response yourself.
924 pub fn send_request(self) -> Result<Easy2<C>, Error<C>> {
925 self.easy.perform().map_err(|e| {
926 trace!("{:?}", e);
927 Error::Perform(async_curl::error::Error::Curl(e))
928 })?;
929
930 Ok(self.easy)
931 }
932
933 /// This will perform the curl operation synchronously.
934 pub fn perform(self) -> Result<Response<Option<Vec<u8>>>, Error<C>> {
935 let easy = self.send_request()?;
936
937 let (data, headers) = easy.get_ref().get_response_body_and_headers();
938 let status_code = easy.response_code().map_err(|e| {
939 trace!("{:?}", e);
940 Error::Curl(e)
941 })? as u16;
942
943 let response_header = if let Some(response_header) = headers {
944 response_header
945 } else {
946 let mut response_header = easy
947 .content_type()
948 .map_err(|e| {
949 trace!("{:?}", e);
950 Error::Curl(e)
951 })?
952 .map(|content_type| {
953 Ok(vec![(
954 CONTENT_TYPE,
955 HeaderValue::from_str(content_type).map_err(|err| {
956 trace!("{:?}", err);
957 Error::Http(err.to_string())
958 })?,
959 )]
960 .into_iter()
961 .collect::<HeaderMap>())
962 })
963 .transpose()?
964 .unwrap_or_else(HeaderMap::new);
965
966 let content_length = easy.content_length_download().map_err(|e| {
967 trace!("{:?}", e);
968 Error::Curl(e)
969 })?;
970
971 response_header.insert(
972 CONTENT_LENGTH,
973 HeaderValue::from_str(content_length.to_string().as_str()).map_err(|err| {
974 trace!("{:?}", err);
975 Error::Http(err.to_string())
976 })?,
977 );
978
979 response_header
980 };
981
982 let mut response = Response::builder();
983 for (name, value) in &response_header {
984 response = response.header(name, value);
985 }
986
987 response = response.status(status_code);
988
989 response.body(data).map_err(|e| Error::Http(e.to_string()))
990 }
991}
992
993/// A strong type unit when setting download speed and upload speed
994/// in Mega bits per second.
995#[derive(Deref)]
996pub struct Mbps(u32);
997impl From<u32> for Mbps {
998 fn from(value: u32) -> Self {
999 Self(value)
1000 }
1001}
1002
1003/// A strong type unit when setting download speed and upload speed
1004/// in bytes per second.
1005#[derive(Deref)]
1006pub struct Bps(u64);
1007
1008impl From<u64> for Bps {
1009 fn from(value: u64) -> Self {
1010 Self(value)
1011 }
1012}
1013
1014impl From<Mbps> for Bps {
1015 fn from(value: Mbps) -> Self {
1016 Self::from((*value * 125_000) as u64)
1017 }
1018}
1019
1020/// A strong type unit when offsetting especially in resuming download
1021/// or upload.
1022#[derive(Deref)]
1023pub struct BytesOffset(usize);
1024
1025impl From<usize> for BytesOffset {
1026 fn from(value: usize) -> Self {
1027 Self(value)
1028 }
1029}
1030
1031/// A strong type unit when setting a file size.
1032#[derive(Deref)]
1033pub struct FileSize(usize);
1034
1035impl From<usize> for FileSize {
1036 fn from(value: usize) -> Self {
1037 Self(value)
1038 }
1039}
1040
1041/// The purpose of this trait is to be able to accept
1042/// request body with Option<Vec<u8>> or Vec<u8>
1043pub trait CurlBodyRequest {
1044 fn get_bytes(&self) -> Option<&Vec<u8>>;
1045}
1046
1047impl CurlBodyRequest for Vec<u8> {
1048 fn get_bytes(&self) -> Option<&Vec<u8>> {
1049 if self.is_empty() {
1050 None
1051 } else {
1052 Some(self)
1053 }
1054 }
1055}
1056
1057impl CurlBodyRequest for Option<Vec<u8>> {
1058 fn get_bytes(&self) -> Option<&Vec<u8>> {
1059 self.as_ref()
1060 }
1061}