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")) {