diff --git a/doc/xml/release.xml b/doc/xml/release.xml index cfa5459a9..a4bd72ecd 100644 --- a/doc/xml/release.xml +++ b/doc/xml/release.xml @@ -64,6 +64,14 @@

Add httpLastModifiedToTime() to parse HTTP last-modified header.

+ + + + + + +

Parse dates in storageS3InfoList() and storageS3Info().

+
diff --git a/src/storage/s3/storage.c b/src/storage/s3/storage.c index 4eb925732..2b5e165a6 100644 --- a/src/storage/s3/storage.c +++ b/src/storage/s3/storage.c @@ -4,7 +4,6 @@ S3 Storage #include "build.auto.h" #include -#include #include "common/crypto/hash.h" #include "common/encode.h" @@ -59,6 +58,7 @@ STRING_STATIC(S3_XML_TAG_CONTENTS_STR, "Contents"); STRING_STATIC(S3_XML_TAG_DELETE_STR, "Delete"); STRING_STATIC(S3_XML_TAG_ERROR_STR, "Error"); STRING_STATIC(S3_XML_TAG_KEY_STR, "Key"); +STRING_STATIC(S3_XML_TAG_LAST_MODIFIED_STR, "LastModified"); STRING_STATIC(S3_XML_TAG_MESSAGE_STR, "Message"); STRING_STATIC(S3_XML_TAG_NEXT_CONTINUATION_TOKEN_STR, "NextContinuationToken"); STRING_STATIC(S3_XML_TAG_OBJECT_STR, "Object"); @@ -574,6 +574,7 @@ storageS3Info(THIS_VOID, const String *file, StorageInterfaceInfoParam param) result.exists = true; result.type = storageTypeFile; result.size = cvtZToUInt64(strPtr(httpHeaderGet(httpResult.responseHeader, HTTP_HEADER_CONTENT_LENGTH_STR))); + result.timeModified = httpLastModifiedToTime(httpHeaderGet(httpResult.responseHeader, HTTP_HEADER_LAST_MODIFIED_STR)); } FUNCTION_LOG_RETURN(STORAGE_INFO, result); @@ -586,6 +587,22 @@ typedef struct StorageS3InfoListData void *callbackData; // User-supplied callback data } StorageS3InfoListData; +// Helper to convert YYYY-MM-DDTHH:MM:SS.MSECZ format to time_t. This format is very nearly ISO-8601 except for the inclusion of +// milliseconds which are discarded here. +static time_t +storageS3CvtTime(const String *time) +{ + FUNCTION_TEST_BEGIN(); + FUNCTION_TEST_PARAM(STRING, time); + FUNCTION_TEST_END(); + + FUNCTION_TEST_RETURN( + epochFromParts( + cvtZToInt(strPtr(strSubN(time, 0, 4))), cvtZToInt(strPtr(strSubN(time, 5, 2))), + cvtZToInt(strPtr(strSubN(time, 8, 2))), cvtZToInt(strPtr(strSubN(time, 11, 2))), + cvtZToInt(strPtr(strSubN(time, 14, 2))), cvtZToInt(strPtr(strSubN(time, 17, 2))))); +} + static void storageS3InfoListCallback(StorageS3 *this, void *callbackData, const String *name, StorageType type, const XmlNode *xml) { @@ -609,6 +626,8 @@ storageS3InfoListCallback(StorageS3 *this, void *callbackData, const String *nam .type = type, .name = name, .size = type == storageTypeFile ? cvtZToUInt64(strPtr(xmlNodeContent(xmlNodeChild(xml, S3_XML_TAG_SIZE_STR, true)))) : 0, + .timeModified = type == storageTypeFile ? + storageS3CvtTime(xmlNodeContent(xmlNodeChild(xml, S3_XML_TAG_LAST_MODIFIED_STR, true))) : 0, }; data->callback(data->callbackData, &info); diff --git a/test/src/module/storage/s3Test.c b/test/src/module/storage/s3Test.c index 279f39b41..dff7a94bb 100644 --- a/test/src/module/storage/s3Test.c +++ b/test/src/module/storage/s3Test.c @@ -200,7 +200,11 @@ testS3Server(void) // File exists harnessTlsServerExpect(testS3ServerRequest(HTTP_VERB_HEAD, "/subdir/file1.txt", NULL)); - harnessTlsServerReply(testS3ServerResponse(200, "OK", "content-length:9999", NULL)); + harnessTlsServerReply(testS3ServerResponse( + 200, "OK", + "content-length:9999\r\n" + "Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT", + NULL)); // InfoList() // ------------------------------------------------------------------------------------------------------------------------- @@ -213,6 +217,7 @@ testS3Server(void) "" " " " path/to/test_file" + " 2009-10-12T17:50:30.000Z" " 787" " " " " @@ -817,6 +822,7 @@ testRun(void) TEST_RESULT_BOOL(info.exists, true, " check exists"); TEST_RESULT_UINT(info.type, storageTypeFile, " check type"); TEST_RESULT_UINT(info.size, 9999, " check exists"); + TEST_RESULT_UINT(info.timeModified, 1445412480, " check time"); // InfoList() // ------------------------------------------------------------------------------------------------------------------------- @@ -833,6 +839,7 @@ testRun(void) TEST_RESULT_UINT(testStorageInfoList[0].type, storageTypePath, " check type"); TEST_RESULT_STR_Z(testStorageInfoList[1].name, "test_file", " check name"); TEST_RESULT_UINT(testStorageInfoList[1].size, 787, " check size"); + TEST_RESULT_UINT(testStorageInfoList[1].timeModified, 1255369830, " check time"); TEST_RESULT_UINT(testStorageInfoList[1].type, storageTypeFile, " check type"); // storageDriverList()