From 70134578e18c9f00b22a69a4b14f3b9f1d141511 Mon Sep 17 00:00:00 2001 From: Umputun Date: Thu, 4 Apr 2019 02:17:12 -0500 Subject: [PATCH] switch to chi v4 --- backend/app/rest/api/rest.go | 2 +- backend/go.mod | 2 +- backend/go.sum | 2 + .../vendor/github.com/go-chi/chi/.travis.yml | 18 +- .../vendor/github.com/go-chi/chi/CHANGELOG.md | 23 ++ .../vendor/github.com/go-chi/chi/README.md | 59 ++-- .../vendor/github.com/go-chi/chi/context.go | 14 +- .../go-chi/chi/middleware/closenotify17.go | 42 --- .../go-chi/chi/middleware/closenotify18.go | 17 -- .../go-chi/chi/middleware/compress.go | 271 +++++++++++------- .../go-chi/chi/middleware/compress18.go | 15 - .../go-chi/chi/middleware/content_type.go | 6 + .../go-chi/chi/middleware/logger.go | 40 +-- .../go-chi/chi/middleware/nocache.go | 2 +- .../go-chi/chi/middleware/realip.go | 2 +- .../go-chi/chi/middleware/request_id.go | 10 +- .../github.com/go-chi/chi/middleware/strip.go | 10 +- .../go-chi/chi/middleware/terminal.go | 6 +- .../go-chi/chi/middleware/timeout.go | 3 +- .../go-chi/chi/middleware/wrap_writer.go | 59 +++- .../go-chi/chi/middleware/wrap_writer17.go | 34 --- .../go-chi/chi/middleware/wrap_writer18.go | 41 --- backend/vendor/github.com/go-chi/chi/mux.go | 3 +- backend/vendor/github.com/go-chi/chi/tree.go | 22 +- .../github.com/go-pkgz/lgr/.golangci.yml | 60 ++++ .../vendor/github.com/go-pkgz/lgr/.travis.yml | 5 +- .../vendor/github.com/go-pkgz/lgr/README.md | 18 +- .../vendor/github.com/go-pkgz/lgr/adaptor.go | 4 +- .../github.com/go-pkgz/lgr/interface.go | 3 +- .../vendor/github.com/go-pkgz/lgr/logger.go | 181 ++++-------- .../vendor/github.com/go-pkgz/lgr/options.go | 70 +++++ backend/vendor/modules.txt | 4 +- 32 files changed, 558 insertions(+), 490 deletions(-) delete mode 100644 backend/vendor/github.com/go-chi/chi/middleware/closenotify17.go delete mode 100644 backend/vendor/github.com/go-chi/chi/middleware/closenotify18.go delete mode 100644 backend/vendor/github.com/go-chi/chi/middleware/compress18.go delete mode 100644 backend/vendor/github.com/go-chi/chi/middleware/wrap_writer17.go delete mode 100644 backend/vendor/github.com/go-chi/chi/middleware/wrap_writer18.go create mode 100644 backend/vendor/github.com/go-pkgz/lgr/.golangci.yml create mode 100644 backend/vendor/github.com/go-pkgz/lgr/options.go diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index 86acf991..e855b942 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -262,7 +262,7 @@ func (s *Rest) routes() chi.Router { rauth.Put("/comment/{id}", s.updateCommentCtrl) rauth.Post("/comment", s.createCommentCtrl) rauth.With(rejectAnonUser).Put("/vote/{id}", s.voteCtrl) - rauth.Post("/deleteme", s.deleteMeCtrl) + rauth.With(rejectAnonUser).Post("/deleteme", s.deleteMeCtrl) }) rapi.Group(func(rauth chi.Router) { diff --git a/backend/go.mod b/backend/go.mod index 3fa72690..577d345e 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -10,7 +10,7 @@ require ( github.com/didip/tollbooth v4.0.0+incompatible github.com/didip/tollbooth_chi v0.0.0-20170928041846-6ab5f3083f3d github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 - github.com/go-chi/chi v3.3.2+incompatible + github.com/go-chi/chi v4.0.2+incompatible github.com/go-chi/cors v1.0.0 github.com/go-chi/render v1.0.0 github.com/go-pkgz/auth v0.5.0 diff --git a/backend/go.sum b/backend/go.sum index 1a75e68e..ee3962c0 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -22,6 +22,8 @@ github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7a github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-chi/chi v3.3.2+incompatible h1:uQNcQN3NsV1j4ANsPh42P4ew4t6rnRbJb8frvpp31qQ= github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= +github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/cors v1.0.0 h1:e6x8k7uWbUwYs+aXDoiUzeQFT6l0cygBYyNhD7/1Tg0= github.com/go-chi/cors v1.0.0/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I= github.com/go-chi/render v1.0.0 h1:cLJlkaTB4xfx5rWhtoB0BSXsXVJKWFqv08Y3cR1bZKA= diff --git a/backend/vendor/github.com/go-chi/chi/.travis.yml b/backend/vendor/github.com/go-chi/chi/.travis.yml index a6d5de85..de3287e1 100644 --- a/backend/vendor/github.com/go-chi/chi/.travis.yml +++ b/backend/vendor/github.com/go-chi/chi/.travis.yml @@ -1,18 +1,18 @@ language: go go: - - 1.7.x - - 1.8.x - - 1.9.x - -install: - - go get -u golang.org/x/tools/cmd/goimports - - go get -u github.com/golang/lint/golint + - 1.10.x + - 1.11.x + - 1.12.x script: - go get -d -t ./... - go vet ./... - - golint ./... - go test ./... - > - goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || : + go_version=$(go version); + if [ ${go_version:13:4} = "1.12" ]; then + go get -u golang.org/x/tools/cmd/goimports; + goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :; + fi + diff --git a/backend/vendor/github.com/go-chi/chi/CHANGELOG.md b/backend/vendor/github.com/go-chi/chi/CHANGELOG.md index 5f0ab254..d03e40c6 100644 --- a/backend/vendor/github.com/go-chi/chi/CHANGELOG.md +++ b/backend/vendor/github.com/go-chi/chi/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## v4.0.0 (2019-01-10) + +- chi v4 requires Go 1.10.3+ (or Go 1.9.7+) - we have deprecated support for Go 1.7 and 1.8 +- router: respond with 404 on router with no routes (#362) +- router: additional check to ensure wildcard is at the end of a url pattern (#333) +- middleware: deprecate use of http.CloseNotifier (#347) +- middleware: fix RedirectSlashes to include query params on redirect (#334) +- History of changes: see https://github.com/go-chi/chi/compare/v3.3.4...v4.0.0 + + +## v3.3.4 (2019-01-07) + +- Minor middleware improvements. No changes to core library/router. Moving v3 into its +- own branch as a version of chi for Go 1.7, 1.8, 1.9, 1.10, 1.11 +- History of changes: see https://github.com/go-chi/chi/compare/v3.3.3...v3.3.4 + + +## v3.3.3 (2018-08-27) + +- Minor release +- See https://github.com/go-chi/chi/compare/v3.3.2...v3.3.3 + + ## v3.3.2 (2017-12-22) - Support to route trailing slashes on mounted sub-routers (#281) diff --git a/backend/vendor/github.com/go-chi/chi/README.md b/backend/vendor/github.com/go-chi/chi/README.md index c71a3a01..d36d4db5 100644 --- a/backend/vendor/github.com/go-chi/chi/README.md +++ b/backend/vendor/github.com/go-chi/chi/README.md @@ -3,7 +3,7 @@ [![GoDoc Widget]][GoDoc] [![Travis Widget]][Travis] -`chi` is a lightweight, idiomatic and composable router for building Go 1.7+ HTTP services. It's +`chi` is a lightweight, idiomatic and composable router for building Go HTTP services. It's especially good at helping you write large REST API services that are kept maintainable as your project grows and changes. `chi` is built on the new `context` package introduced in Go 1.7 to handle signaling, cancelation and request-scoped values across a handler chain. @@ -31,18 +31,12 @@ included some useful/optional subpackages: [middleware](/middleware), [render](h * **Context control** - built on new `context` package, providing value chaining, cancelations and timeouts * **Robust** - in production at Pressly, CloudFlare, Heroku, 99Designs, and many others (see [discussion](https://github.com/go-chi/chi/issues/91)) * **Doc generation** - `docgen` auto-generates routing documentation from your source to JSON or Markdown -* **No external dependencies** - plain ol' Go 1.7+ stdlib + net/http +* **No external dependencies** - plain ol' Go stdlib + net/http ## Examples -* [rest](https://github.com/go-chi/chi/blob/master/_examples/rest/main.go) - REST APIs made easy, productive and maintainable -* [logging](https://github.com/go-chi/chi/blob/master/_examples/logging/main.go) - Easy structured logging for any backend -* [limits](https://github.com/go-chi/chi/blob/master/_examples/limits/main.go) - Timeouts and Throttling -* [todos-resource](https://github.com/go-chi/chi/blob/master/_examples/todos-resource/main.go) - Struct routers/handlers, an example of another code layout style -* [versions](https://github.com/go-chi/chi/blob/master/_examples/versions/main.go) - Demo of `chi/render` subpkg -* [fileserver](https://github.com/go-chi/chi/blob/master/_examples/fileserver/main.go) - Easily serve static files -* [graceful](https://github.com/go-chi/chi/blob/master/_examples/graceful/main.go) - Graceful context signaling and server shutdown +See [_examples/](https://github.com/go-chi/chi/blob/master/_examples/) for a variety of examples. **As easy as:** @@ -70,8 +64,8 @@ Here is a little preview of how routing looks like with chi. Also take a look at in JSON ([routes.json](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.json)) and in Markdown ([routes.md](https://github.com/go-chi/chi/blob/master/_examples/rest/routes.md)). -I highly recommend reading the source of the [examples](#examples) listed above, they will show you all the features -of chi and serve as a good form of documentation. +I highly recommend reading the source of the [examples](https://github.com/go-chi/chi/blob/master/_examples/) listed +above, they will show you all the features of chi and serve as a good form of documentation. ```go import ( @@ -232,7 +226,7 @@ type Router interface { } // Routes interface adds two methods for router traversal, which is also -// used by the `docgen` subpackage to generation documentation for Routers. +// used by the github.com/go-chi/docgen package to generate documentation for Routers. type Routes interface { // Routes returns the routing tree in an easily traversable structure. Routes() []Route @@ -261,7 +255,7 @@ friendly with any middleware in the community. This offers much better extensibi of packages and is at the heart of chi's purpose. Here is an example of a standard net/http middleware handler using the new request context -available in Go 1.7+. This middleware sets a hypothetical user identifier on the request +available in Go. This middleware sets a hypothetical user identifier on the request context and calls the next handler in the chain. ```go @@ -347,6 +341,7 @@ Please see https://github.com/go-chi for additional packages. | package | description | |:---------------------------------------------------|:------------------------------------------------------------- | [cors](https://github.com/go-chi/cors) | Cross-origin resource sharing (CORS) | +| [docgen](https://github.com/go-chi/docgen) | Print chi.Router routes at runtime | | [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 | @@ -374,33 +369,33 @@ and.. The benchmark suite: https://github.com/pkieltyka/go-http-routing-benchmark -Results as of Aug 31, 2017 on Go 1.9.0 +Results as of Jan 9, 2019 with Go 1.11.4 on Linux X1 Carbon laptop ```shell -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 +BenchmarkChi_Param 3000000 475 ns/op 432 B/op 3 allocs/op +BenchmarkChi_Param5 2000000 696 ns/op 432 B/op 3 allocs/op +BenchmarkChi_Param20 1000000 1275 ns/op 432 B/op 3 allocs/op +BenchmarkChi_ParamWrite 3000000 505 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GithubStatic 3000000 508 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GithubParam 2000000 669 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GithubAll 10000 134627 ns/op 87699 B/op 609 allocs/op +BenchmarkChi_GPlusStatic 3000000 402 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GPlusParam 3000000 500 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GPlus2Params 3000000 586 ns/op 432 B/op 3 allocs/op +BenchmarkChi_GPlusAll 200000 7237 ns/op 5616 B/op 39 allocs/op +BenchmarkChi_ParseStatic 3000000 408 ns/op 432 B/op 3 allocs/op +BenchmarkChi_ParseParam 3000000 488 ns/op 432 B/op 3 allocs/op +BenchmarkChi_Parse2Params 3000000 551 ns/op 432 B/op 3 allocs/op +BenchmarkChi_ParseAll 100000 13508 ns/op 11232 B/op 78 allocs/op +BenchmarkChi_StaticAll 20000 81933 ns/op 67826 B/op 471 allocs/op ``` -Comparison with other routers: https://gist.github.com/pkieltyka/c089f309abeb179cfc4deaa519956d8c +Comparison with other routers: https://gist.github.com/pkieltyka/123032f12052520aaccab752bd3e78cc 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 -how setting context on a request in Go 1.7+ works. +how setting context on a request in Go works. ## Credits diff --git a/backend/vendor/github.com/go-chi/chi/context.go b/backend/vendor/github.com/go-chi/chi/context.go index 30c5afed..229c9cbf 100644 --- a/backend/vendor/github.com/go-chi/chi/context.go +++ b/backend/vendor/github.com/go-chi/chi/context.go @@ -84,13 +84,13 @@ func (x *Context) URLParam(key string) string { // // For example, // -// func Instrument(next http.Handler) http.Handler { -// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -// next.ServeHTTP(w, r) -// routePattern := chi.RouteContext(r.Context()).RoutePattern() -// measure(w, r, routePattern) -// }) -// } +// func Instrument(next http.Handler) http.Handler { +// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// next.ServeHTTP(w, r) +// routePattern := chi.RouteContext(r.Context()).RoutePattern() +// measure(w, r, routePattern) +// }) +// } func (x *Context) RoutePattern() string { routePattern := strings.Join(x.RoutePatterns, "") return strings.Replace(routePattern, "/*/", "/", -1) diff --git a/backend/vendor/github.com/go-chi/chi/middleware/closenotify17.go b/backend/vendor/github.com/go-chi/chi/middleware/closenotify17.go deleted file mode 100644 index 95802b13..00000000 --- a/backend/vendor/github.com/go-chi/chi/middleware/closenotify17.go +++ /dev/null @@ -1,42 +0,0 @@ -// +build go1.7,!go1.8 - -package middleware - -import ( - "context" - "net/http" -) - -// CloseNotify is a middleware that cancels ctx when the underlying -// connection has gone away. It can be used to cancel long operations -// on the server when the client disconnects before the response is ready. -// -// Note: this behaviour is standard in Go 1.8+, so the middleware does nothing -// on 1.8+ and exists just for backwards compatibility. -func CloseNotify(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - cn, ok := w.(http.CloseNotifier) - if !ok { - panic("chi/middleware: CloseNotify expects http.ResponseWriter to implement http.CloseNotifier interface") - } - closeNotifyCh := cn.CloseNotify() - - ctx, cancel := context.WithCancel(r.Context()) - defer cancel() - - go func() { - select { - case <-ctx.Done(): - return - case <-closeNotifyCh: - cancel() - return - } - }() - - r = r.WithContext(ctx) - next.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} diff --git a/backend/vendor/github.com/go-chi/chi/middleware/closenotify18.go b/backend/vendor/github.com/go-chi/chi/middleware/closenotify18.go deleted file mode 100644 index 4f0d73cc..00000000 --- a/backend/vendor/github.com/go-chi/chi/middleware/closenotify18.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build go1.8 appengine - -package middleware - -import ( - "net/http" -) - -// CloseNotify is a middleware that cancels ctx when the underlying -// connection has gone away. It can be used to cancel long operations -// on the server when the client disconnects before the response is ready. -// -// Note: this behaviour is standard in Go 1.8+, so the middleware does nothing -// on 1.8+ and exists just for backwards compatibility. -func CloseNotify(next http.Handler) http.Handler { - return next -} diff --git a/backend/vendor/github.com/go-chi/chi/middleware/compress.go b/backend/vendor/github.com/go-chi/chi/middleware/compress.go index 006ad48f..d2876d4e 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/compress.go +++ b/backend/vendor/github.com/go-chi/chi/middleware/compress.go @@ -11,24 +11,98 @@ import ( "strings" ) -type encoding int +var encoders = map[string]EncoderFunc{} -const ( - encodingNone encoding = iota - encodingGzip - encodingDeflate -) +var encodingPrecedence = []string{"br", "gzip", "deflate"} + +func init() { + // TODO: + // lzma: Opera. + // sdch: Chrome, Android. Gzip output + dictionary header. + // br: Brotli, see https://github.com/go-chi/chi/pull/326 + + // TODO: Exception for old MSIE browsers that can't handle non-HTML? + // https://zoompf.com/blog/2012/02/lose-the-wait-http-compression + SetEncoder("gzip", encoderGzip) + + // HTTP 1.1 "deflate" (RFC 2616) stands for DEFLATE data (RFC 1951) + // wrapped with zlib (RFC 1950). The zlib wrapper uses Adler-32 + // checksum compared to CRC-32 used in "gzip" and thus is faster. + // + // But.. some old browsers (MSIE, Safari 5.1) incorrectly expect + // raw DEFLATE data only, without the mentioned zlib wrapper. + // Because of this major confusion, most modern browsers try it + // both ways, first looking for zlib headers. + // Quote by Mark Adler: http://stackoverflow.com/a/9186091/385548 + // + // The list of browsers having problems is quite big, see: + // http://zoompf.com/blog/2012/02/lose-the-wait-http-compression + // https://web.archive.org/web/20120321182910/http://www.vervestudios.co/projects/compression-tests/results + // + // That's why we prefer gzip over deflate. It's just more reliable + // and not significantly slower than gzip. + SetEncoder("deflate", encoderDeflate) + + // NOTE: Not implemented, intentionally: + // case "compress": // LZW. Deprecated. + // case "bzip2": // Too slow on-the-fly. + // case "zopfli": // Too slow on-the-fly. + // case "xz": // Too slow on-the-fly. +} + +// An EncoderFunc is a function that wraps the provided ResponseWriter with a +// streaming compression algorithm and returns it. +// +// In case of failure, the function should return nil. +type EncoderFunc func(w http.ResponseWriter, level int) io.Writer + +// SetEncoder can be used to set the implementation of a compression algorithm. +// +// The encoding should be a standardised identifier. See: +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding +// +// For example, add the Brotli algortithm: +// +// import brotli_enc "gopkg.in/kothar/brotli-go.v0/enc" +// +// middleware.SetEncoder("br", func(w http.ResponseWriter, level int) io.Writer { +// params := brotli_enc.NewBrotliParams() +// params.SetQuality(level) +// return brotli_enc.NewBrotliWriter(params, w) +// }) +func SetEncoder(encoding string, fn EncoderFunc) { + encoding = strings.ToLower(encoding) + if encoding == "" { + panic("the encoding can not be empty") + } + if fn == nil { + panic("attempted to set a nil encoder function") + } + encoders[encoding] = fn + + var e string + for _, v := range encodingPrecedence { + if v == encoding { + e = v + } + } + + if e == "" { + encodingPrecedence = append([]string{e}, encodingPrecedence...) + } +} var defaultContentTypes = map[string]struct{}{ - "text/html": struct{}{}, - "text/css": struct{}{}, - "text/plain": struct{}{}, - "text/javascript": struct{}{}, - "application/javascript": struct{}{}, - "application/x-javascript": struct{}{}, - "application/json": struct{}{}, - "application/atom+xml": struct{}{}, - "application/rss+xml": struct{}{}, + "text/html": {}, + "text/css": {}, + "text/plain": {}, + "text/javascript": {}, + "application/javascript": {}, + "application/x-javascript": {}, + "application/json": {}, + "application/atom+xml": {}, + "application/rss+xml": {}, + "image/svg+xml": {}, } // DefaultCompress is a middleware that compresses response @@ -43,6 +117,11 @@ func DefaultCompress(next http.Handler) http.Handler { // body of a given content types to a data format based // on Accept-Encoding request header. It uses a given // compression level. +// +// NOTE: make sure to set the Content-Type header on your response +// otherwise this middleware will not compress the response body. For ex, in +// your handler you should set w.Header().Set("Content-Type", http.DetectContentType(yourBody)) +// or set it manually. func Compress(level int, types ...string) func(next http.Handler) http.Handler { contentTypes := defaultContentTypes if len(types) > 0 { @@ -54,159 +133,143 @@ func Compress(level int, types ...string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { - mcw := &maybeCompressResponseWriter{ + encoder, encoding := selectEncoder(r.Header) + + cw := &compressResponseWriter{ ResponseWriter: w, w: w, contentTypes: contentTypes, - encoding: selectEncoding(r.Header), + encoder: encoder, + encoding: encoding, level: level, } - defer mcw.Close() + defer cw.Close() - next.ServeHTTP(mcw, r) + next.ServeHTTP(cw, r) } return http.HandlerFunc(fn) } } -func selectEncoding(h http.Header) encoding { - enc := h.Get("Accept-Encoding") +func selectEncoder(h http.Header) (EncoderFunc, string) { + header := h.Get("Accept-Encoding") - switch { - // TODO: - // case "br": // Brotli, experimental. Firefox 2016, to-be-in Chromium. - // case "lzma": // Opera. - // case "sdch": // Chrome, Android. Gzip output + dictionary header. + // Parse the names of all accepted algorithms from the header. + accepted := strings.Split(strings.ToLower(header), ",") - case strings.Contains(enc, "gzip"): - // TODO: Exception for old MSIE browsers that can't handle non-HTML? - // https://zoompf.com/blog/2012/02/lose-the-wait-http-compression - return encodingGzip - - case strings.Contains(enc, "deflate"): - // HTTP 1.1 "deflate" (RFC 2616) stands for DEFLATE data (RFC 1951) - // wrapped with zlib (RFC 1950). The zlib wrapper uses Adler-32 - // checksum compared to CRC-32 used in "gzip" and thus is faster. - // - // But.. some old browsers (MSIE, Safari 5.1) incorrectly expect - // raw DEFLATE data only, without the mentioned zlib wrapper. - // Because of this major confusion, most modern browsers try it - // both ways, first looking for zlib headers. - // Quote by Mark Adler: http://stackoverflow.com/a/9186091/385548 - // - // The list of browsers having problems is quite big, see: - // http://zoompf.com/blog/2012/02/lose-the-wait-http-compression - // https://web.archive.org/web/20120321182910/http://www.vervestudios.co/projects/compression-tests/results - // - // That's why we prefer gzip over deflate. It's just more reliable - // and not significantly slower than gzip. - return encodingDeflate - - // NOTE: Not implemented, intentionally: - // case "compress": // LZW. Deprecated. - // case "bzip2": // Too slow on-the-fly. - // case "zopfli": // Too slow on-the-fly. - // case "xz": // Too slow on-the-fly. + // Find supported encoder by accepted list by precedence + for _, name := range encodingPrecedence { + if fn, ok := encoders[name]; ok && matchAcceptEncoding(accepted, name) { + return fn, name + } } - return encodingNone + // No encoder found to match the accepted encoding + return nil, "" } -type maybeCompressResponseWriter struct { +func matchAcceptEncoding(accepted []string, encoding string) bool { + for _, v := range accepted { + if strings.Index(v, encoding) >= 0 { + return true + } + } + return false +} + +type compressResponseWriter struct { http.ResponseWriter w io.Writer - encoding encoding + encoder EncoderFunc + encoding string contentTypes map[string]struct{} level int wroteHeader bool } -func (w *maybeCompressResponseWriter) WriteHeader(code int) { - if w.wroteHeader { +func (cw *compressResponseWriter) WriteHeader(code int) { + if cw.wroteHeader { return } - w.wroteHeader = true - defer w.ResponseWriter.WriteHeader(code) + cw.wroteHeader = true + defer cw.ResponseWriter.WriteHeader(code) // Already compressed data? - if w.ResponseWriter.Header().Get("Content-Encoding") != "" { + if cw.Header().Get("Content-Encoding") != "" { return } - // The content-length after compression is unknown - w.ResponseWriter.Header().Del("Content-Length") // Parse the first part of the Content-Type response header. contentType := "" - parts := strings.Split(w.ResponseWriter.Header().Get("Content-Type"), ";") + parts := strings.Split(cw.Header().Get("Content-Type"), ";") if len(parts) > 0 { contentType = parts[0] } // Is the content type compressable? - if _, ok := w.contentTypes[contentType]; !ok { + if _, ok := cw.contentTypes[contentType]; !ok { return } - // Select the compress writer. - switch w.encoding { - case encodingGzip: - gw, err := gzip.NewWriterLevel(w.ResponseWriter, w.level) - if err != nil { - w.w = w.ResponseWriter - return - } - w.w = gw - w.ResponseWriter.Header().Set("Content-Encoding", "gzip") + if cw.encoder != nil && cw.encoding != "" { + if wr := cw.encoder(cw.ResponseWriter, cw.level); wr != nil { + cw.w = wr + cw.Header().Set("Content-Encoding", cw.encoding) - case encodingDeflate: - dw, err := flate.NewWriter(w.ResponseWriter, w.level) - if err != nil { - w.w = w.ResponseWriter - return + // The content-length after compression is unknown + cw.Header().Del("Content-Length") } - w.w = dw - w.ResponseWriter.Header().Set("Content-Encoding", "deflate") } } -func (w *maybeCompressResponseWriter) Write(p []byte) (int, error) { - if !w.wroteHeader { - w.WriteHeader(http.StatusOK) +func (cw *compressResponseWriter) Write(p []byte) (int, error) { + if !cw.wroteHeader { + cw.WriteHeader(http.StatusOK) } - return w.w.Write(p) + return cw.w.Write(p) } -func (w *maybeCompressResponseWriter) Flush() { - if f, ok := w.w.(http.Flusher); ok { +func (cw *compressResponseWriter) Flush() { + if f, ok := cw.w.(http.Flusher); ok { f.Flush() } } -func (w *maybeCompressResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - if hj, ok := w.w.(http.Hijacker); ok { +func (cw *compressResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + if hj, ok := cw.w.(http.Hijacker); ok { return hj.Hijack() } return nil, nil, errors.New("chi/middleware: http.Hijacker is unavailable on the writer") } -func (w *maybeCompressResponseWriter) CloseNotify() <-chan bool { - if cn, ok := w.w.(http.CloseNotifier); ok { - return cn.CloseNotify() +func (cw *compressResponseWriter) Push(target string, opts *http.PushOptions) error { + if ps, ok := cw.w.(http.Pusher); ok { + return ps.Push(target, opts) } - - // If the underlying writer does not implement http.CloseNotifier, return - // a channel that never receives a value. The semantics here is that the - // client never disconnnects before the request is processed by the - // http.Handler, which is close enough to the default behavior (when - // CloseNotify() is not even called). - return make(chan bool, 1) + return errors.New("chi/middleware: http.Pusher is unavailable on the writer") } -func (w *maybeCompressResponseWriter) Close() error { - if c, ok := w.w.(io.WriteCloser); ok { +func (cw *compressResponseWriter) Close() error { + if c, ok := cw.w.(io.WriteCloser); ok { return c.Close() } return errors.New("chi/middleware: io.WriteCloser is unavailable on the writer") } + +func encoderGzip(w http.ResponseWriter, level int) io.Writer { + gw, err := gzip.NewWriterLevel(w, level) + if err != nil { + return nil + } + return gw +} + +func encoderDeflate(w http.ResponseWriter, level int) io.Writer { + dw, err := flate.NewWriter(w, level) + if err != nil { + return nil + } + return dw +} diff --git a/backend/vendor/github.com/go-chi/chi/middleware/compress18.go b/backend/vendor/github.com/go-chi/chi/middleware/compress18.go deleted file mode 100644 index 0048f7d9..00000000 --- a/backend/vendor/github.com/go-chi/chi/middleware/compress18.go +++ /dev/null @@ -1,15 +0,0 @@ -// +build go1.8 appengine - -package middleware - -import ( - "errors" - "net/http" -) - -func (w *maybeCompressResponseWriter) Push(target string, opts *http.PushOptions) error { - if ps, ok := w.w.(http.Pusher); ok { - return ps.Push(target, opts) - } - return errors.New("chi/middleware: http.Pusher is unavailable on the writer") -} diff --git a/backend/vendor/github.com/go-chi/chi/middleware/content_type.go b/backend/vendor/github.com/go-chi/chi/middleware/content_type.go index 3a2dc20a..ee495787 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/content_type.go +++ b/backend/vendor/github.com/go-chi/chi/middleware/content_type.go @@ -26,6 +26,12 @@ func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handl return func(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { + if r.ContentLength == 0 { + // skip check for empty content body + next.ServeHTTP(w, r) + return + } + s := strings.ToLower(strings.TrimSpace(r.Header.Get("Content-Type"))) if i := strings.Index(s, ";"); i > -1 { s = s[0:i] diff --git a/backend/vendor/github.com/go-chi/chi/middleware/logger.go b/backend/vendor/github.com/go-chi/chi/middleware/logger.go index 99fac03d..9f119d56 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/logger.go +++ b/backend/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 @@ -81,29 +81,32 @@ type LoggerInterface interface { // DefaultLogFormatter is a simple logger that implements a LogFormatter. type DefaultLogFormatter struct { - Logger LoggerInterface + 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) @@ -114,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()) @@ -148,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/backend/vendor/github.com/go-chi/chi/middleware/nocache.go b/backend/vendor/github.com/go-chi/chi/middleware/nocache.go index e5819ddd..2412829e 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/nocache.go +++ b/backend/vendor/github.com/go-chi/chi/middleware/nocache.go @@ -14,7 +14,7 @@ var epoch = time.Unix(0, 0).Format(time.RFC1123) // Taken from https://github.com/mytrile/nocache var noCacheHeaders = map[string]string{ "Expires": epoch, - "Cache-Control": "no-cache, no-store, must-revalidate, private, max-age=0", + "Cache-Control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", "Pragma": "no-cache", "X-Accel-Expires": "0", } diff --git a/backend/vendor/github.com/go-chi/chi/middleware/realip.go b/backend/vendor/github.com/go-chi/chi/middleware/realip.go index e9addbe3..146c2b0a 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/realip.go +++ b/backend/vendor/github.com/go-chi/chi/middleware/realip.go @@ -22,7 +22,7 @@ var xRealIP = http.CanonicalHeaderKey("X-Real-IP") // You should only use this middleware if you can trust the headers passed to // you (in particular, the two headers this middleware uses), for example // because you have placed a reverse proxy like HAProxy or nginx in front of -// Goji. If your reverse proxies are configured to pass along arbitrary header +// chi. If your reverse proxies are configured to pass along arbitrary header // values from the client, or if you use this middleware without a reverse // proxy, malicious clients will be able to make you very sad (or, depending on // how you're using RemoteAddr, vulnerable to an attack of some sort). diff --git a/backend/vendor/github.com/go-chi/chi/middleware/request_id.go b/backend/vendor/github.com/go-chi/chi/middleware/request_id.go index 4574bde8..65b58f63 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/request_id.go +++ b/backend/vendor/github.com/go-chi/chi/middleware/request_id.go @@ -17,7 +17,7 @@ import ( // Key to use when setting the request ID. type ctxKeyRequestID int -// RequestIDKey is the key that holds th unique request ID in a request context. +// RequestIDKey is the key that holds the unique request ID in a request context. const RequestIDKey ctxKeyRequestID = 0 var prefix string @@ -62,9 +62,13 @@ func init() { // counter. func RequestID(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { - myid := atomic.AddUint64(&reqid, 1) ctx := r.Context() - ctx = context.WithValue(ctx, RequestIDKey, fmt.Sprintf("%s-%06d", prefix, myid)) + requestID := r.Header.Get("X-Request-Id") + if requestID == "" { + myid := atomic.AddUint64(&reqid, 1) + requestID = fmt.Sprintf("%s-%06d", prefix, myid) + } + ctx = context.WithValue(ctx, RequestIDKey, requestID) next.ServeHTTP(w, r.WithContext(ctx)) } return http.HandlerFunc(fn) diff --git a/backend/vendor/github.com/go-chi/chi/middleware/strip.go b/backend/vendor/github.com/go-chi/chi/middleware/strip.go index 8f19766b..2b8b1842 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/strip.go +++ b/backend/vendor/github.com/go-chi/chi/middleware/strip.go @@ -1,6 +1,7 @@ package middleware import ( + "fmt" "net/http" "github.com/go-chi/chi" @@ -28,6 +29,9 @@ func StripSlashes(next http.Handler) http.Handler { // RedirectSlashes is a middleware that will match request paths with a trailing // slash and redirect to the same path, less the trailing slash. +// +// NOTE: RedirectSlashes middleware is *incompatible* with http.FileServer, +// see https://github.com/go-chi/chi/issues/343 func RedirectSlashes(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { var path string @@ -38,7 +42,11 @@ func RedirectSlashes(next http.Handler) http.Handler { path = r.URL.Path } if len(path) > 1 && path[len(path)-1] == '/' { - path = path[:len(path)-1] + if r.URL.RawQuery != "" { + path = fmt.Sprintf("%s?%s", path[:len(path)-1], r.URL.RawQuery) + } else { + path = path[:len(path)-1] + } http.Redirect(w, r, path, 301) return } diff --git a/backend/vendor/github.com/go-chi/chi/middleware/terminal.go b/backend/vendor/github.com/go-chi/chi/middleware/terminal.go index 79930a25..a5d42410 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/terminal.go +++ b/backend/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/backend/vendor/github.com/go-chi/chi/middleware/timeout.go b/backend/vendor/github.com/go-chi/chi/middleware/timeout.go index 5cabf1f9..8e373536 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/timeout.go +++ b/backend/vendor/github.com/go-chi/chi/middleware/timeout.go @@ -15,7 +15,8 @@ import ( // // ie. a route/handler may look like: // -// r.Get("/long", func(ctx context.Context, w http.ResponseWriter, r *http.Request) { +// r.Get("/long", func(w http.ResponseWriter, r *http.Request) { +// ctx := r.Context() // processTime := time.Duration(rand.Intn(4)+1) * time.Second // // select { diff --git a/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer.go b/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer.go index 5d1c286b..5e5594f8 100644 --- a/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer.go +++ b/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer.go @@ -10,6 +10,32 @@ import ( "net/http" ) +// NewWrapResponseWriter wraps an http.ResponseWriter, returning a proxy that allows you to +// hook into various parts of the response process. +func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWriter { + _, fl := w.(http.Flusher) + + bw := basicWriter{ResponseWriter: w} + + if protoMajor == 2 { + _, ps := w.(http.Pusher) + if fl && ps { + return &http2FancyWriter{bw} + } + } else { + _, hj := w.(http.Hijacker) + _, rf := w.(io.ReaderFrom) + if fl && hj && rf { + return &httpFancyWriter{bw} + } + } + if fl { + return &flushWriter{bw} + } + + return &bw +} + // WrapResponseWriter is a proxy around an http.ResponseWriter that allows you to hook // into various parts of the response process. type WrapResponseWriter interface { @@ -47,6 +73,7 @@ func (b *basicWriter) WriteHeader(code int) { b.ResponseWriter.WriteHeader(code) } } + func (b *basicWriter) Write(buf []byte) (int, error) { b.WriteHeader(http.StatusOK) n, err := b.ResponseWriter.Write(buf) @@ -60,20 +87,25 @@ func (b *basicWriter) Write(buf []byte) (int, error) { b.bytes += n return n, err } + func (b *basicWriter) maybeWriteHeader() { if !b.wroteHeader { b.WriteHeader(http.StatusOK) } } + func (b *basicWriter) Status() int { return b.code } + func (b *basicWriter) BytesWritten() int { return b.bytes } + func (b *basicWriter) Tee(w io.Writer) { b.tee = w } + func (b *basicWriter) Unwrap() http.ResponseWriter { return b.ResponseWriter } @@ -83,13 +115,15 @@ type flushWriter struct { } func (f *flushWriter) Flush() { + f.wroteHeader = true + fl := f.basicWriter.ResponseWriter.(http.Flusher) fl.Flush() } var _ http.Flusher = &flushWriter{} -// httpFancyWriter is a HTTP writer that additionally satisfies http.CloseNotifier, +// httpFancyWriter is a HTTP writer that additionally satisfies // http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case // of wrapping the http.ResponseWriter that package http gives you, in order to // make the proxied object support the full method set of the proxied object. @@ -97,18 +131,22 @@ type httpFancyWriter struct { basicWriter } -func (f *httpFancyWriter) CloseNotify() <-chan bool { - cn := f.basicWriter.ResponseWriter.(http.CloseNotifier) - return cn.CloseNotify() -} func (f *httpFancyWriter) Flush() { + f.wroteHeader = true + fl := f.basicWriter.ResponseWriter.(http.Flusher) fl.Flush() } + func (f *httpFancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { hj := f.basicWriter.ResponseWriter.(http.Hijacker) return hj.Hijack() } + +func (f *http2FancyWriter) Push(target string, opts *http.PushOptions) error { + return f.basicWriter.ResponseWriter.(http.Pusher).Push(target, opts) +} + func (f *httpFancyWriter) ReadFrom(r io.Reader) (int64, error) { if f.basicWriter.tee != nil { n, err := io.Copy(&f.basicWriter, r) @@ -122,12 +160,12 @@ func (f *httpFancyWriter) ReadFrom(r io.Reader) (int64, error) { return n, err } -var _ http.CloseNotifier = &httpFancyWriter{} var _ http.Flusher = &httpFancyWriter{} var _ http.Hijacker = &httpFancyWriter{} +var _ http.Pusher = &http2FancyWriter{} var _ io.ReaderFrom = &httpFancyWriter{} -// http2FancyWriter is a HTTP2 writer that additionally satisfies http.CloseNotifier, +// http2FancyWriter is a HTTP2 writer that additionally satisfies // http.Flusher, and io.ReaderFrom. It exists for the common case // of wrapping the http.ResponseWriter that package http gives you, in order to // make the proxied object support the full method set of the proxied object. @@ -135,14 +173,11 @@ type http2FancyWriter struct { basicWriter } -func (f *http2FancyWriter) CloseNotify() <-chan bool { - cn := f.basicWriter.ResponseWriter.(http.CloseNotifier) - return cn.CloseNotify() -} func (f *http2FancyWriter) Flush() { + f.wroteHeader = true + fl := f.basicWriter.ResponseWriter.(http.Flusher) fl.Flush() } -var _ http.CloseNotifier = &http2FancyWriter{} var _ http.Flusher = &http2FancyWriter{} diff --git a/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer17.go b/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer17.go deleted file mode 100644 index c60df608..00000000 --- a/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer17.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build go1.7,!go1.8 - -package middleware - -import ( - "io" - "net/http" -) - -// NewWrapResponseWriter wraps an http.ResponseWriter, returning a proxy that allows you to -// hook into various parts of the response process. -func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWriter { - _, cn := w.(http.CloseNotifier) - _, fl := w.(http.Flusher) - - bw := basicWriter{ResponseWriter: w} - - if protoMajor == 2 { - if cn && fl { - return &http2FancyWriter{bw} - } - } else { - _, hj := w.(http.Hijacker) - _, rf := w.(io.ReaderFrom) - if cn && fl && hj && rf { - return &httpFancyWriter{bw} - } - } - if fl { - return &flushWriter{bw} - } - - return &bw -} diff --git a/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer18.go b/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer18.go deleted file mode 100644 index 115c2d4f..00000000 --- a/backend/vendor/github.com/go-chi/chi/middleware/wrap_writer18.go +++ /dev/null @@ -1,41 +0,0 @@ -// +build go1.8 appengine - -package middleware - -import ( - "io" - "net/http" -) - -// NewWrapResponseWriter wraps an http.ResponseWriter, returning a proxy that allows you to -// hook into various parts of the response process. -func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWriter { - _, cn := w.(http.CloseNotifier) - _, fl := w.(http.Flusher) - - bw := basicWriter{ResponseWriter: w} - - if protoMajor == 2 { - _, ps := w.(http.Pusher) - if cn && fl && ps { - return &http2FancyWriter{bw} - } - } else { - _, hj := w.(http.Hijacker) - _, rf := w.(io.ReaderFrom) - if cn && fl && hj && rf { - return &httpFancyWriter{bw} - } - } - if fl { - return &flushWriter{bw} - } - - return &bw -} - -func (f *http2FancyWriter) Push(target string, opts *http.PushOptions) error { - return f.basicWriter.ResponseWriter.(http.Pusher).Push(target, opts) -} - -var _ http.Pusher = &http2FancyWriter{} diff --git a/backend/vendor/github.com/go-chi/chi/mux.go b/backend/vendor/github.com/go-chi/chi/mux.go index 84a2424a..e553287e 100644 --- a/backend/vendor/github.com/go-chi/chi/mux.go +++ b/backend/vendor/github.com/go-chi/chi/mux.go @@ -60,7 +60,8 @@ func NewMux() *Mux { func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Ensure the mux has some routes defined on the mux if mx.handler == nil { - panic("chi: attempting to route to a mux with no handlers.") + mx.NotFoundHandler().ServeHTTP(w, r) + return } // Check if a routing context already exists from a parent router. diff --git a/backend/vendor/github.com/go-chi/chi/tree.go b/backend/vendor/github.com/go-chi/chi/tree.go index a55d7f14..8a044f3e 100644 --- a/backend/vendor/github.com/go-chi/chi/tree.go +++ b/backend/vendor/github.com/go-chi/chi/tree.go @@ -33,15 +33,15 @@ var mALL = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS | mPATCH | mPOST | mPUT | mTRACE var methodMap = map[string]methodTyp{ - "CONNECT": mCONNECT, - "DELETE": mDELETE, - "GET": mGET, - "HEAD": mHEAD, - "OPTIONS": mOPTIONS, - "PATCH": mPATCH, - "POST": mPOST, - "PUT": mPUT, - "TRACE": mTRACE, + http.MethodConnect: mCONNECT, + http.MethodDelete: mDELETE, + http.MethodGet: mGET, + http.MethodHead: mHEAD, + http.MethodOptions: mOPTIONS, + http.MethodPatch: mPATCH, + http.MethodPost: mPOST, + http.MethodPut: mPUT, + http.MethodTrace: mTRACE, } // RegisterMethod adds support for custom HTTP method handlers, available @@ -706,7 +706,9 @@ func patNextSegment(pattern string) (nodeTyp, string, string, byte, int, int) { } // Wildcard pattern as finale - // TODO: should we panic if there is stuff after the * ??? + if ws < len(pattern)-1 { + panic("chi: wildcard '*' must be the last value in a route. trim trailing text or use a '{param}' instead") + } return ntCatchAll, "*", "", 0, ws, len(pattern) } diff --git a/backend/vendor/github.com/go-pkgz/lgr/.golangci.yml b/backend/vendor/github.com/go-pkgz/lgr/.golangci.yml new file mode 100644 index 00000000..989af86e --- /dev/null +++ b/backend/vendor/github.com/go-pkgz/lgr/.golangci.yml @@ -0,0 +1,60 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 15 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 2 + misspell: + locale: US + lll: + line-length: 140 + gocritic: + enabled-tags: + - performance + - style + - experimental + disabled-checks: + - wrapperFunc + +linters: + disable-all: true + enable: + - megacheck + - govet + - unconvert + - megacheck + - structcheck + - gas + - gocyclo + - dupl + - misspell + - unparam + - varcheck + - deadcode + - typecheck + - ineffassign + - varcheck + fast: false + + +run: +# modules-download-mode: vendor + skip-dirs: + - vendor + +issues: + exclude-rules: + - text: "weak cryptographic primitive" + linters: + - gosec + +service: + golangci-lint-version: 1.16.x \ No newline at end of file diff --git a/backend/vendor/github.com/go-pkgz/lgr/.travis.yml b/backend/vendor/github.com/go-pkgz/lgr/.travis.yml index 4e4d2ade..189a75c8 100644 --- a/backend/vendor/github.com/go-pkgz/lgr/.travis.yml +++ b/backend/vendor/github.com/go-pkgz/lgr/.travis.yml @@ -13,7 +13,6 @@ before_install: script: - GO111MODULE=on go get ./... - - GO111MODULE=on go mod vendor - - GO111MODULE=on go test -v -mod=vendor -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1; - - golangci-lint run || travis_terminate 1; + - GO111MODULE=on go test -v -covermode=count -coverprofile=profile.cov ./... || travis_terminate 1; + - golangci-lint run || travis_terminate 1; - $GOPATH/bin/goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/backend/vendor/github.com/go-pkgz/lgr/README.md b/backend/vendor/github.com/go-pkgz/lgr/README.md index 77d81424..33559091 100644 --- a/backend/vendor/github.com/go-pkgz/lgr/README.md +++ b/backend/vendor/github.com/go-pkgz/lgr/README.md @@ -32,7 +32,7 @@ _Without `lgr.Caller*` it will drop `{caller}` part_ `lgr.New` call accepts functional options: -- `lgr.Debug` - turn debug mode on to allow messages with "DEBUG" level (filtered overwise) +- `lgr.Debug` - turn debug mode on to allow messages with "DEBUG" level (filtered otherwise) - `lgr.Out(io.Writer)` - sets the output writer, default `os.Stdout` - `lgr.Err(io.Writer)` - sets the error writer, default `os.Stderr` - `lgr.CallerFile` - adds the caller file info @@ -42,6 +42,8 @@ _Without `lgr.Caller*` it will drop `{caller}` part_ - `lgr.Msec` - adds milliseconds to timestamp - `lgr.Format` - sets custom template, overwrite all other formatting modifiers. +example: `l := lgr.New(lgr.Debug, lgr.Msec)` + #### formatting templates: Several predefined templates provided and can be passed directly to `lgr.Format`, i.e. `lgr.Format(lgr.WithMsec)` @@ -58,9 +60,11 @@ Several predefined templates provided and can be passed directly to `lgr.Format` User can make a custom template and pass it directly to `lgr.Format`. For example: ```go - lgr.Format(`{{.Level}} - {{.DT.Format "2006-01-02T15:04:05Z07:00") - {{.CallerPkg}} - {{.Message}}`) + lgr.Format(`{{.Level}} - {{.DT.Format "2006-01-02T15:04:05Z07:00"}} - {{.CallerPkg}} - {{.Message}}`) ``` -) + +_Note: formatter (predefined or custom) adds measurable overhead - the cost will depend on the version of Go, but is between 30 + and 50% in recent tests with 1.12. You can validate this in your environment via benchmarks: `go test -bench=. -run=Bench`_ ### levels @@ -70,8 +74,9 @@ User can make a custom template and pass it directly to `lgr.Format`. For exampl - `DEBUG` will be filtered unless `lgr.Debug` or `lgr.Trace` options defined - `INFO` and `WARN` don't have any special behavior attached - `ERROR` sends messages to both out and err writers -- `PANIC` and `FATAL` send messages to both out and err writers. In addition sends dump of callers and runtime info to err only, and calls `os.Exit(1)`. - +- `FATAL` and send messages to both out and err writers and exit(1) +- `PANIC` does the same as `FATAL` but in addition sends dump of callers and runtime info to err. + ### adaptors `lgr` logger can be converted to `io.Writer` or `*log.Logger` @@ -79,11 +84,10 @@ User can make a custom template and pass it directly to `lgr.Format`. For exampl - `lgr.ToWriter(l lgr.L, level string) io.Writer` - makes io.Writer forwarding write ops to underlying `lgr.L` - `lgr.ToStdLogger(l lgr.L, level string) *log.Logger` - makes standard logger on top of `lgr.L` -_`level` parameter is optional, if defined will enforce the level._ +_`level` parameter is optional, if defined (non-empty) will enforce the level._ ### global logger Users **should avoid** global logger and pass the concrete logger as a dependency. However, in some cases a global logger may be needed, for example migration from stdlib `log` to `lgr`. For such cases `log "github.com/go-pkgz/lgr"` can be imported instead of `log` package. Global logger provides `lgr.Printf`, `lgr.Print` and `lgr.Fatalf` functions. User can customize the logger by calling `lgr.Setup(options ...)`. The instance of this logger can be retrieved with `lgr.Default()` - diff --git a/backend/vendor/github.com/go-pkgz/lgr/adaptor.go b/backend/vendor/github.com/go-pkgz/lgr/adaptor.go index 3c213c2f..2627325d 100644 --- a/backend/vendor/github.com/go-pkgz/lgr/adaptor.go +++ b/backend/vendor/github.com/go-pkgz/lgr/adaptor.go @@ -11,9 +11,9 @@ type Writer struct { level string // if defined added to each message } -// Write to lgr.L, trim EOL +// Write to lgr.L func (w *Writer) Write(p []byte) (n int, err error) { - w.Logf(strings.TrimSuffix(w.level+string(p), "\n")) + w.Logf(w.level + string(p)) return len(p), nil } diff --git a/backend/vendor/github.com/go-pkgz/lgr/interface.go b/backend/vendor/github.com/go-pkgz/lgr/interface.go index dbe1969a..46b961aa 100644 --- a/backend/vendor/github.com/go-pkgz/lgr/interface.go +++ b/backend/vendor/github.com/go-pkgz/lgr/interface.go @@ -2,7 +2,6 @@ package lgr import ( stdlog "log" - "os" ) var def = New() // default logger doesn't allow DEBUG and doesn't add caller info @@ -37,7 +36,7 @@ func Print(line string) { // Fatalf simplifies replacement of std logger func Fatalf(format string, args ...interface{}) { def.logf(format, args...) - os.Exit(1) + def.fatal() } // Setup default logger with options diff --git a/backend/vendor/github.com/go-pkgz/lgr/logger.go b/backend/vendor/github.com/go-pkgz/lgr/logger.go index 264c9b2e..2dd86885 100644 --- a/backend/vendor/github.com/go-pkgz/lgr/logger.go +++ b/backend/vendor/github.com/go-pkgz/lgr/logger.go @@ -1,10 +1,11 @@ // Package lgr provides a simple logger with some extras. Primary way to log is Logf method. // The logger's output can be customized in 2 ways: -// - by passing formatting template, i.e. lgr.New(lgr.Format(lgr.Short)) // - by setting individual formatting flags, i.e. lgr.New(lgr.Msec, lgr.CallerFunc) -// Leveled output works for messages based on level prefix, i.e. Logf("INFO some message") means INFO level. +// - by passing formatting template, i.e. lgr.New(lgr.Format(lgr.Short)) +// Leveled output works for messages based on text prefix, i.e. Logf("INFO some message") means INFO level. // Debug and trace levels can be filtered based on lgr.Trace and lgr.Debug options. -// ERROR, FATAL and PANIC levels send to err as well. Both FATAL and PANIC also print stack trace and terminate caller application with os.Exit(1) +// ERROR, FATAL and PANIC levels send to err as well. FATAL terminate caller application with os.Exit(1) +// and PANIC also prints stack trace. package lgr @@ -15,6 +16,7 @@ import ( "os" "path" "runtime" + "strconv" "strings" "sync" "text/template" @@ -59,7 +61,7 @@ type Logger struct { type nowFn func() time.Time type panicFn func() -// layout holds all parts to construct the final message with template +// layout holds all parts to construct the final message with template or with individual flags type layout struct { DT time.Time Level string @@ -85,27 +87,28 @@ func New(options ...Option) *Logger { opt(&res) } - var err error - if res.format == "" { - res.format = res.templateFromOptions() + if res.format != "" { + // formatter defined + var err error + res.templ, err = template.New("lgr").Parse(res.format) + if err != nil { + fmt.Printf("invalid template %s, error %v. switched to %s\n", res.format, err, Short) + res.format = Short + res.templ = template.Must(template.New("lgrDefault").Parse(Short)) + } + + buf := bytes.Buffer{} + if err = res.templ.Execute(&buf, layout{}); err != nil { + fmt.Printf("failed to execute template %s, error %v. switched to %s\n", res.format, err, Short) + res.format = Short + res.templ = template.Must(template.New("lgrDefault").Parse(Short)) + } } - res.templ, err = template.New("lgr").Parse(res.format) - if err != nil { - fmt.Printf("invalid template %s, error %v. switched to %s\n", res.format, err, Short) - res.format = Short - res.templ = template.Must(template.New("lgrDefault").Parse(Short)) - } + // set *On flags once for optimization on multiple Logf calls + res.callerOn = strings.Contains(res.format, "{{.Caller") || res.callerFile || res.callerFunc || res.callerPkg + res.levelBracesOn = strings.Contains(res.format, "[{{.Level}}]") || res.levelBraces - buf := bytes.Buffer{} - if err = res.templ.Execute(&buf, layout{}); err != nil { - fmt.Printf("failed to execute template %s, error %v. switched to %s\n", res.format, err, Short) - res.format = Short - res.templ = template.Must(template.New("lgrDefault").Parse(Short)) - } - - res.callerOn = strings.Contains(res.format, "{{.Caller") - res.levelBracesOn = strings.Contains(res.format, "[{{.Level}}]") return &res } @@ -128,7 +131,7 @@ func (l *Logger) logf(format string, args ...interface{}) { return } - ci := callerInfo{} + var ci callerInfo if l.callerOn { // optimization to avoid expensive caller evaluation if caller info not in the template ci = l.reportCaller(l.callerDepth) } @@ -136,21 +139,26 @@ func (l *Logger) logf(format string, args ...interface{}) { elems := layout{ DT: l.now(), Level: l.formatLevel(lv), - Message: strings.TrimSuffix(msg, "\n"), + Message: strings.TrimSuffix(msg, "\n"), // output adds EOL, trim from the message if passed CallerFunc: ci.FuncName, CallerFile: ci.File, CallerPkg: ci.Pkg, CallerLine: ci.Line, } - buf := bytes.Buffer{} - err := l.templ.Execute(&buf, elems) // once constructed, a template may be executed safely in parallel. - if err != nil { - fmt.Printf("failed to execute template, %v\n", err) + var data []byte + if l.format == "" { + data = []byte(l.formatWithOptions(elems)) + } else { + buf := bytes.Buffer{} + err := l.templ.Execute(&buf, elems) // once constructed, a template may be executed safely in parallel. + if err != nil { + fmt.Printf("failed to execute template, %v\n", err) // should never happen + } + data = buf.Bytes() } - buf.WriteString("\n") + data = append(data, '\n') - data := buf.Bytes() if l.levelBracesOn { // rearrange space in short levels data = bytes.Replace(data, []byte("[WARN ]"), []byte("[WARN] "), 1) data = bytes.Replace(data, []byte("[INFO ]"), []byte("[INFO] "), 1) @@ -230,51 +238,51 @@ func (l *Logger) reportCaller(calldepth int) (res callerInfo) { return res } -// make template from option flags -func (l *Logger) templateFromOptions() (res string) { +// speed-optimized version of formatter, used with individual options only, i.e. without Format call +func (l *Logger) formatWithOptions(elems layout) (res string) { - const ( - // escape { and } from templates to allow "{some/blah}" output for caller - openCallerBrace = `{{"{"}}` - closeCallerBrace = `{{"}"}}` - ) - - orElse := func(flag bool, value string, elseValue string) string { + orElse := func(flag bool, fnTrue func() string, fnFalse func() string) string { if flag { - return value + return fnTrue() } - return elseValue + return fnFalse() } + nothing := func() string { return "" } - var parts []string + parts := make([]string, 0, 4) - parts = append(parts, orElse(l.msec, `{{.DT.Format "2006/01/02 15:04:05.000"}}`, `{{.DT.Format "2006/01/02 15:04:05"}}`)) - parts = append(parts, orElse(l.levelBraces, `[{{.Level}}]`, `{{.Level}}`)) + parts = append(parts, orElse(l.msec, + func() string { return elems.DT.Format("2006/01/02 15:04:05.000") }, + func() string { return elems.DT.Format("2006/01/02 15:04:05") }, + )) + + parts = append(parts, orElse(l.levelBraces, + func() string { return `[` + elems.Level + `]` }, + func() string { return elems.Level }, + )) if l.callerFile || l.callerFunc || l.callerPkg { var callerParts []string - if v := orElse(l.callerFile, `{{.CallerFile}}:{{.CallerLine}}`, ""); v != "" { + v := orElse(l.callerFile, func() string { return elems.CallerFile + ":" + strconv.Itoa(elems.CallerLine) }, nothing) + if v != "" { callerParts = append(callerParts, v) } - if v := orElse(l.callerFunc, `{{.CallerFunc}}`, ""); v != "" { + if v := orElse(l.callerFunc, func() string { return elems.CallerFunc }, nothing); v != "" { callerParts = append(callerParts, v) } - if v := orElse(l.callerPkg, `{{.CallerPkg}}`, ""); v != "" { + if v := orElse(l.callerPkg, func() string { return elems.CallerPkg }, nothing); v != "" { callerParts = append(callerParts, v) } - parts = append(parts, openCallerBrace+strings.Join(callerParts, " ")+closeCallerBrace) + parts = append(parts, "{"+strings.Join(callerParts, " ")+"}") } - parts = append(parts, "{{.Message}}") + + parts = append(parts, elems.Message) return strings.Join(parts, " ") } // formatLevel aligns level to 5 chars func (l *Logger) formatLevel(lv string) string { - if lv == "" { - return "" - } - spaces := "" if len(lv) == 4 { spaces = " " @@ -305,70 +313,3 @@ func getDump() []byte { } return stacktrace[:length] } - -// Option func type -type Option func(l *Logger) - -// Out sets out writer, stdout by default -func Out(w io.Writer) Option { - return func(l *Logger) { - l.stdout = w - } -} - -// Err sets error writer, stderr by default -func Err(w io.Writer) Option { - return func(l *Logger) { - l.stderr = w - } -} - -// Debug turn on dbg mode -func Debug(l *Logger) { - l.dbg = true -} - -// Trace turn on trace + dbg mode -func Trace(l *Logger) { - l.dbg = true - l.trace = true -} - -// CallerDepth sets number of stack frame skipped for caller reporting, 0 by default -func CallerDepth(n int) Option { - return func(l *Logger) { - l.callerDepth = n - } -} - -// Format sets output layout, overwrites all options for individual parts, i.e. Caller*, Msec and LevelBraces -func Format(f string) Option { - return func(l *Logger) { - l.format = f - } -} - -// CallerFunc adds caller info with function name. Ignored if Format option used. -func CallerFunc(l *Logger) { - l.callerFunc = true -} - -// CallerPkg adds caller's package name. Ignored if Format option used. -func CallerPkg(l *Logger) { - l.callerPkg = true -} - -// LevelBraces surrounds level with [], i.e. [INFO]. Ignored if Format option used. -func LevelBraces(l *Logger) { - l.levelBraces = true -} - -// CallerFile adds caller info with file, and line number. Ignored if Format option used. -func CallerFile(l *Logger) { - l.callerFile = true -} - -// Msec adds .msec to timestamp. Ignored if Format option used. -func Msec(l *Logger) { - l.msec = true -} diff --git a/backend/vendor/github.com/go-pkgz/lgr/options.go b/backend/vendor/github.com/go-pkgz/lgr/options.go new file mode 100644 index 00000000..3d790395 --- /dev/null +++ b/backend/vendor/github.com/go-pkgz/lgr/options.go @@ -0,0 +1,70 @@ +package lgr + +import "io" + +// Option func type +type Option func(l *Logger) + +// Out sets out writer, stdout by default +func Out(w io.Writer) Option { + return func(l *Logger) { + l.stdout = w + } +} + +// Err sets error writer, stderr by default +func Err(w io.Writer) Option { + return func(l *Logger) { + l.stderr = w + } +} + +// Debug turn on dbg mode +func Debug(l *Logger) { + l.dbg = true +} + +// Trace turn on trace + dbg mode +func Trace(l *Logger) { + l.dbg = true + l.trace = true +} + +// CallerDepth sets number of stack frame skipped for caller reporting, 0 by default +func CallerDepth(n int) Option { + return func(l *Logger) { + l.callerDepth = n + } +} + +// Format sets output layout, overwrites all options for individual parts, i.e. Caller*, Msec and LevelBraces +func Format(f string) Option { + return func(l *Logger) { + l.format = f + } +} + +// CallerFunc adds caller info with function name. Ignored if Format option used. +func CallerFunc(l *Logger) { + l.callerFunc = true +} + +// CallerPkg adds caller's package name. Ignored if Format option used. +func CallerPkg(l *Logger) { + l.callerPkg = true +} + +// LevelBraces surrounds level with [], i.e. [INFO]. Ignored if Format option used. +func LevelBraces(l *Logger) { + l.levelBraces = true +} + +// CallerFile adds caller info with file, and line number. Ignored if Format option used. +func CallerFile(l *Logger) { + l.callerFile = true +} + +// Msec adds .msec to timestamp. Ignored if Format option used. +func Msec(l *Logger) { + l.msec = true +} diff --git a/backend/vendor/modules.txt b/backend/vendor/modules.txt index bdc6b4eb..55764647 100644 --- a/backend/vendor/modules.txt +++ b/backend/vendor/modules.txt @@ -23,7 +23,7 @@ github.com/globalsign/mgo/bson github.com/globalsign/mgo/internal/sasl github.com/globalsign/mgo/internal/scram github.com/globalsign/mgo/internal/json -# github.com/go-chi/chi v3.3.2+incompatible +# github.com/go-chi/chi v4.0.2+incompatible github.com/go-chi/chi github.com/go-chi/chi/middleware # github.com/go-chi/cors v1.0.0 @@ -39,7 +39,7 @@ github.com/go-pkgz/auth/logger github.com/go-pkgz/auth/middleware # github.com/go-pkgz/lcw v0.2.0 github.com/go-pkgz/lcw -# github.com/go-pkgz/lgr v0.6.1 +# github.com/go-pkgz/lgr v0.6.2 github.com/go-pkgz/lgr # github.com/go-pkgz/mongo v1.1.2 github.com/go-pkgz/mongo