/* * Copyright (C) 2016 Charybdis Development Team * Copyright (C) 2016 Jason Volk * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice is present in all copies. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #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 ) ) /* BOOST_FUSION_ADAPT_STRUCT ( ircd::http::request, ( decltype(ircd::http::request::command), command ) ( decltype(ircd::http::request::headers), headers ) ) BOOST_FUSION_ADAPT_STRUCT ( ircd::http::response, ( decltype(ircd::http::response::status), status ) ( decltype(ircd::http::response::headers), headers ) ) */ namespace ircd { namespace http { namespace spirit = boost::spirit; namespace qi = spirit::qi; using spirit::unused_type; using qi::lit; using qi::string; using qi::char_; using qi::repeat; using qi::omit; 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 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 | SP))] ,"version" } ,status { raw[repeat(3)[char_("0-9")]] ,"status" } ,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 { parser(): grammar { grammar::ws, "http.request" } {} } const parser; } // namespace http } // namespace ircd ircd::http::content::content(parse::context &pc, const headers &h) :std::string(h.content_length, char()) { char *data(const_cast(this->data())); char *const stop(data + size()); const size_t unparsed(pc.read - pc.parsed); const size_t have(std::min(unparsed, size())); memcpy(data, pc.parsed, have); data[have] = '\0'; pc.parsed += have; data += have; if(data != stop) { pc.reader(data, stop); *data = '\0'; } assert(data == stop); } ircd::http::headers::headers(parse::context &pc, const closure &c) { for(line::header h{pc}; !h.first.empty(); h = line::header{pc}) { if(iequals(h.first, "host")) host = h.second; else if(iequals(h.first, "expect")) expect = h.second; else if(iequals(h.first, "te")) te = h.second; else if(iequals(h.first, "content-length")) content_length = lex_cast(h.second); if(c) c(h); } } ircd::http::line::header::header(const line &line) try { static const auto grammar { eps > parser.header }; if(line.empty()) return; const char *start(line.data()); const char *const stop(line.data() + line.size()); qi::parse(start, stop, grammar, *this); } catch(const qi::expectation_failure &e) { throw error(code::BAD_REQUEST); } ircd::http::line::response::response(const line &line) { static const auto grammar { parser.response_line }; const char *start(line.data()); const char *const stop(line.data() + line.size()); qi::parse(start, stop, grammar, *this); } ircd::http::line::request::request(const line &line) try { static const auto grammar { eps > parser.request_line }; const char *start(line.data()); const char *const stop(line.data() + line.size()); qi::parse(start, stop, grammar, *this); } catch(const qi::expectation_failure &e) { throw error(code::BAD_REQUEST); } ircd::http::line::line(parse::context &pc) :string_view{[&pc] { static const auto grammar { parser.line }; string_view ret; pc([&ret](const char *&start, const char *stop) { return qi::parse(start, stop, grammar, ret); }); return ret; }()} { } namespace ircd { namespace http { std::map reason { { code::OK, "OK"s }, { code::BAD_REQUEST, "Bad Request"s }, { code::NOT_FOUND, "Not Found"s }, { code::METHOD_NOT_ALLOWED, "Method Not Allowed"s }, { code::INTERNAL_SERVER_ERROR, "Internal Server Error"s }, }; } // namespace http } // namespace ircd ircd::http::error::error(const enum code &code, const string_view &text) :ircd::error{generate_skip} ,code{code} ,content{text} { snprintf(buf, sizeof(buf), "%d %s", int(code), reason[code].data()); }