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()