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

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: Update documentation and improve code style 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
12 "github.com/julienschmidt/httprouter"
13 "golang.org/x/net/context"
14 )
15
16 type (
17 // Router is the main type for the package. To create a Router, use New.
18 Router struct {
19 hrouter *httprouter.Router
20 middleware MiddlewareChain
21 BasePath string
22 }
23
24 // Context contains the context, response writer, request, and params sh ared
25 // across Middleware and Handler functions.
26 Context struct {
27 context.Context
28 writer http.ResponseWriter
29 Request *http.Request
30 Params httprouter.Params
31 status int
32 }
33 )
34
35 var (
36 _ http.Handler = (*Router)(nil)
37 _ http.ResponseWriter = (*Context)(nil)
38 _ context.Context = (*Context)(nil)
39 )
40
41 // New creates a Router.
42 func New() *Router {
43 return &Router{
44 hrouter: httprouter.New(),
45 BasePath: "/",
46 }
47 }
48
49 // Use adds middleware chains to the group. The added middleware applies to
50 // all handlers registered on the router and to all handlers registered on
51 // routers that may be derived from the router (using Subrouter).
52 func (r *Router) Use(mc MiddlewareChain) {
53 r.middleware = append(r.middleware, mc...)
iannucci 2016/06/16 00:54:24 should you use your join function above for this?
nishanths 2016/06/16 03:43:29 No, because there is no need to force the allocati
54 }
55
56 // Subrouter creates a new router with an updated base path.
57 // The new router copies middleware and configuration from the
58 // router it derives from.
59 func (r *Router) Subrouter(relativePath string) *Router {
60 newRouter := &Router{
61 hrouter: r.hrouter,
62 BasePath: makeBasePath(r.BasePath, relativePath),
63 }
64 if len(r.middleware) > 0 {
65 newRouter.middleware = make(MiddlewareChain, len(r.middleware))
66 copy(newRouter.middleware, r.middleware)
67 }
68 return newRouter
69 }
70
71 // GET is a shortcut for router.Handle("GET", mc, h)
iannucci 2016/06/16 00:54:25 nit: missing 'path' in these comments
nishanths 2016/06/16 03:43:29 Done!
72 func (r *Router) GET(path string, mc MiddlewareChain, h Handler) {
73 r.Handle("GET", path, mc, h)
74 }
75
76 // HEAD is a shortcut for router.Handle("HEAD", mc, h)
77 func (r *Router) HEAD(path string, mc MiddlewareChain, h Handler) {
78 r.Handle("HEAD", path, mc, h)
79 }
80
81 // OPTIONS is a shortcut for router.Handle("OPTIONS", mc, h)
82 func (r *Router) OPTIONS(path string, mc MiddlewareChain, h Handler) {
83 r.Handle("OPTIONS", path, mc, h)
84 }
85
86 // POST is a shortcut for router.Handle("POST", mc, h)
87 func (r *Router) POST(path string, mc MiddlewareChain, h Handler) {
88 r.Handle("POST", path, mc, h)
89 }
90
91 // PUT is a shortcut for router.Handle("PUT", mc, h)
92 func (r *Router) PUT(path string, mc MiddlewareChain, h Handler) {
93 r.Handle("PUT", path, mc, h)
94 }
95
96 // PATCH is a shortcut for router.Handle("PATCH", mc, h)
97 func (r *Router) PATCH(path string, mc MiddlewareChain, h Handler) {
98 r.Handle("PATCH", path, mc, h)
99 }
100
101 // DELETE is a shortcut for router.Handle("DELETE", mc, h)
102 func (r *Router) DELETE(path string, mc MiddlewareChain, h Handler) {
103 r.Handle("DELETE", path, mc, h)
104 }
105
106 // Handle registers a middleware chain and a handler for the given method and
107 // path. len(mc)==0 is allowed.
iannucci 2016/06/16 00:54:24 may want to add a pointer to the format of path in
nishanths 2016/06/16 03:43:29 Done. There is no separate section on path formatt
108 func (r *Router) Handle(method, path string, mc MiddlewareChain, h Handler) {
109 handle := r.adapt(mc, h)
110 r.hrouter.Handle(method, httprouter.CleanPath(r.BasePath+path), handle)
111 }
112
113 // ServeHTTP makes Router implement the http.Handler interface.
114 func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
115 r.hrouter.ServeHTTP(rw, req)
116 }
117
118 // adapt adapts given middleware chain and handler into a httprouter-style handl e.
119 func (r *Router) adapt(mc MiddlewareChain, h Handler) httprouter.Handle {
120 return httprouter.Handle(func(rw http.ResponseWriter, req *http.Request, p httprouter.Params) {
121 // TODO(maybe): Use a free list for making Contexts.
122 run(NewContext(context.Background(), rw, req, p), r.middleware, mc, h)
123 })
124 }
125
126 func makeBasePath(base, relative string) string {
127 path := httprouter.CleanPath(base + relative)
128 // After CleanPath, len(path) >= 0.
iannucci 2016/06/16 00:54:25 what if base and relative are both ""?
nishanths 2016/06/16 03:43:29 Currently, makeBasePath("", "") => "/". We have a
129 if path[len(path)-1] != '/' {
130 path = path + "/"
131 }
132 return path
133 }
134
135 // NewContext creates a Context with the given parameters.
136 func NewContext(c context.Context, rw http.ResponseWriter, req *http.Request, p httprouter.Params) *Context {
iannucci 2016/06/16 00:54:25 is this for testing, or are users expected to call
nishanths 2016/06/16 03:43:29 Removing for now. I could not think of users wanti
137 return &Context{
138 Context: c,
139 writer: rw,
140 Request: req,
141 Params: p,
142 }
143 }
144
145 // Header is used to implement http.ResponseWriter interface.
146 func (c *Context) Header() http.Header {
147 return c.writer.Header()
148 }
149
150 // Write is used to implement http.ResponseWriter interface.
151 // If called from a Middleware function, the rules mentioned in the
152 // documentation for Middleware must be followed.
153 func (c *Context) Write(p []byte) (int, error) {
154 if c.status == 0 {
iannucci 2016/06/16 00:54:24 if !c.Written()
nishanths 2016/06/16 03:43:29 Done.
155 c.status = http.StatusOK
156 }
157 return c.writer.Write(p)
158 }
159
160 // WriteHeader is used to implement http.ResponseWriter interface.
161 // If called from a Middleware function, the rules mentioned in the
162 // documentation for Middleware must be followed.
163 func (c *Context) WriteHeader(code int) {
164 if c.status == 0 {
iannucci 2016/06/16 00:54:24 if !c.Written()
nishanths 2016/06/16 03:43:27 Done.
165 c.status = code
166 }
167 c.writer.WriteHeader(code)
168 }
169
170 // StatusCode returns the written HTTP status code or 0 if it was not written.
171 func (c *Context) StatusCode() int {
172 return c.status
173 }
174
175 // Written indicates whether a HTTP response has been written.
176 func (c *Context) Written() bool {
177 return c.status != 0
178 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698