| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset=utf-8 http-equiv="Content-Language" content="en"/> | |
| <title>Minimal Websocket test app</title> | |
| <style type="text/css"> | |
| 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; | |
| 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; } | |
| .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> | |
| <table width=600px> | |
| <tr> | |
| <td valign=middle align=center> | |
| <a href="https://libwebsockets.org"> | |
| <img src="libwebsockets.org-logo.png"></a></td><td> | |
| <section class="browser">Detected Browser: | |
| <div id=brow>...</div></section> | |
| </td> | |
| </tr> | |
| </table> | |
| </td></tr> | |
| <tr><td colspan=2 align=center> | |
| Click <a href="leaf.jpg" target="_blank">Here</a> to | |
| have the test server send a big picture by http. | |
| </td></tr> | |
| <tr><td colspan=2> | |
| <div class="tabs"> | |
| <div class="tab"> | |
| <input type="radio" id="tab-1" name="tab-group-1" checked> | |
| <label for="tab-1">Dumb Increment Demo</label> | |
| <div class="content"> | |
| <div id="dumb" class="group2"> | |
| <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=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/> | |
| The button sends a message over the websocket link to ask the server | |
| 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();" > | |
| <input type=button id=junk value="Send junk" onclick="junk();" > | |
| </td> | |
| </tr> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="tab"> | |
| <input type="radio" id="tab-2" name="tab-group-1"> | |
| <label for="tab-2">Mirror Demo</label> | |
| <div class="content"> | |
| <div id="mirror" class="group2"> | |
| <table> | |
| <tr> | |
| <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 | |
| well. | |
| <br/><br/> | |
| The lws-mirror protocol doesn't interpret what is being sent to it, it just | |
| re-sends it to every other websocket it has a connection with using that | |
| protocol, including the guy who sent the packet. | |
| <br/><br/> | |
| <b>libwebsockets-test-client</b> joins in by spamming circles on to this shared canvas when | |
| run. | |
| </div> | |
| </td> | |
| </tr> | |
| <tr> | |
| <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> | |
| </tr> | |
| <tr> | |
| <td colspan=2 width=500 height=320> | |
| <div id="wslm_drawing" style="background:white"></div> | |
| </td> | |
| </tr> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="tab"> | |
| <input type="radio" id="tab-3" name="tab-group-1"> | |
| <label for="tab-3">Close Testing</label> | |
| <div class="content"> | |
| <div id="ot" class="group2"> | |
| <table> | |
| <tr> | |
| <td> | |
| </td></tr> | |
| <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 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> | |
| <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 class="tab"> | |
| <input type="radio" id="tab-5" name="tab-group-1"> | |
| <label for="tab-5">POST</label> | |
| <div class="content"> | |
| <div id="ot" class="group2"> | |
| <table> | |
| <tr> | |
| <td colspan=1> | |
| <span class="title">POST Form testing</span> | |
| </td> | |
| </tr><tr> | |
| <td class="explain" colspan=2> | |
| This tests POST handling in lws. | |
| </td></tr> | |
| <tr> | |
| <td align=center colspan=2><div id=postinfo> | |
| <form action="formtest" method="post"> | |
| Some text:<br> | |
| <input type="text" name="Text" value="Give me some text"><br> | |
| <input type="submit" value="Send the form"> | |
| </form> | |
| </div></td> | |
| </tr> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </td></tr></table> | |
| Looking for support? <a href="https://libwebsockets.org">https://libwebsockets.org</a>, <a href="https://github.com/warmcat/libwebsockets">https://github.com/warmcat/libwebsockets</a></a><br/> | |
| Join the mailing list: <a href="https://libwebsockets.org/mailman/listinfo/libwebsockets">https://libwebsockets.org/mailman/listinfo/libwebsockets</a> | |
| </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; | |
| } | |
| /* BrowserDetect came from http://www.quirksmode.org/js/detect.html */ | |
| var BrowserDetect = { | |
| init: function () { | |
| this.browser = this.searchString(this.dataBrowser) || "An unknown browser"; | |
| this.version = this.searchVersion(navigator.userAgent) | |
| || this.searchVersion(navigator.appVersion) | |
| || "an unknown version"; | |
| this.OS = this.searchString(this.dataOS) || "an unknown OS"; | |
| }, | |
| searchString: function (data) { | |
| for (var i=0;i<data.length;i++) { | |
| var dataString = data[i].string; | |
| var dataProp = data[i].prop; | |
| this.versionSearchString = data[i].versionSearch || data[i].identity; | |
| if (dataString) { | |
| if (dataString.indexOf(data[i].subString) != -1) | |
| return data[i].identity; | |
| } | |
| else if (dataProp) | |
| return data[i].identity; | |
| } | |
| }, | |
| searchVersion: function (dataString) { | |
| var index = dataString.indexOf(this.versionSearchString); | |
| if (index == -1) return; | |
| return parseFloat(dataString.substring(index+this.versionSearchString.length+1)); | |
| }, | |
| dataBrowser: [ | |
| { | |
| string: navigator.userAgent, | |
| subString: "Chrome", | |
| identity: "Chrome" | |
| }, | |
| { string: navigator.userAgent, | |
| subString: "OmniWeb", | |
| versionSearch: "OmniWeb/", | |
| identity: "OmniWeb" | |
| }, | |
| { | |
| string: navigator.vendor, | |
| subString: "Apple", | |
| identity: "Safari", | |
| versionSearch: "Version" | |
| }, | |
| { | |
| prop: window.opera, | |
| identity: "Opera", | |
| versionSearch: "Version" | |
| }, | |
| { | |
| string: navigator.vendor, | |
| subString: "iCab", | |
| identity: "iCab" | |
| }, | |
| { | |
| string: navigator.vendor, | |
| subString: "KDE", | |
| identity: "Konqueror" | |
| }, | |
| { | |
| string: navigator.userAgent, | |
| subString: "Firefox", | |
| identity: "Firefox" | |
| }, | |
| { | |
| string: navigator.vendor, | |
| subString: "Camino", | |
| identity: "Camino" | |
| }, | |
| { // for newer Netscapes (6+) | |
| string: navigator.userAgent, | |
| subString: "Netscape", | |
| identity: "Netscape" | |
| }, | |
| { | |
| string: navigator.userAgent, | |
| subString: "MSIE", | |
| identity: "Explorer", | |
| versionSearch: "MSIE" | |
| }, | |
| { | |
| string: navigator.userAgent, | |
| subString: "Gecko", | |
| identity: "Mozilla", | |
| versionSearch: "rv" | |
| }, | |
| { // for older Netscapes (4-) | |
| string: navigator.userAgent, | |
| subString: "Mozilla", | |
| identity: "Netscape", | |
| versionSearch: "Mozilla" | |
| } | |
| ], | |
| dataOS : [ | |
| { | |
| string: navigator.platform, | |
| subString: "Win", | |
| identity: "Windows" | |
| }, | |
| { | |
| string: navigator.platform, | |
| subString: "Mac", | |
| identity: "Mac" | |
| }, | |
| { | |
| string: navigator.userAgent, | |
| subString: "iPhone", | |
| identity: "iPhone/iPod" | |
| }, | |
| { | |
| string: navigator.platform, | |
| subString: "Linux", | |
| identity: "Linux" | |
| } | |
| ] | |
| }; | |
| BrowserDetect.init(); | |
| document.getElementById("brow").textContent = " " + BrowserDetect.browser + " " | |
| + BrowserDetect.version +" " + BrowserDetect.OS +" "; | |
| 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"; | |
| } | |
| document.getElementById("number").textContent = get_appropriate_ws_url(); | |
| /* dumb increment protocol */ | |
| var socket_di; | |
| if (typeof MozWebSocket != "undefined") { | |
| socket_di = new MozWebSocket(get_appropriate_ws_url(), | |
| "dumb-increment-protocol"); | |
| } else { | |
| socket_di = new WebSocket(get_appropriate_ws_url(), | |
| "dumb-increment-protocol"); | |
| } | |
| try { | |
| socket_di.onopen = function() { | |
| document.getElementById("wsdi_statustd").style.backgroundColor = "#40ff40"; | |
| document.getElementById("wsdi_status").innerHTML = | |
| " <b>websocket connection opened</b><br>" + | |
| san(socket_di.extensions); | |
| } | |
| socket_di.onmessage =function got_packet(msg) { | |
| document.getElementById("number").textContent = msg.data + "\n"; | |
| } | |
| socket_di.onclose = function(){ | |
| document.getElementById("wsdi_statustd").style.backgroundColor = "#ff4040"; | |
| document.getElementById("wsdi_status").textContent = " websocket connection CLOSED "; | |
| } | |
| } 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_status.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"); | |
| } | |
| function junk() { | |
| for(var word = ''; word.length < 9000; word += 'a'){} | |
| socket_di.send(word); | |
| } | |
| var socket_ot; | |
| function ot_open() { | |
| if (typeof MozWebSocket != "undefined") { | |
| socket_ot = new MozWebSocket(get_appropriate_ws_url(), | |
| "dumb-increment-protocol"); | |
| } else { | |
| socket_ot = new WebSocket(get_appropriate_ws_url(), | |
| "dumb-increment-protocol"); | |
| } | |
| try { | |
| socket_ot.onopen = function() { | |
| document.getElementById("ot_statustd").style.backgroundColor = "#40ff40"; | |
| 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; | |
| } | |
| socket_ot.onclose = function(e){ | |
| document.getElementById("ot_statustd").style.backgroundColor = "#ff4040"; | |
| document.getElementById("ot_status").textContent = " websocket connection CLOSED, code: " + e.code + | |
| ", reason: " + e.reason; | |
| document.getElementById("ot_open_btn").disabled = false; | |
| document.getElementById("ot_close_btn").disabled = true; | |
| document.getElementById("ot_req_close_btn").disabled = true; | |
| } | |
| } catch(exception) { | |
| alert('<p>Error' + exception); | |
| } | |
| } | |
| /* browser will close the ws in a controlled way */ | |
| function ot_close() { | |
| socket_ot.close(3000, "Bye!"); | |
| } | |
| /* we ask the server to close the ws in a controlled way */ | |
| function ot_req_close() { | |
| socket_ot.send("closeme\n"); | |
| } | |
| /* lws-mirror protocol */ | |
| var down = 0; | |
| var no_last = 1; | |
| var last_x = 0, last_y = 0; | |
| var ctx; | |
| var socket_lm; | |
| var color = "#000000"; | |
| if (typeof MozWebSocket != "undefined") { | |
| socket_lm = new MozWebSocket(get_appropriate_ws_url(), | |
| "lws-mirror-protocol"); | |
| } else { | |
| socket_lm = new WebSocket(get_appropriate_ws_url(), | |
| "lws-mirror-protocol"); | |
| } | |
| try { | |
| socket_lm.onopen = function() { | |
| document.getElementById("wslm_statustd").style.backgroundColor = "#40ff40"; | |
| document.getElementById("wslm_status").innerHTML = | |
| " <b>websocket connection opened</b><br>" + | |
| san(socket_lm.extensions); | |
| } | |
| socket_lm.onmessage =function got_packet(msg) { | |
| j = msg.data.split(';'); | |
| f = 0; | |
| while (f < j.length - 1) { | |
| i = j[f].split(' '); | |
| if (i[0] == 'd') { | |
| ctx.strokeStyle = i[1]; | |
| ctx.beginPath(); | |
| ctx.moveTo(+(i[2]), +(i[3])); | |
| ctx.lineTo(+(i[4]), +(i[5])); | |
| ctx.stroke(); | |
| } | |
| if (i[0] == 'c') { | |
| ctx.strokeStyle = i[1]; | |
| ctx.beginPath(); | |
| ctx.arc(+(i[2]), +(i[3]), +(i[4]), 0, Math.PI*2, true); | |
| ctx.stroke(); | |
| } | |
| f++; | |
| } | |
| } | |
| socket_lm.onclose = function(){ | |
| document.getElementById("wslm_statustd").style.backgroundColor = "#ff4040"; | |
| document.getElementById("wslm_status").textContent = " websocket connection CLOSED "; | |
| } | |
| } catch(exception) { | |
| alert('<p>Error' + exception); | |
| } | |
| var canvas = document.createElement('canvas'); | |
| canvas.height = 300; | |
| canvas.width = 480; | |
| ctx = canvas.getContext("2d"); | |
| document.getElementById('wslm_drawing').appendChild(canvas); | |
| canvas.addEventListener('mousemove', ev_mousemove, false); | |
| canvas.addEventListener('mousedown', ev_mousedown, false); | |
| canvas.addEventListener('mouseup', ev_mouseup, false); | |
| offsetX = offsetY = 0; | |
| element = canvas; | |
| if (element.offsetParent) { | |
| do { | |
| offsetX += element.offsetLeft; | |
| offsetY += element.offsetTop; | |
| } while ((element = element.offsetParent)); | |
| } | |
| function update_color() { | |
| color = document.getElementById("color").value; | |
| } | |
| function ev_mousedown (ev) { | |
| down = 1; | |
| } | |
| function ev_mouseup(ev) { | |
| down = 0; | |
| no_last = 1; | |
| } | |
| function ev_mousemove (ev) { | |
| var x, y; | |
| if (ev.offsetX) { | |
| x = ev.offsetX; | |
| y = ev.offsetY; | |
| } else { | |
| x = ev.layerX - offsetX; | |
| y = ev.layerY - offsetY; | |
| } | |
| if (!down) | |
| return; | |
| if (no_last) { | |
| no_last = 0; | |
| last_x = x; | |
| last_y = y; | |
| return; | |
| } | |
| socket_lm.send("d " + color + " " + last_x + " " + last_y + " " + x + ' ' + y + ';'); | |
| last_x = x; | |
| last_y = y; | |
| } | |
| </script> | |
| </body> | |
| </html> |