Skip to content

🐛 [Bug]: Adaptor does not flush when an net/http handler flushes #3421

Open
@grivera64

Description

@grivera64

Bug Description

The current way the adaptor middleware works, is that it will complete the net/http handler first, then write to a fasthttp response object and return. This makes it incompatible with net/http handlers that may rely on instantly flushing to the client (e.g. Live Data Streams, SSE events).

(Sort of) related to issue #3386.

How to Reproduce

Steps to reproduce the behavior:

  1. Create a net/http handler
func handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.Header().Set("Cache-Control", "no-cache")

	bw := bufio.NewWriter(w)

	for i := 0; i < 10; i++ {
		msg := fmt.Sprintf("Line %d\n", i)
		if _, err := bw.WriteString(msg); err != nil {
			fmt.Println("Error writing:", err)
			return
		}
		if err = bw.Flush(); err != nil {
			fmt.Println("Error flushing:", err)
			return
		}
		if flusher, ok := w.(http.Flusher); ok {
			flusher.Flush()
		}
		time.Sleep(1 * time.Second)
	}
}
  1. Initialize a fiber app and use the adaptor middleware to add a route to it.
app := fiber.New()
app.Get("/", adaptor.HTTPHandler(http.HandlerFunc(handler)))
app.Listen(":3000")
  1. Use curl to try to get a response.
curl localhost:3000

At this step, curl will block for 10 seconds before sending the full response, despite the Flush() calls used in the net/http handler.

Expected Behavior

The Adaptor middleware should have the response flush if the net/http handler defines such behavior. The behavior of w.Flush() and flusher.Flush() should mimic the usage of c.SendStreamWriter(streamWriter) if necessary to emit the correct response.

An issue with this is that it is hard to know when a net/http handler is flushing to the client, and treating all requests in this matter would incur a performance cost with flushing to the client on every write (Express does this behavior by default).

While I wouldn't recommend to do this for all requests since fasthttp tries to avoid flushing as much as it can, I would say it is worth having a way to instantly flush data if the net/http handler depends on this behavior.

Fiber Version

v3.0.0-beta.4

Code Snippet (optional)

package main

import (
  "log"

  "github.com/gofiber/fiber/v3"
  "github.com/gofiber/fiber/v3/middleware/adaptor"
)

func main() {
  app := fiber.New()

  // Steps to reproduce
  app.Get("/", adaptor.HTTPHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    w.Header().Set("Cache-Control", "no-cache")

    bw := bufio.NewWriter(w)
    defer bw.Flush()

    for i := 0; ; i++ {
      msg := fmt.Sprintf("Line %d\n", i)
      _, err := bw.WriteString(msg)
      if err != nil {
        fmt.Println("Error writing:", err)
        return
      }
      err = bw.Flush()
      if err != nil {
        fmt.Println("Error flushing:", err)
        return
      }
      if flusher, ok := w.(http.Flusher); ok {
        flusher.Flush()
      }
      time.Sleep(1 * time.Second)
    }
  }))

  log.Fatal(app.Listen(":3000"))
}

Checklist:

  • I agree to follow Fiber's Code of Conduct.
  • I have checked for existing issues that describe my problem prior to opening this one.
  • I understand that improperly formatted bug reports may be closed without explanation.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions