From 4d391cabc8eb88a67da5448b3d68350a7c13de1d Mon Sep 17 00:00:00 2001 From: niksis02 Date: Mon, 15 Jun 2026 14:48:31 +0400 Subject: [PATCH] feat: migrate Fiber to v3.3.0 Fixes #2180 Fixes #2181 Migrate the gateway from Fiber v2 to Fiber v3.3.0 and update the affected server, middleware, handler, controller, and test code for the new APIs. Replace the deprecated Fiber filesystem middleware used by the WebUI with the Fiber v3 static middleware, serving the embedded WebUI assets from an fs.Sub filesystem. Fix the request header limit handling regression by adding a temporary handler for Fiber v3/fasthttp small-buffer errors so oversized request headers return the expected regulated S3 error response. Fix the debuglogger panic by reworking the boxed key/value formatter used for debug request and response dumps. The formatter now handles long header keys and values without producing invalid wrap widths, negative padding, or out-of-range string slices. --- auth/access-control_test.go | 2 - debuglogger/logger.go | 134 ++++++++++++++---- embedgw/embedgw.go | 5 +- go.mod | 8 +- go.sum | 22 ++- metrics/metrics.go | 6 +- s3api/admin-router.go | 2 +- s3api/admin-server.go | 36 ++--- s3api/controllers/admin.go | 26 ++-- s3api/controllers/base.go | 22 +-- s3api/controllers/base_test.go | 62 ++++---- s3api/controllers/bucket-delete.go | 38 ++--- s3api/controllers/bucket-get.go | 86 +++++------ s3api/controllers/bucket-head.go | 8 +- s3api/controllers/bucket-list.go | 6 +- s3api/controllers/bucket-post.go | 18 +-- s3api/controllers/bucket-put.go | 78 +++++----- s3api/controllers/cors_default_origin_test.go | 6 +- s3api/controllers/object-delete.go | 24 ++-- s3api/controllers/object-get.go | 46 +++--- s3api/controllers/object-head.go | 10 +- s3api/controllers/object-post.go | 36 ++--- s3api/controllers/object-put.go | 66 ++++----- s3api/controllers/options.go | 6 +- s3api/middlewares/acl-parser.go | 6 +- s3api/middlewares/admin.go | 4 +- .../apply-bucket-cors-preflight.go | 8 +- .../apply-bucket-cors-preflight_test.go | 8 +- s3api/middlewares/apply-bucket-cors.go | 10 +- .../apply-default-cors-preflight.go | 6 +- .../apply-default-cors-preflight_test.go | 4 +- s3api/middlewares/apply-default-cors.go | 8 +- s3api/middlewares/apply-default-cors_test.go | 8 +- s3api/middlewares/authentication.go | 6 +- s3api/middlewares/body-reader.go | 4 +- .../bucket-object-name-validator.go | 4 +- s3api/middlewares/checksum.go | 6 +- s3api/middlewares/host-style-parser.go | 4 +- s3api/middlewares/logger.go | 4 +- s3api/middlewares/object-post-auth.go | 6 +- s3api/middlewares/object-post-auth_test.go | 18 +-- s3api/middlewares/presign-auth.go | 4 +- s3api/middlewares/public-bucket.go | 8 +- s3api/middlewares/rate-limiter.go | 6 +- s3api/middlewares/request-ids.go | 4 +- s3api/middlewares/request-ids_test.go | 6 +- s3api/middlewares/router-utilities.go | 8 +- s3api/middlewares/set-default-keys.go | 4 +- s3api/middlewares/url-decoder.go | 4 +- s3api/router.go | 24 ++-- s3api/router_cors_test.go | 2 +- s3api/router_test.go | 2 +- s3api/server.go | 101 +++++++++---- s3api/server_test.go | 15 +- s3api/utils/auth-reader.go | 4 +- s3api/utils/auth_test.go | 27 ++-- s3api/utils/chunk-reader.go | 8 +- s3api/utils/context-keys.go | 10 +- s3api/utils/precondition.go | 10 +- s3api/utils/presign-auth-reader.go | 12 +- s3api/utils/request_ids.go | 8 +- s3api/utils/request_ids_test.go | 2 +- s3api/utils/signed_headers_test.go | 6 +- s3api/utils/utils.go | 38 ++--- s3api/utils/utils_test.go | 4 +- .../request-header-section-too-large-error.go | 60 ++++++++ s3err/s3err.go | 6 + s3event/event.go | 6 +- s3event/kafka.go | 6 +- s3event/nats.go | 6 +- s3event/rabbitmq.go | 6 +- s3event/webhook.go | 6 +- s3log/audit-logger.go | 4 +- s3log/file.go | 6 +- s3log/file_admin.go | 6 +- s3log/webhook.go | 6 +- tests/integration/server.go | 19 +-- tests/integration/utils.go | 2 + website/handler.go | 66 ++++----- website/handler_test.go | 8 +- website/server.go | 26 ++-- webui/embed.go | 7 +- webui/webserver.go | 69 ++++----- 83 files changed, 855 insertions(+), 644 deletions(-) create mode 100644 s3err/request-header-section-too-large-error.go diff --git a/auth/access-control_test.go b/auth/access-control_test.go index 04aa1e6a..14de895b 100644 --- a/auth/access-control_test.go +++ b/auth/access-control_test.go @@ -222,7 +222,6 @@ func TestVerifyObjectCopyAccess_URLEncodedSlashSeparator(t *testing.T) { opts := AccessOptions{ Acl: ACL{Owner: testUser}, AclPermission: PermissionWrite, - IsRoot: false, Acc: Account{Access: testUser, Role: RoleUser}, Bucket: "dst-bucket", Object: "dst-key", @@ -276,7 +275,6 @@ func TestVerifyObjectCopyAccess_LiteralSlashSeparator(t *testing.T) { opts := AccessOptions{ Acl: ACL{Owner: testUser}, AclPermission: PermissionWrite, - IsRoot: false, Acc: Account{Access: testUser, Role: RoleUser}, Bucket: "dst-bucket", Object: "dst-key", diff --git a/debuglogger/logger.go b/debuglogger/logger.go index 364cb2b0..8e06d2b8 100644 --- a/debuglogger/logger.go +++ b/debuglogger/logger.go @@ -22,7 +22,7 @@ import ( "strings" "sync/atomic" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) type Color string @@ -40,9 +40,12 @@ const ( prefixInfo prefix = "[INFO]: " prefixDebug prefix = "[DEBUG]: " - reset = "\033[0m" - borderChar = "─" - boxWidth = 120 + reset = "\033[0m" + borderChar = "─" + boxWidth = 120 + boxContentWidth = boxWidth - 4 // visible width between "│ " and " │" + minKeyColumnWidth = 13 + keyValueSeparator = " : " ) // Panic prints the panics out in the console @@ -60,9 +63,9 @@ func printError(prefix prefix, er error) { } // Logs http request details: headers, body, params, query args -func LogFiberRequestDetails(ctx *fiber.Ctx) { +func LogFiberRequestDetails(ctx fiber.Ctx) { // Log the full request url - fullURL := ctx.Protocol() + "://" + ctx.Hostname() + ctx.OriginalURL() + fullURL := ctx.Scheme() + "://" + ctx.Host() + ctx.OriginalURL() fmt.Printf("%s[URL]: %s%s\n", green, fullURL, reset) // log request headers @@ -90,7 +93,7 @@ func LogFiberRequestDetails(ctx *fiber.Ctx) { } // Logs http response details: body, headers -func LogFiberResponseDetails(ctx *fiber.Ctx) { +func LogFiberResponseDetails(ctx fiber.Ctx) { wrapInBox(green, "RESPONSE HEADERS", boxWidth, func() { for key, value := range ctx.Response().Header.All() { printWrappedLine(yellow, string(key), string(value)) @@ -213,11 +216,10 @@ func wrapInBox(color Color, title string, length int, fn func()) { printHorizontalBorder(color, length, true) } -// returns the provided string length -// defaulting to 13 for exceeding lengths -func getLen(str string) int { - if len(str) < 13 { - return 13 +// paddedKeyLen returns the visible key column width used by printWrappedLine. +func paddedKeyLen(str string) int { + if len(str) < minKeyColumnWidth { + return minKeyColumnWidth } return len(str) @@ -226,32 +228,112 @@ func getLen(str string) int { // prints a formatted key-value pair within a box layout, // wrapping the value text if it exceeds the allowed width. func printWrappedLine(keyColor Color, key, value string) { - prefix := fmt.Sprintf("%s│%s %s%-13s%s : ", green, reset, keyColor, key, reset) - prefixLen := len(prefix) - len(green) - len(reset) - len(keyColor) - len(reset) - // the actual prefix size without colors - actualPrefixLen := getLen(key) + 5 + keyLen := paddedKeyLen(key) + valueIndent := keyLen + len(keyValueSeparator) + lineWidth := boxContentWidth - valueIndent + if lineWidth < 1 { + printWrappedLongKeyLine(keyColor, key, value) + return + } - lineWidth := boxWidth - prefixLen + prefix := fmt.Sprintf("%s│%s %s%-*s%s%s", green, reset, keyColor, minKeyColumnWidth, key, reset, keyValueSeparator) valueLines := wrapText(value, lineWidth) + if len(valueLines) == 0 { + valueLines = []string{""} + } for i, line := range valueLines { if i == 0 { - if len(line) < lineWidth { - line += strings.Repeat(" ", lineWidth-len(line)) - } + line += rightPadding(line, lineWidth) fmt.Printf("%s%s%s %s│%s\n", prefix, reset, line, green, reset) } else { - line = strings.Repeat(" ", actualPrefixLen-2) + line - if len(line) < boxWidth-4 { - line += strings.Repeat(" ", boxWidth-len(line)-4) - } + line = spaces(valueIndent) + line + line += rightPadding(line, boxContentWidth) fmt.Printf("%s│ %s%s %s│%s\n", green, reset, line, green, reset) } } } -// wrapText splits the input text into lines of at most `width` characters each. +// printWrappedLongKeyLine handles headers whose key is too wide to leave +// room for a value in the fixed key/value layout. It wraps the key and value +// independently so keyColor never leaks into the separator or value text. +func printWrappedLongKeyLine(keyColor Color, key, value string) { + keyLine, remainingKey := splitText(key, boxContentWidth) + for remainingKey != "" { + printColoredBoxLine(keyColor, keyLine, boxContentWidth) + keyLine, remainingKey = splitText(remainingKey, boxContentWidth) + } + + valueLineWidth := boxContentWidth - len(keyLine) - len(keyValueSeparator) + if valueLineWidth < 1 { + printColoredBoxLine(keyColor, keyLine, boxContentWidth) + printWrappedPlainText(keyValueSeparator+value, boxContentWidth) + return + } + + valueLine, remainingValue := splitText(value, valueLineWidth) + printKeyValueBoxLine(keyColor, keyLine, keyValueSeparator, valueLine, boxContentWidth) + printWrappedPlainText(remainingValue, boxContentWidth) +} + +func printColoredBoxLine(color Color, text string, width int) { + fmt.Printf("%s│%s %s%s%s%s %s│%s\n", green, reset, color, text, reset, rightPadding(text, width), green, reset) +} + +func printKeyValueBoxLine(keyColor Color, key, separator, value string, width int) { + lineLen := len(key) + len(separator) + len(value) + fmt.Printf("%s│%s %s%s%s%s%s%s %s│%s\n", green, reset, keyColor, key, reset, separator, value, spaces(width-lineLen), green, reset) +} + +func printPlainBoxLine(text string, width int) { + fmt.Printf("%s│%s %s%s %s│%s\n", green, reset, text, rightPadding(text, width), green, reset) +} + +func printWrappedPlainText(text string, width int) { + for text != "" { + var line string + line, text = splitText(text, width) + printPlainBoxLine(line, width) + } +} + +func rightPadding(text string, width int) string { + return spaces(width - len(text)) +} + +func spaces(count int) string { + if count < 1 { + return "" + } + + return strings.Repeat(" ", count) +} + +// splitText returns the first width bytes from text and the remaining suffix. +// The debug logger already measures line width with len, so this keeps the +// wrapping behavior consistent with the rest of this file. +func splitText(text string, width int) (string, string) { + if width < 1 { + return "", text + } + if len(text) <= width { + return text, "" + } + + return text[:width], text[width:] +} + +// wrapText splits the input text into lines of at most width bytes each. +// When width is not positive, it returns the original text as one line so +// callers never slice with an invalid bound while handling malformed layouts. func wrapText(text string, width int) []string { + if width < 1 { + if text == "" { + return nil + } + return []string{text} + } + var lines []string for len(text) > width { lines = append(lines, text[:width]) @@ -265,7 +347,7 @@ func wrapText(text string, width int) []string { // TODO: remove this and use utils.IsBidDataAction after refactoring // and creating 'internal' package -func isLargeDataAction(ctx *fiber.Ctx) bool { +func isLargeDataAction(ctx fiber.Ctx) bool { pathParts := strings.Split(ctx.Path(), "/") // PutObject and UploadPart diff --git a/embedgw/embedgw.go b/embedgw/embedgw.go index ae01cbeb..c456833d 100644 --- a/embedgw/embedgw.go +++ b/embedgw/embedgw.go @@ -899,11 +899,14 @@ func RunVersityGW(ctx context.Context, be backend.Backend, cfg *Config) error { webOpts = append(webOpts, webui.WithSocketPerm(parsedSocketPerm)) } - webSrv = webui.NewServer(&webui.ServerConfig{ + webSrv, err = webui.NewServer(&webui.ServerConfig{ Gateways: gateways, AdminGateways: adminGateways, Region: cfg.Region, }, webOpts...) + if err != nil { + return fmt.Errorf("init webui: %w", err) + } } var wsSrv *website.Server diff --git a/go.mod b/go.mod index 7cb431f9..27dfb2e3 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 github.com/davecgh/go-spew v1.1.1 github.com/go-ldap/ldap/v3 v3.4.13 - github.com/gofiber/fiber/v2 v2.52.13 + github.com/gofiber/fiber/v3 v3.3.0 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/hashicorp/vault-client-go v0.4.3 @@ -56,9 +56,10 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 // indirect - github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect + github.com/gofiber/schema v1.7.1 // indirect + github.com/gofiber/utils/v2 v2.0.6 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect @@ -69,15 +70,16 @@ require ( github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-isatty v0.0.22 // indirect - github.com/mattn/go-runewidth v0.0.24 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/nats-io/nkeys v0.4.16 // indirect github.com/nats-io/nuid v1.0.1 // indirect + github.com/philhofer/fwd v1.2.0 // indirect github.com/pierrec/lz4/v4 v4.1.27 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/tinylib/msgp v1.6.4 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect golang.org/x/crypto v0.53.0 // indirect diff --git a/go.sum b/go.sum index b0bce6c7..596ef862 100644 --- a/go.sum +++ b/go.sum @@ -65,8 +65,6 @@ github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8= github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= -github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -74,12 +72,18 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78= +github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ= github.com/go-ldap/ldap/v3 v3.4.13/go.mod h1:LxsGZV6vbaK0sIvYfsv47rfh4ca0JXokCoKjZxsszv0= -github.com/gofiber/fiber/v2 v2.52.13 h1:TOKP64iqC9b5P49VrBW5tHhUOvDyrtJ0xePEfzJbCbk= -github.com/gofiber/fiber/v2 v2.52.13/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/gofiber/fiber/v3 v3.3.0 h1:QBd3sYCqdy6Qs5gJYzSw4I4SbqL204jPqpdub/ueiw8= +github.com/gofiber/fiber/v3 v3.3.0/go.mod h1:YH7/TAoRaU4kF8slDCtQuFJ1NzC+3MtxUI4KfvQtaIA= +github.com/gofiber/schema v1.7.1 h1:oSJBKdgP8JeIME4TQSAqlNKTU2iBB+2RNmKi8Nsc+TI= +github.com/gofiber/schema v1.7.1/go.mod h1:A/X5Ffyru4p9eBdp99qu+nzviHzQiZ7odLT+TwxWhbk= +github.com/gofiber/utils/v2 v2.0.6 h1:7fXYy7nSsyqbH0GQUMtK4Kwjy4J7R5742VM7JsZxzOs= +github.com/gofiber/utils/v2 v2.0.6/go.mod h1:p7mAHAk3+oUK10ZX2xTw9fZQixb4hCg8SKd4IH2xroU= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= @@ -129,8 +133,6 @@ github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= -github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= -github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -144,6 +146,8 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= +github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= +github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk= github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= @@ -163,6 +167,8 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/segmentio/kafka-go v0.4.51 h1:JgDPPG75tC1rWIS2Me6MwcvXJ6f49UQ4HjAOef71Hno= github.com/segmentio/kafka-go v0.4.51/go.mod h1:Y1gn60kzLEEaW28YshXyk2+VCUKbJ3Qr6DrnT3i4+9E= +github.com/shamaton/msgpack/v3 v3.1.2 h1:d5gWAIyMU4M0WgDjz6IFSCuXJUA2dFwRHBpDclE8CLw= +github.com/shamaton/msgpack/v3 v3.1.2/go.mod h1:DcQG8jrdrQCIxr3HlMYkiXdMhK+KfN2CitkyzsQV4uc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smira/go-statsd v1.3.4 h1:kBYWcLSGT+qC6JVbvfz48kX7mQys32fjDOPrfmsSx2c= github.com/smira/go-statsd v1.3.4/go.mod h1:RjdsESPgDODtg1VpVVf9MJrEW2Hw0wtRNbmB1CAhu6A= @@ -177,6 +183,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ= +github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -185,6 +193,8 @@ github.com/valyala/fasthttp v1.71.0 h1:tepR7H+Guh9VUqxxcPggYi8R3lGUu2Rsdh+z7/FCY github.com/valyala/fasthttp v1.71.0/go.mod h1:z1sDUvOShhXq/C9mwH/fSm1Vb71tUJwmQdgkBrBNwnA= github.com/versity/scoutfs-go v0.0.0-20260414000839-70f8b6bdfa96 h1:tUE4nwTlAfgc8neNzODemrv3LLe1ZpLG99desvXxYW8= github.com/versity/scoutfs-go v0.0.0-20260414000839-70f8b6bdfa96/go.mod h1:gJsq73k+4685y+rbDIpPY8i/5GbsiwP6JFoFyUDB1fQ= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= diff --git a/metrics/metrics.go b/metrics/metrics.go index f191c70d..d96f2ded 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -23,7 +23,7 @@ import ( "strings" "sync" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/s3err" ) @@ -43,7 +43,7 @@ type Tag struct { // Manager is the interface definition for metrics manager type Manager interface { - Send(ctx *fiber.Ctx, err error, action string, count int64, status int) + Send(ctx fiber.Ctx, err error, action string, count int64, status int) Close() } @@ -118,7 +118,7 @@ func NewManager(ctx context.Context, conf Config) (Manager, error) { return mgr, nil } -func (m *manager) Send(ctx *fiber.Ctx, err error, action string, count int64, status int) { +func (m *manager) Send(ctx fiber.Ctx, err error, action string, count int64, status int) { // In case of Authentication failures, url parsing ... if action == "" { action = ActionUndetected diff --git a/s3api/admin-router.go b/s3api/admin-router.go index fd5628b7..a4d4b240 100644 --- a/s3api/admin-router.go +++ b/s3api/admin-router.go @@ -15,7 +15,7 @@ package s3api import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/metrics" diff --git a/s3api/admin-server.go b/s3api/admin-server.go index 547cef55..09460dfd 100644 --- a/s3api/admin-server.go +++ b/s3api/admin-server.go @@ -19,9 +19,9 @@ import ( "net" "os" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" - "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/logger" + "github.com/gofiber/fiber/v3/middleware/recover" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/debuglogger" @@ -57,17 +57,15 @@ func NewAdminServer(be backend.Backend, root middlewares.RootUserConfig, region } app := fiber.New(fiber.Config{ - AppName: "versitygw", - ServerHeader: "VERSITYGW", - Network: fiber.NetworkTCP, - DisableStartupMessage: true, - ErrorHandler: globalErrorHandler, - Concurrency: server.maxConnections, + AppName: "versitygw", + ServerHeader: "VERSITYGW", + ErrorHandler: globalErrorHandler, + Concurrency: server.maxConnections, }) server.app = app - app.Use(recover.New( + app.Use("*", recover.New( recover.Config{ EnableStackTrace: true, StackTraceHandler: stackTraceHandler, @@ -75,21 +73,21 @@ func NewAdminServer(be backend.Backend, root middlewares.RootUserConfig, region // Logging middlewares if !server.quiet { - app.Use(logger.New(logger.Config{ + app.Use("*", logger.New(logger.Config{ Format: "${time} | adm | ${status} | ${latency} | ${ip} | ${method} | ${path} | ${error} | ${queryParams}\n", })) } // initialize requestId middleware - app.Use(middlewares.RequestIDs()) + app.Use("*", middlewares.RequestIDs()) // initialize total requests cap limiter middleware - app.Use(middlewares.RateLimiter(server.maxRequests, nil, l)) + app.Use("*", middlewares.RateLimiter(server.maxRequests, nil, l)) - app.Use(controllers.WrapMiddleware(middlewares.DecodeURL, l, nil)) + app.Use("*", controllers.WrapMiddleware(middlewares.DecodeURL, l, nil)) // initialize the debug logger in debug mode if debuglogger.IsDebugEnabled() { - app.Use(middlewares.DebugLogger()) + app.Use("*", middlewares.DebugLogger()) } server.router.Init(app, be, iam, l, root, region, server.debug, server.corsAllowOrigin) @@ -151,9 +149,9 @@ func (sa *S3AdminServer) ServeMultiPort(ports []string) error { var err error if sa.CertStorage != nil { - ln, err = utils.NewMultiAddrTLSListener(sa.app.Config().Network, portSpec, sa.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: sa.socketPerm}) + ln, err = utils.NewMultiAddrTLSListener(fiber.NetworkTCP, portSpec, sa.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: sa.socketPerm}) } else { - ln, err = utils.NewMultiAddrListener(sa.app.Config().Network, portSpec, utils.ListenerOptions{SocketPerm: sa.socketPerm}) + ln, err = utils.NewMultiAddrListener(fiber.NetworkTCP, portSpec, utils.ListenerOptions{SocketPerm: sa.socketPerm}) } if err != nil { @@ -170,7 +168,9 @@ func (sa *S3AdminServer) ServeMultiPort(ports []string) error { // Combine all listeners finalListener := utils.NewMultiListener(listeners...) - return sa.app.Listener(finalListener) + return sa.app.Listener(finalListener, fiber.ListenConfig{ + DisableStartupMessage: true, + }) } // ShutDown gracefully shuts down the server with a context timeout diff --git a/s3api/controllers/admin.go b/s3api/controllers/admin.go index 4f86b7d6..089a4719 100644 --- a/s3api/controllers/admin.go +++ b/s3api/controllers/admin.go @@ -19,7 +19,7 @@ import ( "net/http" "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/s3err" @@ -38,9 +38,9 @@ func NewAdminController(iam auth.IAMService, be backend.Backend, l s3log.AuditLo return AdminController{iam: iam, be: be, l: l, s3api: s3api} } -func (c AdminController) CreateUser(ctx *fiber.Ctx) (*Response, error) { +func (c AdminController) CreateUser(ctx fiber.Ctx) (*Response, error) { var usr auth.Account - err := xml.Unmarshal(ctx.Body(), &usr) + err := xml.Unmarshal(ctx.BodyRaw(), &usr) if err != nil { return &Response{ MetaOpts: &MetaOptions{}, @@ -71,7 +71,7 @@ func (c AdminController) CreateUser(ctx *fiber.Ctx) (*Response, error) { }, nil } -func (c AdminController) UpdateUser(ctx *fiber.Ctx) (*Response, error) { +func (c AdminController) UpdateUser(ctx fiber.Ctx) (*Response, error) { access := ctx.Query("access") if access == "" { return &Response{ @@ -80,7 +80,7 @@ func (c AdminController) UpdateUser(ctx *fiber.Ctx) (*Response, error) { } var props auth.MutableProps - if err := xml.Unmarshal(ctx.Body(), &props); err != nil { + if err := xml.Unmarshal(ctx.BodyRaw(), &props); err != nil { return &Response{ MetaOpts: &MetaOptions{}, }, s3err.GetAPIError(s3err.ErrMalformedXML) @@ -109,7 +109,7 @@ func (c AdminController) UpdateUser(ctx *fiber.Ctx) (*Response, error) { }, nil } -func (c AdminController) DeleteUser(ctx *fiber.Ctx) (*Response, error) { +func (c AdminController) DeleteUser(ctx fiber.Ctx) (*Response, error) { access := ctx.Query("access") if access == "" { return &Response{ @@ -123,7 +123,7 @@ func (c AdminController) DeleteUser(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c AdminController) ListUsers(ctx *fiber.Ctx) (*Response, error) { +func (c AdminController) ListUsers(ctx fiber.Ctx) (*Response, error) { accs, err := c.iam.ListUserAccounts() return &Response{ Data: auth.ListUserAccountsResult{Accounts: accs}, @@ -131,7 +131,7 @@ func (c AdminController) ListUsers(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c AdminController) ChangeBucketOwner(ctx *fiber.Ctx) (*Response, error) { +func (c AdminController) ChangeBucketOwner(ctx fiber.Ctx) (*Response, error) { owner := ctx.Query("owner") bucket := ctx.Query("bucket") @@ -147,14 +147,14 @@ func (c AdminController) ChangeBucketOwner(ctx *fiber.Ctx) (*Response, error) { }, s3err.GetAPIError(s3err.ErrAdminUserNotFound) } - err = c.be.ChangeBucketOwner(ctx.Context(), bucket, owner) + err = c.be.ChangeBucketOwner(ctx.RequestCtx(), bucket, owner) return &Response{ MetaOpts: &MetaOptions{}, }, err } -func (c AdminController) ListBuckets(ctx *fiber.Ctx) (*Response, error) { - buckets, err := c.be.ListBucketsAndOwners(ctx.Context()) +func (c AdminController) ListBuckets(ctx fiber.Ctx) (*Response, error) { + buckets, err := c.be.ListBucketsAndOwners(ctx.RequestCtx()) return &Response{ Data: s3response.ListBucketsResult{ Buckets: buckets, @@ -163,7 +163,7 @@ func (c AdminController) ListBuckets(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c AdminController) CreateBucket(ctx *fiber.Ctx) (*Response, error) { +func (c AdminController) CreateBucket(ctx fiber.Ctx) (*Response, error) { owner := ctx.Get("x-vgw-owner") if owner == "" { return &Response{ @@ -183,7 +183,7 @@ func (c AdminController) CreateBucket(ctx *fiber.Ctx) (*Response, error) { } // store the owner access key id in context - ctx.Context().SetUserValue("bucket-owner", acc) + ctx.RequestCtx().SetUserValue("bucket-owner", acc) _, err = c.s3api.CreateBucket(ctx) if err != nil { diff --git a/s3api/controllers/base.go b/s3api/controllers/base.go index bbad90e4..191cc4e2 100644 --- a/s3api/controllers/base.go +++ b/s3api/controllers/base.go @@ -21,7 +21,7 @@ import ( "sort" "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/debuglogger" @@ -76,7 +76,7 @@ func New(be backend.Backend, iam auth.IAMService, logger s3log.AuditLogger, evs } } -func (c S3ApiController) getAclHeaderValue(ctx *fiber.Ctx, key string, defaultValues ...string) string { +func (c S3ApiController) getAclHeaderValue(ctx fiber.Ctx, key string, defaultValues ...string) string { if c.disableACL { return "" } @@ -86,7 +86,7 @@ func (c S3ApiController) getAclHeaderValue(ctx *fiber.Ctx, key string, defaultVa // Returns MethodNotAllowed for unmatched routes func (c S3ApiController) HandleErrorRoute(err error) Controller { - return func(ctx *fiber.Ctx) (*Response, error) { + return func(ctx fiber.Ctx) (*Response, error) { return &Response{}, err } } @@ -121,11 +121,11 @@ type Services struct { } // Controller is the type definition for an s3api controller -type Controller func(ctx *fiber.Ctx) (*Response, error) +type Controller func(ctx fiber.Ctx) (*Response, error) // ProcessHandlers groups a controller and multiple middlewares into a single fiber handler func ProcessHandlers(controller Controller, s3action string, svc *Services, handlers ...fiber.Handler) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { // if skip locals is set, skip to the next rout handler if utils.ContextKeySkip.IsSet(ctx) { utils.ContextKeySkip.Delete(ctx) @@ -135,7 +135,7 @@ func ProcessHandlers(controller Controller, s3action string, svc *Services, hand for _, handler := range handlers { err := handler(ctx) if err != nil { - return ProcessController(ctx, func(ctx *fiber.Ctx) (*Response, error) { + return ProcessController(ctx, func(ctx fiber.Ctx) (*Response, error) { return &Response{ MetaOpts: &MetaOptions{}, }, err @@ -150,7 +150,7 @@ func ProcessHandlers(controller Controller, s3action string, svc *Services, hand // WrapMiddleware executes the given middleware and handles sending the audit logs // and metrics. It also handles the error parsing func WrapMiddleware(handler fiber.Handler, logger s3log.AuditLogger, mm metrics.Manager) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { requestID, hostID := utils.EnsureRequestIDs(ctx) err := handler(ctx) @@ -159,7 +159,7 @@ func WrapMiddleware(handler fiber.Handler, logger s3log.AuditLogger, mm metrics. mm.Send(ctx, err, metrics.ActionUndetected, 0, 0) } if logger != nil { - logger.Log(ctx, err, ctx.Body(), s3log.LogMeta{ + logger.Log(ctx, err, ctx.BodyRaw(), s3log.LogMeta{ Action: metrics.ActionUndetected, }) } @@ -187,7 +187,7 @@ func WrapMiddleware(handler fiber.Handler, logger s3log.AuditLogger, mm metrics. // ProcessController executes the given s3api controller and handles the metrics // access logs and s3 events -func ProcessController(ctx *fiber.Ctx, controller Controller, s3action string, svc *Services) error { +func ProcessController(ctx fiber.Ctx, controller Controller, s3action string, svc *Services) error { response, err := controller(ctx) // Set the response headers @@ -342,7 +342,7 @@ func ProcessController(ctx *fiber.Ctx, controller Controller, s3action string, s return ctx.Status(opts.Status).Send(res) } -func ensureExposeMetaHeaders(ctx *fiber.Ctx) { +func ensureExposeMetaHeaders(ctx fiber.Ctx) { // Only attempt to modify expose headers when CORS is actually in use. if len(ctx.Response().Header.Peek("Access-Control-Allow-Origin")) == 0 { return @@ -414,7 +414,7 @@ func ensureExposeMetaHeaders(ctx *fiber.Ctx) { } // Sets the response headers -func SetResponseHeaders(ctx *fiber.Ctx, headers map[string]*string) { +func SetResponseHeaders(ctx fiber.Ctx, headers map[string]*string) { if headers == nil { return } diff --git a/s3api/controllers/base_test.go b/s3api/controllers/base_test.go index d5335d10..00644a8f 100644 --- a/s3api/controllers/base_test.go +++ b/s3api/controllers/base_test.go @@ -26,7 +26,7 @@ import ( "strings" "testing" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/stretchr/testify/assert" "github.com/valyala/fasthttp" "github.com/versity/versitygw/auth" @@ -95,7 +95,7 @@ type ctxInputs struct { func testController(t *testing.T, ctrl Controller, resp *Response, expectedErr error, input ctxInputs) { app := fiber.New() - app.Post("/:bucket/*", func(ctx *fiber.Ctx) error { + app.Post("/:bucket/*", func(ctx fiber.Ctx) error { // set the request body ctx.Request().SetBody(input.body) // set the request locals @@ -259,23 +259,23 @@ func TestEnsureExposeMetaHeaders_AddsActualMetaHeaderNames(t *testing.T) { type mockAuditLogger struct { } -func (m *mockAuditLogger) Log(_ *fiber.Ctx, _ error, _ []byte, _ s3log.LogMeta) {} -func (m *mockAuditLogger) HangUp() error { return nil } -func (m *mockAuditLogger) Shutdown() error { return nil } +func (m *mockAuditLogger) Log(_ fiber.Ctx, _ error, _ []byte, _ s3log.LogMeta) {} +func (m *mockAuditLogger) HangUp() error { return nil } +func (m *mockAuditLogger) Shutdown() error { return nil } // mock S3 event sender type mockEvSender struct { } -func (m *mockEvSender) SendEvent(_ *fiber.Ctx, _ s3event.EventMeta) {} -func (m *mockEvSender) Close() error { return nil } +func (m *mockEvSender) SendEvent(_ fiber.Ctx, _ s3event.EventMeta) {} +func (m *mockEvSender) Close() error { return nil } // mock metrics manager type mockMetricsManager struct{} -func (m *mockMetricsManager) Send(_ *fiber.Ctx, _ error, _ string, _ int64, _ int) {} -func (m *mockMetricsManager) Close() {} +func (m *mockMetricsManager) Send(_ fiber.Ctx, _ error, _ string, _ int64, _ int) {} +func (m *mockMetricsManager) Close() {} func TestProcessController(t *testing.T) { payload, err := xml.Marshal(s3response.Bucket{ @@ -308,7 +308,7 @@ func TestProcessController(t *testing.T) { name: "no services successful response", args: args{ svc: &Services{}, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{}, nil }, }, @@ -320,7 +320,7 @@ func TestProcessController(t *testing.T) { name: "handle api error", args: args{ svc: services, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{}, s3err.GetAPIError(s3err.ErrInvalidRequest) }, }, @@ -333,7 +333,7 @@ func TestProcessController(t *testing.T) { name: "handle custom error", args: args{ svc: services, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{}, errors.New("custom error") }, }, @@ -346,7 +346,7 @@ func TestProcessController(t *testing.T) { name: "body parsing fails", args: args{ svc: services, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{ Data: make(chan int), }, nil @@ -361,7 +361,7 @@ func TestProcessController(t *testing.T) { name: "no data payload", args: args{ svc: services, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{ MetaOpts: &MetaOptions{ ObjectCount: 2, @@ -377,7 +377,7 @@ func TestProcessController(t *testing.T) { name: "should return 204 http status", args: args{ svc: services, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{ MetaOpts: &MetaOptions{ Status: http.StatusNoContent, @@ -393,7 +393,7 @@ func TestProcessController(t *testing.T) { name: "already encoded payload", args: args{ svc: services, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{ Data: []byte("encoded_data"), }, nil @@ -411,7 +411,7 @@ func TestProcessController(t *testing.T) { name: "should set response headers", args: args{ svc: services, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{ Headers: map[string]*string{ "X-Amz-My-Custom-Header": utils.GetStringPtr("my_value"), @@ -432,7 +432,7 @@ func TestProcessController(t *testing.T) { name: "large payload: should return internal error", args: args{ svc: services, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { type Item struct { Value string `xml:"value"` } @@ -475,7 +475,7 @@ func TestProcessController(t *testing.T) { name: "not encoded payload", args: args{ svc: services, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{ Data: s3response.Bucket{ Name: "something", @@ -553,10 +553,10 @@ func TestProcessHandlers(t *testing.T) { name: "handler returns error", args: args{ handlers: []fiber.Handler{ - func(ctx *fiber.Ctx) error { + func(ctx fiber.Ctx) error { return nil }, - func(ctx *fiber.Ctx) error { + func(ctx fiber.Ctx) error { return s3err.GetAPIError(s3err.ErrAccessDenied) }, }, @@ -570,15 +570,15 @@ func TestProcessHandlers(t *testing.T) { name: "should process the controller", args: args{ handlers: []fiber.Handler{ - func(ctx *fiber.Ctx) error { + func(ctx fiber.Ctx) error { return nil }, - func(ctx *fiber.Ctx) error { + func(ctx fiber.Ctx) error { return nil }, }, svc: &Services{}, - controller: func(ctx *fiber.Ctx) (*Response, error) { + controller: func(ctx fiber.Ctx) (*Response, error) { return &Response{ Data: s3response.Checksum{ CRC32: utils.GetStringPtr("crc32"), @@ -597,7 +597,7 @@ func TestProcessHandlers(t *testing.T) { app := fiber.New() - app.Post("/:bucket/*", func(ctx *fiber.Ctx) error { + app.Post("/:bucket/*", func(ctx fiber.Ctx) error { utils.ContextKeyRequestID.Set(ctx, testRequestID) utils.ContextKeyHostID.Set(ctx, testHostID) @@ -620,7 +620,7 @@ func TestProcessHandlers(t *testing.T) { return nil }) - app.All("*", func(ctx *fiber.Ctx) error { + app.All("*", func(ctx fiber.Ctx) error { return nil }) @@ -649,7 +649,7 @@ func TestWrapMiddleware(t *testing.T) { { name: "handler returns no error", args: args{ - handler: func(ctx *fiber.Ctx) error { + handler: func(ctx fiber.Ctx) error { return nil }, }, @@ -657,7 +657,7 @@ func TestWrapMiddleware(t *testing.T) { { name: "handler returns api error", args: args{ - handler: func(ctx *fiber.Ctx) error { + handler: func(ctx fiber.Ctx) error { return s3err.GetAPIError(s3err.ErrAclNotSupported) }, mm: &mockMetricsManager{}, @@ -670,7 +670,7 @@ func TestWrapMiddleware(t *testing.T) { { name: "handler returns custom error", args: args{ - handler: func(ctx *fiber.Ctx) error { + handler: func(ctx fiber.Ctx) error { return errors.New("custom error") }, }, @@ -684,7 +684,7 @@ func TestWrapMiddleware(t *testing.T) { mdlwr := WrapMiddleware(tt.args.handler, tt.args.logger, tt.args.mm) app := fiber.New() - app.Post("/:bucket/*", func(ctx *fiber.Ctx) error { + app.Post("/:bucket/*", func(ctx fiber.Ctx) error { utils.ContextKeyRequestID.Set(ctx, testRequestID) utils.ContextKeyHostID.Set(ctx, testHostID) @@ -700,7 +700,7 @@ func TestWrapMiddleware(t *testing.T) { return nil }) - app.All("*", func(ctx *fiber.Ctx) error { + app.All("*", func(ctx fiber.Ctx) error { return nil }) diff --git a/s3api/controllers/bucket-delete.go b/s3api/controllers/bucket-delete.go index 2ff9e7d1..b847e919 100644 --- a/s3api/controllers/bucket-delete.go +++ b/s3api/controllers/bucket-delete.go @@ -17,19 +17,19 @@ package controllers import ( "net/http" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" ) -func (c S3ApiController) DeleteBucketTagging(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) DeleteBucketTagging(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -49,7 +49,7 @@ func (c S3ApiController) DeleteBucketTagging(ctx *fiber.Ctx) (*Response, error) }, err } - err = c.be.DeleteBucketTagging(ctx.Context(), bucket) + err = c.be.DeleteBucketTagging(ctx.RequestCtx(), bucket) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -58,13 +58,13 @@ func (c S3ApiController) DeleteBucketTagging(ctx *fiber.Ctx) (*Response, error) }, err } -func (c S3ApiController) DeleteBucketOwnershipControls(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) DeleteBucketOwnershipControls(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -83,7 +83,7 @@ func (c S3ApiController) DeleteBucketOwnershipControls(ctx *fiber.Ctx) (*Respons }, err } - err = c.be.DeleteBucketOwnershipControls(ctx.Context(), bucket) + err = c.be.DeleteBucketOwnershipControls(ctx.RequestCtx(), bucket) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -92,13 +92,13 @@ func (c S3ApiController) DeleteBucketOwnershipControls(ctx *fiber.Ctx) (*Respons }, err } -func (c S3ApiController) DeleteBucketPolicy(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) DeleteBucketPolicy(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -117,7 +117,7 @@ func (c S3ApiController) DeleteBucketPolicy(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.DeleteBucketPolicy(ctx.Context(), bucket) + err = c.be.DeleteBucketPolicy(ctx.RequestCtx(), bucket) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -126,14 +126,14 @@ func (c S3ApiController) DeleteBucketPolicy(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) DeleteBucketCors(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) DeleteBucketCors(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -153,7 +153,7 @@ func (c S3ApiController) DeleteBucketCors(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.DeleteBucketCors(ctx.Context(), bucket) + err = c.be.DeleteBucketCors(ctx.RequestCtx(), bucket) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -162,14 +162,14 @@ func (c S3ApiController) DeleteBucketCors(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) DeleteBucketWebsite(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) DeleteBucketWebsite(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -189,7 +189,7 @@ func (c S3ApiController) DeleteBucketWebsite(ctx *fiber.Ctx) (*Response, error) }, err } - err = c.be.DeleteBucketWebsite(ctx.Context(), bucket) + err = c.be.DeleteBucketWebsite(ctx.RequestCtx(), bucket) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -198,14 +198,14 @@ func (c S3ApiController) DeleteBucketWebsite(ctx *fiber.Ctx) (*Response, error) }, err } -func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) DeleteBucket(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -225,7 +225,7 @@ func (c S3ApiController) DeleteBucket(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.DeleteBucket(ctx.Context(), bucket) + err = c.be.DeleteBucket(ctx.RequestCtx(), bucket) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, diff --git a/s3api/controllers/bucket-get.go b/s3api/controllers/bucket-get.go index b86fd9b3..14962e8c 100644 --- a/s3api/controllers/bucket-get.go +++ b/s3api/controllers/bucket-get.go @@ -19,20 +19,20 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3response" ) -func (c S3ApiController) GetBucketTagging(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetBucketTagging(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -51,7 +51,7 @@ func (c S3ApiController) GetBucketTagging(ctx *fiber.Ctx) (*Response, error) { }, err } - tags, err := c.be.GetBucketTagging(ctx.Context(), bucket) + tags, err := c.be.GetBucketTagging(ctx.RequestCtx(), bucket) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -78,14 +78,14 @@ func (c S3ApiController) GetBucketTagging(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) GetBucketOwnershipControls(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetBucketOwnershipControls(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -104,7 +104,7 @@ func (c S3ApiController) GetBucketOwnershipControls(ctx *fiber.Ctx) (*Response, }, err } - data, err := c.be.GetBucketOwnershipControls(ctx.Context(), bucket) + data, err := c.be.GetBucketOwnershipControls(ctx.RequestCtx(), bucket) return &Response{ Data: s3response.OwnershipControls{ Rules: []types.OwnershipControlsRule{ @@ -119,14 +119,14 @@ func (c S3ApiController) GetBucketOwnershipControls(ctx *fiber.Ctx) (*Response, }, err } -func (c S3ApiController) GetBucketVersioning(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetBucketVersioning(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -153,7 +153,7 @@ func (c S3ApiController) GetBucketVersioning(ctx *fiber.Ctx) (*Response, error) }, err } - data, err := c.be.GetBucketVersioning(ctx.Context(), bucket) + data, err := c.be.GetBucketVersioning(ctx.RequestCtx(), bucket) return &Response{ Data: data, MetaOpts: &MetaOptions{ @@ -162,14 +162,14 @@ func (c S3ApiController) GetBucketVersioning(ctx *fiber.Ctx) (*Response, error) }, err } -func (c S3ApiController) GetBucketCors(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetBucketCors(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -188,7 +188,7 @@ func (c S3ApiController) GetBucketCors(ctx *fiber.Ctx) (*Response, error) { }, err } - data, err := c.be.GetBucketCors(ctx.Context(), bucket) + data, err := c.be.GetBucketCors(ctx.RequestCtx(), bucket) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -206,14 +206,14 @@ func (c S3ApiController) GetBucketCors(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) GetBucketWebsite(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetBucketWebsite(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -232,7 +232,7 @@ func (c S3ApiController) GetBucketWebsite(ctx *fiber.Ctx) (*Response, error) { }, err } - data, err := c.be.GetBucketWebsite(ctx.Context(), bucket) + data, err := c.be.GetBucketWebsite(ctx.RequestCtx(), bucket) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -250,14 +250,14 @@ func (c S3ApiController) GetBucketWebsite(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) GetBucketPolicy(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetBucketPolicy(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -276,7 +276,7 @@ func (c S3ApiController) GetBucketPolicy(ctx *fiber.Ctx) (*Response, error) { }, err } - data, err := c.be.GetBucketPolicy(ctx.Context(), bucket) + data, err := c.be.GetBucketPolicy(ctx.RequestCtx(), bucket) return &Response{ Data: data, MetaOpts: &MetaOptions{ @@ -285,14 +285,14 @@ func (c S3ApiController) GetBucketPolicy(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) GetBucketPolicyStatus(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetBucketPolicyStatus(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -311,7 +311,7 @@ func (c S3ApiController) GetBucketPolicyStatus(ctx *fiber.Ctx) (*Response, error }, err } - policyRaw, err := c.be.GetBucketPolicy(ctx.Context(), bucket) + policyRaw, err := c.be.GetBucketPolicy(ctx.RequestCtx(), bucket) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -340,7 +340,7 @@ func (c S3ApiController) GetBucketPolicyStatus(ctx *fiber.Ctx) (*Response, error }, nil } -func (c S3ApiController) ListObjectVersions(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) ListObjectVersions(ctx fiber.Ctx) (*Response, error) { // url values bucket := ctx.Params("bucket") prefix := ctx.Query("prefix") @@ -354,7 +354,7 @@ func (c S3ApiController) ListObjectVersions(ctx *fiber.Ctx) (*Response, error) { isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -382,7 +382,7 @@ func (c S3ApiController) ListObjectVersions(ctx *fiber.Ctx) (*Response, error) { }, err } - data, err := c.be.ListObjectVersions(ctx.Context(), + data, err := c.be.ListObjectVersions(ctx.RequestCtx(), &s3.ListObjectVersionsInput{ Bucket: &bucket, Delimiter: &delimiter, @@ -399,7 +399,7 @@ func (c S3ApiController) ListObjectVersions(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) GetObjectLockConfiguration(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetObjectLockConfiguration(ctx fiber.Ctx) (*Response, error) { // url values bucket := ctx.Params("bucket") // context keys @@ -408,7 +408,7 @@ func (c S3ApiController) GetObjectLockConfiguration(ctx *fiber.Ctx) (*Response, isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -427,7 +427,7 @@ func (c S3ApiController) GetObjectLockConfiguration(ctx *fiber.Ctx) (*Response, }, err } - data, err := c.be.GetObjectLockConfiguration(ctx.Context(), bucket) + data, err := c.be.GetObjectLockConfiguration(ctx.RequestCtx(), bucket) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -445,7 +445,7 @@ func (c S3ApiController) GetObjectLockConfiguration(ctx *fiber.Ctx) (*Response, }, err } -func (c S3ApiController) GetBucketAcl(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetBucketAcl(ctx fiber.Ctx) (*Response, error) { // url values bucket := ctx.Params("bucket") // context keys @@ -454,7 +454,7 @@ func (c S3ApiController) GetBucketAcl(ctx *fiber.Ctx) (*Response, error) { isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionReadAcp, @@ -473,7 +473,7 @@ func (c S3ApiController) GetBucketAcl(ctx *fiber.Ctx) (*Response, error) { }, err } - data, err := c.be.GetBucketAcl(ctx.Context(), + data, err := c.be.GetBucketAcl(ctx.RequestCtx(), &s3.GetBucketAclInput{Bucket: &bucket}) if err != nil { return &Response{ @@ -492,7 +492,7 @@ func (c S3ApiController) GetBucketAcl(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) ListMultipartUploads(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) ListMultipartUploads(ctx fiber.Ctx) (*Response, error) { // url values bucket := ctx.Params("bucket") prefix := ctx.Query("prefix") @@ -506,7 +506,7 @@ func (c S3ApiController) ListMultipartUploads(ctx *fiber.Ctx) (*Response, error) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -532,7 +532,7 @@ func (c S3ApiController) ListMultipartUploads(ctx *fiber.Ctx) (*Response, error) }, }, err } - res, err := c.be.ListMultipartUploads(ctx.Context(), + res, err := c.be.ListMultipartUploads(ctx.RequestCtx(), &s3.ListMultipartUploadsInput{ Bucket: &bucket, Delimiter: &delimiter, @@ -549,7 +549,7 @@ func (c S3ApiController) ListMultipartUploads(ctx *fiber.Ctx) (*Response, error) }, err } -func (c S3ApiController) ListObjectsV2(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) ListObjectsV2(ctx fiber.Ctx) (*Response, error) { // url values bucket := ctx.Params("bucket") prefix := ctx.Query("prefix") @@ -568,7 +568,7 @@ func (c S3ApiController) ListObjectsV2(ctx *fiber.Ctx) (*Response, error) { region = defaultRegion } - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -595,7 +595,7 @@ func (c S3ApiController) ListObjectsV2(ctx *fiber.Ctx) (*Response, error) { }, err } - res, err := c.be.ListObjectsV2(ctx.Context(), + res, err := c.be.ListObjectsV2(ctx.RequestCtx(), &s3.ListObjectsV2Input{ Bucket: &bucket, Prefix: &prefix, @@ -624,7 +624,7 @@ func (c S3ApiController) ListObjectsV2(ctx *fiber.Ctx) (*Response, error) { }, nil } -func (c S3ApiController) ListObjects(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) ListObjects(ctx fiber.Ctx) (*Response, error) { // url values bucket := ctx.Params("bucket") prefix := ctx.Query("prefix") @@ -641,7 +641,7 @@ func (c S3ApiController) ListObjects(ctx *fiber.Ctx) (*Response, error) { region = defaultRegion } - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -669,7 +669,7 @@ func (c S3ApiController) ListObjects(ctx *fiber.Ctx) (*Response, error) { }, err } - res, err := c.be.ListObjects(ctx.Context(), + res, err := c.be.ListObjects(ctx.RequestCtx(), &s3.ListObjectsInput{ Bucket: &bucket, Prefix: &prefix, @@ -697,14 +697,14 @@ func (c S3ApiController) ListObjects(ctx *fiber.Ctx) (*Response, error) { } // GetBucketLocation handles GET /:bucket?location -func (c S3ApiController) GetBucketLocation(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetBucketLocation(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -724,7 +724,7 @@ func (c S3ApiController) GetBucketLocation(ctx *fiber.Ctx) (*Response, error) { } // verify bucket existence/access via backend HeadBucket - _, err = c.be.HeadBucket(ctx.Context(), &s3.HeadBucketInput{Bucket: &bucket}) + _, err = c.be.HeadBucket(ctx.RequestCtx(), &s3.HeadBucketInput{Bucket: &bucket}) if err != nil { return &Response{ MetaOpts: &MetaOptions{ diff --git a/s3api/controllers/bucket-head.go b/s3api/controllers/bucket-head.go index b016cde3..f54214db 100644 --- a/s3api/controllers/bucket-head.go +++ b/s3api/controllers/bucket-head.go @@ -18,13 +18,13 @@ import ( "errors" "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" ) -func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) HeadBucket(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) @@ -32,7 +32,7 @@ func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) (*Response, error) { parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -55,7 +55,7 @@ func (c S3ApiController) HeadBucket(ctx *fiber.Ctx) (*Response, error) { }, err } - _, err = c.be.HeadBucket(ctx.Context(), + _, err = c.be.HeadBucket(ctx.RequestCtx(), &s3.HeadBucketInput{ Bucket: &bucket, }) diff --git a/s3api/controllers/bucket-list.go b/s3api/controllers/bucket-list.go index 0f7663be..edac2d63 100644 --- a/s3api/controllers/bucket-list.go +++ b/s3api/controllers/bucket-list.go @@ -15,13 +15,13 @@ package controllers import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3response" ) -func (c S3ApiController) ListBuckets(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) ListBuckets(ctx fiber.Ctx) (*Response, error) { cToken := ctx.Query("continuation-token") prefix := ctx.Query("prefix") maxBucketsStr := ctx.Query("max-buckets") @@ -38,7 +38,7 @@ func (c S3ApiController) ListBuckets(ctx *fiber.Ctx) (*Response, error) { }, err } - res, err := c.be.ListBuckets(ctx.Context(), + res, err := c.be.ListBuckets(ctx.RequestCtx(), s3response.ListBucketsInput{ Owner: acct.Access, IsAdmin: acct.Role == auth.RoleAdmin, diff --git a/s3api/controllers/bucket-post.go b/s3api/controllers/bucket-post.go index 1015d8dd..40dfec08 100644 --- a/s3api/controllers/bucket-post.go +++ b/s3api/controllers/bucket-post.go @@ -22,7 +22,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3api/middlewares" @@ -32,7 +32,7 @@ import ( "github.com/versity/versitygw/s3response" ) -func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) DeleteObjects(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") bypass := strings.EqualFold(ctx.Get("X-Amz-Bypass-Governance-Retention"), "true") acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) @@ -40,7 +40,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) (*Response, error) { parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -61,7 +61,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) (*Response, error) { } var dObj s3response.DeleteObjects - err = xml.Unmarshal(ctx.Body(), &dObj) + err = xml.Unmarshal(ctx.BodyRaw(), &dObj) if err != nil { debuglogger.Logf("error unmarshalling delete objects: %v", err) return &Response{ @@ -71,7 +71,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) (*Response, error) { }, s3err.GetAPIError(s3err.ErrInvalidRequest) } - err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, dObj.Objects, bypass, IsBucketPublic, c.be, false) + err = auth.CheckObjectAccess(ctx.RequestCtx(), bucket, acct.Access, dObj.Objects, bypass, IsBucketPublic, c.be, false) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -80,7 +80,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) (*Response, error) { }, err } - res, err := c.be.DeleteObjects(ctx.Context(), + res, err := c.be.DeleteObjects(ctx.RequestCtx(), &s3.DeleteObjectsInput{ Bucket: &bucket, Delete: &types.Delete{ @@ -97,7 +97,7 @@ func (c S3ApiController) DeleteObjects(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) POSTObject(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) POSTObject(ctx fiber.Ctx) (*Response, error) { acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) @@ -115,7 +115,7 @@ func (c S3ApiController) POSTObject(ctx *fiber.Ctx) (*Response, error) { key := parsed.Fields["key"] - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -206,7 +206,7 @@ func (c S3ApiController) POSTObject(ctx *fiber.Ctx) (*Response, error) { }, err } - res, err := c.be.PutObject(ctx.Context(), s3response.PutObjectInput{ + res, err := c.be.PutObject(ctx.RequestCtx(), s3response.PutObjectInput{ Bucket: &bucket, Key: &key, ContentType: &contentType, diff --git a/s3api/controllers/bucket-put.go b/s3api/controllers/bucket-put.go index 6d1521b7..f3edbefa 100644 --- a/s3api/controllers/bucket-put.go +++ b/s3api/controllers/bucket-put.go @@ -22,7 +22,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3api/utils" @@ -30,14 +30,14 @@ import ( "github.com/versity/versitygw/s3response" ) -func (c S3ApiController) PutBucketTagging(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutBucketTagging(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -56,7 +56,7 @@ func (c S3ApiController) PutBucketTagging(ctx *fiber.Ctx) (*Response, error) { }, err } - tagging, err := utils.ParseTagging(ctx.Body(), utils.TagLimitBucket) + tagging, err := utils.ParseTagging(ctx.BodyRaw(), utils.TagLimitBucket) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -65,7 +65,7 @@ func (c S3ApiController) PutBucketTagging(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.PutBucketTagging(ctx.Context(), bucket, tagging) + err = c.be.PutBucketTagging(ctx.RequestCtx(), bucket, tagging) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -74,13 +74,13 @@ func (c S3ApiController) PutBucketTagging(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) PutBucketOwnershipControls(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutBucketOwnershipControls(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) - if err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + if err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -98,7 +98,7 @@ func (c S3ApiController) PutBucketOwnershipControls(ctx *fiber.Ctx) (*Response, } var ownershipControls s3response.OwnershipControls - if err := xml.Unmarshal(ctx.Body(), &ownershipControls); err != nil { + if err := xml.Unmarshal(ctx.BodyRaw(), &ownershipControls); err != nil { debuglogger.Logf("failed to unmarshal request body: %v", err) return &Response{ MetaOpts: &MetaOptions{ @@ -125,7 +125,7 @@ func (c S3ApiController) PutBucketOwnershipControls(ctx *fiber.Ctx) (*Response, }, s3err.GetAPIError(s3err.ErrMalformedXML) } - err := c.be.PutBucketOwnershipControls(ctx.Context(), bucket, ownershipControls.Rules[0].ObjectOwnership) + err := c.be.PutBucketOwnershipControls(ctx.RequestCtx(), bucket, ownershipControls.Rules[0].ObjectOwnership) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -133,14 +133,14 @@ func (c S3ApiController) PutBucketOwnershipControls(ctx *fiber.Ctx) (*Response, }, err } -func (c S3ApiController) PutBucketVersioning(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutBucketVersioning(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -160,7 +160,7 @@ func (c S3ApiController) PutBucketVersioning(ctx *fiber.Ctx) (*Response, error) } var versioningConf types.VersioningConfiguration - err = xml.Unmarshal(ctx.Body(), &versioningConf) + err = xml.Unmarshal(ctx.BodyRaw(), &versioningConf) if err != nil { debuglogger.Logf("error unmarshalling versioning configuration: %v", err) return &Response{ @@ -180,7 +180,7 @@ func (c S3ApiController) PutBucketVersioning(ctx *fiber.Ctx) (*Response, error) }, s3err.GetAPIError(s3err.ErrMalformedXML) } - err = c.be.PutBucketVersioning(ctx.Context(), bucket, versioningConf.Status) + err = c.be.PutBucketVersioning(ctx.RequestCtx(), bucket, versioningConf.Status) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -188,14 +188,14 @@ func (c S3ApiController) PutBucketVersioning(ctx *fiber.Ctx) (*Response, error) }, err } -func (c S3ApiController) PutObjectLockConfiguration(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutObjectLockConfiguration(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - if err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + if err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -213,7 +213,7 @@ func (c S3ApiController) PutObjectLockConfiguration(ctx *fiber.Ctx) (*Response, }, err } - config, err := auth.ParseBucketLockConfigurationInput(ctx.Body()) + config, err := auth.ParseBucketLockConfigurationInput(ctx.BodyRaw()) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -222,7 +222,7 @@ func (c S3ApiController) PutObjectLockConfiguration(ctx *fiber.Ctx) (*Response, }, err } - err = c.be.PutObjectLockConfiguration(ctx.Context(), bucket, config) + err = c.be.PutObjectLockConfiguration(ctx.RequestCtx(), bucket, config) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -230,14 +230,14 @@ func (c S3ApiController) PutObjectLockConfiguration(ctx *fiber.Ctx) (*Response, }, err } -func (c S3ApiController) PutBucketCors(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutBucketCors(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -256,7 +256,7 @@ func (c S3ApiController) PutBucketCors(ctx *fiber.Ctx) (*Response, error) { }, err } - body := ctx.Body() + body := ctx.BodyRaw() var corsConfig auth.CORSConfiguration err = xml.Unmarshal(body, &corsConfig) @@ -279,7 +279,7 @@ func (c S3ApiController) PutBucketCors(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.PutBucketCors(ctx.Context(), bucket, body) + err = c.be.PutBucketCors(ctx.RequestCtx(), bucket, body) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -287,14 +287,14 @@ func (c S3ApiController) PutBucketCors(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) PutBucketWebsite(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutBucketWebsite(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -313,7 +313,7 @@ func (c S3ApiController) PutBucketWebsite(ctx *fiber.Ctx) (*Response, error) { }, err } - body := ctx.Body() + body := ctx.BodyRaw() if len(body) > maxWebsiteConfigurationBytes { debuglogger.Logf("the request size exceeded the 128KB limit: %d", len(body)) return &Response{ @@ -343,7 +343,7 @@ func (c S3ApiController) PutBucketWebsite(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.PutBucketWebsite(ctx.Context(), bucket, body) + err = c.be.PutBucketWebsite(ctx.RequestCtx(), bucket, body) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -351,13 +351,13 @@ func (c S3ApiController) PutBucketWebsite(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) PutBucketPolicy(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutBucketPolicy(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -375,7 +375,7 @@ func (c S3ApiController) PutBucketPolicy(ctx *fiber.Ctx) (*Response, error) { }, err } - err = auth.ValidatePolicyDocument(ctx.Body(), bucket, c.iam) + err = auth.ValidatePolicyDocument(ctx.BodyRaw(), bucket, c.iam) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -384,7 +384,7 @@ func (c S3ApiController) PutBucketPolicy(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.PutBucketPolicy(ctx.Context(), bucket, ctx.Body()) + err = c.be.PutBucketPolicy(ctx.RequestCtx(), bucket, ctx.BodyRaw()) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -393,7 +393,7 @@ func (c S3ApiController) PutBucketPolicy(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) PutBucketAcl(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutBucketAcl(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acl := types.BucketCannedACL(ctx.Get("X-Amz-Acl")) grantFullControl := ctx.Get("X-Amz-Grant-Full-Control") @@ -409,7 +409,7 @@ func (c S3ApiController) PutBucketAcl(ctx *fiber.Ctx) (*Response, error) { grants := grantFullControl + grantRead + grantReadACP + grantWrite + grantWriteACP var input *auth.PutBucketAclInput - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -446,7 +446,7 @@ func (c S3ApiController) PutBucketAcl(ctx *fiber.Ctx) (*Response, error) { }, err } - ownership, err := c.be.GetBucketOwnershipControls(ctx.Context(), bucket) + ownership, err := c.be.GetBucketOwnershipControls(ctx.RequestCtx(), bucket) if err != nil && !errors.Is(err, s3err.GetAPIError(s3err.ErrOwnershipControlsNotFound)) { return &Response{ MetaOpts: &MetaOptions{ @@ -463,9 +463,9 @@ func (c S3ApiController) PutBucketAcl(ctx *fiber.Ctx) (*Response, error) { }, s3err.GetAPIError(s3err.ErrAclNotSupported) } - if len(ctx.Body()) > 0 { + if len(ctx.BodyRaw()) > 0 { var accessControlPolicy auth.AccessControlPolicy - err := xml.Unmarshal(ctx.Body(), &accessControlPolicy) + err := xml.Unmarshal(ctx.BodyRaw(), &accessControlPolicy) if err != nil { debuglogger.Logf("error unmarshalling access control policy: %v", err) return &Response{ @@ -554,7 +554,7 @@ func (c S3ApiController) PutBucketAcl(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.PutBucketAcl(ctx.Context(), bucket, updAcl) + err = c.be.PutBucketAcl(ctx.RequestCtx(), bucket, updAcl) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -562,7 +562,7 @@ func (c S3ApiController) PutBucketAcl(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) CreateBucket(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) CreateBucket(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") acl := types.BucketCannedACL(c.getAclHeaderValue(ctx, "X-Amz-Acl")) grantFullControl := c.getAclHeaderValue(ctx, "X-Amz-Grant-Full-Control") @@ -654,9 +654,9 @@ func (c S3ApiController) CreateBucket(ctx *fiber.Ctx) (*Response, error) { } var body s3response.CreateBucketConfiguration - if len(ctx.Body()) != 0 { + if len(ctx.BodyRaw()) != 0 { // request body is optional for CreateBucket - err := xml.Unmarshal(ctx.Body(), &body) + err := xml.Unmarshal(ctx.BodyRaw(), &body) if err != nil { debuglogger.Logf("failed to parse the request body: %v", err) return &Response{ @@ -704,7 +704,7 @@ func (c S3ApiController) CreateBucket(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.CreateBucket(ctx.Context(), &s3.CreateBucketInput{ + err = c.be.CreateBucket(ctx.RequestCtx(), &s3.CreateBucketInput{ Bucket: &bucket, ObjectOwnership: objectOwnership, ObjectLockEnabledForBucket: &lockEnabled, diff --git a/s3api/controllers/cors_default_origin_test.go b/s3api/controllers/cors_default_origin_test.go index a8771423..8729e207 100644 --- a/s3api/controllers/cors_default_origin_test.go +++ b/s3api/controllers/cors_default_origin_test.go @@ -19,7 +19,7 @@ import ( "net/http" "testing" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/s3api/middlewares" "github.com/versity/versitygw/s3err" ) @@ -36,7 +36,7 @@ func TestApplyBucketCORS_FallbackOrigin_NoBucketCors_NoRequestOrigin(t *testing. app := fiber.New() app.Get("/:bucket/test", middlewares.ApplyBucketCORS(mockedBackend, middlewares.BucketFromPath, origin), - func(c *fiber.Ctx) error { + func(c fiber.Ctx) error { return c.SendStatus(http.StatusOK) }, ) @@ -72,7 +72,7 @@ func TestApplyBucketCORS_FallbackOrigin_NotAppliedWhenBucketCorsExists(t *testin app := fiber.New() app.Get("/:bucket/test", middlewares.ApplyBucketCORS(mockedBackend, middlewares.BucketFromPath, origin), - func(c *fiber.Ctx) error { + func(c fiber.Ctx) error { return c.SendStatus(http.StatusOK) }, ) diff --git a/s3api/controllers/object-delete.go b/s3api/controllers/object-delete.go index cfad6b12..111b3957 100644 --- a/s3api/controllers/object-delete.go +++ b/s3api/controllers/object-delete.go @@ -21,13 +21,13 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3event" ) -func (c S3ApiController) DeleteObjectTagging(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) DeleteObjectTagging(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") @@ -41,7 +41,7 @@ func (c S3ApiController) DeleteObjectTagging(ctx *fiber.Ctx) (*Response, error) action = auth.DeleteObjectVersionTaggingAction } - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -62,7 +62,7 @@ func (c S3ApiController) DeleteObjectTagging(ctx *fiber.Ctx) (*Response, error) }, err } - err = c.be.DeleteObjectTagging(ctx.Context(), bucket, key, versionId) + err = c.be.DeleteObjectTagging(ctx.RequestCtx(), bucket, key, versionId) return &Response{ Headers: map[string]*string{ "x-amz-version-id": &versionId, @@ -75,7 +75,7 @@ func (c S3ApiController) DeleteObjectTagging(ctx *fiber.Ctx) (*Response, error) }, err } -func (c S3ApiController) AbortMultipartUpload(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) AbortMultipartUpload(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) uploadId := ctx.Query("uploadId") @@ -85,7 +85,7 @@ func (c S3ApiController) AbortMultipartUpload(ctx *fiber.Ctx) (*Response, error) isBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -106,7 +106,7 @@ func (c S3ApiController) AbortMultipartUpload(ctx *fiber.Ctx) (*Response, error) }, err } - err = c.be.AbortMultipartUpload(ctx.Context(), + err = c.be.AbortMultipartUpload(ctx.RequestCtx(), &s3.AbortMultipartUploadInput{ UploadId: &uploadId, Bucket: &bucket, @@ -121,12 +121,12 @@ func (c S3ApiController) AbortMultipartUpload(ctx *fiber.Ctx) (*Response, error) }, err } -func (c S3ApiController) DeleteObject(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) DeleteObject(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") bypass := strings.EqualFold(ctx.Get("X-Amz-Bypass-Governance-Retention"), "true") - ifMatch := utils.GetStringPtr(utils.TrimQuotes(ctx.Get("If-Match"))) + ifMatch := utils.GetStringPtr(strings.Trim(ctx.Get("If-Match"), `"`)) ifMatchLastModTime := utils.ParsePreconditionDateHeader(ctx.Get("X-Amz-If-Match-Last-Modified-Time")) ifMatchSize := utils.ParseIfMatchSize(ctx) // context locals @@ -140,7 +140,7 @@ func (c S3ApiController) DeleteObject(ctx *fiber.Ctx) (*Response, error) { action = auth.DeleteObjectVersionAction } - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -162,7 +162,7 @@ func (c S3ApiController) DeleteObject(ctx *fiber.Ctx) (*Response, error) { } err = auth.CheckObjectAccess( - ctx.Context(), + ctx.RequestCtx(), bucket, acct.Access, []types.ObjectIdentifier{ @@ -184,7 +184,7 @@ func (c S3ApiController) DeleteObject(ctx *fiber.Ctx) (*Response, error) { }, err } - res, err := c.be.DeleteObject(ctx.Context(), + res, err := c.be.DeleteObject(ctx.RequestCtx(), &s3.DeleteObjectInput{ Bucket: &bucket, Key: &key, diff --git a/s3api/controllers/object-get.go b/s3api/controllers/object-get.go index 07c312ab..d418ad88 100644 --- a/s3api/controllers/object-get.go +++ b/s3api/controllers/object-get.go @@ -23,7 +23,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3api/utils" @@ -31,7 +31,7 @@ import ( "github.com/versity/versitygw/s3response" ) -func (c S3ApiController) GetObjectTagging(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetObjectTagging(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") @@ -45,7 +45,7 @@ func (c S3ApiController) GetObjectTagging(ctx *fiber.Ctx) (*Response, error) { action = auth.GetObjectVersionTaggingAction } - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -65,7 +65,7 @@ func (c S3ApiController) GetObjectTagging(ctx *fiber.Ctx) (*Response, error) { }, err } - data, err := c.be.GetObjectTagging(ctx.Context(), bucket, key, versionId) + data, err := c.be.GetObjectTagging(ctx.RequestCtx(), bucket, key, versionId) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -93,7 +93,7 @@ func (c S3ApiController) GetObjectTagging(ctx *fiber.Ctx) (*Response, error) { }, nil } -func (c S3ApiController) GetObjectRetention(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetObjectRetention(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") @@ -103,7 +103,7 @@ func (c S3ApiController) GetObjectRetention(ctx *fiber.Ctx) (*Response, error) { parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -123,7 +123,7 @@ func (c S3ApiController) GetObjectRetention(ctx *fiber.Ctx) (*Response, error) { }, err } - data, err := c.be.GetObjectRetention(ctx.Context(), bucket, key, versionId) + data, err := c.be.GetObjectRetention(ctx.RequestCtx(), bucket, key, versionId) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -141,7 +141,7 @@ func (c S3ApiController) GetObjectRetention(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) GetObjectLegalHold(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetObjectLegalHold(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") @@ -151,7 +151,7 @@ func (c S3ApiController) GetObjectLegalHold(ctx *fiber.Ctx) (*Response, error) { parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -171,7 +171,7 @@ func (c S3ApiController) GetObjectLegalHold(ctx *fiber.Ctx) (*Response, error) { }, err } - data, err := c.be.GetObjectLegalHold(ctx.Context(), bucket, key, versionId) + data, err := c.be.GetObjectLegalHold(ctx.RequestCtx(), bucket, key, versionId) return &Response{ Data: auth.ParseObjectLegalHoldOutput(data), MetaOpts: &MetaOptions{ @@ -180,7 +180,7 @@ func (c S3ApiController) GetObjectLegalHold(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) GetObjectAcl(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetObjectAcl(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) // context locals @@ -189,7 +189,7 @@ func (c S3ApiController) GetObjectAcl(ctx *fiber.Ctx) (*Response, error) { parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionReadAcp, @@ -208,7 +208,7 @@ func (c S3ApiController) GetObjectAcl(ctx *fiber.Ctx) (*Response, error) { }, }, err } - res, err := c.be.GetObjectAcl(ctx.Context(), &s3.GetObjectAclInput{ + res, err := c.be.GetObjectAcl(ctx.RequestCtx(), &s3.GetObjectAclInput{ Bucket: &bucket, Key: &key, }) @@ -220,7 +220,7 @@ func (c S3ApiController) GetObjectAcl(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) ListParts(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) ListParts(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) uploadId := ctx.Query("uploadId") @@ -232,7 +232,7 @@ func (c S3ApiController) ListParts(ctx *fiber.Ctx) (*Response, error) { parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -272,7 +272,7 @@ func (c S3ApiController) ListParts(ctx *fiber.Ctx) (*Response, error) { }, err } - res, err := c.be.ListParts(ctx.Context(), &s3.ListPartsInput{ + res, err := c.be.ListParts(ctx.RequestCtx(), &s3.ListPartsInput{ Bucket: &bucket, Key: &key, UploadId: &uploadId, @@ -287,7 +287,7 @@ func (c S3ApiController) ListParts(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) GetObjectAttributes(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetObjectAttributes(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") @@ -304,7 +304,7 @@ func (c S3ApiController) GetObjectAttributes(ctx *fiber.Ctx) (*Response, error) action = auth.GetObjectVersionAttributesAction } - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -341,7 +341,7 @@ func (c S3ApiController) GetObjectAttributes(ctx *fiber.Ctx) (*Response, error) }, err } - res, err := c.be.GetObjectAttributes(ctx.Context(), + res, err := c.be.GetObjectAttributes(ctx.RequestCtx(), &s3.GetObjectAttributesInput{ Bucket: &bucket, Key: &key, @@ -381,13 +381,13 @@ func (c S3ApiController) GetObjectAttributes(ctx *fiber.Ctx) (*Response, error) }, err } -func (c S3ApiController) GetObject(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) GetObject(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") acceptRange := ctx.Get("Range") checksumMode := types.ChecksumMode(strings.ToUpper(ctx.Get("x-amz-checksum-mode"))) - partNumberQuery := int32(ctx.QueryInt("partNumber", -1)) + partNumberQuery := int32(fiber.Query[int](ctx, "partNumber", -1)) // Extract response override query parameters responseOverrides := map[string]*string{ @@ -429,7 +429,7 @@ func (c S3ApiController) GetObject(ctx *fiber.Ctx) (*Response, error) { action = auth.GetObjectVersionAction } - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionRead, @@ -484,7 +484,7 @@ func (c S3ApiController) GetObject(ctx *fiber.Ctx) (*Response, error) { conditionalHeaders := utils.ParsePreconditionHeaders(ctx) - res, err := c.be.GetObject(ctx.Context(), &s3.GetObjectInput{ + res, err := c.be.GetObject(ctx.RequestCtx(), &s3.GetObjectInput{ Bucket: &bucket, Key: &key, Range: &acceptRange, diff --git a/s3api/controllers/object-head.go b/s3api/controllers/object-head.go index de949adc..6e1acba7 100644 --- a/s3api/controllers/object-head.go +++ b/s3api/controllers/object-head.go @@ -22,14 +22,14 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" ) -func (c S3ApiController) HeadObject(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) HeadObject(ctx fiber.Ctx) (*Response, error) { // context locals acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) @@ -37,7 +37,7 @@ func (c S3ApiController) HeadObject(ctx *fiber.Ctx) (*Response, error) { isPublicBucket := utils.ContextKeyPublicBucket.IsSet(ctx) // url values bucket := ctx.Params("bucket") - partNumberQuery := int32(ctx.QueryInt("partNumber", -1)) + partNumberQuery := int32(fiber.Query(ctx, "partNumber", -1)) versionId := ctx.Query("versionId") objRange := ctx.Get("Range") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) @@ -76,7 +76,7 @@ func (c S3ApiController) HeadObject(ctx *fiber.Ctx) (*Response, error) { action = auth.GetObjectVersionAction } - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -132,7 +132,7 @@ func (c S3ApiController) HeadObject(ctx *fiber.Ctx) (*Response, error) { conditionalHeaders := utils.ParsePreconditionHeaders(ctx) - res, err := c.be.HeadObject(ctx.Context(), + res, err := c.be.HeadObject(ctx.RequestCtx(), &s3.HeadObjectInput{ Bucket: &bucket, Key: &key, diff --git a/s3api/controllers/object-post.go b/s3api/controllers/object-post.go index 03180c96..fb2052db 100644 --- a/s3api/controllers/object-post.go +++ b/s3api/controllers/object-post.go @@ -22,7 +22,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3api/utils" @@ -31,7 +31,7 @@ import ( "github.com/versity/versitygw/s3response" ) -func (c S3ApiController) RestoreObject(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) RestoreObject(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) @@ -39,7 +39,7 @@ func (c S3ApiController) RestoreObject(ctx *fiber.Ctx) (*Response, error) { isBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -61,7 +61,7 @@ func (c S3ApiController) RestoreObject(ctx *fiber.Ctx) (*Response, error) { } var restoreRequest types.RestoreRequest - if err := xml.Unmarshal(ctx.Body(), &restoreRequest); err != nil { + if err := xml.Unmarshal(ctx.BodyRaw(), &restoreRequest); err != nil { debuglogger.Logf("failed to parse the request body: %v", err) return &Response{ MetaOpts: &MetaOptions{ @@ -70,7 +70,7 @@ func (c S3ApiController) RestoreObject(ctx *fiber.Ctx) (*Response, error) { }, s3err.GetAPIError(s3err.ErrMalformedXML) } - err = c.be.RestoreObject(ctx.Context(), &s3.RestoreObjectInput{ + err = c.be.RestoreObject(ctx.RequestCtx(), &s3.RestoreObjectInput{ Bucket: &bucket, Key: &key, RestoreRequest: &restoreRequest, @@ -83,7 +83,7 @@ func (c S3ApiController) RestoreObject(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) SelectObjectContent(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) SelectObjectContent(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) @@ -91,7 +91,7 @@ func (c S3ApiController) SelectObjectContent(ctx *fiber.Ctx) (*Response, error) isBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -113,7 +113,7 @@ func (c S3ApiController) SelectObjectContent(ctx *fiber.Ctx) (*Response, error) } var payload s3response.SelectObjectContentPayload - err = xml.Unmarshal(ctx.Body(), &payload) + err = xml.Unmarshal(ctx.BodyRaw(), &payload) if err != nil { debuglogger.Logf("error unmarshalling select object content: %v", err) return &Response{ @@ -123,7 +123,7 @@ func (c S3ApiController) SelectObjectContent(ctx *fiber.Ctx) (*Response, error) }, s3err.GetAPIError(s3err.ErrMalformedXML) } - sw := c.be.SelectObjectContent(ctx.Context(), + sw := c.be.SelectObjectContent(ctx.RequestCtx(), &s3.SelectObjectContentInput{ Bucket: &bucket, Key: &key, @@ -135,7 +135,7 @@ func (c S3ApiController) SelectObjectContent(ctx *fiber.Ctx) (*Response, error) ScanRange: payload.ScanRange, }) - ctx.Context().SetBodyStreamWriter(sw) + ctx.RequestCtx().SetBodyStreamWriter(sw) return &Response{ MetaOpts: &MetaOptions{ @@ -144,7 +144,7 @@ func (c S3ApiController) SelectObjectContent(ctx *fiber.Ctx) (*Response, error) }, nil } -func (c S3ApiController) CreateMultipartUpload(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) CreateMultipartUpload(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) contentType := ctx.Get("Content-Type", defaultContentType) @@ -175,7 +175,7 @@ func (c S3ApiController) CreateMultipartUpload(ctx *fiber.Ctx) (*Response, error actions = append(actions, auth.PutObjectRetentionAction) } - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -231,7 +231,7 @@ func (c S3ApiController) CreateMultipartUpload(ctx *fiber.Ctx) (*Response, error }, err } - res, err := c.be.CreateMultipartUpload(ctx.Context(), + res, err := c.be.CreateMultipartUpload(ctx.RequestCtx(), s3response.CreateMultipartUploadInput{ Bucket: &bucket, Key: &key, @@ -266,7 +266,7 @@ func (c S3ApiController) CreateMultipartUpload(ctx *fiber.Ctx) (*Response, error }, err } -func (c S3ApiController) CompleteMultipartUpload(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) CompleteMultipartUpload(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) uploadId := ctx.Query("uploadId") @@ -278,7 +278,7 @@ func (c S3ApiController) CompleteMultipartUpload(ctx *fiber.Ctx) (*Response, err isBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -300,7 +300,7 @@ func (c S3ApiController) CompleteMultipartUpload(ctx *fiber.Ctx) (*Response, err } var body s3response.CompleteMultipartUploadRequestBody - err = xml.Unmarshal(ctx.Body(), &body) + err = xml.Unmarshal(ctx.BodyRaw(), &body) if err != nil { debuglogger.Logf("error unmarshalling complete multipart upload: %v", err) return &Response{ @@ -363,7 +363,7 @@ func (c S3ApiController) CompleteMultipartUpload(ctx *fiber.Ctx) (*Response, err ifMatch, ifNoneMatch := utils.ParsePreconditionMatchHeaders(ctx) - err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []types.ObjectIdentifier{{Key: &key}}, true, isBucketPublic, c.be, true) + err = auth.CheckObjectAccess(ctx.RequestCtx(), bucket, acct.Access, []types.ObjectIdentifier{{Key: &key}}, true, isBucketPublic, c.be, true) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -372,7 +372,7 @@ func (c S3ApiController) CompleteMultipartUpload(ctx *fiber.Ctx) (*Response, err }, err } - res, versid, err := c.be.CompleteMultipartUpload(ctx.Context(), + res, versid, err := c.be.CompleteMultipartUpload(ctx.RequestCtx(), &s3.CompleteMultipartUploadInput{ Bucket: &bucket, Key: &key, diff --git a/s3api/controllers/object-put.go b/s3api/controllers/object-put.go index a66032a3..fc381843 100644 --- a/s3api/controllers/object-put.go +++ b/s3api/controllers/object-put.go @@ -24,7 +24,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3api/utils" @@ -33,7 +33,7 @@ import ( "github.com/versity/versitygw/s3response" ) -func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutObjectTagging(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") @@ -47,7 +47,7 @@ func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) { action = auth.PutObjectVersionTaggingAction } - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -67,7 +67,7 @@ func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) { }, err } - tagging, err := utils.ParseTagging(ctx.Body(), utils.TagLimitObject) + tagging, err := utils.ParseTagging(ctx.BodyRaw(), utils.TagLimitObject) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -76,7 +76,7 @@ func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.PutObjectTagging(ctx.Context(), bucket, key, versionId, tagging) + err = c.be.PutObjectTagging(ctx.RequestCtx(), bucket, key, versionId, tagging) return &Response{ Headers: map[string]*string{ "x-amz-version-id": &versionId, @@ -88,7 +88,7 @@ func (c S3ApiController) PutObjectTagging(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutObjectRetention(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") @@ -98,7 +98,7 @@ func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) { IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -119,7 +119,7 @@ func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) { } // parse the request body bytes into a go struct and validate - retention, err := auth.ParseObjectLockRetentionInput(ctx.Body()) + retention, err := auth.ParseObjectLockRetentionInput(ctx.BodyRaw()) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -129,7 +129,7 @@ func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) { } // check if the operation is allowed - err = auth.IsObjectLockRetentionPutAllowed(ctx.Context(), c.be, bucket, key, versionId, acct.Access, retention, bypass) + err = auth.IsObjectLockRetentionPutAllowed(ctx.RequestCtx(), c.be, bucket, key, versionId, acct.Access, retention, bypass) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -148,7 +148,7 @@ func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.PutObjectRetention(ctx.Context(), bucket, key, versionId, data) + err = c.be.PutObjectRetention(ctx.RequestCtx(), bucket, key, versionId, data) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -156,7 +156,7 @@ func (c S3ApiController) PutObjectRetention(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutObjectLegalHold(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) versionId := ctx.Query("versionId") @@ -165,7 +165,7 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) { IsBucketPublic := utils.ContextKeyPublicBucket.IsSet(ctx) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, auth.AccessOptions{ + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -186,7 +186,7 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) { } var legalHold types.ObjectLockLegalHold - if err := xml.Unmarshal(ctx.Body(), &legalHold); err != nil { + if err := xml.Unmarshal(ctx.BodyRaw(), &legalHold); err != nil { debuglogger.Logf("failed to parse request body: %v", err) return &Response{ MetaOpts: &MetaOptions{ @@ -204,7 +204,7 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) { }, s3err.GetAPIError(s3err.ErrMalformedXML) } - err = c.be.PutObjectLegalHold(ctx.Context(), bucket, key, versionId, legalHold.Status == types.ObjectLockLegalHoldStatusOn) + err = c.be.PutObjectLegalHold(ctx.RequestCtx(), bucket, key, versionId, legalHold.Status == types.ObjectLockLegalHoldStatusOn) return &Response{ MetaOpts: &MetaOptions{ BucketOwner: parsedAcl.Owner, @@ -212,10 +212,10 @@ func (c S3ApiController) PutObjectLegalHold(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) UploadPart(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) UploadPart(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) - partNumber := int32(ctx.QueryInt("partNumber", -1)) + partNumber := int32(fiber.Query(ctx, "partNumber", -1)) uploadId := ctx.Query("uploadId") // context locals acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) @@ -234,7 +234,7 @@ func (c S3ApiController) UploadPart(ctx *fiber.Ctx) (*Response, error) { contentLengthStr = decodedLength } - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -292,7 +292,7 @@ func (c S3ApiController) UploadPart(ctx *fiber.Ctx) (*Response, error) { body = bytes.NewReader([]byte{}) } - res, err := c.be.UploadPart(ctx.Context(), + res, err := c.be.UploadPart(ctx.RequestCtx(), &s3.UploadPartInput{ Bucket: &bucket, Key: &key, @@ -338,13 +338,13 @@ func (c S3ApiController) UploadPart(ctx *fiber.Ctx) (*Response, error) { } -func (c S3ApiController) UploadPartCopy(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) UploadPartCopy(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) copySource := strings.TrimPrefix(ctx.Get("X-Amz-Copy-Source"), "/") copySrcRange := ctx.Get("X-Amz-Copy-Source-Range") expectedSrcBucketOwnerUPC := ctx.Get("X-Amz-Source-Expected-Bucket-Owner") - partNumber := int32(ctx.QueryInt("partNumber", -1)) + partNumber := int32(fiber.Query(ctx, "partNumber", -1)) uploadId := ctx.Query("uploadId") // context locals acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) @@ -361,7 +361,7 @@ func (c S3ApiController) UploadPartCopy(ctx *fiber.Ctx) (*Response, error) { }, err } - err = auth.VerifyObjectCopyAccess(ctx.Context(), c.be, copySource, + err = auth.VerifyObjectCopyAccess(ctx.RequestCtx(), c.be, copySource, auth.AccessOptions{ Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -401,7 +401,7 @@ func (c S3ApiController) UploadPartCopy(ctx *fiber.Ctx) (*Response, error) { preconditionHdrs := utils.ParsePreconditionHeaders(ctx, utils.WithCopySource()) - resp, err := c.be.UploadPartCopy(ctx.Context(), + resp, err := c.be.UploadPartCopy(ctx.RequestCtx(), &s3.UploadPartCopyInput{ Bucket: &bucket, Key: &key, @@ -430,7 +430,7 @@ func (c S3ApiController) UploadPartCopy(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) PutObjectAcl(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutObjectAcl(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) acl := ctx.Get("X-Amz-Acl") @@ -444,7 +444,7 @@ func (c S3ApiController) PutObjectAcl(ctx *fiber.Ctx) (*Response, error) { isRoot := utils.ContextKeyIsRoot.Get(ctx).(bool) parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -463,7 +463,7 @@ func (c S3ApiController) PutObjectAcl(ctx *fiber.Ctx) (*Response, error) { }, err } - err = c.be.PutObjectAcl(ctx.Context(), &s3.PutObjectAclInput{ + err = c.be.PutObjectAcl(ctx.RequestCtx(), &s3.PutObjectAclInput{ Bucket: &bucket, Key: &key, GrantFullControl: &grantFullControl, @@ -481,7 +481,7 @@ func (c S3ApiController) PutObjectAcl(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) CopyObject(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) copySource := strings.TrimPrefix(ctx.Get("X-Amz-Copy-Source"), "/") @@ -525,7 +525,7 @@ func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) { actions = append(actions, auth.PutObjectRetentionAction) } - err = auth.VerifyObjectCopyAccess(ctx.Context(), c.be, copySource, + err = auth.VerifyObjectCopyAccess(ctx.RequestCtx(), c.be, copySource, auth.AccessOptions{ Acl: parsedAcl, AclPermission: auth.PermissionWrite, @@ -609,7 +609,7 @@ func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) { preconditionHdrs := utils.ParsePreconditionHeaders(ctx, utils.WithCopySource()) - err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []types.ObjectIdentifier{{Key: &key}}, true, false, c.be, true) + err = auth.CheckObjectAccess(ctx.RequestCtx(), bucket, acct.Access, []types.ObjectIdentifier{{Key: &key}}, true, false, c.be, true) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -618,7 +618,7 @@ func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) { }, err } - res, err := c.be.CopyObject(ctx.Context(), + res, err := c.be.CopyObject(ctx.RequestCtx(), s3response.CopyObjectInput{ Bucket: &bucket, Key: &key, @@ -667,7 +667,7 @@ func (c S3ApiController) CopyObject(ctx *fiber.Ctx) (*Response, error) { }, err } -func (c S3ApiController) PutObject(ctx *fiber.Ctx) (*Response, error) { +func (c S3ApiController) PutObject(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") key := strings.TrimPrefix(ctx.Path(), fmt.Sprintf("/%s/", bucket)) contentType := ctx.Get("Content-Type", defaultContentType) @@ -710,7 +710,7 @@ func (c S3ApiController) PutObject(ctx *fiber.Ctx) (*Response, error) { actions = append(actions, auth.PutObjectRetentionAction) } - err := auth.VerifyAccess(ctx.Context(), c.be, + err := auth.VerifyAccess(ctx.RequestCtx(), c.be, auth.AccessOptions{ Readonly: c.readonly, Acl: parsedAcl, @@ -750,7 +750,7 @@ func (c S3ApiController) PutObject(ctx *fiber.Ctx) (*Response, error) { }, err } - err = auth.CheckObjectAccess(ctx.Context(), bucket, acct.Access, []types.ObjectIdentifier{{Key: &key}}, true, IsBucketPublic, c.be, true) + err = auth.CheckObjectAccess(ctx.RequestCtx(), bucket, acct.Access, []types.ObjectIdentifier{{Key: &key}}, true, IsBucketPublic, c.be, true) if err != nil { return &Response{ MetaOpts: &MetaOptions{ @@ -797,7 +797,7 @@ func (c S3ApiController) PutObject(ctx *fiber.Ctx) (*Response, error) { ifMatch, ifNoneMatch := utils.ParsePreconditionMatchHeaders(ctx) - res, err := c.be.PutObject(ctx.Context(), + res, err := c.be.PutObject(ctx.RequestCtx(), s3response.PutObjectInput{ Bucket: &bucket, Key: &key, diff --git a/s3api/controllers/options.go b/s3api/controllers/options.go index a78dd596..2f662b0a 100644 --- a/s3api/controllers/options.go +++ b/s3api/controllers/options.go @@ -18,7 +18,7 @@ import ( "errors" "net/http" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3api/middlewares" @@ -26,7 +26,7 @@ import ( "github.com/versity/versitygw/s3err" ) -func (s S3ApiController) CORSOptions(ctx *fiber.Ctx) (*Response, error) { +func (s S3ApiController) CORSOptions(ctx fiber.Ctx) (*Response, error) { bucket := ctx.Params("bucket") parsedAcl := utils.ContextKeyParsedAcl.Get(ctx).(auth.ACL) // get headers @@ -65,7 +65,7 @@ func (s S3ApiController) CORSOptions(ctx *fiber.Ctx) (*Response, error) { }, err } - cors, err := s.be.GetBucketCors(ctx.Context(), bucket) + cors, err := s.be.GetBucketCors(ctx.RequestCtx(), bucket) if err != nil { debuglogger.Logf("failed to get bucket cors: %v", err) if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchCORSConfiguration)) { diff --git a/s3api/middlewares/acl-parser.go b/s3api/middlewares/acl-parser.go index e138a11f..7bc7ccee 100644 --- a/s3api/middlewares/acl-parser.go +++ b/s3api/middlewares/acl-parser.go @@ -16,7 +16,7 @@ package middlewares import ( "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/s3api/utils" @@ -26,9 +26,9 @@ import ( // ParseAcl retreives the bucket acl and stores in the context locals // if no bucket is found, it returns 'NoSuchBucket' func ParseAcl(be backend.Backend) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { bucket := ctx.Params("bucket") - data, err := be.GetBucketAcl(ctx.Context(), &s3.GetBucketAclInput{Bucket: &bucket}) + data, err := be.GetBucketAcl(ctx.RequestCtx(), &s3.GetBucketAclInput{Bucket: &bucket}) if err != nil { return err } diff --git a/s3api/middlewares/admin.go b/s3api/middlewares/admin.go index b8626dff..647df992 100644 --- a/s3api/middlewares/admin.go +++ b/s3api/middlewares/admin.go @@ -15,7 +15,7 @@ package middlewares import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" @@ -23,7 +23,7 @@ import ( // IsAdmin is a middleware that restricts access to admin APIs, allowing only admin users func IsAdmin(action string) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { acct := utils.ContextKeyAccount.Get(ctx).(auth.Account) if acct.Role != auth.RoleAdmin { return s3err.GetAPIError(s3err.ErrAdminAccessDenied) diff --git a/s3api/middlewares/apply-bucket-cors-preflight.go b/s3api/middlewares/apply-bucket-cors-preflight.go index 051a1d2f..4e26a6ef 100644 --- a/s3api/middlewares/apply-bucket-cors-preflight.go +++ b/s3api/middlewares/apply-bucket-cors-preflight.go @@ -17,7 +17,7 @@ package middlewares import ( "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/s3err" ) @@ -36,12 +36,12 @@ import ( func ApplyBucketCORSPreflightFallback(be backend.Backend, fallbackOrigin string) fiber.Handler { fallbackOrigin = strings.TrimSpace(fallbackOrigin) if fallbackOrigin == "" { - return func(ctx *fiber.Ctx) error { return ctx.Next() } + return func(ctx fiber.Ctx) error { return ctx.Next() } } - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { bucket := ctx.Params("bucket") - _, err := be.GetBucketCors(ctx.Context(), bucket) + _, err := be.GetBucketCors(ctx.RequestCtx(), bucket) if err != nil { if s3Err, ok := err.(s3err.S3Error); ok && (s3Err.BaseError().Code == "NoSuchCORSConfiguration" || s3Err.BaseError().Code == "NoSuchBucket") { if len(ctx.Response().Header.Peek("Access-Control-Allow-Origin")) == 0 { diff --git a/s3api/middlewares/apply-bucket-cors-preflight_test.go b/s3api/middlewares/apply-bucket-cors-preflight_test.go index 5a4bdca7..979e04a2 100644 --- a/s3api/middlewares/apply-bucket-cors-preflight_test.go +++ b/s3api/middlewares/apply-bucket-cors-preflight_test.go @@ -19,7 +19,7 @@ import ( "net/http" "testing" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/s3err" ) @@ -43,7 +43,7 @@ func TestApplyBucketCORSPreflightFallback_NoBucketCors_Responds204(t *testing.T) app := fiber.New() app.Options("/:bucket", ApplyBucketCORSPreflightFallback(be, "https://example.com"), - func(c *fiber.Ctx) error { + func(c fiber.Ctx) error { // Should not be reached if fallback triggers return c.SendStatus(http.StatusTeapot) }, @@ -87,7 +87,7 @@ func TestApplyBucketCORSPreflightFallback_NoSuchBucket_Responds204(t *testing.T) app := fiber.New() app.Options("/:bucket", ApplyBucketCORSPreflightFallback(be, "https://example.com"), - func(c *fiber.Ctx) error { + func(c fiber.Ctx) error { return c.SendStatus(http.StatusTeapot) }, ) @@ -127,7 +127,7 @@ func TestApplyBucketCORSPreflightFallback_BucketHasCors_CallsNext(t *testing.T) app := fiber.New() app.Options("/:bucket", ApplyBucketCORSPreflightFallback(be, "https://example.com"), - func(c *fiber.Ctx) error { + func(c fiber.Ctx) error { return c.SendStatus(http.StatusOK) }, ) diff --git a/s3api/middlewares/apply-bucket-cors.go b/s3api/middlewares/apply-bucket-cors.go index 0f2c3a81..94fbe245 100644 --- a/s3api/middlewares/apply-bucket-cors.go +++ b/s3api/middlewares/apply-bucket-cors.go @@ -18,7 +18,7 @@ import ( "fmt" "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/debuglogger" @@ -28,9 +28,9 @@ import ( // Vary http response header is always the same below var VaryHdr = "Origin, Access-Control-Request-Headers, Access-Control-Request-Method" -type BucketResolver func(ctx *fiber.Ctx) (string, error) +type BucketResolver func(ctx fiber.Ctx) (string, error) -func BucketFromPath(ctx *fiber.Ctx) (string, error) { +func BucketFromPath(ctx fiber.Ctx) (string, error) { return ctx.Params("bucket"), nil } @@ -41,7 +41,7 @@ func BucketFromPath(ctx *fiber.Ctx) (string, error) { func ApplyBucketCORS(be backend.Backend, resolveBucket BucketResolver, fallbackOrigin string) fiber.Handler { fallbackOrigin = strings.TrimSpace(fallbackOrigin) - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { origin := ctx.Get("Origin") // If neither Origin is present nor a fallback is configured, skip CORS entirely. if origin == "" && fallbackOrigin == "" { @@ -54,7 +54,7 @@ func ApplyBucketCORS(be backend.Backend, resolveBucket BucketResolver, fallbackO } // if bucket cors is not set, skip the check - data, err := be.GetBucketCors(ctx.Context(), bucket) + data, err := be.GetBucketCors(ctx.RequestCtx(), bucket) if err != nil { // If CORS is not configured, S3Error will have code NoSuchCORSConfiguration. // In this case, we can safely continue. For any other error, we should log it. diff --git a/s3api/middlewares/apply-default-cors-preflight.go b/s3api/middlewares/apply-default-cors-preflight.go index dc8cf018..4ede423a 100644 --- a/s3api/middlewares/apply-default-cors-preflight.go +++ b/s3api/middlewares/apply-default-cors-preflight.go @@ -17,7 +17,7 @@ package middlewares import ( "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) // ApplyDefaultCORSPreflight responds to CORS preflight (OPTIONS) requests for routes @@ -29,10 +29,10 @@ import ( func ApplyDefaultCORSPreflight(fallbackOrigin string) fiber.Handler { fallbackOrigin = strings.TrimSpace(fallbackOrigin) if fallbackOrigin == "" { - return func(ctx *fiber.Ctx) error { return nil } + return func(ctx fiber.Ctx) error { return nil } } - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { if len(ctx.Response().Header.Peek("Access-Control-Allow-Origin")) == 0 { ctx.Response().Header.Add("Access-Control-Allow-Origin", fallbackOrigin) } diff --git a/s3api/middlewares/apply-default-cors-preflight_test.go b/s3api/middlewares/apply-default-cors-preflight_test.go index f3d4d2a3..dda24b4b 100644 --- a/s3api/middlewares/apply-default-cors-preflight_test.go +++ b/s3api/middlewares/apply-default-cors-preflight_test.go @@ -18,7 +18,7 @@ import ( "net/http" "testing" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) func TestApplyDefaultCORSPreflight_OptionsSetsPreflightHeaders(t *testing.T) { @@ -28,7 +28,7 @@ func TestApplyDefaultCORSPreflight_OptionsSetsPreflightHeaders(t *testing.T) { app.Options("/admin", ApplyDefaultCORSPreflight(origin), ApplyDefaultCORS(origin), - func(c *fiber.Ctx) error { return nil }, + func(c fiber.Ctx) error { return nil }, ) req, err := http.NewRequest(http.MethodOptions, "/admin", nil) diff --git a/s3api/middlewares/apply-default-cors.go b/s3api/middlewares/apply-default-cors.go index e414d47e..73197422 100644 --- a/s3api/middlewares/apply-default-cors.go +++ b/s3api/middlewares/apply-default-cors.go @@ -17,10 +17,10 @@ package middlewares import ( "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) -func ensureExposeETag(ctx *fiber.Ctx) { +func ensureExposeETag(ctx fiber.Ctx) { existing := strings.TrimSpace(string(ctx.Response().Header.Peek("Access-Control-Expose-Headers"))) defaults := []string{"ETag", "x-amz-storage-class"} if existing == "" { @@ -57,10 +57,10 @@ func ensureExposeETag(ctx *fiber.Ctx) { func ApplyDefaultCORS(fallbackOrigin string) fiber.Handler { fallbackOrigin = strings.TrimSpace(fallbackOrigin) if fallbackOrigin == "" { - return func(ctx *fiber.Ctx) error { return nil } + return func(ctx fiber.Ctx) error { return nil } } - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { if len(ctx.Response().Header.Peek("Access-Control-Allow-Origin")) == 0 { ctx.Response().Header.Add("Access-Control-Allow-Origin", fallbackOrigin) } diff --git a/s3api/middlewares/apply-default-cors_test.go b/s3api/middlewares/apply-default-cors_test.go index e1597c80..42ea8959 100644 --- a/s3api/middlewares/apply-default-cors_test.go +++ b/s3api/middlewares/apply-default-cors_test.go @@ -18,14 +18,14 @@ import ( "net/http" "testing" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) func TestApplyDefaultCORS_AddsHeaderWhenOriginSet(t *testing.T) { origin := "https://example.com" app := fiber.New() - app.Get("/admin", ApplyDefaultCORS(origin), func(c *fiber.Ctx) error { + app.Get("/admin", ApplyDefaultCORS(origin), func(c fiber.Ctx) error { return c.SendStatus(http.StatusOK) }) @@ -52,10 +52,10 @@ func TestApplyDefaultCORS_DoesNotOverrideExistingHeader(t *testing.T) { origin := "https://example.com" app := fiber.New() - app.Get("/admin", func(c *fiber.Ctx) error { + app.Get("/admin", func(c fiber.Ctx) error { c.Response().Header.Add("Access-Control-Allow-Origin", "https://already-set.com") return nil - }, ApplyDefaultCORS(origin), func(c *fiber.Ctx) error { + }, ApplyDefaultCORS(origin), func(c fiber.Ctx) error { return c.SendStatus(http.StatusOK) }) diff --git a/s3api/middlewares/authentication.go b/s3api/middlewares/authentication.go index ace622a4..2f1cea90 100644 --- a/s3api/middlewares/authentication.go +++ b/s3api/middlewares/authentication.go @@ -21,7 +21,7 @@ import ( "strconv" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" @@ -41,7 +41,7 @@ type RootUserConfig struct { func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string, streamBody, requireContentSha256, allowDefaultRegion bool) fiber.Handler { acct := accounts{root: root, iam: iam} - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { // The bucket is public, no need to check this signature if utils.ContextKeyPublicBucket.IsSet(ctx) { return nil @@ -178,7 +178,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string, if !utils.IsSpecialPayload(hashPayload) { // Calculate the hash of the request payload - hashedPayload := sha256.Sum256(ctx.Body()) + hashedPayload := sha256.Sum256(ctx.BodyRaw()) hexPayload := hex.EncodeToString(hashedPayload[:]) // Compare the calculated hash with the hash provided diff --git a/s3api/middlewares/body-reader.go b/s3api/middlewares/body-reader.go index 97a705a3..0e52cc11 100644 --- a/s3api/middlewares/body-reader.go +++ b/s3api/middlewares/body-reader.go @@ -18,7 +18,7 @@ import ( "bytes" "io" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/s3api/utils" ) @@ -85,7 +85,7 @@ func (rr *MockChecksumReader) Checksum() string { var _ ChecksumReader = &MockChecksumReader{} -func wrapBodyReader(ctx *fiber.Ctx, wr func(io.Reader) io.Reader) { +func wrapBodyReader(ctx fiber.Ctx, wr func(io.Reader) io.Reader) { rdr, ok := utils.ContextKeyBodyReader.Get(ctx).(io.Reader) if !ok { rdr = ctx.Request().BodyStream() diff --git a/s3api/middlewares/bucket-object-name-validator.go b/s3api/middlewares/bucket-object-name-validator.go index 860b0e88..fba07cf8 100644 --- a/s3api/middlewares/bucket-object-name-validator.go +++ b/s3api/middlewares/bucket-object-name-validator.go @@ -15,7 +15,7 @@ package middlewares import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" ) @@ -23,7 +23,7 @@ import ( // BucketObjectNameValidator extracts and validates // the bucket and object names from the request URI. func BucketObjectNameValidator() fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { bucket, object := parsePath(ctx.Path()) // check if the provided bucket name is valid diff --git a/s3api/middlewares/checksum.go b/s3api/middlewares/checksum.go index ca1fb5fb..c1955284 100644 --- a/s3api/middlewares/checksum.go +++ b/s3api/middlewares/checksum.go @@ -20,7 +20,7 @@ import ( "io" "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" ) @@ -32,7 +32,7 @@ import ( // it wraps the body reader to handle Content-MD5: // the x-amz-checksum-* headers are explicitly processed by the backend. func VerifyChecksums(streamBody bool, requireBody bool, requireChecksum bool) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { md5sum := ctx.Get("Content-Md5") if streamBody { @@ -58,7 +58,7 @@ func VerifyChecksums(streamBody bool, requireBody bool, requireChecksum bool) fi return nil } - body := ctx.Body() + body := ctx.BodyRaw() if requireBody && len(body) == 0 { return s3err.GetAPIError(s3err.ErrMissingRequestBody) } diff --git a/s3api/middlewares/host-style-parser.go b/s3api/middlewares/host-style-parser.go index 5f6445b6..e0d5afc2 100644 --- a/s3api/middlewares/host-style-parser.go +++ b/s3api/middlewares/host-style-parser.go @@ -18,13 +18,13 @@ import ( "fmt" "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) // HostStyleParser is a middleware which parses the bucket name // from the 'Host' header and appends in the request URL path func HostStyleParser(virtualDomain string) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { host := string(ctx.Request().Host()) // the host should match this pattern: '.' bucket, _, found := strings.Cut(host, "."+virtualDomain) diff --git a/s3api/middlewares/logger.go b/s3api/middlewares/logger.go index b2b84fdc..bb7e8a0e 100644 --- a/s3api/middlewares/logger.go +++ b/s3api/middlewares/logger.go @@ -15,12 +15,12 @@ package middlewares import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/debuglogger" ) func DebugLogger() fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { debuglogger.LogFiberRequestDetails(ctx) err := ctx.Next() debuglogger.LogFiberResponseDetails(ctx) diff --git a/s3api/middlewares/object-post-auth.go b/s3api/middlewares/object-post-auth.go index 7567c885..6e55f3a9 100644 --- a/s3api/middlewares/object-post-auth.go +++ b/s3api/middlewares/object-post-auth.go @@ -20,7 +20,7 @@ import ( "strconv" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3api/utils" @@ -49,7 +49,7 @@ type PostObjectResult struct { func AuthorizePostObject(root RootUserConfig, iam auth.IAMService, region string) fiber.Handler { acct := accounts{root: root, iam: iam} - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { contentLengthStr := ctx.Get("Content-Length") reqContentLength, err := strconv.ParseInt(contentLengthStr, 10, 64) if err != nil { @@ -71,7 +71,7 @@ func AuthorizePostObject(root RootUserConfig, iam auth.IAMService, region string bodyRdr := ctx.Request().BodyStream() if bodyRdr == nil { - bodyRdr = bytes.NewReader(ctx.Body()) + bodyRdr = bytes.NewReader(ctx.BodyRaw()) } mpParser, err := utils.NewMultipartParser(bodyRdr, boundary, reqContentLength) diff --git a/s3api/middlewares/object-post-auth_test.go b/s3api/middlewares/object-post-auth_test.go index 429bd0f5..db278747 100644 --- a/s3api/middlewares/object-post-auth_test.go +++ b/s3api/middlewares/object-post-auth_test.go @@ -26,7 +26,7 @@ import ( "testing" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/stretchr/testify/assert" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" @@ -37,7 +37,7 @@ import ( // versitygw middlewares) returns nil without calling c.Next(), so they must // be chained explicitly rather than relying on fiber's c.Next() mechanism. func chainHandlers(handlers ...fiber.Handler) fiber.Handler { - return func(c *fiber.Ctx) error { + return func(c fiber.Ctx) error { for _, h := range handlers { if err := h(c); err != nil { return err @@ -51,7 +51,7 @@ func chainHandlers(handlers ...fiber.Handler) fiber.Handler { // the provided follow-up handler on POST /:bucket. func postObjectTestApp(root RootUserConfig, region string, next fiber.Handler) *fiber.App { app := fiber.New(fiber.Config{ - ErrorHandler: func(c *fiber.Ctx, err error) error { + ErrorHandler: func(c fiber.Ctx, err error) error { if s3Err, ok := err.(s3err.S3Error); ok { return c.Status(s3Err.StatusCode()).Send(s3Err.XMLBody("", "")) } @@ -124,7 +124,7 @@ func TestAuthorizePostObject_AnonymousRequest(t *testing.T) { app := postObjectTestApp( RootUserConfig{Access: "root", Secret: "rootsecret"}, "us-east-1", - func(c *fiber.Ctx) error { + func(c fiber.Ctx) error { gotResult = utils.ContextKeyObjectPostResult.Get(c).(PostObjectResult) gotAuthenticated = utils.ContextKeyAuthenticated.IsSet(c) return c.SendStatus(http.StatusOK) @@ -150,7 +150,7 @@ func TestAuthorizePostObject_AnonymousRequest_SetsPostObjectResult(t *testing.T) app := postObjectTestApp( RootUserConfig{Access: "root", Secret: "rootsecret"}, "us-east-1", - func(c *fiber.Ctx) error { + func(c fiber.Ctx) error { gotResult = utils.ContextKeyObjectPostResult.Get(c).(PostObjectResult) return c.SendStatus(http.StatusOK) }, @@ -194,7 +194,7 @@ func TestAuthorizePostObject_SignedRequest(t *testing.T) { app := postObjectTestApp( RootUserConfig{Access: accessKey, Secret: secretKey}, region, - func(c *fiber.Ctx) error { + func(c fiber.Ctx) error { gotAuthenticated = utils.ContextKeyAuthenticated.IsSet(c) return c.SendStatus(http.StatusOK) }, @@ -236,7 +236,7 @@ func TestAuthorizePostObject_SignedRequest_WrongSignature(t *testing.T) { app := postObjectTestApp( RootUserConfig{Access: accessKey, Secret: secretKey}, region, - func(c *fiber.Ctx) error { return c.SendStatus(http.StatusOK) }, + func(c fiber.Ctx) error { return c.SendStatus(http.StatusOK) }, ) body, boundary := buildMultipartBody(t, map[string]string{ @@ -259,7 +259,7 @@ func TestAuthorizePostObject_PartialAuthFields_ReturnsError(t *testing.T) { app := postObjectTestApp( RootUserConfig{Access: "root", Secret: "rootsecret"}, "us-east-1", - func(c *fiber.Ctx) error { return c.SendStatus(http.StatusOK) }, + func(c fiber.Ctx) error { return c.SendStatus(http.StatusOK) }, ) // Only algorithm is provided — credential, date, policy, signature absent. @@ -279,7 +279,7 @@ func TestAuthorizePostObject_InvalidContentType_ReturnsError(t *testing.T) { app := postObjectTestApp( RootUserConfig{Access: "root", Secret: "rootsecret"}, "us-east-1", - func(c *fiber.Ctx) error { return c.SendStatus(http.StatusOK) }, + func(c fiber.Ctx) error { return c.SendStatus(http.StatusOK) }, ) req, err := http.NewRequest(http.MethodPost, "/mybucket", strings.NewReader("body")) diff --git a/s3api/middlewares/presign-auth.go b/s3api/middlewares/presign-auth.go index 7e086aa1..ceae9bd3 100644 --- a/s3api/middlewares/presign-auth.go +++ b/s3api/middlewares/presign-auth.go @@ -18,7 +18,7 @@ import ( "io" "strconv" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" @@ -27,7 +27,7 @@ import ( func VerifyPresignedV4Signature(root RootUserConfig, iam auth.IAMService, region string, streamBody bool) fiber.Handler { acct := accounts{root: root, iam: iam} - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { // The bucket is public, no need to check this signature if utils.ContextKeyPublicBucket.IsSet(ctx) { return nil diff --git a/s3api/middlewares/public-bucket.go b/s3api/middlewares/public-bucket.go index 3a74bef1..28be6bed 100644 --- a/s3api/middlewares/public-bucket.go +++ b/s3api/middlewares/public-bucket.go @@ -20,7 +20,7 @@ import ( "io" "strings" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/metrics" @@ -31,7 +31,7 @@ import ( // AuthorizePublicBucketAccess checks if the bucket grants public // access to anonymous requesters func AuthorizePublicBucketAccess(be backend.Backend, s3action string, policyPermission auth.Action, permission auth.Permission, region string, streamBody bool) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { // skip for authenticated requests if utils.IsPresignedURLAuth(ctx) || ctx.Get("Authorization") != "" || utils.ContextKeyAuthenticated.IsSet(ctx) { return nil @@ -57,7 +57,7 @@ func AuthorizePublicBucketAccess(be backend.Backend, s3action string, policyPerm } bucket, object := parsePath(ctx.Path()) - err := auth.VerifyPublicAccess(ctx.Context(), be, policyPermission, permission, bucket, object) + err := auth.VerifyPublicAccess(ctx.RequestCtx(), be, policyPermission, permission, bucket, object) if err != nil { if s3action == metrics.ActionHeadBucket { // add the bucket region header for HeadBucket @@ -114,7 +114,7 @@ func AuthorizePublicBucketAccess(be backend.Backend, s3action string, policyPerm if payloadHash != "" { // Calculate the hash of the request payload - hashedPayload := sha256.Sum256(ctx.Body()) + hashedPayload := sha256.Sum256(ctx.BodyRaw()) hexPayload := hex.EncodeToString(hashedPayload[:]) // Compare the calculated hash with the hash provided diff --git a/s3api/middlewares/rate-limiter.go b/s3api/middlewares/rate-limiter.go index c34be084..14dc0a54 100644 --- a/s3api/middlewares/rate-limiter.go +++ b/s3api/middlewares/rate-limiter.go @@ -15,7 +15,7 @@ package middlewares import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/metrics" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" @@ -28,7 +28,7 @@ import ( func RateLimiter(limit int, mm metrics.Manager, logger s3log.AuditLogger) fiber.Handler { sem := semaphore.NewWeighted(int64(limit)) - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { requestID, hostID := utils.EnsureRequestIDs(ctx) if !sem.TryAcquire(1) { @@ -39,7 +39,7 @@ func RateLimiter(limit int, mm metrics.Manager, logger s3log.AuditLogger) fiber. mm.Send(ctx, err, metrics.ActionUndetected, 0, 0) } if logger != nil { - logger.Log(ctx, err, ctx.Body(), s3log.LogMeta{ + logger.Log(ctx, err, ctx.BodyRaw(), s3log.LogMeta{ Action: metrics.ActionUndetected, }) } diff --git a/s3api/middlewares/request-ids.go b/s3api/middlewares/request-ids.go index f4597692..ead123ff 100644 --- a/s3api/middlewares/request-ids.go +++ b/s3api/middlewares/request-ids.go @@ -15,13 +15,13 @@ package middlewares import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/s3api/utils" ) // RequestIDs sets requestID and hostID in context locals func RequestIDs() fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { utils.EnsureRequestIDs(ctx) return ctx.Next() } diff --git a/s3api/middlewares/request-ids_test.go b/s3api/middlewares/request-ids_test.go index 26683e1b..3f8b8539 100644 --- a/s3api/middlewares/request-ids_test.go +++ b/s3api/middlewares/request-ids_test.go @@ -20,15 +20,15 @@ import ( "regexp" "testing" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/stretchr/testify/assert" "github.com/versity/versitygw/s3api/utils" ) func TestRequestIDs(t *testing.T) { app := fiber.New() - app.Use(RequestIDs()) - app.Get("/", func(ctx *fiber.Ctx) error { + app.Use("*", RequestIDs()) + app.Get("/", func(ctx fiber.Ctx) error { assert.NotEmpty(t, utils.RequestID(ctx)) assert.NotEmpty(t, utils.HostID(ctx)) return ctx.SendStatus(http.StatusNoContent) diff --git a/s3api/middlewares/router-utilities.go b/s3api/middlewares/router-utilities.go index 47929103..0309de31 100644 --- a/s3api/middlewares/router-utilities.go +++ b/s3api/middlewares/router-utilities.go @@ -15,13 +15,13 @@ package middlewares import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/s3api/utils" ) // Evaluates/Matches the provided requst query params func MatchQueryArgs(args ...string) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { if utils.ContextKeySkip.IsSet(ctx) { return ctx.Next() } @@ -37,7 +37,7 @@ func MatchQueryArgs(args ...string) fiber.Handler { // Evaluates/Matches the requst header func MatchHeader(key string) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { if utils.ContextKeySkip.IsSet(ctx) { return ctx.Next() } @@ -53,7 +53,7 @@ func MatchHeader(key string) fiber.Handler { // Evaluates/Matches the requst query param and value func MatchQueryArgWithValue(key, val string) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { if utils.ContextKeySkip.IsSet(ctx) { return ctx.Next() } diff --git a/s3api/middlewares/set-default-keys.go b/s3api/middlewares/set-default-keys.go index e87cbbfe..38eb624c 100644 --- a/s3api/middlewares/set-default-keys.go +++ b/s3api/middlewares/set-default-keys.go @@ -17,13 +17,13 @@ package middlewares import ( "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" ) func SetDefaultValues(root RootUserConfig, region string) fiber.Handler { - return func(ctx *fiber.Ctx) error { + return func(ctx fiber.Ctx) error { // These are necessary for the server access logs utils.ContextKeyRegion.Set(ctx, region) utils.ContextKeyStartTime.Set(ctx, time.Now()) diff --git a/s3api/middlewares/url-decoder.go b/s3api/middlewares/url-decoder.go index 7c4b209b..ac618527 100644 --- a/s3api/middlewares/url-decoder.go +++ b/s3api/middlewares/url-decoder.go @@ -17,12 +17,12 @@ package middlewares import ( "net/url" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) // DecodeURL url path unescapes the request url for the gateway // to handle some special characters -func DecodeURL(ctx *fiber.Ctx) error { +func DecodeURL(ctx fiber.Ctx) error { unescp, err := url.PathUnescape(string(ctx.Request().URI().PathOriginal())) if err != nil { return err diff --git a/s3api/router.go b/s3api/router.go index 57e4352b..3ab8c66b 100644 --- a/s3api/router.go +++ b/s3api/router.go @@ -17,7 +17,7 @@ package s3api import ( "net/http" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/metrics" @@ -56,7 +56,7 @@ func (sa *S3ApiRouter) Init() { // initialize global host-style parser middleware if virtual domain is specified if sa.virtualDomain != "" { - sa.app.Use(middlewares.HostStyleParser(sa.virtualDomain)) + sa.app.Use("*", middlewares.HostStyleParser(sa.virtualDomain)) } if sa.WithAdmSrv { @@ -158,7 +158,7 @@ func (sa *S3ApiRouter) Init() { // copy source is not allowed on '/' sa.app.Get("/", middlewares.MatchHeader("X-Amz-Copy-Source"), controllers.ProcessHandlers( - func(ctx *fiber.Ctx) (*controllers.Response, error) { + func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, ctx.Get("X-Amz-Copy-Source")) }, metrics.ActionUndetected, @@ -475,7 +475,7 @@ func (sa *S3ApiRouter) Init() { // copy source is not allowed on bucket HEAD operation bucketRouter.Head("/", middlewares.MatchHeader("X-Amz-Copy-Source"), controllers.ProcessHandlers( - func(ctx *fiber.Ctx) (*controllers.Response, error) { + func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, ctx.Get("X-Amz-Copy-Source")) }, metrics.ActionUndetected, @@ -501,7 +501,7 @@ func (sa *S3ApiRouter) Init() { // copy source is not allowed on bucket DELETE operation bucketRouter.Delete("/", middlewares.MatchHeader("X-Amz-Copy-Source"), controllers.ProcessHandlers( - func(ctx *fiber.Ctx) (*controllers.Response, error) { + func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, ctx.Get("X-Amz-Copy-Source")) }, metrics.ActionUndetected, @@ -697,7 +697,7 @@ func (sa *S3ApiRouter) Init() { // copy source is not allowed on bucket GET operation bucketRouter.Get("/", middlewares.MatchHeader("X-Amz-Copy-Source"), controllers.ProcessHandlers( - func(ctx *fiber.Ctx) (*controllers.Response, error) { + func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, ctx.Get("X-Amz-Copy-Source")) }, metrics.ActionUndetected, @@ -1102,7 +1102,7 @@ func (sa *S3ApiRouter) Init() { middlewares.MatchHeader("X-Amz-Copy-Source"), middlewares.MatchQueryArgs("uploadId"), controllers.ProcessHandlers( - func(ctx *fiber.Ctx) (*controllers.Response, error) { + func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, ctx.Get("X-Amz-Copy-Source")) }, metrics.ActionUndetected, @@ -1142,7 +1142,7 @@ func (sa *S3ApiRouter) Init() { objectRouter.Head("/", middlewares.MatchHeader("X-Amz-Copy-Source"), controllers.ProcessHandlers( - func(ctx *fiber.Ctx) (*controllers.Response, error) { + func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, ctx.Get("X-Amz-Copy-Source")) }, metrics.ActionUndetected, @@ -1182,7 +1182,7 @@ func (sa *S3ApiRouter) Init() { objectRouter.Get("/", middlewares.MatchHeader("X-Amz-Copy-Source"), controllers.ProcessHandlers( - func(ctx *fiber.Ctx) (*controllers.Response, error) { + func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, ctx.Get("X-Amz-Copy-Source")) }, metrics.ActionUndetected, @@ -1287,7 +1287,7 @@ func (sa *S3ApiRouter) Init() { objectRouter.Delete("/", middlewares.MatchHeader("X-Amz-Copy-Source"), controllers.ProcessHandlers( - func(ctx *fiber.Ctx) (*controllers.Response, error) { + func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, ctx.Get("X-Amz-Copy-Source")) }, metrics.ActionUndetected, @@ -1341,7 +1341,7 @@ func (sa *S3ApiRouter) Init() { middlewares.MatchHeader("X-Amz-Copy-Source"), middlewares.MatchQueryArgs("uploadId"), controllers.ProcessHandlers( - func(ctx *fiber.Ctx) (*controllers.Response, error) { + func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetInvalidArgumentErr(s3err.InvalidArgCopySource, ctx.Get("X-Amz-Copy-Source")) }, metrics.ActionUndetected, @@ -1561,7 +1561,7 @@ func (sa *S3ApiRouter) Init() { ) // Return MethodNotAllowed for all the unmatched routes - sa.app.All("*", controllers.ProcessHandlers(func(ctx *fiber.Ctx) (*controllers.Response, error) { + sa.app.All("*", controllers.ProcessHandlers(func(ctx fiber.Ctx) (*controllers.Response, error) { return &controllers.Response{}, s3err.GetMethodNotAllowedErr(ctx.Method(), s3err.ResourceTypeService, nil) }, metrics.ActionUndetected, services)) } diff --git a/s3api/router_cors_test.go b/s3api/router_cors_test.go index 3b1f5b4f..a7e06897 100644 --- a/s3api/router_cors_test.go +++ b/s3api/router_cors_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/s3err" diff --git a/s3api/router_test.go b/s3api/router_test.go index 8c870075..576362ba 100644 --- a/s3api/router_test.go +++ b/s3api/router_test.go @@ -17,7 +17,7 @@ package s3api import ( "testing" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" ) diff --git a/s3api/server.go b/s3api/server.go index 7997fc33..efce34e5 100644 --- a/s3api/server.go +++ b/s3api/server.go @@ -23,9 +23,10 @@ import ( "strings" "time" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" - "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/logger" + "github.com/gofiber/fiber/v3/middleware/recover" + "github.com/valyala/fasthttp" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/debuglogger" @@ -40,7 +41,8 @@ import ( ) const ( - shutDownDuration = time.Second * 10 + shutDownDuration = time.Second * 10 + requestHeaderMaxSize = 8 * 1024 ) type S3ApiServer struct { @@ -102,25 +104,24 @@ func New( } app := fiber.New(fiber.Config{ - AppName: "versitygw", - ServerHeader: "VERSITYGW", - StreamRequestBody: true, - DisableKeepalive: !server.keepAlive, - Network: fiber.NetworkTCP, - DisableStartupMessage: true, - ErrorHandler: globalErrorHandler, - Concurrency: server.maxConnections, + AppName: "versitygw", + ServerHeader: "VERSITYGW", + StreamRequestBody: true, + DisableKeepalive: !server.keepAlive, + ErrorHandler: globalErrorHandler, + Concurrency: server.maxConnections, // Sets buffer limit to read/parse incoming requests // if the limit is reached, fiber/fasthttp will throw an error // in the global error handler - ReadBufferSize: 8 * 1024, // 8 KB + ReadBufferSize: requestHeaderMaxSize, }) + installRequestHeaderLimitErrorHandler(app) server.app = app server.Router.app = app // initialize the panic recovery middleware - app.Use(recover.New( + app.Use("*", recover.New( recover.Config{ EnableStackTrace: true, StackTraceHandler: stackTraceHandler, @@ -128,35 +129,41 @@ func New( // Logging middlewares if !server.quiet { - app.Use(logger.New(logger.Config{ + app.Use("*", logger.New(logger.Config{ Format: "${time} | vgw | ${status} | ${latency} | ${ip} | ${method} | ${path} | ${error} | ${queryParams}\n", })) } // initialize requestId middleware - app.Use(middlewares.RequestIDs()) + app.Use("*", middlewares.RequestIDs()) // Set up health endpoint if specified if server.health != "" { - app.Get(server.health, func(ctx *fiber.Ctx) error { + app.Get(server.health, func(ctx fiber.Ctx) error { return ctx.SendStatus(http.StatusOK) }) } // Set up WebUI on the S3 port if configured if server.webuiSrvCfg != nil { - webui.MountOn(app, server.webuiMountPrefix, server.webuiSrvCfg) + if err := webui.MountOn(app, server.webuiMountPrefix, server.webuiSrvCfg); err != nil { + return nil, fmt.Errorf("mount webui: %w", err) + } } // initialize total requests cap limiter middleware - app.Use(middlewares.RateLimiter(server.maxRequests, mm, l)) + app.Use("*", middlewares.RateLimiter(server.maxRequests, mm, l)) for _, route := range server.routes { method, err := validateRouteMount(route) if err != nil { return nil, err } - app.Add(method, route.path, route.handlers...) + handlers := make([]any, len(route.handlers)-1) + for i := range handlers { + handlers[i] = route.handlers[i+1] + } + app.Add([]string{method}, route.path, route.handlers[0], handlers...) } for _, mount := range server.middlewares { @@ -167,15 +174,15 @@ func New( } // initilaze the default value setter middleware - app.Use(middlewares.SetDefaultValues(root, region)) + app.Use("*", middlewares.SetDefaultValues(root, region)) // initialize the 'DecodeURL' middleware which // path unescapes the url - app.Use(controllers.WrapMiddleware(middlewares.DecodeURL, l, mm)) + app.Use("*", controllers.WrapMiddleware(middlewares.DecodeURL, l, mm)) // initialize the debug logger in debug mode if debuglogger.IsDebugEnabled() { - app.Use(middlewares.DebugLogger()) + app.Use("*", middlewares.DebugLogger()) } server.Router.Init() @@ -357,9 +364,9 @@ func (sa *S3ApiServer) ServeMultiPort(ports []string) error { var err error if sa.CertStorage != nil { - ln, err = utils.NewMultiAddrTLSListener(sa.app.Config().Network, portSpec, sa.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: sa.socketPerm}) + ln, err = utils.NewMultiAddrTLSListener(fiber.NetworkTCP, portSpec, sa.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: sa.socketPerm}) } else { - ln, err = utils.NewMultiAddrListener(sa.app.Config().Network, portSpec, utils.ListenerOptions{SocketPerm: sa.socketPerm}) + ln, err = utils.NewMultiAddrListener(fiber.NetworkTCP, portSpec, utils.ListenerOptions{SocketPerm: sa.socketPerm}) } if err != nil { return fmt.Errorf("failed to bind s3 listener %s: %w", portSpec, err) @@ -383,7 +390,9 @@ func (sa *S3ApiServer) ServeMultiPort(ports []string) error { }) } - return sa.app.Listener(finalListener) + return sa.app.Listener(finalListener, fiber.ListenConfig{ + DisableStartupMessage: true, + }) } // ShutDown gracefully shuts down the server with a context timeout @@ -393,13 +402,45 @@ func (sa *S3ApiServer) ShutDown() error { // stackTraceHandler stores the system panics // in the context locals -func stackTraceHandler(ctx *fiber.Ctx, e any) { +func stackTraceHandler(ctx fiber.Ctx, e any) { utils.ContextKeyStack.Set(ctx, e) } +// installRequestHeaderLimitErrorHandler converts fasthttp small-buffer errors +// into the S3 RequestHeaderSectionTooLarge response. +// +// This is a temporary solution until Fiber handles request header limit errors +// before response writes correctly. See: +// https://github.com/gofiber/fiber/issues/4423 +func installRequestHeaderLimitErrorHandler(app *fiber.App) { + server := app.Server() + fiberErrorHandler := server.ErrorHandler + server.ErrorHandler = func(ctx *fasthttp.RequestCtx, err error) { + var smallBufferErr *fasthttp.ErrSmallBuffer + if errors.As(err, &smallBufferErr) { + debuglogger.Logf("total request headers size exceeds the allowed 8KB") + + requestID := utils.NewS3RequestID() + hostID := utils.NewS3HostID() + apiErr := s3err.GetRequestHeaderSectionTooLargeErr(requestHeaderMaxSize) + + ctx.Response.Reset() + ctx.Response.Header.SetContentType(fiber.MIMEApplicationXML) + ctx.Response.Header.Set(utils.HeaderAmzRequestID, requestID) + ctx.Response.Header.Set(utils.HeaderAmzID2, hostID) + ctx.SetStatusCode(apiErr.StatusCode()) + ctx.SetConnectionClose() + ctx.SetBody(apiErr.XMLBody(requestID, hostID)) + return + } + + fiberErrorHandler(ctx, err) + } +} + // globalErrorHandler catches the errors before reaching to // the handlers and any system panics -func globalErrorHandler(ctx *fiber.Ctx, er error) error { +func globalErrorHandler(ctx fiber.Ctx, er error) error { requestID, hostID := utils.EnsureRequestIDs(ctx) // set content type to application/xml @@ -416,8 +457,8 @@ func globalErrorHandler(ctx *fiber.Ctx, er error) error { if errors.As(er, &fiberErr) { if errors.Is(fiberErr, fiber.ErrRequestHeaderFieldsTooLarge) { debuglogger.Logf("total request headers size exceeds the allowed 8KB") - ctx.Status(http.StatusBadRequest) - return nil + err := s3err.GetRequestHeaderSectionTooLargeErr(requestHeaderMaxSize) + return ctx.Status(err.StatusCode()).Send(err.XMLBody(requestID, hostID)) } if strings.Contains(fiberErr.Message, "cannot parse Content-Length") { debuglogger.Logf("failed to parse Content-Length") diff --git a/s3api/server_test.go b/s3api/server_test.go index f31a3275..c76999f0 100644 --- a/s3api/server_test.go +++ b/s3api/server_test.go @@ -20,8 +20,9 @@ import ( "strings" "sync" "testing" + "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/s3api/middlewares" @@ -87,10 +88,10 @@ func TestWithRouteRegistersBeforeMiddleware(t *testing.T) { middlewareCalled := false server, err := newTestS3ApiServer( - WithRoute(http.MethodGet, routePath, func(ctx *fiber.Ctx) error { + WithRoute(http.MethodGet, routePath, func(ctx fiber.Ctx) error { return ctx.SendStatus(http.StatusNoContent) }), - WithMiddleware("/", func(ctx *fiber.Ctx) error { + WithMiddleware("/", func(ctx fiber.Ctx) error { middlewareCalled = true return ctx.SendStatus(http.StatusMisdirectedRequest) }), @@ -126,7 +127,7 @@ func TestWithRouteRegistersAfterRateLimiter(t *testing.T) { server, err := newTestS3ApiServer( WithConcurrencyLimiter(10, 1), - WithRoute(http.MethodGet, routePath, func(ctx *fiber.Ctx) error { + WithRoute(http.MethodGet, routePath, func(ctx fiber.Ctx) error { once.Do(func() { close(started) }) @@ -139,7 +140,7 @@ func TestWithRouteRegistersAfterRateLimiter(t *testing.T) { } go func() { - resp, err := server.app.Test(httptest.NewRequest(http.MethodGet, routePath, nil), -1) + resp, err := server.app.Test(httptest.NewRequest(http.MethodGet, routePath, nil), fiber.TestConfig{Timeout: 0, FailOnTimeout: false}) if err != nil { firstDone <- err return @@ -154,7 +155,7 @@ func TestWithRouteRegistersAfterRateLimiter(t *testing.T) { <-started - resp, err := server.app.Test(httptest.NewRequest(http.MethodGet, routePath, nil), 100) + resp, err := server.app.Test(httptest.NewRequest(http.MethodGet, routePath, nil), fiber.TestConfig{Timeout: time.Duration(100) * time.Millisecond}) if err != nil { close(release) t.Fatalf("second app.Test() error = %v", err) @@ -172,7 +173,7 @@ func TestWithRouteRegistersAfterRateLimiter(t *testing.T) { } func TestCustomMountValidation(t *testing.T) { - validHandler := func(ctx *fiber.Ctx) error { + validHandler := func(ctx fiber.Ctx) error { return ctx.SendStatus(http.StatusNoContent) } diff --git a/s3api/utils/auth-reader.go b/s3api/utils/auth-reader.go index 6f456c88..c2c5c9dc 100644 --- a/s3api/utils/auth-reader.go +++ b/s3api/utils/auth-reader.go @@ -26,7 +26,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/smithy-go/logging" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" v4 "github.com/versity/versitygw/aws/signer/v4" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3err" @@ -53,7 +53,7 @@ const ( ) // CheckValidSignature validates the ctx v4 auth signature -func CheckValidSignature(ctx *fiber.Ctx, auth AuthData, secret, checksum string, tdate time.Time, contentLen int64) (string, error) { +func CheckValidSignature(ctx fiber.Ctx, auth AuthData, secret, checksum string, tdate time.Time, contentLen int64) (string, error) { signedHdrs := strings.Split(auth.SignedHeaders, ";") // Create a new http request instance from fasthttp request diff --git a/s3api/utils/auth_test.go b/s3api/utils/auth_test.go index 20085b00..7997b257 100644 --- a/s3api/utils/auth_test.go +++ b/s3api/utils/auth_test.go @@ -20,7 +20,8 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" + "github.com/valyala/fasthttp" "github.com/valyala/fasthttp/fasthttputil" v4 "github.com/versity/versitygw/aws/signer/v4" ) @@ -84,14 +85,14 @@ func Test_Client_UserAgent(t *testing.T) { expectedSig := "37a35d96998d786113ad420c57c22c5433f6aca74f88f26566caa047fc3601c6" dateStr := "20240206T210328Z" - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New(fiber.Config{}) tdate, err := time.Parse(iso8601Format, dateStr) if err != nil { t.Fatal(err) } - app.Get("/", func(c *fiber.Ctx) error { + app.Get("/", func(c fiber.Ctx) error { req, err := createHttpRequestFromCtx(c, signedHdrs, int64(c.Request().Header.ContentLength())) if err != nil { t.Fatal(err) @@ -135,10 +136,18 @@ func Test_Client_UserAgent(t *testing.T) { } }() - c := fiber.AcquireClient() - c.UserAgent = agent - a := c.Get("http://example.com") - a.HostClient.Dial = func(_ string) (net.Conn, error) { return ln.Dial() } - a.String() - fiber.ReleaseClient(c) + client := fasthttp.Client{ + Dial: func(_ string) (net.Conn, error) { return ln.Dial() }, + } + + req := fasthttp.AcquireRequest() + resp := fasthttp.AcquireResponse() + defer fasthttp.ReleaseRequest(req) + defer fasthttp.ReleaseResponse(resp) + + req.SetRequestURI("http://example.com") + req.Header.SetUserAgent(agent) + if err := client.Do(req, resp); err != nil { + t.Fatal(err) + } } diff --git a/s3api/utils/chunk-reader.go b/s3api/utils/chunk-reader.go index 038a6273..62f5d6e6 100644 --- a/s3api/utils/chunk-reader.go +++ b/s3api/utils/chunk-reader.go @@ -23,7 +23,7 @@ import ( "strings" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3err" ) @@ -100,7 +100,7 @@ func (c checksumType) isValid() bool { } // Extracts and validates the checksum type from the 'X-Amz-Trailer' header -func ExtractChecksumType(ctx *fiber.Ctx) (checksumType, error) { +func ExtractChecksumType(ctx fiber.Ctx) (checksumType, error) { trailer := ctx.Get("X-Amz-Trailer") chType := checksumType(strings.ToLower(trailer)) if chType != "" && !chType.isValid() { @@ -172,7 +172,7 @@ func IsStreamingPayload(str string) bool { // ParseDecodedContentLength extracts and validates the // 'x-amz-decoded-content-length' from fiber context -func ParseDecodedContentLength(ctx *fiber.Ctx) (int64, error) { +func ParseDecodedContentLength(ctx fiber.Ctx) (int64, error) { decContLengthStr := ctx.Get("X-Amz-Decoded-Content-Length") if decContLengthStr == "" { debuglogger.Logf("missing required header 'X-Amz-Decoded-Content-Length'") @@ -192,7 +192,7 @@ func ParseDecodedContentLength(ctx *fiber.Ctx) (int64, error) { return decContLength, nil } -func NewChunkReader(ctx *fiber.Ctx, r io.Reader, authdata AuthData, canonicalString, secret string, date time.Time) (io.Reader, error) { +func NewChunkReader(ctx fiber.Ctx, r io.Reader, authdata AuthData, canonicalString, secret string, date time.Time) (io.Reader, error) { cLength, err := ParseDecodedContentLength(ctx) if err != nil { return nil, err diff --git a/s3api/utils/context-keys.go b/s3api/utils/context-keys.go index 445531c3..96e84273 100644 --- a/s3api/utils/context-keys.go +++ b/s3api/utils/context-keys.go @@ -15,7 +15,7 @@ package utils import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) // Region, StartTime, IsRoot, Account, AccessKey context locals @@ -43,19 +43,19 @@ const ( ContextKeyWebsiteConfig ContextKey = "website-config" ) -func (ck ContextKey) Set(ctx *fiber.Ctx, val any) { +func (ck ContextKey) Set(ctx fiber.Ctx, val any) { ctx.Locals(string(ck), val) } -func (ck ContextKey) IsSet(ctx *fiber.Ctx) bool { +func (ck ContextKey) IsSet(ctx fiber.Ctx) bool { val := ctx.Locals(string(ck)) return val != nil } -func (ck ContextKey) Delete(ctx *fiber.Ctx) { +func (ck ContextKey) Delete(ctx fiber.Ctx) { ctx.Locals(string(ck), nil) } -func (ck ContextKey) Get(ctx *fiber.Ctx) any { +func (ck ContextKey) Get(ctx fiber.Ctx) any { return ctx.Locals(string(ck)) } diff --git a/s3api/utils/precondition.go b/s3api/utils/precondition.go index 52a857b1..f7dc9b01 100644 --- a/s3api/utils/precondition.go +++ b/s3api/utils/precondition.go @@ -18,7 +18,7 @@ import ( "strconv" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/debuglogger" ) @@ -45,7 +45,7 @@ func WithCopySource() preconditionOpt { // - If-None-Match // - If-Modified-Since // - If-Unmodified-Since -func ParsePreconditionHeaders(ctx *fiber.Ctx, opts ...preconditionOpt) ConditionalHeaders { +func ParsePreconditionHeaders(ctx fiber.Ctx, opts ...preconditionOpt) ConditionalHeaders { ifMatch, ifNoneMatch := ParsePreconditionMatchHeaders(ctx, opts...) ifModSince, ifUnmodeSince := ParsePreconditionDateHeaders(ctx, opts...) @@ -58,7 +58,7 @@ func ParsePreconditionHeaders(ctx *fiber.Ctx, opts ...preconditionOpt) Condition } // ParsePreconditionMatchHeaders extracts "If-Match" and "If-None-Match" headers from fiber Ctx -func ParsePreconditionMatchHeaders(ctx *fiber.Ctx, opts ...preconditionOpt) (*string, *string) { +func ParsePreconditionMatchHeaders(ctx fiber.Ctx, opts ...preconditionOpt) (*string, *string) { cfg := new(precondtionCfg) for _, opt := range opts { opt(cfg) @@ -75,7 +75,7 @@ func ParsePreconditionMatchHeaders(ctx *fiber.Ctx, opts ...preconditionOpt) (*st // ParsePreconditionDateHeaders parses the "If-Modified-Since" and "If-Unmodified-Since" // headers from fiber context to *time.Time -func ParsePreconditionDateHeaders(ctx *fiber.Ctx, opts ...preconditionOpt) (*time.Time, *time.Time) { +func ParsePreconditionDateHeaders(ctx fiber.Ctx, opts ...preconditionOpt) (*time.Time, *time.Time) { cfg := new(precondtionCfg) for _, opt := range opts { opt(cfg) @@ -129,7 +129,7 @@ func ParsePreconditionDateHeader(date string) *time.Time { // ParseIfMatchSize parses the 'x-amz-if-match-size' to *int64 // if parsing fails, returns nil -func ParseIfMatchSize(ctx *fiber.Ctx) *int64 { +func ParseIfMatchSize(ctx fiber.Ctx) *int64 { ifMatchSizeHdr := ctx.Get("x-amz-if-match-size") if ifMatchSizeHdr == "" { return nil diff --git a/s3api/utils/presign-auth-reader.go b/s3api/utils/presign-auth-reader.go index 2b438ca6..57db3e3f 100644 --- a/s3api/utils/presign-auth-reader.go +++ b/s3api/utils/presign-auth-reader.go @@ -24,7 +24,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/smithy-go/logging" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" v4 "github.com/versity/versitygw/aws/signer/v4" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3err" @@ -38,7 +38,7 @@ const ( ) // CheckPresignedSignature validates presigned request signature -func CheckPresignedSignature(ctx *fiber.Ctx, auth AuthData, secret string) error { +func CheckPresignedSignature(ctx fiber.Ctx, auth AuthData, secret string) error { signedHdrs := strings.Split(auth.SignedHeaders, ";") var contentLength int64 @@ -60,7 +60,7 @@ func CheckPresignedSignature(ctx *fiber.Ctx, auth AuthData, secret string) error date, _ := time.Parse(iso8601Format, auth.Date) signer := v4.NewSigner() - uri, _, signMeta, signErr := signer.PresignHTTP(ctx.Context(), aws.Credentials{ + uri, _, signMeta, signErr := signer.PresignHTTP(ctx.RequestCtx(), aws.Credentials{ AccessKeyID: auth.Access, SecretAccessKey: secret, }, req, unsignedPayload, service, auth.Region, date, signedHdrs, func(options *v4.SignerOptions) { @@ -104,7 +104,7 @@ func CheckPresignedSignature(ctx *fiber.Ctx, auth AuthData, secret string) error // &X-Amz-Expires=86400 // &X-Amz-SignedHeaders=host // &X-Amz-Signature=1e68ad45c1db540284a4a1eca3884c293ba1a0ff63ab9db9a15b5b29dfa02cd8 -func ParsePresignedURIParts(ctx *fiber.Ctx, region string) (AuthData, error) { +func ParsePresignedURIParts(ctx fiber.Ctx, region string) (AuthData, error) { a := AuthData{} // Get and verify algorithm query parameter @@ -218,7 +218,7 @@ func validateAlgorithm(algo string) error { // IsPresignedURLAuth determines if the request is presigned: // which is authorization with query params -func IsPresignedURLAuth(ctx *fiber.Ctx) bool { +func IsPresignedURLAuth(ctx fiber.Ctx) bool { algo := ctx.Query("X-Amz-Algorithm") creds := ctx.Query("X-Amz-Credential") signature := ctx.Query("X-Amz-Signature") @@ -230,7 +230,7 @@ func IsPresignedURLAuth(ctx *fiber.Ctx) bool { // IsPresignedURLAuthV2 determines if the request is // query-string signed with aws v2 signer -func IsPresignedURLAuthV2(ctx *fiber.Ctx) bool { +func IsPresignedURLAuthV2(ctx fiber.Ctx) bool { expires := ctx.Query("Expires") access := ctx.Query("AWSAccessKeyId") signature := ctx.Query("Signature") diff --git a/s3api/utils/request_ids.go b/s3api/utils/request_ids.go index 77630e31..4a8d366a 100644 --- a/s3api/utils/request_ids.go +++ b/s3api/utils/request_ids.go @@ -18,7 +18,7 @@ import ( "crypto/rand" "encoding/base64" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/debuglogger" ) @@ -70,7 +70,7 @@ func NewS3HostID() string { // EnsureRequestIDs makes sure the request-local IDs exist and are present // in the response headers. Existing local values are reused so headers and XML // bodies stay consistent throughout the request. -func EnsureRequestIDs(ctx *fiber.Ctx) (requestID, hostID string) { +func EnsureRequestIDs(ctx fiber.Ctx) (requestID, hostID string) { requestID = RequestID(ctx) if requestID == "" { requestID = NewS3RequestID() @@ -89,7 +89,7 @@ func EnsureRequestIDs(ctx *fiber.Ctx) (requestID, hostID string) { return requestID, hostID } -func RequestID(ctx *fiber.Ctx) string { +func RequestID(ctx fiber.Ctx) string { requestID, _ := ContextKeyRequestID.Get(ctx).(string) if requestID != "" { return requestID @@ -98,7 +98,7 @@ func RequestID(ctx *fiber.Ctx) string { return string(ctx.Response().Header.Peek(HeaderAmzRequestID)) } -func HostID(ctx *fiber.Ctx) string { +func HostID(ctx fiber.Ctx) string { hostID, _ := ContextKeyHostID.Get(ctx).(string) if hostID != "" { return hostID diff --git a/s3api/utils/request_ids_test.go b/s3api/utils/request_ids_test.go index d2822ebd..104c2892 100644 --- a/s3api/utils/request_ids_test.go +++ b/s3api/utils/request_ids_test.go @@ -19,7 +19,7 @@ import ( "regexp" "testing" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/stretchr/testify/assert" "github.com/valyala/fasthttp" ) diff --git a/s3api/utils/signed_headers_test.go b/s3api/utils/signed_headers_test.go index f40159e9..12a7db98 100644 --- a/s3api/utils/signed_headers_test.go +++ b/s3api/utils/signed_headers_test.go @@ -21,7 +21,7 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/aws" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" v4 "github.com/versity/versitygw/aws/signer/v4" @@ -151,7 +151,7 @@ func buildPresignedURL(t *testing.T, headers http.Header) string { return signedURL } -func signedHeaderAuthCtx(t *testing.T, signedHeaders, extraHeaders http.Header) (*fiber.Ctx, AuthData, time.Time) { +func signedHeaderAuthCtx(t *testing.T, signedHeaders, extraHeaders http.Header) (fiber.Ctx, AuthData, time.Time) { t.Helper() signingTime := time.Now().UTC() @@ -192,7 +192,7 @@ func signedHeaderAuthCtx(t *testing.T, signedHeaders, extraHeaders http.Header) return ctx, authData, signingTime } -func fiberCtxFromURL(t *testing.T, method, rawURL string, headers http.Header) *fiber.Ctx { +func fiberCtxFromURL(t *testing.T, method, rawURL string, headers http.Header) fiber.Ctx { t.Helper() parsedURL, err := url.Parse(rawURL) diff --git a/s3api/utils/utils.go b/s3api/utils/utils.go index 150b12e9..e3375575 100644 --- a/s3api/utils/utils.go +++ b/s3api/utils/utils.go @@ -32,7 +32,7 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/valyala/fasthttp" signerV4 "github.com/versity/versitygw/aws/signer/v4" "github.com/versity/versitygw/debuglogger" @@ -135,7 +135,7 @@ func ExtractMetadataFromFields(fields map[string]string) (map[string]string, err return metadata, nil } -func createHttpRequestFromCtx(ctx *fiber.Ctx, signedHdrs []string, contentLength int64) (*http.Request, error) { +func createHttpRequestFromCtx(ctx fiber.Ctx, signedHdrs []string, contentLength int64) (*http.Request, error) { req := ctx.Request() uri := ctx.OriginalURL() @@ -180,7 +180,7 @@ var ( } ) -func createPresignedHttpRequestFromCtx(ctx *fiber.Ctx, signedHdrs []string, contentLength int64) (*http.Request, error) { +func createPresignedHttpRequestFromCtx(ctx fiber.Ctx, signedHdrs []string, contentLength int64) (*http.Request, error) { req := ctx.Request() uri, _, _ := strings.Cut(ctx.OriginalURL(), "?") @@ -221,7 +221,7 @@ func createPresignedHttpRequestFromCtx(ctx *fiber.Ctx, signedHdrs []string, cont return httpReq, nil } -func SetMetaHeaders(ctx *fiber.Ctx, meta map[string]string) { +func SetMetaHeaders(ctx fiber.Ctx, meta map[string]string) { ctx.Response().Header.DisableNormalizing() for key, val := range meta { ctx.Response().Header.Set(fmt.Sprintf("x-amz-meta-%s", key), val) @@ -311,17 +311,17 @@ type CustomHeader struct { Value string } -func SetResponseHeaders(ctx *fiber.Ctx, headers []CustomHeader) { +func SetResponseHeaders(ctx fiber.Ctx, headers []CustomHeader) { for _, header := range headers { ctx.Set(header.Key, header.Value) } } // Streams the response body by chunks -func StreamResponseBody(ctx *fiber.Ctx, rdr io.ReadCloser, bodysize int) { +func StreamResponseBody(ctx fiber.Ctx, rdr io.ReadCloser, bodysize int) { // SetBodyStream will call Close() on the reader when the stream is done // since rdr is a ReadCloser - ctx.Context().SetBodyStream(rdr, bodysize) + ctx.RequestCtx().SetBodyStream(rdr, bodysize) } func IsValidBucketName(bucket string) bool { @@ -353,7 +353,7 @@ func includeHeader(hdr string, signedHdrs []string) bool { }) } -func addRequestHeadersFromCtx(ctx *fiber.Ctx, httpReq *http.Request, signedHdrs []string) error { +func addRequestHeadersFromCtx(ctx fiber.Ctx, httpReq *http.Request, signedHdrs []string) error { headersNotSigned := []string{} for key, value := range ctx.Request().Header.All() { keyStr := string(key) @@ -424,7 +424,7 @@ func FilterObjectAttributes(attrs map[s3response.ObjectAttributes]struct{}, outp return output } -func ParseObjectAttributes(ctx *fiber.Ctx) (map[s3response.ObjectAttributes]struct{}, error) { +func ParseObjectAttributes(ctx fiber.Ctx) (map[s3response.ObjectAttributes]struct{}, error) { attrs := map[s3response.ObjectAttributes]struct{}{} var err error for key, value := range ctx.Request().Header.All() { @@ -463,7 +463,7 @@ type objLockCfg struct { LegalHoldStatus types.ObjectLockLegalHoldStatus } -func ParsObjectLockHdrs(ctx *fiber.Ctx) (*objLockCfg, error) { +func ParsObjectLockHdrs(ctx fiber.Ctx) (*objLockCfg, error) { legalHoldHdr := ctx.Get("X-Amz-Object-Lock-Legal-Hold") objLockModeHdr := ctx.Get("X-Amz-Object-Lock-Mode") objLockDate := ctx.Get("X-Amz-Object-Lock-Retain-Until-Date") @@ -549,7 +549,7 @@ func (cv ChecksumValues) Headers() string { // ParseCalculatedChecksumHeaders parses and validates x-amz-checksum-x header keys // e.g x-amz-checksum-crc32, x-amz-checksum-sha256 ... -func ParseCalculatedChecksumHeaders(ctx *fiber.Ctx) (ChecksumValues, error) { +func ParseCalculatedChecksumHeaders(ctx fiber.Ctx) (ChecksumValues, error) { checksums := ChecksumValues{} var hdrErr error @@ -639,7 +639,7 @@ func ParseCalculatedChecksumFields(fields map[string]string) (ChecksumValues, er // ParseCompleteMpChecksumHeaders parses and validates // the 'CompleteMultipartUpload' x-amz-checksum-x headers // by supporting both 'checksum' and 'checksum-' formats -func ParseCompleteMpChecksumHeaders(ctx *fiber.Ctx) (ChecksumValues, error) { +func ParseCompleteMpChecksumHeaders(ctx fiber.Ctx) (ChecksumValues, error) { // first parse/validate 'x-amz-checksum-x' headers checksums, err := ParseCalculatedChecksumHeaders(ctx) if err != nil { @@ -673,7 +673,7 @@ func ParseCompleteMpChecksumHeaders(ctx *fiber.Ctx) (ChecksumValues, error) { // ParseChecksumHeadersAndSdkAlgo parses/validates 'x-amz-sdk-checksum-algorithm' and // 'x-amz-checksum-x' precalculated request headers -func ParseChecksumHeadersAndSdkAlgo(ctx *fiber.Ctx) (types.ChecksumAlgorithm, ChecksumValues, error) { +func ParseChecksumHeadersAndSdkAlgo(ctx fiber.Ctx) (types.ChecksumAlgorithm, ChecksumValues, error) { sdkAlgorithm := types.ChecksumAlgorithm(strings.ToUpper(ctx.Get("X-Amz-Sdk-Checksum-Algorithm"))) err := IsChecksumAlgorithmValid(sdkAlgorithm) if err != nil { @@ -862,7 +862,7 @@ func checkChecksumTypeAndAlgo(algo types.ChecksumAlgorithm, t types.ChecksumType } // Parses and validates the x-amz-checksum-algorithm and x-amz-checksum-type headers -func ParseCreateMpChecksumHeaders(ctx *fiber.Ctx) (types.ChecksumAlgorithm, types.ChecksumType, error) { +func ParseCreateMpChecksumHeaders(ctx fiber.Ctx) (types.ChecksumAlgorithm, types.ChecksumType, error) { algo := types.ChecksumAlgorithm(strings.ToUpper(ctx.Get("x-amz-checksum-algorithm"))) if err := IsChecksumAlgorithmValid(algo); err != nil { return "", "", err @@ -1071,7 +1071,7 @@ func ValidateCopySource(input string) error { } // GetQueryParam returns a pointer to the query parameter value if it exists -func GetQueryParam(ctx *fiber.Ctx, key string) *string { +func GetQueryParam(ctx fiber.Ctx, key string) *string { value := ctx.Query(key) if value == "" { return nil @@ -1089,9 +1089,9 @@ func ApplyOverride(original, override *string) *string { // GenerateObjectLocation generates the object location path-styled or host-styled // depending on the gateway configuration -func GenerateObjectLocation(ctx *fiber.Ctx, virtualDomain, bucket, object string) string { - scheme := ctx.Protocol() - host := ctx.Hostname() +func GenerateObjectLocation(ctx fiber.Ctx, virtualDomain, bucket, object string) string { + scheme := ctx.Scheme() + host := ctx.Host() // escape the object name obj := url.PathEscape(object) @@ -1146,7 +1146,7 @@ func NewTLSListener(network string, address string, getCertificateFunc func(*tls return tls.NewListener(ln, config), nil } -func DetectResourceType(ctx *fiber.Ctx) s3err.ResourceType { +func DetectResourceType(ctx fiber.Ctx) s3err.ResourceType { path := ctx.Path() if path == "" || path == "/" { return s3err.ResourceTypeService diff --git a/s3api/utils/utils_test.go b/s3api/utils/utils_test.go index bd6f0402..d496997a 100644 --- a/s3api/utils/utils_test.go +++ b/s3api/utils/utils_test.go @@ -28,7 +28,7 @@ import ( "time" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/stretchr/testify/assert" "github.com/valyala/fasthttp" "github.com/versity/versitygw/backend" @@ -38,7 +38,7 @@ import ( func TestCreateHttpRequestFromCtx(t *testing.T) { type args struct { - ctx *fiber.Ctx + ctx fiber.Ctx } app := fiber.New() diff --git a/s3err/request-header-section-too-large-error.go b/s3err/request-header-section-too-large-error.go new file mode 100644 index 00000000..a331217a --- /dev/null +++ b/s3err/request-header-section-too-large-error.go @@ -0,0 +1,60 @@ +// Copyright 2026 Versity Software +// This file is licensed under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package s3err + +import "encoding/xml" + +// RequestHeaderSectionTooLargeError is returned when the full HTTP request +// header section exceeds the server read buffer. +// Produces a field in the XML response. +type RequestHeaderSectionTooLargeError struct { + APIError + MaxSizeAllowed int +} + +func (e RequestHeaderSectionTooLargeError) XMLBody(requestID, hostID string) []byte { + return encodeResponse(struct { + XMLName xml.Name `xml:"Error"` + Code string + Message string + MaxSizeAllowed int `xml:",omitempty"` + RequestID string `xml:"RequestId,omitempty"` + HostID string `xml:"HostId,omitempty"` + }{ + Code: e.Code, + Message: e.Description, + MaxSizeAllowed: e.MaxSizeAllowed, + RequestID: requestID, + HostID: hostID, + }) +} + +func (e RequestHeaderSectionTooLargeError) HTMLBody(requestID, hostID string) []byte { + return e.APIError.encodeHTMLResponse(requestID, hostID, + ErrorField{Name: "MaxSizeAllowed", Value: e.MaxSizeAllowed}, + ) +} + +func (e RequestHeaderSectionTooLargeError) Is(target error) bool { + t, ok := target.(APIError) + return ok && e.APIError == t +} + +func GetRequestHeaderSectionTooLargeErr(maxSizeAllowed int) RequestHeaderSectionTooLargeError { + return RequestHeaderSectionTooLargeError{ + APIError: GetAPIError(ErrRequestHeaderSectionTooLarge), + MaxSizeAllowed: maxSizeAllowed, + } +} diff --git a/s3err/s3err.go b/s3err/s3err.go index b994d23c..1b1348ff 100644 --- a/s3err/s3err.go +++ b/s3err/s3err.go @@ -182,6 +182,7 @@ const ( ErrInvalidChunkSize ErrSlowDown ErrMetadataTooLarge + ErrRequestHeaderSectionTooLarge ErrUnsupportedAuthorizationMechanism ErrNoBucketInRequest @@ -706,6 +707,11 @@ var errorCodeResponse = map[ErrorCode]APIError{ Description: "Your metadata headers exceed the maximum allowed metadata size", HTTPStatusCode: http.StatusBadRequest, }, + ErrRequestHeaderSectionTooLarge: { + Code: "RequestHeaderSectionTooLarge", + Description: "Your request header section exceeds the maximum allowed size.", + HTTPStatusCode: http.StatusBadRequest, + }, ErrUnsupportedAuthorizationMechanism: { Code: "InvalidRequest", Description: "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.", diff --git a/s3event/event.go b/s3event/event.go index 2e6f53ac..c3d69731 100644 --- a/s3event/event.go +++ b/s3event/event.go @@ -20,13 +20,13 @@ import ( "strings" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" ) type S3EventSender interface { - SendEvent(ctx *fiber.Ctx, meta EventMeta) + SendEvent(ctx fiber.Ctx, meta EventMeta) Close() error } @@ -147,7 +147,7 @@ func InitEventSender(cfg *EventConfig) (S3EventSender, error) { return evSender, err } -func createEventSchema(ctx *fiber.Ctx, meta EventMeta, configId ConfigurationId) EventSchema { +func createEventSchema(ctx fiber.Ctx, meta EventMeta, configId ConfigurationId) EventSchema { path := strings.Split(ctx.Path(), "/") var bucket, object string diff --git a/s3event/kafka.go b/s3event/kafka.go index 257c1c44..bbd50664 100644 --- a/s3event/kafka.go +++ b/s3event/kafka.go @@ -23,7 +23,7 @@ import ( "sync" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/segmentio/kafka-go" "github.com/versity/versitygw/s3response" ) @@ -73,7 +73,7 @@ func InitKafkaEventService(url, topic, key string, filter EventFilter) (S3EventS }, nil } -func (ks *Kafka) SendEvent(ctx *fiber.Ctx, meta EventMeta) { +func (ks *Kafka) SendEvent(ctx fiber.Ctx, meta EventMeta) { ks.mu.Lock() defer ks.mu.Unlock() @@ -84,7 +84,7 @@ func (ks *Kafka) SendEvent(ctx *fiber.Ctx, meta EventMeta) { if meta.EventName == EventObjectRemovedDeleteObjects { var dObj s3response.DeleteObjects - if err := xml.Unmarshal(ctx.Body(), &dObj); err != nil { + if err := xml.Unmarshal(ctx.BodyRaw(), &dObj); err != nil { fmt.Fprintf(os.Stderr, "failed to parse delete objects input payload: %v\n", err.Error()) return } diff --git a/s3event/nats.go b/s3event/nats.go index 189105aa..6ae3e621 100644 --- a/s3event/nats.go +++ b/s3event/nats.go @@ -21,7 +21,7 @@ import ( "os" "sync" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/nats-io/nats.go" "github.com/versity/versitygw/s3response" ) @@ -60,7 +60,7 @@ func InitNatsEventService(url, topic string, filter EventFilter) (S3EventSender, }, nil } -func (ns *NatsEventSender) SendEvent(ctx *fiber.Ctx, meta EventMeta) { +func (ns *NatsEventSender) SendEvent(ctx fiber.Ctx, meta EventMeta) { ns.mu.Lock() defer ns.mu.Unlock() @@ -71,7 +71,7 @@ func (ns *NatsEventSender) SendEvent(ctx *fiber.Ctx, meta EventMeta) { if meta.EventName == EventObjectRemovedDeleteObjects { var dObj s3response.DeleteObjects - if err := xml.Unmarshal(ctx.Body(), &dObj); err != nil { + if err := xml.Unmarshal(ctx.BodyRaw(), &dObj); err != nil { fmt.Fprintf(os.Stderr, "failed to parse delete objects input payload: %v\n", err.Error()) return } diff --git a/s3event/rabbitmq.go b/s3event/rabbitmq.go index fb35f113..8c38f444 100644 --- a/s3event/rabbitmq.go +++ b/s3event/rabbitmq.go @@ -22,7 +22,7 @@ import ( "sync" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/google/uuid" amqp "github.com/rabbitmq/amqp091-go" "github.com/versity/versitygw/s3response" @@ -87,7 +87,7 @@ func InitRabbitmqEventService(url, exchange, routingKey string, filter EventFilt }, nil } -func (rs *RabbitmqEventSender) SendEvent(ctx *fiber.Ctx, meta EventMeta) { +func (rs *RabbitmqEventSender) SendEvent(ctx fiber.Ctx, meta EventMeta) { rs.mu.Lock() defer rs.mu.Unlock() @@ -97,7 +97,7 @@ func (rs *RabbitmqEventSender) SendEvent(ctx *fiber.Ctx, meta EventMeta) { if meta.EventName == EventObjectRemovedDeleteObjects { var dObj s3response.DeleteObjects - if err := xml.Unmarshal(ctx.Body(), &dObj); err != nil { + if err := xml.Unmarshal(ctx.BodyRaw(), &dObj); err != nil { fmt.Fprintf(os.Stderr, "failed to parse delete objects input payload: %v\n", err.Error()) return } diff --git a/s3event/webhook.go b/s3event/webhook.go index bce571c6..327d6b2f 100644 --- a/s3event/webhook.go +++ b/s3event/webhook.go @@ -25,7 +25,7 @@ import ( "sync" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/s3response" ) @@ -72,7 +72,7 @@ func InitWebhookEventSender(url string, filter EventFilter) (S3EventSender, erro }, nil } -func (w *Webhook) SendEvent(ctx *fiber.Ctx, meta EventMeta) { +func (w *Webhook) SendEvent(ctx fiber.Ctx, meta EventMeta) { w.mu.Lock() defer w.mu.Unlock() @@ -83,7 +83,7 @@ func (w *Webhook) SendEvent(ctx *fiber.Ctx, meta EventMeta) { if meta.EventName == EventObjectRemovedDeleteObjects { var dObj s3response.DeleteObjects - if err := xml.Unmarshal(ctx.Body(), &dObj); err != nil { + if err := xml.Unmarshal(ctx.BodyRaw(), &dObj); err != nil { fmt.Fprintf(os.Stderr, "failed to parse delete objects input payload: %v\n", err.Error()) return } diff --git a/s3log/audit-logger.go b/s3log/audit-logger.go index 2d655fbd..50cef248 100644 --- a/s3log/audit-logger.go +++ b/s3log/audit-logger.go @@ -19,11 +19,11 @@ import ( "fmt" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" ) type AuditLogger interface { - Log(ctx *fiber.Ctx, err error, body []byte, meta LogMeta) + Log(ctx fiber.Ctx, err error, body []byte, meta LogMeta) HangUp() error Shutdown() error } diff --git a/s3log/file.go b/s3log/file.go index 8b6f78ca..55b76ac5 100644 --- a/s3log/file.go +++ b/s3log/file.go @@ -22,7 +22,7 @@ import ( "sync" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" @@ -56,7 +56,7 @@ func InitFileLogger(logname string) (AuditLogger, error) { } // Log sends log message to file logger -func (f *FileLogger) Log(ctx *fiber.Ctx, err error, body []byte, meta LogMeta) { +func (f *FileLogger) Log(ctx fiber.Ctx, err error, body []byte, meta LogMeta) { f.mu.Lock() defer f.mu.Unlock() @@ -79,7 +79,7 @@ func (f *FileLogger) Log(ctx *fiber.Ctx, err error, body []byte, meta LogMeta) { if !ok { startTime = time.Now() } - tlsConnState := ctx.Context().TLSConnectionState() + tlsConnState := ctx.RequestCtx().TLSConnectionState() if tlsConnState != nil { lf.CipherSuite = tls.CipherSuiteName(tlsConnState.CipherSuite) lf.TLSVersion = getTLSVersionName(tlsConnState.Version) diff --git a/s3log/file_admin.go b/s3log/file_admin.go index a3fb5414..b13ea085 100644 --- a/s3log/file_admin.go +++ b/s3log/file_admin.go @@ -20,7 +20,7 @@ import ( "os" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" ) @@ -45,7 +45,7 @@ func InitAdminFileLogger(logname string) (AuditLogger, error) { } // Log sends log message to file logger -func (f *AdminFileLogger) Log(ctx *fiber.Ctx, err error, body []byte, meta LogMeta) { +func (f *AdminFileLogger) Log(ctx fiber.Ctx, err error, body []byte, meta LogMeta) { f.mu.Lock() defer f.mu.Unlock() @@ -62,7 +62,7 @@ func (f *AdminFileLogger) Log(ctx *fiber.Ctx, err error, body []byte, meta LogMe if !ok { startTime = time.Now() } - tlsConnState := ctx.Context().TLSConnectionState() + tlsConnState := ctx.RequestCtx().TLSConnectionState() if tlsConnState != nil { lf.CipherSuite = tls.CipherSuiteName(tlsConnState.CipherSuite) lf.TLSVersion = getTLSVersionName(tlsConnState.Version) diff --git a/s3log/webhook.go b/s3log/webhook.go index 3a1c16c0..77aa12ba 100644 --- a/s3log/webhook.go +++ b/s3log/webhook.go @@ -26,7 +26,7 @@ import ( "sync" "time" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/s3api/utils" "github.com/versity/versitygw/s3err" @@ -57,7 +57,7 @@ func InitWebhookLogger(url string) (AuditLogger, error) { } // Log sends log message to webhook -func (wl *WebhookLogger) Log(ctx *fiber.Ctx, err error, body []byte, meta LogMeta) { +func (wl *WebhookLogger) Log(ctx fiber.Ctx, err error, body []byte, meta LogMeta) { wl.mu.Lock() defer wl.mu.Unlock() @@ -76,7 +76,7 @@ func (wl *WebhookLogger) Log(ctx *fiber.Ctx, err error, body []byte, meta LogMet if !ok { startTime = time.Now() } - tlsConnState := ctx.Context().TLSConnectionState() + tlsConnState := ctx.RequestCtx().TLSConnectionState() if tlsConnState != nil { lf.CipherSuite = tls.CipherSuiteName(tlsConnState.CipherSuite) lf.TLSVersion = getTLSVersionName(tlsConnState.Version) diff --git a/tests/integration/server.go b/tests/integration/server.go index bd5e557b..54e23b7b 100644 --- a/tests/integration/server.go +++ b/tests/integration/server.go @@ -16,12 +16,12 @@ package integration import ( "fmt" - "io" "net/http" "strings" "time" "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/versity/versitygw/s3err" ) func Server_large_http_header(s *S3Conf) error { @@ -39,20 +39,11 @@ func Server_large_http_header(s *S3Conf) error { return err } - if resp.StatusCode != http.StatusBadRequest { - return fmt.Errorf("expected the response status to be %v, instead got %v", http.StatusBadRequest, resp.StatusCode) + expectedErr := s3err.GetRequestHeaderSectionTooLargeErr(8 * 1024) + if resp.StatusCode != expectedErr.StatusCode() { + return fmt.Errorf("expected the response status to be %v, instead got %v", expectedErr.StatusCode(), resp.StatusCode) } - defer resp.Body.Close() - body, err := io.ReadAll(resp.Body) - if err != nil { - return err - } - - if len(body) != 0 { - return fmt.Errorf("expected empty response body, instead got %s", body) - } - - return nil + return checkHTTPResponseApiErr(resp, expectedErr) }) } diff --git a/tests/integration/utils.go b/tests/integration/utils.go index 2a224342..bfd8247f 100644 --- a/tests/integration/utils.go +++ b/tests/integration/utils.go @@ -693,6 +693,8 @@ func compareS3ApiErr(expected s3err.S3Error, received *APIErrorResponse) error { compareErrField("Size", int64(err.Size), received.Size), compareErrField("MaxSizeAllowed", int64(err.MaxSizeAllowed), received.MaxSizeAllowed), ) + case s3err.RequestHeaderSectionTooLargeError: + return compareErrField("MaxSizeAllowed", int64(err.MaxSizeAllowed), received.MaxSizeAllowed) case s3err.MethodNotAllowedError: return compareS3ApiErrFields( compareErrField("Method", err.Method, received.Method), diff --git a/website/handler.go b/website/handler.go index 98c69141..c84d7623 100644 --- a/website/handler.go +++ b/website/handler.go @@ -23,7 +23,7 @@ import ( "strings" "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/debuglogger" @@ -63,15 +63,15 @@ func newWebsiteController(be backend.Backend, domain string) *websiteController return controller } -func (c *websiteController) Get(ctx *fiber.Ctx) error { +func (c *websiteController) Get(ctx fiber.Ctx) error { return c.serve(ctx, c.getObject) } -func (c *websiteController) Head(ctx *fiber.Ctx) error { +func (c *websiteController) Head(ctx fiber.Ctx) error { return c.serve(ctx, c.headObject) } -func (c *websiteController) Options(ctx *fiber.Ctx) error { +func (c *websiteController) Options(ctx fiber.Ctx) error { bucket, err := c.resolveBucket(ctx) if err != nil { return sendError(ctx, err) @@ -96,7 +96,7 @@ func (c *websiteController) Options(ctx *fiber.Ctx) error { return sendError(ctx, err) } - cors, err := c.be.GetBucketCors(ctx.Context(), bucket) + cors, err := c.be.GetBucketCors(ctx.RequestCtx(), bucket) if err != nil { debuglogger.Logf("failed to get bucket cors: %v", err) if errors.Is(err, s3err.GetAPIError(s3err.ErrNoSuchCORSConfiguration)) { @@ -131,7 +131,7 @@ func registerWebsiteRoutes(app *fiber.App, be backend.Backend, domain string) { app.All("*", controller.MethodNotAllowed) } -func setCORSPreflightHeaders(ctx *fiber.Ctx, allowConfig *auth.CORSAllowanceConfig) { +func setCORSPreflightHeaders(ctx fiber.Ctx, allowConfig *auth.CORSAllowanceConfig) { ctx.Set("Access-Control-Allow-Origin", allowConfig.Origin) ctx.Set("Access-Control-Allow-Methods", allowConfig.Methods) ctx.Set("Access-Control-Expose-Headers", allowConfig.ExposedHeaders) @@ -143,7 +143,7 @@ func setCORSPreflightHeaders(ctx *fiber.Ctx, allowConfig *auth.CORSAllowanceConf } } -func (c *websiteController) MethodNotAllowed(ctx *fiber.Ctx) error { +func (c *websiteController) MethodNotAllowed(ctx fiber.Ctx) error { return sendError(ctx, s3err.GetMethodNotAllowedErr(ctx.Method(), s3err.ResourceTypeObject, websiteAllowedMethods)) } @@ -153,9 +153,9 @@ type websiteRequestInfo struct { key string } -type websiteObjectReader func(ctx *fiber.Ctx, bucket, key string) websiteResult +type websiteObjectReader func(ctx fiber.Ctx, bucket, key string) websiteResult -func (c *websiteController) serve(ctx *fiber.Ctx, readObject websiteObjectReader) error { +func (c *websiteController) serve(ctx fiber.Ctx, readObject websiteObjectReader) error { req, err := c.resolveRequest(ctx) if err != nil { return sendError(ctx, err) @@ -189,7 +189,7 @@ func (c *websiteController) serve(ctx *fiber.Ctx, readObject websiteObjectReader return serveWebsiteResult(ctx, req.bucket, req.config, result, readObject) } -func (c *websiteController) resolveRequest(ctx *fiber.Ctx) (*websiteRequestInfo, error) { +func (c *websiteController) resolveRequest(ctx fiber.Ctx) (*websiteRequestInfo, error) { bucket, err := c.resolveBucket(ctx) if err != nil { return nil, err @@ -202,7 +202,7 @@ func (c *websiteController) resolveRequest(ctx *fiber.Ctx) (*websiteRequestInfo, return nil, err } - data, err := c.be.GetBucketWebsite(ctx.Context(), bucket) + data, err := c.be.GetBucketWebsite(ctx.RequestCtx(), bucket) if err != nil { return nil, err } @@ -241,8 +241,8 @@ func validateWebsiteNames(bucket, key string) error { // // When domain is empty (catch-all mode): // - The full hostname is used as the bucket name. -func (c *websiteController) resolveBucket(ctx *fiber.Ctx) (string, error) { - host := ctx.Hostname() +func (c *websiteController) resolveBucket(ctx fiber.Ctx) (string, error) { + host := ctx.Host() if host == "" { ctx.Set("Location", c.noBucketLocation(ctx, host)) return "", s3err.GetAPIError(s3err.ErrNoBucketInRequest) @@ -269,11 +269,11 @@ func (c *websiteController) resolveBucket(ctx *fiber.Ctx) (string, error) { } } - ctx.Set("Location", c.noBucketLocation(ctx, ctx.Hostname())) + ctx.Set("Location", c.noBucketLocation(ctx, ctx.Host())) return "", s3err.GetAPIError(s3err.ErrNoBucketInRequest) } -func (c *websiteController) noBucketLocation(ctx *fiber.Ctx, host string) string { +func (c *websiteController) noBucketLocation(ctx fiber.Ctx, host string) string { locationHost := c.domain if locationHost == "" { locationHost = stripHostPort(host) @@ -287,7 +287,7 @@ func (c *websiteController) noBucketLocation(ctx *fiber.Ctx, host string) string } } - return fmt.Sprintf("%s://%s/", ctx.Protocol(), locationHost) + return fmt.Sprintf("%s://%s/", ctx.Scheme(), locationHost) } func stripHostPort(host string) string { @@ -330,8 +330,8 @@ func resolveIndexKey(key string, config *s3response.WebsiteConfiguration) string return key } -func (c *websiteController) getObject(ctx *fiber.Ctx, bucket, key string) websiteResult { - if err := auth.VerifyPublicAccess(ctx.Context(), c.be, auth.GetObjectAction, auth.PermissionRead, bucket, key); err != nil { +func (c *websiteController) getObject(ctx fiber.Ctx, bucket, key string) websiteResult { + if err := auth.VerifyPublicAccess(ctx.RequestCtx(), c.be, auth.GetObjectAction, auth.PermissionRead, bucket, key); err != nil { return websiteResult{ Key: key, StatusCode: statusCodeFromError(err), @@ -339,7 +339,7 @@ func (c *websiteController) getObject(ctx *fiber.Ctx, bucket, key string) websit } } - result, err := c.be.GetObject(ctx.Context(), &s3.GetObjectInput{ + result, err := c.be.GetObject(ctx.RequestCtx(), &s3.GetObjectInput{ Bucket: &bucket, Key: &key, }) @@ -363,8 +363,8 @@ func (c *websiteController) getObject(ctx *fiber.Ctx, bucket, key string) websit } } -func (c *websiteController) headObject(ctx *fiber.Ctx, bucket, key string) websiteResult { - if err := auth.VerifyPublicAccess(ctx.Context(), c.be, auth.GetObjectAction, auth.PermissionRead, bucket, key); err != nil { +func (c *websiteController) headObject(ctx fiber.Ctx, bucket, key string) websiteResult { + if err := auth.VerifyPublicAccess(ctx.RequestCtx(), c.be, auth.GetObjectAction, auth.PermissionRead, bucket, key); err != nil { return websiteResult{ Key: key, StatusCode: statusCodeFromError(err), @@ -372,7 +372,7 @@ func (c *websiteController) headObject(ctx *fiber.Ctx, bucket, key string) websi } } - result, err := c.be.HeadObject(ctx.Context(), &s3.HeadObjectInput{ + result, err := c.be.HeadObject(ctx.RequestCtx(), &s3.HeadObjectInput{ Bucket: &bucket, Key: &key, }) @@ -405,10 +405,10 @@ func statusCodeFromError(err error) int { } // handleRedirectAll sends a 301 redirect for RedirectAllRequestsTo configuration. -func handleRedirectAll(ctx *fiber.Ctx, redirect *s3response.RedirectAllRequestsTo, key string) error { +func handleRedirectAll(ctx fiber.Ctx, redirect *s3response.RedirectAllRequestsTo, key string) error { protocol := redirect.Protocol if protocol == "" { - protocol = ctx.Protocol() + protocol = ctx.Scheme() } location := fmt.Sprintf("%s://%s/%s", protocol, redirect.HostName, key) @@ -419,15 +419,15 @@ func handleRedirectAll(ctx *fiber.Ctx, redirect *s3response.RedirectAllRequestsT } // applyRedirect constructs and sends a redirect response from a routing rule. -func applyRedirect(ctx *fiber.Ctx, redirect *s3response.Redirect, condition *s3response.RoutingRuleCondition, originalKey string) error { +func applyRedirect(ctx fiber.Ctx, redirect *s3response.Redirect, condition *s3response.RoutingRuleCondition, originalKey string) error { protocol := redirect.Protocol if protocol == "" { - protocol = ctx.Protocol() + protocol = ctx.Scheme() } host := redirect.HostName if host == "" { - host = ctx.Hostname() + host = ctx.Host() } key := originalKey @@ -451,7 +451,7 @@ func applyRedirect(ctx *fiber.Ctx, redirect *s3response.Redirect, condition *s3r return sendRedirect(ctx, httpCode, location) } -func sendRedirect(ctx *fiber.Ctx, statusCode int, location string) error { +func sendRedirect(ctx fiber.Ctx, statusCode int, location string) error { ctx.Set("Location", location) _, _ = utils.EnsureRequestIDs(ctx) ctx.Status(statusCode) @@ -494,7 +494,7 @@ func headObjectHeaders(result *s3.HeadObjectOutput) map[string]*string { } } -func serveWebsiteResult(ctx *fiber.Ctx, bucket string, config *s3response.WebsiteConfiguration, result websiteResult, readObject websiteObjectReader) error { +func serveWebsiteResult(ctx fiber.Ctx, bucket string, config *s3response.WebsiteConfiguration, result websiteResult, readObject websiteObjectReader) error { if result.Err == nil { // Precedence: RedirectAllRequestsTo, pre-fetch routing rules, object // redirect metadata, then post-error routing/error documents. @@ -514,7 +514,7 @@ func serveWebsiteResult(ctx *fiber.Ctx, bucket string, config *s3response.Websit return sendError(ctx, result.Err) } -func serveObject(ctx *fiber.Ctx, object websiteObject, statusCode int) error { +func serveObject(ctx fiber.Ctx, object websiteObject, statusCode int) error { ctx.Status(statusCode) setWebsiteObjectHeaders(ctx, object) @@ -531,7 +531,7 @@ func serveObject(ctx *fiber.Ctx, object websiteObject, statusCode int) error { return nil } -func setWebsiteObjectHeaders(ctx *fiber.Ctx, object websiteObject) { +func setWebsiteObjectHeaders(ctx fiber.Ctx, object websiteObject) { utils.SetMetaHeaders(ctx, object.Metadata) for key, value := range object.Headers { if value != nil && *value != "" { @@ -541,7 +541,7 @@ func setWebsiteObjectHeaders(ctx *fiber.Ctx, object websiteObject) { } // serveErrorDocument fetches and serves the configured error document. -func serveErrorDocument(ctx *fiber.Ctx, readObject websiteObjectReader, bucket, errorDocKey string, statusCode int) error { +func serveErrorDocument(ctx fiber.Ctx, readObject websiteObjectReader, bucket, errorDocKey string, statusCode int) error { result := readObject(ctx, bucket, errorDocKey) if result.Err != nil { return sendError(ctx, result.Err) @@ -551,7 +551,7 @@ func serveErrorDocument(ctx *fiber.Ctx, readObject websiteObjectReader, bucket, } // sendError sends a simple HTML error page. -func sendError(ctx *fiber.Ctx, err error) error { +func sendError(ctx fiber.Ctx, err error) error { requestId, hostId := utils.EnsureRequestIDs(ctx) serr, ok := err.(s3err.S3Error) if !ok { diff --git a/website/handler_test.go b/website/handler_test.go index 85a519b0..9d983f6d 100644 --- a/website/handler_test.go +++ b/website/handler_test.go @@ -26,7 +26,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v3" "github.com/versity/versitygw/auth" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/s3err" @@ -1016,9 +1016,7 @@ func websiteRequestWithHostAndHeaders(t *testing.T, be backend.Backend, method, func websiteRequestWithDomainHostAndHeaders(t *testing.T, be backend.Backend, domain, method, host, path string, headers map[string]string) *http.Response { t.Helper() - app := fiber.New(fiber.Config{ - ServerHeader: "VERSITYGW", - }) + app := fiber.New(fiber.Config{ServerHeader: "VERSITYGW"}) registerWebsiteRoutes(app, be, domain) req := httptest.NewRequest(method, path, nil) @@ -1027,7 +1025,7 @@ func websiteRequestWithDomainHostAndHeaders(t *testing.T, be backend.Backend, do for key, value := range headers { req.Header.Set(key, value) } - resp, err := app.Test(req, -1) + resp, err := app.Test(req, fiber.TestConfig{Timeout: 0, FailOnTimeout: false}) if err != nil { t.Fatalf("website request failed: %v", err) } diff --git a/website/server.go b/website/server.go index c9c18d88..a82a31cb 100644 --- a/website/server.go +++ b/website/server.go @@ -19,9 +19,9 @@ import ( "net" "os" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" - "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/logger" + "github.com/gofiber/fiber/v3/middleware/recover" "github.com/versity/versitygw/backend" "github.com/versity/versitygw/debuglogger" "github.com/versity/versitygw/s3api/middlewares" @@ -63,10 +63,8 @@ func WithSocketPerm(perm os.FileMode) Option { // - Host "" (apex, no subdomain) resolves to bucket "" func NewServer(be backend.Backend, domain string, opts ...Option) *Server { app := fiber.New(fiber.Config{ - AppName: "versitygw-website", - ServerHeader: "VERSITYGW", - DisableStartupMessage: true, - Network: fiber.NetworkTCP, + AppName: "versitygw-website", + ServerHeader: "VERSITYGW", }) server := &Server{ @@ -84,19 +82,19 @@ func NewServer(be backend.Backend, domain string, opts ...Option) *Server { } // Panic recovery - app.Use(recover.New()) + app.Use("*", recover.New()) // Request logging if !server.quiet { fmt.Printf("initializing website endpoint (%s)\n", domainInfo) - app.Use(logger.New(logger.Config{ + app.Use("*", logger.New(logger.Config{ Format: "${time} | website | ${status} | ${latency} | ${ip} | ${method} | ${path}\n", })) } // initialize the debug logger in debug mode if debuglogger.IsDebugEnabled() { - app.Use(middlewares.DebugLogger()) + app.Use("*", middlewares.DebugLogger()) } registerWebsiteRoutes(app, be, domain) @@ -118,9 +116,9 @@ func (s *Server) ServeMultiPort(ports []string) error { var err error if s.CertStorage != nil { - ln, err = utils.NewMultiAddrTLSListener(s.app.Config().Network, addrSpec, s.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: s.socketPerm}) + ln, err = utils.NewMultiAddrTLSListener(fiber.NetworkTCP, addrSpec, s.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: s.socketPerm}) } else { - ln, err = utils.NewMultiAddrListener(s.app.Config().Network, addrSpec, utils.ListenerOptions{SocketPerm: s.socketPerm}) + ln, err = utils.NewMultiAddrListener(fiber.NetworkTCP, addrSpec, utils.ListenerOptions{SocketPerm: s.socketPerm}) } if err != nil { @@ -136,7 +134,9 @@ func (s *Server) ServeMultiPort(ports []string) error { finalListener := utils.NewMultiListener(listeners...) - return s.app.Listener(finalListener) + return s.app.Listener(finalListener, fiber.ListenConfig{ + DisableStartupMessage: true, + }) } // Shutdown gracefully shuts down the server. diff --git a/webui/embed.go b/webui/embed.go index 582cd78e..3f5b2d4f 100644 --- a/webui/embed.go +++ b/webui/embed.go @@ -14,7 +14,10 @@ package webui -import "embed" +import ( + "embed" + "io/fs" +) // webFiles embeds the admin GUI static files from web/. // The "all:" prefix recursively includes all files and subdirectories. @@ -23,4 +26,4 @@ import "embed" var webFiles embed.FS // webFS is an alias for webFiles for consistency with server.go -var webFS = webFiles +var webFS fs.FS = webFiles diff --git a/webui/webserver.go b/webui/webserver.go index cbafd897..92970e68 100644 --- a/webui/webserver.go +++ b/webui/webserver.go @@ -17,15 +17,15 @@ package webui import ( "encoding/json" "fmt" + "io/fs" "net" - "net/http" "os" "strings" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" - "github.com/gofiber/fiber/v2/middleware/logger" - "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/logger" + "github.com/gofiber/fiber/v3/middleware/recover" + "github.com/gofiber/fiber/v3/middleware/static" "github.com/versity/versitygw/s3api/utils" ) @@ -73,12 +73,10 @@ func WithSocketPerm(perm os.FileMode) Option { } // NewServer creates a new GUI server instance -func NewServer(cfg *ServerConfig, opts ...Option) *Server { +func NewServer(cfg *ServerConfig, opts ...Option) (*Server, error) { app := fiber.New(fiber.Config{ - AppName: "versitygw", - ServerHeader: "VERSITYGW", - DisableStartupMessage: true, - Network: fiber.NetworkTCP, + AppName: "versitygw", + ServerHeader: "VERSITYGW", }) server := &Server{ @@ -93,50 +91,55 @@ func NewServer(cfg *ServerConfig, opts ...Option) *Server { fmt.Printf("initializing web dashboard\n") server.setupMiddleware() - server.setupRoutes() + if err := server.setupRoutes(); err != nil { + return nil, err + } - return server + return server, nil } // setupMiddleware configures middleware func (s *Server) setupMiddleware() { // Panic recovery - s.app.Use(recover.New()) + s.app.Use("*", recover.New()) // Request logging if !s.quiet { - s.app.Use(logger.New(logger.Config{ + s.app.Use("*", logger.New(logger.Config{ Format: "${time} | web | ${status} | ${latency} | ${ip} | ${method} | ${path}\n", })) } } // setupRoutes configures all routes -func (s *Server) setupRoutes() { +func (s *Server) setupRoutes() error { prefix := s.pathPrefix // Serve index.html with server-side config injection s.app.Get(prefix+"/", s.handleIndexHTML) s.app.Get(prefix+"/index.html", s.handleIndexHTML) - // Serve embedded static files from web/ - s.app.Use(prefix+"/", filesystem.New(filesystem.Config{ - Root: http.FS(webFS), - PathPrefix: "web", - Browse: false, + staticFS, err := fs.Sub(webFS, "web") + if err != nil { + return fmt.Errorf("initialize embedded web UI filesystem: %w", err) + } + + // Serve embedded static files from web/. + s.app.Use(prefix+"/", static.New("", static.Config{ + FS: staticFS, + Browse: false, })) - // Catch-all: absorb any request the filesystem did not fully handle. - // The filesystem middleware calls Next() for non-GET/HEAD methods and for - // paths not found in the embedded FS, which would otherwise fall through - // to the S3 router and be interpreted as bucket/object operations. - s.app.Use(prefix+"/", func(c *fiber.Ctx) error { - return c.SendStatus(http.StatusBadRequest) + // Catch-all: absorb any request the static middleware did not fully handle. + s.app.Use(prefix+"/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusBadRequest) }) + + return nil } // handleIndexHTML serves index.html with server config injected as an inline script. -func (s *Server) handleIndexHTML(c *fiber.Ctx) error { +func (s *Server) handleIndexHTML(c fiber.Ctx) error { data, err := webFiles.ReadFile("web/index.html") if err != nil { return fiber.ErrInternalServerError @@ -184,9 +187,9 @@ func (s *Server) ServeMultiPort(ports []string) error { var err error if s.CertStorage != nil { - ln, err = utils.NewMultiAddrTLSListener(s.app.Config().Network, addrSpec, s.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: s.socketPerm}) + ln, err = utils.NewMultiAddrTLSListener(fiber.NetworkTCP, addrSpec, s.CertStorage.GetCertificate, utils.ListenerOptions{SocketPerm: s.socketPerm}) } else { - ln, err = utils.NewMultiAddrListener(s.app.Config().Network, addrSpec, utils.ListenerOptions{SocketPerm: s.socketPerm}) + ln, err = utils.NewMultiAddrListener(fiber.NetworkTCP, addrSpec, utils.ListenerOptions{SocketPerm: s.socketPerm}) } if err != nil { @@ -203,7 +206,9 @@ func (s *Server) ServeMultiPort(ports []string) error { // Combine all listeners finalListener := utils.NewMultiListener(listeners...) - return s.app.Listener(finalListener) + return s.app.Listener(finalListener, fiber.ListenConfig{ + DisableStartupMessage: true, + }) } // Shutdown gracefully shuts down the server @@ -214,12 +219,12 @@ func (s *Server) Shutdown() error { // MountOn registers the WebUI routes on an existing Fiber app at the given path prefix. // This allows hosting the WebUI on the same port as another service (e.g. the S3 API server). // The prefix must start with "/" and must not be empty or just "/". -func MountOn(app *fiber.App, prefix string, cfg *ServerConfig) { +func MountOn(app *fiber.App, prefix string, cfg *ServerConfig) error { s := &Server{ app: app, config: cfg, pathPrefix: prefix, } fmt.Printf("initializing web dashboard\n") - s.setupRoutes() + return s.setupRoutes() }