Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔥 feat: Add End() method to Ctx #3280

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

grivera64
Copy link
Member

Description

This PR adds the End() method to Ctx in v3 to allow instant flushing and closing of an active connection.

This feature is useful for stopping middleware from controlling the connection after a handler further up the method chain. Some middleware (like fiberprometheus) will work with a response after calling c.Next(). In addition, Fiber's default error handler may also write to the response, while a user may want to handle an error in different ways from within specific handlers and only log detailed errors internally.

It also can be used as an alternative to simply using return nil at the end of a handler, as done in ExpressJS (see below).

Fixes #3276

Changes introduced

List the new features or adjustments introduced in this pull request. Provide details on benchmarks, documentation updates, changelog entries, and if applicable, the migration guide.

  • Benchmarks: Describe any performance benchmarks and improvements related to the changes.
  • Documentation Update: Detail the updates made to the documentation and links to the changed files.
  • Changelog/What's New: Include a summary of the additions for the upcoming release notes.
  • Migration Guide: If necessary, provide a guide or steps for users to migrate their existing code to accommodate these changes.
  • API Alignment with Express: Explain how the changes align with the Express API.
    • Adds res.end() functionality
  • API Longevity: Discuss the steps taken to ensure that the new or updated APIs are consistent and not prone to breaking changes.
  • Examples: Provide examples demonstrating the new features or changes in action.

Type of change

  • New feature (non-breaking change which adds functionality)
  • Documentation update (changes to documentation)

Checklist

Before you submit your pull request, please make sure you meet these requirements:

  • Followed the inspiration of the Express.js framework for new functionalities, making them similar in usage.
  • Conducted a self-review of the code and provided comments for complex or critical parts.
  • Updated the documentation in the /docs/ directory for Fiber's documentation.
  • Added or updated unit tests to validate the effectiveness of the changes or new features.
  • Ensured that new and existing unit tests pass locally with the changes.
  • Verified that any new dependencies are essential and have been agreed upon by the maintainers/community.
  • Aimed for optimal performance with minimal allocations in the new code.
  • Provided benchmarks for the new code to analyze and improve upon.

Commit formatting

Please use emojis in commit messages for an easy way to identify the purpose or intention of a commit. Check out the emoji cheatsheet here: CONTRIBUTING.md

@grivera64 grivera64 requested a review from a team as a code owner January 13, 2025 18:02
@grivera64 grivera64 requested review from gaby, sixcolors, ReneWerner87 and efectn and removed request for a team January 13, 2025 18:02
Copy link
Contributor

coderabbitai bot commented Jan 13, 2025

Walkthrough

The pull request introduces a new End() method to the DefaultCtx struct in the Fiber web framework. This method provides a mechanism to immediately flush the current HTTP response and close the underlying connection. It enhances the context's functionality by allowing developers to explicitly terminate a connection after sending a response, preventing further middleware interactions or modifications to the response.

Changes

File Changes
ctx.go Added End() method to DefaultCtx struct to flush and close connection
ctx_interface_gen.go Updated interface to include new End() method
ctx_test.go Added four new test functions to validate End() method behavior
docs/api/ctx.md Updated documentation to describe new End() method
docs/whats_new.md Documented End() method as part of Fiber v3 changes

Assessment against linked issues

Objective Addressed Explanation
Add End() method to Ctx
Provide mechanism to flush and close connection
Prevent middleware from controlling connection after response

Possibly related PRs

Suggested reviewers

  • sixcolors
  • gaby
  • efectn
  • ReneWerner87

Poem

🐰 A Rabbit's Ode to Connection's End 🌐
With End() method, swift and bright,
Connections close, response takes flight
No middleware can now intrude
Our Fiber's dance, precisely hued!

Hop hop, connection closed! 🚀

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Jan 13, 2025

Codecov Report

Attention: Patch coverage is 75.00000% with 3 lines in your changes missing coverage. Please review.

Project coverage is 84.14%. Comparing base (44b971a) to head (a1dced9).

Files with missing lines Patch % Lines
ctx.go 75.00% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3280      +/-   ##
==========================================
+ Coverage   84.08%   84.14%   +0.05%     
==========================================
  Files         116      116              
  Lines       11541    11553      +12     
==========================================
+ Hits         9704     9721      +17     
+ Misses       1406     1402       -4     
+ Partials      431      430       -1     
Flag Coverage Δ
unittests 84.14% <75.00%> (+0.05%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@grivera64 grivera64 marked this pull request as draft January 13, 2025 18:09
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (6)
ctx.go (1)

1989-2005: LGTM! Clean implementation with proper buffering.

The implementation correctly uses buffered writing and handles the response flushing sequence.

Consider wrapping errors with more context for better debugging:

 func (c *DefaultCtx) End() error {
     ctx := c.RequestCtx()
     conn := ctx.Conn()

     bw := bufio.NewWriter(conn)
     if err := ctx.Response.Write(bw); err != nil {
-        return err
+        return fmt.Errorf("failed to write response: %w", err)
     }

     if err := bw.Flush(); err != nil {
-        return err //nolint:wrapcheck // unnecessary to wrap it
+        return fmt.Errorf("failed to flush response: %w", err)
     }

-    return conn.Close() //nolint:wrapcheck // unnecessary to wrap it
+    if err := conn.Close(); err != nil {
+        return fmt.Errorf("failed to close connection: %w", err)
+    }
+    return nil
 }
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 1997-1998: ctx.go#L1997-L1998
Added lines #L1997 - L1998 were not covered by tests

ctx_test.go (1)

5934-5994: Add test coverage for additional scenarios.

The current tests cover basic functionality, but consider adding tests for:

  1. Error cases (e.g., when Write/Flush/Close fails)
  2. Concurrent End() calls
  3. End() with different response types (JSON, streaming, etc.)
  4. End() with various middleware combinations

Would you like me to help generate these additional test cases?

🧰 Tools
🪛 golangci-lint (1.62.2)

5939-5939: Error return value of c.SendString is not checked

(errcheck)


5946-5946: expected-actual: need to reverse actual and expected values

(testifylint)


5949-5949: expected-actual: need to reverse actual and expected values

(testifylint)


5959-5959: Error return value of c.Next is not checked

(errcheck)


5965-5965: Error return value of c.SendStatus is not checked

(errcheck)


5972-5972: expected-actual: need to reverse actual and expected values

(testifylint)

🪛 GitHub Check: lint

[failure] 5939-5939:
Error return value of c.SendString is not checked (errcheck)


[failure] 5946-5946:
expected-actual: need to reverse actual and expected values (testifylint)


[failure] 5949-5949:
expected-actual: need to reverse actual and expected values (testifylint)


[failure] 5959-5959:
Error return value of c.Next is not checked (errcheck)


[failure] 5965-5965:
Error return value of c.SendStatus is not checked (errcheck)


[failure] 5972-5972:
expected-actual: need to reverse actual and expected values (testifylint)

🪛 GitHub Actions: golangci-lint

[error] 5939-5939: Error return value of c.SendString is not checked (errcheck)

docs/whats_new.md (2)

407-420: Enhance the documentation with additional details.

While the documentation provides a good overview, consider adding:

  1. A note about error handling when the connection is already closed
  2. Clarification that End() returns an error
  3. Mention that this is particularly useful for scenarios involving streaming responses
 ### End
 
 In v3, we introduced a new method to match the Express.js API's `res.end()` method.
 
 ```go
-func (c Ctx) End()
+func (c Ctx) End() error

With this method, you can:

  • Stop middleware from controlling the connection after a handler further up the method chain
    by immediately flushing the current response and closing the connection.
  • Use return c.End() as an alternative to return nil
    +- Handle streaming responses by explicitly closing the connection

+Note: If the connection is already closed, End() will return an error.


---

`421-440`: **Improve the example to demonstrate error handling.**

The example should show proper error handling and a more realistic use case.

```diff
 app.Use(func (c fiber.Ctx) error {
   err := c.Next()
   if err != nil {
     log.Println("Got error: %v", err)
-    return c.SendString(err.Error()) // Will be unsuccessful since the response ended below
+    // The response has already ended, so this error should be logged but not sent
+    log.Printf("Cannot send error to client: %v", err)
+    return nil
   }
   return nil
 })

 app.Get("/hello", func (c fiber.Ctx) error {
   query := c.Query("name", "")
   if query == "" {
-    c.SendString("You don't have a name?")
-    c.End() // Closes the underlying connection
-    return errors.New("No name provided")
+    if err := c.SendString("You don't have a name?"); err != nil {
+      return err
+    }
+    if err := c.End(); err != nil {
+      return fmt.Errorf("failed to end connection: %v", err)
+    }
+    return errors.New("no name provided")
   }
   return c.SendString("Hello, " + query + "!")
 })
docs/api/ctx.md (2)

495-500: Add error handling to the example.

The basic example should demonstrate proper error handling.

 ```go title="Example"
 app.Get("/", func(c fiber.Ctx) error {
-  c.SendString("Hello World!")
-  return c.End()
+  if err := c.SendString("Hello World!"); err != nil {
+    return err
+  }
+  return c.End()
 })

---

`509-534`: **Enhance the middleware example with better practices.**

The example should demonstrate better error handling and logging practices.

```diff
 ```go title="Example"
 // Error Logging/Responding middleware
 app.Use(func(c fiber.Ctx) error {
   err := c.Next()
   
   // Log errors & write the error to the response
   if err != nil {
-    log.Printf("Got error in middleware: %v", err)
-    return c.Writef("(got error %v)", err)
+    // Log the error with context
+    log.Printf("Error in %s %s: %v", c.Method(), c.Path(), err)
+    // The response might have already ended
+    if writeErr := c.Writef("(got error %v)", err); writeErr != nil {
+      log.Printf("Failed to write error response: %v", writeErr)
+    }
+    return err
   }

   // No errors occurred
   return nil
 })

 // Handler with simulated error
 app.Get("/", func(c fiber.Ctx) error {
   // Closes the connection instantly after writing from this handler
   // and disallow further modification of its response
-  defer c.End()
+  defer func() {
+    if err := c.End(); err != nil {
+      log.Printf("Failed to end connection: %v", err)
+    }
+  }()

   c.SendString("Hello, ... I forgot what comes next!")
   return errors.New("some error")
 })

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 4e5fea1d7a830a52cbe736d2da88660a2e959549 and a901e8ff19e43243d5188d721dad10dc7024f431.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `ctx.go` (1 hunks)
* `ctx_interface_gen.go` (2 hunks)
* `ctx_test.go` (1 hunks)
* `docs/api/ctx.md` (1 hunks)
* `docs/whats_new.md` (2 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Check: codecov/patch</summary>

<details>
<summary>ctx.go</summary>

[warning] 1997-1998: ctx.go#L1997-L1998
Added lines #L1997 - L1998 were not covered by tests

</details>

</details>
<details>
<summary>🪛 golangci-lint (1.62.2)</summary>

<details>
<summary>ctx_test.go</summary>

5939-5939: Error return value of `c.SendString` is not checked

(errcheck)

---

5946-5946: expected-actual: need to reverse actual and expected values

(testifylint)

---

5949-5949: expected-actual: need to reverse actual and expected values

(testifylint)

---

5959-5959: Error return value of `c.Next` is not checked

(errcheck)

---

5965-5965: Error return value of `c.SendStatus` is not checked

(errcheck)

---

5972-5972: expected-actual: need to reverse actual and expected values

(testifylint)

</details>

</details>
<details>
<summary>🪛 GitHub Check: lint</summary>

<details>
<summary>ctx_test.go</summary>

[failure] 5939-5939: 
Error return value of `c.SendString` is not checked (errcheck)

---

[failure] 5946-5946: 
expected-actual: need to reverse actual and expected values (testifylint)

---

[failure] 5949-5949: 
expected-actual: need to reverse actual and expected values (testifylint)

---

[failure] 5959-5959: 
Error return value of `c.Next` is not checked (errcheck)

---

[failure] 5965-5965: 
Error return value of `c.SendStatus` is not checked (errcheck)

---

[failure] 5972-5972: 
expected-actual: need to reverse actual and expected values (testifylint)

</details>

</details>
<details>
<summary>🪛 GitHub Actions: golangci-lint</summary>

<details>
<summary>ctx_test.go</summary>

[error] 5939-5939: Error return value of `c.SendString` is not checked (errcheck)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: Compare
* GitHub Check: repeated

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>ctx_interface_gen.go (1)</summary>

`357-358`: **LGTM! Clean interface addition.**

The new `End()` method is well documented and follows the existing interface pattern.

</details>
<details>
<summary>docs/whats_new.md (1)</summary>

`344-344`: **LGTM! Clear and concise introduction of the End method.**

The documentation correctly introduces the `End` method as a new feature in v3, aligning with Express.js API.

</details>
<details>
<summary>docs/api/ctx.md (2)</summary>

`487-494`: **LGTM! Clear method signature and description.**

The API documentation correctly shows the method signature and provides a clear description of its purpose.

---

`502-508`: **LGTM! Important caution about connection closure.**

The caution note clearly warns users about the implications of calling `c.End()`.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

ctx_test.go Outdated Show resolved Hide resolved
ctx_test.go Outdated Show resolved Hide resolved
@grivera64
Copy link
Member Author

There appears to be a decrease in test coverage due to using fashttp's ctx.Response.Write() method error checking. From the looks of it, this is because there is no checking whether or not errTestConnClosed is returned as an error here.

Should we consider adding a TestConfig entry (i.e. FailOnDisconnect bool) for this so that this error is covered by a test?

@gaby
Copy link
Member

gaby commented Jan 14, 2025

@grivera64 I think it's fine as it is.

@grivera64
Copy link
Member Author

@grivera64 I think it's fine as it is.

For sure, I'll mark the PR as ready to review again.

@grivera64 grivera64 marked this pull request as ready for review January 14, 2025 02:18
Copy link
Member

@gaby gaby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small things

docs/whats_new.md Outdated Show resolved Hide resolved
docs/api/ctx.md Outdated Show resolved Hide resolved
ctx.go Show resolved Hide resolved
ctx_test.go Outdated Show resolved Hide resolved
Copy link
Member

@gaby gaby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small things

@gaby gaby changed the title 🔥 Feature(v3): Add End() method to Ctx 🔥 feat: Add End() method to Ctx Jan 14, 2025
@gaby gaby added this to the v3 milestone Jan 14, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
ctx_test.go (1)

5952-5965: Make timeout test more deterministic.

The test uses a hardcoded sleep duration which could be flaky in CI environments. Consider using a context with timeout or a more deterministic mechanism to test timeout behavior.

app.Get("/", func(c Ctx) error {
-    time.Sleep(2 * time.Second)
+    ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
+    defer cancel()
+    <-ctx.Done()
    return c.End()
})
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5747064 and 1d1689f.

📒 Files selected for processing (3)
  • ctx_test.go (1 hunks)
  • docs/api/ctx.md (1 hunks)
  • docs/whats_new.md (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/api/ctx.md
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: unit (1.23.x, windows-latest)
  • GitHub Check: Compare
  • GitHub Check: repeated
🔇 Additional comments (3)
docs/whats_new.md (1)

Line range hint 1-1000: Ensure consistent indentation in code examples.

All code examples should use 4 spaces for indentation to maintain consistency throughout the documentation.

ctx_test.go (2)

5967-5988: 🛠️ Refactor suggestion

Add error handling for SendStatus and Next calls.

The test should check errors returned by c.SendStatus() and c.Next() to ensure proper error propagation.

app.Use(func(c Ctx) error {
-    c.Next() //nolint:errcheck // unnecessary to check error
+    if err := c.Next(); err != nil {
+        return err
+    }
    return c.Drop()
})

app.Get("/", func(c Ctx) error {
-    c.SendStatus(StatusOK) //nolint:errcheck // unnecessary to check error
+    if err := c.SendStatus(StatusOK); err != nil {
+        return err
+    }
    return c.End()
})

Likely invalid or redundant comment.


5934-5950: 🛠️ Refactor suggestion

Add error handling for SendString call.

The test should check the error returned by c.SendString() to ensure the response was set correctly before calling End().

app.Get("/", func(c Ctx) error {
-    c.SendString("Hello, World!") //nolint:errcheck // unnecessary to check error
+    if err := c.SendString("Hello, World!"); err != nil {
+        return err
+    }
    return c.End()
})

Likely invalid or redundant comment.

docs/whats_new.md Show resolved Hide resolved
ctx_test.go Show resolved Hide resolved
Copy link
Member

@gaby gaby left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

📝 [Proposal v3]: Add End() method to Ctx
2 participants