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

net/http: Add Error Callbacks #21548

Open
theory opened this Issue Aug 21, 2017 · 9 comments

Comments

Projects
None yet
5 participants
@theory

theory commented Aug 21, 2017

I've written a little REST server on net/http that's supposed to only ever return JSON contents. At least, that's what the parts I wrote do. However, there are certain errors that net/http encounters before my code is ever called. In those situations, I have no access to any code to determine what the content type and message should be for a given error. For example, take this stupid simple implementation:

package main

import (
	"encoding/json"
	"net/http"
)

type Server struct{}

func (server *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(http.StatusText(http.StatusOK)); err != nil {
		panic(err)
	}
}

func main() {
	http.ListenAndServe(":8080", &Server{})
}

If I submit a request with borked headers (this one has a wayward newline in the Authorization header), my ServeHTTP() method is never called, presumably because there's an error when parsing the request to create the Request object:

> curl -i -H 'Authorization: Basic hi
there' 'http://localhost:8080/'
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close

400 Bad Request

This makes complete sense to me, but if I'm creating an API with a contract to only ever return JSON, I'd like to have some way to plug into this error response and set the content-type and the body. I think the same challenge exists for Not Found errors. Perhaps there could be special handlers to configure these situations, similar to how Gorilla mux provides an assignable NotFoundHandler field in its Router object?

I'm not sure exactly what a generalized interface would look like for such error handlers (methods on Handler protocol?), but some way to hook in and get some control over net/http-initiated error responses would be very useful for guaranteeing the content-type contract of a web API.

@artyom

This comment has been minimized.

Contributor

artyom commented Aug 22, 2017

@theory consider this: practically there's big enough chance that API would be running behind some kind of load balancer that may send error replies on its own in some scenarios (including malformed client requests). This would violate your assumption about getting replies that only have json payload in their bodies.

@theory

This comment has been minimized.

theory commented Aug 22, 2017

That's often true, @artyom, but not an assumption I'd always want to make. In order to ensure the integrity of the interface I provide, I'd like to have some ability to determine output formats for errors from http itself, regardless of what the operational deployment ultimately looks like. Helps me avoid QA questions, too.

@artyom

This comment has been minimized.

Contributor

artyom commented Aug 23, 2017

This one is a potential duplicate of #10123.

@theory

This comment has been minimized.

theory commented Aug 23, 2017

Thanks @artyom, I think you're right.

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Aug 23, 2017

#10123 is closed, so should we close this one as well? Or with more experience does this seem like a real problem that net/http ought to address?

@opennota

This comment has been minimized.

opennota commented Aug 23, 2017

I don't think it's a dup of #10123. Bypassing the handler altogether is a different thing.

@tombergan

This comment has been minimized.

Contributor

tombergan commented Sep 1, 2017

I believe this is a dup of #18997 (via #18095). Please reopen if you disagree.

#18997 is a long thread. The tl;dr is here:
#18997 (comment)

@tombergan tombergan closed this Sep 1, 2017

@tombergan

This comment has been minimized.

Contributor

tombergan commented Sep 1, 2017

Sorry, reopening, it's not a dup. You don't just want to log, you want to return a custom response. I would not consider it a dup of #10123 either, because #10123 was about overriding http.Error, while this is about returning a custom response for internal errors that user code currently has no control over.

I don't know how to fix this cleanly. The quick hack is to add a field http.Server.InternalErrorResponseWriter, but that's ugly. Another approach is to have the server create an actual request, but somehow mark that request "bad". I'm not sure how to do that without breaking existing code.

@tombergan tombergan reopened this Sep 1, 2017

@theory

This comment has been minimized.

theory commented Sep 1, 2017

Not beautiful, but the simplest solution is to add a method one can override something like:

type ErrorHandler interface {
    HandleError func(w http.ResponseWriter, code int)
}

Then check to see if the Handler implements it.

Pretty similar to Error(), I guess.

@ianlancetaylor ianlancetaylor added this to the Unplanned milestone Mar 30, 2018

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