From 5d665ecd1799d6a1b67a684b6f09f4c774f63a15 Mon Sep 17 00:00:00 2001 From: Tor Brede Vekterli Date: Fri, 16 Sep 2022 11:59:31 +0000 Subject: Allow path prefix alias for legacy content status pages This lets status pages that today are served at the root `/` path be aliased under `/contentnode-status/v1/`. Legacy paths continue working as before. Change existing status page absolute paths to relative to avoid having to care about this particular detail internally. Note: both distributor and search/storage node process status pages use the `/contentnode-status/v1/` prefix, as they're both technically processes that are part of a _logical_ content node. --- .../src/tests/frameworkimpl/status/statustest.cpp | 61 +++++++++++++++------- .../src/vespa/storage/bucketdb/bucketmanager.cpp | 4 +- .../frameworkimpl/status/statuswebserver.cpp | 15 +++++- .../persistence/filestorage/filestormanager.cpp | 2 +- .../src/vespa/storage/visiting/visitormanager.cpp | 2 +- 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/storage/src/tests/frameworkimpl/status/statustest.cpp b/storage/src/tests/frameworkimpl/status/statustest.cpp index 2593eabecec..0db5f2cf6b0 100644 --- a/storage/src/tests/frameworkimpl/status/statustest.cpp +++ b/storage/src/tests/frameworkimpl/status/statustest.cpp @@ -12,6 +12,7 @@ #include #include #include +#include using namespace ::testing; @@ -99,6 +100,19 @@ void StatusTest::SetUp() { _node = std::make_unique(); } +namespace { + +std::string additional_fixed_http_response_headers() { + return ("X-XSS-Protection: 1; mode=block\r\n" + "X-Frame-Options: DENY\r\n" + "Content-Security-Policy: default-src 'none'; frame-ancestors 'none'\r\n" + "X-Content-Type-Options: nosniff\r\n" + "Cache-Control: no-store\r\n" + "Pragma: no-cache\r\n"); +} + +} + TEST_F(StatusTest, index_status_page) { StatusComponent rep1(_node->getComponentRegister(), "foo", new HtmlStatusReporter( @@ -115,12 +129,7 @@ TEST_F(StatusTest, index_status_page) { "Connection: close\r\n" "Content-Type: text\\/html\r\n" "Content-Length: [0-9]+\r\n" - "X-XSS-Protection: 1; mode=block\r\n" - "X-Frame-Options: DENY\r\n" - "Content-Security-Policy: default-src 'none'; frame-ancestors 'none'\r\n" - "X-Content-Type-Options: nosniff\r\n" - "Cache-Control: no-store\r\n" - "Pragma: no-cache\r\n" + + additional_fixed_http_response_headers() + "\r\n" "\n" "\n" @@ -150,12 +159,33 @@ TEST_F(StatusTest, html_status) { "Connection: close\r\n" "Content-Type: text/html\r\n" "Content-Length: 117\r\n" - "X-XSS-Protection: 1; mode=block\r\n" - "X-Frame-Options: DENY\r\n" - "Content-Security-Policy: default-src 'none'; frame-ancestors 'none'\r\n" - "X-Content-Type-Options: nosniff\r\n" - "Cache-Control: no-store\r\n" - "Pragma: no-cache\r\n" + + additional_fixed_http_response_headers() + + "\r\n" + "\n" + "\n" + " Foo impl\n" + "\n" + "\n" + "

Foo impl

\n" + "

info

\n" + "\n" + ); + EXPECT_EQ(expected, std::string(actual)); +} + +TEST_F(StatusTest, path_with_v1_prefix_aliases_to_handler_under_root) { + StatusComponent rep1(_node->getComponentRegister(), "foo", + new HtmlStatusReporter("fooid", "Foo impl", "

info

", "")); + StatusWebServer webServer(_node->getComponentRegister(), + _node->getComponentRegister(), + config::ConfigUri("raw:httpport 0")); + auto actual = fetch(webServer.getListenPort(), "/contentnode-status/v1/fooid?unusedParam"); + std::string expected( + "HTTP/1.1 200 OK\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n" + "Content-Length: 117\r\n" + + additional_fixed_http_response_headers() + "\r\n" "\n" "\n" @@ -182,12 +212,7 @@ TEST_F(StatusTest, xml_sStatus) { "Connection: close\r\n" "Content-Type: application/xml\r\n" "Content-Length: 100\r\n" - "X-XSS-Protection: 1; mode=block\r\n" - "X-Frame-Options: DENY\r\n" - "Content-Security-Policy: default-src 'none'; frame-ancestors 'none'\r\n" - "X-Content-Type-Options: nosniff\r\n" - "Cache-Control: no-store\r\n" - "Pragma: no-cache\r\n" + + additional_fixed_http_response_headers() + "\r\n" "\n" "\n" diff --git a/storage/src/vespa/storage/bucketdb/bucketmanager.cpp b/storage/src/vespa/storage/bucketdb/bucketmanager.cpp index 166bda1adbb..51422de07e6 100644 --- a/storage/src/vespa/storage/bucketdb/bucketmanager.cpp +++ b/storage/src/vespa/storage/bucketdb/bucketmanager.cpp @@ -376,8 +376,8 @@ BucketManager::reportStatus(std::ostream& out, } else { framework::PartlyHtmlStatusReporter htmlReporter(*this); htmlReporter.reportHtmlHeader(out, path); - // Print menu - out << "[ Back to top" + // Print menu + out << "[ Back to top" << " | Show all buckets ]"; htmlReporter.reportHtmlFooter(out, path); } diff --git a/storage/src/vespa/storage/frameworkimpl/status/statuswebserver.cpp b/storage/src/vespa/storage/frameworkimpl/status/statuswebserver.cpp index 7139ab0eb41..b2bce8a1241 100644 --- a/storage/src/vespa/storage/frameworkimpl/status/statuswebserver.cpp +++ b/storage/src/vespa/storage/frameworkimpl/status/statuswebserver.cpp @@ -169,10 +169,21 @@ void StatusWebServer::handlePage(const framework::HttpUrlPath& urlpath, vespalib::Portal::GetRequest request) { vespalib::string link(urlpath.getPath()); - if (!link.empty() && link[0] == '/') link = link.substr(1); + + // We allow a fixed path prefix that aliases down to whatever is provided after the prefix. + vespalib::stringref optional_status_path_prefix = "/contentnode-status/v1/"; + if (link.starts_with(optional_status_path_prefix)) { + link = link.substr(optional_status_path_prefix.size()); + } + + if (!link.empty() && link[0] == '/') { + link = link.substr(1); + } size_t slashPos = link.find('/'); - if (slashPos != std::string::npos) link = link.substr(0, slashPos); + if (slashPos != std::string::npos) { + link = link.substr(0, slashPos); + } if ( ! link.empty()) { const framework::StatusReporter *reporter = _reporterMap.getStatusReporter(link); diff --git a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp b/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp index 62be96447a4..314836384ce 100644 --- a/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp +++ b/storage/src/vespa/storage/persistence/filestorage/filestormanager.cpp @@ -893,7 +893,7 @@ FileStorManager::reportHtmlStatus(std::ostream& out, const framework::HttpUrlPat bool showStatus = !path.hasAttribute("thread"); bool verbose = path.hasAttribute("verbose"); // Print menu - out << "[ Back to top" + out << "[ Back to top" << " | Main filestor manager status page" << " | [ Back to top" + out << "[ Back to top" << " | Main visitor manager status page" << " |