From 2520fa3997b319e207d231f557a6d69b2c3a4d7e Mon Sep 17 00:00:00 2001 From: Miod Vallat Date: Mon, 17 Nov 2025 16:01:32 +0100 Subject: [PATCH] Recognize Accept: */* when deciding which content type to use for replies. Signed-off-by: Miod Vallat --- pdns/webserver.cc | 48 ++++++++++++++++++------------ regression-tests.api/test_Zones.py | 3 +- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/pdns/webserver.cc b/pdns/webserver.cc index 593ecf5666..77f8719f04 100644 --- a/pdns/webserver.cc +++ b/pdns/webserver.cc @@ -250,6 +250,34 @@ static void* WebServerConnectionThreadStart(const WebServer* webServer, const st return nullptr; } +// Decide which content type to use for the answer, based on the +// `accept' request header. If this header is missing, we try to +// reply with the same content type as the request. +static void setupAllowedContentType(HttpRequest& req) +{ + auto header = req.headers.find("accept"); + if (header != req.headers.end()) { + // If Accept: */* then we'll happily serve anything! + if (header->second.find("*/*") != std::string::npos) { + req.accept_yaml = req.accept_json = req.accept_html = true; + return; + } + } + else { + header = req.headers.find("content-type"); + } + if (header != req.headers.end()) { + // yaml wins over json, json wins over html + if (header->second.find("application/x-yaml") != std::string::npos || header->second.find("text/x-yaml") != std::string::npos) { + req.accept_yaml = true; + } else if (header->second.find("application/json") != std::string::npos) { + req.accept_json = true; + } else if (header->second.find("text/html") != std::string::npos) { + req.accept_html = true; + } + } +} + void WebServer::handleRequest(HttpRequest& req, HttpResponse& resp) const { // set default headers @@ -268,25 +296,7 @@ void WebServer::handleRequest(HttpRequest& req, HttpResponse& resp) const SLOG(g_log<info(Logr::Debug, "Handling request")); - // Decide which content type to use for the answer, based on the - // `accept' request header. If this header is missing, we try to - // reply with the same content type as the request. - auto header = req.headers.find("accept"); - if (header == req.headers.end()) { - header = req.headers.find("content-type"); - } - if (header != req.headers.end()) { - // yaml wins over json, json wins over html - if (header->second.find("application/x-yaml") != std::string::npos) { - req.accept_yaml = true; - } else if (header->second.find("text/x-yaml") != std::string::npos) { - req.accept_yaml = true; - } else if (header->second.find("application/json") != std::string::npos) { - req.accept_json = true; - } else if (header->second.find("text/html") != std::string::npos) { - req.accept_html = true; - } - } + setupAllowedContentType(req); YaHTTP::THandlerFunction handler; YaHTTP::RoutingResult res = YaHTTP::Router::Route(&req, handler); diff --git a/regression-tests.api/test_Zones.py b/regression-tests.api/test_Zones.py index 796eacbe2a..ee2f182815 100644 --- a/regression-tests.api/test_Zones.py +++ b/regression-tests.api/test_Zones.py @@ -1241,8 +1241,7 @@ $NAME$ 1D IN SOA ns1.example.org. hostmaster.example.org. ( name, payload, zone = self.create_zone(nameservers=['ns1.foo.com.', 'ns2.foo.com.'], soa_edit_api='') # export it r = self.session.get( - self.url("/api/v1/servers/localhost/zones/" + name + "/export"), - headers={'accept': '*/*'} + self.url("/api/v1/servers/localhost/zones/" + name + "/export") ) data = r.text.strip().split("\n") expected_data = [name + '\t3600\tIN\tNS\tns1.foo.com.', -- 2.47.3