mirror of
https://github.com/go-gitea/gitea.git
synced 2025-06-20 05:30:29 +02:00
Backport #34666 by wxiaoguang Fix #25846 1. the ImageConfig can be empty, fall back to default 2. the blob size can be empty, it still needs "Content-Length" header Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
bf5d00074d
commit
18dc41d6f8
@ -4,6 +4,7 @@
|
|||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
@ -83,7 +84,8 @@ func ParseImageConfig(mt string, r io.Reader) (*Metadata, error) {
|
|||||||
|
|
||||||
func parseOCIImageConfig(r io.Reader) (*Metadata, error) {
|
func parseOCIImageConfig(r io.Reader) (*Metadata, error) {
|
||||||
var image oci.Image
|
var image oci.Image
|
||||||
if err := json.NewDecoder(r).Decode(&image); err != nil {
|
// EOF means empty input, still use the default data
|
||||||
|
if err := json.NewDecoder(r).Decode(&image); err != nil && !errors.Is(err, io.EOF) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
oci "github.com/opencontainers/image-spec/specs-go/v1"
|
oci "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseImageConfig(t *testing.T) {
|
func TestParseImageConfig(t *testing.T) {
|
||||||
@ -59,3 +60,9 @@ func TestParseImageConfig(t *testing.T) {
|
|||||||
assert.Equal(t, projectURL, metadata.ProjectURL)
|
assert.Equal(t, projectURL, metadata.ProjectURL)
|
||||||
assert.Equal(t, repositoryURL, metadata.RepositoryURL)
|
assert.Equal(t, repositoryURL, metadata.RepositoryURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseOCIImageConfig(t *testing.T) {
|
||||||
|
metadata, err := parseOCIImageConfig(strings.NewReader(""))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, &Metadata{Type: TypeOCI, Platform: DefaultPlatform, ImageLayers: []string{}}, metadata)
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/httplib"
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/optional"
|
||||||
packages_module "code.gitea.io/gitea/modules/packages"
|
packages_module "code.gitea.io/gitea/modules/packages"
|
||||||
container_module "code.gitea.io/gitea/modules/packages/container"
|
container_module "code.gitea.io/gitea/modules/packages/container"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@ -50,7 +51,7 @@ type containerHeaders struct {
|
|||||||
Range string
|
Range string
|
||||||
Location string
|
Location string
|
||||||
ContentType string
|
ContentType string
|
||||||
ContentLength int64
|
ContentLength optional.Option[int64]
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#legacy-docker-support-http-headers
|
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#legacy-docker-support-http-headers
|
||||||
@ -64,8 +65,8 @@ func setResponseHeaders(resp http.ResponseWriter, h *containerHeaders) {
|
|||||||
if h.ContentType != "" {
|
if h.ContentType != "" {
|
||||||
resp.Header().Set("Content-Type", h.ContentType)
|
resp.Header().Set("Content-Type", h.ContentType)
|
||||||
}
|
}
|
||||||
if h.ContentLength != 0 {
|
if h.ContentLength.Has() {
|
||||||
resp.Header().Set("Content-Length", strconv.FormatInt(h.ContentLength, 10))
|
resp.Header().Set("Content-Length", strconv.FormatInt(h.ContentLength.Value(), 10))
|
||||||
}
|
}
|
||||||
if h.UploadUUID != "" {
|
if h.UploadUUID != "" {
|
||||||
resp.Header().Set("Docker-Upload-Uuid", h.UploadUUID)
|
resp.Header().Set("Docker-Upload-Uuid", h.UploadUUID)
|
||||||
@ -505,7 +506,7 @@ func HeadBlob(ctx *context.Context) {
|
|||||||
|
|
||||||
setResponseHeaders(ctx.Resp, &containerHeaders{
|
setResponseHeaders(ctx.Resp, &containerHeaders{
|
||||||
ContentDigest: blob.Properties.GetByName(container_module.PropertyDigest),
|
ContentDigest: blob.Properties.GetByName(container_module.PropertyDigest),
|
||||||
ContentLength: blob.Blob.Size,
|
ContentLength: optional.Some(blob.Blob.Size),
|
||||||
Status: http.StatusOK,
|
Status: http.StatusOK,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -644,7 +645,7 @@ func HeadManifest(ctx *context.Context) {
|
|||||||
setResponseHeaders(ctx.Resp, &containerHeaders{
|
setResponseHeaders(ctx.Resp, &containerHeaders{
|
||||||
ContentDigest: manifest.Properties.GetByName(container_module.PropertyDigest),
|
ContentDigest: manifest.Properties.GetByName(container_module.PropertyDigest),
|
||||||
ContentType: manifest.Properties.GetByName(container_module.PropertyMediaType),
|
ContentType: manifest.Properties.GetByName(container_module.PropertyMediaType),
|
||||||
ContentLength: manifest.Blob.Size,
|
ContentLength: optional.Some(manifest.Blob.Size),
|
||||||
Status: http.StatusOK,
|
Status: http.StatusOK,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -708,14 +709,14 @@ func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor)
|
|||||||
headers := &containerHeaders{
|
headers := &containerHeaders{
|
||||||
ContentDigest: pfd.Properties.GetByName(container_module.PropertyDigest),
|
ContentDigest: pfd.Properties.GetByName(container_module.PropertyDigest),
|
||||||
ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType),
|
ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType),
|
||||||
ContentLength: pfd.Blob.Size,
|
ContentLength: optional.Some(pfd.Blob.Size),
|
||||||
Status: http.StatusOK,
|
Status: http.StatusOK,
|
||||||
}
|
}
|
||||||
|
|
||||||
if u != nil {
|
if u != nil {
|
||||||
headers.Status = http.StatusTemporaryRedirect
|
headers.Status = http.StatusTemporaryRedirect
|
||||||
headers.Location = u.String()
|
headers.Location = u.String()
|
||||||
headers.ContentLength = 0 // do not set Content-Length for redirect responses
|
headers.ContentLength = optional.None[int64]() // do not set Content-Length for redirect responses
|
||||||
setResponseHeaders(ctx.Resp, headers)
|
setResponseHeaders(ctx.Resp, headers)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -623,6 +624,22 @@ func TestPackageContainer(t *testing.T) {
|
|||||||
assert.Equal(t, blobContent, resp.Body.Bytes())
|
assert.Equal(t, blobContent, resp.Body.Bytes())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("GetBlob/Empty", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
emptyDigestBuf := sha256.Sum256(nil)
|
||||||
|
emptyDigest := "sha256:" + hex.EncodeToString(emptyDigestBuf[:])
|
||||||
|
req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/blobs/uploads?digest=%s", url, emptyDigest), strings.NewReader("")).AddTokenAuth(userToken)
|
||||||
|
MakeRequest(t, req, http.StatusCreated)
|
||||||
|
|
||||||
|
req = NewRequest(t, "HEAD", fmt.Sprintf("%s/blobs/%s", url, emptyDigest)).AddTokenAuth(userToken)
|
||||||
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
assert.Equal(t, "0", resp.Header().Get("Content-Length"))
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", fmt.Sprintf("%s/blobs/%s", url, emptyDigest)).AddTokenAuth(userToken)
|
||||||
|
resp = MakeRequest(t, req, http.StatusOK)
|
||||||
|
assert.Equal(t, "0", resp.Header().Get("Content-Length"))
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("GetTagList", func(t *testing.T) {
|
t.Run("GetTagList", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user