Skip to main content

sova_http/
bound.rs

1//! `req.http()` — client bound to inbound deadline and trace headers.
2
3use crate::client::{propagation_headers, HttpClient, NamedClient, PendingRequest};
4use sova_core::RequestId;
5use http::Method;
6use sova_core::Request;
7
8/// Extension: outbound client with request context.
9pub trait HttpExt {
10    fn http(&self) -> HttpBound<'_>;
11}
12
13impl HttpExt for Request {
14    fn http(&self) -> HttpBound<'_> {
15        HttpBound { req: self }
16    }
17}
18
19/// Bound view: deadline + propagation applied on each call.
20pub struct HttpBound<'a> {
21    req: &'a Request,
22}
23
24impl HttpBound<'_> {
25    fn client(&self) -> std::sync::Arc<HttpClient> {
26        self.req.state::<HttpClient>()
27    }
28
29    fn apply(&self, mut pending: PendingRequest) -> PendingRequest {
30        // Ensure id exists for propagation (middleware may have set it).
31        let _ = self.req.get::<RequestId>();
32        for (k, v) in propagation_headers(self.req).iter() {
33            pending.headers.insert(k.clone(), v.clone());
34        }
35        pending.with_budget(self.req.deadline_remaining())
36    }
37
38    pub fn get(&self, url: impl Into<String>) -> PendingRequest {
39        self.apply(self.client().get(url))
40    }
41
42    pub fn post(&self, url: impl Into<String>) -> PendingRequest {
43        self.apply(self.client().post(url))
44    }
45
46    pub fn put(&self, url: impl Into<String>) -> PendingRequest {
47        self.apply(self.client().put(url))
48    }
49
50    pub fn patch(&self, url: impl Into<String>) -> PendingRequest {
51        self.apply(self.client().patch(url))
52    }
53
54    pub fn delete(&self, url: impl Into<String>) -> PendingRequest {
55        self.apply(self.client().delete(url))
56    }
57
58    pub fn request(&self, method: Method, url: impl Into<String>) -> PendingRequest {
59        self.apply(self.client().request(method, url))
60    }
61
62    pub fn named(&self, name: &str) -> NamedBound<'_> {
63        NamedBound {
64            req: self.req,
65            named: self.client().named(name),
66        }
67    }
68}
69
70pub struct NamedBound<'a> {
71    req: &'a Request,
72    named: NamedClient,
73}
74
75impl NamedBound<'_> {
76    fn apply(&self, mut pending: PendingRequest) -> PendingRequest {
77        for (k, v) in propagation_headers(self.req).iter() {
78            pending.headers.insert(k.clone(), v.clone());
79        }
80        pending.with_budget(self.req.deadline_remaining())
81    }
82
83    pub fn get(&self, path: impl Into<String>) -> PendingRequest {
84        self.apply(self.named.get(path))
85    }
86
87    pub fn post(&self, path: impl Into<String>) -> PendingRequest {
88        self.apply(self.named.post(path))
89    }
90
91    pub fn put(&self, path: impl Into<String>) -> PendingRequest {
92        self.apply(self.named.put(path))
93    }
94
95    pub fn patch(&self, path: impl Into<String>) -> PendingRequest {
96        self.apply(self.named.patch(path))
97    }
98
99    pub fn delete(&self, path: impl Into<String>) -> PendingRequest {
100        self.apply(self.named.delete(path))
101    }
102}