Added websocket examples for the server and client.

This commit is contained in:
2016-10-13 22:01:50 +02:00
parent 1e4203111f
commit 01a9d02586
18 changed files with 10562 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
note
description: "ws_client application root class"
date: "$Date$"
revision: "$Revision$"
class
APPLICATION
inherit
SHARED_EXECUTION_ENVIRONMENT
create
make
feature {NONE} -- Initialization
make
-- Run application.
local
ws_client: EXAMPLE_WS_CLIENT
do
-- create ws_client.make_with_port ("wss://echo.websocket.org", 443, Void)
-- if ws_client.is_ssl_supported and ws_client.is_tunneled then
-- ws_client.set_ssl_certificate_file ("ca.crt")
-- ws_client.set_ssl_key_file ("ca.key")
-- else
-- -- Not supported!
-- end
create ws_client.make_with_port ("ws://echo.websocket.org", 80, Void)
-- create ws_client.make_with_port ("ws://127.0.0.1", 9090, Void)
ws_client.launch
ws_client.join_all
execution_environment.sleep (5_000_000)
end
end

View File

@@ -0,0 +1,78 @@
note
description: "Summary description for {EXAMPLE_WS_CLIENT}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
EXAMPLE_WS_CLIENT
inherit
WEB_SOCKET_CLIENT
create
make, make_with_port
feature -- Initialization
make (a_uri: STRING; a_protocols: detachable LIST [STRING])
do
initialize (a_uri, a_protocols)
create implementation.make (create {WEB_SOCKET_NULL_CLIENT}, a_uri)
end
make_with_port (a_uri: STRING; a_port: INTEGER; a_protocols: detachable LIST [STRING])
do
initialize_with_port (a_uri, a_port, a_protocols)
create implementation.make (create {WEB_SOCKET_NULL_CLIENT}, a_uri)
end
feature -- Access
count: INTEGER
feature -- Event
on_open (a_message: STRING)
do
print (a_message)
print ("%NProtocol:" + protocol)
on_text_message (a_message)
end
on_text_message (a_message: STRING)
local
l_message: STRING
do
if count <= 10 then
print ("%NMessage is %"" + a_message + "%".%N")
print ("%NCount:" + count.out)
create l_message.make_empty
l_message.append ("mesg#" + count.out)
send (l_message)
count := count + 1
else -- Send close initiated by the client
close (1001)
end
end
on_binary_message (a_message: STRING)
do
send_binary (a_message)
end
on_close (a_code: INTEGER; a_reason: STRING)
do
ready_state.set_state ({WEB_SOCKET_READY_STATE}.closed)
end
on_error (a_error: STRING)
do
end
connection: like socket
do
Result := socket
end
end

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="ws_client" uuid="AF6EDC56-D7B4-4E1F-A62B-40EBED3D93DF">
<target name="common">
<file_rule>
<exclude>/.git$</exclude>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" is_obsolete_routine_type="true" void_safety="transitional">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="web_socket_client" location="..\..\web_socket_client-safe.ecf" readonly="false"/>
<cluster name="ws_client" location=".\" recursive="true"/>
</target>
<target name="ws_client" extends="common">
<root class="APPLICATION" feature="make"/>
<setting name="concurrency" value="thread"/>
</target>
<target name="ws_client_ssl" extends="ws_client">
<variable name="net_ssl_enabled" value="true"/>
</target>
</system>

View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>WebSocket prototype</title>
<script src="js/jquery-1.10.1.js"></script>
<script src="js/main.js"></script>
</head>
<body>
<h1>WebSocket prototype</h1>
<button id="run">Run</button>
<p>Output :</p>
<div id=console>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
$(function() {
var wsEchoURI = "ws://localhost:9999/";
var websocket;
function printConsole(text) {
$("#console").append($(document.createElement("p")).html(text));
}
function onWSOpen(evt) {
printConsole("WebSocket open");
if(websocket.readyState == websocket.OPEN) {
printConsole("WebSocket ready");
}
}
function onWSClose(evt) {
printConsole("WebSocket closed");
printConsole(websocket.readyState);
}
function onWSMessage(evt) {
printConsole(evt.data);
}
function onWSError(evt) {
printConsole("An error occured in the WebSocket : " + evt.data);
}
$("#run").click(function buttonRun() {
printConsole("Connection");
websocket = new WebSocket(wsEchoURI);
websocket.onopen = function(evt) { onWSOpen(evt) };
websocket.onclose = function(evt) { onWSClose(evt) };
websocket.onmessage = function(evt) { onWSMessage(evt) };
websocket.onerror = function(evt) { onWSError(evt) };
});
});

View File

@@ -0,0 +1,40 @@
note
description: "Websocket server."
date: "$Date$"
revision: "$Revision$"
class
APPLICATION
inherit
WEB_SOCKET_SERVICE [APPLICATION_EXECUTION]
redefine
set_options
end
create
make_and_launch
feature {NONE} -- Initialization
set_options (opts: WEB_SOCKET_SERVICE_OPTIONS)
do
opts.set_is_verbose (True) -- For debug purpose
opts.set_verbose_level ("debug")
opts.set_ssl_enabled (True) -- If SSL is supported
opts.set_ssl_ca_crt ("C:\OpenSSL-Win64\bin\ca.crt") -- Change to use your own crt file.
opts.set_ssl_ca_key ("C:\OpenSSL-Win64\bin\ca.key") -- Change to use your own key file.
opts.set_port (default_port_number)
end
feature -- Access
default_port_number: INTEGER = 9090
note
copyright: "2011-2016, Javier Velilla, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,173 @@
note
description: "[
Implementation of {WEB_SOCKET_EXECUTION} specific for this application.
]"
date: "$Date$"
revision: "$Revision$"
class
APPLICATION_EXECUTION
inherit
WEB_SOCKET_EXECUTION
create
make
feature -- Request processing
is_verbose: BOOLEAN = True
execute
-- Process request ...
local
s: STRING
do
if
request.is_get_request_method and then
request.request_uri.is_case_insensitive_equal_general ("/app")
then
s := websocket_app_html ({APPLICATION}.default_port_number)
else
s := "Open the websocket client example: <a href=%"/app%">click here</a>."
end
response.header.put_content_type_text_html
response.header.put_content_length (s.count)
response.put_string (s)
end
on_open (ws: WEB_SOCKET)
do
if is_verbose then
log ("Connecting", {HTTPD_LOGGER_CONSTANTS}.debug_level)
end
end
on_binary (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
do
ws.send (Binary_frame, a_message)
end
on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
do
-- if is_verbose then
-- log ("On text (message length=" + a_message.count.out + ")", {HTTPD_LOGGER_CONSTANTS}.debug_level)
-- end
ws.send (Text_frame, a_message)
end
on_close (ws: WEB_SOCKET)
-- Called after the WebSocket connection is closed.
do
if is_verbose then
log ("Connection closed", {HTTPD_LOGGER_CONSTANTS}.debug_level)
end
end
log (m: READABLE_STRING_8; lev: INTEGER)
do
response.put_error (m)
end
websocket_app_html (a_port: INTEGER): STRING
do
Result := "[
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var socket;
function connect(){
var host = "ws://127.0.0.1:##PORTNUMBER##";
try{
socket = new WebSocket(host);
message('<p class="event">Socket Status: '+socket.readyState);
socket.onopen = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (open)');
}
socket.onmessage = function(msg){
message('<p class="message">Received: '+msg.data);
}
socket.onclose = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
}
} catch(exception){
message('<p>Error'+exception);
}
}
function send(){
var text = $('#text').val();
if(text==""){
message('<p class="warning">Please enter a message');
return ;
}
try{
socket.send(text);
message('<p class="event">Sent: '+text)
} catch(exception){
message('<p class="warning">');
}
$('#text').val("");
}
function message(msg){
$('#chatLog').append(msg+'</p>');
}//End message()
$('#text').keypress(function(event) {
if (event.keyCode == '13') {
send();
}
});
$('#disconnect').click(function(){
socket.close();
});
if (!("WebSocket" in window)){
$('#chatLog, input, button, #examples').fadeOut("fast");
$('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
}else{
//The user has WebSockets
connect();
}
});
</script>
<meta charset="utf-8" />
<style type="text/css">
body {font-family:Arial, Helvetica, sans-serif;}
#container { border:5px solid grey; width:800px; margin:0 auto; padding:10px; }
#chatLog { padding:5px; border:1px solid black; }
#chatLog p {margin:0;}
.event {color:#999;}
.warning { font-weight:bold; color:#CCC; }
</style>
<title>WebSockets Client</title>
</head>
<body>
<div id="wrapper">
<div id="container">
<h1>WebSockets Client</h1>
<div id="chatLog"></div>
<input id="text" type="text" />
<button id="disconnect">Disconnect</button>
</div>
</div>
</body>
</html>
]"
Result.replace_substring_all ("##PORTNUMBER##", a_port.out)
end
note
copyright: "2011-2016, Javier Velilla, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="echo_websocket_server" uuid="C9B3DA5F-DF0D-4C0F-924A-130B5C1E6604">
<target name="common">
<file_rule>
<exclude>/.git$</exclude>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option concurrency="none" debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="all" syntax="transitional">
<debug name="ws" enabled="true"/>
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="console_application" value="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf">
<option>
<assertions precondition="true"/>
</option>
</library>
<library name="http_network" location="..\..\..\..\http_network\http_network-safe.ecf" readonly="false"/>
<library name="httpd" location="..\..\..\..\..\server\httpd\httpd-safe.ecf" readonly="false"/>
<library name="standalone_websocket_connector" location="..\..\..\..\..\server\wsf\connector\standalone_websocket-safe.ecf" readonly="false"/>
<library name="websocket_server" location="..\..\websocket_server-safe.ecf" readonly="false">
<option debug="true">
<debug name="ws" enabled="true"/>
</option>
</library>
<cluster name="src" location=".\" recursive="true">
</cluster>
</target>
<target name="echo_websocket_server_mt" extends="common">
<root class="APPLICATION" feature="make_and_launch"/>
<option concurrency="thread" root_concurrency="thread">
</option>
<variable name="httpd_ssl_enabled" value="true"/>
</target>
<target name="echo_websocket_server_mt_no_ssl" extends="echo_websocket_server_mt">
<variable name="httpd_ssl_enabled" value="false"/>
</target>
<target name="echo_websocket_server_scoop" extends="common">
<root class="APPLICATION" feature="make_and_launch"/>
<option concurrency="scoop" root_concurrency="scoop">
</option>
<variable name="httpd_ssl_enabled" value="true"/>
</target>
<target name="echo_websocket_server_scoop_no_ssl" extends="echo_websocket_server_scoop">
<variable name="httpd_ssl_enabled" value="false"/>
</target>
</system>

View File

@@ -0,0 +1,4 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, Javier Velilla, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,116 @@
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
if(!("WebSocket" in window)){
$('#chatLog, input, button, #examples').fadeOut("fast");
$('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
}else{
//The user has WebSockets
connect();
function connect(){
var socket;
var host = "ws://127.0.0.1:9090";
try{
var socket = new WebSocket(host);
message('<p class="event">Socket Status: '+socket.readyState);
socket.onopen = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (open)');
}
socket.onmessage = function(msg){
message('<p class="message">Received: '+msg.data);
}
socket.onclose = function(){
message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
}
} catch(exception){
message('<p>Error'+exception);
}
function send(){
var text = $('#text').val();
if(text==""){
message('<p class="warning">Please enter a message');
return ;
}
try{
socket.send(text);
message('<p class="event">Sent: '+text)
} catch(exception){
message('<p class="warning">');
}
$('#text').val("");
}
function message(msg){
$('#chatLog').append(msg+'</p>');
}//End message()
$('#text').keypress(function(event) {
if (event.keyCode == '13') {
send();
}
});
$('#disconnect').click(function(){
socket.close();
});
}
}//End connect()
});
</script>
<meta charset=utf-8 />
<style type="text/css">
body{font-family:Arial, Helvetica, sans-serif;}
#container{
border:5px solid grey;
width:800px;
margin:0 auto;
padding:10px;
}
#chatLog{
padding:5px;
border:1px solid black;
}
#chatLog p{margin:0;}
.event{color:#999;}
.warning{
font-weight:bold;
color:#CCC;
}
</style>
<title>WebSockets Client</title>
</head>
<body>
<div id="wrapper">
<div id="container">
<h1>WebSockets Client</h1>
<div id="chatLog">
</div>
<input id="text" type="text" />
<button id="disconnect">Disconnect</button>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<title>WebSocket Image Drop</title>
<h1>Drop Image Here</h1>
<script>
// Initialize WebSocket connection
var wsUrl = "ws://127.0.0.1:9090";
var ws = new WebSocket(wsUrl);
ws.onopen = function() {
console.log("open");
}
// Handle binary image data received on the WebSocket
ws.onmessage = function(e) {
var blob = e.data;
console.log("message: " + blob.size + " bytes");
// Work with prefixed URL API
if (window.webkitURL) {
URL = webkitURL;
}
var uri = URL.createObjectURL(blob);
var img = document.createElement("img");
img.src = uri;
document.body.appendChild(img);
}
// Handle drop event
document.ondrop = function(e) {
document.body.style.backgroundColor = "#fff";
try {
e.preventDefault();
handleFileDrop(e.dataTransfer.files[0]);
return false;
} catch(err) {
console.log(err);
}
}
// Provide visual feedback for the drop area
document.ondragover = function(e) {
e.preventDefault();
document.body.style.backgroundColor = "#6fff41";
}
document.ondragleave = function() {
document.body.style.backgroundColor = "#fff";
}
// Read binary file contents and send them over WebSocket
function handleFileDrop(file) {
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function() {
console.log("sending: " + file.name);
ws.send(reader.result);
}
}
</script>

View File

@@ -0,0 +1,21 @@
note
description: "Summary description for {WEB_SOCKET_EXECUTION}."
date: "$Date$"
revision: "$Revision$"
deferred class
WEB_SOCKET_EXECUTION
inherit
WSF_WEBSOCKET_EXECUTION
WEB_SOCKET_EVENT_I
feature -- Websocket execution
new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
do
create Result.make (ws, Current)
end
end

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {WEB_SOCKET_SERVICE}."
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_SERVICE [G -> WEB_SOCKET_EXECUTION create make end]
create
make_and_launch
feature {NONE} -- Initialization
make_and_launch
local
l_launcher: WSF_STANDALONE_WEBSOCKET_SERVICE_LAUNCHER [G]
opts: WEB_SOCKET_SERVICE_OPTIONS
do
create opts
set_options (opts)
opts.append_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("ws.ini"))
create l_launcher.make_and_launch (opts)
end
set_options (opts: WEB_SOCKET_SERVICE_OPTIONS)
-- Set values on `opts'.
do
end
end

View File

@@ -0,0 +1,12 @@
note
description: "Summary description for {WEB_SOCKET_SERVICE_OPTIONS}."
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_SERVICE_OPTIONS
inherit
WSF_STANDALONE_WEBSOCKET_SERVICE_OPTIONS
end

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="websocket_server" uuid="FDD2276B-7149-4490-9E09-46AFEA122D1E" library_target="websocket_server">
<target name="websocket_server">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="standard">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="encoder" location="..\..\..\text\encoder\encoder-safe.ecf"/>
<library name="standalone_websocket_connector" location="..\..\..\server\wsf\connector\standalone_websocket-safe.ecf" readonly="false"/>
<library name="wsf" location="..\..\..\server\wsf\wsf-safe.ecf" readonly="false"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf">
<condition>
<concurrency excluded_value="none"/>
</condition>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<!--
<cluster name="protocol" location="..\protocol\" recursive="true"/>
-->
<cluster name="websocket_server" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="websocket_server" uuid="FDD2276B-7149-4490-9E09-46AFEA122D1E" library_target="websocket_server">
<target name="websocket_server">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" is_attached_by_default="false" void_safety="none" syntax="standard">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
<library name="http_network" location="..\..\http_network\http_network.ecf" readonly="false"/>
<library name="httpd" location="lib\httpd\httpd.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf">
<condition>
<concurrency excluded_value="none"/>
</condition>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="protocol" location="..\protocol\" recursive="true"/>
<cluster name="websocket_server" location=".\src\" recursive="true"/>
</target>
</system>

View File

@@ -122,6 +122,7 @@ feature -- Websocket events: implemented
ws_valid: ws.is_open_read and then ws.is_open_write
do
ws.send (Connection_close_frame, "")
on_close (ws)
end
note