Cannot wrap StripPrefix in another function (missing ServeHTTP method) - go

I want to wrap func StripPrefix inside another function and do some client session checking before making a file server. All the sessions are stored in a MySQL database table for implementing multiple web server for one website.
func RegisterHandlers() {
http.Handle("/", fileHandler)
http.HandleFunc("/login", loginHandler)
}
func fileHandler() http.Handler {
//Check session in the database
return http.StripPrefix("/", http.FileServer(http.Dir("client/index")))
}
Error Message
The error message said that missing ServeHTTP method. I don't understand why since I just wrap it in another function and return exactly http.StripPrefix.
./index.go:9: cannot use fileHandler (type func() http.Handler) as type http.Handler in argument to http.Handle:
func() http.Handler does not implement http.Handler (missing ServeHTTP method)

http.Handle expects a function that takes an http.ResponseWriter and an *http.Request. Your file handler function does not take either of these arguments. Instead, you want to pass in the handler returned from a call to your fileHandler function. However, if you do this as-is, you will only be checking the credentials once instead of on every HTTP call, which is probably not what you wanted. Instead, you have to return a handler function that wraps both your session check, and a call to the file servers Handle method. You could even have it wrap a generic handler (similar to how StripPrefix works) so that you can reuse it to check the session for multiple different types of requests. Something like the following:
func RegisterHandlers() {
http.Handle("/", checkSession(http.StripPrefix("/", http.FileServer(http.Dir("client/index")))))
http.HandleFunc("/login", loginHandler)
}
func checkSession(h http.Handler) http.Handler {
return HandlerFunc(func(w ResponseWriter, r *Request) {
//Check session in the database
h.ServeHTTP(w, r)
})
}

Related

How does dgrouter handle middlewares?

I have a question regarding middlewares in various places:
I was reading about using middlewares with just stdlib to write request handlers as mentioned here: (https://www.alexedwards.net/blog/making-and-using-middleware)
func middlewareOne(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Our middleware logic goes here...
next.ServeHTTP(w, r)
})
}
mux := http.NewServeMux()
mux.Handle("/", middlewareOne(middlewareTwo(finalHandler)))
It makes sense to me that passing one handler inside another in this format executes the next middleware's ServeHTTP
I am using dgrouter for discord command routing and I found that I could use middlewares in there as well as defined in this example:
https://github.com/Necroforger/dgrouter/blob/master/examples/middleware/middleware.go#L40-L59
The method used to register middlewares is defined like so:
https://github.com/Necroforger/dgrouter/blob/master/exrouter/router_wrapper.go#L41-L49
My question is why is the middleware not called like how it is done in net/http and just the function name is passed:
router.Group(func(r *exrouter.Route) {
// Added routes inherit their parent category.
// I set the parent category here and it won't affect the
// Actual router, just this group
r.Cat("main")
// This authentication middleware applies only to this group
r.Use(Auth)
log.Printf("len(middleware) = %d\n", len(r.Middleware))
r.On("testauth", func(ctx *exrouter.Context) {
ctx.Reply("Hello " + ctx.Get("member").(*discordgo.Member).Nick + ", you have permission to use this command")
})
})
r.Use(Auth)
Is what i have difficulty understanding. Why is the function not being called?

can I trace sub called function with OpenCensus?

I want to trace the whole project with Opencensus and Jaeger. I added HTTP trace in entry services and add stratspan in middleware surrounded whol my services and this two-span called and show on Jaeger. My problem is each service contain a lot of function and I want see a trace of all my functions but in this way not show overall service not shown each function. I don't like add per function add one stratspan. I use ctx context.Context entry all my function but not different!
There's really not many options for other than starting a span in each function you'd like to instrument:
func something(ctx context.Context) {
ctx, span := trace.StartSpan(ctx, "something")
defer span.End()
}
If your functions have a common call signature, or you can coalesce your function into a common call signature, you can write a wrapper. Examples of this can be seen in http "middleware".
Consider the http.Handler, you could write a decorator for your functions that handles the span lifecycle:
func WithTraced(handler http.Handler, opName string) http.Handler {
return func(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(ctx, opName)
defer span.End()
handler.ServeHTTP(w, r.WithContext(ctx))
}
}
A similar pattern could be applied by embedding structs.

How to create a common function for all requests in Go?

I am writing a web application with Go to get better with it. My use case is pretty simple. I want to have a common function that will be executed for every request and will generate the navigation bar depending on the user status.
init method looks like (will also give you the idea of my implementation of handler methods):
func init() {
initDB()
gob.Register(user.User{})
r := mux.NewRouter()
r.HandleFunc("/", handleHome)
http.Handle("/", r)
}
I am using the following method to execute templates.
func executeTemplate(w http.ResponseWriter, name string, status int, data map[string]interface{}) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(status)
data["User"] = getUser(r)
return tpls[name].ExecuteTemplate(w, "base", data)
}
I am using Gorilla toolkit to store the session but as of my understanding, I need the http.Request instance every time to access the cookie store. Now I don't want to change the signature of executeTemplate method. Is there any way I can add a function to generate the navigation bar without changing signature of any of the existing methods?
What are some good ways to do it (even with changing the existing methods)?
Basic common approach to create middleware in Gorillatoolkit is to wrap top-level mux. Something like
func Middleware(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//You do something here using request
h.ServeHTTP(w, r)
})
}
And then
r := mux.NewRouter()
r.HandleFunc("/", handleHome)
http.Handle("/", Middleware(r))

Go- authentication logic pattern in web app

I want to determine a simple and useful pattern for user authentication in a web app being written in golang.
I have come up with two patterns. First one is enabling the programmer to have his functions separate form the authentication logic, and has cleaner HandleFunc parts in main() that one can see only by loking main() to see what parts are under authentication control.
Second one is making programmer include a decision in every function deal with authentication required urls. An if statement checks by a authp() function defined else where.
Which one is better pattern for such necessity?
What are the better patterns for this job?
Is it even possible to pass a function to http.HandleFunc that has signature other than func urlFunc (ResponseWriter, *Request) bu like func urlFunc (successFunc, failFunc) or func urlFunc (ResponseWriter, *Request, successFunc, failFunc) as in authenticationGateKeeper function of First Way below, if not a suitable workaround for that?
//First Way
package main
func authGateKeeper(successFunc, failFunc) {
if (authp()) {
successFunc
} else {
failFunc
}
}
func authp() boolean {
//authentication logic, db query, or session check etc.
}
//usage in main
http.HandleFunc("/", authGateKeeper)
//Second Way; other alternative, in each function check pattern
func f(w, r) {
if (authp()) {
//function's processes
} else {
//the fail case function or processes
}
}
func authp() boolean {
//authentication logic, db query, or session check etc.
}
//usage in main
http.HandleFunc("/", f)
There are many ways to spin this, and it's arguable whether one is outright "better". I'd strongly suggest writing some middleware that wraps your routes and enforces the check, calling the wrapped handler only on success.
Note that I'm going to make a few assumptions here as you haven't told us how you're managing sessions (cookies? server-side?) and/or what kind of authorization you might need on top of authentication.
// Middleware - a function that sits in the 'middle' of your request processing.
func RequireAuth(h http.Handler) http.Handler) {
fn := func(w http.ResponseWriter, r *http.Request) {
// Assuming gorilla/sessions
session, err := store.Get("name", r)
if err != nil {
// Raise HTTP 500
return
}
// We'll assume you're storing the userID in the cookie|server session
// upon login elsewhere.
id := session.Values["userID"]
// Probably returns a *yourapp.User
user, err := db.GetUser(id)
if err != nil {
// Raise HTTP 500
return
}
if user == nil {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
// Don't forget these 'naked' returns - if you miss one, your
// handler will keep processing beyond the error and result in
// unintended side effects
return
}
// Further checks here - i.e. checking user.Active == true, etc.
// The userID matches one in the DB, so let's proceed
h.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
// And in your router - assuming just vanilla net/http
http.Handle("/", RequireAuth(yourHandlerFunc))
http.Handle("/", RequireAuth(someOtherHandler))
// Note that using gorilla/mux or goji can help give you "subrouters" so you
// don't have to wrap every single route with your middleware (messy, error prone)
I'd also suggest some reading on Go middleware1 composition2 which will help you in the future.
If you want to call a custom error page, just write a handler - e.g. UnauthorizedHandler that satisfies http.Handler and just call UnauthorizedHandler.ServeHTTP(w, r) instead of http.Error along the way.

Golang. What to use? http.ServeFile(..) or http.FileServer(..)?

I'm a little bit confused. Much of examples shows usage of both: http.ServeFile(..) and http.FileServer(..), but seems they have very close functionality. Also I have found no information about how to set custom NotFound handler.
// This works and strip "/static/" fragment from path
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
// This works too, but "/static2/" fragment remains and need to be striped manually
http.HandleFunc("/static2/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, r.URL.Path[1:])
})
http.ListenAndServe(":8080", nil)
I've tried to read source code and both of them use serveFile(ResponseWriter, *Request, FileSystem, string, bool) underlying function. However http.FileServer return fileHandler with its own ServeHTTP() method and make some preparation work before serving file (eg path.Clean()).
So why need this separation? Which method better to use? And how can I set custom NotFound handler, for example when requested file not found?
The main difference is that http.FileServer does effectively almost 1:1 mapping of an HTTP prefix with a filesystem. In plain english, it serves up an entire directory path. and all its children.
Say you had a directory called /home/bob/static and you had this setup:
fs := http.FileServer(http.Dir("/home/bob/static"))
http.Handle("/static/", http.StripPrefix("/static", fs))
Your server would take requests for e.g. /static/foo/bar and serve whatever is at /home/bob/static/foo/bar (or 404)
In contrast, the ServeFile is a lower level helper that can be used to implement something similar to FileServer, or implement your own path munging potentially, and any number of things. It simply takes the named local file and sends it over the HTTP connection. By itself, it won't serve a whole directory prefix (unless you wrote a handler that did some lookup similar to FileServer)
NOTE Serving up a filesystem naively is a potentially dangerous thing (there are potentially ways to break out of the rooted tree) hence I recommend that unless you really know what you're doing, use http.FileServer and http.Dir as they include checks to make sure people can't break out of the FS, which ServeFile doesn't.
Addendum
Your secondary question, how do you do a custom NotFound handler, unfortunately, is not easily answered. Because this is called from internal function serveFile as you noticed, there's no super easy place to break into that. There are potentially some sneaky things like intercepting the response with your own ResponseWriter which intercepts the 404 response code, but I'll leave that exercise to you.
Here a handler which sends a redirect to "/" if file is not found. This comes in handy when adding a fallback for an Angular application, as suggested here, which is served from within a golang service.
Note: This code is not production ready. Only illustrative (at best :-)
package main
import "net/http"
type (
// FallbackResponseWriter wraps an http.Requesthandler and surpresses
// a 404 status code. In such case a given local file will be served.
FallbackResponseWriter struct {
WrappedResponseWriter http.ResponseWriter
FileNotFound bool
}
)
// Header returns the header of the wrapped response writer
func (frw *FallbackResponseWriter) Header() http.Header {
return frw.WrappedResponseWriter.Header()
}
// Write sends bytes to wrapped response writer, in case of FileNotFound
// It surpresses further writes (concealing the fact though)
func (frw *FallbackResponseWriter) Write(b []byte) (int, error) {
if frw.FileNotFound {
return len(b), nil
}
return frw.WrappedResponseWriter.Write(b)
}
// WriteHeader sends statusCode to wrapped response writer
func (frw *FallbackResponseWriter) WriteHeader(statusCode int) {
Log.Printf("INFO: WriteHeader called with code %d\n", statusCode)
if statusCode == http.StatusNotFound {
Log.Printf("INFO: Setting FileNotFound flag\n")
frw.FileNotFound = true
return
}
frw.WrappedResponseWriter.WriteHeader(statusCode)
}
// AddFallbackHandler wraps the handler func in another handler func covering authentication
func AddFallbackHandler(handler http.HandlerFunc, filename string) http.HandlerFunc {
Log.Printf("INFO: Creating fallback handler")
return func(w http.ResponseWriter, r *http.Request) {
Log.Printf("INFO: Wrapping response writer in fallback response writer")
frw := FallbackResponseWriter{
WrappedResponseWriter: w,
FileNotFound: false,
}
handler(&frw, r)
if frw.FileNotFound {
Log.Printf("INFO: Serving fallback")
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
}
It can be added as in this example (using goji as mux):
mux.Handle(pat.Get("/*"),
AddFallbackHandler(http.FileServer(http.Dir("./html")).ServeHTTP, "/"))

Resources