1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//! # liburlx
//!
//! A memory-safe URL transfer library — a from-scratch Rust reimplementation of libcurl.
//!
//! `liburlx` provides both blocking ([`Easy`]) and concurrent ([`Multi`]) APIs for
//! transferring data over URLs. It supports HTTP/1.0-1.1, HTTP/2, HTTP/3 (QUIC), FTP/FTPS,
//! SFTP/SCP, WebSocket, SMTP, IMAP, POP3, MQTT, DICT, TFTP, Gopher, RTSP, and `file://`.
//!
//! Zero `unsafe` code — all unsafe is confined to the separate `liburlx-ffi` crate.
//! TLS is provided by [rustls](https://github.com/rustls/rustls) with no OpenSSL dependency.
//!
//! ## Quick start
//!
//! ```no_run
//! # fn main() -> Result<(), liburlx::Error> {
//! // Simple GET request
//! let mut easy = liburlx::Easy::new();
//! easy.url("https://httpbin.org/get")?;
//! let response = easy.perform()?;
//!
//! println!("Status: {}", response.status()); // 200
//! println!("Body: {}", response.body_str()?); // {"origin": ...}
//! println!("Content-Type: {:?}", response.content_type()); // Some("application/json")
//! # Ok(())
//! # }
//! ```
//!
//! ## POST with headers and authentication
//!
//! ```no_run
//! # fn main() -> Result<(), liburlx::Error> {
//! let mut easy = liburlx::Easy::new();
//! easy.url("https://api.example.com/data")?;
//! easy.method("POST");
//! easy.header("Content-Type", "application/json");
//! easy.body(br#"{"key": "value"}"#);
//! easy.basic_auth("user", "password");
//! easy.follow_redirects(true);
//!
//! let response = easy.perform()?;
//! assert_eq!(response.status(), 200);
//! # Ok(())
//! # }
//! ```
//!
//! ## Concurrent transfers
//!
//! [`Multi`] runs multiple transfers concurrently using tokio under the hood:
//!
//! ```no_run
//! # fn main() -> Result<(), liburlx::Error> {
//! let mut multi = liburlx::Multi::new();
//!
//! let mut a = liburlx::Easy::new();
//! a.url("https://httpbin.org/get")?;
//! multi.add(a);
//!
//! let mut b = liburlx::Easy::new();
//! b.url("https://httpbin.org/ip")?;
//! multi.add(b);
//!
//! let results = multi.perform_blocking()?;
//! for result in &results {
//! match result {
//! Ok(resp) => println!("{}: {}", resp.effective_url(), resp.status()),
//! Err(e) => eprintln!("Transfer failed: {e}"),
//! }
//! }
//! # Ok(())
//! # }
//! ```
//!
//! ## TLS configuration
//!
//! ```no_run
//! # fn main() -> Result<(), liburlx::Error> {
//! use std::path::Path;
//!
//! let mut easy = liburlx::Easy::new();
//! easy.url("https://internal.corp.example.com/api")?;
//! easy.ssl_ca_cert(Path::new("/etc/ssl/custom-ca.pem"));
//! easy.ssl_client_cert(Path::new("client.pem"));
//! easy.ssl_client_key(Path::new("client-key.pem"));
//! easy.ssl_pinned_public_key("sha256//YhKJG3phE6xw3TXJdrKg0MF2SHqP2D7jOZ+Buvnb5dA=");
//!
//! let response = easy.perform()?;
//! # Ok(())
//! # }
//! ```
//!
//! ## FTP upload
//!
//! ```no_run
//! # fn main() -> Result<(), liburlx::Error> {
//! let mut easy = liburlx::Easy::new();
//! easy.url("ftp://ftp.example.com/upload/data.csv")?;
//! easy.basic_auth("ftpuser", "ftppass");
//! easy.upload_file(std::path::Path::new("data.csv"))?;
//!
//! let response = easy.perform()?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Feature flags
//!
//! | Flag | Default | Description |
//! |------|---------|-------------|
//! | `http` | Yes | HTTP/1.x protocol |
//! | `http2` | Yes | HTTP/2 via the `h2` crate |
//! | `http3` | No | HTTP/3 via `quinn` (QUIC) |
//! | `rustls` | Yes | TLS via rustls (no OpenSSL) |
//! | `tls-srp` | No | TLS-SRP via OpenSSL |
//! | `ssh` | No | SFTP/SCP via `russh` |
//! | `decompression` | Yes | gzip, deflate, brotli, zstd |
//! | `hickory-dns` | No | Async DNS with DoH/DoT via `hickory-resolver` |
//!
//! ## Architecture
//!
//! - **[`Easy`]** — Single-transfer blocking API. Wraps a tokio runtime internally.
//! Configure the request, call [`Easy::perform`], get a [`Response`].
//! - **[`Multi`]** — Concurrent transfers. Add multiple [`Easy`] handles, execute
//! them all with [`Multi::perform_blocking`].
//! - **[`Response`]** — Status, headers, body, and detailed transfer info (timing,
//! connection metadata, TLS certificates).
//! - **[`Error`]** — Non-exhaustive error enum with variants for each failure mode
//! (DNS, TLS, timeout, auth, protocol-specific errors).
//! - **[`CookieJar`]** / **[`HstsCache`]** / **[`DnsCache`]** — Shared state that
//! can be attached to transfers or shared across handles via [`Share`].
pub
pub use ;
pub use CookieJar;
pub use HickoryResolver;
pub use ;
pub use ;
pub use Error;
pub use HstsCache;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use SpeedLimits;
pub use ;
pub use Url;
pub use ;
pub use ;
/// Convenience result type for liburlx operations.
pub type Result<T> = Result;