mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-04 07:07:18 +00:00
fix(replication/s3sink): forward entry.Attributes.Mime as ContentType
Same gap as the remote_storage S3 client: filer.replicate uploads via s3manager.Uploader without populating ContentType, so replicated objects on S3-compatible backends (e.g. Backblaze B2) store binary/octet-stream and browsers refuse to render HTML, CSS, etc. Pass entry.Attributes.Mime through to UploadInput.ContentType, leaving the header unset when no Mime is recorded so the remote keeps its own default.
This commit is contained in:
@@ -209,6 +209,9 @@ func (s3sink *S3Sink) CreateEntry(key string, entry *filer_pb.Entry, signatures
|
||||
if tags != "" {
|
||||
uploadInput.Tagging = aws.String(tags)
|
||||
}
|
||||
if entry.Attributes.Mime != "" {
|
||||
uploadInput.ContentType = aws.String(entry.Attributes.Mime)
|
||||
}
|
||||
if len(entry.Attributes.Md5) > 0 {
|
||||
uploadInput.ContentMD5 = aws.String(base64.StdEncoding.EncodeToString([]byte(entry.Attributes.Md5)))
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
package S3Sink
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
awss3 "github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBuildTaggingString_ShouldStripTagPrefix(t *testing.T) {
|
||||
@@ -44,6 +52,72 @@ func TestBuildTaggingString_ShouldURLEncodeValues(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// capturePutRoundTripper records the s3manager.Uploader's PUT and replies 200
|
||||
// so the SDK is satisfied without making a real network call.
|
||||
type capturePutRoundTripper struct {
|
||||
uploadReq *http.Request
|
||||
}
|
||||
|
||||
func (c *capturePutRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if req.Method == http.MethodPut {
|
||||
c.uploadReq = req.Clone(req.Context())
|
||||
}
|
||||
if req.Body != nil {
|
||||
_, _ = io.Copy(io.Discard, req.Body)
|
||||
_ = req.Body.Close()
|
||||
}
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Body: io.NopCloser(strings.NewReader("")),
|
||||
Header: http.Header{"ETag": []string{"\"etag\""}},
|
||||
Request: req,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newCapturingS3Sink(t *testing.T) (*S3Sink, *capturePutRoundTripper) {
|
||||
t.Helper()
|
||||
rt := &capturePutRoundTripper{}
|
||||
sess, err := session.NewSession(&aws.Config{
|
||||
Region: aws.String("us-east-1"),
|
||||
Endpoint: aws.String("https://example.invalid"),
|
||||
S3ForcePathStyle: aws.Bool(true),
|
||||
Credentials: credentials.NewStaticCredentials("k", "s", ""),
|
||||
HTTPClient: &http.Client{Transport: rt},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
s := &S3Sink{
|
||||
conn: awss3.New(sess),
|
||||
bucket: "bucket",
|
||||
uploaderConcurrency: 1,
|
||||
uploaderPartSizeMb: 5,
|
||||
}
|
||||
return s, rt
|
||||
}
|
||||
|
||||
func TestS3SinkCreateEntryPassesMimeAsContentType(t *testing.T) {
|
||||
sink, rt := newCapturingS3Sink(t)
|
||||
entry := &filer_pb.Entry{
|
||||
Attributes: &filer_pb.FuseAttributes{Mime: "text/html"},
|
||||
Content: []byte("<html></html>"),
|
||||
}
|
||||
require.NoError(t, sink.CreateEntry("/dir/test.html", entry, nil))
|
||||
|
||||
require.NotNil(t, rt.uploadReq, "uploader should have issued a PUT")
|
||||
require.Equal(t, "text/html", rt.uploadReq.Header.Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestS3SinkCreateEntryOmitsContentTypeWhenMimeMissing(t *testing.T) {
|
||||
sink, rt := newCapturingS3Sink(t)
|
||||
entry := &filer_pb.Entry{
|
||||
Attributes: &filer_pb.FuseAttributes{},
|
||||
Content: []byte("data"),
|
||||
}
|
||||
require.NoError(t, sink.CreateEntry("/dir/test.bin", entry, nil))
|
||||
|
||||
require.NotNil(t, rt.uploadReq, "uploader should have issued a PUT")
|
||||
require.Equal(t, "", rt.uploadReq.Header.Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestBuildTaggingString_EmptyWhenNoTags(t *testing.T) {
|
||||
extended := map[string][]byte{
|
||||
"Content-Encoding": []byte("gzip"),
|
||||
|
||||
Reference in New Issue
Block a user