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 committed by Matthew Zipkin
parent 70d003ca10
commit 1b14d00a57
No known key found for this signature in database
GPG key ID: E7E2984B6289C93A
4 changed files with 62 additions and 0 deletions

View file

@ -846,4 +846,9 @@ 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, m_reason, m_headers.Stringify());
}
} // namespace http_bitcoin

View file

@ -11,6 +11,7 @@
#include <span>
#include <string>
#include <rpc/protocol.h>
#include <util/strencodings.h>
#include <util/string.h>
@ -214,6 +215,20 @@ public:
private:
std::map<std::string, std::string, util::CaseInsensitiveComparator> m_map;
};
class HTTPResponse
{
public:
int m_version_major;
int m_version_minor;
HTTPStatusCode m_status;
std::string m_reason;
HTTPHeaders m_headers;
std::vector<std::byte> m_body;
bool m_keep_alive{false};
std::string StringifyHeaders() const;
};
} // namespace http_bitcoin
#endif // BITCOIN_HTTPSERVER_H

View file

@ -20,6 +20,20 @@ enum HTTPStatusCode
HTTP_SERVICE_UNAVAILABLE = 503,
};
// Copied from libevent http.c success_phrases[] and client_error_phrases[]
// TODO: Should HTTPStatusCode and HTTPReason be moved since they are not RPC protocols?
const std::map<HTTPStatusCode, std::string> HTTPReason{
{HTTP_OK, "OK"},
{HTTP_NO_CONTENT, "No Content"},
{HTTP_BAD_REQUEST, "Bad Request"},
{HTTP_UNAUTHORIZED, "Unauthorized"},
{HTTP_FORBIDDEN, "Forbidden"},
{HTTP_NOT_FOUND, "Not Found"},
{HTTP_BAD_METHOD, "Method Not Allowed"},
{HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error"},
{HTTP_SERVICE_UNAVAILABLE, "Service Unavailable"},
};
//! Bitcoin RPC error codes
enum RPCErrorCode
{

View file

@ -3,12 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <httpserver.h>
#include <rpc/protocol.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <boost/test/unit_test.hpp>
using http_bitcoin::HTTPHeaders;
using http_bitcoin::HTTPResponse;
BOOST_FIXTURE_TEST_SUITE(httpserver_tests, BasicTestingSetup)
@ -94,4 +96,30 @@ BOOST_AUTO_TEST_CASE(http_headers_tests)
BOOST_CHECK(!headers.Find("Pizza"));
}
}
BOOST_AUTO_TEST_CASE(http_response_tests)
{
// Typical HTTP 1.1 response headers
HTTPHeaders headers{};
headers.Write("Content-Type", "application/json");
headers.Write("Date", "Tue, 15 Oct 2024 17:54:12 GMT");
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;
res.m_version_minor = 1;
res.m_status = HTTP_OK;
res.m_reason = HTTPReason.find(res.m_status)->second;
res.m_body = StringToBuffer("{\"result\":865793,\"error\":null,\"id\":null\"}");
// Everything except the body, which might be raw bytes instead of a string
res.m_headers = std::move(headers);
BOOST_CHECK_EQUAL(
res.StringifyHeaders(),
"HTTP/1.1 200 OK\r\n"
"Content-Length: 41\r\n"
"Content-Type: application/json\r\n"
"Date: Tue, 15 Oct 2024 17:54:12 GMT\r\n"
"\r\n");
}
BOOST_AUTO_TEST_SUITE_END()