mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
fix(proxy+exec): harden proxy TLS forwarding (set servername, reset Host, normalize hop-by-hop headers) and make exec exit non-zero on agent Error events so workflows fail fast when streaming fails
This commit is contained in:
@@ -384,7 +384,16 @@ jobs:
|
||||
if (!incoming['openai-beta']) incoming['openai-beta'] = 'responses=experimental';
|
||||
}
|
||||
if (!incoming['originator']) incoming['originator'] = 'codex_cli_rs';
|
||||
// Normalize hop-by-hop and target-specific headers for upstream
|
||||
// Ensure TLS SNI/Host align with the upstream authority
|
||||
delete incoming['host'];
|
||||
incoming['host'] = up.host; // includes port when present
|
||||
incoming['connection'] = 'keep-alive';
|
||||
// Normalize content-length to actual buffered body size when present
|
||||
if (incoming['content-length']) incoming['content-length'] = String(body.length);
|
||||
// Remove proxy-specific headers that should never reach upstream
|
||||
delete incoming['proxy-connection'];
|
||||
delete incoming['proxy-authorization'];
|
||||
|
||||
log({ level: 'info', rid, phase: 'request', method: req.method, url: up.toString(), headers: redactHeaders(incoming), body_bytes: body.length });
|
||||
|
||||
@@ -395,6 +404,12 @@ jobs:
|
||||
path: up.pathname + up.search,
|
||||
method: req.method,
|
||||
headers: incoming,
|
||||
// Ensure correct SNI for TLS handshake
|
||||
servername: up.hostname,
|
||||
// Reuse connections for streaming stability
|
||||
agent: (up.protocol === 'https:'
|
||||
? new https.Agent({ keepAlive: true, maxSockets: 64, servername: up.hostname })
|
||||
: new http.Agent({ keepAlive: true, maxSockets: 64 }))
|
||||
};
|
||||
|
||||
const upstream = (up.protocol === 'https:' ? https : http).request(opts, (upr) => {
|
||||
|
||||
@@ -15,6 +15,13 @@ pub(crate) trait EventProcessor {
|
||||
|
||||
/// Handle a single event emitted by the agent.
|
||||
fn process_event(&mut self, event: Event) -> CodexStatus;
|
||||
|
||||
/// Exit code to use upon completion. Implementations should set a non-zero
|
||||
/// value when a fatal error was encountered (e.g., upstream streaming
|
||||
/// failure).
|
||||
fn exit_code(&self) -> i32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_last_message(last_agent_message: Option<&str>, output_file: &Path) {
|
||||
|
||||
@@ -63,6 +63,7 @@ pub(crate) struct EventProcessorWithHumanOutput {
|
||||
reasoning_started: bool,
|
||||
raw_reasoning_started: bool,
|
||||
last_message_path: Option<PathBuf>,
|
||||
had_error: bool,
|
||||
}
|
||||
|
||||
impl EventProcessorWithHumanOutput {
|
||||
@@ -91,6 +92,7 @@ impl EventProcessorWithHumanOutput {
|
||||
reasoning_started: false,
|
||||
raw_reasoning_started: false,
|
||||
last_message_path,
|
||||
had_error: false,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
@@ -109,6 +111,7 @@ impl EventProcessorWithHumanOutput {
|
||||
reasoning_started: false,
|
||||
raw_reasoning_started: false,
|
||||
last_message_path,
|
||||
had_error: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,6 +174,7 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
EventMsg::Error(ErrorEvent { message }) => {
|
||||
let prefix = "ERROR:".style(self.red);
|
||||
ts_println!(self, "{prefix} {message}");
|
||||
self.had_error = true;
|
||||
}
|
||||
EventMsg::BackgroundEvent(BackgroundEventEvent { message }) => {
|
||||
ts_println!(self, "{}", message.style(self.dimmed));
|
||||
@@ -611,8 +615,15 @@ impl EventProcessor for EventProcessorWithHumanOutput {
|
||||
}
|
||||
CodexStatus::Running
|
||||
}
|
||||
|
||||
fn exit_code(&self) -> i32 {
|
||||
if self.had_error { 1 } else { 0 }
|
||||
}
|
||||
}
|
||||
|
||||
// Extend trait with exit_code override
|
||||
// Add exit_code method to the existing EventProcessor impl
|
||||
|
||||
fn escape_command(command: &[String]) -> String {
|
||||
try_join(command.iter().map(|s| s.as_str())).unwrap_or_else(|_| command.join(" "))
|
||||
}
|
||||
|
||||
@@ -14,11 +14,12 @@ use codex_common::create_config_summary_entries;
|
||||
|
||||
pub(crate) struct EventProcessorWithJsonOutput {
|
||||
last_message_path: Option<PathBuf>,
|
||||
had_error: bool,
|
||||
}
|
||||
|
||||
impl EventProcessorWithJsonOutput {
|
||||
pub fn new(last_message_path: Option<PathBuf>) -> Self {
|
||||
Self { last_message_path }
|
||||
Self { last_message_path, had_error: false }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +42,7 @@ impl EventProcessor for EventProcessorWithJsonOutput {
|
||||
|
||||
fn process_event(&mut self, event: Event) -> CodexStatus {
|
||||
match event.msg {
|
||||
EventMsg::Error(_) => { self.had_error = true; CodexStatus::Running }
|
||||
EventMsg::AgentMessageDelta(_) | EventMsg::AgentReasoningDelta(_) => {
|
||||
// Suppress streaming events in JSON mode.
|
||||
CodexStatus::Running
|
||||
@@ -60,4 +62,6 @@ impl EventProcessor for EventProcessorWithJsonOutput {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_code(&self) -> i32 { if self.had_error { 1 } else { 0 } }
|
||||
}
|
||||
|
||||
@@ -280,6 +280,8 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If any fatal Error events occurred, exit non‑zero so CI fails fast.
|
||||
let exit_code = event_processor.exit_code();
|
||||
if exit_code != 0 { std::process::exit(exit_code); }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user