Skip to content

Custom ResponseWriter not used by echo.WrapMiddleware #1257

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

Closed
Daniel-Houston opened this issue Jan 15, 2019 · 3 comments · Fixed by #1341
Closed

Custom ResponseWriter not used by echo.WrapMiddleware #1257

Daniel-Houston opened this issue Jan 15, 2019 · 3 comments · Fixed by #1341
Assignees

Comments

@Daniel-Houston
Copy link

Daniel-Houston commented Jan 15, 2019

Issue Description

I want to wrap all response writers for my handlers with a custom response writer that also records the status code.

I realize that echo's Response struct records the status code, but I want to write my middleware to target the standard http library so it is usable by multiple muxers, as such I do not want to use the echo.Context or echo.Context.Response, or the Response.Before method in my middleware.

My custom writer looks like this:

type statusRecorder struct {                                                                                             
    http.ResponseWriter                                                                                       
    statusCode int                                                                                                       
}                                                                                                                        
                                                                                                                         
func (rec *statusRecorder) WriteHeader(code int) {
    rec.statusCode = code
    rec.WriteHeader(code)
}

I am using it in some middleware as follows:

func Middleware(handler http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
        writer := statusRecorder{w, 0}
        handler.ServeHTTP(&writer, req)
        if writer.statusCode == 0 {
            log.Errorf("Expected status code to be set but it wasn't")
        }
    })
}

and then wrapping that middleware with echo.WrapMiddleware:

func main() {
    e := echo.New()
    e.GET("/endpoint", handler, echo.WrapMiddleware(Middleware))
}

func handler(c echo.Context) error {
    return c.String(http.StatusOK, "Hello, World!")
}

However, when I make a request to "/endpoint", I get the error message "Expected status code to be set but it wasn't".

I believe this is because echo's WrapMiddleware function does not use the response writer provided by the middleware. See lines 728-731 of echo.go:

echo/echo.go

Lines 728 to 731 in 7867fce

m(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
c.SetRequest(r)
err = next(c)
})).ServeHTTP(c.Response(), c.Request())

c.SetRequest() is called but there is no call to set the response.

Would something like this work?

c.response = echo.NewResponse(w, c.Echo())

I would be happy to submit a patch that also sets the response writer if that is the desired solution.

If this is not at viable solution, please help me know how to best go about doing this

Checklist

  • [ x] Dependencies installed
  • [x ] No typos
  • [ x] Searched existing issues and docs

Expected behaviour

I would expect echo to use the wrapped ResponseWriter when writing http responses through something like c.String(). I would expect the statusCode field in my response writer to be set.

Actual behaviour

the statusCode field is not set.

Steps to reproduce

Run the code below, make an http request to http://localhost:8080/endpoint and check the log message.

Working code to debug

package main
                                                                                                                           
import (                                                                                                                           
    "net/http"                                                                                                                     
                                                                                                                                   
    "github.com/labstack/echo"                                                                                                     
    "github.com/labstack/gommon/log"                                                                                               
)                                                                                                                                  
                                                                                                                                   
type statusRecorder struct {                                                                                                       
    http.ResponseWriter                                                                                                            
    statusCode int                                                                                                                 
}                                                                                                                                  
                                                                                                                                   
func (rec *statusRecorder) WriteHeader(code int) {                                                                                 
    rec.statusCode = code                                                                                                          
    rec.WriteHeader(code)                                                                                                          
}                                                                                                                                  
                                                                                                                                   
func main() {                                                                                                                      
    e := echo.New()                                                                                                                
    e.GET("/endpoint", handler, echo.WrapMiddleware(Middleware))                                                                   
    e.Logger.Fatal(e.Start(":8080"))                                                                                               
}                                                                                                                                  
                                                                                                                                   
func handler(c echo.Context) error {                                                                                               
    return c.String(http.StatusOK, "Hello, World!")                                                                                
}                                                                                                                                  
                                                                                                                                   
func Middleware(handler http.Handler) http.Handler {                                                                               
    return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {                                                       
        writer := statusRecorder{w, 0}                                                                                             
        handler.ServeHTTP(&writer, req)                                                                                            
        if writer.statusCode == 0 {                                                                                                
            log.Errorf("Expected status code to be set but it wasn't")                                                             
        }                                                                                                                          
    })                                                                                                                             
}  

Version/commit

master branch latest commit

@stale
Copy link

stale bot commented Mar 16, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Mar 16, 2019
@stale stale bot closed this as completed Mar 23, 2019
@SlevinWasAlreadyTaken
Copy link

SlevinWasAlreadyTaken commented Mar 28, 2019

Hi @Daniel-Houston, if you want to be able to implement a custom ResponseWriter, you can override the Writer contained in echo.Response structure.

echo.Response implements http.ResponseWriter interface and at the end calls its internal Writer http.ResponseWriter methods.

@vishr
Copy link
Member

vishr commented Aug 7, 2019

@Daniel-Houston This is fixed in #1341

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants