How to assign multiple handlers to the same uri in go? - http

I have two tasks I need to fulfill when the "/" pattern is present in a request, both of which require using http handlers.
They are:
http.Handle("/", http.FileServer(http.Dir("dtfw-tool/build/")))
http.HandleFunc("/", index)
The index handler checks for proper authentication to access a webpage, and the handler above it serves up a directory (in the future I will make it to where it will only serve the directory if authentication requirements are met).
Is it possible to have two handlers for the same pattern (currently gives error)? If not, is there any other way to check authentication and serve up the directory with a single handler?

Create a middleware to authenticate users and return the handler to main Handle which will wrap your final handler
package main
import (
"log"
"net/http"
)
func main() {
finalHandler := http.HandlerFunc(final)
http.Handle("/", authentication(finalHandler))
http.ListenAndServe(":3000", nil)
}
func authentication(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Executing authentication")
next.ServeHTTP(w, r) //`next.ServeHTTP(w, r)` will forward the request and response to next handler.
})
}
func final(w http.ResponseWriter, r *http.Request) {
log.Println("Executing finalHandler")
w.Write([]byte("User authenticated"))
}
In Golang HanlderFunc is used to return hanlder which will become a middlware to wrap the main function:
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
It is also defined in the source code for server.go
Playground Example

I'm going to have to retract my previous answer acceptance because this is much closer to what I was looking for (have to import github.com/abbot/go-http-auth):
package main
import (
"fmt"
"net/http"
auth "github.com/abbot/go-http-auth"
)
func Secret(user, realm string) string {
if user == "john" {
// password is "hello"
return "$1$dlPL2MqE$oQmn16q49SqdmhenQuNgs1"
}
return ""
}
func main() {
fmt.Println("-----> Starting HTTP server...")
authenticator := auth.NewBasicAuthenticator("secret.com", Secret)
http.HandleFunc("/", authenticator.Wrap(func(res http.ResponseWriter, req *auth.AuthenticatedRequest) {
http.FileServer(http.Dir(".")).ServeHTTP(res, &req.Request)
}))
http.ListenAndServe(":5042", nil)
}
This method is much easier to follow and more intutive (for me at least).

Related

How can I terminate my handler early if some permission check fails?

I am finding a way to implement the permission check functionality using http
The idea is there are APIs that should be used only by login sessions.
func CheckPermissionFilter(w http.ResponseWriter, r *http.Response){
sid, err := r.Cookie("sid")
// check the permission with sid, if permission is granted then just let the
// process go on, otherwise, just break the filter chain and return Http Error Code.
}
func SomeHttpHandler(w http.ResponseWriter, r *http.Response){
CheckPermissionFilter(w, r)
// if not breaked by above filter function, process the request...
}
I have no problem with the permission checking, but I can't find a way to break the HTTP Request processing.
The call to CheckPermissionFilter within your SomeHttpHandler handler cannot terminate the latter early. Instead, you should define CheckPermissionFilter as a middleware (see also decorator pattern):
package main
import (
"net/http"
)
func main() {
http.Handle("/foo", CheckPermissionFilter(SomeHttpHandler))
// ...
}
func CheckPermissionFilter(h http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
sid, err := r.Cookie("sid")
// handle err
if !Validate(sid) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
h(w, r)
})
}
func SomeHttpHandler(w http.ResponseWriter, r *http.Request) {
// ...
}
func Validate(sid string) bool {
return true // simplistic implementation for this example
}

Golang HTTP custom error handling response

I was going through https://blog.golang.org/error-handling-and-go and at the end it gave a good example on how to handle returning errors in a cleaner way and just made something simple:
// Wrapper for handler functions.
type rootHandler func(http.ResponseWriter, *http.Request) error
// Implement the http.Handler interface.
func (fn rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := fn(w, r) // Call handler function.
if err == nil {
return
}
...
}
// POST /api/test/
testRouter.
Handle("/", rootHandler(create)).
Methods("POST")
// GET /api/test/{id}/
testRouter.
HandleFunc("/{id:[0-9]+}/", rootHandler(getByID)).
Methods("GET")
func create(w http.ResponseWriter, r *http.Request) error {
// CustomError implementes error
return &CustomError{
Kind: EPARSE,
Status: http.StatusBadRequest,
Message: "some message",
Op: "create",
Err: err,
}
}
This worked very well, but I would prefer not to wrap every controller method (create in this case) in rootHandler, and figured the best way is to figure out some sort of post-middleware. I've failed at trying to create a post-middlewhere which the router uses instead of each controller method, and wondering how you may go about implementing this. The closest answer on SO I could find was emmbee's answer on How can I combine Go middleware pattern with error returning request handlers? except for fn in AuthMiddleware would be a controller method.
So ideally, I would have the below handler which handles the CustomError if it exists
// POST /api/test
testRouter.
Handle("/", create).
Methods("POST")
For context, I'm using gorilla mux and negroni.
Any ideas are appreciated! Thank you very much.
Your ideal solution will not work. The mux API supports http.Handler and func(http.ResponseWriter, *http.Request) arguments. You have a func(http.ResponseWriter, *http.Request) error. A func(http.ResponseWriter, *http.Request) error cannot be passed as one of the argument types supported by the mux API.
The only solution is to adapt each controller function to a type supported by the mux API using the rootHandler wrapper. The wrapper does not add overhead compared to the standard http.HandlerFunc wrapper.
Use a helper function to reduce the amount of code required for the wrappers:
func handle(r *mux.Router, p string, fn func(http.ResponseWriter, *http.Request) error) *mux.Route {
return r.Handle(path, rootHandler(fn))
}
...
handle(testRouter, "/", create).Methods("POST")
handle(testRouter, "/{id:[0-9]+}/", getByID).Methods("GET")
Use a type switch in the helper function to handle different controller types:
func handle(r *mux.Router, p string, h interface{}) *mux.Route {
switch h := h.(type) {
case func(http.ResponseWriter, *http.Request) error:
return r.Handle(p, rootHandler(h))
case func(http.ResponseWriter, *http.Request):
return r.HandleFunc(p, h)
case http.Handler:
return r.Handle(p, h)
default:
panic(fmt.Sprintf("handler type %T not supported", h))
}
}

How do I pass the app around function?

In main, the app is started as such:
// ...
func main () {
initializeAppDefault()
go lib.GetData()
http.HandleFunc("/_ah/somepoint", lib.SomeHandler)
// ..
func initializeAppDefault() *firebase.App {
// [START initialize_app_default]
app, err := firebase.NewApp(context.Background(), nil)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
// [END initialize_app_default]
return app
}
In SomeHandler, I need the app which initializeAppDefault returns, to verify a JSON Web Token(JWT).
func SomeHandler(w http.ResponseWriter, r *http.Request) {
// Set content type:
w.Header().Set("Content-Type", "application/json")
if r.Header != nil {
ReqToken := r.Header.Get("Authorization")
splitToken := strings.Split(ReqToken, "Bearer")
ReqToken = splitToken[1]
fmt.Println(ReqToken)
// Verify JWT
// If it's invalid, return?
verifyIDToken(app, ReqToken)
// How do I pass the app in here?
func verifyIDToken(app *firebase.App, idToken string) *auth.Token {
// ...
My question is, when the app is initialized in the main.go file by invoking initializeAppDefault(), how do I pass it to the SomeHandler which handles requests at /_ah/somepoint?
The way to pass arbitrary dependencies into an HTTP handler function is by returning a closure:
func myHandler(a *Something, b *SomethingElse) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// The body of the handler here, using a and b
}
}
Then you'll use it as:
http.Handle("/some/path", myHandler(a, b))
Define new struct type with member of your *firebase.App:
type myapp struct {
fbapp *firebase.App
// here can be other common states and resources
// like sessions, db connections, etc...
}
Define your handlers as methods of that type
func (ma *myapp) SomeHandler(w http.ResponseWriter, r *http.Request) {
// here you have access to all members of myapp, including ma.fbapp
// also you can use your lib.* funcs here
}
And in your main you need to create myapp and pass it to http.HandleFunc.
func main () {
ma := &myapp{
fbapp: initializeAppDefault()
}
go lib.GetData()
http.HandleFunc("/_ah/somepoint", ma.SomeHandler)
This is a common pattern. Check out how i use it in my interview task: pay attention how handlers are defined, how they getting access to the common s.store and how main function inits all common resources, creates router and runs it.

Not handling GET in net/http golang

I am trying to turn off handling GET requests in golang.
I just want to handle POST.
Is it possible to do?
Reason for doing so is that i can see more and more memory being allocated by golang whenever i go to localhost:8080 and refresh page multiple times.
Here is my test code:
package main
import (
"fmt"
"net/http"
"encoding/json"
)
type test_struct struct {
Test string
}
var t test_struct
func handlePOST(rw http.ResponseWriter, req *http.Request) {
switch req.Method {
case "POST":
decoder := json.NewDecoder(req.Body)
decoder.Decode(&t)
defer req.Body.Close()
fmt.Println(t.Test)
}
}
func main() {
http.HandleFunc("/", handlePOST)
http.ListenAndServe(":8080", nil)
}
You cannot not handle GET requests, Go's HTTP server (or rather its http.ServeMux) only allows you to specify a path pattern before dispatching the request to your handler. HTTP method related routing can only happen at the handler level.
Note that some external mux libraries allow you to register handlers to specific HTTP methods only, but the decision and routing based on that also happens in "hidden" handlers of those libraries.
What you're doing is the best: simply do nothing in the handler if the HTTP method is not the one you intend to handle, or even better: send back a http.StatusMethodNotAllowed error response:
func myHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Only POST is allowed", http.StatusMethodNotAllowed)
return
}
var t test_struct // Use local var not global, else it's a data race
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&t); err != nil {
fmt.Println("Error decoding:", err)
}
fmt.Println(t.Test)
}

Gorilla mux, best way to 'catch' response codes

I'm using Gorilla mux for all my routing. Now my app is working fine, I want to find a way to log all my response codes to -for example- statds. I have found this package: https://godoc.org/github.com/gorilla/handlers#LoggingHandler
Which allows me to output all responses into apache format. Although this is nice, it's not 100% what I want. I just want to extract the response statusses and send them to statds. Now what's the best/easiest way to achieve this?
package main
import (
"log"
"net/http"
"os"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/rogierlommers/mux-status-handler/articles"
"github.com/rogierlommers/mux-status-handler/users"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/products", articles.Handler)
r.HandleFunc("/users", users.Handler)
loggedRouter := handlers.LoggingHandler(os.Stdout, r)
log.Println("listening on 8080")
http.ListenAndServe(":8080", loggedRouter)
}
Above code gives me this:
So I'm looking for something similar, but instead of outputting the Apache access logs to stdout, I would like to be able to "do something" with the response code. I have also created a simple repo which contains my sample code. You can find it here.
I found this useful Blog post from Tim Andersson.
First he builds a new struct that satisfies the interface:
type loggingResponseWriter struct {
http.ResponseWriter
statusCode int
}
func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
return &loggingResponseWriter{w, http.StatusOK}
}
func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code)
}
Then he's using it as a wrapper (or middleware):
func wrapHandlerWithLogging(wrappedHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
log.Printf("--> %s %s", req.Method, req.URL.Path)
lrw := NewLoggingResponseWriter(w)
wrappedHandler.ServeHTTP(lrw, req)
statusCode := lrw.statusCode
log.Printf("<-- %d %s", statusCode, http.StatusText(statusCode))
})
}
This is how it can be made with violetear, probably can give you a hint about how to deal with the status code within the handler:
package main
import (
"fmt"
"log"
"net/http"
"github.com/nbari/violetear"
)
func handleGET(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("I handle GET requests\n"))
// do anything here with the Status code
cw := w.(*violetear.ResponseWriter)
fmt.Printf("The status code is: %d\n", cw.Status())
}
func main() {
router := violetear.New()
router.HandleFunc("/", handleGET, "GET")
log.Fatal(http.ListenAndServe(":8080", router))
}
By using:
cw := w.(*violetear.ResponseWriter)
You can access the violetear.ResponseWriter which exposes the status code by using cw.Status()
You can write your own middleware, here's a very base example
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/rogierlommers/mux-status-handler/articles"
"github.com/rogierlommers/mux-status-handler/users"
)
// middleWare ...
func middleWare(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// right not all this does is log like
// "github.com/gorilla/handlers"
log.Printf("%s %s %s", r.RemoteAddr, r.Method, r.URL)
// However since this is middleware you can have it do other things
// Examples, auth users, write to file, redirects, handle panics, ect
// add code to log to statds, remove log.Printf if you want
handler.ServeHTTP(w, r)
})
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/products", articles.Handler)
r.HandleFunc("/users", users.Handler)
log.Println("listening on 8080")
http.ListenAndServe(":8080", middleWare(r))
}

Resources