diff --git a/ircd/http.cc b/ircd/http.cc index 88ac1609d..f9437d383 100644 --- a/ircd/http.cc +++ b/ircd/http.cc @@ -21,29 +21,6 @@ #include -BOOST_FUSION_ADAPT_STRUCT -( - ircd::http::line::request, - ( decltype(ircd::http::line::request::method), method ) - ( decltype(ircd::http::line::request::resource), resource ) - ( decltype(ircd::http::line::request::version), version ) -) - -BOOST_FUSION_ADAPT_STRUCT -( - ircd::http::line::response, - ( decltype(ircd::http::line::response::version), version ) - ( decltype(ircd::http::line::response::status), status ) - ( decltype(ircd::http::line::response::reason), reason ) -) - -BOOST_FUSION_ADAPT_STRUCT -( - ircd::http::line::header, - ( decltype(ircd::http::line::header::first), first ) - ( decltype(ircd::http::line::header::second), second ) -) - namespace ircd { namespace http { @@ -65,206 +42,6 @@ using qi::raw; using qi::attr; using qi::eps; -template -struct grammar -:qi::grammar -,parse::grammar -{ - template using rule = qi::rule; - - rule<> NUL; - rule<> SP; - rule<> HT; - rule<> CR; - rule<> LF; - rule<> CRLF; - rule<> colon; - - rule<> WS; - rule<> ws; - rule token; - rule string; - - rule line; - rule method; - rule resource; - rule version; - rule status; - rule status_code; - rule reason; - rule request_line; - rule response_line; - - rule key; - rule value; - rule header; - - rule headers; - rule request; - rule response; - - grammar(rule &top_rule, const char *const &name); -}; - -[[noreturn]] -void bail() -{ - throw error(code::BAD_REQUEST); -} - -template -grammar::grammar(qi::rule &top_rule, - const char *const &name) -:grammar::base_type -{ - top_rule -} -,parse::grammar -{ - name -} -,NUL -{ - lit('\0') - ,"NUL" -} -,SP -{ - lit(' ') - ,"SP" -} -,HT -{ - lit('\t') - ,"HT" -} -,CR -{ - lit('\r') - ,"CR" -} -,CRLF -{ - lit('\r') >> lit('\n') - ,"CRLF" -} -,colon -{ - lit(':') - ,"colon" -} -,WS -{ - (SP | HT) - ,"whitespace" -} -,ws -{ - +(SP | HT) - ,"whitespaces" -} -,token -{ - raw[+(char_ - (NUL | CR | LF | WS))] - ,"token" -} -,string -{ - raw[+(char_ - (NUL | CR | LF))] - ,"string" -} -,line -{ - -ws >> -string >> CRLF - ,"line" -} -,method -{ - raw[+(char_ - (NUL | CR | LF | WS))] - ,"method" -} -,resource -{ - raw[+(char_ - (NUL | CR | LF | WS))] - ,"resource" -} -,version -{ - raw[+(char_ - (NUL | CR | LF | WS))] - ,"version" -} -,status -{ - raw[repeat(3)[char_("0-9")]] - ,"status" -} -,status_code -{ - short_ - ,"status code" -} -,reason -{ - raw[+(char_ - (NUL | CR | LF))] - ,"status" -} -,request_line -{ - method >> +SP >> resource >> +SP >> version - ,"request line" -} -,response_line -{ - version >> +SP >> status >> +SP >> reason - ,"response line" -} -,key -{ - raw[+(char_ - (NUL | CR | LF | WS | colon))] - ,"key" -} -,value -{ - string - ,"value" -} -,header -{ - key >> -ws >> colon >> -ws >> value - ,"header" -} -,headers -{ - +(-header % CRLF) >> CRLF - ,"headers" -} -,request -{ - request_line >> -ws >> CRLF >> headers - ,"request" -} -,response -{ - response_line >> -ws >> CRLF >> headers - ,"response" -} -{ -} - -struct parser -:grammar -{ - static size_t content_length(const string_view &val); - - parser(): grammar { grammar::ws, "http.request" } {} -} -const parser; - -size_t printed_size(const std::initializer_list &headers); -size_t print(char *const &buf, const size_t &max, const std::initializer_list &headers); - std::map reason { { code::CONTINUE, "Continue"s }, @@ -295,6 +72,122 @@ std::map reason } // namespace http } // namespace ircd +BOOST_FUSION_ADAPT_STRUCT +( + ircd::http::line::request, + ( decltype(ircd::http::line::request::method), method ) + ( decltype(ircd::http::line::request::resource), resource ) + ( decltype(ircd::http::line::request::version), version ) +) + +BOOST_FUSION_ADAPT_STRUCT +( + ircd::http::line::response, + ( decltype(ircd::http::line::response::version), version ) + ( decltype(ircd::http::line::response::status), status ) + ( decltype(ircd::http::line::response::reason), reason ) +) + +BOOST_FUSION_ADAPT_STRUCT +( + ircd::http::line::header, + ( decltype(ircd::http::line::header::first), first ) + ( decltype(ircd::http::line::header::second), second ) +) + +namespace ircd { +namespace http { + +template +struct grammar +:qi::grammar +,parse::grammar +{ + template using rule = qi::rule; + + rule<> NUL { lit('\0') ,"nul" }; + + // insignificant whitespaces + rule<> SP { lit('\x20') ,"space" }; + rule<> HT { lit('\x09') ,"horizontal tab" }; + rule<> CR { lit('\x0D') ,"carriage return" }; + rule<> LF { lit('\x0A') ,"line feed" }; + + // whitespace skipping + rule<> ws { SP | HT ,"whitespace" }; + + rule<> CRLF { lit('\r') >> lit('\n') ,"carriage return, line feed" }; + rule<> colon { lit(':') ,"colon" }; + + rule token { raw[+(char_ - (NUL | CR | LF | ws))] ,"token" }; + rule string { raw[+(char_ - (NUL | CR | LF))] ,"string" }; + rule line { *ws >> -string >> CRLF ,"line" }; + + rule method { raw[+(char_ - (NUL | CR | LF | ws))] ,"method" }; + rule resource { raw[+(char_ - (NUL | CR | LF | ws))] ,"resource" }; + rule version { raw[+(char_ - (NUL | CR | LF | ws))] ,"version" }; + + rule status { raw[repeat(3)[char_("0-9")]] ,"status" }; + rule status_code { short_ ,"status code" }; + rule reason { raw[+(char_ - (NUL | CR | LF))] ,"status" }; + + rule key { raw[+(char_ - (NUL | CR | LF | ws | colon))] ,"key" }; + rule value { string ,"value" }; + rule header { key >> *ws >> colon >> *ws >> value ,"header" }; + rule headers { (header % (*ws >> CRLF)) ,"headers" }; + + rule request_line + { + method >> +SP >> resource >> +SP >> version + ,"request line" + }; + + rule response_line + { + version >> +SP >> status >> -(+SP >> reason) + ,"response line" + }; + + rule request + { + request_line >> *ws >> CRLF >> -headers >> CRLF + ,"request" + }; + + rule response + { + response_line >> *ws >> CRLF >> -headers >> CRLF + ,"response" + }; + + grammar(rule &top_rule, const char *const &name) + :grammar::base_type + { + top_rule + } + ,parse::grammar + { + name + } + {} +}; + +struct parser +:grammar +{ + static size_t content_length(const string_view &val); + + parser(): grammar { grammar::ws, "http.request" } {} +} +const parser; + +size_t printed_size(const std::initializer_list &headers); +size_t print(char *const &buf, const size_t &max, const std::initializer_list &headers); + +} // namespace http +} // namespace ircd + size_t ircd::http::print(char *const &buf, const size_t &max, @@ -597,9 +490,12 @@ try catch(const qi::expectation_failure &e) { char buf[256]; - snprintf(buf, sizeof(buf), "I require a valid %s starting at character %d.", - ircd::string(e.what_).data(), - int(e.last - e.first)); + const auto rule(ircd::string(e.what_)); + fmt::snprintf(buf, sizeof(buf), + "I require a valid HTTP %s. You sent %zu invalid characters starting with `%s'.", + between(rule, "<", ">"), + ssize_t(e.last - e.first), + string_view{e.first, e.last}); throw error(code::BAD_REQUEST, buf); } @@ -608,7 +504,7 @@ ircd::http::line::response::response(const line &line) { static const auto grammar { - parser.response_line + eps > parser.response_line }; const char *start(line.data()); @@ -631,9 +527,12 @@ try catch(const qi::expectation_failure &e) { char buf[256]; - snprintf(buf, sizeof(buf), "I require a valid %s starting at character %d.", - ircd::string(e.what_).data(), - int(e.last - e.first)); + const auto rule(ircd::string(e.what_)); + fmt::snprintf(buf, sizeof(buf), + "I require a valid HTTP %s. You sent %zu invalid characters starting with `%s'.", + between(rule, "<", ">"), + ssize_t(e.last - e.first), + string_view{e.first, e.last}); throw error(code::BAD_REQUEST, buf); }