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