*BEWARE* this commit alters the RPI_ARM cross-compiler and may break things *BEWARE*; implement support for HTTPS SSL REST API configuration; add contrib binary overlay for the RPI_ARM compiler (to add libssl-dev dependences);
parent
cda6b5965f
commit
31ca17449d
Binary file not shown.
@ -0,0 +1,190 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/**
|
||||
* Digital Voice Modem - Common Library
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* @package DVM / Common Library
|
||||
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||
*
|
||||
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#if !defined(__REST_HTTP__SECURE_CLIENT_CONNECTION_H__)
|
||||
#define __REST_HTTP__SECURE_CLIENT_CONNECTION_H__
|
||||
|
||||
#if defined(ENABLE_TCP_SSL)
|
||||
|
||||
#include "common/Defines.h"
|
||||
#include "common/network/rest/http/HTTPLexer.h"
|
||||
#include "common/network/rest/http/HTTPPayload.h"
|
||||
#include "common/Log.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <asio.hpp>
|
||||
#include <asio/ssl.hpp>
|
||||
|
||||
namespace network
|
||||
{
|
||||
namespace rest
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// This class represents a single connection from a client.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <typename RequestHandlerType>
|
||||
class SecureClientConnection {
|
||||
public:
|
||||
auto operator=(SecureClientConnection&) -> SecureClientConnection& = delete;
|
||||
auto operator=(SecureClientConnection&&) -> SecureClientConnection& = delete;
|
||||
SecureClientConnection(SecureClientConnection&) = delete;
|
||||
|
||||
/// <summary>Initializes a new instance of the SecureClientConnection class.</summary>
|
||||
explicit SecureClientConnection(asio::ip::tcp::socket socket, asio::ssl::context& context, RequestHandlerType& handler) :
|
||||
m_socket(std::move(socket), context),
|
||||
m_requestHandler(handler),
|
||||
m_lexer(HTTPLexer(true))
|
||||
{
|
||||
m_socket.set_verify_mode(asio::ssl::verify_none);
|
||||
m_socket.set_verify_callback(std::bind(&SecureClientConnection::verify_certificate, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
/// <summary>Start the first asynchronous operation for the connection.</summary>
|
||||
void start()
|
||||
{
|
||||
m_socket.handshake(asio::ssl::stream_base::client);
|
||||
read();
|
||||
}
|
||||
/// <summary>Stop all asynchronous operations associated with the connection.</summary>
|
||||
void stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
ensureNoLinger();
|
||||
if (m_socket.lowest_layer().is_open()) {
|
||||
m_socket.lowest_layer().close();
|
||||
}
|
||||
}
|
||||
catch(const std::exception&) { /* ignore */ }
|
||||
}
|
||||
|
||||
/// <summary>Helper to enable the SO_LINGER socket option during shutdown.</summary>
|
||||
void ensureNoLinger()
|
||||
{
|
||||
try
|
||||
{
|
||||
// enable SO_LINGER timeout 0
|
||||
asio::socket_base::linger linger(true, 0);
|
||||
m_socket.lowest_layer().set_option(linger);
|
||||
}
|
||||
catch(const asio::system_error& e)
|
||||
{
|
||||
asio::error_code ec = e.code();
|
||||
if (ec) {
|
||||
::LogError(LOG_REST, "SecureClientConnection::ensureNoLinger(), %s, code = %u", ec.message().c_str(), ec.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Perform an synchronous write operation.</summary>
|
||||
void send(HTTPPayload request)
|
||||
{
|
||||
request.attachHostHeader(m_socket.lowest_layer().remote_endpoint());
|
||||
write(request);
|
||||
}
|
||||
private:
|
||||
/// <summary>Perform an SSL certificate verification.</summary>
|
||||
bool verify_certificate(bool preverified, asio::ssl::verify_context& context)
|
||||
{
|
||||
return true; // ignore always valid
|
||||
}
|
||||
|
||||
/// <summary>Perform an asynchronous read operation.</summary>
|
||||
void read()
|
||||
{
|
||||
m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t bytes_transferred) {
|
||||
if (!ec) {
|
||||
HTTPLexer::ResultType result;
|
||||
char* content;
|
||||
|
||||
std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred);
|
||||
|
||||
std::string contentLength = m_request.headers.find("Content-Length");
|
||||
if (contentLength != "") {
|
||||
size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10);
|
||||
m_request.content = std::string(content, length);
|
||||
}
|
||||
|
||||
m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string());
|
||||
|
||||
if (result == HTTPLexer::GOOD) {
|
||||
m_requestHandler.handleRequest(m_request, m_reply);
|
||||
}
|
||||
else if (result == HTTPLexer::BAD) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
read();
|
||||
}
|
||||
}
|
||||
else if (ec != asio::error::operation_aborted) {
|
||||
if (ec) {
|
||||
::LogError(LOG_REST, "SecureClientConnection::read(), %s, code = %u", ec.message().c_str(), ec.value());
|
||||
}
|
||||
stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Perform an synchronous write operation.</summary>
|
||||
void write(HTTPPayload request)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_socket.handshake(asio::ssl::stream_base::client);
|
||||
|
||||
auto buffers = request.toBuffers();
|
||||
asio::write(m_socket, buffers);
|
||||
}
|
||||
catch(const asio::system_error& e)
|
||||
{
|
||||
asio::error_code ec = e.code();
|
||||
if (ec) {
|
||||
::LogError(LOG_REST, "SecureClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value());
|
||||
|
||||
try
|
||||
{
|
||||
// initiate graceful connection closure
|
||||
asio::error_code ignored_ec;
|
||||
m_socket.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||
}
|
||||
catch(const std::exception& e) {
|
||||
::LogError(LOG_REST, "SecureClientConnection::write(), %s, code = %u", ec.message().c_str(), ec.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asio::ssl::stream<asio::ip::tcp::socket> m_socket;
|
||||
|
||||
RequestHandlerType& m_requestHandler;
|
||||
|
||||
std::array<char, 8192> m_buffer;
|
||||
|
||||
HTTPPayload m_request;
|
||||
HTTPLexer m_lexer;
|
||||
HTTPPayload m_reply;
|
||||
};
|
||||
} // namespace http
|
||||
} // namespace rest
|
||||
} // namespace network
|
||||
|
||||
#endif // ENABLE_TCP_SSL
|
||||
|
||||
#endif // __REST_HTTP__SECURE_CLIENT_CONNECTION_H__
|
||||
@ -0,0 +1,178 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/**
|
||||
* Digital Voice Modem - Common Library
|
||||
* GPLv2 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* @package DVM / Common Library
|
||||
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
|
||||
*
|
||||
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#if !defined(__REST_HTTP__SECURE_HTTP_CLIENT_H__)
|
||||
#define __REST_HTTP__SECURE_HTTP_CLIENT_H__
|
||||
|
||||
#if defined(ENABLE_TCP_SSL)
|
||||
|
||||
#include "common/Defines.h"
|
||||
#include "common/network/rest/http/SecureClientConnection.h"
|
||||
#include "common/network/rest/http/HTTPRequestHandler.h"
|
||||
#include "common/Thread.h"
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <asio/ssl.hpp>
|
||||
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <signal.h>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace network
|
||||
{
|
||||
namespace rest
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// This class implements top-level routines of the secure HTTP client.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template<typename RequestHandlerType, template<class> class ConnectionImpl = SecureClientConnection>
|
||||
class SecureHTTPClient : private Thread {
|
||||
public:
|
||||
auto operator=(SecureHTTPClient&) -> SecureHTTPClient& = delete;
|
||||
auto operator=(SecureHTTPClient&&) -> SecureHTTPClient& = delete;
|
||||
SecureHTTPClient(SecureHTTPClient&) = delete;
|
||||
|
||||
/// <summary>Initializes a new instance of the SecureHTTPClient class.</summary>
|
||||
SecureHTTPClient(const std::string& address, uint16_t port) :
|
||||
m_address(address),
|
||||
m_port(port),
|
||||
m_connection(nullptr),
|
||||
m_ioContext(),
|
||||
m_context(asio::ssl::context::tlsv12),
|
||||
m_socket(m_ioContext),
|
||||
m_requestHandler()
|
||||
{
|
||||
/* stub */
|
||||
}
|
||||
/// <summary>Finalizes a instance of the SecureHTTPClient class.</summary>
|
||||
~SecureHTTPClient() override
|
||||
{
|
||||
if (m_connection != nullptr) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Helper to set the HTTP request handlers.</summary>
|
||||
template<typename Handler>
|
||||
void setHandler(Handler&& handler)
|
||||
{
|
||||
m_requestHandler = RequestHandlerType(std::forward<Handler>(handler));
|
||||
}
|
||||
|
||||
/// <summary>Send HTTP request to HTTP server.</summary>
|
||||
bool request(HTTPPayload& request)
|
||||
{
|
||||
if (m_completed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
asio::post(m_ioContext, [this, request]() {
|
||||
std::lock_guard<std::mutex> guard(m_lock);
|
||||
{
|
||||
if (m_connection != nullptr) {
|
||||
m_connection->send(request);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Opens connection to the network.</summary>
|
||||
bool open()
|
||||
{
|
||||
if (m_completed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return run();
|
||||
}
|
||||
|
||||
/// <summary>Closes connection to the network.</summary>
|
||||
void close()
|
||||
{
|
||||
if (m_completed) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_completed = true;
|
||||
m_ioContext.stop();
|
||||
|
||||
wait();
|
||||
}
|
||||
|
||||
private:
|
||||
/// <summary></summary>
|
||||
void entry() override
|
||||
{
|
||||
if (m_completed) {
|
||||
return;
|
||||
}
|
||||
|
||||
asio::ip::tcp::resolver resolver(m_ioContext);
|
||||
auto endpoints = resolver.resolve(m_address, std::to_string(m_port));
|
||||
|
||||
try {
|
||||
connect(endpoints);
|
||||
|
||||
// the entry() call will block until all asynchronous operations
|
||||
// have finished
|
||||
m_ioContext.run();
|
||||
}
|
||||
catch (std::exception&) { /* stub */ }
|
||||
|
||||
if (m_connection != nullptr) {
|
||||
m_connection->stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Perform an asynchronous connect operation.</summary>
|
||||
void connect(asio::ip::basic_resolver_results<asio::ip::tcp>& endpoints)
|
||||
{
|
||||
asio::connect(m_socket, endpoints);
|
||||
|
||||
m_connection = std::make_unique<ConnectionType>(std::move(m_socket), m_context, m_requestHandler);
|
||||
m_connection->start();
|
||||
}
|
||||
|
||||
std::string m_address;
|
||||
uint16_t m_port;
|
||||
|
||||
typedef ConnectionImpl<RequestHandlerType> ConnectionType;
|
||||
|
||||
std::unique_ptr<ConnectionType> m_connection;
|
||||
|
||||
bool m_completed = false;
|
||||
asio::io_context m_ioContext;
|
||||
|
||||
asio::ssl::context m_context;
|
||||
asio::ip::tcp::socket m_socket;
|
||||
|
||||
RequestHandlerType m_requestHandler;
|
||||
|
||||
std::mutex m_lock;
|
||||
};
|
||||
} // namespace http
|
||||
} // namespace rest
|
||||
} // namespace network
|
||||
|
||||
#endif // ENABLE_TCP_SSL
|
||||
|
||||
#endif // __REST_HTTP__SECURE_HTTP_CLIENT_H__
|
||||
@ -0,0 +1,164 @@
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/**
|
||||
* Digital Voice Modem - Common Library
|
||||
* BSL-1.0 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* @package DVM / Common Library
|
||||
* @derivedfrom CRUD (https://github.com/venediktov/CRUD)
|
||||
* @license BSL-1.0 License (https://opensource.org/license/bsl1-0-html)
|
||||
*
|
||||
* Copyright (c) 2003-2013 Christopher M. Kohlhoff
|
||||
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#if !defined(__REST_HTTP__SECURE_HTTP_SERVER_H__)
|
||||
#define __REST_HTTP__SECURE_HTTP_SERVER_H__
|
||||
|
||||
#if defined(ENABLE_TCP_SSL)
|
||||
|
||||
#include "common/Defines.h"
|
||||
#include "common/network/rest/http/SecureServerConnection.h"
|
||||
#include "common/network/rest/http/ServerConnectionManager.h"
|
||||
#include "common/network/rest/http/HTTPRequestHandler.h"
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <asio/ssl.hpp>
|
||||
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <signal.h>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
namespace network
|
||||
{
|
||||
namespace rest
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// This class implements top-level routines of the secure HTTP server.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template<typename RequestHandlerType, template<class> class ConnectionImpl = SecureServerConnection>
|
||||
class SecureHTTPServer {
|
||||
public:
|
||||
auto operator=(SecureHTTPServer&) -> SecureHTTPServer& = delete;
|
||||
auto operator=(SecureHTTPServer&&) -> SecureHTTPServer& = delete;
|
||||
SecureHTTPServer(SecureHTTPServer&) = delete;
|
||||
|
||||
/// <summary>Initializes a new instance of the SecureHTTPServer class.</summary>
|
||||
explicit SecureHTTPServer(const std::string& address, uint16_t port) :
|
||||
m_ioService(),
|
||||
m_acceptor(m_ioService),
|
||||
m_connectionManager(),
|
||||
m_context(asio::ssl::context::tlsv12),
|
||||
m_socket(m_ioService),
|
||||
m_requestHandler()
|
||||
{
|
||||
asio::ip::address ipAddress = asio::ip::address::from_string(address);
|
||||
m_endpoint = asio::ip::tcp::endpoint(ipAddress, port);
|
||||
}
|
||||
|
||||
/// <summary>Helper to set the SSL certificate and private key.</summary>
|
||||
bool setCertAndKey(const std::string& keyFile, const std::string& certFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_context.use_certificate_chain_file(certFile);
|
||||
m_context.use_private_key_file(keyFile, asio::ssl::context::pem);
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception& e) {
|
||||
::LogError(LOG_REST, "%s", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Helper to set the HTTP request handlers.</summary>
|
||||
template<typename Handler>
|
||||
void setHandler(Handler&& handler)
|
||||
{
|
||||
m_requestHandler = RequestHandlerType(std::forward<Handler>(handler));
|
||||
}
|
||||
|
||||
/// <summary>Open TCP acceptor.</summary>
|
||||
void open()
|
||||
{
|
||||
// open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR)
|
||||
m_acceptor.open(m_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(m_endpoint);
|
||||
m_acceptor.listen();
|
||||
|
||||
accept();
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
|
||||
/// <summary>Helper to stop running ASIO IO services.</summary>
|
||||
void stop()
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
|
||||
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<ConnectionType>(std::move(m_socket), m_context, m_connectionManager, m_requestHandler));
|
||||
}
|
||||
|
||||
accept();
|
||||
});
|
||||
}
|
||||
|
||||
typedef ConnectionImpl<RequestHandlerType> ConnectionType;
|
||||
typedef std::shared_ptr<ConnectionType> ConnectionTypePtr;
|
||||
|
||||
asio::io_service m_ioService;
|
||||
asio::ip::tcp::acceptor m_acceptor;
|
||||
|
||||
asio::ip::tcp::endpoint m_endpoint;
|
||||
|
||||
ServerConnectionManager<ConnectionTypePtr> m_connectionManager;
|
||||
|
||||
asio::ssl::context m_context;
|
||||
asio::ip::tcp::socket m_socket;
|
||||
|
||||
std::string m_certFile;
|
||||
std::string m_keyFile;
|
||||
|
||||
RequestHandlerType m_requestHandler;
|
||||
};
|
||||
} // namespace http
|
||||
} // namespace rest
|
||||
} // namespace network
|
||||
|
||||
#endif // ENABLE_TCP_SSL
|
||||
|
||||
#endif // __REST_HTTP__SECURE_HTTP_SERVER_H__
|
||||
@ -0,0 +1,202 @@
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/**
|
||||
* Digital Voice Modem - Common Library
|
||||
* BSL-1.0 Open Source. Use is subject to license terms.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* @package DVM / Common Library
|
||||
* @derivedfrom CRUD (https://github.com/venediktov/CRUD)
|
||||
* @license BSL-1.0 License (https://opensource.org/license/bsl1-0-html)
|
||||
*
|
||||
* Copyright (c) 2003-2013 Christopher M. Kohlhoff
|
||||
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
|
||||
*
|
||||
*/
|
||||
#if !defined(__REST_HTTP__SECURE_SERVER_CONNECTION_H__)
|
||||
#define __REST_HTTP__SECURE_SERVER_CONNECTION_H__
|
||||
|
||||
#if defined(ENABLE_TCP_SSL)
|
||||
|
||||
#include "common/Defines.h"
|
||||
#include "common/network/rest/http/HTTPLexer.h"
|
||||
#include "common/network/rest/http/HTTPPayload.h"
|
||||
#include "common/Log.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <asio.hpp>
|
||||
#include <asio/ssl.hpp>
|
||||
|
||||
namespace network
|
||||
{
|
||||
namespace rest
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Prototypes
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template<class> class ServerConnectionManager;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Class Declaration
|
||||
// This class represents a single connection from a client.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <typename RequestHandlerType>
|
||||
class SecureServerConnection : public std::enable_shared_from_this<SecureServerConnection<RequestHandlerType>> {
|
||||
typedef SecureServerConnection<RequestHandlerType> selfType;
|
||||
typedef std::shared_ptr<selfType> selfTypePtr;
|
||||
typedef ServerConnectionManager<selfTypePtr> ConnectionManagerType;
|
||||
public:
|
||||
auto operator=(SecureServerConnection&) -> SecureServerConnection& = delete;
|
||||
auto operator=(SecureServerConnection&&) -> SecureServerConnection& = delete;
|
||||
SecureServerConnection(SecureServerConnection&) = delete;
|
||||
|
||||
/// <summary>Initializes a new instance of the SecureServerConnection class.</summary>
|
||||
explicit SecureServerConnection(asio::ip::tcp::socket socket, asio::ssl::context& context, ConnectionManagerType& manager, RequestHandlerType& handler,
|
||||
bool persistent = false) :
|
||||
m_socket(std::move(socket), context),
|
||||
m_connectionManager(manager),
|
||||
m_requestHandler(handler),
|
||||
m_lexer(HTTPLexer(false)),
|
||||
m_persistent(persistent)
|
||||
{
|
||||
/* stub */
|
||||
}
|
||||
|
||||
/// <summary>Start the first asynchronous operation for the connection.</summary>
|
||||
void start() { handshake(); }
|
||||
/// <summary>Stop all asynchronous operations associated with the connection.</summary>
|
||||
void stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_socket.lowest_layer().is_open()) {
|
||||
m_socket.lowest_layer().close();
|
||||
}
|
||||
}
|
||||
catch(const std::exception&) { /* ignore */ }
|
||||
}
|
||||
|
||||
private:
|
||||
/// <summary>Perform an asynchronous SSL handshake.</summary>
|
||||
void handshake()
|
||||
{
|
||||
if (!m_persistent) {
|
||||
auto self(this->shared_from_this());
|
||||
}
|
||||
|
||||
m_socket.async_handshake(asio::ssl::stream_base::server, [this](asio::error_code ec) {
|
||||
if (!ec) {
|
||||
read();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Perform an asynchronous read operation.</summary>
|
||||
void read()
|
||||
{
|
||||
if (!m_persistent) {
|
||||
auto self(this->shared_from_this());
|
||||
}
|
||||
|
||||
m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t bytes_transferred) {
|
||||
if (!ec) {
|
||||
HTTPLexer::ResultType result;
|
||||
char* content;
|
||||
|
||||
std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred);
|
||||
|
||||
std::string contentLength = m_request.headers.find("Content-Length");
|
||||
if (contentLength != "") {
|
||||
size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10);
|
||||
m_request.content = std::string(content, length);
|
||||
}
|
||||
|
||||
m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string());
|
||||
|
||||
if (result == HTTPLexer::GOOD) {
|
||||
m_requestHandler.handleRequest(m_request, m_reply);
|
||||
write();
|
||||
}
|
||||
else if (result == HTTPLexer::BAD) {
|
||||
m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST);
|
||||
write();
|
||||
}
|
||||
else {
|
||||
read();
|
||||
}
|
||||
}
|
||||
else if (ec != asio::error::operation_aborted) {
|
||||
if (ec) {
|
||||
::LogError(LOG_REST, "SecureServerConnection::read(), %s, code = %u", ec.message().c_str(), ec.value());
|
||||
}
|
||||
m_connectionManager.stop(this->shared_from_this());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Perform an asynchronous write operation.</summary>
|
||||
void write()
|
||||
{
|
||||
if (!m_persistent) {
|
||||
auto self(this->shared_from_this());
|
||||
} else {
|
||||
m_reply.headers.add("Connection", "keep-alive");
|
||||
}
|
||||
|
||||
auto buffers = m_reply.toBuffers();
|
||||
asio::async_write(m_socket, buffers, [=](asio::error_code ec, std::size_t) {
|
||||
if (m_persistent) {
|
||||
m_lexer.reset();
|
||||
m_reply.headers = HTTPHeaders();
|
||||
m_reply.status = HTTPPayload::OK;
|
||||
m_reply.content = "";
|
||||
m_request = HTTPPayload();
|
||||
read();
|
||||
}
|
||||
else {
|
||||
if (!ec) {
|
||||
try
|
||||
{
|
||||
// initiate graceful connection closure
|
||||
asio::error_code ignored_ec;
|
||||
m_socket.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||
}
|
||||
catch(const std::exception& e) { ::LogError(LOG_REST, "%s", ec.message().c_str()); }
|
||||
}
|
||||
|
||||
if (ec != asio::error::operation_aborted) {
|
||||
if (ec) {
|
||||
::LogError(LOG_REST, "SecureServerConnection::write(), %s, code = %u", ec.message().c_str(), ec.value());
|
||||
}
|
||||
m_connectionManager.stop(this->shared_from_this());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
asio::ssl::stream<asio::ip::tcp::socket> m_socket;
|
||||
|
||||
ConnectionManagerType& m_connectionManager;
|
||||
RequestHandlerType& m_requestHandler;
|
||||
|
||||
std::array<char, 8192> m_buffer;
|
||||
|
||||
HTTPPayload m_request;
|
||||
HTTPLexer m_lexer;
|
||||
HTTPPayload m_reply;
|
||||
|
||||
bool m_persistent;
|
||||
};
|
||||
} // namespace http
|
||||
} // namespace rest
|
||||
} // namespace network
|
||||
|
||||
#endif // ENABLE_TCP_SSL
|
||||
|
||||
#endif // __REST_HTTP__SECURE_SERVER_CONNECTION_H__
|
||||
Loading…
Reference in new issue