diff --git a/vendor/github.com/go-chi/chi/CHANGELOG.md b/vendor/github.com/go-chi/chi/CHANGELOG.md index cd2139b6..5f0ab254 100644 --- a/vendor/github.com/go-chi/chi/CHANGELOG.md +++ b/vendor/github.com/go-chi/chi/CHANGELOG.md @@ -1,6 +1,36 @@ # Changelog -## v3.1.5 +## v3.3.2 (2017-12-22) + +- Support to route trailing slashes on mounted sub-routers (#281) +- middleware: new `ContentCharset` to check matching charsets. Thank you + @csucu for your community contribution! + + +## v3.3.1 (2017-11-20) + +- middleware: new `AllowContentType` handler for explicit whitelist of accepted request Content-Types +- middleware: new `SetHeader` handler for short-hand middleware to set a response header key/value +- Minor bug fixes + + +## v3.3.0 (2017-10-10) + +- New chi.RegisterMethod(method) to add support for custom HTTP methods, see _examples/custom-method for usage +- Deprecated LINK and UNLINK methods from the default list, please use `chi.RegisterMethod("LINK")` and `chi.RegisterMethod("UNLINK")` in an `init()` function + + +## v3.2.1 (2017-08-31) + +- Add new `Match(rctx *Context, method, path string) bool` method to `Routes` interface + and `Mux`. Match searches the mux's routing tree for a handler that matches the method/path +- Add new `RouteMethod` to `*Context` +- Add new `Routes` pointer to `*Context` +- Add new `middleware.GetHead` to route missing HEAD requests to GET handler +- Updated benchmarks (see README) + + +## v3.1.5 (2017-08-02) - Setup golint and go vet for the project - As per golint, we've redefined `func ServerBaseContext(h http.Handler, baseCtx context.Context) http.Handler` diff --git a/vendor/github.com/go-chi/chi/LICENSE b/vendor/github.com/go-chi/chi/LICENSE index 7b5f914b..d99f02ff 100644 --- a/vendor/github.com/go-chi/chi/LICENSE +++ b/vendor/github.com/go-chi/chi/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka) +Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka), Google Inc. MIT License diff --git a/vendor/github.com/go-chi/chi/README.md b/vendor/github.com/go-chi/chi/README.md index a2cf2f59..c71a3a01 100644 --- a/vendor/github.com/go-chi/chi/README.md +++ b/vendor/github.com/go-chi/chi/README.md @@ -24,7 +24,7 @@ included some useful/optional subpackages: [middleware](/middleware), [render](h ## Features -* **Lightweight** - cloc'd in <1000 LOC for the chi router +* **Lightweight** - cloc'd in ~1000 LOC for the chi router * **Fast** - yes, see [benchmarks](#benchmarks) * **100% compatible with net/http** - use any http or middleware pkg in the ecosystem that is also compatible with `net/http` * **Designed for modular/composable APIs** - middlewares, inline middlewares, route groups and subrouter mounting @@ -109,7 +109,7 @@ func main() { // Regexp url parameters: r.Get("/{articleSlug:[a-z-]+}", getArticleBySlug) // GET /articles/home-is-toronto - + // Subrouters: r.Route("/{articleID}", func(r chi.Router) { r.Use(ArticleCtx) @@ -239,6 +239,11 @@ type Routes interface { // Middlewares returns the list of middlewares in use by the router. Middlewares() Middlewares + + // Match searches the routing tree for a handler that matches + // the method/path - similar to routing a http request, but without + // executing the handler thereafter. + Match(rctx *Context, method, path string) bool } ``` @@ -310,32 +315,46 @@ chi comes equipped with an optional `middleware` package, providing a suite of s `net/http` middlewares. Please note, any middleware in the ecosystem that is also compatible with `net/http` can be used with chi's mux. ----------------------------------------------------------------------------------------------------------- -| Middleware | Description | -|:---------------------|:--------------------------------------------------------------------------------- -| RequestID | Injects a request ID into the context of each request. | -| RealIP | Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP. | -| Logger | Logs the start and end of each request with the elapsed processing time. | -| Recoverer | Gracefully absorb panics and prints the stack trace. | -| NoCache | Sets response headers to prevent clients from caching. | -| Timeout | Signals to the request context when the timeout deadline is reached. | -| Throttle | Puts a ceiling on the number of concurrent requests. | -| Compress | Gzip compression for clients that accept compressed responses. | -| Profiler | Easily attach net/http/pprof to your routers. | -| StripSlashes | Strip slashes on routing paths. | -| RedirectSlashes | Redirect slashes on routing paths. | -| WithValue | Short-hand middleware to set a key/value on the request context. | -| Heartbeat | Monitoring endpoint to check the servers pulse. | ----------------------------------------------------------------------------------------------------------- +### Core middlewares -Other cool community net/http middlewares: +----------------------------------------------------------------------------------------------------------- +| chi/middleware Handler | description | +|:----------------------|:--------------------------------------------------------------------------------- +| AllowContentType | Explicit whitelist of accepted request Content-Types | +| Compress | Gzip compression for clients that accept compressed responses | +| GetHead | Automatically route undefined HEAD requests to GET handlers | +| Heartbeat | Monitoring endpoint to check the servers pulse | +| Logger | Logs the start and end of each request with the elapsed processing time | +| NoCache | Sets response headers to prevent clients from caching | +| Profiler | Easily attach net/http/pprof to your routers | +| RealIP | Sets a http.Request's RemoteAddr to either X-Forwarded-For or X-Real-IP | +| Recoverer | Gracefully absorb panics and prints the stack trace | +| RequestID | Injects a request ID into the context of each request | +| RedirectSlashes | Redirect slashes on routing paths | +| SetHeader | Short-hand middleware to set a response header key/value | +| StripSlashes | Strip slashes on routing paths | +| Throttle | Puts a ceiling on the number of concurrent requests | +| Timeout | Signals to the request context when the timeout deadline is reached | +| URLFormat | Parse extension from url and put it on request context | +| WithValue | Short-hand middleware to set a key/value on the request context | +----------------------------------------------------------------------------------------------------------- -* [jwtauth](https://github.com/goware/jwtauth) - JWT authenticator -* [cors](https://github.com/goware/cors) - CORS middleware -* [httpcoala](https://github.com/goware/httpcoala) - Request coalescer -* [chi-authz](https://github.com/casbin/chi-authz) - Authorization middleware built on https://github.com/hsluoyz/casbin +### Auxiliary middlewares & packages -please [submit a PR](./CONTRIBUTING.md) if you'd like to include a link to a chi middleware +Please see https://github.com/go-chi for additional packages. + +-------------------------------------------------------------------------------------------------------------------- +| package | description | +|:---------------------------------------------------|:------------------------------------------------------------- +| [cors](https://github.com/go-chi/cors) | Cross-origin resource sharing (CORS) | +| [jwtauth](https://github.com/go-chi/jwtauth) | JWT authentication | +| [hostrouter](https://github.com/go-chi/hostrouter) | Domain/host based request routing | +| [httpcoala](https://github.com/go-chi/httpcoala) | HTTP request coalescer | +| [chi-authz](https://github.com/casbin/chi-authz) | Request ACL via https://github.com/hsluoyz/casbin | +| [phi](https://github.com/fate-lovely/phi) | Port chi to [fasthttp](https://github.com/valyala/fasthttp) | +-------------------------------------------------------------------------------------------------------------------- + +please [submit a PR](./CONTRIBUTING.md) if you'd like to include a link to a chi-compatible middleware ## context? @@ -355,27 +374,29 @@ and.. The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark -Comparison with other routers (as of June 21, 2017): https://gist.github.com/pkieltyka/c089f309abeb179cfc4deaa519956d8c +Results as of Aug 31, 2017 on Go 1.9.0 ```shell -BenchmarkChi_Param 3000000 427 ns/op 304 B/op 2 allocs/op -BenchmarkChi_Param5 2000000 631 ns/op 304 B/op 2 allocs/op -BenchmarkChi_Param20 1000000 1343 ns/op 304 B/op 2 allocs/op -BenchmarkChi_ParamWrite 3000000 477 ns/op 304 B/op 2 allocs/op -BenchmarkChi_GithubStatic 3000000 452 ns/op 304 B/op 2 allocs/op -BenchmarkChi_GithubParam 2000000 616 ns/op 304 B/op 2 allocs/op -BenchmarkChi_GithubAll 10000 130637 ns/op 61716 B/op 406 allocs/op -BenchmarkChi_GPlusStatic 3000000 415 ns/op 304 B/op 2 allocs/op -BenchmarkChi_GPlusParam 3000000 465 ns/op 304 B/op 2 allocs/op -BenchmarkChi_GPlus2Params 3000000 548 ns/op 304 B/op 2 allocs/op -BenchmarkChi_GPlusAll 200000 6895 ns/op 3952 B/op 26 allocs/op -BenchmarkChi_ParseStatic 3000000 407 ns/op 304 B/op 2 allocs/op -BenchmarkChi_ParseParam 3000000 451 ns/op 304 B/op 2 allocs/op -BenchmarkChi_Parse2Params 3000000 504 ns/op 304 B/op 2 allocs/op -BenchmarkChi_ParseAll 100000 13221 ns/op 7904 B/op 52 allocs/op -BenchmarkChi_StaticAll 20000 84327 ns/op 47731 B/op 314 allocs/op +BenchmarkChi_Param 3000000 607 ns/op 432 B/op 3 allocs/op +BenchmarkChi_Param5 2000000 935 ns/op 432 B/op 3 allocs/op +BenchmarkChi_Param20 1000000 1944 ns/op 432 B/op 3 allocs/op +BenchmarkChi_ParamWrite 2000000 664 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GithubStatic 2000000 627 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GithubParam 2000000 847 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GithubAll 10000 175556 ns/op 87700 B/op 609 allocs/op +BenchmarkChi_GPlusStatic 3000000 566 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GPlusParam 2000000 652 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GPlus2Params 2000000 767 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GPlusAll 200000 9794 ns/op 5616 B/op 39 allocs/op +BenchmarkChi_ParseStatic 3000000 590 ns/op 432 B/op 3 allocs/op +BenchmarkChi_ParseParam 2000000 656 ns/op 432 B/op 3 allocs/op +BenchmarkChi_Parse2Params 2000000 715 ns/op 432 B/op 3 allocs/op +BenchmarkChi_ParseAll 100000 18045 ns/op 11232 B/op 78 allocs/op +BenchmarkChi_StaticAll 10000 108871 ns/op 67827 B/op 471 allocs/op ``` +Comparison with other routers: https://gist.github.com/pkieltyka/c089f309abeb179cfc4deaa519956d8c + NOTE: the allocs in the benchmark above are from the calls to http.Request's `WithContext(context.Context)` method that clones the http.Request, sets the `Context()` on the duplicated (alloc'd) request and returns it the new request object. This is just diff --git a/vendor/github.com/go-chi/chi/chi.go b/vendor/github.com/go-chi/chi/chi.go index 9f8d38a4..9962229d 100644 --- a/vendor/github.com/go-chi/chi/chi.go +++ b/vendor/github.com/go-chi/chi/chi.go @@ -27,6 +27,32 @@ // // See github.com/go-chi/chi/_examples/ for more in-depth examples. // +// URL patterns allow for easy matching of path components in HTTP +// requests. The matching components can then be accessed using +// chi.URLParam(). All patterns must begin with a slash. +// +// A simple named placeholder {name} matches any sequence of characters +// up to the next / or the end of the URL. Trailing slashes on paths must +// be handled explicitly. +// +// A placeholder with a name followed by a colon allows a regular +// expression match, for example {number:\\d+}. The regular expression +// syntax is Go's normal regexp RE2 syntax, except that regular expressions +// including { or } are not supported, and / will never be +// matched. An anonymous regexp pattern is allowed, using an empty string +// before the colon in the placeholder, such as {:\\d+} +// +// The special placeholder of asterisk matches the rest of the requested +// URL. Any trailing characters in the pattern are ignored. This is the only +// placeholder which will match / characters. +// +// Examples: +// "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/" +// "/user/{name}/info" matches "/user/jsmith/info" +// "/page/*" matches "/page/intro/latest" +// "/page/*/index" also matches "/page/intro/latest" +// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01" +// package chi import "net/http" @@ -96,6 +122,11 @@ type Routes interface { // Middlewares returns the list of middlewares in use by the router. Middlewares() Middlewares + + // Match searches the routing tree for a handler that matches + // the method/path - similar to routing a http request, but without + // executing the handler thereafter. + Match(rctx *Context, method, path string) bool } // Middlewares type is a slice of standard middleware handlers with methods diff --git a/vendor/github.com/go-chi/chi/context.go b/vendor/github.com/go-chi/chi/context.go index 155dcc9e..30c5afed 100644 --- a/vendor/github.com/go-chi/chi/context.go +++ b/vendor/github.com/go-chi/chi/context.go @@ -16,9 +16,12 @@ var ( // request context to track route patterns, URL parameters and // an optional routing path. type Context struct { - // Routing path override used during the route search. + Routes Routes + + // Routing path/method override used during the route search. // See Mux#routeHTTP method. - RoutePath string + RoutePath string + RouteMethod string // Routing pattern stack throughout the lifecycle of the request, // across all connected routers. It is a record of all matching @@ -48,9 +51,11 @@ func NewRouteContext() *Context { return &Context{} } -// reset a routing context to its initial state. -func (x *Context) reset() { +// Reset a routing context to its initial state. +func (x *Context) Reset() { + x.Routes = nil x.RoutePath = "" + x.RouteMethod = "" x.RoutePatterns = x.RoutePatterns[:0] x.URLParams.Keys = x.URLParams.Keys[:0] x.URLParams.Values = x.URLParams.Values[:0] diff --git a/vendor/github.com/go-chi/chi/middleware/closenotify18.go b/vendor/github.com/go-chi/chi/middleware/closenotify18.go index ec54bcaa..4f0d73cc 100644 --- a/vendor/github.com/go-chi/chi/middleware/closenotify18.go +++ b/vendor/github.com/go-chi/chi/middleware/closenotify18.go @@ -1,4 +1,4 @@ -// +build go1.8 +// +build go1.8 appengine package middleware diff --git a/vendor/github.com/go-chi/chi/middleware/compress18.go b/vendor/github.com/go-chi/chi/middleware/compress18.go index ad12b14a..0048f7d9 100644 --- a/vendor/github.com/go-chi/chi/middleware/compress18.go +++ b/vendor/github.com/go-chi/chi/middleware/compress18.go @@ -1,4 +1,4 @@ -// +build go1.8 +// +build go1.8 appengine package middleware diff --git a/vendor/github.com/go-chi/chi/middleware/content_charset.go b/vendor/github.com/go-chi/chi/middleware/content_charset.go new file mode 100644 index 00000000..07b5ce6f --- /dev/null +++ b/vendor/github.com/go-chi/chi/middleware/content_charset.go @@ -0,0 +1,51 @@ +package middleware + +import ( + "net/http" + "strings" +) + +// ContentCharset generates a handler that writes a 415 Unsupported Media Type response if none of the charsets match. +// An empty charset will allow requests with no Content-Type header or no specified charset. +func ContentCharset(charsets ...string) func(next http.Handler) http.Handler { + for i, c := range charsets { + charsets[i] = strings.ToLower(c) + } + + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if !contentEncoding(r.Header.Get("Content-Type"), charsets...) { + w.WriteHeader(http.StatusUnsupportedMediaType) + return + } + + next.ServeHTTP(w, r) + }) + } +} + +// Check the content encoding against a list of acceptable values. +func contentEncoding(ce string, charsets ...string) bool { + _, ce = split(strings.ToLower(ce), ";") + _, ce = split(ce, "charset=") + ce, _ = split(ce, ";") + for _, c := range charsets { + if ce == c { + return true + } + } + + return false +} + +// Split a string in two parts, cleaning any whitespace. +func split(str, sep string) (string, string) { + var a, b string + var parts = strings.SplitN(str, sep, 2) + a = strings.TrimSpace(parts[0]) + if len(parts) == 2 { + b = strings.TrimSpace(parts[1]) + } + + return a, b +} diff --git a/vendor/github.com/go-chi/chi/middleware/content_type.go b/vendor/github.com/go-chi/chi/middleware/content_type.go new file mode 100644 index 00000000..3a2dc20a --- /dev/null +++ b/vendor/github.com/go-chi/chi/middleware/content_type.go @@ -0,0 +1,45 @@ +package middleware + +import ( + "net/http" + "strings" +) + +// SetHeader is a convenience handler to set a response header key/value +func SetHeader(key, value string) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + w.Header().Set(key, value) + next.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) + } +} + +// AllowContentType enforces a whitelist of request Content-Types otherwise responds +// with a 415 Unsupported Media Type status. +func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler { + cT := []string{} + for _, t := range contentTypes { + cT = append(cT, strings.ToLower(t)) + } + + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + s := strings.ToLower(strings.TrimSpace(r.Header.Get("Content-Type"))) + if i := strings.Index(s, ";"); i > -1 { + s = s[0:i] + } + + for _, t := range cT { + if t == s { + next.ServeHTTP(w, r) + return + } + } + + w.WriteHeader(http.StatusUnsupportedMediaType) + } + return http.HandlerFunc(fn) + } +} diff --git a/vendor/github.com/go-chi/chi/middleware/get_head.go b/vendor/github.com/go-chi/chi/middleware/get_head.go new file mode 100644 index 00000000..86068a96 --- /dev/null +++ b/vendor/github.com/go-chi/chi/middleware/get_head.go @@ -0,0 +1,39 @@ +package middleware + +import ( + "net/http" + + "github.com/go-chi/chi" +) + +// GetHead automatically route undefined HEAD requests to GET handlers. +func GetHead(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + rctx := chi.RouteContext(r.Context()) + routePath := rctx.RoutePath + if routePath == "" { + if r.URL.RawPath != "" { + routePath = r.URL.RawPath + } else { + routePath = r.URL.Path + } + } + + // Temporary routing context to look-ahead before routing the request + tctx := chi.NewRouteContext() + + // Attempt to find a HEAD handler for the routing path, if not found, traverse + // the router as through its a GET route, but proceed with the request + // with the HEAD method. + if !rctx.Routes.Match(tctx, "HEAD", routePath) { + rctx.RouteMethod = "GET" + rctx.RoutePath = routePath + next.ServeHTTP(w, r) + return + } + } + + next.ServeHTTP(w, r) + }) +} diff --git a/vendor/github.com/go-chi/chi/middleware/logger.go b/vendor/github.com/go-chi/chi/middleware/logger.go index 72a7cdec..9f119d56 100644 --- a/vendor/github.com/go-chi/chi/middleware/logger.go +++ b/vendor/github.com/go-chi/chi/middleware/logger.go @@ -16,7 +16,7 @@ var ( // DefaultLogger is called by the Logger middleware handler to log each request. // Its made a package-level variable so that it can be reconfigured for custom // logging configurations. - DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags)}) + DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: false}) ) // Logger is a middleware that logs the start and end of each request, along @@ -74,31 +74,39 @@ func WithLogEntry(r *http.Request, entry LogEntry) *http.Request { return r } +// LoggerInterface accepts printing to stdlib logger or compatible logger. +type LoggerInterface interface { + Print(v ...interface{}) +} + // DefaultLogFormatter is a simple logger that implements a LogFormatter. type DefaultLogFormatter struct { - Logger *log.Logger + Logger LoggerInterface + NoColor bool } // NewLogEntry creates a new LogEntry for the request. func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry { + useColor := !l.NoColor entry := &defaultLogEntry{ DefaultLogFormatter: l, request: r, buf: &bytes.Buffer{}, + useColor: useColor, } reqID := GetReqID(r.Context()) if reqID != "" { - cW(entry.buf, nYellow, "[%s] ", reqID) + cW(entry.buf, useColor, nYellow, "[%s] ", reqID) } - cW(entry.buf, nCyan, "\"") - cW(entry.buf, bMagenta, "%s ", r.Method) + cW(entry.buf, useColor, nCyan, "\"") + cW(entry.buf, useColor, bMagenta, "%s ", r.Method) scheme := "http" if r.TLS != nil { scheme = "https" } - cW(entry.buf, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto) + cW(entry.buf, useColor, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto) entry.buf.WriteString("from ") entry.buf.WriteString(r.RemoteAddr) @@ -109,33 +117,34 @@ func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry { type defaultLogEntry struct { *DefaultLogFormatter - request *http.Request - buf *bytes.Buffer + request *http.Request + buf *bytes.Buffer + useColor bool } func (l *defaultLogEntry) Write(status, bytes int, elapsed time.Duration) { switch { case status < 200: - cW(l.buf, bBlue, "%03d", status) + cW(l.buf, l.useColor, bBlue, "%03d", status) case status < 300: - cW(l.buf, bGreen, "%03d", status) + cW(l.buf, l.useColor, bGreen, "%03d", status) case status < 400: - cW(l.buf, bCyan, "%03d", status) + cW(l.buf, l.useColor, bCyan, "%03d", status) case status < 500: - cW(l.buf, bYellow, "%03d", status) + cW(l.buf, l.useColor, bYellow, "%03d", status) default: - cW(l.buf, bRed, "%03d", status) + cW(l.buf, l.useColor, bRed, "%03d", status) } - cW(l.buf, bBlue, " %dB", bytes) + cW(l.buf, l.useColor, bBlue, " %dB", bytes) l.buf.WriteString(" in ") if elapsed < 500*time.Millisecond { - cW(l.buf, nGreen, "%s", elapsed) + cW(l.buf, l.useColor, nGreen, "%s", elapsed) } else if elapsed < 5*time.Second { - cW(l.buf, nYellow, "%s", elapsed) + cW(l.buf, l.useColor, nYellow, "%s", elapsed) } else { - cW(l.buf, nRed, "%s", elapsed) + cW(l.buf, l.useColor, nRed, "%s", elapsed) } l.Logger.Print(l.buf.String()) @@ -143,7 +152,7 @@ func (l *defaultLogEntry) Write(status, bytes int, elapsed time.Duration) { func (l *defaultLogEntry) Panic(v interface{}, stack []byte) { panicEntry := l.NewLogEntry(l.request).(*defaultLogEntry) - cW(panicEntry.buf, bRed, "panic: %+v", v) + cW(panicEntry.buf, l.useColor, bRed, "panic: %+v", v) l.Logger.Print(panicEntry.buf.String()) l.Logger.Print(string(stack)) } diff --git a/vendor/github.com/go-chi/chi/middleware/profiler.go b/vendor/github.com/go-chi/chi/middleware/profiler.go index 620717e5..1d44b825 100644 --- a/vendor/github.com/go-chi/chi/middleware/profiler.go +++ b/vendor/github.com/go-chi/chi/middleware/profiler.go @@ -29,15 +29,11 @@ func Profiler() http.Handler { http.Redirect(w, r, r.RequestURI+"/", 301) }) - r.HandleFunc("/pprof/", pprof.Index) + r.HandleFunc("/pprof/*", pprof.Index) r.HandleFunc("/pprof/cmdline", pprof.Cmdline) r.HandleFunc("/pprof/profile", pprof.Profile) r.HandleFunc("/pprof/symbol", pprof.Symbol) r.HandleFunc("/pprof/trace", pprof.Trace) - r.Handle("/pprof/block", pprof.Handler("block")) - r.Handle("/pprof/heap", pprof.Handler("heap")) - r.Handle("/pprof/goroutine", pprof.Handler("goroutine")) - r.Handle("/pprof/threadcreate", pprof.Handler("threadcreate")) r.HandleFunc("/vars", expVars) return r diff --git a/vendor/github.com/go-chi/chi/middleware/terminal.go b/vendor/github.com/go-chi/chi/middleware/terminal.go index 79930a25..a5d42410 100644 --- a/vendor/github.com/go-chi/chi/middleware/terminal.go +++ b/vendor/github.com/go-chi/chi/middleware/terminal.go @@ -52,12 +52,12 @@ func init() { } // colorWrite -func cW(w io.Writer, color []byte, s string, args ...interface{}) { - if isTTY { +func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) { + if isTTY && useColor { w.Write(color) } fmt.Fprintf(w, s, args...) - if isTTY { + if isTTY && useColor { w.Write(reset) } } diff --git a/vendor/github.com/go-chi/chi/middleware/throttler.go b/vendor/github.com/go-chi/chi/middleware/throttle.go similarity index 100% rename from vendor/github.com/go-chi/chi/middleware/throttler.go rename to vendor/github.com/go-chi/chi/middleware/throttle.go diff --git a/vendor/github.com/go-chi/chi/middleware/wrap_writer.go b/vendor/github.com/go-chi/chi/middleware/wrap_writer.go index 5d1c286b..e49fa5be 100644 --- a/vendor/github.com/go-chi/chi/middleware/wrap_writer.go +++ b/vendor/github.com/go-chi/chi/middleware/wrap_writer.go @@ -83,6 +83,8 @@ type flushWriter struct { } func (f *flushWriter) Flush() { + f.wroteHeader = true + fl := f.basicWriter.ResponseWriter.(http.Flusher) fl.Flush() } @@ -102,6 +104,8 @@ func (f *httpFancyWriter) CloseNotify() <-chan bool { return cn.CloseNotify() } func (f *httpFancyWriter) Flush() { + f.wroteHeader = true + fl := f.basicWriter.ResponseWriter.(http.Flusher) fl.Flush() } @@ -140,6 +144,8 @@ func (f *http2FancyWriter) CloseNotify() <-chan bool { return cn.CloseNotify() } func (f *http2FancyWriter) Flush() { + f.wroteHeader = true + fl := f.basicWriter.ResponseWriter.(http.Flusher) fl.Flush() } diff --git a/vendor/github.com/go-chi/chi/middleware/wrap_writer18.go b/vendor/github.com/go-chi/chi/middleware/wrap_writer18.go index 9233d8b1..115c2d4f 100644 --- a/vendor/github.com/go-chi/chi/middleware/wrap_writer18.go +++ b/vendor/github.com/go-chi/chi/middleware/wrap_writer18.go @@ -1,4 +1,4 @@ -// +build go1.8 +// +build go1.8 appengine package middleware diff --git a/vendor/github.com/go-chi/chi/mux.go b/vendor/github.com/go-chi/chi/mux.go index b6f62ede..84a2424a 100644 --- a/vendor/github.com/go-chi/chi/mux.go +++ b/vendor/github.com/go-chi/chi/mux.go @@ -35,7 +35,7 @@ type Mux struct { handler http.Handler // Routing context pool - pool sync.Pool + pool *sync.Pool // Custom route not found handler notFoundHandler http.HandlerFunc @@ -47,7 +47,7 @@ type Mux struct { // NewMux returns a newly initialized Mux object that implements the Router // interface. func NewMux() *Mux { - mux := &Mux{tree: &node{}} + mux := &Mux{tree: &node{}, pool: &sync.Pool{}} mux.pool.New = func() interface{} { return NewRouteContext() } @@ -75,7 +75,8 @@ func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Once the request is finished, reset the routing context and put it back // into the pool for reuse from another request. rctx = mx.pool.Get().(*Context) - rctx.reset() + rctx.Reset() + rctx.Routes = mx r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, rctx)) mx.handler.ServeHTTP(w, r) mx.pool.Put(rctx) @@ -232,7 +233,8 @@ func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router { } mws = append(mws, middlewares...) - im := &Mux{inline: true, parent: mx, tree: mx.tree, middlewares: mws} + im := &Mux{pool: mx.pool, inline: true, parent: mx, tree: mx.tree, middlewares: mws} + return im } @@ -269,7 +271,7 @@ func (mx *Mux) Route(pattern string, fn func(r Router)) Router { func (mx *Mux) Mount(pattern string, handler http.Handler) { // Provide runtime safety for ensuring a pattern isn't mounted on an existing // routing pattern. - if mx.tree.matchPattern(pattern+"*") || mx.tree.matchPattern(pattern+"/*") { + if mx.tree.findPattern(pattern+"*") || mx.tree.findPattern(pattern+"/*") { panic(fmt.Sprintf("chi: attempting to Mount() a handler on an existing path, '%s'", pattern)) } @@ -285,23 +287,13 @@ func (mx *Mux) Mount(pattern string, handler http.Handler) { // Wrap the sub-router in a handlerFunc to scope the request path for routing. mountHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { rctx := RouteContext(r.Context()) - rctx.RoutePath = "/" - - nx := len(rctx.routeParams.Keys) - 1 // index of last param in list - if nx >= 0 && rctx.routeParams.Keys[nx] == "*" && len(rctx.routeParams.Values) > nx { - rctx.RoutePath += rctx.routeParams.Values[nx] - } - + rctx.RoutePath = mx.nextRoutePath(rctx) handler.ServeHTTP(w, r) }) if pattern == "" || pattern[len(pattern)-1] != '/' { - notFoundHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - mx.NotFoundHandler().ServeHTTP(w, r) - }) - mx.handle(mALL|mSTUB, pattern, mountHandler) - mx.handle(mALL|mSTUB, pattern+"/", notFoundHandler) + mx.handle(mALL|mSTUB, pattern+"/", mountHandler) pattern += "/" } @@ -317,15 +309,37 @@ func (mx *Mux) Mount(pattern string, handler http.Handler) { } } +// Routes returns a slice of routing information from the tree, +// useful for traversing available routes of a router. +func (mx *Mux) Routes() []Route { + return mx.tree.routes() +} + // Middlewares returns a slice of middleware handler functions. func (mx *Mux) Middlewares() Middlewares { return mx.middlewares } -// Routes returns a slice of routing information from the tree, -// useful for traversing available routes of a router. -func (mx *Mux) Routes() []Route { - return mx.tree.routes() +// Match searches the routing tree for a handler that matches the method/path. +// It's similar to routing a http request, but without executing the handler +// thereafter. +// +// Note: the *Context state is updated during execution, so manage +// the state carefully or make a NewRouteContext(). +func (mx *Mux) Match(rctx *Context, method, path string) bool { + m, ok := methodMap[method] + if !ok { + return false + } + + node, _, h := mx.tree.FindRoute(rctx, m, path) + + if node != nil && node.subroutes != nil { + rctx.RoutePath = mx.nextRoutePath(rctx) + return node.subroutes.Match(rctx, method, rctx.RoutePath) + } + + return h != nil } // NotFoundHandler returns the default Mux 404 responder whenever a route @@ -396,26 +410,34 @@ func (mx *Mux) routeHTTP(w http.ResponseWriter, r *http.Request) { } // Check if method is supported by chi - method, ok := methodMap[r.Method] + if rctx.RouteMethod == "" { + rctx.RouteMethod = r.Method + } + method, ok := methodMap[rctx.RouteMethod] if !ok { mx.MethodNotAllowedHandler().ServeHTTP(w, r) return } // Find the route - hs := mx.tree.FindRoute(rctx, method, routePath) - if hs == nil { - if rctx.methodNotAllowed { - mx.MethodNotAllowedHandler().ServeHTTP(w, r) - } else { - mx.NotFoundHandler().ServeHTTP(w, r) - } + if _, _, h := mx.tree.FindRoute(rctx, method, routePath); h != nil { + h.ServeHTTP(w, r) return } - h, _ := hs[method] + if rctx.methodNotAllowed { + mx.MethodNotAllowedHandler().ServeHTTP(w, r) + } else { + mx.NotFoundHandler().ServeHTTP(w, r) + } +} - // Serve it up - h.handler.ServeHTTP(w, r) +func (mx *Mux) nextRoutePath(rctx *Context) string { + routePath := "/" + nx := len(rctx.routeParams.Keys) - 1 // index of last param in list + if nx >= 0 && rctx.routeParams.Keys[nx] == "*" && len(rctx.routeParams.Values) > nx { + routePath += rctx.routeParams.Values[nx] + } + return routePath } // Recursively update data on child routers. diff --git a/vendor/github.com/go-chi/chi/tree.go b/vendor/github.com/go-chi/chi/tree.go index 6610d44f..a55d7f14 100644 --- a/vendor/github.com/go-chi/chi/tree.go +++ b/vendor/github.com/go-chi/chi/tree.go @@ -6,44 +6,61 @@ package chi import ( "fmt" + "math" "net/http" "regexp" "sort" + "strconv" "strings" ) type methodTyp int const ( - mCONNECT methodTyp = 1 << iota + mSTUB methodTyp = 1 << iota + mCONNECT mDELETE mGET mHEAD - mLINK mOPTIONS mPATCH mPOST mPUT mTRACE - mUNLINK - mSTUB - - mALL methodTyp = mCONNECT | mDELETE | mGET | mHEAD | mLINK | - mOPTIONS | mPATCH | mPOST | mPUT | mTRACE | mUNLINK ) +var mALL = mCONNECT | mDELETE | mGET | mHEAD | + mOPTIONS | mPATCH | mPOST | mPUT | mTRACE + var methodMap = map[string]methodTyp{ "CONNECT": mCONNECT, "DELETE": mDELETE, "GET": mGET, "HEAD": mHEAD, - "LINK": mLINK, "OPTIONS": mOPTIONS, "PATCH": mPATCH, "POST": mPOST, "PUT": mPUT, "TRACE": mTRACE, - "UNLINK": mUNLINK, +} + +// RegisterMethod adds support for custom HTTP method handlers, available +// via Router#Method and Router#MethodFunc +func RegisterMethod(method string) { + if method == "" { + return + } + method = strings.ToUpper(method) + if _, ok := methodMap[method]; ok { + return + } + n := len(methodMap) + if n > strconv.IntSize { + panic(fmt.Sprintf("chi: max number of methods reached (%d)", strconv.IntSize)) + } + mt := methodTyp(math.Exp2(float64(n))) + methodMap[method] = mt + mALL |= mt } type nodeTyp uint8 @@ -341,7 +358,7 @@ func (n *node) setEndpoint(method methodTyp, handler http.Handler, pattern strin } } -func (n *node) FindRoute(rctx *Context, method methodTyp, path string) endpoints { +func (n *node) FindRoute(rctx *Context, method methodTyp, path string) (*node, endpoints, http.Handler) { // Reset the context routing pattern and params rctx.routePattern = "" rctx.routeParams.Keys = rctx.routeParams.Keys[:0] @@ -350,7 +367,7 @@ func (n *node) FindRoute(rctx *Context, method methodTyp, path string) endpoints // Find the routing handlers for the path rn := n.findRoute(rctx, method, path) if rn == nil { - return nil + return nil, nil, nil } // Record the routing params in the request lifecycle @@ -363,7 +380,7 @@ func (n *node) FindRoute(rctx *Context, method methodTyp, path string) endpoints rctx.RoutePatterns = append(rctx.RoutePatterns, rctx.routePattern) } - return rn.endpoints + return rn, rn.endpoints, rn.endpoints[method].handler } // Recursive edge traversal by checking all nodeTyp groups along the way. @@ -407,7 +424,7 @@ func (n *node) findRoute(rctx *Context, method methodTyp, path string) *node { // label for param nodes is the delimiter byte p := strings.IndexByte(xsearch, xn.tail) - if p <= 0 { + if p < 0 { if xn.tail == '/' { p = len(xsearch) } else { @@ -514,7 +531,7 @@ func (n *node) isLeaf() bool { return n.endpoints != nil } -func (n *node) matchPattern(pattern string) bool { +func (n *node) findPattern(pattern string) bool { nn := n for _, nds := range nn.children { if len(nds) == 0 { @@ -551,7 +568,7 @@ func (n *node) matchPattern(pattern string) bool { return true } - return n.matchPattern(xpattern) + return n.findPattern(xpattern) } return false } @@ -643,8 +660,22 @@ func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) { if ps >= 0 { // Param/Regexp pattern is next nt := ntParam - pe := strings.Index(pattern, "}") - if pe < 0 { + + // Read to closing } taking into account opens and closes in curl count (cc) + cc := 0 + pe := ps + for i, c := range pattern[ps:] { + if c == '{' { + cc++ + } else if c == '}' { + cc-- + if cc == 0 { + pe = ps + i + break + } + } + } + if pe == ps { panic("chi: route param closing delimiter '}' is missing") } @@ -662,6 +693,15 @@ func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) { key = key[:idx] } + if len(rexpat) > 0 { + if rexpat[0] != '^' { + rexpat = "^" + rexpat + } + if rexpat[len(rexpat)-1] != '$' { + rexpat = rexpat + "$" + } + } + return nt, key, rexpat, tail, ps, pe } diff --git a/vendor/github.com/patrickmn/go-cache/CONTRIBUTORS b/vendor/github.com/patrickmn/go-cache/CONTRIBUTORS index 8a4da4ed..2b16e997 100644 --- a/vendor/github.com/patrickmn/go-cache/CONTRIBUTORS +++ b/vendor/github.com/patrickmn/go-cache/CONTRIBUTORS @@ -6,3 +6,4 @@ code was contributed.) Dustin Sallings Jason Mooberry Sergey Shepelev +Alex Edwards diff --git a/vendor/github.com/patrickmn/go-cache/LICENSE b/vendor/github.com/patrickmn/go-cache/LICENSE index 159e1e72..db9903c7 100644 --- a/vendor/github.com/patrickmn/go-cache/LICENSE +++ b/vendor/github.com/patrickmn/go-cache/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2015 Patrick Mylund Nielsen and the go-cache contributors +Copyright (c) 2012-2017 Patrick Mylund Nielsen and the go-cache contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/patrickmn/go-cache/README.md b/vendor/github.com/patrickmn/go-cache/README.md index 168ff7ba..c5789cc6 100644 --- a/vendor/github.com/patrickmn/go-cache/README.md +++ b/vendor/github.com/patrickmn/go-cache/README.md @@ -20,86 +20,62 @@ one) to recover from downtime quickly. (See the docs for `NewFrom()` for caveats ### Usage ```go - import ( - "fmt" - "github.com/patrickmn/go-cache" - "time" - ) +import ( + "fmt" + "github.com/patrickmn/go-cache" + "time" +) - func main() { +func main() { + // Create a cache with a default expiration time of 5 minutes, and which + // purges expired items every 10 minutes + c := cache.New(5*time.Minute, 10*time.Minute) - // Create a cache with a default expiration time of 5 minutes, and which - // purges expired items every 30 seconds - c := cache.New(5*time.Minute, 30*time.Second) + // Set the value of the key "foo" to "bar", with the default expiration time + c.Set("foo", "bar", cache.DefaultExpiration) - // Set the value of the key "foo" to "bar", with the default expiration time - c.Set("foo", "bar", cache.DefaultExpiration) - - // Set the value of the key "baz" to 42, with no expiration time - // (the item won't be removed until it is re-set, or removed using - // c.Delete("baz") - c.Set("baz", 42, cache.NoExpiration) - - // Get the string associated with the key "foo" from the cache - foo, found := c.Get("foo") - if found { - fmt.Println(foo) - } - - // Since Go is statically typed, and cache values can be anything, type - // assertion is needed when values are being passed to functions that don't - // take arbitrary types, (i.e. interface{}). The simplest way to do this for - // values which will only be used once--e.g. for passing to another - // function--is: - foo, found := c.Get("foo") - if found { - MyFunction(foo.(string)) - } - - // This gets tedious if the value is used several times in the same function. - // You might do either of the following instead: - if x, found := c.Get("foo"); found { - foo := x.(string) - // ... - } - // or - var foo string - if x, found := c.Get("foo"); found { - foo = x.(string) - } - // ... - // foo can then be passed around freely as a string - - // Want performance? Store pointers! - c.Set("foo", &MyStruct, cache.DefaultExpiration) - if x, found := c.Get("foo"); found { - foo := x.(*MyStruct) - // ... - } - - // If you store a reference type like a pointer, slice, map or channel, you - // do not need to run Set if you modify the underlying data. The cached - // reference points to the same memory, so if you modify a struct whose - // pointer you've stored in the cache, retrieving that pointer with Get will - // point you to the same data: - foo := &MyStruct{Num: 1} - c.Set("foo", foo, cache.DefaultExpiration) - // ... - x, _ := c.Get("foo") - foo := x.(*MyStruct) - fmt.Println(foo.Num) - // ... - foo.Num++ - // ... - x, _ := c.Get("foo") - foo := x.(*MyStruct) - foo.Println(foo.Num) - - // will print: - // 1 - // 2 + // Set the value of the key "baz" to 42, with no expiration time + // (the item won't be removed until it is re-set, or removed using + // c.Delete("baz") + c.Set("baz", 42, cache.NoExpiration) + // Get the string associated with the key "foo" from the cache + foo, found := c.Get("foo") + if found { + fmt.Println(foo) } + + // Since Go is statically typed, and cache values can be anything, type + // assertion is needed when values are being passed to functions that don't + // take arbitrary types, (i.e. interface{}). The simplest way to do this for + // values which will only be used once--e.g. for passing to another + // function--is: + foo, found := c.Get("foo") + if found { + MyFunction(foo.(string)) + } + + // This gets tedious if the value is used several times in the same function. + // You might do either of the following instead: + if x, found := c.Get("foo"); found { + foo := x.(string) + // ... + } + // or + var foo string + if x, found := c.Get("foo"); found { + foo = x.(string) + } + // ... + // foo can then be passed around freely as a string + + // Want performance? Store pointers! + c.Set("foo", &MyStruct, cache.DefaultExpiration) + if x, found := c.Get("foo"); found { + foo := x.(*MyStruct) + // ... + } +} ``` ### Reference diff --git a/vendor/github.com/patrickmn/go-cache/cache.go b/vendor/github.com/patrickmn/go-cache/cache.go index 35625430..db88d2f2 100644 --- a/vendor/github.com/patrickmn/go-cache/cache.go +++ b/vendor/github.com/patrickmn/go-cache/cache.go @@ -81,6 +81,12 @@ func (c *cache) set(k string, x interface{}, d time.Duration) { } } +// Add an item to the cache, replacing any existing item, using the default +// expiration. +func (c *cache) SetDefault(k string, x interface{}) { + c.Set(k, x, DefaultExpiration) +} + // Add an item to the cache only if an item doesn't already exist for the given // key, or if the existing item has expired. Returns an error otherwise. func (c *cache) Add(k string, x interface{}, d time.Duration) error { @@ -129,6 +135,36 @@ func (c *cache) Get(k string) (interface{}, bool) { return item.Object, true } +// GetWithExpiration returns an item and its expiration time from the cache. +// It returns the item or nil, the expiration time if one is set (if the item +// never expires a zero value for time.Time is returned), and a bool indicating +// whether the key was found. +func (c *cache) GetWithExpiration(k string) (interface{}, time.Time, bool) { + c.mu.RLock() + // "Inlining" of get and Expired + item, found := c.items[k] + if !found { + c.mu.RUnlock() + return nil, time.Time{}, false + } + + if item.Expiration > 0 { + if time.Now().UnixNano() > item.Expiration { + c.mu.RUnlock() + return nil, time.Time{}, false + } + + // Return the item and the expiration time + c.mu.RUnlock() + return item.Object, time.Unix(0, item.Expiration), true + } + + // If expiration <= 0 (i.e. no expiration time set) then return the item + // and a zeroed time.Time + c.mu.RUnlock() + return item.Object, time.Time{}, true +} + func (c *cache) get(k string) (interface{}, bool) { item, found := c.items[k] if !found { @@ -998,19 +1034,26 @@ func (c *cache) LoadFile(fname string) error { return fp.Close() } -// Returns the items in the cache. This may include items that have expired, -// but have not yet been cleaned up. If this is significant, the Expiration -// fields of the items should be checked. Note that explicit synchronization -// is needed to use a cache and its corresponding Items() return value at -// the same time, as the map is shared. +// Copies all unexpired items in the cache into a new map and returns it. func (c *cache) Items() map[string]Item { c.mu.RLock() defer c.mu.RUnlock() - return c.items + m := make(map[string]Item, len(c.items)) + now := time.Now().UnixNano() + for k, v := range c.items { + // "Inlining" of Expired + if v.Expiration > 0 { + if now > v.Expiration { + continue + } + } + m[k] = v + } + return m } // Returns the number of items in the cache. This may include items that have -// expired, but have not yet been cleaned up. Equivalent to len(c.Items()). +// expired, but have not yet been cleaned up. func (c *cache) ItemCount() int { c.mu.RLock() n := len(c.items) @@ -1031,7 +1074,6 @@ type janitor struct { } func (j *janitor) Run(c *cache) { - j.stop = make(chan bool) ticker := time.NewTicker(j.Interval) for { select { @@ -1051,6 +1093,7 @@ func stopJanitor(c *Cache) { func runJanitor(c *cache, ci time.Duration) { j := &janitor{ Interval: ci, + stop: make(chan bool), } c.janitor = j go j.Run(c) diff --git a/vendor/vendor.json b/vendor/vendor.json index 6225bb6c..79e5c624 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -46,16 +46,16 @@ "revisionTime": "2016-05-15T16:26:28Z" }, { - "checksumSHA1": "Eg449rulPIiIoW2+Aoum/Af/wiM=", + "checksumSHA1": "YODbzvhUr0Tzp2/MkXflXph2hdY=", "path": "github.com/go-chi/chi", - "revision": "25354a53cca531cb2efd3f1d3c565d90ff04d802", - "revisionTime": "2017-08-02T21:27:05Z" + "revision": "e223a795a06a5598451cf022558c17c779f5a7ab", + "revisionTime": "2018-02-02T19:41:35Z" }, { - "checksumSHA1": "OpRUup8sYz/4ywe48bp0vPs+tHU=", + "checksumSHA1": "1Xlo4sCR+zeOzjslXc/NAOmkgXg=", "path": "github.com/go-chi/chi/middleware", - "revision": "25354a53cca531cb2efd3f1d3c565d90ff04d802", - "revisionTime": "2017-08-02T21:27:05Z" + "revision": "e223a795a06a5598451cf022558c17c779f5a7ab", + "revisionTime": "2018-02-02T19:41:35Z" }, { "checksumSHA1": "KlhjFlnkWjrgYdOiUCgD+uuiCRU=", @@ -106,10 +106,10 @@ "revisionTime": "2017-12-22T15:26:07Z" }, { - "checksumSHA1": "vNL7xL08iaySNAxSUz+AoXGu3DQ=", + "checksumSHA1": "JVGDxPn66bpe6xEiexs1r+y6jF0=", "path": "github.com/patrickmn/go-cache", - "revision": "1881a9bccb818787f68c52bfba648c6cf34c34fa", - "revisionTime": "2016-01-27T17:00:04Z" + "revision": "a3647f8e31d79543b2d0f0ae2fe5c379d72cedc0", + "revisionTime": "2017-07-22T04:01:10Z" }, { "checksumSHA1": "rJab1YdNhQooDiBWNnt7TLWPyBU=",