Dispatch table; server statistics

This commit is contained in:
Tony Garnock-Jones 2012-04-28 18:08:15 -04:00
parent ef5c9c061a
commit ea0d0e7681
7 changed files with 498 additions and 3 deletions

54
json.ml Normal file
View File

@ -0,0 +1,54 @@
(* Copyright 2012 Tony Garnock-Jones <tonygarnockjones@gmail.com>. *)
(* This file is part of Ocamlmsg. *)
(* Ocamlmsg is free software: you can redistribute it and/or modify it *)
(* under the terms of the GNU General Public License as published by the *)
(* Free Software Foundation, either version 3 of the License, or (at your *)
(* option) any later version. *)
(* Ocamlmsg is distributed in the hope that it will be useful, but *)
(* WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *)
(* General Public License for more details. *)
(* You should have received a copy of the GNU General Public License *)
(* along with Ocamlmsg. If not, see <http://www.gnu.org/licenses/>. *)
type t =
| Num of float
| Str of string
| Arr of t list
| Rec of (string * t) list
let escape_re = Str.regexp "\""
let escape_char s =
match s with
| "\"" -> "\\\""
| _ -> failwith ("Unexpected JSON char to escape: " ^ s)
let escape s = Str.global_substitute escape_re escape_char s
let str_to_string s =
"\"" ^ escape s ^ "\""
let rec to_string j =
match j with
| Num f ->
if float_of_int (int_of_float f) = f
then string_of_int (int_of_float f)
else string_of_float f
| Str s ->
str_to_string s
| Arr js ->
"[" ^ String.concat "," (List.map to_string js) ^ "]"
| Rec kvs ->
"{" ^ String.concat "," (List.map kv_to_string kvs) ^ "}"
and kv_to_string (k, v) =
str_to_string k ^ ":" ^ to_string v
let resp code reason j =
Httpd.resp_generic code reason
[Httpd.content_type_header_name, "application/json"]
(Httpd.Fixed (to_string j))
let resp_ok j = resp 200 "OK" j

View File

@ -17,8 +17,23 @@
open Html
let dispatch_table = ref []
let register_dispatcher (prefix, handler) =
dispatch_table := (prefix, handler) :: !dispatch_table
let handle_dynamic_req r =
Httpd.http_error_html 500 "Not yet implemented" []
let rec search_table table =
match table with
| [] ->
Httpd.http_error_html 404 "Not found"
[Html.tag "p" [] [Html.text ("No route for URL path "^r.Httpd.path)]]
| (prefix, handler) :: rest ->
if Util.starts_with r.Httpd.path prefix
then handler r
else search_table rest
in
search_table !dispatch_table
let handle_req r =
if Util.starts_with r.Httpd.path "/_"
@ -34,5 +49,17 @@ let start (s, peername) =
(Httpd.main handle_req)
(s, peername)
let boot_time = Unix.time ()
let api_server_stats r =
Json.resp_ok (Json.Rec
["connection_count", Json.Num (float_of_int !Connections.connection_count);
"boot_time", Json.Num boot_time;
"uptime", Json.Num (Unix.time () -. boot_time)])
let register_api_hooks () =
List.iter register_dispatcher
["/_/server_stats", api_server_stats]
let init () =
register_api_hooks ();
ignore (Util.create_thread "HTTP listener" None (Net.start_net "HTTP" 5678) start)

View File

@ -1,9 +1,48 @@
<html>
<head>
<title>Ocamlmsg</title>
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" type="text/css" href="ui.css">
<script type="text/javascript" src="jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="ui_main.js"></script>
</head>
<body>
<h1>Ocamlmsg</h1>
<p>Welcome to Ocamlmsg.</p>
<div id="page">
<div id="header">
<div class="mainmenu">
<ul>
<li><a href="/">Main page</a></li>
</ul>
</div>
<div class="clear"></div>
</div>
<div class="content">
<h1>Ocamlmsg</h1>
<table>
<tr>
<th>Server Status</th>
<td id="server_ok">&nbsp;</td>
</tr>
</table>
<h2>Server statistics</h2>
<table>
<tr>
<th>Connection count</th>
<td id="server_stats_connection_count"></td>
</tr>
<tr>
<th>Boot time</th>
<td id="server_stats_boot_time"></td>
</tr>
<tr>
<th>Uptime</th>
<td id="server_stats_uptime"></td>
</tr>
</table>
</div>
</div>
<script>$(document).ready(ui_main);</script>
</body>
</html>

4
web/jquery-1.7.2.min.js vendored Normal file

File diff suppressed because one or more lines are too long

347
web/style.css Normal file
View File

@ -0,0 +1,347 @@
* {
margin: 0;
padding: 0;
}
body {
background: #4B4B61;
}
body, th, td, input, textarea {
font-family: 'Lora', Georgia;
font-size: 10pt;
color: #4B4B61;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Mako', Georgia;
}
h1, h2, h3 {
margin-top: 0.5em;
color: #C11C51;
font-weight: normal;
}
h1 a, h2 a, h3 a {
color: #C11C51;
}
h1 {
font-size: 3em;
}
h2 {
font-size: 1.5em;
}
h3 {
font-size: 1.1em;
}
p, ul, ol {
margin-top: 1em;
line-height: 1.2em;
font-size: 1.1em;
}
ul ul {
margin-top: 0.5em;
font-size: 1em;
}
ul p {
font-size: 1em;
}
ul, ol {
margin-bottom: 0.5em;
margin-left: 3em;
}
blockquote {
margin-left: 3em;
margin-right: 3em;
}
a {
text-decoration: none;
color: #ff8827;
}
hr {
display: none;
}
#page {
width: 630px;
margin: 0 auto;
background: #FFFFFF;
padding: 1em;
}
#header {
padding-bottom: 2px;
border-bottom: solid #4B4B61 1px;
}
#header h2 {
float: right;
}
#header a {
font-variant: small-caps;
}
.historylinks {
text-align: center;
}
.historylinks.bottom {
margin-top: 10px;
}
.footnotes {
border-top: dashed #4B4B61 1px;
font-size: 0.8em;
margin-top: 2em;
}
.content {
margin: 0;
padding-top: 0;
padding-right: 15px;
padding-left: 15px;
padding-bottom: 15px;
min-height: 400px;
}
.content h1 {
margin: 0;
padding-top: 10px;
padding-bottom: 5px;
text-align: left;
font-size: 24pt;
}
.content .entry_footer {
text-align: right;
}
.sidebar {
float: left;
width: 150px;
padding-top: 30px;
padding-right: 10px;
padding-bottom: 0px;
padding-left: 10px;
text-align: left;
border-left: solid #A5A5AF 1px;
margin-left: -1px;
}
.sidebar ul {
margin: 0;
padding: 0;
list-style: none;
font-size: 1.5em;
color: #C11C51;
}
.sidebar li {
padding-bottom: 0.5em;
}
.sidebar li ul {
padding-top: 0.5em;
padding-left: 10px;
font-size: 0.7em;
}
.sidebar li li {
}
.sidebar li a { color: #FF8827; }
.sidebar li li a { color: #C11C51; }
.sidebar a:hover {
}
.clear {
clear: both;
}
#footer {
background: #FFFFFF;
padding-top: 0.5em;
margin-bottom: 1em;
border-top: solid #4B4B61 1px;
}
#footer p {
text-align: center;
font-size: 8pt;
color: #dd2865;
}
div.sitemap_subcategory {
margin-left: 2em;
}
.sitemap_categorylink {
font-size: 1.1em;
}
.center {
text-align: center;
}
.pinkborder {
border: solid #C11C51 1px;
padding: 8px;
}
.rightfloat, .leftfloat {
margin: 0.3em;
}
a img {
border: 0;
}
.imagecenter {
text-align: center;
margin: 0.3em;
}
.leftfloat {
float: left;
margin-left: 0px;
margin-right: 1em;
}
.rightfloat {
float: right;
margin-left: 1em;
margin-right: 0px;
}
.topspace {
margin-top: 2em;
}
table {
margin-top: 1em;
margin-bottom: 1em;
}
th, td {
padding: 0.5em;
font-size: 0.88em;
}
th {
font-weight: normal;
color: #C11C51;
text-align: right;
vertical-align: top;
}
td {
text-align: left;
vertical-align: top;
}
.mainmenu ul {
margin-top: 0;
}
.mainmenu ul li {
display: inline;
float: right;
text-align: center;
margin-left: 0.5em;
margin-right: 0.5em;
}
pre {
padding: 0.5em;
margin-top: 0.5em;
background-color: #eeeeee;
color: black;
overflow: auto;
}
sup {
line-height: 0;
}
/*---------------------------------------------------------------------------*/
.lshift_archive_dateline {
font-style: italic;
}
.lshift_archive_explanation {
text-align: center;
padding-bottom: 1em;
margin-bottom: 1em;
font-size: 0.9em;
}
.lshift_archive .right {
float: right;
}
.lshift_archive blockquote {
margin-top: 0.5em;
}
.lshift_archive table {
margin-left: auto;
margin-right: auto;
border-top: solid #C11C51 2px;
border-bottom: solid #C11C51 2px;
}
.lshift_archive td, .lshift_archive th {
font-size: 100%;
padding: 0.1em 0.2em;
}
.lshift_archive th {
text-align: center;
border-bottom: solid #C11C51 1px;
}
.lshift_archive .center {
display: block;
margin-left: auto;
margin-right: auto;
}
.lshift_archive hr {
display: block;
border: none;
border-top: dashed #4B4B61 1px;
margin-top: 2em;
}
.lshift_archive_comment_heading {
padding-top: 1em;
margin-top: 1em;
}
.lshift_archive .blog_comment_header {
color: #C11C51;
font-family: 'Lora', Georgia;
font-style: italic;
font-size: 10pt;
margin-top: 1em;
}
.lshift_archive .blog_comment {
margin-left: 2em;
border-left: solid #ff8827 1px;
padding-left: 1em;
}

9
web/ui.css Normal file
View File

@ -0,0 +1,9 @@
.server_ok {
width: 100px;
background: green;
}
.server_not_ok {
width: 100px;
background: red;
}

15
web/ui_main.js Normal file
View File

@ -0,0 +1,15 @@
function refresh_server_stats() {
$.getJSON("/_/server_stats", function (data) {
$("#server_ok")[0].className = "server_ok";
$("#server_stats_connection_count").text(data.connection_count);
$("#server_stats_boot_time").text(new Date(data.boot_time * 1000));
$("#server_stats_uptime").text(data.uptime + " seconds");
}).error(function () {
$("#server_ok")[0].className = "server_not_ok";
});
}
function ui_main() {
refresh_server_stats();
setInterval(refresh_server_stats, 5000);
}