1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! The HTTP request a transcoded call was made from.
//!
//! A transcoded call reaches the upstream as a gRPC call to the RPC its
//! binding selected; the client's HTTP method and request target are no
//! longer part of it, and one RPC may be bound to several paths (additional
//! bindings, aliases), so the RPC path cannot be turned back into them. An
//! upstream served in process gets them as a [`ReceivedRequest`] in the
//! request extensions. The proxy records them and checks nothing about them.
//! A remote upstream never sees request extensions.
use PathAndQuery;
use Method;
/// The HTTP request a transcoded gRPC call was made from, as the proxy
/// received it: its method, its path and query, and the gRPC path of the RPC
/// the matched binding selected.
///
/// The path is the one on the request line, before a router the proxy is
/// nested in strips its prefix, so an alias or a public prefix stays visible.
/// An absolute-form target (any HTTP/2 request, an HTTP/1.1 request through a
/// forward proxy) records its path and query alone, so one request records the
/// same value over either protocol. No header the client sends
/// (`x-original-uri`, forwarding headers) changes it.
///
/// # Examples
///
/// ```
/// use structured_proxy::ReceivedRequest;
///
/// // What a tonic handler behind the proxy does with it.
/// fn target(request: &tonic::Request<()>) -> Option<String> {
/// let received = request.extensions().get::<ReceivedRequest>()?;
/// Some(format!("{} {}", received.method(), received.path_and_query()))
/// }
///
/// let received = ReceivedRequest::new(
/// http::Method::POST,
/// http::uri::PathAndQuery::from_static("/v1/orgs/42:suspend?dry=1"),
/// http::uri::PathAndQuery::from_static("/acme.v1.Orgs/Suspend"),
/// );
/// let mut request = tonic::Request::new(());
/// request.extensions_mut().insert(received);
/// assert_eq!(target(&request).as_deref(), Some("POST /v1/orgs/42:suspend?dry=1"));
/// ```