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() }