fiber icon indicating copy to clipboard operation
fiber copied to clipboard

๐Ÿ”ฅ feat: Add StreamResponseBody support for the Client

Open Abhirup-99 opened this issue 4 months ago โ€ข 40 comments

This pull request introduces support for streaming HTTP response bodies in the client, allowing responses to be read as streams rather than being fully loaded into memory. This is particularly useful for handling large responses or server-sent events. The changes include new configuration options at both the client and request levels, implementation of the streaming logic, and comprehensive tests to ensure correct behavior.

Streaming response body support:

  • Added a streamResponseBody field to the Client struct, along with SetStreamResponseBody and StreamResponseBody methods to enable or disable response body streaming at the client level (client/client.go). [1] [2]
  • Added a streamResponseBody field to the Request struct, with corresponding SetStreamResponseBody and StreamResponseBody methods to allow per-request configuration that overrides the client setting (client/request.go). [1] [2]
  • Updated the request execution logic to set and restore the underlying HTTP client's streaming option based on the request or client configuration (client/core.go).
  • Implemented the BodyStream method on the Response struct to provide an io.Reader for streaming the response body, falling back to an in-memory reader if streaming is not enabled (client/response.go).

Testing and validation:

  • Added extensive tests to verify streaming behavior, including tests for server-sent events, large responses, default and overridden settings, and chainable configuration methods (client/client_test.go, client/response_test.go). [1] [2]
  • Ensured that request resetting clears the streamResponseBody override (client/request.go).# Description

Please provide a clear and concise description of the changes you've made and the problem they address. Include the purpose of the change, any relevant issues it solves, and the benefits it brings to the project. If this change introduces new features or adjustments, highlight them here.

Related #3425

Abhirup-99 avatar Aug 25 '25 13:08 Abhirup-99

Thanks for opening this pull request! ๐ŸŽ‰ Please check out our contributing guidelines. If you need help or want to chat with us, join us on Discord https://gofiber.io/discord

welcome[bot] avatar Aug 25 '25 13:08 welcome[bot]

[!NOTE]

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds response-body streaming support across client API, transport implementations, and response handling: new Client StreamResponseBody/SetStreamResponseBody, Response.BodyStream()/IsStreaming, transport propagation (standard/host/lb), Save() streaming paths, request BodyStream forwarding, core resource-safety fixes, tests, and docs.

Changes

Cohort / File(s) Summary
Client API
\client/client.go``
Adds StreamResponseBody() bool and SetStreamResponseBody(enable bool) *Client that proxy to the transport.
Response streaming
\client/response.go``
Adds BodyStream() io.Reader and IsStreaming() bool; updates Save() to use streaming (BodyStream()/io.Copy) when available and adjusts error paths.
Core request/response flow
\client/core.go``
Forwards request BodyStream into fasthttp (SetBodyStream with ContentLength); guards deferred ReleaseResponse with nil check; swaps RawResponse safely and releases previous response to avoid leaks.
Transport layer
\client/transport.go``
Extends httpClientTransport with StreamResponseBody() / SetStreamResponseBody(enable bool); implements on standardClientTransport, hostClientTransport, and lbClientTransport with traversal/propagation across nested hosts.
Tests โ€” response
\client/response_test.go``
Adds extensive streaming tests (BodyStream, IsStreaming, large/chunked/progressive streams, SSE, Save to writer/file, write/close error helpers).
Tests โ€” transport & client
\client/transport_test.go`, `client/client_test.go``
Adds tests for streaming flag default/enable/disable and propagation across standard, host, LB, nested, and mixed transports; client-level getter/setter tests.
Tests โ€” test stubs
\client/core_test.go``
Adds StreamResponseBody() and SetStreamResponseBody(_ bool) stubs on blockingErrTransport to satisfy new transport API in tests.
Tests โ€” ctx
\ctx_test.go``
Two tests updated to call app.Test(req, TestConfig{Timeout: 5 * time.Second}) to set per-call timeout.
Docs
\docs/client/rest.md``
Documents StreamResponseBody() and SetStreamResponseBody(stream bool) and adds SetRetryConfig doc; no code changes.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Client
    participant Transport
    participant Core
    participant fasthttp as "fasthttp.Client"
    participant Response

    User->>Client: SetStreamResponseBody(true)
    Client->>Transport: SetStreamResponseBody(true)
    Transport->>fasthttp: set internal streaming flag(s)

    User->>Client: Do(request)
    Client->>Core: executeRequest(...)
    Core->>fasthttp: Do()/DoDeadline(request)
    fasthttp-->>Core: *fasthttp.Response

    alt request contains BodyStream
        Core->>fasthttp: SetBodyStream(contentLen, reader)
    end

    Core-->>Client: build Response (wrap RawResponse)
    User->>Response: IsStreaming()
    Response-->>User: true/false

    alt streaming response
        User->>Response: BodyStream()
        Response-->>User: io.Reader (stream)
        User->>Response: Save(dest)
        Response->>Response: io.Copy(BodyStream -> dest)
    else non-streaming response
        User->>Response: Body()
        Response-->>User: []byte
        User->>Response: Save(dest)
        Response->>Response: io.Copy(bytes.Reader -> dest)
    end

Estimated code review effort

๐ŸŽฏ 4 (Complex) | โฑ๏ธ ~45 minutes

  • Focus areas:
    • client/transport.go: lbClientTransport traversal and propagation logic for nested balancing clients.
    • client/core.go: RawResponse swap, ReleaseResponse nil-guard, and BodyStream forwarding into fasthttp.
    • client/response.go: Save() streaming vs non-streaming branches and resource handling.
    • client/response_test.go & client/transport_test.go: many timing-sensitive streaming tests.

Possibly related issues

  • gofiber/fiber#3425 โ€” Matches the feature request to add fasthttp StreamResponseBody support and Response.BodyStream()/IsStreaming; this PR implements those APIs.

Possibly related PRs

  • gofiber/fiber#3774 โ€” Adds/extends transport abstractions that this change further extends with streaming getters/setters.
  • gofiber/fiber#3799 โ€” Implements BodyStream()/IsStreaming and streaming-aware flows; strongly related at the streaming API surface.

Suggested reviewers

  • sixcolors
  • gaby
  • ReneWerner87

Poem

๐Ÿ‡
I nibble bytes that trickle soft and slow,
I ferry streams where wandering packets go,
From client burrow to the distant shore,
My tiny hops keep data flowing more. ๐Ÿฅ•

Pre-merge checks and finishing touches

โŒ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage โš ๏ธ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
โœ… Passed checks (2 passed)
Check name Status Explanation
Title check โœ… Passed The title clearly summarizes the main feature: adding StreamResponseBody support to the Client. It is specific, concise, and directly reflects the primary change.
Description check โœ… Passed The description comprehensively covers the changes introduced, references related issues, explains the streaming support implementation, and documents testing efforts, aligning with template requirements.
โœจ Finishing touches
  • [ ] ๐Ÿ“ Generate docstrings
๐Ÿงช Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

โค๏ธ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Aug 25 '25 13:08 coderabbitai[bot]

I will update the documentation if the approach seems sound enough.

Abhirup-99 avatar Aug 25 '25 13:08 Abhirup-99

[!WARNING] Gemini encountered an error creating the review. You can try again by commenting /gemini review.

gemini-code-assist[bot] avatar Aug 25 '25 13:08 gemini-code-assist[bot]

@Abhirup-99 The tests are failing. You can run them locally by running make tests

gaby avatar Aug 25 '25 15:08 gaby

@gaby fixed the failing tests

Abhirup-99 avatar Aug 25 '25 17:08 Abhirup-99

Codecov Report

:x: Patch coverage is 96.42857% with 2 lines in your changes missing coverage. Please review. :white_check_mark: Project coverage is 91.67%. Comparing base (c235f85) to head (0dfe1c3).

Files with missing lines Patch % Lines
client/core.go 80.00% 1 Missing and 1 partial :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3711      +/-   ##
==========================================
+ Coverage   91.66%   91.67%   +0.01%     
==========================================
  Files         119      119              
  Lines       10096    10144      +48     
==========================================
+ Hits         9254     9300      +46     
- Misses        534      537       +3     
+ Partials      308      307       -1     
Flag Coverage ฮ”
unittests 91.67% <96.42%> (+0.01%) :arrow_up:

Flags with carried forward coverage won't be shown. Click here to find out more.

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

codecov[bot] avatar Aug 25 '25 17:08 codecov[bot]

@Abhirup-99 To fix the alignment you can run make betteralign it will fix it for you

gaby avatar Aug 25 '25 21:08 gaby

@gaby @Abhirup-99 hi, I have some questions.

I don't think the current tests cover streaming response scenarios. For example, Test_Client_StreamResponseBody_ServerSentEvents only reads from bodyStream once and asserts it contains the first message โ€” it doesn't verify that subsequent messages arrive incrementally, nor can it detect mid-stream disconnects or cases where the server writes and returns data progressively. In Test_Request_StreamResponseBody the server immediately returns the full string (c.SendString("Hello, World!")) with no chunked or delayed writes, so it can't validate chunked/streaming delivery or on-demand reads โ€” the client, even in streaming mode, would read the whole response at once.

JIeJaitt avatar Aug 26 '25 05:08 JIeJaitt

@JIeJaitt Correct, the tests are not testing streaming, the server needs to stream the resp

gaby avatar Aug 26 '25 09:08 gaby

@codex review

gaby avatar Aug 27 '25 20:08 gaby

@gaby have updated the code.

Abhirup-99 avatar Sep 02 '25 10:09 Abhirup-99

@Abhirup-99

ReneWerner87 avatar Sep 03 '25 10:09 ReneWerner87

LGTM

JIeJaitt avatar Sep 04 '25 02:09 JIeJaitt

/gemini review

gaby avatar Sep 04 '25 07:09 gaby

Increased test coverage and added documentation. @gaby @ReneWerner87

Abhirup-99 avatar Sep 04 '25 10:09 Abhirup-99

@gaby @ReneWerner87 bump

Abhirup-99 avatar Sep 09 '25 21:09 Abhirup-99

We will check tomorrow(sickness weekend)

ReneWerner87 avatar Sep 09 '25 21:09 ReneWerner87

@Abhirup-99 can you check the linting and the last hints

ReneWerner87 avatar Sep 10 '25 08:09 ReneWerner87

@ReneWerner87 updated the code. Also, can I work on #3731 ? Checking lint is very difficult because make lint seems to fail for go 1.25.

Abhirup-99 avatar Sep 11 '25 22:09 Abhirup-99

@Abhirup-99 How exactly is the server streaming a response back to the client during the tests?

The creation of a BodyStream by the client is masking this

gaby avatar Sep 12 '25 14:09 gaby

@Abhirup-99 How exactly is the server streaming a response back to the client during the tests?

The creation of a BodyStream by the client is masking this

Fixed this

Abhirup-99 avatar Sep 24 '25 17:09 Abhirup-99

@gaby can you check this out?

Abhirup-99 avatar Oct 04 '25 18:10 Abhirup-99

@codex review

gaby avatar Oct 30 '25 12:10 gaby

PR #3830 got merged, and seems to start some work on refactoring the core execution logic.

@Abhirup-99 Can you merge the most recent changes to this PR and retry the Stream Writer change I requested some time ago?

I still think the CopyToResponse method from fasthttp still doesn't copy the stream body in this case, but I think we can start experimenting with how to add this via fasthttp or Fiber if we merge the most recent updates to this PR.

grivera64 avatar Nov 01 '25 23:11 grivera64

@gaby @griver please review now.

Abhirup-99 avatar Nov 03 '25 19:11 Abhirup-99

@grivera64 done

Abhirup-99 avatar Nov 04 '25 16:11 Abhirup-99

@gaby can you check this out?

Abhirup-99 avatar Nov 05 '25 17:11 Abhirup-99

@Abhirup-99 Can you take a look at the coverage report from the codecov workflow? I think a few of the low coverage lines can be covered with a few simple unit tests.

grivera64 avatar Nov 08 '25 03:11 grivera64

@grivera64 can you check now?

Abhirup-99 avatar Nov 09 '25 08:11 Abhirup-99