diff --git a/Makefile.am b/Makefile.am index 13a9fcd..43066c5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -54,7 +54,8 @@ MISC = \ src/misc/sys-wrapper.hh \ src/misc/unistd.hh \ src/misc/logger.cc \ - src/misc/logger.hh + src/misc/logger.hh \ + src/misc/buffer.cc EVENTS = \ src/events/event-loop.hh \ @@ -69,17 +70,7 @@ EVENTS = \ src/events/receive.hh \ src/events/receive.cc \ src/events/send.hh \ - src/events/send.cc \ - src/events/send-file.hh \ - src/events/send-file.cc \ - src/events/send-request.hh \ - src/events/send-request.cc \ - src/events/receive-response.hh \ - src/events/receive-response.cc \ - src/events/file-sender.hh \ - src/events/file-sender.cc \ - src/events/reverse-proxy.cc \ - src/events/reverse-proxy.hh + src/events/send.cc LEGISLATOR = \ src/legislator/legislator.cc \ @@ -88,10 +79,12 @@ LEGISLATOR = \ SOCKET = \ src/socket/default-socket.cc \ src/socket/default-socket.hh \ + src/connection/connection.cc \ src/socket/socket.hh SOURCES = \ $(CONFIG) \ + $(EVENTS) \ $(LEGISLATOR) \ $(MISC) \ $(SOCKET) \ diff --git a/src/connection/connection.cc b/src/connection/connection.cc new file mode 100644 index 0000000..ec8206e --- /dev/null +++ b/src/connection/connection.cc @@ -0,0 +1,54 @@ +#include "connection.hh" + +namespace paxos +{ + Connection::Connection(shared_socket socket) + { + this->socket_ = socket; + servername_ = std::string(); + } + + Connection::~Connection() + { + } + + const shared_socket& Connection::get_socket() + { + return socket_; + } + + const misc::shared_fd Connection::get_socket_fd() + { + return socket_->fd_get(); + } + + misc::Buffer& Connection::get_buffer_in() + { + return buffer_in_; + } + + misc::Buffer& Connection::get_buffer_out() + { + return buffer_out_; + } + + const std::string Connection::get_origin_ip() const + { + return socket_->getsockname(); + } + + const std::string& Connection::get_origin_port() const + { + return socket_->get_listener_port(); + } + + void Connection::set_servername(const std::string& servername) + { + servername_ = servername; + } + + const std::string& Connection::get_servername() const + { + return servername_; + } +} diff --git a/src/connection/connection.hh b/src/connection/connection.hh new file mode 100644 index 0000000..2f91d2e --- /dev/null +++ b/src/connection/connection.hh @@ -0,0 +1,76 @@ +/** + * \file vhost/connection.hh + * \brief Connection declaration. + */ + +#pragma once + +#include +#include + +#include "misc/fd.hh" +#include "socket/socket.hh" +#include "misc/buffer.hh" + +namespace paxos +{ + /** + * \struct Connection + * \brief Value object representing a connection. + * + * We need to keep track of the state of each request while it has not + * been fully processed. + */ + struct Connection + { + friend class APM; + + Connection(shared_socket socket); + + Connection(const Connection& con) = default; + Connection& operator=(const Connection&) = default; + Connection(Connection&&) = default; + Connection& operator=(Connection&&) = default; + ~Connection(); + + /* Add members to store the information relative to the + ** connection. + */ + + const shared_socket& get_socket(); + const misc::shared_fd get_socket_fd(); + + /* + ** \brief Getter of the input misc::Buffer + ** \return misc::Buffer + */ + misc::Buffer& get_buffer_in(); + + /* + ** \brief Getter of the output misc::Buffer + ** \return misc::Buffer + */ + misc::Buffer& get_buffer_out(); + + /** + * \brief Returns the ip the client accessed while connecting + */ + const std::string get_origin_ip() const; + + /** + * \brief Returns the port the client accessed while connecting + */ + const std::string& get_origin_port() const; + + const std::string& get_servername() const; + void set_servername(const std::string& servername); + + private: + shared_socket socket_; + std::string servername_; + misc::Buffer buffer_in_; + misc::Buffer buffer_out_; + }; + + using shared_connection = std::shared_ptr; +} // namespace http diff --git a/src/events/event-loop.cc b/src/events/event-loop.cc new file mode 100644 index 0000000..39b8206 --- /dev/null +++ b/src/events/event-loop.cc @@ -0,0 +1,52 @@ +#include "event-loop.hh" + +namespace paxos +{ + EventLoop::EventLoop() + { + /* Macro equivalent of ev_default_loop(int flags) */ + loop = EV_DEFAULT; + } + + EventLoop::EventLoop(struct ev_loop* loopev) + { + loop = loopev; + } + + EventLoop::~EventLoop() + { + /* Maybe need to stop all watcher or easier, free them */ + ev_loop_destroy(loop); + } + + void EventLoop::register_watcher(EventWatcher* watcher) + { + ev_io_start(loop, &(watcher->watcher_get())); + } + + void EventLoop::unregister_watcher(EventWatcher* watcher) + { + ev_io_stop(loop, &(watcher->watcher_get())); + } + + void EventLoop::register_sigint_watcher(ev_signal* signal) const + { + ev_signal_start(loop, signal); + } + + void EventLoop::register_timer_watcher(ev_timer* et) const + { + ev_timer_start(loop, et); + } + + void EventLoop::unregister_timer_watcher(ev_timer* et) const + { + ev_timer_stop(loop, et); + } + + void EventLoop::operator()() const + { + ev_run(loop, 0); + } + +} diff --git a/src/events/event-loop.hh b/src/events/event-loop.hh new file mode 100644 index 0000000..cc6205d --- /dev/null +++ b/src/events/event-loop.hh @@ -0,0 +1,95 @@ +/** + * \file events/event-loop.hh + * \brief EventLoop declaration. + */ + +#pragma once + +#include + +#include "error/not-implemented.hh" +#include "events/events.hh" + +namespace paxos +{ + class EventWatcher; + + /** + * \struct EventLoop + * \brief Value object wrapping libev's ev_loop. + * + * Event loop design pattern. + */ + struct EventLoop + { + /** + * \brief Create a default EventLoop based on a default ev_loop. + */ + EventLoop(); + + /** + * \brief Create an EventLoop from an existing ev_loop. + * + * \param loop ev_loop* custom ev_loop. + */ + explicit EventLoop(struct ev_loop*); + + EventLoop(const EventLoop&) = default; + EventLoop& operator=(const EventLoop&) = default; + EventLoop(EventLoop&&) = default; + EventLoop& operator=(EventLoop&&) = default; + + /** + * \brief Destroy the ev_loop. + */ + ~EventLoop(); + + /** + * \brief Activate the given ev_io. + * + * Note that only activated watchers will receive events. + * + * \param watcher EventWatcher* to register in the loop. + */ + void register_watcher(EventWatcher*); + + /** + * \brief Stop the given ev_io. + * + * \param watcher EventWatcher* to unregister in the loop. + */ + void unregister_watcher(EventWatcher*); + + /** + * \brief Register SIGINT ev_signal. + * + * \param watcher ev_signal* to register in the loop. + */ + void register_sigint_watcher(ev_signal*) const; + + /** + * \brief Start ev_timer. + * + * \param watcher ev_timer* to register in the loop. + */ + void register_timer_watcher(ev_timer* et) const; + + /** + * \brief Stop ev_timer. + * + * \param watcher ev_timer* to unregister in the loop. + */ + void unregister_timer_watcher(ev_timer* et) const; + + /** + * \brief Start waiting for events. + */ + void operator()() const; + + /** + * \brief Libev's event loop. + */ + struct ev_loop* loop; + }; + +} // namespace http diff --git a/src/events/events.cc b/src/events/events.cc new file mode 100644 index 0000000..67f3c6b --- /dev/null +++ b/src/events/events.cc @@ -0,0 +1,20 @@ +#include "events/events.hh" + +#include "events/register.hh" + +namespace paxos +{ + EventWatcher::EventWatcher(int fd, int flags) + { + ev_io_init(&watcher_, EventWatcher::event_callback, fd, flags); + watcher_.data = reinterpret_cast(this); + } + + + void EventWatcher::event_callback(struct ev_loop*, ev_io* w, int) + { + auto ew = reinterpret_cast(w->data); + auto shared_ew = event_register.at(ew).value(); + (*shared_ew)(); + } +} // namespace http diff --git a/src/events/events.hh b/src/events/events.hh new file mode 100644 index 0000000..cc02126 --- /dev/null +++ b/src/events/events.hh @@ -0,0 +1,75 @@ +/** + * \file events/events.hh + * \brief EvenWatcher declaration. + */ + +#pragma once + +#include +#include +#include + +#include "connection/connection.hh" + +namespace paxos +{ + /** + * \class EventWatcher + * \brief Abstract class representing event watchers. + * + * Its subclasses will act as functors and implement most of the server's + * workflow. Every potentially blocking operation should be represented by + * an EventWatcher. + */ + class EventWatcher + { + public: + /** + * \brief Initialize EventWatcher's ev_io. + * + * Initialize the ev_io held by the EventWatcher and store the + * EventWatcher's address in it. + * + * \param fd int socket fd. + * \param flags int initialization flags. + */ + EventWatcher(int fd, int flags); + + EventWatcher(const EventWatcher&) = delete; + EventWatcher& operator=(const EventWatcher&) = delete; + EventWatcher(EventWatcher&&) = delete; + EventWatcher& operator=(EventWatcher&&) = delete; + virtual ~EventWatcher() = default; + + /** + * \brief Virtual pure functor + * + * Each EventWatcher deriving from this class will need to implement + * its own workflow by overriding this functor. + */ + virtual void operator()() = 0; + + + ev_io& watcher_get() noexcept + { + return watcher_; + } + + protected: + /** + * \brief Callback for event handling. + * + * Each time an event occurs in the loop, this callback will be called + * with its associated ev_io. This method is called by libev and uses + * dynamic dispatch to run the correct overload of the operator(). + * + * \param watcher ev_io* which received an event. + */ + static void event_callback(struct ev_loop* loop, ev_io* w, int revents); + + /** + * \brief Libev's io watcher. + */ + ev_io watcher_; + }; +} // namespace http diff --git a/src/events/listener.cc b/src/events/listener.cc new file mode 100644 index 0000000..2ea0bf0 --- /dev/null +++ b/src/events/listener.cc @@ -0,0 +1,41 @@ +#include "events/listener.hh" +#include "events/register.hh" +#include "events/receive.hh" +#include "misc/socket.hh" +#include "misc/logger.hh" + +namespace paxos +{ + ListenerEW::ListenerEW(shared_socket socket) : + EventWatcher(socket->fd_get()->fd_, EV_READ) + { + /*struct sockaddr_in addr; + int addrlen = sizeof(addr); + sys::getsockname(socket->fd_get()->fd_, &addr, &addrlen);*/ + + sock_ = socket; + //port_ = addr.sin_port; + } + + void ListenerEW::operator()() + { + /* accept client */ + + try + { + auto client_socket = sock_->accept(nullptr, nullptr); + + client_socket->fcntl_set_O_NONBLOCK(); + + std::cout << "Listener: accepting client ... on " << getpid() << "\n"; + + /* Register client_socket */ + auto e = event_register.register_event(client_socket); + } + catch (const std::system_error& e) + { + std::cerr << "Listener: Could not accept client\n"; + return; + } + } +} diff --git a/src/events/listener.hh b/src/events/listener.hh new file mode 100644 index 0000000..8ce30c0 --- /dev/null +++ b/src/events/listener.hh @@ -0,0 +1,39 @@ +/** + * \file events/listener.hh + * \brief ListenerEW declaration. + */ + +#pragma once + +#include "events/events.hh" +#include "socket/socket.hh" +namespace paxos +{ + /** + * \class ListenerEW + * \brief Workflow for listener socket. + */ + class ListenerEW : public EventWatcher + { + public: + /** + * \brief Create a ListenerEW from a listener socket. + */ + explicit ListenerEW(shared_socket socket); + + /** + * \brief Start accepting connections on listener socket. + */ + void operator()() final; + + private: + /** + * \brief Listener socket. + */ + shared_socket sock_; + /** + * \brief Port on which the socket is listening. + */ + uint16_t port_; + }; +} // namespace http diff --git a/src/events/receive.cc b/src/events/receive.cc new file mode 100644 index 0000000..d5f413f --- /dev/null +++ b/src/events/receive.cc @@ -0,0 +1,34 @@ +#include "error/parsing-error.hh" +#include "error/connection-closed.hh" +#include "events/receive.hh" +#include "events/register.hh" +#include "events/send.hh" +#include "misc/addrinfo/addrinfo.hh" + +namespace paxos +{ + RecvEW::RecvEW(int socket) + : EventWatcher(socket, EV_READ) + { + } + + RecvEW::RecvEW(shared_socket socket) + : RecvEW(socket->fd_get()->fd_) + { + connection_ = std::make_shared(socket); + } + + RecvEW::RecvEW(shared_connection connection) + : RecvEW(connection->get_socket_fd()->fd_) + { + connection_ = connection; + } + + + void RecvEW::operator()() + { + + } + + +} diff --git a/src/events/receive.hh b/src/events/receive.hh new file mode 100644 index 0000000..20eb76a --- /dev/null +++ b/src/events/receive.hh @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +#include "events/events.hh" + +namespace paxos +{ + class RecvEW : public EventWatcher + { + public: + /** + * \brief Create a RecvEW from a listener socket. + */ + explicit RecvEW(shared_socket socket); + explicit RecvEW(shared_connection connection); + /** + * \brief Read on socket. + */ + void operator()() final; + + shared_connection& get_connection(); + + private: + explicit RecvEW(int socket); + + shared_connection connection_; + }; +} diff --git a/src/events/register.cc b/src/events/register.cc new file mode 100644 index 0000000..1717be8 --- /dev/null +++ b/src/events/register.cc @@ -0,0 +1,20 @@ +#include "register.hh" + +namespace paxos +{ + std::optional> + EventWatcherRegistry::at(EventWatcher* event) + { + return events_[event]; + } + + bool EventWatcherRegistry::unregister_ew(EventWatcher *ew) + { + if (events_.find(ew) == events_.end()) + return false; + + loop_.unregister_watcher(ew); + events_.erase(ew); + return true; + } +} diff --git a/src/events/register.hh b/src/events/register.hh new file mode 100644 index 0000000..a46f7a9 --- /dev/null +++ b/src/events/register.hh @@ -0,0 +1,98 @@ +/** + * \file events/register.hh + * \brief EventWatcherRegistry declaration. + */ + +#pragma once + +#include +#include +#include +#include + +#include "events/event-loop.hh" +#include "events/events.hh" + +namespace paxos +{ + /** + * \class EventWatcherRegistry + * \brief Registry containing an ev_loop and its ev_io. + * + * This class is used to instantiate a global registry inside of each + * instance of the server. Each registry will contain an EventLoop and + * its associated EventWatchers. + * An ev_io stores a raw pointer that needs to be immutable. To ensure + * that, we allocate EventWatcher using smart pointers. The registry + * holds a map to translate raw pointer to the smart one. + */ + class EventWatcherRegistry + { + public: + EventWatcherRegistry() = default; + EventWatcherRegistry(const EventWatcherRegistry&) = delete; + EventWatcherRegistry& operator=(const EventWatcherRegistry&) = delete; + EventWatcherRegistry(EventWatcherRegistry&&) = delete; + EventWatcherRegistry& operator=(EventWatcherRegistry&&) = delete; + + ~EventWatcherRegistry() = default; + + /** + * \brief Register a given event watcher. + * + * Associate the address stored in the ev_io data with a new + * EventWatcher instance inside of the registry's events_ map to + * operate on the ev_io as an EventWatcher object. Then, register this + * ev_io inside of the loop to start receiving its events. + * + * \param args Args variadic number of arguments of unknown types used + * to construct a specific EventWatcher with the proper parameters. + * \return A shared pointer on the constructed EventWatcher object. + */ + template + std::shared_ptr register_event(Args&&...); + + /** + * \brief Unregister a given event watcher. + * + * \param watcher EventWatcher* to unregister. + * \return If the operation was successful. + */ + bool unregister_ew(EventWatcher*); + + /** + * \brief Access EventWatcher in the map. + * + * \param watcher EventWatcher* key to the events_ map. + * \return The found EventWatcher otherwise std::nullopt. + */ + std::optional> at(EventWatcher*); + + EventLoop& get_event_loop() + { + return loop_; + } + + private: + /** + * \brief Map used to store EventWatchers + * + * This map is used to associate an ev_io to its registered + * EventWatcher instance. + */ + std::unordered_map> + events_; + + /** + * \brief Registry's event loop. + */ + EventLoop loop_; + }; + + /** + * \brief Service object. + */ + extern EventWatcherRegistry event_register; +} // namespace http + +#include "events/register.hxx" diff --git a/src/events/register.hxx b/src/events/register.hxx new file mode 100644 index 0000000..0eea34d --- /dev/null +++ b/src/events/register.hxx @@ -0,0 +1,18 @@ +#pragma once + +#include "events/register.hh" + +namespace paxos +{ + template + std::shared_ptr + EventWatcherRegistry::register_event(Args&&... args) + { + auto ew = std::make_shared(std::forward(args)...); + + events_[ew.get()] = ew; + loop_.register_watcher(ew.get()); + + return ew; + } +} // namespace http diff --git a/src/events/send.cc b/src/events/send.cc new file mode 100644 index 0000000..e51bf9b --- /dev/null +++ b/src/events/send.cc @@ -0,0 +1,23 @@ +#include "events/send.hh" +#include "events/receive.hh" +#include "events/register.hh" +#include "error/connection-closed.hh" +#include "error/parsing-error.hh" + +namespace paxos +{ + SendEW::SendEW(shared_connection connection) + : EventWatcher(connection->get_socket_fd()->fd_, EV_WRITE) + , connection_(connection) + { + } + + SendEW::~SendEW() + { + } + + void SendEW::operator()() + { + + } +} diff --git a/src/events/send.hh b/src/events/send.hh new file mode 100644 index 0000000..514b759 --- /dev/null +++ b/src/events/send.hh @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "events/events.hh" +#include "connection/connection.hh" + +namespace paxos +{ + class SendEW : public EventWatcher + { + public: + /** + * \brief Create a RecvEW from a listener socket. + */ + explicit SendEW(shared_connection connection); + + ~SendEW(); + SendEW(const SendEW&) = delete; + SendEW& operator=(const SendEW&) = delete; + + /** + * \brief Read on socket. + */ + void operator()() final; + + private: + shared_connection connection_; + }; + +} // namespace http diff --git a/src/legislator/legislator-factory.cc b/src/legislator/legislator-factory.cc index 92f2457..e61cd08 100644 --- a/src/legislator/legislator-factory.cc +++ b/src/legislator/legislator-factory.cc @@ -3,6 +3,8 @@ #include "legislator-factory.hh" #include "socket/default-socket.hh" #include "misc/addrinfo/addrinfo.hh" +#include "events/register.hh" +#include "events/listener.hh" namespace paxos { @@ -67,6 +69,7 @@ namespace paxos if (config.is_self) { shared_socket socket = prepare_socket(config); + event_register.register_event(socket); } return shared; } diff --git a/src/main.cc b/src/main.cc index ca67a40..7945368 100644 --- a/src/main.cc +++ b/src/main.cc @@ -2,7 +2,9 @@ #include "config/config.hh" #include "legislator/legislator-factory.hh" +#include "events/register.hh" +paxos::EventWatcherRegistry paxos::event_register; int main(int, char **argv) { diff --git a/src/misc/buffer.cc b/src/misc/buffer.cc index 7fc86f7..2559a99 100644 --- a/src/misc/buffer.cc +++ b/src/misc/buffer.cc @@ -39,7 +39,7 @@ namespace misc } } - size_t Buffer::receive(http::Socket& socket) + size_t Buffer::receive(paxos::Socket& socket) { char buf[BUFF_SIZE]; ssize_t n = socket.recv(buf, BUFF_SIZE); diff --git a/src/misc/buffer.hh b/src/misc/buffer.hh index fe5883c..cdb2784 100644 --- a/src/misc/buffer.hh +++ b/src/misc/buffer.hh @@ -26,7 +26,7 @@ namespace misc ** \brief Call the socket recv method and append to the buffer ** \param socket The default socket */ - size_t receive(http::Socket& socket); + size_t receive(paxos::Socket& socket); /** ** \brief Pop the string in front of data_ and returns it.