Skip to content

Commit

Permalink
fix: support Last-Modified to parse full RFC7231 compliant format (#1575
Browse files Browse the repository at this point in the history
)

fixes #1566
  • Loading branch information
harshavardhana authored Oct 18, 2021
1 parent d5d9452 commit 56f3625
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 5 deletions.
42 changes: 37 additions & 5 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ var expirationRegex = regexp.MustCompile(`expiry-date="(.*?)", rule-id="(.*?)"`)

func amzExpirationToExpiryDateRuleID(expiration string) (time.Time, string) {
if matches := expirationRegex.FindStringSubmatch(expiration); len(matches) == 3 {
expTime, err := time.Parse(http.TimeFormat, matches[1])
expTime, err := parseRFC7231Time(matches[1])
if err != nil {
return time.Time{}, ""
}
Expand All @@ -73,7 +73,7 @@ func amzRestoreToStruct(restore string) (ongoing bool, expTime time.Time, err er
return false, time.Time{}, err
}
if matches[3] != "" {
expTime, err = time.Parse(http.TimeFormat, matches[3])
expTime, err = parseRFC7231Time(matches[3])
if err != nil {
return false, time.Time{}, err
}
Expand Down Expand Up @@ -240,6 +240,27 @@ func extractObjMetadata(header http.Header) http.Header {
return filteredHeader
}

const (
// RFC 7231#section-7.1.1.1 timetamp format. e.g Tue, 29 Apr 2014 18:30:38 GMT
rfc822TimeFormat = "Mon, 2 Jan 2006 15:04:05 GMT"
rfc822TimeFormatSingleDigitDay = "Mon, _2 Jan 2006 15:04:05 GMT"
rfc822TimeFormatSingleDigitDayTwoDigitYear = "Mon, _2 Jan 06 15:04:05 GMT"
)

func parseTime(t string, formats ...string) (time.Time, error) {
for _, format := range formats {
tt, err := time.Parse(format, t)
if err == nil {
return tt, nil
}
}
return time.Time{}, fmt.Errorf("unable to parse %s in any of the input formats: %s", t, formats)
}

func parseRFC7231Time(lastModified string) (time.Time, error) {
return parseTime(lastModified, rfc822TimeFormat, rfc822TimeFormatSingleDigitDay, rfc822TimeFormatSingleDigitDayTwoDigitYear)
}

// ToObjectInfo converts http header values into ObjectInfo type,
// extracts metadata and fills in all the necessary fields in ObjectInfo.
func ToObjectInfo(bucketName string, objectName string, h http.Header) (ObjectInfo, error) {
Expand Down Expand Up @@ -267,7 +288,7 @@ func ToObjectInfo(bucketName string, objectName string, h http.Header) (ObjectIn
}

// Parse Last-Modified has http time format.
date, err := time.Parse(http.TimeFormat, h.Get("Last-Modified"))
mtime, err := parseRFC7231Time(h.Get("Last-Modified"))
if err != nil {
return ObjectInfo{}, ErrorResponse{
Code: "InternalError",
Expand All @@ -289,7 +310,18 @@ func ToObjectInfo(bucketName string, objectName string, h http.Header) (ObjectIn
expiryStr := h.Get("Expires")
var expiry time.Time
if expiryStr != "" {
expiry, _ = time.Parse(http.TimeFormat, expiryStr)
expiry, err = parseRFC7231Time(expiryStr)
if err != nil {
return ObjectInfo{}, ErrorResponse{
Code: "InternalError",
Message: fmt.Sprintf("'Expiry' is not in supported format: %v", err),
BucketName: bucketName,
Key: objectName,
RequestID: h.Get("x-amz-request-id"),
HostID: h.Get("x-amz-id-2"),
Region: h.Get("x-amz-bucket-region"),
}
}
}

metadata := extractObjMetadata(h)
Expand Down Expand Up @@ -337,7 +369,7 @@ func ToObjectInfo(bucketName string, objectName string, h http.Header) (ObjectIn
ETag: etag,
Key: objectName,
Size: size,
LastModified: date,
LastModified: mtime,
ContentType: contentType,
Expires: expiry,
VersionID: h.Get(amzVersionID),
Expand Down
40 changes: 40 additions & 0 deletions utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,46 @@ import (
"github.com/minio/minio-go/v7/pkg/s3utils"
)

func TestParseRFC7231Time(t *testing.T) {
testCases := []struct {
timeStr string
expectedSuccess bool
}{
{
timeStr: "Sun, 2 Jan 2000 20:34:56 GMT",
expectedSuccess: true,
},
{
timeStr: "Sun, 02 Jan 2000 20:34:56 GMT",
expectedSuccess: true,
},
{
timeStr: "Sun, 2 Jan 00 20:34:56 GMT",
expectedSuccess: true,
},
{
timeStr: "Sun, 02 Jan 00 20:34:56 GMT",
expectedSuccess: true,
},
{
timeStr: "Su, 2 Jan 00 20:34:56 GMT",
expectedSuccess: false,
},
}
for _, testCase := range testCases {
testCase := testCase
t.Run("", func(t *testing.T) {
_, err := parseRFC7231Time(testCase.timeStr)
if err != nil && testCase.expectedSuccess {
t.Errorf("expected success found failure %v", err)
}
if err == nil && !testCase.expectedSuccess {
t.Errorf("expected failure found success")
}
})
}
}

// Tests signature redacting function used
// in filtering on-wire Authorization header.
func TestRedactSignature(t *testing.T) {
Expand Down

0 comments on commit 56f3625

Please sign in to comment.