This is an automated email from the ASF dual-hosted git repository.

alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new 7ecad3550 KUDU-3543 Fix Content-Type headers
7ecad3550 is described below

commit 7ecad355065e4d0eb891c87813f347bb7d6819b2
Author: Marton Greber <[email protected]>
AuthorDate: Sat Jan 6 00:50:16 2024 +0100

    KUDU-3543 Fix Content-Type headers
    
    This patch addresses an issue with the webserver's Content-Type headers.
    The problem lies in the webserver's handler functions, where we use a
    boolean variable called 'is_styled' to decide on the style mode.
    
    In the root Webserver::RunPathHandler function, only StyleMode::STYLED
    or StyleMode::UNSTYLED are returned. Although SendResponse is invoked
    with a use_style variable of type StyleMode, we only end up with
    StyleMode::STYLED or StyleMode::UNSTYLED in the subsequent code paths.
    Consequently, we never utilize StyleMode::BINARY. This means that the
    master's /ipki-ca-cert-der endpoint does not have the expected
    application/octet-stream content-type header; instead, it has text/plain.
    (See KUDU-3543 for evidence).
    
    This patch replaces the 'is_styled' boolean with the StyleMode enum
    entirely. I have added test cases to verify the Content-Type headers of
    the Master and TServer endpoints.
    
    [1] 
https://github.com/apache/kudu/blob/master/src/kudu/server/webserver.cc#L686-L687
    
    Change-Id: I746dcfbaadb2fb95292c2d4047cb7adb9971b42f
    Reviewed-on: http://gerrit.cloudera.org:8080/20868
    Tested-by: Marton Greber <[email protected]>
    Reviewed-by: Zoltan Chovan <[email protected]>
    Reviewed-by: Alexey Serbin <[email protected]>
---
 src/kudu/master/master-test.cc            | 36 ++++++++++++++++
 src/kudu/master/master_path_handlers.cc   | 15 ++++---
 src/kudu/server/default_path_handlers.cc  | 26 ++++++------
 src/kudu/server/pprof_path_handlers.cc    | 15 ++++---
 src/kudu/server/rpcz-path-handler.cc      |  5 ++-
 src/kudu/server/startup_path_handler.cc   |  3 +-
 src/kudu/server/tracing_path_handlers.cc  |  3 +-
 src/kudu/server/webserver.cc              | 22 +++++-----
 src/kudu/server/webserver.h               | 29 +++++--------
 src/kudu/tserver/tablet_server-test.cc    | 23 +++++++++++
 src/kudu/tserver/tserver_path_handlers.cc | 18 ++++----
 src/kudu/util/jwt-util-test.cc            |  4 +-
 src/kudu/util/mini_oidc.cc                |  6 +--
 src/kudu/util/test_util.cc                | 68 +++++++++++++++++++++++++++++++
 src/kudu/util/test_util.h                 | 15 +++++++
 src/kudu/util/thread.cc                   |  2 +-
 src/kudu/util/web_callback_registry.h     | 20 +++++++--
 17 files changed, 227 insertions(+), 83 deletions(-)

diff --git a/src/kudu/master/master-test.cc b/src/kudu/master/master-test.cc
index 2554f3903..fd58a0b75 100644
--- a/src/kudu/master/master-test.cc
+++ b/src/kudu/master/master-test.cc
@@ -3798,5 +3798,41 @@ TEST_F(MasterTest, TestMastersListedInDumpEndpoints) {
   ASSERT_NE("", master["start_time"].GetString());
 }
 
+TEST_F(MasterTest, TestMasterContentTypeHeaders) {
+  // /pprof endpoints take longer to finish.
+  SKIP_IF_SLOW_NOT_ALLOWED();
+
+  // Create a table with one partition for the '/table?id=' endpoint.
+  constexpr const char* const kTableName = "testtb";
+  const Schema kTableSchema({ColumnSchema("key", INT32),
+                             ColumnSchema("val", INT32)}, 2);
+  FLAGS_default_num_replicas = 1;
+  KuduPartialRow a_lower(&kTableSchema);
+  KuduPartialRow a_upper(&kTableSchema);
+  ASSERT_OK(a_lower.SetInt32("key", 0));
+  ASSERT_OK(a_upper.SetInt32("key", 100));
+  CreateTableResponsePB create_table_resp;
+  ASSERT_OK(CreateTable(
+      kTableName, kTableSchema, nullopt, nullopt, nullopt, {}, {{a_lower, 
a_upper}},
+      {}, {{{"key"}, 2, 0}}, &create_table_resp));
+
+  // Get all the endpoints to be tested.
+  unordered_map<string, string> endpoint_type_map = 
GetCommonWebserverEndpoints();
+  unordered_map<string, string> master_endpoints =
+      GetMasterWebserverEndpoints(create_table_resp.table_id());
+  endpoint_type_map.merge(master_endpoints);
+
+  EasyCurl c;
+  c.set_return_headers(true);
+  faststring buf;
+  const auto addr = Substitute("http://$0";, 
mini_master_->bound_http_addr().ToString());
+
+  // Check that all the endpoints have the defined Content-Type headers.
+  for (const auto& [endpoint, type] : endpoint_type_map) {
+    ASSERT_OK(c.FetchURL(Substitute("$0/$1", addr, endpoint), &buf));
+    ASSERT_STR_CONTAINS(buf.ToString(), Substitute("Content-Type: $0", type));
+  }
+}
+
 } // namespace master
 } // namespace kudu
diff --git a/src/kudu/master/master_path_handlers.cc 
b/src/kudu/master/master_path_handlers.cc
index 92cdc223d..f1c36fdfe 100644
--- a/src/kudu/master/master_path_handlers.cc
+++ b/src/kudu/master/master_path_handlers.cc
@@ -873,44 +873,43 @@ void MasterPathHandlers::HandleDumpEntities(const 
Webserver::WebRequest& /*req*/
 }
 
 Status MasterPathHandlers::Register(Webserver* server) {
-  constexpr const bool is_styled = true;
   constexpr const bool is_on_nav_bar = true;
   server->RegisterPathHandler(
       "/tablet-servers", "Tablet Servers",
       [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
         this->HandleTabletServers(req, resp);
       },
-      is_styled, is_on_nav_bar);
+      StyleMode::STYLED, is_on_nav_bar);
   server->RegisterPathHandler(
       "/tables", "Tables",
       [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
         this->HandleCatalogManager(req, resp);
       },
-      is_styled, is_on_nav_bar);
+      StyleMode::STYLED, is_on_nav_bar);
   server->RegisterPathHandler(
       "/table", "",
       [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
         this->HandleTablePage(req, resp);
       },
-      is_styled, false);
+      StyleMode::STYLED, false);
   server->RegisterPathHandler(
       "/masters", "Masters",
       [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
         this->HandleMasters(req, resp);
       },
-      is_styled, is_on_nav_bar);
+      StyleMode::STYLED, is_on_nav_bar);
   server->RegisterPrerenderedPathHandler(
       "/ipki-ca-cert", "IPKI CA certificate",
       [this](const Webserver::WebRequest& req, 
Webserver::PrerenderedWebResponse* resp) {
         this->HandleIpkiCaCert(DataFormat::PEM, req, resp);
       },
-      false /*is_styled*/, true /*is_on_nav_bar*/);
+      StyleMode::UNSTYLED , true /*is_on_nav_bar*/);
   server->RegisterPrerenderedPathHandler(
       "/ipki-ca-cert-pem", "IPKI CA certificate (PEM format)",
       [this](const Webserver::WebRequest& req, 
Webserver::PrerenderedWebResponse* resp) {
         this->HandleIpkiCaCert(DataFormat::PEM, req, resp);
       },
-      false /*is_styled*/, false /*is_on_nav_bar*/);
+      StyleMode::UNSTYLED, false /*is_on_nav_bar*/);
   server->RegisterBinaryDataPathHandler(
       "/ipki-ca-cert-der", "IPKI CA certificate (DER format)",
       [this](const Webserver::WebRequest& req, 
Webserver::PrerenderedWebResponse* resp) {
@@ -921,7 +920,7 @@ Status MasterPathHandlers::Register(Webserver* server) {
       [this](const Webserver::WebRequest& req, 
Webserver::PrerenderedWebResponse* resp) {
         this->HandleDumpEntities(req, resp);
       },
-      false, false);
+      StyleMode::UNSTYLED, false);
   return Status::OK();
 }
 
diff --git a/src/kudu/server/default_path_handlers.cc 
b/src/kudu/server/default_path_handlers.cc
index 8856743b4..7457710ac 100644
--- a/src/kudu/server/default_path_handlers.cc
+++ b/src/kudu/server/default_path_handlers.cc
@@ -441,27 +441,27 @@ static void ConfigurationHandler(const 
Webserver::WebRequest& /* req */,
   FillTimeSourceConfigs(output);
 }
 void AddPreInitializedDefaultPathHandlers(Webserver* webserver) {
-  bool styled = true;
   bool on_nav_bar = true;
-  webserver->RegisterPathHandler("/logs", "Logs", LogsHandler, styled, 
on_nav_bar);
-  webserver->RegisterPrerenderedPathHandler("/varz", "Flags", FlagsHandler, 
styled, on_nav_bar);
+  webserver->RegisterPathHandler("/logs", "Logs", LogsHandler, 
StyleMode::STYLED, on_nav_bar);
+  webserver->RegisterPrerenderedPathHandler("/varz", "Flags", FlagsHandler, 
StyleMode::STYLED,
+                                            on_nav_bar);
   webserver->RegisterPathHandler("/config", "Configuration", 
ConfigurationHandler,
-                                 styled, on_nav_bar);
+                                 StyleMode::STYLED, on_nav_bar);
   webserver->RegisterPrerenderedPathHandler("/memz", "Memory (total)", 
MemUsageHandler,
-                                            styled, on_nav_bar);
+                                            StyleMode::STYLED, on_nav_bar);
   webserver->RegisterPrerenderedPathHandler("/mem-trackers", "Memory 
(detail)", MemTrackersHandler,
-                                            styled, on_nav_bar);
+                                            StyleMode::STYLED, on_nav_bar);
 }
 
 void AddPostInitializedDefaultPathHandlers(Webserver* webserver) {
   webserver->RegisterPrerenderedPathHandler("/stacks", "Stacks", StacksHandler,
-                                            /*is_styled=*/false,
+                                            StyleMode::UNSTYLED,
                                             /*is_on_nav_bar=*/true);
   webserver->RegisterPrerenderedPathHandler("/version", "VersionInfo", 
VersionInfoHandler,
-                                            /*is_styled=*/false,
+                                            StyleMode::UNSTYLED,
                                             /*is_on_nav_bar*/false);
   webserver->RegisterPrerenderedPathHandler("/healthz", "Health", 
HealthHandler,
-                                            /*is_styled=*/false,
+                                            StyleMode::UNSTYLED,
                                             /*is_on_nav_bar=*/true);
   AddPprofPathHandlers(webserver);
 }
@@ -531,16 +531,15 @@ void RegisterMetricsJsonHandler(Webserver* webserver, 
const MetricRegistry* cons
                             Webserver::PrerenderedWebResponse* resp) {
     WriteMetricsAsJson(metrics, req, resp);
   };
-  bool not_styled = false;
   bool not_on_nav_bar = false;
   bool is_on_nav_bar = true;
   webserver->RegisterPrerenderedPathHandler("/metrics", "JSON Metrics", 
callback,
-                                            not_styled, is_on_nav_bar);
+                                            StyleMode::UNSTYLED, 
is_on_nav_bar);
 
   // The old name -- this is preserved for compatibility with older releases of
   // monitoring software which expects the old name.
   webserver->RegisterPrerenderedPathHandler("/jsonmetricz", "Metrics", 
callback,
-                                            not_styled, not_on_nav_bar);
+                                            StyleMode::UNSTYLED, 
not_on_nav_bar);
 }
 
 void RegisterMetricsPrometheusHandler(Webserver* webserver, const 
MetricRegistry* const metrics) {
@@ -548,10 +547,9 @@ void RegisterMetricsPrometheusHandler(Webserver* 
webserver, const MetricRegistry
                             Webserver::PrerenderedWebResponse* resp) {
     WriteMetricsAsPrometheus(metrics, req, resp);
   };
-  constexpr bool not_styled = false;
   constexpr bool is_on_nav_bar = true;
   webserver->RegisterPrerenderedPathHandler("/metrics_prometheus", "Prometheus 
Metrics", callback,
-                                            not_styled, is_on_nav_bar);
+                                            StyleMode::UNSTYLED, 
is_on_nav_bar);
 }
 
 } // namespace kudu
diff --git a/src/kudu/server/pprof_path_handlers.cc 
b/src/kudu/server/pprof_path_handlers.cc
index 3b809218d..6ee5508b7 100644
--- a/src/kudu/server/pprof_path_handlers.cc
+++ b/src/kudu/server/pprof_path_handlers.cc
@@ -258,14 +258,17 @@ void AddPprofPathHandlers(Webserver* webserver) {
   // Path handlers for remote pprof profiling. For information see:
   // https://gperftools.googlecode.com/svn/trunk/doc/pprof_remote_servers.html
   webserver->RegisterPrerenderedPathHandler("/pprof/cmdline", "", 
PprofCmdLineHandler,
-                                            false, false);
-  webserver->RegisterPrerenderedPathHandler("/pprof/heap", "", 
PprofHeapHandler, false, false);
-  webserver->RegisterPrerenderedPathHandler("/pprof/growth", "", 
PprofGrowthHandler, false, false);
+                                            StyleMode::UNSTYLED, false);
+  webserver->RegisterPrerenderedPathHandler("/pprof/heap", "", 
PprofHeapHandler,
+                                            StyleMode::UNSTYLED, false);
+  webserver->RegisterPrerenderedPathHandler("/pprof/growth", "", 
PprofGrowthHandler,
+                                            StyleMode::UNSTYLED, false);
   webserver->RegisterPrerenderedPathHandler("/pprof/profile", "", 
PprofCpuProfileHandler,
-                                            false, false);
-  webserver->RegisterPrerenderedPathHandler("/pprof/symbol", "", 
PprofSymbolHandler, false, false);
+                                            StyleMode::UNSTYLED, false);
+  webserver->RegisterPrerenderedPathHandler("/pprof/symbol", "", 
PprofSymbolHandler,
+                                            StyleMode::UNSTYLED, false);
   webserver->RegisterPrerenderedPathHandler("/pprof/contention", "", 
PprofContentionHandler,
-                                            false, false);
+                                            StyleMode::UNSTYLED, false);
 }
 
 } // namespace kudu
diff --git a/src/kudu/server/rpcz-path-handler.cc 
b/src/kudu/server/rpcz-path-handler.cc
index 528fa4e60..159639f9d 100644
--- a/src/kudu/server/rpcz-path-handler.cc
+++ b/src/kudu/server/rpcz-path-handler.cc
@@ -21,11 +21,12 @@
 #include <memory>
 #include <sstream>
 #include <string>
+#include <type_traits>
 #include <unordered_map>
 
 #include "kudu/gutil/map-util.h"
 #include "kudu/gutil/strings/numbers.h"
-#include "kudu/rpc/messenger.h"
+#include "kudu/rpc/messenger.h" // IWYU pragma: keep
 #include "kudu/rpc/rpc_introspection.pb.h"
 #include "kudu/rpc/rpcz_store.h"
 #include "kudu/server/webserver.h"
@@ -81,7 +82,7 @@ void AddRpczPathHandlers(const shared_ptr<Messenger>& 
messenger, Webserver* webs
       [messenger](const Webserver::WebRequest& req, 
Webserver::PrerenderedWebResponse* resp) {
         RpczPathHandler(messenger, req, resp);
       },
-      false, true);
+      StyleMode::UNSTYLED, true /*is_on_nav_bar*/);
 }
 
 } // namespace kudu
diff --git a/src/kudu/server/startup_path_handler.cc 
b/src/kudu/server/startup_path_handler.cc
index e059c1f98..13a3abf5f 100644
--- a/src/kudu/server/startup_path_handler.cc
+++ b/src/kudu/server/startup_path_handler.cc
@@ -119,14 +119,13 @@ void StartupPathHandler::Startup(const 
Webserver::WebRequest& /*req*/,
 }
 
 void StartupPathHandler::RegisterStartupPathHandler(Webserver *webserver) {
-  bool styled = true;
   bool on_nav_bar = true;
   webserver->RegisterPathHandler("/startup", "Startup",
                                  [this](const Webserver::WebRequest& req,
                                         Webserver::WebResponse* resp) {
                                           this->Startup(req, resp);
                                         },
-                                 styled, on_nav_bar);
+                                 StyleMode::STYLED, on_nav_bar);
 }
 
 void StartupPathHandler::set_is_tablet_server(bool is_tablet_server) {
diff --git a/src/kudu/server/tracing_path_handlers.cc 
b/src/kudu/server/tracing_path_handlers.cc
index 842504b45..086424936 100644
--- a/src/kudu/server/tracing_path_handlers.cc
+++ b/src/kudu/server/tracing_path_handlers.cc
@@ -277,8 +277,7 @@ void TracingPathHandlers::RegisterHandlers(Webserver* 
server) {
         e.first, "", [e](const Webserver::WebRequest& req,
                          Webserver::PrerenderedWebResponse* resp) {
           HandleRequest(e.second, req, resp);
-        },
-      false /* styled */, false /* is_on_nav_bar */);
+        }, StyleMode::UNSTYLED, false /* is_on_nav_bar */);
   }
 }
 
diff --git a/src/kudu/server/webserver.cc b/src/kudu/server/webserver.cc
index 07727949c..0a52227d3 100644
--- a/src/kudu/server/webserver.cc
+++ b/src/kudu/server/webserver.cc
@@ -442,7 +442,7 @@ Status Webserver::Start() {
                       [this](const WebRequest& req, WebResponse* resp) {
                         this->RootHandler(req, resp);
                       },
-                      /*is_styled=*/true, /*is_on_nav_bar=*/true);
+                      StyleMode::STYLED, /*is_on_nav_bar=*/true);
 
   vector<Sockaddr> addrs;
   RETURN_NOT_OK(GetBoundAddresses(&addrs));
@@ -682,8 +682,8 @@ sq_callback_result_t Webserver::RunPathHandler(
   }
 
   // Should we render with css styles?
-  StyleMode use_style = handler.is_styled() && !ContainsKey(req.parsed_args, 
"raw") ?
-                        StyleMode::STYLED : StyleMode::UNSTYLED;
+  StyleMode use_style = ContainsKey(req.parsed_args, "raw") ?
+                        StyleMode::UNSTYLED : handler.style_mode();
   SendResponse(connection, resp, &req, use_style);
   return SQ_HANDLED_OK;
 }
@@ -834,7 +834,7 @@ string Webserver::MustachePartialTag(const string& path) {
 }
 
 void Webserver::RegisterPathHandler(const string& path, const string& alias,
-    const PathHandlerCallback& callback, bool is_styled, bool is_on_nav_bar) {
+    const PathHandlerCallback& callback, StyleMode style_mode, bool 
is_on_nav_bar) {
   string render_path = (path == "/") ? "/home" : path;
   auto wrapped_cb = [=](const WebRequest& req, PrerenderedWebResponse* 
rendered_resp) {
     WebResponse resp;
@@ -846,17 +846,17 @@ void Webserver::RegisterPathHandler(const string& path, 
const string& alias,
     // do not render the page
     if (render_path != "/home" || is_started_) {
       stringstream out;
-      Render(render_path, resp.output, is_styled, &out);
+      Render(render_path, resp.output, style_mode, &out);
       rendered_resp->output << out.rdbuf();
     }
   };
-  RegisterPrerenderedPathHandler(path, alias, wrapped_cb, is_styled, 
is_on_nav_bar);
+  RegisterPrerenderedPathHandler(path, alias, wrapped_cb, style_mode, 
is_on_nav_bar);
 }
 
 void Webserver::RegisterPrerenderedPathHandler(const string& path, const 
string& alias,
-    const PrerenderedPathHandlerCallback& callback, bool is_styled, bool 
is_on_nav_bar) {
+    const PrerenderedPathHandlerCallback& callback, StyleMode style_mode, bool 
is_on_nav_bar) {
   std::lock_guard l(lock_);
-  InsertOrDie(&path_handlers_, path, new PathHandler(is_styled, is_on_nav_bar, 
alias, callback));
+  InsertOrDie(&path_handlers_, path, new PathHandler(style_mode, 
is_on_nav_bar, alias, callback));
 }
 
 void Webserver::RegisterBinaryDataPathHandler(
@@ -864,7 +864,7 @@ void Webserver::RegisterBinaryDataPathHandler(
     const string& alias,
     const PrerenderedPathHandlerCallback& callback) {
   std::lock_guard l(lock_);
-  InsertOrDie(&path_handlers_, path, new PathHandler(false /*is_styled*/,
+  InsertOrDie(&path_handlers_, path, new PathHandler(StyleMode::BINARY,
                                                      false /*is_on_nav_bar*/,
                                                      alias,
                                                      callback));
@@ -954,11 +954,11 @@ void Webserver::RenderMainTemplate(
   RenderTemplate(kMainTemplate, opts_.doc_root, ej.value(), output);
 }
 
-void Webserver::Render(const string& path, const EasyJson& ej, bool use_style,
+void Webserver::Render(const string& path, const EasyJson& ej, StyleMode 
style_mode,
                        stringstream* output) {
   if (MustacheTemplateAvailable(path)) {
     RenderTemplate(MustachePartialTag(path), opts_.doc_root, ej.value(), 
output);
-  } else if (use_style) {
+  } else if (style_mode == StyleMode::STYLED) {
     (*output) << "<pre>" << ej.ToString() << "</pre>";
   } else {
     (*output) << ej.ToString();
diff --git a/src/kudu/server/webserver.h b/src/kudu/server/webserver.h
index 073ee8d84..a1be885f0 100644
--- a/src/kudu/server/webserver.h
+++ b/src/kudu/server/webserver.h
@@ -67,17 +67,17 @@ class Webserver : public WebCallbackRegistry {
 
   // Register a route 'path' to be rendered via template.
   // The appropriate template to use is determined by 'path'.
-  // If 'is_styled' is true, the page will be styled and include a header and 
footer.
+  // If 'style_mode' is StyleMode::STYLED, the page will be styled and include 
a header and footer.
   // If 'is_on_nav_bar' is true, a link to the page will be placed on the 
navbar
   // in the header of styled pages. The link text is given by 'alias'.
   void RegisterPathHandler(const std::string& path, const std::string& alias,
                            const PathHandlerCallback& callback,
-                           bool is_styled, bool is_on_nav_bar) override;
+                           StyleMode style_mode, bool is_on_nav_bar) override;
 
   // Register a route 'path'. See the RegisterPathHandler for details.
   void RegisterPrerenderedPathHandler(const std::string& path, const 
std::string& alias,
                                       const PrerenderedPathHandlerCallback& 
callback,
-                                      bool is_styled,
+                                      StyleMode style_mode,
                                       bool is_on_nav_bar) override;
 
   // Register route 'path' for application/octet-stream (binary data) 
responses.
@@ -105,21 +105,21 @@ class Webserver : public WebCallbackRegistry {
   // Container class for a list of path handler callbacks for a single URL.
   class PathHandler {
    public:
-    PathHandler(bool is_styled, bool is_on_nav_bar, std::string alias,
+    PathHandler(StyleMode style_mode, bool is_on_nav_bar, std::string alias,
                 PrerenderedPathHandlerCallback callback)
-        : is_styled_(is_styled),
+        : style_mode_(style_mode),
           is_on_nav_bar_(is_on_nav_bar),
           alias_(std::move(alias)),
           callback_(std::move(callback)) {}
 
-    bool is_styled() const { return is_styled_; }
+    StyleMode style_mode() const { return style_mode_; }
     bool is_on_nav_bar() const { return is_on_nav_bar_; }
     const std::string& alias() const { return alias_; }
     const PrerenderedPathHandlerCallback& callback() const { return callback_; 
}
 
    private:
-    // If true, the page appears is rendered styled.
-    const bool is_styled_;
+    // The style mode controls how the page is rendered, and what content-type 
header is used.
+    const StyleMode style_mode_;
 
     // If true, the page appears in the navigation bar.
     const bool is_on_nav_bar_;
@@ -156,7 +156,7 @@ class Webserver : public WebCallbackRegistry {
 
   // Renders the template corresponding to 'path' (if available), using
   // fields in 'ej'.
-  void Render(const std::string& path, const EasyJson& ej, bool use_style,
+  void Render(const std::string& path, const EasyJson& ej, StyleMode 
style_mode,
               std::stringstream* output);
 
   // Dispatch point for all incoming requests.
@@ -188,19 +188,10 @@ class Webserver : public WebCallbackRegistry {
   //
   // 'req' may be null if we're early enough in processing that we haven't
   // parsed the request yet (e.g. an early error out).
-  //
-  // If 'mode' is STYLED, includes page styling elements like CSS, navigation 
bar, etc.
-  //
-  // BINARY is a rare case when a binary data is sent as a response.
-  enum class StyleMode {
-    STYLED,
-    UNSTYLED,
-    BINARY,
-  };
   void SendResponse(struct sq_connection* connection,
                     PrerenderedWebResponse* resp,
                     const WebRequest* req = nullptr,
-                    StyleMode mode = StyleMode::UNSTYLED);
+                    StyleMode style_mode = StyleMode::UNSTYLED);
 
   const WebserverOptions opts_;
 
diff --git a/src/kudu/tserver/tablet_server-test.cc 
b/src/kudu/tserver/tablet_server-test.cc
index 16b8b9fac..e20ec51d7 100644
--- a/src/kudu/tserver/tablet_server-test.cc
+++ b/src/kudu/tserver/tablet_server-test.cc
@@ -34,6 +34,7 @@
 #include <string>
 #include <thread>
 #include <type_traits>
+#include <unordered_map>
 #include <unordered_set>
 #include <utility>
 #include <vector>
@@ -157,6 +158,7 @@ using std::string;
 using std::thread;
 using std::unique_ptr;
 using std::unordered_set;
+using std::unordered_map;
 using std::vector;
 using strings::Substitute;
 
@@ -618,6 +620,27 @@ TEST_F(TabletServerTest, TestWebPages) {
 #endif
 }
 
+TEST_F(TabletServerTest, TestTabletServerContentTypeHeaders) {
+  // /pprof endpoints take longer to finish.
+  SKIP_IF_SLOW_NOT_ALLOWED();
+
+  // Get all the endpoints to be tested.
+  unordered_map<string, string> endpoint_type_map = 
GetCommonWebserverEndpoints();
+  unordered_map<string, string> tserver_endpoints = 
GetTServerWebserverEndpoints(kTabletId);
+  endpoint_type_map.merge(tserver_endpoints);
+
+  EasyCurl c;
+  c.set_return_headers(true);
+  faststring buf;
+  const auto addr = Substitute("http://$0";, 
mini_server_->bound_http_addr().ToString());
+
+  // Check that all the endpoints have the defined Content-Type headers.
+  for (const auto& [endpoint, type] : endpoint_type_map) {
+    ASSERT_OK(c.FetchURL(Substitute("$0/$1", addr, endpoint), &buf));
+    ASSERT_STR_CONTAINS(buf.ToString(), Substitute("Content-Type: $0", type));
+  }
+}
+
 // Ensure that when a replica is in a failed / shutdown state, it returns an
 // error for ConsensusState() requests.
 TEST_F(TabletServerTest, TestFailedTabletsRejectConsensusState) {
diff --git a/src/kudu/tserver/tserver_path_handlers.cc 
b/src/kudu/tserver/tserver_path_handlers.cc
index 37e925650..a1ba5bcb2 100644
--- a/src/kudu/tserver/tserver_path_handlers.cc
+++ b/src/kudu/tserver/tserver_path_handlers.cc
@@ -202,55 +202,55 @@ Status TabletServerPathHandlers::Register(Webserver* 
server) {
     [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
       this->HandleScansPage(req, resp);
     },
-    true /* styled */, false /* is_on_nav_bar */);
+    StyleMode::STYLED, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/tablets", "Tablets",
     [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
       this->HandleTabletsPage(req, resp);
     },
-    true /* styled */, true /* is_on_nav_bar */);
+    StyleMode::STYLED, true /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/tablet", "",
     [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
       this->HandleTabletPage(req, resp);
     },
-    true /* styled */, false /* is_on_nav_bar */);
+    StyleMode::STYLED, false /* is_on_nav_bar */);
   server->RegisterPrerenderedPathHandler(
     "/transactions", "",
     [this](const Webserver::WebRequest& req, 
Webserver::PrerenderedWebResponse* resp) {
       this->HandleTransactionsPage(req, resp);
     },
-    true /* styled */, false /* is_on_nav_bar */);
+    StyleMode::STYLED, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/tablet-rowsetlayout-svg", "",
     [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
       this->HandleTabletSVGPage(req, resp);
     },
-    true /* styled */, false /* is_on_nav_bar */);
+    StyleMode::STYLED, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/tablet-consensus-status", "",
     [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
       this->HandleConsensusStatusPage(req, resp);
     },
-    true /* styled */, false /* is_on_nav_bar */);
+    StyleMode::STYLED, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/log-anchors", "",
     [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
       this->HandleLogAnchorsPage(req, resp);
     },
-    true /* styled */, false /* is_on_nav_bar */);
+    StyleMode::STYLED, false /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/dashboards", "Dashboards",
     [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
       this->HandleDashboardsPage(req, resp);
     },
-    true /* styled */, true /* is_on_nav_bar */);
+    StyleMode::STYLED, true /* is_on_nav_bar */);
   server->RegisterPathHandler(
     "/maintenance-manager", "",
     [this](const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
       this->HandleMaintenanceManagerPage(req, resp);
     },
-    true /* styled */, false /* is_on_nav_bar */);
+    StyleMode::STYLED, false /* is_on_nav_bar */);
 
   return Status::OK();
 }
diff --git a/src/kudu/util/jwt-util-test.cc b/src/kudu/util/jwt-util-test.cc
index c3655964c..0b2d380ec 100644
--- a/src/kudu/util/jwt-util-test.cc
+++ b/src/kudu/util/jwt-util-test.cc
@@ -851,7 +851,7 @@ class JWKSMockServer {
     opts.bind_interface = "127.0.0.1";
     webserver_.reset(new Webserver(opts));
     webserver_->RegisterPrerenderedPathHandler("/jwks", "JWKS", 
SimpleJWKSHandler,
-                                               /*is_styled*/false, 
/*is_on_nav_bar*/false);
+                                               StyleMode::UNSTYLED, 
/*is_on_nav_bar*/false);
     RETURN_NOT_OK(webserver_->Start());
     vector<Sockaddr> addrs;
     RETURN_NOT_OK(webserver_->GetBoundAddresses(&addrs));
@@ -876,7 +876,7 @@ class JWKSMockServer {
             resp->output << jwks;
             resp->status_code = HttpStatusCode::Ok;
           },
-          /*is_styled*/false, /*is_on_nav_bar*/false);
+          StyleMode::UNSTYLED, /*is_on_nav_bar*/false);
     }
     RETURN_NOT_OK(webserver_->Start());
     vector<Sockaddr> addrs;
diff --git a/src/kudu/util/mini_oidc.cc b/src/kudu/util/mini_oidc.cc
index 36103f0e5..fda284588 100644
--- a/src/kudu/util/mini_oidc.cc
+++ b/src/kudu/util/mini_oidc.cc
@@ -126,7 +126,7 @@ Status MiniOidc::Start() {
                                      kRsaInvalidPubKeyJwkN, kRsaPubKeyJwkE),
               resp->status_code = HttpStatusCode::Ok;
         },
-        /*is_styled*/ false,
+        StyleMode::UNSTYLED,
         /*is_on_nav_bar*/ false);
   }
 
@@ -146,7 +146,7 @@ Status MiniOidc::Start() {
                                    kRsaPubKeyJwkE),
         resp->status_code = HttpStatusCode::Ok;
       },
-      /*is_styled*/ false,
+      StyleMode::UNSTYLED,
       /*is_on_nav_bar*/ false);
 
   LOG(INFO) << "Starting JWKS server";
@@ -183,7 +183,7 @@ Status MiniOidc::Start() {
                            Webserver::PrerenderedWebResponse* resp) {
         OidcDiscoveryHandler(req, resp, discovery_jwks_url);
       },
-      /*is_styled*/ false,
+      StyleMode::UNSTYLED,
       /*is_on_nav_bar*/ false);
 
   LOG(INFO) << "Starting OIDC Discovery server";
diff --git a/src/kudu/util/test_util.cc b/src/kudu/util/test_util.cc
index b491018fc..9129a5ce8 100644
--- a/src/kudu/util/test_util.cc
+++ b/src/kudu/util/test_util.cc
@@ -72,6 +72,7 @@ DECLARE_bool(enable_multi_tenancy);
 DECLARE_bool(encrypt_data_at_rest);
 
 using std::string;
+using std::unordered_map;
 using std::vector;
 using strings::Substitute;
 
@@ -96,6 +97,10 @@ static const char* const kEncryptionTenantID = 
"00000000000000000000000000000000
 
 static const uint64_t kTestBeganAtMicros = Env::Default()->NowMicros();
 
+static const char* const kContentTypeTextPlain = "text/plain";
+static const char* const kContentTypeTextHtml = "text/html";
+static const char* const kContentTypeApplicationOctet = 
"application/octet-stream";
+
 // Global which production code can check to see if it is running
 // in a GTest environment (assuming the test binary links in this module,
 // which is typically a good assumption).
@@ -652,4 +657,67 @@ Status FindHomeDir(const string& name, const string& 
bin_dir, string* home_dir)
   return Status::OK();
 }
 
+const unordered_map<string, string>& GetCommonWebserverEndpoints() {
+  static const unordered_map<string, string> common_endpoints = {
+      {"logs", kContentTypeTextHtml},
+      {"varz", kContentTypeTextHtml},
+      {"config", kContentTypeTextHtml},
+      {"memz", kContentTypeTextHtml},
+      {"mem-trackers", kContentTypeTextHtml},
+      {"stacks", kContentTypeTextPlain},
+      {"version", kContentTypeTextPlain},
+      {"healthz", kContentTypeTextPlain},
+      {"metrics", kContentTypeTextPlain},
+      {"jsonmetricz", kContentTypeTextPlain},
+      {"metrics_prometheus", kContentTypeTextPlain},
+      {"rpcz", kContentTypeTextPlain},
+      {"startup", kContentTypeTextHtml},
+      {"pprof/cmdline", kContentTypeTextPlain},
+      {"pprof/heap", kContentTypeTextPlain},
+      {"pprof/growth", kContentTypeTextPlain},
+      {"pprof/profile", kContentTypeTextPlain},
+      {"pprof/symbol", kContentTypeTextPlain},
+      {"pprof/contention", kContentTypeTextPlain},
+      {"tracing/json/begin_monitoring", kContentTypeTextPlain},
+      {"tracing/json/end_monitoring", kContentTypeTextPlain},
+      {"tracing/json/capture_monitoring", kContentTypeTextPlain},
+      {"tracing/json/get_monitoring_status", kContentTypeTextPlain},
+      {"tracing/json/categories", kContentTypeTextPlain},
+      {"tracing/json/begin_recording", kContentTypeTextPlain},
+      {"tracing/json/get_buffer_percent_full", kContentTypeTextPlain},
+      {"tracing/json/end_recording", kContentTypeTextPlain},
+      {"tracing/json/end_recording_compressed", kContentTypeTextPlain},
+      {"tracing/json/simple_dump", kContentTypeTextPlain}};
+  return common_endpoints;
+}
+
+// Add necessary query params to get 200 response in tests.
+const unordered_map<string, string>& GetTServerWebserverEndpoints(const 
string& tablet_id) {
+  static const unordered_map<string, string> tserver_endpoints = {
+      {"scans", kContentTypeTextHtml},
+      {"tablets", kContentTypeTextHtml},
+      {Substitute("tablet?id=$0", tablet_id), kContentTypeTextHtml},
+      {"transactions", kContentTypeTextHtml},
+      {Substitute("tablet-rowsetlayout-svg?id=$0", tablet_id), 
kContentTypeTextHtml},
+      {Substitute("tablet-consensus-status?id=$0", tablet_id), 
kContentTypeTextHtml},
+      {Substitute("log-anchors?id=$0", tablet_id), kContentTypeTextHtml},
+      {"dashboards", kContentTypeTextHtml},
+      {"maintenance-manager", kContentTypeTextHtml}};
+  return tserver_endpoints;
+}
+
+// Add necessary query params to get 200 response in tests.
+const unordered_map<string, string>& GetMasterWebserverEndpoints(const string& 
table_id) {
+  static unordered_map<string, string> master_endpoints = {
+      {"tablet-servers", kContentTypeTextHtml},
+      {"tables", kContentTypeTextHtml},
+      {Substitute("table?id=$0", table_id), kContentTypeTextHtml},
+      {"masters", kContentTypeTextHtml},
+      {"ipki-ca-cert", kContentTypeTextPlain},
+      {"ipki-ca-cert-pem", kContentTypeTextPlain},
+      {"ipki-ca-cert-der", kContentTypeApplicationOctet},
+      {"dump-entities", kContentTypeTextPlain}};
+  return master_endpoints;
+}
+
 } // namespace kudu
diff --git a/src/kudu/util/test_util.h b/src/kudu/util/test_util.h
index afff0d507..33071112b 100644
--- a/src/kudu/util/test_util.h
+++ b/src/kudu/util/test_util.h
@@ -26,6 +26,7 @@
 #include <memory>
 #include <string>
 #include <vector>
+#include <unordered_map>
 
 #include <gtest/gtest.h>
 
@@ -201,5 +202,19 @@ Status FindHomeDir(const std::string& name,
                    const std::string& bin_dir,
                    std::string* home_dir) WARN_UNUSED_RESULT;
 
+// Contains the endpoints which are to be found on Master and TServer as well.
+// Key: endpoint name, value: content-type header for that particular endpoint.
+const std::unordered_map<std::string, std::string>& 
GetCommonWebserverEndpoints();
+
+// Contains the endpoints which are to be found on TServer only.
+// Key: endpoint name, value: content-type header for that particular endpoint.
+const std::unordered_map<std::string, std::string>& 
GetTServerWebserverEndpoints(
+    const std::string& tablet_id);
+
+// Contains the endpoints which are to be found on Master only.
+// Key: endpoint name, value: content-type header for that particular endpoint.
+const std::unordered_map<std::string, std::string>& 
GetMasterWebserverEndpoints(
+    const std::string& table_id);
+
 } // namespace kudu
 #endif
diff --git a/src/kudu/util/thread.cc b/src/kudu/util/thread.cc
index beab6f06e..0293ef1a4 100644
--- a/src/kudu/util/thread.cc
+++ b/src/kudu/util/thread.cc
@@ -337,7 +337,7 @@ Status ThreadMgr::StartInstrumentation(const 
scoped_refptr<MetricEntity>& metric
                                       WebCallbackRegistry::WebResponse* resp) {
           this->ThreadPathHandler(req, resp);
         },
-        /* is_styled= */ true,
+        StyleMode::STYLED,
         /* is_on_nav_bar= */ true);
   }
   return Status::OK();
diff --git a/src/kudu/util/web_callback_registry.h 
b/src/kudu/util/web_callback_registry.h
index 7bd4d309b..d0e39db44 100644
--- a/src/kudu/util/web_callback_registry.h
+++ b/src/kudu/util/web_callback_registry.h
@@ -37,6 +37,18 @@ enum class HttpStatusCode {
   ServiceUnavailable, // 503
 };
 
+// StyleMode is an enumeration used to define the format of the server's 
response to the client.
+// This format determines how the response data is presented and interpreted 
by the client.
+enum class StyleMode {
+  // This mode includes additional styling elements in the response,
+  // such as CSS, navigation bar, etc.
+  STYLED,
+  // In this mode, the response is sent without any styling elements.
+  UNSTYLED,
+  // In rare cases when a binary data is sent as a response.
+  BINARY
+};
+
 // Interface for registering webserver callbacks.
 //
 // To register a webserver callback for /example/path:
@@ -104,25 +116,25 @@ class WebCallbackRegistry {
   virtual ~WebCallbackRegistry() = default;
 
   // Register a callback for a URL path. Path should not include the
-  // http://hostname/ prefix. If is_styled is true, the page is meant to be for
+  // http://hostname/ prefix. If style_mode is StyleMode::STYLED, the page is 
meant to be for
   // people to look at and is styled.  If false, it is meant to be for 
machines to
   // scrape.  If is_on_nav_bar is true,  a link to this page is
   // printed in the navigation bar at the top of each debug page. Otherwise the
   // link does not appear, and the page is rendered without HTML headers and
   // footers.
-  // The first registration's choice of is_styled overrides all
+  // The first registration's choice of style_mode overrides all
   // subsequent registrations for that URL.
   // For each call to RegisterPathHandler(), the file 
$KUDU_HOME/www<path>.mustache
   // should exist.
   virtual void RegisterPathHandler(const std::string& path, const std::string& 
alias,
                                    const PathHandlerCallback& callback,
-                                   bool is_styled, bool is_on_nav_bar) = 0;
+                                   StyleMode style_mode, bool is_on_nav_bar) = 
0;
 
   // Same as RegisterPathHandler(), except that callback produces prerendered 
HTML.
   // Use RegisterPathHandler() with a mustache template instead.
   virtual void RegisterPrerenderedPathHandler(const std::string& path, const 
std::string& alias,
                                               const 
PrerenderedPathHandlerCallback& callback,
-                                              bool is_styled,
+                                              StyleMode style_mode,
                                               bool is_on_nav_bar) = 0;
 
   // Register a callback for a URL path that returns binary data, a.k.a. octet


Reply via email to