mirror of https://github.com/goharbor/harbor.git
upload blob by chunk should return 416
Signed-off-by: yminer <yminer@vmware.com>
This commit is contained in:
parent
aa4a142bc1
commit
a4f05e0fc8
|
@ -19,6 +19,8 @@ const (
|
|||
NotFoundCode = "NOT_FOUND"
|
||||
// ConflictCode ...
|
||||
ConflictCode = "CONFLICT"
|
||||
// RangeUnsatisfy = "RequestRange_Unsatisfy"
|
||||
RangeUnsatisfy = "RequestRange_Unsatisfy"
|
||||
// UnAuthorizedCode ...
|
||||
UnAuthorizedCode = "UNAUTHORIZED"
|
||||
// BadRequestCode ...
|
||||
|
|
|
@ -43,6 +43,7 @@ var (
|
|||
errors.ViolateForeignKeyConstraintCode: http.StatusPreconditionFailed,
|
||||
errors.PROJECTPOLICYVIOLATION: http.StatusPreconditionFailed,
|
||||
errors.GeneralCode: http.StatusInternalServerError,
|
||||
errors.RangeUnsatisfy: http.StatusRequestedRangeNotSatisfiable,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -15,15 +15,79 @@
|
|||
package blob
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/pkg/distribution"
|
||||
"github.com/goharbor/harbor/src/server/middleware"
|
||||
)
|
||||
|
||||
type blobUploadState struct {
|
||||
// name is the primary repository under which the blob will be linked.
|
||||
Name string
|
||||
|
||||
// UUID identifies the upload.
|
||||
UUID string
|
||||
|
||||
// offset contains the current progress of the upload.
|
||||
Offset int64
|
||||
|
||||
// StartedAt is the original start time of the upload.
|
||||
StartedAt time.Time
|
||||
}
|
||||
|
||||
// /v2/conformance/testrepo/blobs/uploads/96a6fe7e-6683-4dce-a0d5-80fdc50f3822?_state=PwxpQahplvWdosoKCWat7zK2PtCo_4pUEEAmWzV2YOl7Ik5hbWUiOiJjb25mb3JtYW5jZS90ZXN0cmVwbyIsIlVVSUQiOiI5NmE2ZmU3ZS02NjgzLTRkY2UtYTBkNS04MGZkYzUwZjM4MjIiLCJPZmZzZXQiOjAsIlN0YXJ0ZWRBdCI6IjIwMjQtMDMtMTBUMTU6MzA6MjMuMjE1MjU1MzVaIn0%3D
|
||||
func unpackUploadState(r *http.Request) (blobUploadState, error) {
|
||||
var state blobUploadState
|
||||
token := r.FormValue("_state")
|
||||
tokenBytes, err := base64.URLEncoding.DecodeString(token)
|
||||
if err != nil {
|
||||
return state, err
|
||||
}
|
||||
|
||||
secret := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer")
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
if len(tokenBytes) < mac.Size() {
|
||||
return state, err
|
||||
}
|
||||
macBytes := tokenBytes[:mac.Size()]
|
||||
messageBytes := tokenBytes[mac.Size():]
|
||||
|
||||
mac.Write(messageBytes)
|
||||
if !hmac.Equal(mac.Sum(nil), macBytes) {
|
||||
return state, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(messageBytes, &state); err != nil {
|
||||
return state, err
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func isDisorder(state *blobUploadState, r *http.Request) (bool, error) {
|
||||
cntRange := r.Header.Get("Content-Range")
|
||||
startstr := strings.Split(cntRange, "-")[0]
|
||||
offset := state.Offset
|
||||
|
||||
start, err := strconv.ParseInt(startstr, 10, 64)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if start > offset {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// PatchBlobUploadMiddleware middleware to record the accepted blob size for stream blob upload
|
||||
func PatchBlobUploadMiddleware() func(http.Handler) http.Handler {
|
||||
return middleware.AfterResponse(func(w http.ResponseWriter, r *http.Request, statusCode int) error {
|
||||
|
@ -31,6 +95,18 @@ func PatchBlobUploadMiddleware() func(http.Handler) http.Handler {
|
|||
if statusCode != http.StatusAccepted {
|
||||
return nil
|
||||
}
|
||||
//check if disorder when upload by chunk
|
||||
state, err := unpackUploadState(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
disorder, err := isDisorder(&state, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if disorder {
|
||||
return errors.New(nil).WithCode(errors.RangeUnsatisfy).WithMessage("Request Range is disordered")
|
||||
}
|
||||
|
||||
size, err := parseAcceptedBlobSize(w.Header().Get("Range"))
|
||||
if err != nil {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
set -e
|
||||
|
||||
echo "get the conformance testing code..."
|
||||
git clone https://github.com/opencontainers/distribution-spec.git
|
||||
# git clone https://github.com/opencontainers/distribution-spec.git
|
||||
|
||||
function createPro {
|
||||
echo "create testing project: $2"
|
||||
|
@ -31,4 +31,5 @@ export OCI_CROSSMOUNT_NAMESPACE="crossmount/testrepo"
|
|||
export OCI_AUTOMATIC_CROSSMOUNT="false"
|
||||
|
||||
cd ./distribution-spec/conformance
|
||||
git checkout tags/v1.1.0
|
||||
go test .
|
Loading…
Reference in New Issue