Codex Handbook
app-server-test-client/src/loopback_responses_server.rs 145 lines
use anyhow::Context;use anyhow::Result;use std::io;use std::io::Read;use std::io::Write;use std::net::TcpListener;use std::net::TcpStream;use std::sync::Arc;use std::sync::atomic::AtomicBool;use std::sync::atomic::Ordering;use std::thread;use std::thread::JoinHandle;use std::time::Duration;pub(super) struct LoopbackResponsesServer {    base_url: String,    shutdown: Arc<AtomicBool>,    thread: Option<JoinHandle<()>>,}impl LoopbackResponsesServer {    pub(super) fn start() -> Result<Self> {        let listener =            TcpListener::bind("127.0.0.1:0").context("bind loopback Responses API server")?;        listener            .set_nonblocking(true)            .context("set loopback Responses API server nonblocking")?;        let address = listener.local_addr()?;        let shutdown = Arc::new(AtomicBool::new(false));        let thread_shutdown = Arc::clone(&shutdown);        let thread = thread::spawn(move || {            while !thread_shutdown.load(Ordering::Relaxed) {                match listener.accept() {                    Ok((stream, _)) => {                        if let Err(err) = handle_model_connection(stream) {                            eprintln!("loopback Responses API server error: {err}");                        }                    }                    Err(err) if err.kind() == io::ErrorKind::WouldBlock => {                        thread::sleep(Duration::from_millis(10));                    }                    Err(err) => {                        eprintln!("loopback Responses API accept error: {err}");                        break;                    }                }            }        });        Ok(Self {            base_url: format!("http://{address}"),            shutdown,            thread: Some(thread),        })    }    pub(super) fn base_url(&self) -> &str {        &self.base_url    }}impl Drop for LoopbackResponsesServer {    fn drop(&mut self) {        self.shutdown.store(true, Ordering::Relaxed);        if let Some(thread) = self.thread.take() {            let _ = thread.join();        }    }}fn handle_model_connection(mut stream: TcpStream) -> io::Result<()> {    stream.set_nonblocking(false)?;    stream.set_read_timeout(Some(Duration::from_secs(2)))?;    let request = read_http_request(&mut stream)?;    let request_line = request        .split(|byte| *byte == b'\n')        .next()        .and_then(|line| std::str::from_utf8(line).ok())        .unwrap_or_default();    if request_line.starts_with("POST ") && request_line.contains("/responses ") {        let body = concat!(            "event: response.created\n",            "data: {\"type\":\"response.created\",\"response\":{\"id\":\"resp-plugin-analytics\"}}\n\n",            "event: response.completed\n",            "data: {\"type\":\"response.completed\",\"response\":{\"id\":\"resp-plugin-analytics\",\"usage\":{\"input_tokens\":0,\"input_tokens_details\":null,\"output_tokens\":0,\"output_tokens_details\":null,\"total_tokens\":0}}}\n\n"        );        write_http_response(&mut stream, "200 OK", "text/event-stream", body)    } else {        write_http_response(            &mut stream,            "404 Not Found",            "application/json",            r#"{"error":"not found"}"#,        )    }}fn read_http_request(stream: &mut TcpStream) -> io::Result<Vec<u8>> {    let mut request = Vec::new();    let mut buffer = [0_u8; 4096];    let header_end = loop {        let read = stream.read(&mut buffer)?;        if read == 0 {            return Ok(request);        }        request.extend_from_slice(&buffer[..read]);        if let Some(position) = request.windows(4).position(|window| window == b"\r\n\r\n") {            break position + 4;        }    };    let content_length = parse_content_length(&request[..header_end]);    while request.len() < header_end + content_length {        let read = stream.read(&mut buffer)?;        if read == 0 {            break;        }        request.extend_from_slice(&buffer[..read]);    }    Ok(request)}fn parse_content_length(headers: &[u8]) -> usize {    String::from_utf8_lossy(headers)        .lines()        .find_map(|line| {            let (name, value) = line.split_once(':')?;            name.eq_ignore_ascii_case("content-length")                .then(|| value.trim().parse().ok())                .flatten()        })        .unwrap_or(0)}fn write_http_response(    stream: &mut TcpStream,    status: &str,    content_type: &str,    body: &str,) -> io::Result<()> {    write!(        stream,        "HTTP/1.1 {status}\r\nContent-Type: {content_type}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}",        body.len()    )?;    stream.flush()}