Skip to content

Commit 0dd9031

Browse files
authored
Merge pull request #57 from bgpkit/features/custom-headers-utils
Custom headers utils
2 parents ed0d9d4 + 64cdcc5 commit 0dd9031

File tree

8 files changed

+105
-43
lines changed

8 files changed

+105
-43
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## v0.18.0 -- 2025-05-30
6+
7+
### Highlights
8+
9+
* split `remote` features into `http` and `ftp`, allowing users who only need HTTP or FTP support to use the
10+
corresponding feature flag
11+
* in most cases, users will likely not need to use the `ftp` feature
12+
* add `create_client_with_headers` function to allow creating a `reqwest::blocking::Client` with custom headers
13+
* this simplifies the process of creating a client with custom headers for HTTP requests
14+
* this also allows users to create custom clients without importing `reqwest` crate directly
15+
* add `rustls_sys` dependency to support `rustls` as the default TLS backend
16+
17+
### Documentation improvements
18+
19+
* update examples on custom HTTP request function `oneio::get_http_reader` to use `create_client_with_headers`
20+
521
## v0.17.0 -- 2024-08-04
622

723
### Highlights

Cargo.toml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "oneio"
3-
version = "0.17.0"
3+
version = "0.18.0"
44
authors = ["Mingwei Zhang <[email protected]>"]
55
edition = "2021"
66
readme = "README.md"
@@ -43,6 +43,7 @@ zstd = { version = "0.13.2", optional = true }
4343
# feature: digest
4444
ring = { version = "0.17", optional = true }
4545
hex = { version = "0.4", optional = true }
46+
rustls_sys = { package = "rustls", version = "0.23", optional = true }
4647

4748
# feature: json
4849
serde = { version = "1.0", optional = true }
@@ -64,6 +65,12 @@ default = ["lib-core", "rustls"]
6465

6566
# library core dependency to enable reading from local/remote with compressions enabled
6667
lib-core = ["remote", "compressions", "json"]
68+
69+
# remote IO features
70+
remote = ["http", "ftp"]
71+
http = ["reqwest"]
72+
ftp = ["suppaftp"]
73+
6774
# cli dependencies
6875
cli = [
6976
# core dependency
@@ -82,7 +89,12 @@ native-tls = [
8289
"suppaftp?/native-tls",
8390
"rust-s3?/sync-native-tls",
8491
]
85-
rustls = ["reqwest?/rustls-tls", "suppaftp?/rustls", "rust-s3?/sync-rustls-tls"]
92+
rustls = [
93+
"reqwest?/rustls-tls",
94+
"suppaftp?/rustls",
95+
"rust-s3?/sync-rustls-tls",
96+
"rustls_sys"
97+
]
8698

8799
digest = ["ring", "hex"]
88100

@@ -94,7 +106,6 @@ lz = ["lz4"]
94106
xz = ["xz2"]
95107
zstd = ["dep:zstd"]
96108

97-
remote = ["reqwest", "suppaftp"]
98109
json = ["serde", "serde_json"]
99110

100111
# s3 support, off by default

README.md

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
# OneIO - all-in-one convenient IO library for Rust
1+
# OneIO - all-in-one IO library for Rust
22

33
[![Rust](https://github.com/bgpkit/oneio/actions/workflows/rust.yml/badge.svg)](https://github.com/bgpkit/oneio/actions/workflows/rust.yml)
44
[![Crates.io](https://img.shields.io/crates/v/oneio)](https://crates.io/crates/oneio)
55
[![Docs.rs](https://docs.rs/oneio/badge.svg)](https://docs.rs/oneio)
66
[![License](https://img.shields.io/crates/l/oneio)](https://raw.githubusercontent.com/bgpkit/oneio/main/LICENSE)
77

8-
OneIO is a Rust library that provides a unified simple IO interface for reading and writing to and from data files from different sources and compressions.
8+
OneIO is a Rust library that provides a unified IO interface for synchronously reading and writing
9+
to and from data files from different sources and compressions.
910

1011
### Usage and Feature Flags
1112

1213
Enable all compression algorithms and handle remote files (default)
1314

1415
```toml
15-
oneio = "0.17"
16+
oneio = "0.18"
1617
```
1718

1819
Select from supported feature flags
1920

2021
```toml
21-
oneio = { version = "0.17", default-features = false, features = ["remote", "gz"] }
22+
oneio = { version = "0.18", default-features = false, features = ["remote", "gz"] }
2223
```
2324

2425
Default flags include `lib-core` and `rustls`.
@@ -28,6 +29,8 @@ Default flags include `lib-core` and `rustls`.
2829
`lib-core` core features include:
2930

3031
- `remote`: allow reading from remote files, including http(s) and ftp
32+
- `http`: support reading from http(s) remote files using `reqwest` crate
33+
- `ftp`: support reading from ftp remote files using `suppaftp` crate
3134
- `compressions`: support all compression algorithms
3235
- `gz`: support `gzip` files using `flate2` crate
3336
- `bz`: support `bzip2` files using `bzip2` crate
@@ -57,7 +60,7 @@ Users can also manually opt-in to specific compression algorithms. For example,
5760
and `bzip2` files:
5861

5962
```toml
60-
oneio = { version = "0.17", default-features = false, features = ["gz", "bz"] }
63+
oneio = { version = "0.18", default-features = false, features = ["gz", "bz"] }
6164
```
6265

6366
### Use `oneio` commandline tool
@@ -165,7 +168,7 @@ assert_eq!(lines[1].as_str(), "This is a test.");
165168

166169
### Use OneIO Writer as a Library
167170

168-
[get_writer] returns a generic writer that implements [Write], and handles decompression from the following types:
171+
[get_writer] returns a generic writer that implements [std::io::Write], and handles decompression from the following types:
169172

170173
- `gzip`: files ending with `gz` or `gzip`
171174
- `bzip2`: files ending with `bz` or `bz2`
@@ -204,16 +207,10 @@ std::fs::remove_file(to_write_file).unwrap();
204207
use std::collections::HashMap;
205208
use reqwest::header::HeaderMap;
206209

207-
let headers: HeaderMap = (&HashMap::from([("X-Custom-Auth-Key".to_string(), "TOKEN".to_string())]))
208-
.try_into().expect("invalid headers");
209-
210-
let client = reqwest::blocking::Client::builder()
211-
.default_headers(headers)
212-
.danger_accept_invalid_certs(true)
213-
.build().unwrap();
210+
let client = oneio::create_client_with_headers([("X-Custom-Auth-Key", "TOKEN")]).unwrap();
214211
let mut reader = oneio::get_http_reader(
215-
"https://SOME_REMOTE_RESOURCE_PROTECTED_BY_ACCESS_TOKEN",
216-
Some(client),
212+
"https://SOME_REMOTE_RESOURCE_PROTECTED_BY_ACCESS_TOKEN",
213+
Some(client),
217214
).unwrap();
218215
let mut text = "".to_string();
219216
reader.read_to_string(&mut text).unwrap();

README.tpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# OneIO - all-in-one convenient IO library for Rust
1+
# OneIO - all-in-one IO library for Rust
22

33
[![Rust](https://github.com/bgpkit/oneio/actions/workflows/rust.yml/badge.svg)](https://github.com/bgpkit/oneio/actions/workflows/rust.yml)
44
[![Crates.io](https://img.shields.io/crates/v/oneio)](https://crates.io/crates/oneio)

src/bin/oneio.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ fn main() {
164164

165165
if cli.download {
166166
let out_path = match outfile {
167-
None => path.split('/').last().unwrap().to_string(),
167+
None => path.split('/').next_back().unwrap().to_string(),
168168
Some(p) => p.to_str().unwrap().to_string(),
169169
};
170170

src/lib.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
/*!
2-
OneIO is a Rust library that provides a unified simple IO interface for reading and writing to and from data files from different sources and compressions.
2+
OneIO is a Rust library that provides a unified IO interface for synchronously reading and writing
3+
to and from data files from different sources and compressions.
34
45
## Usage and Feature Flags
56
67
Enable all compression algorithms and handle remote files (default)
78
89
```toml
9-
oneio = "0.17"
10+
oneio = "0.18"
1011
```
1112
1213
Select from supported feature flags
1314
1415
```toml
15-
oneio = { version = "0.17", default-features = false, features = ["remote", "gz"] }
16+
oneio = { version = "0.18", default-features = false, features = ["remote", "gz"] }
1617
```
1718
1819
Default flags include `lib-core` and `rustls`.
@@ -22,6 +23,8 @@ Default flags include `lib-core` and `rustls`.
2223
`lib-core` core features include:
2324
2425
- `remote`: allow reading from remote files, including http(s) and ftp
26+
- `http`: support reading from http(s) remote files using `reqwest` crate
27+
- `ftp`: support reading from ftp remote files using `suppaftp` crate
2528
- `compressions`: support all compression algorithms
2629
- `gz`: support `gzip` files using `flate2` crate
2730
- `bz`: support `bzip2` files using `bzip2` crate
@@ -51,7 +54,7 @@ Users can also manually opt-in to specific compression algorithms. For example,
5154
and `bzip2` files:
5255
5356
```toml
54-
oneio = { version = "0.17", default-features = false, features = ["gz", "bz"] }
57+
oneio = { version = "0.18", default-features = false, features = ["gz", "bz"] }
5558
```
5659
5760
## Use `oneio` commandline tool
@@ -159,7 +162,7 @@ assert_eq!(lines[1].as_str(), "This is a test.");
159162
160163
## Use OneIO Writer as a Library
161164
162-
[get_writer] returns a generic writer that implements [Write], and handles decompression from the following types:
165+
[get_writer] returns a generic writer that implements [std::io::Write], and handles decompression from the following types:
163166
164167
- `gzip`: files ending with `gz` or `gzip`
165168
- `bzip2`: files ending with `bz` or `bz2`
@@ -198,16 +201,10 @@ std::fs::remove_file(to_write_file).unwrap();
198201
use std::collections::HashMap;
199202
use reqwest::header::HeaderMap;
200203
201-
let headers: HeaderMap = (&HashMap::from([("X-Custom-Auth-Key".to_string(), "TOKEN".to_string())]))
202-
.try_into().expect("invalid headers");
203-
204-
let client = reqwest::blocking::Client::builder()
205-
.default_headers(headers)
206-
.danger_accept_invalid_certs(true)
207-
.build().unwrap();
204+
let client = oneio::create_client_with_headers([("X-Custom-Auth-Key", "TOKEN")]).unwrap();
208205
let mut reader = oneio::get_http_reader(
209-
"https://SOME_REMOTE_RESOURCE_PROTECTED_BY_ACCESS_TOKEN",
210-
Some(client),
206+
"https://SOME_REMOTE_RESOURCE_PROTECTED_BY_ACCESS_TOKEN",
207+
Some(client),
211208
).unwrap();
212209
let mut text = "".to_string();
213210
reader.read_to_string(&mut text).unwrap();

src/oneio/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ pub fn get_cache_reader(
109109
path.split('/')
110110
.collect::<Vec<&str>>()
111111
.into_iter()
112-
.last()
112+
.next_back()
113113
.unwrap()
114114
.to_string()
115115
});

src/oneio/remote.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
//! This module provides functionality to handle remote file operations such as downloading files
2+
//! from HTTP, FTP, and S3 protocols.
3+
//!
14
use crate::oneio::compressions::OneIOCompression;
25
use crate::oneio::{compressions, get_writer_raw};
36
use crate::OneIoError;
@@ -43,6 +46,10 @@ fn get_http_reader_raw(
4346
.as_str(),
4447
"true" | "yes" | "y" | "1"
4548
);
49+
#[cfg(feature = "rustls")]
50+
rustls_sys::crypto::aws_lc_rs::default_provider()
51+
.install_default()
52+
.ok();
4653

4754
let client = match opt_client {
4855
Some(c) => c,
@@ -73,16 +80,24 @@ fn get_http_reader_raw(
7380
Ok(res)
7481
}
7582

76-
/// Get a reader for remote content with the capability to specify headers, and customer reqwest options.
83+
/// Creates a reqwest blocking client with custom headers.
84+
///
85+
/// # Arguments
86+
///
87+
/// * `headers_map` - A argument of header key-value pairs.
88+
///
89+
/// # Returns
90+
///
91+
/// Returns a Result containing the constructed Client or a [OneIoError].
92+
///
93+
/// # Example
7794
///
7895
/// Example usage with custom header fields:
7996
/// ```no_run
8097
/// use std::collections::HashMap;
8198
/// use reqwest::header::HeaderMap;
82-
/// let headers: HeaderMap = (&HashMap::from([("X-Custom-Auth-Key".to_string(), "TOKEN".to_string())])).try_into().expect("invalid headers");
83-
/// let client = reqwest::blocking::Client::builder()
84-
/// .default_headers(headers)
85-
/// .build().unwrap();
99+
///
100+
/// let client = oneio::create_client_with_headers([("X-Custom-Auth-Key", "TOKEN")]).unwrap();
86101
/// let mut reader = oneio::get_http_reader(
87102
/// "https://SOME_REMOTE_RESOURCE_PROTECTED_BY_ACCESS_TOKEN",
88103
/// Some(client),
@@ -91,6 +106,28 @@ fn get_http_reader_raw(
91106
/// reader.read_to_string(&mut text).unwrap();
92107
/// println!("{}", text);
93108
/// ```
109+
pub fn create_client_with_headers<I, K, V>(headers: I) -> Result<Client, OneIoError>
110+
where
111+
I: IntoIterator<Item = (K, V)>,
112+
K: Into<String>,
113+
V: Into<String>,
114+
{
115+
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
116+
let mut header_map = HeaderMap::new();
117+
for (k, v) in headers {
118+
if let (Ok(name), Ok(value)) = (
119+
HeaderName::from_bytes(k.into().as_bytes()),
120+
HeaderValue::from_str(&v.into()),
121+
) {
122+
header_map.insert(name, value);
123+
}
124+
}
125+
Ok(Client::builder().default_headers(header_map).build()?)
126+
}
127+
128+
/// Get a reader for remote content with the capability to specify headers, and customer reqwest options.
129+
///
130+
/// See [`create_client_with_headers`] for more details on how to create a client with custom headers.
94131
///
95132
/// Example with customer builder that allows invalid certificates (bad practice):
96133
/// ```no_run
@@ -132,7 +169,7 @@ pub fn get_http_reader(
132169
///
133170
/// * `remote_path` - The remote path of the file to download.
134171
/// * `local_path` - The local path where the downloaded file will be saved.
135-
/// * `header` - Optional header information to include in the request. If not specified, an empty HashMap should be provided.
172+
/// * `opt_client` - Optional custom [reqwest::blocking::Client] to use for the request.
136173
///
137174
/// # Errors
138175
///
@@ -194,7 +231,7 @@ pub fn download(
194231
///
195232
/// * `remote_path` - The URL or file path of the file to download.
196233
/// * `local_path` - The file path to save the downloaded file.
197-
/// * `header` - Optional headers to include in the download request.
234+
/// * `opt_client` - Optional custom [reqwest::blocking::Client] to use for the request.
198235
/// * `retry` - The number of times to retry downloading in case of failure.
199236
///
200237
/// # Errors
@@ -280,7 +317,11 @@ pub(crate) fn remote_file_exists(path: &str) -> Result<bool, OneIoError> {
280317
match get_protocol(path) {
281318
Some(protocol) => match protocol.as_str() {
282319
"http" | "https" => {
283-
let client = reqwest::blocking::Client::builder()
320+
#[cfg(feature = "rustls")]
321+
rustls_sys::crypto::aws_lc_rs::default_provider()
322+
.install_default()
323+
.ok();
324+
let client = Client::builder()
284325
.timeout(std::time::Duration::from_secs(2))
285326
.build()?;
286327
let res = client.head(path).send()?;

0 commit comments

Comments
 (0)