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.
Related
I have a generic Webserver which I want to use on different domains / servers.
For setting up each server I simply read a JSON config file with all necessary information. One would be for example the redirect for all traffic which reaches port 80 and forward it to a TLS service. Since I don't want to make the config object global. How can I pass the content from my inputFromConfigFile to the redirectTLS function?
Here is an example:
func main(){
var inputFromConfigFile = "https://www.example.com:443"
go func() {
if err := http.ListenAndServe(":80", http.HandlerFunc(redirectTLS)); err != nil {
log.Fatalf("ListenAndServe error: %v", err)
}
}()
}
//Pass the above string to this function:
func redirectTLS(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://www.example.com:443"+r.RequestURI,http.StatusMovedPermanently)
}
You can define a custom Handler (could be implemented as a struct) as long as it matches the http.Handler interface. The config could be saved inside the Handler as a struct field.
type Handler struct {
// config goes here
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// anything that handler needs to do here
}
Example: https://pkg.go.dev/net/http#example-Handle
You can define redirectTLS as an inline closure function directly in main:
var inputFromConfigFile = "https://www.example.com:443"
go func() {
err := http.ListenAndServe(":80", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, inputFromConfigFile+r.RequestURI, http.StatusMovedPermanently)
})
if err != nil {
log.Fatalf("ListenAndServe error: %v", err)
}
}()
I would make the config object global.
Otherwise, you can define a function that accepts the config as an argument, and returns a handler function that closes over the configuration object:
var inputFromConfigFile = "https://www.example.com:443"
http.ListenAndServe(":80", createHandler(inputFromConfigFile))
// ...
func createHandler(config string) http.HandlerFunc {
return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, config+r.RequestURI,http.StatusMovedPermanently)
})
}
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
}
-- routes.go --
package main
import (
"hotelsystem/pkg/config"
"hotelsystem/pkg/handlers"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func routes(app *config.AppConfig) http.Handler {
mux := chi.NewRouter()
mux.Use(middleware.Recoverer)
mux.Use(WriteToConsole)
mux.Get("/", handlers.Repo.Home)
mux.Get("/about", handlers.Repo.About)
return mux
}
-- middleware.go --
package main
import (
"fmt"
"net/http"
)
func WriteToConsole(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Hit the page")
next.ServeHTTP(w, r)
})
}
-- main.go --
package main
import (
"hotelsystem/pkg/config"
"hotelsystem/pkg/handlers"
"hotelsystem/pkg/render"
"log"
"net/http"
)
const portNumber = ":3000"
func main() {
var app config.AppConfig
tc, err := render.CreateTemplateCache()
if err != nil {
log.Fatal("Can't create templatecache", err)
}
app.TemplateCache = tc
app.UseCache = false
repo := handlers.NewRepo(&app)
handlers.NewHandlers(repo)
render.NewTemplate(&app)
// http.HandleFunc("/", handlers.Repo.Home)
// http.HandleFunc("/about", handlers.Repo.About)
// http.ListenAndServe(portNumber, nil)
srv := &http.Server{
Addr: portNumber,
Handler: routes(&app),
}
err = srv.ListenAndServe()
if err != nil {
log.Fatal(err)
}
}
I am having a hard time understanding the middleware.
I am using chi for routing.
What I didn't understand is what does that (next http.Handler) argument in the WriteToConsole refers to?
Does it refer to our mux router?
Also when I comment down the line next.ServeHTTP of function writetoconsole the html is not rendered or anything? can someone explain me what does that next http.Handler refers to and what next.serveHTTP does?
next is the next handler in the "handler chain".
When you do:
mux.Use(middleware.Recoverer)
mux.Use(WriteToConsole)
mux.Get("/", handlers.Repo.Home)
mux.Get("/about", handlers.Repo.About)
You are essentially registering two "handler chains":
mux.Get("/", middleware.Recoverer(WriteToConsole(handlers.Repo.Home)))
mux.Get("/about", middleware.Recoverer(WriteToConsole(handlers.Repo.About)))
Each handler returned by the middleware function has to invoke the next handler given to it, i.e. do next.ServeHTTP(w, r), if it doesn't invoke next then the chain is broken and the rest of the handlers in that chain will be ignored.
A simplified code example may illustrate the chaining better:
type handler func()
// your handler
func f() { fmt.Println("f") }
// one middleware
func g(next handler) handler {
return func() {
fmt.Print("g.")
next()
}
}
// another middleware
func h(next handler) handler {
return func() {
fmt.Print("h.")
next()
}
}
With the above you can then do:
func main() {
h1 := h(g(f))
h1()
h2 := g(h(f))
h2()
// And you can chain as many of these as you like
// and in any order you like.
h3 := h(g(h(h(h(g(g(h(f))))))))
h3()
}
https://play.golang.org/p/4NXquYsaljr
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).
Problem:
Unable to access mux.CurrentRoute(r).GetName() from middleware. (Although I had been able to access it from my middleware, I had to change the way my middleware works due to it's previous inability to access the request). So I've mucked something up and I'm not sure how to get back to a working state where I can access the route name.
Any help would be much appreciated!
Error:
runtime error: invalid memory address or nil pointer dereference
Code:
func main() {
var (
err error
r *mux.Router
devRouter *mux.Router
usersRouter *mux.Router
brandsRouter *mux.Router
)
defer db.Close()
defer store.Close()
r = mux.NewRouter()
devRouter = r.PathPrefix("/api/v1/dev").Subrouter()
usersRouter = r.PathPrefix("/api/v1/users").Subrouter()
brandsRouter = r.PathPrefix("/api/v1/brands").Subrouter()
// development endpoints
devRouter.HandleFunc("/db/seed", devDbSeed)
...
// users
usersRouter.HandleFunc("/create", usersCreateHandlerFunc).Methods("POST").Name("USERS_CREATE")
...
// brands
brandsRouter.HandleFunc("/create", brandsCreateHandlerFunc).Methods("POST").Name("BRANDS_CREATE")
...
// products
brandsRouter.HandleFunc("/{brand_id:[0-9]+}/products", brandsProductsListHandlerFunc).Methods("GET").Name("BRANDS_PRODUCTS_LIST")
...
// mwAuthorize and mwAuthenticate basically work the same
mw := []func(http.Handler) http.Handler{mwAuthenticate, mwAuthorize}
http.Handle("/", use(r, mw...))
err = http.ListenAndServe(":9000", nil)
if err != nil {
logIt(err)
}
}
func use(h http.Handler, mw ...func(http.Handler) http.Handler) http.Handler {
// exec order: mw[0],mw[1],mw[N]...
for i := len(mw) - 1; i >= 0; i-- {
h = mw[i](h)
}
return h
}
func mwAuthorize(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if true != authorize(r) {
w.WriteHeader(http.StatusForbidden)
return
} else {
next.ServeHTTP(w, r)
}
})
}
func authorize(r *http.Request) (isAuthorized bool) {
isAuthorized = false
/**
This is where it's failing!
*/
routeName := mux.CurrentRoute(r).GetName()
switch routeName {
case "USERS_CREATE":
// route-specific authorization
break
...
default:
break
}
return
}
Update (2015-01-04 # 4:49PM EST):
So after removing the middleware (or at least commenting out the section that's trying to read mux.CurrentRoute) I am able to retrieve the route name from the destination handlerfunc (ex: usersCreateHandlerFunc or brandsCreateHandlerFunc). This doesn't solve my problem (I'd still like to perform authentication/authorization in middleware as opposed to every handlerfunc), I have a hunch it's letting me know *mux.Router isn't available in my middleware until after the final .ServeHTTP call. (Or something along those lines...)
Update (2015-01-04 # 5:41PM EST):
Tried a different (albeit less-preferred) direction of using Negroni as the middleware component. Still getting nil-pointer error when I try to get mux.CurrentRoute.
Update (2015-01-04 # 6:17PM EST):
I am able to access the request (ex: r.URL) from the middleware func's, but still no luck on accessing the mux.Route (ex: mux.CurrentRoute(r)). After looking a bit more at the mux source, I think it's because the current mux context isn't getting set because the router hasn't executed the matcher yet (and therefore it doesn't know what route it's currently on until AFTER the middleware is complete). However, I'm still not sure how to either resolve this, or re-structure my code to handle this.
What about:
routeName := mux.CurrentRoute(r).GetName()
Where r is the *http.Request. Don't forget to import "github.com/gorilla/mux". Remember that in order to use this, you must give you route a name when you define it
From CurrentRoute godoc:
CurrentRoute returns the matched route for the current request, if any. This only works when called inside the handler of the matched route because the matched route is stored in the request context[...]
In your example, your chain of mwAuthenticate, mwAuthorize is attached to the route "/" without using gorilla mux. That means when the request passes your handlers, it has not passed gorilla mux router.
Try the following (your example stripped down):
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
var (
err error
r *mux.Router
devRouter *mux.Router
)
func devDbSeed(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "devDbSeed")
return
}
func main() {
r = mux.NewRouter()
devRouter = r.PathPrefix("/api/v1/dev").Subrouter()
// mwAuthorize and mwAuthenticate basically work the same
mw := []func(http.Handler) http.Handler{mwAuthenticate, mwAuthorize}
// development endpoints
devRouter.Handle("/db/seed", use(http.HandlerFunc(devDbSeed), mw...)).Name("foo")
// Send all requests into the mux router
err = http.ListenAndServe(":9000", r)
if err != nil {
log.Fatal(err)
}
}
func use(h http.Handler, mw ...func(http.Handler) http.Handler) http.Handler {
// exec order: mw[0],mw[1],mw[N]...
for i := len(mw) - 1; i >= 0; i-- {
h = mw[i](h)
}
return h
}
func mwAuthorize(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !authorize(r) {
w.WriteHeader(http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
func mwAuthenticate(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
})
}
func authorize(r *http.Request) (isAuthorized bool) {
isAuthorized = false
handlerName := "UNKNOWN"
if route := mux.CurrentRoute(r); route != nil {
routeName := route.GetName()
if routeName != "" {
handlerName = routeName
}
}
log.Println(handlerName)
switch handlerName {
case "USERS_CREATE":
// route-specific authorization
log.Println("USERS_CREATE")
break
default:
break
}
return
}
I had the same problem and I resolved in that way:
var match mux.RouteMatch
routeExists := s.Router.Match(r, &match)
if routeExists && match.Route.GetName(){
routeName := match.Route.GetName()
}
And when I defined the route I added .Name("route/:param") where route/:param is my route.