plugin lws server status
authorAndy Green <andy@warmcat.com>
Fri, 15 Apr 2016 06:01:29 +0000 (14:01 +0800)
committerAndy Green <andy@warmcat.com>
Mon, 18 Apr 2016 12:05:43 +0000 (20:05 +0800)
Signed-off-by: Andy Green <andy@warmcat.com>
14 files changed:
CMakeLists.txt
README.lwsws.md
lib/context.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/output.c
lib/private-libwebsockets.h
lib/server.c
lws_config.h.in
plugins/protocol_dumb_increment.c
plugins/protocol_lws_mirror.c
plugins/protocol_lws_server_status.c [new file with mode: 0644]
plugins/protocol_lws_status.c
plugins/server-status.html [new file with mode: 0644]

index 3469402..6612601 100644 (file)
@@ -95,13 +95,14 @@ option(LWS_WITH_HTTP_PROXY "Support for rewriting HTTP proxying" OFF)
 option(LWS_WITH_LWSWS "Libwebsockets Webserver" OFF)
 option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions" OFF)
 option(LWS_WITH_ACCESS_LOG "Support generating Apache-compatible access logs" OFF)
-
+option(LWS_WITH_SERVER_STATUS "Support json + jscript server monitoring" OFF)
 
 if (LWS_WITH_LWSWS)
  message(STATUS "LWS_WITH_LWSWS --> Enabling LWS_WITH_PLUGINS and LWS_WITH_LIBUV")
  set(LWS_WITH_PLUGINS 1)
  set(LWS_WITH_LIBUV 1)
  set(LWS_WITH_ACCESS_LOG 1)
+ set(LWS_WITH_SERVER_STATUS 1)
 endif()
 
 if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV)
@@ -1199,6 +1200,10 @@ if (NOT LWS_WITHOUT_TESTAPPS)
                              "plugins/protocol_lws_mirror.c")
                create_plugin(protocol_lws_status
                              "plugins/protocol_lws_status.c")
+if (LWS_WITH_SERVER_STATUS)
+               create_plugin(protocol_lws_server_status
+                             "plugins/protocol_lws_server_status.c")
+endif()
        endif(LWS_WITH_PLUGINS AND LWS_WITH_SHARED)
 
        #
@@ -1399,6 +1404,11 @@ if (LWS_WITH_PLUGINS)
                PERMISSIONS  OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE OWNER_READ GROUP_READ WORLD_READ
                DESTINATION share/libwebsockets-test-server/plugins
                COMPONENT plugins)
+if (LWS_WITH_SERVER_STATUS)
+       install(FILES plugins/server-status.html
+               DESTINATION share/libwebsockets-test-server/server-status
+                       COMPONENT examples)
+endif()
 endif()
 
 # Install the LibwebsocketsConfig.cmake and LibwebsocketsConfigVersion.cmake
@@ -1466,6 +1476,7 @@ message(" LWS_WITH_HTTP_PROXY = ${LWS_WITH_HTTP_PROXY}")
 message(" LIBHUBBUB_LIBRARIES = ${LIBHUBBUB_LIBRARIES}")
 message(" PLUGINS = ${PLUGINS_LIST}")
 message(" LWS_WITH_ACCESS_LOG = ${LWS_WITH_ACCESS_LOG}")
+message(" LWS_WITH_SERVER_STATUS = ${LWS_WITH_SERVER_STATUS}")
 message("---------------------------------------------------------------------")
 
 # These will be available to parent projects including libwebsockets using add_subdirectory()
index 6b82127..15a8bd3 100644 (file)
@@ -153,6 +153,11 @@ Vhosts can select which plugins they want to offer and give them per-vhost setti
 
 ```
 
+The "x":"y" parameters like "status":"ok" are made available to the protocol during its per-vhost
+LWS_CALLBACK_PROTOCOL_INIT (@in is a pointer to a linked list of struct lws_protocol_vhost_options
+containing the name and value pointers).
+
+
 Other vhost options
 -------------------
 
@@ -265,3 +270,28 @@ dumb increment, mirror and status protocol plugins are provided as examples.
 
 
 
+lws-server-status plugin
+------------------------
+
+One provided protocol can be used to monitor the server status.
+
+Enable the protocol like this on a vhost's ws-protocols section
+
+       "lws-server-status": {
+         "status": "ok",
+         "update-ms": "5000"
+       }
+
+"update-ms" is used to control how often updated JSON is sent on a ws link.
+
+And map the provided HTML into the vhost in the mounts section
+
+       {
+        "mountpoint": "/server-status",
+        "origin": "file:///usr/local/share/libwebsockets-test-server/server-status",
+        "default": "server-status.html"
+       }
+
+You might choose to put it on its own vhost which has "interface": "lo", so it's not
+externally visible.
+
index e746e8c..8ee5ffa 100644 (file)
@@ -440,7 +440,6 @@ lws_create_context(struct lws_context_creation_info *info)
        struct rlimit rt;
 #endif
 
-
        lwsl_notice("Initial logging level %d\n", log_level);
        lwsl_notice("Libwebsockets version: %s\n", library_version);
 #if LWS_POSIX
@@ -471,6 +470,8 @@ lws_create_context(struct lws_context_creation_info *info)
                lwsl_err("No memory for websocket context\n");
                return NULL;
        }
+
+       context->time_up = time(NULL);
 #ifndef LWS_NO_DAEMONIZE
        if (pid_daemon) {
                context->started_with_parent = pid_daemon;
index 22368a2..58d0006 100644 (file)
@@ -2234,6 +2234,8 @@ lws_access_log(struct lws *wsi)
 }
 #endif
 
+#ifdef LWS_WITH_SERVER_STATUS
+
 LWS_EXTERN int
 lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 {
@@ -2254,10 +2256,15 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
        buf += snprintf(buf, end - buf,
                        "{\n \"name\":\"%s\",\n"
                        " \"port\":\"%d\",\n"
-                       " \"use-ssl\":\"%d\",\n"
+                       " \"use_ssl\":\"%d\",\n"
                        " \"sts\":\"%d\",\n"
                        " \"rx\":\"%lu\",\n"
-                       " \"tx\":\"%lu\",\n",
+                       " \"tx\":\"%lu\",\n"
+                       " \"conn\":\"%lu\",\n"
+                       " \"trans\":\"%lu\",\n"
+                       " \"ws_upg\":\"%lu\",\n"
+                       " \"http2_upg\":\"%lu\""
+                       ,
                        vh->name, vh->listen_port,
 #ifdef LWS_OPENSSL_SUPPORT
                        vh->use_ssl,
@@ -2265,7 +2272,8 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
                        0,
 #endif
                        !!(vh->options & LWS_SERVER_OPTION_STS),
-                       vh->rx, vh->tx
+                       vh->rx, vh->tx, vh->conn, vh->trans, vh->ws_upgrades,
+                       vh->http2_upgrades
        );
 
        if (vh->mount_list) {
@@ -2277,7 +2285,7 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
                                buf += snprintf(buf, end - buf, ",");
                        buf += snprintf(buf, end - buf,
                                        "\n  {\n   \"mountpoint\":\"%s\",\n"
-                                       "  \"origin\":\"%s%s\""
+                                       "  \"origin\":\"%s%s\"\n"
                                        ,
                                        m->mountpoint,
                                        prots[m->origin_protocol],
@@ -2316,3 +2324,34 @@ lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len)
 
        return buf - orig;
 }
+
+
+LWS_EXTERN LWS_VISIBLE int
+lws_json_dump_context(const struct lws_context *context, char *buf, int len)
+{
+       char *orig = buf, *end = buf + len - 1, first = 1;
+       struct lws_vhost *vh = context->vhost_list;
+       time_t t = time(NULL);
+
+       buf += snprintf(buf, end - buf, "{ "
+                                       "\"uptime\":\"%ld\",\n"
+                                       "\"wsi_alive\":\"%d\",\n"
+                                       "\"vhosts\":[\n ",
+                                       (unsigned long)(t - context->time_up),
+                                       context->count_wsi_allocated);
+
+       while (vh) {
+               if (!first)
+                       if(buf != end)
+                               *buf++ = ',';
+               buf += lws_json_dump_vhost(vh, buf, end - buf);
+               first = 0;
+               vh = vh->vhost_next;
+       }
+
+       buf += snprintf(buf, end - buf, "]}\n ");
+
+       return buf - orig;
+}
+
+#endif
index 0f6687d..60bbe4f 100644 (file)
@@ -1566,6 +1566,9 @@ lws_write_http_mount(struct lws_http_mount *next, struct lws_http_mount **res,
 LWS_EXTERN int
 lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len);
 
+LWS_EXTERN int
+lws_json_dump_context(const struct lws_context *context, char *buf, int len);
+
 LWS_VISIBLE LWS_EXTERN void
 lws_set_log_level(int level,
                  void (*log_emit_function)(int level, const char *line));
index c3fc2c8..451ca0d 100644 (file)
@@ -251,6 +251,8 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
 #ifdef LWS_WITH_ACCESS_LOG
        wsi->access_log.sent += len;
 #endif
+       if (wsi->vhost)
+               wsi->vhost->tx += len;
 
        if (wsi->state == LWSS_ESTABLISHED && wsi->u.ws.tx_draining_ext) {
                /* remove us from the list */
@@ -634,8 +636,11 @@ lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, int len)
        int n;
 
        n = recv(wsi->sock, (char *)buf, len, 0);
-       if (n > 0)
+       if (n > 0) {
+               if (wsi->vhost)
+                       wsi->vhost->rx += n;
                return n;
+       }
 #if LWS_POSIX
        if (LWS_ERRNO == LWS_EAGAIN ||
            LWS_ERRNO == LWS_EWOULDBLOCK ||
index f91a5c1..60c038c 100644 (file)
@@ -668,7 +668,7 @@ struct lws_vhost {
 #ifndef LWS_NO_EXTENSIONS
        const struct lws_extension *extensions;
 #endif
-       unsigned long rx, tx;
+       unsigned long rx, tx, conn, trans, ws_upgrades, http2_upgrades;
 
        int listen_port;
        unsigned int http_proxy_port;
@@ -698,6 +698,7 @@ struct lws_vhost {
 
 struct lws_context {
        time_t last_timeout_check_s;
+       time_t time_up;
        struct lws_plat_file_ops fops;
        struct lws_context_per_thread pt[LWS_MAX_SMP];
 #ifdef _WIN32
@@ -1255,6 +1256,7 @@ struct lws {
        unsigned int socket_is_permanently_unusable:1;
        unsigned int rxflow_change_to:2;
        unsigned int more_rx_waiting:1; /* has to live here since ah may stick to end */
+       unsigned int conn_stat_done:1;
 #ifdef LWS_WITH_ACCESS_LOG
        unsigned int access_log_pending:1;
 #endif
index 7615908..41e6339 100644 (file)
@@ -700,6 +700,23 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
                lwsl_debug("%s: wsi->more_rx_waiting=%d\n", __func__,
                                wsi->more_rx_waiting);
 
+               /* select vhost */
+
+               if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
+                       struct lws_vhost *vhost = lws_select_vhost(
+                               context, wsi->vhost->listen_port,
+                               lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
+
+                       if (vhost)
+                               wsi->vhost = vhost;
+               }
+
+               wsi->vhost->trans++;
+               if (!wsi->conn_stat_done) {
+                       wsi->vhost->conn++;
+                       wsi->conn_stat_done = 1;
+               }
+
                wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
                lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
 
@@ -708,12 +725,14 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
                if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
                        if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
                                        "websocket")) {
+                               wsi->vhost->ws_upgrades++;
                                lwsl_info("Upgrade to ws\n");
                                goto upgrade_ws;
                        }
 #ifdef LWS_USE_HTTP2
                        if (!strcasecmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE),
                                        "h2c")) {
+                               wsi->vhost->http2_upgrades++;
                                lwsl_info("Upgrade to h2c\n");
                                goto upgrade_h2c;
                        }
@@ -728,17 +747,6 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
                lwsl_info("No upgrade\n");
                ah = wsi->u.hdr.ah;
 
-               /* select vhost */
-
-               if (lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {
-                       struct lws_vhost *vhost = lws_select_vhost(
-                               context, wsi->vhost->listen_port,
-                               lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
-
-                       if (vhost)
-                               wsi->vhost = vhost;
-               }
-
                lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
                wsi->state = LWSS_HTTP;
                wsi->u.http.fd = LWS_INVALID_FILE;
index a9298ff..87a0c6d 100644 (file)
@@ -94,6 +94,7 @@
 
 /* Http access log support */
 #cmakedefine LWS_WITH_ACCESS_LOG
+#cmakedefine LWS_WITH_SERVER_STATUS
 
 /* Maximum supported service threads */
 #define LWS_MAX_SMP ${LWS_MAX_SMP}
index fd4eb29..5c1a343 100644 (file)
@@ -59,7 +59,6 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
 
        switch (reason) {
        case LWS_CALLBACK_PROTOCOL_INIT:
-               lwsl_notice("%s: pvo %p\n", __func__, in);
                vhd = lws_protocol_vh_priv_zalloc(lws_vhost_get(wsi),
                                lws_protocol_get(wsi),
                                sizeof(struct per_vhost_data__dumb_increment));
@@ -73,6 +72,8 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
                break;
 
        case LWS_CALLBACK_PROTOCOL_DESTROY:
+               if (!vhd)
+                       break;
                uv_timer_stop(&vhd->timeout_watcher);
                break;
 
index 05614db..4b9319c 100644 (file)
@@ -66,6 +66,8 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
                break;
 
        case LWS_CALLBACK_PROTOCOL_DESTROY: /* per vhost */
+               if (!v)
+                       break;
                lwsl_info("%s: mirror protocol cleaning up %p\n", __func__, v);
                for (n = 0; n < ARRAY_SIZE(v->ringbuffer); n++)
                        if (v->ringbuffer[n].payload) {
diff --git a/plugins/protocol_lws_server_status.c b/plugins/protocol_lws_server_status.c
new file mode 100644 (file)
index 0000000..588ada5
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * libwebsockets-test-server - libwebsockets test implementation
+ *
+ * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * The person who associated a work with this deed has dedicated
+ * the work to the public domain by waiving all of his or her rights
+ * to the work worldwide under copyright law, including all related
+ * and neighboring rights, to the extent allowed by law. You can copy,
+ * modify, distribute and perform the work, even for commercial purposes,
+ * all without asking permission.
+ *
+ * The test apps are intended to be adapted for use in your code, which
+ * may be proprietary.  So unlike the library itself, they are licensed
+ * Public Domain.
+ */
+#include "../lib/libwebsockets.h"
+#include <string.h>
+
+#define LWS_SS_VERSIONS 3
+
+struct lws_ss_dumps {
+       char buf[32768];
+       int length;
+};
+
+static struct lws_ss_dumps d[LWS_SS_VERSIONS];
+static int last_dump;
+static uv_timer_t timeout_watcher;
+static struct lws_context *context;
+static int tow_flag;
+
+struct per_session_data__server_status {
+       int ver;
+       int pos;
+};
+
+static const struct lws_protocols protocols[];
+
+static void
+uv_timeout_cb_server_status(uv_timer_t *w
+#if UV_VERSION_MAJOR == 0
+               , int status
+#endif
+)
+{
+       int n;
+
+       last_dump = (last_dump + 1) % LWS_SS_VERSIONS;
+       n = lws_json_dump_context(context, d[last_dump].buf + LWS_PRE,
+                       sizeof(d[0].buf) - LWS_PRE);
+       d[last_dump].length = n;
+
+       lws_callback_on_writable_all_protocol(context, &protocols[0]);
+}
+
+static int
+callback_lws_server_status(struct lws *wsi, enum lws_callback_reasons reason,
+                          void *user, void *in, size_t len)
+{
+       struct lws_protocol_vhost_options *pvo =
+                       (struct lws_protocol_vhost_options *)in;
+       int m, period = 1000;
+
+       switch (reason) {
+
+       case LWS_CALLBACK_ESTABLISHED:
+               lwsl_info("%s: LWS_CALLBACK_ESTABLISHED\n", __func__);
+               lws_callback_on_writable(wsi);
+               break;
+
+       case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
+               if (tow_flag)
+                       break;
+               while (pvo) {
+                       if (!strcmp(pvo->name, "update-ms"))
+                               period = atoi(pvo->value);
+                       pvo = pvo->next;
+               }
+               context = lws_get_context(wsi);
+               uv_timer_init(lws_uv_getloop(context, 0), &timeout_watcher);
+               uv_timer_start(&timeout_watcher,
+                               uv_timeout_cb_server_status, 2000, period);
+               tow_flag = 1;
+               break;
+
+       case LWS_CALLBACK_PROTOCOL_DESTROY: /* per vhost */
+               if (!tow_flag)
+                       break;
+               uv_timer_stop(&timeout_watcher);
+               tow_flag = 0;
+               break;
+
+       case LWS_CALLBACK_SERVER_WRITEABLE:
+               m = lws_write(wsi, (unsigned char *)
+                               d[last_dump].buf + LWS_PRE, d[last_dump].length,
+                             LWS_WRITE_TEXT);
+               if (m < 0)
+                       return -1;
+               break;
+
+       case LWS_CALLBACK_RECEIVE:
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const struct lws_protocols protocols[] = {
+       {
+               "lws-server-status",
+               callback_lws_server_status,
+               sizeof(struct per_session_data__server_status),
+               1024,
+       },
+};
+
+LWS_VISIBLE int
+init_protocol_lws_server_status(struct lws_context *context,
+                            struct lws_plugin_capability *c)
+{
+       if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
+               lwsl_err("Plugin API %d, library API %d",
+                        LWS_PLUGIN_API_MAGIC, c->api_magic);
+               return 1;
+       }
+
+       c->protocols = protocols;
+       c->count_protocols = ARRAY_SIZE(protocols);
+       c->extensions = NULL;
+       c->count_extensions = 0;
+
+       return 0;
+}
+
+LWS_VISIBLE int
+destroy_protocol_lws_server_status(struct lws_context *context)
+{
+       return 0;
+}
+
index 76d2563..38281b7 100644 (file)
@@ -102,8 +102,7 @@ callback_lws_status(struct lws *wsi, enum lws_callback_reasons reason,
                    void *user, void *in, size_t len)
 {
        struct per_session_data__lws_status *pss =
-                       (struct per_session_data__lws_status *)user,
-                       **pp;
+                       (struct per_session_data__lws_status *)user, **pp;
        char name[128], rip[128];
        int m;
 
diff --git a/plugins/server-status.html b/plugins/server-status.html
new file mode 100644 (file)
index 0000000..0ecebb4
--- /dev/null
@@ -0,0 +1,219 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset=utf-8 http-equiv="Content-Language" content="en"/>
+ <title>LWS Server Status</title>
+<style type="text/css">
+       span.title { font-size:18pt; font: Arial; font-weight:normal;
+                       text-align:center; color:#000000; }
+       span.mount { font-size:10pt; font: Arial; font-weight:normal;
+                       text-align:center; color:#000000; }
+       .browser { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
+       .group2 { vertical-align:middle;
+               text-align:center;
+               background:#f0f0e0; 
+               padding:12px; 
+               -webkit-border-radius:10px; 
+               -moz-border-radius:10px;
+               border-radius:10px; }
+       .explain { vertical-align:middle;
+               text-align:center;
+               background:#f0f0c0; padding:12px;
+               -webkit-border-radius:10px;
+               -moz-border-radius:10px;
+               border-radius:10px;
+               color:#404000; }
+       td.wsstatus { vertical-align:middle; width:200px; height:50px;
+               text-align:center;
+               background:#f0f0c0; padding:6px;
+               -webkit-border-radius:8px;
+               -moz-border-radius:8px;
+               border-radius:8px;
+               color:#404000; }
+       td.l { vertical-align:middle;
+               text-align:center;
+               background:#d0d0b0; 
+               padding:3px; 
+               -webkit-border-radius:3px; 
+               -moz-border-radius:3px;
+               border-radius:3px; }
+       td.c { vertical-align:middle;
+               text-align:center;
+               background:#c0c0a0; 
+               padding:3px; 
+               -webkit-border-radius:3px; 
+               -moz-border-radius:3px;
+               border-radius:3px; }
+       td.t { vertical-align:middle;
+               text-align:center;
+               background:#e0e0c0; 
+               padding:3px; 
+               -webkit-border-radius:3px; 
+               -moz-border-radius:3px;
+               border-radius:3px; }
+       .content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
+       .canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
+.tabs {
+  position: relative;   
+  min-height: 750px; /* This part sucks */
+  clear: both;
+  margin: 25px 0;
+}
+.tab {
+  float: left;
+}
+.tab label {
+  background: #eee; 
+  padding: 10px; 
+  border: 1px solid #ccc; 
+  margin-left: -1px; 
+  position: relative;
+  left: 1px; 
+}
+.tab [type=radio] {
+  display: none;   
+}
+.content {
+  position: absolute;
+  top: 28px;
+  left: 0;
+  background: white;
+  right: 0;
+  bottom: 0;
+  padding: 20px;
+  border: 1px solid #ccc; 
+}
+[type=radio]:checked ~ label {
+  background: white;
+  border-bottom: 1px solid white;
+  z-index: 2;
+}
+[type=radio]:checked ~ label ~ .content {
+  z-index: 1;
+}
+</style>
+</head>
+
+<body>
+<header></header>
+<article>
+
+<table><tr><td align=center>
+<div id="conninfo">...</div>
+</td></tr>
+
+
+</table>
+
+</article>
+
+<script>
+
+/*
+ * We display untrusted stuff in html context... reject anything
+ * that has HTML stuff in it
+ */
+
+function san(s)
+{
+       if (s.search("<") != -1)
+               return "invalid string";
+       
+       return s;
+}
+
+       var pos = 0;
+
+function get_appropriate_ws_url()
+{
+       var pcol;
+       var u = document.URL;
+
+       /*
+        * We open the websocket encrypted if this page came on an
+        * https:// url itself, otherwise unencrypted
+        */
+
+       if (u.substring(0, 5) == "https") {
+               pcol = "wss://";
+               u = u.substr(8);
+       } else {
+               pcol = "ws://";
+               if (u.substring(0, 4) == "http")
+                       u = u.substr(7);
+       }
+
+       u = u.split('/');
+
+       /* + "/xxx" bit is for IE10 workaround */
+
+       return pcol + u[0] + "/xxx";
+}
+
+
+       var socket_status, jso, s;
+
+       if (typeof MozWebSocket != "undefined") {
+               socket_status = new MozWebSocket(get_appropriate_ws_url(),
+                                  "lws-server-status");
+       } else {
+               socket_status = new WebSocket(get_appropriate_ws_url(),
+                                  "lws-server-status");
+       }
+
+
+       try {
+               socket_status.onopen = function() {
+               } 
+
+               socket_status.onmessage =function got_packet(msg) {
+                       document.getElementById("conninfo").innerHTML = "<pre>"+msg.data+"</pre>";
+                       jso = JSON.parse(msg.data);
+                       
+                       s="<table><tr><td class=\"c\">" +
+                         "Context</td><td>Uptime: " + san(jso.uptime) + "<br>" +
+                         "Current wsi alive: " + san(jso.wsi_alive) +
+                         "</td></tr>";
+                       var n;
+                       for (n = 0; n < jso.vhosts.length; n++) {
+                               s = s + "<tr><td class=\"l\">vhost " + (n + 1) +
+                               "</td><td><b>" + san(jso.vhosts[n].name) + ":" +
+                               san(jso.vhosts[n].port) +
+                               "</b><br>" +
+                               "ssl " + san(jso.vhosts[n].use_ssl) + ", " +
+                               "sts " + san(jso.vhosts[n].sts) + "<br>" +
+                               "rx " + san(jso.vhosts[n].rx) + ", " +
+                               "tx " + san(jso.vhosts[n].tx) + "<br>" +
+                               "total connections " + san(jso.vhosts[n].conn) + ", " +
+                               "total http transactions " + san(jso.vhosts[n].trans) + "<br>" +
+                               "Upgrades to ws " + san(jso.vhosts[n].ws_upg) + ", " +
+                               "to http2 " + san(jso.vhosts[n].http2_upg) + "<br>" +
+                               "<table><tr><td class=t colspan=2>Mounts</td></tr>";
+                               
+                               var m;
+                               for (m = 0; m < jso.vhosts[n].mounts.length; m++) {
+                                       s = s + "<tr><td>";
+                                       s = s + san(jso.vhosts[n].mounts[m].mountpoint) +
+                                               "</td><td>" +
+                                               san(jso.vhosts[n].mounts[m].origin);
+                                       s = s + "</td></tr>"
+                               }
+                               s = s + "</table>";
+                               s = s + "</td></tr>";
+                       }
+                       s = s + "</table>";
+                       
+                       document.getElementById("conninfo").innerHTML = s;
+               } 
+
+               socket_status.onclose = function(){
+                       document.getElementById("s_statustd").style.backgroundColor = "#ff4040";
+                       document.getElementById("s_status").textContent = " websocket connection CLOSED ";
+               }
+       } catch(exception) {
+               alert('<p>Error' + exception);  
+       }
+</script>
+
+</body>
+</html>