NetServices: Implement BHttpTime, parse_http_time() and format_http_time().
These utilities convert timestamp strings that are formatted according to the HTTP RFC into BDateTime objects, and vice versa. Change-Id: Ia2498944fb63d09233839f19d08f15d82a0a9685
This commit is contained in:
@@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Haiku, Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Niels Sascha Reedijk, [email protected]
|
||||||
|
*
|
||||||
|
* Corresponds to:
|
||||||
|
* headers/private/netservices2/HttpTime.h hrev?????
|
||||||
|
* src/kits/network/libnetservices2/HttpTime.cpp hrev?????
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#if __cplusplus >= 201703L
|
||||||
|
|
||||||
|
|
||||||
|
namespace BPrivate {
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\file HttpTime.h
|
||||||
|
\ingroup netservices
|
||||||
|
\brief Provides tools to parse and format HTTP dates.
|
||||||
|
|
||||||
|
The HTTP protocols prescribe that each HTTP response should have a \c Date header field with a
|
||||||
|
timestamp the response was generated. Optionally, there are other fields that may have a
|
||||||
|
timestamp in them, such as \c Set-Cookie or \c If-Modified-Since.
|
||||||
|
|
||||||
|
According to section 3.3 of RFC 2616, the standard date format is the format described by
|
||||||
|
RFC 1123, which updates the previous RFC 822. However, a proper implementation of a HTTP parser
|
||||||
|
may also want to support the RFC 850 format (which was obsoleted by RFC 1036) and the old
|
||||||
|
C-library standard date formatting of \c asctime().
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
<table>
|
||||||
|
<tr><td>RFC1123 / RFC822</td><td>Sun, 06 Nov 1994 08:49:37 GMT</td></tr>
|
||||||
|
<tr><td>RFC850</td><td>Sunday, 06-Nov-94 08:49:37 GMT</td></tr>
|
||||||
|
<tr><td>asctime</td><td>Sun Nov 6 08:49:37 1994</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
The tools in this module will make it easer to parse and format dates according to those
|
||||||
|
standards. When parsing the
|
||||||
|
|
||||||
|
You can use \ref parse_http_time() to parse a string that contains a HTTP timestamp. You can
|
||||||
|
use \ref format_http_time() to format the HTTP time according to the prescribed format. If you
|
||||||
|
want slightly more information about parsing, or if you want to hold an intermediate
|
||||||
|
representation of the timestamp, have a look at the \ref BPrivate::Network::BHttpTime class.
|
||||||
|
|
||||||
|
Note that when parsing a timestamp string, the tools are slightly more permissive than the
|
||||||
|
standards. For example, if the RFC 1123 timestamp does not have the GMT timezone indicator at
|
||||||
|
the end, it will still be accepted. Likewise, there is support for RFC 850 timestamps with a
|
||||||
|
4-digit year format. When formatting a \ref BPrivate::BDateTime to a string, it will always use
|
||||||
|
prescribed representation.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\enum BHttpTimeFormat
|
||||||
|
\brief Describes the three time formats supported by the HTTP RFC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class BHttpTime::InvalidInput
|
||||||
|
\ingroup netservices
|
||||||
|
\brief Error that indicates that a string cannot be parsed as a valid HTTP timestamp.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\var BString BHttpTime::InvalidInput::input
|
||||||
|
\brief Copy of the original timestamp that could not be parsed.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BHttpTime::InvalidInput::InvalidInput(const char *origin, BString input)
|
||||||
|
\brief Constructor that sets the \a origin and the invalid \a input.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class BHttpTime
|
||||||
|
\ingroup netservices
|
||||||
|
\brief Utility class that can parse and format HTTP Date strings.
|
||||||
|
|
||||||
|
See the description of the module in \ref HttpTime.h for more information about HTTP
|
||||||
|
timestamps.
|
||||||
|
|
||||||
|
Note that for quick conversions of a \ref BDateTime into a \ref BString and vice versa, you can
|
||||||
|
also use the \ref format_http_time() and \ref parse_http_time() utilities.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BHttpTime::BHttpTime() noexcept
|
||||||
|
\brief Constructs a new object and sets the timestamp to the current time.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BHttpTime::BHttpTime(BDateTime date)
|
||||||
|
\brief Constructs a new object and sets the timestamp to \a date.
|
||||||
|
|
||||||
|
\param date A valid \ref BDateTime object for the desired timestamp.
|
||||||
|
|
||||||
|
\exception BHttpTime::InvalidInput The \a date does not contain a valid timestamp.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BHttpTime::BHttpTime(const BString &dateString)
|
||||||
|
\brief Constructs a new object and parses the timestamp from \a dateString.
|
||||||
|
|
||||||
|
\param dateString A string that contains a valid HTTP timestamp. The \a dateString must not
|
||||||
|
contain any characters, other than the timestamp. It is up to the caller to sanitize any
|
||||||
|
input, including trimming whitespace at the beginning and end of the string.
|
||||||
|
|
||||||
|
\exception BHttpTime::InvalidInput The \a dateString cannot be parsed as a valid timestamp.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void BHttpTime::SetTo(BDateTime date)
|
||||||
|
\brief Set the current timestamp to \a date.
|
||||||
|
|
||||||
|
\param date A valid \ref BDateTime object for the desired timestamp.
|
||||||
|
|
||||||
|
\exception BHttpTime::InvalidInput The \a date does not contain a valid timestamp.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn void BPrivate::Network::BHttpTime::SetTo(const BString &string)
|
||||||
|
\brief Set the current timestamp by parsing \a string.
|
||||||
|
|
||||||
|
\param string A string that contains a valid HTTP timestamp. The \a dateString must not
|
||||||
|
contain any characters, other than the timestamp. It is up to the caller to sanitize any
|
||||||
|
input, including trimming whitespace at the beginning and end of the string.
|
||||||
|
|
||||||
|
\exception BHttpTime::InvalidInput The \a dateString cannot be parsed as a valid timestamp.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BDateTime BPrivate::Network::BHttpTime::DateTime() const noexcept
|
||||||
|
\brief Get the current timestamp.
|
||||||
|
|
||||||
|
\return A valid \ref BDateTime object that contains the timestamp that this object is currently
|
||||||
|
set to.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BHttpTimeFormat BHttpTime::DateTimeFormat() const noexcept
|
||||||
|
\brief Get the format that the current timestamp parsed from.
|
||||||
|
|
||||||
|
If the timestamp was parsed from a string, this method supplies a bit of information about what
|
||||||
|
format the original string was in. Note that for both the RFC 1132 and RFC 850 formats, the
|
||||||
|
parsing is slightly less strict than the RFC prescribes. This may mean that if you parse a
|
||||||
|
non-canonical string, and then format it back using the same format specifier, the two strings
|
||||||
|
may differ in content.
|
||||||
|
|
||||||
|
If the timestamp was set by setting it to a \ref BDateTime object, then this will always return
|
||||||
|
\c BHttpTimeFormat::RFC1123.
|
||||||
|
|
||||||
|
\return The \ref BHttpTimeFormat that describes the format the input string was in, or
|
||||||
|
\c BHttpTimeFormat::RFC1123 if the timestamp was set by a \ref BDateTime.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BString BHttpTime::ToString(BHttpTimeFormat outputFormat=BHttpTimeFormat::RFC1123) const
|
||||||
|
\brief Formats the timestamp to a string.
|
||||||
|
|
||||||
|
\param outputFormat The requested outputformat. The default is the recommended RFC 1123 format.
|
||||||
|
|
||||||
|
\return A string that contains the formatted timestamp.
|
||||||
|
|
||||||
|
\exception std::bad_alloc In the future this method may throw this exception when the memory
|
||||||
|
for the output string cannot be allocated.
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BString format_http_time(BDateTime timestamp,
|
||||||
|
BHttpTimeFormat outputFormat=BHttpTimeFormat::RFC1123)
|
||||||
|
\brief Format the \a timestamp into a string according to the \a outputFormat.
|
||||||
|
|
||||||
|
See the description of the module in \ref HttpTime.h for more information about HTTP
|
||||||
|
timestamps.
|
||||||
|
|
||||||
|
\param timestamp A valid \ref BDateTime object for the desired timestamp.
|
||||||
|
\param outputFormat The requested outputformat. The default is the recommended RFC 1123 format.
|
||||||
|
|
||||||
|
\return A string that contains the formatted timestamp.
|
||||||
|
|
||||||
|
\exception BHttpTime::InvalidInput The \a date does not contain a valid timestamp.
|
||||||
|
\exception std::bad_alloc In the future this method may throw this exception when the memory
|
||||||
|
for the output string cannot be allocated.
|
||||||
|
|
||||||
|
\see \ref BHttpTime \ref parse_http_time()
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn BDateTime parse_http_time(const BString &string)
|
||||||
|
\brief Parse a \a string that contains a timestamp and return a \ref BDateTime object.
|
||||||
|
|
||||||
|
See the description of the module in \ref HttpTime.h for more information about HTTP
|
||||||
|
timestamps.
|
||||||
|
|
||||||
|
\param string A string that contains a valid HTTP timestamp. The \a dateString must not
|
||||||
|
contain any characters, other than the timestamp. It is up to the caller to sanitize any
|
||||||
|
input, including trimming whitespace at the beginning and end of the string.
|
||||||
|
|
||||||
|
\exception BHttpTime::InvalidInput The \a string cannot be parsed as a valid timestamp.
|
||||||
|
|
||||||
|
\see \ref BHttpTime \ref format_http_time()
|
||||||
|
|
||||||
|
\since Haiku R1
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace BPrivate
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _B_HTTP_TIME_H_
|
||||||
|
#define _B_HTTP_TIME_H_
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#include <DateTime.h>
|
||||||
|
#include <ErrorsExt.h>
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
namespace BPrivate {
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
enum class BHttpTimeFormat : int8 {
|
||||||
|
RFC1123 = 0,
|
||||||
|
RFC850,
|
||||||
|
AscTime
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class BHttpTime {
|
||||||
|
public:
|
||||||
|
// Error type
|
||||||
|
class InvalidInput : public BError {
|
||||||
|
public:
|
||||||
|
InvalidInput(const char* origin, BString input);
|
||||||
|
|
||||||
|
virtual const char* Message() const noexcept override;
|
||||||
|
virtual BString DebugMessage() const override;
|
||||||
|
|
||||||
|
BString input;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
BHttpTime() noexcept;
|
||||||
|
BHttpTime(BDateTime date);
|
||||||
|
BHttpTime(const BString& dateString);
|
||||||
|
|
||||||
|
// Date modification
|
||||||
|
void SetTo(const BString& string);
|
||||||
|
void SetTo(BDateTime date);
|
||||||
|
|
||||||
|
|
||||||
|
// Date Access
|
||||||
|
BDateTime DateTime() const noexcept;
|
||||||
|
BHttpTimeFormat DateTimeFormat() const noexcept;
|
||||||
|
BString ToString(BHttpTimeFormat outputFormat = BHttpTimeFormat::RFC1123) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void _Parse(const BString& dateString);
|
||||||
|
|
||||||
|
BDateTime fDate;
|
||||||
|
BHttpTimeFormat fDateFormat;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Convenience functions
|
||||||
|
BDateTime parse_http_time(const BString& string);
|
||||||
|
BString format_http_time(BDateTime timestamp,
|
||||||
|
BHttpTimeFormat outputFormat = BHttpTimeFormat::RFC1123);
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
|
|
||||||
|
} // namespace BPrivate
|
||||||
|
|
||||||
|
#endif // _B_HTTP_TIME_H_
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 Haiku Inc. All rights reserved.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Christophe Huriaux, [email protected]
|
||||||
|
* Adrien Destugues, [email protected]
|
||||||
|
* Niels Sascha Reedijk, [email protected]
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <HttpTime.h>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
using namespace BPrivate::Network;
|
||||||
|
|
||||||
|
|
||||||
|
// The formats used should be, in order of preference (according to RFC2616,
|
||||||
|
// section 3.3):
|
||||||
|
// RFC1123 / RFC822: "Sun, 06 Nov 1994 08:49:37 GMT"
|
||||||
|
// RFC850 : "Sunday, 06-Nov-94 08:49:37 GMT" (obsoleted by RFC 1036)
|
||||||
|
// asctime : "Sun Nov 6 08:49:37 1994"
|
||||||
|
//
|
||||||
|
// RFC1123 is the preferred one because it has 4 digit years.
|
||||||
|
//
|
||||||
|
// But of course in real life, all possible mixes of the formats are used.
|
||||||
|
// Believe it or not, it's even possible to find some website that gets this
|
||||||
|
// right and use one of the 3 formats above.
|
||||||
|
// Often seen variants are:
|
||||||
|
// - RFC1036 but with 4 digit year,
|
||||||
|
// - Missing or different timezone indicator
|
||||||
|
// - Invalid weekday
|
||||||
|
static const std::list<std::pair<BHttpTimeFormat, const char*>> kDateFormats = {
|
||||||
|
// RFC822
|
||||||
|
{BHttpTimeFormat::RFC1123, "%a, %d %b %Y %H:%M:%S GMT"},// canonical
|
||||||
|
{BHttpTimeFormat::RFC1123, "%a, %d %b %Y %H:%M:%S"}, // without timezone
|
||||||
|
// Standard RFC850
|
||||||
|
{BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S GMT"}, // canonical
|
||||||
|
{BHttpTimeFormat::RFC850, "%A, %d-%b-%y %H:%M:%S"}, // without timezone
|
||||||
|
// RFC 850 with 4 digit year
|
||||||
|
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S"}, // without timezone
|
||||||
|
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S GMT"}, // with 4-digit year
|
||||||
|
{BHttpTimeFormat::RFC850, "%a, %d-%b-%Y %H:%M:%S UTC"}, // "UTC" timezone
|
||||||
|
// asctime
|
||||||
|
{BHttpTimeFormat::AscTime, "%a %b %e %H:%M:%S %Y"},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark BHttpTime::InvalidInput
|
||||||
|
|
||||||
|
|
||||||
|
BHttpTime::InvalidInput::InvalidInput(const char* origin, BString input)
|
||||||
|
:
|
||||||
|
BError(origin),
|
||||||
|
input(std::move(input))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
BHttpTime::InvalidInput::Message() const noexcept
|
||||||
|
{
|
||||||
|
if (input.IsEmpty())
|
||||||
|
return "A HTTP timestamp cannot be empty";
|
||||||
|
else
|
||||||
|
return "The HTTP timestamp string does not match the expected format";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
BHttpTime::InvalidInput::DebugMessage() const
|
||||||
|
{
|
||||||
|
BString output = BError::DebugMessage();
|
||||||
|
if (!input.IsEmpty())
|
||||||
|
output << ":\t " << input << "\n";
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark BHttpTime
|
||||||
|
|
||||||
|
|
||||||
|
BHttpTime::BHttpTime() noexcept
|
||||||
|
:
|
||||||
|
fDate(BDateTime::CurrentDateTime(B_GMT_TIME)),
|
||||||
|
fDateFormat(BHttpTimeFormat::RFC1123)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BHttpTime::BHttpTime(BDateTime date)
|
||||||
|
:
|
||||||
|
fDate(date),
|
||||||
|
fDateFormat(BHttpTimeFormat::RFC1123)
|
||||||
|
{
|
||||||
|
if (!fDate.IsValid())
|
||||||
|
throw InvalidInput(__PRETTY_FUNCTION__, "Invalid BDateTime object");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BHttpTime::BHttpTime(const BString& dateString)
|
||||||
|
:
|
||||||
|
fDate(0),
|
||||||
|
fDateFormat(BHttpTimeFormat::RFC1123)
|
||||||
|
{
|
||||||
|
_Parse(dateString);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark Date modification
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BHttpTime::SetTo(const BString& string)
|
||||||
|
{
|
||||||
|
_Parse(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BHttpTime::SetTo(BDateTime date)
|
||||||
|
{
|
||||||
|
if (!date.IsValid())
|
||||||
|
throw InvalidInput(__PRETTY_FUNCTION__, "Invalid BDateTime object");
|
||||||
|
|
||||||
|
fDate = date;
|
||||||
|
fDateFormat = BHttpTimeFormat::RFC1123;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark Date Access
|
||||||
|
|
||||||
|
|
||||||
|
BDateTime
|
||||||
|
BHttpTime::DateTime() const noexcept
|
||||||
|
{
|
||||||
|
return fDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BHttpTimeFormat
|
||||||
|
BHttpTime::DateTimeFormat() const noexcept
|
||||||
|
{
|
||||||
|
return fDateFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
BHttpTime::ToString(BHttpTimeFormat outputFormat) const
|
||||||
|
{
|
||||||
|
BString expirationFinal;
|
||||||
|
struct tm expirationTm = {};
|
||||||
|
expirationTm.tm_sec = fDate.Time().Second();
|
||||||
|
expirationTm.tm_min = fDate.Time().Minute();
|
||||||
|
expirationTm.tm_hour = fDate.Time().Hour();
|
||||||
|
expirationTm.tm_mday = fDate.Date().Day();
|
||||||
|
expirationTm.tm_mon = fDate.Date().Month() - 1;
|
||||||
|
expirationTm.tm_year = fDate.Date().Year() - 1900;
|
||||||
|
// strftime starts weekday count at 0 for Sunday,
|
||||||
|
// while DayOfWeek starts at 1 for Monday and thus uses 7 for Sunday
|
||||||
|
expirationTm.tm_wday = fDate.Date().DayOfWeek() % 7;
|
||||||
|
expirationTm.tm_yday = 0;
|
||||||
|
expirationTm.tm_isdst = 0;
|
||||||
|
|
||||||
|
for (auto& [format, formatString]: kDateFormats) {
|
||||||
|
if (format != outputFormat)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
static const uint16 kTimetToStringMaxLength = 128;
|
||||||
|
char expirationString[kTimetToStringMaxLength + 1];
|
||||||
|
size_t strLength;
|
||||||
|
|
||||||
|
strLength = strftime(expirationString, kTimetToStringMaxLength, formatString,
|
||||||
|
&expirationTm);
|
||||||
|
|
||||||
|
expirationFinal.SetTo(expirationString, strLength);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return expirationFinal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BHttpTime::_Parse(const BString& dateString)
|
||||||
|
{
|
||||||
|
if (dateString.Length() < 4)
|
||||||
|
throw InvalidInput(__PRETTY_FUNCTION__, dateString);
|
||||||
|
|
||||||
|
struct tm expireTime = {};
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for (auto& [format, formatString]: kDateFormats) {
|
||||||
|
const char* result = strptime(dateString.String(), formatString, &expireTime);
|
||||||
|
|
||||||
|
if (result == dateString.String() + dateString.Length()) {
|
||||||
|
fDateFormat = format;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did we identify some valid format?
|
||||||
|
if (!found)
|
||||||
|
throw InvalidInput(__PRETTY_FUNCTION__, dateString);
|
||||||
|
|
||||||
|
// Now convert the struct tm from strptime into a BDateTime.
|
||||||
|
BTime time(expireTime.tm_hour, expireTime.tm_min, expireTime.tm_sec);
|
||||||
|
BDate date(expireTime.tm_year + 1900, expireTime.tm_mon + 1,
|
||||||
|
expireTime.tm_mday);
|
||||||
|
fDate = BDateTime(date, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark Convenience Functions
|
||||||
|
|
||||||
|
|
||||||
|
BDateTime
|
||||||
|
BPrivate::Network::parse_http_time(const BString& string)
|
||||||
|
{
|
||||||
|
BHttpTime httpTime(string);
|
||||||
|
return httpTime.DateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BString
|
||||||
|
BPrivate::Network::format_http_time(BDateTime timestamp, BHttpTimeFormat outputFormat)
|
||||||
|
{
|
||||||
|
BHttpTime httpTime(timestamp);
|
||||||
|
return httpTime.ToString(outputFormat);
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
|||||||
HttpResult.cpp
|
HttpResult.cpp
|
||||||
HttpSession.cpp
|
HttpSession.cpp
|
||||||
HttpStream.cpp
|
HttpStream.cpp
|
||||||
|
HttpTime.cpp
|
||||||
NetServicesMisc.cpp
|
NetServicesMisc.cpp
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -13,20 +13,27 @@
|
|||||||
#include <cppunit/TestSuite.h>
|
#include <cppunit/TestSuite.h>
|
||||||
#include <tools/cppunit/ThreadedTestCaller.h>
|
#include <tools/cppunit/ThreadedTestCaller.h>
|
||||||
|
|
||||||
|
#include <DateTime.h>
|
||||||
#include <HttpFields.h>
|
#include <HttpFields.h>
|
||||||
#include <HttpRequest.h>
|
#include <HttpRequest.h>
|
||||||
#include <HttpResult.h>
|
#include <HttpResult.h>
|
||||||
#include <HttpStream.h>
|
#include <HttpStream.h>
|
||||||
|
#include <HttpTime.h>
|
||||||
#include <NetServicesDefs.h>
|
#include <NetServicesDefs.h>
|
||||||
#include <Url.h>
|
#include <Url.h>
|
||||||
|
|
||||||
|
using BPrivate::BDateTime;
|
||||||
using BPrivate::Network::BHttpFields;
|
using BPrivate::Network::BHttpFields;
|
||||||
using BPrivate::Network::BHttpMethod;
|
using BPrivate::Network::BHttpMethod;
|
||||||
using BPrivate::Network::BHttpRequest;
|
using BPrivate::Network::BHttpRequest;
|
||||||
|
using BPrivate::Network::BHttpRequestStream;
|
||||||
using BPrivate::Network::BHttpResult;
|
using BPrivate::Network::BHttpResult;
|
||||||
using BPrivate::Network::BHttpSession;
|
using BPrivate::Network::BHttpSession;
|
||||||
using BPrivate::Network::BHttpRequestStream;
|
using BPrivate::Network::BHttpTime;
|
||||||
|
using BPrivate::Network::BHttpTimeFormat;
|
||||||
using BPrivate::Network::BNetworkRequestError;
|
using BPrivate::Network::BNetworkRequestError;
|
||||||
|
using BPrivate::Network::format_http_time;
|
||||||
|
using BPrivate::Network::parse_http_time;
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
@@ -360,6 +367,52 @@ HttpProtocolTest::HttpRequestStreamTest()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
HttpProtocolTest::HttpTimeTest()
|
||||||
|
{
|
||||||
|
const std::vector<BString> kValidTimeStrings = {
|
||||||
|
"Sun, 07 Dec 2003 16:01:00 GMT",
|
||||||
|
"Sun, 07 Dec 2003 16:01:00",
|
||||||
|
"Sunday, 07-Dec-03 16:01:00 GMT",
|
||||||
|
"Sunday, 07-Dec-03 16:01:00 GMT",
|
||||||
|
"Sunday, 07-Dec-2003 16:01:00",
|
||||||
|
"Sunday, 07-Dec-2003 16:01:00 GMT",
|
||||||
|
"Sunday, 07-Dec-2003 16:01:00 UTC",
|
||||||
|
"Sun Dec 7 16:01:00 2003"
|
||||||
|
};
|
||||||
|
const BDateTime kExpectedDateTime = {BDate{2003, 12, 7}, BTime{16, 01, 0}};
|
||||||
|
|
||||||
|
for (const auto& timeString: kValidTimeStrings) {
|
||||||
|
CPPUNIT_ASSERT(kExpectedDateTime == parse_http_time(timeString));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<BString> kInvalidTimeStrings = {
|
||||||
|
"Sun, 07 Dec 2003", // Date only
|
||||||
|
"Sun, 07 Dec 2003 16:01:00 BST", // Invalid timezone
|
||||||
|
"On Sun, 07 Dec 2003 16:01:00 GMT", // Extra data in front of the string
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& timeString: kInvalidTimeStrings) {
|
||||||
|
try {
|
||||||
|
parse_http_time(timeString);
|
||||||
|
BString errorMessage = "Expected exception with invalid timestring: ";
|
||||||
|
errorMessage.Append(timeString);
|
||||||
|
CPPUNIT_FAIL(errorMessage.String());
|
||||||
|
} catch (const BHttpTime::InvalidInput& e) {
|
||||||
|
// expected exception; continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate format_http_time()
|
||||||
|
CPPUNIT_ASSERT_EQUAL(BString("Sun, 07 Dec 2003 16:01:00 GMT"),
|
||||||
|
format_http_time(kExpectedDateTime));
|
||||||
|
CPPUNIT_ASSERT_EQUAL(BString("Sunday, 07-Dec-03 16:01:00 GMT"),
|
||||||
|
format_http_time(kExpectedDateTime, BHttpTimeFormat::RFC850));
|
||||||
|
CPPUNIT_ASSERT_EQUAL(BString("Sun Dec 7 16:01:00 2003"),
|
||||||
|
format_http_time(kExpectedDateTime, BHttpTimeFormat::AscTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* static */ void
|
/* static */ void
|
||||||
HttpProtocolTest::AddTests(BTestSuite& parent)
|
HttpProtocolTest::AddTests(BTestSuite& parent)
|
||||||
{
|
{
|
||||||
@@ -373,6 +426,8 @@ HttpProtocolTest::AddTests(BTestSuite& parent)
|
|||||||
"HttpProtocolTest::HttpRequestTest", &HttpProtocolTest::HttpRequestTest));
|
"HttpProtocolTest::HttpRequestTest", &HttpProtocolTest::HttpRequestTest));
|
||||||
suite.addTest(new CppUnit::TestCaller<HttpProtocolTest>(
|
suite.addTest(new CppUnit::TestCaller<HttpProtocolTest>(
|
||||||
"HttpProtocolTest::HttpRequestStreamTest", &HttpProtocolTest::HttpRequestStreamTest));
|
"HttpProtocolTest::HttpRequestStreamTest", &HttpProtocolTest::HttpRequestStreamTest));
|
||||||
|
suite.addTest(new CppUnit::TestCaller<HttpProtocolTest>(
|
||||||
|
"HttpProtocolTest::HttpTimeTest", &HttpProtocolTest::HttpTimeTest));
|
||||||
|
|
||||||
parent.addTest("HttpProtocolTest", &suite);
|
parent.addTest("HttpProtocolTest", &suite);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public:
|
|||||||
void HttpMethodTest();
|
void HttpMethodTest();
|
||||||
void HttpRequestTest();
|
void HttpRequestTest();
|
||||||
void HttpRequestStreamTest();
|
void HttpRequestStreamTest();
|
||||||
|
void HttpTimeTest();
|
||||||
|
|
||||||
static void AddTests(BTestSuite& suite);
|
static void AddTests(BTestSuite& suite);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user