/** * Digital Voice Modem - Host Software * GPLv2 Open Source. Use is subject to license terms. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * @package DVM / Host Software * */ // // Based on code from the CRUD project. (https://github.com/venediktov/CRUD) // Licensed under the BPL-1.0 License (https://opensource.org/license/bsl1-0-html) // /* * Copyright (c) 2003-2013 Christopher M. Kohlhoff * Copyright (C) 2023 by Bryan Biedenkapp N2PLL * * Permission is hereby granted, free of charge, to any person or organization * obtaining a copy of the software and accompanying documentation covered by * this license (the “Software”) to use, reproduce, display, distribute, execute, * and transmit the Software, and to prepare derivative works of the Software, and * to permit third-parties to whom the Software is furnished to do so, all subject * to the following: * * The copyright notices in the Software and this entire statement, including the * above license grant, this restriction and the following disclaimer, must be included * in all copies of the Software, in whole or in part, and all derivative works of the * Software, unless such copies or derivative works are solely in the form of * machine-executable object code generated by a source language processor. * * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #if !defined(__REST_HTTP__HTTP_SERVER_H__) #define __REST_HTTP__HTTP_SERVER_H__ #include "Defines.h" #include "network/rest/Connection.h" #include "network/rest/ConnectionManager.h" #include "network/rest/HTTPRequestHandler.h" #include #include #include #include #include #include namespace rest { namespace server { // --------------------------------------------------------------------------- // Class Declaration // This class implements top-level routines of the HTTP server. // --------------------------------------------------------------------------- template class ConnectionImpl = Connection> class HTTPServer { public: /// Initializes a new instance of the HTTPServer class. template explicit HTTPServer(const std::string& address, const std::string& port, Handler&& handler) : m_ioService(), m_signals(m_ioService), m_acceptor(m_ioService), m_connectionManager(), m_socket(m_ioService), m_requestHandler(std::forward(handler)) { // register to handle the signals that indicate when the server should exit // it is safe to register for the same signal multiple times in a program, // provided all registration for the specified signal is made through ASIO m_signals.add(SIGINT); m_signals.add(SIGTERM); #if defined(SIGQUIT) m_signals.add(SIGQUIT); #endif awaitStop(); // open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR) asio::ip::tcp::resolver resolver(m_ioService); asio::ip::tcp::endpoint endpoint = *resolver.resolve({address, port}); m_acceptor.open(endpoint.protocol()); m_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true)); m_acceptor.set_option(asio::socket_base::keep_alive(true)); m_acceptor.bind(endpoint); m_acceptor.listen(); accept(); } /// Initializes a copy instance of the HTTPServer class. HTTPServer(const HTTPServer&) = delete; /// HTTPServer& operator=(const HTTPServer&) = delete; /// Run the servers ASIO IO service loop. void run() { // the run() call will block until all asynchronous operations // have finished; while the server is running, there is always at least one // asynchronous operation outstanding: the asynchronous accept call waiting // for new incoming connections m_ioService.run(); } private: /// Perform an asynchronous accept operation. void accept() { m_acceptor.async_accept(m_socket, [this](asio::error_code ec) { // check whether the server was stopped by a signal before this // completion handler had a chance to run if (!m_acceptor.is_open()) { return; } if (!ec) { m_connectionManager.start(std::make_shared(std::move(m_socket), m_connectionManager, m_requestHandler)); } accept(); }); } /// Wait for a request to stop the server. void awaitStop() { m_signals.async_wait([this](asio::error_code /*ec*/, int /*signo*/) { // the server is stopped by cancelling all outstanding asynchronous // operations; once all operations have finished the m_ioService::run() // call will exit m_acceptor.close(); m_connectionManager.stopAll(); }); } typedef ConnectionImpl ConnectionType; typedef std::shared_ptr ConnectionTypePtr; asio::io_service m_ioService; asio::signal_set m_signals; asio::ip::tcp::acceptor m_acceptor; ConnectionManager m_connectionManager; asio::ip::tcp::socket m_socket; RequestHandlerType m_requestHandler; }; } // namespace server } // namespace rest #endif // __REST_HTTP__HTTP_SERVER_H__