Commit b63025ad authored by Mathieu Stefani's avatar Mathieu Stefani

Moved some definitions inside an independent http_defs header. Also implemented some more headers

parent 8e503d63
......@@ -7,6 +7,7 @@ set(SOURCE_FILES
http.cc
http_header.cc
http_headers.cc
http_defs.cc
mime.cc
)
......
......@@ -339,42 +339,6 @@ namespace Private {
} // namespace Private
const char* methodString(Method method)
{
switch (method) {
#define METHOD(name, str) \
case Method::name: \
return str;
HTTP_METHODS
#undef METHOD
}
unreachable();
}
const char* codeString(Code code)
{
switch (code) {
#define CODE(_, name, str) \
case Code::name: \
return str;
STATUS_CODES
#undef CODE
}
return "";
}
HttpError::HttpError(Code code, std::string reason)
: code_(static_cast<int>(code))
, reason_(std::move(reason))
{ }
HttpError::HttpError(int code, std::string reason)
: code_(code)
, reason_(std::move(reason))
{ }
Message::Message()
: version(Version::Http11)
{ }
......
......@@ -12,100 +12,12 @@
#include "listener.h"
#include "net.h"
#include "http_headers.h"
#include "http_defs.h"
namespace Net {
namespace Http {
#define HTTP_METHODS \
METHOD(Options, "OPTIONS") \
METHOD(Get, "GET") \
METHOD(Post, "POST") \
METHOD(Head, "HEAD") \
METHOD(Put, "PUT") \
METHOD(Delete, "DELETE") \
METHOD(Trace, "TRACE") \
METHOD(Connect, "CONNECT")
// 10. Status Code Definitions
#define STATUS_CODES \
CODE(100, Continue, "Continue") \
CODE(101, Switching_Protocols, "Switching Protocols") \
CODE(200, Ok, "OK") \
CODE(201, Created, "Created") \
CODE(202, Accepted, "Accepted") \
CODE(203, NonAuthoritative_Information, "Non-Authoritative Information") \
CODE(204, No_Content, "No Content") \
CODE(205, Reset_Content, "Reset Content") \
CODE(206, Partial_Content, "Partial Content") \
CODE(300, Multiple_Choices, "Multiple Choices") \
CODE(301, Moved_Permanently, "Moved Permanently") \
CODE(302, Found, "Found") \
CODE(303, See_Other, "See Other") \
CODE(304, Not_Modified, "Not Modified") \
CODE(305, Use_Proxy, "Use Proxy") \
CODE(307, Temporary_Redirect, "Temporary Redirect") \
CODE(400, Bad_Request, "Bad Request") \
CODE(401, Unauthorized, "Unauthorized") \
CODE(402, Payment_Required, "Payment Required") \
CODE(403, Forbidden, "Forbidden") \
CODE(404, Not_Found, "Not Found") \
CODE(405, Method_Not_Allowed, "Method Not Allowed") \
CODE(406, Not_Acceptable, "Not Acceptable") \
CODE(407, Proxy_Authentication_Required, "Proxy Authentication Required") \
CODE(408, Request_Timeout, "Request Timeout") \
CODE(409, Conflict, "Conflict") \
CODE(410, Gone, "Gone") \
CODE(411, Length_Required, "Length Required") \
CODE(412, Precondition_Failed, "Precondition Failed") \
CODE(413, Request_Entity_Too_Large, "Request Entity Too Large") \
CODE(414, RequestURI_Too_Long, "Request-URI Too Long") \
CODE(415, Unsupported_Media_Type, "Unsupported Media Type") \
CODE(416, Requested_Range_Not_Satisfiable, "Requested Range Not Satisfiable") \
CODE(417, Expectation_Failed, "Expectation Failed") \
CODE(500, Internal_Server_Error, "Internal Server Error") \
CODE(501, Not_Implemented, "Not Implemented") \
CODE(502, Bad_Gateway, "Bad Gateway") \
CODE(503, Service_Unavailable, "Service Unavailable") \
CODE(504, Gateway_Timeout, "Gateway Timeout")
enum class Method {
#define METHOD(m, _) m,
HTTP_METHODS
#undef METHOD
};
enum class Code {
#define CODE(value, name, _) name = value,
STATUS_CODES
#undef CODE
};
enum class Version {
Http10, // HTTP/1.0
Http11 // HTTP/1.1
};
const char* methodString(Method method);
const char* codeString(Code code);
struct HttpError : public std::exception {
HttpError(Code code, std::string reason);
HttpError(int code, std::string reason);
~HttpError() noexcept { }
const char* what() const noexcept { return reason_.c_str(); }
int code() const { return code_; }
std::string reason() const { return reason_; }
private:
int code_;
std::string reason_;
};
// 4. HTTP Message
class Message {
public:
......
/* http_defs.cc
Mathieu Stefani, 01 September 2015
Implementation of http definitions
*/
#include "http_defs.h"
#include "common.h"
namespace Net {
namespace Http {
CacheDirective::CacheDirective(Directive directive)
{
init(directive, std::chrono::seconds(0));
}
CacheDirective::CacheDirective(Directive directive, std::chrono::seconds delta)
{
init(directive, delta);
}
std::chrono::seconds
CacheDirective::delta() const
{
switch (directive_) {
case MaxAge:
return std::chrono::seconds(data.maxAge);
case SMaxAge:
return std::chrono::seconds(data.sMaxAge);
case MaxStale:
return std::chrono::seconds(data.maxStale);
case MinFresh:
return std::chrono::seconds(data.minFresh);
}
throw std::domain_error("Invalid operation on cache directive");
}
void
CacheDirective::init(Directive directive, std::chrono::seconds delta)
{
directive_ = directive;
switch (directive) {
case MaxAge:
data.maxAge = delta.count();
break;
case SMaxAge:
data.sMaxAge = delta.count();
break;
case MaxStale:
data.maxStale = delta.count();
break;
case MinFresh:
data.minFresh = delta.count();
break;
}
}
const char* methodString(Method method)
{
switch (method) {
#define METHOD(name, str) \
case Method::name: \
return str;
HTTP_METHODS
#undef METHOD
}
unreachable();
}
const char* codeString(Code code)
{
switch (code) {
#define CODE(_, name, str) \
case Code::name: \
return str;
STATUS_CODES
#undef CODE
}
return "";
}
std::ostream& operator<<(std::ostream& os, Method method) {
os << methodString(method);
return os;
}
std::ostream& operator<<(std::ostream& os, Code code) {
os << codeString(code);
}
HttpError::HttpError(Code code, std::string reason)
: code_(static_cast<int>(code))
, reason_(std::move(reason))
{ }
HttpError::HttpError(int code, std::string reason)
: code_(code)
, reason_(std::move(reason))
{ }
} // namespace Http
} // namespace Net
/* http_defs.h
Mathieu Stefani, 01 September 2015
Various http definitions
*/
#pragma once
#include <string>
#include <ostream>
#include <stdexcept>
#include <chrono>
namespace Net {
namespace Http {
#define HTTP_METHODS \
METHOD(Options, "OPTIONS") \
METHOD(Get, "GET") \
METHOD(Post, "POST") \
METHOD(Head, "HEAD") \
METHOD(Put, "PUT") \
METHOD(Delete, "DELETE") \
METHOD(Trace, "TRACE") \
METHOD(Connect, "CONNECT")
// 10. Status Code Definitions
#define STATUS_CODES \
CODE(100, Continue, "Continue") \
CODE(101, Switching_Protocols, "Switching Protocols") \
CODE(200, Ok, "OK") \
CODE(201, Created, "Created") \
CODE(202, Accepted, "Accepted") \
CODE(203, NonAuthoritative_Information, "Non-Authoritative Information") \
CODE(204, No_Content, "No Content") \
CODE(205, Reset_Content, "Reset Content") \
CODE(206, Partial_Content, "Partial Content") \
CODE(300, Multiple_Choices, "Multiple Choices") \
CODE(301, Moved_Permanently, "Moved Permanently") \
CODE(302, Found, "Found") \
CODE(303, See_Other, "See Other") \
CODE(304, Not_Modified, "Not Modified") \
CODE(305, Use_Proxy, "Use Proxy") \
CODE(307, Temporary_Redirect, "Temporary Redirect") \
CODE(400, Bad_Request, "Bad Request") \
CODE(401, Unauthorized, "Unauthorized") \
CODE(402, Payment_Required, "Payment Required") \
CODE(403, Forbidden, "Forbidden") \
CODE(404, Not_Found, "Not Found") \
CODE(405, Method_Not_Allowed, "Method Not Allowed") \
CODE(406, Not_Acceptable, "Not Acceptable") \
CODE(407, Proxy_Authentication_Required, "Proxy Authentication Required") \
CODE(408, Request_Timeout, "Request Timeout") \
CODE(409, Conflict, "Conflict") \
CODE(410, Gone, "Gone") \
CODE(411, Length_Required, "Length Required") \
CODE(412, Precondition_Failed, "Precondition Failed") \
CODE(413, Request_Entity_Too_Large, "Request Entity Too Large") \
CODE(414, RequestURI_Too_Long, "Request-URI Too Long") \
CODE(415, Unsupported_Media_Type, "Unsupported Media Type") \
CODE(416, Requested_Range_Not_Satisfiable, "Requested Range Not Satisfiable") \
CODE(417, Expectation_Failed, "Expectation Failed") \
CODE(500, Internal_Server_Error, "Internal Server Error") \
CODE(501, Not_Implemented, "Not Implemented") \
CODE(502, Bad_Gateway, "Bad Gateway") \
CODE(503, Service_Unavailable, "Service Unavailable") \
CODE(504, Gateway_Timeout, "Gateway Timeout")
enum class Method {
#define METHOD(m, _) m,
HTTP_METHODS
#undef METHOD
};
enum class Code {
#define CODE(value, name, _) name = value,
STATUS_CODES
#undef CODE
};
enum class Version {
Http10, // HTTP/1.0
Http11 // HTTP/1.1
};
class CacheDirective {
public:
enum Directive { NoCache, NoStore, MaxAge, MaxStale, MinFresh,
NoTransform, OnlyIfCached,
Public, Private, MustRevalidate, ProxyRevalidate, SMaxAge,
Ext };
CacheDirective() { }
CacheDirective(Directive directive);
CacheDirective(Directive directive, std::chrono::seconds delta);
Directive directive() const { return directive_; }
std::chrono::seconds delta() const;
private:
void init(Directive directive, std::chrono::seconds delta);
Directive directive_;
// Poor way of representing tagged unions in C++
union {
struct { uint64_t maxAge; };
struct { uint64_t sMaxAge; };
struct { uint64_t maxStale; };
struct { uint64_t minFresh; };
} data;
};
const char* methodString(Method method);
const char* codeString(Code code);
std::ostream& operator<<(std::ostream& os, Method method);
std::ostream& operator<<(std::ostream& os, Code code);
struct HttpError : public std::exception {
HttpError(Code code, std::string reason);
HttpError(int code, std::string reason);
~HttpError() noexcept { }
const char* what() const noexcept { return reason_.c_str(); }
int code() const { return code_; }
std::string reason() const { return reason_; }
private:
int code_;
std::string reason_;
};
} // namespace Http
} // namespace Net
......@@ -47,6 +47,187 @@ Header::parseRaw(const char *str, size_t len) {
parse(std::string(str, len));
}
void
Allow::parseRaw(const char* str, size_t len) {
}
void
Allow::write(std::ostream& os) const {
/* This puts an extra ',' at the end :/
std::copy(std::begin(methods_), std::end(methods_),
std::ostream_iterator<Http::Method>(os, ", "));
*/
for (std::vector<Http::Method>::size_type i = 0; i < methods_.size(); ++i) {
os << methods_[i];
if (i < methods_.size() - 1) os << ", ";
}
}
void
Allow::addMethod(Http::Method method) {
methods_.push_back(method);
}
void
Allow::addMethods(std::initializer_list<Method> methods) {
std::copy(std::begin(methods), std::end(methods), std::back_inserter(methods_));
}
void
Allow::addMethods(const std::vector<Http::Method>& methods)
{
std::copy(std::begin(methods), std::end(methods), std::back_inserter(methods_));
}
CacheControl::CacheControl(Http::CacheDirective directive)
{
directives_.push_back(directive);
}
void
CacheControl::parseRaw(const char* str, size_t len) {
using Http::CacheDirective;
auto eof = [&](const char *p) {
return p - str == len;
};
#define MAX_SIZE(s) \
std::min(sizeof(s) - 1, len - (begin - str))
#define TRY_PARSE_TRIVIAL_DIRECTIVE(dstr, directive) \
if (memcmp(begin, dstr, MAX_SIZE(dstr)) == 0) { \
directives_.push_back(CacheDirective(CacheDirective::directive)); \
begin += sizeof(dstr) - 1; \
break; \
} \
(void) 0
// @Todo: check for overflow
#define TRY_PARSE_TIMED_DIRECTIVE(dstr, directive) \
if (memcmp(begin, dstr, MAX_SIZE(dstr)) == 0) { \
const char *p = static_cast<const char *>(memchr(str, '=', len)); \
if (p == NULL) { \
throw std::runtime_error("Invalid caching directive, missing delta-seconds"); \
} \
char *end; \
int secs = strtol(p + 1, &end, 10); \
if (!eof(end) && *end != ',') { \
throw std::runtime_error("Invalid caching directive, malformated delta-seconds"); \
} \
directives_.push_back(CacheDirective(CacheDirective::directive, std::chrono::seconds(secs))); \
begin = end; \
break; \
} \
(void) 0
const char *begin = str;
do {
do {
TRY_PARSE_TRIVIAL_DIRECTIVE("no-cache", NoCache);
TRY_PARSE_TRIVIAL_DIRECTIVE("no-store", NoStore);
TRY_PARSE_TRIVIAL_DIRECTIVE("no-transform", NoTransform);
TRY_PARSE_TRIVIAL_DIRECTIVE("only-if-cached", OnlyIfCached);
TRY_PARSE_TRIVIAL_DIRECTIVE("public", Public);
TRY_PARSE_TRIVIAL_DIRECTIVE("private", Private);
TRY_PARSE_TRIVIAL_DIRECTIVE("must-revalidate", MustRevalidate);
TRY_PARSE_TRIVIAL_DIRECTIVE("proxy-revalidate", ProxyRevalidate);
TRY_PARSE_TIMED_DIRECTIVE("max-age", MaxAge);
TRY_PARSE_TIMED_DIRECTIVE("max-stale", MaxStale);
TRY_PARSE_TIMED_DIRECTIVE("min-fresh", MinFresh);
TRY_PARSE_TIMED_DIRECTIVE("s-maxage", SMaxAge);
} while (false);
if (!eof(begin)) {
if (*begin != ',')
throw std::runtime_error("Invalid caching directive, expected a comma");
while (!eof(begin) && *begin == ',' || *begin == ' ') ++begin;
}
} while (!eof(begin));
#undef TRY_PARSE_TRIVIAL_DIRECTIVE
#undef TRY_PARSE_TIMED_DIRECTIVE
}
void
CacheControl::write(std::ostream& os) const {
using Http::CacheDirective;
auto directiveString = [](CacheDirective directive) -> const char* const {
switch (directive.directive()) {
case CacheDirective::NoCache:
return "no-cache";
case CacheDirective::NoStore:
return "no-store";
case CacheDirective::NoTransform:
return "no-transform";
case CacheDirective::OnlyIfCached:
return "only-if-cached";
case CacheDirective::Public:
return "public";
case CacheDirective::Private:
return "private";
case CacheDirective::MustRevalidate:
return "must-revalidate";
case CacheDirective::ProxyRevalidate:
return "proxy-revalidate";
case CacheDirective::MaxAge:
return "max-age";
case CacheDirective::MaxStale:
return "max-stale";
case CacheDirective::MinFresh:
return "min-fresh";
case CacheDirective::SMaxAge:
return "s-maxage";
}
};
auto hasDelta = [](CacheDirective directive) {
switch (directive.directive()) {
case CacheDirective::MaxAge:
case CacheDirective::MaxStale:
case CacheDirective::MinFresh:
case CacheDirective::SMaxAge:
return true;
}
return false;
};
for (std::vector<CacheDirective>::size_type i = 0; i < directives_.size(); ++i) {
const auto& d = directives_[i];
os << directiveString(d);
if (hasDelta(d)) {
auto delta = d.delta();
if (delta.count() > 0) {
os << "=" << delta.count();
}
}
if (i < directives_.size() - 1) {
os << ", ";
}
}
}
void
CacheControl::addDirective(Http::CacheDirective directive) {
directives_.push_back(directive);
}
void
CacheControl::addDirectives(const std::vector<Http::CacheDirective>& directives) {
std::copy(std::begin(directives), std::end(directives), std::back_inserter(directives_));
}
void
ContentLength::parse(const std::string& data) {
try {
......
......@@ -8,6 +8,7 @@
#include "mime.h"
#include "net.h"
#include "http_defs.h"
#include <string>
#include <type_traits>
#include <memory>
......@@ -111,104 +112,160 @@ header_cast(const std::shared_ptr<const Header>& from)
}
#endif
class ContentLength : public Header {
class Allow : public Header {
public:
NAME("Content-Length");
NAME("Allow");
ContentLength()
: value_(0)
Allow() { }
explicit Allow(const std::vector<Http::Method>& methods)
: methods_(methods)
{ }
explicit Allow(std::initializer_list<Http::Method> methods)
: methods_(methods)
{ }
explicit ContentLength(uint64_t val)
: value_(val)
explicit Allow(Http::Method method)
{
methods_.push_back(method);
}
void parseRaw(const char *str, size_t len);
void write(std::ostream& os) const;
void addMethod(Http::Method method);
void addMethods(std::initializer_list<Method> methods);
void addMethods(const std::vector<Http::Method>& methods);
std::vector<Http::Method> methods() const { return methods_; }
private:
std::vector<Http::Method> methods_;
};
class Accept : public Header {
public:
NAME("Accept")
Accept() { }
void parseRaw(const char *str, size_t len);
void write(std::ostream& os) const;
const std::vector<Mime::MediaType> media() const { return mediaRange_; }
private:
std::vector<Mime::MediaType> mediaRange_;
};
class CacheControl : public Header {
public:
NAME("Cache-Control")
CacheControl() { }
explicit CacheControl(const std::vector<Http::CacheDirective>& directives)
: directives_(directives)
{ }
explicit CacheControl(Http::CacheDirective directive);
void parse(const std::string& data);
void parseRaw(const char* str, size_t len);
void write(std::ostream& os) const;
uint64_t value() const { return value_; }
std::vector<Http::CacheDirective> directives() const { return directives_; }
void addDirective(Http::CacheDirective directive);
void addDirectives(const std::vector<Http::CacheDirective>& directives);
private:
uint64_t value_;
std::vector<Http::CacheDirective> directives_;
};
class Host : public Header {
class ContentEncoding : public Header {
public:
NAME("Host");
NAME("Content-Encoding")
Host()
: host_()
, port_(-1)
ContentEncoding()
: encoding_(Encoding::Identity)
{ }
explicit Host(const std::string& host, Net::Port port = 80)
: host_(host)
, port_(port)
explicit ContentEncoding(Encoding encoding)
: encoding_(encoding)
{ }
void parse(const std::string& data);
void parseRaw(const char* str, size_t len);
void write(std::ostream& os) const;
std::string host() const { return host_; }
Net::Port port() const { return port_; }
Encoding encoding() const { return encoding_; }
private:
std::string host_;
Net::Port port_;
Encoding encoding_;
};
class UserAgent : public Header {
class ContentLength : public Header {
public:
NAME("User-Agent")
NAME("Content-Length");
UserAgent() { }
explicit UserAgent(const std::string& ua) :
ua_(ua)
ContentLength()
: value_(0)
{ }
explicit ContentLength(uint64_t val)
: value_(val)
{ }
void parse(const std::string& data);
void write(std::ostream& os) const;
std::string ua() const { return ua_; }
uint64_t value() const { return value_; }
private:
std::string ua_;
uint64_t value_;
};
class Accept : public Header {
class ContentType : public Header {
public:
NAME("Accept")
NAME("Content-Type")
Accept() { }
ContentType() { }
void parseRaw(const char *str, size_t len);
explicit ContentType(const Mime::MediaType& mime) :
mime_(mime)
{ }
void parseRaw(const char* str, size_t len);
void write(std::ostream& os) const;
const std::vector<Mime::MediaType> media() const { return mediaRange_; }
Mime::MediaType mime() const { return mime_; }
private:
std::vector<Mime::MediaType> mediaRange_;
Mime::MediaType mime_;
};
class ContentEncoding : public Header {
class Host : public Header {
public:
NAME("Content-Encoding")
NAME("Host");
ContentEncoding()
: encoding_(Encoding::Identity)
Host()
: host_()
, port_(-1)
{ }
explicit ContentEncoding(Encoding encoding)
: encoding_(encoding)
explicit Host(const std::string& host, Net::Port port = 80)
: host_(host)
, port_(port)
{ }
void parseRaw(const char* str, size_t len);
void parse(const std::string& data);
void write(std::ostream& os) const;
Encoding encoding() const { return encoding_; }
std::string host() const { return host_; }
Net::Port port() const { return port_; }
private:
Encoding encoding_;
std::string host_;
Net::Port port_;
};
class Server : public Header {
......@@ -229,24 +286,22 @@ private:
std::vector<std::string> tokens_;
};
class ContentType : public Header {
class UserAgent : public Header {
public:
NAME("Content-Type")
ContentType() { }
NAME("User-Agent")
explicit ContentType(const Mime::MediaType& mime) :
mime_(mime)
UserAgent() { }
explicit UserAgent(const std::string& ua) :
ua_(ua)
{ }
void parseRaw(const char* str, size_t len);
void parse(const std::string& data);
void write(std::ostream& os) const;
Mime::MediaType mime() const { return mime_; }
std::string ua() const { return ua_; }
private:
Mime::MediaType mime_;
std::string ua_;
};
} // namespace Header
......
......@@ -61,6 +61,106 @@ TEST(headers_test, accept) {
}
TEST(headers_test, allow) {
Header::Allow a1(Method::Get);
std::ostringstream os;
a1.write(os);
ASSERT_EQ(os.str(), "GET");
os.str("");
Header::Allow a2({ Method::Post, Method::Put });
a2.write(os);
ASSERT_EQ(os.str(), "POST, PUT");
os.str("");
Header::Allow a3;
a3.addMethod(Method::Get);
a3.write(os);
ASSERT_EQ(os.str(), "GET");
os.str("");
a3.addMethod(Method::Options);
a3.write(os);
ASSERT_EQ(os.str(), "GET, OPTIONS");
os.str("");
Header::Allow a4(Method::Head);
a4.addMethods({ Method::Get, Method::Options });
a4.write(os);
ASSERT_EQ(os.str(), "HEAD, GET, OPTIONS");
}
TEST(headers_test, cache_control) {
auto testTrivial = [](std::string str, CacheDirective::Directive expected) {
Header::CacheControl cc;
cc.parse(str);
auto directives = cc.directives();
ASSERT_EQ(directives.size(), 1);
ASSERT_EQ(directives[0].directive(), expected);
};
auto testTimed = [](
std::string str, CacheDirective::Directive expected, uint64_t delta) {
Header::CacheControl cc;
cc.parse(str);
auto directives = cc.directives();
ASSERT_EQ(directives.size(), 1);
ASSERT_EQ(directives[0].directive(), expected);
ASSERT_EQ(directives[0].delta(), std::chrono::seconds(delta));
};
testTrivial("no-cache", CacheDirective::NoCache);
testTrivial("no-store", CacheDirective::NoStore);
testTrivial("no-transform", CacheDirective::NoTransform);
testTrivial("only-if-cached", CacheDirective::OnlyIfCached);
testTimed("max-age=0", CacheDirective::MaxAge, 0);
testTimed("max-age=12", CacheDirective::MaxAge, 12);
testTimed("max-stale=12345", CacheDirective::MaxStale, 12345);
testTimed("min-fresh=48", CacheDirective::MinFresh, 48);
Header::CacheControl cc1;
cc1.parse("private, max-age=600");
auto d1 = cc1.directives();
ASSERT_EQ(d1.size(), 2);
ASSERT_EQ(d1[0].directive(), CacheDirective::Private);
ASSERT_EQ(d1[1].directive(), CacheDirective::MaxAge);
ASSERT_EQ(d1[1].delta(), std::chrono::seconds(600));
Header::CacheControl cc2;
cc2.parse("public, s-maxage=200, proxy-revalidate");
auto d2 = cc2.directives();
ASSERT_EQ(d2.size(), 3);
ASSERT_EQ(d2[0].directive(), CacheDirective::Public);
ASSERT_EQ(d2[1].directive(), CacheDirective::SMaxAge);
ASSERT_EQ(d2[1].delta(), std::chrono::seconds(200));
ASSERT_EQ(d2[2].directive(), CacheDirective::ProxyRevalidate);
Header::CacheControl cc3(CacheDirective::NoCache);
std::ostringstream oss;
cc3.write(oss);
ASSERT_EQ(oss.str(), "no-cache");
oss.str("");
cc3.addDirective(CacheDirective::NoStore);
cc3.write(oss);
ASSERT_EQ(oss.str(), "no-cache, no-store");
oss.str("");
Header::CacheControl cc4;
cc4.addDirectives({
CacheDirective::Public,
CacheDirective(CacheDirective::MaxAge, std::chrono::seconds(600))
});
cc4.write(oss);
ASSERT_EQ(oss.str(), "public, max-age=600");
}
TEST(headers_test, content_length) {
Header::ContentLength cl;
......
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