@@ -93,6 +93,14 @@ type redirectError struct {
9393 Code int
9494}
9595
96+ type bytesCloser struct {
97+ * bytes.Reader
98+ }
99+
100+ func (r * bytesCloser ) Close () error {
101+ return nil
102+ }
103+
96104func (e redirectError ) Error () string { return fmt .Sprintf ("redirecting (%d): %s" , e .Code , e .Location ) }
97105
98106// errNotFound represents an error locating the blob.
@@ -115,6 +123,7 @@ func (m *memHandler) Stat(_ context.Context, _ string, h v1.Hash) (int64, error)
115123 }
116124 return int64 (len (b )), nil
117125}
126+
118127func (m * memHandler ) Get (_ context.Context , _ string , h v1.Hash ) (io.ReadCloser , error ) {
119128 m .lock .Lock ()
120129 defer m .lock .Unlock ()
@@ -123,8 +132,9 @@ func (m *memHandler) Get(_ context.Context, _ string, h v1.Hash) (io.ReadCloser,
123132 if ! found {
124133 return nil , errNotFound
125134 }
126- return io . NopCloser ( bytes .NewReader (b )) , nil
135+ return & bytesCloser { bytes .NewReader (b )} , nil
127136}
137+
128138func (m * memHandler ) Put (_ context.Context , _ string , h v1.Hash , rc io.ReadCloser ) error {
129139 m .lock .Lock ()
130140 defer m .lock .Unlock ()
@@ -137,6 +147,7 @@ func (m *memHandler) Put(_ context.Context, _ string, h v1.Hash, rc io.ReadClose
137147 m .m [h .String ()] = all
138148 return nil
139149}
150+
140151func (m * memHandler ) Delete (_ context.Context , _ string , h v1.Hash ) error {
141152 m .lock .Lock ()
142153 defer m .lock .Unlock ()
@@ -177,6 +188,7 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
177188 service := elem [len (elem )- 2 ]
178189 digest := req .URL .Query ().Get ("digest" )
179190 contentRange := req .Header .Get ("Content-Range" )
191+ rangeHeader := req .Header .Get ("Range" )
180192
181193 repo := req .URL .Host + path .Join (elem [1 :len (elem )- 2 ]... )
182194
@@ -265,8 +277,10 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
265277
266278 return regErrInternal (err )
267279 }
280+
268281 defer rc .Close ()
269282 r = rc
283+
270284 } else {
271285 tmp , err := b .blobHandler .Get (req .Context (), repo , h )
272286 if errors .Is (err , errNotFound ) {
@@ -287,9 +301,48 @@ func (b *blobs) handle(resp http.ResponseWriter, req *http.Request) *regError {
287301 r = & buf
288302 }
289303
290- resp .Header ().Set ("Content-Length" , fmt .Sprint (size ))
291- resp .Header ().Set ("Docker-Content-Digest" , h .String ())
292- resp .WriteHeader (http .StatusOK )
304+ if rangeHeader != "" {
305+ start , end := int64 (0 ), int64 (0 )
306+ if _ , err := fmt .Sscanf (rangeHeader , "bytes=%d-%d" , & start , & end ); err != nil {
307+ return & regError {
308+ Status : http .StatusRequestedRangeNotSatisfiable ,
309+ Code : "BLOB_UNKNOWN" ,
310+ Message : "We don't understand your Range" ,
311+ }
312+ }
313+
314+ n := (end + 1 ) - start
315+ if ra , ok := r .(io.ReaderAt ); ok {
316+ if end + 1 > size {
317+ return & regError {
318+ Status : http .StatusRequestedRangeNotSatisfiable ,
319+ Code : "BLOB_UNKNOWN" ,
320+ Message : fmt .Sprintf ("range end %d > %d size" , end + 1 , size ),
321+ }
322+ }
323+ r = io .NewSectionReader (ra , start , n )
324+ } else {
325+ if _ , err := io .CopyN (io .Discard , r , start ); err != nil {
326+ return & regError {
327+ Status : http .StatusRequestedRangeNotSatisfiable ,
328+ Code : "BLOB_UNKNOWN" ,
329+ Message : fmt .Sprintf ("Failed to discard %d bytes" , start ),
330+ }
331+ }
332+
333+ r = io .LimitReader (r , n )
334+ }
335+
336+ resp .Header ().Set ("Content-Range" , fmt .Sprintf ("bytes %d-%d/%d" , start , end , size ))
337+ resp .Header ().Set ("Content-Length" , fmt .Sprint (n ))
338+ resp .Header ().Set ("Docker-Content-Digest" , h .String ())
339+ resp .WriteHeader (http .StatusPartialContent )
340+ } else {
341+ resp .Header ().Set ("Content-Length" , fmt .Sprint (size ))
342+ resp .Header ().Set ("Docker-Content-Digest" , h .String ())
343+ resp .WriteHeader (http .StatusOK )
344+ }
345+
293346 io .Copy (resp , r )
294347 return nil
295348
0 commit comments