note
description : "simple application execution"
date : "$Date$"
revision : "$Revision$"
class
APPLICATION_EXECUTION
inherit
WSF_WEBSOCKET_EXECUTION
WEB_SOCKET_EVENT_I
redefine
on_timer
end
create
make
feature -- Basic operations
execute
local
s: STRING
dt: HTTP_DATE
do
-- To send a response we need to setup, the status code and
-- the response headers.
if request.path_info.same_string_general ("/favicon.ico") then
response.put_header ({HTTP_STATUS_CODE}.not_found, <<["Content-Length", "0"]>>)
else
if request.path_info.same_string_general ("/app") then
s := websocket_app_html (request.server_name, request.server_port)
else
s := "Hello World!"
create dt.make_now_utc
s.append (" (UTC time is " + dt.rfc850_string + ").")
s.append ("
Websocket demo
")
end
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
response.set_status_code ({HTTP_STATUS_CODE}.ok)
response.header.put_content_type_text_html
response.header.put_content_length (s.count)
if request.is_keep_alive_http_connection then
response.header.put_connection_keep_alive
end
response.put_string (s)
end
end
feature -- Websocket execution
new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
do
create Result.make (ws, Current)
end
feature -- Websocket execution
on_open (ws: WEB_SOCKET)
do
initialize_commands
set_timer_delay (1) -- Every 1 second.
ws.put_error ("Connecting")
ws.send (Text_frame, "Hello, this is a simple demo with Websocket using Eiffel. (/help for more information).%N")
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)
local
i: INTEGER
cmd_name: READABLE_STRING_8
do
if a_message.starts_with_general ("/") then
from
i := 1
until
i >= a_message.count or else a_message[i + 1].is_space
loop
i := i + 1
end
cmd_name := a_message.substring (2, i)
if attached command (cmd_name) as cmd then
cmd (ws, a_message.substring (i + 1, a_message.count))
elseif a_message.same_string_general ("/help") then
on_help_command (ws, Void)
else
ws.send (Text_frame, "Error: unknown command '/" + cmd_name + "'!%N")
end
else
-- Echo the message for testing.
ws.send (Text_frame, a_message)
end
end
on_close (ws: WEB_SOCKET)
-- Called after the WebSocket connection is closed.
do
ws.put_error ("Connection closed")
end
on_timer (ws: WEB_SOCKET)
-- .
-- If ever the file ".stop" exists, stop gracefully the connection.
local
fut: FILE_UTILITIES
f: RAW_FILE
do
if fut.file_exists (".stop") then
ws.send_text ("End of the communication ...%N")
ws.send_connection_close ("")
create f.make_with_name (".stop")
f.delete
end
end
feature -- Command
initialize_commands
do
register_command (agent on_help_command, "help", "Display this help.")
register_command (agent on_time_command, "time", "Return the server UTC time.")
register_command (agent on_shutdown_command, "shutdown", "Shutdown the service (ends the websocket).")
end
register_command (a_cmd: attached like command; a_name: READABLE_STRING_8; a_description: READABLE_STRING_8)
local
tb: like commands
do
tb := commands
if tb = Void then
create tb.make_caseless (1)
commands := tb
end
tb.force ([a_cmd, a_name, a_description], a_name)
end
commands: detachable STRING_TABLE [TUPLE [cmd: attached like command; name, description: READABLE_STRING_8]]
command (a_name: READABLE_STRING_GENERAL): detachable PROCEDURE [TUPLE [ws: WEB_SOCKET; args: detachable READABLE_STRING_GENERAL]]
do
if
attached commands as tb and then
attached tb.item (a_name) as d
then
Result := d.cmd
end
end
on_help_command (ws: WEB_SOCKET; args: detachable READABLE_STRING_GENERAL)
local
s: STRING
do
create s.make_from_string ("Help: available commands:%N")
if attached commands as tb then
across
tb as ic
loop
s.append (" /")
s.append (ic.item.name)
s.append (" : ")
s.append (ic.item.description)
s.append ("%N")
end
end
ws.send_text (s)
end
on_time_command (ws: WEB_SOCKET; args: detachable READABLE_STRING_GENERAL)
do
ws.send_text ("Server time is " + (create {HTTP_DATE}.make_now_utc).string)
end
on_shutdown_command (ws: WEB_SOCKET; args: detachable READABLE_STRING_GENERAL)
local
f: RAW_FILE
do
ws.send_text ("Active websockets will end soon.%N")
create f.make_create_read_write (".stop")
f.put_string ("stop%N")
f.close
end
feature -- HTML Resource
websocket_app_html (a_host: STRING; a_port: INTEGER): STRING
do
Result := "[
WebSockets Client
]"
Result.replace_substring_all ("##HOSTNAME##", a_host)
Result.replace_substring_all ("##PORTNUMBER##", a_port.out)
if request.is_https then
Result.replace_substring_all ("##HTTPSCHEME##", "https")
Result.replace_substring_all ("##WSSCHEME##", "wss")
else
Result.replace_substring_all ("##HTTPSCHEME##", "http")
Result.replace_substring_all ("##WSSCHEME##", "ws")
end
end
end