Compare commits
23 Commits
improve_wi
...
es_rev9919
| Author | SHA1 | Date | |
|---|---|---|---|
| 40fb3893af | |||
| 21407f8dcf | |||
| 356eb143ea | |||
| df551d4a4f | |||
| f010da04e9 | |||
| 5029049ef0 | |||
| 80254b2278 | |||
| 210fae5000 | |||
| 9cc9b95190 | |||
| 8b172b5d33 | |||
| cc2d7dbb1c | |||
| c88394b9fd | |||
| 4283662f43 | |||
| 1b951376f9 | |||
| 193cc3cbde | |||
| b49e841ac7 | |||
| 8ba74e1c90 | |||
| 0cecb9594c | |||
| e384a6d6ed | |||
| 71a5c086a5 | |||
| dfa60bf8f5 | |||
| 113aa69efc | |||
| af5fc75743 |
29
examples/proxy/application.e
Normal file
29
examples/proxy/application.e
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
note
|
||||||
|
description: "Launcher for reverse proxy web application."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||||
|
redefine
|
||||||
|
initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make_and_launch
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
-- Initialize current service.
|
||||||
|
do
|
||||||
|
-- Specific to `standalone' connector (the EiffelWeb server).
|
||||||
|
-- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
|
||||||
|
set_service_option ("port", 9090)
|
||||||
|
import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("server.ini"))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
49
examples/proxy/application_execution.e
Normal file
49
examples/proxy/application_execution.e
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
note
|
||||||
|
description: "Reverse proxy example."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION_EXECUTION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_EXECUTION
|
||||||
|
|
||||||
|
WSF_URI_REWRITER
|
||||||
|
rename
|
||||||
|
uri as proxy_uri
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Basic operations
|
||||||
|
|
||||||
|
execute
|
||||||
|
do
|
||||||
|
-- NOTE: please enter the target server uri here
|
||||||
|
-- replace "http://localhost:8080/foobar"
|
||||||
|
send_proxy_response ("http://localhost:8080/foobar", Current)
|
||||||
|
end
|
||||||
|
|
||||||
|
send_proxy_response (a_remote: READABLE_STRING_8; a_rewriter: detachable WSF_URI_REWRITER)
|
||||||
|
local
|
||||||
|
h: WSF_SIMPLE_REVERSE_PROXY_HANDLER
|
||||||
|
do
|
||||||
|
create h.make (a_remote)
|
||||||
|
h.set_uri_rewriter (a_rewriter)
|
||||||
|
h.set_uri_rewriter (create {WSF_AGENT_URI_REWRITER}.make (agent proxy_uri))
|
||||||
|
h.set_timeout (30) -- 30 seconds
|
||||||
|
h.set_connect_timeout (5_000) -- milliseconds = 5 seconds
|
||||||
|
h.execute (request, response)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Helpers
|
||||||
|
|
||||||
|
proxy_uri (a_request: WSF_REQUEST): STRING
|
||||||
|
-- Request uri rewriten as url.
|
||||||
|
do
|
||||||
|
Result := a_request.request_uri
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
28
examples/proxy/proxy.ecf
Normal file
28
examples/proxy/proxy.ecf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?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="proxy" uuid="B55F0D95-3793-4C90-BBAC-BF5F2DECD5E6" library_target="proxy">
|
||||||
|
<target name="common" abstract="true">
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<setting name="console_application" value="true"/>
|
||||||
|
<variable name="ssl_supported" value="false"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||||||
|
<library name="wsf_proxy" location="..\..\library\server\wsf_proxy\wsf_proxy-safe.ecf" readonly="false"/>
|
||||||
|
</target>
|
||||||
|
<target name="proxy" extends="common">
|
||||||
|
<root class="APPLICATION" feature="make_and_launch"/>
|
||||||
|
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||||
|
<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="proxy" location=".\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
8
examples/proxy/server.ini
Normal file
8
examples/proxy/server.ini
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
verbose=true
|
||||||
|
verbose_level=ALERT
|
||||||
|
port=9090
|
||||||
|
#max_concurrent_connections=100
|
||||||
|
#keep_alive_timeout=15
|
||||||
|
#max_tcp_clients=100
|
||||||
|
#socket_timeout=300
|
||||||
|
#max_keep_alive_requests=300
|
||||||
@@ -22,10 +22,8 @@ feature {NONE} -- Initialization
|
|||||||
do
|
do
|
||||||
-- Specific to `standalone' connector (the EiffelWeb server).
|
-- Specific to `standalone' connector (the EiffelWeb server).
|
||||||
-- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
|
-- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
|
||||||
set_service_option ("port", 9090)
|
set_service_option ("port", 9090)
|
||||||
set_service_option ("max_concurrent_connections", 10)
|
import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("simple.ini"))
|
||||||
set_service_option ("keep_alive_timeout", 1)
|
|
||||||
set_service_option ("verbose", True)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ feature -- Basic operations
|
|||||||
local
|
local
|
||||||
s: STRING
|
s: STRING
|
||||||
dt: HTTP_DATE
|
dt: HTTP_DATE
|
||||||
do
|
do
|
||||||
-- To send a response we need to setup, the status code and
|
-- To send a response we need to setup, the status code and
|
||||||
-- the response headers.
|
-- the response headers.
|
||||||
s := "Hello World!"
|
s := "Hello World!"
|
||||||
create dt.make_now_utc
|
create dt.make_now_utc
|
||||||
s.append (" (UTC time is " + dt.rfc850_string + ").")
|
s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||||||
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
||||||
|
|||||||
@@ -16,19 +16,11 @@
|
|||||||
</target>
|
</target>
|
||||||
<target name="simple_standalone" extends="common">
|
<target name="simple_standalone" extends="common">
|
||||||
<root class="APPLICATION" feature="make_and_launch"/>
|
<root class="APPLICATION" feature="make_and_launch"/>
|
||||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
</option>
|
</option>
|
||||||
<setting name="concurrency" value="scoop"/>
|
<setting name="concurrency" value="scoop"/>
|
||||||
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false"/>
|
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
|
||||||
<cluster name="simple" location=".\" recursive="true"/>
|
|
||||||
</target>
|
|
||||||
<target name="simple_nino" extends="common">
|
|
||||||
<root class="APPLICATION" feature="make_and_launch"/>
|
|
||||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
|
||||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
|
||||||
</option>
|
|
||||||
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf"/>
|
|
||||||
<cluster name="simple" location=".\" recursive="true"/>
|
<cluster name="simple" location=".\" recursive="true"/>
|
||||||
</target>
|
</target>
|
||||||
<target name="simple_cgi" extends="common">
|
<target name="simple_cgi" extends="common">
|
||||||
|
|||||||
8
examples/simple/simple.ini
Normal file
8
examples/simple/simple.ini
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
verbose=true
|
||||||
|
verbose_level=ALERT
|
||||||
|
port=9090
|
||||||
|
#max_concurrent_connections=100
|
||||||
|
#keep_alive_timeout=15
|
||||||
|
#max_tcp_clients=100
|
||||||
|
#socket_timeout=300
|
||||||
|
#max_keep_alive_requests=300
|
||||||
29
examples/simple_ssl/application.e
Normal file
29
examples/simple_ssl/application.e
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
note
|
||||||
|
description : "simple application root class"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||||
|
redefine
|
||||||
|
initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make_and_launch
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
-- Initialize current service.
|
||||||
|
do
|
||||||
|
-- Specific to `standalone' connector (the EiffelWeb server).
|
||||||
|
-- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
|
||||||
|
set_service_option ("port", 9090)
|
||||||
|
import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("simple.ini"))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
41
examples/simple_ssl/application_execution.e
Normal file
41
examples/simple_ssl/application_execution.e
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
note
|
||||||
|
description : "simple application execution"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION_EXECUTION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_EXECUTION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Basic operations
|
||||||
|
|
||||||
|
execute
|
||||||
|
local
|
||||||
|
s: STRING
|
||||||
|
dt: HTTP_DATE
|
||||||
|
do
|
||||||
|
-- To send a response we need to setup, the status code and
|
||||||
|
-- the response headers.
|
||||||
|
s := "Hello World!"
|
||||||
|
create dt.make_now_utc
|
||||||
|
s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||||||
|
if request.is_https then
|
||||||
|
s.append ("<p>This is a secured connection! (https)</p>%N")
|
||||||
|
end
|
||||||
|
|
||||||
|
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
||||||
|
response.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||||
|
response.header.put_content_type_text_html
|
||||||
|
response.header.put_content_length (s.count)
|
||||||
|
if attached request.http_connection as l_connection and then l_connection.is_case_insensitive_equal_general ("keep-alive") then
|
||||||
|
response.header.put_header_key_value ("Connection", "keep-alive")
|
||||||
|
end
|
||||||
|
response.put_string (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
15
examples/simple_ssl/simple.crt
Normal file
15
examples/simple_ssl/simple.crt
Normal 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-----
|
||||||
28
examples/simple_ssl/simple.ini
Normal file
28
examples/simple_ssl/simple.ini
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
##########################################################
|
||||||
|
### EiffelWeb settings for related connector ###
|
||||||
|
### Mostly for EiffelWeb standalone connector ###
|
||||||
|
### See {WGI_STANDALONE_CONSTANTS} for default values. ###
|
||||||
|
##########################################################
|
||||||
|
|
||||||
|
### Connection settings
|
||||||
|
port=9090
|
||||||
|
#max_concurrent_connections=100
|
||||||
|
#max_tcp_clients=100
|
||||||
|
|
||||||
|
### Timeout settings
|
||||||
|
#socket_timeout=60
|
||||||
|
#socket_recv_timeout=5
|
||||||
|
|
||||||
|
### Persistent connection settings
|
||||||
|
#keep_alive_timeout=15
|
||||||
|
#max_keep_alive_requests=100
|
||||||
|
|
||||||
|
### SSL settings
|
||||||
|
# enable SSL, with file certificate.
|
||||||
|
ssl_enabled=true
|
||||||
|
ssl_ca_key=simple.key
|
||||||
|
ssl_ca_crt=simple.crt
|
||||||
|
|
||||||
|
### App settings
|
||||||
|
verbose=true
|
||||||
|
verbose_level=ALERT
|
||||||
15
examples/simple_ssl/simple.key
Normal file
15
examples/simple_ssl/simple.key
Normal 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-----
|
||||||
27
examples/simple_ssl/simple_ssl.ecf
Normal file
27
examples/simple_ssl/simple_ssl.ecf
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?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_ssl" uuid="C2FE296C-3C18-4609-A5AB-F604BDEE4410" library_target="simple_ssl">
|
||||||
|
<target name="simple_ssl">
|
||||||
|
<description>Simple EiffelWeb standalone server with SSL support (Concurrent connection supported thanks to SCOOP).</description>
|
||||||
|
<root class="APPLICATION" feature="make_and_launch"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||||
|
<assertions/>
|
||||||
|
</option>
|
||||||
|
<setting name="console_application" value="true"/>
|
||||||
|
<setting name="concurrency" value="scoop"/>
|
||||||
|
<variable name="httpd_ssl_enabled" value="true"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
|
||||||
|
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||||||
|
<cluster name="simple" location=".\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
<target name="simple_ssl_st" extends="simple_ssl">
|
||||||
|
<description>Simple EiffelWeb standalone server with SSL support (Single threaded, thus no concurrent connection.)</description>
|
||||||
|
<setting name="concurrency" value="none"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
29
examples/websocket/application.e
Normal file
29
examples/websocket/application.e
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
note
|
||||||
|
description : "simple application root class"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION
|
||||||
|
|
||||||
|
create
|
||||||
|
make_and_launch
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make_and_launch
|
||||||
|
local
|
||||||
|
l_launcher: WSF_STANDALONE_WEBSOCKET_SERVICE_LAUNCHER [APPLICATION_EXECUTION]
|
||||||
|
opts: WSF_SERVICE_LAUNCHER_OPTIONS
|
||||||
|
do
|
||||||
|
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} opts.make_from_file ("ws.ini")
|
||||||
|
create l_launcher.make_and_launch (options)
|
||||||
|
end
|
||||||
|
|
||||||
|
options: WSF_SERVICE_LAUNCHER_OPTIONS
|
||||||
|
-- Initialize current service.
|
||||||
|
do
|
||||||
|
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} Result.make_from_file ("ws.ini")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
184
examples/websocket/application_execution.e
Normal file
184
examples/websocket/application_execution.e
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
note
|
||||||
|
description : "simple application execution"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION_EXECUTION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_WEBSOCKET_EXECUTION
|
||||||
|
|
||||||
|
WEB_SOCKET_EVENT_I
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Basic operations
|
||||||
|
|
||||||
|
execute
|
||||||
|
local
|
||||||
|
s: STRING
|
||||||
|
dt: HTTP_DATE
|
||||||
|
do
|
||||||
|
-- To send a response we need to setup, the status code and
|
||||||
|
-- the response headers.
|
||||||
|
if request.path_info.same_string_general ("/app") then
|
||||||
|
s := websocket_app_html (9090)
|
||||||
|
else
|
||||||
|
s := "Hello World!"
|
||||||
|
create dt.make_now_utc
|
||||||
|
s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||||||
|
s.append ("<p><a href=%"/app%">Websocket demo</a></p>")
|
||||||
|
end
|
||||||
|
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
||||||
|
response.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||||
|
response.header.put_content_type_text_html
|
||||||
|
response.header.put_content_length (s.count)
|
||||||
|
if attached request.http_connection as l_connection and then l_connection.is_case_insensitive_equal_general ("keep-alive") then
|
||||||
|
response.header.put_header_key_value ("Connection", "keep-alive")
|
||||||
|
end
|
||||||
|
response.put_string (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Websocket execution
|
||||||
|
|
||||||
|
new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
|
||||||
|
do
|
||||||
|
create Result.make (ws, Current)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Websocket execution
|
||||||
|
|
||||||
|
on_open (ws: WEB_SOCKET)
|
||||||
|
do
|
||||||
|
ws.put_error ("Connecting")
|
||||||
|
ws.send (Text_frame, "Hello, this is a simple demo with Websocket using Eiffel. (/help for more information).%N")
|
||||||
|
end
|
||||||
|
|
||||||
|
on_binary (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
ws.send (Binary_frame, a_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
if a_message.same_string_general ("/help") then
|
||||||
|
-- Echo the message for testing.
|
||||||
|
ws.send (Text_frame, "Help: available commands%N - /time : return the server UTC time.%N")
|
||||||
|
elseif a_message.starts_with_general ("/time") then
|
||||||
|
ws.send (Text_frame, "Server time is " + (create {HTTP_DATE}.make_now_utc).string)
|
||||||
|
else
|
||||||
|
-- Echo the message for testing.
|
||||||
|
ws.send (Text_frame, a_message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
on_close (ws: WEB_SOCKET)
|
||||||
|
-- Called after the WebSocket connection is closed.
|
||||||
|
do
|
||||||
|
ws.put_error ("Connection closed")
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- HTML Resource
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
21
examples/websocket/websocket_app.ecf
Normal file
21
examples/websocket/websocket_app.ecf
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?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="websocket_app" uuid="75D17C20-10A8-4E4C-A059-33D72A2B6AEF">
|
||||||
|
<target name="websocket_app">
|
||||||
|
<root class="APPLICATION" feature="make_and_launch"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<setting name="console_application" value="true"/>
|
||||||
|
<setting name="concurrency" value="scoop"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="standalone_websocket_connector" location="..\..\library\server\wsf\connector\standalone_websocket-safe.ecf" readonly="false"/>
|
||||||
|
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||||||
|
<cluster name="app" location=".\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
8
examples/websocket/ws.ini
Normal file
8
examples/websocket/ws.ini
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
verbose=true
|
||||||
|
verbose_level=INFORMATION
|
||||||
|
port=9090
|
||||||
|
max_concurrent_connections=100
|
||||||
|
keep_alive_timeout=35
|
||||||
|
max_tcp_clients=100
|
||||||
|
socket_timeout=30000
|
||||||
|
max_keep_alive_requests=3000
|
||||||
@@ -180,7 +180,9 @@ feature -- Header: adding
|
|||||||
if line [line.count] = '%R' then
|
if line [line.count] = '%R' then
|
||||||
line.remove_tail (1)
|
line.remove_tail (1)
|
||||||
end
|
end
|
||||||
add_header (line)
|
if not line.is_empty then
|
||||||
|
add_header (line)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -508,9 +508,9 @@ feature -- Helper
|
|||||||
|
|
||||||
new_session (a_uri: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
new_session (a_uri: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
local
|
local
|
||||||
cl: LIBCURL_HTTP_CLIENT
|
cl: DEFAULT_HTTP_CLIENT
|
||||||
do
|
do
|
||||||
create cl.make
|
create cl
|
||||||
Result := cl.new_session (a_uri)
|
Result := cl.new_session (a_uri)
|
||||||
Result.set_is_insecure (True)
|
Result.set_is_insecure (True)
|
||||||
Result.set_max_redirects (5)
|
Result.set_max_redirects (5)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ feature {NONE} -- Initialization
|
|||||||
local
|
local
|
||||||
n: INTEGER
|
n: INTEGER
|
||||||
p: like pool
|
p: like pool
|
||||||
do
|
do
|
||||||
n := max_concurrent_connections (server).max (1) -- At least one processor!
|
n := max_concurrent_connections (server).max (1) -- At least one processor!
|
||||||
create p.make (n)
|
create p.make (n)
|
||||||
initialize_pool (p, n)
|
initialize_pool (p, n)
|
||||||
@@ -25,20 +25,27 @@ feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
|
|||||||
release
|
release
|
||||||
-- <Precursor>
|
-- <Precursor>
|
||||||
local
|
local
|
||||||
d: STRING
|
d: detachable STRING
|
||||||
do
|
do
|
||||||
if attached internal_client_socket as l_socket then
|
|
||||||
d := l_socket.descriptor.out
|
|
||||||
else
|
|
||||||
d := "N/A"
|
|
||||||
end
|
|
||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
|
if
|
||||||
|
attached internal_client_socket as l_socket and then
|
||||||
|
l_socket.descriptor_available
|
||||||
|
then
|
||||||
|
d := l_socket.descriptor.out
|
||||||
|
else
|
||||||
|
d := "N/A"
|
||||||
|
end
|
||||||
dbglog (generator + ".release: ENTER {" + d + "}")
|
dbglog (generator + ".release: ENTER {" + d + "}")
|
||||||
end
|
end
|
||||||
Precursor {HTTPD_REQUEST_HANDLER_I}
|
Precursor {HTTPD_REQUEST_HANDLER_I}
|
||||||
release_pool_item
|
release_pool_item
|
||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
dbglog (generator + ".release: LEAVE {" + d + "}")
|
if d /= Void then
|
||||||
|
dbglog (generator + ".release: LEAVE {" + d + "}")
|
||||||
|
else
|
||||||
|
dbglog (generator + ".release: LEAVE {N/A}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -18,21 +18,28 @@ inherit
|
|||||||
feature {HTTPD_CONNECTION_HANDLER_I} -- Basic operation
|
feature {HTTPD_CONNECTION_HANDLER_I} -- Basic operation
|
||||||
|
|
||||||
release
|
release
|
||||||
|
-- <Precursor>
|
||||||
local
|
local
|
||||||
d: STRING
|
d: detachable STRING
|
||||||
do
|
do
|
||||||
-- FIXME: for log purpose
|
|
||||||
if attached internal_client_socket as l_socket then
|
|
||||||
d := l_socket.descriptor.out
|
|
||||||
else
|
|
||||||
d := "N/A"
|
|
||||||
end
|
|
||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
|
if
|
||||||
|
attached internal_client_socket as l_socket and then
|
||||||
|
l_socket.descriptor_available
|
||||||
|
then
|
||||||
|
d := l_socket.descriptor.out
|
||||||
|
else
|
||||||
|
d := "N/A"
|
||||||
|
end
|
||||||
dbglog (generator + ".release: ENTER {" + d + "}")
|
dbglog (generator + ".release: ENTER {" + d + "}")
|
||||||
end
|
end
|
||||||
Precursor {HTTPD_REQUEST_HANDLER_I}
|
Precursor {HTTPD_REQUEST_HANDLER_I}
|
||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
dbglog (generator + ".release: LEAVE {" + d + "}")
|
if d /= Void then
|
||||||
|
dbglog (generator + ".release: LEAVE {" + d + "}")
|
||||||
|
else
|
||||||
|
dbglog (generator + ".release: LEAVE {N/A}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
note
|
||||||
|
description: "Configuration for the standalone HTTPd server."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
HTTPD_CONFIGURATION_I
|
||||||
|
|
||||||
|
inherit
|
||||||
|
ANY
|
||||||
|
|
||||||
|
HTTPD_CONSTANTS
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make
|
||||||
|
do
|
||||||
|
http_server_port := default_http_server_port
|
||||||
|
max_concurrent_connections := default_max_concurrent_connections
|
||||||
|
max_tcp_clients := default_max_tcp_clients
|
||||||
|
socket_timeout := default_socket_timeout
|
||||||
|
socket_recv_timeout := default_socket_recv_timeout
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
Server_details: STRING_8
|
||||||
|
-- Detail of the server.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
http_server_name: detachable READABLE_STRING_8 assign set_http_server_name
|
||||||
|
http_server_port: INTEGER assign set_http_server_port
|
||||||
|
max_tcp_clients: INTEGER assign set_max_tcp_clients
|
||||||
|
-- Listen on socket for at most `queue' connections.
|
||||||
|
|
||||||
|
socket_timeout: INTEGER assign set_socket_timeout
|
||||||
|
-- Amount of seconds that the server waits for receipts and transmissions during communications.
|
||||||
|
-- note: with timeout of 0, socket can wait for ever.
|
||||||
|
-- By default: 60 seconds, which is appropriate for most situations.
|
||||||
|
|
||||||
|
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
|
||||||
|
-- Amount of seconds that the server waits for receiving data during communications.
|
||||||
|
-- note: with timeout of 0, socket can wait for ever.
|
||||||
|
-- By default: 5 seconds.
|
||||||
|
|
||||||
|
max_concurrent_connections: INTEGER assign set_max_concurrent_connections
|
||||||
|
-- Max number of concurrent connections.
|
||||||
|
|
||||||
|
force_single_threaded: BOOLEAN assign set_force_single_threaded
|
||||||
|
do
|
||||||
|
Result := max_concurrent_connections = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
is_verbose: BOOLEAN assign set_is_verbose
|
||||||
|
-- Display verbose message to the output?
|
||||||
|
|
||||||
|
verbose_level: INTEGER assign set_verbose_level
|
||||||
|
-- Verbosity of output.
|
||||||
|
|
||||||
|
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
|
||||||
|
-- Persistent connection timeout.
|
||||||
|
-- Number of seconds the server waits after a request has been served before it closes the connection.
|
||||||
|
-- Timeout unit in Seconds.
|
||||||
|
-- By default: 5 seconds.
|
||||||
|
|
||||||
|
max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
|
||||||
|
-- Maximum number of requests allowed per persistent connection.
|
||||||
|
-- Recommended a high setting.
|
||||||
|
-- To disable KeepAlive, set `max_keep_alive_requests' to 0.
|
||||||
|
-- By default: 100 .
|
||||||
|
|
||||||
|
has_ssl_support: BOOLEAN
|
||||||
|
-- Has SSL support?
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
request_settings: HTTPD_REQUEST_SETTINGS
|
||||||
|
do
|
||||||
|
Result.is_verbose := is_verbose
|
||||||
|
Result.verbose_level := verbose_level
|
||||||
|
Result.timeout := socket_timeout
|
||||||
|
Result.socket_recv_timeout := socket_recv_timeout
|
||||||
|
Result.keep_alive_timeout := keep_alive_timeout
|
||||||
|
Result.max_keep_alive_requests := max_keep_alive_requests
|
||||||
|
Result.is_secure := is_secure
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access: SSL
|
||||||
|
|
||||||
|
is_secure: BOOLEAN
|
||||||
|
-- Is SSL/TLS session?.
|
||||||
|
|
||||||
|
ca_crt: detachable IMMUTABLE_STRING_32
|
||||||
|
-- the signed certificate.
|
||||||
|
|
||||||
|
ca_key: detachable IMMUTABLE_STRING_32
|
||||||
|
-- private key to the certificate.
|
||||||
|
|
||||||
|
ssl_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])
|
||||||
|
local
|
||||||
|
prot: STRING_32
|
||||||
|
do
|
||||||
|
is_secure := False
|
||||||
|
ca_crt := Void
|
||||||
|
ca_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)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_http_server_name (v: detachable separate READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
if v = Void then
|
||||||
|
unset_http_server_name
|
||||||
|
else
|
||||||
|
create {IMMUTABLE_STRING_8} http_server_name.make_from_separate (v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unset_http_server_name
|
||||||
|
-- Unset `http_server_name' value.
|
||||||
|
do
|
||||||
|
http_server_name := Void
|
||||||
|
ensure
|
||||||
|
unset_http_server_name: http_server_name = Void
|
||||||
|
end
|
||||||
|
|
||||||
|
set_http_server_port (v: like http_server_port)
|
||||||
|
-- Set `http_server_port' with `v'.
|
||||||
|
do
|
||||||
|
http_server_port := v
|
||||||
|
ensure
|
||||||
|
http_server_port_set: http_server_port = v
|
||||||
|
end
|
||||||
|
|
||||||
|
set_max_tcp_clients (v: like max_tcp_clients)
|
||||||
|
-- Set `max_tcp_clients' with `v'.
|
||||||
|
do
|
||||||
|
max_tcp_clients := v
|
||||||
|
ensure
|
||||||
|
max_tcp_clients_set: max_tcp_clients = v
|
||||||
|
end
|
||||||
|
|
||||||
|
set_max_concurrent_connections (v: like max_concurrent_connections)
|
||||||
|
-- Set `max_concurrent_connections' with `v'.
|
||||||
|
do
|
||||||
|
max_concurrent_connections := v
|
||||||
|
ensure
|
||||||
|
max_concurrent_connections_set : max_concurrent_connections = v
|
||||||
|
end
|
||||||
|
|
||||||
|
set_socket_timeout (a_nb_seconds: like socket_timeout)
|
||||||
|
-- Set `socket_timeout' with `a_nb_seconds'
|
||||||
|
do
|
||||||
|
socket_timeout := a_nb_seconds
|
||||||
|
ensure
|
||||||
|
socket_timeout_set: socket_timeout = a_nb_seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
|
||||||
|
-- Set `socket_recv_timeout' with `a_nb_seconds'
|
||||||
|
do
|
||||||
|
socket_recv_timeout := a_nb_seconds
|
||||||
|
ensure
|
||||||
|
socket_recv_timeout_set: socket_recv_timeout = a_nb_seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
|
||||||
|
-- Set `keep_alive_timeout' with `a_seconds'
|
||||||
|
do
|
||||||
|
keep_alive_timeout := a_seconds
|
||||||
|
ensure
|
||||||
|
keep_alive_timeout_set: keep_alive_timeout = a_seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
|
||||||
|
-- Set `max_keep_alive_requests' with `nb'
|
||||||
|
do
|
||||||
|
max_keep_alive_requests := nb
|
||||||
|
ensure
|
||||||
|
max_keep_alive_requests_set: max_keep_alive_requests = nb
|
||||||
|
end
|
||||||
|
|
||||||
|
set_force_single_threaded (v: like force_single_threaded)
|
||||||
|
-- Force server to handle incoming request in a single thread.
|
||||||
|
-- i.e set max_concurrent_connections to 0!
|
||||||
|
obsolete
|
||||||
|
"Use set_max_concurrent_connections (0) [June/2016]"
|
||||||
|
do
|
||||||
|
if v then
|
||||||
|
set_max_concurrent_connections (0)
|
||||||
|
end
|
||||||
|
--|Missing postcondition
|
||||||
|
--| force_single_thread_set: v implies max_concurrent_connections = 0
|
||||||
|
--| not_single_thread: not v implies max_concurrent_connections > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
set_is_verbose (b: BOOLEAN)
|
||||||
|
-- Set `is_verbose' to `b'
|
||||||
|
do
|
||||||
|
is_verbose := b
|
||||||
|
ensure
|
||||||
|
is_verbose_set: is_verbose = b
|
||||||
|
end
|
||||||
|
|
||||||
|
set_verbose_level (lev: INTEGER)
|
||||||
|
-- Set `verbose_level' to `lev'.
|
||||||
|
do
|
||||||
|
verbose_level := lev
|
||||||
|
ensure
|
||||||
|
verbose_level_set: verbose_level = lev
|
||||||
|
end
|
||||||
|
|
||||||
|
set_is_secure (b: BOOLEAN)
|
||||||
|
-- Set `is_secure' to `b'.
|
||||||
|
do
|
||||||
|
if b and has_ssl_support then
|
||||||
|
is_secure := True
|
||||||
|
if
|
||||||
|
http_server_port = 80
|
||||||
|
then
|
||||||
|
set_http_server_port (443)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
is_secure := False
|
||||||
|
if
|
||||||
|
http_server_port = 443
|
||||||
|
then
|
||||||
|
set_http_server_port (80)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
is_secure_set: has_ssl_support implies is_secure
|
||||||
|
is_not_secure: not has_ssl_support implies not is_secure
|
||||||
|
end
|
||||||
|
|
||||||
|
mark_secure
|
||||||
|
-- Set is_secure in True
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_ca_crt (a_value: detachable separate READABLE_STRING_GENERAL)
|
||||||
|
-- Set `ca_crt' from `a_value'.
|
||||||
|
do
|
||||||
|
if a_value /= Void then
|
||||||
|
create ca_crt.make_from_separate (a_value)
|
||||||
|
else
|
||||||
|
ca_crt := Void
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_ca_key (a_value: detachable separate READABLE_STRING_GENERAL)
|
||||||
|
-- Set `ca_key' with `a_value'.
|
||||||
|
do
|
||||||
|
if a_value /= Void then
|
||||||
|
create ca_key.make_from_separate (a_value)
|
||||||
|
else
|
||||||
|
ca_key := Void
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_ssl_protocol (a_version: NATURAL)
|
||||||
|
-- Set `ssl_protocol' with `a_version'
|
||||||
|
do
|
||||||
|
ssl_protocol := a_version
|
||||||
|
ensure
|
||||||
|
ssl_protocol_set: ssl_protocol = a_version
|
||||||
|
end
|
||||||
|
|
||||||
|
set_ssl_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
|
||||||
|
-- Set `ssl_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
|
||||||
|
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_0") then
|
||||||
|
set_ssl_protocol_to_tls_1_0
|
||||||
|
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_1") then
|
||||||
|
set_ssl_protocol_to_tls_1_1
|
||||||
|
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_2") then
|
||||||
|
set_ssl_protocol_to_tls_1_2
|
||||||
|
elseif a_ssl_version.is_case_insensitive_equal ("dtls_1_0") then
|
||||||
|
set_ssl_protocol_to_dtls_1_0
|
||||||
|
else -- Default
|
||||||
|
set_ssl_protocol_to_tls_1_2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- SSL Helpers
|
||||||
|
|
||||||
|
set_ssl_protocol_to_ssl_2_or_3
|
||||||
|
-- Set `ssl_protocol' with `Ssl_23'.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
set_ssl_protocol_to_tls_1_0
|
||||||
|
-- Set `ssl_protocol' with `Tls_1_0'.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
set_ssl_protocol_to_tls_1_1
|
||||||
|
-- Set `ssl_protocol' with `Tls_1_1'.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
set_ssl_protocol_to_tls_1_2
|
||||||
|
-- Set `ssl_protocol' with `Tls_1_2'.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
set_ssl_protocol_to_dtls_1_0
|
||||||
|
-- Set `ssl_protocol' with `Dtls_1_0'.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2014, 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
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Various constant values used in httpd settings.
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
HTTPD_CONSTANTS
|
||||||
|
|
||||||
|
feature -- Default connection settings
|
||||||
|
|
||||||
|
default_http_server_port: INTEGER = 80
|
||||||
|
default_max_concurrent_connections: INTEGER = 100
|
||||||
|
default_max_tcp_clients: INTEGER = 100
|
||||||
|
|
||||||
|
feature -- Default timeout settings
|
||||||
|
|
||||||
|
default_socket_timeout: INTEGER = 60 -- seconds
|
||||||
|
default_socket_recv_timeout: INTEGER = 5 -- seconds
|
||||||
|
|
||||||
|
feature -- Default persistent connection settings
|
||||||
|
|
||||||
|
default_keep_alive_timeout: INTEGER = 15 -- seconds
|
||||||
|
default_max_keep_alive_requests: INTEGER = 100
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Request settings for the standalone HTTPd server.
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
expanded class
|
||||||
|
HTTPD_REQUEST_SETTINGS
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
is_verbose: BOOLEAN assign set_is_verbose
|
||||||
|
-- Is verbose?
|
||||||
|
|
||||||
|
verbose_level: INTEGER assign set_verbose_level
|
||||||
|
-- Verbosity of output.
|
||||||
|
|
||||||
|
is_secure: BOOLEAN assign set_is_secure
|
||||||
|
-- Is using secure connection? i.e SSL?
|
||||||
|
|
||||||
|
timeout: INTEGER assign set_timeout
|
||||||
|
-- Amount of seconds that the server waits for receipts and transmissions during communications.
|
||||||
|
|
||||||
|
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
|
||||||
|
-- Amount of seconds that the server waits for receiving data on socket during communications.
|
||||||
|
|
||||||
|
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
|
||||||
|
-- Keep-alive timeout, also known as persistent-connection timeout.
|
||||||
|
-- Number of seconds the server waits after a request has been served before it closes the connection.
|
||||||
|
-- Unit in Seconds.
|
||||||
|
|
||||||
|
max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
|
||||||
|
-- Maximum number of requests allowed per persistent connection.
|
||||||
|
|
||||||
|
feature -- Change
|
||||||
|
|
||||||
|
set_is_verbose (b: BOOLEAN)
|
||||||
|
-- Set `is_verbose' to `b'.
|
||||||
|
do
|
||||||
|
is_verbose := b
|
||||||
|
end
|
||||||
|
|
||||||
|
set_verbose_level (lev: INTEGER)
|
||||||
|
-- Set `verbose_level' to `lev'.
|
||||||
|
do
|
||||||
|
verbose_level := lev
|
||||||
|
end
|
||||||
|
|
||||||
|
set_is_secure (b: BOOLEAN)
|
||||||
|
-- Set `is_secure' to `b'.
|
||||||
|
do
|
||||||
|
is_secure := b
|
||||||
|
end
|
||||||
|
|
||||||
|
set_timeout (a_timeout_in_seconds: INTEGER)
|
||||||
|
-- Set `timeout' to `a_timeout_in_seconds'.
|
||||||
|
do
|
||||||
|
timeout := a_timeout_in_seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
set_socket_recv_timeout (a_timeout_in_seconds: INTEGER)
|
||||||
|
-- Set `socket_recv_timeout' to `a_timeout_in_seconds'.
|
||||||
|
do
|
||||||
|
socket_recv_timeout := a_timeout_in_seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
set_keep_alive_timeout (a_timeout_in_seconds: INTEGER)
|
||||||
|
-- Set `keep_alive_timeout' to `a_timeout_in_seconds'.
|
||||||
|
do
|
||||||
|
keep_alive_timeout := a_timeout_in_seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
|
||||||
|
-- Set `max_keep_alive_requests' with `nb'
|
||||||
|
do
|
||||||
|
max_keep_alive_requests := nb
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?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\">
|
||||||
|
<cluster name="ssl_network" location="$|ssl\" recursive="true">
|
||||||
|
<condition>
|
||||||
|
<custom name="net_ssl_enabled" value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="network_until_16_05" location="$|until_16_05\">
|
||||||
|
<condition>
|
||||||
|
<version type="compiler" max="16.11.0.0"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="network_from_16_11" location="$|from_16_11\">
|
||||||
|
<condition>
|
||||||
|
<version type="compiler" min="16.11.0.0"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?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" void_safety="none" 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.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="net_ssl_enabled" value="true"/>
|
||||||
|
</condition>
|
||||||
|
</library>
|
||||||
|
<cluster name="network" location=".\network\">
|
||||||
|
<cluster name="ssl_network" location="$|ssl\" recursive="true">
|
||||||
|
<condition>
|
||||||
|
<custom name="net_ssl_enabled" value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="network_until_16_05" location="$|until_16_05\">
|
||||||
|
<condition>
|
||||||
|
<version type="compiler" max="16.11.0.0"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="network_from_16_11" location="$|from_16_11\">
|
||||||
|
<condition>
|
||||||
|
<version type="compiler" min="16.11.0.0"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
@@ -24,11 +24,29 @@
|
|||||||
</condition>
|
</condition>
|
||||||
</library>
|
</library>
|
||||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||||
|
<cluster name="network" location=".\network" recursive="false">
|
||||||
|
<cluster name="ssl_network" location="$|ssl" recursive="true">
|
||||||
|
<condition>
|
||||||
|
<custom name="httpd_ssl_enabled" value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="network_until_16_05" location="$|until_16_05\">
|
||||||
|
<condition>
|
||||||
|
<version type="compiler" max="16.11.0.0"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="network_from_16_11" location="$|from_16_11\">
|
||||||
|
<condition>
|
||||||
|
<version type="compiler" min="16.11.0.0"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
</cluster>
|
||||||
<cluster name="httpd_server" location=".\" recursive="true">
|
<cluster name="httpd_server" location=".\" recursive="true">
|
||||||
<file_rule>
|
<file_rule>
|
||||||
<exclude>/concurrency$</exclude>
|
<exclude>/concurrency$</exclude>
|
||||||
<exclude>/no_ssl$</exclude>
|
<exclude>/no_ssl$</exclude>
|
||||||
<exclude>/ssl$</exclude>
|
<exclude>/ssl$</exclude>
|
||||||
|
<exclude>/network$</exclude>
|
||||||
</file_rule>
|
</file_rule>
|
||||||
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
||||||
<condition>
|
<condition>
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
</option>
|
</option>
|
||||||
<setting name="concurrency" value="thread"/>
|
<setting name="concurrency" value="thread"/>
|
||||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
<library name="base" location="$ISE_LIBRARY\library\base\base.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="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||||||
<condition>
|
<condition>
|
||||||
@@ -24,12 +23,29 @@
|
|||||||
</condition>
|
</condition>
|
||||||
</library>
|
</library>
|
||||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||||
|
<cluster name="network" location=".\network" recursive="false">
|
||||||
|
<cluster name="ssl_network" location="$|ssl" recursive="true">
|
||||||
|
<condition>
|
||||||
|
<custom name="httpd_ssl_enabled" value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="network_until_16_05" location="$|until_16_05\">
|
||||||
|
<condition>
|
||||||
|
<version type="compiler" max="16.11.0.0"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="network_from_16_11" location="$|from_16_11\">
|
||||||
|
<condition>
|
||||||
|
<version type="compiler" min="16.11.0.0"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
</cluster>
|
||||||
<cluster name="httpd_server" location=".\" recursive="true">
|
<cluster name="httpd_server" location=".\" recursive="true">
|
||||||
<file_rule>
|
<file_rule>
|
||||||
<exclude>/EIFGENs$</exclude>
|
|
||||||
<exclude>/concurrency$</exclude>
|
<exclude>/concurrency$</exclude>
|
||||||
<exclude>/no_ssl$</exclude>
|
<exclude>/no_ssl$</exclude>
|
||||||
<exclude>/ssl$</exclude>
|
<exclude>/ssl$</exclude>
|
||||||
|
<exclude>/network$</exclude>
|
||||||
</file_rule>
|
</file_rule>
|
||||||
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
||||||
<condition>
|
<condition>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
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
|
||||||
@@ -9,12 +9,24 @@ deferred class
|
|||||||
inherit
|
inherit
|
||||||
HTTPD_DEBUG_FACILITIES
|
HTTPD_DEBUG_FACILITIES
|
||||||
|
|
||||||
|
HTTPD_LOGGER_CONSTANTS
|
||||||
|
|
||||||
|
HTTPD_SOCKET_FACTORY
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
make (a_settings: HTTPD_CONNECTION_SETTINGS)
|
make (a_request_settings: HTTPD_REQUEST_SETTINGS)
|
||||||
do
|
do
|
||||||
connection_settings := a_settings
|
|
||||||
reset
|
reset
|
||||||
|
-- Import global request settings.
|
||||||
|
timeout := a_request_settings.timeout -- seconds
|
||||||
|
socket_recv_timeout := a_request_settings.socket_recv_timeout -- seconds
|
||||||
|
keep_alive_timeout := a_request_settings.keep_alive_timeout -- seconds
|
||||||
|
max_keep_alive_requests := a_request_settings.max_keep_alive_requests
|
||||||
|
|
||||||
|
is_verbose := a_request_settings.is_verbose
|
||||||
|
verbose_level := a_request_settings.verbose_level
|
||||||
|
is_secure := a_request_settings.is_secure
|
||||||
end
|
end
|
||||||
|
|
||||||
reset
|
reset
|
||||||
@@ -42,10 +54,6 @@ feature {NONE} -- Initialization
|
|||||||
is_persistent_connection_requested := False
|
is_persistent_connection_requested := False
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
connection_settings: HTTPD_CONNECTION_SETTINGS
|
|
||||||
|
|
||||||
feature -- Status report
|
feature -- Status report
|
||||||
|
|
||||||
is_connected: BOOLEAN
|
is_connected: BOOLEAN
|
||||||
@@ -64,7 +72,7 @@ feature -- Access
|
|||||||
do
|
do
|
||||||
s := internal_client_socket
|
s := internal_client_socket
|
||||||
if s = Void then
|
if s = Void then
|
||||||
create s.make_empty
|
s := new_client_socket (is_secure)
|
||||||
internal_client_socket := s
|
internal_client_socket := s
|
||||||
end
|
end
|
||||||
Result := s
|
Result := s
|
||||||
@@ -112,37 +120,51 @@ feature -- Access
|
|||||||
feature -- Settings
|
feature -- Settings
|
||||||
|
|
||||||
is_verbose: BOOLEAN
|
is_verbose: BOOLEAN
|
||||||
do
|
-- Output messages?
|
||||||
Result := connection_settings.is_verbose
|
|
||||||
end
|
verbose_level: INTEGER
|
||||||
|
-- Output verbosity.
|
||||||
|
|
||||||
|
is_secure: BOOLEAN
|
||||||
|
-- Is secure socket?
|
||||||
|
-- i.e: SSL?
|
||||||
|
|
||||||
is_persistent_connection_supported: BOOLEAN
|
is_persistent_connection_supported: BOOLEAN
|
||||||
-- Is persistent connection supported?
|
-- Is persistent connection supported?
|
||||||
do
|
do
|
||||||
Result := {HTTPD_SERVER}.is_persistent_connection_supported
|
Result := {HTTPD_SERVER}.is_persistent_connection_supported and then max_keep_alive_requests > 0
|
||||||
end
|
|
||||||
|
|
||||||
persistent_connection_timeout: INTEGER -- seconds
|
|
||||||
-- Number of seconds for persistent connection timeout.
|
|
||||||
-- Default: 5 sec.
|
|
||||||
do
|
|
||||||
Result := connection_settings.keep_alive_timeout
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
is_next_persistent_connection_supported: BOOLEAN
|
||||||
|
-- Is next persistent connection supported?
|
||||||
|
-- note: it is relevant only if `is_persistent_connection_supported' is True.
|
||||||
|
|
||||||
|
timeout: INTEGER -- seconds
|
||||||
|
-- Amount of seconds that the server waits for receipts and transmissions during communications.
|
||||||
|
|
||||||
|
socket_recv_timeout: INTEGER -- seconds
|
||||||
|
-- Amount of seconds that the server waits for receiving data on socket during communications.
|
||||||
|
|
||||||
|
max_keep_alive_requests: INTEGER
|
||||||
|
-- Maximum number of requests allowed per persistent connection.
|
||||||
|
|
||||||
|
keep_alive_timeout: INTEGER -- seconds
|
||||||
|
-- Number of seconds for persistent connection timeout.
|
||||||
|
|
||||||
feature -- Status report
|
feature -- Status report
|
||||||
|
|
||||||
has_error: BOOLEAN
|
has_error: BOOLEAN
|
||||||
-- Error occurred during `analyze_request_message'
|
-- Error occurred during `analyze_request_message'
|
||||||
|
|
||||||
--feature -- Change
|
feature -- Change
|
||||||
--
|
|
||||||
-- set_is_verbose (b: BOOLEAN)
|
set_is_verbose (b: BOOLEAN)
|
||||||
-- -- Set `is_verbose' with `b'.
|
-- Set `is_verbose' with `b'.
|
||||||
-- do
|
do
|
||||||
-- connection_settings.is_verbose := b
|
is_verbose := b
|
||||||
-- ensure
|
ensure
|
||||||
-- is_verbose_set: is_verbose = b
|
is_verbose_set: is_verbose = b
|
||||||
-- end
|
end
|
||||||
|
|
||||||
feature -- Execution
|
feature -- Execution
|
||||||
|
|
||||||
@@ -173,9 +195,10 @@ feature -- Execution
|
|||||||
local
|
local
|
||||||
l_socket: like client_socket
|
l_socket: like client_socket
|
||||||
l_exit: BOOLEAN
|
l_exit: BOOLEAN
|
||||||
n: INTEGER
|
n,m: INTEGER
|
||||||
do
|
do
|
||||||
l_socket := client_socket
|
l_socket := client_socket
|
||||||
|
l_socket.set_recv_timeout (socket_recv_timeout)
|
||||||
check
|
check
|
||||||
socket_attached: l_socket /= Void
|
socket_attached: l_socket /= Void
|
||||||
socket_valid: l_socket.is_open_read and then l_socket.is_open_write
|
socket_valid: l_socket.is_open_read and then l_socket.is_open_write
|
||||||
@@ -183,27 +206,40 @@ feature -- Execution
|
|||||||
from
|
from
|
||||||
-- Process persistent connection as long the socket is not closed.
|
-- Process persistent connection as long the socket is not closed.
|
||||||
n := 0
|
n := 0
|
||||||
|
m := max_keep_alive_requests
|
||||||
|
is_next_persistent_connection_supported := True
|
||||||
until
|
until
|
||||||
l_exit
|
l_exit
|
||||||
loop
|
loop
|
||||||
n := n + 1
|
n := n + 1
|
||||||
|
if n >= m then
|
||||||
|
is_next_persistent_connection_supported := False
|
||||||
|
elseif n > 1 and is_verbose then
|
||||||
|
log ("Reuse connection (" + n.out + ")", information_level)
|
||||||
|
end
|
||||||
-- FIXME: it seems to be called one more time, mostly to see this is done.
|
-- FIXME: it seems to be called one more time, mostly to see this is done.
|
||||||
execute_request
|
execute_request (n > 1)
|
||||||
l_exit := not is_persistent_connection_supported
|
l_exit := not is_persistent_connection_supported
|
||||||
or has_error or l_socket.is_closed or not l_socket.is_open_read
|
or not is_next_persistent_connection_supported -- related to `max_keep_alive_requests'
|
||||||
or not is_persistent_connection_requested
|
or not is_persistent_connection_requested
|
||||||
|
or has_error or l_socket.is_closed or not l_socket.is_open_read
|
||||||
reset_request
|
reset_request
|
||||||
end
|
end
|
||||||
|
if l_exit and has_error and not l_socket.is_closed then
|
||||||
|
l_socket.close
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
execute_request
|
execute_request (a_is_reusing_connection: BOOLEAN)
|
||||||
|
-- Execute http request, and if `a_is_reusing_connection' is True
|
||||||
|
-- the execution is reusing the persistent connection.
|
||||||
require
|
require
|
||||||
is_connected: is_connected
|
is_connected: is_connected
|
||||||
|
reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported
|
||||||
local
|
local
|
||||||
l_remote_info: detachable like remote_info
|
l_remote_info: detachable like remote_info
|
||||||
l_socket: like client_socket
|
l_socket: like client_socket
|
||||||
l_is_ready: BOOLEAN
|
l_is_ready: BOOLEAN
|
||||||
i: INTEGER
|
|
||||||
do
|
do
|
||||||
l_socket := client_socket
|
l_socket := client_socket
|
||||||
check
|
check
|
||||||
@@ -219,12 +255,16 @@ feature -- Execution
|
|||||||
dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
||||||
end
|
end
|
||||||
|
|
||||||
--| TODO: add configuration options for socket timeout.
|
if a_is_reusing_connection then
|
||||||
--| set by default 5 seconds.
|
--| set by default 5 seconds.
|
||||||
l_socket.set_timeout (persistent_connection_timeout) -- 5 seconds!
|
l_socket.set_recv_timeout (keep_alive_timeout) -- in seconds!
|
||||||
l_is_ready := l_socket.ready_for_reading
|
l_is_ready := l_socket.ready_for_reading
|
||||||
|
else
|
||||||
|
l_is_ready := True
|
||||||
|
end
|
||||||
|
|
||||||
if l_is_ready then
|
if l_is_ready then
|
||||||
|
l_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response ..
|
||||||
create l_remote_info
|
create l_remote_info
|
||||||
if attached l_socket.peer_address as l_addr then
|
if attached l_socket.peer_address as l_addr then
|
||||||
l_remote_info.addr := l_addr.host_address.host_address
|
l_remote_info.addr := l_addr.host_address.host_address
|
||||||
@@ -233,9 +273,6 @@ feature -- Execution
|
|||||||
remote_info := l_remote_info
|
remote_info := l_remote_info
|
||||||
end
|
end
|
||||||
analyze_request_message (l_socket)
|
analyze_request_message (l_socket)
|
||||||
if is_verbose then
|
|
||||||
log (request_header)
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
has_error := True
|
has_error := True
|
||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
@@ -247,10 +284,13 @@ feature -- Execution
|
|||||||
if l_is_ready then
|
if l_is_ready then
|
||||||
-- check catch_bad_incoming_connection: False end
|
-- check catch_bad_incoming_connection: False end
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("ERROR: invalid HTTP incoming request")
|
log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
if is_verbose then
|
||||||
|
log (request_header, information_level)
|
||||||
|
end
|
||||||
process_request (l_socket)
|
process_request (l_socket)
|
||||||
end
|
end
|
||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
@@ -294,11 +334,11 @@ feature -- Parsing
|
|||||||
do
|
do
|
||||||
create txt.make (64)
|
create txt.make (64)
|
||||||
request_header := txt
|
request_header := txt
|
||||||
if
|
if
|
||||||
not has_error and then
|
not has_error and then
|
||||||
a_socket.is_readable and then
|
a_socket.is_readable and then
|
||||||
attached next_line (a_socket) as l_request_line and then
|
attached next_line (a_socket) as l_request_line and then
|
||||||
not l_request_line.is_empty
|
not l_request_line.is_empty
|
||||||
then
|
then
|
||||||
txt.append (l_request_line)
|
txt.append (l_request_line)
|
||||||
txt.append_character ('%N')
|
txt.append_character ('%N')
|
||||||
@@ -307,16 +347,17 @@ feature -- Parsing
|
|||||||
has_error := True
|
has_error := True
|
||||||
end
|
end
|
||||||
l_is_verbose := is_verbose
|
l_is_verbose := is_verbose
|
||||||
if not has_error or l_is_verbose then
|
if not has_error then
|
||||||
-- if `is_verbose' we can try to print the request, even if it is a bad HTTP request
|
|
||||||
from
|
from
|
||||||
line := next_line (a_socket)
|
line := next_line (a_socket)
|
||||||
until
|
until
|
||||||
line = Void or end_of_stream or has_error
|
line = Void or end_of_stream or has_error
|
||||||
loop
|
loop
|
||||||
n := line.count
|
n := line.count
|
||||||
if l_is_verbose then
|
debug ("ew_standalone")
|
||||||
log (line)
|
if l_is_verbose then
|
||||||
|
log (line, debug_level)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
pos := line.index_of (':', 1)
|
pos := line.index_of (':', 1)
|
||||||
if pos > 0 then
|
if pos > 0 then
|
||||||
@@ -363,9 +404,11 @@ feature -- Parsing
|
|||||||
local
|
local
|
||||||
n, pos, next_pos: INTEGER
|
n, pos, next_pos: INTEGER
|
||||||
do
|
do
|
||||||
if is_verbose then
|
debug ("ew_standalone")
|
||||||
log ("%N## Parse HTTP request line ##")
|
if is_verbose then
|
||||||
log (line)
|
log ("%N## Parse HTTP request line ##", debug_level)
|
||||||
|
log (line, debug_level)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
pos := line.index_of (' ', 1)
|
pos := line.index_of (' ', 1)
|
||||||
method := line.substring (1, pos - 1)
|
method := line.substring (1, pos - 1)
|
||||||
@@ -382,7 +425,7 @@ feature -- Parsing
|
|||||||
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
|
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
|
||||||
-- Next line fetched from `a_socket' is available.
|
-- Next line fetched from `a_socket' is available.
|
||||||
require
|
require
|
||||||
not_has_error: not has_error
|
not_has_error: not has_error or is_verbose
|
||||||
is_readable: a_socket.is_open_read
|
is_readable: a_socket.is_open_read
|
||||||
local
|
local
|
||||||
retried: BOOLEAN
|
retried: BOOLEAN
|
||||||
@@ -393,19 +436,19 @@ feature -- Parsing
|
|||||||
elseif a_socket.readable then
|
elseif a_socket.readable then
|
||||||
a_socket.read_line_thread_aware
|
a_socket.read_line_thread_aware
|
||||||
Result := a_socket.last_string
|
Result := a_socket.last_string
|
||||||
-- Do no check `socket_ok' before socket operation,
|
-- Do no check `socket_ok' before socket operation,
|
||||||
-- otherwise it may be False, due to error during other socket operation in same thread.
|
-- otherwise it may be False, due to error during other socket operation in same thread.
|
||||||
if not a_socket.socket_ok then
|
if not a_socket.socket_ok then
|
||||||
has_error := True
|
has_error := True
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("%N## Socket is not ok! ##")
|
log (request_header +"%N" + Result + "%N## socket_ok=False! ##", debug_level)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Error with socket...
|
-- Error with socket...
|
||||||
has_error := True
|
has_error := True
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("%N## Socket is not readable! ##")
|
log (request_header + "%N## Socket is not readable! ##", debug_level)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
@@ -425,13 +468,17 @@ feature -- Output
|
|||||||
logger_set: logger = a_logger
|
logger_set: logger = a_logger
|
||||||
end
|
end
|
||||||
|
|
||||||
log (m: STRING)
|
log (m: STRING; a_level: INTEGER)
|
||||||
-- Log message `m'.
|
-- Log message `m'.
|
||||||
|
require
|
||||||
|
is_verbose: is_verbose
|
||||||
do
|
do
|
||||||
if attached logger as l_logger then
|
if is_verbose and (verbose_level & a_level) = a_level then
|
||||||
l_logger.log (m)
|
if attached logger as l_logger then
|
||||||
else
|
l_logger.log (m)
|
||||||
io.put_string (m + "%N")
|
else
|
||||||
|
io.put_string (m + "%N")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -51,6 +51,9 @@ feature -- Access
|
|||||||
is_verbose: BOOLEAN
|
is_verbose: BOOLEAN
|
||||||
-- Is verbose for output messages.
|
-- Is verbose for output messages.
|
||||||
|
|
||||||
|
verbose_level: INTEGER
|
||||||
|
-- Verbosity of output.
|
||||||
|
|
||||||
configuration: HTTPD_CONFIGURATION
|
configuration: HTTPD_CONFIGURATION
|
||||||
-- Associated server configuration.
|
-- Associated server configuration.
|
||||||
|
|
||||||
@@ -100,7 +103,17 @@ feature -- Execution
|
|||||||
apply_configuration
|
apply_configuration
|
||||||
is_terminated := False
|
is_terminated := False
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("%N%NStarting Web Application Server (port=" + configuration.http_server_port.out + "):%N")
|
log ("%N%NStarting Web Application Server ...")
|
||||||
|
log (" - port = " + configuration.http_server_port.out)
|
||||||
|
log (" - max_tcp_clients = " + configuration.max_tcp_clients.out)
|
||||||
|
log (" - max_concurrent_connections = " + configuration.max_concurrent_connections.out)
|
||||||
|
log (" - socket_timeout = " + configuration.socket_timeout.out + " seconds")
|
||||||
|
log (" - socket_recv_timeout = " + configuration.socket_recv_timeout.out + " seconds")
|
||||||
|
log (" - keep_alive_timeout = " + configuration.keep_alive_timeout.out + " seconds")
|
||||||
|
log (" - max_keep_alive_requests = " + configuration.max_keep_alive_requests.out)
|
||||||
|
if configuration.verbose_level > 0 then
|
||||||
|
log (" - verbose_level = " + configuration.verbose_level.out)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
is_shutdown_requested := False
|
is_shutdown_requested := False
|
||||||
listen
|
listen
|
||||||
@@ -150,7 +163,7 @@ feature -- Listening
|
|||||||
|
|
||||||
if not l_listening_socket.is_bound then
|
if not l_listening_socket.is_bound then
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("Socket could not be bound on port " + l_http_port.out)
|
log ("Socket could not be bound on port " + l_http_port.out + " !")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
l_http_port := l_listening_socket.port
|
l_http_port := l_listening_socket.port
|
||||||
@@ -159,9 +172,9 @@ feature -- Listening
|
|||||||
l_listening_socket.listen (configuration.max_tcp_clients)
|
l_listening_socket.listen (configuration.max_tcp_clients)
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
if configuration.is_secure then
|
if configuration.is_secure then
|
||||||
log ("%NHTTP Connection Server ready on port " + l_http_port.out +" : https://localhost:" + l_http_port.out + "/")
|
log ("%NListening on port " + l_http_port.out +" : https://localhost:" + l_http_port.out + "/")
|
||||||
else
|
else
|
||||||
log ("%NHTTP Connection Server ready on port " + l_http_port.out +" : http://localhost:" + l_http_port.out + "/")
|
log ("%NListening on port " + l_http_port.out +" : http://localhost:" + l_http_port.out + "/")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
on_launched (l_http_port)
|
on_launched (l_http_port)
|
||||||
@@ -312,6 +325,7 @@ feature -- Configuration change
|
|||||||
is_not_launched: not is_launched
|
is_not_launched: not is_launched
|
||||||
do
|
do
|
||||||
is_verbose := configuration.is_verbose
|
is_verbose := configuration.is_verbose
|
||||||
|
verbose_level := configuration.verbose_level
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Output
|
feature -- Output
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Since 16.11, the EiffelNet socket interface has recv_timeout and send_timeout.
|
||||||
|
]"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
TCP_STREAM_SOCKET_EXT
|
||||||
|
|
||||||
|
end
|
||||||
@@ -12,6 +12,8 @@ class
|
|||||||
create
|
create
|
||||||
make_server_by_address_and_port,
|
make_server_by_address_and_port,
|
||||||
make_server_by_port,
|
make_server_by_port,
|
||||||
|
make_client_by_address_and_port,
|
||||||
|
make_client_by_port,
|
||||||
make_from_separate,
|
make_from_separate,
|
||||||
make_empty
|
make_empty
|
||||||
|
|
||||||
@@ -30,6 +32,16 @@ feature {NONE} -- Initialization
|
|||||||
create {TCP_STREAM_SOCKET} socket.make_server_by_port (a_port)
|
create {TCP_STREAM_SOCKET} socket.make_server_by_port (a_port)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
make_client_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER)
|
||||||
|
do
|
||||||
|
create {TCP_STREAM_SOCKET} socket.make_client_by_address_and_port (an_address, a_port)
|
||||||
|
end
|
||||||
|
|
||||||
|
make_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING)
|
||||||
|
do
|
||||||
|
create {TCP_STREAM_SOCKET} socket.make_client_by_port (a_peer_port, a_peer_host)
|
||||||
|
end
|
||||||
|
|
||||||
make_from_separate (s: separate HTTPD_STREAM_SOCKET)
|
make_from_separate (s: separate HTTPD_STREAM_SOCKET)
|
||||||
require
|
require
|
||||||
descriptor_available: s.descriptor_available
|
descriptor_available: s.descriptor_available
|
||||||
@@ -50,6 +62,7 @@ feature {NONE} -- Initialization
|
|||||||
feature -- Change
|
feature -- Change
|
||||||
|
|
||||||
set_timeout (n: INTEGER)
|
set_timeout (n: INTEGER)
|
||||||
|
-- Set timeout to `n' seconds.
|
||||||
do
|
do
|
||||||
if attached {NETWORK_STREAM_SOCKET} socket as l_socket then
|
if attached {NETWORK_STREAM_SOCKET} socket as l_socket then
|
||||||
l_socket.set_timeout (n)
|
l_socket.set_timeout (n)
|
||||||
@@ -70,6 +83,22 @@ feature -- Change
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_recv_timeout (a_timeout_seconds: INTEGER)
|
||||||
|
-- Set the receive timeout in seconds on Current socket.
|
||||||
|
do
|
||||||
|
if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||||||
|
l_socket.set_recv_timeout (a_timeout_seconds)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_send_timeout (a_timeout_seconds: INTEGER)
|
||||||
|
-- Set the send timeout in seconds on Current socket.
|
||||||
|
do
|
||||||
|
if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||||||
|
l_socket.set_send_timeout (a_timeout_seconds)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
|
|
||||||
last_string: STRING
|
last_string: STRING
|
||||||
@@ -164,6 +193,11 @@ feature -- Status Report
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
exists: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := socket.exists
|
||||||
|
end
|
||||||
|
|
||||||
is_blocking: BOOLEAN
|
is_blocking: BOOLEAN
|
||||||
do
|
do
|
||||||
Result := socket.is_blocking
|
Result := socket.is_blocking
|
||||||
@@ -176,6 +210,13 @@ feature -- Status Report
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
is_connected: BOOLEAN
|
||||||
|
do
|
||||||
|
if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||||||
|
Result := l_socket.is_connected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
is_created: BOOLEAN
|
is_created: BOOLEAN
|
||||||
do
|
do
|
||||||
if attached {NETWORK_SOCKET} socket as l_socket then
|
if attached {NETWORK_SOCKET} socket as l_socket then
|
||||||
@@ -220,6 +261,16 @@ feature -- Status Report
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
connect
|
||||||
|
do
|
||||||
|
socket.connect
|
||||||
|
end
|
||||||
|
|
||||||
|
close
|
||||||
|
do
|
||||||
|
socket.close
|
||||||
|
end
|
||||||
|
|
||||||
listen (a_queue: INTEGER)
|
listen (a_queue: INTEGER)
|
||||||
do
|
do
|
||||||
socket.listen (a_queue)
|
socket.listen (a_queue)
|
||||||
@@ -17,36 +17,66 @@ inherit
|
|||||||
ready_for_writing,
|
ready_for_writing,
|
||||||
ready_for_reading,
|
ready_for_reading,
|
||||||
try_ready_for_reading,
|
try_ready_for_reading,
|
||||||
put_readable_string_8
|
put_readable_string_8,
|
||||||
|
make_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
create
|
create
|
||||||
make_ssl_server_by_address_and_port, make_ssl_server_by_port,
|
make_ssl_server_by_address_and_port, make_ssl_server_by_port,
|
||||||
make_server_by_address_and_port, make_server_by_port
|
make_server_by_address_and_port, make_server_by_port,
|
||||||
|
make_ssl_client_by_address_and_port, make_ssl_client_by_port,
|
||||||
|
make_client_by_address_and_port, make_client_by_port,
|
||||||
|
make_empty
|
||||||
|
|
||||||
create {HTTPD_STREAM_SOCKET}
|
create {HTTPD_STREAM_SOCKET}
|
||||||
make
|
make
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
make_ssl_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING)
|
make_ssl_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
|
||||||
local
|
local
|
||||||
l_socket: SSL_TCP_STREAM_SOCKET
|
l_socket: SSL_TCP_STREAM_SOCKET
|
||||||
do
|
do
|
||||||
create l_socket.make_server_by_address_and_port (an_address, a_port)
|
create l_socket.make_server_by_address_and_port (an_address, a_port)
|
||||||
l_socket.set_tls_protocol (a_ssl_protocol)
|
l_socket.set_tls_protocol (a_ssl_protocol)
|
||||||
socket := l_socket
|
socket := l_socket
|
||||||
set_certificates (a_crt, a_key)
|
set_certificates (a_crt_fn, a_key_fn)
|
||||||
end
|
end
|
||||||
|
|
||||||
make_ssl_server_by_port (a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING)
|
make_ssl_server_by_port (a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
|
||||||
local
|
local
|
||||||
l_socket: SSL_TCP_STREAM_SOCKET
|
l_socket: SSL_TCP_STREAM_SOCKET
|
||||||
do
|
do
|
||||||
create l_socket.make_server_by_port (a_port)
|
create l_socket.make_server_by_port (a_port)
|
||||||
l_socket.set_tls_protocol (a_ssl_protocol)
|
l_socket.set_tls_protocol (a_ssl_protocol)
|
||||||
socket := l_socket
|
socket := l_socket
|
||||||
set_certificates (a_crt, a_key)
|
set_certificates (a_crt_fn, a_key_fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
make_ssl_client_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
|
||||||
|
local
|
||||||
|
l_socket: SSL_TCP_STREAM_SOCKET
|
||||||
|
do
|
||||||
|
create l_socket.make_client_by_address_and_port (an_address, a_port)
|
||||||
|
l_socket.set_tls_protocol (a_ssl_protocol)
|
||||||
|
socket := l_socket
|
||||||
|
set_certificates (a_crt_fn, a_key_fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
make_ssl_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
|
||||||
|
local
|
||||||
|
l_socket: SSL_TCP_STREAM_SOCKET
|
||||||
|
do
|
||||||
|
create l_socket.make_client_by_port (a_peer_port, a_peer_host)
|
||||||
|
l_socket.set_tls_protocol (a_ssl_protocol)
|
||||||
|
socket := l_socket
|
||||||
|
set_certificates (a_crt_fn, a_key_fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
make_empty
|
||||||
|
-- <Precursor>.
|
||||||
|
do
|
||||||
|
create {SSL_TCP_STREAM_SOCKET} socket.make_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Output
|
feature -- Output
|
||||||
@@ -114,15 +144,15 @@ feature -- Status Report
|
|||||||
|
|
||||||
feature {HTTPD_STREAM_SOCKET} -- Implementation
|
feature {HTTPD_STREAM_SOCKET} -- Implementation
|
||||||
|
|
||||||
set_certificates (a_crt: STRING; a_key: STRING)
|
set_certificates (a_crt_filename, a_key_filename: detachable READABLE_STRING_GENERAL)
|
||||||
local
|
|
||||||
a_file_name: FILE_NAME
|
|
||||||
do
|
do
|
||||||
if attached {SSL_NETWORK_STREAM_SOCKET} socket as l_socket then
|
if attached {SSL_NETWORK_STREAM_SOCKET} socket as l_socket then
|
||||||
create a_file_name.make_from_string (a_crt)
|
if a_crt_filename /= Void then
|
||||||
l_socket.set_certificate_file_name (a_file_name)
|
l_socket.set_certificate_file_name (a_crt_filename)
|
||||||
create a_file_name.make_from_string (a_key)
|
end
|
||||||
l_socket.set_key_file_name (a_file_name)
|
if a_key_filename /= Void then
|
||||||
|
l_socket.set_key_file_name (a_key_filename)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ inherit
|
|||||||
|
|
||||||
create
|
create
|
||||||
make_server_by_address_and_port, make_server_by_port,
|
make_server_by_address_and_port, make_server_by_port,
|
||||||
|
make_client_by_address_and_port, make_client_by_port,
|
||||||
make_empty
|
make_empty
|
||||||
|
|
||||||
create {SSL_NETWORK_STREAM_SOCKET}
|
create {SSL_NETWORK_STREAM_SOCKET}
|
||||||
@@ -12,9 +12,13 @@ inherit
|
|||||||
make
|
make
|
||||||
end
|
end
|
||||||
|
|
||||||
|
TCP_STREAM_SOCKET_EXT
|
||||||
|
|
||||||
create
|
create
|
||||||
make_server_by_address_and_port,
|
make_server_by_address_and_port,
|
||||||
make_server_by_port,
|
make_server_by_port,
|
||||||
|
make_client_by_address_and_port,
|
||||||
|
make_client_by_port,
|
||||||
make_from_separate,
|
make_from_separate,
|
||||||
make_empty
|
make_empty
|
||||||
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Until 16.05, the EiffelNet socket interface DOES NOT have recv_timeout and send_timeout.
|
||||||
|
]"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
TCP_STREAM_SOCKET_EXT
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
descriptor: INTEGER
|
||||||
|
-- Socket descriptor of current socket
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Socket Recv and Send timeout.
|
||||||
|
|
||||||
|
-- recv_timeout: INTEGER
|
||||||
|
-- -- Receive timeout in seconds on Current socket.
|
||||||
|
-- do
|
||||||
|
-- Result := c_get_sock_recv_timeout (descriptor, level_sol_socket)
|
||||||
|
-- ensure
|
||||||
|
-- result_not_negative: Result >= 0
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- send_timeout: INTEGER
|
||||||
|
-- -- Send timeout in seconds on Current socket.
|
||||||
|
-- do
|
||||||
|
-- Result := c_get_sock_send_timeout (descriptor, level_sol_socket)
|
||||||
|
-- ensure
|
||||||
|
-- result_not_negative: Result >= 0
|
||||||
|
-- end
|
||||||
|
|
||||||
|
set_recv_timeout (a_timeout_seconds: INTEGER)
|
||||||
|
-- Set the receive timeout in seconds on Current socket.
|
||||||
|
-- if `0' the related operations will never timeout.
|
||||||
|
require
|
||||||
|
positive_timeout: a_timeout_seconds >= 0
|
||||||
|
do
|
||||||
|
c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds)
|
||||||
|
end
|
||||||
|
|
||||||
|
set_send_timeout (a_timeout_seconds: INTEGER)
|
||||||
|
-- Set the send timeout in milliseconds on Current socket.
|
||||||
|
-- if `0' the related operations will never timeout.
|
||||||
|
require
|
||||||
|
positive_timeout: a_timeout_seconds >= 0
|
||||||
|
do
|
||||||
|
c_set_sock_send_timeout (descriptor, level_sol_socket, a_timeout_seconds)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Externals
|
||||||
|
|
||||||
|
level_sol_socket: INTEGER
|
||||||
|
-- SOL_SOCKET level of options
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set_so_rcvtimeo (a_timeout_seconds: INTEGER)
|
||||||
|
-- -- Set the receive timeout in seconds on Current socket.
|
||||||
|
-- -- if `0' the related operations will never timeout.
|
||||||
|
-- require
|
||||||
|
-- positive_timeout: a_timeout_seconds >= 0
|
||||||
|
-- do
|
||||||
|
-- c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds)
|
||||||
|
-- end
|
||||||
|
|
||||||
|
c_set_sock_recv_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER)
|
||||||
|
-- C routine to set socket option `SO_RCVTIMEO' with `a_timeout_seconds' seconds.
|
||||||
|
external
|
||||||
|
"C inline"
|
||||||
|
alias
|
||||||
|
"[
|
||||||
|
#ifdef EIF_WINDOWS
|
||||||
|
int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
|
||||||
|
setsockopt((SOCKET) $a_fd, (int) $a_level, (int) SO_RCVTIMEO, (char *) &arg, sizeof(arg));
|
||||||
|
#else
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
|
||||||
|
|
||||||
|
setsockopt((int) $a_fd, (int) $a_level, (int) SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
|
||||||
|
#endif
|
||||||
|
]"
|
||||||
|
end
|
||||||
|
|
||||||
|
c_set_sock_send_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER)
|
||||||
|
-- C routine to set socket option `SO_SNDTIMEO' with `a_timeout_seconds' seconds.
|
||||||
|
external
|
||||||
|
"C inline"
|
||||||
|
alias
|
||||||
|
"[
|
||||||
|
#ifdef EIF_WINDOWS
|
||||||
|
int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
|
||||||
|
setsockopt((SOCKET) $a_fd, (int) $a_level, (int) SO_SNDTIMEO, (char *) &arg, sizeof(arg));
|
||||||
|
#else
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
|
||||||
|
|
||||||
|
setsockopt((int) $a_fd, (int) $a_level, (int) SO_SNDTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
|
||||||
|
#endif
|
||||||
|
]"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package httpd
|
||||||
|
|
||||||
|
project
|
||||||
|
httpd = "httpd-safe.ecf"
|
||||||
|
httpd = "httpd.ecf"
|
||||||
|
|
||||||
|
note
|
||||||
|
title: HTTP server
|
||||||
|
description: "[
|
||||||
|
Simple HTTP listener and handler, that can be extended easily.
|
||||||
|
]"
|
||||||
|
tags: http,httpd,server,web
|
||||||
|
collection: EWF
|
||||||
|
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)
|
||||||
|
link[license]: http://www.eiffel.com/licensing/forum.txt
|
||||||
|
link[source]: "Github" https://github.com/EiffelWebFramework/EWF/library/server/httpd
|
||||||
|
link[doc]: "Documentation" http://eiffelwebframework.github.io/EWF/
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
note
|
|
||||||
description: "Configuration for the standalone HTTPd server."
|
|
||||||
date: "$Date$"
|
|
||||||
revision: "$Revision$"
|
|
||||||
|
|
||||||
deferred class
|
|
||||||
HTTPD_CONFIGURATION_I
|
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
|
||||||
|
|
||||||
make
|
|
||||||
do
|
|
||||||
http_server_port := 80
|
|
||||||
max_concurrent_connections := 100
|
|
||||||
max_tcp_clients := 100
|
|
||||||
socket_accept_timeout := 1_000
|
|
||||||
socket_connect_timeout := 5_000
|
|
||||||
|
|
||||||
is_secure := False
|
|
||||||
create ca_crt.make_empty
|
|
||||||
create ca_key.make_empty
|
|
||||||
|
|
||||||
connection_settings.keep_alive_timeout := default_keep_alive_timeout
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
feature -- Default values
|
|
||||||
|
|
||||||
default_server_port: INTEGER = 80
|
|
||||||
default_max_concurrent_connections: INTEGER = 100
|
|
||||||
default_keep_alive_timeout: INTEGER = 5 -- in seconds.
|
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
Server_details: STRING_8
|
|
||||||
-- Detail of the server.
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
http_server_name: detachable READABLE_STRING_8 assign set_http_server_name
|
|
||||||
http_server_port: INTEGER assign set_http_server_port
|
|
||||||
max_tcp_clients: INTEGER assign set_max_tcp_clients
|
|
||||||
max_concurrent_connections: INTEGER assign set_max_concurrent_connections
|
|
||||||
socket_accept_timeout: INTEGER assign set_socket_accept_timeout
|
|
||||||
socket_connect_timeout: INTEGER assign set_socket_connect_timeout
|
|
||||||
force_single_threaded: BOOLEAN assign set_force_single_threaded
|
|
||||||
do
|
|
||||||
Result := (max_concurrent_connections = 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
is_verbose: BOOLEAN assign set_is_verbose
|
|
||||||
-- Display verbose message to the output?
|
|
||||||
|
|
||||||
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
|
|
||||||
-- Persistent connection timeout
|
|
||||||
-- Timeout unit in Seconds.
|
|
||||||
do
|
|
||||||
Result := connection_settings.keep_alive_timeout
|
|
||||||
end
|
|
||||||
|
|
||||||
connection_settings: HTTPD_CONNECTION_SETTINGS
|
|
||||||
|
|
||||||
has_ssl_support: BOOLEAN
|
|
||||||
-- Has SSL support?
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Access: SSL
|
|
||||||
|
|
||||||
is_secure: BOOLEAN
|
|
||||||
-- Is SSL/TLS session?.
|
|
||||||
|
|
||||||
ca_crt: STRING
|
|
||||||
-- the signed certificate.
|
|
||||||
|
|
||||||
ca_key: STRING
|
|
||||||
-- private key to the certificate.
|
|
||||||
|
|
||||||
ssl_protocol: NATURAL
|
|
||||||
-- By default protocol is tls 1.2.
|
|
||||||
|
|
||||||
feature -- Element change
|
|
||||||
|
|
||||||
set_http_server_name (v: detachable separate READABLE_STRING_8)
|
|
||||||
do
|
|
||||||
if v = Void then
|
|
||||||
unset_http_server_name
|
|
||||||
else
|
|
||||||
create {IMMUTABLE_STRING_8} http_server_name.make_from_separate (v)
|
|
||||||
end
|
|
||||||
--| Missing postcondition.
|
|
||||||
end
|
|
||||||
|
|
||||||
unset_http_server_name
|
|
||||||
-- Unset `http_server_name' value.
|
|
||||||
do
|
|
||||||
http_server_name := Void
|
|
||||||
ensure
|
|
||||||
unset_http_server_name: http_server_name = Void
|
|
||||||
end
|
|
||||||
|
|
||||||
set_http_server_port (v: like http_server_port)
|
|
||||||
-- Set `http_server_port' with `v'.
|
|
||||||
do
|
|
||||||
http_server_port := v
|
|
||||||
ensure
|
|
||||||
http_server_port_set: http_server_port = v
|
|
||||||
end
|
|
||||||
|
|
||||||
set_max_tcp_clients (v: like max_tcp_clients)
|
|
||||||
-- Set `max_tcp_clients' with `v'.
|
|
||||||
do
|
|
||||||
max_tcp_clients := v
|
|
||||||
ensure
|
|
||||||
max_tcp_clients_set: max_tcp_clients = v
|
|
||||||
end
|
|
||||||
|
|
||||||
set_max_concurrent_connections (v: like max_concurrent_connections)
|
|
||||||
-- Set `max_concurrent_connections' with `v'.
|
|
||||||
do
|
|
||||||
max_concurrent_connections := v
|
|
||||||
ensure
|
|
||||||
max_concurrent_connections_set : max_concurrent_connections = v
|
|
||||||
end
|
|
||||||
|
|
||||||
set_socket_accept_timeout (v: like socket_accept_timeout)
|
|
||||||
-- Set `socket_accept_timeout' with `v'
|
|
||||||
do
|
|
||||||
socket_accept_timeout := v
|
|
||||||
ensure
|
|
||||||
socket_accept_timeout_set: socket_accept_timeout = v
|
|
||||||
end
|
|
||||||
|
|
||||||
set_socket_connect_timeout (v: like socket_connect_timeout)
|
|
||||||
-- Set `socket_connect_timeout' with `v'
|
|
||||||
do
|
|
||||||
socket_connect_timeout := v
|
|
||||||
ensure
|
|
||||||
socket_connect_timeout_set: socket_connect_timeout = v
|
|
||||||
end
|
|
||||||
|
|
||||||
set_force_single_threaded (v: like force_single_threaded)
|
|
||||||
-- Force server to handle incoming request in a single thread.
|
|
||||||
-- i.e set max_concurrent_connections to 0!
|
|
||||||
obsolete
|
|
||||||
"Use set_max_concurrent_connections (0) [June/2016]"
|
|
||||||
do
|
|
||||||
if v then
|
|
||||||
set_max_concurrent_connections (0)
|
|
||||||
end
|
|
||||||
--|Missing postcondition
|
|
||||||
--| force_single_thread_set: v implies max_concurrent_connections = 0
|
|
||||||
--| not_single_thread: not v implies max_concurrent_connections > 0
|
|
||||||
end
|
|
||||||
|
|
||||||
set_is_verbose (b: BOOLEAN)
|
|
||||||
-- Set `is_verbose' to `b'
|
|
||||||
do
|
|
||||||
is_verbose := b
|
|
||||||
connection_settings.is_verbose := b
|
|
||||||
ensure
|
|
||||||
is_verbose_set: is_verbose = b
|
|
||||||
end
|
|
||||||
|
|
||||||
set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
|
|
||||||
-- Set `keep_alive_timeout' with `a_seconds'
|
|
||||||
do
|
|
||||||
connection_settings.keep_alive_timeout := a_seconds
|
|
||||||
ensure
|
|
||||||
keep_alive_timeout_set: keep_alive_timeout = a_seconds
|
|
||||||
end
|
|
||||||
|
|
||||||
mark_secure
|
|
||||||
-- Set is_secure in True
|
|
||||||
do
|
|
||||||
if has_ssl_support then
|
|
||||||
is_secure := True
|
|
||||||
if http_server_port = 80 then
|
|
||||||
set_http_server_port (443)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
is_secure := False
|
|
||||||
end
|
|
||||||
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
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Element change
|
|
||||||
|
|
||||||
set_ca_crt (a_value: STRING)
|
|
||||||
-- Set `ca_crt' with `a_value'
|
|
||||||
do
|
|
||||||
ca_crt := a_value
|
|
||||||
ensure
|
|
||||||
ca_crt_set: ca_crt = a_value
|
|
||||||
end
|
|
||||||
|
|
||||||
set_ca_key (a_value: STRING)
|
|
||||||
-- Set `ca_key' with `a_value'
|
|
||||||
do
|
|
||||||
ca_key := a_value
|
|
||||||
ensure
|
|
||||||
ca_key_set: ca_key = a_value
|
|
||||||
end
|
|
||||||
|
|
||||||
set_ssl_protocol (a_version: NATURAL)
|
|
||||||
-- Set `ssl_protocol' with `a_version'
|
|
||||||
do
|
|
||||||
ssl_protocol := a_version
|
|
||||||
ensure
|
|
||||||
ssl_protocol_set: ssl_protocol = a_version
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- SSL Helpers
|
|
||||||
|
|
||||||
set_ssl_protocol_to_ssl_2_or_3
|
|
||||||
-- Set `ssl_protocol' with `Ssl_23'.
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
set_ssl_protocol_to_tls_1_0
|
|
||||||
-- Set `ssl_protocol' with `Tls_1_0'.
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
set_ssl_protocol_to_tls_1_1
|
|
||||||
-- Set `ssl_protocol' with `Tls_1_1'.
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
set_ssl_protocol_to_tls_1_2
|
|
||||||
-- Set `ssl_protocol' with `Tls_1_2'.
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
set_ssl_protocol_to_dtls_1_0
|
|
||||||
-- Set `ssl_protocol' with `Dtls_1_0'.
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
note
|
|
||||||
copyright: "2011-2014, 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
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
note
|
|
||||||
description: "[
|
|
||||||
Connection settings for the standalone HTTPd server.
|
|
||||||
]"
|
|
||||||
author: "$Author$"
|
|
||||||
date: "$Date$"
|
|
||||||
revision: "$Revision$"
|
|
||||||
|
|
||||||
expanded class
|
|
||||||
HTTPD_CONNECTION_SETTINGS
|
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
is_verbose: BOOLEAN assign set_is_verbose
|
|
||||||
-- Is verbose?
|
|
||||||
|
|
||||||
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
|
|
||||||
-- Keep-alive timeout, also known as persistent-connection timeout.
|
|
||||||
|
|
||||||
feature -- Change
|
|
||||||
|
|
||||||
set_is_verbose (b: BOOLEAN)
|
|
||||||
-- Set `is_verbose' to `b'.
|
|
||||||
do
|
|
||||||
is_verbose := True
|
|
||||||
end
|
|
||||||
|
|
||||||
set_keep_alive_timeout (a_timeout_in_seconds: INTEGER)
|
|
||||||
-- Set `keep_alive_timeout' to `a_timeout_in_seconds'.
|
|
||||||
do
|
|
||||||
keep_alive_timeout := a_timeout_in_seconds
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -19,13 +19,14 @@ inherit
|
|||||||
SHARED_HTML_ENCODER
|
SHARED_HTML_ENCODER
|
||||||
|
|
||||||
create
|
create
|
||||||
|
make,
|
||||||
make_with_connector
|
make_with_connector
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
make_with_connector (a_connection_settings: HTTPD_CONNECTION_SETTINGS; conn: like connector)
|
make_with_connector (a_request_settings: HTTPD_REQUEST_SETTINGS; conn: like connector)
|
||||||
do
|
do
|
||||||
make (a_connection_settings)
|
make (a_request_settings)
|
||||||
connector := conn
|
connector := conn
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -52,11 +53,6 @@ feature -- SCOOP helpers
|
|||||||
Result := conn.base
|
Result := conn.base
|
||||||
end
|
end
|
||||||
|
|
||||||
-- is_connector_verbose (conn: separate WGI_STANDALONE_CONNECTOR [G]): BOOLEAN
|
|
||||||
-- do
|
|
||||||
-- Result := conn.is_verbose
|
|
||||||
-- end
|
|
||||||
|
|
||||||
feature -- Request processing
|
feature -- Request processing
|
||||||
|
|
||||||
process_request (a_socket: HTTPD_STREAM_SOCKET)
|
process_request (a_socket: HTTPD_STREAM_SOCKET)
|
||||||
@@ -83,7 +79,7 @@ feature -- Request processing
|
|||||||
else
|
else
|
||||||
l_output.set_http_version (version)
|
l_output.set_http_version (version)
|
||||||
end
|
end
|
||||||
res.set_is_persistent_connection_supported ({HTTPD_SERVER}.is_persistent_connection_supported)
|
res.set_is_persistent_connection_supported (is_persistent_connection_supported and is_next_persistent_connection_supported)
|
||||||
res.set_is_persistent_connection_requested (is_persistent_connection_requested)
|
res.set_is_persistent_connection_requested (is_persistent_connection_requested)
|
||||||
|
|
||||||
req.set_meta_string_variable ("RAW_HEADER_DATA", request_header)
|
req.set_meta_string_variable ("RAW_HEADER_DATA", request_header)
|
||||||
@@ -234,6 +230,9 @@ feature -- Request processing
|
|||||||
set_environment_variable (l_server_port, "SERVER_PORT", Result)
|
set_environment_variable (l_server_port, "SERVER_PORT", Result)
|
||||||
set_environment_variable (version, "SERVER_PROTOCOL", Result)
|
set_environment_variable (version, "SERVER_PROTOCOL", Result)
|
||||||
set_environment_variable ({HTTPD_CONFIGURATION}.Server_details, "SERVER_SOFTWARE", Result)
|
set_environment_variable ({HTTPD_CONFIGURATION}.Server_details, "SERVER_SOFTWARE", Result)
|
||||||
|
if is_secure then
|
||||||
|
set_environment_variable ("on", "HTTPS", Result)
|
||||||
|
end
|
||||||
|
|
||||||
--| Apply `base' value
|
--| Apply `base' value
|
||||||
l_base := base
|
l_base := base
|
||||||
|
|||||||
@@ -14,23 +14,23 @@ feature -- Access
|
|||||||
connector: detachable separate WGI_STANDALONE_CONNECTOR [G]
|
connector: detachable separate WGI_STANDALONE_CONNECTOR [G]
|
||||||
-- httpd solution.
|
-- httpd solution.
|
||||||
|
|
||||||
connection_settings: HTTPD_CONNECTION_SETTINGS
|
request_settings: HTTPD_REQUEST_SETTINGS
|
||||||
-- Connection settings related to httpd solution.
|
-- Settings specific to request handling.
|
||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
set_connector (a_settings: HTTPD_CONNECTION_SETTINGS; conn: like connector)
|
update_with (conn: like connector; a_conf: separate HTTPD_CONFIGURATION)
|
||||||
-- Set `connector' with `conn'.
|
-- Set `connector' with `conn'.
|
||||||
do
|
do
|
||||||
connection_settings := a_settings
|
|
||||||
connector := conn
|
connector := conn
|
||||||
|
request_settings := a_conf.request_settings
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Factory
|
feature -- Factory
|
||||||
|
|
||||||
new_handler: separate WGI_HTTPD_REQUEST_HANDLER [G]
|
new_handler: separate WGI_HTTPD_REQUEST_HANDLER [G]
|
||||||
do
|
do
|
||||||
create Result.make_with_connector (connection_settings, connector)
|
create Result.make_with_connector (request_settings, connector)
|
||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
note
|
note
|
||||||
description: "[
|
description: "[
|
||||||
Standalone Web Server connector
|
Standalone Web Server connector.
|
||||||
]"
|
]"
|
||||||
date: "$Date$"
|
date: "$Date: 2016-08-06 13:34:52 +0200 (sam., 06 août 2016) $"
|
||||||
revision: "$Revision$"
|
revision: "$Revision: 99106 $"
|
||||||
|
|
||||||
class
|
class
|
||||||
WGI_STANDALONE_CONNECTOR [G -> WGI_EXECUTION create make end]
|
WGI_STANDALONE_CONNECTOR [G -> WGI_EXECUTION create make end]
|
||||||
@@ -20,24 +20,19 @@ feature {NONE} -- Initialization
|
|||||||
make
|
make
|
||||||
-- Create current standalone connector.
|
-- Create current standalone connector.
|
||||||
local
|
local
|
||||||
fac: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G]
|
fac: like request_handler_factory
|
||||||
do
|
do
|
||||||
-- Callbacks
|
-- Callbacks
|
||||||
create on_launched_actions
|
create on_launched_actions
|
||||||
|
|
||||||
-- Server
|
-- Server
|
||||||
-- create fac
|
|
||||||
create <NONE> fac
|
create <NONE> fac
|
||||||
request_handler_factory := fac
|
request_handler_factory := fac
|
||||||
|
|
||||||
create server.make (fac)
|
create server.make (fac)
|
||||||
-- create <NONE> server.make (fac)
|
|
||||||
-- create observer
|
|
||||||
create <NONE> observer
|
create <NONE> observer
|
||||||
configuration := server_configuration (server)
|
configuration := server_configuration (server)
|
||||||
controller := server_controller (server)
|
controller := server_controller (server)
|
||||||
initialize_server (server)
|
initialize_server (server)
|
||||||
set_factory_connector (configuration, Current, fac)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
make_with_base (a_base: like base)
|
make_with_base (a_base: like base)
|
||||||
@@ -56,9 +51,9 @@ feature {NONE} -- Separate helper
|
|||||||
a_server.set_observer (observer)
|
a_server.set_observer (observer)
|
||||||
end
|
end
|
||||||
|
|
||||||
set_factory_connector (a_conf: like configuration; conn: detachable separate WGI_STANDALONE_CONNECTOR [G]; fac: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G])
|
update_factory (conn: detachable separate WGI_STANDALONE_CONNECTOR [G]; fac: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G]; a_conf: like configuration)
|
||||||
do
|
do
|
||||||
fac.set_connector (a_conf.connection_settings, conn)
|
fac.update_with (conn, a_conf)
|
||||||
end
|
end
|
||||||
|
|
||||||
server_configuration (a_server: like server): like configuration
|
server_configuration (a_server: like server): like configuration
|
||||||
@@ -66,22 +61,28 @@ feature {NONE} -- Separate helper
|
|||||||
Result := a_server.configuration
|
Result := a_server.configuration
|
||||||
end
|
end
|
||||||
|
|
||||||
request_handler_factory: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G]
|
|
||||||
-- REQUEST Handler factory.
|
|
||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
|
|
||||||
name: STRING_8 = "httpd"
|
name: STRING_8
|
||||||
-- Name of Current connector
|
-- Name of Current connector
|
||||||
|
once
|
||||||
|
Result := "httpd"
|
||||||
|
end
|
||||||
|
|
||||||
version: STRING_8 = "0.1"
|
version: STRING_8
|
||||||
-- Version of Current connector
|
-- Version of Current connector
|
||||||
|
once
|
||||||
|
Result := "1.0"
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
|
|
||||||
server: separate HTTPD_SERVER
|
server: separate HTTPD_SERVER
|
||||||
-- HTTPd server object.
|
-- HTTPd server object.
|
||||||
|
|
||||||
|
request_handler_factory: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G]
|
||||||
|
-- Factory for request handlers.
|
||||||
|
|
||||||
controller: separate HTTPD_CONTROLLER
|
controller: separate HTTPD_CONTROLLER
|
||||||
-- Controller used to shutdown server.
|
-- Controller used to shutdown server.
|
||||||
|
|
||||||
@@ -105,9 +106,6 @@ feature -- Status report
|
|||||||
-- Listening port.
|
-- Listening port.
|
||||||
--| 0: not launched
|
--| 0: not launched
|
||||||
|
|
||||||
is_verbose: BOOLEAN
|
|
||||||
-- Is verbose?
|
|
||||||
|
|
||||||
feature -- Callbacks
|
feature -- Callbacks
|
||||||
|
|
||||||
on_launched_actions: ACTION_SEQUENCE [TUPLE [WGI_STANDALONE_CONNECTOR [WGI_EXECUTION]]]
|
on_launched_actions: ACTION_SEQUENCE [TUPLE [WGI_STANDALONE_CONNECTOR [WGI_EXECUTION]]]
|
||||||
@@ -125,11 +123,6 @@ feature -- Event
|
|||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
update_configuration (cfg: separate HTTPD_CONFIGURATION)
|
|
||||||
do
|
|
||||||
set_factory_connector (cfg, Current, request_handler_factory)
|
|
||||||
end
|
|
||||||
|
|
||||||
set_base (v: like base)
|
set_base (v: like base)
|
||||||
-- Set base url `base' to `v'.
|
-- Set base url `base' to `v'.
|
||||||
require
|
require
|
||||||
@@ -162,6 +155,12 @@ feature -- Element change
|
|||||||
set_is_verbose_on_configuration (b, configuration)
|
set_is_verbose_on_configuration (b, configuration)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_is_secure (b: BOOLEAN)
|
||||||
|
-- Set is_secure connection mode.
|
||||||
|
-- i.e: using SSL.
|
||||||
|
do
|
||||||
|
set_is_secure_on_configuration (b, configuration)
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Server
|
feature -- Server
|
||||||
|
|
||||||
@@ -203,18 +202,22 @@ feature {NONE} -- Implementation
|
|||||||
Result := a_server.controller
|
Result := a_server.controller
|
||||||
end
|
end
|
||||||
|
|
||||||
configure_server (a_configuration: like configuration)
|
apply_configuration (a_configuration: like configuration)
|
||||||
|
local
|
||||||
|
v: BOOLEAN
|
||||||
do
|
do
|
||||||
if a_configuration.is_verbose then
|
v := a_configuration.is_verbose
|
||||||
if attached base as l_base then
|
if v then
|
||||||
|
if attached base as l_base and then not l_base.is_whitespace then
|
||||||
io.error.put_string ("Base=" + l_base + "%N")
|
io.error.put_string ("Base=" + l_base + "%N")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
update_factory (Current, request_handler_factory, a_configuration)
|
||||||
end
|
end
|
||||||
|
|
||||||
launch_server (a_server: like server)
|
launch_server (a_server: like server)
|
||||||
do
|
do
|
||||||
configure_server (a_server.configuration)
|
apply_configuration (a_server.configuration)
|
||||||
a_server.launch
|
a_server.launch
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -242,13 +245,17 @@ feature {NONE} -- Implementation: element change
|
|||||||
|
|
||||||
set_is_verbose_on_configuration (b: BOOLEAN; cfg: like configuration)
|
set_is_verbose_on_configuration (b: BOOLEAN; cfg: like configuration)
|
||||||
do
|
do
|
||||||
is_verbose := b
|
|
||||||
cfg.set_is_verbose (b)
|
cfg.set_is_verbose (b)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_is_secure_on_configuration (b: BOOLEAN; cfg: like configuration)
|
||||||
|
do
|
||||||
|
cfg.set_is_secure (b)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
note
|
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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Interface to access protected feature from {WGI_STANDALONE_CONNECTOR}.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WGI_STANDALONE_CONNECTOR_EXPORTER
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Constants value related to Standalone connector,
|
||||||
|
and indirectly to `httpd' component.
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WGI_STANDALONE_CONSTANTS
|
||||||
|
|
||||||
|
inherit
|
||||||
|
ANY
|
||||||
|
|
||||||
|
HTTPD_CONSTANTS
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
note
|
||||||
|
description: "Export HTTPD_LOGGER_CONSTANTS to Standlone connector interfaces."
|
||||||
|
|
||||||
|
class
|
||||||
|
WGI_STANDALONE_HTTPD_LOGGER_CONSTANTS
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTPD_LOGGER_CONSTANTS
|
||||||
|
|
||||||
|
end
|
||||||
@@ -24,7 +24,7 @@ feature {NONE} -- Initialization
|
|||||||
set_source (a_source)
|
set_source (a_source)
|
||||||
end
|
end
|
||||||
|
|
||||||
feature {WGI_STANDALONE_CONNECTOR, WGI_SERVICE} -- Standalone connector.
|
feature {WGI_STANDALONE_CONNECTOR, WGI_SERVICE, WGI_STANDALONE_CONNECTOR_EXPORTER} -- Standalone
|
||||||
|
|
||||||
set_source (i: like source)
|
set_source (i: like source)
|
||||||
do
|
do
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ feature -- Header output operation
|
|||||||
l_connection := s.substring (i + 12, j - 1)
|
l_connection := s.substring (i + 12, j - 1)
|
||||||
l_connection.adjust
|
l_connection.adjust
|
||||||
if
|
if
|
||||||
not is_http_version_1_0 and
|
|
||||||
not l_connection.is_case_insensitive_equal_general ("close")
|
not l_connection.is_case_insensitive_equal_general ("close")
|
||||||
then
|
then
|
||||||
s.replace_substring ("Connection: close", i + 1, j - 1)
|
s.replace_substring ("Connection: close", i + 1, j - 1)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<library name="encoder" location="..\..\..\..\text\encoder\encoder-safe.ecf"/>
|
<library name="encoder" location="..\..\..\..\text\encoder\encoder-safe.ecf"/>
|
||||||
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
|
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
|
||||||
<library name="http" location="..\..\..\..\network\protocol\http\http-safe.ecf"/>
|
<library name="http" location="..\..\..\..\network\protocol\http\http-safe.ecf"/>
|
||||||
<library name="httpd" location="src\httpd\httpd-safe.ecf" readonly="false"/>
|
<library name="httpd" location="lib\httpd\httpd-safe.ecf" readonly="false"/>
|
||||||
<cluster name="src" location=".\src\">
|
<cluster name="src" location=".\src\">
|
||||||
<cluster name="implementation" location="$|implementation\" hidden="true"/>
|
<cluster name="implementation" location="$|implementation\" hidden="true"/>
|
||||||
</cluster>
|
</cluster>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<library name="encoder" location="..\..\..\..\text\encoder\encoder.ecf"/>
|
<library name="encoder" location="..\..\..\..\text\encoder\encoder.ecf"/>
|
||||||
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
|
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
|
||||||
<library name="http" location="..\..\..\..\network\protocol\http\http.ecf"/>
|
<library name="http" location="..\..\..\..\network\protocol\http\http.ecf"/>
|
||||||
<library name="httpd" location="src\httpd\httpd.ecf" readonly="false"/>
|
<library name="httpd" location="lib\httpd\httpd.ecf" readonly="false"/>
|
||||||
<cluster name="src" location=".\src\">
|
<cluster name="src" location=".\src\">
|
||||||
<cluster name="implementation" location="$|implementation\" hidden="true"/>
|
<cluster name="implementation" location="$|implementation\" hidden="true"/>
|
||||||
</cluster>
|
</cluster>
|
||||||
|
|||||||
@@ -10,12 +10,11 @@
|
|||||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
<library name="connector_standalone" location="standalone-safe.ecf" readonly="false"/>
|
<library name="connector_standalone" location="standalone-safe.ecf" readonly="false"/>
|
||||||
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
|
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
|
||||||
<library name="httpd_edit" location="src\httpd\httpd-safe.ecf" readonly="false">
|
<library name="httpd_edit" location="lib\httpd\httpd-safe.ecf" readonly="false">
|
||||||
<option debug="true">
|
<option debug="true">
|
||||||
<debug name="dbglog" enabled="true"/>
|
<debug name="dbglog" enabled="true"/>
|
||||||
</option>
|
</option>
|
||||||
</library>
|
</library>
|
||||||
<library name="net_ssl_edit" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf" readonly="false"/>
|
|
||||||
<library name="wsf" location="..\..\..\wsf\wsf-safe.ecf" readonly="false"/>
|
<library name="wsf" location="..\..\..\wsf\wsf-safe.ecf" readonly="false"/>
|
||||||
<cluster name="tests" location="tests\" recursive="true"/>
|
<cluster name="tests" location="tests\" recursive="true"/>
|
||||||
</target>
|
</target>
|
||||||
|
|||||||
@@ -2,14 +2,27 @@ note
|
|||||||
description: "[
|
description: "[
|
||||||
Component to launch the service using the default connector
|
Component to launch the service using the default connector
|
||||||
|
|
||||||
Eiffel Web httpd for this class
|
EiffelWeb httpd for this class
|
||||||
|
|
||||||
|
The httpd default connector support options:
|
||||||
|
verbose: to display verbose output
|
||||||
|
port: numeric such as 8099 (or equivalent string as "8099")
|
||||||
|
base: base_url (very specific to standalone server)
|
||||||
|
|
||||||
|
max_concurrent_connections: set one, for single threaded behavior
|
||||||
|
max_tcp_clients: max number of open tcp connection
|
||||||
|
|
||||||
|
socket_timeout: connection timeout
|
||||||
|
socket_recv_timeout: read data timeout
|
||||||
|
|
||||||
|
keep_alive_timeout: amount of time the server will wait for subsequent
|
||||||
|
requests on a persistent connection,
|
||||||
|
max_keep_alive_requests: number of requests allowed on a persistent connection,
|
||||||
|
|
||||||
|
ssl_enabled: set to True for https support.
|
||||||
|
ssl_ca_crt: path to the certificat crt file (relevant when ssl_enabled is True)
|
||||||
|
ssl_ca_key: path to the certificat key file (relevant when ssl_enabled is True)
|
||||||
|
|
||||||
The httpd default connector support options:
|
|
||||||
port: numeric such as 8099 (or equivalent string as "8099")
|
|
||||||
base: base_url (very specific to standalone server)
|
|
||||||
verbose: to display verbose output, useful for standalone connector
|
|
||||||
force_single_threaded: use only one thread, useful for standalone connector
|
|
||||||
|
|
||||||
check WSF_SERVICE_LAUNCHER for more documentation
|
check WSF_SERVICE_LAUNCHER for more documentation
|
||||||
]"
|
]"
|
||||||
@@ -25,6 +38,8 @@ inherit
|
|||||||
launchable
|
launchable
|
||||||
end
|
end
|
||||||
|
|
||||||
|
WGI_STANDALONE_HTTPD_LOGGER_CONSTANTS
|
||||||
|
|
||||||
create
|
create
|
||||||
make,
|
make,
|
||||||
make_and_launch
|
make_and_launch
|
||||||
@@ -34,12 +49,21 @@ feature {NONE} -- Initialization
|
|||||||
initialize
|
initialize
|
||||||
local
|
local
|
||||||
conn: like connector
|
conn: like connector
|
||||||
|
s: READABLE_STRING_GENERAL
|
||||||
do
|
do
|
||||||
create on_launched_actions
|
create on_launched_actions
|
||||||
create on_stopped_actions
|
create on_stopped_actions
|
||||||
|
|
||||||
port_number := 80 --| Default, but quite often, this port is already used ...
|
port_number := {WGI_STANDALONE_CONSTANTS}.default_http_server_port --| Default, but quite often, this port is already used ...
|
||||||
keep_alive_timeout := 5_000 -- 5 seconds.
|
max_concurrent_connections := {WGI_STANDALONE_CONSTANTS}.default_max_concurrent_connections
|
||||||
|
max_tcp_clients := {WGI_STANDALONE_CONSTANTS}.default_max_tcp_clients
|
||||||
|
socket_timeout := {WGI_STANDALONE_CONSTANTS}.default_socket_timeout -- seconds
|
||||||
|
socket_recv_timeout := {WGI_STANDALONE_CONSTANTS}.default_socket_recv_timeout -- seconds
|
||||||
|
keep_alive_timeout := {WGI_STANDALONE_CONSTANTS}.default_keep_alive_timeout -- seconds.
|
||||||
|
max_keep_alive_requests := {WGI_STANDALONE_CONSTANTS}.default_max_keep_alive_requests
|
||||||
|
verbose := False
|
||||||
|
verbose_level := notice_level
|
||||||
|
|
||||||
base_url := ""
|
base_url := ""
|
||||||
|
|
||||||
if attached options as opts then
|
if attached options as opts then
|
||||||
@@ -51,13 +75,52 @@ feature {NONE} -- Initialization
|
|||||||
end
|
end
|
||||||
|
|
||||||
verbose := opts.option_boolean_value ("verbose", verbose)
|
verbose := opts.option_boolean_value ("verbose", verbose)
|
||||||
|
-- See `{HTTPD_REQUEST_HANDLER_I}.*_verbose_level`
|
||||||
|
|
||||||
|
if opts.has_integer_option ("verbose_level") then
|
||||||
|
verbose_level := opts.option_integer_value ("verbose_level", verbose_level)
|
||||||
|
elseif attached {READABLE_STRING_GENERAL} opts.option ("verbose_level") as s_verbose_level then
|
||||||
|
verbose_level := 0 -- Reset
|
||||||
|
across
|
||||||
|
s_verbose_level.split ('+') as ic
|
||||||
|
loop
|
||||||
|
s := ic.item
|
||||||
|
if s.is_case_insensitive_equal ("alert") then
|
||||||
|
verbose_level := verbose_level | alert_level
|
||||||
|
elseif s.is_case_insensitive_equal ("critical") then
|
||||||
|
verbose_level := verbose_level | critical_level
|
||||||
|
elseif s.is_case_insensitive_equal ("error") then
|
||||||
|
verbose_level := verbose_level | error_level
|
||||||
|
elseif s.is_case_insensitive_equal ("warning") then
|
||||||
|
verbose_level := verbose_level | warning_level
|
||||||
|
elseif s.is_case_insensitive_equal ("notice") then
|
||||||
|
verbose_level := verbose_level | notice_level
|
||||||
|
elseif s.is_case_insensitive_equal ("information") then
|
||||||
|
verbose_level := verbose_level | information_level
|
||||||
|
elseif s.is_case_insensitive_equal ("debug") then
|
||||||
|
verbose_level := verbose_level | debug_level
|
||||||
|
else
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
port_number := opts.option_integer_value ("port", port_number)
|
port_number := opts.option_integer_value ("port", port_number)
|
||||||
|
|
||||||
if opts.option_boolean_value ("force_single_threaded", single_threaded) then
|
if opts.option_boolean_value ("force_single_threaded", False) then
|
||||||
force_single_threaded
|
force_single_threaded
|
||||||
end
|
end
|
||||||
max_concurrent_connections := opts.option_integer_value ("max_concurrent_connections", max_concurrent_connections)
|
max_concurrent_connections := opts.option_integer_value ("max_concurrent_connections", max_concurrent_connections)
|
||||||
|
max_tcp_clients := opts.option_integer_value ("max_tcp_clients", max_tcp_clients)
|
||||||
|
socket_timeout := opts.option_integer_value ("socket_timeout", socket_timeout)
|
||||||
|
socket_recv_timeout := opts.option_integer_value ("socket_recv_timeout", socket_recv_timeout)
|
||||||
keep_alive_timeout := opts.option_integer_value ("keep_alive_timeout", keep_alive_timeout)
|
keep_alive_timeout := opts.option_integer_value ("keep_alive_timeout", keep_alive_timeout)
|
||||||
|
max_keep_alive_requests := opts.option_integer_value ("max_keep_alive_requests", max_keep_alive_requests)
|
||||||
|
|
||||||
|
if
|
||||||
|
opts.option_boolean_value ("ssl_enabled", ssl_enabled) and then
|
||||||
|
attached opts.option_string_32_value ("ssl_protocol", "tls_1_2") as ssl_prot
|
||||||
|
then
|
||||||
|
ssl_settings := [ssl_prot, opts.option_string_32_value ("ssl_ca_crt", Void), opts.option_string_32_value ("ssl_ca_key", Void)]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
create conn.make
|
create conn.make
|
||||||
@@ -65,27 +128,30 @@ feature {NONE} -- Initialization
|
|||||||
conn.on_launched_actions.extend (agent on_launched)
|
conn.on_launched_actions.extend (agent on_launched)
|
||||||
conn.set_base (base_url)
|
conn.set_base (base_url)
|
||||||
|
|
||||||
update_configuration (conn, conn.configuration)
|
update_configuration (conn.configuration)
|
||||||
end
|
end
|
||||||
|
|
||||||
force_single_threaded
|
force_single_threaded
|
||||||
-- Set `single_threaded' to True.
|
-- Set `single_threaded' to True.
|
||||||
do
|
do
|
||||||
max_concurrent_connections := 0
|
max_concurrent_connections := 1
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Execution
|
feature -- Execution
|
||||||
|
|
||||||
update_configuration (conn: like connector; cfg: like connector.configuration)
|
update_configuration (cfg: like connector.configuration)
|
||||||
do
|
do
|
||||||
cfg.set_is_verbose (verbose)
|
cfg.set_is_verbose (verbose)
|
||||||
if attached server_name as l_server_name then
|
cfg.set_verbose_level (verbose_level)
|
||||||
cfg.set_http_server_name (l_server_name)
|
cfg.set_ssl_settings (ssl_settings)
|
||||||
end
|
cfg.set_http_server_name (server_name)
|
||||||
cfg.http_server_port := port_number
|
cfg.http_server_port := port_number
|
||||||
cfg.set_max_concurrent_connections (max_concurrent_connections)
|
cfg.set_max_concurrent_connections (max_concurrent_connections)
|
||||||
|
cfg.set_max_tcp_clients (max_tcp_clients)
|
||||||
|
cfg.set_socket_timeout (socket_timeout)
|
||||||
|
cfg.set_socket_recv_timeout (socket_recv_timeout)
|
||||||
cfg.set_keep_alive_timeout (keep_alive_timeout)
|
cfg.set_keep_alive_timeout (keep_alive_timeout)
|
||||||
conn.update_configuration (cfg)
|
cfg.set_max_keep_alive_requests (max_keep_alive_requests)
|
||||||
end
|
end
|
||||||
|
|
||||||
launch
|
launch
|
||||||
@@ -99,28 +165,34 @@ feature -- Execution
|
|||||||
debug ("ew_standalone")
|
debug ("ew_standalone")
|
||||||
if verbose then
|
if verbose then
|
||||||
io.error.put_string ("Launching standalone web server on port " + port_number.out)
|
io.error.put_string ("Launching standalone web server on port " + port_number.out)
|
||||||
if attached server_name as l_name then
|
if ssl_enabled then
|
||||||
io.error.put_string ("%N http://" + l_name + ":" + port_number.out + "/" + base_url + "%N")
|
io.error.put_string ("%N https://")
|
||||||
else
|
else
|
||||||
io.error.put_string ("%N http://localhost:" + port_number.out + "/" + base_url + "%N")
|
io.error.put_string ("%N http://")
|
||||||
end
|
end
|
||||||
|
if attached server_name as l_name then
|
||||||
|
io.error.put_string (l_name)
|
||||||
|
else
|
||||||
|
io.error.put_string ("localhost")
|
||||||
|
end
|
||||||
|
io.error.put_string (":" + port_number.out + "/" + base_url + "%N")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
update_configuration (conn, conn.configuration)
|
update_configuration (conn.configuration)
|
||||||
conn.launch
|
conn.launch
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Callback
|
feature -- Callback
|
||||||
|
|
||||||
on_launched_actions: ACTION_SEQUENCE [TUPLE [WGI_STANDALONE_CONNECTOR [G]]]
|
on_launched_actions: ACTION_SEQUENCE [TUPLE [like connector]]
|
||||||
-- Actions triggered when launched
|
-- Actions triggered when launched
|
||||||
|
|
||||||
on_stopped_actions: ACTION_SEQUENCE [TUPLE [WGI_STANDALONE_CONNECTOR [G]]]
|
on_stopped_actions: ACTION_SEQUENCE [TUPLE [like connector]]
|
||||||
-- Actions triggered when stopped
|
-- Actions triggered when stopped
|
||||||
|
|
||||||
feature {NONE} -- Implementation
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
on_launched (conn: WGI_STANDALONE_CONNECTOR [G])
|
on_launched (conn: like connector)
|
||||||
do
|
do
|
||||||
on_launched_actions.call ([conn])
|
on_launched_actions.call ([conn])
|
||||||
end
|
end
|
||||||
@@ -132,16 +204,30 @@ feature {NONE} -- Implementation
|
|||||||
base_url: READABLE_STRING_8
|
base_url: READABLE_STRING_8
|
||||||
|
|
||||||
verbose: BOOLEAN
|
verbose: BOOLEAN
|
||||||
|
verbose_level: INTEGER
|
||||||
|
-- Help defining the verbosity.
|
||||||
|
-- The higher, the more output.
|
||||||
|
|
||||||
|
ssl_settings: detachable TUPLE [protocol: READABLE_STRING_GENERAL; ca_crt, ca_key: detachable READABLE_STRING_GENERAL]
|
||||||
|
|
||||||
|
ssl_enabled: BOOLEAN
|
||||||
|
-- Is secure server? i.e using SSL?
|
||||||
|
do
|
||||||
|
Result := attached ssl_settings as ssl and then attached ssl.protocol as prot and then not prot.is_whitespace
|
||||||
|
end
|
||||||
|
|
||||||
|
max_concurrent_connections: INTEGER
|
||||||
|
max_tcp_clients: INTEGER
|
||||||
|
socket_timeout: INTEGER
|
||||||
|
socket_recv_timeout: INTEGER
|
||||||
|
keep_alive_timeout: INTEGER
|
||||||
|
max_keep_alive_requests: INTEGER
|
||||||
|
|
||||||
single_threaded: BOOLEAN
|
single_threaded: BOOLEAN
|
||||||
do
|
do
|
||||||
Result := max_concurrent_connections = 0
|
Result := max_concurrent_connections = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
max_concurrent_connections: INTEGER
|
|
||||||
|
|
||||||
keep_alive_timeout: INTEGER
|
|
||||||
|
|
||||||
feature -- Status report
|
feature -- Status report
|
||||||
|
|
||||||
connector: WGI_STANDALONE_CONNECTOR [G]
|
connector: WGI_STANDALONE_CONNECTOR [G]
|
||||||
|
|||||||
25
library/server/wsf/connector/standalone_websocket-safe.ecf
Normal file
25
library/server/wsf/connector/standalone_websocket-safe.ecf
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="wsf_standalone_websocket" uuid="7C83D4B4-39C9-4D27-941B-0F0AAD45122E" library_target="wsf_standalone_websocket">
|
||||||
|
<target name="wsf_standalone_websocket">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/\.git$</exclude>
|
||||||
|
<exclude>/\.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||||
|
</option>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="connector_standalone" location="..\..\ewsgi\connectors\standalone\standalone-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" readonly="false"/>
|
||||||
|
<library name="error" location="..\..\..\utility\general\error\error-safe.ecf"/>
|
||||||
|
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf"/>
|
||||||
|
<library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="httpd" location="..\..\ewsgi\connectors\standalone\lib\httpd\httpd-safe.ecf"/>
|
||||||
|
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||||
|
<library name="wsf" location="..\wsf-safe.ecf"/>
|
||||||
|
<library name="wsf_standalone" location="standalone-safe.ecf"/>
|
||||||
|
<cluster name="wsf_standalone_websocket" location=".\standalone_websocket\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
25
library/server/wsf/connector/standalone_websocket.ecf
Normal file
25
library/server/wsf/connector/standalone_websocket.ecf
Normal 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="wsf_standalone_websocket" uuid="7C83D4B4-39C9-4D27-941B-0F0AAD45122E" library_target="wsf_standalone_websocket">
|
||||||
|
<target name="wsf_standalone_websocket">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/\.git$</exclude>
|
||||||
|
<exclude>/\.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" full_class_checking="true" is_obsolete_routine_type="true" void_safety="none" syntax="provisional">
|
||||||
|
</option>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||||
|
<library name="connector_standalone" location="..\..\ewsgi\connectors\standalone\standalone.ecf"/>
|
||||||
|
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/>
|
||||||
|
<library name="encoder" location="..\..\..\text\encoder\encoder.ecf" readonly="false"/>
|
||||||
|
<library name="error" location="..\..\..\utility\general\error\error.ecf"/>
|
||||||
|
<library name="ewsgi" location="..\..\ewsgi\ewsgi.ecf"/>
|
||||||
|
<library name="http" location="..\..\..\network\protocol\http\http.ecf"/>
|
||||||
|
<library name="httpd" location="..\..\ewsgi\connectors\standalone\lib\httpd\httpd.ecf"/>
|
||||||
|
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||||
|
<library name="wsf" location="..\wsf.ecf"/>
|
||||||
|
<library name="wsf_standalone" location="standalone.ecf"/>
|
||||||
|
<cluster name="wsf_standalone_websocket" location=".\standalone_websocket\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Websocket callback events for actions like opening and closing the connection,
|
||||||
|
sending and receiving messages, and listening.
|
||||||
|
|
||||||
|
Define the websocket events:
|
||||||
|
- on_open
|
||||||
|
- on_binary
|
||||||
|
- on_text
|
||||||
|
- on_close
|
||||||
|
|
||||||
|
note: the following features could also be redefined:
|
||||||
|
- on_pong
|
||||||
|
- on_ping
|
||||||
|
- on_unsupported
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WEB_SOCKET_EVENT_I
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WEB_SOCKET_CONSTANTS
|
||||||
|
|
||||||
|
REFACTORING_HELPER
|
||||||
|
|
||||||
|
feature -- Web Socket Interface
|
||||||
|
|
||||||
|
on_event (ws: WEB_SOCKET; a_message: detachable READABLE_STRING_8; a_opcode: INTEGER)
|
||||||
|
-- Called when a frame from the client has been receive
|
||||||
|
require
|
||||||
|
ws_attached: ws /= Void
|
||||||
|
ws_valid: ws.is_open_read and then ws.is_open_write
|
||||||
|
local
|
||||||
|
l_message: READABLE_STRING_8
|
||||||
|
do
|
||||||
|
debug ("ws")
|
||||||
|
ws.log ("%Non_event (ws, a_message, " + opcode_name (a_opcode) + ")%N", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
|
end
|
||||||
|
if a_message = Void then
|
||||||
|
create {STRING} l_message.make_empty
|
||||||
|
else
|
||||||
|
l_message := a_message
|
||||||
|
end
|
||||||
|
|
||||||
|
if a_opcode = Binary_frame then
|
||||||
|
on_binary (ws, l_message)
|
||||||
|
elseif a_opcode = Text_frame then
|
||||||
|
on_text (ws, l_message)
|
||||||
|
elseif a_opcode = Pong_frame then
|
||||||
|
on_pong (ws, l_message)
|
||||||
|
elseif a_opcode = Ping_frame then
|
||||||
|
on_ping (ws, l_message)
|
||||||
|
elseif a_opcode = Connection_close_frame then
|
||||||
|
on_connection_close (ws, "")
|
||||||
|
else
|
||||||
|
on_unsupported (ws, l_message, a_opcode)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Websocket events
|
||||||
|
|
||||||
|
on_open (ws: WEB_SOCKET)
|
||||||
|
-- Called after handshake, indicates that a complete WebSocket connection has been established.
|
||||||
|
require
|
||||||
|
ws_attached: ws /= Void
|
||||||
|
ws_valid: ws.is_open_read and then ws.is_open_write
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
on_binary (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
|
require
|
||||||
|
ws_attached: ws /= Void
|
||||||
|
ws_valid: ws.is_open_read and then ws.is_open_write
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
|
require
|
||||||
|
ws_attached: ws /= Void
|
||||||
|
ws_valid: ws.is_open_read and then ws.is_open_write
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
on_close (ws: detachable WEB_SOCKET)
|
||||||
|
-- Called after the WebSocket connection is closed.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Websocket events: implemented
|
||||||
|
|
||||||
|
on_pong (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
|
require
|
||||||
|
ws_attached: ws /= Void
|
||||||
|
ws_valid: ws.is_open_read and then ws.is_open_write
|
||||||
|
do
|
||||||
|
-- log ("Its a pong frame")
|
||||||
|
-- at first we ignore pong
|
||||||
|
-- FIXME: provide better explanation
|
||||||
|
end
|
||||||
|
|
||||||
|
on_ping (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
|
require
|
||||||
|
ws_attached: ws /= Void
|
||||||
|
ws_valid: ws.is_open_read and then ws.is_open_write
|
||||||
|
do
|
||||||
|
ws.send (Pong_frame, a_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
on_unsupported (ws: WEB_SOCKET; a_message: READABLE_STRING_8; a_opcode: INTEGER)
|
||||||
|
require
|
||||||
|
ws_attached: ws /= Void
|
||||||
|
ws_valid: ws.is_open_read and then ws.is_open_write
|
||||||
|
do
|
||||||
|
-- do nothing
|
||||||
|
end
|
||||||
|
|
||||||
|
on_connection_close (ws: WEB_SOCKET; a_message: detachable READABLE_STRING_8)
|
||||||
|
require
|
||||||
|
ws_attached: ws /= Void
|
||||||
|
ws_valid: ws.is_open_read and then ws.is_open_write
|
||||||
|
do
|
||||||
|
ws.send (Connection_close_frame, "")
|
||||||
|
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
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
A web socket message has an opcode specifying the type of the message payload. The
|
||||||
|
opcode consists of the last four bits in the first byte of the frame header.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
EIS: "name=Data Frame", "src=http://tools.ietf.org/html/rfc6455#section-5.6", "protocol=uri"
|
||||||
|
EIS: "name=Control Frame", "src=http://tools.ietf.org/html/rfc6455#section-5.5", "protocol=uri"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_MESSAGE_TYPE
|
||||||
|
|
||||||
|
feature -- Data Frames
|
||||||
|
|
||||||
|
Text: INTEGER = 0x1
|
||||||
|
-- The data type of the message is text.
|
||||||
|
|
||||||
|
Binary: INTEGER = 0x2
|
||||||
|
-- The data type of the message is binary.
|
||||||
|
|
||||||
|
feature -- Control Frames
|
||||||
|
|
||||||
|
Close: INTEGER = 0x8
|
||||||
|
-- The client or server is sending a closing
|
||||||
|
-- handshake to the server or client.
|
||||||
|
|
||||||
|
Ping: INTEGER = 0x9
|
||||||
|
-- The client or server sends a ping to the server or client.
|
||||||
|
|
||||||
|
Pong: INTEGER = 0xA
|
||||||
|
-- The client or server sends a pong to the server or client.
|
||||||
|
|
||||||
|
feature -- Reserverd
|
||||||
|
|
||||||
|
-- Opcodes 0x3-0x7 are reserved for further non-control frames yet to be
|
||||||
|
-- defined.
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
note
|
||||||
|
description: "Constants for WebSockets"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_CONSTANTS
|
||||||
|
|
||||||
|
feature -- Constants
|
||||||
|
|
||||||
|
|
||||||
|
HTTP_1_1: STRING = "HTTP/1.1 101 WebSocket Protocol Handshake"
|
||||||
|
|
||||||
|
Upgrade_ws: STRING = "Upgrade: websocket"
|
||||||
|
|
||||||
|
Connection_ws: STRING = "Connection: Upgrade"
|
||||||
|
|
||||||
|
Sec_WebSocket_Origin: STRING = "Sec-WebSocket-Origin: "
|
||||||
|
|
||||||
|
Sec_WebSocket_Protocol: STRING = "Sec-WebSocket-Protocol: "
|
||||||
|
|
||||||
|
Sec_WebSocket_Location: STRING = "Sec-WebSocket-Location: "
|
||||||
|
|
||||||
|
Sec_WebSocket_Version: STRING = "Sec-WebSocket-Version: "
|
||||||
|
|
||||||
|
Sec_WebSocket_Extensions: STRING = "Sec-WebSocket-Extensions: "
|
||||||
|
|
||||||
|
WebSocket_Origin: STRING = "WebSocket-Origin: "
|
||||||
|
|
||||||
|
WebSocket_Protocol: STRING = "WebSocket-Protocol: "
|
||||||
|
|
||||||
|
WebSocket_Location: STRING = "WebSocket-Location: "
|
||||||
|
|
||||||
|
Origin: STRING = "Origin"
|
||||||
|
|
||||||
|
Server: STRING = "EWSS"
|
||||||
|
|
||||||
|
Sec_WebSocket_Key: STRING = "Sec-WebSocket-Key"
|
||||||
|
|
||||||
|
Ws_scheme: STRING = "ws://"
|
||||||
|
|
||||||
|
Wss_scheme: STRING = "wss://"
|
||||||
|
|
||||||
|
Magic_guid: STRING = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||||
|
|
||||||
|
-- The handshake from the client looks as follows:
|
||||||
|
|
||||||
|
-- GET /chat HTTP/1.1
|
||||||
|
-- Host: server.example.com
|
||||||
|
-- Upgrade: websocket
|
||||||
|
-- Connection: Upgrade
|
||||||
|
-- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||||||
|
-- Origin: http://example.com
|
||||||
|
-- Sec-WebSocket-Protocol: chat, superchat
|
||||||
|
-- Sec-WebSocket-Version: 13
|
||||||
|
|
||||||
|
-- The handshake from the server looks as follows:
|
||||||
|
|
||||||
|
-- HTTP/1.1 101 Switching Protocols
|
||||||
|
-- Upgrade: websocket
|
||||||
|
-- Connection: Upgrade
|
||||||
|
-- Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|
||||||
|
-- Sec-WebSocket-Protocol: chat
|
||||||
|
|
||||||
|
feature -- Opcodes Standard actions
|
||||||
|
|
||||||
|
--| Maybe we need an enum STANDARD_ACTIONS_OPCODES?
|
||||||
|
-- |Opcode | Meaning | Reference |
|
||||||
|
-- -+--------+-------------------------------------+-----------|
|
||||||
|
-- | 0 | Continuation Frame | RFC 6455 |
|
||||||
|
-- -+--------+-------------------------------------+-----------|
|
||||||
|
-- | 1 | Text Frame | RFC 6455 |
|
||||||
|
-- -+--------+-------------------------------------+-----------|
|
||||||
|
-- | 2 | Binary Frame | RFC 6455 |
|
||||||
|
-- -+--------+-------------------------------------+-----------|
|
||||||
|
-- | 8 | Connection Close Frame | RFC 6455 |
|
||||||
|
-- -+--------+-------------------------------------+-----------|
|
||||||
|
-- | 9 | Ping Frame | RFC 6455 |
|
||||||
|
-- -+--------+-------------------------------------+-----------|
|
||||||
|
-- | 10 | Pong Frame | RFC 6455 |
|
||||||
|
-- -+--------+-------------------------------------+-----------|
|
||||||
|
|
||||||
|
Continuation_frame: INTEGER = 0
|
||||||
|
|
||||||
|
Text_frame: INTEGER = 1
|
||||||
|
|
||||||
|
Binary_frame: INTEGER = 2
|
||||||
|
|
||||||
|
Connection_close_frame: INTEGER = 8
|
||||||
|
|
||||||
|
Ping_frame: INTEGER = 9
|
||||||
|
|
||||||
|
Pong_frame: INTEGER = 10
|
||||||
|
|
||||||
|
is_control_frame (a_opcode: INTEGER): BOOLEAN
|
||||||
|
-- Is `a_opcode' a control frame?
|
||||||
|
do
|
||||||
|
inspect a_opcode
|
||||||
|
when Connection_close_frame, Ping_frame, Pong_frame then
|
||||||
|
Result := True
|
||||||
|
else
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
opcode_name (a_opcode: INTEGER): STRING
|
||||||
|
do
|
||||||
|
inspect a_opcode
|
||||||
|
when Continuation_frame then Result := "Continuation"
|
||||||
|
when Text_frame then Result := "Text"
|
||||||
|
when Binary_frame then Result := "Binary"
|
||||||
|
when Connection_close_frame then Result := "Connection Close"
|
||||||
|
when Ping_frame then Result := "Ping"
|
||||||
|
when Pong_frame then Result := "Pong"
|
||||||
|
else
|
||||||
|
Result := "Unknown-Opcode"
|
||||||
|
end
|
||||||
|
Result := "0x" + a_opcode.to_hex_string + " " + Result
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Close code numbers
|
||||||
|
|
||||||
|
-- Maybe an ENUM CLOSE_CODES
|
||||||
|
|
||||||
|
-- |Status Code | Meaning | Contact | Reference |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1000 | Normal Closure | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1001 | Going Away | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1002 | Protocol error | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1003 | Unsupported Data| hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1004 | ---Reserved---- | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1005 | No Status Rcvd | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1006 | Abnormal Closure| hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1007 | Invalid frame | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- | | payload data | | |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1008 | Policy Violation| hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1009 | Message Too Big | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1010 | Mandatory Ext. | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1011 | Internal Server | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- | | Error | | |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
-- | 1015 | TLS handshake | hybi@ietf.org | RFC 6455 |
|
||||||
|
-- -+------------+-----------------+---------------+-----------|
|
||||||
|
|
||||||
|
Normal_closure: INTEGER = 1000
|
||||||
|
-- Indicates a normal closure, meaning that the purpose for
|
||||||
|
-- which the connection was established has been fulfilled.
|
||||||
|
|
||||||
|
Going_away: INTEGER = 1001
|
||||||
|
-- Indicates that an endpoint is "going away", such as a server
|
||||||
|
-- going down or a browser having navigated away from a page.
|
||||||
|
|
||||||
|
Protocol_error: INTEGER = 1002
|
||||||
|
-- Indicates that an endpoint is terminating the connection due
|
||||||
|
-- to a protocol error.
|
||||||
|
|
||||||
|
Unsupported_data: INTEGER = 1003
|
||||||
|
-- Indicates that an endpoint is terminating the connection
|
||||||
|
-- because it has received a type of data it cannot accept (e.g., an
|
||||||
|
-- endpoint that understands only text data MAY send this if it
|
||||||
|
-- receives a binary message).
|
||||||
|
|
||||||
|
Invalid_data: INTEGER = 1007
|
||||||
|
-- Indicates that an endpoint is terminating the connection
|
||||||
|
-- because it has received data within a message that was not
|
||||||
|
-- consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
|
||||||
|
-- data within a text message).
|
||||||
|
|
||||||
|
Policy_violation: INTEGER = 1008
|
||||||
|
-- Indicates that an endpoint is terminating the connection
|
||||||
|
-- because it has received a message that violates its policy. This
|
||||||
|
-- is a generic status code that can be returned when there is no
|
||||||
|
-- other more suitable status code (e.g., 1003 or 1009) or if there
|
||||||
|
-- is a need to hide specific details about the policy.
|
||||||
|
|
||||||
|
Message_too_large: INTEGER = 1009
|
||||||
|
-- Indicates that an endpoint is terminating the connection
|
||||||
|
-- because it has received a message that is too big for it to
|
||||||
|
-- process.
|
||||||
|
|
||||||
|
Extension_required: INTEGER = 1010
|
||||||
|
-- Indicates that an endpoint (client) is terminating the
|
||||||
|
-- connection because it has expected the server to negotiate one or
|
||||||
|
-- more extension, but the server didn't return them in the response
|
||||||
|
-- message of the WebSocket handshake.
|
||||||
|
|
||||||
|
Internal_error: INTEGER = 1011
|
||||||
|
-- Indicates that a server is terminating the connection because
|
||||||
|
-- it encountered an unexpected condition that prevented it from
|
||||||
|
-- fulfilling the request.
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WEB_SOCKET_ERROR_FRAME}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_ERROR_FRAME
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_code: INTEGER; a_desc: like description)
|
||||||
|
do
|
||||||
|
code := a_code
|
||||||
|
description := a_desc
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
code: INTEGER
|
||||||
|
|
||||||
|
description: READABLE_STRING_8
|
||||||
|
|
||||||
|
feature -- Conversion
|
||||||
|
|
||||||
|
string: STRING
|
||||||
|
do
|
||||||
|
create Result.make_from_string ("Error(" + code.out + "): " + description)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,437 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Summary description for {WEB_SOCKET_FRAME}.
|
||||||
|
See Base Framing Protocol: http://tools.ietf.org/html/rfc6455#section-5.2
|
||||||
|
0 1 2 3
|
||||||
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
+-+-+-+-+-------+-+-------------+-------------------------------+
|
||||||
|
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|
||||||
|
|I|S|S|S| (4) |A| (7) | (16/64) |
|
||||||
|
|N|V|V|V| |S| | (if payload len==126/127) |
|
||||||
|
| |1|2|3| |K| | |
|
||||||
|
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||||||
|
| Extended payload length continued, if payload len == 127 |
|
||||||
|
+ - - - - - - - - - - - - - - - +-------------------------------+
|
||||||
|
| |Masking-key, if MASK set to 1 |
|
||||||
|
+-------------------------------+-------------------------------+
|
||||||
|
| Masking-key (continued) | Payload Data |
|
||||||
|
+-------------------------------- - - - - - - - - - - - - - - - +
|
||||||
|
: Payload Data continued ... :
|
||||||
|
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
||||||
|
| Payload Data continued ... |
|
||||||
|
+---------------------------------------------------------------+
|
||||||
|
|
||||||
|
Check the `check_utf_8_validity_on_chop' if there is performance issue
|
||||||
|
with bigger data.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
EIS: "name=Websocket RFC6455 section-5.2", "protocol=URI", "src=http://tools.ietf.org/html/rfc6455#section-5.2", "tag=rfc"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_FRAME
|
||||||
|
|
||||||
|
inherit
|
||||||
|
ANY
|
||||||
|
|
||||||
|
WEB_SOCKET_CONSTANTS
|
||||||
|
|
||||||
|
create
|
||||||
|
make,
|
||||||
|
make_as_injected_control
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_opcode: INTEGER; flag_is_fin: BOOLEAN)
|
||||||
|
-- Create current frame with opcode `a_opcode'
|
||||||
|
-- and `a_fin' to indicate if this is the final fragment.
|
||||||
|
do
|
||||||
|
is_incomplete := False
|
||||||
|
opcode := a_opcode
|
||||||
|
is_fin := flag_is_fin
|
||||||
|
|
||||||
|
inspect opcode
|
||||||
|
when
|
||||||
|
Continuation_frame, -- 0
|
||||||
|
Text_frame, -- 1
|
||||||
|
Binary_frame -- 2
|
||||||
|
then
|
||||||
|
--| Supported opcode
|
||||||
|
when
|
||||||
|
Connection_close_frame, -- 8
|
||||||
|
Ping_frame, -- 9
|
||||||
|
Pong_frame -- 10
|
||||||
|
then
|
||||||
|
--| Supported control opcode
|
||||||
|
-- All control frames MUST have a payload length of 125 bytes or less
|
||||||
|
-- and MUST NOT be fragmented.
|
||||||
|
if flag_is_fin then
|
||||||
|
-- So far it is valid.
|
||||||
|
else
|
||||||
|
report_error (Protocol_error, "Control frames MUST NOT be fragmented.")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
report_error (Protocol_error, "Unknown opcode")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
make_as_injected_control (a_opcode: INTEGER; a_parent: WEB_SOCKET_FRAME)
|
||||||
|
require
|
||||||
|
parent_is_not_control_frame: not a_parent.is_control
|
||||||
|
a_opcode_is_control_frame: is_control_frame (a_opcode)
|
||||||
|
do
|
||||||
|
make (a_opcode, True)
|
||||||
|
parent := a_parent
|
||||||
|
a_parent.add_injected_control_frame (Current)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
opcode: INTEGER
|
||||||
|
-- CONTINUOUS, TEXT, BINARY, PING, PONG, CLOSING
|
||||||
|
|
||||||
|
is_fin: BOOLEAN
|
||||||
|
-- is the final fragment in a message?
|
||||||
|
|
||||||
|
fragment_count: INTEGER
|
||||||
|
|
||||||
|
payload_length: NATURAL_64
|
||||||
|
payload_data: detachable STRING_8
|
||||||
|
-- Maybe we need a buffer here.
|
||||||
|
|
||||||
|
uncoded_payload_data: detachable STRING_32
|
||||||
|
local
|
||||||
|
utf: UTF_CONVERTER
|
||||||
|
do
|
||||||
|
if attached payload_data as d then
|
||||||
|
Result := utf.utf_8_string_8_to_string_32 (d)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
error: detachable WEB_SOCKET_ERROR_FRAME
|
||||||
|
-- Describe the type of error
|
||||||
|
|
||||||
|
feature -- Access: injected control frames
|
||||||
|
|
||||||
|
injected_control_frames: detachable LIST [WEB_SOCKET_FRAME]
|
||||||
|
|
||||||
|
parent: detachable WEB_SOCKET_FRAME
|
||||||
|
-- If Current is injected, `parent' is the related fragmented frame
|
||||||
|
|
||||||
|
is_injected_control: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := parent /= Void
|
||||||
|
ensure
|
||||||
|
Result implies (is_control_frame (opcode))
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Operation
|
||||||
|
|
||||||
|
update_fin (a_flag_is_fin: BOOLEAN)
|
||||||
|
do
|
||||||
|
is_fin := a_flag_is_fin
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {WEB_SOCKET_FRAME} -- Change: injected control frames
|
||||||
|
|
||||||
|
add_injected_control_frame (f: WEB_SOCKET_FRAME)
|
||||||
|
require
|
||||||
|
Current_is_not_control: not is_control
|
||||||
|
f_is_control_frame: f.is_control
|
||||||
|
parented_to_current: f.parent = Current
|
||||||
|
local
|
||||||
|
lst: like injected_control_frames
|
||||||
|
do
|
||||||
|
lst := injected_control_frames
|
||||||
|
if lst = Void then
|
||||||
|
create {ARRAYED_LIST [WEB_SOCKET_FRAME]} lst.make (1)
|
||||||
|
injected_control_frames := lst
|
||||||
|
end
|
||||||
|
lst.force (f)
|
||||||
|
ensure
|
||||||
|
parented_to_current: f.parent = Current
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_injected_control_frame (f: WEB_SOCKET_FRAME)
|
||||||
|
require
|
||||||
|
Current_is_not_control: not is_control
|
||||||
|
f_is_control_frame: f.is_control
|
||||||
|
parented_to_current: f.parent = Current
|
||||||
|
local
|
||||||
|
lst: like injected_control_frames
|
||||||
|
do
|
||||||
|
lst := injected_control_frames
|
||||||
|
if lst /= Void then
|
||||||
|
lst.prune (f)
|
||||||
|
if lst.is_empty then
|
||||||
|
injected_control_frames := Void
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Query
|
||||||
|
|
||||||
|
is_binary: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := opcode = binary_frame
|
||||||
|
end
|
||||||
|
|
||||||
|
is_text: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := opcode = text_frame
|
||||||
|
end
|
||||||
|
|
||||||
|
is_continuation: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := opcode = continuation_frame
|
||||||
|
end
|
||||||
|
|
||||||
|
is_connection_close: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := opcode = connection_close_frame
|
||||||
|
end
|
||||||
|
|
||||||
|
is_control: BOOLEAN
|
||||||
|
do
|
||||||
|
inspect opcode
|
||||||
|
when connection_close_frame, Ping_frame, Pong_frame then
|
||||||
|
Result := True
|
||||||
|
else
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
is_ping: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := opcode = ping_frame
|
||||||
|
end
|
||||||
|
|
||||||
|
is_pong: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := opcode = pong_frame
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
is_valid: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := not has_error
|
||||||
|
end
|
||||||
|
|
||||||
|
is_incomplete: BOOLEAN
|
||||||
|
|
||||||
|
has_error: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := error /= Void
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Change
|
||||||
|
|
||||||
|
increment_fragment_count
|
||||||
|
do
|
||||||
|
fragment_count := fragment_count + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
check_utf_8_validity_on_chop: BOOLEAN = False
|
||||||
|
-- True: check for each chop
|
||||||
|
-- False: check only for each fragment
|
||||||
|
--| see autobahntestsuite #6.4.3 and #6.4.4
|
||||||
|
|
||||||
|
append_payload_data_chop (a_data: STRING_8; a_len: INTEGER; a_flag_chop_complete: BOOLEAN)
|
||||||
|
do
|
||||||
|
if a_flag_chop_complete then
|
||||||
|
increment_fragment_count
|
||||||
|
end
|
||||||
|
if attached payload_data as l_payload_data then
|
||||||
|
l_payload_data.append (a_data)
|
||||||
|
else
|
||||||
|
payload_data := a_data
|
||||||
|
end
|
||||||
|
payload_length := payload_length + a_len.to_natural_64
|
||||||
|
|
||||||
|
if is_text then
|
||||||
|
if is_fin and a_flag_chop_complete then
|
||||||
|
-- Check the whole message is a valid UTF-8 string
|
||||||
|
if attached payload_data as d then
|
||||||
|
if not is_valid_utf_8_string (d) then
|
||||||
|
report_error (invalid_data, "The text message is not a valid UTF-8 text!")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- empty payload??
|
||||||
|
end
|
||||||
|
elseif check_utf_8_validity_on_chop or else a_flag_chop_complete then
|
||||||
|
-- Check the payload data as utf-8 stream (may be incomplete at this point)
|
||||||
|
if not is_valid_text_payload_stream then
|
||||||
|
report_error (invalid_data, "This is not a valid UTF-8 stream!")
|
||||||
|
-- is_valid implies the connection will be closed!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
report_error (a_code: INTEGER; a_description: READABLE_STRING_8)
|
||||||
|
require
|
||||||
|
not has_error
|
||||||
|
do
|
||||||
|
create error.make (a_code, a_description)
|
||||||
|
ensure
|
||||||
|
has_error: has_error
|
||||||
|
is_not_valid: not is_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Helper
|
||||||
|
|
||||||
|
last_utf_8_stream_validation_position: INTEGER
|
||||||
|
-- In relation with `is_valid_utf_8 (.., a_is_stream=True)'
|
||||||
|
|
||||||
|
is_valid_text_payload_stream: BOOLEAN
|
||||||
|
require
|
||||||
|
is_text_frame: is_text
|
||||||
|
do
|
||||||
|
if attached payload_data as s then
|
||||||
|
Result := is_valid_utf_8 (s, not is_fin)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
is_valid_utf_8_string (s: READABLE_STRING_8): BOOLEAN
|
||||||
|
do
|
||||||
|
Result := is_valid_utf_8 (s, False)
|
||||||
|
-- and (create {UTF_CONVERTER}).is_valid_utf_8_string_8 (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
is_valid_utf_8 (s: READABLE_STRING_8; a_is_stream: BOOLEAN): BOOLEAN
|
||||||
|
-- UTF-8 validity checker.
|
||||||
|
note
|
||||||
|
EIS: "name=UTF-8 RFC3629", "protocol=URI", "src=https://tools.ietf.org/html/rfc3629", "tag=rfc"
|
||||||
|
require
|
||||||
|
is_text_frame: is_text
|
||||||
|
local
|
||||||
|
i: like {STRING_8}.count
|
||||||
|
n: like {STRING_8}.count
|
||||||
|
c,c2,c3,c4,w: NATURAL_32
|
||||||
|
l_is_incomplete_stream: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := True
|
||||||
|
-- Following code also check that codepoint is between 0 and 0x10FFFF
|
||||||
|
-- (as expected by spec, and tested by autobahn ws testsuite)
|
||||||
|
from
|
||||||
|
if a_is_stream then
|
||||||
|
i := last_utf_8_stream_validation_position -- to avoid recomputing from the beginning each time.
|
||||||
|
else
|
||||||
|
i := 0
|
||||||
|
end
|
||||||
|
n := s.count
|
||||||
|
until
|
||||||
|
i >= n or not Result
|
||||||
|
loop
|
||||||
|
i := i + 1
|
||||||
|
c := s.code (i)
|
||||||
|
if c <= 0x7F then
|
||||||
|
-- 0xxxxxxx
|
||||||
|
w := c
|
||||||
|
elseif c <= 0xC1 then
|
||||||
|
-- The octet values C0, C1, F5 to FF never appear.
|
||||||
|
--| case 0xC0 and 0xC1
|
||||||
|
Result := False
|
||||||
|
elseif (c & 0xE0) = 0xC0 then
|
||||||
|
-- 110xxxxx 10xxxxxx
|
||||||
|
i := i + 1
|
||||||
|
if i <= n then
|
||||||
|
c2 := s.code (i)
|
||||||
|
if
|
||||||
|
(c2 & 0xC0) = 0x80
|
||||||
|
then
|
||||||
|
w := ((c & 0x1F) |<< 6)
|
||||||
|
| (c2 & 0x3F)
|
||||||
|
Result := 0x80 <= w and w <= 0x7FF
|
||||||
|
else
|
||||||
|
Result := False
|
||||||
|
end
|
||||||
|
else
|
||||||
|
l_is_incomplete_stream := True
|
||||||
|
end
|
||||||
|
elseif (c & 0xF0) = 0xE0 then
|
||||||
|
-- 1110xxxx 10xxxxxx 10xxxxxx
|
||||||
|
i := i + 2
|
||||||
|
if i <= n then
|
||||||
|
c2 := s.code (i - 1)
|
||||||
|
c3 := s.code (i)
|
||||||
|
if
|
||||||
|
(c2 & 0xC0) = 0x80 and
|
||||||
|
(c3 & 0xC0) = 0x80
|
||||||
|
then
|
||||||
|
w := ((c & 0xF) |<< 12)
|
||||||
|
| ((c2 & 0x3F) |<< 6)
|
||||||
|
| (c3 & 0x3F)
|
||||||
|
if 0x800 <= w and w <= 0xFFFF then
|
||||||
|
if 0xD800 <= w and w <= 0xDFFF then
|
||||||
|
-- The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF
|
||||||
|
Result := False
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result := False
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result := False
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if i - 1 <= n then
|
||||||
|
Result := (s.code (i - 1) & 0xC0) = 0x80
|
||||||
|
end
|
||||||
|
l_is_incomplete_stream := True
|
||||||
|
end
|
||||||
|
elseif (c & 0xF8) = 0xF0 then -- 0001 0000-0010 FFFF
|
||||||
|
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||||
|
if 0xF5 <= c and c <= 0xFF then
|
||||||
|
-- The octet values C0, C1, F5 to FF never appear.
|
||||||
|
Result := False
|
||||||
|
else
|
||||||
|
i := i + 3
|
||||||
|
if i <= n then
|
||||||
|
c2 := s.code (i - 2)
|
||||||
|
c3 := s.code (i - 1)
|
||||||
|
c4 := s.code (i)
|
||||||
|
if
|
||||||
|
(c2 & 0xC0) = 0x80 and
|
||||||
|
(c3 & 0xC0) = 0x80 and
|
||||||
|
(c4 & 0xC0) = 0x80
|
||||||
|
then
|
||||||
|
w := ((c & 0x7) |<< 18) |
|
||||||
|
((c2 & 0x3F) |<< 12) |
|
||||||
|
((c3 & 0x3F) |<< 6) |
|
||||||
|
(c4 & 0x3F)
|
||||||
|
Result := 0x1_0000 <= w and w <= 0x10_FFFF
|
||||||
|
else
|
||||||
|
Result := False
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if i - 2 <= n then
|
||||||
|
c2 := s.code (i - 2)
|
||||||
|
Result := (c2 & 0xC0) = 0x80
|
||||||
|
if Result then
|
||||||
|
if c = 0xF4 and c2 >= 0x90 then
|
||||||
|
--| any byte 10xxxxxx (i.e >= 0x80) that would come after,
|
||||||
|
-- will result in out of range code point
|
||||||
|
-- indeed 0xF4 0x90 0x80 0x80 = 0x1100 0000 > 0x10_FFFF
|
||||||
|
Result := False
|
||||||
|
elseif i - 1 <= n then
|
||||||
|
Result := (s.code (i - 1) & 0xC0) = 0x80
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
l_is_incomplete_stream := True
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Invalid byte in UTF-8
|
||||||
|
Result := False
|
||||||
|
end
|
||||||
|
if Result then
|
||||||
|
if l_is_incomplete_stream then
|
||||||
|
Result := a_is_stream
|
||||||
|
elseif a_is_stream then
|
||||||
|
last_utf_8_stream_validation_position := i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,805 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Object representing the websocket connection.
|
||||||
|
It contains the `request` and `response`, and more important the `socket` itself.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WGI_STANDALONE_CONNECTOR_EXPORTER
|
||||||
|
|
||||||
|
WSF_RESPONSE_EXPORTER
|
||||||
|
|
||||||
|
WGI_EXPORTER
|
||||||
|
|
||||||
|
HTTPD_LOGGER_CONSTANTS
|
||||||
|
|
||||||
|
WEB_SOCKET_CONSTANTS
|
||||||
|
|
||||||
|
SHARED_BASE64
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
request := req
|
||||||
|
response := res
|
||||||
|
is_verbose := False
|
||||||
|
verbose_level := notice_level
|
||||||
|
|
||||||
|
if
|
||||||
|
attached {WGI_STANDALONE_INPUT_STREAM} req.input as r_input
|
||||||
|
then
|
||||||
|
socket := r_input.source
|
||||||
|
else
|
||||||
|
create socket.make_empty
|
||||||
|
check has_socket: False end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
socket: HTTPD_STREAM_SOCKET
|
||||||
|
-- Underlying connected socket.
|
||||||
|
|
||||||
|
feature {NONE} -- Access
|
||||||
|
|
||||||
|
request: WSF_REQUEST
|
||||||
|
-- Associated request.
|
||||||
|
|
||||||
|
response: WSF_RESPONSE
|
||||||
|
-- Associated response stream.
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
is_websocket: BOOLEAN
|
||||||
|
-- Does `open_ws_handshake' detect valid websocket upgrade handshake?
|
||||||
|
|
||||||
|
feature -- Settings
|
||||||
|
|
||||||
|
is_verbose: BOOLEAN
|
||||||
|
-- Output verbose log messages?
|
||||||
|
|
||||||
|
verbose_level: INTEGER
|
||||||
|
-- Level of verbosity.
|
||||||
|
|
||||||
|
feature -- Status
|
||||||
|
|
||||||
|
has_error: BOOLEAN
|
||||||
|
-- Error occured during processing?
|
||||||
|
|
||||||
|
feature -- Socket status
|
||||||
|
|
||||||
|
is_ready_for_reading: BOOLEAN
|
||||||
|
-- Is `socket' ready for reading?
|
||||||
|
--| at this point, socket should be set to blocking.
|
||||||
|
do
|
||||||
|
Result := socket.ready_for_reading
|
||||||
|
end
|
||||||
|
|
||||||
|
is_open_read: BOOLEAN
|
||||||
|
-- Is `socket' open for reading?
|
||||||
|
do
|
||||||
|
Result := socket.is_open_read
|
||||||
|
end
|
||||||
|
|
||||||
|
is_open_write: BOOLEAN
|
||||||
|
-- Is `socket' open for writing?
|
||||||
|
do
|
||||||
|
Result := socket.is_open_write
|
||||||
|
end
|
||||||
|
|
||||||
|
socket_descriptor: INTEGER
|
||||||
|
-- Descriptor for current `socket'.
|
||||||
|
do
|
||||||
|
Result := socket.descriptor
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_is_verbose (b: BOOLEAN)
|
||||||
|
do
|
||||||
|
is_verbose := b
|
||||||
|
end
|
||||||
|
|
||||||
|
set_verbose_level (lev: INTEGER)
|
||||||
|
do
|
||||||
|
verbose_level := lev
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Basic operation
|
||||||
|
|
||||||
|
put_error (a_message: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
response.put_error (a_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
log (m: READABLE_STRING_8; lev: INTEGER)
|
||||||
|
-- Log `m' in the error channel, i.e stderr for standalone.
|
||||||
|
do
|
||||||
|
if is_verbose then
|
||||||
|
put_error (m)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Basic Operation
|
||||||
|
|
||||||
|
open_ws_handshake
|
||||||
|
-- The opening handshake is intended to be compatible with HTTP-based
|
||||||
|
-- server-side software and intermediaries, so that a single port can be
|
||||||
|
-- used by both HTTP clients alking to that server and WebSocket
|
||||||
|
-- clients talking to that server. To this end, the WebSocket client's
|
||||||
|
-- handshake is an HTTP Upgrade request:
|
||||||
|
|
||||||
|
-- GET /chat HTTP/1.1
|
||||||
|
-- Host: server.example.com
|
||||||
|
-- Upgrade: websocket
|
||||||
|
-- Connection: Upgrade
|
||||||
|
-- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||||||
|
-- Origin: http://example.com
|
||||||
|
-- Sec-WebSocket-Protocol: chat, superchat
|
||||||
|
-- Sec-WebSocket-Version: 13
|
||||||
|
local
|
||||||
|
l_sha1: SHA1
|
||||||
|
l_key : STRING
|
||||||
|
req: like request
|
||||||
|
res: like response
|
||||||
|
do
|
||||||
|
-- Reset values.
|
||||||
|
is_websocket := False
|
||||||
|
has_error := False
|
||||||
|
|
||||||
|
-- Local cache.
|
||||||
|
req := request
|
||||||
|
res := response
|
||||||
|
|
||||||
|
-- Reading client's opening GT
|
||||||
|
|
||||||
|
-- TODO extract to a validator handshake or something like that.
|
||||||
|
if is_verbose then
|
||||||
|
log ("%NReceive <====================", debug_level)
|
||||||
|
if attached req.raw_header_data as rhd then
|
||||||
|
log (rhd, debug_level)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if
|
||||||
|
req.is_get_request_method and then -- MUST be GET request!
|
||||||
|
attached req.meta_string_variable ("HTTP_UPGRADE") as l_upgrade_key and then
|
||||||
|
l_upgrade_key.is_case_insensitive_equal_general ("websocket") -- Upgrade header must be present with value websocket
|
||||||
|
then
|
||||||
|
is_websocket := True
|
||||||
|
socket.set_blocking
|
||||||
|
if
|
||||||
|
attached req.meta_string_variable ("HTTP_SEC_WEBSOCKET_KEY") as l_ws_key and then -- Sec-websocket-key must be present
|
||||||
|
attached req.meta_string_variable ("HTTP_CONNECTION") as l_connection_key and then -- Connection header must be present with value Upgrade
|
||||||
|
l_connection_key.has_substring ("Upgrade") and then
|
||||||
|
attached req.meta_string_variable ("HTTP_SEC_WEBSOCKET_VERSION") as l_version_key and then -- Version header must be present with value 13
|
||||||
|
l_version_key.is_case_insensitive_equal ("13") and then
|
||||||
|
attached req.http_host -- Host header must be present
|
||||||
|
then
|
||||||
|
if is_verbose then
|
||||||
|
log ("key " + l_ws_key, debug_level)
|
||||||
|
end
|
||||||
|
-- Sending the server's opening handshake
|
||||||
|
create l_sha1.make
|
||||||
|
l_sha1.update_from_string (l_ws_key + magic_guid)
|
||||||
|
l_key := Base64_encoder.encoded_string (digest (l_sha1))
|
||||||
|
res.header.add_header_key_value ("Upgrade", "websocket")
|
||||||
|
res.header.add_header_key_value ("Connection", "Upgrade")
|
||||||
|
res.header.add_header_key_value ("Sec-WebSocket-Accept", l_key)
|
||||||
|
|
||||||
|
if is_verbose then
|
||||||
|
log ("%N================> Send Handshake", debug_level)
|
||||||
|
if attached {HTTP_HEADER} res.header as h then
|
||||||
|
log (h.string, debug_level)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
res.set_status_code_with_reason_phrase (101, "Switching Protocols")
|
||||||
|
res.wgi_response.push
|
||||||
|
else
|
||||||
|
has_error := True
|
||||||
|
if is_verbose then
|
||||||
|
log ("Error (opening_handshake)!!!", debug_level)
|
||||||
|
end
|
||||||
|
-- If we cannot complete the handshake, then the server MUST stop processing the client's handshake and return an HTTP response with an
|
||||||
|
-- appropriate error code (such as 400 Bad Request).
|
||||||
|
res.set_status_code_with_reason_phrase (400, "Bad Request")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
is_websocket := False
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Response!
|
||||||
|
|
||||||
|
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
|
||||||
|
do
|
||||||
|
debug ("ws")
|
||||||
|
print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N")
|
||||||
|
end
|
||||||
|
if not retried then
|
||||||
|
create l_header_message.make_empty
|
||||||
|
l_header_message.append_code ((0x80 | a_opcode).to_natural_32)
|
||||||
|
l_message_count := a_message.count
|
||||||
|
n := l_message_count.to_natural_64
|
||||||
|
if l_message_count > 0xffff then
|
||||||
|
--! Improve. this code needs to be checked.
|
||||||
|
l_header_message.append_code ((0 | 127).to_natural_32)
|
||||||
|
l_header_message.append_character ((n |>> 56).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 48).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 40).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 32).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 24).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 16).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 8).to_character_8)
|
||||||
|
l_header_message.append_character ( n.to_character_8)
|
||||||
|
elseif l_message_count > 125 then
|
||||||
|
l_header_message.append_code ((0 | 126).to_natural_32)
|
||||||
|
l_header_message.append_code ((n |>> 8).as_natural_32)
|
||||||
|
l_header_message.append_character (n.to_character_8)
|
||||||
|
else
|
||||||
|
l_header_message.append_code (n.as_natural_32)
|
||||||
|
end
|
||||||
|
socket.put_string (l_header_message)
|
||||||
|
|
||||||
|
l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable.
|
||||||
|
if l_message_count < l_chunk_size then
|
||||||
|
socket.put_string (a_message)
|
||||||
|
else
|
||||||
|
from
|
||||||
|
i := 0
|
||||||
|
until
|
||||||
|
l_chunk_size = 0
|
||||||
|
loop
|
||||||
|
debug ("ws")
|
||||||
|
print ("Sending chunk " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
|
||||||
|
end
|
||||||
|
l_chunk := a_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
|
||||||
|
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
|
||||||
|
|
||||||
|
next_frame: 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
|
||||||
|
socket_in_blocking_mode: socket.is_blocking
|
||||||
|
local
|
||||||
|
l_socket: like socket
|
||||||
|
l_opcode: INTEGER
|
||||||
|
l_len: INTEGER
|
||||||
|
l_remaining_len: INTEGER
|
||||||
|
l_payload_len: NATURAL_64
|
||||||
|
l_masking_key: detachable READABLE_STRING_8
|
||||||
|
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
|
||||||
|
l_socket := socket
|
||||||
|
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 (l_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 + ")", debug_level)
|
||||||
|
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 + ")", debug_level)
|
||||||
|
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 (l_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 (l_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 (l_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
|
||||||
|
l_masking_key := next_bytes (l_socket, 4) -- 32 bits
|
||||||
|
debug ("ws")
|
||||||
|
print (" Masking key bits=" + string_to_byte_representation (l_masking_key))
|
||||||
|
io.put_new_line
|
||||||
|
end
|
||||||
|
if l_masking_key.count < 4 then
|
||||||
|
debug ("ws")
|
||||||
|
print ("masking-key read stream -> " + l_socket.bytes_read.out + " bits%N")
|
||||||
|
end
|
||||||
|
Result.report_error (Invalid_data, "Incomplete data for Masking-key")
|
||||||
|
l_masking_key := Void
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result.report_error (protocol_error, "All frames sent from client to server are masked!")
|
||||||
|
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
|
||||||
|
l_socket.read_stream (l_chunk_size)
|
||||||
|
l_bytes_read := l_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 l_bytes_read > 0 then
|
||||||
|
l_remaining_len := l_remaining_len - l_bytes_read
|
||||||
|
l_chunk := l_socket.last_string
|
||||||
|
if l_masking_key /= Void then
|
||||||
|
-- Masking
|
||||||
|
-- http://tools.ietf.org/html/rfc6455#section-5.3
|
||||||
|
unmask (l_chunk, l_fetch_count + 1, l_masking_key)
|
||||||
|
else
|
||||||
|
check
|
||||||
|
client_frame_should_always_be_encoded: False
|
||||||
|
end
|
||||||
|
end
|
||||||
|
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 <===============", debug_level)
|
||||||
|
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, debug_level)
|
||||||
|
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
|
||||||
|
has_error := Result = Void or else Result.has_error
|
||||||
|
rescue
|
||||||
|
retried := True
|
||||||
|
if Result /= Void then
|
||||||
|
Result.report_error (internal_error, "Internal error")
|
||||||
|
end
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
feature -- Encoding
|
||||||
|
|
||||||
|
digest (a_sha1: SHA1): STRING
|
||||||
|
-- Digest of `a_sha1'.
|
||||||
|
-- Should by in SHA1 class
|
||||||
|
local
|
||||||
|
l_digest: SPECIAL [NATURAL_8]
|
||||||
|
index, l_upper: INTEGER
|
||||||
|
do
|
||||||
|
l_digest := a_sha1.digest
|
||||||
|
create Result.make (l_digest.count // 2)
|
||||||
|
from
|
||||||
|
index := l_digest.Lower
|
||||||
|
l_upper := l_digest.upper
|
||||||
|
until
|
||||||
|
index > l_upper
|
||||||
|
loop
|
||||||
|
Result.append_character (l_digest [index].to_character_8)
|
||||||
|
index := index + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Socket helpers
|
||||||
|
|
||||||
|
next_bytes (a_socket: HTTPD_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
|
||||||
|
|
||||||
|
feature -- Masking Data Client - Server
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
To implement websocket handling, provide a `callbacks` object implementing the {WEB_SOCKET_EVENT_I} interface.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WEB_SOCKET_CONSTANTS
|
||||||
|
|
||||||
|
REFACTORING_HELPER
|
||||||
|
|
||||||
|
HTTPD_LOGGER_CONSTANTS
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (ws: WEB_SOCKET; a_callbacks: WEB_SOCKET_EVENT_I)
|
||||||
|
do
|
||||||
|
web_socket := ws
|
||||||
|
callbacks := a_callbacks
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
web_socket: WEB_SOCKET
|
||||||
|
-- Associated websocket.
|
||||||
|
|
||||||
|
callbacks: WEB_SOCKET_EVENT_I
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
frozen execute
|
||||||
|
do
|
||||||
|
callbacks.on_open (web_socket)
|
||||||
|
execute_websocket
|
||||||
|
end
|
||||||
|
|
||||||
|
execute_websocket
|
||||||
|
local
|
||||||
|
exit: BOOLEAN
|
||||||
|
l_frame: detachable WEB_SOCKET_FRAME
|
||||||
|
l_client_message: detachable READABLE_STRING_8
|
||||||
|
l_utf: UTF_CONVERTER
|
||||||
|
ws: like web_socket
|
||||||
|
s: STRING
|
||||||
|
do
|
||||||
|
from
|
||||||
|
-- loop until ws is closed or has error.
|
||||||
|
ws := web_socket
|
||||||
|
until
|
||||||
|
exit
|
||||||
|
loop
|
||||||
|
debug ("dbglog")
|
||||||
|
dbglog (generator + ".execute_websocket (loop) WS_REQUEST_HANDLER.process_request {" + ws.socket_descriptor.out + "}")
|
||||||
|
end
|
||||||
|
if ws.is_ready_for_reading then
|
||||||
|
l_frame := ws.next_frame
|
||||||
|
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.
|
||||||
|
callbacks.on_event (ws, ic.item.payload_data, ic.item.opcode)
|
||||||
|
exit := True
|
||||||
|
elseif ic.item.is_ping then
|
||||||
|
-- FIXME reply only to the most recent ping ...
|
||||||
|
callbacks.on_event (ws, ic.item.payload_data, ic.item.opcode)
|
||||||
|
else
|
||||||
|
callbacks.on_event (ws, ic.item.payload_data, ic.item.opcode)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
l_client_message := l_frame.payload_data
|
||||||
|
if l_client_message = Void then
|
||||||
|
l_client_message := ""
|
||||||
|
end
|
||||||
|
|
||||||
|
debug ("ws")
|
||||||
|
create s.make_from_string ("%NExecute: %N")
|
||||||
|
s.append (" [opcode: "+ opcode_name (l_frame.opcode) +"]%N")
|
||||||
|
if l_frame.is_text then
|
||||||
|
s.append (" [client message: %""+ l_client_message +"%"]%N")
|
||||||
|
elseif l_frame.is_binary then
|
||||||
|
s.append (" [client binary message length: %""+ l_client_message.count.out +"%"]%N")
|
||||||
|
end
|
||||||
|
s.append (" [is_control: " + l_frame.is_control.out + "]%N")
|
||||||
|
s.append (" [is_binary: " + l_frame.is_binary.out + "]%N")
|
||||||
|
s.append (" [is_text: " + l_frame.is_text.out + "]%N")
|
||||||
|
dbglog (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
if l_frame.is_connection_close then
|
||||||
|
callbacks.on_event (ws, l_client_message, l_frame.opcode)
|
||||||
|
exit := True
|
||||||
|
elseif l_frame.is_binary then
|
||||||
|
callbacks.on_event (ws, l_client_message, l_frame.opcode)
|
||||||
|
elseif l_frame.is_text then
|
||||||
|
check is_valid_utf_8: l_utf.is_valid_utf_8_string_8 (l_client_message) end
|
||||||
|
callbacks.on_event (ws, l_client_message, l_frame.opcode)
|
||||||
|
else
|
||||||
|
callbacks.on_event (ws, l_client_message, l_frame.opcode)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
debug ("ws")
|
||||||
|
create s.make_from_string ("%NExecute: %N")
|
||||||
|
s.append (" [ERROR: invalid frame]%N")
|
||||||
|
if l_frame /= Void and then attached l_frame.error as err then
|
||||||
|
s.append (" [Code: "+ err.code.out +"]%N")
|
||||||
|
s.append (" [Description: "+ err.description +"]%N")
|
||||||
|
end
|
||||||
|
dbglog (s)
|
||||||
|
end
|
||||||
|
callbacks.on_event (ws, "", connection_close_frame)
|
||||||
|
exit := True -- FIXME: check proper close protocol
|
||||||
|
end
|
||||||
|
else
|
||||||
|
debug ("ws")
|
||||||
|
dbglog (generator + ".WAITING WS_REQUEST_HANDLER.process_request {" + ws.socket_descriptor.out + "}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Logging
|
||||||
|
|
||||||
|
dbglog (m: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
web_socket.log (m, debug_level)
|
||||||
|
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
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WGI_STANDALONE_WEBSOCKET_CONNECTOR}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WGI_STANDALONE_WEBSOCKET_CONNECTOR [G -> WGI_EXECUTION create make end]
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WGI_STANDALONE_CONNECTOR [G]
|
||||||
|
redefine
|
||||||
|
name, version
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make,
|
||||||
|
make_with_base
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
name: STRING_8
|
||||||
|
-- Name of Current connector
|
||||||
|
once
|
||||||
|
Result := "ws_httpd"
|
||||||
|
end
|
||||||
|
|
||||||
|
version: STRING_8
|
||||||
|
-- Version of Current connector
|
||||||
|
once
|
||||||
|
Result := "1.0"
|
||||||
|
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
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Component to launch the service using the default connector
|
||||||
|
|
||||||
|
Eiffel Web httpd for this class
|
||||||
|
|
||||||
|
|
||||||
|
The httpd default connector support options:
|
||||||
|
port: numeric such as 8099 (or equivalent string as "8099")
|
||||||
|
base: base_url (very specific to standalone server)
|
||||||
|
verbose: to display verbose output, useful for standalone connector
|
||||||
|
force_single_threaded: use only one thread, useful for standalone connector
|
||||||
|
|
||||||
|
check WSF_SERVICE_LAUNCHER for more documentation
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_STANDALONE_WEBSOCKET_SERVICE_LAUNCHER [G -> WSF_WEBSOCKET_EXECUTION create make end]
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_STANDALONE_SERVICE_LAUNCHER [G]
|
||||||
|
redefine
|
||||||
|
connector
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make,
|
||||||
|
make_and_launch
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
connector: WGI_STANDALONE_WEBSOCKET_CONNECTOR [G]
|
||||||
|
-- Default connector
|
||||||
|
|
||||||
|
;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
|
||||||
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Request execution based on attributes `request' and `response'.
|
||||||
|
Also support Upgrade to Websocket protocol.
|
||||||
|
|
||||||
|
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WSF_WEBSOCKET_EXECUTION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_EXECUTION
|
||||||
|
rename
|
||||||
|
execute as http_execute
|
||||||
|
end
|
||||||
|
|
||||||
|
--create
|
||||||
|
-- make
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
frozen http_execute
|
||||||
|
local
|
||||||
|
ws: WEB_SOCKET
|
||||||
|
ws_h: like new_websocket_handler
|
||||||
|
do
|
||||||
|
create ws.make (request, response)
|
||||||
|
ws.open_ws_handshake
|
||||||
|
if ws.is_websocket then
|
||||||
|
if ws.has_error then
|
||||||
|
-- Upgrade to websocket raised an error
|
||||||
|
-- stay on standard HTTP/1.1 protocol
|
||||||
|
execute
|
||||||
|
else
|
||||||
|
ws_h := new_websocket_handler (ws)
|
||||||
|
ws_h.execute
|
||||||
|
end
|
||||||
|
else
|
||||||
|
execute
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
execute
|
||||||
|
-- Execute Current request,
|
||||||
|
-- getting data from `request'
|
||||||
|
-- and response to client via `response'.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
|
||||||
|
-- Websocket request specific handler on socket `ws'.
|
||||||
|
--| For the creation, it requires an instance of `{WEB_SOCKET_EVENT_I}'
|
||||||
|
--| to receive the websocket events.
|
||||||
|
--| One can inherit from {WEB_SOCKET_EVENT_I} and implement the related
|
||||||
|
--| deferred features.
|
||||||
|
--| Or even provide a new class implementing {WEB_SOCKET_EVENT_I}.
|
||||||
|
require
|
||||||
|
is_websocket: ws.is_websocket
|
||||||
|
no_error: not ws.has_error
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -1,13 +1,3 @@
|
|||||||
note
|
|
||||||
title: Web Server Foundation
|
|
||||||
description: Core of the Eiffel Web Framework, used to build web server application.
|
|
||||||
tags: ewf,server,httpd,request,connector
|
|
||||||
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
|
|
||||||
link[license]: https://github.com/EiffelWebFramework/EWF/blob/master/LICENSE
|
|
||||||
link[source]: "Github" https://github.com/EiffelWebFramework/EWF
|
|
||||||
link[doc]: "Documentation" http://eiffelwebframework.github.io/EWF/
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
package wsf
|
package wsf
|
||||||
|
|
||||||
@@ -37,6 +27,7 @@ project
|
|||||||
default_standalone = "default/standalone-safe.ecf"
|
default_standalone = "default/standalone-safe.ecf"
|
||||||
default_standalone = "default/standalone.ecf"
|
default_standalone = "default/standalone.ecf"
|
||||||
|
|
||||||
|
|
||||||
note
|
note
|
||||||
title: Web Server Foundation
|
title: Web Server Foundation
|
||||||
description: "[
|
description: "[
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ note
|
|||||||
For instance, you can use
|
For instance, you can use
|
||||||
create s.make_and_launch_and_options (agent execute, <<["port", 8099]>>)
|
create s.make_and_launch_and_options (agent execute, <<["port", 8099]>>)
|
||||||
|
|
||||||
And if Standalone (the EiffelWeb server) is the default connector it will support:
|
And if the connector is the Standalone connector,
|
||||||
|
check {WSF_STANDALONE_SERVICE_LAUNCHER} for options description, such as:
|
||||||
port: numeric such as 8099 (or equivalent string as "8099")
|
port: numeric such as 8099 (or equivalent string as "8099")
|
||||||
base: base_url (very specific to standalone server)
|
base: base_url (very specific to standalone server)
|
||||||
max_concurrent_connections: by default 100
|
verbose: to display verbose output.
|
||||||
verbose: to display verbose output, useful for Standalone connector.
|
|
||||||
]"
|
]"
|
||||||
date: "$Date$"
|
date: "$Date$"
|
||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ note
|
|||||||
force_single_threaded: use only one thread, useful for Nino
|
force_single_threaded: use only one thread, useful for Nino
|
||||||
verbose: to display verbose output, useful for Nino
|
verbose: to display verbose output, useful for Nino
|
||||||
]"
|
]"
|
||||||
date: "$Date$"
|
date: "$Date: 2016-08-06 13:34:52 +0200 (sam., 06 août 2016) $"
|
||||||
revision: "$Revision$"
|
revision: "$Revision: 99106 $"
|
||||||
|
|
||||||
class
|
class
|
||||||
WSF_SERVICE_LAUNCHER_OPTIONS
|
WSF_SERVICE_LAUNCHER_OPTIONS
|
||||||
@@ -85,6 +85,50 @@ feature -- Access
|
|||||||
|
|
||||||
feature -- Helpers
|
feature -- Helpers
|
||||||
|
|
||||||
|
has_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
|
||||||
|
-- Is there any value associated to option name `a_opt_name'?
|
||||||
|
do
|
||||||
|
Result := attached option (a_opt_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
has_integer_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
|
||||||
|
-- Is there any INTEGER value associated to option name `a_opt_name'?
|
||||||
|
local
|
||||||
|
s: READABLE_STRING_GENERAL
|
||||||
|
do
|
||||||
|
if attached option (a_opt_name) as opt then
|
||||||
|
if attached {INTEGER} opt as i then
|
||||||
|
Result := True
|
||||||
|
else
|
||||||
|
s := opt.out
|
||||||
|
Result := s.is_integer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
has_string_32_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
|
||||||
|
-- Is there any string 32 value associated to option name `a_opt_name'?
|
||||||
|
do
|
||||||
|
if attached option (a_opt_name) as opt then
|
||||||
|
Result := attached {READABLE_STRING_GENERAL} opt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
option_string_32_value (a_opt_name: READABLE_STRING_GENERAL; a_default: detachable READABLE_STRING_GENERAL): detachable IMMUTABLE_STRING_32
|
||||||
|
-- Unicode String value associated to option name `a_opt_name', other return `a_default'.
|
||||||
|
do
|
||||||
|
if attached option (a_opt_name) as opt then
|
||||||
|
if attached {READABLE_STRING_32} opt as s32 then
|
||||||
|
create Result.make_from_string (s32)
|
||||||
|
elseif attached {READABLE_STRING_GENERAL} opt as s then
|
||||||
|
create Result.make_from_string_general (s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if Result = Void and a_default /= Void then
|
||||||
|
create Result.make_from_string_general (a_default)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
option_integer_value (a_opt_name: READABLE_STRING_GENERAL; a_default: INTEGER): INTEGER
|
option_integer_value (a_opt_name: READABLE_STRING_GENERAL; a_default: INTEGER): INTEGER
|
||||||
-- INTEGER value associated to option name `a_opt_name', other return `a_default'.
|
-- INTEGER value associated to option name `a_opt_name', other return `a_default'.
|
||||||
local
|
local
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ inherit
|
|||||||
make_from_execution as make_from_wgi_execution
|
make_from_execution as make_from_wgi_execution
|
||||||
redefine
|
redefine
|
||||||
make,
|
make,
|
||||||
execute,
|
|
||||||
clean,
|
clean,
|
||||||
is_valid_end_of_execution
|
is_valid_end_of_execution
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -261,10 +261,12 @@ feature -- Access: Input
|
|||||||
local
|
local
|
||||||
l_input: WGI_INPUT_STREAM
|
l_input: WGI_INPUT_STREAM
|
||||||
n: INTEGER
|
n: INTEGER
|
||||||
|
buf_initial_size: INTEGER
|
||||||
do
|
do
|
||||||
if raw_input_data_recorded and then attached raw_input_data as d then
|
if raw_input_data_recorded and then attached raw_input_data as d then
|
||||||
buf.append (d)
|
buf.append (d)
|
||||||
else
|
else
|
||||||
|
buf_initial_size := buf.count
|
||||||
l_input := input
|
l_input := input
|
||||||
if is_chunked_input then
|
if is_chunked_input then
|
||||||
from
|
from
|
||||||
@@ -286,73 +288,53 @@ feature -- Access: Input
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if raw_input_data_recorded then
|
if raw_input_data_recorded then
|
||||||
set_raw_input_data (buf)
|
set_raw_input_data (buf.substring (buf_initial_size + 1, buf.count))
|
||||||
|
-- Only the input data! And differente reference.
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
read_input_data_into_file (a_file: FILE)
|
read_input_data_into_file (a_medium: IO_MEDIUM)
|
||||||
-- retrieve the content from the `input' stream into `s'
|
-- retrieve the content from the `input' stream into `s'
|
||||||
-- warning: if the input data has already been retrieved
|
-- warning: if the input data has already been retrieved
|
||||||
-- you might not get anything
|
-- you might not get anything
|
||||||
require
|
require
|
||||||
a_file_is_open_write: a_file.is_open_write
|
a_medium_is_open_write: a_medium.is_open_write
|
||||||
local
|
local
|
||||||
s: STRING
|
s: STRING
|
||||||
l_input: WGI_INPUT_STREAM
|
l_input: WGI_INPUT_STREAM
|
||||||
l_raw_data: detachable STRING_8
|
l_raw_data: detachable STRING_8
|
||||||
len: NATURAL_64
|
|
||||||
nb, l_step: INTEGER
|
nb, l_step: INTEGER
|
||||||
l_size: NATURAL_64
|
|
||||||
do
|
do
|
||||||
if raw_input_data_recorded and then attached raw_input_data as d then
|
if raw_input_data_recorded and then attached raw_input_data as d then
|
||||||
a_file.put_string (d)
|
a_medium.put_string (d)
|
||||||
else
|
else
|
||||||
if raw_input_data_recorded then
|
if raw_input_data_recorded then
|
||||||
create l_raw_data.make_empty
|
create l_raw_data.make_empty
|
||||||
end
|
end
|
||||||
l_input := input
|
l_input := input
|
||||||
len := content_length_value
|
|
||||||
|
|
||||||
debug ("wsf")
|
|
||||||
io.error.put_string (generator + ".read_input_data_into_file (a_file) content_length=" + len.out + "%N")
|
|
||||||
end
|
|
||||||
|
|
||||||
from
|
from
|
||||||
l_size := 0
|
|
||||||
l_step := 8_192
|
l_step := 8_192
|
||||||
create s.make (l_step)
|
create s.make (l_step)
|
||||||
until
|
until
|
||||||
l_step = 0 or l_input.end_of_input
|
l_step = 0 or l_input.end_of_input
|
||||||
loop
|
loop
|
||||||
if len < l_step.to_natural_64 then
|
l_input.append_to_string (s, l_step)
|
||||||
l_step := len.to_integer_32
|
nb := l_input.last_appended_count
|
||||||
|
|
||||||
|
a_medium.put_string (s)
|
||||||
|
if l_raw_data /= Void then
|
||||||
|
l_raw_data.append (s)
|
||||||
end
|
end
|
||||||
if l_step > 0 then
|
s.wipe_out
|
||||||
l_input.append_to_string (s, l_step)
|
if nb < l_step then
|
||||||
nb := l_input.last_appended_count
|
l_step := 0
|
||||||
l_size := l_size + nb.to_natural_64
|
|
||||||
len := len - nb.to_natural_64
|
|
||||||
|
|
||||||
debug ("wsf")
|
|
||||||
io.error.put_string (" append (s, " + l_step.out + ") -> " + nb.out + " (" + l_size.out + " / "+ content_length_value.out + ")%N")
|
|
||||||
end
|
|
||||||
|
|
||||||
a_file.put_string (s)
|
|
||||||
if l_raw_data /= Void then
|
|
||||||
l_raw_data.append (s)
|
|
||||||
end
|
|
||||||
s.wipe_out
|
|
||||||
if nb < l_step then
|
|
||||||
l_step := 0
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
a_file.flush
|
if attached {FILE} a_medium as f then
|
||||||
debug ("wsf")
|
f.flush
|
||||||
io.error.put_string ("offset =" + len.out + "%N")
|
|
||||||
end
|
end
|
||||||
check got_all_data: len = 0 end
|
|
||||||
if l_raw_data /= Void then
|
if l_raw_data /= Void then
|
||||||
set_raw_input_data (l_raw_data)
|
set_raw_input_data (l_raw_data)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package wsf_html
|
|
||||||
|
|
||||||
project
|
|
||||||
wsf_html = "wsf_html-safe.ecf"
|
|
||||||
wsf_html = "wsf_html.ecf"
|
|
||||||
|
|
||||||
note
|
|
||||||
title: WSF Html widget
|
|
||||||
description: "[
|
|
||||||
HTML widget, such as table, web form, and more based on the WSF interfaces.
|
|
||||||
]"
|
|
||||||
tags: ewf,server,html,form,widget
|
|
||||||
copyright: 2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others
|
|
||||||
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
|
|
||||||
link[license]: http://www.eiffel.com/licensing/forum.txt
|
|
||||||
link[source]: "Github" https://github.com/EiffelWebFramework/EWF/library/server/wsf_html
|
|
||||||
link[doc]: "Documentation" http://eiffelwebframework.github.io/EWF/
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_PROXY_SOCKET_FACTORY}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_PROXY_SOCKET_FACTORY
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_PROXY_SOCKET_FACTORY_I
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
ssl_socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable NETWORK_STREAM_SOCKET
|
||||||
|
do
|
||||||
|
check supported: False end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_PROXY_SOCKET_FACTORY}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_PROXY_SOCKET_FACTORY
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_PROXY_SOCKET_FACTORY_I
|
||||||
|
redefine
|
||||||
|
is_ssl_supported
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
ssl_socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable SSL_NETWORK_STREAM_SOCKET
|
||||||
|
do
|
||||||
|
if attached create_from_name (a_host) as l_peer_address then
|
||||||
|
create Result.make_client_by_address_and_port (l_peer_address, a_port)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Status
|
||||||
|
|
||||||
|
is_ssl_supported: BOOLEAN = True
|
||||||
|
-- Is https:// supported?
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_PROXY_SOCKET_FACTORY_I}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WSF_PROXY_SOCKET_FACTORY_I
|
||||||
|
|
||||||
|
inherit
|
||||||
|
INET_ADDRESS_FACTORY
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
socket_from_uri (a_uri: URI): like socket
|
||||||
|
local
|
||||||
|
l_port: INTEGER
|
||||||
|
do
|
||||||
|
if a_uri.is_valid and then attached a_uri.host as l_host then
|
||||||
|
l_port := a_uri.port
|
||||||
|
if a_uri.scheme.is_case_insensitive_equal_general ("https") then
|
||||||
|
if is_ssl_supported then
|
||||||
|
if l_port <= 0 then
|
||||||
|
l_port := 443
|
||||||
|
end
|
||||||
|
Result := ssl_socket (l_host, l_port)
|
||||||
|
end
|
||||||
|
elseif a_uri.scheme.is_case_insensitive_equal_general ("http") then
|
||||||
|
if l_port <= 0 then
|
||||||
|
l_port := 80
|
||||||
|
end
|
||||||
|
Result := socket (l_host, l_port)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Status
|
||||||
|
|
||||||
|
is_uri_supported (a_uri: URI): BOOLEAN
|
||||||
|
do
|
||||||
|
Result := a_uri.scheme.is_case_insensitive_equal_general ("http")
|
||||||
|
or else (
|
||||||
|
a_uri.scheme.is_case_insensitive_equal_general ("https")
|
||||||
|
and is_ssl_supported
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
is_ssl_supported: BOOLEAN
|
||||||
|
-- Is https:// supported?
|
||||||
|
do
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable NETWORK_STREAM_SOCKET
|
||||||
|
do
|
||||||
|
if attached create_from_name (a_host) as l_peer_address then
|
||||||
|
create Result.make_client_by_address_and_port (l_peer_address, a_port)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ssl_socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable NETWORK_STREAM_SOCKET
|
||||||
|
require
|
||||||
|
is_ssl_supported: is_ssl_supported
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_SIMPLE_REVERSE_PROXY_HANDLER}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_SIMPLE_REVERSE_PROXY_HANDLER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_remote_uri: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
create remote_uri.make_from_string (a_remote_uri)
|
||||||
|
timeout := 30 -- seconds. See {NETWORK_SOCKET}.default_timeout
|
||||||
|
connect_timeout := 5_000 -- 5 seconds.
|
||||||
|
is_via_header_supported := True
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
remote_uri: URI
|
||||||
|
-- Url for the targetted service.
|
||||||
|
|
||||||
|
uri_rewriter: detachable WSF_URI_REWRITER assign set_uri_rewriter
|
||||||
|
-- URI rewriter component, to compute the URI on targetted service
|
||||||
|
-- based on current request.
|
||||||
|
|
||||||
|
feature -- Settings
|
||||||
|
|
||||||
|
connect_timeout: INTEGER assign set_connect_timeout
|
||||||
|
-- In milliseconds.
|
||||||
|
|
||||||
|
timeout: INTEGER assign set_timeout
|
||||||
|
-- In seconds.
|
||||||
|
|
||||||
|
is_via_header_supported: BOOLEAN
|
||||||
|
-- Via: header supported.
|
||||||
|
-- Default: True.
|
||||||
|
|
||||||
|
feature -- Change
|
||||||
|
|
||||||
|
set_uri_rewriter (a_rewriter: like uri_rewriter)
|
||||||
|
do
|
||||||
|
uri_rewriter := a_rewriter
|
||||||
|
end
|
||||||
|
|
||||||
|
set_timeout (a_timeout_in_seconds: INTEGER)
|
||||||
|
-- in seconds.
|
||||||
|
do
|
||||||
|
timeout := a_timeout_in_seconds
|
||||||
|
end
|
||||||
|
|
||||||
|
set_connect_timeout (a_timeout_in_milliseconds: INTEGER)
|
||||||
|
-- in milliseconds.
|
||||||
|
do
|
||||||
|
connect_timeout := a_timeout_in_milliseconds
|
||||||
|
end
|
||||||
|
|
||||||
|
set_is_via_header_supported (b: BOOLEAN)
|
||||||
|
-- Set `is_via_header_supported' to `b'.
|
||||||
|
do
|
||||||
|
is_via_header_supported := b
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
proxy_uri (request: WSF_REQUEST): STRING
|
||||||
|
-- URI to query on proxyfied host.
|
||||||
|
do
|
||||||
|
if attached uri_rewriter as r then
|
||||||
|
Result := r.uri (request)
|
||||||
|
else
|
||||||
|
Result := request.request_uri
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
execute (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||||||
|
-- Execute reverse proxy request.
|
||||||
|
local
|
||||||
|
h: HTTP_HEADER
|
||||||
|
l_http_query: STRING
|
||||||
|
l_status_line: STRING
|
||||||
|
l_max_forward: INTEGER
|
||||||
|
l_via: detachable STRING
|
||||||
|
l_protocol: STRING
|
||||||
|
i: INTEGER
|
||||||
|
l_completed: BOOLEAN
|
||||||
|
l_remote_uri: like remote_uri
|
||||||
|
l_socket_factory: WSF_PROXY_SOCKET_FACTORY
|
||||||
|
do
|
||||||
|
l_remote_uri := remote_uri
|
||||||
|
create l_socket_factory
|
||||||
|
if not l_socket_factory.is_uri_supported (l_remote_uri) then
|
||||||
|
send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, l_remote_uri.scheme + " is not supported! [for remote " + l_remote_uri.string + "]")
|
||||||
|
elseif attached l_socket_factory.socket_from_uri (l_remote_uri) as l_socket then
|
||||||
|
l_socket.set_connect_timeout (connect_timeout) -- milliseconds
|
||||||
|
l_socket.set_timeout (timeout) -- seconds
|
||||||
|
|
||||||
|
l_socket.connect
|
||||||
|
if l_socket.is_connected then
|
||||||
|
create l_http_query.make_from_string (request.request_method)
|
||||||
|
l_http_query.append_character (' ')
|
||||||
|
l_http_query.append (l_remote_uri.path)
|
||||||
|
l_http_query.append (proxy_uri (request))
|
||||||
|
l_http_query.append_character (' ')
|
||||||
|
l_http_query.append (request.server_protocol)
|
||||||
|
if attached request.raw_header_data as l_raw_header then
|
||||||
|
i := l_raw_header.substring_index ("%R%N", 1)
|
||||||
|
if i > 0 then
|
||||||
|
-- Skip the first status line.
|
||||||
|
create h.make_from_raw_header_data (l_raw_header.substring (i + 2, l_raw_header.count))
|
||||||
|
else
|
||||||
|
create h.make_from_raw_header_data (l_raw_header)
|
||||||
|
end
|
||||||
|
if attached l_remote_uri.host as l_remote_host then
|
||||||
|
if l_remote_uri.port > 0 then
|
||||||
|
h.put_header_key_value ("Host", l_remote_host + ":" + l_remote_uri.port.out)
|
||||||
|
else
|
||||||
|
h.put_header_key_value ("Host", l_remote_host)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Via header
|
||||||
|
if is_via_header_supported then
|
||||||
|
if attached h.item ("Via") as v then
|
||||||
|
l_via := v
|
||||||
|
l_via.append (", ")
|
||||||
|
else
|
||||||
|
create l_via.make_empty
|
||||||
|
end
|
||||||
|
l_via.append (request.server_protocol + " " + request.server_name + " (PROXY-" + request.server_software + ")")
|
||||||
|
h.put_header_key_value ("Via", l_via)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Max-Forwards header handling
|
||||||
|
if attached h.item ("Max-Forwards") as h_max_forward then
|
||||||
|
-- Max-Forwards: 0 stop, otherwise decrement by one.
|
||||||
|
-- see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.31
|
||||||
|
if h_max_forward.is_integer then
|
||||||
|
l_max_forward := h_max_forward.to_integer - 1
|
||||||
|
if l_max_forward >= 0 then
|
||||||
|
h.put_header_key_value ("Max-Forwards", l_max_forward.out)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if l_max_forward < 0 then
|
||||||
|
-- i.e previous Max-Forwards was '0'
|
||||||
|
send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, "Reached maximum number of Forwards, not forwarded to " + l_remote_uri.string)
|
||||||
|
else
|
||||||
|
l_socket.put_string (l_http_query)
|
||||||
|
l_socket.put_string ("%R%N")
|
||||||
|
l_socket.put_string (h.string)
|
||||||
|
l_socket.put_string ("%R%N")
|
||||||
|
if request.content_length_value > 0 then
|
||||||
|
request.read_input_data_into_file (l_socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get HTTP status
|
||||||
|
l_socket.read_line_thread_aware
|
||||||
|
create l_status_line.make_from_string (l_socket.last_string)
|
||||||
|
-- Get HTTP header block
|
||||||
|
if attached next_http_header_block (l_socket) as l_resp_header then
|
||||||
|
create h.make_from_raw_header_data (l_resp_header)
|
||||||
|
if attached status_line_info (l_status_line) as l_status_info then
|
||||||
|
l_protocol := l_status_info.protocol
|
||||||
|
if attached l_status_info.reason_phrase as l_phrase then
|
||||||
|
response.set_status_code_with_reason_phrase (l_status_info.status_code, l_phrase)
|
||||||
|
else
|
||||||
|
response.set_status_code (l_status_info.status_code)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
check has_status_line: False end
|
||||||
|
l_protocol := "1.0" -- Default?
|
||||||
|
response.set_status_code (80)
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_via_header_supported then
|
||||||
|
if attached h.item ("Via") as v then
|
||||||
|
l_via := v
|
||||||
|
l_via.append (", ")
|
||||||
|
else
|
||||||
|
create l_via.make_empty
|
||||||
|
end
|
||||||
|
l_via.append (l_protocol + " " + request.server_name + " (PROXY-" + request.server_software + ")")
|
||||||
|
h.put_header_key_value ("Via", l_via)
|
||||||
|
end
|
||||||
|
|
||||||
|
response.add_header_lines (h)
|
||||||
|
from
|
||||||
|
l_socket.read_stream (2_048)
|
||||||
|
until
|
||||||
|
l_socket.was_error
|
||||||
|
or not l_socket.is_connected
|
||||||
|
or l_socket.bytes_read <= 0
|
||||||
|
or l_completed
|
||||||
|
loop
|
||||||
|
response.put_string (l_socket.last_string)
|
||||||
|
if l_socket.bytes_read = 2_048 then
|
||||||
|
l_socket.read_stream (2_048)
|
||||||
|
else
|
||||||
|
l_completed := True
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
send_error (request, response, {HTTP_STATUS_CODE}.internal_server_error, "Invalid response header!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
send_error (request, response, {HTTP_STATUS_CODE}.internal_server_error, "Can not access request header!")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
send_error (request, response, {HTTP_STATUS_CODE}.gateway_timeout, "Unable to connect " + l_remote_uri.string)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, "Unable to connect " + l_remote_uri.string)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
status_line_info (a_line: READABLE_STRING_8): detachable TUPLE [protocol: READABLE_STRING_8; status_code: INTEGER; reason_phrase: detachable READABLE_STRING_8]
|
||||||
|
-- Info from status line
|
||||||
|
--| Such as "HTTP/1.1 200 OK" -> ["1.1", 200, "OK"]
|
||||||
|
local
|
||||||
|
i,j: INTEGER
|
||||||
|
p,s: detachable READABLE_STRING_8
|
||||||
|
c: INTEGER
|
||||||
|
do
|
||||||
|
i := a_line.index_of (' ', 1)
|
||||||
|
if i > 0 then
|
||||||
|
p := a_line.substring (1, i - 1)
|
||||||
|
if p.starts_with_general ("HTTP/") then
|
||||||
|
p := p.substring (6, p.count) -- We could also keep HTTP/
|
||||||
|
end
|
||||||
|
j := i + 1
|
||||||
|
i := a_line.index_of (' ', j)
|
||||||
|
if i > 0 then
|
||||||
|
s := a_line.substring (j, i - 1)
|
||||||
|
if s.is_integer then
|
||||||
|
c := s.to_integer
|
||||||
|
s := a_line.substring (i + 1, a_line.count)
|
||||||
|
if s.is_whitespace then
|
||||||
|
s := Void
|
||||||
|
elseif s[s.count].is_space then
|
||||||
|
s := s.substring (1, s.count - 1)
|
||||||
|
end
|
||||||
|
Result := [p, c, s]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
next_http_header_block (a_socket: NETWORK_STREAM_SOCKET): detachable STRING
|
||||||
|
local
|
||||||
|
h: STRING
|
||||||
|
do
|
||||||
|
create h.make_empty
|
||||||
|
from
|
||||||
|
a_socket.read_line_thread_aware
|
||||||
|
until
|
||||||
|
Result /= Void
|
||||||
|
or a_socket.was_error
|
||||||
|
or (a_socket.bytes_read = 0 or a_socket.bytes_read = -1)
|
||||||
|
or not a_socket.is_connected
|
||||||
|
loop
|
||||||
|
if a_socket.last_string.same_string ("%R") then
|
||||||
|
-- End of header
|
||||||
|
Result := h
|
||||||
|
else
|
||||||
|
h.append (a_socket.last_string)
|
||||||
|
h.append ("%N")
|
||||||
|
a_socket.read_line_thread_aware
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send_error (request: WSF_REQUEST; response: WSF_RESPONSE; a_status_code: INTEGER; a_message: READABLE_STRING_8)
|
||||||
|
local
|
||||||
|
s: STRING
|
||||||
|
do
|
||||||
|
-- To send a response we need to setup, the status code and
|
||||||
|
-- the response headers.
|
||||||
|
create s.make_from_string (a_message)
|
||||||
|
debug
|
||||||
|
s.append ("%N(UTC time is " + (create {HTTP_DATE}.make_now_utc).rfc850_string + ").%N")
|
||||||
|
end
|
||||||
|
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "plain/text"], ["Content-Length", s.count.out]>>)
|
||||||
|
response.set_status_code (a_status_code)
|
||||||
|
response.header.put_content_type_text_html
|
||||||
|
response.header.put_content_length (s.count)
|
||||||
|
if
|
||||||
|
attached request.http_connection as l_connection and then
|
||||||
|
l_connection.is_case_insensitive_equal_general ("keep-alive")
|
||||||
|
then
|
||||||
|
response.header.put_header_key_value ("Connection", "keep-alive")
|
||||||
|
end
|
||||||
|
response.put_string (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
38
library/server/wsf_proxy/rewriter/wsf_agent_uri_rewriter.e
Normal file
38
library/server/wsf_proxy/rewriter/wsf_agent_uri_rewriter.e
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_AGENT_URI_REWRITER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_AGENT_URI_REWRITER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_URI_REWRITER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
--convert
|
||||||
|
-- make ({FUNCTION [TUPLE [WSF_REQUEST], STRING]})
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_rewriter_function: like rewriter_function)
|
||||||
|
do
|
||||||
|
rewriter_function := a_rewriter_function
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
rewriter_function: FUNCTION [TUPLE [WSF_REQUEST], STRING]
|
||||||
|
|
||||||
|
feature -- Conversion
|
||||||
|
|
||||||
|
uri (a_request: WSF_REQUEST): STRING
|
||||||
|
-- <Precursor>.
|
||||||
|
do
|
||||||
|
Result := rewriter_function (a_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user