oss请求处理
This commit is contained in:
@@ -271,6 +271,20 @@ func (h *assetsHandler) download(w http.ResponseWriter, r *http.Request, public
|
||||
}
|
||||
return
|
||||
}
|
||||
inline := r.URL.Query().Get("inline") == "1"
|
||||
if inline {
|
||||
signedURL, signed, signErr := h.service.SignedDownloadURL(r.Context(), scope, id, assets.DefaultSignedURLTTL)
|
||||
if signErr != nil {
|
||||
writeAssetError(w, signErr, public, "")
|
||||
return
|
||||
}
|
||||
if signed {
|
||||
w.Header().Set("Cache-Control", "private, no-store")
|
||||
w.Header().Set("Location", signedURL)
|
||||
w.WriteHeader(http.StatusTemporaryRedirect)
|
||||
return
|
||||
}
|
||||
}
|
||||
blob, err := h.service.Download(r.Context(), scope, id)
|
||||
if err != nil {
|
||||
if public {
|
||||
@@ -281,7 +295,11 @@ func (h *assetsHandler) download(w http.ResponseWriter, r *http.Request, public
|
||||
return
|
||||
}
|
||||
defer blob.Body.Close()
|
||||
writeBlob(w, blob, "private, no-store", contentDisposition(a.Name))
|
||||
disposition := contentDisposition(a.Name)
|
||||
if inline {
|
||||
disposition = inlineContentDisposition(a.Name)
|
||||
}
|
||||
writeBlob(w, blob, "private, no-store", disposition)
|
||||
}
|
||||
|
||||
func (h *assetsHandler) serveStored(w http.ResponseWriter, r *http.Request, key string) {
|
||||
@@ -383,6 +401,12 @@ func writeBlob(w http.ResponseWriter, blob assets.Blob, cache, disposition strin
|
||||
_, _ = io.Copy(w, blob.Body)
|
||||
}
|
||||
func contentDisposition(name string) string {
|
||||
return namedContentDisposition("attachment", name)
|
||||
}
|
||||
func inlineContentDisposition(name string) string {
|
||||
return namedContentDisposition("inline", name)
|
||||
}
|
||||
func namedContentDisposition(kind, name string) string {
|
||||
clean := strings.TrimSpace(strings.NewReplacer("\r", "_", "\n", "_", "/", "_", "\\", "_").Replace(name))
|
||||
if clean == "" {
|
||||
clean = "download"
|
||||
@@ -395,7 +419,7 @@ func contentDisposition(name string) string {
|
||||
ascii.WriteByte('_')
|
||||
}
|
||||
}
|
||||
return `attachment; filename="` + ascii.String() + `"; filename*=UTF-8''` + url.PathEscape(clean)
|
||||
return kind + `; filename="` + ascii.String() + `"; filename*=UTF-8''` + url.PathEscape(clean)
|
||||
}
|
||||
func requestOrigin(r *http.Request) string {
|
||||
scheme := "http"
|
||||
|
||||
@@ -81,6 +81,13 @@ func (c *assetCatalog) DeleteOwner(_ context.Context, owner, id string) (assets.
|
||||
|
||||
type assetBlobs struct{ values map[string][]byte }
|
||||
|
||||
type signedAssetBlobs struct {
|
||||
*assetBlobs
|
||||
url string
|
||||
}
|
||||
|
||||
func (b *signedAssetBlobs) SignReadURL(string, time.Duration) (string, error) { return b.url, nil }
|
||||
|
||||
func (b *assetBlobs) Put(_ context.Context, key string, r io.Reader, _ int64, _ string) (assets.StoredObject, error) {
|
||||
p, _ := io.ReadAll(r)
|
||||
b.values[key] = p
|
||||
@@ -164,6 +171,24 @@ func TestAssetsMultipartDownloadServingAndMethods(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssetInlineReadRedirectsToShortLivedSignedURL(t *testing.T) {
|
||||
cat := &assetCatalog{values: []assets.Asset{{
|
||||
ID: "asset-private", OwnerID: "demo-merchant", Name: "private.png", URL: "https://private.test/private.png", StoragePath: "uploads/private.png",
|
||||
}}}
|
||||
blobs := &signedAssetBlobs{
|
||||
assetBlobs: &assetBlobs{values: map[string][]byte{"uploads/private.png": []byte("png")}},
|
||||
url: "https://private.test/private.png?OSSAccessKeyId=test&Expires=1&Signature=signed",
|
||||
}
|
||||
svc := assets.NewService(cat, blobs, nil, time.Now, nil)
|
||||
platform, _ := httpapi.NewPlatformAuthorizer(httpapi.AuthState{}, nil)
|
||||
h, _ := httpapi.NewAssetsHandler(svc, platform, publicapi.NewAuthenticator(publicapi.Config{APIKeys: "a:k"}), httpapi.AssetsConfig{})
|
||||
|
||||
inline := request(t, h, http.MethodGet, "/api/assets/asset-private/download?inline=1", nil, nil)
|
||||
if inline.Code != http.StatusTemporaryRedirect || inline.Header().Get("Location") != blobs.url || inline.Header().Get("Cache-Control") != "private, no-store" || inline.Header().Get("Content-Disposition") != "" {
|
||||
t.Fatalf("inline response = %d %#v", inline.Code, inline.Header())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssetsLimitsAndInfrastructureErrorsDoNotLeak(t *testing.T) {
|
||||
cat := &assetCatalog{}
|
||||
svc := assets.NewService(cat, &assetBlobs{values: map[string][]byte{}}, nil, time.Now, nil)
|
||||
|
||||
Reference in New Issue
Block a user