diff --git a/doc/xml/release.xml b/doc/xml/release.xml
index dc4a8c26c..cfa5459a9 100644
--- a/doc/xml/release.xml
+++ b/doc/xml/release.xml
@@ -56,6 +56,14 @@
Add basic time management functions.
+
+
+
+
+
+
+ Add httpLastModifiedToTime()
to parse HTTP last-modified header.
+
diff --git a/src/Makefile.in b/src/Makefile.in
index 6cc94a579..4010b0e81 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -380,7 +380,7 @@ common/io/http/cache.o: common/io/http/cache.c build.auto.h common/assert.h comm
common/io/http/client.o: common/io/http/client.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/http/client.h common/io/http/common.h common/io/http/header.h common/io/http/query.h common/io/io.h common/io/read.h common/io/read.intern.h common/io/tls/client.h common/io/write.h common/log.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/param.h common/type/string.h common/type/stringList.h common/type/stringz.h common/type/variant.h common/type/variantList.h common/wait.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c common/io/http/client.c -o common/io/http/client.o
-common/io/http/common.o: common/io/http/common.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/http/common.h common/logLevel.h common/memContext.h common/stackTrace.h common/type/buffer.h common/type/convert.h common/type/string.h common/type/stringz.h
+common/io/http/common.o: common/io/http/common.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/http/common.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/string.h common/type/stringz.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c common/io/http/common.c -o common/io/http/common.o
common/io/http/header.o: common/io/http/header.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/http/header.h common/logLevel.h common/macro.h common/memContext.h common/object.h common/stackTrace.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/param.h common/type/string.h common/type/stringList.h common/type/stringz.h common/type/variant.h common/type/variantList.h
diff --git a/src/common/io/http/client.c b/src/common/io/http/client.c
index cda39fb56..034666bd2 100644
--- a/src/common/io/http/client.c
+++ b/src/common/io/http/client.c
@@ -32,6 +32,7 @@ STRING_EXTERN(HTTP_HEADER_CONTENT_MD5_STR, HTTP_HEADER_
#define HTTP_HEADER_TRANSFER_ENCODING "transfer-encoding"
STRING_STATIC(HTTP_HEADER_TRANSFER_ENCODING_STR, HTTP_HEADER_TRANSFER_ENCODING);
STRING_EXTERN(HTTP_HEADER_ETAG_STR, HTTP_HEADER_ETAG);
+STRING_EXTERN(HTTP_HEADER_LAST_MODIFIED_STR, HTTP_HEADER_LAST_MODIFIED);
#define HTTP_VALUE_CONNECTION_CLOSE "close"
STRING_STATIC(HTTP_VALUE_CONNECTION_CLOSE_STR, HTTP_VALUE_CONNECTION_CLOSE);
diff --git a/src/common/io/http/client.h b/src/common/io/http/client.h
index ac4faf613..6e29d6376 100644
--- a/src/common/io/http/client.h
+++ b/src/common/io/http/client.h
@@ -45,6 +45,8 @@ HTTP Constants
STRING_DECLARE(HTTP_HEADER_CONTENT_MD5_STR);
#define HTTP_HEADER_ETAG "etag"
STRING_DECLARE(HTTP_HEADER_ETAG_STR);
+#define HTTP_HEADER_LAST_MODIFIED "last-modified"
+ STRING_DECLARE(HTTP_HEADER_LAST_MODIFIED_STR);
#define HTTP_RESPONSE_CODE_FORBIDDEN 403
#define HTTP_RESPONSE_CODE_NOT_FOUND 404
diff --git a/src/common/io/http/common.c b/src/common/io/http/common.c
index 8c9b3cad4..8b90a8d4b 100644
--- a/src/common/io/http/common.c
+++ b/src/common/io/http/common.c
@@ -3,8 +3,52 @@ Http Common
***********************************************************************************************************************************/
#include "build.auto.h"
+#include
+
#include "common/debug.h"
#include "common/io/http/common.h"
+#include "common/time.h"
+
+/***********************************************************************************************************************************
+Convert the time using the format specified in https://tools.ietf.org/html/rfc7231#section-7.1.1.1 which is used by HTTP 1.1 (the
+only version we support).
+***********************************************************************************************************************************/
+time_t
+httpLastModifiedToTime(const String *lastModified)
+{
+ FUNCTION_TEST_BEGIN();
+ FUNCTION_TEST_PARAM(STRING, lastModified);
+ FUNCTION_TEST_END();
+
+ time_t result = 0;
+
+ MEM_CONTEXT_TEMP_BEGIN()
+ {
+ // Find the month
+ static const char *monthList[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+ const char *month = strPtr(strSubN(lastModified, 8, 3));
+ unsigned int monthIdx = 0;
+
+ for (; monthIdx < sizeof(monthList) / sizeof(char *); monthIdx++)
+ {
+ if (strcmp(month, monthList[monthIdx]) == 0)
+ break;
+ }
+
+ if (monthIdx == sizeof(monthList) / sizeof(char *))
+ THROW_FMT(FormatError, "invalid month '%s'", month);
+
+ // Convert to time_t
+ result = epochFromParts(
+ cvtZToInt(strPtr(strSubN(lastModified, 12, 4))), (int)monthIdx + 1, cvtZToInt(strPtr(strSubN(lastModified, 5, 2))),
+ cvtZToInt(strPtr(strSubN(lastModified, 17, 2))), cvtZToInt(strPtr(strSubN(lastModified, 20, 2))),
+ cvtZToInt(strPtr(strSubN(lastModified, 23, 2))));
+ }
+ MEM_CONTEXT_TEMP_END();
+
+ FUNCTION_TEST_RETURN(result);
+}
/***********************************************************************************************************************************
Encode string to conform with URI specifications
diff --git a/src/common/io/http/common.h b/src/common/io/http/common.h
index 6e458cb7f..926078b76 100644
--- a/src/common/io/http/common.h
+++ b/src/common/io/http/common.h
@@ -6,11 +6,16 @@ Http common functions.
#ifndef COMMON_IO_HTTP_COMMON_H
#define COMMON_IO_HTTP_COMMON_H
+#include
+
#include "common/type/string.h"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
+// Convert Last-Modified header to time_t
+time_t httpLastModifiedToTime(const String *lastModified);
+
String *httpUriEncode(const String *uri, bool path);
#endif
diff --git a/test/define.yaml b/test/define.yaml
index 23d41a437..674fc24a8 100644
--- a/test/define.yaml
+++ b/test/define.yaml
@@ -247,7 +247,7 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: io-http
- total: 5
+ total: 6
coverage:
common/io/http/cache: full
diff --git a/test/src/module/common/ioHttpTest.c b/test/src/module/common/ioHttpTest.c
index 159ec613c..539578a35 100644
--- a/test/src/module/common/ioHttpTest.c
+++ b/test/src/module/common/ioHttpTest.c
@@ -311,6 +311,16 @@ testRun(void)
TEST_RESULT_STR_Z(httpUriEncode(strNew("0-9_~/A Z.az"), true), "0-9_~/A%20Z.az", "path encoding");
}
+ // *****************************************************************************************************************************
+ if (testBegin("httpLastModifiedToTime()"))
+ {
+ TEST_ERROR(httpLastModifiedToTime(STRDEF("Wed, 21 Bog 2015 07:28:00 GMT")), FormatError, "invalid month 'Bog'");
+ TEST_ERROR(
+ httpLastModifiedToTime(STRDEF("Wed, 1 Oct 2015 07:28:00 GMT")), FormatError,
+ "unable to convert base 10 string ' 1' to int");
+ TEST_RESULT_INT(httpLastModifiedToTime(STRDEF("Wed, 21 Oct 2015 07:28:00 GMT")), 1445412480, "convert gmt datetime");
+ }
+
// *****************************************************************************************************************************
if (testBegin("HttpHeader"))
{