fix(util/http): default POST Content-Type to application/octet-stream#829
Conversation
DoHTTPRequest hardcoded Content-Type: application/json for any POST body. The only internal caller that sends a body is subtreevalidation.getMissingTransactionsBatch, which streams packed 32-byte tx hashes (binary). When that POST traverses a ModSecurity WAF in front of asset, the JSON body parser fails on binary data and Rule 200002 rejects the request with HTTP 400. Each rejection drops the source peer's reputation; with the WAF in place across the mainnet ingress the failure path eventually pushes peers below the selection threshold and starves catchup. Switch to application/octet-stream — matches every body shape this helper actually carries today. JSON callers do not exist; if one is added later it should set the header explicitly.
|
🤖 Claude Code Review Status: Complete Current Review: No issues found. The change is correct and well-justified:
|
|
Benchmark Comparison ReportBaseline: Current: Summary
All benchmark results (sec/op)
Threshold: >10% with p < 0.05 | Generated: 2026-05-07 18:23 UTC |
| if len(requestBody) > 0 && requestBody[0] != nil { | ||
| req.Body = io.NopCloser(bytes.NewReader(requestBody[0])) | ||
| req.Method = http.MethodPost | ||
| req.Header.Set("Content-Type", "application/json") |
…efs #4571) Add TestCheckBlockSubtrees_OversizedBody to cover the third peer-fetch call site at check_block_subtrees.go:218, mirroring the existing fetchSubtreeFromPeer and getSubtreeTxHashes coverage so all three peer- fetch paths fail closed on oversized responses. Align TestDoHTTPRequestBounded_POST content-type assertion with the application/octet-stream default introduced upstream by bsv-blockchain#829, surfaced after rebasing onto main.



Summary
util/http.go:DoHTTPRequesthardcodedContent-Type: application/jsonfor every POST request body. The only internal caller that sends a body via this helper issubtreevalidation.getMissingTransactionsBatch, which streams packed 32-byte tx hashes — binary data, never JSON.When that POST traverses a ModSecurity WAF (deployed in front of the mainnet asset ingress), the JSON request body parser is selected based on the header, fails on binary content, and Rule
200002(Failed to parse request body) denies the request with HTTP 400 + an HTML error page.The downstream effect is severe: each rejected
getMissingTransactionsBatchcall counts as a catchup failure against the source peer. Reputation math (successRate * 0.6 + baseScore * 0.4 - recentFailurePenalty) drives reputation toward 5.0 over time, eventually starving sync entirely. Observed in production onmainnet-eu-1peers (mainnet.gorillanode.io,mainnet2.gorillanode.io,bsva-ovh-teranode-eu-3).ModSecurity log excerpt:
Fix
Switch the default Content-Type from
application/jsontoapplication/octet-stream. This matches every body shape the helper actually carries today. There are no JSON-body callers — if one is added later it should set the header explicitly rather than relying on the helper default.Changes
util/http.go: change hardcodedContent-Typetoapplication/octet-stream, document the WAF interaction in a comment so future readers don't revert it.util/http_test.go: update the four POST test assertions that checked for the old header.Test plan
go test ./util/ -run 'TestDoHTTPRequest' -count=1— 29 passedgo vet ./util/...— cleangetMissingTransactionsBatchno longer 400s when traversing a ModSecurity-fronted ingressNotes
A complementary infra patch is being applied to the ModSecurity rule set on the affected ingress to add a path-scoped
requestBodyAccess=Offfor/api/v1/(subtree/{hash}/txs|tx|txs). That covers older peers still sending the wrong header until they upgrade past this PR.