mirror of https://github.com/go-gitea/gitea.git
Fix "oras" OCI client compatibility (#34666)
Fix #25846 1. the ImageConfig can be empty, fall back to default 2. the blob size can be empty, it still needs "Content-Length" headerpull/34667/head^2
parent
b408bf2f0b
commit
7b39c82587
|
@ -4,6 +4,7 @@
|
|||
package container
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
@ -83,7 +84,8 @@ func ParseImageConfig(mt string, r io.Reader) (*Metadata, error) {
|
|||
|
||||
func parseOCIImageConfig(r io.Reader) (*Metadata, error) {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
oci "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseImageConfig(t *testing.T) {
|
||||
|
@ -59,3 +60,9 @@ func TestParseImageConfig(t *testing.T) {
|
|||
assert.Equal(t, projectURL, metadata.ProjectURL)
|
||||
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/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
container_module "code.gitea.io/gitea/modules/packages/container"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -50,7 +51,7 @@ type containerHeaders struct {
|
|||
Range string
|
||||
Location string
|
||||
ContentType string
|
||||
ContentLength int64
|
||||
ContentLength optional.Option[int64]
|
||||
}
|
||||
|
||||
// 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 != "" {
|
||||
resp.Header().Set("Content-Type", h.ContentType)
|
||||
}
|
||||
if h.ContentLength != 0 {
|
||||
resp.Header().Set("Content-Length", strconv.FormatInt(h.ContentLength, 10))
|
||||
if h.ContentLength.Has() {
|
||||
resp.Header().Set("Content-Length", strconv.FormatInt(h.ContentLength.Value(), 10))
|
||||
}
|
||||
if h.UploadUUID != "" {
|
||||
resp.Header().Set("Docker-Upload-Uuid", h.UploadUUID)
|
||||
|
@ -505,7 +506,7 @@ func HeadBlob(ctx *context.Context) {
|
|||
|
||||
setResponseHeaders(ctx.Resp, &containerHeaders{
|
||||
ContentDigest: blob.Properties.GetByName(container_module.PropertyDigest),
|
||||
ContentLength: blob.Blob.Size,
|
||||
ContentLength: optional.Some(blob.Blob.Size),
|
||||
Status: http.StatusOK,
|
||||
})
|
||||
}
|
||||
|
@ -644,7 +645,7 @@ func HeadManifest(ctx *context.Context) {
|
|||
setResponseHeaders(ctx.Resp, &containerHeaders{
|
||||
ContentDigest: manifest.Properties.GetByName(container_module.PropertyDigest),
|
||||
ContentType: manifest.Properties.GetByName(container_module.PropertyMediaType),
|
||||
ContentLength: manifest.Blob.Size,
|
||||
ContentLength: optional.Some(manifest.Blob.Size),
|
||||
Status: http.StatusOK,
|
||||
})
|
||||
}
|
||||
|
@ -708,14 +709,14 @@ func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor)
|
|||
headers := &containerHeaders{
|
||||
ContentDigest: pfd.Properties.GetByName(container_module.PropertyDigest),
|
||||
ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType),
|
||||
ContentLength: pfd.Blob.Size,
|
||||
ContentLength: optional.Some(pfd.Blob.Size),
|
||||
Status: http.StatusOK,
|
||||
}
|
||||
|
||||
if u != nil {
|
||||
headers.Status = http.StatusTemporaryRedirect
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
@ -623,6 +624,22 @@ func TestPackageContainer(t *testing.T) {
|
|||
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) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
|
|
Loading…
Reference in New Issue