Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| 6 | |
| 7 import ( | |
| 8 "net/http" | |
| 9 | |
| 10 "github.com/julienschmidt/httprouter" | |
| 11 "golang.org/x/net/context" | |
| 12 ) | |
| 13 | |
| 14 // TODO(nishanths): Implement adapter methods to convert http style handlers to Handlers. | |
| 15 | |
| 16 // Router represents the main router type. | |
| 17 type ( | |
| 18 // Router is the main type for the package. It contains the base path fo r | |
| 19 // the router, a list of handlers, and configuration. | |
|
dnj (Google)
2016/06/13 18:50:42
nit: Note that Router shouldn't be instantiated by
| |
| 20 Router struct { | |
| 21 hrouter *httprouter.Router | |
| 22 handlers []Handler | |
| 23 BasePath string | |
| 24 parent *Router | |
| 25 } | |
| 26 | |
| 27 // Context is the argument to a Handler method. It is used to carry the | |
| 28 // HTTP request and response writer between handlers, share context, | |
| 29 // and manage flow using the Next and Abort methods. | |
| 30 Context struct { | |
| 31 Context context.Context | |
| 32 Writer http.ResponseWriter | |
| 33 Request *http.Request | |
| 34 Params httprouter.Params | |
| 35 | |
| 36 handlers []Handler | |
| 37 index int | |
| 38 aborted bool | |
| 39 } | |
| 40 ) | |
| 41 | |
| 42 var ( | |
| 43 // To ensure that Router conforms to the http.Handler interface. | |
| 44 _ http.Handler = New() | |
|
dnj (Google)
2016/06/13 18:50:42
Use:
var _ http.Handler = (*Router)(nil)
rather t
iannucci
2016/06/13 19:23:29
Do:
_ http.Handler = (*Router)(nil)
As it will
| |
| 45 | |
| 46 // VoidHandler is a handler that has no effect. | |
| 47 VoidHandler Handler = func(_ *Context) {} | |
| 48 ) | |
| 49 | |
| 50 // New creates a Router with specified initial context. | |
| 51 func New() *Router { | |
| 52 return &Router{ | |
| 53 hrouter: httprouter.New(), | |
| 54 BasePath: "/", | |
| 55 } | |
| 56 } | |
| 57 | |
| 58 // Use adds middleware chains to the group. The added middleware applies to | |
| 59 // all routes in the current group and in groups derived from the current group. | |
| 60 func (r *Router) Use(handlers ...Handler) { | |
| 61 r.handlers = append(r.handlers, handlers...) | |
| 62 } | |
| 63 | |
| 64 // Group creates a new router with an updated base path. | |
| 65 // The new router carries over configuration from the router it derives | |
| 66 // from. | |
| 67 func (r *Router) Group(relativePath string) *Router { | |
|
dnj (Google)
2016/06/13 18:50:42
We should probably clean leading/trailing "/" from
nodir
2016/06/15 20:14:43
I think other routers use method names Path and Su
nishanths
2016/06/16 00:18:09
Chose 'Subrouter'. Open to changing the name if an
| |
| 68 h := make([]Handler, len(r.handlers)) | |
| 69 copy(h, r.handlers) | |
| 70 return &Router{ | |
| 71 hrouter: r.hrouter, | |
| 72 handlers: h, | |
| 73 BasePath: r.BasePath + relativePath, | |
| 74 parent: r, | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 // GET is a shortcut for router.Handle("GET", path, handlers) | |
| 79 func (r *Router) GET(path string, handlers ...Handler) { | |
| 80 r.Handle("GET", path, handlers...) | |
| 81 } | |
| 82 | |
| 83 // HEAD is a shortcut for router.Handle("HEAD", path, handlers) | |
| 84 func (r *Router) HEAD(path string, handlers ...Handler) { | |
| 85 r.Handle("HEAD", path, handlers...) | |
| 86 } | |
| 87 | |
| 88 // OPTIONS is a shortcut for router.Handle("OPTIONS", path, handlers) | |
| 89 func (r *Router) OPTIONS(path string, handlers ...Handler) { | |
| 90 r.Handle("OPTIONS", path, handlers...) | |
| 91 } | |
| 92 | |
| 93 // POST is a shortcut for router.Handle("POST", path, handlers) | |
| 94 func (r *Router) POST(path string, handlers ...Handler) { | |
| 95 r.Handle("POST", path, handlers...) | |
| 96 } | |
| 97 | |
| 98 // PUT is a shortcut for router.Handle("PUT", path, handlers) | |
| 99 func (r *Router) PUT(path string, handlers ...Handler) { | |
| 100 r.Handle("PUT", path, handlers...) | |
| 101 } | |
| 102 | |
| 103 // PATCH is a shortcut for router.Handle("PATCH", path, handlers) | |
| 104 func (r *Router) PATCH(path string, handlers ...Handler) { | |
| 105 r.Handle("PATCH", path, handlers...) | |
| 106 } | |
| 107 | |
| 108 // DELETE is a shortcut for router.Handle("DELETE", path, handlers) | |
| 109 func (r *Router) DELETE(path string, handlers ...Handler) { | |
| 110 r.Handle("DELETE", path, handlers...) | |
| 111 } | |
| 112 | |
| 113 // Handle registers handlers for the given method and path. Handle panics if | |
| 114 // no handlers are provided. | |
| 115 func (r *Router) Handle(method, path string, handlers ...Handler) { | |
| 116 if len(handlers) == 0 { | |
| 117 panic("router: at least one handler should be specified") | |
| 118 } | |
| 119 h := r.adapt(handlers) | |
| 120 r.hrouter.Handle(method, path, h) | |
| 121 } | |
| 122 | |
| 123 // ServeHTTP makes Router implement the http.Handler interface. | |
| 124 func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) { | |
| 125 r.hrouter.ServeHTTP(rw, req) | |
| 126 } | |
| 127 | |
| 128 // copyHandlers allocates a new array and copies the | |
| 129 // the handlers from the two handler lists into it. | |
| 130 func copyHandlers(m, n []Handler) []Handler { | |
| 131 totalLen := len(m) + len(n) | |
| 132 merged := make([]Handler, totalLen) | |
|
dnj (Google)
2016/06/13 18:50:42
nit: probably cleaner to just allocate size 0, cap
nishanths
2016/06/15 18:25:08
Acknowledged.
| |
| 133 copy(merged, m) | |
| 134 copy(merged[len(m):], n) | |
| 135 return merged | |
| 136 } | |
| 137 | |
| 138 // adapt adapts a list of handlers into a httprouter-style handler. | |
| 139 func (r *Router) adapt(handlers []Handler) httprouter.Handle { | |
| 140 return httprouter.Handle(func(rw http.ResponseWriter, req *http.Request, p httprouter.Params) { | |
| 141 // TODO(maybe): Use a free list for making Contexts. | |
| 142 c := &Context{ | |
| 143 Context: context.Background(), | |
| 144 Writer: rw, | |
| 145 Request: req, | |
| 146 Params: p, | |
| 147 handlers: copyHandlers(r.handlers, handlers), | |
|
dnj (Google)
2016/06/13 18:50:42
Do we really need to allocate/copy handlers each t
nishanths
2016/06/15 18:25:08
Thanks for catching this. Done.
| |
| 148 index: -1, | |
| 149 } | |
| 150 c.Next() | |
| 151 }) | |
| 152 } | |
| 153 | |
| 154 // Next executes the next handler in the chain. If Next is called from inside a | |
| 155 // handler, the next registered handler begins execution immdediately. | |
|
dnj (Google)
2016/06/13 18:50:42
nit: two spaces.
| |
| 156 func (c *Context) Next() { | |
| 157 c.index++ | |
| 158 for ; c.index < len(c.handlers) && !c.aborted; c.index++ { | |
| 159 c.handlers[c.index](c) | |
| 160 } | |
| 161 } | |
|
iannucci
2016/06/13 19:23:29
Is the intended usecase of this to allow a middlew
| |
| 162 | |
| 163 // Abort prevents upcoming handlers from being executed. Calling Abort inside a | |
| 164 // handler however does not stop the execution of the current handler. | |
| 165 func (c *Context) Abort() { | |
| 166 c.aborted = true | |
|
dnj (Google)
2016/06/13 18:50:42
Just thinking here, but maybe we want "Abort()" to
nodir
2016/06/13 20:20:52
perhaps it may be simple to remove Abort and make
nodir
2016/06/13 20:26:48
and if you do that, replace "index" field in Conte
nodir
2016/06/13 20:29:36
or rather remove handlers from Context (because re
| |
| 167 } | |
|
iannucci
2016/06/13 19:23:29
Yeah, I'm not convinced this is a good/safe api :/
| |
| OLD | NEW |