diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 59e537f76..774e9efb1 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -8,6 +8,7 @@ #include "remote/apifunction.hpp" #include "remote/configpackageutility.hpp" #include "remote/configobjectutility.hpp" +#include "remote/httputility.hpp" #include "base/atomic-file.hpp" #include "base/convert.hpp" #include "base/defer.hpp" @@ -2008,6 +2009,31 @@ void ApiListener::ValidateTlsHandshakeTimeout(const Lazy& lvalue, const BOOST_THROW_EXCEPTION(ValidationError(this, { "tls_handshake_timeout" }, "Value must be greater than 0.")); } +void ApiListener::ValidateHttpResponseHeaders(const Lazy& lvalue, const ValidationUtils& utils) +{ + ObjectImpl::ValidateHttpResponseHeaders(lvalue, utils); + + if (Dictionary::Ptr headers = lvalue(); headers) { + ObjectLock lock(headers); + for (auto& [name, value] : headers) { + if (!HttpUtility::IsValidHeaderName(name.GetData())) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "http_response_headers", name }, + "Header name is invalid.")); + } + + if (!value.IsString()) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "http_response_headers", name }, + "Header value must be a string.")); + } + + if (!HttpUtility::IsValidHeaderValue(value.Get().GetData())) { + BOOST_THROW_EXCEPTION(ValidationError(this, { "http_response_headers", name }, + "Header value is invalid.")); + } + } + } +} + bool ApiListener::IsHACluster() { Zone::Ptr zone = Zone::GetLocalZone(); diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 866af7614..7b98db964 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -170,6 +170,7 @@ public: protected: void ValidateTlsProtocolmin(const Lazy& lvalue, const ValidationUtils& utils) override; void ValidateTlsHandshakeTimeout(const Lazy& lvalue, const ValidationUtils& utils) override; + void ValidateHttpResponseHeaders(const Lazy& lvalue, const ValidationUtils& utils) override; private: Shared::Ptr m_SSLContext; diff --git a/lib/remote/apilistener.ti b/lib/remote/apilistener.ti index 55ec749c5..8c9fdab40 100644 --- a/lib/remote/apilistener.ti +++ b/lib/remote/apilistener.ti @@ -55,6 +55,7 @@ class ApiListener : ConfigObject [config, deprecated] String access_control_allow_headers; [config, deprecated] String access_control_allow_methods; + [config] Dictionary::Ptr http_response_headers; [state, no_user_modify] Timestamp log_message_timestamp; diff --git a/lib/remote/httpserverconnection.cpp b/lib/remote/httpserverconnection.cpp index 39fa2d79d..6ae147452 100644 --- a/lib/remote/httpserverconnection.cpp +++ b/lib/remote/httpserverconnection.cpp @@ -466,6 +466,16 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc) request.Parser().body_limit(-1); response.set(http::field::server, l_ServerHeader); + if (auto listener (ApiListener::GetInstance()); listener) { + if (Dictionary::Ptr headers = listener->GetHttpResponseHeaders(); headers) { + ObjectLock lock(headers); + for (auto& [header, value] : headers) { + if (value.IsString()) { + response.set(header, value.Get()); + } + } + } + } if (!EnsureValidHeaders(buf, request, response, m_ShuttingDown, yc)) { break;