parent
7d1ae452dc
commit
d3f5d00e5a
@ -0,0 +1,40 @@
|
|||||||
|
#
|
||||||
|
# Finds the ASIO library.
|
||||||
|
#
|
||||||
|
# from https://think-async.com/Asio/
|
||||||
|
#
|
||||||
|
# This will define the following variables
|
||||||
|
#
|
||||||
|
# ASIO_FOUND
|
||||||
|
# ASIO_INCLUDE_DIR
|
||||||
|
#
|
||||||
|
# and the following imported targets
|
||||||
|
#
|
||||||
|
# asio::asio
|
||||||
|
#
|
||||||
|
|
||||||
|
find_package(Threads QUIET)
|
||||||
|
if (Threads_FOUND)
|
||||||
|
find_path(ASIO_INCLUDE_DIR asio.hpp)
|
||||||
|
|
||||||
|
mark_as_advanced(ASIO_FOUND ASIO_INCLUDE_DIR)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(ASIO
|
||||||
|
FOUND_VAR ASIO_FOUND
|
||||||
|
REQUIRED_VARS ASIO_INCLUDE_DIR
|
||||||
|
)
|
||||||
|
|
||||||
|
if(ASIO_FOUND AND NOT TARGET asio::asio)
|
||||||
|
add_library(asio::asio INTERFACE IMPORTED)
|
||||||
|
target_include_directories(asio::asio INTERFACE ${ASIO_INCLUDE_DIR})
|
||||||
|
target_compile_definitions(asio::asio INTERFACE "ASIO_STANDALONE")
|
||||||
|
target_link_libraries(asio::asio INTERFACE Threads::Threads)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if(asio_FIND_REQUIRED)
|
||||||
|
message(FATAL_ERROR "asio requires Threads, which couldn't be found.")
|
||||||
|
elseif(asio_FIND_QUIETLY)
|
||||||
|
message(STATUS "asio requires Threads, which couldn't be found.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* 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__DISPATHER_H__)
|
||||||
|
#define __REST__DISPATHER_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <regex>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace rest {
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Structure Declaration
|
||||||
|
//
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<typename Matched>
|
||||||
|
struct RequestMatch : Matched
|
||||||
|
{
|
||||||
|
/// <summary>Initializes a new instance of the RequestMatch structure.</summary>
|
||||||
|
RequestMatch(const Matched& m, const std::string& d) : Matched(m) , data(d) { /* stub */ }
|
||||||
|
|
||||||
|
std::string data;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Structure Declaration
|
||||||
|
//
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<typename Response, typename Regex, typename Matched>
|
||||||
|
struct RequestMatcher {
|
||||||
|
typedef std::function<void(Response&, const RequestMatch<Matched>&)> RequestHandlerType;
|
||||||
|
typedef RequestMatcher<Response, Regex, Matched> selfType;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the RequestMatch structure.</summary>
|
||||||
|
explicit RequestMatcher(const Regex& expression) : m_expression(expression) { /* stub */ }
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
selfType& get(RequestHandlerType handler) {
|
||||||
|
m_handlers["GET"] = handler;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/// <summary></summary>
|
||||||
|
selfType& post(RequestHandlerType handler) {
|
||||||
|
m_handlers["POST"] = handler;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/// <summary></summary>
|
||||||
|
selfType& put(RequestHandlerType handler) {
|
||||||
|
m_handlers["PUT"] = handler;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/// <summary></summary>
|
||||||
|
selfType& del(RequestHandlerType handler) {
|
||||||
|
m_handlers["DELETE"] = handler;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
/// <summary></summary>
|
||||||
|
selfType& options(RequestHandlerType handler) {
|
||||||
|
m_handlers["OPTIONS"] = handler;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
template<typename Request>
|
||||||
|
void handleRequest(const Request& request, Response& response, const Matched &what) {
|
||||||
|
// dispatching to matching based on handler
|
||||||
|
RequestMatch<Matched> match(what, request.data);
|
||||||
|
auto& handler = m_handlers[request.method];
|
||||||
|
if (handler) {
|
||||||
|
handler(response, match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Regex m_expression;
|
||||||
|
std::map<std::string, RequestHandlerType> m_handlers;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements RESTful web request dispatching.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<typename Request, typename Response, typename Match = std::cmatch, typename Expression = std::regex>
|
||||||
|
class RequestDispatcher {
|
||||||
|
typedef RequestMatcher<Response, Expression, Match> MatcherType;
|
||||||
|
typedef std::shared_ptr<MatcherType> MatcherTypePtr;
|
||||||
|
public:
|
||||||
|
/// <summary>Initializes a new instance of the RequestDispatcher class.</summary>
|
||||||
|
RequestDispatcher() : m_basePath() { /* stub */ }
|
||||||
|
/// <summary>Initializes a new instance of the RequestDispatcher class.</summary>
|
||||||
|
RequestDispatcher(const std::string &basePath) : m_basePath(basePath) { /* stub */ }
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
MatcherType& match(const Expression& expression)
|
||||||
|
{
|
||||||
|
MatcherTypePtr& p = m_matchers[expression];
|
||||||
|
if(!p) {
|
||||||
|
p = std::make_shared<crud_matcher_type>(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
template<typename E = Expression>
|
||||||
|
typename std::enable_if<!std::is_same<E, std::string>::value, void>::type
|
||||||
|
handleRequest(const Request& request, Response& response)
|
||||||
|
{
|
||||||
|
for (const auto& matcher : m_matchers) {
|
||||||
|
Match what;
|
||||||
|
if (std::regex_match(request.uri.c_str(), what, matcher.first)) {
|
||||||
|
matcher.second->handle_request(request, response, what);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
template<typename E = Expression>
|
||||||
|
typename std::enable_if<std::is_same<E, std::string>::value, void>::type
|
||||||
|
handleRequest(const Request& request, Response& response)
|
||||||
|
{
|
||||||
|
for ( const auto& matcher : m_matchers) {
|
||||||
|
Match what;
|
||||||
|
if (request.uri.find(matcher.first) != std::string::npos) {
|
||||||
|
what = matcher.first;
|
||||||
|
matcher.second->handle_request(request, response, what);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_basePath;
|
||||||
|
std::map<Expression, MatcherTypePtr> m_matchers;
|
||||||
|
};
|
||||||
|
} // namespace rest
|
||||||
|
|
||||||
|
#endif // __REST__DISPATHER_H__
|
||||||
@ -0,0 +1,174 @@
|
|||||||
|
/**
|
||||||
|
* 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__CONNECTION_H__)
|
||||||
|
#define __REST_HTTP__CONNECTION_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "network/rest/http/HTTPReply.h"
|
||||||
|
#include "network/rest/http/HTTPRequest.h"
|
||||||
|
#include "network/rest/http/HTTPRequestLexer.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
#include <iterator>
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
namespace rest {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Prototypes
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class> class ConnectionManager;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class represents a single connection from a client.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <typename RequestHandlerType>
|
||||||
|
class Connection : public std::enable_shared_from_this<Connection<RequestHandlerType>>
|
||||||
|
{
|
||||||
|
typedef Connection<RequestHandlerType> selfType;
|
||||||
|
typedef std::shared_ptr<selfType> selfTypePtr;
|
||||||
|
typedef ConnectionManager<selfTypePtr> ConnectionManagerType;
|
||||||
|
public:
|
||||||
|
/// <summary>Initializes a new instance of the Connection class.</summary>
|
||||||
|
explicit Connection(asio::ip::tcp::socket socket, ConnectionManagerType& manager, RequestHandlerType& handler,
|
||||||
|
bool persistent = false) :
|
||||||
|
m_socket(std::move(socket)),
|
||||||
|
m_connectionManager(manager),
|
||||||
|
m_requestHandler(handler),
|
||||||
|
m_persistent(persistent)
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
/// <summary>Initializes a copy instance of the Connection class.</summary>
|
||||||
|
Connection(const Connection&) = delete;
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
Connection& operator=(const Connection&) = delete;
|
||||||
|
|
||||||
|
/// <summary>Start the first asynchronous operation for the connection.</summary>
|
||||||
|
void start() { read(); }
|
||||||
|
/// <summary>Stop all asynchronous operations associated with the connection.</summary>
|
||||||
|
void stop() { m_socket.close(); }
|
||||||
|
private:
|
||||||
|
/// <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) {
|
||||||
|
HTTPRequestLexer::ResultType result;
|
||||||
|
char* data;
|
||||||
|
|
||||||
|
std::tie(result, data) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred);
|
||||||
|
auto itr = std::find_if(m_request.headers.begin(), m_request.headers.end(), [](const header &h) { return h.name == "content-length"; });
|
||||||
|
if (itr != request_.headers.end()) {
|
||||||
|
m_request.data = std::string(data, std::static_cast<long>(itr->value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == HTTPRequestLexer::GOOD) {
|
||||||
|
m_requestHandler.handleRequest(m_request, m_reply);
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
else if (result == HTTPRequestLexer::BAD) {
|
||||||
|
m_reply = reply::stockReply(HTTPReply::BAD_REQUEST);
|
||||||
|
write();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ec != asio::error::operation_aborted) {
|
||||||
|
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.emplace_back("Connection:", "keep-alive");
|
||||||
|
}
|
||||||
|
|
||||||
|
asio::async_write(m_socket, m_reply.to_buffers(), [=](asio::error_code ec, std::size_t) {
|
||||||
|
if (m_persistent) {
|
||||||
|
m_lexer.reset();
|
||||||
|
m_reply.headers.resize(0);
|
||||||
|
m_reply.status = HTTPReply::OK;
|
||||||
|
m_reply.content = "";
|
||||||
|
m_request = HTTPRequest();
|
||||||
|
read();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!ec) {
|
||||||
|
// initiate graceful connection closure
|
||||||
|
asio::error_code ignored_ec;
|
||||||
|
m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ec != asio::error::operation_aborted) {
|
||||||
|
m_connectionManager.stop(this->shared_from_this());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
asio::ip::tcp::socket m_socket;
|
||||||
|
ConnectionManagerType& m_connectionManager;
|
||||||
|
RequestHandlerType& m_requestHandler;
|
||||||
|
std::array<char, 8192> m_buffer;
|
||||||
|
HTTPRequest m_request;
|
||||||
|
HTTPRequestLexer m_lexer;
|
||||||
|
HTTPReply m_reply;
|
||||||
|
bool m_persistent;
|
||||||
|
};
|
||||||
|
} // namespace server
|
||||||
|
} // namespace rest
|
||||||
|
|
||||||
|
#endif // __REST_HTTP__CONNECTION_H__
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* 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__CONNECTION_MANAGER_H__)
|
||||||
|
#define __REST_HTTP__CONNECTION_MANAGER_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace rest {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// Manages open connections so that they may be cleanly stopped when the server
|
||||||
|
// needs to shut down.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<typename ConnectionPtr>
|
||||||
|
class ConnectionManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>Initializes a new instance of the ConnectionManager class.</summary>
|
||||||
|
ConnectionManager() { /* stub */ }
|
||||||
|
/// <summary>Initializes a copy instance of the ConnectionManager class.</summary>
|
||||||
|
ConnectionManager(const ConnectionManager&) = delete;
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
ConnectionManager& operator=(const ConnectionManager&) = delete;
|
||||||
|
|
||||||
|
/// <summary>Add the specified connection to the manager and start it.</summary>
|
||||||
|
void start(ConnectionPtr c)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock);
|
||||||
|
{
|
||||||
|
m_connections.insert(c);
|
||||||
|
}
|
||||||
|
c->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Stop the specified connection.</summary>
|
||||||
|
void stop(connection_ptr c)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock);
|
||||||
|
{
|
||||||
|
m_connections.erase(c);
|
||||||
|
}
|
||||||
|
c->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Stop all connections.</summary>
|
||||||
|
void stopAll()
|
||||||
|
{
|
||||||
|
for (auto c : m_connections)
|
||||||
|
c->stop();
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> guard(m_lock);
|
||||||
|
m_connections.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::set<ConnectionPtr> m_connections;
|
||||||
|
std::mutex m_lock;
|
||||||
|
};
|
||||||
|
} // namespace server
|
||||||
|
} // namespace rest
|
||||||
|
|
||||||
|
#endif // __REST_HTTP__CONNECTION_MANAGER_H__
|
||||||
|
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* 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_HEADER_H__)
|
||||||
|
#define __REST_HTTP__HTTP_HEADER_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace rest {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Structure Declaration
|
||||||
|
// This class implements a model for an HTTP header.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct HTTPHeader
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the HTTPHeader struct.</summary>
|
||||||
|
HTTPHeader() { /* stub */ }
|
||||||
|
/// <summary>Initializes a new instance of the HTTPHeader struct.</summary>
|
||||||
|
HTTPHeader(const std::string& name, const std::string& value) : name{name}, value{value} { /* stub */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace server
|
||||||
|
} // namespace rest
|
||||||
|
|
||||||
|
#endif // __REST_HTTP__HTTP_HEADER_H__
|
||||||
@ -0,0 +1,267 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "network/rest/http/HTTPReply.h"
|
||||||
|
|
||||||
|
using namespace rest::server;
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace status_strings {
|
||||||
|
const std::string ok = "HTTP/1.0 200 OK\r\n";
|
||||||
|
const std::string created = "HTTP/1.0 201 Created\r\n";
|
||||||
|
const std::string accepted = "HTTP/1.0 202 Accepted\r\n";
|
||||||
|
const std::string no_content = "HTTP/1.0 204 No Content\r\n";
|
||||||
|
const std::string multiple_choices = "HTTP/1.0 300 Multiple Choices\r\n";
|
||||||
|
const std::string moved_permanently = "HTTP/1.0 301 Moved Permanently\r\n";
|
||||||
|
const std::string moved_temporarily = "HTTP/1.0 302 Moved Temporarily\r\n";
|
||||||
|
const std::string not_modified = "HTTP/1.0 304 Not Modified\r\n";
|
||||||
|
const std::string bad_request = "HTTP/1.0 400 Bad Request\r\n";
|
||||||
|
const std::string unauthorized = "HTTP/1.0 401 Unauthorized\r\n";
|
||||||
|
const std::string forbidden = "HTTP/1.0 403 Forbidden\r\n";
|
||||||
|
const std::string not_found = "HTTP/1.0 404 Not Found\r\n";
|
||||||
|
const std::string internal_server_error = "HTTP/1.0 500 Internal Server Error\r\n";
|
||||||
|
const std::string not_implemented = "HTTP/1.0 501 Not Implemented\r\n";
|
||||||
|
const std::string bad_gateway = "HTTP/1.0 502 Bad Gateway\r\n";
|
||||||
|
const std::string service_unavailable = "HTTP/1.0 503 Service Unavailable\r\n";
|
||||||
|
|
||||||
|
asio::const_buffer toBuffer(HTTPReply::StatusType status)
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case HTTPReply::OK:
|
||||||
|
return asio::buffer(ok);
|
||||||
|
case HTTPReply::CREATED:
|
||||||
|
return asio::buffer(created);
|
||||||
|
case HTTPReply::ACCEPTED:
|
||||||
|
return asio::buffer(accepted);
|
||||||
|
case HTTPReply::NO_CONTENT:
|
||||||
|
return asio::buffer(no_content);
|
||||||
|
case HTTPReply::MULTIPLE_CHOICES:
|
||||||
|
return asio::buffer(multiple_choices);
|
||||||
|
case HTTPReply::MOVED_PERMANENTLY:
|
||||||
|
return asio::buffer(moved_permanently);
|
||||||
|
case HTTPReply::MOVED_TEMPORARILY:
|
||||||
|
return asio::buffer(moved_temporarily);
|
||||||
|
case HTTPReply::NOT_MODIFIED:
|
||||||
|
return asio::buffer(not_modified);
|
||||||
|
case HTTPReply::BAD_REQUEST:
|
||||||
|
return asio::buffer(bad_request);
|
||||||
|
case HTTPReply::UNAUTHORIZED:
|
||||||
|
return asio::buffer(unauthorized);
|
||||||
|
case HTTPReply::FORBIDDEN:
|
||||||
|
return asio::buffer(forbidden);
|
||||||
|
case HTTPReply::NOT_FOUND:
|
||||||
|
return asio::buffer(not_found);
|
||||||
|
case HTTPReply::INTERNAL_SERVER_ERROR:
|
||||||
|
return asio::buffer(internal_server_error);
|
||||||
|
case HTTPReply::NOT_IMPLEMENTED:
|
||||||
|
return asio::buffer(not_implemented);
|
||||||
|
case HTTPReply::BAD_GATEWAY:
|
||||||
|
return asio::buffer(bad_gateway);
|
||||||
|
case HTTPReply::SERVICE_UNAVAILABLE:
|
||||||
|
return asio::buffer(service_unavailable);
|
||||||
|
default:
|
||||||
|
return asio::buffer(internal_server_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace status_strings
|
||||||
|
|
||||||
|
namespace misc_strings {
|
||||||
|
const char name_value_separator[] = { ':', ' ' };
|
||||||
|
const char crlf[] = { '\r', '\n' };
|
||||||
|
} // namespace misc_strings
|
||||||
|
|
||||||
|
std::vector<asio::const_buffer> HTTPReply::toBuffers()
|
||||||
|
{
|
||||||
|
std::vector<asio::const_buffer> buffers;
|
||||||
|
buffers.push_back(status_strings::toBuffer(status));
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < headers.size(); ++i) {
|
||||||
|
HTTPHeader& h = headers[i];
|
||||||
|
buffers.push_back(asio::buffer(h.name));
|
||||||
|
buffers.push_back(asio::buffer(misc_strings::name_value_separator));
|
||||||
|
buffers.push_back(asio::buffer(h.value));
|
||||||
|
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||||
|
}
|
||||||
|
|
||||||
|
buffers.push_back(asio::buffer(misc_strings::crlf));
|
||||||
|
buffers.push_back(asio::buffer(content));
|
||||||
|
return buffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace stock_replies {
|
||||||
|
const char ok[] = "";
|
||||||
|
const char created[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Created</title></head>"
|
||||||
|
"<body><h1>201 Created</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char accepted[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Accepted</title></head>"
|
||||||
|
"<body><h1>202 Accepted</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char no_content[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>No Content</title></head>"
|
||||||
|
"<body><h1>204 Content</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char multiple_choices[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Multiple Choices</title></head>"
|
||||||
|
"<body><h1>300 Multiple Choices</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char moved_permanently[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Moved Permanently</title></head>"
|
||||||
|
"<body><h1>301 Moved Permanently</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char moved_temporarily[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Moved Temporarily</title></head>"
|
||||||
|
"<body><h1>302 Moved Temporarily</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char not_modified[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Not Modified</title></head>"
|
||||||
|
"<body><h1>304 Not Modified</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char bad_request[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Bad Request</title></head>"
|
||||||
|
"<body><h1>400 Bad Request</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char unauthorized[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Unauthorized</title></head>"
|
||||||
|
"<body><h1>401 Unauthorized</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char forbidden[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Forbidden</title></head>"
|
||||||
|
"<body><h1>403 Forbidden</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char not_found[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Not Found</title></head>"
|
||||||
|
"<body><h1>404 Not Found</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char internal_server_error[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Internal Server Error</title></head>"
|
||||||
|
"<body><h1>500 Internal Server Error</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char not_implemented[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Not Implemented</title></head>"
|
||||||
|
"<body><h1>501 Not Implemented</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char bad_gateway[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Bad Gateway</title></head>"
|
||||||
|
"<body><h1>502 Bad Gateway</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
const char service_unavailable[] =
|
||||||
|
"<html>"
|
||||||
|
"<head><title>Service Unavailable</title></head>"
|
||||||
|
"<body><h1>503 Service Unavailable</h1></body>"
|
||||||
|
"</html>";
|
||||||
|
|
||||||
|
std::string to_string(HTTPReply::StatusType status)
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case HTTPReply::OK:
|
||||||
|
return ok;
|
||||||
|
case HTTPReply::CREATED:
|
||||||
|
return created;
|
||||||
|
case HTTPReply::ACCEPTED:
|
||||||
|
return accepted;
|
||||||
|
case HTTPReply::NO_CONTENT:
|
||||||
|
return no_content;
|
||||||
|
case HTTPReply::MULTIPLE_CHOICES:
|
||||||
|
return multiple_choices;
|
||||||
|
case HTTPReply::MOVED_PERMANENTLY:
|
||||||
|
return moved_permanently;
|
||||||
|
case HTTPReply::MOVED_TEMPORARILY:
|
||||||
|
return moved_temporarily;
|
||||||
|
case HTTPReply::NOT_MODIFIED:
|
||||||
|
return not_modified;
|
||||||
|
case HTTPReply::BAD_REQUEST:
|
||||||
|
return bad_request;
|
||||||
|
case HTTPReply::UNAUTHORIZED:
|
||||||
|
return unauthorized;
|
||||||
|
case HTTPReply::FORBIDDEN:
|
||||||
|
return forbidden;
|
||||||
|
case HTTPReply::NOT_FOUND:
|
||||||
|
return not_found;
|
||||||
|
case HTTPReply::INTERNAL_SERVER_ERROR:
|
||||||
|
return internal_server_error;
|
||||||
|
case HTTPReply::NOT_IMPLEMENTED:
|
||||||
|
return not_implemented;
|
||||||
|
case HTTPReply::BAD_GATEWAY:
|
||||||
|
return bad_gateway;
|
||||||
|
case HTTPReply::SERVICE_UNAVAILABLE:
|
||||||
|
return service_unavailable;
|
||||||
|
default:
|
||||||
|
return internal_server_error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace stock_replies
|
||||||
|
|
||||||
|
HTTPReply HTTPReply::stockReply(HTTPReply::StatusType status, const char* mime)
|
||||||
|
{
|
||||||
|
HTTPReply rep;
|
||||||
|
rep.status = status;
|
||||||
|
|
||||||
|
if (status != HTTPReply::NO_CONTENT) {
|
||||||
|
rep.content = stock_replies::to_string(status);
|
||||||
|
rep.headers.resize(2);
|
||||||
|
rep.headers[0].name = "Content-Length";
|
||||||
|
rep.headers[0].value = std::to_string(rep.content.size());
|
||||||
|
rep.headers[1].name = "Content-Type";
|
||||||
|
rep.headers[1].value = mime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPReply& operator<<(HTTPReply& r, const std::string &value) {
|
||||||
|
r.content.append(value);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* 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_REPLY_H__)
|
||||||
|
#define __REST_HTTP__HTTP_REPLY_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "network/rest/http/HTTPHeader.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <asio.hpp>
|
||||||
|
|
||||||
|
namespace rest {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Structure Declaration
|
||||||
|
// This struct implements a model of a reply to be sent to a HTTP client.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct HTTPReply
|
||||||
|
{
|
||||||
|
enum StatusType {
|
||||||
|
OK = 200,
|
||||||
|
CREATED = 201,
|
||||||
|
ACCEPTED = 202,
|
||||||
|
NO_CONTENT = 204,
|
||||||
|
MULTIPLE_CHOICES = 300,
|
||||||
|
MOVED_PERMANENTLY = 301,
|
||||||
|
MOVED_TEMPORARILY = 302,
|
||||||
|
NOT_MODIFIED = 304,
|
||||||
|
BAD_REQUEST = 400,
|
||||||
|
UNAUTHORIZED = 401,
|
||||||
|
FORBIDDEN = 403,
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
INTERNAL_SERVER_ERROR = 500,
|
||||||
|
NOT_IMPLEMENTED = 501,
|
||||||
|
BAD_GATEWAY = 502,
|
||||||
|
SERVICE_UNAVAILABLE = 503
|
||||||
|
} status;
|
||||||
|
|
||||||
|
std::vector<HTTPHeader> headers;
|
||||||
|
std::string content;
|
||||||
|
|
||||||
|
/// <summary>Convert the reply into a vector of buffers. The buffers do not own the
|
||||||
|
/// underlying memory blocks, therefore the reply object must remain valid and
|
||||||
|
/// not be changed until the write operation has completed.</summary>
|
||||||
|
std::vector<asio::const_buffer> toBuffers();
|
||||||
|
|
||||||
|
/// <summary>Get a stock reply.</summary>
|
||||||
|
static HTTPReply stockReply(StatusType status, const char* mime = "text/html");
|
||||||
|
};
|
||||||
|
|
||||||
|
HTTPReply& operator<<(HTTPReply& r, const std::string& value);
|
||||||
|
} // namespace server
|
||||||
|
} // namespace rest
|
||||||
|
|
||||||
|
#endif // __REST_HTTP__HTTP_REPLY_H__
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* 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_REQUEST_H__)
|
||||||
|
#define __REST_HTTP__HTTP_REQUEST_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "network/rest/http/HTTPHeader.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace rest {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Structure Declaration
|
||||||
|
// This struct implements a model of a request received from a HTTP client.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct HTTPRequest
|
||||||
|
{
|
||||||
|
std::string method;
|
||||||
|
std::string uri;
|
||||||
|
|
||||||
|
int httpVersionMajor;
|
||||||
|
int httpVersionMinor;
|
||||||
|
|
||||||
|
std::vector<HTTPHeader> headers;
|
||||||
|
std::string data;
|
||||||
|
};
|
||||||
|
} // namespace server
|
||||||
|
} // namespace http
|
||||||
|
|
||||||
|
#endif // __REST_HTTP__HTTP_REQUEST_H__
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "network/rest/http/HTTPRequestHandler.h"
|
||||||
|
#include "network/rest/http/HTTPRequest.h"
|
||||||
|
#include "network/rest/http/HTTPReply.h"
|
||||||
|
|
||||||
|
using namespace rest::server;
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the HTTPRequestHandler class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="docRoot"></param>
|
||||||
|
HTTPRequestHandler::HTTPRequestHandler(const std::string& docRoot) :
|
||||||
|
m_docRoot(docRoot)
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle a request and produce a reply.
|
||||||
|
/// </summary>
|
||||||
|
void HTTPRequestHandler::handleRequest(const HTTPRequest& request, HTTPReply& reply)
|
||||||
|
{
|
||||||
|
// decode url to path
|
||||||
|
std::string requestPath;
|
||||||
|
if (!urlDecode(request.uri, requestPath)) {
|
||||||
|
reply = HTTPReply::stockReply(HTTPReply::BAD_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// request path must be absolute and not contain "..".
|
||||||
|
if (requestPath.empty() || requestPath[0] != '/' ||
|
||||||
|
requestPath.find("..") != std::string::npos) {
|
||||||
|
reply = HTTPReply::stockReply(HTTPReply::BAD_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if path ends in slash (i.e. is a directory) then add "index.html"
|
||||||
|
if (requestPath[requestPath.size() - 1] == '/') {
|
||||||
|
requestPath += "index.html";
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine the file extension
|
||||||
|
std::size_t lastSlashPos = requestPath.find_last_of("/");
|
||||||
|
std::size_t lastDotPos = requestPath.find_last_of(".");
|
||||||
|
std::string extension;
|
||||||
|
if (lastDotPos != std::string::npos && lastDotPos > lastSlashPos) {
|
||||||
|
extension = requestPath.substr(lastDotPos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the file to send back
|
||||||
|
std::string fullPath = m_docRoot + requestPath;
|
||||||
|
std::ifstream is(fullPath.c_str(), std::ios::in | std::ios::binary);
|
||||||
|
if (!is) {
|
||||||
|
reply = HTTPReply::stockReply(HTTPReply::NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill out the reply to be sent to the client
|
||||||
|
reply.status = HTTPReply::OK;
|
||||||
|
|
||||||
|
char buf[512];
|
||||||
|
while (is.read(buf, sizeof(buf)).gcount() > 0)
|
||||||
|
reply.content.append(buf, is.gcount());
|
||||||
|
|
||||||
|
reply.headers.resize(2);
|
||||||
|
reply.headers[0].name = "Content-Length";
|
||||||
|
reply.headers[0].value = std::to_string(reply.content.size());
|
||||||
|
reply.headers[1].name = "Content-Type";
|
||||||
|
reply.headers[1].value = "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform URL-decoding on a string. Returns false if the encoding was invalid.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="in"></param>
|
||||||
|
/// <param name="out"></param>
|
||||||
|
bool HTTPRequestHandler::urlDecode(const std::string& in, std::string& out)
|
||||||
|
{
|
||||||
|
out.clear();
|
||||||
|
out.reserve(in.size());
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < in.size(); ++i) {
|
||||||
|
if (in[i] == '%') {
|
||||||
|
if (i + 3 <= in.size()) {
|
||||||
|
int value = 0;
|
||||||
|
std::istringstream is(in.substr(i + 1, 2));
|
||||||
|
if (is >> std::hex >> value) {
|
||||||
|
out += static_cast<char>(value);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (in[i] == '+') {
|
||||||
|
out += ' ';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out += in[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* 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_REQUEST_HANDLER_H__)
|
||||||
|
#define __REST_HTTP__HTTP_REQUEST_HANDLER_H__
|
||||||
|
|
||||||
|
#include "Defines.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace rest {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Prototypes
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct HTTPReply;
|
||||||
|
struct HTTPRequest;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the common handler for all incoming requests.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HTTPRequestHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// <summary>Initializes a new instance of the HTTPRequestHandler class.</summary>
|
||||||
|
explicit HTTPRequestHandler(const std::string& docRoot);
|
||||||
|
/// <summary>Initializes a copy instance of the HTTPRequestHandler class.</summary>
|
||||||
|
HTTPRequestHandler(const HTTPRequestHandler&) = delete;
|
||||||
|
/// <summary></summary>
|
||||||
|
HTTPRequestHandler(HTTPRequestHandler&&) = default;
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
HTTPRequestHandler& operator=(const HTTPRequestHandler&) = delete;
|
||||||
|
/// <summary></summary>
|
||||||
|
HTTPRequestHandler& operator=(HTTPRequestHandler&&) = default;
|
||||||
|
|
||||||
|
/// <summary>Handle a request and produce a reply.</summary>
|
||||||
|
void handleRequest(const HTTPRequest& req, HTTPReply& reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// <summary>Perform URL-decoding on a string. Returns false if the encoding was
|
||||||
|
/// invalid.</summary>
|
||||||
|
static bool urlDecode(const std::string& in, std::string& out);
|
||||||
|
|
||||||
|
std::string m_docRoot;
|
||||||
|
};
|
||||||
|
} // namespace server
|
||||||
|
} // namespace rest
|
||||||
|
|
||||||
|
#endif // __REST_HTTP__HTTP_REQUEST_HANDLER_H__
|
||||||
@ -0,0 +1,361 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
#include "Defines.h"
|
||||||
|
#include "network/rest/http/HTTPRequestLexer.h"
|
||||||
|
#include "network/rest/http/HTTPRequest.h"
|
||||||
|
|
||||||
|
using namespace rest::server;
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the HTTPRequestLexer class.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
HTTPRequestLexer::HTTPRequestLexer() :
|
||||||
|
m_state(METHOD_START)
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reset to initial parser state.</summary>
|
||||||
|
void HTTPRequestLexer::reset()
|
||||||
|
{
|
||||||
|
m_state = METHOD_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Private Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle the next character of input.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="req"></param>
|
||||||
|
/// <param name="input"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
HTTPRequestLexer::ResultType HTTPRequestLexer::consume(HTTPRequest& req, char input)
|
||||||
|
{
|
||||||
|
switch (m_state)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
** HTTP Method
|
||||||
|
*/
|
||||||
|
|
||||||
|
case METHOD_START:
|
||||||
|
if (!isChar(input) || isControl(input) || isSpecial(input))
|
||||||
|
return BAD;
|
||||||
|
else {
|
||||||
|
m_state = METHOD;
|
||||||
|
req.method.push_back(input);
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
case METHOD:
|
||||||
|
if (input == ' ') {
|
||||||
|
m_state = URI;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (!isChar(input) || isControl(input) || isSpecial(input)) {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
req.method.push_back(input);
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** URI
|
||||||
|
*/
|
||||||
|
|
||||||
|
case URI:
|
||||||
|
if (input == ' ') {
|
||||||
|
m_state = HTTP_VERSION_H;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (isControl(input)) {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
req.uri.push_back(input);
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** HTTP/1.0
|
||||||
|
*/
|
||||||
|
case HTTP_VERSION_H:
|
||||||
|
if (input == 'H') {
|
||||||
|
m_state = HTTP_VERSION_T_1;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
case HTTP_VERSION_T_1:
|
||||||
|
if (input == 'T') {
|
||||||
|
m_state = HTTP_VERSION_T_2;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
case HTTP_VERSION_T_2:
|
||||||
|
if (input == 'T') {
|
||||||
|
m_state = HTTP_VERSION_P;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
case HTTP_VERSION_P:
|
||||||
|
if (input == 'P') {
|
||||||
|
m_state = HTTP_VERSION_SLASH;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
case HTTP_VERSION_SLASH:
|
||||||
|
if (input == '/') {
|
||||||
|
req.httpVersionMajor = 0;
|
||||||
|
req.httpVersionMinor = 0;
|
||||||
|
m_state = HTTP_VERSION_MAJOR_START;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
case HTTP_VERSION_MAJOR_START:
|
||||||
|
if (isDigit(input)) {
|
||||||
|
req.httpVersionMajor = req.httpVersionMajor * 10 + input - '0';
|
||||||
|
m_state = HTTP_VERSION_MAJOR;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
case HTTP_VERSION_MAJOR:
|
||||||
|
if (input == '.') {
|
||||||
|
m_state = HTTP_VERSION_MINOR_START;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (isDigit(input)) {
|
||||||
|
req.httpVersionMajor = req.httpVersionMajor * 10 + input - '0';
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
case HTTP_VERSION_MINOR_START:
|
||||||
|
if (isDigit(input)) {
|
||||||
|
req.httpVersionMinor = req.httpVersionMinor * 10 + input - '0';
|
||||||
|
m_state = HTTP_VERSION_MINOR;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
case HTTP_VERSION_MINOR:
|
||||||
|
if (input == '\r')
|
||||||
|
{
|
||||||
|
m_state = EXPECTING_NEWLINE_1;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (isDigit(input))
|
||||||
|
{
|
||||||
|
req.httpVersionMinor = req.httpVersionMinor * 10 + input - '0';
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPECTING_NEWLINE_1:
|
||||||
|
if (input == '\n') {
|
||||||
|
m_state = HEADER_LINE_START;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Headers
|
||||||
|
*/
|
||||||
|
|
||||||
|
case HEADER_LINE_START:
|
||||||
|
if (input == '\r') {
|
||||||
|
m_state = EXPECTING_NEWLINE_3;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (!req.headers.empty() && (input == ' ' || input == '\t')) {
|
||||||
|
m_state = HEADER_LWS;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (!isChar(input) || isControl(input) || isSpecial(input)) {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
req.headers.push_back(HTTPHeader());
|
||||||
|
req.headers.back().name.push_back(std::tolower(input));
|
||||||
|
m_state = HEADER_NAME;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HEADER_LWS:
|
||||||
|
if (input == '\r') {
|
||||||
|
m_state = EXPECTING_NEWLINE_2;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (input == ' ' || input == '\t') {
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (isControl(input)) {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_state = HEADER_VALUE;
|
||||||
|
req.headers.back().value.push_back(input);
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HEADER_NAME:
|
||||||
|
if (input == ':') {
|
||||||
|
m_state = SPACE_BEFORE_HEADER_VALUE;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (!isChar(input) || isControl(input) || isSpecial(input)) {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
req.headers.back().name.push_back(std::tolower(input));
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SPACE_BEFORE_HEADER_VALUE:
|
||||||
|
if (input == ' ')
|
||||||
|
{
|
||||||
|
m_state = HEADER_VALUE;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
case HEADER_VALUE:
|
||||||
|
if (input == '\r') {
|
||||||
|
m_state = EXPECTING_NEWLINE_2;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else if (isControl(input)) {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
req.headers.back().value.push_back(input);
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPECTING_NEWLINE_2:
|
||||||
|
if (input == '\n') {
|
||||||
|
m_state = HEADER_LINE_START;
|
||||||
|
return INDETERMINATE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPECTING_NEWLINE_3:
|
||||||
|
return (input == '\n') ? GOOD : BAD;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return BAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a byte is an HTTP character.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool HTTPRequestLexer::isChar(int c)
|
||||||
|
{
|
||||||
|
return c >= 0 && c <= 127;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a byte is an HTTP control character.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool HTTPRequestLexer::isControl(int c)
|
||||||
|
{
|
||||||
|
return (c >= 0 && c <= 31) || (c == 127);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a byte is an HTTP special character.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool HTTPRequestLexer::isSpecial(int c)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '(': case ')': case '<': case '>': case '@':
|
||||||
|
case ',': case ';': case ':': case '\\': case '"':
|
||||||
|
case '/': case '[': case ']': case '?': case '=':
|
||||||
|
case '{': case '}': case ' ': case '\t':
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a byte is an digit.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="c"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool HTTPRequestLexer::isDigit(int c)
|
||||||
|
{
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
@ -0,0 +1,122 @@
|
|||||||
|
/**
|
||||||
|
* 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_REQUEST_PARSER_H__)
|
||||||
|
#define __REST_HTTP__HTTP_REQUEST_PARSER_H__
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace rest {
|
||||||
|
namespace server {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Prototypes
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct HTTPRequest;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Declaration
|
||||||
|
// This class implements the lexer for incoming requests.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HTTPRequestLexer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ResultType { GOOD, BAD, INDETERMINATE };
|
||||||
|
|
||||||
|
/// <summary>Initializes a new instance of the HTTPRequestLexer class.</summary>
|
||||||
|
HTTPRequestLexer();
|
||||||
|
|
||||||
|
/// <summary>Reset to initial parser state.</summary>
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/// <summary>Parse some data. The enum return value is good when a complete request has
|
||||||
|
/// been parsed, bad if the data is invalid, indeterminate when more data is
|
||||||
|
/// required. The InputIterator return value indicates how much of the input
|
||||||
|
/// has been consumed.</summary>
|
||||||
|
template <typename InputIterator>
|
||||||
|
std::tuple<ResultType, InputIterator> parse(HTTPRequest& req, InputIterator begin, InputIterator end)
|
||||||
|
{
|
||||||
|
while (begin != end) {
|
||||||
|
ResultType result = consume(req, *begin++);
|
||||||
|
if (result == GOOD || result == BAD)
|
||||||
|
return std::make_tuple(result, begin);
|
||||||
|
}
|
||||||
|
return std::make_tuple(INDETERMINATE, begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// <summary>Handle the next character of input.</summary>
|
||||||
|
ResultType consume(HTTPRequest& req, char input);
|
||||||
|
|
||||||
|
/// <summary>Check if a byte is an HTTP character.</summary>
|
||||||
|
static bool isChar(int c);
|
||||||
|
/// <summary>Check if a byte is an HTTP control character.</summary>
|
||||||
|
static bool isControl(int c);
|
||||||
|
/// <summary>Check if a byte is an HTTP special character.</summary>
|
||||||
|
static bool isSpecial(int c);
|
||||||
|
/// <summary>Check if a byte is an digit.</summary>
|
||||||
|
static bool isDigit(int c);
|
||||||
|
|
||||||
|
enum state
|
||||||
|
{
|
||||||
|
METHOD_START,
|
||||||
|
METHOD,
|
||||||
|
URI,
|
||||||
|
HTTP_VERSION_H,
|
||||||
|
HTTP_VERSION_T_1,
|
||||||
|
HTTP_VERSION_T_2,
|
||||||
|
HTTP_VERSION_P,
|
||||||
|
HTTP_VERSION_SLASH,
|
||||||
|
HTTP_VERSION_MAJOR_START,
|
||||||
|
HTTP_VERSION_MAJOR,
|
||||||
|
HTTP_VERSION_MINOR_START,
|
||||||
|
HTTP_VERSION_MINOR,
|
||||||
|
EXPECTING_NEWLINE_1,
|
||||||
|
HEADER_LINE_START,
|
||||||
|
HEADER_LWS,
|
||||||
|
HEADER_NAME,
|
||||||
|
SPACE_BEFORE_HEADER_VALUE,
|
||||||
|
HEADER_VALUE,
|
||||||
|
EXPECTING_NEWLINE_2,
|
||||||
|
EXPECTING_NEWLINE_3
|
||||||
|
} m_state;
|
||||||
|
};
|
||||||
|
} // namespace server
|
||||||
|
} // namespace rest
|
||||||
|
|
||||||
|
#endif // __REST_HTTP__HTTP_REQUEST_PARSER_H__
|
||||||
@ -0,0 +1,160 @@
|
|||||||
|
/**
|
||||||
|
* 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__
|
||||||
Loading…
Reference in new issue