1use std::{
19 future::Future,
20 sync::{
21 Arc,
22 atomic::{AtomicUsize, Ordering},
23 },
24 time::Duration,
25};
26
27use tokio::io::{AsyncReadExt, AsyncWriteExt};
28
29pub async fn assert_http_redirect_rejected<F, Fut>(send: F)
41where
42 F: FnOnce(String) -> Fut,
43 Fut: Future<Output = u16>,
44{
45 let origin = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
46 let addr = origin.local_addr().unwrap();
47 let destination = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
48 let target = destination.local_addr().unwrap();
49 let destination_requests = Arc::new(AtomicUsize::new(0));
50 let requests = destination_requests.clone();
51
52 let destination_task = tokio::spawn(async move {
53 let (mut stream, _) = destination.accept().await.unwrap();
54 read_request_headers(&mut stream).await;
55 requests.fetch_add(1, Ordering::SeqCst);
56 stream
57 .write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n")
58 .await
59 .unwrap();
60 });
61
62 let origin_task = tokio::spawn(async move {
63 let (mut stream, _) = origin.accept().await.unwrap();
64 read_request_headers(&mut stream).await;
65 stream.write_all(format!("HTTP/1.1 307 Temporary Redirect\r\nContent-Length: 0\r\nConnection: close\r\nLocation: http://{target}/destination\r\n\r\n").as_bytes()).await.unwrap();
66 });
67 let result = tokio::time::timeout(
68 Duration::from_secs(5),
69 send(format!("http://{addr}/origin")),
70 )
71 .await;
72 origin_task.abort();
73 destination_task.abort();
74
75 assert_eq!(result.unwrap(), 307);
76 assert_eq!(destination_requests.load(Ordering::SeqCst), 0);
77}
78
79async fn read_request_headers(stream: &mut tokio::net::TcpStream) {
80 let mut headers = Vec::new();
81 while !headers.ends_with(b"\r\n\r\n") {
82 let mut byte = [0];
83 stream.read_exact(&mut byte).await.unwrap();
84 headers.push(byte[0]);
85 }
86}