Intent to contribute
I would like to contribute a small follow-up to #6256 / #6499 for successful streamed proxy responses. The existing issue and PR correctly address the error-response path; this report covers the separate success-stream branch in handleResponse.
Live reproduction
We encountered this while qualifying self-hosted Nango 0.70.7 (nangohq/nango-server@sha256:041a8e747266d0d6b6132ad022dfc377e5bf8c7a5b783f055bc70fe5f988ea49) as a credential broker for Runner:
- Deploy the pinned Nango server directly as a Cloud Run ingress container.
- Connect a real Google Calendar account through Nango OAuth.
- Call
GET /proxy/calendar/v3/calendars/primary/events?maxResults=1.
The same image, database, credentials, connection, and provider request returned Google's real 200 when reached directly, but Cloud Run returned an ingress-level 502 with reset reason: protocol error for every public Proxy call. Compression, decompression, and Accept-Encoding: identity variants did not change the result.
Putting Nginx in front of the same Nango container made the 200 path pass, showing that terminating and regenerating the HTTP hop removed the incompatibility. The same run independently reproduced #6256 on a Google 404; Nginx logged:
upstream sent "Content-Length" and "Transfer-Encoding" headers at the same time while reading response header from upstream
A bounded localhost response normalizer that removed framing and hop-by-hop headers then preserved provider 200, 404, 409, and 204 responses through the public Cloud Run ingress.
Success-path cause
For chunked or attachment responses, handleResponse currently copies every provider response header into res.writeHead(...). This forwards transport metadata from the provider-to-Nango hop, including Transfer-Encoding, Connection, and headers named by Connection. It also calls writeHead after starting the pipe.
Even when the copied framing happens to be accepted, these fields describe the previous HTTP connection and should be regenerated for the Nango-to-client hop.
Proposed change
- Strip hop-by-hop headers and headers named by
Connection from streamed success responses.
- Strip
Content-Length when the provider response was chunked or Axios decompressed the body.
- Preserve
Content-Length for an uncompressed attachment with a known length.
- Write the sanitized status and headers before piping the body directly to the response.
- Add a regression test covering conflicting framing, standard hop-by-hop headers, a
Connection-nominated header, and write-before-pipe ordering.
I have a focused implementation ready and will open it as a draft PR referencing this issue unless maintainers prefer that #6499 absorb the success-path change.
Intent to contribute
I would like to contribute a small follow-up to #6256 / #6499 for successful streamed proxy responses. The existing issue and PR correctly address the error-response path; this report covers the separate success-stream branch in
handleResponse.Live reproduction
We encountered this while qualifying self-hosted Nango
0.70.7(nangohq/nango-server@sha256:041a8e747266d0d6b6132ad022dfc377e5bf8c7a5b783f055bc70fe5f988ea49) as a credential broker for Runner:GET /proxy/calendar/v3/calendars/primary/events?maxResults=1.The same image, database, credentials, connection, and provider request returned Google's real
200when reached directly, but Cloud Run returned an ingress-level502withreset reason: protocol errorfor every public Proxy call. Compression, decompression, andAccept-Encoding: identityvariants did not change the result.Putting Nginx in front of the same Nango container made the
200path pass, showing that terminating and regenerating the HTTP hop removed the incompatibility. The same run independently reproduced #6256 on a Google404; Nginx logged:A bounded localhost response normalizer that removed framing and hop-by-hop headers then preserved provider
200,404,409, and204responses through the public Cloud Run ingress.Success-path cause
For chunked or attachment responses,
handleResponsecurrently copies every provider response header intores.writeHead(...). This forwards transport metadata from the provider-to-Nango hop, includingTransfer-Encoding,Connection, and headers named byConnection. It also callswriteHeadafter starting the pipe.Even when the copied framing happens to be accepted, these fields describe the previous HTTP connection and should be regenerated for the Nango-to-client hop.
Proposed change
Connectionfrom streamed success responses.Content-Lengthwhen the provider response was chunked or Axios decompressed the body.Content-Lengthfor an uncompressed attachment with a known length.Connection-nominated header, and write-before-pipe ordering.I have a focused implementation ready and will open it as a draft PR referencing this issue unless maintainers prefer that #6499 absorb the success-path change.