mirror of
https://github.com/versity/versitygw.git
synced 2026-07-02 16:54:25 +00:00
375c2764d5
Replace PutBucketWebsite, GetBucketWebsite, DeleteBucketWebsite NotImplemented test stubs with comprehensive integration tests covering: - non-existing bucket errors - validation (empty suffix, suffix with slash, invalid protocol, mutual exclusion of RedirectAllRequestsTo and IndexDocument) - successful put/get round-trips for both index+error and redirect-all configs - delete idempotency and verification Signed-off-by: Marc Singer <marc@singer.gg> Add error document serving, routing rules, and integration tests Implement Features 1 and 2 of S3 static website hosting: - WebsiteErrorDocument controller wrapper intercepts 4xx errors on website-enabled buckets and serves the configured error document or evaluates post-request routing rules (error code match redirects) - ResolveWebsiteIndex middleware now caches parsed WebsiteConfiguration in context, handles RedirectAllRequestsTo, evaluates pre-request routing rules (key prefix match redirects), and rewrites directory keys for index document - MatchPreRequestRule and MatchPostRequestRule methods on WebsiteConfiguration for routing rule evaluation - 14 unit tests for routing rule matching - 7 integration tests covering error document, routing rules, redirect-all, and index document behavior Signed-off-by: Marc Singer <marc@singer.gg> Add separate website hosting endpoint with virtual-host routing Signed-off-by: Marc Singer <marc@singer.gg> Support catch-all mode for website endpoint when --website-domain is omitted Signed-off-by: Marc Singer <marc@singer.gg>
127 lines
4.2 KiB
Go
127 lines
4.2 KiB
Go
// 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 integration
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
|
"github.com/versity/versitygw/s3err"
|
|
)
|
|
|
|
func GetBucketWebsite_non_existing_bucket(s *S3Conf) error {
|
|
testName := "GetBucketWebsite_non_existing_bucket"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := s3client.GetBucketWebsite(ctx, &s3.GetBucketWebsiteInput{
|
|
Bucket: getPtr("non-existing-bucket"),
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchBucket))
|
|
})
|
|
}
|
|
|
|
func GetBucketWebsite_no_such_website_config(s *S3Conf) error {
|
|
testName := "GetBucketWebsite_no_such_website_config"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := s3client.GetBucketWebsite(ctx, &s3.GetBucketWebsiteInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
return checkApiErr(err, s3err.GetAPIError(s3err.ErrNoSuchWebsiteConfiguration))
|
|
})
|
|
}
|
|
|
|
func GetBucketWebsite_success(s *S3Conf) error {
|
|
testName := "GetBucketWebsite_success"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := s3client.PutBucketWebsite(ctx, &s3.PutBucketWebsiteInput{
|
|
Bucket: &bucket,
|
|
WebsiteConfiguration: &types.WebsiteConfiguration{
|
|
IndexDocument: &types.IndexDocument{
|
|
Suffix: getPtr("index.html"),
|
|
},
|
|
ErrorDocument: &types.ErrorDocument{
|
|
Key: getPtr("error.html"),
|
|
},
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.GetBucketWebsite(ctx, &s3.GetBucketWebsiteInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.IndexDocument == nil || res.IndexDocument.Suffix == nil || *res.IndexDocument.Suffix != "index.html" {
|
|
return fmt.Errorf("expected IndexDocument.Suffix to be %q, got %v", "index.html", res.IndexDocument)
|
|
}
|
|
if res.ErrorDocument == nil || res.ErrorDocument.Key == nil || *res.ErrorDocument.Key != "error.html" {
|
|
return fmt.Errorf("expected ErrorDocument.Key to be %q, got %v", "error.html", res.ErrorDocument)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func GetBucketWebsite_success_redirect_all(s *S3Conf) error {
|
|
testName := "GetBucketWebsite_success_redirect_all"
|
|
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
|
|
_, err := s3client.PutBucketWebsite(ctx, &s3.PutBucketWebsiteInput{
|
|
Bucket: &bucket,
|
|
WebsiteConfiguration: &types.WebsiteConfiguration{
|
|
RedirectAllRequestsTo: &types.RedirectAllRequestsTo{
|
|
HostName: getPtr("example.com"),
|
|
Protocol: types.ProtocolHttps,
|
|
},
|
|
},
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
|
|
res, err := s3client.GetBucketWebsite(ctx, &s3.GetBucketWebsiteInput{
|
|
Bucket: &bucket,
|
|
})
|
|
cancel()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if res.RedirectAllRequestsTo == nil || res.RedirectAllRequestsTo.HostName == nil || *res.RedirectAllRequestsTo.HostName != "example.com" {
|
|
return fmt.Errorf("expected RedirectAllRequestsTo.HostName to be %q, got %v", "example.com", res.RedirectAllRequestsTo)
|
|
}
|
|
if res.RedirectAllRequestsTo.Protocol != types.ProtocolHttps {
|
|
return fmt.Errorf("expected RedirectAllRequestsTo.Protocol to be %q, got %q", types.ProtocolHttps, res.RedirectAllRequestsTo.Protocol)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|