diff --git a/src/httpserver.cpp b/src/httpserver.cpp index dbff10eeaad..5efb1b5d7f9 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -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 diff --git a/src/httpserver.h b/src/httpserver.h index ec1fefc5bca..d78fed17018 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -214,6 +215,20 @@ public: private: std::map 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 m_body; + bool m_keep_alive{false}; + + std::string StringifyHeaders() const; +}; } // namespace http_bitcoin #endif // BITCOIN_HTTPSERVER_H diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 0574335c964..fc413f74269 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -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 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 { diff --git a/src/test/httpserver_tests.cpp b/src/test/httpserver_tests.cpp index 7c5ba7a3ad7..29cf4145118 100644 --- a/src/test/httpserver_tests.cpp +++ b/src/test/httpserver_tests.cpp @@ -3,12 +3,14 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include #include 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()