Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/google.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- uses: actions/checkout@v4

- id: auth
uses: google-github-actions/[email protected].7
uses: google-github-actions/[email protected].10
with:
token_format: "access_token"
create_credentials_file: true
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
Cargo.lock
.env
.claude/*.local.json
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async-trait = "0.1"
base64 = "0.22"
bytes = "1"
chrono = "0.4.35"
criterion = { version = "0.5", features = ["async_tokio", "html_reports"] }
criterion = { version = "0.6", features = ["async_tokio", "html_reports"] }
dotenv = "0.15"
env_logger = "0.11"
form_urlencoded = "1"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async fn main() -> Result<()> {
let req = http::Request::get("https://s3.amazonaws.com/testbucket").body(reqwest::Body::from(""))?;
let (mut parts, body) = req.into_parts();
// Signing request with Signer, and convert it back to reqwest::Request
let credential = loader.load().await?.unwrap();
let credential = loader.provide_credential().await?.unwrap();
signer.sign(&mut parts, &credential)?;
let req = http::Request::from_parts(parts, body).try_into()?;
// Sending already signed request.
Expand Down
4 changes: 4 additions & 0 deletions context/file-read-tokio/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ anyhow = "1"
async-trait = "0.1"
reqsign-core.workspace = true
tokio = { version = "1", features = ["fs"] }

[dev-dependencies]
reqsign-http-send-reqwest = { path = "../http-send-reqwest" }
tokio = { version = "1", features = ["fs", "macros", "rt-multi-thread"] }
72 changes: 72 additions & 0 deletions context/file-read-tokio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# reqsign-file-read-tokio

Tokio-based file reading implementation for reqsign.

---

This crate provides `TokioFileRead`, an async file reader that implements the `FileRead` trait from `reqsign_core` using Tokio's file system operations.

## Quick Start

```rust
use reqsign_core::Context;
use reqsign_file_read_tokio::TokioFileRead;

// Create a context with Tokio file reader
let ctx = Context::new(
TokioFileRead::default(),
http_client, // Your HTTP client
);

// Read files asynchronously
let content = ctx.file_read("/path/to/file").await?;
```

## Features

- **Async File I/O**: Leverages Tokio's async file system operations
- **Zero Configuration**: Works out of the box with sensible defaults
- **Lightweight**: Minimal dependencies, only what's needed

## Use Cases

This crate is essential when:
- Loading credentials from file system (e.g., `~/.aws/credentials`)
- Reading service account keys (e.g., Google Cloud service account JSON)
- Accessing configuration files for various cloud providers

## Examples

### Reading Credentials

Check out the [read_credentials example](examples/read_credentials.rs) to see how to read credential files:

```bash
cargo run --example read_credentials -- ~/.aws/credentials
```

### Integration with Services

```rust
use reqsign_core::{Context, Signer};
use reqsign_file_read_tokio::TokioFileRead;
use reqsign_http_send_reqwest::ReqwestHttpSend;

// Create context with Tokio file reader
let ctx = Context::new(
TokioFileRead::default(),
ReqwestHttpSend::default(),
);

// Use with any service that needs file access
let signer = Signer::new(ctx, loader, builder);
```

## Requirements

- Tokio runtime with `fs` feature enabled
- Compatible with all reqsign service implementations

## License

Licensed under [Apache License, Version 2.0](./LICENSE).
49 changes: 49 additions & 0 deletions context/file-read-tokio/examples/read_credentials.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use anyhow::Result;
use reqsign_core::Context;
use reqsign_file_read_tokio::TokioFileRead;
use reqsign_http_send_reqwest::ReqwestHttpSend;
use std::env;

#[tokio::main]
async fn main() -> Result<()> {
// Create a context with Tokio file reader
let ctx = Context::new(TokioFileRead, ReqwestHttpSend::default());

// Get the path from command line arguments or use a demo file
let path = env::args().nth(1).unwrap_or_else(|| {
// Create a temporary demo file for the example
let demo_content =
"[default]\naws_access_key_id = DEMO_KEY\naws_secret_access_key = DEMO_SECRET\n";
if let Some(temp_dir) = std::env::temp_dir().to_str() {
let demo_path = format!("{}/reqsign_demo_credentials", temp_dir);
let _ = std::fs::write(&demo_path, demo_content);
return demo_path;
}
"demo_credentials".to_string()
});

println!("Attempting to read file: {}", path);

// Read the file asynchronously
match ctx.file_read(&path).await {
Ok(content) => {
println!("Successfully read {} bytes from {}", content.len(), path);

// Try to parse as UTF-8 and show a preview
if let Ok(text) = String::from_utf8(content.clone()) {
let preview: String = text.lines().take(5).collect::<Vec<_>>().join("\n");
println!("\nFirst few lines:");
println!("{}", preview);
if text.lines().count() > 5 {
println!("... ({} more lines)", text.lines().count() - 5);
}
}
}
Err(e) => {
eprintln!("Failed to read file: {}", e);
eprintln!("Make sure the file exists and you have permission to read it.");
}
}

Ok(())
}
58 changes: 58 additions & 0 deletions context/file-read-tokio/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,65 @@
//! Tokio-based file reading implementation for reqsign.
//!
//! This crate provides `TokioFileRead`, an async file reader that implements
//! the `FileRead` trait from `reqsign_core` using Tokio's file system operations.
//!
//! ## Overview
//!
//! `TokioFileRead` enables reqsign to read files asynchronously using Tokio's
//! efficient async I/O primitives. This is particularly useful when loading
//! credentials or configuration from the file system.
//!
//! ## Example
//!
//! ```no_run
//! use reqsign_core::Context;
//! use reqsign_file_read_tokio::TokioFileRead;
//! use reqsign_http_send_reqwest::ReqwestHttpSend;
//!
//! #[tokio::main]
//! async fn main() {
//! // Create a context with Tokio file reader
//! let ctx = Context::new(
//! TokioFileRead::default(),
//! ReqwestHttpSend::default(),
//! );
//!
//! // The context can now read files asynchronously
//! match ctx.file_read("/path/to/credentials.json").await {
//! Ok(content) => println!("Read {} bytes", content.len()),
//! Err(e) => eprintln!("Failed to read file: {}", e),
//! }
//! }
//! ```
//!
//! ## Usage with Service Signers
//!
//! ```no_run
//! use reqsign_core::{Context, Signer};
//! use reqsign_file_read_tokio::TokioFileRead;
//! use reqsign_http_send_reqwest::ReqwestHttpSend;
//!
//! # async fn example() -> anyhow::Result<()> {
//! // Many cloud services require reading credentials from files
//! let ctx = Context::new(
//! TokioFileRead::default(),
//! ReqwestHttpSend::default(),
//! );
//!
//! // Create a signer that can load credentials from files
//! // let signer = Signer::new(ctx, credential_loader, request_builder);
//! # Ok(())
//! # }
//! ```

use anyhow::Result;
use async_trait::async_trait;
use reqsign_core::FileRead;

/// Tokio-based implementation of the `FileRead` trait.
///
/// This struct provides async file reading capabilities using Tokio's
/// file system operations.
#[derive(Debug, Clone, Copy, Default)]
pub struct TokioFileRead;

Expand Down
6 changes: 5 additions & 1 deletion context/http-send-reqwest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ version = "0.1.0"
anyhow = "1"
async-trait = "0.1"
bytes.workspace = true
http-body-util = "0.1.2"
http.workspace = true
http-body-util = "0.1.2"
reqsign-core.workspace = true
reqwest = { version = "0.12", default-features = false }

[dev-dependencies]
reqsign-file-read-tokio = { path = "../file-read-tokio" }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
107 changes: 107 additions & 0 deletions context/http-send-reqwest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# reqsign-http-send-reqwest

Reqwest-based HTTP client implementation for reqsign.

---

This crate provides `ReqwestHttpSend`, an HTTP client that implements the `HttpSend` trait from `reqsign_core` using the popular reqwest library.

## Quick Start

```rust
use reqsign_core::Context;
use reqsign_http_send_reqwest::ReqwestHttpSend;

// Use with default configuration
let ctx = Context::new(
file_reader,
ReqwestHttpSend::default(),
);

// Or with custom client configuration
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(30))
.build()
.unwrap();

let ctx = Context::new(
file_reader,
ReqwestHttpSend::new(client),
);
```

## Features

- **Full reqwest compatibility**: Use all of reqwest's powerful features
- **Seamless integration**: Automatic conversion between `http` and `reqwest` types
- **Customizable**: Configure timeouts, proxies, TLS settings, and more
- **Async/await**: Built for modern async Rust applications

## Configuration Options

```rust
use reqwest::Client;
use reqsign_http_send_reqwest::ReqwestHttpSend;

let client = Client::builder()
// Timeouts
.timeout(Duration::from_secs(30))
.connect_timeout(Duration::from_secs(10))

// Connection pooling
.pool_max_idle_per_host(10)
.pool_idle_timeout(Duration::from_secs(90))

// HTTP settings
.user_agent("my-app/1.0")
.default_headers(headers)

// Proxy configuration
.proxy(reqwest::Proxy::https("https://proxy.example.com")?)

// TLS configuration
.danger_accept_invalid_certs(false)
.min_tls_version(reqwest::tls::Version::TLS_1_2)

.build()?;

let http_send = ReqwestHttpSend::new(client);
```

## Examples

### Custom Client Configuration

Check out the [custom_client example](examples/custom_client.rs) to see various configuration options:

```bash
cargo run --example custom_client
```

### Integration with Services

```rust
use reqsign_core::{Context, Signer};
use reqsign_file_read_tokio::TokioFileRead;
use reqsign_http_send_reqwest::ReqwestHttpSend;

// Create context for cloud service clients
let ctx = Context::new(
TokioFileRead::default(),
ReqwestHttpSend::default(),
);

// Use with any reqsign service
let signer = Signer::new(ctx, loader, builder);
```

## Why reqwest?

- **Mature and stable**: One of the most popular HTTP clients in the Rust ecosystem
- **Feature-rich**: Supports proxies, cookies, redirect policies, and more
- **Well-maintained**: Regular updates and security patches
- **Extensive ecosystem**: Compatible with many Rust libraries and frameworks

## License

Licensed under [Apache License, Version 2.0](./LICENSE).
Loading