#
# Helper function for adding a test app.
#
- macro(create_test_app TEST_NAME MAIN_SRC S2 S3 S4 S5)
+ macro(create_test_app TEST_NAME MAIN_SRC S2 S3 S4 S5 S6)
set(TEST_SRCS ${MAIN_SRC})
set(TEST_HDR)
else()
list(APPEND TEST_SRCS ${S5})
endif()
+ if ("${S6}" STREQUAL "")
+ else()
+ list(APPEND TEST_SRCS ${S6})
+ endif()
if (WIN32)
list(APPEND TEST_SRCS
${WIN32_HELPERS_PATH}/getopt.c
"test-server/test-server-http.c"
"test-server/test-server-dumb-increment.c"
"test-server/test-server-mirror.c"
+ "test-server/test-server-status.c"
"test-server/test-server-echogen.c")
if (UNIX)
create_test_app(test-fuzxy "test-server/fuzxy.c"
""
""
""
+ ""
"")
endif()
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")))
"test-server/test-server-http.c"
"test-server/test-server-dumb-increment.c"
"test-server/test-server-mirror.c"
+ "test-server/test-server-status.c"
"test-server/test-server-echogen.c")
endif()
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
"test-server/test-server-http.c"
"test-server/test-server-dumb-increment.c"
"test-server/test-server-mirror.c"
+ "test-server/test-server-status.c"
"test-server/test-server-echogen.c")
endif()
if (UNIX AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
"test-server/test-server-http.c"
"test-server/test-server-dumb-increment.c"
"test-server/test-server-mirror.c"
+ "test-server/test-server-status.c"
"test-server/test-server-echogen.c")
endif()
endif()
"test-server/test-server-http.c"
"test-server/test-server-dumb-increment.c"
"test-server/test-server-mirror.c"
+ "test-server/test-server-status.c"
"test-server/test-server-echogen.c")
# Set defines for this executable only.
set_property(
# test-client
#
if (NOT LWS_WITHOUT_TEST_CLIENT)
- create_test_app(test-client "test-server/test-client.c" "" "" "" "")
+ create_test_app(test-client "test-server/test-client.c" "" "" "" "" "")
endif()
#
# test-fraggle
#
if (NOT LWS_WITHOUT_TEST_FRAGGLE)
- create_test_app(test-fraggle "test-server/test-fraggle.c" "" "" "" "")
+ create_test_app(test-fraggle "test-server/test-fraggle.c" "" "" "" "" "")
endif()
#
# test-ping
#
if (NOT LWS_WITHOUT_TEST_PING)
- create_test_app(test-ping "test-server/test-ping.c" "" "" "" "")
+ create_test_app(test-ping "test-server/test-ping.c" "" "" "" "" "")
endif()
#
# test-echo
#
if (NOT LWS_WITHOUT_TEST_ECHO)
- create_test_app(test-echo "test-server/test-echo.c" "" "" "" "")
+ create_test_app(test-echo "test-server/test-echo.c" "" "" "" "" "")
endif()
endif(NOT LWS_WITHOUT_CLIENT)
1) libuv one-per-session valgrind leak fixed
+Changes
+-------
+
+1) test server html is updated with tabs and a new live server monitoring
+feature. Input sanitization added to the js.
+
+
v1.7.1
======
--- /dev/null
+/*
+ * 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 "test-server.h"
+
+static unsigned char server_info[1024];
+static int server_info_len;
+static int current;
+static char cache[16384];
+static int cache_len;
+static struct per_session_data__lws_status *list;
+static int live_wsi;
+
+
+static void
+update_status(struct lws *wsi, struct per_session_data__lws_status *pss)
+{
+ struct per_session_data__lws_status **pp = &list;
+ int subsequent = 0;
+ char *p = cache;
+ char date[128];
+ time_t t;
+ struct tm tm;
+
+ p += snprintf(p, 512, " { %s, \"wsi\":\"%d\", \"conns\":[",
+ server_info, live_wsi);
+
+ /* render the list */
+ while (*pp) {
+ t = (*pp)->tv_established.tv_sec;
+ if (!localtime_r(&t, &tm))
+ strcpy(date, "unknown");
+ else
+ strftime(date, sizeof(date), "%F %H:%M %Z", &tm);
+ if ((p - cache) > (sizeof(cache) - 512))
+ break;
+ if (subsequent)
+ *p++ = ',';
+ subsequent = 1;
+ p += snprintf(p, sizeof(cache) - (p - cache) - 1,
+ "{\"peer\":\"%s\",\"time\":\"%s\","
+ "\"ua\":\"%s\"}",
+ (*pp)->ip, date, (*pp)->user_agent);
+ pp = &((*pp)->list);
+ }
+
+ p += sprintf(p, "]}");
+ cache_len = p - cache;
+ lwsl_err("cache_len %d\n", cache_len);
+ cache[cache_len] = '\0';
+
+ /* since we changed the list, increment the 'version' */
+ current++;
+ /* update everyone */
+ lws_callback_on_writable_all_protocol(lws_get_context(wsi),
+ lws_get_protocol(wsi));
+}
+
+
+/* lws-status protocol */
+
+int
+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;
+ char name[128], rip[128];
+ int m;
+
+ switch (reason) {
+
+ case LWS_CALLBACK_PROTOCOL_INIT:
+ /*
+ * Prepare the static server info
+ */
+ server_info_len = sprintf((char *)server_info,
+ "\"version\":\"%s\","
+ " \"hostname\":\"%s\"",
+ lws_get_library_version(),
+ lws_canonical_hostname(
+ lws_get_context(wsi)));
+ break;
+
+ case LWS_CALLBACK_ESTABLISHED:
+ /*
+ * we keep a linked list of live pss, so we can walk it
+ */
+ pss->last = 0;
+ pss->list = list;
+ list = pss;
+ live_wsi++;
+ lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name,
+ sizeof(name), rip, sizeof(rip));
+ sprintf(pss->ip, "%s (%s)", name, rip);
+ gettimeofday(&pss->tv_established, NULL);
+ strcpy(pss->user_agent, "unknown");
+ lws_hdr_copy(wsi, pss->user_agent, sizeof(pss->user_agent),
+ WSI_TOKEN_HTTP_USER_AGENT);
+ update_status(wsi, pss);
+ break;
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+ m = lws_write(wsi, (unsigned char *)cache, cache_len,
+ LWS_WRITE_TEXT);
+ if (m < server_info_len) {
+ lwsl_err("ERROR %d writing to di socket\n", m);
+ return -1;
+ }
+ break;
+
+ case LWS_CALLBACK_CLOSED:
+ /*
+ * remove ourselves from live pss list
+ */
+ lwsl_err("CLOSING pss %p ********\n", pss);
+
+ pp = &list;
+ while (*pp) {
+ if (*pp == pss) {
+ *pp = pss->list;
+ pss->list = NULL;
+ live_wsi--;
+ break;
+ }
+ pp = &((*pp)->list);
+ }
+
+ update_status(wsi, pss);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
PROTOCOL_DUMB_INCREMENT,
PROTOCOL_LWS_MIRROR,
PROTOCOL_LWS_ECHOGEN,
+ PROTOCOL_LWS_STATUS,
/* always last */
DEMO_PROTOCOL_COUNT
sizeof(struct per_session_data__echogen),
128,
},
+ {
+ "lws-status",
+ callback_lws_status,
+ sizeof(struct per_session_data__lws_status),
+ 128,
+ },
{ NULL, NULL, 0, 0 } /* terminator */
};
int wr;
};
+struct per_session_data__lws_status {
+ struct per_session_data__lws_status *list;
+ struct timeval tv_established;
+ int last;
+ char ip[270];
+ char user_agent[512];
+ const char *pos;
+ int len;
+};
+
extern int
callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
void *in, size_t len);
extern int
callback_lws_echogen(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len);
+extern int
+callback_lws_status(struct lws *wsi, enum lws_callback_reasons reason,
+ void *user, void *in, size_t len);
+
extern void
dump_handshake_info(struct lws *wsi);
<meta charset=utf-8 http-equiv="Content-Language" content="en"/>
<title>Minimal Websocket test app</title>
<style type="text/css">
- div.title { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#000000; }
+ span.title { font-size:18pt; 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;
-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; }
.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 {
<div class="content">
<div id="dumb" class="group2">
- <table>
- <tr>
- <td width=200px id=wsdi_statustd align=center class="explain">
- <div id=wsdi_status>Not initialized</div>
- </td>
-
- <td align=center><div id=number> </div></td>
- <td align=center>
- <input type=button id=offset value="Reset counter" onclick="reset();" >
- </td>
- </tr>
+ <table>
+ <tr>
+ <td id=wsdi_statustd align=center class="wsstatus">
+ <span id=wsdi_status>Websocket connection not initialized</span></td>
+ <td><span class="title">dumb increment-protocol</span></td>
+ </tr>
<tr>
- <td class="explain" colspan=3>
+ <td class="explain" colspan=2>
The incrementing number is coming from the server at 20Hz and is individual for
each connection to the server... try opening a second browser window.
<br/><br/>
to zero just this connection's number.
</td>
</tr>
+ <tr>
+ <td align=center><div id=number style="font-size:120%;"> </div></td>
+ <td align=center>
+ <input type=button id=offset value="Reset counter" onclick="reset();" >
+ </td>
+ </tr>
</table>
</div>
</div>
<div id="mirror" class="group2">
<table>
<tr>
- <td colspan="3">
- <div class="title">libwebsockets "lws-mirror-protocol"</div>
+ <td colspan=1 id=wslm_statustd align=center class="wsstatus">
+ <span id=wslm_status>Websocket connection not initialized</span>
+ </td>
+ <td>
+ <span class="title">lws-mirror-protocol</span>
+ </td>
+ </tr>
+ <tr>
+ <td colspan=2>
<div class="explain">
Use the mouse to draw on the canvas below -- all other browser windows open
on this page see your drawing in realtime and you can see any of theirs as
</td>
</tr>
<tr>
- <td>Drawing color:
+ <td colspan=2>Drawing color:
<select id="color" onchange="update_color();">
<option value=#000000>Black</option>
<option value=#0000ff>Blue</option>
<option value=#20ff20>Green</option>
<option value=#802020>Dark Red</option>
</select>
- </td>
- <td colspan=2 id=wslm_statustd align=center class="explain">
- <div id=wslm_status>Not initialized</div>
- </td>
</tr>
<tr>
- <td colspan=3 width=500 height=320>
+ <td colspan=2 width=500 height=320>
<div id="wslm_drawing" style="background:white"></div>
</td>
</tr>
<div id="ot" class="group2">
<table>
<tr>
- <td colspan=3>
-<div class="title">libwebsockets "open and close testing"</div>
+ <td>
+
</td></tr>
- <tr>
- <td align=center><input type=button id=ot_open_btn value="Open" onclick="ot_open();" ></td>
- <td align=center><input type=button id=ot_close_btn disabled value="Close" onclick="ot_close();" ></td>
- <td align=center><input type=button id=ot_req_close_btn disabled value="Request Server Close" onclick="ot_req_close();" ></td>
+ <tr><td id=ot_statustd align=center class="wsstatus">
+ <span id=ot_status>Websocket connection not initialized</span>
+ </td>
+ <td colspan=2><span class="title">Open and close testing</span></td>
</tr>
- <tr><td id=ot_statustd align=center class="explain">
- <div id=ot_status>Not initialized</div>
- </td>
-<td class="explain" colspan=2>
+ <tr>
+<td class="explain" colspan=3 style="padding:3">
To help with open and close testing, you can open and close a connection by hand using
the buttons.<br>
"<b>Close</b>" closes the connection from the browser with code 3000
and reason 'Bye!".<br>
"<b>Request Server Close</b>" sends a message asking the server to
initiate the close, which it does with code 1001 and reason "Seeya".
-</td></tr></table>
+</td></tr>
+ <tr>
+ <td align=center><input type=button id=ot_open_btn value="Open" onclick="ot_open();" ></td>
+ <td align=center><input type=button id=ot_close_btn disabled value="Close" onclick="ot_close();" ></td>
+ <td align=center><input type=button id=ot_req_close_btn disabled value="Request Server Close" onclick="ot_req_close();" ></td>
+ </tr>
+
+</table>
</div>
</div>
</div>
+
+ <div class="tab">
+ <input type="radio" id="tab-4" name="tab-group-1">
+ <label for="tab-4">Server info</label>
+
+ <div class="content">
+<div id="ot" class="group2">
+ <table>
+ <tr>
+ <td id=s_statustd align=center class="wsstatus">
+ <div id=s_status>Websocket connection not initialized</div>
+ </td>
+ <td colspan=1>
+<span class="title">Server Info</span>
+ </td>
+ </tr><tr>
+<td class="explain" colspan=2>
+This information is sent by the server over a ws[s] link and updated live
+whenever the information changes server-side.
+</td></tr>
+ <tr>
+ <td align=center colspan=2><div id=servinfo></</div></td>
+ </tr>
+ <tr>
+ <td align=center colspan=2><div id=conninfo style="border : solid 2px #e0d040; padding : 4px; width : 500px; height : 350px; overflow : auto; "></</div></td>
+ </tr>
+</table>
+</div>
+ </div>
+ </div>
</div>
</td></tr></table>
<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;
+}
+
/* BrowserDetect came from http://www.quirksmode.org/js/detect.html */
var BrowserDetect = {
try {
socket_di.onopen = function() {
document.getElementById("wsdi_statustd").style.backgroundColor = "#40ff40";
- document.getElementById("wsdi_status").innerHTML = " <b>websocket connection opened</b><br>" + socket_di.extensions;
+ document.getElementById("wsdi_status").innerHTML =
+ " <b>websocket connection opened</b><br>" +
+ san(socket_di.extensions);
}
socket_di.onmessage =function got_packet(msg) {
} catch(exception) {
alert('<p>Error' + exception);
}
+
+ var socket_status, jso, s;
+
+ if (typeof MozWebSocket != "undefined") {
+ socket_status = new MozWebSocket(get_appropriate_ws_url(),
+ "lws-status");
+ } else {
+ socket_status = new WebSocket(get_appropriate_ws_url(),
+ "lws-status");
+ }
+
+
+ try {
+ socket_status.onopen = function() {
+ document.getElementById("s_statustd").style.backgroundColor = "#40ff40";
+ document.getElementById("s_status").innerHTML =
+ " <b>websocket connection opened</b><br>" +
+ san(socket_di.extensions);
+ }
+
+ socket_status.onmessage =function got_packet(msg) {
+ jso = JSON.parse(msg.data);
+
+ document.getElementById("servinfo").innerHTML =
+ "<table><tr><td class=l>Build info</td><td>"+
+ san(jso.version) + "</td></tr>" +
+ "<tr><td class=l>Server info</td><td>" +
+ san(jso.hostname) + "</td></tr>" +
+ "</table>";
+ s="<table>";
+ var n;
+ for (n = 0; n < jso.conns.length; n++)
+ s = s + "<tr><td class=l>client " + (n + 1) +
+ "</td><td><b>" + san(jso.conns[n].peer) +
+ "</b><br>" + san(jso.conns[n].time) +
+ "<br>" + san(jso.conns[n].ua) +
+ "</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);
+ }
function reset() {
socket_di.send("reset\n");
try {
socket_ot.onopen = function() {
document.getElementById("ot_statustd").style.backgroundColor = "#40ff40";
- document.getElementById("ot_status").innerHTML = " <b>websocket connection opened</b><br>" + socket_di.extensions;
+ document.getElementById("ot_status").innerHTML = " <b>websocket connection opened</b><br>" + san(socket_di.extensions);
document.getElementById("ot_open_btn").disabled = true;
document.getElementById("ot_close_btn").disabled = false;
document.getElementById("ot_req_close_btn").disabled = false;
try {
socket_lm.onopen = function() {
document.getElementById("wslm_statustd").style.backgroundColor = "#40ff40";
- document.getElementById("wslm_status").innerHTML = " <b>websocket connection opened</b><br>" + socket_di.extensions;
+ document.getElementById("wslm_status").innerHTML =
+ " <b>websocket connection opened</b><br>" +
+ san(socket_di.extensions);
}
socket_lm.onmessage =function got_packet(msg) {