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