From f4b236c66aa5658dfe073adab03d9972c291bf91 Mon Sep 17 00:00:00 2001 From: Dmitry Verkhoturov Date: Tue, 30 Jun 2026 22:54:58 +0100 Subject: [PATCH] Replace chi middleware.Timeout with the timeout helper, drop chi/middleware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit middleware.Timeout was the last use of go-chi/chi/v5/middleware (RealIP, the other user, landed in #2099). Swap it for the local timeout helper (context deadline + 504 on deadline, matching chi exactly; covered by TestTimeout), which removes the go-chi/chi/v5/middleware package from the vendor tree. The go-chi/chi module stays in go.mod — the router (chi.NewRouter etc.) still uses it, so go.mod only shrinks after the router migration. Build, vet, race tests and golangci-lint clean. --- backend/app/rest/api/rest.go | 23 +- .../go-chi/chi/v5/middleware/basic_auth.go | 33 -- .../go-chi/chi/v5/middleware/clean_path.go | 28 -- .../go-chi/chi/v5/middleware/compress.go | 392 ------------------ .../chi/v5/middleware/content_charset.go | 45 -- .../chi/v5/middleware/content_encoding.go | 34 -- .../go-chi/chi/v5/middleware/content_type.go | 45 -- .../go-chi/chi/v5/middleware/get_head.go | 39 -- .../go-chi/chi/v5/middleware/heartbeat.go | 26 -- .../go-chi/chi/v5/middleware/logger.go | 172 -------- .../go-chi/chi/v5/middleware/maybe.go | 18 - .../go-chi/chi/v5/middleware/middleware.go | 23 - .../go-chi/chi/v5/middleware/nocache.go | 59 --- .../go-chi/chi/v5/middleware/page_route.go | 20 - .../go-chi/chi/v5/middleware/path_rewrite.go | 16 - .../go-chi/chi/v5/middleware/profiler.go | 49 --- .../go-chi/chi/v5/middleware/realip.go | 56 --- .../go-chi/chi/v5/middleware/recoverer.go | 203 --------- .../go-chi/chi/v5/middleware/request_id.go | 96 ----- .../go-chi/chi/v5/middleware/request_size.go | 18 - .../go-chi/chi/v5/middleware/route_headers.go | 146 ------- .../go-chi/chi/v5/middleware/strip.go | 77 ---- .../go-chi/chi/v5/middleware/sunset.go | 25 -- .../chi/v5/middleware/supress_notfound.go | 27 -- .../go-chi/chi/v5/middleware/terminal.go | 63 --- .../go-chi/chi/v5/middleware/throttle.go | 151 ------- .../go-chi/chi/v5/middleware/timeout.go | 48 --- .../go-chi/chi/v5/middleware/url_format.go | 77 ---- .../go-chi/chi/v5/middleware/value.go | 17 - .../go-chi/chi/v5/middleware/wrap_writer.go | 241 ----------- backend/vendor/modules.txt | 1 - 31 files changed, 11 insertions(+), 2257 deletions(-) delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/get_head.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/logger.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/maybe.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/middleware.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/nocache.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/page_route.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/request_id.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/request_size.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/sunset.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/terminal.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/timeout.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/value.go delete mode 100644 backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index f5dbf987..6cc46fc7 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -18,7 +18,6 @@ import ( "github.com/didip/tollbooth/v8" "github.com/didip/tollbooth/v8/limiter" "github.com/go-chi/chi/v5" - "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" "github.com/go-pkgz/auth/v2" "github.com/go-pkgz/lcw/v2" @@ -250,14 +249,14 @@ func (s *Rest) routes() chi.Router { authHandler, avatarHandler := s.Authenticator.Handlers() router.Group(func(r chi.Router) { - r.Use(middleware.Timeout(5 * time.Second)) + r.Use(timeout(5 * time.Second)) r.Use(logInfoWithBody, rateLimiter(2), R.NoCache) r.Use(validEmailAuth()) // reject suspicious email logins r.Mount("/auth", authHandler) }) router.Group(func(r chi.Router) { - r.Use(middleware.Timeout(5 * time.Second)) + r.Use(timeout(5 * time.Second)) r.Use(rateLimiter(100)) r.Mount("/avatar", avatarHandler) }) @@ -268,14 +267,14 @@ func (s *Rest) routes() chi.Router { router.Route("/api/v1", func(rapi chi.Router) { rapi.Use(apiCSPMiddleware) rapi.Group(func(rava chi.Router) { - rava.Use(middleware.Timeout(5 * time.Second)) + rava.Use(timeout(5 * time.Second)) rava.Use(rateLimiter(100)) rava.Mount("/avatar", avatarHandler) }) // open routes rapi.Group(func(ropen chi.Router) { - ropen.Use(middleware.Timeout(30 * time.Second)) + ropen.Use(timeout(30 * time.Second)) ropen.Use(rateLimiter(s.openRouteLimiter)) ropen.Use(authMiddleware.Trace, R.NoCache, logInfoWithBody) ropen.Get("/config", s.configCtrl) @@ -302,7 +301,7 @@ func (s *Rest) routes() chi.Router { // invalidation on revalidation); error responses get Cache-Control: no-store // so transient failures aren't pinned in the cache. rapi.Group(func(ropen chi.Router) { - ropen.Use(middleware.Timeout(30 * time.Second)) + ropen.Use(timeout(30 * time.Second)) ropen.Use(rateLimiter(10)) ropen.Use(authMiddleware.Trace, logInfoWithBody) ropen.Get("/img", s.ImageProxy.Handler) @@ -312,7 +311,7 @@ func (s *Rest) routes() chi.Router { // protected routes, require auth rapi.Group(func(rauth chi.Router) { - rauth.Use(middleware.Timeout(30 * time.Second)) + rauth.Use(timeout(30 * time.Second)) rauth.Use(rateLimiter(10)) rauth.Use(authMiddleware.Auth, matchSiteID, R.NoCache, logInfoWithBody) rauth.Get("/user", s.privRest.userInfoCtrl) @@ -321,7 +320,7 @@ func (s *Rest) routes() chi.Router { // admin routes, require auth and admin users only rapi.Route("/admin", func(radmin chi.Router) { - radmin.Use(middleware.Timeout(30 * time.Second)) + radmin.Use(timeout(30 * time.Second)) radmin.Use(rateLimiter(10)) radmin.Use(authMiddleware.Auth, authMiddleware.AdminOnly, matchSiteID) radmin.Use(R.NoCache, logInfoWithBody) @@ -347,7 +346,7 @@ func (s *Rest) routes() chi.Router { // protected routes, throttled to 10/s by default, controlled by external UpdateLimiter param rapi.Group(func(rauth chi.Router) { - rauth.Use(middleware.Timeout(10 * time.Second)) + rauth.Use(timeout(10 * time.Second)) rauth.Use(rateLimiter(s.updateLimiter())) rauth.Use(authMiddleware.Auth, matchSiteID, subscribersOnly(s.SubscribersOnly)) rauth.Use(R.NoCache, logInfoWithBody) @@ -367,7 +366,7 @@ func (s *Rest) routes() chi.Router { // protected routes, anonymous rejected rapi.Group(func(rauth chi.Router) { - rauth.Use(middleware.Timeout(10 * time.Second)) + rauth.Use(timeout(10 * time.Second)) rauth.Use(rateLimiter(s.updateLimiter())) rauth.Use(authMiddleware.Auth, rejectAnonUser, matchSiteID) rauth.Use(logger.New(logger.Log(log.Default()), logger.Prefix("[DEBUG]"), logger.IPfn(ipFn)).Handler) @@ -377,7 +376,7 @@ func (s *Rest) routes() chi.Router { // open routes on root level router.Group(func(rroot chi.Router) { - rroot.Use(middleware.Timeout(10 * time.Second)) + rroot.Use(timeout(10 * time.Second)) rroot.Use(rateLimiter(50)) rroot.Get("/robots.txt", s.pubRest.robotsCtrl) rroot.Get("/email/unsubscribe.html", s.privRest.emailUnsubscribeCtrl) @@ -515,7 +514,7 @@ func addFileServer(r chi.Router, embedFS embed.FS, webRoot, version string) { r.Get("/web", http.RedirectHandler("/web/", http.StatusMovedPermanently).ServeHTTP) r.With(rateLimiter(20), - middleware.Timeout(10*time.Second), + timeout(10*time.Second), cacheControl(time.Hour, version), ).Get("/web/*", func(w http.ResponseWriter, r *http.Request) { // don't show dirs, just serve files diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go deleted file mode 100644 index a546c9e9..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/basic_auth.go +++ /dev/null @@ -1,33 +0,0 @@ -package middleware - -import ( - "crypto/subtle" - "fmt" - "net/http" -) - -// BasicAuth implements a simple middleware handler for adding basic http auth to a route. -func BasicAuth(realm string, creds map[string]string) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - user, pass, ok := r.BasicAuth() - if !ok { - basicAuthFailed(w, realm) - return - } - - credPass, credUserOk := creds[user] - if !credUserOk || subtle.ConstantTimeCompare([]byte(pass), []byte(credPass)) != 1 { - basicAuthFailed(w, realm) - return - } - - next.ServeHTTP(w, r) - }) - } -} - -func basicAuthFailed(w http.ResponseWriter, realm string) { - w.Header().Add("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm)) - w.WriteHeader(http.StatusUnauthorized) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go deleted file mode 100644 index adeba429..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/clean_path.go +++ /dev/null @@ -1,28 +0,0 @@ -package middleware - -import ( - "net/http" - "path" - - "github.com/go-chi/chi/v5" -) - -// CleanPath middleware will clean out double slash mistakes from a user's request path. -// For example, if a user requests /users//1 or //users////1 will both be treated as: /users/1 -func CleanPath(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - rctx := chi.RouteContext(r.Context()) - - routePath := rctx.RoutePath - if routePath == "" { - if r.URL.RawPath != "" { - routePath = r.URL.RawPath - } else { - routePath = r.URL.Path - } - rctx.RoutePath = path.Clean(routePath) - } - - next.ServeHTTP(w, r) - }) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go deleted file mode 100644 index 9c64bd48..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/compress.go +++ /dev/null @@ -1,392 +0,0 @@ -package middleware - -import ( - "bufio" - "compress/flate" - "compress/gzip" - "errors" - "fmt" - "io" - "net" - "net/http" - "strings" - "sync" -) - -var defaultCompressibleContentTypes = []string{ - "text/html", - "text/css", - "text/plain", - "text/javascript", - "application/javascript", - "application/x-javascript", - "application/json", - "application/atom+xml", - "application/rss+xml", - "image/svg+xml", -} - -// Compress is a middleware that compresses response -// 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. -// -// Passing a compression level of 5 is sensible value -func Compress(level int, types ...string) func(next http.Handler) http.Handler { - compressor := NewCompressor(level, types...) - return compressor.Handler -} - -// Compressor represents a set of encoding configurations. -type Compressor struct { - // The mapping of encoder names to encoder functions. - encoders map[string]EncoderFunc - // The mapping of pooled encoders to pools. - pooledEncoders map[string]*sync.Pool - // The set of content types allowed to be compressed. - allowedTypes map[string]struct{} - allowedWildcards map[string]struct{} - // The list of encoders in order of decreasing precedence. - encodingPrecedence []string - level int // The compression level. -} - -// NewCompressor creates a new Compressor that will handle encoding responses. -// -// The level should be one of the ones defined in the flate package. -// The types are the content types that are allowed to be compressed. -func NewCompressor(level int, types ...string) *Compressor { - // If types are provided, set those as the allowed types. If none are - // provided, use the default list. - allowedTypes := make(map[string]struct{}) - allowedWildcards := make(map[string]struct{}) - if len(types) > 0 { - for _, t := range types { - if strings.Contains(strings.TrimSuffix(t, "/*"), "*") { - panic(fmt.Sprintf("middleware/compress: Unsupported content-type wildcard pattern '%s'. Only '/*' supported", t)) - } - if strings.HasSuffix(t, "/*") { - allowedWildcards[strings.TrimSuffix(t, "/*")] = struct{}{} - } else { - allowedTypes[t] = struct{}{} - } - } - } else { - for _, t := range defaultCompressibleContentTypes { - allowedTypes[t] = struct{}{} - } - } - - c := &Compressor{ - level: level, - encoders: make(map[string]EncoderFunc), - pooledEncoders: make(map[string]*sync.Pool), - allowedTypes: allowedTypes, - allowedWildcards: allowedWildcards, - } - - // Set the default encoders. The precedence order uses the reverse - // ordering that the encoders were added. This means adding new encoders - // will move them to the front of the order. - // - // TODO: - // lzma: Opera. - // sdch: Chrome, Android. Gzip output + dictionary header. - // br: Brotli, see https://github.com/go-chi/chi/pull/326 - - // 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 deflate. - c.SetEncoder("deflate", encoderDeflate) - - // TODO: Exception for old MSIE browsers that can't handle non-HTML? - // https://zoompf.com/blog/2012/02/lose-the-wait-http-compression - c.SetEncoder("gzip", encoderGzip) - - // 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. - return c -} - -// 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 algorithm: -// -// import brotli_enc "gopkg.in/kothar/brotli-go.v0/enc" -// -// compressor := middleware.NewCompressor(5, "text/html") -// compressor.SetEncoder("br", func(w io.Writer, level int) io.Writer { -// params := brotli_enc.NewBrotliParams() -// params.SetQuality(level) -// return brotli_enc.NewBrotliWriter(params, w) -// }) -func (c *Compressor) 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") - } - - // If we are adding a new encoder that is already registered, we have to - // clear that one out first. - delete(c.pooledEncoders, encoding) - delete(c.encoders, encoding) - - // If the encoder supports Resetting (IoReseterWriter), then it can be pooled. - encoder := fn(io.Discard, c.level) - if _, ok := encoder.(ioResetterWriter); ok { - pool := &sync.Pool{ - New: func() interface{} { - return fn(io.Discard, c.level) - }, - } - c.pooledEncoders[encoding] = pool - } - // If the encoder is not in the pooledEncoders, add it to the normal encoders. - if _, ok := c.pooledEncoders[encoding]; !ok { - c.encoders[encoding] = fn - } - - for i, v := range c.encodingPrecedence { - if v == encoding { - c.encodingPrecedence = append(c.encodingPrecedence[:i], c.encodingPrecedence[i+1:]...) - } - } - - c.encodingPrecedence = append([]string{encoding}, c.encodingPrecedence...) -} - -// Handler returns a new middleware that will compress the response based on the -// current Compressor. -func (c *Compressor) Handler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - encoder, encoding, cleanup := c.selectEncoder(r.Header, w) - - cw := &compressResponseWriter{ - ResponseWriter: w, - w: w, - contentTypes: c.allowedTypes, - contentWildcards: c.allowedWildcards, - encoding: encoding, - compressible: false, // determined in post-handler - } - if encoder != nil { - cw.w = encoder - } - // Re-add the encoder to the pool if applicable. - defer cleanup() - defer cw.Close() - - next.ServeHTTP(cw, r) - }) -} - -// selectEncoder returns the encoder, the name of the encoder, and a closer function. -func (c *Compressor) selectEncoder(h http.Header, w io.Writer) (io.Writer, string, func()) { - header := h.Get("Accept-Encoding") - - // Parse the names of all accepted algorithms from the header. - accepted := strings.Split(strings.ToLower(header), ",") - - // Find supported encoder by accepted list by precedence - for _, name := range c.encodingPrecedence { - if matchAcceptEncoding(accepted, name) { - if pool, ok := c.pooledEncoders[name]; ok { - encoder := pool.Get().(ioResetterWriter) - cleanup := func() { - pool.Put(encoder) - } - encoder.Reset(w) - return encoder, name, cleanup - - } - if fn, ok := c.encoders[name]; ok { - return fn(w, c.level), name, func() {} - } - } - - } - - // No encoder found to match the accepted encoding - return nil, "", func() {} -} - -func matchAcceptEncoding(accepted []string, encoding string) bool { - for _, v := range accepted { - if strings.Contains(v, encoding) { - return true - } - } - return false -} - -// An EncoderFunc is a function that wraps the provided io.Writer with a -// streaming compression algorithm and returns it. -// -// In case of failure, the function should return nil. -type EncoderFunc func(w io.Writer, level int) io.Writer - -// Interface for types that allow resetting io.Writers. -type ioResetterWriter interface { - io.Writer - Reset(w io.Writer) -} - -type compressResponseWriter struct { - http.ResponseWriter - - // The streaming encoder writer to be used if there is one. Otherwise, - // this is just the normal writer. - w io.Writer - contentTypes map[string]struct{} - contentWildcards map[string]struct{} - encoding string - wroteHeader bool - compressible bool -} - -func (cw *compressResponseWriter) isCompressible() bool { - // Parse the first part of the Content-Type response header. - contentType := cw.Header().Get("Content-Type") - contentType, _, _ = strings.Cut(contentType, ";") - - // Is the content type compressible? - if _, ok := cw.contentTypes[contentType]; ok { - return true - } - if contentType, _, hadSlash := strings.Cut(contentType, "/"); hadSlash { - _, ok := cw.contentWildcards[contentType] - return ok - } - return false -} - -func (cw *compressResponseWriter) WriteHeader(code int) { - if cw.wroteHeader { - cw.ResponseWriter.WriteHeader(code) // Allow multiple calls to propagate. - return - } - cw.wroteHeader = true - defer cw.ResponseWriter.WriteHeader(code) - - // Already compressed data? - if cw.Header().Get("Content-Encoding") != "" { - return - } - - if !cw.isCompressible() { - cw.compressible = false - return - } - - if cw.encoding != "" { - cw.compressible = true - cw.Header().Set("Content-Encoding", cw.encoding) - cw.Header().Add("Vary", "Accept-Encoding") - - // The content-length after compression is unknown - cw.Header().Del("Content-Length") - } -} - -func (cw *compressResponseWriter) Write(p []byte) (int, error) { - if !cw.wroteHeader { - cw.WriteHeader(http.StatusOK) - } - - return cw.writer().Write(p) -} - -func (cw *compressResponseWriter) writer() io.Writer { - if cw.compressible { - return cw.w - } - return cw.ResponseWriter -} - -type compressFlusher interface { - Flush() error -} - -func (cw *compressResponseWriter) Flush() { - if f, ok := cw.writer().(http.Flusher); ok { - f.Flush() - } - // If the underlying writer has a compression flush signature, - // call this Flush() method instead - if f, ok := cw.writer().(compressFlusher); ok { - f.Flush() - - // Also flush the underlying response writer - if f, ok := cw.ResponseWriter.(http.Flusher); ok { - f.Flush() - } - } -} - -func (cw *compressResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - if hj, ok := cw.writer().(http.Hijacker); ok { - return hj.Hijack() - } - return nil, nil, errors.New("chi/middleware: http.Hijacker is unavailable on the writer") -} - -func (cw *compressResponseWriter) Push(target string, opts *http.PushOptions) error { - if ps, ok := cw.writer().(http.Pusher); ok { - return ps.Push(target, opts) - } - return errors.New("chi/middleware: http.Pusher is unavailable on the writer") -} - -func (cw *compressResponseWriter) Close() error { - if c, ok := cw.writer().(io.WriteCloser); ok { - return c.Close() - } - return errors.New("chi/middleware: io.WriteCloser is unavailable on the writer") -} - -func (cw *compressResponseWriter) Unwrap() http.ResponseWriter { - return cw.ResponseWriter -} - -func encoderGzip(w io.Writer, level int) io.Writer { - gw, err := gzip.NewWriterLevel(w, level) - if err != nil { - return nil - } - return gw -} - -func encoderDeflate(w io.Writer, 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/v5/middleware/content_charset.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go deleted file mode 100644 index 8e75fe8e..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_charset.go +++ /dev/null @@ -1,45 +0,0 @@ -package middleware - -import ( - "net/http" - "slices" - "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, ";") - return slices.Contains(charsets, ce) -} - -// Split a string in two parts, cleaning any whitespace. -func split(str, sep string) (string, string) { - a, b, found := strings.Cut(str, sep) - a = strings.TrimSpace(a) - if found { - b = strings.TrimSpace(b) - } - - return a, b -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go deleted file mode 100644 index e0b9ccc0..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_encoding.go +++ /dev/null @@ -1,34 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// AllowContentEncoding enforces a whitelist of request Content-Encoding otherwise responds -// with a 415 Unsupported Media Type status. -func AllowContentEncoding(contentEncoding ...string) func(next http.Handler) http.Handler { - allowedEncodings := make(map[string]struct{}, len(contentEncoding)) - for _, encoding := range contentEncoding { - allowedEncodings[strings.TrimSpace(strings.ToLower(encoding))] = struct{}{} - } - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - requestEncodings := r.Header["Content-Encoding"] - // skip check for empty content body or no Content-Encoding - if r.ContentLength == 0 { - next.ServeHTTP(w, r) - return - } - // All encodings in the request must be allowed - for _, encoding := range requestEncodings { - if _, ok := allowedEncodings[strings.TrimSpace(strings.ToLower(encoding))]; !ok { - w.WriteHeader(http.StatusUnsupportedMediaType) - return - } - } - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go deleted file mode 100644 index cdfc21ee..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/content_type.go +++ /dev/null @@ -1,45 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// SetHeader is a convenience handler to set a response header key/value -func SetHeader(key, value string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set(key, value) - next.ServeHTTP(w, r) - }) - } -} - -// AllowContentType enforces a whitelist of request Content-Types otherwise responds -// with a 415 Unsupported Media Type status. -func AllowContentType(contentTypes ...string) func(http.Handler) http.Handler { - allowedContentTypes := make(map[string]struct{}, len(contentTypes)) - for _, ctype := range contentTypes { - allowedContentTypes[strings.TrimSpace(strings.ToLower(ctype))] = struct{}{} - } - - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.ContentLength == 0 { - // Skip check for empty content body - next.ServeHTTP(w, r) - return - } - - s, _, _ := strings.Cut(r.Header.Get("Content-Type"), ";") - s = strings.ToLower(strings.TrimSpace(s)) - - if _, ok := allowedContentTypes[s]; ok { - next.ServeHTTP(w, r) - return - } - - w.WriteHeader(http.StatusUnsupportedMediaType) - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/get_head.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/get_head.go deleted file mode 100644 index d4606d8b..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/get_head.go +++ /dev/null @@ -1,39 +0,0 @@ -package middleware - -import ( - "net/http" - - "github.com/go-chi/chi/v5" -) - -// 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/backend/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go deleted file mode 100644 index f36e8ccf..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/heartbeat.go +++ /dev/null @@ -1,26 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// Heartbeat endpoint middleware useful to setting up a path like -// `/ping` that load balancers or uptime testing external services -// can make a request before hitting any routes. It's also convenient -// to place this above ACL middlewares as well. -func Heartbeat(endpoint string) func(http.Handler) http.Handler { - f := func(h http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - if (r.Method == "GET" || r.Method == "HEAD") && strings.EqualFold(r.URL.Path, endpoint) { - w.Header().Set("Content-Type", "text/plain") - w.WriteHeader(http.StatusOK) - w.Write([]byte(".")) - return - } - h.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } - return f -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/logger.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/logger.go deleted file mode 100644 index cff9bd20..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/logger.go +++ /dev/null @@ -1,172 +0,0 @@ -package middleware - -import ( - "bytes" - "context" - "log" - "net/http" - "os" - "runtime" - "time" -) - -var ( - // LogEntryCtxKey is the context.Context key to store the request log entry. - LogEntryCtxKey = &contextKey{"LogEntry"} - - // 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 func(next http.Handler) http.Handler -) - -// Logger is a middleware that logs the start and end of each request, along -// with some useful data about what was requested, what the response status was, -// and how long it took to return. When standard output is a TTY, Logger will -// print in color, otherwise it will print in black and white. Logger prints a -// request ID if one is provided. -// -// Alternatively, look at https://github.com/goware/httplog for a more in-depth -// http logger with structured logging support. -// -// IMPORTANT NOTE: Logger should go before any other middleware that may change -// the response, such as middleware.Recoverer. Example: -// -// r := chi.NewRouter() -// r.Use(middleware.Logger) // <--<< Logger should come before Recoverer -// r.Use(middleware.Recoverer) -// r.Get("/", handler) -func Logger(next http.Handler) http.Handler { - return DefaultLogger(next) -} - -// RequestLogger returns a logger handler using a custom LogFormatter. -func RequestLogger(f LogFormatter) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - entry := f.NewLogEntry(r) - ww := NewWrapResponseWriter(w, r.ProtoMajor) - - t1 := time.Now() - defer func() { - entry.Write(ww.Status(), ww.BytesWritten(), ww.Header(), time.Since(t1), nil) - }() - - next.ServeHTTP(ww, WithLogEntry(r, entry)) - } - return http.HandlerFunc(fn) - } -} - -// LogFormatter initiates the beginning of a new LogEntry per request. -// See DefaultLogFormatter for an example implementation. -type LogFormatter interface { - NewLogEntry(r *http.Request) LogEntry -} - -// LogEntry records the final log when a request completes. -// See defaultLogEntry for an example implementation. -type LogEntry interface { - Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) - Panic(v interface{}, stack []byte) -} - -// GetLogEntry returns the in-context LogEntry for a request. -func GetLogEntry(r *http.Request) LogEntry { - entry, _ := r.Context().Value(LogEntryCtxKey).(LogEntry) - return entry -} - -// WithLogEntry sets the in-context LogEntry for a request. -func WithLogEntry(r *http.Request, entry LogEntry) *http.Request { - r = r.WithContext(context.WithValue(r.Context(), LogEntryCtxKey, entry)) - 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 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, useColor, nYellow, "[%s] ", reqID) - } - cW(entry.buf, useColor, nCyan, "\"") - cW(entry.buf, useColor, bMagenta, "%s ", r.Method) - - scheme := "http" - if r.TLS != nil { - scheme = "https" - } - 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) - entry.buf.WriteString(" - ") - - return entry -} - -type defaultLogEntry struct { - *DefaultLogFormatter - request *http.Request - buf *bytes.Buffer - useColor bool -} - -func (l *defaultLogEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) { - switch { - case status < 200: - cW(l.buf, l.useColor, bBlue, "%03d", status) - case status < 300: - cW(l.buf, l.useColor, bGreen, "%03d", status) - case status < 400: - cW(l.buf, l.useColor, bCyan, "%03d", status) - case status < 500: - cW(l.buf, l.useColor, bYellow, "%03d", status) - default: - cW(l.buf, l.useColor, bRed, "%03d", status) - } - - cW(l.buf, l.useColor, bBlue, " %dB", bytes) - - l.buf.WriteString(" in ") - if elapsed < 500*time.Millisecond { - cW(l.buf, l.useColor, nGreen, "%s", elapsed) - } else if elapsed < 5*time.Second { - cW(l.buf, l.useColor, nYellow, "%s", elapsed) - } else { - cW(l.buf, l.useColor, nRed, "%s", elapsed) - } - - l.Logger.Print(l.buf.String()) -} - -func (l *defaultLogEntry) Panic(v interface{}, stack []byte) { - PrintPrettyStack(v) -} - -func init() { - color := true - if runtime.GOOS == "windows" { - color = false - } - DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: !color}) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/maybe.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/maybe.go deleted file mode 100644 index eabca005..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/maybe.go +++ /dev/null @@ -1,18 +0,0 @@ -package middleware - -import "net/http" - -// Maybe middleware will allow you to change the flow of the middleware stack execution depending on return -// value of maybeFn(request). This is useful for example if you'd like to skip a middleware handler if -// a request does not satisfy the maybeFn logic. -func Maybe(mw func(http.Handler) http.Handler, maybeFn func(r *http.Request) bool) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if maybeFn(r) { - mw(next).ServeHTTP(w, r) - } else { - next.ServeHTTP(w, r) - } - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/middleware.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/middleware.go deleted file mode 100644 index cc371e00..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/middleware.go +++ /dev/null @@ -1,23 +0,0 @@ -package middleware - -import "net/http" - -// New will create a new middleware handler from a http.Handler. -func New(h http.Handler) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - h.ServeHTTP(w, r) - }) - } -} - -// contextKey is a value for use with context.WithValue. It's used as -// a pointer so it fits in an interface{} without allocation. This technique -// for defining context keys was copied from Go 1.7's new use of context in net/http. -type contextKey struct { - name string -} - -func (k *contextKey) String() string { - return "chi/middleware context value " + k.name -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/nocache.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/nocache.go deleted file mode 100644 index 9308d40d..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/nocache.go +++ /dev/null @@ -1,59 +0,0 @@ -package middleware - -// Ported from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "net/http" - "time" -) - -// Unix epoch time -var epoch = time.Unix(0, 0).UTC().Format(http.TimeFormat) - -// Taken from https://github.com/mytrile/nocache -var noCacheHeaders = map[string]string{ - "Expires": epoch, - "Cache-Control": "no-cache, no-store, no-transform, must-revalidate, private, max-age=0", - "Pragma": "no-cache", - "X-Accel-Expires": "0", -} - -var etagHeaders = []string{ - "ETag", - "If-Modified-Since", - "If-Match", - "If-None-Match", - "If-Range", - "If-Unmodified-Since", -} - -// NoCache is a simple piece of middleware that sets a number of HTTP headers to prevent -// a router (or subrouter) from being cached by an upstream proxy and/or client. -// -// As per http://wiki.nginx.org/HttpProxyModule - NoCache sets: -// -// Expires: Thu, 01 Jan 1970 00:00:00 UTC -// Cache-Control: no-cache, private, max-age=0 -// X-Accel-Expires: 0 -// Pragma: no-cache (for HTTP/1.0 proxies/clients) -func NoCache(h http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - - // Delete any ETag headers that may have been set - for _, v := range etagHeaders { - if r.Header.Get(v) != "" { - r.Header.Del(v) - } - } - - // Set our NoCache headers - for k, v := range noCacheHeaders { - w.Header().Set(k, v) - } - - h.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/page_route.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/page_route.go deleted file mode 100644 index 32871b7e..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/page_route.go +++ /dev/null @@ -1,20 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// PageRoute is a simple middleware which allows you to route a static GET request -// at the middleware stack level. -func PageRoute(path string, handler http.Handler) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == "GET" && strings.EqualFold(r.URL.Path, path) { - handler.ServeHTTP(w, r) - return - } - next.ServeHTTP(w, r) - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go deleted file mode 100644 index 99af62c0..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/path_rewrite.go +++ /dev/null @@ -1,16 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// PathRewrite is a simple middleware which allows you to rewrite the request URL path. -func PathRewrite(old, new string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - r.URL.Path = strings.Replace(r.URL.Path, old, new, 1) - next.ServeHTTP(w, r) - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go deleted file mode 100644 index 0ad6a996..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/profiler.go +++ /dev/null @@ -1,49 +0,0 @@ -//go:build !tinygo -// +build !tinygo - -package middleware - -import ( - "expvar" - "net/http" - "net/http/pprof" - - "github.com/go-chi/chi/v5" -) - -// Profiler is a convenient subrouter used for mounting net/http/pprof. ie. -// -// func MyService() http.Handler { -// r := chi.NewRouter() -// // ..middlewares -// r.Mount("/debug", middleware.Profiler()) -// // ..routes -// return r -// } -func Profiler() http.Handler { - r := chi.NewRouter() - r.Use(NoCache) - - r.Get("/", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, r.RequestURI+"/pprof/", http.StatusMovedPermanently) - }) - r.HandleFunc("/pprof", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, r.RequestURI+"/", http.StatusMovedPermanently) - }) - - 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("/vars", expvar.Handler()) - - r.Handle("/pprof/goroutine", pprof.Handler("goroutine")) - r.Handle("/pprof/threadcreate", pprof.Handler("threadcreate")) - r.Handle("/pprof/mutex", pprof.Handler("mutex")) - r.Handle("/pprof/heap", pprof.Handler("heap")) - r.Handle("/pprof/block", pprof.Handler("block")) - r.Handle("/pprof/allocs", pprof.Handler("allocs")) - - return r -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go deleted file mode 100644 index afcb79e2..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/realip.go +++ /dev/null @@ -1,56 +0,0 @@ -package middleware - -// Ported from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "net" - "net/http" - "strings" -) - -var trueClientIP = http.CanonicalHeaderKey("True-Client-IP") -var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") -var xRealIP = http.CanonicalHeaderKey("X-Real-IP") - -// RealIP is a middleware that sets a http.Request's RemoteAddr to the results -// of parsing either the True-Client-IP, X-Real-IP or the X-Forwarded-For headers -// (in that order). -// -// This middleware should be inserted fairly early in the middleware stack to -// ensure that subsequent layers (e.g., request loggers) which examine the -// RemoteAddr will see the intended value. -// -// You should only use this middleware if you can trust the headers passed to -// you (in particular, the three headers this middleware uses), for example -// because you have placed a reverse proxy like HAProxy or nginx in front of -// 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). -func RealIP(h http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - if rip := realIP(r); rip != "" { - r.RemoteAddr = rip - } - h.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} - -func realIP(r *http.Request) string { - var ip string - - if tcip := r.Header.Get(trueClientIP); tcip != "" { - ip = tcip - } else if xrip := r.Header.Get(xRealIP); xrip != "" { - ip = xrip - } else if xff := r.Header.Get(xForwardedFor); xff != "" { - ip, _, _ = strings.Cut(xff, ",") - } - if ip == "" || net.ParseIP(ip) == nil { - return "" - } - return ip -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go deleted file mode 100644 index 81342dfa..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/recoverer.go +++ /dev/null @@ -1,203 +0,0 @@ -package middleware - -// The original work was derived from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "bytes" - "errors" - "fmt" - "io" - "net/http" - "os" - "runtime/debug" - "strings" -) - -// Recoverer is a middleware that recovers from panics, logs the panic (and a -// backtrace), and returns a HTTP 500 (Internal Server Error) status if -// possible. Recoverer prints a request ID if one is provided. -// -// Alternatively, look at https://github.com/go-chi/httplog middleware pkgs. -func Recoverer(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - defer func() { - if rvr := recover(); rvr != nil { - if rvr == http.ErrAbortHandler { - // we don't recover http.ErrAbortHandler so the response - // to the client is aborted, this should not be logged - panic(rvr) - } - - logEntry := GetLogEntry(r) - if logEntry != nil { - logEntry.Panic(rvr, debug.Stack()) - } else { - PrintPrettyStack(rvr) - } - - if r.Header.Get("Connection") != "Upgrade" { - w.WriteHeader(http.StatusInternalServerError) - } - } - }() - - next.ServeHTTP(w, r) - } - - return http.HandlerFunc(fn) -} - -// for ability to test the PrintPrettyStack function -var recovererErrorWriter io.Writer = os.Stderr - -func PrintPrettyStack(rvr interface{}) { - debugStack := debug.Stack() - s := prettyStack{} - out, err := s.parse(debugStack, rvr) - if err == nil { - recovererErrorWriter.Write(out) - } else { - // print stdlib output as a fallback - os.Stderr.Write(debugStack) - } -} - -type prettyStack struct { -} - -func (s prettyStack) parse(debugStack []byte, rvr interface{}) ([]byte, error) { - var err error - useColor := true - buf := &bytes.Buffer{} - - cW(buf, false, bRed, "\n") - cW(buf, useColor, bCyan, " panic: ") - cW(buf, useColor, bBlue, "%v", rvr) - cW(buf, false, bWhite, "\n \n") - - // process debug stack info - stack := strings.Split(string(debugStack), "\n") - lines := []string{} - - // locate panic line, as we may have nested panics - for i := len(stack) - 1; i > 0; i-- { - lines = append(lines, stack[i]) - if strings.HasPrefix(stack[i], "panic(") { - lines = lines[0 : len(lines)-2] // remove boilerplate - break - } - } - - // reverse - for i := len(lines)/2 - 1; i >= 0; i-- { - opp := len(lines) - 1 - i - lines[i], lines[opp] = lines[opp], lines[i] - } - - // decorate - for i, line := range lines { - lines[i], err = s.decorateLine(line, useColor, i) - if err != nil { - return nil, err - } - } - - for _, l := range lines { - fmt.Fprintf(buf, "%s", l) - } - return buf.Bytes(), nil -} - -func (s prettyStack) decorateLine(line string, useColor bool, num int) (string, error) { - line = strings.TrimSpace(line) - if strings.HasPrefix(line, "\t") || strings.Contains(line, ".go:") { - return s.decorateSourceLine(line, useColor, num) - } - if strings.HasSuffix(line, ")") { - return s.decorateFuncCallLine(line, useColor, num) - } - if strings.HasPrefix(line, "\t") { - return strings.Replace(line, "\t", " ", 1), nil - } - return fmt.Sprintf(" %s\n", line), nil -} - -func (s prettyStack) decorateFuncCallLine(line string, useColor bool, num int) (string, error) { - idx := strings.LastIndex(line, "(") - if idx < 0 { - return "", errors.New("not a func call line") - } - - buf := &bytes.Buffer{} - pkg := line[0:idx] - // addr := line[idx:] - method := "" - - if idx := strings.LastIndex(pkg, string(os.PathSeparator)); idx < 0 { - if idx := strings.Index(pkg, "."); idx > 0 { - method = pkg[idx:] - pkg = pkg[0:idx] - } - } else { - method = pkg[idx+1:] - pkg = pkg[0 : idx+1] - if idx := strings.Index(method, "."); idx > 0 { - pkg += method[0:idx] - method = method[idx:] - } - } - pkgColor := nYellow - methodColor := bGreen - - if num == 0 { - cW(buf, useColor, bRed, " -> ") - pkgColor = bMagenta - methodColor = bRed - } else { - cW(buf, useColor, bWhite, " ") - } - cW(buf, useColor, pkgColor, "%s", pkg) - cW(buf, useColor, methodColor, "%s\n", method) - // cW(buf, useColor, nBlack, "%s", addr) - return buf.String(), nil -} - -func (s prettyStack) decorateSourceLine(line string, useColor bool, num int) (string, error) { - idx := strings.LastIndex(line, ".go:") - if idx < 0 { - return "", errors.New("not a source line") - } - - buf := &bytes.Buffer{} - path := line[0 : idx+3] - lineno := line[idx+3:] - - idx = strings.LastIndex(path, string(os.PathSeparator)) - dir := path[0 : idx+1] - file := path[idx+1:] - - idx = strings.Index(lineno, " ") - if idx > 0 { - lineno = lineno[0:idx] - } - fileColor := bCyan - lineColor := bGreen - - if num == 1 { - cW(buf, useColor, bRed, " -> ") - fileColor = bRed - lineColor = bMagenta - } else { - cW(buf, false, bWhite, " ") - } - cW(buf, useColor, bWhite, "%s", dir) - cW(buf, useColor, fileColor, "%s", file) - cW(buf, useColor, lineColor, "%s", lineno) - if num == 1 { - cW(buf, false, bWhite, "\n") - } - cW(buf, false, bWhite, "\n") - - return buf.String(), nil -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/request_id.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/request_id.go deleted file mode 100644 index e1d4ccb7..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/request_id.go +++ /dev/null @@ -1,96 +0,0 @@ -package middleware - -// Ported from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "context" - "crypto/rand" - "encoding/base64" - "fmt" - "net/http" - "os" - "strings" - "sync/atomic" -) - -// Key to use when setting the request ID. -type ctxKeyRequestID int - -// RequestIDKey is the key that holds the unique request ID in a request context. -const RequestIDKey ctxKeyRequestID = 0 - -// RequestIDHeader is the name of the HTTP Header which contains the request id. -// Exported so that it can be changed by developers -var RequestIDHeader = "X-Request-Id" - -var prefix string -var reqid atomic.Uint64 - -// A quick note on the statistics here: we're trying to calculate the chance that -// two randomly generated base62 prefixes will collide. We use the formula from -// http://en.wikipedia.org/wiki/Birthday_problem -// -// P[m, n] \approx 1 - e^{-m^2/2n} -// -// We ballpark an upper bound for $m$ by imagining (for whatever reason) a server -// that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$ -// -// For a $k$ character base-62 identifier, we have $n(k) = 62^k$ -// -// Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for -// our purposes, and is surely more than anyone would ever need in practice -- a -// process that is rebooted a handful of times a day for a hundred years has less -// than a millionth of a percent chance of generating two colliding IDs. - -func init() { - hostname, err := os.Hostname() - if hostname == "" || err != nil { - hostname = "localhost" - } - var buf [12]byte - var b64 string - for len(b64) < 10 { - rand.Read(buf[:]) - b64 = base64.StdEncoding.EncodeToString(buf[:]) - b64 = strings.NewReplacer("+", "", "/", "").Replace(b64) - } - - prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10]) -} - -// RequestID is a middleware that injects a request ID into the context of each -// request. A request ID is a string of the form "host.example.com/random-0001", -// where "random" is a base62 random string that uniquely identifies this go -// process, and where the last number is an atomically incremented request -// counter. -func RequestID(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - requestID := r.Header.Get(RequestIDHeader) - if requestID == "" { - myid := reqid.Add(1) - requestID = fmt.Sprintf("%s-%06d", prefix, myid) - } - ctx = context.WithValue(ctx, RequestIDKey, requestID) - next.ServeHTTP(w, r.WithContext(ctx)) - } - return http.HandlerFunc(fn) -} - -// GetReqID returns a request ID from the given context if one is present. -// Returns the empty string if a request ID cannot be found. -func GetReqID(ctx context.Context) string { - if ctx == nil { - return "" - } - if reqID, ok := ctx.Value(RequestIDKey).(string); ok { - return reqID - } - return "" -} - -// NextRequestID generates the next request ID in the sequence. -func NextRequestID() uint64 { - return reqid.Add(1) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/request_size.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/request_size.go deleted file mode 100644 index 678248c4..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/request_size.go +++ /dev/null @@ -1,18 +0,0 @@ -package middleware - -import ( - "net/http" -) - -// RequestSize is a middleware that will limit request sizes to a specified -// number of bytes. It uses MaxBytesReader to do so. -func RequestSize(bytes int64) func(http.Handler) http.Handler { - f := func(h http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - r.Body = http.MaxBytesReader(w, r.Body, bytes) - h.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } - return f -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go deleted file mode 100644 index 1c3334d3..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/route_headers.go +++ /dev/null @@ -1,146 +0,0 @@ -package middleware - -import ( - "net/http" - "strings" -) - -// RouteHeaders is a neat little header-based router that allows you to direct -// the flow of a request through a middleware stack based on a request header. -// -// For example, lets say you'd like to setup multiple routers depending on the -// request Host header, you could then do something as so: -// -// r := chi.NewRouter() -// rSubdomain := chi.NewRouter() -// r.Use(middleware.RouteHeaders(). -// Route("Host", "example.com", middleware.New(r)). -// Route("Host", "*.example.com", middleware.New(rSubdomain)). -// Handler) -// r.Get("/", h) -// rSubdomain.Get("/", h2) -// -// Another example, imagine you want to setup multiple CORS handlers, where for -// your origin servers you allow authorized requests, but for third-party public -// requests, authorization is disabled. -// -// r := chi.NewRouter() -// r.Use(middleware.RouteHeaders(). -// Route("Origin", "https://app.skyweaver.net", cors.Handler(cors.Options{ -// AllowedOrigins: []string{"https://api.skyweaver.net"}, -// AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, -// AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"}, -// AllowCredentials: true, // <----------<<< allow credentials -// })). -// Route("Origin", "*", cors.Handler(cors.Options{ -// AllowedOrigins: []string{"*"}, -// AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, -// AllowedHeaders: []string{"Accept", "Content-Type"}, -// AllowCredentials: false, // <----------<<< do not allow credentials -// })). -// Handler) -func RouteHeaders() HeaderRouter { - return HeaderRouter{} -} - -type HeaderRouter map[string][]HeaderRoute - -func (hr HeaderRouter) Route(header, match string, middlewareHandler func(next http.Handler) http.Handler) HeaderRouter { - header = strings.ToLower(header) - k := hr[header] - if k == nil { - hr[header] = []HeaderRoute{} - } - hr[header] = append(hr[header], HeaderRoute{MatchOne: NewPattern(match), Middleware: middlewareHandler}) - return hr -} - -func (hr HeaderRouter) RouteAny(header string, match []string, middlewareHandler func(next http.Handler) http.Handler) HeaderRouter { - header = strings.ToLower(header) - k := hr[header] - if k == nil { - hr[header] = []HeaderRoute{} - } - patterns := []Pattern{} - for _, m := range match { - patterns = append(patterns, NewPattern(m)) - } - hr[header] = append(hr[header], HeaderRoute{MatchAny: patterns, Middleware: middlewareHandler}) - return hr -} - -func (hr HeaderRouter) RouteDefault(handler func(next http.Handler) http.Handler) HeaderRouter { - hr["*"] = []HeaderRoute{{Middleware: handler}} - return hr -} - -func (hr HeaderRouter) Handler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if len(hr) == 0 { - // skip if no routes set - next.ServeHTTP(w, r) - return - } - - // find first matching header route, and continue - for header, matchers := range hr { - headerValue := r.Header.Get(header) - if headerValue == "" { - continue - } - headerValue = strings.ToLower(headerValue) - for _, matcher := range matchers { - if matcher.IsMatch(headerValue) { - matcher.Middleware(next).ServeHTTP(w, r) - return - } - } - } - - // if no match, check for "*" default route - matcher, ok := hr["*"] - if !ok || matcher[0].Middleware == nil { - next.ServeHTTP(w, r) - return - } - matcher[0].Middleware(next).ServeHTTP(w, r) - }) -} - -type HeaderRoute struct { - Middleware func(next http.Handler) http.Handler - MatchOne Pattern - MatchAny []Pattern -} - -func (r HeaderRoute) IsMatch(value string) bool { - if len(r.MatchAny) > 0 { - for _, m := range r.MatchAny { - if m.Match(value) { - return true - } - } - } else if r.MatchOne.Match(value) { - return true - } - return false -} - -type Pattern struct { - prefix string - suffix string - wildcard bool -} - -func NewPattern(value string) Pattern { - p := Pattern{} - p.prefix, p.suffix, p.wildcard = strings.Cut(value, "*") - return p -} - -func (p Pattern) Match(v string) bool { - if !p.wildcard { - return p.prefix == v - } - return len(v) >= len(p.prefix+p.suffix) && strings.HasPrefix(v, p.prefix) && strings.HasSuffix(v, p.suffix) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go deleted file mode 100644 index 32d21e90..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/strip.go +++ /dev/null @@ -1,77 +0,0 @@ -package middleware - -import ( - "fmt" - "net/http" - "strings" - - "github.com/go-chi/chi/v5" -) - -// StripSlashes is a middleware that will match request paths with a trailing -// slash, strip it from the path and continue routing through the mux, if a route -// matches, then it will serve the handler. -func StripSlashes(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - var path string - rctx := chi.RouteContext(r.Context()) - if rctx != nil && rctx.RoutePath != "" { - path = rctx.RoutePath - } else { - path = r.URL.Path - } - if len(path) > 1 && path[len(path)-1] == '/' { - newPath := path[:len(path)-1] - if rctx == nil { - r.URL.Path = newPath - } else { - rctx.RoutePath = newPath - } - } - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) -} - -// 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 - rctx := chi.RouteContext(r.Context()) - if rctx != nil && rctx.RoutePath != "" { - path = rctx.RoutePath - } else { - path = r.URL.Path - } - - if len(path) > 1 && path[len(path)-1] == '/' { - // Normalize backslashes to forward slashes to prevent "/\evil.com" style redirects - // that some clients may interpret as protocol-relative. - path = strings.ReplaceAll(path, `\`, `/`) - - // Collapse leading/trailing slashes and force a single leading slash. - path := "/" + strings.Trim(path, "/") - - if r.URL.RawQuery != "" { - path = fmt.Sprintf("%s?%s", path, r.URL.RawQuery) - } - http.Redirect(w, r, path, 301) - return - } - - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) -} - -// StripPrefix is a middleware that will strip the provided prefix from the -// request path before handing the request over to the next handler. -func StripPrefix(prefix string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.StripPrefix(prefix, next) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/sunset.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/sunset.go deleted file mode 100644 index 18815d58..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/sunset.go +++ /dev/null @@ -1,25 +0,0 @@ -package middleware - -import ( - "net/http" - "time" -) - -// Sunset set Deprecation/Sunset header to response -// This can be used to enable Sunset in a route or a route group -// For more: https://www.rfc-editor.org/rfc/rfc8594.html -func Sunset(sunsetAt time.Time, links ...string) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !sunsetAt.IsZero() { - w.Header().Set("Sunset", sunsetAt.Format(http.TimeFormat)) - w.Header().Set("Deprecation", sunsetAt.Format(http.TimeFormat)) - - for _, link := range links { - w.Header().Add("Link", link) - } - } - next.ServeHTTP(w, r) - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go deleted file mode 100644 index 83a8a872..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/supress_notfound.go +++ /dev/null @@ -1,27 +0,0 @@ -package middleware - -import ( - "net/http" - - "github.com/go-chi/chi/v5" -) - -// SupressNotFound will quickly respond with a 404 if the route is not found -// and will not continue to the next middleware handler. -// -// This is handy to put at the top of your middleware stack to avoid unnecessary -// processing of requests that are not going to match any routes anyway. For -// example its super annoying to see a bunch of 404's in your logs from bots. -func SupressNotFound(router *chi.Mux) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - rctx := chi.RouteContext(r.Context()) - match := rctx.Routes.Match(rctx, r.Method, r.URL.Path) - if !match { - router.NotFoundHandler().ServeHTTP(w, r) - return - } - next.ServeHTTP(w, r) - }) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/terminal.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/terminal.go deleted file mode 100644 index 5ead7b92..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/terminal.go +++ /dev/null @@ -1,63 +0,0 @@ -package middleware - -// Ported from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "fmt" - "io" - "os" -) - -var ( - // Normal colors - nBlack = []byte{'\033', '[', '3', '0', 'm'} - nRed = []byte{'\033', '[', '3', '1', 'm'} - nGreen = []byte{'\033', '[', '3', '2', 'm'} - nYellow = []byte{'\033', '[', '3', '3', 'm'} - nBlue = []byte{'\033', '[', '3', '4', 'm'} - nMagenta = []byte{'\033', '[', '3', '5', 'm'} - nCyan = []byte{'\033', '[', '3', '6', 'm'} - nWhite = []byte{'\033', '[', '3', '7', 'm'} - // Bright colors - bBlack = []byte{'\033', '[', '3', '0', ';', '1', 'm'} - bRed = []byte{'\033', '[', '3', '1', ';', '1', 'm'} - bGreen = []byte{'\033', '[', '3', '2', ';', '1', 'm'} - bYellow = []byte{'\033', '[', '3', '3', ';', '1', 'm'} - bBlue = []byte{'\033', '[', '3', '4', ';', '1', 'm'} - bMagenta = []byte{'\033', '[', '3', '5', ';', '1', 'm'} - bCyan = []byte{'\033', '[', '3', '6', ';', '1', 'm'} - bWhite = []byte{'\033', '[', '3', '7', ';', '1', 'm'} - - reset = []byte{'\033', '[', '0', 'm'} -) - -var IsTTY bool - -func init() { - // This is sort of cheating: if stdout is a character device, we assume - // that means it's a TTY. Unfortunately, there are many non-TTY - // character devices, but fortunately stdout is rarely set to any of - // them. - // - // We could solve this properly by pulling in a dependency on - // code.google.com/p/go.crypto/ssh/terminal, for instance, but as a - // heuristic for whether to print in color or in black-and-white, I'd - // really rather not. - fi, err := os.Stdout.Stat() - if err == nil { - m := os.ModeDevice | os.ModeCharDevice - IsTTY = fi.Mode()&m == m - } -} - -// colorWrite -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 && useColor { - w.Write(reset) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go deleted file mode 100644 index 7ea482b9..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/throttle.go +++ /dev/null @@ -1,151 +0,0 @@ -package middleware - -import ( - "net/http" - "strconv" - "time" -) - -const ( - errCapacityExceeded = "Server capacity exceeded." - errTimedOut = "Timed out while waiting for a pending request to complete." - errContextCanceled = "Context was canceled." -) - -var ( - defaultBacklogTimeout = time.Second * 60 -) - -// ThrottleOpts represents a set of throttling options. -type ThrottleOpts struct { - RetryAfterFn func(ctxDone bool) time.Duration - Limit int - BacklogLimit int - BacklogTimeout time.Duration - StatusCode int -} - -// Throttle is a middleware that limits number of currently processed requests -// at a time across all users. Note: Throttle is not a rate-limiter per user, -// instead it just puts a ceiling on the number of current in-flight requests -// being processed from the point from where the Throttle middleware is mounted. -func Throttle(limit int) func(http.Handler) http.Handler { - return ThrottleWithOpts(ThrottleOpts{Limit: limit, BacklogTimeout: defaultBacklogTimeout}) -} - -// ThrottleBacklog is a middleware that limits number of currently processed -// requests at a time and provides a backlog for holding a finite number of -// pending requests. -func ThrottleBacklog(limit, backlogLimit int, backlogTimeout time.Duration) func(http.Handler) http.Handler { - return ThrottleWithOpts(ThrottleOpts{Limit: limit, BacklogLimit: backlogLimit, BacklogTimeout: backlogTimeout}) -} - -// ThrottleWithOpts is a middleware that limits number of currently processed requests using passed ThrottleOpts. -func ThrottleWithOpts(opts ThrottleOpts) func(http.Handler) http.Handler { - if opts.Limit < 1 { - panic("chi/middleware: Throttle expects limit > 0") - } - - if opts.BacklogLimit < 0 { - panic("chi/middleware: Throttle expects backlogLimit to be positive") - } - - statusCode := opts.StatusCode - if statusCode == 0 { - statusCode = http.StatusTooManyRequests - } - - t := throttler{ - tokens: make(chan token, opts.Limit), - backlogTokens: make(chan token, opts.Limit+opts.BacklogLimit), - backlogTimeout: opts.BacklogTimeout, - statusCode: statusCode, - retryAfterFn: opts.RetryAfterFn, - } - - // Filling tokens. - for i := 0; i < opts.Limit+opts.BacklogLimit; i++ { - if i < opts.Limit { - t.tokens <- token{} - } - t.backlogTokens <- token{} - } - - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - select { - - case <-ctx.Done(): - t.setRetryAfterHeaderIfNeeded(w, true) - http.Error(w, errContextCanceled, t.statusCode) - return - - case btok := <-t.backlogTokens: - defer func() { - t.backlogTokens <- btok - }() - - // Try to get a processing token immediately first - select { - case tok := <-t.tokens: - defer func() { - t.tokens <- tok - }() - next.ServeHTTP(w, r) - return - default: - // No immediate token available, need to wait with timer - } - - timer := time.NewTimer(t.backlogTimeout) - select { - case <-timer.C: - t.setRetryAfterHeaderIfNeeded(w, false) - http.Error(w, errTimedOut, t.statusCode) - return - case <-ctx.Done(): - timer.Stop() - t.setRetryAfterHeaderIfNeeded(w, true) - http.Error(w, errContextCanceled, t.statusCode) - return - case tok := <-t.tokens: - defer func() { - timer.Stop() - t.tokens <- tok - }() - next.ServeHTTP(w, r) - } - return - - default: - t.setRetryAfterHeaderIfNeeded(w, false) - http.Error(w, errCapacityExceeded, t.statusCode) - return - } - } - - return http.HandlerFunc(fn) - } -} - -// token represents a request that is being processed. -type token struct{} - -// throttler limits number of currently processed requests at a time. -type throttler struct { - tokens chan token - backlogTokens chan token - retryAfterFn func(ctxDone bool) time.Duration - backlogTimeout time.Duration - statusCode int -} - -// setRetryAfterHeaderIfNeeded sets Retry-After HTTP header if corresponding retryAfterFn option of throttler is initialized. -func (t throttler) setRetryAfterHeaderIfNeeded(w http.ResponseWriter, ctxDone bool) { - if t.retryAfterFn == nil { - return - } - w.Header().Set("Retry-After", strconv.Itoa(int(t.retryAfterFn(ctxDone).Seconds()))) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/timeout.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/timeout.go deleted file mode 100644 index add596d6..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/timeout.go +++ /dev/null @@ -1,48 +0,0 @@ -package middleware - -import ( - "context" - "net/http" - "time" -) - -// Timeout is a middleware that cancels ctx after a given timeout and return -// a 504 Gateway Timeout error to the client. -// -// It's required that you select the ctx.Done() channel to check for the signal -// if the context has reached its deadline and return, otherwise the timeout -// signal will be just ignored. -// -// ie. a route/handler may look like: -// -// r.Get("/long", func(w http.ResponseWriter, r *http.Request) { -// ctx := r.Context() -// processTime := time.Duration(rand.Intn(4)+1) * time.Second -// -// select { -// case <-ctx.Done(): -// return -// -// case <-time.After(processTime): -// // The above channel simulates some hard work. -// } -// -// w.Write([]byte("done")) -// }) -func Timeout(timeout time.Duration) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx, cancel := context.WithTimeout(r.Context(), timeout) - defer func() { - cancel() - if ctx.Err() == context.DeadlineExceeded { - w.WriteHeader(http.StatusGatewayTimeout) - } - }() - - r = r.WithContext(ctx) - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go deleted file mode 100644 index 2ec6657e..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/url_format.go +++ /dev/null @@ -1,77 +0,0 @@ -package middleware - -import ( - "context" - "net/http" - "strings" - - "github.com/go-chi/chi/v5" -) - -var ( - // URLFormatCtxKey is the context.Context key to store the URL format data - // for a request. - URLFormatCtxKey = &contextKey{"URLFormat"} -) - -// URLFormat is a middleware that parses the url extension from a request path and stores it -// on the context as a string under the key `middleware.URLFormatCtxKey`. The middleware will -// trim the suffix from the routing path and continue routing. -// -// Routers should not include a url parameter for the suffix when using this middleware. -// -// Sample usage for url paths `/articles/1`, `/articles/1.json` and `/articles/1.xml`: -// -// func routes() http.Handler { -// r := chi.NewRouter() -// r.Use(middleware.URLFormat) -// -// r.Get("/articles/{id}", ListArticles) -// -// return r -// } -// -// func ListArticles(w http.ResponseWriter, r *http.Request) { -// urlFormat, _ := r.Context().Value(middleware.URLFormatCtxKey).(string) -// -// switch urlFormat { -// case "json": -// render.JSON(w, r, articles) -// case "xml:" -// render.XML(w, r, articles) -// default: -// render.JSON(w, r, articles) -// } -// } -func URLFormat(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - var format string - path := r.URL.Path - - rctx := chi.RouteContext(r.Context()) - if rctx != nil && rctx.RoutePath != "" { - path = rctx.RoutePath - } - - if strings.Index(path, ".") > 0 { - base := strings.LastIndex(path, "/") - idx := strings.LastIndex(path[base:], ".") - - if idx > 0 { - idx += base - format = path[idx+1:] - - if rctx != nil { - rctx.RoutePath = path[:idx] - } - } - } - - r = r.WithContext(context.WithValue(ctx, URLFormatCtxKey, format)) - - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/value.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/value.go deleted file mode 100644 index a9dfd434..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/value.go +++ /dev/null @@ -1,17 +0,0 @@ -package middleware - -import ( - "context" - "net/http" -) - -// WithValue is a middleware that sets a given key/value in a context chain. -func WithValue(key, val interface{}) func(next http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { - r = r.WithContext(context.WithValue(r.Context(), key, val)) - next.ServeHTTP(w, r) - } - return http.HandlerFunc(fn) - } -} diff --git a/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go b/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go deleted file mode 100644 index 367e0fcd..00000000 --- a/backend/vendor/github.com/go-chi/chi/v5/middleware/wrap_writer.go +++ /dev/null @@ -1,241 +0,0 @@ -package middleware - -// The original work was derived from Goji's middleware, source: -// https://github.com/zenazn/goji/tree/master/web/middleware - -import ( - "bufio" - "io" - "net" - "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 && hj { - return &flushHijackWriter{bw} - } - if hj { - return &hijackWriter{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 { - http.ResponseWriter - // Status returns the HTTP status of the request, or 0 if one has not - // yet been sent. - Status() int - // BytesWritten returns the total number of bytes sent to the client. - BytesWritten() int - // Tee causes the response body to be written to the given io.Writer in - // addition to proxying the writes through. Only one io.Writer can be - // tee'd to at once: setting a second one will overwrite the first. - // Writes will be sent to the proxy before being written to this - // io.Writer. It is illegal for the tee'd writer to be modified - // concurrently with writes. - Tee(io.Writer) - // Unwrap returns the original proxied target. - Unwrap() http.ResponseWriter - // Discard causes all writes to the original ResponseWriter be discarded, - // instead writing only to the tee'd writer if it's set. - // The caller is responsible for calling WriteHeader and Write on the - // original ResponseWriter once the processing is done. - Discard() -} - -// basicWriter wraps a http.ResponseWriter that implements the minimal -// http.ResponseWriter interface. -type basicWriter struct { - http.ResponseWriter - tee io.Writer - code int - bytes int - wroteHeader bool - discard bool -} - -func (b *basicWriter) WriteHeader(code int) { - if code >= 100 && code <= 199 && code != http.StatusSwitchingProtocols { - if !b.discard { - b.ResponseWriter.WriteHeader(code) - } - } else if !b.wroteHeader { - b.code = code - b.wroteHeader = true - if !b.discard { - b.ResponseWriter.WriteHeader(code) - } - } -} - -func (b *basicWriter) Write(buf []byte) (n int, err error) { - b.maybeWriteHeader() - if !b.discard { - n, err = b.ResponseWriter.Write(buf) - if b.tee != nil { - _, err2 := b.tee.Write(buf[:n]) - // Prefer errors generated by the proxied writer. - if err == nil { - err = err2 - } - } - } else if b.tee != nil { - n, err = b.tee.Write(buf) - } else { - n, err = io.Discard.Write(buf) - } - 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 -} - -func (b *basicWriter) Discard() { - b.discard = true -} - -// flushWriter ... -type flushWriter struct { - basicWriter -} - -func (f *flushWriter) Flush() { - f.wroteHeader = true - fl := f.basicWriter.ResponseWriter.(http.Flusher) - fl.Flush() -} - -var _ http.Flusher = &flushWriter{} - -// hijackWriter ... -type hijackWriter struct { - basicWriter -} - -func (f *hijackWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hj := f.basicWriter.ResponseWriter.(http.Hijacker) - return hj.Hijack() -} - -var _ http.Hijacker = &hijackWriter{} - -// flushHijackWriter ... -type flushHijackWriter struct { - basicWriter -} - -func (f *flushHijackWriter) Flush() { - f.wroteHeader = true - fl := f.basicWriter.ResponseWriter.(http.Flusher) - fl.Flush() -} - -func (f *flushHijackWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { - hj := f.basicWriter.ResponseWriter.(http.Hijacker) - return hj.Hijack() -} - -var _ http.Flusher = &flushHijackWriter{} -var _ http.Hijacker = &flushHijackWriter{} - -// 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. -type httpFancyWriter struct { - basicWriter -} - -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) - f.basicWriter.bytes += int(n) - return n, err - } - rf := f.basicWriter.ResponseWriter.(io.ReaderFrom) - f.basicWriter.maybeWriteHeader() - n, err := rf.ReadFrom(r) - f.basicWriter.bytes += int(n) - return n, err -} - -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.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. -type http2FancyWriter struct { - basicWriter -} - -func (f *http2FancyWriter) Flush() { - f.wroteHeader = true - fl := f.basicWriter.ResponseWriter.(http.Flusher) - fl.Flush() -} - -var _ http.Flusher = &http2FancyWriter{} diff --git a/backend/vendor/modules.txt b/backend/vendor/modules.txt index ef04c67a..30cc5bf5 100644 --- a/backend/vendor/modules.txt +++ b/backend/vendor/modules.txt @@ -45,7 +45,6 @@ github.com/dlclark/regexp2/v2/syntax # github.com/go-chi/chi/v5 v5.2.5 ## explicit; go 1.22 github.com/go-chi/chi/v5 -github.com/go-chi/chi/v5/middleware # github.com/go-chi/cors v1.2.2 ## explicit; go 1.14 github.com/go-chi/cors