golang - Exit http handler from anywhere - go

I'm using the net/http package and wondering how I can exit the handler from anywhere in the code. Say I have this code:
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
func handler(w http.ResponseWriter, r *http.Request){
err := checkSomeThing(w, r)
if err != nil {
return
}
fmt.Println("End of Handler.")
return
}
func checkSomeThing(w http.ResponseWriter, r *http.Request) error{
http.Error(w, "Bad Request!", http.StatusBadRequest)
return errors.New("bad request")
}
Ideally I'd like to exit the handler from within the checkSomeThing function without having to return and then return again up a level, which will get worse as the application grows. This is purely for code readability.

The idiomatic approach is to check error returns up the call chain.
To exit the the handler from anywhere, use panic and recover following the pattern in the encoding/json package.
Define a unique type for panic:
type httpError struct {
status int
message string
}
Write a function to be used in a defer statement. The function checks for the type and handles the error as appropriate. Otherwise, the function continues the panic.
func handleExit(w http.ResponseWriter) {
if r := recover(); r != nil {
if he, ok := r.(httpError); ok {
http.Error(w, he.message, he.status)
} else {
panic(r)
}
}
}
Write a helper function for the call to panic:
func exit(status int, message string) {
panic(httpError{status: status, message: message})
}
Use the functions like this:
func example() {
exit(http.StatusBadRequest, "Bad!")
}
func someHandler(w http.ResponseWriter, r *http.Request) {
defer handleExit(w)
example()
}

My answer:
First, the common pattern established in Golang is to have errors "bubble up" from callee back to caller as a return value. It has a lot of advantages with regards to readability and re-use. The side effect is that there's a lot of if err != nil {return} checks.
My suggestion if you really want to break from the norm
I'm going to pitch an idea, that I don't think is common or standard with respect to golang coding styles and patterns. But I didn't see anything online suggesting this was catastrophic. Let's see what I get in the comments to say this is awful.
You could use runtime.Goexit() to achieve what you want. The handler just waits on another goroutine to do the work. If the inner code running in the go-routine wants to abort processing, it can call Goexit(). It has the advantage that all defer statements will still execute.
This just seems like a weak version of exception handling that Golang currently doesn't support. But I'm throwing it out there.
func handler(w http.ResponseWriter, r *http.Request) {
var cleanExit bool = false
var ch = make(chan bool)
// the actual handler implementation in a goroutine
go func() {
defer close(ch)
handlerImpl(w, r)
cleanExit = true // if handlerImpl invokes goExit, this line doesn't execute
}()
// wait for goroutine to exit
<-ch
if cleanExit {
fmt.Println("Handler exited normally")
} else {
fmt.Println("Hanlder was aborted")
}
}
func handlerImpl(w http.ResponseWriter, r *http.Request) {
checkSomeThing(w, r)
}
func checkSomeThing(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Bad Request!", http.StatusBadRequest)
runtime.Goexit()
}

If checkSomeThing() is specific to that route, you should probably keep going with the code sample you pasted.
If checkSomeThing() is a function common to all your routes (or to a subset of routes), you can choose a way to run a middleware before calling the handler for specific routes.
See for example this answer or this answer, or here is a way to do it using only code from the standard http package :
func checkSomething(...) error {
...
}
func WrapWithCheck(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
err := checkSomething(w, req)
if err != nil {
return
}
handler.ServeHTTP(w, req)
})
}
func setupRouter() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/foo/", handleFoo)
mux.HandleFunc("/bar/", handleBar)
mux.HandleFunc("/baz/", handleBaz)
// add your common call to 'checkSomething' here :
handler := WrapWithCheck(mux)
return handler
}
playground
note : I tried using httptest in the playground above, and for some reason it deadlocks in the playground. It works fine if you copy/paste this code in a sample.go file and use go run sample.go

Related

Return response from a Handler Func

I am fairly new to golang and in one of the handler functions I'm collecting the data using channels from different goroutines and now wanted to return the array of result as a response object
So I have given a return type as the struct details but it's throwing an error
if this is not the way to return the slice of struct as response then how can I return my results array as a response to post my request
Error:
cannot use homePage (type func(http.ResponseWriter, *http.Request) []details) as type func(http.ResponseWriter, *http.Request) in argument to http.HandleFunc
Handler Func:
func homePage(w http.ResponseWriter, r *http.Request) []details{
var wg sync.WaitGroup
for _, url := range urls {
out, err := json.Marshal(url)
if err != nil {
panic (err)
}
wg.Add(1)
go do_calc(ch,client,string(out),&wg)
}
fmt.Println("Returning Response")
go func() {
for v := range ch {
results = append(results, v)
}
}()
wg.Wait()
close(ch)
return results
}
So, your question is two fold. Firstly the reason for the error is because if you look at the documentation here you can see that http.HandleFunc has the following definition.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
Since your function has a return with []details it does not meet the requirements.
So going off the other part of your question;
if this is not the way to return the slice of struct as response then how can I return my results array as a response to post my request
To solve your problem we need to write your data back to the response, you'll notice in the arguments passed to your HandleFunc you have a ResponseWriter, where you can use the Write() method to return your response
Not entirely sure how you want to display your result but you could do it with JSON easy enough.
b, err := json.Marshal(results)
if err != nil {
// Handle Error
}
w.Write(b)

How can I combine Go middleware pattern with error returning request handlers?

I am familiar with the Go middleware pattern like this:
// Pattern for writing HTTP middleware.
func middlewareHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Our middleware logic goes here before executing application handler.
next.ServeHTTP(w, r)
// Our middleware logic goes here after executing application handler.
})
}
So for example if I had a loggingHandler:
func loggingHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Before executing the handler.
start := time.Now()
log.Printf("Strated %s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
// After executing the handler.
log.Printf("Completed %s in %v", r.URL.Path, time.Since(start))
})
}
And a simple handleFunc:
func handleFunc(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`Hello World!`))
}
I could combine them like this:
http.Handle("/", loggingHandler(http.HandlerFunc(handleFunc)))
log.Fatal(http.ListenAndServe(":8080", nil))
That is all fine.
But I like the idea of Handlers being able to return errors like normal functions do. This makes error handling much easier as I can just return an error if there is an error, or just return nil at the end of the function.
I have done it like this:
type errorHandler func(http.ResponseWriter, *http.Request) error
func (f errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := f(w, r)
if err != nil {
// log.Println(err)
fmt.Println(err)
os.Exit(1)
}
}
func errorHandle(w http.ResponseWriter, r *http.Request) error {
w.Write([]byte(`Hello World from errorHandle!`))
return nil
}
And then use it by wrapping it like this:
http.Handle("/", errorHandler(errorHandle))
I can make these two patterns work separately, but I don't know how I could combine them. I like that I am able to chain middlewares with a library like Alice. But it would be nice if they could return errors too. Is there a way for me to achieve this?
I like this pattern of HandlerFuncs returning errors too, it's much neater and you just write your error handler once. Just think of your middleware separately from the handlers it contains, you don't need the middleware to pass errors. The middleware is like a chain which executes each one in turn, and then the very last middleware is one which is aware of your handler signature, and deals with the error appropriately.
So in it's simplest form, keep the middleware you have exactly the same, but at the end insert one which is of this form (and doesn't execute another middleware but a special HandlerFunc):
// Use this special type for your handler funcs
type MyHandlerFunc func(w http.ResponseWriter, r *http.Request) error
// Pattern for endpoint on middleware chain, not takes a diff signature.
func errorHandler(h MyHandlerFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Execute the final handler, and deal with errors
err := h(w, r)
if err != nil {
// Deal with error here, show user error template, log etc
}
})
}
...
Then wrap your function like this:
moreMiddleware(myMiddleWare(errorHandler(myhandleFuncReturningError)))
That means this special error middleware can only ever wrap your special function signature, and come at the end of the chain, but that's fine. Also I'd consider wrapping this behaviour in your own mux to make it a little simpler and avoid passing error handlers around, and let you build a chain of middleware more easily without having ugly wrapping in your route setup.
I think if you're using a router library, it needs explicit support for this pattern to work probably. You can see an example of this in action in a modified form in this router, which uses exactly the signatures you're after, but handles building a middleware chain and executing it without manual wrapping:
https://github.com/fragmenta/mux/blob/master/mux.go
The most flexible solution would be like this:
First define a type that matches your handler signature and implement ServeHTTP to satisfy the http.Handler interface. By doing so, ServeHTTP will be able to call the handler function and process the error if it fails. Something like:
type httpHandlerWithError func(http.ResponseWriter, *http.Request) error
func (fn httpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
http.Error(w, err.Message, err.StatusCode)
}
}
Now create the middleware as usual. The middleware should create a function which returns an error if it fails or calls the next in the chain on success. Then convert the function to the defined type something like:
func AuthMiddleware(next http.Handler) http.Handler {
// create handler which returns error
fn := func(w http.ResponseWriter, r *http.Request) error {
//a custom error value
unauthorizedError := &httpError{Code: http.StatusUnauthorized, Message: http.StatusText(http.StatusUnauthorized)}
auth := r.Header.Get("authorization")
creds := credentialsFromHeader(auth)
if creds != nil {
return unauthorizedError
}
user, err := db.ReadUser(creds.username)
if err != nil {
return &httpError{Code: http.StatusInternalServerError, Message: http.StatusText(http.StatusInternalServerError)}
}
err = checkPassword(creds.password+user.Salt, user.Hash)
if err != nil {
return unauthorizedError
}
ctx := r.Context()
userCtx := UserToCtx(ctx, user)
// we got here so there was no error
next.ServeHTTP(w, r.WithContext(userCtx))
return nil
}
// convert function
return httpHandlerWithError(fn)
}
Now you can use the middleware as you would use any regular middleware.
The output of a middleware, by definition, is an HTTP response. If an error occurred, either it prevents the request from being fulfilled, in which case the middleware should return an HTTP error (500 if something unexpectedly went wrong on the server), or it does not, in which case whatever happened should be logged so that it can be fixed by a system administrator, and the execution should continue.
If you want to achieve this by allowing your functions to panic (although I would not recommend doing this intentionally), catching this situation and handling it later without crashing the server, there is an example in this blog post in section Panic Recovery (it even uses Alice).
From what I understand you wanted to chain your errorHandler function and and combine them in your loggingHandler.
One way to do this is using a struct passing it to your loggingHandler as parameter like this :
func loggingHandler(errorHandler ErrorHandler, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Call your error handler to do thing
err := errorHandler.ServeHTTP()
if err != nil {
log.Panic(err)
}
// next you can do what you want if error is nil.
log.Printf("Strated %s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
// After executing the handler.
log.Printf("Completed %s in %v", r.URL.Path, time.Since(start))
})
}
// create the struct that has error handler
type ErrorHandler struct {
}
// I return nil for the sake of example.
func (e ErrorHandler) ServeHTTP() error {
return nil
}
and in the main you call it like this :
func main() {
port := "8080"
// you can pass any field to the struct. right now it is empty.
errorHandler := ErrorHandler{}
// and pass the struct to your loggingHandler.
http.Handle("/", loggingHandler(errorHandler, http.HandlerFunc(index)))
log.Println("App started on port = ", port)
err := http.ListenAndServe(":"+port, nil)
if err != nil {
log.Panic("App Failed to start on = ", port, " Error : ", err.Error())
}
}

Middleware using Alice and HttpRouter

I can't seem to work out how to use middleware and Http Router properly together.
My code is:
type appContext struct {
db *mgo.Database
}
func main(){
c := appContext{session.DB("db-name")}
commonHandlers := alice.New(context.ClearHandler, basicAuthHandler)
router := NewRouter()
router.Post("/", commonHandlers.ThenFunc(c.final))
http.ListenAndServe(":5000", router)
}
The final middleware is:
func (c *appContext) final(w http.ResponseWriter, r *http.Request) {
log.Println("Executing finalHandler")
w.Write([]byte("TESTING"))
}
but I want my basicAuthHandler to be part of the commonHandlers. It also needs the context so that I can query the db.
I have tried this:
func (c *appContext) basicAuthHandler(w http.ResponseWriter, r *http.Request) {
var app App
err := c.db.C("apps").Find(bson.M{"id":"abcde"}).One(&app)
if err != nil {
panic(err)
}
//do something with the app
}
but I get the error undefined: basicAuthHandler. I understand why I'm getting the error but I don't know how to avoid it. How can I provide the context to the basicAuthHandler and still use it in the commonHandlers list for Alice?
Your middleware needs to have the signature
func(http.Handler) http.Handler
This way your middleware is wrapping handlers, not just providing a final handler. You need to accept an http.Handler, do whatever processing needs to be done, and call ServeHTTP on the next handler in the chain. Your basicAuthHandler example could look like this:
func (c *appContext) basicAuthHandler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var app App
err := c.db.C("apps").Find(bson.M{"id": "abcde"}).One(&app)
if err != nil {
panic(err)
}
h.ServeHTTP(w, r)
})
}
(though you don't want to panic in your app, and should provide a better error response)

Golang net/http and Gorilla: run code before handler

Is it possible using the net/http package and/or any of the gorilla libraries to make some code execute on EVERY URL before going to the handler? For example, to check if a connection is coming from a black listed IP address?
Create a handler that invokes another handler after checking the IP address:
type checker struct {
h http.Handler
}
func (c checker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if blackListed(r.RemoteAddr) {
http.Error(w, "not authorized", http.StatusForbidden)
return
}
c.h.ServeHTTP(w, r)
}
Pass this handler to ListenAndServe instead of your original handler. For example, if you had:
err := http.ListenAndServe(addr, mux)
change the code to
err := http.ListenAndServe(addr, checker{mux})
This also applies to all the variations of ListenAndServe. It works with http.ServeMux, Gorilla mux and other routers.
If you want to use the default muxer, which i find is common, you can create middleware like so:
func mustBeLoggedIn(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
// Am i logged in?
if ...not logged in... {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
// Pass through to the original handler.
handler(w, r)
}
}
Use it like so:
http.HandleFunc("/some/priveliged/action", mustBeLoggedIn(myVanillaHandler))
http.ListenAndServe(":80", nil)

How to refactor error handling in go properly?

I'm just starting with Go so I'm still not used to its patterns.
I have a web server that serves as a proxy to other remote services. I'm using mux to map routes to handlers, the code it's using App Engine.
// imports ommited.
func init() {
m = mux.NewRouter()
m.HandleFunc("/ponies", listPonies)
m.HandleFunc("/rainbows", listRainbows)
http.Handle("/", m)
}
func listPonies(w http.ResponseWriter, r *http.Request) {
ponies, err := ponyService.getAll()
if err != nil {
w.write(err.Error())
return;
}
w.write(string(ponies))
}
func listRainbows(w http.ResponseWriter, r *http.Request) {
rainbows, err := rainbowService.getAll()
if err != nil {
w.write(err.Error())
return;
}
w.write(string(rainbows))
}
I would like to refactor the common code (error handling, converting to string and writing the response) into a single function.
My first attempt was simply defining a common function to call:
func handleErrorAndWriteResponse(w http.ResponseWriter, obj Stringer, err error) {
if err != nil {
w.write(err.Error())
return;
}
w.write(string(obj))
}
And call it like this
func listPonies(w http.ResponseWriter, r *http.Request) {
handleErrorAndWriteResponse(w, ponyService.getAll())
}
func listRainbows(w http.ResponseWriter, r *http.Request) {
handleErrorAndWriteResponse(w, rainbowService.getAll())
}
But
It doesn't work. I get an insufficient arguments error. It probably has to do with mixing the multiple response values from the services that don't translate directly into arguments of the function called.
I don't like the idea of passing the error arguments around. Maybe that's fine, it just looks dirty to me. Then again, I don't know much about Go yet.
What's the "right way" (or the Go way) to do this?
There is an blog post on golang.org about error handling that specifically talks about error handling in an AppEngine application. Specifically, check out the "Simplifying repetitive error handling" section.
The way to do error handling is basically to let your handle functions return an error in case they fail and then wrap the call to them with the common error handler:
type appHandler func(http.ResponseWriter, *http.Request) error
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
http.Error(w, err.Error(), 500)
}
}
func listPonies(w http.ResponseWriter, r *http.Request) error {
ponies, err := ponyService.getAll()
if err != nil {
return err;
}
w.write(string(ponies))
}
You would also need to register your handlers differently:
func init() {
http.Handle("/view", appHandler(listPonies))
}

Resources