|
|
|
|
@ -9,7 +9,7 @@
|
|
|
|
|
* @license BSL-1.0 License (https://opensource.org/license/bsl1-0-html)
|
|
|
|
|
*
|
|
|
|
|
* Copyright (c) 2003-2013 Christopher M. Kohlhoff
|
|
|
|
|
* Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL
|
|
|
|
|
* Copyright (C) 2023-2024 Bryan Biedenkapp, N2PLL
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
#if !defined(__REST_HTTP__SERVER_CONNECTION_H__)
|
|
|
|
|
@ -19,6 +19,7 @@
|
|
|
|
|
#include "common/network/rest/http/HTTPLexer.h"
|
|
|
|
|
#include "common/network/rest/http/HTTPPayload.h"
|
|
|
|
|
#include "common/Log.h"
|
|
|
|
|
#include "common/Utils.h"
|
|
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
|
#include <memory>
|
|
|
|
|
@ -55,13 +56,21 @@ namespace network
|
|
|
|
|
ServerConnection(ServerConnection&) = delete;
|
|
|
|
|
|
|
|
|
|
/// <summary>Initializes a new instance of the ServerConnection class.</summary>
|
|
|
|
|
/// <param name="socket"></param>
|
|
|
|
|
/// <param name="manager"></param>
|
|
|
|
|
/// <param name="handler"></param>
|
|
|
|
|
/// <param name="persistent"></param>
|
|
|
|
|
/// <param name="debug"></param>
|
|
|
|
|
explicit ServerConnection(asio::ip::tcp::socket socket, ConnectionManagerType& manager, RequestHandlerType& handler,
|
|
|
|
|
bool persistent = false) :
|
|
|
|
|
bool persistent = false, bool debug = false) :
|
|
|
|
|
m_socket(std::move(socket)),
|
|
|
|
|
m_connectionManager(manager),
|
|
|
|
|
m_requestHandler(handler),
|
|
|
|
|
m_lexer(HTTPLexer(false)),
|
|
|
|
|
m_persistent(persistent)
|
|
|
|
|
m_continue(false),
|
|
|
|
|
m_contResult(HTTPLexer::INDETERMINATE),
|
|
|
|
|
m_persistent(persistent),
|
|
|
|
|
m_debug(debug)
|
|
|
|
|
{
|
|
|
|
|
/* stub */
|
|
|
|
|
}
|
|
|
|
|
@ -88,29 +97,74 @@ namespace network
|
|
|
|
|
auto self(this->shared_from_this());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t bytes_transferred) {
|
|
|
|
|
m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t recvLength) {
|
|
|
|
|
if (!ec) {
|
|
|
|
|
HTTPLexer::ResultType result;
|
|
|
|
|
HTTPLexer::ResultType result = HTTPLexer::GOOD;
|
|
|
|
|
char* content;
|
|
|
|
|
|
|
|
|
|
// catch exceptions here so we don't blatently crash the system
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
if (!m_continue) {
|
|
|
|
|
std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + recvLength);
|
|
|
|
|
|
|
|
|
|
m_request.content = std::string();
|
|
|
|
|
std::string contentLength = m_request.headers.find("Content-Length");
|
|
|
|
|
if (contentLength != "" && (::strlen(content) != 0)) {
|
|
|
|
|
size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10);
|
|
|
|
|
m_request.contentLength = length;
|
|
|
|
|
m_request.content = std::string(content, length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string());
|
|
|
|
|
|
|
|
|
|
uint32_t consumed = m_lexer.consumed();
|
|
|
|
|
if (result == HTTPLexer::GOOD && consumed == recvLength &&
|
|
|
|
|
((m_request.method == HTTP_POST) || (m_request.method == HTTP_PUT))) {
|
|
|
|
|
if (m_debug) {
|
|
|
|
|
LogDebug(LOG_REST, "HTTP Partial Request, recvLength = %u, consumed = %u, result = %u", recvLength, consumed, result);
|
|
|
|
|
Utils::dump(1U, "m_buffer", (uint8_t*)m_buffer.data(), recvLength);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_contResult = result = HTTPLexer::INDETERMINATE;
|
|
|
|
|
m_continue = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (m_debug) {
|
|
|
|
|
LogDebug(LOG_REST, "HTTP Partial Request, recvLength = %u, result = %u", recvLength, result);
|
|
|
|
|
Utils::dump(1U, "m_buffer", (uint8_t*)m_buffer.data(), recvLength);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_contResult == HTTPLexer::INDETERMINATE) {
|
|
|
|
|
m_request.content = std::string(m_buffer.data(), recvLength);
|
|
|
|
|
} else {
|
|
|
|
|
m_request.content.append(std::string(m_buffer.data(), recvLength));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_request.contentLength != 0 && recvLength < m_request.contentLength) {
|
|
|
|
|
m_contResult = result = HTTPLexer::CONTINUE;
|
|
|
|
|
m_continue = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_request.headers.add("RemoteHost", m_socket.remote_endpoint().address().to_string());
|
|
|
|
|
|
|
|
|
|
if (result == HTTPLexer::GOOD) {
|
|
|
|
|
if (m_debug) {
|
|
|
|
|
Utils::dump(1U, "HTTP Request Content", (uint8_t*)m_request.content.c_str(), m_request.content.length());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_continue = false;
|
|
|
|
|
m_contResult = HTTPLexer::INDETERMINATE;
|
|
|
|
|
m_requestHandler.handleRequest(m_request, m_reply);
|
|
|
|
|
|
|
|
|
|
if (m_debug) {
|
|
|
|
|
Utils::dump(1U, "HTTP Reply Content", (uint8_t*)m_reply.content.c_str(), m_reply.content.length());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
write();
|
|
|
|
|
}
|
|
|
|
|
else if (result == HTTPLexer::BAD) {
|
|
|
|
|
m_continue = false;
|
|
|
|
|
m_contResult = HTTPLexer::INDETERMINATE;
|
|
|
|
|
m_reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST);
|
|
|
|
|
write();
|
|
|
|
|
}
|
|
|
|
|
@ -118,13 +172,19 @@ namespace network
|
|
|
|
|
read();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch(const std::exception& e) { ::LogError(LOG_REST, "ServerConnection::read(), %s", ec.message().c_str()); }
|
|
|
|
|
catch(const std::exception& e) {
|
|
|
|
|
::LogError(LOG_REST, "ServerConnection::read(), %s", ec.message().c_str());
|
|
|
|
|
m_continue = false;
|
|
|
|
|
m_contResult = HTTPLexer::INDETERMINATE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (ec != asio::error::operation_aborted) {
|
|
|
|
|
if (ec) {
|
|
|
|
|
::LogError(LOG_REST, "ServerConnection::read(), %s, code = %u", ec.message().c_str(), ec.value());
|
|
|
|
|
}
|
|
|
|
|
m_connectionManager.stop(this->shared_from_this());
|
|
|
|
|
m_continue = false;
|
|
|
|
|
m_contResult = HTTPLexer::INDETERMINATE;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@ -180,7 +240,11 @@ namespace network
|
|
|
|
|
HTTPLexer m_lexer;
|
|
|
|
|
HTTPPayload m_reply;
|
|
|
|
|
|
|
|
|
|
bool m_continue;
|
|
|
|
|
HTTPLexer::ResultType m_contResult;
|
|
|
|
|
|
|
|
|
|
bool m_persistent;
|
|
|
|
|
bool m_debug;
|
|
|
|
|
};
|
|
|
|
|
} // namespace http
|
|
|
|
|
} // namespace rest
|
|
|
|
|
|