Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1964)

Unified Diff: server/router/router.go

Issue 2043423004: Make HTTP middleware easier to use (Closed) Base URL: https://github.com/luci/luci-go@master
Patch Set: router: Do not allocate merged array in Handle Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: server/router/router.go
diff --git a/server/router/router.go b/server/router/router.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c95e44f92f5b6c0c57195609fd4576edfa0ca18
--- /dev/null
+++ b/server/router/router.go
@@ -0,0 +1,165 @@
+// Copyright 2016 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package router
+
+import (
+ "net/http"
+
+ "github.com/julienschmidt/httprouter"
+ "golang.org/x/net/context"
+)
+
+type (
+ // Router is the main type for the package. To create a Router, use New.
+ Router struct {
+ hrouter *httprouter.Router
+ middleware MiddlewareChain
+ BasePath string
+ parent *Router
nodir 2016/06/15 20:14:43 why do you need parent? is not used and seems unne
nishanths 2016/06/16 00:14:18 Done. Remnants of old stuff I was trying, thanks f
+ }
+
+ // Context contains the context, response writer, request, and params shared
+ // across Middleware and Handler functions.
+ Context struct {
+ context.Context
+ writer http.ResponseWriter
+ Request *http.Request
+ Params httprouter.Params
+ status int
+ }
+)
+
+var (
+ _ http.Handler = (*Router)(nil)
+ _ http.ResponseWriter = (*Context)(nil)
+ _ context.Context = (*Context)(nil)
+)
+
+// New creates a Router.
+func New() *Router {
+ return &Router{
+ hrouter: httprouter.New(),
+ BasePath: "/",
+ }
+}
+
+// Use adds middleware chains to the group. The added middleware applies to
+// all handlers registered on the router and to all handlers handlers registered on
nodir 2016/06/15 20:14:43 s/handlers handlers/handlers/
nishanths 2016/06/16 00:14:18 Done.
+// routers derived from the router (using Group).
nodir 2016/06/15 20:14:44 s/derived/that will be derived/ because Group copi
nishanths 2016/06/16 00:14:19 Done.
+func (r *Router) Use(mc MiddlewareChain) {
nodir 2016/06/15 20:14:43 consider `...Middleware`
nishanths 2016/06/16 00:14:18 +1 Will reconsider while refactoring the rest of t
+ r.middleware = append(r.middleware, mc...)
+}
+
+// Group creates a new router with an updated base path.
+// The new router carries over configuration from the router it derives
+// from.
+func (r *Router) Group(relativePath string) *Router {
+ newRouter := &Router{
+ hrouter: r.hrouter,
+ BasePath: httprouter.CleanPath(r.BasePath + relativePath),
nodir 2016/06/15 20:14:44 this line assumes that BasePath ends with "/" at t
nishanths 2016/06/16 00:14:18 Fixed now. See the makeBasePath function. I'm stil
+ parent: r,
+ }
+ if len(r.middleware) > 0 {
+ newRouter.middleware = make(MiddlewareChain, len(r.middleware))
+ copy(newRouter.middleware, r.middleware)
+ }
+ return newRouter
+}
+
+// GET is a shortcut for router.Handle("GET", mc, h)
+func (r *Router) GET(path string, mc MiddlewareChain, h Handler) {
+ r.Handle("GET", path, mc, h)
+}
+
+// HEAD is a shortcut for router.Handle("HEAD", mc, h)
+func (r *Router) HEAD(path string, mc MiddlewareChain, h Handler) {
+ r.Handle("HEAD", path, mc, h)
+}
+
+// OPTIONS is a shortcut for router.Handle("OPTIONS", mc, h)
+func (r *Router) OPTIONS(path string, mc MiddlewareChain, h Handler) {
+ r.Handle("OPTIONS", path, mc, h)
+}
+
+// POST is a shortcut for router.Handle("POST", mc, h)
+func (r *Router) POST(path string, mc MiddlewareChain, h Handler) {
+ r.Handle("POST", path, mc, h)
+}
+
+// PUT is a shortcut for router.Handle("PUT", mc, h)
+func (r *Router) PUT(path string, mc MiddlewareChain, h Handler) {
+ r.Handle("PUT", path, mc, h)
+}
+
+// PATCH is a shortcut for router.Handle("PATCH", mc, h)
+func (r *Router) PATCH(path string, mc MiddlewareChain, h Handler) {
+ r.Handle("PATCH", path, mc, h)
+}
+
+// DELETE is a shortcut for router.Handle("DELETE", mc, h)
+func (r *Router) DELETE(path string, mc MiddlewareChain, h Handler) {
+ r.Handle("DELETE", path, mc, h)
+}
+
+// Handle registers a middleware chain and a handler for the given method and
+// path. len(mc)==0 is allowed.
+func (r *Router) Handle(method, path string, mc MiddlewareChain, h Handler) {
+ handle := r.adapt(mc, h)
+ r.hrouter.Handle(method, httprouter.CleanPath(r.BasePath+path), handle)
+}
+
+// ServeHTTP makes Router implement the http.Handler interface.
+func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+ r.hrouter.ServeHTTP(rw, req)
+}
+
+// adapt adapts given middleware chain and handler into a httprouter-style handle.
+func (r *Router) adapt(mc MiddlewareChain, h Handler) httprouter.Handle {
+ return httprouter.Handle(func(rw http.ResponseWriter, req *http.Request, p httprouter.Params) {
+ // TODO(maybe): Use a free list for making Contexts.
+ run(NewContext(context.Background(), rw, req, p), r.middleware, mc, h)
+ })
+}
+
+// NewContext creates a Context that with the given parameters.
nodir 2016/06/15 20:14:43 s/that //
nishanths 2016/06/16 00:14:18 Done.
+func NewContext(c context.Context, rw http.ResponseWriter, req *http.Request, p httprouter.Params) *Context {
+ return &Context{
+ Context: c,
+ writer: rw,
+ Request: req,
+ Params: p,
+ }
+}
+
+// Header is used to implement http.ResponseWriter interface on Context.
nodir 2016/06/15 20:14:43 s/ on Context//g
nishanths 2016/06/16 00:14:18 Done.
+func (c *Context) Header() http.Header {
+ return c.writer.Header()
+}
+
+// Write is used to implement http.ResponseWriter interface on Context.
+// If called from a Middleware function, the rules mentioned in the
+// documentation for Middleware must be followed.
+func (c *Context) Write(p []byte) (int, error) {
+ c.status = http.StatusOK
nodir 2016/06/15 20:14:44 do it only if c.status == 0
nishanths 2016/06/16 00:14:18 Done.
+ return c.writer.Write(p)
+}
+
+// WriteHeader is used to implement http.ResponseWriter interface on Context.
+// If called from a Middleware function, the rules mentioned in the
+// documentation for Middleware must be followed.
+func (c *Context) WriteHeader(code int) {
+ c.status = code
nodir 2016/06/15 20:14:44 do it only if c.status == 0
nishanths 2016/06/16 00:14:18 Done.
+ c.writer.WriteHeader(code)
+}
+
+// StatusCode returns the written HTTP status code.
nodir 2016/06/15 20:14:43 " or 0 if it was not written"
nishanths 2016/06/16 00:14:18 Done.
+func (c *Context) StatusCode() int {
+ return c.status
+}
+
+// Written indicates whether a HTTP response has been written.
+func (c *Context) Written() bool {
+ return c.status != 0
+}

Powered by Google App Engine
This is Rietveld 408576698