I just read the article: Build You Own Web Framework In Go and for sharing values among handlers I picked the context.Context and I'm using it in the following way to share values across handlers and middlewares:
type appContext struct {
db *sql.DB
ctx context.Context
cancel context.CancelFunc
}
func (c *appContext)authHandler(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request {
defer c.cancel() //this feels weird
authToken := r.Header.Get("Authorization") // this fakes a form
c.ctx = getUser(c.ctx, c.db, authToken) // this also feels weird
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
func (c *appContext)adminHandler(w http.ResponseWriter, r *http.Request) {
defer c.cancel()
user := c.ctx.Value(0).(user)
json.NewEncoder(w).Encode(user)
}
func getUser(ctx context.Context, db *sql.DB, token string) context.Context{
//this function mimics a database access
return context.WithValue(ctx, 0, user{Nome:"Default user"})
}
func main() {
db, err := sql.Open("my-driver", "my.db")
if err != nil {
panic(err)
}
ctx, cancel := context.WithCancel(context.Background())
appC := appContext{db, ctx, cancel}
//....
}
Everything is working and handlers are loading faster than using gorilla/context So my questions are:
You have a problem with your code because you are storing the user into the app context. With multiple users at the same time, it doesn't work. The context must be related to the request to not be overwrote by other requests. The user must be stored in a request context. In my articles I use the following gorilla function: context.Set(r, "user", user)
. r
is the request.
If you want to use context.Context
in your app, you should use their gorilla wrapper (you can find it at the end of this article: https://blog.golang.org/context).
Also, you don't need a context with cancel. context.Background()
is okay for the root context.
Note: go 1.7.0-rc2 does clarify a bit how to release resources associated with Contexts (Sameer Ajmani):
Some users don't realize that creating a
Context
with aCancelFunc
attaches a subtree to the parent, and that that subtree is not released until theCancelFunc
is called or the parent is canceled.Make this clear early in the package docs, so that people learning about this package have the right conceptual model.
The documentation now includes:
Incoming requests to a server should create a
Context
, and outgoing calls to servers should accept aContext
.
The chain of function calls between them must propagate theContext
, optionally replacing it with a derivedContext
created usingWithCancel
,WithDeadline
,WithTimeout
, orWithValue
.
TheseContext
values form a tree: when aContext
is canceled, allContexts
derived from it are also canceled.The
WithCancel
,WithDeadline
, andWithTimeout
functions return a derivedContext
and aCancelFunc
.
Calling theCancelFunc
cancels the newContext
and any Contexts derived from it, removes theContext
from the parent's tree, and stops any associated timers.
Failing to call theCancelFunc
leaks the associated resources until the parentContext
is canceled or the timer fires.