Merged changes related to websocket and restructured httpd, http_

network libraries.
This commit is contained in:
2016-10-14 13:17:17 +02:00
121 changed files with 13772 additions and 494 deletions

View File

@@ -1,5 +1,5 @@
<?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="debug" uuid="AA458565-7711-4BE1-ADA3-91716EABFA21" library_target="debug">
<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="debug" uuid="AA458565-7711-4BE1-ADA3-91716EABFA21" library_target="debug_standalone">
<target name="common" abstract="true">
<file_rule>
<exclude>/EIFGENs$</exclude>
@@ -18,7 +18,7 @@
</target>
<target name="debug_any" extends="common">
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="thread"/>
<setting name="concurrency" value="scoop"/>
<library name="cgi" location="..\..\library\server\wsf\connector\cgi-safe.ecf" readonly="false"/>
<library name="libfcgi" location="..\..\library\server\wsf\connector\libfcgi-safe.ecf" readonly="false"/>
<library name="standalone" location="..\..\library\server\wsf\connector\standalone-safe.ecf" readonly="false"/>
@@ -27,7 +27,7 @@
</target>
<target name="debug_standalone" extends="common">
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
<setting name="concurrency" value="thread"/>
<setting name="concurrency" value="scoop"/>
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false"/>
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
@@ -44,6 +44,4 @@
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
<cluster name="src" location=".\src\" recursive="true"/>
</target>
<target name="debug" extends="debug_standalone">
</target>
</system>

2
examples/debug/debug.ini Normal file
View File

@@ -0,0 +1,2 @@
port=9090
verbose=true

View File

@@ -1,5 +1,5 @@
<?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="simple" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple">
<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="simple" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple_standalone">
<target name="common" abstract="true">
<file_rule>
<exclude>/.svn$</exclude>
@@ -16,13 +16,20 @@
</target>
<target name="simple_standalone" extends="common">
<root class="APPLICATION" feature="make_and_launch"/>
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
<option debug="false" warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
<debug name="dbglog" enabled="true"/>
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="scoop"/>
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
<cluster name="simple" location=".\" recursive="true"/>
</target>
<target name="simple_standalone_mt" extends="simple_standalone">
<setting name="concurrency" value="thread"/>
</target>
<target name="simple_standalone_st" extends="simple_standalone">
<setting name="concurrency" value="none"/>
</target>
<target name="simple_cgi" extends="common">
<root class="APPLICATION" feature="make_and_launch"/>
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
@@ -39,6 +46,4 @@
<library name="default_libfcgi" location="..\..\library\server\wsf\default\libfcgi-safe.ecf"/>
<cluster name="simple" location=".\" recursive="true"/>
</target>
<target name="simple" extends="simple_standalone">
</target>
</system>

View File

@@ -2,7 +2,8 @@ verbose=true
verbose_level=ALERT
port=9090
#max_concurrent_connections=100
#keep_alive_timeout=15
keep_alive_timeout=3
#max_tcp_clients=100
#socket_timeout=300
socket_timeout=60
socket_recv_timeout=15
#max_keep_alive_requests=300

View File

@@ -17,11 +17,11 @@ port=9090
#keep_alive_timeout=15
#max_keep_alive_requests=100
### SSL settings
### Secure connection settings
# enable SSL, with file certificate.
ssl_enabled=true
ssl_ca_key=simple.key
ssl_ca_crt=simple.crt
is_secure=true
secure_certificate=ca.crt
secure_certificate_key=ca.key
### App settings
verbose=true

View File

@@ -14,16 +14,18 @@ feature {NONE} -- Initialization
make_and_launch
local
l_launcher: WSF_STANDALONE_WEBSOCKET_SERVICE_LAUNCHER [APPLICATION_EXECUTION]
opts: WSF_SERVICE_LAUNCHER_OPTIONS
opts: WSF_STANDALONE_WEBSOCKET_SERVICE_OPTIONS
do
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} opts.make_from_file ("ws.ini")
create l_launcher.make_and_launch (options)
end
create opts
if opts.is_secure_connection_supported then
opts.is_secure := True
opts.set_secure_protocol_to_tls_1_2
opts.secure_certificate := "ca.crt"
opts.secure_certificate_key := "ca.key"
end
options: WSF_SERVICE_LAUNCHER_OPTIONS
-- Initialize current service.
do
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} Result.make_from_file ("ws.ini")
opts.import_ini_file_options ("ws.ini")
create l_launcher.make_and_launch (opts)
end
end

View File

@@ -88,7 +88,7 @@ feature -- HTML Resource
<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="##HTTPSCHEME##://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
@@ -96,7 +96,7 @@ $(document).ready(function() {
function connect(){
var host = "ws://127.0.0.1:##PORTNUMBER##";
var host = "##WSSCHEME##://127.0.0.1:##PORTNUMBER##";
try{
socket = new WebSocket(host);
@@ -178,6 +178,13 @@ body {font-family:Arial, Helvetica, sans-serif;}
</html>
]"
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

15
examples/websocket/ca.crt Normal file
View File

@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICWDCCAcGgAwIBAgIJAJnXGtV+PtiYMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUwNDAzMjIxNTA0WhcNMTYwNDAyMjIxNTA0WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3HtHcKIl5wSUvn
G8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J53K0xuUlxz4d4
4D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQABo1AwTjAdBgNV
HQ4EFgQUbWpk2HoHa0YqpEwr7CGEatBFTMkwHwYDVR0jBBgwFoAUbWpk2HoHa0Yq
pEwr7CGEatBFTMkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAi+h4/
IgEocWkdRZBKHEcTrRxz5WhEDJMoVo9LhnXvCfn1G/4p6Un6sYv7Xzpi9NuSY8uV
cjfJJXhtF3AtyZ70iTAxWaRWjGaZ03PYOjlledJ5rqJEt6CCn8m+JsfznduZvbxQ
zQ6jCLXfyD/tvemB+yYEI3NntvRKx5/zt6Q26Q==
-----END CERTIFICATE-----

15
examples/websocket/ca.key Normal file
View File

@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3H
tHcKIl5wSUvnG8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J5
3K0xuUlxz4d44D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQAB
AoGAR5efMg+dieRyLU8rieJcImxVbfOPg9gRsjdtIVkXTR+RL7ow59q7hXBo/Td/
WU8cm1gXoJ/bK+71YYqWyB+BaLRIWvRWb7Gdw203tu4e136Ca5uuY+71qdbVTVcl
NQ7J+T+eAQFP+a+DdT3ZQxu9eze87SMbu6i5YSpIk2kusOECQQDunv/DQ+nc+NgR
DF+Td3sNYUVRT9a1CWi6abAG6reXwp8MS4NobWDf+Ps4JODhEEwlIdq5qL7qqYBZ
Gc1TJJ53AkEA0404Fn6vAzzegBcS4RLlYTK7nMr0m4pMmDMCI6YzAYdMmKHp1e6f
IwxSmQrmwyAgwcT01bc0+A8yipcC2BWQaQJBAJ01QZm635OGmos41KsKF5bsE8gL
SpBBH69Yu/ECqGwie7iU84FUNnO4zIHjwghlPVVlZX3Vz9o4S+fn2N9DC+cCQGyZ
QyCxGdC0r5fbwHJQS/ZQn+UGfvlVzqoXDVMVn3t6ZES6YZrT61eHnOM5qGqklIxE
Old3vDZXPt/MU8Zvk3kCQBOgUx2VxvTrHN37hk9/QIDiM62+RenBm1M3ah8xTosf
1mSeEb6d9Kwb3TgPBmA7YXzJuAQfRIvEPMPxT5SSr6Q=
-----END RSA PRIVATE KEY-----

View File

@@ -18,4 +18,7 @@
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
<cluster name="app" location=".\" recursive="true"/>
</target>
<target name="websocket_app_ssl" extends="websocket_app">
<variable name="ssl_enabled" value="true"/>
</target>
</system>

View File

@@ -4,5 +4,10 @@ port=9090
max_concurrent_connections=100
keep_alive_timeout=35
max_tcp_clients=100
socket_timeout=30000
max_keep_alive_requests=3000
socket_timeout=30
socket_recv_timeout=5
max_keep_alive_requests=300
is_secure=false
secure_certificate=ca.crt
secure_certificate_key=ca.key

View File

@@ -13,8 +13,12 @@
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
<library name="http" location="..\protocol\http\http-safe.ecf"/>
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
<library name="http_network" location="..\http_network\http_network-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
@@ -25,10 +29,14 @@
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
<cluster name="net_ssl_disabled" location="$|no_ssl\">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="net_ssl_enabled" location="$|ssl\">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>

View File

@@ -13,8 +13,12 @@
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
<library name="http" location="..\protocol\http\http.ecf"/>
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
<library name="http_network" location="..\http_network\http_network.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
@@ -25,10 +29,14 @@
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
<cluster name="net_ssl_disabled" location="$|no_ssl\">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="net_ssl_enabled" location="$|ssl\">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>

View File

@@ -11,7 +11,7 @@ create
feature {NONE} -- Initialization
make (a_socket: NETWORK_STREAM_SOCKET; a_host: READABLE_STRING_GENERAL; a_port: INTEGER)
make (a_socket: HTTP_STREAM_SOCKET; a_host: READABLE_STRING_GENERAL; a_port: INTEGER)
do
socket := a_socket
host := a_host
@@ -20,7 +20,7 @@ feature {NONE} -- Initialization
feature -- Access
socket: NETWORK_STREAM_SOCKET
socket: HTTP_STREAM_SOCKET
-- Persistent connection socket.
host: READABLE_STRING_GENERAL
@@ -40,7 +40,7 @@ feature -- Status report
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

@@ -28,11 +28,11 @@ feature {NONE} -- Internal
session: NET_HTTP_CLIENT_SESSION
net_http_client_version: STRING = "0.1"
session_socket (a_host: READABLE_STRING_8; a_port: INTEGER; a_is_https: BOOLEAN; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): NETWORK_STREAM_SOCKET
session_socket (a_host: READABLE_STRING_8; a_port: INTEGER; a_is_https: BOOLEAN; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_STREAM_SOCKET
-- Session socket to use for connection.
-- Eventually reuse the persistent connection if any.
local
l_socket: detachable NETWORK_STREAM_SOCKET
l_socket: detachable HTTP_STREAM_SOCKET
do
if
attached session.persistent_connection as l_persistent_connection and then
@@ -40,12 +40,12 @@ feature {NONE} -- Internal
then
l_socket := l_persistent_connection.socket
if a_is_https then
if attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
if attached {HTTP_STREAM_SECURE_SOCKET} l_socket as l_ssl_socket then
Result := l_ssl_socket
else
l_socket := Void
end
elseif attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
elseif attached {HTTP_STREAM_SECURE_SOCKET} l_socket as l_ssl_socket then
l_socket := Void
end
if l_socket /= Void and then not l_socket.is_connected then
@@ -59,7 +59,7 @@ feature {NONE} -- Internal
else
session.set_persistent_connection (Void)
if a_is_https then
create {SSL_NETWORK_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
create {HTTP_STREAM_SECURE_SOCKET} Result.make_client_by_port (a_port, a_host)
else
create Result.make_client_by_port (a_port, a_host)
end
@@ -81,7 +81,7 @@ feature -- Access
l_cookie: detachable READABLE_STRING_8
l_request_uri: STRING
l_url: HTTP_URL
l_socket: NETWORK_STREAM_SOCKET
l_socket: HTTP_STREAM_SOCKET
s: STRING
l_message: STRING
l_content_length: INTEGER
@@ -438,7 +438,7 @@ feature {NONE} -- Helpers
io.error.put_string (m)
end
is_ready_for_reading (a_socket: NETWORK_STREAM_SOCKET): BOOLEAN
is_ready_for_reading (a_socket: HTTP_STREAM_SOCKET): BOOLEAN
-- Is `a_socket' ready for reading?
do
Result := a_socket.ready_for_reading
@@ -518,7 +518,7 @@ feature {NONE} -- Helpers
Result := utf.utf_32_string_to_utf_8_string_8 (s)
end
put_string_using_chunked_transfer_encoding (a_string: READABLE_STRING_8; a_chunk_size: INTEGER; a_output: NETWORK_STREAM_SOCKET)
put_string_using_chunked_transfer_encoding (a_string: READABLE_STRING_8; a_chunk_size: INTEGER; a_output: HTTP_STREAM_SOCKET)
local
i,n: INTEGER
do
@@ -534,7 +534,7 @@ feature {NONE} -- Helpers
put_chunk_end (Void, Void, a_output)
end
put_chunk (a_content: READABLE_STRING_8; a_ext: detachable READABLE_STRING_8; a_output: NETWORK_STREAM_SOCKET)
put_chunk (a_content: READABLE_STRING_8; a_ext: detachable READABLE_STRING_8; a_output: HTTP_STREAM_SOCKET)
-- Write chunk non empty `a_content' to `a_output'
-- with optional extension `a_ext': chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
-- Note: that header "Transfer-Encoding: chunked" is required.
@@ -569,7 +569,7 @@ feature {NONE} -- Helpers
a_output.put_string (crlf)
end
put_chunk_end (a_ext: detachable READABLE_STRING_8; a_trailer: detachable READABLE_STRING_8; a_output: NETWORK_STREAM_SOCKET)
put_chunk_end (a_ext: detachable READABLE_STRING_8; a_trailer: detachable READABLE_STRING_8; a_output: HTTP_STREAM_SOCKET)
-- Put end of chunked content,
-- with optional extension `a_ext': chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
-- and with optional trailer `a_trailer' : trailer= *(entity-header CRLF)
@@ -595,7 +595,7 @@ feature {NONE} -- Helpers
a_output.put_string (crlf)
end
append_file_content_to_socket_using_chunked_transfer_encoding (a_file: FILE; a_len: INTEGER; a_chunk_size: INTEGER; a_output: NETWORK_STREAM_SOCKET)
append_file_content_to_socket_using_chunked_transfer_encoding (a_file: FILE; a_len: INTEGER; a_chunk_size: INTEGER; a_output: HTTP_STREAM_SOCKET)
-- Append `a_file' content as chunks of `a_chunk_size' length to `a_output'.
-- If `a_len' >= 0 then read only `a_len' characters.
require
@@ -631,7 +631,7 @@ feature {NONE} -- Helpers
end
end
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: NETWORK_STREAM_SOCKET)
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: HTTP_STREAM_SOCKET)
-- Append `a_file' content to `a_output'.
-- If `a_len' >= 0 then read only `a_len' characters.
require
@@ -704,7 +704,7 @@ feature {NONE} -- Helpers
end
end
append_socket_header_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
append_socket_header_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: HTTP_STREAM_SOCKET; a_output: STRING)
-- Get header from `a_socket' into `a_output'.
local
s: READABLE_STRING_8
@@ -714,7 +714,7 @@ feature {NONE} -- Helpers
until
s.same_string ("%R") or not a_socket.readable or a_response.error_occurred
loop
a_socket.read_line_thread_aware
a_socket.read_line_noexception
s := a_socket.last_string
if s.is_empty then
if session.is_debug_verbose then
@@ -730,7 +730,7 @@ feature {NONE} -- Helpers
end
end
append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_len: INTEGER; a_output: STRING)
append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: HTTP_STREAM_SOCKET; a_len: INTEGER; a_output: STRING)
-- Get content from `a_socket' and append it to `a_output'.
-- If `a_len' is negative, try to get as much as possible,
-- this is probably HTTP/1.0 without any Content-Length.
@@ -749,7 +749,7 @@ feature {NONE} -- Helpers
until
r = 0 or else not a_socket.readable or else a_response.error_occurred
loop
a_socket.read_stream_thread_aware (r)
a_socket.read_stream_noexception (r)
l_count := l_count + a_socket.bytes_read
if session.is_debug_verbose then
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
@@ -772,7 +772,7 @@ feature {NONE} -- Helpers
until
n < l_chunk_size or not a_socket.readable
loop
a_socket.read_stream_thread_aware (l_chunk_size)
a_socket.read_stream_noexception (l_chunk_size)
s := a_socket.last_string
n := a_socket.bytes_read
l_count := l_count + n
@@ -782,7 +782,7 @@ feature {NONE} -- Helpers
end
end
append_socket_chunked_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
append_socket_chunked_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: HTTP_STREAM_SOCKET; a_output: STRING)
-- Get chunked content from `a_socket' and append it to `a_output'.
require
socket_readable: a_socket.readable
@@ -803,7 +803,7 @@ feature {NONE} -- Helpers
until
n = 0 or not a_socket.readable
loop
a_socket.read_line_thread_aware -- Read chunk info
a_socket.read_line_noexception
s := a_socket.last_string
s.right_adjust
if session.is_debug_verbose then
@@ -832,7 +832,7 @@ feature {NONE} -- Helpers
until
r = 0 or else not a_socket.readable or else a_response.error_occurred
loop
a_socket.read_stream_thread_aware (r)
a_socket.read_stream_noexception (r)
l_count := l_count + a_socket.bytes_read
if session.is_debug_verbose then
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
@@ -842,9 +842,9 @@ feature {NONE} -- Helpers
a_output.append (a_socket.last_string)
end
a_socket.read_character
a_socket.read_character_noexception
check a_socket.last_character = '%R' end
a_socket.read_character
a_socket.read_character_noexception
check a_socket.last_character = '%N' end
if session.is_debug_verbose then
log ("Debug: - Found CRNL %N")

View File

@@ -1,22 +0,0 @@
note
description: "[
A fake SSL network stream socket... when SSL is disabled at compilation time.
Its behavior is similar to NETWORK_STREAM_SOCKET.
]"
date: "$Date$"
revision: "$Revision$"
class
SSL_NETWORK_STREAM_SOCKET
inherit
NETWORK_STREAM_SOCKET
create
make, make_empty, make_client_by_port, make_client_by_address_and_port, make_server_by_port, make_loopback_server_by_port
create {SSL_NETWORK_STREAM_SOCKET}
make_from_descriptor_and_address, create_from_descriptor
end

View File

@@ -1,6 +1,6 @@
<?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="httpd" uuid="50FE258D-CC94-4748-9223-55F1129E5FB3" library_target="httpd">
<target name="httpd">
<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="http_network" uuid="7C7AD84D-B7BD-4709-B4B2-9365B86582AE" library_target="http_network">
<target name="http_network">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
@@ -24,62 +24,50 @@
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</library>
<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="httpd_server" location=".\" recursive="true">
<file_rule>
<exclude>/concurrency$</exclude>
<exclude>/network$</exclude>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
</file_rule>
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>
<custom name="httpd_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl" location="$|ssl\" recursive="true">
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</cluster>
<cluster name="concurrency_none" location="$|concurrency\none\" recursive="true">
<condition>
<concurrency value="none"/>
</condition>
</cluster>
<cluster name="concurrency_scoop" location="$|concurrency\scoop\" recursive="true">
<condition>
<concurrency value="scoop"/>
</condition>
</cluster>
<cluster name="concurrency_thread" location="$|concurrency\thread\" recursive="true">
<condition>
<concurrency value="thread"/>
</condition>
</cluster>
</cluster>
<cluster name="network" location=".\network\">
<cluster name="network" location=".\src\">
<file_rule>
<exclude>/httpd_stream_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</file_rule>
<cluster name="disabled_ssl_network" location="$|no_ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="net_ssl_enabled" excluded_value="true"/>
<custom name="httpd_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl_network" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
<file_rule>
<exclude>/httpd_stream_ssl_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="16.11.0.0"/>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<version type="compiler" max="16.11.0.0"/>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<version type="compiler" max="16.11.0.0"/>
<custom name="httpd_ssl_enabled" value="true"/>
@@ -87,11 +75,19 @@
</file_rule>
</cluster>
</cluster>
<cluster name="network_until_16_05" location=".\network\until_16_05\" recursive="false">
<cluster name="network_until_16_05" location=".\src\until_16_05\" recursive="false">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
<cluster name="ssl_network_until_16_05" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
<version type="compiler" max="16.11.0.0"/>
</condition>
<condition>
<custom name="net_ssl_enabled" value="true"/>
<version type="compiler" max="16.11.0.0"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
<version type="compiler" max="16.11.0.0"/>

View File

@@ -1,5 +1,5 @@
<?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="http_network" uuid="56DAA1CE-0A2E-451A-BFC9-7821578E79F0" library_target="http_network">
<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="http_network" uuid="7C7AD84D-B7BD-4709-B4B2-9365B86582AE" library_target="http_network">
<target name="http_network">
<root all_classes="true"/>
<file_rule>
@@ -11,42 +11,87 @@
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="scoop"/>
<external_include location="$ECF_CONFIG_PATH/spec/include">
<condition>
<version type="compiler" min="16.11.0.0"/>
</condition>
</external_include>
<external_include location="$ECF_CONFIG_PATH/spec/include_until_16_05">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</external_include>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</library>
<cluster name="network" location=".\network\">
<cluster name="network" location=".\src\">
<file_rule>
<exclude>/httpd_stream_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</file_rule>
<cluster name="disabled_ssl_network" location="$|no_ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="net_ssl_enabled" excluded_value="true"/>
<custom name="httpd_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl_network" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
<file_rule>
<exclude>/httpd_stream_ssl_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="16.11.0.0"/>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<version type="compiler" max="16.11.0.0"/>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<version type="compiler" max="16.11.0.0"/>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</file_rule>
</cluster>
</cluster>
<cluster name="network_until_16_05" location=".\network\until_16_05\" recursive="false">
<cluster name="network_until_16_05" location=".\src\until_16_05\" recursive="false">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
<cluster name="ssl_network_until_16_05" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
<version type="compiler" max="16.11.0.0"/>
</condition>
<condition>
<custom name="net_ssl_enabled" value="true"/>
<version type="compiler" max="16.11.0.0"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
<version type="compiler" max="16.11.0.0"/>
</condition>
</cluster>
</cluster>
</target>

View File

@@ -0,0 +1,10 @@
${NOTE_KEYWORD}
copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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
]"

View File

@@ -1,15 +1,15 @@
note
description: "Summary description for {HTTPD_STREAM_SOCKET}."
description: "Summary description for {HTTP_STREAM_SOCKET}."
date: "$Date$"
revision: "$Revision$"
class
HTTPD_STREAM_SOCKET
HTTP_STREAM_SOCKET
inherit
NETWORK_STREAM_SOCKET
HTTPD_STREAM_SOCKET_EXT
HTTP_STREAM_SOCKET_EXT
create
make, make_empty,
@@ -19,6 +19,16 @@ create
create {NETWORK_STREAM_SOCKET}
make_from_descriptor_and_address
feature -- Status report
is_secure_connection_supported: BOOLEAN
-- SSL/TLS supported?
once
Result := False
ensure
Result = {HTTP_SECURE_HELPER}.is_secure_connection_supported
end
feature -- Input
read_character_noexception
@@ -27,7 +37,9 @@ feature -- Input
-- No exception raised!
do
read_to_managed_pointer_noexception (socket_buffer, 0, character_8_bytes)
if bytes_read /= character_8_bytes then
if was_error then
-- Socket error already set.
elseif bytes_read /= character_8_bytes then
socket_error := "Peer closed connection"
else
last_character := socket_buffer.read_character (0)
@@ -135,6 +147,8 @@ feature {NONE} -- Input
l_last_read := c_read_stream_noexception (descriptor, nb_bytes - l_read, p + start_pos + l_read)
if l_last_read >= 0 then
l_read := l_read + l_last_read
else
socket_error := "Network error!"
end
end
bytes_read := l_read
@@ -198,7 +212,7 @@ feature -- Output
put_managed_pointer_noexception (socket_buffer, 0, character_8_bytes)
end
put_readable_string_8_noexception (s: READABLE_STRING_8)
put_string_8_noexception (s: READABLE_STRING_8)
-- Write readable string `s' to socket.
-- No exception raised!
local
@@ -230,7 +244,7 @@ feature -- Status report
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -6,7 +6,7 @@ note
]"
deferred class
HTTPD_STREAM_SOCKET_EXT
HTTP_STREAM_SOCKET_EXT
feature {NONE} -- No-Exception network operation

View File

@@ -0,0 +1,18 @@
note
description: "[
Interface helping using SSL.
For now, mainly for `is_secure_connection_supported' to indicate if current project is compiled with SSL support.
i.e compiled with EiffelNet-SSL library.
]"
date: "$Date$"
revision: "$Revision$"
class
HTTP_SECURE_HELPER
feature -- Status
is_secure_connection_supported: BOOLEAN = False
-- Is Current system compiled with EiffelNet-SSL support?
end

View File

@@ -0,0 +1,69 @@
note
description: "[
A fake SSL network stream socket... when SSL is disabled at compilation time.
Its behavior is similar to HTTP_STREAM_SOCKET.
]"
date: "$Date$"
revision: "$Revision$"
class
HTTP_STREAM_SECURE_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 {HTTP_STREAM_SECURE_SOCKET}
make_from_descriptor_and_address
feature -- Element change
set_certificate_file_path (a_crt_filename: PATH)
do
end
set_key_file_path (a_key_filename: PATH)
do
end
feature -- SSL Helpers
set_secure_protocol (v: NATURAL)
do
end
set_secure_protocol_to_ssl_2_or_3
-- Set `ssl_protocol' with `Ssl_23'.
do
end
set_secure_protocol_to_tls_1_0
-- Set `ssl_protocol' with `Tls_1_0'.
do
end
set_secure_protocol_to_tls_1_1
-- Set `ssl_protocol' with `Tls_1_1'.
do
end
set_secure_protocol_to_tls_1_2
-- Set `ssl_protocol' with `Tls_1_2'.
do
end
set_secure_protocol_to_dtls_1_0
-- Set `ssl_protocol' with `Dtls_1_0'.
do
end
invariant
secure_connection_not_supported: not is_secure_connection_supported -- Current is a Fake SSL interface!
note
copyright: "2011-2013, 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,18 @@
note
description: "[
Interface helping using SSL.
For now, mainly for `is_secure_connection_supported' to indicate if current project is compiled with SSL support.
i.e compiled with EiffelNet-SSL library.
]"
date: "$Date$"
revision: "$Revision$"
class
HTTP_SECURE_HELPER
feature -- Status
is_secure_connection_supported: BOOLEAN = True
-- Is Current system compiled with EiffelNet-SSL support?
end

View File

@@ -4,10 +4,10 @@ note
revision: "$Revision$"
class
HTTPD_STREAM_SSL_SOCKET
HTTP_STREAM_SECURE_SOCKET
inherit
HTTPD_STREAM_SOCKET
HTTP_STREAM_SOCKET
undefine
make_empty, make_from_descriptor_and_address,
error_number,
@@ -20,6 +20,7 @@ inherit
connect, shutdown,
do_accept
redefine
is_secure_connection_supported,
put_managed_pointer,
read_stream_noexception,
read_into_pointer_noexception,
@@ -31,7 +32,7 @@ inherit
put_managed_pointer -- Redefine to allow support of compiler before 16.11.
end
HTTPD_STREAM_SSL_SOCKET_EXT
HTTP_STREAM_SECURE_SOCKET_EXT
create
make, make_empty,
@@ -41,6 +42,48 @@ create
create {SSL_NETWORK_STREAM_SOCKET}
make_from_descriptor_and_address
feature -- Status report
is_secure_connection_supported: BOOLEAN = True
-- SSL supported?
feature -- Secure connection Helpers
set_secure_protocol (v: NATURAL)
do
set_tls_protocol (v)
end
set_secure_protocol_to_ssl_2_or_3
-- Set `ssl_protocol' with `Ssl_23'.
do
set_secure_protocol ({SSL_PROTOCOL}.Ssl_23)
end
set_secure_protocol_to_tls_1_0
-- Set `ssl_protocol' with `Tls_1_0'.
do
set_secure_protocol ({SSL_PROTOCOL}.Tls_1_0)
end
set_secure_protocol_to_tls_1_1
-- Set `ssl_protocol' with `Tls_1_1'.
do
set_secure_protocol ({SSL_PROTOCOL}.Tls_1_1)
end
set_secure_protocol_to_tls_1_2
-- Set `ssl_protocol' with `Tls_1_2'.
do
set_secure_protocol ({SSL_PROTOCOL}.Tls_1_2)
end
set_secure_protocol_to_dtls_1_0
-- Set `ssl_protocol' with `Dtls_1_0'.
do
set_secure_protocol ({SSL_PROTOCOL}.Dtls_1_0)
end
feature -- Input
read_stream_noexception (nb_char: INTEGER)
@@ -91,6 +134,8 @@ feature {NONE} -- Input
l_last_read := l_ssl.read (p + start_pos + l_read, nb_bytes - l_read)
if l_last_read >= 0 then
l_read := l_read + l_last_read
else
socket_error := "Secure network error!"
end
end
bytes_read := l_read
@@ -105,7 +150,7 @@ feature -- Output
-- Put data of length `nb_bytes' pointed by `start_pos' index in `p' at
-- current position.
do
Precursor {HTTPD_STREAM_SOCKET} (p, start_pos, nb_bytes)
Precursor {HTTP_STREAM_SOCKET} (p, start_pos, nb_bytes)
end
put_pointer_content_noexception (a_pointer: POINTER; a_offset, a_byte_count: INTEGER)
@@ -130,18 +175,6 @@ feature -- Output
end
end
feature -- Element change
set_certificate_filenames (a_crt_filename, a_key_filename: detachable READABLE_STRING_GENERAL)
do
if a_crt_filename /= Void then
set_certificate_file_name (a_crt_filename)
end
if a_key_filename /= Void then
set_key_file_name (a_key_filename)
end
end
note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -1,12 +1,12 @@
note
description: "[
Extension to HTTPD_STREAM_SOCKET to support backward compatibility.
Extension to HTTP_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTPD_STREAM_SSL_SOCKET_EXT
HTTP_STREAM_SECURE_SOCKET_EXT
feature {NONE} -- SSL bridge

View File

@@ -9,7 +9,7 @@ note
]"
deferred class
HTTPD_STREAM_SOCKET_EXT
HTTP_STREAM_SOCKET_EXT
inherit
PLATFORM

View File

@@ -1,12 +1,12 @@
note
description: "[
Extension to HTTPD_STREAM_SOCKET to support backward compatibility.
Extension to HTTP_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTPD_STREAM_SSL_SOCKET_EXT
HTTP_STREAM_SECURE_SOCKET_EXT
feature {NONE} -- SSL bridge

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="ssl_enabled" value="true"/>
</target>
</system>

View File

@@ -0,0 +1,29 @@
note
description: "[
API to perform actions like opening and closing the connection, sending and receiving messages, and listening
for events triggered by the server
]"
date: "$Date$"
revision: "$Revision$"
deferred class
WEB_SOCKET_CLIENT
inherit
WEB_SOCKET_CLIENT_I
feature -- Status report
is_secure_connection_supported: BOOLEAN = False
feature -- Factory
new_socket (a_port: INTEGER; a_host: STRING): HTTP_STREAM_SOCKET
do
if is_secure then
check is_secure_connection_supported: False end
end
create {HTTP_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
end
end

View File

@@ -0,0 +1,54 @@
note
description: "[
API to perform actions like opening and closing the connection, sending and receiving messages, and listening
for events triggered by the server
]"
date: "$Date$"
revision: "$Revision$"
deferred class
WEB_SOCKET_CLIENT
inherit
WEB_SOCKET_CLIENT_I
feature -- Status report
is_secure_connection_supported: BOOLEAN = True
feature -- Factory
new_socket (a_port: INTEGER; a_host: STRING): HTTP_STREAM_SOCKET
local
l_secure: HTTP_STREAM_SECURE_SOCKET
do
if is_secure then
create l_secure.make_client_by_port (a_port, a_host)
Result := l_secure
if attached secure_protocol as l_prot then
if l_prot.is_case_insensitive_equal ("ssl_2_3") then
l_secure.set_secure_protocol_to_ssl_2_or_3
elseif l_prot.is_case_insensitive_equal ("tls_1_0") then
l_secure.set_secure_protocol_to_tls_1_0
elseif l_prot.is_case_insensitive_equal ("tls_1_1") then
l_secure.set_secure_protocol_to_tls_1_1
elseif l_prot.is_case_insensitive_equal ("tls_1_2") then
l_secure.set_secure_protocol_to_tls_1_2
elseif l_prot.is_case_insensitive_equal ("dtls_1_0") then
l_secure.set_secure_protocol_to_dtls_1_0
else -- Default
l_secure.set_secure_protocol_to_tls_1_2
end
end
if attached secure_certificate_file as c then
l_secure.set_certificate_file_path (c)
end
if attached secure_certificate_key_file as k then
l_secure.set_key_file_path (k)
end
else
create {HTTP_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
end
end
end

View File

@@ -0,0 +1,99 @@
note
description: "[
API to perform actions like opening and closing the connection, sending and receiving messages, and listening
for events triggered by the server
]"
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WEB_SOCKET
inherit
WEB_SOCKET_CONSTANTS
feature -- Access
uri: READABLE_STRING_GENERAL
-- WebSocket Protocol defines two URI schemes, ws and wss for
-- unencrypted and encrypted traffic between the client and the server.
port: INTEGER
-- Current WebSocket protocol.
ws_port_default: INTEGER = 80
-- WebSocket Protocol uses port 80 for regular WebSocket connections.
wss_port_default: INTEGER = 443
-- WebSocket connections tunneled over Transport Layer Security (TLS) use port 443.
protocols: detachable LIST [STRING]
-- List of protocol names that the client supports.
protocol: STRING
-- Client-Server protocol selected.
-- Has the result fo protocol negotiation between client and the server
-- By default it's an empty string.
is_secure: BOOLEAN
-- Is the current connection tunneled over TLS/SSL?
local
l_uri: STRING
do
l_uri := uri.as_lower.as_string_8
Result := l_uri.starts_with ("wss") -- TODO extract to ws_constants.
end
ready_state: WEB_SOCKET_READY_STATE
-- Connection state
feature -- Methods
send (a_message: STRING)
-- Send a message `a_message'
require
is_open: ready_state.is_open
deferred
end
close (a_id: INTEGER)
-- Close a websocket connection with a close id : `a_id'
deferred
ensure
is_closed: ready_state.is_closed
end
close_with_description (a_id: INTEGER; a_description: READABLE_STRING_GENERAL)
-- Close a websocket connection with a close id : `a_id' and a description `a_description'
deferred
ensure
is_closed: ready_state.is_closed
end
feature -- Change Element
set_protocol (a_protocol: STRING)
-- Set `protocol' with `a_protocol'
do
protocol := a_protocol
ensure
protocol_set: protocol = a_protocol
end
set_protocols (a_protocols: detachable ITERABLE [STRING])
local
l_protocols: LIST [STRING]
do
if a_protocols /= Void then
create {ARRAYED_LIST [STRING]} l_protocols.make (0)
across
a_protocols as ic
loop
l_protocols.force (ic.item)
end
protocols := l_protocols
end
end
end

View File

@@ -0,0 +1,603 @@
note
description: "[
API to perform actions like opening and closing the connection, sending and receiving messages, and listening
for events triggered by the server
]"
date: "$Date$"
revision: "$Revision$"
deferred class
WEB_SOCKET_CLIENT_I
inherit
WEB_SOCKET_SUBSCRIBER
redefine
on_websocket_error,
on_websocket_text_message,
on_websocket_binary_message,
on_websocket_close,
on_websocket_open,
on_websocket_ping,
on_websocket_pong
end
WEB_SOCKET
THREAD
rename
make as thread_make
end
feature -- Initialization
initialize (a_uri: READABLE_STRING_GENERAL; a_protocols: detachable ITERABLE [STRING])
-- Initialize websocket client
require
is_valid_uri: is_valid_uri (a_uri)
do
thread_make
uri := a_uri
set_default_port
create protocol.make_empty
set_protocols (a_protocols)
create ready_state.make
socket := new_socket (port, host)
create server_handshake.make
end
initialize_with_port (a_uri: READABLE_STRING_GENERAL; a_port: INTEGER; a_protocols: detachable ITERABLE [STRING])
-- Initialize websocket client
require
is_valid_uri: is_valid_uri (a_uri)
do
thread_make
uri := a_uri
port := a_port
create protocol.make_empty
set_protocols (a_protocols)
create ready_state.make
socket := new_socket (port, host)
create server_handshake.make
end
initialize_with_host_port_and_path (a_host: READABLE_STRING_GENERAL; a_port: INTEGER; a_path: READABLE_STRING_GENERAL)
require
is_valid_uri: is_valid_uri (a_host)
do
thread_make
uri := a_host + ":" + a_port.out + a_path
port := a_port
create protocol.make_empty
-- set_protocols (a_protocols)
create ready_state.make
socket := new_socket (port, host)
create server_handshake.make
end
feature -- Factory
new_socket (a_port: INTEGER; a_host: STRING): HTTP_STREAM_SOCKET
-- New socket for port `a_port' on host `a_host'.
deferred
end
feature -- Access
socket: HTTP_STREAM_SOCKET
-- Socket
has_error: BOOLEAN
do
Result := implementation.has_error
end
is_server_hanshake_accepted: BOOLEAN
is_valid_uri (a_uri: READABLE_STRING_GENERAL): BOOLEAN
-- Is `a_uri' a valid URI?
local
l_uri: URI
do
create l_uri.make_from_string (a_uri.as_string_8)
Result := l_uri.is_valid
end
server_handshake: WEB_SOCKET_HANDSHAKE_DATA
-- Handshake data received from the server
feature -- Access: secure
is_secure_connection_supported: BOOLEAN
-- Is SSL supported?
deferred
end
secure_protocol: detachable READABLE_STRING_GENERAL
-- SSL protocol , if `is_secure_connection_supported'.
secure_certificate_file: detachable PATH
-- SSL certificate file , if `is_secure_connection_supported'.
secure_certificate_key_file: detachable PATH
-- SSL key file , if `is_secure_connection_supported'.
feature -- Element change
set_secure_protocol (a_prot: like secure_protocol)
do
secure_protocol := a_prot
end
set_secure_certificate_file (p: detachable PATH)
-- Set SSL certificate from file at `p'.
do
secure_certificate_file := p
end
set_secure_certificate_key_file (p: detachable PATH)
-- Set SSL key from file at `p'.
do
secure_certificate_key_file := p
end
feature -- Events API
on_open (a_message: STRING)
deferred
end
on_text_message (a_message: STRING)
deferred
end
on_binary_message (a_message: STRING)
deferred
end
on_close (a_code: INTEGER; a_reason: STRING)
deferred
end
on_error (a_error: STRING)
deferred
end
feature -- Subscriber Events
on_websocket_handshake (a_request: STRING)
-- Send handshake message
do
socket.put_string (a_request)
end
on_websocket_text_message (a_message: STRING)
do
on_text_message (a_message)
end
on_websocket_binary_message (a_message: STRING)
do
on_binary_message (a_message)
end
on_websocket_open (a_message: STRING)
do
on_open (a_message)
end
on_websocket_close (a_message: STRING)
do
close_with_description (1000, a_message)
on_close (1000, a_message)
end
on_websocket_error (a_error: STRING)
do
on_error (a_error)
close_with_description (1002,"")
end
on_websocket_ping (a_message: STRING)
do
do_send (10, a_message)
end
on_websocket_pong (a_message: STRING)
do
-- do_send (9, a_message)
end
feature -- Execute
execute
require else
is_socket_valid: socket.exists
do
set_implementation
socket.connect
check
socket_connected: socket.is_connected
end
send_handshake
receive_handshake
if is_server_hanshake_accepted then
ready_state.set_state ({WEB_SOCKET_READY_STATE}.open)
on_websocket_open ("Open Connection")
from
until
ready_state.is_closed or has_error
loop
receive
end
else
on_websocket_error ("Server Handshake not accepted")
--log(Not connected)
socket.close
end
rescue
on_websocket_close ("")
socket.close
end
feature -- Methods
send (a_message: STRING)
do
do_send (1, a_message)
end
send_binary (a_message: STRING)
do
do_send (2, a_message)
end
close (a_id: INTEGER)
-- Close a websocket connection with a close id : `a_id'
do
do_send (8, "")
ready_state.set_state ({WEB_SOCKET_READY_STATE}.closed)
end
close_with_description (a_id: INTEGER; a_description: READABLE_STRING_GENERAL)
-- Close a websocket connection with a close id : `a_id' and a description `a_description'
do
do_send (8, "")
ready_state.set_state ({WEB_SOCKET_READY_STATE}.closed)
end
feature {NONE} -- Implementation
set_implementation
do
create implementation.make_with_protocols_and_port (Current, host, protocols, port)
end
send_handshake
local
l_uri: URI
l_handshake: STRING
l_random: SALT_XOR_SHIFT_64_GENERATOR
l_secure_protocol: STRING
do
create l_uri.make_from_string (uri.as_string_8)
create l_handshake.make_empty
if l_uri.path.is_empty then
l_handshake.append ("GET / HTTP/1.1")
l_handshake.append (crlf)
elseif l_uri.query = Void then
l_handshake.append ("GET " + l_uri.path + " HTTP/1.1")
l_handshake.append (crlf)
else
if attached l_uri.query as l_query then
l_handshake.append ("GET " + l_uri.path + "?" + l_query + " HTTP/1.1")
l_handshake.append (crlf)
end
end
if attached l_uri.host as l_host then
l_handshake.replace_substring_all ("$host", l_host)
l_handshake.append ("Host: " + l_host + ":" + port.out)
l_handshake.append (crlf)
end
l_handshake.append_string ("Upgrade: websocket")
l_handshake.append (crlf)
l_handshake.append_string ("Connection: Upgrade")
l_handshake.append (crlf)
l_handshake.append_string ("Sec-WebSocket-Key: ")
create l_random.make (16)
l_handshake.append_string (base64_encode_array (l_random.new_sequence))
l_handshake.append (crlf)
if attached protocols as l_protocols then
create l_secure_protocol.make_empty
across
l_protocols as c
loop
l_secure_protocol.append (c.item)
l_secure_protocol.append (" ,")
end
l_secure_protocol.remove_tail (1)
l_handshake.append_string ("Sec-WebSocket-Protocol:" + l_secure_protocol)
l_handshake.append (crlf)
end
l_handshake.append_string ("Sec-WebSocket-Version: 13")
l_handshake.append (crlf)
l_handshake.append (crlf)
implementation.start_handshake (l_handshake)
end
receive_handshake
do
analyze_request_message
if server_handshake.request_header.has_substring ("HTTP/1.1 101") and then attached server_handshake.request_header_map.item ("Upgrade") as l_upgrade_key and then -- Upgrade header must be present with value websocket
l_upgrade_key.is_case_insensitive_equal ("websocket") and then attached server_handshake.request_header_map.item ("Connection") as l_connection_key and then -- Connection header must be present with value Upgrade
l_connection_key.has_substring ("Upgrade")
then
is_server_hanshake_accepted := True
if attached server_handshake.request_header_map.item ("Sec-WebSocet-Protocol") as l_protocol then
set_protocol (l_protocol)
end
end
end
receive
do
implementation.receive
end
set_default_port
do
if is_secure then
port := wss_port_default
else
port := ws_port_default
end
end
client_handshake_required_template: STRING = "[
GET $resource HTTP/1.1
Host: $host
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: $key
Sec-WebSocket-Version: 13
]"
base64_encode_array (a_sequence: ARRAY [NATURAL_8]): STRING_8
-- Encode a byte array `a_sequence' into Base64 notation.
local
l_result: STRING
l_base_64: BASE64
do
create l_result.make_empty
across
a_sequence as i
loop
l_result.append_character (i.item.to_character_8)
end
create l_base_64
Result := l_base_64.encoded_string (l_result)
end
host: STRING
local
l_uri: URI
do
create Result.make_empty
create l_uri.make_from_string (uri.as_string_8)
if attached l_uri.host as l_host then
Result := l_host
end
end
feature -- Parse Request line
analyze_request_message
-- Analyze message extracted from `socket' as HTTP request
require
input_readable: socket /= Void and then socket.is_open_read
local
end_of_stream: BOOLEAN
pos, n: INTEGER
line: detachable STRING
k, val: STRING
txt: STRING
do
create txt.make (64)
server_handshake.set_request_header (txt)
if attached next_line as l_request_line and then not l_request_line.is_empty then
txt.append (l_request_line)
txt.append_character ('%N')
else
server_handshake.mark_error
end
-- l_is_verbose := is_verbose
if not server_handshake.has_error then -- or l_is_verbose then
-- if `is_verbose' we can try to print the request, even if it is a bad HTTP request
from
line := next_line
until
line = Void or end_of_stream
loop
n := line.count
-- if l_is_verbose then
-- log (line)
-- end
pos := line.index_of (':', 1)
if pos > 0 then
k := line.substring (1, pos - 1)
if line [pos + 1].is_space then
pos := pos + 1
end
if line [n] = '%R' then
n := n - 1
end
val := line.substring (pos + 1, n)
server_handshake.put_header (k, val)
end
txt.append (line)
txt.append_character ('%N')
if line.is_empty or else line [1] = '%R' then
end_of_stream := True
else
line := next_line
end
end
end
end
next_line: detachable STRING
-- Next line fetched from `socket' is available.
require
is_readable: socket.is_open_read
do
if socket.socket_ok then
socket.read_line_thread_aware
Result := socket.last_string
end
end
feature -- {WEB_SOCKET_CLIENT}
-- do_send (a_opcode: NATURAL_32; a_message: STRING)
-- local
-- l_chunks: INTEGER
-- i: INTEGER
-- l_index: INTEGER
-- l_chunk_size: INTEGER
-- l_key: STRING
-- l_message: STRING
-- l_frame : STRING
-- do
-- print ("%NMessage count:" + a_message.count.out)
-- create l_frame.make_empty
-- create l_message.make_empty
-- if a_message.count > 65535 then
-- --!Improve. this code need to be checked.
-- print("%N Case:1")
-- l_frame.append_code ((0x80 | a_opcode).to_natural_32)
-- l_frame.append_code ((0x80 | 127).to_natural_32)
-- l_frame.append_code (0x0)
-- l_frame.append_code (0x0)
-- l_frame.append_code (0x0)
-- l_frame.append_code (0x0)
-- l_frame.append_code (((a_message.count) |>> 32).to_character_8.code.as_natural_32)
-- l_frame.append_code (((a_message.count) |>> 16).to_character_8.code.as_natural_32)
-- l_frame.append_code (((a_message.count ) |>> 8).to_character_8.code.as_natural_32)
-- l_frame.append_code ((a_message.count).to_character_8.code.as_natural_32)
-- elseif a_message.count > 125 then
-- print("%N Case:2")
-- print ("Message count:" + a_message.count.out)
-- l_frame.append_code ((0x80 | a_opcode).to_natural_32)
-- l_frame.append_code ((0x80 | 126).to_natural_32)
-- l_frame.append_code (((a_message.count ) |>> 8).as_natural_32)
-- l_frame.append_code ((a_message.count).to_character_8.code.as_natural_32)
-- print ("%NHeaderMessage:" + l_frame)
-- else
-- print("%N Case:3")
-- print ("Message count:" + a_message.count.out)
-- l_frame.append_code ((0x80 | a_opcode).to_natural_32)
-- l_frame.append_code ((0x80 | a_message.count).to_natural_32)
-- end
--
-- l_key := new_key
-- l_frame.append (l_key.substring (1, 4))
-- l_message := implementation.unmmask (a_message, l_key.substring (1, 4))
-- l_frame.append (l_message)
-- socket.send_message (l_frame)
-- end
do_send (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
l_message : STRING
l_key: STRING
do
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 ((0x80 | 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 ((0x80 | 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 ((0x80 | n).as_natural_32)
end
l_key := new_key
l_header_message.append (l_key.substring (1, 4))
socket.put_string (l_header_message)
l_message := implementation.unmmask (a_message, l_key.substring (1, 4))
l_chunk_size := 16_384 -- 16K
if l_message_count < l_chunk_size then
socket.put_string (l_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 := l_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
socket.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
new_key: STRING
local
l_random: SALT_XOR_SHIFT_64_GENERATOR
do
create Result.make_empty
create l_random.make (4)
across
l_random.new_sequence as i
loop
Result.append_integer (i.item)
end
end
implementation: WEB_SOCKET_IMPL
-- Web Socket implementation
crlf: STRING = "%R%N"
end

View File

@@ -0,0 +1,90 @@
note
description: "Summary description for {WEB_SOCKET_HANDSHAKE_DATA}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_HANDSHAKE_DATA
create
make
feature {NONE} -- Initialization
make
do
reset
end
reset
do
has_error := False
version := Void
create method.make_empty
create uri.make_empty
create request_header.make_empty
create request_header_map.make (10)
end
feature -- Access
request_header: STRING
-- Header' source
request_header_map: HASH_TABLE [STRING, STRING]
-- Contains key:value of the header
has_error: BOOLEAN
-- Error occurred during `analyze_request_message'
method: STRING
-- http verb
uri: STRING
-- http endpoint
version: detachable STRING
-- http_version
feature -- Change Element
set_method (a_method: STRING)
-- Set `method' with `a_method'
do
method := a_method
ensure
method_set: method = a_method
end
set_version (a_version: STRING)
-- Set `version' with `a_version'
do
version := a_version
ensure
version_set: attached version as l_version implies l_version = a_version
end
mark_error
do
has_error := True
end
set_request_header (a_header: STRING)
-- Set `request_header' with `a_header'
do
request_header := a_header
ensure
request_header_set: request_header = a_header
end
put_header (a_key: STRING; a_value : STRING)
do
request_header_map.put (a_value, a_key)
end
end

View File

@@ -0,0 +1,739 @@
note
description: "Summary description for {WEB_SOCKET_IMPL}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_IMPL
inherit
WEB_SOCKET
create
make, make_with_port, make_with_protocols, make_with_protocols_and_port
feature {NONE} -- Initialization
make (a_subscriber: WEB_SOCKET_SUBSCRIBER; a_uri: READABLE_STRING_GENERAL)
-- Create a websocket instante with a default port.
do
reset
subscriber := a_subscriber
uri := a_uri
create protocol.make_empty
set_default_port
create ready_state.make
ensure
uri_set: a_uri = uri
port_wss: is_secure implies port = wss_port_default
port_ws: not is_secure implies port = ws_port_default
ready_state_set: ready_state.state = {WEB_SOCKET_READY_STATE}.connecting
subscriber_set: subscriber = a_subscriber
protocol_set: protocol.is_empty
end
make_with_port (a_subscriber: WEB_SOCKET_SUBSCRIBER; a_uri: READABLE_STRING_GENERAL; a_port: INTEGER)
-- Create a websocket instance with port `a_port',
do
make (a_subscriber, a_uri)
port := a_port
ensure
uri_set: a_uri = uri
port_set: port = a_port
ready_state_set: ready_state.state = {WEB_SOCKET_READY_STATE}.connecting
subscriber_set: subscriber = a_subscriber
end
make_with_protocols (a_subscriber: WEB_SOCKET_SUBSCRIBER; a_uri: READABLE_STRING_GENERAL; a_protocols: detachable LIST [STRING])
-- Create a web socket instance with a list of protocols `a_protocols' and default port.
do
reset
subscriber := a_subscriber
uri := a_uri
create protocol.make_empty
protocols := a_protocols
set_default_port
create ready_state.make
ensure
uri_set: a_uri = uri
port_wss: is_secure implies port = wss_port_default
port_ws: not is_secure implies port = ws_port_default
protocols_set: protocols = a_protocols
ready_state_set: ready_state.state = {WEB_SOCKET_READY_STATE}.connecting
subscriber_set: subscriber = a_subscriber
protocol_set: protocol.is_empty
end
make_with_protocols_and_port (a_subscriber: WEB_SOCKET_SUBSCRIBER; a_uri: READABLE_STRING_GENERAL; a_protocols: detachable LIST [STRING]; a_port: INTEGER)
-- Create a web socket instance with a list of protocols `a_protocols' and port `a_port'.
do
make_with_protocols (a_subscriber, a_uri, a_protocols)
port := a_port
ensure
uri_set: a_uri = uri
protocols_set: protocols = a_protocols
port_set: port = a_port
ready_state_set: ready_state.state = {WEB_SOCKET_READY_STATE}.connecting
subscriber_set: subscriber = a_subscriber
end
reset
do
has_error := False
end
feature -- Access
has_error: BOOLEAN
is_verbose: BOOLEAN
feature -- Handshake
start_handshake (a_handshake: STRING)
do
subscriber.on_websocket_handshake (a_handshake)
end
feature -- Receive
receive
local
l_frame: detachable WEB_SOCKET_FRAME
l_client_message: detachable READABLE_STRING_8
do
l_frame := next_frame (subscriber.connection)
if l_frame /= Void and then l_frame.is_valid then
if attached l_frame.injected_control_frames as l_injections then
-- Process injected control frames now.
-- FIXME
across
l_injections as ic
loop
if ic.item.is_connection_close then
-- FIXME: we should probably send this event .. after the `l_frame.parent' frame event.
subscriber.on_websocket_close ("Normal Close")
elseif ic.item.is_ping then
-- FIXME reply only to the most recent ping ...
subscriber.on_websocket_ping (ic.item.payload_data)
elseif ic.item.is_pong then
subscriber.on_websocket_pong (ic.item.payload_data)
else
subscriber.on_websocket_error ("Wrong Opcode")
end
end
end
l_client_message := l_frame.payload_data
if l_client_message = Void then
l_client_message := ""
end
debug ("ws")
print ("%NExecute: %N")
print (" [opcode: " + opcode_name (l_frame.opcode) + "]%N")
if l_frame.is_text then
print (" [client message: %"" + l_client_message + "%"]%N")
elseif l_frame.is_binary then
print (" [client binary message length: %"" + l_client_message.count.out + "%"]%N")
end
print (" [is_control: " + l_frame.is_control.out + "]%N")
print (" [is_binary: " + l_frame.is_binary.out + "]%N")
print (" [is_text: " + l_frame.is_text.out + "]%N")
end
if l_frame.is_connection_close then
subscriber.on_websocket_close ("Normal Close")
elseif l_frame.is_binary then
subscriber.on_websocket_binary_message (l_client_message)
elseif l_frame.is_text then
subscriber.on_websocket_text_message (l_client_message)
elseif l_frame.is_ping then
subscriber.on_websocket_ping (l_client_message)
elseif l_frame.is_pong then
subscriber.on_websocket_pong (l_client_message)
else
subscriber.on_websocket_error ("Wrong Opcode")
end
else
debug ("ws")
print ("%NExecute: %N")
print (" [ERROR: invalid frame]%N")
if l_frame /= Void and then attached l_frame.error as err then
print (" [Code: " + err.code.out + "]%N")
print (" [Description: " + err.description + "]%N")
end
end
subscriber.on_websocket_close ("")
end
-- if l_utf.is_valid_utf_8_string_8 (l_message) then
-- if is_data_frame_ok then
-- if opcode = text_frame then
-- subscriber.on_websocket_text_message (l_message)
-- elseif opcode = binary_frame then
-- subscriber.on_websocket_binary_message (l_message)
-- elseif opcode = ping_frame then
-- subscriber.on_websocket_ping (l_message)
-- elseif opcode = pong_frame then
-- subscriber.on_websocket_pong (l_message)
-- elseif opcode = Connection_close_frame then
-- subscriber.on_websocket_close ("Normal Close")
-- elseif opcode = ping_frame then
-- subscriber.on_websocket_ping (l_message)
-- elseif opcode = pong_frame then
-- subscriber.on_websocket_pong (l_message)
-- else
-- subscriber.on_websocket_error ("Wrong Opcode")
-- end
-- else
-- subscriber.on_websocket_close ("Invalid data frame")
-- end
-- else
-- subscriber.on_websocket_error ("Invalid UTF-8")
-- end
end
feature -- Methods
send (a_message: STRING)
do
end
close (a_id: INTEGER)
-- Close a websocket connection with a close id : `a_id'
do
end
close_with_description (a_id: INTEGER; a_description: READABLE_STRING_GENERAL)
-- Close a websocket connection with a close id : `a_id' and a description `a_description'
do
end
feature {NONE} -- Implementation
set_default_port
do
if is_secure then
port := wss_port_default
else
port := ws_port_default
end
end
subscriber: WEB_SOCKET_SUBSCRIBER
next_frame (a_socket: HTTP_STREAM_SOCKET): detachable WEB_SOCKET_FRAME
-- TODO Binary messages
-- Handle error responses in a better way.
-- IDEA:
-- class FRAME
-- is_fin: BOOLEAN
-- opcode: WEB_SOCKET_STATUS_CODE (TEXT, BINARY, CLOSE, CONTINUE,PING, PONG)
-- data/payload
-- status_code: #see Status Codes http://tools.ietf.org/html/rfc6455#section-7.3
-- has_error
--
-- 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 ... |
-- +---------------------------------------------------------------+
note
EIS: "name=WebSocket RFC", "protocol=URI", "src=http://tools.ietf.org/html/rfc6455#section-5.2"
require
a_socket_in_blocking_mode: a_socket.is_blocking
local
l_opcode: INTEGER
l_len: INTEGER
l_remaining_len: INTEGER
l_payload_len: NATURAL_64
l_chunk: STRING
l_rsv: BOOLEAN
l_fin: BOOLEAN
l_has_mask: BOOLEAN
l_chunk_size: INTEGER
l_byte: INTEGER
l_fetch_count: INTEGER
l_bytes_read: INTEGER
s: STRING
is_data_frame_ok: BOOLEAN -- Is the last process data framing ok?
retried: BOOLEAN
do
if not retried then
debug ("ws")
print ("next_frame:%N")
end
from
is_data_frame_ok := True
until
l_fin or not is_data_frame_ok
loop
-- multi-frames or continue is only valid for Binary or Text
s := next_bytes (a_socket, 1)
if s.is_empty then
is_data_frame_ok := False
debug ("ws")
print ("[ERROR] incomplete_data!%N")
end
else
l_byte := s [1].code
debug ("ws")
print (" fin,rsv(3),opcode(4)=")
print (to_byte_representation (l_byte))
print ("%N")
end
l_fin := l_byte & (0b10000000) /= 0
l_rsv := l_byte & (0b01110000) = 0
l_opcode := l_byte & 0b00001111
if Result /= Void then
if l_opcode = Result.opcode then
-- should not occur in multi-fragment frame!
create Result.make (l_opcode, l_fin)
Result.report_error (protocol_error, "Unexpected injected frame")
elseif l_opcode = continuation_frame then
-- Expected
Result.update_fin (l_fin)
elseif is_control_frame (l_opcode) then
-- Control frames (see Section 5.5) MAY be injected in the middle of
-- a fragmented message. Control frames themselves MUST NOT be fragmented.
-- if the l_opcode is a control frame then there is an error!!!
-- CLOSE, PING, PONG
create Result.make_as_injected_control (l_opcode, Result)
else
-- should not occur in multi-fragment frame!
create Result.make (l_opcode, l_fin)
Result.report_error (protocol_error, "Unexpected frame")
end
else
create Result.make (l_opcode, l_fin)
if Result.is_continuation then
-- Continuation frame is not expected without parent frame!
Result.report_error (protocol_error, "There is no message to continue!")
end
end
if Result.is_valid then
--| valid frame/fragment
if is_verbose then
log ("+ frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")")
end
-- rsv validation
if not l_rsv then
-- RSV1, RSV2, RSV3: 1 bit each
-- MUST be 0 unless an extension is negotiated that defines meanings
-- for non-zero values. If a nonzero value is received and none of
-- the negotiated extensions defines the meaning of such a nonzero
-- value, the receiving endpoint MUST _Fail the WebSocket
-- Connection_
-- FIXME: add support for extension ?
Result.report_error (protocol_error, "RSV values MUST be 0 unless an extension is negotiated that defines meanings for non-zero values")
end
else
if is_verbose then
log ("+ INVALID frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")")
end
end
-- At the moment only TEXT, (pending Binary)
if Result.is_valid then
if Result.is_text or Result.is_binary or Result.is_control then
-- Reading next byte (mask+payload_len)
s := next_bytes (a_socket, 1)
if s.is_empty then
Result.report_error (invalid_data, "Incomplete data for mask and payload len")
else
l_byte := s [1].code
debug ("ws")
print (" mask,payload_len(7)=")
print (to_byte_representation (l_byte))
io.put_new_line
end
l_has_mask := l_byte & (0b10000000) /= 0 -- MASK
l_len := l_byte & 0b01111111 -- 7bits
debug ("ws")
print (" payload_len=" + l_len.out)
io.put_new_line
end
if Result.is_control and then l_len > 125 then
-- All control frames MUST have a payload length of 125 bytes or less
-- and MUST NOT be fragmented.
Result.report_error (protocol_error, "Control frame MUST have a payload length of 125 bytes or less")
elseif l_len = 127 then -- TODO proof of concept read 8 bytes.
-- the following 8 bytes interpreted as a 64-bit unsigned integer
-- (the most significant bit MUST be 0) are the payload length.
-- Multibyte length quantities are expressed in network byte order.
s := next_bytes (a_socket, 8) -- 64 bits
debug ("ws")
print (" extended payload length=" + string_to_byte_representation (s))
io.put_new_line
end
if s.count < 8 then
Result.report_error (Invalid_data, "Incomplete data for 64 bit Extended payload length")
else
l_payload_len := s [8].natural_32_code.to_natural_64
l_payload_len := l_payload_len | (s [7].natural_32_code.to_natural_64 |<< 8)
l_payload_len := l_payload_len | (s [6].natural_32_code.to_natural_64 |<< 16)
l_payload_len := l_payload_len | (s [5].natural_32_code.to_natural_64 |<< 24)
l_payload_len := l_payload_len | (s [4].natural_32_code.to_natural_64 |<< 32)
l_payload_len := l_payload_len | (s [3].natural_32_code.to_natural_64 |<< 40)
l_payload_len := l_payload_len | (s [2].natural_32_code.to_natural_64 |<< 48)
l_payload_len := l_payload_len | (s [1].natural_32_code.to_natural_64 |<< 56)
end
elseif l_len = 126 then
s := next_bytes (a_socket, 2) -- 16 bits
debug ("ws")
print (" extended payload length bits=" + string_to_byte_representation (s))
io.put_new_line
end
if s.count < 2 then
Result.report_error (Invalid_data, "Incomplete data for 16 bit Extended payload length")
else
l_payload_len := s [2].natural_32_code.to_natural_64
l_payload_len := l_payload_len | (s [1].natural_32_code.to_natural_64 |<< 8)
end
else
l_payload_len := l_len.to_natural_64
end
debug ("ws")
print (" Full payload length=" + l_payload_len.out)
io.put_new_line
end
if Result.is_valid then
if l_has_mask then
Result.report_error (protocol_error, "Server sent a masked frame!")
else
end
if Result.is_valid then
l_chunk_size := 0x4000 -- 16 K
if l_payload_len > {INTEGER_32}.max_value.to_natural_64 then
-- Issue .. to big to store in STRING
-- FIXME !!!
Result.report_error (Message_too_large, "Can not handle payload data (len=" + l_payload_len.out + ")")
else
l_len := l_payload_len.to_integer_32
end
from
l_fetch_count := 0
l_remaining_len := l_len
until
l_fetch_count >= l_len or l_len = 0 or not Result.is_valid
loop
if l_remaining_len < l_chunk_size then
l_chunk_size := l_remaining_len
end
a_socket.read_stream (l_chunk_size)
l_bytes_read := a_socket.bytes_read
debug ("ws")
print ("read chunk size=" + l_chunk_size.out + " fetch_count=" + l_fetch_count.out + " l_len=" + l_len.out + " -> " + l_bytes_read.out + "bytes%N")
end
if a_socket.bytes_read > 0 then
l_remaining_len := l_remaining_len - l_bytes_read
l_chunk := a_socket.last_string
l_fetch_count := l_fetch_count + l_bytes_read
Result.append_payload_data_chop (l_chunk, l_bytes_read, l_remaining_len = 0)
else
Result.report_error (internal_error, "Issue reading payload data...")
end
end
if is_verbose then
log (" Received " + l_fetch_count.out + " out of " + l_len.out + " bytes <===============")
end
debug ("ws")
print (" -> ")
if attached Result.payload_data as l_payload_data then
s := l_payload_data.tail (l_fetch_count)
if s.count > 50 then
print (string_to_byte_hexa_representation (s.head (50) + ".."))
else
print (string_to_byte_hexa_representation (s))
end
print ("%N")
if Result.is_text and Result.is_fin and Result.fragment_count = 0 then
print (" -> ")
if s.count > 50 then
print (s.head (50) + "..")
else
print (s)
end
print ("%N")
end
end
end
end
end
end
end
end
end
if Result /= Void then
if attached Result.error as err then
if is_verbose then
log (" !Invalid frame: " + err.string)
end
end
if Result.is_injected_control then
if attached Result.parent as l_parent then
if not Result.is_valid then
l_parent.report_error (protocol_error, "Invalid injected frame")
end
if Result.is_connection_close then
-- Return this and process the connection close right away!
l_parent.update_fin (True)
l_fin := Result.is_fin
else
Result := l_parent
l_fin := l_parent.is_fin
check
-- This is a control frame but occurs in fragmented frame.
inside_fragmented_frame: not l_fin
end
end
else
check
has_parent: False
end
l_fin := False -- This is a control frame but occurs in fragmented frame.
end
end
if not Result.is_valid then
is_data_frame_ok := False
end
else
is_data_frame_ok := False
end
end
end
rescue
retried := True
if Result /= Void then
Result.report_error (internal_error, "Internal error")
end
retry
end
next_bytes (a_socket: HTTP_STREAM_SOCKET; nb: INTEGER): STRING
require
nb > 0
local
n, l_bytes_read: INTEGER
do
create Result.make (nb)
from
n := nb
until
n = 0
loop
a_socket.read_stream (nb)
l_bytes_read := a_socket.bytes_read
if l_bytes_read > 0 then
Result.append (a_socket.last_string)
n := n - l_bytes_read
else
n := 0
end
end
end
unmask (a_chunk: STRING_8; a_pos: INTEGER; a_key: READABLE_STRING_8)
local
i, n: INTEGER
do
from
i := 1
n := a_chunk.count
until
i > n
loop
a_chunk.put_code (a_chunk.code (i).bit_xor (a_key [((i + (a_pos - 1) - 1) \\ 4) + 1].natural_32_code), i)
i := i + 1
end
end
append_chunk_unmasked (a_chunk: READABLE_STRING_8; a_pos: INTEGER; a_key: READABLE_STRING_8; a_target: STRING)
-- To convert masked data into unmasked data, or vice versa, the following
-- algorithm is applied. The same algorithm applies regardless of the
-- direction of the translation, e.g., the same steps are applied to
-- mask the data as to unmask the data.
-- Octet i of the transformed data ("transformed-octet-i") is the XOR of
-- octet i of the original data ("original-octet-i") with octet at index
-- i modulo 4 of the masking key ("masking-key-octet-j"):
-- j = i MOD 4
-- transformed-octet-i = original-octet-i XOR masking-key-octet-j
-- The payload length, indicated in the framing as frame-payload-length,
-- does NOT include the length of the masking key. It is the length of
-- the "Payload data", e.g., the number of bytes following the masking
-- key.
note
EIS: "name=Masking", "src=http://tools.ietf.org/html/rfc6455#section-5.3", "protocol=uri"
local
i, n: INTEGER
do
-- debug ("ws")
-- print ("append_chunk_unmasked (%"" + string_to_byte_representation (a_chunk) + "%",%N%Ta_pos=" + a_pos.out+ ", a_key, a_target #.count=" + a_target.count.out + ")%N")
-- end
from
i := 1
n := a_chunk.count
until
i > n
loop
a_target.append_code (a_chunk.code (i).bit_xor (a_key [((i + (a_pos - 1) - 1) \\ 4) + 1].natural_32_code))
i := i + 1
end
end
feature {NONE} -- Debug
log (m: STRING)
do
io.put_string (m + "%N")
end
to_byte (a_integer: INTEGER): ARRAY [INTEGER]
require
valid: a_integer >= 0 and then a_integer <= 255
local
l_val: INTEGER
l_index: INTEGER
do
create Result.make_filled (0, 1, 8)
from
l_val := a_integer
l_index := 8
until
l_val < 2
loop
Result.put (l_val \\ 2, l_index)
l_val := l_val // 2
l_index := l_index - 1
end
Result.put (l_val, l_index)
end
feature -- Masking
unmmask (a_frame: READABLE_STRING_8; a_key: READABLE_STRING_8): STRING
-- To convert masked data into unmasked data, or vice versa, the following
-- algorithm is applied. The same algorithm applies regardless of the
-- direction of the translation, e.g., the same steps are applied to
-- mask the data as to unmask the data.
-- Octet i of the transformed data ("transformed-octet-i") is the XOR of
-- octet i of the original data ("original-octet-i") with octet at index
-- i modulo 4 of the masking key ("masking-key-octet-j"):
-- j = i MOD 4
-- transformed-octet-i = original-octet-i XOR masking-key-octet-j
-- The payload length, indicated in the framing as frame-payload-length,
-- does NOT include the length of the masking key. It is the length of
-- the "Payload data", e.g., the number of bytes following the masking
-- key.
note
EIS: "name=Masking", "src=S", "protocol=uri"
local
l_frame: STRING
i: INTEGER
do
l_frame := a_frame.twin
from
i := 1
until
i > l_frame.count
loop
l_frame [i] := (l_frame [i].code.to_integer_8.bit_xor (a_key [((i - 1) \\ 4) + 1].code.to_integer_8)).to_character_8
i := i + 1
end
Result := l_frame
end
to_byte_representation (a_integer: INTEGER): STRING
require
valid: a_integer >= 0 and then a_integer <= 255
local
l_val: INTEGER
do
create Result.make (8)
from
l_val := a_integer
until
l_val < 2
loop
Result.prepend_integer (l_val \\ 2)
l_val := l_val // 2
end
Result.prepend_integer (l_val)
end
string_to_byte_representation (s: STRING): STRING
require
valid: s.count > 0
local
i, n: INTEGER
do
n := s.count
create Result.make (8 * n)
if n > 0 then
from
i := 1
until
i > n
loop
if not Result.is_empty then
Result.append_character (':')
end
Result.append (to_byte_representation (s [i].code))
i := i + 1
end
end
end
string_to_byte_hexa_representation (s: STRING): STRING
local
i, n: INTEGER
c: INTEGER
do
n := s.count
create Result.make (8 * n)
if n > 0 then
from
i := 1
until
i > n
loop
if not Result.is_empty then
Result.append_character (':')
end
c := s [i].code
check
c <= 0xFF
end
Result.append_character (((c |>> 4) & 0xF).to_hex_character)
Result.append_character (((c) & 0xF).to_hex_character)
i := i + 1
end
end
end
end

View File

@@ -0,0 +1,30 @@
note
description: "[
Objects that is used to create dummy web socket client object
used for void-safety
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_NULL_CLIENT
inherit
WEB_SOCKET_SUBSCRIBER
feature -- Handshake
on_websocket_handshake (request: STRING)
-- Default do nothing
do
end
feature -- TCP connection
connection: HTTP_STREAM_SOCKET
do
create Result.make_client_by_port (0, "null")
end
end

View File

@@ -0,0 +1,120 @@
note
description: "Report the state of the connection: [CONNECTING, OPEN, CLOSING, CLOSED]"
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_READY_STATE
create
make
feature -- Initialization
make
do
set_state (Connecting)
ensure
state_connecting: state = Connecting
end
feature -- Access
state: INTEGER
-- Current ready state
feature -- Status Report
is_valid_state (a_state: INTEGER): BOOLEAN
-- Is `a_state' a valid ready state?
do
Result := Connecting = a_state or else Open = a_state or else Closing = a_state or else Closed = a_state
end
is_connecting: BOOLEAN
-- Is current `state' connecting?
do
Result := state = Connecting
end
is_open: BOOLEAN
-- Is current `state' open?
do
Result := state = Open
end
is_closing: BOOLEAN
-- Is current `state' closing?
do
Result := state = Closing
end
is_closed: BOOLEAN
-- Is current `state' closed?
do
Result := state = Closed
end
feature -- Change Element
set_state (a_state: INTEGER)
-- Set `state' to `a_state'
require
valid_state: is_valid_state (a_state)
do
state := a_state
ensure
state_set: state = a_state
end
mark_connecting
--Set `state' to `connecting'
do
set_state (Connecting)
ensure
connecting_state: state = Connecting
end
mark_open
--Set `state' to `open'
do
set_state (Open)
ensure
open_state: state = Open
end
mark_closing
--Set `state' to `closing'
do
set_state (Closing)
ensure
open_state: state = Closing
end
mark_closed
--Set `state' to `closed'
do
set_state (Closed)
ensure
open_state: state = Closed
end
feature --State connection
Connecting: INTEGER = 0
-- The connection is in progress but has not been established.
Open: INTEGER = 1
-- The connection has been stablished. Messages can flow between client and server.
Closing: INTEGER = 2
-- The connection is going through the closing handshake
Closed: INTEGER = 3
-- The connection has been closed or could not be opened.
invariant
is_valid_sate: is_valid_state (state)
end

View File

@@ -0,0 +1,61 @@
note
description: "Summary description for {WEB_SOCKET_SUBSCRIBER}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WEB_SOCKET_SUBSCRIBER
feature -- Events
on_websocket_ping (a_message: detachable STRING)
-- Send a ping in response to a received ping.
do
end
on_websocket_pong (a_message: detachable STRING)
-- Default implementation do nothing.
do
end
on_websocket_text_message (a_message: detachable STRING)
-- Default implementation do nothing.
do
end
on_websocket_binary_message (a_message: detachable STRING)
-- Default implementation do nothing.
do
end
on_websocket_open (a_message: detachable STRING)
do
end
on_websocket_close (a_message: detachable STRING)
do
end
on_websocket_error (a_error: detachable STRING)
do
end
feature -- Handshake
on_websocket_handshake (request: STRING)
-- Default do nothing
deferred
end
feature -- TCP connection
connection: HTTP_STREAM_SOCKET
deferred
end
end

View File

@@ -0,0 +1,45 @@
<?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="web_socket_client" uuid="EE010507-597F-4FAD-8EFA-B7251E800911" library_target="web_socket_client">
<target name="web_socket_client">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" void_safety="all">
<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="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
<library name="lib_http_network" location="..\..\http_network\http_network-safe.ecf"/>
<library name="lib_web_socket_protocol" location="..\protocol\web_socket_protocol-safe.ecf"/>
<cluster name="web_socket_client" location=".\src\" recursive="true">
<file_rule>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
<exclude>/spec$</exclude>
</file_rule>
<cluster name="ssl" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
</cluster>
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="net_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,45 @@
<?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="web_socket_client" uuid="EE010507-597F-4FAD-8EFA-B7251E800911" library_target="web_socket_client">
<target name="web_socket_client">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/CVS$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" void_safety="none">
<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="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf"/>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<library name="lib_http_network" location="..\..\http_network\http_network.ecf"/>
<library name="lib_web_socket_protocol" location="..\protocol\web_socket_protocol.ecf"/>
<cluster name="web_socket_client" location=".\src\" recursive="true">
<file_rule>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
<exclude>/spec$</exclude>
</file_rule>
<cluster name="ssl" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
</cluster>
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="net_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
</cluster>
</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,8 @@
<?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="protocol" uuid="97582991-7BF3-4F4D-8944-8141ADE34274" library_target="protocol">
<target name="protocol">
<root all_classes="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,8 @@
<?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="protocol" uuid="97582991-7BF3-4F4D-8944-8141ADE34274" library_target="protocol">
<target name="protocol">
<root all_classes="true"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

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 ("ca.crt") -- Change to use your own crt file.
opts.set_ssl_ca_key ("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,15 @@
-----BEGIN CERTIFICATE-----
MIICWDCCAcGgAwIBAgIJAJnXGtV+PtiYMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUwNDAzMjIxNTA0WhcNMTYwNDAyMjIxNTA0WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3HtHcKIl5wSUvn
G8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J53K0xuUlxz4d4
4D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQABo1AwTjAdBgNV
HQ4EFgQUbWpk2HoHa0YqpEwr7CGEatBFTMkwHwYDVR0jBBgwFoAUbWpk2HoHa0Yq
pEwr7CGEatBFTMkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAi+h4/
IgEocWkdRZBKHEcTrRxz5WhEDJMoVo9LhnXvCfn1G/4p6Un6sYv7Xzpi9NuSY8uV
cjfJJXhtF3AtyZ70iTAxWaRWjGaZ03PYOjlledJ5rqJEt6CCn8m+JsfznduZvbxQ
zQ6jCLXfyD/tvemB+yYEI3NntvRKx5/zt6Q26Q==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3H
tHcKIl5wSUvnG8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J5
3K0xuUlxz4d44D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQAB
AoGAR5efMg+dieRyLU8rieJcImxVbfOPg9gRsjdtIVkXTR+RL7ow59q7hXBo/Td/
WU8cm1gXoJ/bK+71YYqWyB+BaLRIWvRWb7Gdw203tu4e136Ca5uuY+71qdbVTVcl
NQ7J+T+eAQFP+a+DdT3ZQxu9eze87SMbu6i5YSpIk2kusOECQQDunv/DQ+nc+NgR
DF+Td3sNYUVRT9a1CWi6abAG6reXwp8MS4NobWDf+Ps4JODhEEwlIdq5qL7qqYBZ
Gc1TJJ53AkEA0404Fn6vAzzegBcS4RLlYTK7nMr0m4pMmDMCI6YzAYdMmKHp1e6f
IwxSmQrmwyAgwcT01bc0+A8yipcC2BWQaQJBAJ01QZm635OGmos41KsKF5bsE8gL
SpBBH69Yu/ECqGwie7iU84FUNnO4zIHjwghlPVVlZX3Vz9o4S+fn2N9DC+cCQGyZ
QyCxGdC0r5fbwHJQS/ZQn+UGfvlVzqoXDVMVn3t6ZES6YZrT61eHnOM5qGqklIxE
Old3vDZXPt/MU8Zvk3kCQBOgUx2VxvTrHN37hk9/QIDiM62+RenBm1M3ah8xTosf
1mSeEb6d9Kwb3TgPBmA7YXzJuAQfRIvEPMPxT5SSr6Q=
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,47 @@
<?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>
</target>
<target name="echo_websocket_server_mt_ssl" extends="echo_websocket_server_mt">
<variable name="ssl_enabled" value="true"/>
</target>
<target name="echo_websocket_server_scoop" extends="common">
<root class="APPLICATION" feature="make_and_launch"/>
<option concurrency="scoop" root_concurrency="scoop">
</option>
</target>
<target name="echo_websocket_server_scoop_ssl" extends="echo_websocket_server_scoop">
<variable name="httpd_ssl_enabled" value="true"/>
</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

@@ -1,53 +0,0 @@
<?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="http_network" uuid="56DAA1CE-0A2E-451A-BFC9-7821578E79F0" library_target="http_network">
<target name="http_network">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" 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>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf" readonly="false"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
</library>
<cluster name="network" location=".\network\">
<file_rule>
<exclude>/httpd_stream_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</file_rule>
<cluster name="ssl_network" location="$|ssl\" recursive="true">
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<file_rule>
<exclude>/httpd_stream_ssl_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="16.11.0.0"/>
<custom name="net_ssl_enabled" value="true"/>
</condition>
</file_rule>
</cluster>
</cluster>
<cluster name="network_until_16_05" location=".\network\until_16_05\" recursive="false">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
<cluster name="ssl_network_until_16_05" location="$|ssl\" recursive="true">
<condition>
<custom name="net_ssl_enabled" value="true"/>
<version type="compiler" max="16.11.0.0"/>
</condition>
</cluster>
</cluster>
</target>
</system>

View File

@@ -1,22 +0,0 @@
note
description: "[
Constant value to define the logging level.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_LOGGER_CONSTANTS
feature -- Access
alert_level: INTEGER = 1 -- 0000 0001
critical_level: INTEGER = 2 -- 0000 0010
error_level: INTEGER = 4 -- 0000 0100
warning_level: INTEGER = 8 -- 0000 1000
notice_level: INTEGER = 16 -- 0001 0000
information_level: INTEGER = 32 -- 0010 0000
debug_level: INTEGER = 64 -- 0100 0000
end

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): HTTPD_STREAM_SOCKET
do
check not_secure: not a_is_secure end
create Result.make_empty
end
end

View File

@@ -1,41 +0,0 @@
note
description: "[
SSL enabled server
]"
date: "$Date$"
revision: "$Revision$"
class
HTTPD_SERVER
inherit
HTTPD_SERVER_I
redefine
new_listening_socket
end
create
make
feature {NONE} -- Factory
new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET
local
s_ssl: HTTPD_STREAM_SSL_SOCKET
do
if configuration.is_secure then
if a_addr /= Void then
create s_ssl.make_server_by_address_and_port (a_addr, a_http_port)
Result := s_ssl
else
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)
Result := s_ssl
else
Result := Precursor (a_addr, a_http_port)
end
end
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): HTTPD_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

@@ -75,7 +75,7 @@ feature -- Output
-- Send `s' to http client
do
last_target_call_succeed := False
target.put_readable_string_8_noexception (s)
target.put_string_8_noexception (s)
last_target_call_succeed := not target.was_error
end
@@ -83,7 +83,7 @@ feature -- Output
-- Send `s' to http client
do
last_target_call_succeed := False
target.put_readable_string_8_noexception (s)
target.put_string_8_noexception (s)
last_target_call_succeed := not target.was_error
end

View File

@@ -16,7 +16,7 @@
<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="httpd" location="lib\httpd\httpd-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"/>
</cluster>

View File

@@ -15,7 +15,7 @@
<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="httpd" location="lib\httpd\httpd.ecf" readonly="false"/>
<library name="httpd" location="..\..\..\httpd\httpd.ecf" readonly="false"/>
<cluster name="src" location=".\src\">
<cluster name="implementation" location="$|implementation\" hidden="true"/>
</cluster>

View File

@@ -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

@@ -23,8 +23,8 @@ feature {NONE} -- Initialization
keep_alive_timeout := default_keep_alive_timeout
max_keep_alive_requests := default_max_keep_alive_requests
is_secure := False
create ca_crt.make_empty
create ca_key.make_empty
create secure_certificate.make_empty
create secure_certificate_key.make_empty
end
feature -- Access
@@ -75,8 +75,8 @@ feature -- Access
-- To disable KeepAlive, set `max_keep_alive_requests' to 0.
-- By default: 100 .
has_ssl_support: BOOLEAN
-- Has SSL support?
has_secure_support: BOOLEAN
-- Has SSL/TLS secure support?
deferred
end
@@ -96,30 +96,30 @@ feature -- Access: SSL
is_secure: BOOLEAN
-- Is SSL/TLS session?.
ca_crt: detachable IMMUTABLE_STRING_32
secure_certificate: detachable IMMUTABLE_STRING_32
-- the signed certificate.
ca_key: detachable IMMUTABLE_STRING_32
-- private key to the certificate.
secure_certificate_key: detachable IMMUTABLE_STRING_32
-- private key to the certificate authority.
ssl_protocol: NATURAL
secure_protocol: NATURAL
-- By default protocol is tls 1.2.
feature -- Element change
set_ssl_settings (v: detachable separate TUPLE [protocol: separate READABLE_STRING_GENERAL; ca_crt, ca_key: detachable separate READABLE_STRING_GENERAL])
set_secure_settings (v: detachable separate TUPLE [protocol: separate READABLE_STRING_GENERAL; ca_crt, ca_key: detachable separate READABLE_STRING_GENERAL])
local
prot: STRING_32
do
is_secure := False
ca_crt := Void
ca_key := Void
secure_certificate := Void
secure_certificate_key := Void
if v /= Void then
is_secure := True
create prot.make_from_separate (v.protocol)
set_ssl_protocol_from_string (prot)
set_ca_crt (v.ca_crt)
set_ca_key (v.ca_key)
set_secure_protocol_from_string (prot)
set_secure_certificate (v.ca_crt)
set_secure_certificate_key (v.ca_key)
end
end
@@ -229,7 +229,7 @@ feature -- Element change
set_is_secure (b: BOOLEAN)
-- Set `is_secure' to `b'.
do
if b and has_ssl_support then
if b and has_secure_support then
is_secure := True
if
http_server_port = 80
@@ -245,8 +245,8 @@ feature -- Element change
end
end
ensure
is_secure_set: has_ssl_support implies is_secure
is_not_secure: not has_ssl_support implies not is_secure
is_secure_set: has_secure_support implies is_secure
is_not_secure: not has_secure_support implies not is_secure
end
mark_secure
@@ -254,89 +254,89 @@ feature -- Element change
do
set_is_secure (True)
ensure
is_secure_set: has_ssl_support implies is_secure
-- http_server_port_set: has_ssl_support implies http_server_port = 443
is_not_secure: not has_ssl_support implies not is_secure
-- default_port: not has_ssl_support implies http_server_port = 80
is_secure_set: has_secure_support implies is_secure
-- http_server_port_set: has_secure_support implies http_server_port = 443
is_not_secure: not has_secure_support implies not is_secure
-- default_port: not has_secure_support implies http_server_port = 80
end
feature -- Element change
set_ca_crt (a_value: detachable separate READABLE_STRING_GENERAL)
-- Set `ca_crt' from `a_value'.
set_secure_certificate (a_value: detachable separate READABLE_STRING_GENERAL)
-- Set `secure_certificate' from `a_value'.
do
if a_value /= Void then
create ca_crt.make_from_separate (a_value)
create secure_certificate.make_from_separate (a_value)
else
ca_crt := Void
secure_certificate := Void
end
end
set_ca_key (a_value: detachable separate READABLE_STRING_GENERAL)
-- Set `ca_key' with `a_value'.
set_secure_certificate_key (a_value: detachable separate READABLE_STRING_GENERAL)
-- Set `secure_certificate_key' with `a_value'.
do
if a_value /= Void then
create ca_key.make_from_separate (a_value)
create secure_certificate_key.make_from_separate (a_value)
else
ca_key := Void
secure_certificate_key := Void
end
end
set_ssl_protocol (a_version: NATURAL)
-- Set `ssl_protocol' with `a_version'
set_secure_protocol (a_version: NATURAL)
-- Set `secure_protocol' with `a_version'
do
ssl_protocol := a_version
secure_protocol := a_version
ensure
ssl_protocol_set: ssl_protocol = a_version
secure_protocol_set: secure_protocol = a_version
end
set_ssl_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
-- Set `ssl_protocol' with `a_ssl_version'
set_secure_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
-- Set `secure_protocol' with `a_ssl_version'
do
if a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then
set_ssl_protocol_to_ssl_2_or_3
set_secure_protocol_to_ssl_2_or_3
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_0") then
set_ssl_protocol_to_tls_1_0
set_secure_protocol_to_tls_1_0
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_1") then
set_ssl_protocol_to_tls_1_1
set_secure_protocol_to_tls_1_1
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_2") then
set_ssl_protocol_to_tls_1_2
set_secure_protocol_to_tls_1_2
elseif a_ssl_version.is_case_insensitive_equal ("dtls_1_0") then
set_ssl_protocol_to_dtls_1_0
set_secure_protocol_to_dtls_1_0
else -- Default
set_ssl_protocol_to_tls_1_2
set_secure_protocol_to_tls_1_2
end
end
feature -- SSL Helpers
set_ssl_protocol_to_ssl_2_or_3
-- Set `ssl_protocol' with `Ssl_23'.
set_secure_protocol_to_ssl_2_or_3
-- Set `secure_protocol' with `Ssl_23'.
deferred
end
set_ssl_protocol_to_tls_1_0
-- Set `ssl_protocol' with `Tls_1_0'.
set_secure_protocol_to_tls_1_0
-- Set `secure_protocol' with `Tls_1_0'.
deferred
end
set_ssl_protocol_to_tls_1_1
-- Set `ssl_protocol' with `Tls_1_1'.
set_secure_protocol_to_tls_1_1
-- Set `secure_protocol' with `Tls_1_1'.
deferred
end
set_ssl_protocol_to_tls_1_2
-- Set `ssl_protocol' with `Tls_1_2'.
set_secure_protocol_to_tls_1_2
-- Set `secure_protocol' with `Tls_1_2'.
deferred
end
set_ssl_protocol_to_dtls_1_0
-- Set `ssl_protocol' with `Dtls_1_0'.
set_secure_protocol_to_dtls_1_0
-- Set `secure_protocol' with `Dtls_1_0'.
deferred
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

@@ -9,6 +9,9 @@ note
deferred class
HTTPD_CONSTANTS
inherit
HTTP_SECURE_HELPER
feature -- Default connection settings
default_http_server_port: INTEGER = 80
@@ -25,4 +28,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

@@ -0,0 +1,71 @@
<?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="httpd" uuid="50FE258D-CC94-4748-9223-55F1129E5FB3" library_target="httpd">
<target name="httpd">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="false" 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="scoop"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="http_network" location="..\..\network\http_network\http_network-safe.ecf" readonly="false"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</library>
<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="httpd_server" location=".\" recursive="true">
<file_rule>
<exclude>/concurrency$</exclude>
<exclude>/no_ssl$</exclude>
<exclude>/ssl$</exclude>
</file_rule>
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="httpd_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</cluster>
<cluster name="concurrency_none" location="$|concurrency\none\" recursive="true">
<condition>
<concurrency value="none"/>
</condition>
</cluster>
<cluster name="concurrency_scoop" location="$|concurrency\scoop\" recursive="true">
<condition>
<concurrency value="scoop"/>
</condition>
</cluster>
<cluster name="concurrency_thread" location="$|concurrency\thread\" recursive="true">
<condition>
<concurrency value="thread"/>
</condition>
</cluster>
</cluster>
</target>
<target name="httpd_ssl" extends="httpd">
<variable name="ssl_enabled" value="true" />
</target>
</system>

View File

@@ -11,8 +11,12 @@
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="http_network" location="..\..\network\http_network\http_network.ecf" readonly="false"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
@@ -23,50 +27,22 @@
</condition>
</library>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<cluster name="network" location=".\network\">
<file_rule>
<exclude>/httpd_stream_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
</file_rule>
<cluster name="ssl_network" location="$|ssl\" recursive="true">
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
<file_rule>
<exclude>/httpd_stream_ssl_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="16.11.0.0"/>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</file_rule>
</cluster>
</cluster>
<cluster name="network_until_16_05" location=".\network\until_16_05\" recursive="false">
<condition>
<version type="compiler" max="16.11.0.0"/>
</condition>
<cluster name="ssl_network_until_16_05" location="$|ssl\" recursive="true">
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
<version type="compiler" max="16.11.0.0"/>
</condition>
</cluster>
</cluster>
<cluster name="httpd_server" location=".\" recursive="true">
<file_rule>
<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>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="httpd_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>

View File

@@ -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

@@ -0,0 +1,50 @@
note
description: "[
Constant value to define the logging level.
]"
date: "$Date$"
revision: "$Revision$"
deferred class
HTTPD_LOGGER_CONSTANTS
feature -- Access
alert_level: INTEGER = 1 -- 0000 0001
critical_level: INTEGER = 2 -- 0000 0010
error_level: INTEGER = 4 -- 0000 0100
warning_level: INTEGER = 8 -- 0000 1000
notice_level: INTEGER = 16 -- 0001 0000
information_level: INTEGER = 32 -- 0010 0000
debug_level: INTEGER = 64 -- 0100 0000
feature -- Conversion
logger_level_representation (a_level: INTEGER): STRING
-- String representation of `a_level'.
do
inspect a_level
when alert_level then Result := "alert"
when critical_level then Result := "critical"
when error_level then Result := "error"
when warning_level then Result := "warning"
when notice_level then Result := "notice"
when information_level then Result := "information"
when debug_level then Result := "debug"
else
Result := "level #" + a_level.out
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

@@ -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
@@ -525,7 +525,7 @@ feature -- Parsing
if a_socket.was_error then
report_error ("Socket error")
if is_verbose then
log (request_header +"%N" + Result + "%N## was_error=False! ##", debug_level)
log (request_header +"%N" + Result + "%N## Network error: " + a_socket.error + " ##", debug_level)
end
end
else
@@ -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

Some files were not shown because too many files have changed in this diff Show More