From 9c0519c4ed1832328353d3cda9b90158878e62e1 Mon Sep 17 00:00:00 2001 From: Anton Tiurin Date: Tue, 3 Feb 2015 00:34:38 +0300 Subject: [PATCH] [Client] Fix error in parsing of 'Range' header. * Result of regexp.FindStringSubmatch must be checked to be not nil. Otherwise it leads to `index out of range`. * Range header regexp is compiled only once to speedup (5x) the header parsing. Signed-off-by: Anton Tiurin --- client/client.go | 17 ++++++++++++----- client/client_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/client/client.go b/client/client.go index dba57da1..f303512d 100644 --- a/client/client.go +++ b/client/client.go @@ -67,6 +67,10 @@ type Client interface { CancelBlobUpload(location string) error } +var ( + patternRangeHeader = regexp.MustCompile("bytes=0-(\\d+)/(\\d+)") +) + // New returns a new Client which operates against a registry with the // given base endpoint // This endpoint should not include /v2/ or any part of the url after this. @@ -553,15 +557,18 @@ func (r *clientImpl) CancelBlobUpload(location string) error { // parseRangeHeader parses out the offset and length from a returned Range // header func parseRangeHeader(byteRangeHeader string) (int, int, error) { - r := regexp.MustCompile("bytes=0-(\\d+)/(\\d+)") - submatches := r.FindStringSubmatch(byteRangeHeader) - offset, err := strconv.ParseInt(submatches[1], 10, 0) + submatches := patternRangeHeader.FindStringSubmatch(byteRangeHeader) + if submatches == nil || len(submatches) < 3 { + return 0, 0, fmt.Errorf("Malformed Range header") + } + + offset, err := strconv.Atoi(submatches[1]) if err != nil { return 0, 0, err } - length, err := strconv.ParseInt(submatches[2], 10, 0) + length, err := strconv.Atoi(submatches[2]) if err != nil { return 0, 0, err } - return int(offset), int(length), nil + return offset, length, nil } diff --git a/client/client_test.go b/client/client_test.go index 65e73f34..2c1d1cc2 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -19,6 +19,37 @@ type testBlob struct { contents []byte } +func TestRangeHeaderParser(t *testing.T) { + const ( + malformedRangeHeader = "bytes=0-A/C" + emptyRangeHeader = "" + rFirst = 100 + rSecond = 200 + ) + + var ( + wellformedRangeHeader = fmt.Sprintf("bytes=0-%d/%d", rFirst, rSecond) + ) + + if _, _, err := parseRangeHeader(malformedRangeHeader); err == nil { + t.Fatalf("malformedRangeHeader: error expected, got nil") + } + + if _, _, err := parseRangeHeader(emptyRangeHeader); err == nil { + t.Fatalf("emptyRangeHeader: error expected, got nil") + } + + first, second, err := parseRangeHeader(wellformedRangeHeader) + if err != nil { + t.Fatalf("wellformedRangeHeader: unexpected error %v", err) + } + + if first != rFirst || second != rSecond { + t.Fatalf("Range has been parsed unproperly: %d/%d", first, second) + } + +} + func TestPush(t *testing.T) { name := "hello/world" tag := "sometag"