(response_body: &str)
| 50 | |
| 51 | impl HostServer { |
| 52 | async fn start(response_body: &str) -> Result<Self, String> { |
| 53 | let listener = TcpListener::bind(("0.0.0.0", 0)) |
| 54 | .await |
| 55 | .map_err(|e| format!("bind host test server: {e}"))?; |
| 56 | let port = listener |
| 57 | .local_addr() |
| 58 | .map_err(|e| format!("read host test server address: {e}"))? |
| 59 | .port(); |
| 60 | let response_body = response_body.as_bytes().to_vec(); |
| 61 | let task = tokio::spawn(async move { |
| 62 | loop { |
| 63 | let Ok((mut stream, _)) = listener.accept().await else { |
| 64 | break; |
| 65 | }; |
| 66 | let body = response_body.clone(); |
| 67 | tokio::spawn(async move { |
| 68 | let mut request = Vec::new(); |
| 69 | let mut buf = [0_u8; 1024]; |
| 70 | loop { |
| 71 | let Ok(read) = stream.read(&mut buf).await else { |
| 72 | return; |
| 73 | }; |
| 74 | if read == 0 { |
| 75 | return; |
| 76 | } |
| 77 | request.extend_from_slice(&buf[..read]); |
| 78 | if request.windows(4).any(|window| window == b"\r\n\r\n") { |
| 79 | break; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | let response = format!( |
| 84 | "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", |
| 85 | body.len() |
| 86 | ); |
| 87 | if stream.write_all(response.as_bytes()).await.is_err() { |
| 88 | return; |
| 89 | } |
| 90 | let _ = stream.write_all(&body).await; |
| 91 | let _ = stream.shutdown().await; |
| 92 | }); |
| 93 | } |
| 94 | }); |
| 95 | |
| 96 | Ok(Self { port, task }) |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | impl Drop for HostServer { |
no test coverage detected