Skip to main content

plexus_core/
request.rs

1//! Raw HTTP request context and extraction trait for Plexus request extraction.
2//!
3//! This module defines:
4//! - `RawRequestContext` — the raw HTTP view passed through the dispatch chain
5//! - `PlexusRequest` — trait for typed field extraction from `RawRequestContext`
6//!
7//! Defined here (in plexus-core) so that:
8//! - The `Activation` trait can reference `RawRequestContext` without a circular dep
9//! - The generated dispatch code can call `PlexusRequest::extract` via `#crate_path::request`
10
11use std::net::SocketAddr;
12use crate::plexus::{AuthContext, PlexusError};
13
14/// The raw HTTP context available when extracting a typed request struct.
15///
16/// This is populated from the HTTP upgrade/connection phase and made available
17/// to every `#[derive(PlexusRequest)]` extraction.
18pub struct RawRequestContext {
19    /// HTTP headers from the request.
20    pub headers: http::HeaderMap,
21
22    /// The request URI (used for query-parameter extraction).
23    pub uri: http::Uri,
24
25    /// Authenticated user context, if authentication succeeded.
26    pub auth: Option<AuthContext>,
27
28    /// Remote peer socket address, if available.
29    pub peer: Option<SocketAddr>,
30}
31
32/// Trait implemented by `#[derive(PlexusRequest)]` structs.
33///
34/// A `PlexusRequest` struct is a typed view of an inbound HTTP request, where
35/// each field is extracted from a specific part of the raw context (cookie,
36/// header, query string, peer address, or auth context).
37///
38/// # Deriving
39///
40/// Use the `PlexusRequest` derive macro from `plexus_macros`:
41///
42/// ```ignore
43/// use plexus_macros::PlexusRequest;
44///
45/// #[derive(PlexusRequest)]
46/// struct MyRequest {
47///     #[from_cookie("access_token")]
48///     auth_token: String,
49///
50///     #[from_header("origin")]
51///     origin: Option<String>,
52///
53///     #[from_peer]
54///     peer_addr: Option<std::net::SocketAddr>,
55/// }
56/// ```
57pub trait PlexusRequest: Sized {
58    /// Extract a typed request from the raw HTTP context.
59    fn extract(ctx: &RawRequestContext) -> Result<Self, PlexusError>;
60
61    /// Return the JSON Schema for this request type as a `serde_json::Value`.
62    ///
63    /// The schema includes `x-plexus-source` metadata on each field describing
64    /// where the field value is extracted from (cookie, header, peer, etc.).
65    ///
66    /// The default implementation returns `None`; the `#[derive(PlexusRequest)]`
67    /// macro generates a concrete implementation with the full schema.
68    fn request_schema() -> Option<serde_json::Value> {
69        None
70    }
71}
72
73/// Trait for newtype field types that carry their own extraction and validation logic.
74///
75/// Implement this trait on a newtype wrapper to enable `#[derive(PlexusRequest)]`
76/// to extract it without an explicit source annotation. The macro generates:
77///
78/// ```ignore
79/// let field_name = FieldType::extract_from_raw(ctx)?;
80/// ```
81///
82/// for fields of any type that implements `PlexusRequestField`.
83pub trait PlexusRequestField: Sized {
84    /// Extract and validate `Self` from the raw HTTP request context.
85    ///
86    /// Return `Ok(Self)` on success, or a `PlexusError` (typically
87    /// `PlexusError::Unauthenticated`) on validation failure.
88    fn extract_from_raw(ctx: &RawRequestContext) -> Result<Self, PlexusError>;
89}
90
91/// Parse a named cookie from a raw `Cookie` header value.
92///
93/// The cookie header value is like `"session=abc; access_token=tok123; other=xyz"`.
94/// Returns the value for the first matching key, or `None` if not found.
95///
96/// This is exported so that the `#[derive(PlexusRequest)]` generated code can call it.
97pub fn parse_cookie<'a>(cookie_str: &'a str, name: &str) -> Option<&'a str> {
98    for part in cookie_str.split(';') {
99        let part = part.trim();
100        if let Some(rest) = part.strip_prefix(name) {
101            if let Some(value) = rest.strip_prefix('=') {
102                return Some(value);
103            }
104        }
105    }
106    None
107}