11package transport
22
33import (
4+ "bytes"
45 "context"
56 "crypto/sha256"
67 "encoding/hex"
@@ -11,7 +12,6 @@ import (
1112 "net/http/httptest"
1213 "net/url"
1314 "os"
14- "regexp"
1515 "strconv"
1616 "testing"
1717 "time"
@@ -23,50 +23,52 @@ import (
2323 "github.com/google/go-containerregistry/pkg/v1/types"
2424)
2525
26- var rangeRe = regexp .MustCompile (`bytes=(\d+)-(\d+)?` )
27-
2826func handleResumableLayer (data []byte , w http.ResponseWriter , r * http.Request , t * testing.T ) {
2927 if r .Method != http .MethodGet {
3028 w .WriteHeader (http .StatusMethodNotAllowed )
3129 return
3230 }
3331
34- contentRange := r .Header .Get ("Range" )
35- if contentRange == "" {
36- w .WriteHeader (http .StatusBadRequest )
37- return
38- }
39-
40- matches := rangeRe .FindStringSubmatch (contentRange )
41- if len (matches ) != 3 {
42- w .WriteHeader (http .StatusBadRequest )
43- return
44- }
45-
46- contentLength := int64 (len (data ))
47- start , err := strconv .ParseInt (matches [1 ], 10 , 64 )
48- if err != nil || start < 0 {
49- w .WriteHeader (http .StatusBadRequest )
50- return
51- }
32+ var (
33+ contentLength , start , end int64
34+ statusCode = http .StatusOK
35+ err error
36+ )
5237
53- if start >= int64 (contentLength ) {
54- w .WriteHeader (http .StatusRequestedRangeNotSatisfiable )
55- return
56- }
38+ contentLength = int64 (len (data ))
39+ end = contentLength - 1
40+ contentRange := r .Header .Get ("Range" )
41+ if contentRange != "" {
42+ matches := rangeRe .FindStringSubmatch (contentRange )
43+ if len (matches ) != 3 {
44+ w .WriteHeader (http .StatusBadRequest )
45+ return
46+ }
5747
58- var end = int64 (contentLength ) - 1
59- if matches [2 ] != "" {
60- end , err = strconv .ParseInt (matches [2 ], 10 , 64 )
61- if err != nil || end < 0 {
48+ if start , err = strconv .ParseInt (matches [1 ], 10 , 64 ); err != nil || start < 0 {
6249 w .WriteHeader (http .StatusBadRequest )
6350 return
6451 }
6552
66- if end >= int64 (contentLength ) {
53+ if start >= int64 (contentLength ) {
6754 w .WriteHeader (http .StatusRequestedRangeNotSatisfiable )
6855 return
6956 }
57+
58+ if matches [2 ] != "" {
59+ end , err = strconv .ParseInt (matches [2 ], 10 , 64 )
60+ if err != nil || end < 0 {
61+ w .WriteHeader (http .StatusBadRequest )
62+ return
63+ }
64+
65+ if end >= int64 (contentLength ) {
66+ w .WriteHeader (http .StatusRequestedRangeNotSatisfiable )
67+ return
68+ }
69+ }
70+
71+ statusCode = http .StatusPartialContent
7072 }
7173
7274 var currentContentLength = end - start + 1
@@ -91,14 +93,19 @@ func handleResumableLayer(data []byte, w http.ResponseWriter, r *http.Request, t
9193
9294 end = start + currentContentLength - 1
9395
94- w .Header ().Set ("Content-Range" , fmt .Sprintf ("bytes %d-%d/%d" , start , end , contentLength ))
95- w .Header ().Set ("Content-Length" , strconv .FormatInt (currentContentLength , 10 ))
96- w .WriteHeader (http .StatusPartialContent )
96+ if statusCode == http .StatusPartialContent {
97+ w .Header ().Set ("Content-Length" , strconv .FormatInt (currentContentLength , 10 ))
98+ w .Header ().Set ("Content-Range" , fmt .Sprintf ("bytes %d-%d/%d" , start , end , contentLength ))
99+ } else {
100+ w .Header ().Set ("Content-Length" , strconv .FormatInt (contentLength , 10 ))
101+ }
102+
103+ w .WriteHeader (statusCode )
97104 w .Write (data [start : end + 1 ])
98105 time .Sleep (time .Second )
99106}
100107
101- func resumableRequest (client * http.Client , url string , size int64 , digest string , overlap bool , t * testing.T ) {
108+ func resumableRequest (client * http.Client , url string , leading , trailing [] byte , size int64 , digest string , overlap bool , t * testing.T ) {
102109 req , err := http .NewRequest (http .MethodGet , url , http .NoBody )
103110 if err != nil {
104111 t .Fatalf ("http.NewRequest(): %v" , err )
@@ -108,6 +115,16 @@ func resumableRequest(client *http.Client, url string, size int64, digest string
108115 req .Header .Set ("X-Overlap" , "true" )
109116 }
110117
118+ if len (leading ) > 0 || len (trailing ) > 0 {
119+ var buf bytes.Buffer
120+ buf .WriteString ("bytes=" )
121+ buf .WriteString (fmt .Sprintf ("%d-" , len (leading )))
122+ if len (trailing ) > 0 {
123+ buf .WriteString (fmt .Sprintf ("%d" , size - int64 (len (trailing ))- 1 ))
124+ }
125+ req .Header .Set ("Range" , buf .String ())
126+ }
127+
111128 resp , err := client .Do (req .WithContext (t .Context ()))
112129 if err != nil {
113130 t .Fatalf ("client.Do(): %v" , err )
@@ -120,12 +137,19 @@ func resumableRequest(client *http.Client, url string, size int64, digest string
120137 }
121138
122139 hash := sha256 .New ()
140+ if len (leading ) > 0 {
141+ io .Copy (hash , bytes .NewReader (leading ))
142+ }
123143
124144 if _ , err = io .Copy (hash , resp .Body ); err != nil {
125145 t .Errorf ("unexpected error: %v" , err )
126146 return
127147 }
128148
149+ if len (trailing ) > 0 {
150+ io .Copy (hash , bytes .NewReader (trailing ))
151+ }
152+
129153 actualDigest := "sha256:" + hex .EncodeToString (hash .Sum (nil ))
130154
131155 if actualDigest != digest {
@@ -248,23 +272,40 @@ func TestResumableTransport(t *testing.T) {
248272 }
249273
250274 tests := []struct {
251- name string
252- digest string
253- size int64
254- timeout bool
255- cancel bool
256- nonResumable bool
257- overlap bool
275+ name string
276+ digest string
277+ leading , trailing int64
278+ timeout bool
279+ cancel bool
280+ nonResumable bool
281+ overlap bool
282+ ranged bool
258283 }{
259284 {
260- name : "resumable" ,
261- digest : digest .String (),
262- size : size ,
285+ name : "resumable" ,
286+ digest : digest .String (),
287+ leading : 0 ,
288+ },
289+ {
290+ name : "resumable-range-leading" ,
291+ digest : digest .String (),
292+ leading : 3 ,
293+ },
294+ {
295+ name : "resumable-range-trailing" ,
296+ digest : digest .String (),
297+ leading : 0 ,
298+ },
299+ {
300+ name : "resumable-range-leading-trailing" ,
301+ digest : digest .String (),
302+ leading : 3 ,
303+ trailing : 6 ,
263304 },
264305 {
265306 name : "resumable-overlap" ,
266307 digest : digest .String (),
267- size : size ,
308+ leading : 0 ,
268309 overlap : true ,
269310 },
270311 {
@@ -290,8 +331,8 @@ func TestResumableTransport(t *testing.T) {
290331 resumableStopByCancelRequest (client , url , t )
291332 } else if tt .timeout {
292333 resumableStopByTimeoutRequest (client , url , t )
293- } else if tt .digest != "" && tt . size > 0 {
294- resumableRequest (client , url , tt .size , tt .digest , tt .overlap , t )
334+ } else if tt .digest != "" {
335+ resumableRequest (client , url , data [: tt .leading ], data [ size - tt . trailing :], size , tt .digest , tt .overlap , t )
295336 }
296337 })
297338 }
0 commit comments