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

[bug] Panic on nil Hijack interface due to compress handler #169

Closed
muirdm opened this issue Oct 2, 2019 · 8 comments · Fixed by #193
Closed

[bug] Panic on nil Hijack interface due to compress handler #169

muirdm opened this issue Oct 2, 2019 · 8 comments · Fixed by #193
Assignees
Labels

Comments

@muirdm
Copy link

muirdm commented Oct 2, 2019

Describe the bug

The compress handler leaves an empty http.Hijacker interface in the wrapped response writer even when the incoming response writer does not implement http.Hijacker. This causes the wrapped response writer to still implement http.Hijacker, but if you try to call Hijack() it will panic.

Steps to Reproduce

type myWriter struct {
	http.ResponseWriter
}

func main() {
	http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
		if h, ok := w.(http.Hijacker); ok {
			h.Hijack()
		}
	})

	myMiddleware := func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			h.ServeHTTP(&myWriter{w}, r)
		})
	}

	handler := myMiddleware(handlers.CompressHandler(http.DefaultServeMux))

	go func() {
		log.Panic(http.ListenAndServe("127.0.0.1:1337", handler))
	}()

	time.Sleep(1 * time.Second)

	http.Get("http://127.0.0.1:1337/test")
}
@muirdm muirdm added the bug label Oct 2, 2019
@stale
Copy link

stale bot commented Dec 1, 2019

This issue has been automatically marked as stale because it hasn't seen a recent update. It'll be automatically closed in a few days.

@stale stale bot added the stale label Dec 1, 2019
@muirdm
Copy link
Author

muirdm commented Dec 2, 2019

Can a project contributor shares their thoughts before this is automatically closed? I don't think an issue with no responses should be marked as stale.

@stale stale bot removed the stale label Dec 2, 2019
@elithrar
Copy link
Contributor

elithrar commented Dec 2, 2019

This doesn’t seem ideal. Are you willing to submit a PR + tests to fix?

Happy to review & merge but don’t have time to push a PR for a while.

@elithrar elithrar self-assigned this Dec 2, 2019
@fharding1
Copy link
Contributor

Agreed, this seems weird. When I changed how CompressHandlerLevel works a bit I thought that code for checking interfaces and creating the compressResponseWriter was a bit smelly but I didn't really check it out since that's not what I was focused on changing. I can also help out with a review, or push a PR myself if needed. 👍

@stale
Copy link

stale bot commented Jan 31, 2020

This issue has been automatically marked as stale because it hasn't seen a recent update. It'll be automatically closed in a few days.

@stale stale bot added the stale label Jan 31, 2020
@stale stale bot closed this as completed Feb 8, 2020
@muirdm
Copy link
Author

muirdm commented Jul 23, 2020

Sorry, I'm not able to submit a PR because I don't know what the best solution is. Here is an example of what is required for proper ResponseWriter wrapping: https://github.com/aws/aws-xray-sdk-go/blob/master/xray/response_capturer.go.

Since gorilla is widely used, what do you think about gorilla providing a wrapping mechanism other libraries can use to safely wrap a ResponseWriter?

@elithrar elithrar removed the stale label Aug 19, 2020
@elithrar elithrar reopened this Aug 19, 2020
@muirdm
Copy link
Author

muirdm commented Aug 19, 2020

Since my last comment we found the "httpsnoop" package which seems like the best option to properly wrap response writers. It uses code generation to cover all the cases and handles changes across Go versions properly. We used it to fix a similar problem in opentelemetry. If that looks reasonable I can open a PR using that package.

@elithrar
Copy link
Contributor

@muirdm Seems pretty straightforward - would love a PR for this.

muirdm pushed a commit to retailnext/handlers that referenced this issue Aug 19, 2020
Wrapping http.ResponseWriter is fraught with danger. Our compress
handler made sure to implement all the optional ResponseWriter
interfaces, but that made it implement them even if the underlying
writer did not. For example, if the underlying ResponseWriter was
_not_ an http.Hijacker, the compress writer nonetheless appeared to
implement http.Hijacker, but would panic if you called Hijack().

On the other hand, the logging handler checked for certain
combinations of optional interfaces and only implemented them as
appropriate. However, it didn't check for all optional interfaces or
all combinations, so most optional interfaces would still get lost.

Fix both problems by using httpsnoop to do the wrapping. It uses code
generation to ensure correctness, and it handles std lib changes like
the http.Pusher addition in Go 1.8.

Fixes gorilla#169.
elithrar pushed a commit that referenced this issue Aug 20, 2020
Wrapping http.ResponseWriter is fraught with danger. Our compress
handler made sure to implement all the optional ResponseWriter
interfaces, but that made it implement them even if the underlying
writer did not. For example, if the underlying ResponseWriter was
_not_ an http.Hijacker, the compress writer nonetheless appeared to
implement http.Hijacker, but would panic if you called Hijack().

On the other hand, the logging handler checked for certain
combinations of optional interfaces and only implemented them as
appropriate. However, it didn't check for all optional interfaces or
all combinations, so most optional interfaces would still get lost.

Fix both problems by using httpsnoop to do the wrapping. It uses code
generation to ensure correctness, and it handles std lib changes like
the http.Pusher addition in Go 1.8.

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

Successfully merging a pull request may close this issue.

3 participants