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

Side by Side 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: remove unecessary type assertion in tests 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file.
4
5 // Package router provides an HTTP router with support for middleware and
6 // subrouters. It wraps around julienschmidt/httprouter.
7 package router
8
9 import (
10 "net/http"
11 "strings"
12
13 "github.com/julienschmidt/httprouter"
14 "golang.org/x/net/context"
15 )
16
17 type (
Vadim Sh. 2016/06/16 22:47:14 same here
nishanths 2016/06/16 23:33:00 Done.
18 // Router is the main type for the package. To create a Router, use New.
19 Router struct {
20 hrouter *httprouter.Router
21 middleware MiddlewareChain
22 BasePath string
23 }
24
25 // Context contains the context, response writer, request, and params sh ared
26 // across Middleware and Handler functions.
27 Context struct {
28 context.Context
29 writer http.ResponseWriter
Vadim Sh. 2016/06/16 22:47:14 I think it should be mockable (e.g. be "Writer htt
nodir 2016/06/16 23:04:47 +1, I didn't notice there is no NewContext that ac
nishanths 2016/06/16 23:33:00 Done.
30 Request *http.Request
31 Params httprouter.Params
32 status int
33 }
34 )
35
36 var (
37 _ http.Handler = (*Router)(nil)
38 _ http.ResponseWriter = (*Context)(nil)
39 _ context.Context = (*Context)(nil)
40 )
41
42 // New creates a Router.
43 func New() *Router {
44 return &Router{
45 hrouter: httprouter.New(),
46 BasePath: "/",
47 }
48 }
49
50 // Use adds middleware chains to the group. The added middleware applies to
51 // all handlers registered on the router and to all handlers registered on
52 // routers that may be derived from the router (using Subrouter).
53 func (r *Router) Use(mc MiddlewareChain) {
54 r.middleware = append(r.middleware, mc...)
55 }
56
57 // Subrouter creates a new router with an updated base path.
58 // The new router copies middleware and configuration from the
59 // router it derives from.
60 func (r *Router) Subrouter(relativePath string) *Router {
61 newRouter := &Router{
62 hrouter: r.hrouter,
63 BasePath: makeBasePath(r.BasePath, relativePath),
64 }
65 if len(r.middleware) > 0 {
66 newRouter.middleware = make(MiddlewareChain, len(r.middleware))
67 copy(newRouter.middleware, r.middleware)
68 }
69 return newRouter
70 }
71
72 // GET is a shortcut for router.Handle("GET", path, mc, h)
73 func (r *Router) GET(path string, mc MiddlewareChain, h Handler) {
74 r.Handle("GET", path, mc, h)
75 }
76
77 // HEAD is a shortcut for router.Handle("HEAD", path, mc, h)
78 func (r *Router) HEAD(path string, mc MiddlewareChain, h Handler) {
79 r.Handle("HEAD", path, mc, h)
80 }
81
82 // OPTIONS is a shortcut for router.Handle("OPTIONS", path, mc, h)
83 func (r *Router) OPTIONS(path string, mc MiddlewareChain, h Handler) {
84 r.Handle("OPTIONS", path, mc, h)
85 }
86
87 // POST is a shortcut for router.Handle("POST", path, mc, h)
88 func (r *Router) POST(path string, mc MiddlewareChain, h Handler) {
89 r.Handle("POST", path, mc, h)
90 }
91
92 // PUT is a shortcut for router.Handle("PUT", path, mc, h)
93 func (r *Router) PUT(path string, mc MiddlewareChain, h Handler) {
94 r.Handle("PUT", path, mc, h)
95 }
96
97 // PATCH is a shortcut for router.Handle("PATCH", path, mc, h)
98 func (r *Router) PATCH(path string, mc MiddlewareChain, h Handler) {
99 r.Handle("PATCH", path, mc, h)
100 }
101
102 // DELETE is a shortcut for router.Handle("DELETE", path, mc, h)
103 func (r *Router) DELETE(path string, mc MiddlewareChain, h Handler) {
104 r.Handle("DELETE", path, mc, h)
105 }
106
107 // Handle registers a middleware chain and a handler for the given method and
108 // path. len(mc)==0 is allowed. See https://godoc.org/github.com/julienschmidt/h ttprouter
109 // for documentation on how the path may be formatted.
110 func (r *Router) Handle(method, path string, mc MiddlewareChain, h Handler) {
111 handle := r.adapt(mc, h)
112 r.hrouter.Handle(method, httprouter.CleanPath(r.BasePath+path), handle)
113 }
114
115 // ServeHTTP makes Router implement the http.Handler interface.
116 func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
117 r.hrouter.ServeHTTP(rw, req)
118 }
119
120 // adapt adapts given middleware chain and handler into a httprouter-style handl e.
121 func (r *Router) adapt(mc MiddlewareChain, h Handler) httprouter.Handle {
122 return httprouter.Handle(func(rw http.ResponseWriter, req *http.Request, p httprouter.Params) {
123 // TODO(maybe): Use a free list for making Contexts.
Vadim Sh. 2016/06/16 22:47:14 IMHO, not worth it, it's lightweight object and it
nishanths 2016/06/16 23:33:00 Cool. Removed the TODO comment.
124 run(&Context{
125 Context: context.Background(),
126 writer: rw,
127 Request: req,
128 Params: p,
129 }, r.middleware, mc, h)
130 })
131 }
132
133 func makeBasePath(base, relative string) string {
134 path := httprouter.CleanPath(base + relative)
135 if !strings.HasSuffix(path, "/") {
136 path += "/"
137 }
138 return path
139 }
140
141 // Header is used to implement http.ResponseWriter interface.
142 func (c *Context) Header() http.Header {
143 return c.writer.Header()
144 }
145
146 // Write is used to implement http.ResponseWriter interface.
147 // If called from a Middleware function, the rules mentioned in the
148 // documentation for Middleware must be followed.
149 func (c *Context) Write(p []byte) (int, error) {
150 if !c.Written() {
151 c.status = http.StatusOK
152 }
153 return c.writer.Write(p)
154 }
155
156 // WriteHeader is used to implement http.ResponseWriter interface.
157 // If called from a Middleware function, the rules mentioned in the
158 // documentation for Middleware must be followed.
159 func (c *Context) WriteHeader(code int) {
160 if !c.Written() {
161 c.status = code
162 }
163 c.writer.WriteHeader(code)
164 }
165
166 // StatusCode returns the written HTTP status code or 0 if it was not written.
167 func (c *Context) StatusCode() int {
168 return c.status
169 }
170
171 // Written indicates whether a HTTP response has been written.
172 func (c *Context) Written() bool {
173 return c.status != 0
174 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698