mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
## Why Requests with `Connection: close` must not reuse the connection even when the server responds with `Connection: keep-alive`. ## What changed Update `hyper` from 1.8.1 to 1.11.1 in `codex-rs/Cargo.lock` and add the corresponding Bazel lockfile metadata. ## Testing Add a regression test that sends two requests with `Connection: close` to a server that ignores the header and verifies that the second request uses a new connection. GitOrigin-RevId: 72b464c8ea3d9a1ad5d9e62f7896ea9a4f15a93c
117 lines
4.1 KiB
Rust
117 lines
4.1 KiB
Rust
use super::*;
|
|
use http::HeaderValue;
|
|
use std::io::Read;
|
|
use std::io::Write;
|
|
use std::path::PathBuf;
|
|
|
|
#[tokio::test]
|
|
async fn custom_ca_fallback_preserves_builder_configuration() {
|
|
let listener =
|
|
std::net::TcpListener::bind(("127.0.0.1", 0)).expect("HTTP listener should bind");
|
|
let address = listener
|
|
.local_addr()
|
|
.expect("HTTP listener should have an address");
|
|
let server = std::thread::spawn(move || {
|
|
let (mut stream, _) = listener.accept().expect("HTTP listener should accept");
|
|
let mut request = Vec::new();
|
|
let mut chunk = [0_u8; 1024];
|
|
while !request.windows(4).any(|window| window == b"\r\n\r\n") {
|
|
let bytes_read = stream.read(&mut chunk).expect("HTTP request should read");
|
|
assert!(bytes_read > 0, "HTTP request should include headers");
|
|
request.extend_from_slice(&chunk[..bytes_read]);
|
|
}
|
|
stream
|
|
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n")
|
|
.expect("HTTP listener should write response");
|
|
String::from_utf8(request).expect("HTTP request should be UTF-8")
|
|
});
|
|
let mut headers = HeaderMap::new();
|
|
headers.insert("x-builder-test", HeaderValue::from_static("preserved"));
|
|
let client = HttpClientBuilder::new()
|
|
.default_headers(headers)
|
|
.build_with_custom_ca_fallback_using(ProxyRouting::Direct, |_| {
|
|
Err(BuildCustomCaTransportError::InvalidCaFile {
|
|
source_env: "TEST_CA_ENV",
|
|
path: PathBuf::from("invalid-test-ca.pem"),
|
|
detail: "synthetic invalid CA".to_string(),
|
|
})
|
|
});
|
|
|
|
let client = HttpClient {
|
|
backend: HttpClientBackend::Direct(client),
|
|
};
|
|
|
|
let response = client
|
|
.get(format!("http://{address}/fallback"))
|
|
.send()
|
|
.await
|
|
.expect("fallback client should send request");
|
|
assert!(response.status().is_success());
|
|
let request = server.join().expect("HTTP listener should finish");
|
|
assert!(
|
|
request
|
|
.lines()
|
|
.any(|line| line.eq_ignore_ascii_case("x-builder-test: preserved"))
|
|
);
|
|
}
|
|
|
|
/// A server cannot keep a connection reusable after the caller requests closure.
|
|
#[tokio::test]
|
|
async fn connection_close_prevents_reuse_when_server_ignores_it()
|
|
-> Result<(), Box<dyn std::error::Error>> {
|
|
use tokio::io::AsyncReadExt;
|
|
use tokio::io::AsyncWriteExt;
|
|
|
|
let listener = tokio::net::TcpListener::bind(("127.0.0.1", 0)).await?;
|
|
let url = format!("http://{}/", listener.local_addr()?);
|
|
let server = tokio::spawn(async move {
|
|
let (mut stream, _) = listener.accept().await?;
|
|
let mut headers = Vec::new();
|
|
while !headers.ends_with(b"\r\n\r\n") {
|
|
headers.push(stream.read_u8().await?);
|
|
}
|
|
// Deliberately ignore the request's Connection: close header.
|
|
let response = b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n";
|
|
stream.write_all(response).await?;
|
|
let mut byte = [0];
|
|
let reused = tokio::select! {
|
|
read = stream.read(&mut byte) => {
|
|
if read? == 0 {
|
|
stream = listener.accept().await?.0;
|
|
false
|
|
} else {
|
|
true
|
|
}
|
|
}
|
|
accepted = listener.accept() => {
|
|
stream = accepted?.0;
|
|
false
|
|
}
|
|
};
|
|
headers.clear();
|
|
if reused {
|
|
headers.push(byte[0]);
|
|
}
|
|
while !headers.ends_with(b"\r\n\r\n") {
|
|
headers.push(stream.read_u8().await?);
|
|
}
|
|
stream.write_all(response).await?;
|
|
Ok::<bool, std::io::Error>(reused)
|
|
});
|
|
let client = HttpClientBuilder::new().build_direct()?;
|
|
for _ in 0..2 {
|
|
client
|
|
.get(&url)
|
|
.header(http::header::CONNECTION, HeaderValue::from_static("close"))
|
|
.send()
|
|
.await?
|
|
.bytes()
|
|
.await?;
|
|
}
|
|
assert!(
|
|
!server.await??,
|
|
"the server reused a connection marked closed"
|
|
);
|
|
Ok(())
|
|
}
|