use super::hops::{rewrite_request_head, HttpOnlyStream};
use super::*;
fn http_only_target() -> TargetAddr {
TargetAddr {
host: TargetHost::Domain("target.example".into()),
port: 8080,
}
}
#[test]
fn httponly_rewrite_preserves_header_terminators() {
let request = b"GET /path HTTP/1.1\r\nHost: example.com\r\nX-Foo: bar\r\n\r\nbody";
let rewritten = rewrite_request_head(request, &http_only_target());
assert_eq!(
std::str::from_utf8(&rewritten).unwrap(),
"GET http://target.example:8080/path HTTP/1.1\r\n\
Host: example.com\r\n\
X-Foo: bar\r\n\
\r\n\
body"
);
}
#[test]
fn httponly_rewrite_preserves_mixed_line_endings() {
let request = b"GET /path HTTP/1.1\nHost: example.com\r\nX-Foo: bar\r\n\r\n";
let rewritten = rewrite_request_head(request, &http_only_target());
assert_eq!(
std::str::from_utf8(&rewritten).unwrap(),
"GET http://target.example:8080/path HTTP/1.1\n\
Host: example.com\r\n\
X-Foo: bar\r\n\
\r\n"
);
}
#[test]
fn httponly_rewrite_waits_for_complete_head() {
let partial = b"GET /path HTTP/1.1\r\nHost: example.com\r\n".as_slice();
assert_eq!(rewrite_request_head(partial, &http_only_target()), partial);
}
#[test]
fn httponly_rewrite_leaves_absolute_form_and_incomplete_heads_alone() {
let absolute = b"GET http://example.com/path HTTP/1.1\r\nHost: example.com\r\n\r\n".as_slice();
assert_eq!(
rewrite_request_head(absolute, &http_only_target()),
absolute
);
let partial = b"GET /path HTTP/1.1\r\nHost: example.com\r\n".as_slice();
assert_eq!(rewrite_request_head(partial, &http_only_target()), partial);
assert!(rewrite_request_head(b"", &http_only_target()).is_empty());
}
#[tokio::test]
async fn httponly_stream_rewrites_once_and_drains_on_shutdown() {
let (mut peer, inner) = tokio::io::duplex(16);
let mut stream = HttpOnlyStream {
inner: Box::new(inner),
target: http_only_target(),
pending: Vec::new(),
rewritten: false,
};
let request =
b"GET /path HTTP/1.1\r\nHost: example.com\r\nX-Foo: /bar baz\r\n\r\ntail".as_slice();
let expected =
b"GET http://target.example:8080/path HTTP/1.1\r\nHost: example.com\r\nX-Foo: /bar baz\r\n\r\ntail";
let reader = tokio::spawn(async move {
let mut received = Vec::new();
peer.read_to_end(&mut received).await.unwrap();
received
});
use tokio::io::AsyncWriteExt;
stream.write_all(request).await.unwrap();
stream.shutdown().await.unwrap();
let received = reader.await.unwrap();
assert_eq!(received, expected);
}