Files
sausage/userland/curl/tests/server/src/main.rs
T

94 lines
3.3 KiB
Rust

//! Deterministic native HTTP fixture for curl comparisons.
use std::{
collections::BTreeMap,
io::{BufRead, BufReader, Read, Write},
net::TcpListener,
};
fn main() {
let ready = std::env::args().nth(1).expect("readiness path");
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
std::fs::write(ready, listener.local_addr().unwrap().port().to_string()).unwrap();
for stream in listener.incoming() {
let mut stream = stream.unwrap();
let mut read = BufReader::new(stream.try_clone().unwrap());
let mut first = String::new();
read.read_line(&mut first).unwrap();
let words: Vec<_> = first.split_whitespace().collect();
if words.len() < 2 {
continue;
}
let method = words[0];
let path = words[1];
let mut headers: BTreeMap<String, Vec<String>> = BTreeMap::new();
let mut length = 0;
loop {
let mut line = String::new();
read.read_line(&mut line).unwrap();
if line == "\r\n" || line.is_empty() {
break;
}
let (name, value) = line.split_once(':').unwrap();
let name = name.to_ascii_lowercase();
let value = value.trim();
if name == "content-length" {
length = value.parse().unwrap();
}
if [
"accept",
"content-type",
"authorization",
"user-agent",
"x-test",
]
.contains(&name.as_str())
{
headers.entry(name).or_default().push(value.into());
}
}
let mut body = vec![0; length];
read.read_exact(&mut body).unwrap();
let echo = serde_json::json!({"method":method,"path":path,"body":String::from_utf8_lossy(&body),"headers":headers}).to_string();
let (status, reason, response, extra) = match path {
"/plain" => (200, "OK", "hello\n".into(), "".into()),
"/404" => (404, "Not Found", "missing\n".into(), "".into()),
"/redirect" => (
302,
"Found",
"redirect\n".into(),
"location: /echo\r\n".into(),
),
"/303" => (
303,
"See Other",
"redirect\n".into(),
"location: /echo\r\n".into(),
),
"/307" => (
307,
"Temporary Redirect",
"redirect\n".into(),
"location: /echo\r\n".into(),
),
"/loop" => (
302,
"Found",
"redirect\n".into(),
"location: /loop\r\n".into(),
),
"/slow" => {
std::thread::sleep(std::time::Duration::from_millis(100));
(200, "OK", "slow\n".into(), "".into())
}
_ => (200, "OK", echo, "".into()),
};
let response: String = response;
let extra: String = extra;
let _ = write!(
stream,
"HTTP/1.1 {status} {reason}\r\ncontent-type: text/plain\r\ncontent-length: {}\r\n{extra}\r\n{}",
response.len(),
if method == "HEAD" { "" } else { &response }
);
}
}