0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-11 06:28:55 +02:00

ircd::http: Add status category enum; move static grammars out of function; minor reorg enums.

This commit is contained in:
Jason Volk 2020-06-12 16:39:00 -07:00
parent 59665babae
commit 09fe270c54
2 changed files with 187 additions and 84 deletions

View file

@ -14,7 +14,6 @@
/// HyperText TransPort: formal grammars & tools
namespace ircd::http
{
enum code :ushort;
struct error;
struct line;
struct query;
@ -23,8 +22,14 @@ namespace ircd::http
struct request;
struct response;
string_view status(const code &);
code status(const string_view &);
enum code :ushort;
string_view status(const enum code &) noexcept;
enum code status(const string_view &);
enum class category :uint8_t;
string_view category(const enum category &) noexcept;
enum category category(const enum code &) noexcept;
enum category category(const string_view &) noexcept;
void writeline(window_buffer &);
void writeline(window_buffer &, const window_buffer::closure &);
@ -38,21 +43,95 @@ namespace ircd::http
bool has(const vector_view<const header> &, const string_view &key);
}
/// HTTP Status classifications.
enum class ircd::http::category
:uint8_t
{
NONE = 0, ///< Sentinel
INFO = 1, ///< Informational
SUCCESS = 2, ///< Successful
REDIRECT = 3, ///< Redirectional
ERROR = 4, ///< Erroneous
SERVER = 6, ///< Server Error
UNKNOWN = 7, /// Pair with default case in switch/tables
};
/// HTTP Status codes.
enum ircd::http::code
:ushort
{
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
EARLY_HINTS = 103,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
PARTIAL_CONTENT = 206,
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
USE_PROXY = 305,
SWITCH_PROXY = 306,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PAYLOAD_TOO_LARGE = 413,
REQUEST_URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
IM_A_TEAPOT = 418,
UNPROCESSABLE_ENTITY = 422,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
INSUFFICIENT_STORAGE = 507,
CLOUDFLARE_REFUSED = 521,
CLOUDFLARE_TIMEDOUT = 522,
CLOUDFLARE_UNREACHABLE = 523,
CLOUDFLARE_REQUEST_TIMEOUT = 524,
};
/// Root exception for HTTP.
struct ircd::http::error
:ircd::error
{
std::string content;
std::string headers;
http::code code {http::code(0)};
enum code code {http::code(0)};
explicit operator bool() const { return code != http::code(0); }
bool operator!() const { return !bool(*this); }
error() = default;
error(const http::code &, std::string content = {}, std::string headers = {});
error(const http::code &, std::string content, const vector_view<const header> &);
template<class... args> error(const string_view &fmt, const http::code &, args&&...);
error(const enum code &, std::string content = {}, std::string headers = {});
error(const enum code &, std::string content, const vector_view<const header> &);
template<class... args> error(const string_view &fmt, const enum code &, args&&...);
~error() noexcept;
};
@ -263,7 +342,7 @@ struct ircd::http::response
// compose a response into buffer
response(window_buffer &,
const code &,
const enum code &,
const size_t &content_length = 0,
const string_view &content_type = {},
const http::headers &headers = {},
@ -302,69 +381,6 @@ struct ircd::http::response::chunk
chunk() = default;
};
//
// Add more as you go...
//
enum ircd::http::code
:ushort
{
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
EARLY_HINTS = 103,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
PARTIAL_CONTENT = 206,
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
USE_PROXY = 305,
SWITCH_PROXY = 306,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PAYLOAD_TOO_LARGE = 413,
REQUEST_URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
IM_A_TEAPOT = 418,
UNPROCESSABLE_ENTITY = 422,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
INSUFFICIENT_STORAGE = 507,
CLOUDFLARE_REFUSED = 521,
CLOUDFLARE_TIMEDOUT = 522,
CLOUDFLARE_UNREACHABLE = 523,
CLOUDFLARE_REQUEST_TIMEOUT = 524,
};
template<size_t MAX>
inline ircd::vector_view<ircd::string_view>
ircd::http::query::string::array(const mutable_buffer &buf,

View file

@ -117,7 +117,10 @@ struct ircd::http::grammar
:qi::grammar<const char *, unused_type>
{
using it = const char *;
template<class R = unused_type, class... S> using rule = qi::rule<it, R, S...>;
template<class R = unused_type,
class... S>
using rule = qi::rule<it, R, S...>;
rule<> NUL { lit('\0') ,"nul" };
@ -143,7 +146,6 @@ struct ircd::http::grammar
rule<string_view> line { *ws >> -string >> CRLF ,"line" };
rule<string_view> status { raw[repeat(3)[char_("0-9")]] ,"status" };
rule<short> status_code { short_ ,"status code" };
rule<string_view> reason { string ,"status" };
rule<string_view> head_key { raw[+(char_ - (illegal | ws | colon))] ,"head key" };
@ -1241,33 +1243,58 @@ noexcept
// status
//
ircd::http::code
#pragma GCC visibility push(internal)
namespace ircd::http
{
extern const parser::rule<uint8_t> status_codepoint;
extern const parser::rule<enum category> status_category;
extern const parser::rule<enum code> status_code;
}
#pragma GCC visibility pop
decltype(ircd::http::status_codepoint)
ircd::http::status_codepoint
{
qi::uint_parser<uint8_t, 10, 1, 1>{}
,"status codepoint"
};
decltype(ircd::http::status_category)
ircd::http::status_category
{
&char_("1-9") >> status_codepoint >> omit[repeat(2)[char_("0-9")] >> (eoi | parser.ws)]
};
decltype(ircd::http::status_code)
ircd::http::status_code
{
qi::uint_parser<uint16_t, 10, 3, 3>{} >> omit[eoi | parser.ws]
,"status code"
};
enum ircd::http::code
ircd::http::status(const string_view &str)
{
static const auto grammar
{
parser.status_code
};
short ret;
const char *start(str.data());
const char *start(begin(str)), *const stop(end(str));
const bool parsed
{
parser(start, start + str.size(), grammar, ret)
parser(start, stop, status_code, ret)
};
if(!parsed || ret < 0 || ret >= 1000)
if(!parsed)
throw ircd::error
{
"Invalid HTTP status code"
};
assert(ret >= 0 && ret < 1000);
return http::code(ret);
}
ircd::string_view
ircd::http::status(const http::code &code)
try
ircd::http::status(const enum code &code)
noexcept try
{
return reason.at(code);
}
@ -1280,3 +1307,63 @@ catch(const std::out_of_range &e)
return ""_sv;
}
enum ircd::http::category
ircd::http::category(const string_view &str)
noexcept
{
enum category ret;
const char *start(begin(str)), *const stop(end(str));
const bool parsed
{
parser(start, stop, status_category, ret)
};
if(!parsed || ret > category::UNKNOWN)
ret = category::UNKNOWN;
assert(uint8_t(ret) <= uint8_t(category::UNKNOWN));
return ret;
}
enum ircd::http::category
ircd::http::category(const enum code &code)
noexcept
{
if(ushort(code) == 0)
return category::NONE;
if(ushort(code) < 200)
return category::INFO;
if(ushort(code) < 300)
return category::SUCCESS;
if(ushort(code) < 400)
return category::REDIRECT;
if(ushort(code) < 500)
return category::ERROR;
if(ushort(code) < 600)
return category::SERVER;
return category::UNKNOWN;
}
ircd::string_view
ircd::http::category(const enum category &category)
noexcept
{
switch(category)
{
case category::NONE: return "NONE";
case category::INFO: return "INFO";
case category::SUCCESS: return "SUCCESS";
case category::REDIRECT: return "REDIRECT";
case category::ERROR: return "ERROR";
case category::SERVER: return "SERVER";
default:
case category::UNKNOWN: return "UNKNOWN";
}
}