Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -654,3 +654,54 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

--------------------------------------------------------------------------------

webroot/be/bootstrap/css/bootstrap-table.min.css and webroot/be/bootstrap/js/bootstrap-table.min.js MIT license

The MIT License (MIT)

Copyright (c) 2018 zhixin wen

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

--------------------------------------------------------------------------------

webroot/be/bootstrap/css/bootstrap.min.css and webroot/be/bootstrap/js/bootstrap.min.js MIT license

The MIT License (MIT)

Copyright (c) 2011-2016 Twitter, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

4 changes: 4 additions & 0 deletions be/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -569,3 +569,7 @@ install(FILES
${BASE_DIR}/../conf/be.conf
DESTINATION ${OUTPUT_DIR}/conf)

install(DIRECTORY
${BASE_DIR}/../webroot/be/
DESTINATION ${OUTPUT_DIR}/www)

189 changes: 118 additions & 71 deletions be/src/http/web_page_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <boost/bind.hpp>
#include <boost/mem_fn.hpp>

#include <mustache.h>

#include "common/config.h"
#include "env/env.h"
#include "gutil/stl_util.h"
Expand All @@ -44,18 +46,32 @@ namespace doris {
static std::string s_html_content_type = "text/html";

WebPageHandler::WebPageHandler(EvHttpServer* server) : _http_server(server) {
_www_path = std::string(getenv("DORIS_HOME")) + "/www/";

// Make WebPageHandler to be static file handler, static files, e.g. css, png, will be handled by WebPageHandler.
_http_server->register_static_file_handler(this);

PageHandlerCallback root_callback =
TemplatePageHandlerCallback root_callback =
boost::bind<void>(boost::mem_fn(&WebPageHandler::root_handler), this, _1, _2);
register_page("/", "Home", root_callback, false /* is_on_nav_bar */);
register_template_page("/", "Home", root_callback, false /* is_on_nav_bar */);
}

WebPageHandler::~WebPageHandler() {
STLDeleteValues(&_page_map);
}

void WebPageHandler::register_template_page(const std::string& path, const string& alias,
const TemplatePageHandlerCallback& callback, bool is_on_nav_bar) {
// Relative path which will be used to find .mustache file in _www_path
string render_path = (path == "/") ? "/home" : path;
auto wrapped_cb = [=](const ArgumentMap& args, std::stringstream* output) {
EasyJson ej;
callback(args, &ej);
Render(render_path, ej, true /* is_styled */, output);
};
register_page(path, alias, wrapped_cb, is_on_nav_bar);
}

void WebPageHandler::register_page(const std::string& path, const string& alias,
const PageHandlerCallback& callback, bool is_on_nav_bar) {
boost::mutex::scoped_lock lock(_map_lock);
Expand All @@ -79,7 +95,7 @@ void WebPageHandler::handle(HttpRequest* req) {

if (handler == nullptr) {
// Try to handle static file request
do_file_response(std::string(getenv("DORIS_HOME")) + "/www/" + req->raw_path(), req);
do_file_response(_www_path + req->raw_path(), req);
// Has replied in do_file_response, so we return here.
return;
}
Expand All @@ -90,88 +106,119 @@ void WebPageHandler::handle(HttpRequest* req) {
bool use_style = (params.find("raw") == params.end());

std::stringstream content;
// Append header
if (use_style) {
bootstrap_page_header(&content);
}

// Append content
handler->callback()(params, &content);

// Append footer
std::string output;
if (use_style) {
bootstrap_page_footer(&content);
std::stringstream oss;
RenderMainTemplate(content.str(), &oss);
output = oss.str();
} else {
output = content.str();
}

req->add_output_header(HttpHeaders::CONTENT_TYPE, s_html_content_type.c_str());
HttpChannel::send_reply(req, HttpStatus::OK, content.str());
HttpChannel::send_reply(req, HttpStatus::OK, output);
}

static const std::string PAGE_HEADER =
"<!DOCTYPE html>"
" <html>"
" <head><title>Doris</title>"
" <link href='www/bootstrap/css/bootstrap.min.css' rel='stylesheet' media='screen'>"
" <style>"
" body {"
" padding-top: 60px; "
" }"
" </style>"
" </head>"
" <body>";

static const std::string PAGE_FOOTER = "</div></body></html>";

static const std::string NAVIGATION_BAR_PREFIX =
"<div class='navbar navbar-inverse navbar-fixed-top'>"
" <div class='navbar-inner'>"
" <div class='container'>"
" <a class='btn btn-navbar' data-toggle='collapse' data-target='.nav-collapse'>"
" <span class='icon-bar'></span>"
" <span class='icon-bar'></span>"
" <span class='icon-bar'></span>"
" </a>"
" <a class='brand' href='/'>Doris</a>"
" <div class='nav-collapse collapse'>"
" <ul class='nav'>";

static const std::string NAVIGATION_BAR_SUFFIX =
" </ul>"
" </div>"
" </div>"
" </div>"
" </div>"
" <div class='container'>";

void WebPageHandler::bootstrap_page_header(std::stringstream* output) {
boost::mutex::scoped_lock lock(_map_lock);
(*output) << PAGE_HEADER;
(*output) << NAVIGATION_BAR_PREFIX;
for (auto& iter : _page_map) {
(*output) << "<li><a href=\"" << iter.first << "\">" << iter.first << "</a></li>";
void WebPageHandler::root_handler(const ArgumentMap& args, EasyJson* output) {
(*output)["version"] = get_version_string(false);
(*output)["cpuinfo"] = CpuInfo::debug_string();
(*output)["meminfo"] = MemInfo::debug_string();
(*output)["diskinfo"] = DiskInfo::debug_string();
}

static const char* const kMainTemplate = R"(
<!DOCTYPE html>
<html>
<head>
<title>Doris</title>
<meta charset='utf-8'/>
<link href='/bootstrap/css/bootstrap.min.css' rel='stylesheet' media='screen' />
<link href='/bootstrap/css/bootstrap-table.min.css' rel='stylesheet' media='screen' />
<script src='/jquery-3.2.1.min.js' defer></script>
<script src='/bootstrap/js/bootstrap.min.js' defer></script>
<script src='/bootstrap/js/bootstrap-table.min.js' defer></script>
<script src='/doris.js' defer></script>
<link href='/doris.css' rel='stylesheet' />
</head>
<body>

<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" style="padding-top: 5px;" href="/">
<img src="/logo.png" width='40' height='40' alt="Doris" />
</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
{{#path_handlers}}
<li><a class="nav-link"href="{{path}}">{{alias}}</a></li>
{{/path_handlers}}
</ul>
</div><!--/.nav-collapse -->
</div><!--/.container-fluid -->
</nav>
{{^static_pages_available}}
<div style="color: red">
<strong>Static pages not available. Make sure ${DORIS_HOME}/www/ exists and contains web static files.</strong>
</div>
{{/static_pages_available}}
{{{content}}}
</div>
{{#footer_html}}
<footer class="footer"><div class="container text-muted">
{{{.}}}
</div></footer>
{{/footer_html}}
</body>
</html>
)";

std::string WebPageHandler::MustachePartialTag(const std::string& path) const {
return Substitute("{{> $0.mustache}}", path);
}

bool WebPageHandler::MustacheTemplateAvailable(const std::string& path) const {
if (!static_pages_available()) {
return false;
}
(*output) << NAVIGATION_BAR_SUFFIX;
return Env::Default()->path_exists(Substitute("$0/$1.mustache", _www_path, path)).ok();
}

void WebPageHandler::bootstrap_page_footer(std::stringstream* output) {
(*output) << PAGE_FOOTER;
void WebPageHandler::RenderMainTemplate(const std::string& content, std::stringstream* output) {
static const std::string& footer = std::string("<pre>") + get_version_string(true) + std::string("</pre>");

EasyJson ej;
ej["static_pages_available"] = static_pages_available();
ej["content"] = content;
ej["footer_html"] = footer;
EasyJson path_handlers = ej.Set("path_handlers", EasyJson::kArray);
for (const auto& handler : _page_map) {
if (handler.second->is_on_nav_bar()) {
EasyJson path_handler = path_handlers.PushBack(EasyJson::kObject);
path_handler["path"] = handler.first;
path_handler["alias"] = handler.second->alias();
}
}
mustache::RenderTemplate(kMainTemplate, _www_path, ej.value(), output);
}

void WebPageHandler::root_handler(const ArgumentMap& args, std::stringstream* output) {
// _path_handler_lock already held by MongooseCallback
(*output) << "<h2>Version</h2>";
(*output) << "<pre>" << get_version_string(false) << "</pre>" << std::endl;
(*output) << "<h2>Hardware Info</h2>";
(*output) << "<pre>";
(*output) << CpuInfo::debug_string();
(*output) << MemInfo::debug_string();
(*output) << DiskInfo::debug_string();
(*output) << "</pre>";

(*output) << "<h2>Status Pages</h2>";
for (auto& iter : _page_map) {
(*output) << "<a href=\"" << iter.first << "\">" << iter.first << "</a><br/>";
void WebPageHandler::Render(const string& path, const EasyJson& ej, bool use_style,
std::stringstream* output) {
if (MustacheTemplateAvailable(path)) {
mustache::RenderTemplate(MustachePartialTag(path), _www_path, ej.value(), output);
} else if (use_style) {
(*output) << "<pre>" << ej.ToString() << "</pre>";
} else {
(*output) << ej.ToString();
}
}

bool WebPageHandler::static_pages_available() const {
bool is_dir = false;
return Env::Default()->is_directory(_www_path, &is_dir).ok() && is_dir;
}

} // namespace doris
34 changes: 30 additions & 4 deletions be/src/http/web_page_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <boost/thread/mutex.hpp>

#include "http/http_handler.h"
#include "util/easy_json.h"

namespace doris {

Expand All @@ -39,22 +40,46 @@ class WebPageHandler : public HttpHandler {
typedef std::map<std::string, std::string> ArgumentMap;
typedef boost::function<void (const ArgumentMap& args, std::stringstream* output)>
PageHandlerCallback;
typedef boost::function<void (const ArgumentMap& args, EasyJson* output)>
TemplatePageHandlerCallback;

WebPageHandler(EvHttpServer* http_server);
virtual ~WebPageHandler();

void handle(HttpRequest *req) override;

// Register a route 'path'.
// Register a route 'path' to be rendered via template.
// The appropriate template to use is determined by 'path'.
// 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 register_template_page(const std::string& path, const std::string& alias,
const TemplatePageHandlerCallback& callback, bool is_on_nav_bar);

// Register a route 'path'. See the register_template_page for details.
void register_page(const std::string& path, const std::string& alias,
const PageHandlerCallback& callback, bool is_on_nav_bar);

private:
void bootstrap_page_header(std::stringstream* output);
void bootstrap_page_footer(std::stringstream* output);
void root_handler(const ArgumentMap& args, std::stringstream* output);
void root_handler(const ArgumentMap& args, EasyJson* output);

// Returns a mustache tag that renders the partial at path when
// passed to mustache::RenderTemplate.
std::string MustachePartialTag(const std::string& path) const;

// Returns whether or not a mustache template corresponding
// to the given path can be found.
bool MustacheTemplateAvailable(const std::string& path) const;

// Renders the main HTML template with the pre-rendered string 'content'
// in the main body of the page, into 'output'.
void RenderMainTemplate(const std::string& content, std::stringstream* output);

// Renders the template corresponding to 'path' (if available), using
// fields in 'ej'.
void Render(const std::string& path, const EasyJson& ej, bool use_style,
std::stringstream* output);

bool static_pages_available() const;

// Container class for a list of path handler callbacks for a single URL.
class PathHandler {
Expand Down Expand Up @@ -85,6 +110,7 @@ class WebPageHandler : public HttpHandler {
PageHandlerCallback callback_;
};

std::string _www_path;
EvHttpServer* _http_server;
// Lock guarding the _path_handlers map
boost::mutex _map_lock;
Expand Down
2 changes: 2 additions & 0 deletions be/src/util/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ set(UTIL_FILES
trace.cpp
trace_metrics.cpp
timezone_utils.cpp
easy_json.cc
mustache/mustache.cc
)

if (WITH_MYSQL)
Expand Down
Loading