correct various issues with HTTPClient not connecting properly; correct HTTPLexer not lexing HTTP server responses properly; various payload corrections;

pull/19/head
Bryan Biedenkapp 3 years ago
parent 70782c5101
commit df028fd2f8

@ -206,6 +206,7 @@ option(DEBUG_P25_TDULC "" off)
option(DEBUG_P25_TSBK "" off) option(DEBUG_P25_TSBK "" off)
option(DEBUG_P25_DFSI "" off) option(DEBUG_P25_DFSI "" off)
option(DEBUG_RINGBUFFER "" off) option(DEBUG_RINGBUFFER "" off)
option(DEBUG_HTTP_PAYLOAD "" off)
if (DEBUG_DMR_PDU_DATA) if (DEBUG_DMR_PDU_DATA)
add_definitions(-DDEBUG_DMR_PDU_DATA) add_definitions(-DDEBUG_DMR_PDU_DATA)
@ -261,6 +262,9 @@ endif (DEBUG_P25_DFSI)
if (DEBUG_RINGBUFFER) if (DEBUG_RINGBUFFER)
add_definitions(-DDEBUG_RINGBUFFER) add_definitions(-DDEBUG_RINGBUFFER)
endif (DEBUG_RINGBUFFER) endif (DEBUG_RINGBUFFER)
if (DEBUG_HTTP_PAYLOAD)
add_definitions(-DDEBUG_HTTP_PAYLOAD)
endif (DEBUG_HTTP_PAYLOAD)
# cross-compile options # cross-compile options
option(CROSS_COMPILE_ARM "Cross-compile for 32-bit ARM" off) option(CROSS_COMPILE_ARM "Cross-compile for 32-bit ARM" off)

@ -185,6 +185,27 @@ namespace network
bool m_debug; bool m_debug;
}; };
// ---------------------------------------------------------------------------
// Class Declaration
// This class implements a generic debug request dispatcher.
// ---------------------------------------------------------------------------
template<typename Request = http::HTTPPayload, typename Reply = http::HTTPPayload>
class DebugRequestDispatcher {
public:
/// <summary>Initializes a new instance of the DebugRequestDispatcher class.</summary>
DebugRequestDispatcher() { /* stub */ }
/// <summary></summary>
void handleRequest(const Request& request, Reply& reply)
{
for (auto header : request.headers.headers())
::LogDebug(LOG_REST, "DebugRequestDispatcher::handleRequest() header = %s, value = %s", header.name.c_str(), header.value.c_str());
::LogDebug(LOG_REST, "DebugRequestDispatcher::handleRequest() content = %s", request.content.c_str());
}
};
typedef RequestDispatcher<http::HTTPPayload, http::HTTPPayload> DefaultRequestDispatcher; typedef RequestDispatcher<http::HTTPPayload, http::HTTPPayload> DefaultRequestDispatcher;
} // namespace rest } // namespace rest
} // namespace network } // namespace network

@ -94,6 +94,13 @@ namespace network
void start() { read(); } void start() { read(); }
/// <summary>Stop all asynchronous operations associated with the connection.</summary> /// <summary>Stop all asynchronous operations associated with the connection.</summary>
void stop() { m_socket.close(); } void stop() { m_socket.close(); }
/// <summary>Perform an asynchronous write operation.</summary>
void send(HTTPPayload request)
{
request.attachHostHeader(m_socket.local_endpoint());
write(request);
}
private: private:
/// <summary>Perform an asynchronous read operation.</summary> /// <summary>Perform an asynchronous read operation.</summary>
void read() void read()
@ -116,7 +123,15 @@ namespace network
} }
if (m_client) { if (m_client) {
m_requestHandler.handleRequest(m_request, m_reply); if (result == HTTPLexer::GOOD) {
m_requestHandler.handleRequest(m_request, m_reply);
}
else if (result == HTTPLexer::BAD) {
return;
}
else {
read();
}
} }
else { else {
if (result == HTTPLexer::GOOD) { if (result == HTTPLexer::GOOD) {
@ -165,8 +180,7 @@ namespace network
m_request = HTTPPayload(); m_request = HTTPPayload();
read(); read();
} }
else else {
{
if (!ec) { if (!ec) {
// initiate graceful connection closure // initiate graceful connection closure
asio::error_code ignored_ec; asio::error_code ignored_ec;
@ -180,6 +194,29 @@ namespace network
}); });
} }
/// <summary>Perform an asynchronous write operation.</summary>
void write(HTTPPayload request)
{
if (!m_client) {
return;
}
asio::async_write(m_socket, request.toBuffers(), [=](asio::error_code ec, std::size_t) {
if (ec) {
// initiate graceful connection closure
asio::error_code ignored_ec;
m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec);
}
if (ec.value() == 0) {
return;
}
else if (ec != asio::error::operation_aborted) {
m_socket.close();
}
});
}
asio::ip::tcp::socket m_socket; asio::ip::tcp::socket m_socket;
ConnectionManagerType& m_connectionManager; ConnectionManagerType& m_connectionManager;

@ -93,7 +93,9 @@ namespace network
asio::post(m_ioContext, [this, request]() { asio::post(m_ioContext, [this, request]() {
std::lock_guard<std::mutex> guard(m_lock); std::lock_guard<std::mutex> guard(m_lock);
{ {
write(request); if (m_connection != nullptr) {
m_connection->send(request);
}
} }
}); });
} }
@ -131,28 +133,9 @@ namespace network
/// <summary>Perform an asynchronous connect operation.</summary> /// <summary>Perform an asynchronous connect operation.</summary>
void connect(asio::ip::basic_resolver_results<asio::ip::tcp>& endpoints) void connect(asio::ip::basic_resolver_results<asio::ip::tcp>& endpoints)
{ {
asio::async_connect(m_socket, endpoints, [this](asio::error_code ec, asio::ip::tcp::endpoint) { asio::connect(m_socket, endpoints);
if (!ec) { m_connection = std::make_shared<ConnectionType>(std::move(m_socket), m_connectionManager, m_requestHandler, false, true);
m_connection = std::make_shared<ConnectionType>(std::move(m_socket), m_connectionManager, m_requestHandler, false, true); m_connection->start();
m_connection->start();
}
});
}
/// <summary>Perform an asynchronous write operation.</summary>
void write(HTTPPayload request)
{
asio::async_write(m_socket, request.toBuffers(), [=](asio::error_code ec, std::size_t) {
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_socket.close();
}
});
} }
std::string m_address; std::string m_address;

@ -279,6 +279,10 @@ HTTPLexer::ResultType HTTPLexer::consume(HTTPPayload& req, char input)
m_state = EXPECTING_NEWLINE_1; m_state = EXPECTING_NEWLINE_1;
return INDETERMINATE; return INDETERMINATE;
} }
else if (input == ' ') {
m_state = HTTP_STATUS_MESSAGE;
return INDETERMINATE;
}
else if (!isChar(input) || isControl(input) || isSpecial(input)) { else if (!isChar(input) || isControl(input) || isSpecial(input)) {
return BAD; return BAD;
} }

@ -37,6 +37,7 @@
#include "Defines.h" #include "Defines.h"
#include "network/rest/http/HTTPPayload.h" #include "network/rest/http/HTTPPayload.h"
#include "Log.h" #include "Log.h"
#include "Utils.h"
using namespace network::rest::http; using namespace network::rest::http;
@ -104,7 +105,10 @@ namespace status_strings {
namespace misc_strings { namespace misc_strings {
const char name_value_separator[] = { ':', ' ' }; const char name_value_separator[] = { ':', ' ' };
const char request_method_separator[] = { ' ' };
const char crlf[] = { '\r', '\n' }; const char crlf[] = { '\r', '\n' };
const char http_default_version[] = { 'H', 'T', 'T', 'P', '/', '1', '.', '0' };
} // namespace misc_strings } // namespace misc_strings
namespace stock_replies { namespace stock_replies {
@ -298,11 +302,20 @@ std::vector<asio::const_buffer> HTTPPayload::toBuffers()
{ {
std::vector<asio::const_buffer> buffers; std::vector<asio::const_buffer> buffers;
if (isClientPayload) { if (isClientPayload) {
std::stringstream ss; // copy method and erase zero terminator
ss << ::strtoupper(method) << " " << uri << " " << HTTP_DEFAULT_VERSION; method.erase(std::find(method.begin(), method.end(), '\0'), method.end());
std::string request = ss.str();
buffers.push_back(asio::buffer(request)); // copy URI and erase zero terminator
uri.erase(std::find(uri.begin(), uri.end(), '\0'), uri.end());
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "HTTPPayload::toBuffers() method = %s, uri = %s", method.c_str(), uri.c_str());
#endif
buffers.push_back(asio::buffer(method));
buffers.push_back(asio::buffer(misc_strings::request_method_separator));
buffers.push_back(asio::buffer(uri));
buffers.push_back(asio::buffer(misc_strings::request_method_separator));
buffers.push_back(asio::buffer(misc_strings::http_default_version));
buffers.push_back(asio::buffer(misc_strings::crlf));
} }
else { else {
buffers.push_back(status_strings::toBuffer(status)); buffers.push_back(status_strings::toBuffer(status));
@ -310,7 +323,9 @@ std::vector<asio::const_buffer> HTTPPayload::toBuffers()
for (std::size_t i = 0; i < headers.size(); ++i) { for (std::size_t i = 0; i < headers.size(); ++i) {
HTTPHeaders::Header& h = headers.m_headers[i]; HTTPHeaders::Header& h = headers.m_headers[i];
//::LogDebug(LOG_REST, "HTTPPayload::toBuffers() header = %s, value = %s", h.name.c_str(), h.value.c_str()); #if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "HTTPPayload::toBuffers() header = %s, value = %s", h.name.c_str(), h.value.c_str());
#endif
buffers.push_back(asio::buffer(h.name)); buffers.push_back(asio::buffer(h.name));
buffers.push_back(asio::buffer(misc_strings::name_value_separator)); buffers.push_back(asio::buffer(misc_strings::name_value_separator));
@ -319,7 +334,15 @@ std::vector<asio::const_buffer> HTTPPayload::toBuffers()
} }
buffers.push_back(asio::buffer(misc_strings::crlf)); buffers.push_back(asio::buffer(misc_strings::crlf));
buffers.push_back(asio::buffer(content)); if (content.size() > 0)
buffers.push_back(asio::buffer(content));
#if DEBUG_HTTP_PAYLOAD
::LogDebug(LOG_REST, "HTTPPayload::toBuffers() content = %s", content.c_str());
for (auto buffer : buffers)
Utils::dump("buffer[]", (uint8_t*)buffer.data(), buffer.size());
#endif
return buffers; return buffers;
} }
@ -341,7 +364,7 @@ void HTTPPayload::payload(json::object obj, HTTPPayload::StatusType s)
/// <param name="c"></param> /// <param name="c"></param>
/// <param name="s"></param> /// <param name="s"></param>
/// <param name="contentType"></param> /// <param name="contentType"></param>
void HTTPPayload::payload(std::string c, HTTPPayload::StatusType s, std::string contentType) void HTTPPayload::payload(std::string c, HTTPPayload::StatusType s, const std::string contentType)
{ {
content = c; content = c;
status = s; status = s;
@ -358,12 +381,12 @@ void HTTPPayload::payload(std::string c, HTTPPayload::StatusType s, std::string
/// <param name="method"></param> /// <param name="method"></param>
/// <param name="uri"></param> /// <param name="uri"></param>
/// <param name="contentType"></param> /// <param name="contentType"></param>
HTTPPayload HTTPPayload::requestPayload(std::string method, std::string uri, std::string contentType) HTTPPayload HTTPPayload::requestPayload(std::string method, std::string uri, const std::string contentType)
{ {
HTTPPayload rep; HTTPPayload rep;
rep.isClientPayload = true; rep.isClientPayload = true;
rep.method = ::strtoupper(method); rep.method = ::strtoupper(method);
rep.uri = uri; rep.uri = std::string(uri);
rep.ensureDefaultHeaders(contentType); rep.ensureDefaultHeaders(contentType);
return rep; return rep;
@ -388,6 +411,16 @@ HTTPPayload HTTPPayload::statusPayload(HTTPPayload::StatusType status, const std
return rep; return rep;
} }
/// <summary>
///
/// </summary>
/// <param name="localEndpoint"></param>
void HTTPPayload::attachHostHeader(const asio::ip::tcp::endpoint localEndpoint)
{
headers.add("Host", std::string(localEndpoint.address().to_string() + ":" + std::to_string(localEndpoint.port())));
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Private Members // Private Members
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -396,7 +429,7 @@ HTTPPayload HTTPPayload::statusPayload(HTTPPayload::StatusType status, const std
/// ///
/// </summary> /// </summary>
/// <param name="contentType"></param> /// <param name="contentType"></param>
void HTTPPayload::ensureDefaultHeaders(std::string contentType) void HTTPPayload::ensureDefaultHeaders(const std::string& contentType)
{ {
if (!isClientPayload) { if (!isClientPayload) {
headers.add("Content-Type", contentType); headers.add("Content-Type", contentType);

@ -63,8 +63,6 @@ namespace network
#define HTTP_DELETE "DELETE" #define HTTP_DELETE "DELETE"
#define HTTP_OPTIONS "OPTIONS" #define HTTP_OPTIONS "OPTIONS"
#define HTTP_DEFAULT_VERSION "HTTP/1.0"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Structure Declaration // Structure Declaration
// This struct implements a model of a payload to be sent to a // This struct implements a model of a payload to be sent to a
@ -114,16 +112,18 @@ namespace network
/// <summary>Prepares payload for transmission by finalizing status and content type.</summary> /// <summary>Prepares payload for transmission by finalizing status and content type.</summary>
void payload(json::object obj, StatusType status = OK); void payload(json::object obj, StatusType status = OK);
/// <summary>Prepares payload for transmission by finalizing status and content type.</summary> /// <summary>Prepares payload for transmission by finalizing status and content type.</summary>
void payload(std::string content, StatusType status = OK, std::string contentType = "text/html"); void payload(std::string content, StatusType status = OK, const std::string contentType = "text/html");
/// <summary>Get a request payload.</summary> /// <summary>Get a request payload.</summary>
static HTTPPayload requestPayload(std::string method, std::string uri, std::string contentType = "text/html"); static HTTPPayload requestPayload(std::string method, std::string uri, const std::string contentType = "text/html");
/// <summary>Get a status payload.</summary> /// <summary>Get a status payload.</summary>
static HTTPPayload statusPayload(StatusType status, std::string contentType = "text/html"); static HTTPPayload statusPayload(StatusType status, const std::string contentType = "text/html");
/// <summary></summary>
void attachHostHeader(const asio::ip::tcp::endpoint localEndpoint);
private: private:
/// <summary></summary> /// <summary></summary>
void ensureDefaultHeaders(std::string contentType = "text/html"); void ensureDefaultHeaders(const std::string& contentType = "text/html");
}; };
} // namespace http } // namespace http
} // namespace rest } // namespace rest

Loading…
Cancel
Save

Powered by TurnKey Linux.