Added WSF standalone_websocket connector, that provides websocket on top of standalone connector.
This commit is contained in:
+183
@@ -0,0 +1,183 @@
|
||||
note
|
||||
description: "[
|
||||
API to perform actions like opening and closing the connection, sending and receiving messages, and listening
|
||||
for events.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WEB_SOCKET_EVENT_I
|
||||
|
||||
inherit
|
||||
WEB_SOCKET_CONSTANTS
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature -- Web Socket Interface
|
||||
|
||||
on_event (conn: HTTPD_STREAM_SOCKET; a_message: detachable READABLE_STRING_8; a_opcode: INTEGER)
|
||||
-- Called when a frame from the client has been receive
|
||||
require
|
||||
conn_attached: conn /= Void
|
||||
conn_valid: conn.is_open_read and then conn.is_open_write
|
||||
local
|
||||
l_message: READABLE_STRING_8
|
||||
do
|
||||
debug ("ws")
|
||||
print ("%Non_event (conn, a_message, " + opcode_name (a_opcode) + ")%N")
|
||||
end
|
||||
if a_message = Void then
|
||||
create {STRING} l_message.make_empty
|
||||
else
|
||||
l_message := a_message
|
||||
end
|
||||
|
||||
if a_opcode = Binary_frame then
|
||||
on_binary (conn, l_message)
|
||||
elseif a_opcode = Text_frame then
|
||||
on_text (conn, l_message)
|
||||
elseif a_opcode = Pong_frame then
|
||||
on_pong (conn, l_message)
|
||||
elseif a_opcode = Ping_frame then
|
||||
on_ping (conn, l_message)
|
||||
elseif a_opcode = Connection_close_frame then
|
||||
on_connection_close (conn, "")
|
||||
else
|
||||
on_unsupported (conn, l_message, a_opcode)
|
||||
end
|
||||
end
|
||||
|
||||
on_open (conn: HTTPD_STREAM_SOCKET)
|
||||
-- Called after handshake, indicates that a complete WebSocket connection has been established.
|
||||
require
|
||||
conn_attached: conn /= Void
|
||||
conn_valid: conn.is_open_read and then conn.is_open_write
|
||||
deferred
|
||||
end
|
||||
|
||||
on_binary (conn: HTTPD_STREAM_SOCKET; a_message: READABLE_STRING_8)
|
||||
require
|
||||
conn_attached: conn /= Void
|
||||
conn_valid: conn.is_open_read and then conn.is_open_write
|
||||
deferred
|
||||
end
|
||||
|
||||
on_pong (conn: HTTPD_STREAM_SOCKET; a_message: READABLE_STRING_8)
|
||||
require
|
||||
conn_attached: conn /= Void
|
||||
conn_valid: conn.is_open_read and then conn.is_open_write
|
||||
do
|
||||
-- log ("Its a pong frame")
|
||||
-- at first we ignore pong
|
||||
-- FIXME: provide better explanation
|
||||
end
|
||||
|
||||
on_ping (conn: HTTPD_STREAM_SOCKET; a_message: READABLE_STRING_8)
|
||||
require
|
||||
conn_attached: conn /= Void
|
||||
conn_valid: conn.is_open_read and then conn.is_open_write
|
||||
do
|
||||
send (conn, Pong_frame, a_message)
|
||||
end
|
||||
|
||||
on_text (conn: HTTPD_STREAM_SOCKET; a_message: READABLE_STRING_8)
|
||||
require
|
||||
conn_attached: conn /= Void
|
||||
conn_valid: conn.is_open_read and then conn.is_open_write
|
||||
deferred
|
||||
end
|
||||
|
||||
on_unsupported (conn: HTTPD_STREAM_SOCKET; a_message: READABLE_STRING_8; a_opcode: INTEGER)
|
||||
require
|
||||
conn_attached: conn /= Void
|
||||
conn_valid: conn.is_open_read and then conn.is_open_write
|
||||
do
|
||||
-- do nothing
|
||||
end
|
||||
|
||||
on_connection_close (conn: HTTPD_STREAM_SOCKET; a_message: detachable READABLE_STRING_8)
|
||||
require
|
||||
conn_attached: conn /= Void
|
||||
conn_valid: conn.is_open_read and then conn.is_open_write
|
||||
do
|
||||
send (conn, Connection_close_frame, "")
|
||||
end
|
||||
|
||||
on_close (conn: detachable HTTPD_STREAM_SOCKET)
|
||||
-- Called after the WebSocket connection is closed.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
send (conn: HTTPD_STREAM_SOCKET; a_opcode:INTEGER; a_message: READABLE_STRING_8)
|
||||
local
|
||||
i: INTEGER
|
||||
l_chunk_size: INTEGER
|
||||
l_chunk: READABLE_STRING_8
|
||||
l_header_message: STRING
|
||||
l_message_count: INTEGER
|
||||
n: NATURAL_64
|
||||
retried: BOOLEAN
|
||||
do
|
||||
print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N")
|
||||
if not retried then
|
||||
create l_header_message.make_empty
|
||||
l_header_message.append_code ((0x80 | a_opcode).to_natural_32)
|
||||
l_message_count := a_message.count
|
||||
n := l_message_count.to_natural_64
|
||||
if l_message_count > 0xffff then
|
||||
--! Improve. this code needs to be checked.
|
||||
l_header_message.append_code ((0 | 127).to_natural_32)
|
||||
l_header_message.append_character ((n |>> 56).to_character_8)
|
||||
l_header_message.append_character ((n |>> 48).to_character_8)
|
||||
l_header_message.append_character ((n |>> 40).to_character_8)
|
||||
l_header_message.append_character ((n |>> 32).to_character_8)
|
||||
l_header_message.append_character ((n |>> 24).to_character_8)
|
||||
l_header_message.append_character ((n |>> 16).to_character_8)
|
||||
l_header_message.append_character ((n |>> 8).to_character_8)
|
||||
l_header_message.append_character ( n.to_character_8)
|
||||
elseif l_message_count > 125 then
|
||||
l_header_message.append_code ((0 | 126).to_natural_32)
|
||||
l_header_message.append_code ((n |>> 8).as_natural_32)
|
||||
l_header_message.append_character (n.to_character_8)
|
||||
else
|
||||
l_header_message.append_code (n.as_natural_32)
|
||||
end
|
||||
conn.put_string (l_header_message)
|
||||
|
||||
|
||||
l_chunk_size := 16_384 -- 16K
|
||||
if l_message_count < l_chunk_size then
|
||||
conn.put_string (a_message)
|
||||
else
|
||||
from
|
||||
i := 0
|
||||
until
|
||||
l_chunk_size = 0
|
||||
loop
|
||||
debug ("ws")
|
||||
print ("Sending chunk " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
|
||||
end
|
||||
l_chunk := a_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
|
||||
conn.put_string (l_chunk)
|
||||
if l_chunk.count < l_chunk_size then
|
||||
l_chunk_size := 0
|
||||
end
|
||||
i := i + l_chunk_size
|
||||
end
|
||||
debug ("ws")
|
||||
print ("Sending chunk done%N")
|
||||
end
|
||||
end
|
||||
else
|
||||
-- FIXME: what should be done on rescue?
|
||||
end
|
||||
rescue
|
||||
retried := True
|
||||
io.put_string ("Internal error in " + generator + ".do_send (conn, a_opcode=" + a_opcode.out + ", a_message) !%N")
|
||||
retry
|
||||
end
|
||||
|
||||
end
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
note
|
||||
description: "[
|
||||
A web socket message has an opcode specifying the type of the message payload. The
|
||||
opcode consists of the last four bits in the first byte of the frame header.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=Data Frame", "src=http://tools.ietf.org/html/rfc6455#section-5.6", "protocol=uri"
|
||||
EIS: "name=Control Frame", "src=http://tools.ietf.org/html/rfc6455#section-5.5", "protocol=uri"
|
||||
|
||||
class
|
||||
WEB_SOCKET_MESSAGE_TYPE
|
||||
|
||||
feature -- Data Frames
|
||||
|
||||
Text: INTEGER = 0x1
|
||||
-- The data type of the message is text.
|
||||
|
||||
Binary: INTEGER = 0x2
|
||||
-- The data type of the message is binary.
|
||||
|
||||
feature -- Control Frames
|
||||
|
||||
Close: INTEGER = 0x8
|
||||
-- The client or server is sending a closing
|
||||
-- handshake to the server or client.
|
||||
|
||||
Ping: INTEGER = 0x9
|
||||
-- The client or server sends a ping to the server or client.
|
||||
|
||||
Pong: INTEGER = 0xA
|
||||
-- The client or server sends a pong to the server or client.
|
||||
|
||||
feature -- Reserverd
|
||||
|
||||
-- Opcodes 0x3-0x7 are reserved for further non-control frames yet to be
|
||||
-- defined.
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user