Skip to content
Merged
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: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

https://github.com/oxidecomputer/dropshot/compare/v0.9.0\...HEAD[Full list of commits]

* https://github.com/oxidecomputer/dropshot/pull/651[#651] The address of the remote peer is now available to request handlers via the `RequestInfo` struct.

== 0.9.0 (released 2023-01-20)

https://github.com/oxidecomputer/dropshot/compare/v0.8.0\...v0.9.0[Full list of commits]
Expand Down
19 changes: 11 additions & 8 deletions dropshot/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,25 +96,24 @@ pub struct RequestInfo {
uri: http::Uri,
version: http::Version,
headers: http::HeaderMap<http::HeaderValue>,
remote_addr: std::net::SocketAddr,
}

impl<B> From<&hyper::Request<B>> for RequestInfo {
fn from(request: &hyper::Request<B>) -> Self {
impl RequestInfo {
pub(crate) fn new<B>(
request: &hyper::Request<B>,
remote_addr: std::net::SocketAddr,
) -> Self {
RequestInfo {
method: request.method().clone(),
uri: request.uri().clone(),
version: request.version(),
headers: request.headers().clone(),
remote_addr,
}
}
}

impl<B> From<hyper::Request<B>> for RequestInfo {
fn from(request: hyper::Request<B>) -> Self {
Self::from(&request)
}
}

impl RequestInfo {
pub fn method(&self) -> &http::Method {
&self.method
Expand All @@ -132,6 +131,10 @@ impl RequestInfo {
&self.headers
}

pub fn remote_addr(&self) -> std::net::SocketAddr {
self.remote_addr
}

/// Returns a reference to the `RequestInfo` itself
///
/// This is provided for source compatibility. In previous versions of
Expand Down
4 changes: 3 additions & 1 deletion dropshot/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ async fn http_request_handle_wrap<C: ServerContext>(
request,
&request_id,
request_log.new(o!()),
remote_addr,
)
.await;

Expand Down Expand Up @@ -773,6 +774,7 @@ async fn http_request_handle<C: ServerContext>(
request: Request<Body>,
request_id: &str,
request_log: Logger,
remote_addr: std::net::SocketAddr,
) -> Result<Response<Body>, HttpError> {
// TODO-hardening: is it correct to (and do we correctly) read the entire
// request body even if we decide it's too large and are going to send a 400
Expand All @@ -786,7 +788,7 @@ async fn http_request_handle<C: ServerContext>(
server.router.lookup_route(&method, uri.path().into())?;
let rqctx = RequestContext {
server: Arc::clone(&server),
request: RequestInfo::from(&request),
request: RequestInfo::new(&request, remote_addr),
path_variables: lookup_result.variables,
body_content_type: lookup_result.body_content_type,
request_id: request_id.to_string(),
Expand Down
4 changes: 3 additions & 1 deletion dropshot/src/websocket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ mod tests {
.header(http::header::SEC_WEBSOCKET_KEY, "aGFjayB0aGUgcGxhbmV0IQ==")
.body(Body::empty())
.unwrap();
let remote_addr =
SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 12345);
let rqctx = RequestContext {
server: Arc::new(DropshotState {
private: (),
Expand All @@ -332,7 +334,7 @@ mod tests {
),
tls_acceptor: None,
}),
request: RequestInfo::from(&request),
request: RequestInfo::new(&request, remote_addr),
path_variables: Default::default(),
body_content_type: Default::default(),
request_id: "".to_string(),
Expand Down
47 changes: 47 additions & 0 deletions dropshot/tests/test_demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ fn demo_api() -> ApiDescription<usize> {
api.register(demo_handler_307_temporary_redirect).unwrap();
api.register(demo_handler_websocket).unwrap();
api.register(demo_handler_request_compat).unwrap();
api.register(demo_handler_request_addresses).unwrap();

// We don't need to exhaustively test these cases, as they're tested by unit
// tests.
Expand Down Expand Up @@ -982,6 +983,35 @@ async fn test_request_compat() {
testctx.teardown().await;
}

#[tokio::test]
async fn test_request_remote_addr() {
let api = demo_api();
let testctx = common::test_setup("test_request_remote_addr", api);
let laddr = testctx.server.local_addr();
let mut response = testctx
.client_testctx
.make_request(
Method::GET,
"/testing/request_addresses",
None as Option<()>,
StatusCode::OK,
)
.await
.expect("expected success");
let json: Vec<String> = read_json(&mut response).await;
assert_eq!(json.len(), 4);
// Confirm the local address and port seen from inside the server matches
// the one that we got from the test context:
assert_eq!(json[0], laddr.ip().to_string());
assert_eq!(json[1], laddr.port().to_string());
// There does not appear to be an easy way to determine which port was
// used for the outbound request we make here, but we at least know that
// it must not be the same as the server listen port:
assert_eq!(json[2], laddr.ip().to_string());
assert_ne!(json[3], laddr.port().to_string());
testctx.teardown().await;
}

// Demo handler functions

type RequestCtx = RequestContext<usize>;
Expand Down Expand Up @@ -1316,6 +1346,23 @@ async fn demo_handler_request_compat(
http_echo(&value)
}

#[endpoint {
method = GET,
path = "/testing/request_addresses",
}]
async fn demo_handler_request_addresses(
rqctx: RequestCtx,
) -> Result<Response<Body>, HttpError> {
let laddr = rqctx.server.local_addr;
let raddr = rqctx.request.remote_addr();
http_echo(&vec![
laddr.ip().to_string(),
laddr.port().to_string(),
raddr.ip().to_string(),
raddr.port().to_string(),
])
}

fn http_echo<T: Serialize>(t: &T) -> Result<Response<Body>, HttpError> {
Ok(Response::builder()
.header(http::header::CONTENT_TYPE, CONTENT_TYPE_JSON)
Expand Down