Description
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:
- 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)
}
}
- Initialize a
fiber
app and use theadaptor
middleware to add a route to it.
app := fiber.New()
app.Get("/", adaptor.HTTPHandler(http.HandlerFunc(handler)))
app.Listen(":3000")
- 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.