Skip to content

Commit

Permalink
pkg/linksharing: check for lowercase metadata set by s3
Browse files Browse the repository at this point in the history
Content-Type and Cache-Control could be lowercase if they're set by
S3 PUT operations, so we need to check for those cases too.

Change-Id: I4307c9a86a040d90beb942710ef6ce00645c68c2
  • Loading branch information
halkyon authored and Storj Robot committed Sep 15, 2022
1 parent 3d67116 commit a91ac2b
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 60 deletions.
18 changes: 15 additions & 3 deletions pkg/linksharing/sharing/present.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,19 @@ func (handler *Handler) showObject(ctx context.Context, w http.ResponseWriter, r
w.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(o.Key))
}
}

// note: s3 stores metadata keys in lowercase, so we should check both.
// todo: canonicalize and support more headers as part of https://github.com/storj/gateway-mt/issues/196.
cacheControl := o.Custom["Cache-Control"]
if cacheControl == "" {
cacheControl = o.Custom["cache-control"]
}

if (download || !wrap) && !mapOnly {
if len(archivePath) > 0 { // handle zip archives
contentType := mime.TypeByExtension(filepath.Ext(archivePath))
handler.setHeaders(w, contentType, o.Custom["Cache-Control"], pr.hosting, archivePath)

handler.setHeaders(w, contentType, cacheControl, pr.hosting, archivePath)
if len(r.Header.Get("Range")) > 0 { // prohibit range requests for archives for now
return errdata.WithStatus(errs.New("Range header isn't compatible with path query"), http.StatusRequestedRangeNotSatisfiable)
}
Expand All @@ -172,9 +181,12 @@ func (handler *Handler) showObject(ctx context.Context, w http.ResponseWriter, r
} else {
contentType := o.Custom["Content-Type"]
if contentType == "" {
contentType = mime.TypeByExtension(filepath.Ext(o.Key))
contentType = o.Custom["content-type"]
if contentType == "" {
contentType = mime.TypeByExtension(filepath.Ext(o.Key))
}
}
handler.setHeaders(w, contentType, o.Custom["Cache-Control"], pr.hosting, filepath.Base(o.Key))
handler.setHeaders(w, contentType, cacheControl, pr.hosting, filepath.Base(o.Key))
httpranger.ServeContent(ctx, w, r, o.Key, o.System.Created, objectranger.New(project, o, pr.bucket))
}
return nil
Expand Down
135 changes: 78 additions & 57 deletions pkg/linksharing/sharing/present_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,65 +22,86 @@ import (
)

func TestDownloadContentTypeHeader(t *testing.T) {
cfg := Config{
URLBases: []string{"http://test.test"},
Templates: "../../../pkg/linksharing/web/",
}

handler, err := NewHandler(&zap.Logger{}, &objectmap.IPDB{}, cfg)
require.NoError(t, err)

ctx := testcontext.New(t)
w := httptest.NewRecorder()
r, err := http.NewRequestWithContext(ctx, "GET", "http://test.test?download", nil)
require.NoError(t, err)

pr := &parsedRequest{}
project := &uplink.Project{}
object := &uplink.Object{
Key: "test.jpg",
}
err = handler.showObject(ctx, w, r, pr, project, object)
require.NoError(t, err)

ctypes, haveType := w.Header()["Content-Type"]
require.True(t, haveType)
require.Equal(t, "image/jpeg", ctypes[0])

require.Equal(t, "", w.Header().Get("Cache-Control"))

object.Key = "test"
object.Custom = uplink.CustomMetadata{
"Cache-Control": "max-age=0, must-revalidate",
}
err = handler.showObject(ctx, w, r, pr, project, object)
require.NoError(t, err)

ctypes, haveType = w.Header()["Content-Type"]
require.True(t, haveType)
require.Equal(t, "application/octet-stream", ctypes[0])

require.Equal(t, "max-age=0, must-revalidate", w.Header().Get("Cache-Control"))

object.Custom = uplink.CustomMetadata{
"Content-Type": "image/somethingelse",
testCases := []struct {
desc string
cacheControlMetadataKey string
contentTypeMetadataKey string
}{
{
desc: "lowercase cache control and content type",
cacheControlMetadataKey: "cache-control",
contentTypeMetadataKey: "content-type",
},
{
desc: "capitalized cache control and content type",
cacheControlMetadataKey: "Cache-Control",
contentTypeMetadataKey: "Content-Type",
},
}
err = handler.showObject(ctx, w, r, pr, project, object)
require.NoError(t, err)

ctypes, haveType = w.Header()["Content-Type"]
require.True(t, haveType)
require.Equal(t, "image/somethingelse", ctypes[0])

object.Custom = uplink.CustomMetadata{
"Content-Type": "text/html",
for _, tc := range testCases {
tc := tc
t.Run(tc.desc, func(t *testing.T) {
cfg := Config{
URLBases: []string{"http://test.test"},
Templates: "../../../pkg/linksharing/web/",
}

handler, err := NewHandler(&zap.Logger{}, &objectmap.IPDB{}, cfg)
require.NoError(t, err)

ctx := testcontext.New(t)
w := httptest.NewRecorder()
r, err := http.NewRequestWithContext(ctx, "GET", "http://test.test?download", nil)
require.NoError(t, err)

pr := &parsedRequest{}
project := &uplink.Project{}
object := &uplink.Object{
Key: "test.jpg",
}
err = handler.showObject(ctx, w, r, pr, project, object)
require.NoError(t, err)

ctypes, haveType := w.Header()["Content-Type"]
require.True(t, haveType)
require.Equal(t, "image/jpeg", ctypes[0])

require.Equal(t, "", w.Header().Get("Cache-Control"))

object.Key = "test"
object.Custom = uplink.CustomMetadata{
tc.cacheControlMetadataKey: "max-age=0, must-revalidate",
}
err = handler.showObject(ctx, w, r, pr, project, object)
require.NoError(t, err)

ctypes, haveType = w.Header()["Content-Type"]
require.True(t, haveType)
require.Equal(t, "application/octet-stream", ctypes[0])

require.Equal(t, "max-age=0, must-revalidate", w.Header().Get("Cache-Control"))

object.Custom = uplink.CustomMetadata{
tc.contentTypeMetadataKey: "image/somethingelse",
}
err = handler.showObject(ctx, w, r, pr, project, object)
require.NoError(t, err)

ctypes, haveType = w.Header()["Content-Type"]
require.True(t, haveType)
require.Equal(t, "image/somethingelse", ctypes[0])

object.Custom = uplink.CustomMetadata{
tc.contentTypeMetadataKey: "text/html",
}
err = handler.showObject(ctx, w, r, pr, project, object)
require.NoError(t, err)

ctypes, haveType = w.Header()["Content-Type"]
require.True(t, haveType)
require.Equal(t, "text/plain", ctypes[0]) // html isn't allowed for security reasons
})
}
err = handler.showObject(ctx, w, r, pr, project, object)
require.NoError(t, err)

ctypes, haveType = w.Header()["Content-Type"]
require.True(t, haveType)
require.Equal(t, "text/plain", ctypes[0]) // html isn't allowed for security reasons
}

func TestZipArchiveContentType(t *testing.T) {
Expand Down

0 comments on commit a91ac2b

Please sign in to comment.