Commit bddf6aae authored by Mathieu Stefani's avatar Mathieu Stefani

Added a Date and a Raw header. Raw headers will be used when the parser...

Added a Date and a Raw header. Raw headers will be used when the parser encounters a header that it does not know. Raw headers only store strings for now
parent 0ab17b40
......@@ -7,6 +7,8 @@
#include <cstring>
#include <iostream>
#include <stdexcept>
#include <ctime>
#include <iomanip>
#include "common.h"
#include "http.h"
#include "net.h"
......@@ -233,6 +235,10 @@ namespace Private {
header->parseRaw(cursor.offset(start), cursor.diff(start));
request->headers.add(header);
}
else {
std::string value(cursor.offset(start), cursor.diff(start));
request->headers.addRaw(Header::Raw(std::move(name), std::move(value)));
}
// CRLF
if (!cursor.advance(2)) return State::Again;
......
......@@ -6,11 +6,28 @@
#include "http_defs.h"
#include "common.h"
#include <iostream>
namespace Net {
namespace Http {
namespace {
bool parseRFC1123Date(std::tm& tm, const char* str, size_t len) {
char *p = strptime(str, "%a, %d %b %Y %H:%M:%S %Z", &tm);
return p != NULL;
}
bool parseRFC850Date(std::tm& tm, const char* str, size_t len) {
char *p = strptime(str, "%A, %d-%b-%y %H:%M:%S %Z", &tm);
return p != NULL;
}
bool parseAscTimeDate(std::tm& tm, const char* str, size_t len) {
char *p = strptime(str, "%a %b %d %H:%M:%S %Y", &tm);
}
} // anonymous namespace
CacheDirective::CacheDirective(Directive directive)
{
init(directive, std::chrono::seconds(0));
......@@ -58,6 +75,37 @@ CacheDirective::init(Directive directive, std::chrono::seconds delta)
}
}
FullDate::FullDate() {
std::memset(&date_, 0, sizeof date_);
}
FullDate
FullDate::fromRaw(const char* str, size_t len)
{
// As per the RFC, implementation MUST support all three formats.
std::tm tm = {};
if (parseRFC1123Date(tm, str, len)) {
return FullDate(tm);
}
memset(&tm, 0, sizeof tm);
if (parseRFC850Date(tm, str, len)) {
return FullDate(tm);
}
memset(&tm, 0, sizeof tm);
if (parseAscTimeDate(tm, str, len)) {
return FullDate(tm);
}
throw std::runtime_error("Invalid Date format");
}
FullDate
FullDate::fromString(const std::string& str) {
return FullDate::fromRaw(str.c_str(), str.size());
}
const char* methodString(Method method)
{
switch (method) {
......
......@@ -9,6 +9,7 @@
#include <ostream>
#include <stdexcept>
#include <chrono>
#include <ctime>
namespace Net {
......@@ -66,6 +67,32 @@ namespace Http {
CODE(503, Service_Unavailable, "Service Unavailable") \
CODE(504, Gateway_Timeout, "Gateway Timeout")
// 3.4. Character Sets
// See http://tools.ietf.org/html/rfc2978 and
// http://www.iana.org/assignments/character-sets/character-sets.xhtml
#define CHARSETS \
CHARSET(UsAscii, "us-ascii") \
CHARSET(Iso-8859-1, "iso-8859-1") \
CHARSET(Iso-8859-2, "iso-8859-2") \
CHARSET(Iso-8859-3, "iso-8859-3") \
CHARSET(Iso-8859-4, "iso-8859-4") \
CHARSET(Iso-8859-5, "iso-8859-5") \
CHARSET(Iso-8859-6, "iso-8859-6") \
CHARSET(Iso-8859-7, "iso-8859-7") \
CHARSET(Iso-8859-8, "iso-8859-8") \
CHARSET(Iso-8859-9, "iso-8859-9") \
CHARSET(Iso-8859-10, "iso-8859-10") \
CHARSET(Shift-JIS, "shift_jis") \
CHARSET(Utf7, "utf-7") \
CHARSET(Utf8, "utf-8") \
CHARSET(Utf16, "utf-16") \
CHARSET(Utf16-BE, "utf-16be") \
CHARSET(Utf16-LE, "utf-16le") \
CHARSET(Utf32, "utf-32") \
CHARSET(Utf32-BE, "utf-32be") \
CHARSET(Utf32-LE, "utf-32le") \
CHARSET(Unicode-11, "unicode-1-1")
enum class Method {
#define METHOD(m, _) m,
HTTP_METHODS
......@@ -109,6 +136,24 @@ private:
} data;
};
// 3.3.1 Full Date
class FullDate {
public:
FullDate();
FullDate(std::tm date)
: date_(date)
{ }
std::tm date() const { return date_; }
static FullDate fromRaw(const char* str, size_t len);
static FullDate fromString(const std::string& str);
private:
std::tm date_;
};
const char* methodString(Method method);
const char* codeString(Code code);
......
......@@ -261,6 +261,15 @@ ContentLength::write(std::ostream& os) const {
os << value_;
}
void
Date::parseRaw(const char* str, size_t len) {
fullDate_ = FullDate::fromRaw(str, len);
}
void
Date::write(std::ostream& os) const {
}
void
Host::parse(const std::string& data) {
auto pos = data.find(':');
......
......@@ -243,6 +243,25 @@ private:
};
class Date : public Header {
public:
NAME("Date")
Date() { }
explicit Date(const FullDate& date) :
fullDate_(date)
{ }
void parseRaw(const char* str, size_t len);
void write(std::ostream& os) const;
FullDate fullDate() const { return fullDate_; }
private:
FullDate fullDate_;
};
class Host : public Header {
public:
NAME("Host");
......@@ -304,6 +323,29 @@ private:
std::string ua_;
};
class Raw {
public:
Raw();
Raw(std::string name, std::string value)
: name_(std::move(name))
, value_(std::move(value))
{ }
Raw(const Raw& other) = default;
Raw& operator=(const Raw& other) = default;
Raw(Raw&& other) = default;
Raw& operator=(Raw&& other) = default;
std::string name() const { return name_; }
std::string value() const { return value_; }
private:
std::string name_;
std::string value_;
};
} // namespace Header
} // namespace Http
......
......@@ -21,10 +21,13 @@ namespace {
}
RegisterHeader(Accept);
RegisterHeader(CacheControl);
RegisterHeader(ContentEncoding);
RegisterHeader(ContentLength);
RegisterHeader(ContentType);
RegisterHeader(Date);
RegisterHeader(Host);
RegisterHeader(ContentEncoding);
RegisterHeader(Server);
RegisterHeader(UserAgent);
void
......@@ -73,6 +76,11 @@ Collection::add(const std::shared_ptr<Header>& header) {
return *this;
}
Collection&
Collection::addRaw(const Raw& raw) {
rawHeaders.insert(std::make_pair(raw.name(), raw));
}
std::shared_ptr<const Header>
Collection::get(const std::string& name) const {
auto header = getImpl(name);
......@@ -93,6 +101,16 @@ Collection::get(const std::string& name) {
return header.second;
}
Raw
Collection::getRaw(const std::string& name) const {
auto it = rawHeaders.find(name);
if (it == std::end(rawHeaders)) {
throw std::runtime_error("Could not find header");
}
return it->second;
}
std::shared_ptr<const Header>
Collection::tryGet(const std::string& name) const {
auto header = getImpl(name);
......@@ -109,6 +127,16 @@ Collection::tryGet(const std::string& name) {
return header.second;
}
Optional<Raw>
Collection::tryGetRaw(const std::string& name) const {
auto it = rawHeaders.find(name);
if (it == std::end(rawHeaders)) {
return None();
}
return Some(it->second);
}
bool
Collection::has(const std::string& name) const {
return getImpl(name).first;
......@@ -128,6 +156,7 @@ Collection::list() const {
void
Collection::clear() {
headers.clear();
rawHeaders.clear();
}
std::pair<bool, std::shared_ptr<Header>>
......
......@@ -51,12 +51,15 @@ public:
}
Collection& add(const std::shared_ptr<Header>& header);
Collection& addRaw(const Raw& raw);
std::shared_ptr<const Header> get(const std::string& name) const;
std::shared_ptr<Header> get(const std::string& name);
Raw getRaw(const std::string& name) const;
std::shared_ptr<const Header> tryGet(const std::string& name) const;
std::shared_ptr<Header> tryGet(const std::string& name);
Optional<Raw> tryGetRaw(const std::string& name) const;
template<typename H>
typename std::enable_if<
......@@ -75,6 +78,7 @@ private:
std::pair<bool, std::shared_ptr<Header>> getImpl(const std::string& name) const;
std::unordered_map<std::string, std::shared_ptr<Header>> headers;
std::unordered_map<std::string, Raw> rawHeaders;
};
struct Registry {
......
......@@ -168,6 +168,48 @@ TEST(headers_test, content_length) {
ASSERT_EQ(cl.value(), 3495);
}
TEST(headers_test, date_test) {
/* RFC-1123 */
Header::Date d1;
d1.parse("Sun, 06 Nov 1994 08:49:37 GMT");
auto fd1 = d1.fullDate();
auto dd1 = fd1.date();
ASSERT_EQ(dd1.tm_year, 94);
ASSERT_EQ(dd1.tm_mon, 10);
ASSERT_EQ(dd1.tm_mday, 6);
ASSERT_EQ(dd1.tm_hour, 8);
ASSERT_EQ(dd1.tm_min, 49);
ASSERT_EQ(dd1.tm_sec, 37);
/* RFC-850 */
Header::Date d2;
d2.parse("Sunday, 06-Nov-94 08:49:37 GMT");
auto fd2 = d2.fullDate();
auto dd2 = fd2.date();
ASSERT_EQ(dd2.tm_year, 94);
ASSERT_EQ(dd2.tm_mon, 10);
ASSERT_EQ(dd2.tm_mday, 6);
ASSERT_EQ(dd2.tm_hour, 8);
ASSERT_EQ(dd2.tm_min, 49);
ASSERT_EQ(dd2.tm_sec, 37);
/* ANSI C's asctime format */
Header::Date d3;
d3.parse("Sun Nov 6 08:49:37 1994");
auto fd3 = d3.fullDate();
auto dd3 = fd3.date();
ASSERT_EQ(dd3.tm_year, 94);
ASSERT_EQ(dd3.tm_mon, 10);
ASSERT_EQ(dd3.tm_mday, 6);
ASSERT_EQ(dd3.tm_hour, 8);
ASSERT_EQ(dd3.tm_min, 49);
ASSERT_EQ(dd3.tm_sec, 37);
}
TEST(headers_test, host) {
Header::Host host;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment