You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
dvmhost/network/rest/http/HTTPServer.h

161 lines
6.7 KiB

/**
* 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 <asio.hpp>
#include <thread>
#include <string>
#include <signal.h>
#include <utility>
#include <memory>
namespace rest {
namespace server {
// ---------------------------------------------------------------------------
// Class Declaration
// This class implements top-level routines of the HTTP server.
// ---------------------------------------------------------------------------
template<typename RequestHandlerType, template<class> class ConnectionImpl = Connection>
class HTTPServer {
public:
/// <summary>Initializes a new instance of the HTTPServer class.</summary>
template<typename Handler>
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>(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();
}
/// <summary>Initializes a copy instance of the HTTPServer class.</summary>
HTTPServer(const HTTPServer&) = delete;
/// <summary></summary>
HTTPServer& operator=(const HTTPServer&) = delete;
/// <summary>Run the servers ASIO IO service loop.</summary>
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:
/// <summary>Perform an asynchronous accept operation.</summary>
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<connection_type>(std::move(m_socket), m_connectionManager, m_requestHandler));
}
accept();
});
}
/// <summary>Wait for a request to stop the server.</summary>
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<RequestHandlerType> ConnectionType;
typedef std::shared_ptr<ConnectionType> ConnectionTypePtr;
asio::io_service m_ioService;
asio::signal_set m_signals;
asio::ip::tcp::acceptor m_acceptor;
ConnectionManager<ConnectionTypePtr> m_connectionManager;
asio::ip::tcp::socket m_socket;
RequestHandlerType m_requestHandler;
};
} // namespace server
} // namespace rest
#endif // __REST_HTTP__HTTP_SERVER_H__

Powered by TurnKey Linux.