fix issue with HTTP ClientConnection and SecureClientConnection not properly handling responses >65K bytes; add missing backward compat flags (for now, after R05A06 these will be removed); update copyright headers properly in modified files;

r05a06_dev
Bryan Biedenkapp 14 hours ago
parent 3595819cc8
commit 9d0d1f221d

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL * Copyright (C) 2024,2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "bridge/Defines.h" #include "bridge/Defines.h"

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL * Copyright (C) 2023,2024,2026 Bryan Biedenkapp, N2PLL
* *
*/ */
/** /**
@ -20,6 +20,7 @@
#include "common/Log.h" #include "common/Log.h"
#include <array> #include <array>
#include <vector>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <iterator> #include <iterator>
@ -54,6 +55,9 @@ namespace restapi
explicit ClientConnection(asio::ip::tcp::socket socket, RequestHandlerType& handler) : explicit ClientConnection(asio::ip::tcp::socket socket, RequestHandlerType& handler) :
m_socket(std::move(socket)), m_socket(std::move(socket)),
m_requestHandler(handler), m_requestHandler(handler),
m_sizeToTransfer(0U),
m_bytesTransferred(0U),
m_fullBuffer(65536),
m_lexer(HTTPLexer(true)) m_lexer(HTTPLexer(true))
{ {
/* stub */ /* stub */
@ -121,39 +125,42 @@ namespace restapi
try try
{ {
if (m_sizeToTransfer > 0U && (m_bytesTransferred + bytes_transferred) < m_sizeToTransfer) { // grow accumulation buffer as needed
if (m_bytesTransferred + bytes_transferred > m_fullBuffer.size()) {
m_fullBuffer.resize(m_bytesTransferred + bytes_transferred);
}
// always accumulate into m_fullBuffer
::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred);
m_bytesTransferred += bytes_transferred; m_bytesTransferred += bytes_transferred;
if (m_sizeToTransfer > 0U && m_bytesTransferred < m_sizeToTransfer) {
// still waiting for more data
read(); read();
} }
else { else {
if (m_sizeToTransfer > 0U) { bool wasMultiRead = (m_sizeToTransfer > 0U);
// final copy
::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred);
m_bytesTransferred += bytes_transferred;
m_sizeToTransfer = 0U; m_sizeToTransfer = 0U;
bytes_transferred = m_bytesTransferred;
// reset lexer and re-parse the full content if (wasMultiRead) {
// re-parse the full reassembled response from the beginning
m_lexer.reset(); m_lexer.reset();
std::tie(result, content) = m_lexer.parse(m_request, m_fullBuffer.data(), m_fullBuffer.data() + bytes_transferred);
} else {
::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred);
m_bytesTransferred += bytes_transferred;
std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred);
} }
// always parse from m_fullBuffer so content pointer is always valid
std::tie(result, content) = m_lexer.parse(m_request, m_fullBuffer.data(), m_fullBuffer.data() + m_bytesTransferred);
// determine content length // determine content length
std::string contentLength = m_request.headers.find("Content-Length"); std::string contentLength = m_request.headers.find("Content-Length");
if (contentLength != "") { if (contentLength != "") {
size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10);
// setup a full read if necessary // body bytes available: distance from parsed body start to end of accumulated data
if (length > bytes_transferred && m_sizeToTransfer == 0U) { // content and m_fullBuffer.data() are in the same allocation, so this arithmetic is valid
m_sizeToTransfer = length; size_t bodyBytesAvailable = (size_t)((m_fullBuffer.data() + m_bytesTransferred) - content);
if (length > bodyBytesAvailable) {
// total bytes we need = current accumulated + remaining body bytes
m_sizeToTransfer = m_bytesTransferred + (length - bodyBytesAvailable);
} }
if (m_sizeToTransfer > 0U) { if (m_sizeToTransfer > 0U) {
@ -225,7 +232,7 @@ namespace restapi
std::size_t m_sizeToTransfer; std::size_t m_sizeToTransfer;
std::size_t m_bytesTransferred; std::size_t m_bytesTransferred;
std::array<char, 65535> m_fullBuffer; std::vector<char> m_fullBuffer;
std::array<char, 4096> m_buffer; std::array<char, 4096> m_buffer;

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL * Copyright (C) 2024,2026 Bryan Biedenkapp, N2PLL
* *
*/ */
/** /**
@ -22,6 +22,7 @@
#include "common/Log.h" #include "common/Log.h"
#include <array> #include <array>
#include <vector>
#include <memory> #include <memory>
#include <utility> #include <utility>
#include <iterator> #include <iterator>
@ -58,6 +59,9 @@ namespace restapi
explicit SecureClientConnection(asio::ip::tcp::socket socket, asio::ssl::context& context, RequestHandlerType& handler) : explicit SecureClientConnection(asio::ip::tcp::socket socket, asio::ssl::context& context, RequestHandlerType& handler) :
m_socket(std::move(socket), context), m_socket(std::move(socket), context),
m_requestHandler(handler), m_requestHandler(handler),
m_sizeToTransfer(0U),
m_bytesTransferred(0U),
m_fullBuffer(65536),
m_lexer(HTTPLexer(true)) m_lexer(HTTPLexer(true))
{ {
m_socket.set_verify_mode(asio::ssl::verify_none); m_socket.set_verify_mode(asio::ssl::verify_none);
@ -140,39 +144,42 @@ namespace restapi
try try
{ {
if (m_sizeToTransfer > 0U && (m_bytesTransferred + bytes_transferred) < m_sizeToTransfer) { // grow accumulation buffer as needed
if (m_bytesTransferred + bytes_transferred > m_fullBuffer.size()) {
m_fullBuffer.resize(m_bytesTransferred + bytes_transferred);
}
// always accumulate into m_fullBuffer
::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred); ::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred);
m_bytesTransferred += bytes_transferred; m_bytesTransferred += bytes_transferred;
if (m_sizeToTransfer > 0U && m_bytesTransferred < m_sizeToTransfer) {
// still waiting for more data
read(); read();
} }
else { else {
if (m_sizeToTransfer > 0U) { bool wasMultiRead = (m_sizeToTransfer > 0U);
// final copy
::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred);
m_bytesTransferred += bytes_transferred;
m_sizeToTransfer = 0U; m_sizeToTransfer = 0U;
bytes_transferred = m_bytesTransferred;
// reset lexer and re-parse the full content if (wasMultiRead) {
// re-parse the full reassembled response from the beginning
m_lexer.reset(); m_lexer.reset();
std::tie(result, content) = m_lexer.parse(m_request, m_fullBuffer.data(), m_fullBuffer.data() + bytes_transferred);
} else {
::memcpy(m_fullBuffer.data() + m_bytesTransferred, m_buffer.data(), bytes_transferred);
m_bytesTransferred += bytes_transferred;
std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred);
} }
// always parse from m_fullBuffer so content pointer is always valid
std::tie(result, content) = m_lexer.parse(m_request, m_fullBuffer.data(), m_fullBuffer.data() + m_bytesTransferred);
// determine content length // determine content length
std::string contentLength = m_request.headers.find("Content-Length"); std::string contentLength = m_request.headers.find("Content-Length");
if (contentLength != "") { if (contentLength != "") {
size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10); size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10);
// setup a full read if necessary // body bytes available: distance from parsed body start to end of accumulated data
if (length > bytes_transferred && m_sizeToTransfer == 0U) { // content and m_fullBuffer.data() are in the same allocation, so this arithmetic is valid
m_sizeToTransfer = length; size_t bodyBytesAvailable = (size_t)((m_fullBuffer.data() + m_bytesTransferred) - content);
if (length > bodyBytesAvailable) {
// total bytes we need = current accumulated + remaining body bytes
m_sizeToTransfer = m_bytesTransferred + (length - bodyBytesAvailable);
} }
if (m_sizeToTransfer > 0U) { if (m_sizeToTransfer > 0U) {
@ -183,6 +190,7 @@ namespace restapi
} }
m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string()); m_request.headers.add("RemoteHost", m_socket.lowest_layer().remote_endpoint().address().to_string());
if (result == HTTPLexer::GOOD) { if (result == HTTPLexer::GOOD) {
m_sizeToTransfer = m_bytesTransferred = 0U; m_sizeToTransfer = m_bytesTransferred = 0U;
m_requestHandler.handleRequest(m_request, m_reply); m_requestHandler.handleRequest(m_request, m_reply);
@ -245,7 +253,7 @@ namespace restapi
std::size_t m_sizeToTransfer; std::size_t m_sizeToTransfer;
std::size_t m_bytesTransferred; std::size_t m_bytesTransferred;
std::array<char, 65535> m_fullBuffer; std::vector<char> m_fullBuffer;
std::array<char, 4096> m_buffer; std::array<char, 4096> m_buffer;

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2024-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "fne/Defines.h" #include "fne/Defines.h"
@ -523,9 +523,19 @@ bool PeerNetwork::writeConfig()
uint32_t peerClass = (uint32_t)PEER_CONN_CLASS::PEER_CONN_CLASS_NEIGHBOR; uint32_t peerClass = (uint32_t)PEER_CONN_CLASS::PEER_CONN_CLASS_NEIGHBOR;
config["peerClass"].set<uint32_t>(peerClass); // Peer Connection Class config["peerClass"].set<uint32_t>(peerClass); // Peer Connection Class
config["masterPeerId"].set<uint32_t>(m_masterPeerId); // Master Peer ID config["masterPeerId"].set<uint32_t>(m_masterPeerId); // Master Peer ID
/*
** bryanb: this is deprecated -- it remains here for backwards compatibility with older master versions,
** but is no longer used by the master and have no effect on R05A06 systems, and may be removed in a future release
** {
*/
bool external = true;
config["externalPeer"].set<bool>(external); // External Peer Marker
/*
** }
*/
config["software"].set<std::string>(std::string(software)); // Software ID config["software"].set<std::string>(std::string(software)); // Software ID
json::value v = json::value(config); json::value v = json::value(config);

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2025-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "patch/Defines.h" #include "patch/Defines.h"

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2023,2024 Bryan Biedenkapp, N2PLL * Copyright (C) 2023,2024,2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "Defines.h" #include "Defines.h"
@ -68,11 +68,13 @@ bool parseResponseBody(const HTTPPayload& response, json::object& obj)
json::value v; json::value v;
std::string err = json::parse(v, response.content); std::string err = json::parse(v, response.content);
if (!err.empty()) { if (!err.empty()) {
LogError(LOG_REST, "Failed to parse REST API response body: %s", err.c_str());
return false; return false;
} }
// ensure parsed JSON is an object // ensure parsed JSON is an object
if (!v.is<json::object>()) { if (!v.is<json::object>()) {
LogError(LOG_REST, "Failed to parse REST API response body: expected JSON object");
return false; return false;
} }

@ -4,7 +4,7 @@
* GPLv2 Open Source. Use is subject to license terms. * GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* Copyright (C) 2024-2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2024-2026 Bryan Biedenkapp, N2PLL
* *
*/ */
#include "sysview/Defines.h" #include "sysview/Defines.h"
@ -279,6 +279,23 @@ bool PeerNetwork::writeConfig()
uint32_t peerClass = PEER_CONN_CLASS::PEER_CONN_CLASS_SYSVIEW; uint32_t peerClass = PEER_CONN_CLASS::PEER_CONN_CLASS_SYSVIEW;
config["peerClass"].set<uint32_t>(peerClass); // Peer Connection Class config["peerClass"].set<uint32_t>(peerClass); // Peer Connection Class
config["masterPeerId"].set<uint32_t>(m_peerId); // Master Peer ID
bool convPeer = true;
config["conventionalPeer"].set<bool>(convPeer); // Conventional Peer Marker
/*
** bryanb: these are all deprecated -- they remain here for backwards compatibility with older master versions,
** but they are no longer used by the master and have no effect on R05A06 systems, and may be removed in a future release
** {
*/
bool external = true;
config["externalPeer"].set<bool>(external); // External Peer Marker
bool sysView = true;
config["sysView"].set<bool>(sysView); // SysView Peer Marker
/*
** }
*/
config["software"].set<std::string>(std::string(software)); // Software ID config["software"].set<std::string>(std::string(software)); // Software ID

Loading…
Cancel
Save

Powered by TurnKey Linux.