package uploads import ( "bytes" "context" "errors" "fmt" "github.com/sirupsen/logrus" "io" "net/http" "nf-repo/internal/interfaces" "nf-repo/internal/model" "nf-repo/internal/util/rerr" "nf-repo/internal/verify" "strconv" "sync" "time" ) type memUploader struct { lock sync.Mutex uploads map[string][]byte } func (m *memUploader) UploadId() string { m.lock.Lock() m.lock.Unlock() id := strconv.Itoa(int(time.Now().UnixNano())) m.uploads[id] = []byte{} return id } func (m *memUploader) Write(ctx context.Context, id string, reader io.ReadCloser, start, end int) (int, *rerr.RepositoryError) { m.lock.Lock() defer m.lock.Unlock() if start != len(m.uploads[id]) { return 0, &rerr.RepositoryError{ Status: http.StatusRequestedRangeNotSatisfiable, Code: "BLOB_UPLOAD_UNKNOWN", Message: "Your content range doesn't match what we have", } } l := bytes.NewBuffer(m.uploads[id]) size, err := io.Copy(l, reader) if err != nil { return 0, rerr.ErrInternal(err) } _ = size m.uploads[id] = l.Bytes() return len(m.uploads[id]), nil } func (m *memUploader) Done(ctx context.Context, bh interfaces.BlobHandler, id string, reader io.ReadCloser, contentLength int, repo string, hash model.Hash) *rerr.RepositoryError { size := verify.SizeUnknown if contentLength > 0 { size = len(m.uploads[id]) + contentLength } m.lock.Lock() defer m.lock.Unlock() in := io.NopCloser(io.MultiReader(bytes.NewBuffer(m.uploads[id]), reader)) vrc, err := verify.ReadCloser(in, int64(size), hash) if err != nil { return rerr.ErrInternal(err) } defer vrc.Close() if err := bh.Put(ctx, repo, hash, vrc); err != nil { if errors.As(err, &verify.Error{}) { logrus. WithField("path", "handleBlobs.Put"). WithField("repo", repo). WithField("hash", hash.String()). WithField("err", fmt.Sprintf("Digest mismatch: %v", err)).Debug() return rerr.ErrDigestMismatch } return rerr.ErrInternal(err) } m.uploads[id] = nil delete(m.uploads, id) return nil } func NewMemUploader() interfaces.UploadHandler { return &memUploader{ lock: sync.Mutex{}, uploads: make(map[string][]byte), } }