http: Implement HTTPResponse class

HTTP Response message:
https://datatracker.ietf.org/doc/html/rfc1945#section-6

Status line (first line of response):
https://datatracker.ietf.org/doc/html/rfc1945#section-6.1

Status code definitions:
https://datatracker.ietf.org/doc/html/rfc1945#section-9
This commit is contained in:
Matthew Zipkin
2024-10-16 10:57:06 -04:00
parent 68b5d289d1
commit ad50aa4a0f
4 changed files with 76 additions and 0 deletions

View File

@@ -808,4 +808,14 @@ std::string HTTPHeaders::Stringify() const
return out;
}
std::string HTTPResponse::StringifyHeaders() const
{
return strprintf("HTTP/%d.%d %d %s\r\n%s",
m_version.major,
m_version.minor,
m_status,
HTTPStatusReasonString(m_status),
m_headers.Stringify());
}
} // namespace http_bitcoin

View File

@@ -10,6 +10,7 @@
#include <span>
#include <string>
#include <rpc/protocol.h>
#include <util/strencodings.h>
#include <util/string.h>
@@ -234,6 +235,29 @@ private:
*/
std::vector<std::pair<std::string, std::string>> m_headers;
};
struct HTTPVersion {
/**
* Default HTTP protocol version 1.1 is used by error responses
* when a request is unreadable.
*/
/// @{
uint8_t major{1};
uint8_t minor{1};
/// @}
};
class HTTPResponse
{
public:
HTTPVersion m_version;
HTTPStatusCode m_status{HTTP_INTERNAL_SERVER_ERROR};
HTTPHeaders m_headers;
std::string StringifyHeaders() const;
};
} // namespace http_bitcoin
#endif // BITCOIN_HTTPSERVER_H

View File

@@ -20,6 +20,27 @@ enum HTTPStatusCode : int
HTTP_SERVICE_UNAVAILABLE = 503,
};
//! Mapping of HTTP status codes to short string explanation.
//! Copied from libevent http.c success_phrases[] and client_error_phrases[]
inline std::string_view HTTPStatusReasonString(HTTPStatusCode code)
{
switch (code) {
case HTTP_OK: return "OK";
case HTTP_NO_CONTENT: return "No Content";
case HTTP_BAD_REQUEST: return "Bad Request";
case HTTP_UNAUTHORIZED: return "Unauthorized";
case HTTP_FORBIDDEN: return "Forbidden";
case HTTP_NOT_FOUND: return "Not Found";
case HTTP_BAD_METHOD: return "Method Not Allowed";
case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
case HTTP_SERVICE_UNAVAILABLE: return "Service Unavailable";
}
// Reason phrases are optional and may be replaced by local variants.
// https://httpwg.org/specs/rfc9110.html#rfc.section.15.1
return "";
}
//! Bitcoin RPC error codes
enum RPCErrorCode
{

View File

@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <httpserver.h>
#include <rpc/protocol.h>
#include <test/util/common.h>
#include <test/util/setup_common.h>
#include <util/string.h>
@@ -10,6 +11,7 @@
#include <boost/test/unit_test.hpp>
using http_bitcoin::HTTPHeaders;
using http_bitcoin::HTTPResponse;
using http_bitcoin::MAX_HEADERS_SIZE;
BOOST_FIXTURE_TEST_SUITE(httpserver_tests, BasicTestingSetup)
@@ -144,4 +146,23 @@ BOOST_AUTO_TEST_CASE(http_headers_tests)
BOOST_CHECK_EQUAL(headers.FindFirst("key"), "value");
}
}
BOOST_AUTO_TEST_CASE(http_response_tests)
{
// Typical HTTP 1.1 response headers
HTTPHeaders headers{};
headers.Write("Content-Length", "41");
// Response points to headers which already exist because some of them
// are set before we even know what the response will be.
HTTPResponse res;
res.m_version = {.major = 1, .minor = 1};
res.m_status = HTTP_OK;
res.m_headers = std::move(headers);
BOOST_CHECK_EQUAL(
res.StringifyHeaders(),
"HTTP/1.1 200 OK\r\n"
"Content-Length: 41\r\n"
"\r\n");
}
BOOST_AUTO_TEST_SUITE_END()