Skip to content

Commit

Permalink
feat: Add ConnectionInfo extractor (LukeMathWalker#169)
Browse files Browse the repository at this point in the history
The logic of handlers and middleware often use information concerning
the underlying connection.

We enable this by adding a `ConnectionInfo` structure to the framework.

---------

Co-authored-by: Luca Palmieri <[email protected]>
  • Loading branch information
hlbarber and LukeMathWalker authored Apr 3, 2024
1 parent 397dc04 commit 58452eb
Show file tree
Hide file tree
Showing 101 changed files with 1,203 additions and 97 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ We contributors to Pavex:
* Luca Palmieri (@LukeMathWalker)
* Bozhidar Atanasov (@Bobby-Wan)
* Karl Lindfors (@darkkeh)
* Harry Barber (@hlbarber)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
```rust title="src/connection/peer.rs"
use pavex::connection::ConnectionInfo;
use pavex::response::Response;
pub fn handler(conn: ConnectionInfo) -> Response {
let addr = conn.peer_addr();
Response::ok().set_typed_body(format!("Your address is {addr}"))
}
```
2 changes: 2 additions & 0 deletions doc_examples/guide/request_data/connection/project/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
Cargo.lock
13 changes: 13 additions & 0 deletions doc_examples/guide/request_data/connection/project/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "connection"
version = "0.1.0"
edition = "2021"

[dependencies]
pavex = { path = "../../../../../libs/pavex" }
pavex_cli_client = { path = "../../../../../libs/pavex_cli_client" }
serde = { version = "1", features = ["derive"] }
cargo_px_env = "0.1"

[workspace]
members = [".", "server_sdk"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "connection_server_sdk"
version = "0.1.0"
edition = "2021"

[package.metadata.px.generate]
generator_type = "cargo_workspace_binary"
generator_name = "connection"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use pavex::blueprint::Blueprint;

pub fn blueprint() -> Blueprint {
let mut bp = Blueprint::new();

bp.nest(crate::connection::blueprint());
bp
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use pavex::blueprint::router::GET;
use pavex::blueprint::Blueprint;
use pavex::f;

pub fn blueprint() -> Blueprint {
let mut bp = Blueprint::new();
bp.route(GET, "/connection", f!(super::handler));
bp
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub use blueprint::blueprint;
pub use peer::handler;

mod blueprint;
mod peer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use pavex::connection::ConnectionInfo;
use pavex::response::Response;

pub fn handler(conn: ConnectionInfo) -> Response {
let addr = conn.peer_addr();
Response::ok().set_typed_body(format!("Your address is {addr}"))
}
7 changes: 7 additions & 0 deletions doc_examples/guide/request_data/connection/project/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![allow(dead_code)]
#![allow(unused_variables)]

pub use blueprint::blueprint;

mod blueprint;
pub mod connection;
13 changes: 13 additions & 0 deletions doc_examples/guide/request_data/connection/project/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::error::Error;

use cargo_px_env::generated_pkg_manifest_path;
use connection::blueprint;
use pavex_cli_client::Client;

fn main() -> Result<(), Box<dyn Error>> {
let generated_dir = generated_pkg_manifest_path()?.parent().unwrap().into();
Client::new()
.generate(blueprint(), generated_dir)
.execute()?;
Ok(())
}
8 changes: 8 additions & 0 deletions doc_examples/guide/request_data/connection/tutorial.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
starter_project_folder: "project"
commands:
- command: "cargo px c"
expected_outcome: "success"
snippets:
- name: "connection_peer"
source_path: "src/connection/peer.rs"
ranges: [ ".." ]
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ The framework primitives are:
- [`RawIncomingBody`][RawIncomingBody]. The raw body of the incoming request.
- [`RawPathParams`][RawPathParams]. The raw path parameters extracted from the incoming request.
- [`AllowedMethods`][AllowedMethods]. The HTTP methods allowed for the current request path.
- [`ConnectionInfo`][ConnectionInfo]. The peer address for the current connection.

They represent raw data from the incoming request ([`RequestHead`][RequestHead], [`RawIncomingBody`][RawIncomingBody])
or information coming from the routing system ([`AllowedMethods`][AllowedMethods], [`RawPathParams`][RawPathParams]).
They represent raw data about the underlying connection ([`ConnectionInfo`][ConnectionInfo]),
from the incoming request ([`RequestHead`][RequestHead], [`RawIncomingBody`][RawIncomingBody])
or from the routing system ([`AllowedMethods`][AllowedMethods], [`RawPathParams`][RawPathParams]).

## Convenient, but inflexible

Expand All @@ -26,6 +28,7 @@ You lose this flexibility with framework primitives: you can't customize how the
That's why we try to keep their number to a minimum.

[RequestHead]: ../../../api_reference/pavex/request/struct.RequestHead.html
[ConnectionInfo]: ../../../api_reference/pavex/connection/struct.ConnectionInfo.html
[RawPathParams]: ../../../api_reference/pavex/request/path/struct.RawPathParams.html
[AllowedMethods]: ../../../api_reference/pavex/router/enum.AllowedMethods.html
[RawIncomingBody]: ../../../api_reference/pavex/request/body/struct.RawIncomingBody.html
Expand Down
24 changes: 24 additions & 0 deletions docs/guide/request_data/connection_info.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Connection info

[`ConnectionInfo`][ConnectionInfo] groups together information about the HTTP connection
used to transmit the request you're currently processing.

[`ConnectionInfo`][ConnectionInfo] gives you access to the peer address of the client that sent the request, via
the [`ConnectionInfo::peer_addr`][ConnectionInfo::peer_addr] method.
Many applications include the peer address in their request logs, for example.

!!! warning "Security implications"

The peer address should **not** be treated as the clients IP
address. Check out ["The perils of the 'real' client IP"](https://adam-p.ca/blog/2022/03/x-forwarded-for/)
by Adam Pritchard to learn more about the issues you might run into.

## Injection

Inject [`ConnectionInfo`][ConnectionInfo] to access the peer address via its [`peer_addr`][ConnectionInfo::peer_addr]
method:

--8<-- "doc_examples/guide/request_data/connection/project-connection_peer.snap"

[ConnectionInfo]: ../../../api_reference/pavex/connection/struct.ConnectionInfo.html
[ConnectionInfo::peer_addr]: ../../../api_reference/pavex/connection/struct.ConnectionInfo.html#method.peer_addr
6 changes: 5 additions & 1 deletion examples/realworld/server/configuration/base.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
server:
port: 8000
port: 8000
app:
cookie:
percent_encode: true
crypto_rules: [ ]
1 change: 1 addition & 0 deletions examples/realworld/server_sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ fn build_router() -> pavex_matchit::Router<u32> {
}
async fn route_request(
request: http::Request<hyper::body::Incoming>,
_connection_info: Option<pavex::connection::ConnectionInfo>,
server_state: std::sync::Arc<ServerState>,
) -> pavex::response::Response {
let (request_head, request_body) = request.into_parts();
Expand Down
Loading

0 comments on commit 58452eb

Please sign in to comment.