blob: 2140b2749d391abe56a4cfb46e6b47e0e8551488 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "http/web_page_handler.h"
#include <boost/bind.hpp>
#include <boost/mem_fn.hpp>
#include "http/ev_http_server.h"
#include "http/http_channel.h"
#include "http/http_headers.h"
#include "http/http_request.h"
#include "http/http_response.h"
#include "http/http_status.h"
#include "util/cpu_info.h"
#include "util/debug_util.h"
#include "util/disk_info.h"
#include "util/mem_info.h"
namespace doris {
static std::string s_html_content_type = "text/html";
WebPageHandler::WebPageHandler(EvHttpServer* server) : _http_server(server) {
PageHandlerCallback default_callback =
boost::bind<void>(boost::mem_fn(&WebPageHandler::root_handler), this, _1, _2);
register_page("/", default_callback);
}
void WebPageHandler::register_page(const std::string& path, const PageHandlerCallback& callback) {
// Put this handler to to s_handler_by_name
// because handler does't often new
// So we insert it to this set when everytime
boost::mutex::scoped_lock lock(_map_lock);
auto map_iter = _page_map.find(path);
if (map_iter == _page_map.end()) {
// first time, register this to web server
_http_server->register_handler(HttpMethod::GET, path, this);
}
_page_map[path].add_callback(callback);
}
void WebPageHandler::handle(HttpRequest* req) {
// Should we render with css styles?
bool use_style = true;
auto& params = *req->params();
if (params.find("raw") != params.end()) {
use_style = false;
}
std::stringstream output;
// Append header
if (use_style) {
bootstrap_page_header(&output);
}
// Append content
// push_content(&output);
LOG(INFO) << req->debug_string();
{
boost::mutex::scoped_lock lock(_map_lock);
auto iter = _page_map.find(req->raw_path());
if (iter != _page_map.end()) {
for (auto& callback : iter->second.callbacks()) {
callback(*req->params(), &output);
}
}
}
// Append footer
if (use_style) {
bootstrap_page_footer(&output);
}
std::string str = output.str();
req->add_output_header(HttpHeaders::CONTENT_TYPE, s_html_content_type.c_str());
HttpChannel::send_reply(req, HttpStatus::OK, str);
#if 0
HttpResponse response(HttpStatus::OK, s_html_content_type, &str);
channel->send_response(response);
#endif
}
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>";
}
(*output) << NAVIGATION_BAR_SUFFIX;
}
void WebPageHandler::bootstrap_page_footer(std::stringstream* output) {
(*output) << PAGE_FOOTER;
}
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/>";
}
}
} // namespace doris