Passing a string to a handler function in Go - http

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)
})
}

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
}

Conditional middleware behavior via handler

I am trying to implement conditional middleware behavior after a handler call.
I've looked into contexts and was able to alter/use them as flags, passing data INTO the handler from the middleware but not FROM the handler to middleware. The method signatures do not allow for return values.
This is a quick writeup of what I am working with:
func Reporter(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
// conditionally check if err != nil here and log
})
}
func appRoutes(r chi.Router) {
r.Use(Reporter)
r.Get("/", ListApplications)
}
func ListApplications(w http.ResponseWriter, , r *http.Request) {
apps, err := fetchApps()
if err != nil {
render.Render(w, r, ErrorStructRenderer(err))
return
}
render.Respond(w, r, apps)
}

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.

In go, how to inspect the http response that is written to http.ResponseWriter?

There's probably something obvious that I'm missing but I'm trying to debug the HTTP response written by my go server.
I see that there's httputil.DumpResponse available but it takes a http.Response object and what I have available is http.ResponseWriter
Is there a way to extract the http.Response from http.ResponseWriter so I can inspect the content of the response to console or log?
Context:
I'm writing a simple server-side authentication using https://github.com/RangelReale/osin and it's default example, but could not understand why the front-end (using http://ember-simple-auth.com) interprets a failed authentication (incorrect password) as success.
Here's the snippet:
r = mux.NewRouter()
r.HandleFunc("/token", func (w http.ResponseWriter, r *http.Request) {
fmt.Printf("r.HandleFunc /token\n")
resp := server.NewResponse()
defer resp.Close()
r.ParseForm()
grantType := r.FormValue("grant_type")
username := r.FormValue("username")
password := r.FormValue("password")
fmt.Printf("/token : grantType=%s username=%s password=%s\n", grantType, username, password)
if ar := server.HandleAccessRequest(resp, r); ar != nil {
if username == "user" && password == "correct-password" {
ar.Authorized = true
} else {
ar.Authorized = false
}
server.FinishAccessRequest(resp, r, ar)
}
osin.OutputJSON(resp, w, r)
// Debug - doesn't work yet
dump, err := httputil.DumpResponse(w, true)
if err != nil {
fmt.Printf("%s\n", dump)
}
});
http.Handle("/token", r)
Write to an *httptest.ResponseRecorder (which implements http.ResponseWriter) and inspect it.
Example from the package:
package main
import (
"fmt"
"log"
"net/http"
"net/http/httptest"
)
func main() {
handler := func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "something failed", http.StatusInternalServerError)
}
req, err := http.NewRequest("GET", "http://example.com/foo", nil)
if err != nil {
log.Fatal(err)
}
w := httptest.NewRecorder()
handler(w, req)
fmt.Printf("%d - %s", w.Code, w.Body.String())
}
Edit to answer question in comments:
If I understand your question correctly, then yes, you can make use of closures for this.
Consider the following to be your handler:
func MyHandler(w http.ResponseWriter, r *http.Request) {
// do useful stuff...
}
You could then register the following closure with your servemux to attain the desired effect:
http.HandleFunc("/my/url", func(w http.ResponseWriter, r *http.Request) {
// first call MyHandler
MyHandler(w, r)
// then log whatever you need
log.Printf("%#v\n", w)
})
If this pattern proves useful to you then you could write a higher-order method that wraps any func(http.ResponseWriter, *http.Request) in such a closure. That's a topic for itself, though.

Get gorilla/mux router current route name from middleware

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.

Resources