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_DFSI "" off)
option(DEBUG_RINGBUFFER "" off)
option(DEBUG_HTTP_PAYLOAD "" off)
if (DEBUG_DMR_PDU_DATA)
add_definitions(-DDEBUG_DMR_PDU_DATA)
@ -261,6 +262,9 @@ endif (DEBUG_P25_DFSI)
if (DEBUG_RINGBUFFER)
add_definitions(-DDEBUG_RINGBUFFER)
endif (DEBUG_RINGBUFFER)
if (DEBUG_HTTP_PAYLOAD)
add_definitions(-DDEBUG_HTTP_PAYLOAD)
endif (DEBUG_HTTP_PAYLOAD)
# cross-compile options
option(CROSS_COMPILE_ARM "Cross-compile for 32-bit ARM" off)

@ -185,6 +185,27 @@ namespace network
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;
} // namespace rest
} // namespace network

@ -59,7 +59,7 @@ namespace network
// ---------------------------------------------------------------------------
template<class> class ConnectionManager;
// ---------------------------------------------------------------------------
// Class Declaration
// This class represents a single connection from a client.
@ -94,6 +94,13 @@ namespace network
void start() { read(); }
/// <summary>Stop all asynchronous operations associated with the connection.</summary>
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:
/// <summary>Perform an asynchronous read operation.</summary>
void read()
@ -116,7 +123,15 @@ namespace network
}
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 {
if (result == HTTPLexer::GOOD) {
@ -165,8 +180,7 @@ namespace network
m_request = HTTPPayload();
read();
}
else
{
else {
if (!ec) {
// initiate graceful connection closure
asio::error_code ignored_ec;
@ -179,7 +193,30 @@ 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;
ConnectionManagerType& m_connectionManager;

@ -93,7 +93,9 @@ namespace network
asio::post(m_ioContext, [this, request]() {
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>
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) {
if (!ec) {
m_connection = std::make_shared<ConnectionType>(std::move(m_socket), m_connectionManager, m_requestHandler, false, true);
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();
}
});
asio::connect(m_socket, endpoints);
m_connection = std::make_shared<ConnectionType>(std::move(m_socket), m_connectionManager, m_requestHandler, false, true);
m_connection->start();
}
std::string m_address;

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

@ -37,6 +37,7 @@
#include "Defines.h"
#include "network/rest/http/HTTPPayload.h"
#include "Log.h"
#include "Utils.h"
using namespace network::rest::http;
@ -104,7 +105,10 @@ namespace status_strings {
namespace misc_strings {
const char name_value_separator[] = { ':', ' ' };
const char request_method_separator[] = { ' ' };
const char crlf[] = { '\r', '\n' };
const char http_default_version[] = { 'H', 'T', 'T', 'P', '/', '1', '.', '0' };
} // namespace misc_strings
namespace stock_replies {
@ -298,11 +302,20 @@ std::vector<asio::const_buffer> HTTPPayload::toBuffers()
{
std::vector<asio::const_buffer> buffers;
if (isClientPayload) {
std::stringstream ss;
ss << ::strtoupper(method) << " " << uri << " " << HTTP_DEFAULT_VERSION;
std::string request = ss.str();
// copy method and erase zero terminator
method.erase(std::find(method.begin(), method.end(), '\0'), method.end());
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 {
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) {
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(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(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;
}
@ -341,7 +364,7 @@ void HTTPPayload::payload(json::object obj, HTTPPayload::StatusType s)
/// <param name="c"></param>
/// <param name="s"></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;
status = s;
@ -358,14 +381,14 @@ void HTTPPayload::payload(std::string c, HTTPPayload::StatusType s, std::string
/// <param name="method"></param>
/// <param name="uri"></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;
rep.isClientPayload = true;
rep.method = ::strtoupper(method);
rep.uri = uri;
rep.uri = std::string(uri);
rep.ensureDefaultHeaders(contentType);
return rep;
}
@ -388,6 +411,16 @@ HTTPPayload HTTPPayload::statusPayload(HTTPPayload::StatusType status, const std
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
// ---------------------------------------------------------------------------
@ -396,7 +429,7 @@ HTTPPayload HTTPPayload::statusPayload(HTTPPayload::StatusType status, const std
///
/// </summary>
/// <param name="contentType"></param>
void HTTPPayload::ensureDefaultHeaders(std::string contentType)
void HTTPPayload::ensureDefaultHeaders(const std::string& contentType)
{
if (!isClientPayload) {
headers.add("Content-Type", contentType);

@ -63,8 +63,6 @@ namespace network
#define HTTP_DELETE "DELETE"
#define HTTP_OPTIONS "OPTIONS"
#define HTTP_DEFAULT_VERSION "HTTP/1.0"
// ---------------------------------------------------------------------------
// Structure Declaration
// 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>
void payload(json::object obj, StatusType status = OK);
/// <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>
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>
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:
/// <summary></summary>
void ensureDefaultHeaders(std::string contentType = "text/html");
void ensureDefaultHeaders(const std::string& contentType = "text/html");
};
} // namespace http
} // namespace rest

Loading…
Cancel
Save

Powered by TurnKey Linux.