What an adversarial review caught when checked against real httpkit source + GCS docs · a production incident
← Back to Lesson 4 ·
Reference →
| # | Claim | Verdict |
|---|---|---|
| 1 | Authorization/OTel headers would conflict with GCS validation | WRONG |
| 2 | GCS validates field presence/values, not order | UNVERIFIED |
| 3 | MIME type must match FormFields["content-type"] | CONFIRMED |
| 4 | Filename from path.Base(FormFields["key"]) | CONFIRMED |
| 5 | limit+1 fixes an edge-case bug in the standard path | CONFIRMED |
| 6 | GCS returns 204/empty body on success | WRONG |
| 7 | httpkit Post() does not buffer the pipe body | CONFIRMED |
Lesson 4 said:
"Adding an Authorization header on top would conflict with GCS's own auth validation."
This is false on two counts, both verifiable in the actual httpkit source.
handleOptions in httpkit/request/v2/request.go (in the wrapper source) only sets
headers the caller explicitly passed via WithHeader:
// the wrapper source func handleOptions(req *http.Request, opts *options) { if len(opts.headers) > 0 { req.Header = opts.headers // only what the caller passed } if opts.username != "" || opts.password != "" { req.SetBasicAuth(opts.username, opts.password) } }
No Authorization header would ever appear on a GCS request unless we explicitly sent one. The "conflict" scenario can't even arise.
// the wrapper source — runs on every request otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header)) // adds: traceparent, tracestate
GCS ignores unknown request headers — traceparent and tracestate
are simply not part of its signed policy, so they pass through harmlessly. There is no
rejection, no 400, no conflict.
http.DefaultClient is used for
minimal, explicit header control — sending exactly the headers we specify
with no framework-injected OTel tracing noise on a third-party (Google) endpoint. That is a
cleanliness/correctness-of-intent choice, not a "GCS will reject it otherwise" requirement.
Lesson 4 said:
"GCS pre-signed POST returns HTTP 204 or 200 with an empty body on success."
The real GCS behaviour, from the docs, depends on the success_action_status
form field:
success_action_status | Response |
|---|---|
| absent (default) | 204 No Content, empty body |
200 | 200 OK, empty body |
201 | 201 Created, XML body — Bucket, Key, ETag, Location |
success_action_redirect set | 303 redirect to that URL |
Our code discards the body unconditionally:
// download_and_upload_reserved.go io.Copy(io.Discard, resp.Body) return nil
FormFields we receive contain only key, policy,
x-goog-date, content-type, x-goog-algorithm,
x-goog-signature, x-goog-credential. No
success_action_status → GCS defaults to 204 → discarding the body is correct.
But if upload-svc's URL generator ever adds success_action_status=201,
we would silently throw away returned metadata. The code is correct by coincidence of the
current policy shape, not by design.
Five claims survived. Two are worth re-stating because the review strengthened the evidence behind them:
io.LimitReader(r, n) returns exactly n bytes when the underlying
reader holds exactly n bytes, then EOF. So in the standard path:
n, _ := io.Copy(fw, io.LimitReader(body, limit))
if n == limit { /* reject */ } // a file EXACTLY limit bytes is wrongly rejected
The reserved path's limit+1 with n > limit is the correct
formulation. This is a genuine latent bug in DownloadAndUploadFile, confirmed
not just by reading our code but by the documented semantics of io.LimitReader.
The GCS docs explicitly require the file field to be last. They do not
document whether the order of the other (non-file) fields matters. Our code relies on Go's
randomized map iteration for those fields and assumes order-independence. No evidence
contradicts this in practice, but it is an assumption, not a documented guarantee. Flagging
it as "safe because documented" (as Lesson 4 implied) overstated the certainty.
handleOptions, reading
the GCS response-code docs. When you write down a justification for a design decision, the
honest follow-up question is always: what specific line of code or paragraph of docs
proves this? If you can't cite it, mark it as an assumption.