#pragma once
#include "Message.h"
#include <panda/date.h>
#include <panda/memory.h>
#include <panda/optional.h>
namespace panda { namespace protocol { namespace http {
struct Request;
panda::time::TimezoneSP gmtz();
struct Response : Message, AllocatedObject<Response> {
using Date = panda::date::Date;
struct Builder; template <class, class> struct BuilderImpl;
struct Cookie {
enum class SameSite { disabled = 0, Strict, Lax, None };
Cookie (const string& value = "", const string& domain = "", const string& path = "", uint64_t max_age = 0, bool secure = false,
bool http_only = false, SameSite same_site = SameSite::disabled) :
_value(value), _domain(domain), _path(path), _max_age(max_age), _secure(secure), _http_only(http_only), _same_site(same_site)
{}
const string& value () const { return _value; }
const string& domain () const { return _domain; }
const string& path () const { return _path; }
uint64_t max_age () const { return _max_age; }
optional<Date> expires () const { if (!_expires) return {}; return Date(_expires); }
bool secure () const { return _secure; }
bool http_only () const { return _http_only; }
bool session () const { return !(_max_age || _expires); }
SameSite same_site () const { return _same_site; }
int64_t max_age_any () const;
optional<Date> expires_any () const;
Cookie& value (const string& v) { _value = v; return *this; }
Cookie& domain (const string& v) { _domain = v; return *this; }
Cookie& path (const string& v) { _path = v; return *this; }
Cookie& max_age (uint64_t v) { _max_age = v; _expires.clear(); return *this; }
Cookie& secure (bool v) { _secure = v; return *this; }
Cookie& http_only (bool v) { _http_only = v; return *this; }
Cookie& same_site (SameSite v) { _same_site = v; return *this; }
Cookie& expires (const Date& _d) {
auto d = _d;
d.to_timezone(gmtz());
_expires = d.to_string(Date::Format::rfc1123);
_max_age = 0;
return *this;
}
string to_string (const string& cookie_name, const Request* context = nullptr) const;
void serialize_to (string& acc, const string& name, const Request* req = nullptr) const;
private:
friend struct ResponseParser; friend struct RequestParser;
string _value;
string _domain;
string _path;
uint64_t _max_age = 0;
string _expires;
bool _secure = false;
bool _http_only = false;
SameSite _same_site = SameSite::disabled;
};
struct Cookies : Fields<Cookie, true, 2> {
optional<Cookie> get (const string& key) {
auto it = find(key);
if (it == fields.cend()) return {};
return it->value;
}
};
int code = 0;
string message;
Cookies cookies;
Response () : code() {}
Response (int code, Headers&& header = Headers(), Body&& body = Body(), bool chunked = false, int http_version = 0, const string& message = {}) :
Message(std::move(header), std::move(body), chunked, http_version), code(code), message(message)
{}
string full_message () const { return panda::to_string(code) + " " + (message ? message : message_for_code(code)); }
std::vector<string> to_vector (const Request* req = nullptr) const;
string to_string (const Request* req = nullptr) const { return Message::to_string(to_vector(req)); }
static string message_for_code (int code);
void parse_set_cookie (const string& buffer);
protected:
~Response () {}
private:
friend struct ResponseParser;
string _http_header (SerializationContext& ctx) const;
};
using ResponseSP = iptr<Response>;
template <class T, class R>
struct Response::BuilderImpl : Message::Builder<T, R> {
using Message::Builder<T, R>::Builder;
T& code (int code) {
this->_message->code = code;
return this->self();
}
T& message (const string& message) {
this->_message->message = message;
return this->self();
}
T& cookie (const string& name, const Response::Cookie& coo) {
this->_message->cookies.add(name, coo);
return this->self();
}
};
struct Response::Builder : Response::BuilderImpl<Builder, ResponseSP> {
Builder () : BuilderImpl(new Response()) {}
};
}}}