/** * 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 #include #include #include #include namespace rest { namespace server { // --------------------------------------------------------------------------- // Class Prototypes // --------------------------------------------------------------------------- template class ConnectionManager; // --------------------------------------------------------------------------- // Class Declaration // This class represents a single connection from a client. // --------------------------------------------------------------------------- template class Connection : public std::enable_shared_from_this> { typedef Connection selfType; typedef std::shared_ptr selfTypePtr; typedef ConnectionManager ConnectionManagerType; public: /// Initializes a new instance of the Connection class. 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 */ } /// Initializes a copy instance of the Connection class. Connection(const Connection&) = delete; /// Connection& operator=(const Connection&) = delete; /// Start the first asynchronous operation for the connection. void start() { read(); } /// Stop all asynchronous operations associated with the connection. void stop() { m_socket.close(); } private: /// Perform an asynchronous read operation. 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(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()); } }); } /// Perform an asynchronous write operation. 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 m_buffer; HTTPRequest m_request; HTTPRequestLexer m_lexer; HTTPReply m_reply; bool m_persistent; }; } // namespace server } // namespace rest #endif // __REST_HTTP__CONNECTION_H__