Skip to content

Commit 6b79f71

Browse files
chopperbrianoclaude
andcommitted
CRITICAL: WinHTTP POST missing Content-Type header (PSP payment fix)
Linux libcurl automatically sets Content-Type: application/x-www-form-urlencoded when CURLOPT_POSTFIELDS is used. Our WinHTTP stub did not. The PSP keepalive endpoint at ipfs.digiassetx.com is an Express server using body-parser, which SILENTLY DROPS the request body when no Content-Type header is present. So our keepalive POSTs were arriving with empty bodies — no address, no peerId, no secret. The server saw an empty request and returned "unsubscribe failed will time out anyways" (its generic error for malformed/empty registrations). Linux users with libcurl get paid because their POSTs include the auto-set Content-Type header. Windows users on this port got nothing because our minimal WinHTTP stub didn't replicate this behavior. Fix: in CurlHandler::post, add Content-Type: application/x-www-form-urlencoded automatically when posting form data, matching libcurl's behavior. Skip if user already set a Content-Type header explicitly. This affects ALL HTTP POSTs from the Windows build, not just PSP. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 239be65 commit 6b79f71

2 files changed

Lines changed: 19 additions & 4 deletions

File tree

src/ConsoleDashboard.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,13 +380,13 @@ void ConsoleDashboard::render() {
380380
if (_pspStatus.find("reachable") != std::string::npos &&
381381
_pspStatus.find("unreachable") == std::string::npos) {
382382
out << FG_GREEN << _pspStatus << RESET;
383-
if (_pspNodeCount > 0) {
384-
out << " Network: " << FG_BRIGHT_WHITE << _pspNodeCount
385-
<< RESET << DIM << " nodes online" << RESET;
386-
}
387383
} else {
388384
out << FG_YELLOW << _pspStatus << RESET;
389385
}
386+
if (_pspNodeCount > 0) {
387+
out << " Network: " << FG_BRIGHT_WHITE << _pspNodeCount
388+
<< RESET << DIM << " nodes online" << RESET;
389+
}
390390
out << "\n"; totalRows++;
391391
}
392392

src/curl_stubs.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,21 @@ CURLcode curl_easy_perform(CURL* easy_handle) {
264264
extraHeaders += utf8ToWide(encoded);
265265
extraHeaders += L"\r\n";
266266
}
267+
// libcurl automatically sets Content-Type for POSTFIELDS — match that behavior.
268+
// Without this, servers like Express body-parser silently drop the body.
269+
if (h->isPost && !h->postFields.empty()) {
270+
// Only add if user hasn't already provided one
271+
bool hasContentType = false;
272+
for (curl_slist* sl = h->headers; sl; sl = sl->next) {
273+
std::string hdr(sl->data);
274+
std::string lower;
275+
for (char c : hdr) lower += (char)tolower(c);
276+
if (lower.find("content-type:") == 0) { hasContentType = true; break; }
277+
}
278+
if (!hasContentType) {
279+
extraHeaders += L"Content-Type: application/x-www-form-urlencoded\r\n";
280+
}
281+
}
267282

268283
const void* postData = h->isPost ? h->postFields.c_str() : nullptr;
269284
DWORD postDataLen = h->isPost ? (DWORD)h->postFields.size() : 0;

0 commit comments

Comments
 (0)