Reuse http_network library.

Reintroduced HTTPD_STREAM_SOCKET for backward compatibility, and ease of usage.
Added websocket libraries (client, and protocol).
This commit is contained in:
2016-10-13 16:25:11 +02:00
parent 981942b2d6
commit 897f64e4fe
54 changed files with 2233 additions and 95 deletions

View File

@@ -16,7 +16,6 @@
<library name="encoder" location="..\..\..\..\text\encoder\encoder-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\network\protocol\http\http-safe.ecf"/>
<library name="http_network" location="..\..\..\..\network\http_network\http_network-safe.ecf" readonly="false"/>
<library name="httpd" location="..\..\..\httpd\httpd-safe.ecf" readonly="false"/>
<cluster name="src" location=".\src\">
<cluster name="implementation" location="$|implementation\" hidden="true"/>

View File

@@ -15,7 +15,6 @@
<library name="encoder" location="..\..\..\..\text\encoder\encoder.ecf"/>
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
<library name="http" location="..\..\..\..\network\protocol\http\http.ecf"/>
<library name="http_network" location="..\..\..\..\network\http_network\http_network.ecf" readonly="false"/>
<library name="httpd" location="..\..\..\httpd\httpd.ecf" readonly="false"/>
<cluster name="src" location=".\src\">
<cluster name="implementation" location="$|implementation\" hidden="true"/>

View File

@@ -33,9 +33,9 @@ feature -- Access
feature {HTTPD_SERVER_I} -- Execution
accept_incoming_connection (a_listening_socket: HTTP_STREAM_SOCKET)
accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
local
cl: HTTP_STREAM_SOCKET
cl: HTTPD_STREAM_SOCKET
do
is_shutdown_requested := is_shutdown_requested or shutdown_requested (server)
if is_shutdown_requested then

View File

@@ -64,19 +64,19 @@ feature {HTTPD_SERVER_I} -- Execution
p.gracefull_stop
end
accept_incoming_connection (a_listening_socket: HTTP_STREAM_SOCKET)
accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
-- <Precursor>
do
accept_connection_on_pool (pool, a_listening_socket) -- Wait on not pool.is_full or is_stop_requested
end
accept_connection_on_pool (a_pool: like pool; a_listening_socket: HTTP_STREAM_SOCKET)
accept_connection_on_pool (a_pool: like pool; a_listening_socket: HTTPD_STREAM_SOCKET)
-- Process accept connection
-- note that the precondition matters for scoop synchronization.
require
concurrency: not a_pool.is_full or is_shutdown_requested or a_pool.stop_requested
local
cl: separate HTTP_STREAM_SOCKET
cl: separate HTTPD_STREAM_SOCKET
do
debug ("dbglog")
dbglog (generator + ".ENTER accept_connection_on_pool")
@@ -121,7 +121,7 @@ feature {HTTPD_SERVER_I} -- Status report
feature {NONE} -- Implementation
separate_client_socket (hdl: separate HTTPD_REQUEST_HANDLER): separate HTTP_STREAM_SOCKET
separate_client_socket (hdl: separate HTTPD_REQUEST_HANDLER): separate HTTPD_STREAM_SOCKET
-- Client socket for request handler `hdl'.
do
Result := hdl.client_socket
@@ -134,7 +134,7 @@ invariant
pool_attached: pool /= Void
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -28,7 +28,7 @@ feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
d: detachable STRING
do
debug ("dbglog")
if
if
attached internal_client_socket as l_socket and then
l_socket.descriptor_available
then
@@ -50,7 +50,7 @@ feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -15,7 +15,7 @@ inherit
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -178,7 +178,7 @@ feature {NONE} -- Implementation
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -19,7 +19,7 @@ feature -- Access
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -40,7 +40,7 @@ feature {NONE} -- Implementation
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -51,9 +51,9 @@ feature {HTTPD_SERVER_I} -- Execution
p.terminate
end
accept_incoming_connection (a_listening_socket: HTTP_STREAM_SOCKET)
accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
local
cl: separate HTTP_STREAM_SOCKET
cl: separate HTTPD_STREAM_SOCKET
do
debug ("dbglog")
dbglog (generator + ".ENTER accept_connection {"+ a_listening_socket.descriptor.out +"}")

View File

@@ -336,7 +336,7 @@ feature -- SSL Helpers
end
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -25,4 +25,14 @@ feature -- Default persistent connection settings
default_keep_alive_timeout: INTEGER = 15 -- seconds
default_max_keep_alive_requests: INTEGER = 100
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -64,7 +64,7 @@ feature -- Change
-- Set `socket_recv_timeout' to `a_timeout_in_seconds'.
do
socket_recv_timeout := a_timeout_in_seconds
end
end
set_keep_alive_timeout (a_timeout_in_seconds: INTEGER)
-- Set `keep_alive_timeout' to `a_timeout_in_seconds'.
@@ -78,5 +78,15 @@ feature -- Change
max_keep_alive_requests := nb
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -31,7 +31,6 @@
<cluster name="httpd_server" location=".\" recursive="true">
<file_rule>
<exclude>/concurrency$</exclude>
<exclude>/network$</exclude>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
</file_rule>

View File

@@ -32,7 +32,6 @@
<exclude>/concurrency$</exclude>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
<exclude>/network$</exclude>
</file_rule>
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>

View File

@@ -39,7 +39,7 @@ feature {NONE} -- Access
feature {HTTPD_SERVER_I} -- Execution
accept_incoming_connection (a_listening_socket: HTTP_STREAM_SOCKET)
accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
-- Accept incoming connection from `a_listening_socket'.
deferred
end
@@ -85,7 +85,7 @@ feature {NONE} -- Implementation
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -23,6 +23,13 @@ feature -- Status report.
-- Shutdown requested.
;note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -38,7 +38,7 @@ feature -- runtime
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -14,7 +14,7 @@ feature -- Logs
end
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -19,4 +19,14 @@ feature -- Access
information_level: INTEGER = 32 -- 0010 0000
debug_level: INTEGER = 64 -- 0100 0000
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -14,7 +14,7 @@ feature -- Factory
end
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -45,7 +45,7 @@ feature {NONE} -- Initialization
-- still alive.
do
if a_is_reusing_connection then
-- Keep `remote_info' as it stays the same for the successive
-- Keep `remote_info' as it stays the same for the successive
-- persistent connections.
else
remote_info := Void
@@ -72,9 +72,9 @@ feature -- Status report
feature -- Access
internal_client_socket: detachable HTTP_STREAM_SOCKET
internal_client_socket: detachable HTTPD_STREAM_SOCKET
client_socket: HTTP_STREAM_SOCKET
client_socket: HTTPD_STREAM_SOCKET
local
s: like internal_client_socket
do
@@ -331,7 +331,7 @@ feature -- Execution
feature -- Request processing
process_request (a_socket: HTTP_STREAM_SOCKET)
process_request (a_socket: HTTPD_STREAM_SOCKET)
-- Process request on socket `a_socket'.
require
no_error: not has_error
@@ -343,7 +343,7 @@ feature -- Request processing
deferred
end
process_bad_request (a_socket: HTTP_STREAM_SOCKET)
process_bad_request (a_socket: HTTPD_STREAM_SOCKET)
-- Process bad request catched on `a_socket'.
require
has_error: has_error
@@ -378,7 +378,7 @@ feature -- Request processing
feature -- Parsing
get_request_header (a_socket: HTTP_STREAM_SOCKET; a_is_reusing_connection: BOOLEAN)
get_request_header (a_socket: HTTPD_STREAM_SOCKET; a_is_reusing_connection: BOOLEAN)
-- Analyze message extracted from `a_socket' as HTTP request.
-- If `a_is_reusing_connection' is True, then first use
-- Note: it reads from socket.
@@ -504,7 +504,7 @@ feature -- Parsing
end
end
next_line (a_socket: HTTP_STREAM_SOCKET): detachable STRING
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
-- Next line fetched from `a_socket' is available.
-- note: may update `has_error'.
require
@@ -569,7 +569,7 @@ feature -- Output
feature {NONE} -- Helpers
socket_has_incoming_data (a_socket: HTTP_STREAM_SOCKET): BOOLEAN
socket_has_incoming_data (a_socket: HTTPD_STREAM_SOCKET): BOOLEAN
-- Is there any data to read on `a_socket' ?
require
a_socket.readable
@@ -584,7 +584,7 @@ invariant
request_header_attached: request_header /= Void
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -151,7 +151,7 @@ feature -- Listening
-- Creates a socket and connects to the http server.
-- `a_server': The main server object
local
l_listening_socket: detachable HTTP_STREAM_SOCKET
l_listening_socket: detachable HTTPD_STREAM_SOCKET
l_http_port: INTEGER
l_connection_handler: HTTPD_CONNECTION_HANDLER
do
@@ -234,7 +234,7 @@ feature -- Listening
feature {NONE} -- Factory
new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTP_STREAM_SOCKET
new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET
do
if a_addr /= Void then
create Result.make_server_by_address_and_port (a_addr, a_http_port)
@@ -365,7 +365,7 @@ feature -- Output
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -24,4 +24,14 @@ feature -- Event
deferred
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -1,5 +1,5 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -0,0 +1,30 @@
note
description: "Summary description for {HTTPD_SOCKET_FACTORY}."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_SOCKET_FACTORY
feature -- Access
new_client_socket (a_is_secure: BOOLEAN): HTTPD_STREAM_SOCKET
do
if a_is_secure then
create {HTTPD_STREAM_SSL_SOCKET} Result.make_empty
else
create Result.make_empty
end
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,32 @@
note
description: "[
Socket for HTTPD networking.
]"
date: "$Date$"
revision: "$Revision$"
class
HTTPD_STREAM_SOCKET
inherit
HTTP_STREAM_SOCKET
create
make, make_empty,
make_client_by_port, make_client_by_address_and_port,
make_server_by_port, make_server_by_address_and_port, make_loopback_server_by_port
create {NETWORK_STREAM_SOCKET}
make_from_descriptor_and_address
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,51 @@
note
description: "[
SSL enabled Socket for HTTPD networking.
]"
date: "$Date$"
revision: "$Revision$"
class
HTTPD_STREAM_SSL_SOCKET
inherit
HTTP_STREAM_SSL_SOCKET
HTTPD_STREAM_SOCKET
undefine
make_empty, make_from_descriptor_and_address,
error_number,
readstream, read_stream,
read_into_pointer,
read_to_managed_pointer,
put_pointer_content,
write, send,
close_socket,
connect, shutdown,
do_accept,
put_managed_pointer,
read_stream_noexception,
read_into_pointer_noexception,
put_pointer_content_noexception,
is_ssl_supported
end
create
make, make_empty,
make_client_by_port, make_client_by_address_and_port,
make_server_by_port, make_server_by_address_and_port, make_loopback_server_by_port
create {NETWORK_STREAM_SOCKET}
make_from_descriptor_and_address
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -54,7 +54,7 @@ feature -- SSL Helpers
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -15,7 +15,7 @@ create
make
note
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -1,17 +0,0 @@
note
description: "Summary description for {HTTPD_SOCKET_FACTORY}."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_SOCKET_FACTORY
feature -- Access
new_client_socket (a_is_secure: BOOLEAN): HTTP_STREAM_SOCKET
do
check not_secure: not a_is_secure end
create Result.make_empty
end
end

View File

@@ -19,7 +19,7 @@ create
feature {NONE} -- Factory
new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTP_STREAM_SOCKET
new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET
local
s_ssl: HTTPD_STREAM_SSL_SOCKET
do
@@ -31,11 +31,27 @@ feature {NONE} -- Factory
create s_ssl.make_server_by_port (a_http_port)
end
s_ssl.set_tls_protocol (configuration.ssl_protocol)
s_ssl.set_certificate_filenames (configuration.ca_crt, configuration.ca_key)
if attached configuration.ca_crt as l_crt then
s_ssl.set_certificate_file_name (l_crt)
end
if attached configuration.ca_key as l_key then
s_ssl.set_key_file_name (l_key)
end
Result := s_ssl
else
Result := Precursor (a_addr, a_http_port)
end
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -1,20 +0,0 @@
note
description: "Summary description for {HTTPD_SOCKET_FACTORY}."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_SOCKET_FACTORY
feature -- Access
new_client_socket (a_is_secure: BOOLEAN): HTTP_STREAM_SOCKET
do
if a_is_secure then
create {HTTPD_STREAM_SSL_SOCKET} Result.make_empty
else
create Result.make_empty
end
end
end

View File

@@ -16,10 +16,11 @@
<library name="error" location="..\..\..\utility\general\error\error-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf"/>
<library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
<library name="httpd" location="..\..\ewsgi\connectors\standalone\lib\httpd\httpd-safe.ecf"/>
<library name="httpd" location="..\..\httpd\httpd-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="..\wsf-safe.ecf"/>
<library name="wsf_standalone" location="standalone-safe.ecf"/>
<library name="web_socket_protocol" location="..\..\..\network\websocket\protocol\web_socket_protocol-safe.ecf"/>
<cluster name="wsf_standalone_websocket" location=".\standalone_websocket\" recursive="true"/>
</target>
</system>

View File

@@ -16,10 +16,11 @@
<library name="error" location="..\..\..\utility\general\error\error.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi.ecf"/>
<library name="http" location="..\..\..\network\protocol\http\http.ecf"/>
<library name="httpd" location="..\..\ewsgi\connectors\standalone\lib\httpd\httpd.ecf"/>
<library name="httpd" location="..\..\httpd\httpd.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="wsf" location="..\wsf.ecf"/>
<library name="wsf_standalone" location="standalone.ecf"/>
<library name="web_socket_protocol" location="..\..\..\network\websocket\protocol\web_socket_protocol.ecf"/>
<cluster name="wsf_standalone_websocket" location=".\standalone_websocket\" recursive="true"/>
</target>
</system>

View File

@@ -1,203 +0,0 @@
note
description: "Constants for WebSockets"
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_CONSTANTS
feature -- Constants
HTTP_1_1: STRING = "HTTP/1.1 101 WebSocket Protocol Handshake"
Upgrade_ws: STRING = "Upgrade: websocket"
Connection_ws: STRING = "Connection: Upgrade"
Sec_WebSocket_Origin: STRING = "Sec-WebSocket-Origin: "
Sec_WebSocket_Protocol: STRING = "Sec-WebSocket-Protocol: "
Sec_WebSocket_Location: STRING = "Sec-WebSocket-Location: "
Sec_WebSocket_Version: STRING = "Sec-WebSocket-Version: "
Sec_WebSocket_Extensions: STRING = "Sec-WebSocket-Extensions: "
WebSocket_Origin: STRING = "WebSocket-Origin: "
WebSocket_Protocol: STRING = "WebSocket-Protocol: "
WebSocket_Location: STRING = "WebSocket-Location: "
Origin: STRING = "Origin"
Server: STRING = "EWSS"
Sec_WebSocket_Key: STRING = "Sec-WebSocket-Key"
Ws_scheme: STRING = "ws://"
Wss_scheme: STRING = "wss://"
Magic_guid: STRING = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-- The handshake from the client looks as follows:
-- GET /chat HTTP/1.1
-- Host: server.example.com
-- Upgrade: websocket
-- Connection: Upgrade
-- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-- Origin: http://example.com
-- Sec-WebSocket-Protocol: chat, superchat
-- Sec-WebSocket-Version: 13
-- The handshake from the server looks as follows:
-- HTTP/1.1 101 Switching Protocols
-- Upgrade: websocket
-- Connection: Upgrade
-- Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-- Sec-WebSocket-Protocol: chat
feature -- Opcodes Standard actions
--| Maybe we need an enum STANDARD_ACTIONS_OPCODES?
-- |Opcode | Meaning | Reference |
-- -+--------+-------------------------------------+-----------|
-- | 0 | Continuation Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 1 | Text Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 2 | Binary Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 8 | Connection Close Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 9 | Ping Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 10 | Pong Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
Continuation_frame: INTEGER = 0
Text_frame: INTEGER = 1
Binary_frame: INTEGER = 2
Connection_close_frame: INTEGER = 8
Ping_frame: INTEGER = 9
Pong_frame: INTEGER = 10
is_control_frame (a_opcode: INTEGER): BOOLEAN
-- Is `a_opcode' a control frame?
do
inspect a_opcode
when Connection_close_frame, Ping_frame, Pong_frame then
Result := True
else
end
end
opcode_name (a_opcode: INTEGER): STRING
do
inspect a_opcode
when Continuation_frame then Result := "Continuation"
when Text_frame then Result := "Text"
when Binary_frame then Result := "Binary"
when Connection_close_frame then Result := "Connection Close"
when Ping_frame then Result := "Ping"
when Pong_frame then Result := "Pong"
else
Result := "Unknown-Opcode"
end
Result := "0x" + a_opcode.to_hex_string + " " + Result
end
feature -- Close code numbers
-- Maybe an ENUM CLOSE_CODES
-- |Status Code | Meaning | Contact | Reference |
-- -+------------+-----------------+---------------+-----------|
-- | 1000 | Normal Closure | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1001 | Going Away | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1002 | Protocol error | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1003 | Unsupported Data| hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1004 | ---Reserved---- | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1005 | No Status Rcvd | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1006 | Abnormal Closure| hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1007 | Invalid frame | hybi@ietf.org | RFC 6455 |
-- | | payload data | | |
-- -+------------+-----------------+---------------+-----------|
-- | 1008 | Policy Violation| hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1009 | Message Too Big | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1010 | Mandatory Ext. | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1011 | Internal Server | hybi@ietf.org | RFC 6455 |
-- | | Error | | |
-- -+------------+-----------------+---------------+-----------|
-- | 1015 | TLS handshake | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
Normal_closure: INTEGER = 1000
-- Indicates a normal closure, meaning that the purpose for
-- which the connection was established has been fulfilled.
Going_away: INTEGER = 1001
-- Indicates that an endpoint is "going away", such as a server
-- going down or a browser having navigated away from a page.
Protocol_error: INTEGER = 1002
-- Indicates that an endpoint is terminating the connection due
-- to a protocol error.
Unsupported_data: INTEGER = 1003
-- Indicates that an endpoint is terminating the connection
-- because it has received a type of data it cannot accept (e.g., an
-- endpoint that understands only text data MAY send this if it
-- receives a binary message).
Invalid_data: INTEGER = 1007
-- Indicates that an endpoint is terminating the connection
-- because it has received data within a message that was not
-- consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
-- data within a text message).
Policy_violation: INTEGER = 1008
-- Indicates that an endpoint is terminating the connection
-- because it has received a message that violates its policy. This
-- is a generic status code that can be returned when there is no
-- other more suitable status code (e.g., 1003 or 1009) or if there
-- is a need to hide specific details about the policy.
Message_too_large: INTEGER = 1009
-- Indicates that an endpoint is terminating the connection
-- because it has received a message that is too big for it to
-- process.
Extension_required: INTEGER = 1010
-- Indicates that an endpoint (client) is terminating the
-- connection because it has expected the server to negotiate one or
-- more extension, but the server didn't return them in the response
-- message of the WebSocket handshake.
Internal_error: INTEGER = 1011
-- Indicates that a server is terminating the connection because
-- it encountered an unexpected condition that prevented it from
-- fulfilling the request.
end

View File

@@ -1,35 +0,0 @@
note
description: "Summary description for {WEB_SOCKET_ERROR_FRAME}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_ERROR_FRAME
create
make
feature {NONE} -- Initialization
make (a_code: INTEGER; a_desc: like description)
do
code := a_code
description := a_desc
end
feature -- Access
code: INTEGER
description: READABLE_STRING_8
feature -- Conversion
string: STRING
do
create Result.make_from_string ("Error(" + code.out + "): " + description)
end
end

View File

@@ -1,437 +0,0 @@
note
description: "[
Summary description for {WEB_SOCKET_FRAME}.
See Base Framing Protocol: http://tools.ietf.org/html/rfc6455#section-5.2
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
Check the `check_utf_8_validity_on_chop' if there is performance issue
with bigger data.
]"
date: "$Date$"
revision: "$Revision$"
EIS: "name=Websocket RFC6455 section-5.2", "protocol=URI", "src=http://tools.ietf.org/html/rfc6455#section-5.2", "tag=rfc"
class
WEB_SOCKET_FRAME
inherit
ANY
WEB_SOCKET_CONSTANTS
create
make,
make_as_injected_control
feature {NONE} -- Initialization
make (a_opcode: INTEGER; flag_is_fin: BOOLEAN)
-- Create current frame with opcode `a_opcode'
-- and `a_fin' to indicate if this is the final fragment.
do
is_incomplete := False
opcode := a_opcode
is_fin := flag_is_fin
inspect opcode
when
Continuation_frame, -- 0
Text_frame, -- 1
Binary_frame -- 2
then
--| Supported opcode
when
Connection_close_frame, -- 8
Ping_frame, -- 9
Pong_frame -- 10
then
--| Supported control opcode
-- All control frames MUST have a payload length of 125 bytes or less
-- and MUST NOT be fragmented.
if flag_is_fin then
-- So far it is valid.
else
report_error (Protocol_error, "Control frames MUST NOT be fragmented.")
end
else
report_error (Protocol_error, "Unknown opcode")
end
end
make_as_injected_control (a_opcode: INTEGER; a_parent: WEB_SOCKET_FRAME)
require
parent_is_not_control_frame: not a_parent.is_control
a_opcode_is_control_frame: is_control_frame (a_opcode)
do
make (a_opcode, True)
parent := a_parent
a_parent.add_injected_control_frame (Current)
end
feature -- Access
opcode: INTEGER
-- CONTINUOUS, TEXT, BINARY, PING, PONG, CLOSING
is_fin: BOOLEAN
-- is the final fragment in a message?
fragment_count: INTEGER
payload_length: NATURAL_64
payload_data: detachable STRING_8
-- Maybe we need a buffer here.
uncoded_payload_data: detachable STRING_32
local
utf: UTF_CONVERTER
do
if attached payload_data as d then
Result := utf.utf_8_string_8_to_string_32 (d)
end
end
error: detachable WEB_SOCKET_ERROR_FRAME
-- Describe the type of error
feature -- Access: injected control frames
injected_control_frames: detachable LIST [WEB_SOCKET_FRAME]
parent: detachable WEB_SOCKET_FRAME
-- If Current is injected, `parent' is the related fragmented frame
is_injected_control: BOOLEAN
do
Result := parent /= Void
ensure
Result implies (is_control_frame (opcode))
end
feature -- Operation
update_fin (a_flag_is_fin: BOOLEAN)
do
is_fin := a_flag_is_fin
end
feature {WEB_SOCKET_FRAME} -- Change: injected control frames
add_injected_control_frame (f: WEB_SOCKET_FRAME)
require
Current_is_not_control: not is_control
f_is_control_frame: f.is_control
parented_to_current: f.parent = Current
local
lst: like injected_control_frames
do
lst := injected_control_frames
if lst = Void then
create {ARRAYED_LIST [WEB_SOCKET_FRAME]} lst.make (1)
injected_control_frames := lst
end
lst.force (f)
ensure
parented_to_current: f.parent = Current
end
remove_injected_control_frame (f: WEB_SOCKET_FRAME)
require
Current_is_not_control: not is_control
f_is_control_frame: f.is_control
parented_to_current: f.parent = Current
local
lst: like injected_control_frames
do
lst := injected_control_frames
if lst /= Void then
lst.prune (f)
if lst.is_empty then
injected_control_frames := Void
end
end
end
feature -- Query
is_binary: BOOLEAN
do
Result := opcode = binary_frame
end
is_text: BOOLEAN
do
Result := opcode = text_frame
end
is_continuation: BOOLEAN
do
Result := opcode = continuation_frame
end
is_connection_close: BOOLEAN
do
Result := opcode = connection_close_frame
end
is_control: BOOLEAN
do
inspect opcode
when connection_close_frame, Ping_frame, Pong_frame then
Result := True
else
end
end
is_ping: BOOLEAN
do
Result := opcode = ping_frame
end
is_pong: BOOLEAN
do
Result := opcode = pong_frame
end
feature -- Status report
is_valid: BOOLEAN
do
Result := not has_error
end
is_incomplete: BOOLEAN
has_error: BOOLEAN
do
Result := error /= Void
end
feature -- Change
increment_fragment_count
do
fragment_count := fragment_count + 1
end
check_utf_8_validity_on_chop: BOOLEAN = False
-- True: check for each chop
-- False: check only for each fragment
--| see autobahntestsuite #6.4.3 and #6.4.4
append_payload_data_chop (a_data: STRING_8; a_len: INTEGER; a_flag_chop_complete: BOOLEAN)
do
if a_flag_chop_complete then
increment_fragment_count
end
if attached payload_data as l_payload_data then
l_payload_data.append (a_data)
else
payload_data := a_data
end
payload_length := payload_length + a_len.to_natural_64
if is_text then
if is_fin and a_flag_chop_complete then
-- Check the whole message is a valid UTF-8 string
if attached payload_data as d then
if not is_valid_utf_8_string (d) then
report_error (invalid_data, "The text message is not a valid UTF-8 text!")
end
else
-- empty payload??
end
elseif check_utf_8_validity_on_chop or else a_flag_chop_complete then
-- Check the payload data as utf-8 stream (may be incomplete at this point)
if not is_valid_text_payload_stream then
report_error (invalid_data, "This is not a valid UTF-8 stream!")
-- is_valid implies the connection will be closed!
end
end
end
end
report_error (a_code: INTEGER; a_description: READABLE_STRING_8)
require
not has_error
do
create error.make (a_code, a_description)
ensure
has_error: has_error
is_not_valid: not is_valid
end
feature {NONE} -- Helper
last_utf_8_stream_validation_position: INTEGER
-- In relation with `is_valid_utf_8 (.., a_is_stream=True)'
is_valid_text_payload_stream: BOOLEAN
require
is_text_frame: is_text
do
if attached payload_data as s then
Result := is_valid_utf_8 (s, not is_fin)
end
end
is_valid_utf_8_string (s: READABLE_STRING_8): BOOLEAN
do
Result := is_valid_utf_8 (s, False)
-- and (create {UTF_CONVERTER}).is_valid_utf_8_string_8 (s)
end
is_valid_utf_8 (s: READABLE_STRING_8; a_is_stream: BOOLEAN): BOOLEAN
-- UTF-8 validity checker.
note
EIS: "name=UTF-8 RFC3629", "protocol=URI", "src=https://tools.ietf.org/html/rfc3629", "tag=rfc"
require
is_text_frame: is_text
local
i: like {STRING_8}.count
n: like {STRING_8}.count
c,c2,c3,c4,w: NATURAL_32
l_is_incomplete_stream: BOOLEAN
do
Result := True
-- Following code also check that codepoint is between 0 and 0x10FFFF
-- (as expected by spec, and tested by autobahn ws testsuite)
from
if a_is_stream then
i := last_utf_8_stream_validation_position -- to avoid recomputing from the beginning each time.
else
i := 0
end
n := s.count
until
i >= n or not Result
loop
i := i + 1
c := s.code (i)
if c <= 0x7F then
-- 0xxxxxxx
w := c
elseif c <= 0xC1 then
-- The octet values C0, C1, F5 to FF never appear.
--| case 0xC0 and 0xC1
Result := False
elseif (c & 0xE0) = 0xC0 then
-- 110xxxxx 10xxxxxx
i := i + 1
if i <= n then
c2 := s.code (i)
if
(c2 & 0xC0) = 0x80
then
w := ((c & 0x1F) |<< 6)
| (c2 & 0x3F)
Result := 0x80 <= w and w <= 0x7FF
else
Result := False
end
else
l_is_incomplete_stream := True
end
elseif (c & 0xF0) = 0xE0 then
-- 1110xxxx 10xxxxxx 10xxxxxx
i := i + 2
if i <= n then
c2 := s.code (i - 1)
c3 := s.code (i)
if
(c2 & 0xC0) = 0x80 and
(c3 & 0xC0) = 0x80
then
w := ((c & 0xF) |<< 12)
| ((c2 & 0x3F) |<< 6)
| (c3 & 0x3F)
if 0x800 <= w and w <= 0xFFFF then
if 0xD800 <= w and w <= 0xDFFF then
-- The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF
Result := False
end
else
Result := False
end
else
Result := False
end
else
if i - 1 <= n then
Result := (s.code (i - 1) & 0xC0) = 0x80
end
l_is_incomplete_stream := True
end
elseif (c & 0xF8) = 0xF0 then -- 0001 0000-0010 FFFF
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
if 0xF5 <= c and c <= 0xFF then
-- The octet values C0, C1, F5 to FF never appear.
Result := False
else
i := i + 3
if i <= n then
c2 := s.code (i - 2)
c3 := s.code (i - 1)
c4 := s.code (i)
if
(c2 & 0xC0) = 0x80 and
(c3 & 0xC0) = 0x80 and
(c4 & 0xC0) = 0x80
then
w := ((c & 0x7) |<< 18) |
((c2 & 0x3F) |<< 12) |
((c3 & 0x3F) |<< 6) |
(c4 & 0x3F)
Result := 0x1_0000 <= w and w <= 0x10_FFFF
else
Result := False
end
else
if i - 2 <= n then
c2 := s.code (i - 2)
Result := (c2 & 0xC0) = 0x80
if Result then
if c = 0xF4 and c2 >= 0x90 then
--| any byte 10xxxxxx (i.e >= 0x80) that would come after,
-- will result in out of range code point
-- indeed 0xF4 0x90 0x80 0x80 = 0x1100 0000 > 0x10_FFFF
Result := False
elseif i - 1 <= n then
Result := (s.code (i - 1) & 0xC0) = 0x80
end
end
end
l_is_incomplete_stream := True
end
end
else
-- Invalid byte in UTF-8
Result := False
end
if Result then
if l_is_incomplete_stream then
Result := a_is_stream
elseif a_is_stream then
last_utf_8_stream_validation_position := i
end
end
end
end
end