/* * 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. */ namespace ircd { IRCD_INIT_PRIORITY(STD_CONTAINER) decltype(resource::resources) resource::resources {}; } // namespace ircd ircd::resource::resource(const char *const &name, const char *const &description) :name{name} ,description{description} ,resources_it{[this, &name] { const auto iit(resources.emplace(this->name, this)); if(!iit.second) throw error("resource \"%s\" already registered", name); return iit.first; }()} { log::info("Registered resource \"%s\" by handler @ %p", name, this); } ircd::resource::~resource() noexcept { resources.erase(resources_it); log::info("Unregistered resource \"%s\" by handler @ %p", name, this); } void ircd::resource::operator()(client &client, parse::capstan &pc, const http::request::head &head) try { auto &method(*methods.at(head.method)); http::request::content content{pc, head}; resource::request request { head, content }; call_method(client, method, request); } catch(const std::out_of_range &e) { throw http::error { http::METHOD_NOT_ALLOWED }; } void ircd::resource::call_method(client &client, method &method, resource::request &request) try { method(client, request); } catch(const json::error &e) { throw m::error { "M_BAD_JSON", "Required JSON field: %s", e.what() }; } ircd::resource::method::method(struct resource &resource, const char *const &name, const handler &handler, const std::initializer_list &members) :function{handler} ,name{name} ,resource{&resource} ,methods_it{[this, &name] { const auto iit(this->resource->methods.emplace(this->name, this)); if(!iit.second) throw error("resource \"%s\" already registered", name); return iit.first; }()} { for(const auto &member : members) this->members.emplace(member->name, member); } ircd::resource::method::~method() noexcept { resource->methods.erase(methods_it); } ircd::resource::response::response(client &client, const json::obj &obj, const http::code &code) try { char cbuf[1024], *out(cbuf); const auto doc(serialize(obj, out, cbuf + sizeof(cbuf))); response(client, doc, code); } catch(const json::error &e) { throw m::error { http::INTERNAL_SERVER_ERROR, "M_NOT_JSON", "Generator Protection: %s", e.what() }; } ircd::resource::response::response(client &client, const json::doc &doc, const http::code &code) { http::response { code, doc, write_closure(client), { { "Content-Type", "application/json" } } }; } ircd::resource::response::~response() noexcept { } ircd::resource::member::member(const char *const &name, const enum json::type &type, validator valid) :name{name} ,type{type} ,valid{std::move(valid)} { }