diff --git a/include/ircd/server.h b/include/ircd/server.h new file mode 100644 index 000000000..d16611e48 --- /dev/null +++ b/include/ircd/server.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2017 Charybdis Development Team + * Copyright (C) 2017 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. + */ + +#pragma once +#define HAVE_IRCD_SERVER_H + +namespace ircd +{ + struct server; + + size_t write(server &, const ilist &iov); + char *read(server &, char *&start, char *const &stop); + + http::request::write_closure write_closure(server &); + parse::read_closure read_closure(server &); +} + +/// The interface for when IRCd plays the role of client to other servers +/// +/// This is a handle for being a client to another server. This class is +/// named ircd::server and not ircd::client because this project is IRCd +/// and not IRCc. This handle will attempt to find an existing connection +/// pool for the remote server and then multiplex your requests and demultiplex +/// your responses to achieve concurrency and sharing and limitations and +/// shape the pipeline as best as the pool allows along with other instances +/// of ircd::server for the same remote. +/// +/// Note that this means ircd::server is only appropriate for stateless +/// protocols like HTTP and chunked-encoding should be avoided if we are +/// to get the best out of nagle'ing the pipe. Individual net::socket should +/// be used otherwise. +/// +/// This handle is a "tag" which services a single request and response per +/// instance to an ircd::server::node over one ircd::server::link available +/// to that node. Those interfaces are internal and don't have to be messed +/// with. +/// +/// Instances of this class can be used in arrays to make bulk requests. +/// +struct ircd::server +{ + struct init; + struct node; + struct link; + + static std::map> nodes; + + std::shared_ptr n; + unique_iterator> it; + + public: + operator const net::remote &() const; + + server() = default; + server(server &&) noexcept = default; + server &operator=(server &&) noexcept = default; + server(net::remote); + ~server() noexcept; +}; + +struct ircd::server::link +{ + enum state :int; + + std::shared_ptr s; + std::deque q; + enum state state; + + link(const net::remote &remote); +}; + +enum ircd::server::link::state +:int +{ + DEAD, + IDLE, + BUSY, +}; + +struct ircd::server::node +:std::enable_shared_from_this +{ + enum state :int; + + net::remote remote; + std::list tags; + std::list links; + + void add(const size_t &num = 1); + void del(const size_t &num = 1); + + node(net::remote remote); + ~node() noexcept; +}; + +struct ircd::server::init +{ + init(); + ~init() noexcept; +}; diff --git a/include/ircd/stdinc.h b/include/ircd/stdinc.h index 2e29148ec..7281b4c3a 100644 --- a/include/ircd/stdinc.h +++ b/include/ircd/stdinc.h @@ -175,6 +175,7 @@ namespace ircd constexpr size_t BUFSIZE { 512 }; struct client; + struct server; std::string demangle(const std::string &symbol); template std::string demangle(); @@ -216,6 +217,7 @@ namespace ircd #include "net/net.h" #include "m/m.h" #include "resource.h" +#include "server.h" #include "client.h" template diff --git a/ircd/Makefile.am b/ircd/Makefile.am index 3833be1b7..ed47d5ecd 100644 --- a/ircd/Makefile.am +++ b/ircd/Makefile.am @@ -80,6 +80,7 @@ libircd_la_SOURCES = \ parse.cc \ resource.cc \ rfc1459.cc \ + server.cc \ sodium.cc \ ### diff --git a/ircd/server.cc b/ircd/server.cc new file mode 100644 index 000000000..265c04d2c --- /dev/null +++ b/ircd/server.cc @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2017 Charybdis Development Team + * Copyright (C) 2017 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 + +// +// init +// + +ircd::server::init::init() +{ +} + +ircd::server::init::~init() +noexcept +{ + ircd::server::nodes.clear(); +} + +// +// server +// + +ircd::http::request::write_closure +ircd::write_closure(server &server) +{ + // returns a function that can be called to send an iovector of data to a server + return [&server](const ilist &iov) + { + //std::cout << "<<<<" << std::endl; + //std::cout << iov << std::endl; + //std::cout << "----" << std::endl; + write(server, iov); + }; +} + +ircd::parse::read_closure +ircd::read_closure(server &server) +{ + // Returns a function the parser can call when it wants more data + return [&server](char *&start, char *const &stop) + { + char *const s(start); + read(server, start, stop); + //std::cout << ">>>>" << std::endl; + //std::cout << string_view{s, start} << std::endl; + //std::cout << "----" << std::endl; + }; +} + +char * +ircd::read(server &server, + char *&start, + char *const &stop) +try +{ + auto &sock + { + *(*begin(server.n->links)).s + }; + + const std::array bufs + {{ + { start, stop } + }}; + + char *const base(start); + start += sock.read_some(bufs); + return base; +} +catch(const boost::system::system_error &e) +{ + using namespace boost::system::errc; + + log::error("read error: %s: %s", + string(server.n->remote), + e.what()); + + switch(e.code().value()) + { + case operation_canceled: + throw http::error(http::REQUEST_TIMEOUT); + + default: + throw; + } +} + +size_t +ircd::write(server &server, + const ilist &iov) +try +{ + auto &sock + { + *(*begin(server.n->links)).s + }; + + return sock.write(iov); +} +catch(const boost::system::system_error &e) +{ + using namespace boost::system::errc; + + log::error("write error: %s: %s", + string(server.n->remote), + e.what()); + + switch(e.code().value()) + { + case operation_canceled: + throw http::error(http::REQUEST_TIMEOUT); + + default: + throw; + } +} + +// +// server +// + +ircd::server::server(net::remote remote) +:n{[&remote] +{ + const auto &ipp + { + static_cast(remote) + }; + + const auto it(nodes.lower_bound(ipp)); + if(it == nodes.end() || it->first != ipp) + { + const auto ipp{static_cast(remote)}; + const auto n{std::make_shared(std::move(remote))}; + nodes.emplace_hint(it, ipp, n); + return n; + } + + return it->second; +}()} +,it +{ + n->tags, n->tags.emplace(n->tags.end(), this) +} +{ +} + +ircd::server::~server() +noexcept +{ +} + +ircd::server::operator +const ircd::net::remote &() +const +{ + static const ircd::net::remote null_remote {}; + if(unlikely(!n)) + return null_remote; + + return n->remote; +} + +decltype(ircd::server::nodes) +ircd::server::nodes +{}; + +// +// node +// + +ircd::server::node::node(net::remote remote) +:remote{std::move(remote)} +{ + add(1); +} + +ircd::server::node::~node() +noexcept +{ +} + +void +ircd::server::node::add(const size_t &num) +{ + links.emplace_back(remote); +} + +void +ircd::server::node::del(const size_t &num) +{ +} + +// +// link +// + +ircd::server::link::link(const net::remote &remote) +:s{std::make_shared(remote)} +,state{state::DEAD} +{ +}