fix(http_request): drop "2>&1" on Windows — CreateProcessW has no shell

POSIX popen routes via /bin/sh -c, so "2>&1" is a shell redirect. On
Windows we use CreateProcessW directly (no shell): curl receives "2>&1"
as a positional arg, treats it as a second URL, and fails with exit 3
"URL rejected: Bad hostname".

Stderr is already merged into the same pipe via STARTUPINFOW.hStdError
on Windows, so the redirect is also unnecessary there. Guard with
#ifndef _WIN32.

Also adds FN_HTTP_DEBUG env var to dump the cmdline + req.url for
future bug triage (zero-cost when unset).

Detected via agents_dashboard.exe --connect-test against
https://agents.organic-machine.com — same .exe with the fix now returns
"OK 11" in <2s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-22 22:15:37 +02:00
parent cc1e88fe55
commit e387c91b4c
+15 -2
View File
@@ -269,8 +269,21 @@ Response request(const Request& req) {
}
cmd << ' ' << sh_q(req.url)
<< " -o " << sh_q(tmp_body_out)
<< " 2>&1";
<< " -o " << sh_q(tmp_body_out);
// On POSIX we go through /bin/sh -c via popen, so `2>&1` is a shell redirect.
// On Windows we use CreateProcessW (no shell): `2>&1` would be passed as an
// extra positional arg to curl, which treats it as a second URL → "Bad
// hostname" (exit 3). stderr is already merged via STARTUPINFOW.hStdError.
#ifndef _WIN32
cmd << " 2>&1";
#endif
if (std::getenv("FN_HTTP_DEBUG")) {
fprintf(stderr, "[fn_http debug] cmdline: %s\n", cmd.str().c_str());
fprintf(stderr, "[fn_http debug] req.url=[%s] len=%zu\n",
req.url.c_str(), req.url.size());
}
// Capture stderr (curl prints transport errors to stderr with -sS).
std::string curl_stderr;