Compare commits
40 Commits
es_rev9928
...
es_rev9936
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a9ccce558 | |||
| d05c919668 | |||
| 087b5d49bb | |||
| c7b1d4bb4c | |||
| 193f22ebc8 | |||
| 5e79751522 | |||
| ac908e4efd | |||
| 885195dbaa | |||
| 2e49febca8 | |||
| 53f4f64596 | |||
| dff9007aa6 | |||
| c34f89df9b | |||
|
|
4a47a00747 | ||
|
|
12508c8e21 | ||
|
|
3d58ef84fa | ||
|
|
3a120f3311 | ||
|
|
d45b90e52a | ||
| c74b9e0c45 | |||
| b8aee435dd | |||
|
|
a4d737d548 | ||
|
|
16f667af2c | ||
|
|
27a5c9d969 | ||
| 32ad7f0c65 | |||
| 78f24ecb37 | |||
| 1161e541fa | |||
| d4ec640ac8 | |||
| 01a9d02586 | |||
| 1e4203111f | |||
| b84f4838f5 | |||
| 8b43cb909a | |||
| 897f64e4fe | |||
| 981942b2d6 | |||
| c132d7734b | |||
|
|
b66cfce138 | ||
| d28f794828 | |||
| 9e92b8c0fa | |||
| ef704790a8 | |||
| 57b1691243 | |||
| 29590b1c0d | |||
| ed959042d7 |
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="atom" uuid="076DEABB-20DA-43E9-A4C7-F3FAEDF5B1FC" library_target="atom">
|
||||
<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="atom" uuid="076DEABB-20DA-43E9-A4C7-F3FAEDF5B1FC" library_target="atom">
|
||||
<target name="atom">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base-safe.ecf"/>
|
||||
<cluster name="src" location="./src" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="atom" uuid="076DEABB-20DA-43E9-A4C7-F3FAEDF5B1FC" library_target="atom">
|
||||
<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="atom" uuid="076DEABB-20DA-43E9-A4C7-F3FAEDF5B1FC" library_target="atom">
|
||||
<target name="atom">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
<cluster name="src" location="./src" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="rss" uuid="8D8E3E5A-2685-40AF-9EF9-E3113B3C62AA" library_target="rss">
|
||||
<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="rss" uuid="8D8E3E5A-2685-40AF-9EF9-E3113B3C62AA" library_target="rss">
|
||||
<target name="rss">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base-safe.ecf"/>
|
||||
<cluster name="src" location="./src" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="rss" uuid="8D8E3E5A-2685-40AF-9EF9-E3113B3C62AA" library_target="rss">
|
||||
<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="rss" uuid="8D8E3E5A-2685-40AF-9EF9-E3113B3C62AA" library_target="rss">
|
||||
<target name="rss">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
<cluster name="src" location="./src" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="oauth" uuid="F8B4DF74-C71B-45A3-9B9A-F6141C4D3C56" library_target="oauth">
|
||||
<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="oauth" uuid="F8B4DF74-C71B-45A3-9B9A-F6141C4D3C56" library_target="oauth">
|
||||
<target name="oauth">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="oauth" uuid="F8B4DF74-C71B-45A3-9B9A-F6141C4D3C56" library_target="oauth">
|
||||
<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="oauth" uuid="F8B4DF74-C71B-45A3-9B9A-F6141C4D3C56" library_target="oauth">
|
||||
<target name="oauth">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
<cluster name="src" location="./src" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -23,8 +23,8 @@ feature {NONE} -- Initialization
|
||||
|
||||
make (a_columns: ITERABLE [WSF_GRID_COLUMN]; a_datasource: WSF_DATASOURCE [G])
|
||||
do
|
||||
make_repeater (a_datasource)
|
||||
columns := a_columns
|
||||
make_repeater (a_datasource)
|
||||
end
|
||||
|
||||
feature -- Render
|
||||
@@ -72,4 +72,14 @@ feature -- Properties
|
||||
|
||||
columns: ITERABLE [WSF_GRID_COLUMN]
|
||||
|
||||
;note
|
||||
copyright: "2011-2016, Yassin Hassan, Severin Munger, Jocelyn Fiat, 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
|
||||
|
||||
@@ -199,7 +199,7 @@ feature -- Properties
|
||||
-- List of current controls in this multi control
|
||||
|
||||
;note
|
||||
copyright: "2011-2014, Yassin Hassan, Severin Munger, Jocelyn Fiat, Eiffel Software and others"
|
||||
copyright: "2011-2016, Yassin Hassan, Severin Munger, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
<exclude>/.svn$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="standard">
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<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="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf"/>
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="false" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="http" location="..\..\..\..\library\network\protocol\http\http.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf"/>
|
||||
<library name="pcre" location="$ISE_LIBRARY\unstable\library\text\regexp\pcre\pcre.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="wsf" location="..\..\..\..\library\server\wsf\wsf.ecf"/>
|
||||
<cluster name="kernel" location=".\kernel\" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="debug" uuid="AA458565-7711-4BE1-ADA3-91716EABFA21" library_target="debug">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="debug" uuid="AA458565-7711-4BE1-ADA3-91716EABFA21" library_target="debug_standalone">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
@@ -18,7 +18,7 @@
|
||||
</target>
|
||||
<target name="debug_any" extends="common">
|
||||
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="cgi" location="..\..\library\server\wsf\connector\cgi-safe.ecf" readonly="false"/>
|
||||
<library name="libfcgi" location="..\..\library\server\wsf\connector\libfcgi-safe.ecf" readonly="false"/>
|
||||
<library name="standalone" location="..\..\library\server\wsf\connector\standalone-safe.ecf" readonly="false"/>
|
||||
@@ -27,7 +27,7 @@
|
||||
</target>
|
||||
<target name="debug_standalone" extends="common">
|
||||
<root class="EWF_DEBUG_SERVER" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false"/>
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
@@ -44,6 +44,4 @@
|
||||
<cluster name="launcher" location=".\launcher\default\" recursive="true"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="debug" extends="debug_standalone">
|
||||
</target>
|
||||
</system>
|
||||
|
||||
2
examples/debug/debug.ini
Normal file
2
examples/debug/debug.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
port=9090
|
||||
verbose=true
|
||||
@@ -28,6 +28,7 @@ feature {NONE} -- Initialization
|
||||
-- set_service_option ("verbose", True)
|
||||
set_service_option ("port", 9090)
|
||||
-- set_service_option ("base", "/www-debug/debug_service.fcgi/")
|
||||
import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("debug.ini"))
|
||||
end
|
||||
|
||||
-- execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
|
||||
@@ -9,11 +9,10 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http_client" location="..\..\..\library\network\http_client\http_client-safe.ecf" readonly="false"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf"/>
|
||||
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="client" uuid="D0059CEB-5F5C-4D21-8C71-842BD0F88468">
|
||||
<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="client" uuid="D0059CEB-5F5C-4D21-8C71-842BD0F88468" library_target="client">
|
||||
<target name="client">
|
||||
<root class="RESTBUCK_CLIENT" feature="make"/>
|
||||
<file_rule>
|
||||
@@ -9,11 +9,10 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
<library name="http_client" location="../../../library/network/http_client/http_client.ecf"/>
|
||||
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf"/>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
<library name="thread" location="$ISE_LIBRARY/library/thread/thread.ecf"/>
|
||||
<cluster name="src" location="./src" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="restbucks" uuid="2773FEAA-448F-410E-BEDE-9298C4749066" library_target="restbucks">
|
||||
<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="restbucks" uuid="2773FEAA-448F-410E-BEDE-9298C4749066" library_target="restbucks">
|
||||
<target name="restbucks_common">
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
@@ -8,7 +8,7 @@
|
||||
</file_rule>
|
||||
<option full_class_checking="false" void_safety="all">
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="conneg" location="..\..\library\network\protocol\content_negotiation\conneg-safe.ecf"/>
|
||||
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf" readonly="false"/>
|
||||
@@ -24,7 +24,7 @@
|
||||
</target>
|
||||
<target name="restbucks" extends="restbucks_common">
|
||||
<root class="RESTBUCKS_SERVER" feature="make"/>
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="provisional">
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<debug name="standalone" enabled="true"/>
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
@@ -36,7 +36,7 @@
|
||||
</target>
|
||||
<target name="policy_driven_restbucks" extends="restbucks_common">
|
||||
<root class="RESTBUCKS_SERVER" feature="make"/>
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="transitional" syntax="provisional">
|
||||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<debug name="standalone" enabled="true"/>
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
|
||||
@@ -30,8 +30,8 @@ feature -- Basic operations
|
||||
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")
|
||||
if request.is_keep_alive_http_connection then
|
||||
response.header.put_connection_keep_alive
|
||||
end
|
||||
response.put_string (s)
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="simple" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="simple" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple_standalone">
|
||||
<target name="common" abstract="true">
|
||||
<file_rule>
|
||||
<exclude>/.svn$</exclude>
|
||||
@@ -16,13 +16,20 @@
|
||||
</target>
|
||||
<target name="simple_standalone" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<option debug="false" warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||
<debug name="dbglog" enabled="true"/>
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
|
||||
<cluster name="simple" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple_standalone_mt" extends="simple_standalone">
|
||||
<setting name="concurrency" value="thread"/>
|
||||
</target>
|
||||
<target name="simple_standalone_st" extends="simple_standalone">
|
||||
<setting name="concurrency" value="none"/>
|
||||
</target>
|
||||
<target name="simple_cgi" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||||
@@ -39,6 +46,4 @@
|
||||
<library name="default_libfcgi" location="..\..\library\server\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="simple" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple" extends="simple_standalone">
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -2,7 +2,8 @@ verbose=true
|
||||
verbose_level=ALERT
|
||||
port=9090
|
||||
#max_concurrent_connections=100
|
||||
#keep_alive_timeout=15
|
||||
keep_alive_timeout=3
|
||||
#max_tcp_clients=100
|
||||
#socket_timeout=300
|
||||
socket_timeout=60
|
||||
socket_recv_timeout=15
|
||||
#max_keep_alive_requests=300
|
||||
|
||||
4
examples/simple_file/service.ini
Normal file
4
examples/simple_file/service.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
port=9090
|
||||
verbose=true
|
||||
socket_recv_timeout=15
|
||||
keep_alive_timeout=30
|
||||
@@ -21,6 +21,7 @@ feature {NONE} -- Initialization
|
||||
do
|
||||
Precursor
|
||||
set_service_option ("port", 9090)
|
||||
import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("service.ini"))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
note
|
||||
description : "simple application root class"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
description: "simple application root class"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||
WSF_STANDALONE_SERVICE [APPLICATION_EXECUTION]
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
@@ -19,11 +19,17 @@ feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
local
|
||||
opts: WSF_STANDALONE_SERVICE_OPTIONS
|
||||
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"))
|
||||
create opts
|
||||
opts.port := 9090
|
||||
opts.socket_recv_timeout := 5 -- seconds
|
||||
|
||||
opts.import_ini_file_options ("simple.ini")
|
||||
import_service_options (opts)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -32,8 +32,8 @@ feature -- Basic operations
|
||||
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")
|
||||
if request.is_keep_alive_http_connection then
|
||||
response.header.put_connection_keep_alive
|
||||
end
|
||||
response.put_string (s)
|
||||
end
|
||||
|
||||
@@ -17,11 +17,11 @@ port=9090
|
||||
#keep_alive_timeout=15
|
||||
#max_keep_alive_requests=100
|
||||
|
||||
### SSL settings
|
||||
### Secure connection settings
|
||||
# enable SSL, with file certificate.
|
||||
ssl_enabled=true
|
||||
ssl_ca_key=simple.key
|
||||
ssl_ca_crt=simple.crt
|
||||
is_secure=true
|
||||
secure_certificate=ca.crt
|
||||
secure_certificate_key=ca.key
|
||||
|
||||
### App settings
|
||||
verbose=true
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<variable name="httpd_ssl_enabled" value="true"/>
|
||||
<variable name="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="wsf_standalone" location="..\..\library\server\wsf\connector\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"/>
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf" readonly="false"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<library name="uri_template" location="..\..\library\text\parser\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false" use_application_options="true"/>
|
||||
</target>
|
||||
@@ -34,5 +33,6 @@
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
<target name="upload_image" extends="upload_image_standalone">
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -14,16 +14,18 @@ feature {NONE} -- Initialization
|
||||
make_and_launch
|
||||
local
|
||||
l_launcher: WSF_STANDALONE_WEBSOCKET_SERVICE_LAUNCHER [APPLICATION_EXECUTION]
|
||||
opts: WSF_SERVICE_LAUNCHER_OPTIONS
|
||||
opts: WSF_STANDALONE_WEBSOCKET_SERVICE_OPTIONS
|
||||
do
|
||||
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} opts.make_from_file ("ws.ini")
|
||||
create l_launcher.make_and_launch (options)
|
||||
end
|
||||
create opts
|
||||
if opts.is_secure_connection_supported then
|
||||
opts.is_secure := True
|
||||
opts.set_secure_protocol_to_tls_1_2
|
||||
opts.secure_certificate := "ca.crt"
|
||||
opts.secure_certificate_key := "ca.key"
|
||||
end
|
||||
|
||||
options: WSF_SERVICE_LAUNCHER_OPTIONS
|
||||
-- Initialize current service.
|
||||
do
|
||||
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} Result.make_from_file ("ws.ini")
|
||||
opts.import_ini_file_options ("ws.ini")
|
||||
create l_launcher.make_and_launch (opts)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -35,8 +35,8 @@ feature -- Basic operations
|
||||
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")
|
||||
if request.is_keep_alive_http_connection then
|
||||
response.header.put_connection_keep_alive
|
||||
end
|
||||
response.put_string (s)
|
||||
end
|
||||
@@ -88,7 +88,7 @@ feature -- HTML Resource
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
|
||||
<script src="##HTTPSCHEME##://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
|
||||
@@ -96,9 +96,10 @@ $(document).ready(function() {
|
||||
|
||||
function connect(){
|
||||
|
||||
var host = "ws://127.0.0.1:##PORTNUMBER##";
|
||||
var host = "##WSSCHEME##://127.0.0.1:##PORTNUMBER##/app";
|
||||
|
||||
try{
|
||||
|
||||
socket = new WebSocket(host);
|
||||
message('<p class="event">Socket Status: '+socket.readyState);
|
||||
socket.onopen = function(){
|
||||
@@ -178,6 +179,13 @@ body {font-family:Arial, Helvetica, sans-serif;}
|
||||
</html>
|
||||
]"
|
||||
Result.replace_substring_all ("##PORTNUMBER##", a_port.out)
|
||||
if request.is_https then
|
||||
Result.replace_substring_all ("##HTTPSCHEME##", "https")
|
||||
Result.replace_substring_all ("##WSSCHEME##", "wss")
|
||||
else
|
||||
Result.replace_substring_all ("##HTTPSCHEME##", "http")
|
||||
Result.replace_substring_all ("##WSSCHEME##", "ws")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
15
examples/websocket/ca.crt
Normal file
15
examples/websocket/ca.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-----
|
||||
15
examples/websocket/ca.key
Normal file
15
examples/websocket/ca.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-----
|
||||
@@ -18,4 +18,7 @@
|
||||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||||
<cluster name="app" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="websocket_app_ssl" extends="websocket_app">
|
||||
<variable name="ssl_enabled" value="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -4,5 +4,10 @@ port=9090
|
||||
max_concurrent_connections=100
|
||||
keep_alive_timeout=35
|
||||
max_tcp_clients=100
|
||||
socket_timeout=30000
|
||||
max_keep_alive_requests=3000
|
||||
socket_timeout=30
|
||||
socket_recv_timeout=5
|
||||
max_keep_alive_requests=300
|
||||
|
||||
is_secure=false
|
||||
secure_certificate=ca.crt
|
||||
secure_certificate_key=ca.key
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option debug="false" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf">
|
||||
<condition>
|
||||
@@ -18,12 +19,17 @@
|
||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http-safe.ecf"/>
|
||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
|
||||
<library name="http_network" location="..\http_network\http_network-safe.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
@@ -37,18 +43,6 @@
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true">
|
||||
<condition>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf">
|
||||
<condition>
|
||||
@@ -18,12 +19,17 @@
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http.ecf"/>
|
||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
|
||||
<library name="http_network" location="..\http_network\http_network.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
@@ -37,18 +43,6 @@
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true">
|
||||
<condition>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option debug="false" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
</file_rule>
|
||||
<option debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
|
||||
@@ -9,12 +9,17 @@
|
||||
</file_rule>
|
||||
<option debug="false" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http-safe.ecf"/>
|
||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
|
||||
<library name="http_network" location="..\http_network\http_network-safe.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
@@ -23,16 +28,6 @@
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="spec_net" location="$|spec\net\">
|
||||
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="default_net" location="$|default\net\"/>
|
||||
</cluster>
|
||||
|
||||
@@ -9,12 +9,17 @@
|
||||
</file_rule>
|
||||
<option debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http.ecf"/>
|
||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
|
||||
<library name="http_network" location="..\http_network\http_network.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
@@ -23,16 +28,6 @@
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="spec_net" location="$|spec\net\">
|
||||
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="default_net" location="$|default\net\"/>
|
||||
</cluster>
|
||||
|
||||
@@ -164,11 +164,24 @@ feature -- Settings
|
||||
Result := session.ignore_content_length
|
||||
end
|
||||
|
||||
buffer_size: NATURAL
|
||||
-- Set the buffer size for request. This option will
|
||||
-- only be set if buffer_size is positive
|
||||
buffer_size: INTEGER
|
||||
-- Buffer size for request,
|
||||
-- initialized from the session buffer_size value, or default 2_048.
|
||||
do
|
||||
Result := session.buffer_size
|
||||
Result := session.buffer_size.to_integer_32
|
||||
if Result <= 0 then
|
||||
Result := 2_048
|
||||
end
|
||||
end
|
||||
|
||||
chunk_size: INTEGER
|
||||
-- Chunk size for request, when "Transfer-Encoding: chunked"
|
||||
-- initialized from the session buffer_size value, or default 2_048.
|
||||
do
|
||||
Result := session.chunk_size.to_integer_32
|
||||
if Result <= 0 then
|
||||
Result := 2_048
|
||||
end
|
||||
end
|
||||
|
||||
default_response_charset: detachable READABLE_STRING_8
|
||||
@@ -249,7 +262,7 @@ feature {NONE} -- Utilities: encoding
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -243,9 +243,13 @@ feature -- Settings
|
||||
ignore_content_length: BOOLEAN
|
||||
-- Does this session ignore Content-Size headers?
|
||||
|
||||
buffer_size: NATURAL
|
||||
buffer_size: NATURAL assign set_buffer_size
|
||||
-- Set the buffer size for request. This option will
|
||||
-- only be set if buffer_size is positive
|
||||
-- only be set if buffer_size > 0.
|
||||
|
||||
chunk_size: NATURAL assign set_chunk_size
|
||||
-- Set the chunk size for request, when "Transfer-Encoding: chunked".
|
||||
-- This option will only be set if chunk_size > 0.
|
||||
|
||||
default_response_charset: detachable READABLE_STRING_8
|
||||
-- Default encoding of responses. Used if no charset is provided by the host.
|
||||
@@ -405,8 +409,18 @@ feature -- Element change
|
||||
end
|
||||
end
|
||||
|
||||
set_buffer_size (a_size: like buffer_size)
|
||||
do
|
||||
buffer_size := a_size
|
||||
end
|
||||
|
||||
set_chunk_size (a_size: like chunk_size)
|
||||
do
|
||||
chunk_size := a_size
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -11,7 +11,7 @@ create
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_socket: NETWORK_STREAM_SOCKET; a_host: READABLE_STRING_GENERAL; a_port: INTEGER)
|
||||
make (a_socket: HTTP_STREAM_SOCKET; a_host: READABLE_STRING_GENERAL; a_port: INTEGER)
|
||||
do
|
||||
socket := a_socket
|
||||
host := a_host
|
||||
@@ -20,7 +20,7 @@ feature {NONE} -- Initialization
|
||||
|
||||
feature -- Access
|
||||
|
||||
socket: NETWORK_STREAM_SOCKET
|
||||
socket: HTTP_STREAM_SOCKET
|
||||
-- Persistent connection socket.
|
||||
|
||||
host: READABLE_STRING_GENERAL
|
||||
@@ -40,7 +40,7 @@ feature -- Status report
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -28,11 +28,11 @@ feature {NONE} -- Internal
|
||||
session: NET_HTTP_CLIENT_SESSION
|
||||
net_http_client_version: STRING = "0.1"
|
||||
|
||||
session_socket (a_host: READABLE_STRING_8; a_port: INTEGER; a_is_https: BOOLEAN; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): NETWORK_STREAM_SOCKET
|
||||
session_socket (a_host: READABLE_STRING_8; a_port: INTEGER; a_is_https: BOOLEAN; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_STREAM_SOCKET
|
||||
-- Session socket to use for connection.
|
||||
-- Eventually reuse the persistent connection if any.
|
||||
local
|
||||
l_socket: detachable NETWORK_STREAM_SOCKET
|
||||
l_socket: detachable HTTP_STREAM_SOCKET
|
||||
do
|
||||
if
|
||||
attached session.persistent_connection as l_persistent_connection and then
|
||||
@@ -40,12 +40,12 @@ feature {NONE} -- Internal
|
||||
then
|
||||
l_socket := l_persistent_connection.socket
|
||||
if a_is_https then
|
||||
if attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
|
||||
if attached {HTTP_STREAM_SECURE_SOCKET} l_socket as l_ssl_socket then
|
||||
Result := l_ssl_socket
|
||||
else
|
||||
l_socket := Void
|
||||
end
|
||||
elseif attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
|
||||
elseif attached {HTTP_STREAM_SECURE_SOCKET} l_socket as l_ssl_socket then
|
||||
l_socket := Void
|
||||
end
|
||||
if l_socket /= Void and then not l_socket.is_connected then
|
||||
@@ -59,7 +59,7 @@ feature {NONE} -- Internal
|
||||
else
|
||||
session.set_persistent_connection (Void)
|
||||
if a_is_https then
|
||||
create {SSL_NETWORK_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
|
||||
create {HTTP_STREAM_SECURE_SOCKET} Result.make_client_by_port (a_port, a_host)
|
||||
else
|
||||
create Result.make_client_by_port (a_port, a_host)
|
||||
end
|
||||
@@ -81,7 +81,7 @@ feature -- Access
|
||||
l_cookie: detachable READABLE_STRING_8
|
||||
l_request_uri: STRING
|
||||
l_url: HTTP_URL
|
||||
l_socket: NETWORK_STREAM_SOCKET
|
||||
l_socket: HTTP_STREAM_SOCKET
|
||||
s: STRING
|
||||
l_message: STRING
|
||||
l_content_length: INTEGER
|
||||
@@ -100,6 +100,7 @@ feature -- Access
|
||||
l_boundary: READABLE_STRING_8
|
||||
l_is_http_1_0_request: BOOLEAN
|
||||
l_is_keep_alive: BOOLEAN
|
||||
l_is_chunked_transfer_encoding: BOOLEAN
|
||||
retried: BOOLEAN
|
||||
do
|
||||
if not retried then
|
||||
@@ -179,6 +180,8 @@ feature -- Access
|
||||
end
|
||||
|
||||
-- handle sending data
|
||||
l_is_chunked_transfer_encoding := attached headers.item ("Transfer-Encoding") as l_transfer_encoding and then l_transfer_encoding.same_string ("chunked")
|
||||
|
||||
if ctx /= Void then
|
||||
if ctx.has_upload_filename then
|
||||
l_upload_filename := ctx.upload_filename
|
||||
@@ -195,14 +198,21 @@ feature -- Access
|
||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||
l_upload_data := ctx.form_parameters_to_url_encoded_string
|
||||
headers.force (l_upload_data.count.out, "Content-Length")
|
||||
|
||||
else
|
||||
if l_is_chunked_transfer_encoding then
|
||||
-- Discard chunked transfer encoding
|
||||
headers.remove ("Transfer-Encoding")
|
||||
l_is_chunked_transfer_encoding := False
|
||||
end
|
||||
elseif l_form_data /= Void then
|
||||
-- create form using multipart/form-data encoding
|
||||
l_boundary := new_mime_boundary
|
||||
l_boundary := new_mime_boundary (l_form_data)
|
||||
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
|
||||
if l_form_data /= Void then
|
||||
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
|
||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
|
||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||
if l_is_chunked_transfer_encoding then
|
||||
-- Discard chunked transfer encoding
|
||||
headers.remove ("Transfer-Encoding")
|
||||
l_is_chunked_transfer_encoding := False
|
||||
end
|
||||
end
|
||||
elseif l_upload_data /= Void then
|
||||
@@ -210,12 +220,16 @@ feature -- Access
|
||||
if not headers.has ("Content-Type") then
|
||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||
end
|
||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||
if not l_is_chunked_transfer_encoding then
|
||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||
end
|
||||
elseif l_upload_filename /= Void then
|
||||
check ctx.has_upload_filename end
|
||||
create l_upload_file.make_with_name (l_upload_filename)
|
||||
if l_upload_file.exists and then l_upload_file.readable then
|
||||
headers.extend (l_upload_file.count.out, "Content-Length")
|
||||
if not l_is_chunked_transfer_encoding then
|
||||
headers.extend (l_upload_file.count.out, "Content-Length")
|
||||
end
|
||||
end
|
||||
check l_upload_file /= Void end
|
||||
end
|
||||
@@ -289,12 +303,7 @@ feature -- Access
|
||||
--| End of client header.
|
||||
s.append (Http_end_of_header_line)
|
||||
|
||||
if l_upload_data /= Void then
|
||||
s.append (l_upload_data)
|
||||
s.append (http_end_of_header_line)
|
||||
end
|
||||
|
||||
--| Note that any remaining file to upload will be done directly via the socket
|
||||
--| Note that any remaining data or file to upload will be done directly via the socket
|
||||
--| to optimize memory usage
|
||||
|
||||
|
||||
@@ -315,9 +324,20 @@ feature -- Access
|
||||
end
|
||||
l_socket.put_string (s)
|
||||
--| Send remaining payload data, if needed.
|
||||
if l_upload_data /= Void then
|
||||
if l_is_chunked_transfer_encoding then
|
||||
put_string_using_chunked_transfer_encoding (l_upload_data, chunk_size, l_socket)
|
||||
else
|
||||
l_socket.put_string (l_upload_data)
|
||||
end
|
||||
end
|
||||
if l_upload_file /= Void then
|
||||
-- i.e: not yet processed
|
||||
append_file_content_to_socket (l_upload_file, l_upload_file.count, l_socket)
|
||||
if l_is_chunked_transfer_encoding then
|
||||
-- i.e: not yet processed
|
||||
append_file_content_to_socket_using_chunked_transfer_encoding (l_upload_file, l_upload_file.count, chunk_size, l_socket)
|
||||
else
|
||||
append_file_content_to_socket (l_upload_file, l_upload_file.count, l_socket)
|
||||
end
|
||||
end
|
||||
|
||||
--|-------------------------|--
|
||||
@@ -418,7 +438,7 @@ feature {NONE} -- Helpers
|
||||
io.error.put_string (m)
|
||||
end
|
||||
|
||||
is_ready_for_reading (a_socket: NETWORK_STREAM_SOCKET): BOOLEAN
|
||||
is_ready_for_reading (a_socket: HTTP_STREAM_SOCKET): BOOLEAN
|
||||
-- Is `a_socket' ready for reading?
|
||||
do
|
||||
Result := a_socket.ready_for_reading
|
||||
@@ -498,8 +518,85 @@ feature {NONE} -- Helpers
|
||||
Result := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
|
||||
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: NETWORK_STREAM_SOCKET)
|
||||
-- Append `a_file' content to `a_output'.
|
||||
put_string_using_chunked_transfer_encoding (a_string: READABLE_STRING_8; a_chunk_size: INTEGER; a_output: HTTP_STREAM_SOCKET)
|
||||
local
|
||||
i,n: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := a_string.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
put_chunk (a_string.substring (i, i + a_chunk_size), Void, a_output)
|
||||
i := i + a_chunk_size
|
||||
end
|
||||
put_chunk_end (Void, Void, a_output)
|
||||
end
|
||||
|
||||
put_chunk (a_content: READABLE_STRING_8; a_ext: detachable READABLE_STRING_8; a_output: HTTP_STREAM_SOCKET)
|
||||
-- Write chunk non empty `a_content' to `a_output'
|
||||
-- with optional extension `a_ext': chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
-- Note: that header "Transfer-Encoding: chunked" is required.
|
||||
require
|
||||
a_content_not_empty: a_content /= Void and then not a_content.is_empty
|
||||
valid_chunk_extension: (a_ext /= Void and then not a_ext.is_empty) implies
|
||||
( a_ext.starts_with (";") and not a_ext.has ('%N') and not not a_ext.has ('%R') )
|
||||
local
|
||||
l_chunk_size_line: STRING_8
|
||||
i: INTEGER
|
||||
do
|
||||
--| Remove all left '0'
|
||||
l_chunk_size_line := a_content.count.to_hex_string
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
l_chunk_size_line[i] /= '0'
|
||||
loop
|
||||
i := i + 1
|
||||
end
|
||||
if i > 1 then
|
||||
l_chunk_size_line := l_chunk_size_line.substring (i, l_chunk_size_line.count)
|
||||
end
|
||||
|
||||
if a_ext /= Void then
|
||||
l_chunk_size_line.append (a_ext)
|
||||
end
|
||||
l_chunk_size_line.append (crlf)
|
||||
|
||||
a_output.put_string (l_chunk_size_line)
|
||||
a_output.put_string (a_content)
|
||||
a_output.put_string (crlf)
|
||||
end
|
||||
|
||||
put_chunk_end (a_ext: detachable READABLE_STRING_8; a_trailer: detachable READABLE_STRING_8; a_output: HTTP_STREAM_SOCKET)
|
||||
-- Put end of chunked content,
|
||||
-- with optional extension `a_ext': chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
-- and with optional trailer `a_trailer' : trailer= *(entity-header CRLF)
|
||||
local
|
||||
l_chunk_size_line: STRING_8
|
||||
do
|
||||
-- Chunk end
|
||||
create l_chunk_size_line.make (1)
|
||||
l_chunk_size_line.append_integer (0)
|
||||
|
||||
if a_ext /= Void then
|
||||
l_chunk_size_line.append (a_ext)
|
||||
end
|
||||
l_chunk_size_line.append (crlf)
|
||||
a_output.put_string (l_chunk_size_line)
|
||||
|
||||
-- Optional trailer
|
||||
if a_trailer /= Void and then not a_trailer.is_empty then
|
||||
a_output.put_string (a_trailer)
|
||||
end
|
||||
|
||||
-- Final CRLF
|
||||
a_output.put_string (crlf)
|
||||
end
|
||||
|
||||
append_file_content_to_socket_using_chunked_transfer_encoding (a_file: FILE; a_len: INTEGER; a_chunk_size: INTEGER; a_output: HTTP_STREAM_SOCKET)
|
||||
-- Append `a_file' content as chunks of `a_chunk_size' length to `a_output'.
|
||||
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||
require
|
||||
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||
@@ -523,7 +620,44 @@ feature {NONE} -- Helpers
|
||||
until
|
||||
l_count = 0 or a_file.exhausted
|
||||
loop
|
||||
a_file.read_stream_thread_aware (l_count.min (2_048))
|
||||
a_file.read_stream_thread_aware (l_count.min (a_chunk_size))
|
||||
put_chunk (a_file.last_string, Void, a_output)
|
||||
l_count := l_count - a_file.bytes_read
|
||||
end
|
||||
if not l_was_open then
|
||||
a_file.close
|
||||
end
|
||||
put_chunk_end (Void, Void, a_output)
|
||||
end
|
||||
end
|
||||
|
||||
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: HTTP_STREAM_SOCKET)
|
||||
-- Append `a_file' content to `a_output'.
|
||||
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||
require
|
||||
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||
local
|
||||
l_was_open: BOOLEAN
|
||||
l_count, l_buffer_size: INTEGER
|
||||
do
|
||||
if a_len >= 0 then
|
||||
l_count := a_len
|
||||
else
|
||||
l_count := a_file.count
|
||||
end
|
||||
if l_count > 0 then
|
||||
l_was_open := a_file.is_open_read
|
||||
if a_file.is_open_read then
|
||||
l_was_open := True
|
||||
else
|
||||
a_file.open_read
|
||||
end
|
||||
from
|
||||
l_buffer_size := buffer_size
|
||||
until
|
||||
l_count = 0 or a_file.exhausted
|
||||
loop
|
||||
a_file.read_stream_thread_aware (l_count.min (l_buffer_size))
|
||||
a_output.put_string (a_file.last_string)
|
||||
l_count := l_count - a_file.bytes_read
|
||||
end
|
||||
@@ -541,6 +675,7 @@ feature {NONE} -- Helpers
|
||||
local
|
||||
l_was_open: BOOLEAN
|
||||
l_count: INTEGER
|
||||
l_buffer_size: INTEGER
|
||||
do
|
||||
if a_len >= 0 then
|
||||
l_count := a_len
|
||||
@@ -555,11 +690,11 @@ feature {NONE} -- Helpers
|
||||
a_file.open_read
|
||||
end
|
||||
from
|
||||
|
||||
l_buffer_size := buffer_size
|
||||
until
|
||||
l_count = 0 or a_file.exhausted
|
||||
loop
|
||||
a_file.read_stream_thread_aware (l_count.min (2_048))
|
||||
a_file.read_stream_thread_aware (l_count.min (l_buffer_size))
|
||||
a_output.append (a_file.last_string)
|
||||
l_count := l_count - a_file.bytes_read
|
||||
end
|
||||
@@ -569,7 +704,7 @@ feature {NONE} -- Helpers
|
||||
end
|
||||
end
|
||||
|
||||
append_socket_header_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
|
||||
append_socket_header_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: HTTP_STREAM_SOCKET; a_output: STRING)
|
||||
-- Get header from `a_socket' into `a_output'.
|
||||
local
|
||||
s: READABLE_STRING_8
|
||||
@@ -579,7 +714,7 @@ feature {NONE} -- Helpers
|
||||
until
|
||||
s.same_string ("%R") or not a_socket.readable or a_response.error_occurred
|
||||
loop
|
||||
a_socket.read_line_thread_aware
|
||||
a_socket.read_line_noexception
|
||||
s := a_socket.last_string
|
||||
if s.is_empty then
|
||||
if session.is_debug_verbose then
|
||||
@@ -595,7 +730,7 @@ feature {NONE} -- Helpers
|
||||
end
|
||||
end
|
||||
|
||||
append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_len: INTEGER; a_output: STRING)
|
||||
append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: HTTP_STREAM_SOCKET; a_len: INTEGER; a_output: STRING)
|
||||
-- Get content from `a_socket' and append it to `a_output'.
|
||||
-- If `a_len' is negative, try to get as much as possible,
|
||||
-- this is probably HTTP/1.0 without any Content-Length.
|
||||
@@ -614,7 +749,7 @@ feature {NONE} -- Helpers
|
||||
until
|
||||
r = 0 or else not a_socket.readable or else a_response.error_occurred
|
||||
loop
|
||||
a_socket.read_stream_thread_aware (r)
|
||||
a_socket.read_stream_noexception (r)
|
||||
l_count := l_count + a_socket.bytes_read
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
|
||||
@@ -632,12 +767,12 @@ feature {NONE} -- Helpers
|
||||
-- FIXME: check solution!
|
||||
from
|
||||
l_count := 0
|
||||
l_chunk_size := 1_024
|
||||
l_chunk_size := buffer_size
|
||||
n := l_chunk_size --| value to satisfy until condition on first loop.
|
||||
until
|
||||
n < l_chunk_size or not a_socket.readable
|
||||
loop
|
||||
a_socket.read_stream_thread_aware (l_chunk_size)
|
||||
a_socket.read_stream_noexception (l_chunk_size)
|
||||
s := a_socket.last_string
|
||||
n := a_socket.bytes_read
|
||||
l_count := l_count + n
|
||||
@@ -647,7 +782,7 @@ feature {NONE} -- Helpers
|
||||
end
|
||||
end
|
||||
|
||||
append_socket_chunked_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
|
||||
append_socket_chunked_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: HTTP_STREAM_SOCKET; a_output: STRING)
|
||||
-- Get chunked content from `a_socket' and append it to `a_output'.
|
||||
require
|
||||
socket_readable: a_socket.readable
|
||||
@@ -668,7 +803,7 @@ feature {NONE} -- Helpers
|
||||
until
|
||||
n = 0 or not a_socket.readable
|
||||
loop
|
||||
a_socket.read_line_thread_aware -- Read chunk info
|
||||
a_socket.read_line_noexception
|
||||
s := a_socket.last_string
|
||||
s.right_adjust
|
||||
if session.is_debug_verbose then
|
||||
@@ -697,7 +832,7 @@ feature {NONE} -- Helpers
|
||||
until
|
||||
r = 0 or else not a_socket.readable or else a_response.error_occurred
|
||||
loop
|
||||
a_socket.read_stream_thread_aware (r)
|
||||
a_socket.read_stream_noexception (r)
|
||||
l_count := l_count + a_socket.bytes_read
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
|
||||
@@ -707,9 +842,9 @@ feature {NONE} -- Helpers
|
||||
a_output.append (a_socket.last_string)
|
||||
end
|
||||
|
||||
a_socket.read_character
|
||||
a_socket.read_character_noexception
|
||||
check a_socket.last_character = '%R' end
|
||||
a_socket.read_character
|
||||
a_socket.read_character_noexception
|
||||
check a_socket.last_character = '%N' end
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: - Found CRNL %N")
|
||||
@@ -718,16 +853,42 @@ feature {NONE} -- Helpers
|
||||
end
|
||||
end
|
||||
|
||||
new_mime_boundary: STRING
|
||||
new_mime_boundary (a_data: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING
|
||||
-- New MIME boundary.
|
||||
local
|
||||
s: STRING
|
||||
ran: RANDOM
|
||||
n: INTEGER
|
||||
i,j: INTEGER
|
||||
do
|
||||
-- FIXME: better boundary creation
|
||||
Result := "----------------------------5eadfcf3bb3e"
|
||||
across
|
||||
a_data as ic
|
||||
loop
|
||||
i := i + ic.item.count + ic.key.count
|
||||
end
|
||||
create ran.set_seed (i) -- FIXME: use a real random seed.
|
||||
ran.start
|
||||
ran.forth
|
||||
n := (20 * ran.real_item).truncated_to_integer
|
||||
create Result.make_filled ('-', 3 + n)
|
||||
s := "_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
from
|
||||
until
|
||||
Result.count >= 40
|
||||
loop
|
||||
ran.forth
|
||||
j := (ran.real_item * s.count).truncated_to_integer.max (1)
|
||||
Result.append_character (s[j])
|
||||
end
|
||||
check Result.count = 40 and Result.starts_with ("---") end
|
||||
end
|
||||
|
||||
crlf: STRING = "%R%N"
|
||||
-- CR and NL sequence.
|
||||
|
||||
invariant
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -14,7 +14,7 @@ inherit
|
||||
close
|
||||
end
|
||||
|
||||
NET_HTTP_CLIENT_INFO
|
||||
HTTP_SECURE_HELPER
|
||||
|
||||
create
|
||||
make
|
||||
@@ -32,7 +32,7 @@ feature -- Status report
|
||||
do
|
||||
Result := True
|
||||
if base_url.starts_with_general ("https://") then
|
||||
Result := has_https_support
|
||||
Result := is_secure_connection_supported
|
||||
end
|
||||
end
|
||||
|
||||
@@ -172,7 +172,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
note
|
||||
description: "[
|
||||
A fake SSL network stream socket... when SSL is disabled at compilation time.
|
||||
Its behavior is similar to NETWORK_STREAM_SOCKET.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
SSL_NETWORK_STREAM_SOCKET
|
||||
|
||||
inherit
|
||||
NETWORK_STREAM_SOCKET
|
||||
|
||||
create
|
||||
make, make_empty, make_client_by_port, make_client_by_address_and_port, make_server_by_port, make_loopback_server_by_port
|
||||
|
||||
create {SSL_NETWORK_STREAM_SOCKET}
|
||||
make_from_descriptor_and_address, create_from_descriptor
|
||||
|
||||
|
||||
end
|
||||
@@ -1,24 +0,0 @@
|
||||
note
|
||||
description: "Additional information related to NET HTTP Client.."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
NET_HTTP_CLIENT_INFO
|
||||
|
||||
feature -- Access
|
||||
|
||||
has_https_support: BOOLEAN = True
|
||||
-- Is HTTPS supported?
|
||||
|
||||
note
|
||||
copyright: "2011-2015, 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
|
||||
@@ -64,4 +64,9 @@ feature -- Tests
|
||||
test_get_with_authentication
|
||||
end
|
||||
|
||||
net_test_post_with_file_using_chunked_transfer_encoding
|
||||
do
|
||||
test_post_with_file_using_chunked_transfer_encoding
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -253,6 +253,38 @@ feature -- Factory
|
||||
end
|
||||
end
|
||||
|
||||
test_post_with_file_using_chunked_transfer_encoding
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
if attached global_requestbin_path as requestbin_path then
|
||||
|
||||
-- POST REQUEST WITH A FILE AND FORM DATA
|
||||
-- check requestbin to ensure the file and form parameters are correctly received
|
||||
-- set filename to a local file
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
l_ctx.add_header ("Transfer-Encoding", "chunked")
|
||||
l_ctx.set_upload_filename ("logo.jpg")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.post (requestbin_path, l_ctx, Void) as res and then
|
||||
attached res.headers as hds
|
||||
then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
print (h)
|
||||
else
|
||||
assert ("Has requestbin path", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_get_with_redirection
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
|
||||
123
library/network/http_network/http_network-safe.ecf
Normal file
123
library/network/http_network/http_network-safe.ecf
Normal file
@@ -0,0 +1,123 @@
|
||||
<?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="7C7AD84D-B7BD-4709-B4B2-9365B86582AE" 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>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<external_include location="$ECF_CONFIG_PATH/spec/include">
|
||||
<condition>
|
||||
<version type="compiler" min="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_include>
|
||||
<external_include location="$ECF_CONFIG_PATH/spec/include_until_16_05">
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_include>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="httpd_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
|
||||
<external_include location="$(ISE_LIBRARY)/unstable/library/network/socket/netssl/spec/include">
|
||||
<condition>
|
||||
<platform excluded_value="windows"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_include>
|
||||
<external_include location="$(ISE_LIBRARY)\unstable\library\network\socket\netssl\spec\include">
|
||||
<condition>
|
||||
<platform value="windows"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_include>
|
||||
<external_cflag value="-D_WINSOCKAPI_">
|
||||
<condition>
|
||||
<platform value="windows"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_cflag>
|
||||
<cluster name="network" location=".\src\">
|
||||
<file_rule>
|
||||
<exclude>/http_stream_socket_ext.e$</exclude>
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
<cluster name="disabled_ssl_network" location="$|no_ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" excluded_value="true"/>
|
||||
<custom name="net_ssl_enabled" excluded_value="true"/>
|
||||
<custom name="httpd_ssl_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="ssl_network" location="$|ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="httpd_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<file_rule>
|
||||
<exclude>/http_stream_secure_socket_ext.e$</exclude>
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
<custom name="httpd_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="network_until_16_05" location=".\src\until_16_05\" recursive="false">
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
<cluster name="ssl_network_until_16_05" location="$|ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="httpd_ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
</target>
|
||||
<target name="http_network_ssl" extends="http_network">
|
||||
<variable name="ssl_enabled" value="true" />
|
||||
</target>
|
||||
</system>
|
||||
123
library/network/http_network/http_network.ecf
Normal file
123
library/network/http_network/http_network.ecf
Normal file
@@ -0,0 +1,123 @@
|
||||
<?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="7C7AD84D-B7BD-4709-B4B2-9365B86582AE" 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"/>
|
||||
<external_include location="$ECF_CONFIG_PATH/spec/include">
|
||||
<condition>
|
||||
<version type="compiler" min="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_include>
|
||||
<external_include location="$ECF_CONFIG_PATH/spec/include_until_16_05">
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_include>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="httpd_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
|
||||
<external_include location="$(ISE_LIBRARY)/unstable/library/network/socket/netssl/spec/include">
|
||||
<condition>
|
||||
<platform excluded_value="windows"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_include>
|
||||
<external_include location="$(ISE_LIBRARY)\unstable\library\network\socket\netssl\spec\include">
|
||||
<condition>
|
||||
<platform value="windows"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_include>
|
||||
<external_cflag value="-D_WINSOCKAPI_">
|
||||
<condition>
|
||||
<platform value="windows"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</external_cflag>
|
||||
<cluster name="network" location=".\src\">
|
||||
<file_rule>
|
||||
<exclude>/http_stream_socket_ext.e$</exclude>
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
<cluster name="disabled_ssl_network" location="$|no_ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" excluded_value="true"/>
|
||||
<custom name="net_ssl_enabled" excluded_value="true"/>
|
||||
<custom name="httpd_ssl_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="ssl_network" location="$|ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="httpd_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<file_rule>
|
||||
<exclude>/http_stream_secure_socket_ext.e$</exclude>
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
<custom name="httpd_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="network_until_16_05" location=".\src\until_16_05\" recursive="false">
|
||||
<condition>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
<cluster name="ssl_network_until_16_05" location="$|ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="httpd_ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="16.09.9.9124"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
</target>
|
||||
<target name="http_network_ssl" extends="http_network">
|
||||
<variable name="ssl_enabled" value="true" />
|
||||
</target>
|
||||
</system>
|
||||
10
library/network/http_network/license.lic
Normal file
10
library/network/http_network/license.lic
Normal file
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
indexing
|
||||
description: "Functions used by the EiffelWeb httpd networking classes. "
|
||||
description: "Functions used by the EiffelWeb http networking classes. "
|
||||
copyright: "Copyright (c) 2011-2016, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
@@ -12,8 +12,8 @@ indexing
|
||||
]"
|
||||
*/
|
||||
|
||||
#ifndef _ew_httpd_net_h_
|
||||
#define _ew_httpd_net_h_
|
||||
#ifndef _ew_network_h_
|
||||
#define _ew_network_h_
|
||||
|
||||
#include "eif_config.h"
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
note
|
||||
description: "Summary description for {HTTPD_STREAM_SOCKET}."
|
||||
description: "Summary description for {HTTP_STREAM_SOCKET}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTPD_STREAM_SOCKET
|
||||
HTTP_STREAM_SOCKET
|
||||
|
||||
inherit
|
||||
NETWORK_STREAM_SOCKET
|
||||
|
||||
HTTPD_STREAM_SOCKET_EXT
|
||||
HTTP_STREAM_SOCKET_EXT
|
||||
|
||||
create
|
||||
make, make_empty,
|
||||
@@ -19,6 +19,16 @@ create
|
||||
create {NETWORK_STREAM_SOCKET}
|
||||
make_from_descriptor_and_address
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_secure_connection_supported: BOOLEAN
|
||||
-- SSL/TLS supported?
|
||||
once
|
||||
Result := False
|
||||
ensure
|
||||
Result = {HTTP_SECURE_HELPER}.is_secure_connection_supported
|
||||
end
|
||||
|
||||
feature -- Input
|
||||
|
||||
read_character_noexception
|
||||
@@ -27,7 +37,9 @@ feature -- Input
|
||||
-- No exception raised!
|
||||
do
|
||||
read_to_managed_pointer_noexception (socket_buffer, 0, character_8_bytes)
|
||||
if bytes_read /= character_8_bytes then
|
||||
if was_error then
|
||||
-- Socket error already set.
|
||||
elseif bytes_read /= character_8_bytes then
|
||||
socket_error := "Peer closed connection"
|
||||
else
|
||||
last_character := socket_buffer.read_character (0)
|
||||
@@ -135,6 +147,8 @@ feature {NONE} -- Input
|
||||
l_last_read := c_read_stream_noexception (descriptor, nb_bytes - l_read, p + start_pos + l_read)
|
||||
if l_last_read >= 0 then
|
||||
l_read := l_read + l_last_read
|
||||
else
|
||||
socket_error := "Network error!"
|
||||
end
|
||||
end
|
||||
bytes_read := l_read
|
||||
@@ -198,7 +212,7 @@ feature -- Output
|
||||
put_managed_pointer_noexception (socket_buffer, 0, character_8_bytes)
|
||||
end
|
||||
|
||||
put_readable_string_8_noexception (s: READABLE_STRING_8)
|
||||
put_string_8_noexception (s: READABLE_STRING_8)
|
||||
-- Write readable string `s' to socket.
|
||||
-- No exception raised!
|
||||
local
|
||||
@@ -230,7 +244,7 @@ feature -- Status report
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
@@ -6,7 +6,7 @@ note
|
||||
]"
|
||||
|
||||
deferred class
|
||||
HTTPD_STREAM_SOCKET_EXT
|
||||
HTTP_STREAM_SOCKET_EXT
|
||||
|
||||
feature {NONE} -- No-Exception network operation
|
||||
|
||||
18
library/network/http_network/src/no_ssl/http_secure_helper.e
Normal file
18
library/network/http_network/src/no_ssl/http_secure_helper.e
Normal file
@@ -0,0 +1,18 @@
|
||||
note
|
||||
description: "[
|
||||
Interface helping using SSL.
|
||||
For now, mainly for `is_secure_connection_supported' to indicate if current project is compiled with SSL support.
|
||||
i.e compiled with EiffelNet-SSL library.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_SECURE_HELPER
|
||||
|
||||
feature -- Status
|
||||
|
||||
is_secure_connection_supported: BOOLEAN = False
|
||||
-- Is Current system compiled with EiffelNet-SSL support?
|
||||
|
||||
end
|
||||
@@ -0,0 +1,69 @@
|
||||
note
|
||||
description: "[
|
||||
A fake SSL network stream socket... when SSL is disabled at compilation time.
|
||||
Its behavior is similar to HTTP_STREAM_SOCKET.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_STREAM_SECURE_SOCKET
|
||||
|
||||
inherit
|
||||
HTTP_STREAM_SOCKET
|
||||
|
||||
create
|
||||
make, make_empty,
|
||||
make_client_by_port, make_client_by_address_and_port,
|
||||
make_server_by_port, make_server_by_address_and_port, make_loopback_server_by_port
|
||||
|
||||
create {HTTP_STREAM_SECURE_SOCKET}
|
||||
make_from_descriptor_and_address
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_certificate_file_path (a_crt_filename: PATH)
|
||||
do
|
||||
end
|
||||
|
||||
set_key_file_path (a_key_filename: PATH)
|
||||
do
|
||||
end
|
||||
feature -- SSL Helpers
|
||||
|
||||
set_secure_protocol (v: NATURAL)
|
||||
do
|
||||
end
|
||||
|
||||
set_secure_protocol_to_ssl_2_or_3
|
||||
-- Set `ssl_protocol' with `Ssl_23'.
|
||||
do
|
||||
end
|
||||
|
||||
set_secure_protocol_to_tls_1_0
|
||||
-- Set `ssl_protocol' with `Tls_1_0'.
|
||||
do
|
||||
end
|
||||
|
||||
set_secure_protocol_to_tls_1_1
|
||||
-- Set `ssl_protocol' with `Tls_1_1'.
|
||||
do
|
||||
end
|
||||
|
||||
set_secure_protocol_to_tls_1_2
|
||||
-- Set `ssl_protocol' with `Tls_1_2'.
|
||||
do
|
||||
end
|
||||
|
||||
set_secure_protocol_to_dtls_1_0
|
||||
-- Set `ssl_protocol' with `Dtls_1_0'.
|
||||
do
|
||||
end
|
||||
|
||||
invariant
|
||||
secure_connection_not_supported: not is_secure_connection_supported -- Current is a Fake SSL interface!
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
end
|
||||
18
library/network/http_network/src/ssl/http_secure_helper.e
Normal file
18
library/network/http_network/src/ssl/http_secure_helper.e
Normal file
@@ -0,0 +1,18 @@
|
||||
note
|
||||
description: "[
|
||||
Interface helping using SSL.
|
||||
For now, mainly for `is_secure_connection_supported' to indicate if current project is compiled with SSL support.
|
||||
i.e compiled with EiffelNet-SSL library.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_SECURE_HELPER
|
||||
|
||||
feature -- Status
|
||||
|
||||
is_secure_connection_supported: BOOLEAN = True
|
||||
-- Is Current system compiled with EiffelNet-SSL support?
|
||||
|
||||
end
|
||||
@@ -4,10 +4,10 @@ note
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTPD_STREAM_SSL_SOCKET
|
||||
HTTP_STREAM_SECURE_SOCKET
|
||||
|
||||
inherit
|
||||
HTTPD_STREAM_SOCKET
|
||||
HTTP_STREAM_SOCKET
|
||||
undefine
|
||||
make_empty, make_from_descriptor_and_address,
|
||||
error_number,
|
||||
@@ -20,6 +20,7 @@ inherit
|
||||
connect, shutdown,
|
||||
do_accept
|
||||
redefine
|
||||
is_secure_connection_supported,
|
||||
put_managed_pointer,
|
||||
read_stream_noexception,
|
||||
read_into_pointer_noexception,
|
||||
@@ -31,7 +32,7 @@ inherit
|
||||
put_managed_pointer -- Redefine to allow support of compiler before 16.11.
|
||||
end
|
||||
|
||||
HTTPD_STREAM_SSL_SOCKET_EXT
|
||||
HTTP_STREAM_SECURE_SOCKET_EXT
|
||||
|
||||
create
|
||||
make, make_empty,
|
||||
@@ -41,6 +42,48 @@ create
|
||||
create {SSL_NETWORK_STREAM_SOCKET}
|
||||
make_from_descriptor_and_address
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_secure_connection_supported: BOOLEAN = True
|
||||
-- SSL supported?
|
||||
|
||||
feature -- Secure connection Helpers
|
||||
|
||||
set_secure_protocol (v: NATURAL)
|
||||
do
|
||||
set_tls_protocol (v)
|
||||
end
|
||||
|
||||
set_secure_protocol_to_ssl_2_or_3
|
||||
-- Set `ssl_protocol' with `Ssl_23'.
|
||||
do
|
||||
set_secure_protocol ({SSL_PROTOCOL}.Ssl_23)
|
||||
end
|
||||
|
||||
set_secure_protocol_to_tls_1_0
|
||||
-- Set `ssl_protocol' with `Tls_1_0'.
|
||||
do
|
||||
set_secure_protocol ({SSL_PROTOCOL}.Tls_1_0)
|
||||
end
|
||||
|
||||
set_secure_protocol_to_tls_1_1
|
||||
-- Set `ssl_protocol' with `Tls_1_1'.
|
||||
do
|
||||
set_secure_protocol ({SSL_PROTOCOL}.Tls_1_1)
|
||||
end
|
||||
|
||||
set_secure_protocol_to_tls_1_2
|
||||
-- Set `ssl_protocol' with `Tls_1_2'.
|
||||
do
|
||||
set_secure_protocol ({SSL_PROTOCOL}.Tls_1_2)
|
||||
end
|
||||
|
||||
set_secure_protocol_to_dtls_1_0
|
||||
-- Set `ssl_protocol' with `Dtls_1_0'.
|
||||
do
|
||||
set_secure_protocol ({SSL_PROTOCOL}.Dtls_1_0)
|
||||
end
|
||||
|
||||
feature -- Input
|
||||
|
||||
read_stream_noexception (nb_char: INTEGER)
|
||||
@@ -91,6 +134,8 @@ feature {NONE} -- Input
|
||||
l_last_read := l_ssl.read (p + start_pos + l_read, nb_bytes - l_read)
|
||||
if l_last_read >= 0 then
|
||||
l_read := l_read + l_last_read
|
||||
else
|
||||
socket_error := "Secure network error!"
|
||||
end
|
||||
end
|
||||
bytes_read := l_read
|
||||
@@ -105,7 +150,7 @@ feature -- Output
|
||||
-- Put data of length `nb_bytes' pointed by `start_pos' index in `p' at
|
||||
-- current position.
|
||||
do
|
||||
Precursor {HTTPD_STREAM_SOCKET} (p, start_pos, nb_bytes)
|
||||
Precursor {HTTP_STREAM_SOCKET} (p, start_pos, nb_bytes)
|
||||
end
|
||||
|
||||
put_pointer_content_noexception (a_pointer: POINTER; a_offset, a_byte_count: INTEGER)
|
||||
@@ -130,18 +175,6 @@ feature -- Output
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_certificate_filenames (a_crt_filename, a_key_filename: detachable READABLE_STRING_GENERAL)
|
||||
do
|
||||
if a_crt_filename /= Void then
|
||||
set_certificate_file_name (a_crt_filename)
|
||||
end
|
||||
if a_key_filename /= Void then
|
||||
set_key_file_name (a_key_filename)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
@@ -1,12 +1,12 @@
|
||||
note
|
||||
description: "[
|
||||
Extension to HTTPD_STREAM_SOCKET to support backward compatibility.
|
||||
Extension to HTTP_STREAM_SOCKET to support backward compatibility.
|
||||
|
||||
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
|
||||
]"
|
||||
|
||||
deferred class
|
||||
HTTPD_STREAM_SSL_SOCKET_EXT
|
||||
HTTP_STREAM_SECURE_SOCKET_EXT
|
||||
|
||||
feature {NONE} -- SSL bridge
|
||||
|
||||
@@ -9,7 +9,7 @@ note
|
||||
]"
|
||||
|
||||
deferred class
|
||||
HTTPD_STREAM_SOCKET_EXT
|
||||
HTTP_STREAM_SOCKET_EXT
|
||||
|
||||
inherit
|
||||
PLATFORM
|
||||
@@ -81,7 +81,7 @@ feature {NONE} -- Externals
|
||||
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 use %"ew_httpd_net.h%""
|
||||
"C inline use %"ew_network.h%""
|
||||
alias
|
||||
"[
|
||||
#ifdef SO_RCVTIMEO
|
||||
@@ -105,7 +105,7 @@ feature {NONE} -- Externals
|
||||
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 use %"ew_httpd_net.h%""
|
||||
"C inline use %"ew_network.h%""
|
||||
alias
|
||||
"[
|
||||
#ifdef SO_RCVTIMEO
|
||||
@@ -131,7 +131,7 @@ feature {NONE} -- No-Exception network operation
|
||||
-- External routine to read a `len' number of characters
|
||||
-- into buffer `buf' from socket `a_fd' with options `flags'.
|
||||
external
|
||||
"C inline use %"ew_httpd_net.h%""
|
||||
"C inline use %"ew_network.h%""
|
||||
alias
|
||||
"[
|
||||
recv((int) $a_fd, (char *) $buf, (int) $len, (int) $flags)
|
||||
@@ -150,7 +150,7 @@ feature {NONE} -- No-Exception network operation
|
||||
-- length `length' to socket `fd'.
|
||||
-- Note: does not raise exception on error, but return error value as Result.
|
||||
external
|
||||
"C inline use %"ew_httpd_net.h%""
|
||||
"C inline use %"ew_network.h%""
|
||||
alias
|
||||
"[
|
||||
send((int) $a_fd, (char *) $buf, (int) $len, (int) 0)
|
||||
@@ -1,12 +1,12 @@
|
||||
note
|
||||
description: "[
|
||||
Extension to HTTPD_STREAM_SOCKET to support backward compatibility.
|
||||
Extension to HTTP_STREAM_SOCKET to support backward compatibility.
|
||||
|
||||
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
|
||||
]"
|
||||
|
||||
deferred class
|
||||
HTTPD_STREAM_SSL_SOCKET_EXT
|
||||
HTTP_STREAM_SECURE_SOCKET_EXT
|
||||
|
||||
feature {NONE} -- SSL bridge
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="..\http\http-safe.ecf" readonly="false"/>
|
||||
<cluster name="conneg" location=".\src\" recursive="true">
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<option warning="true" full_class_checking="true">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="http" location="../http/http.ecf"/>
|
||||
<cluster name="conneg" location=".\src\" recursive="true">
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true">
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<option warning="true" full_class_checking="true" syntax="provisional">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true">
|
||||
|
||||
@@ -508,9 +508,16 @@ feature -- Others
|
||||
|
||||
put_expires (a_seconds: INTEGER)
|
||||
-- Put "Expires" header to `a_seconds' seconds
|
||||
-- and also "Cache-Control: max-age=.." .
|
||||
-- To be supported by most browser.
|
||||
local
|
||||
dt: DATE_TIME
|
||||
do
|
||||
put_expires_string (a_seconds.out)
|
||||
end
|
||||
create dt.make_now_utc
|
||||
dt.second_add (a_seconds)
|
||||
put_expires_date (dt)
|
||||
put_cache_control ("max-age=" + a_seconds.out)
|
||||
end
|
||||
|
||||
put_expires_string (a_expires: STRING)
|
||||
-- Put "Expires" header with `a_expires' string value
|
||||
@@ -544,6 +551,26 @@ feature -- Others
|
||||
put_pragma ("no-cache")
|
||||
end
|
||||
|
||||
feature -- Connection
|
||||
|
||||
put_connection (a_conn: READABLE_STRING_8)
|
||||
-- Put "Connection" header with `a_conn' value.
|
||||
do
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_connection, a_conn)
|
||||
end
|
||||
|
||||
put_connection_keep_alive
|
||||
-- Put "Connection" header with "keep-alive".
|
||||
do
|
||||
put_connection ("keep-alive")
|
||||
end
|
||||
|
||||
put_connection_close
|
||||
-- Put "Connection" header with "close".
|
||||
do
|
||||
put_connection ("close")
|
||||
end
|
||||
|
||||
feature -- Redirection
|
||||
|
||||
put_location (a_uri: READABLE_STRING_8)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
note
|
||||
description: "ws_client application root class"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Run application.
|
||||
local
|
||||
ws_client: EXAMPLE_WS_CLIENT
|
||||
do
|
||||
-- create ws_client.make_with_port ("wss://echo.websocket.org", 443, Void)
|
||||
-- if ws_client.is_ssl_supported and ws_client.is_tunneled then
|
||||
-- ws_client.set_ssl_certificate_file ("ca.crt")
|
||||
-- ws_client.set_ssl_key_file ("ca.key")
|
||||
-- else
|
||||
-- -- Not supported!
|
||||
-- end
|
||||
|
||||
create ws_client.make_with_port ("ws://echo.websocket.org", 80, Void)
|
||||
-- create ws_client.make_with_port ("ws://127.0.0.1", 9090, Void)
|
||||
ws_client.launch
|
||||
ws_client.join_all
|
||||
|
||||
execution_environment.sleep (5_000_000)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,78 @@
|
||||
note
|
||||
description: "Summary description for {EXAMPLE_WS_CLIENT}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
EXAMPLE_WS_CLIENT
|
||||
|
||||
inherit
|
||||
WEB_SOCKET_CLIENT
|
||||
|
||||
create
|
||||
make, make_with_port
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (a_uri: STRING; a_protocols: detachable LIST [STRING])
|
||||
do
|
||||
initialize (a_uri, a_protocols)
|
||||
create implementation.make (create {WEB_SOCKET_NULL_CLIENT}, a_uri)
|
||||
end
|
||||
|
||||
make_with_port (a_uri: STRING; a_port: INTEGER; a_protocols: detachable LIST [STRING])
|
||||
do
|
||||
initialize_with_port (a_uri, a_port, a_protocols)
|
||||
create implementation.make (create {WEB_SOCKET_NULL_CLIENT}, a_uri)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
count: INTEGER
|
||||
|
||||
feature -- Event
|
||||
|
||||
on_open (a_message: STRING)
|
||||
do
|
||||
print (a_message)
|
||||
print ("%NProtocol:" + protocol)
|
||||
on_text_message (a_message)
|
||||
end
|
||||
|
||||
on_text_message (a_message: STRING)
|
||||
local
|
||||
l_message: STRING
|
||||
do
|
||||
if count <= 10 then
|
||||
print ("%NMessage is %"" + a_message + "%".%N")
|
||||
print ("%NCount:" + count.out)
|
||||
create l_message.make_empty
|
||||
l_message.append ("mesg#" + count.out)
|
||||
send (l_message)
|
||||
count := count + 1
|
||||
else -- Send close initiated by the client
|
||||
close (1001)
|
||||
end
|
||||
end
|
||||
|
||||
on_binary_message (a_message: STRING)
|
||||
do
|
||||
send_binary (a_message)
|
||||
end
|
||||
|
||||
on_close (a_code: INTEGER; a_reason: STRING)
|
||||
do
|
||||
ready_state.set_state ({WEB_SOCKET_READY_STATE}.closed)
|
||||
end
|
||||
|
||||
on_error (a_error: STRING)
|
||||
do
|
||||
end
|
||||
|
||||
connection: like socket
|
||||
do
|
||||
Result := socket
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="ws_client" uuid="AF6EDC56-D7B4-4E1F-A62B-40EBED3D93DF">
|
||||
<target name="common">
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" is_obsolete_routine_type="true" void_safety="transitional">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="web_socket_client" location="..\..\web_socket_client-safe.ecf" readonly="false"/>
|
||||
<cluster name="ws_client" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="ws_client" extends="common">
|
||||
<root class="APPLICATION" feature="make"/>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
</target>
|
||||
<target name="ws_client_ssl" extends="ws_client">
|
||||
<variable name="ssl_enabled" value="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,29 @@
|
||||
note
|
||||
description: "[
|
||||
API to perform actions like opening and closing the connection, sending and receiving messages, and listening
|
||||
for events triggered by the server
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WEB_SOCKET_CLIENT
|
||||
|
||||
inherit
|
||||
WEB_SOCKET_CLIENT_I
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_secure_connection_supported: BOOLEAN = False
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_socket (a_port: INTEGER; a_host: STRING): HTTP_STREAM_SOCKET
|
||||
do
|
||||
if is_secure then
|
||||
check is_secure_connection_supported: False end
|
||||
end
|
||||
create {HTTP_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
|
||||
end
|
||||
|
||||
end
|
||||
54
library/network/websocket/client/src/ssl/web_socket_client.e
Normal file
54
library/network/websocket/client/src/ssl/web_socket_client.e
Normal file
@@ -0,0 +1,54 @@
|
||||
note
|
||||
description: "[
|
||||
API to perform actions like opening and closing the connection, sending and receiving messages, and listening
|
||||
for events triggered by the server
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WEB_SOCKET_CLIENT
|
||||
|
||||
inherit
|
||||
WEB_SOCKET_CLIENT_I
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_secure_connection_supported: BOOLEAN = True
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_socket (a_port: INTEGER; a_host: STRING): HTTP_STREAM_SOCKET
|
||||
local
|
||||
l_secure: HTTP_STREAM_SECURE_SOCKET
|
||||
do
|
||||
if is_secure then
|
||||
create l_secure.make_client_by_port (a_port, a_host)
|
||||
Result := l_secure
|
||||
if attached secure_protocol as l_prot then
|
||||
if l_prot.is_case_insensitive_equal ("ssl_2_3") then
|
||||
l_secure.set_secure_protocol_to_ssl_2_or_3
|
||||
elseif l_prot.is_case_insensitive_equal ("tls_1_0") then
|
||||
l_secure.set_secure_protocol_to_tls_1_0
|
||||
elseif l_prot.is_case_insensitive_equal ("tls_1_1") then
|
||||
l_secure.set_secure_protocol_to_tls_1_1
|
||||
elseif l_prot.is_case_insensitive_equal ("tls_1_2") then
|
||||
l_secure.set_secure_protocol_to_tls_1_2
|
||||
elseif l_prot.is_case_insensitive_equal ("dtls_1_0") then
|
||||
l_secure.set_secure_protocol_to_dtls_1_0
|
||||
else -- Default
|
||||
l_secure.set_secure_protocol_to_tls_1_2
|
||||
end
|
||||
end
|
||||
if attached secure_certificate_file as c then
|
||||
l_secure.set_certificate_file_path (c)
|
||||
end
|
||||
if attached secure_certificate_key_file as k then
|
||||
l_secure.set_key_file_path (k)
|
||||
end
|
||||
else
|
||||
create {HTTP_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
99
library/network/websocket/client/src/web_socket.e
Normal file
99
library/network/websocket/client/src/web_socket.e
Normal file
@@ -0,0 +1,99 @@
|
||||
note
|
||||
description: "[
|
||||
API to perform actions like opening and closing the connection, sending and receiving messages, and listening
|
||||
for events triggered by the server
|
||||
]"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WEB_SOCKET
|
||||
|
||||
inherit
|
||||
WEB_SOCKET_CONSTANTS
|
||||
|
||||
feature -- Access
|
||||
|
||||
uri: READABLE_STRING_GENERAL
|
||||
-- WebSocket Protocol defines two URI schemes, ws and wss for
|
||||
-- unencrypted and encrypted traffic between the client and the server.
|
||||
|
||||
port: INTEGER
|
||||
-- Current WebSocket protocol.
|
||||
|
||||
ws_port_default: INTEGER = 80
|
||||
-- WebSocket Protocol uses port 80 for regular WebSocket connections.
|
||||
|
||||
wss_port_default: INTEGER = 443
|
||||
-- WebSocket connections tunneled over Transport Layer Security (TLS) use port 443.
|
||||
|
||||
protocols: detachable LIST [STRING]
|
||||
-- List of protocol names that the client supports.
|
||||
|
||||
protocol: STRING
|
||||
-- Client-Server protocol selected.
|
||||
-- Has the result fo protocol negotiation between client and the server
|
||||
-- By default it's an empty string.
|
||||
|
||||
is_secure: BOOLEAN
|
||||
-- Is the current connection tunneled over TLS/SSL?
|
||||
local
|
||||
l_uri: STRING
|
||||
do
|
||||
l_uri := uri.as_lower.as_string_8
|
||||
Result := l_uri.starts_with ("wss") -- TODO extract to ws_constants.
|
||||
end
|
||||
|
||||
ready_state: WEB_SOCKET_READY_STATE
|
||||
-- Connection state
|
||||
|
||||
feature -- Methods
|
||||
|
||||
send (a_message: STRING)
|
||||
-- Send a message `a_message'
|
||||
require
|
||||
is_open: ready_state.is_open
|
||||
deferred
|
||||
end
|
||||
|
||||
close (a_id: INTEGER)
|
||||
-- Close a websocket connection with a close id : `a_id'
|
||||
deferred
|
||||
ensure
|
||||
is_closed: ready_state.is_closed
|
||||
end
|
||||
|
||||
close_with_description (a_id: INTEGER; a_description: READABLE_STRING_GENERAL)
|
||||
-- Close a websocket connection with a close id : `a_id' and a description `a_description'
|
||||
deferred
|
||||
ensure
|
||||
is_closed: ready_state.is_closed
|
||||
end
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
set_protocol (a_protocol: STRING)
|
||||
-- Set `protocol' with `a_protocol'
|
||||
do
|
||||
protocol := a_protocol
|
||||
ensure
|
||||
protocol_set: protocol = a_protocol
|
||||
end
|
||||
|
||||
set_protocols (a_protocols: detachable ITERABLE [STRING])
|
||||
local
|
||||
l_protocols: LIST [STRING]
|
||||
do
|
||||
if a_protocols /= Void then
|
||||
create {ARRAYED_LIST [STRING]} l_protocols.make (0)
|
||||
across
|
||||
a_protocols as ic
|
||||
loop
|
||||
l_protocols.force (ic.item)
|
||||
end
|
||||
protocols := l_protocols
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
603
library/network/websocket/client/src/web_socket_client_i.e
Normal file
603
library/network/websocket/client/src/web_socket_client_i.e
Normal file
@@ -0,0 +1,603 @@
|
||||
note
|
||||
description: "[
|
||||
API to perform actions like opening and closing the connection, sending and receiving messages, and listening
|
||||
for events triggered by the server
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WEB_SOCKET_CLIENT_I
|
||||
|
||||
inherit
|
||||
WEB_SOCKET_SUBSCRIBER
|
||||
redefine
|
||||
on_websocket_error,
|
||||
on_websocket_text_message,
|
||||
on_websocket_binary_message,
|
||||
on_websocket_close,
|
||||
on_websocket_open,
|
||||
on_websocket_ping,
|
||||
on_websocket_pong
|
||||
end
|
||||
|
||||
WEB_SOCKET
|
||||
|
||||
THREAD
|
||||
rename
|
||||
make as thread_make
|
||||
end
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
initialize (a_uri: READABLE_STRING_GENERAL; a_protocols: detachable ITERABLE [STRING])
|
||||
-- Initialize websocket client
|
||||
require
|
||||
is_valid_uri: is_valid_uri (a_uri)
|
||||
do
|
||||
thread_make
|
||||
uri := a_uri
|
||||
set_default_port
|
||||
create protocol.make_empty
|
||||
set_protocols (a_protocols)
|
||||
create ready_state.make
|
||||
socket := new_socket (port, host)
|
||||
create server_handshake.make
|
||||
end
|
||||
|
||||
initialize_with_port (a_uri: READABLE_STRING_GENERAL; a_port: INTEGER; a_protocols: detachable ITERABLE [STRING])
|
||||
-- Initialize websocket client
|
||||
require
|
||||
is_valid_uri: is_valid_uri (a_uri)
|
||||
do
|
||||
thread_make
|
||||
uri := a_uri
|
||||
port := a_port
|
||||
create protocol.make_empty
|
||||
set_protocols (a_protocols)
|
||||
create ready_state.make
|
||||
socket := new_socket (port, host)
|
||||
create server_handshake.make
|
||||
end
|
||||
|
||||
initialize_with_host_port_and_path (a_host: READABLE_STRING_GENERAL; a_port: INTEGER; a_path: READABLE_STRING_GENERAL)
|
||||
require
|
||||
is_valid_uri: is_valid_uri (a_host)
|
||||
do
|
||||
thread_make
|
||||
uri := a_host + ":" + a_port.out + a_path
|
||||
port := a_port
|
||||
create protocol.make_empty
|
||||
-- set_protocols (a_protocols)
|
||||
create ready_state.make
|
||||
socket := new_socket (port, host)
|
||||
create server_handshake.make
|
||||
end
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_socket (a_port: INTEGER; a_host: STRING): HTTP_STREAM_SOCKET
|
||||
-- New socket for port `a_port' on host `a_host'.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
socket: HTTP_STREAM_SOCKET
|
||||
-- Socket
|
||||
|
||||
has_error: BOOLEAN
|
||||
do
|
||||
Result := implementation.has_error
|
||||
end
|
||||
|
||||
is_server_hanshake_accepted: BOOLEAN
|
||||
|
||||
is_valid_uri (a_uri: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Is `a_uri' a valid URI?
|
||||
local
|
||||
l_uri: URI
|
||||
do
|
||||
create l_uri.make_from_string (a_uri.as_string_8)
|
||||
Result := l_uri.is_valid
|
||||
end
|
||||
|
||||
server_handshake: WEB_SOCKET_HANDSHAKE_DATA
|
||||
-- Handshake data received from the server
|
||||
|
||||
feature -- Access: secure
|
||||
|
||||
is_secure_connection_supported: BOOLEAN
|
||||
-- Is SSL supported?
|
||||
deferred
|
||||
end
|
||||
|
||||
secure_protocol: detachable READABLE_STRING_GENERAL
|
||||
-- SSL protocol , if `is_secure_connection_supported'.
|
||||
|
||||
secure_certificate_file: detachable PATH
|
||||
-- SSL certificate file , if `is_secure_connection_supported'.
|
||||
|
||||
secure_certificate_key_file: detachable PATH
|
||||
-- SSL key file , if `is_secure_connection_supported'.
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_secure_protocol (a_prot: like secure_protocol)
|
||||
do
|
||||
secure_protocol := a_prot
|
||||
end
|
||||
|
||||
set_secure_certificate_file (p: detachable PATH)
|
||||
-- Set SSL certificate from file at `p'.
|
||||
do
|
||||
secure_certificate_file := p
|
||||
end
|
||||
|
||||
set_secure_certificate_key_file (p: detachable PATH)
|
||||
-- Set SSL key from file at `p'.
|
||||
do
|
||||
secure_certificate_key_file := p
|
||||
end
|
||||
|
||||
feature -- Events API
|
||||
|
||||
on_open (a_message: STRING)
|
||||
deferred
|
||||
end
|
||||
|
||||
on_text_message (a_message: STRING)
|
||||
deferred
|
||||
end
|
||||
|
||||
on_binary_message (a_message: STRING)
|
||||
deferred
|
||||
end
|
||||
|
||||
on_close (a_code: INTEGER; a_reason: STRING)
|
||||
deferred
|
||||
end
|
||||
|
||||
on_error (a_error: STRING)
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Subscriber Events
|
||||
|
||||
on_websocket_handshake (a_request: STRING)
|
||||
-- Send handshake message
|
||||
do
|
||||
socket.put_string (a_request)
|
||||
end
|
||||
|
||||
on_websocket_text_message (a_message: STRING)
|
||||
do
|
||||
on_text_message (a_message)
|
||||
end
|
||||
|
||||
on_websocket_binary_message (a_message: STRING)
|
||||
do
|
||||
on_binary_message (a_message)
|
||||
end
|
||||
|
||||
on_websocket_open (a_message: STRING)
|
||||
do
|
||||
on_open (a_message)
|
||||
end
|
||||
|
||||
on_websocket_close (a_message: STRING)
|
||||
do
|
||||
close_with_description (1000, a_message)
|
||||
on_close (1000, a_message)
|
||||
end
|
||||
|
||||
on_websocket_error (a_error: STRING)
|
||||
do
|
||||
on_error (a_error)
|
||||
close_with_description (1002,"")
|
||||
end
|
||||
|
||||
on_websocket_ping (a_message: STRING)
|
||||
do
|
||||
do_send (10, a_message)
|
||||
end
|
||||
|
||||
on_websocket_pong (a_message: STRING)
|
||||
do
|
||||
-- do_send (9, a_message)
|
||||
end
|
||||
|
||||
feature -- Execute
|
||||
|
||||
execute
|
||||
require else
|
||||
is_socket_valid: socket.exists
|
||||
do
|
||||
set_implementation
|
||||
socket.connect
|
||||
check
|
||||
socket_connected: socket.is_connected
|
||||
end
|
||||
send_handshake
|
||||
receive_handshake
|
||||
if is_server_hanshake_accepted then
|
||||
ready_state.set_state ({WEB_SOCKET_READY_STATE}.open)
|
||||
on_websocket_open ("Open Connection")
|
||||
from
|
||||
until
|
||||
ready_state.is_closed or has_error
|
||||
loop
|
||||
receive
|
||||
end
|
||||
else
|
||||
on_websocket_error ("Server Handshake not accepted")
|
||||
--log(Not connected)
|
||||
socket.close
|
||||
end
|
||||
rescue
|
||||
on_websocket_close ("")
|
||||
socket.close
|
||||
end
|
||||
|
||||
feature -- Methods
|
||||
|
||||
send (a_message: STRING)
|
||||
do
|
||||
do_send (1, a_message)
|
||||
end
|
||||
|
||||
send_binary (a_message: STRING)
|
||||
do
|
||||
do_send (2, a_message)
|
||||
end
|
||||
|
||||
close (a_id: INTEGER)
|
||||
-- Close a websocket connection with a close id : `a_id'
|
||||
do
|
||||
do_send (8, "")
|
||||
ready_state.set_state ({WEB_SOCKET_READY_STATE}.closed)
|
||||
end
|
||||
|
||||
close_with_description (a_id: INTEGER; a_description: READABLE_STRING_GENERAL)
|
||||
-- Close a websocket connection with a close id : `a_id' and a description `a_description'
|
||||
do
|
||||
do_send (8, "")
|
||||
ready_state.set_state ({WEB_SOCKET_READY_STATE}.closed)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
set_implementation
|
||||
do
|
||||
create implementation.make_with_protocols_and_port (Current, host, protocols, port)
|
||||
end
|
||||
|
||||
send_handshake
|
||||
local
|
||||
l_uri: URI
|
||||
l_handshake: STRING
|
||||
l_random: SALT_XOR_SHIFT_64_GENERATOR
|
||||
l_secure_protocol: STRING
|
||||
do
|
||||
create l_uri.make_from_string (uri.as_string_8)
|
||||
create l_handshake.make_empty
|
||||
if l_uri.path.is_empty then
|
||||
l_handshake.append ("GET / HTTP/1.1")
|
||||
l_handshake.append (crlf)
|
||||
elseif l_uri.query = Void then
|
||||
l_handshake.append ("GET " + l_uri.path + " HTTP/1.1")
|
||||
l_handshake.append (crlf)
|
||||
else
|
||||
if attached l_uri.query as l_query then
|
||||
l_handshake.append ("GET " + l_uri.path + "?" + l_query + " HTTP/1.1")
|
||||
l_handshake.append (crlf)
|
||||
end
|
||||
end
|
||||
if attached l_uri.host as l_host then
|
||||
l_handshake.replace_substring_all ("$host", l_host)
|
||||
l_handshake.append ("Host: " + l_host + ":" + port.out)
|
||||
l_handshake.append (crlf)
|
||||
end
|
||||
l_handshake.append_string ("Upgrade: websocket")
|
||||
l_handshake.append (crlf)
|
||||
l_handshake.append_string ("Connection: Upgrade")
|
||||
l_handshake.append (crlf)
|
||||
l_handshake.append_string ("Sec-WebSocket-Key: ")
|
||||
create l_random.make (16)
|
||||
l_handshake.append_string (base64_encode_array (l_random.new_sequence))
|
||||
l_handshake.append (crlf)
|
||||
if attached protocols as l_protocols then
|
||||
create l_secure_protocol.make_empty
|
||||
across
|
||||
l_protocols as c
|
||||
loop
|
||||
l_secure_protocol.append (c.item)
|
||||
l_secure_protocol.append (" ,")
|
||||
end
|
||||
l_secure_protocol.remove_tail (1)
|
||||
l_handshake.append_string ("Sec-WebSocket-Protocol:" + l_secure_protocol)
|
||||
l_handshake.append (crlf)
|
||||
end
|
||||
l_handshake.append_string ("Sec-WebSocket-Version: 13")
|
||||
l_handshake.append (crlf)
|
||||
l_handshake.append (crlf)
|
||||
implementation.start_handshake (l_handshake)
|
||||
end
|
||||
|
||||
receive_handshake
|
||||
do
|
||||
analyze_request_message
|
||||
if server_handshake.request_header.has_substring ("HTTP/1.1 101") and then attached server_handshake.request_header_map.item ("Upgrade") as l_upgrade_key and then -- Upgrade header must be present with value websocket
|
||||
l_upgrade_key.is_case_insensitive_equal ("websocket") and then attached server_handshake.request_header_map.item ("Connection") as l_connection_key and then -- Connection header must be present with value Upgrade
|
||||
l_connection_key.has_substring ("Upgrade")
|
||||
then
|
||||
is_server_hanshake_accepted := True
|
||||
if attached server_handshake.request_header_map.item ("Sec-WebSocet-Protocol") as l_protocol then
|
||||
set_protocol (l_protocol)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
receive
|
||||
do
|
||||
implementation.receive
|
||||
end
|
||||
|
||||
set_default_port
|
||||
do
|
||||
if is_secure then
|
||||
port := wss_port_default
|
||||
else
|
||||
port := ws_port_default
|
||||
end
|
||||
end
|
||||
|
||||
client_handshake_required_template: STRING = "[
|
||||
GET $resource HTTP/1.1
|
||||
Host: $host
|
||||
Upgrade: websocket
|
||||
Connection: Upgrade
|
||||
Sec-WebSocket-Key: $key
|
||||
Sec-WebSocket-Version: 13
|
||||
]"
|
||||
|
||||
base64_encode_array (a_sequence: ARRAY [NATURAL_8]): STRING_8
|
||||
-- Encode a byte array `a_sequence' into Base64 notation.
|
||||
local
|
||||
l_result: STRING
|
||||
l_base_64: BASE64
|
||||
do
|
||||
create l_result.make_empty
|
||||
across
|
||||
a_sequence as i
|
||||
loop
|
||||
l_result.append_character (i.item.to_character_8)
|
||||
end
|
||||
create l_base_64
|
||||
Result := l_base_64.encoded_string (l_result)
|
||||
end
|
||||
|
||||
host: STRING
|
||||
local
|
||||
l_uri: URI
|
||||
do
|
||||
create Result.make_empty
|
||||
create l_uri.make_from_string (uri.as_string_8)
|
||||
if attached l_uri.host as l_host then
|
||||
Result := l_host
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Parse Request line
|
||||
|
||||
analyze_request_message
|
||||
-- Analyze message extracted from `socket' as HTTP request
|
||||
require
|
||||
input_readable: socket /= Void and then socket.is_open_read
|
||||
local
|
||||
end_of_stream: BOOLEAN
|
||||
pos, n: INTEGER
|
||||
line: detachable STRING
|
||||
k, val: STRING
|
||||
txt: STRING
|
||||
do
|
||||
create txt.make (64)
|
||||
server_handshake.set_request_header (txt)
|
||||
if attached next_line as l_request_line and then not l_request_line.is_empty then
|
||||
txt.append (l_request_line)
|
||||
txt.append_character ('%N')
|
||||
else
|
||||
server_handshake.mark_error
|
||||
end
|
||||
-- l_is_verbose := is_verbose
|
||||
if not server_handshake.has_error then -- or l_is_verbose then
|
||||
-- if `is_verbose' we can try to print the request, even if it is a bad HTTP request
|
||||
from
|
||||
line := next_line
|
||||
until
|
||||
line = Void or end_of_stream
|
||||
loop
|
||||
n := line.count
|
||||
-- if l_is_verbose then
|
||||
-- log (line)
|
||||
-- end
|
||||
pos := line.index_of (':', 1)
|
||||
if pos > 0 then
|
||||
k := line.substring (1, pos - 1)
|
||||
if line [pos + 1].is_space then
|
||||
pos := pos + 1
|
||||
end
|
||||
if line [n] = '%R' then
|
||||
n := n - 1
|
||||
end
|
||||
val := line.substring (pos + 1, n)
|
||||
server_handshake.put_header (k, val)
|
||||
end
|
||||
txt.append (line)
|
||||
txt.append_character ('%N')
|
||||
if line.is_empty or else line [1] = '%R' then
|
||||
end_of_stream := True
|
||||
else
|
||||
line := next_line
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
next_line: detachable STRING
|
||||
-- Next line fetched from `socket' is available.
|
||||
require
|
||||
is_readable: socket.is_open_read
|
||||
do
|
||||
if socket.socket_ok then
|
||||
socket.read_line_thread_aware
|
||||
Result := socket.last_string
|
||||
end
|
||||
end
|
||||
|
||||
feature -- {WEB_SOCKET_CLIENT}
|
||||
|
||||
-- do_send (a_opcode: NATURAL_32; a_message: STRING)
|
||||
-- local
|
||||
-- l_chunks: INTEGER
|
||||
-- i: INTEGER
|
||||
-- l_index: INTEGER
|
||||
-- l_chunk_size: INTEGER
|
||||
-- l_key: STRING
|
||||
-- l_message: STRING
|
||||
-- l_frame : STRING
|
||||
-- do
|
||||
-- print ("%NMessage count:" + a_message.count.out)
|
||||
-- create l_frame.make_empty
|
||||
-- create l_message.make_empty
|
||||
-- if a_message.count > 65535 then
|
||||
-- --!Improve. this code need to be checked.
|
||||
-- print("%N Case:1")
|
||||
-- l_frame.append_code ((0x80 | a_opcode).to_natural_32)
|
||||
-- l_frame.append_code ((0x80 | 127).to_natural_32)
|
||||
|
||||
-- l_frame.append_code (0x0)
|
||||
-- l_frame.append_code (0x0)
|
||||
-- l_frame.append_code (0x0)
|
||||
-- l_frame.append_code (0x0)
|
||||
-- l_frame.append_code (((a_message.count) |>> 32).to_character_8.code.as_natural_32)
|
||||
-- l_frame.append_code (((a_message.count) |>> 16).to_character_8.code.as_natural_32)
|
||||
-- l_frame.append_code (((a_message.count ) |>> 8).to_character_8.code.as_natural_32)
|
||||
-- l_frame.append_code ((a_message.count).to_character_8.code.as_natural_32)
|
||||
-- elseif a_message.count > 125 then
|
||||
-- print("%N Case:2")
|
||||
-- print ("Message count:" + a_message.count.out)
|
||||
-- l_frame.append_code ((0x80 | a_opcode).to_natural_32)
|
||||
-- l_frame.append_code ((0x80 | 126).to_natural_32)
|
||||
-- l_frame.append_code (((a_message.count ) |>> 8).as_natural_32)
|
||||
-- l_frame.append_code ((a_message.count).to_character_8.code.as_natural_32)
|
||||
-- print ("%NHeaderMessage:" + l_frame)
|
||||
-- else
|
||||
-- print("%N Case:3")
|
||||
-- print ("Message count:" + a_message.count.out)
|
||||
-- l_frame.append_code ((0x80 | a_opcode).to_natural_32)
|
||||
-- l_frame.append_code ((0x80 | a_message.count).to_natural_32)
|
||||
-- end
|
||||
--
|
||||
-- l_key := new_key
|
||||
-- l_frame.append (l_key.substring (1, 4))
|
||||
-- l_message := implementation.unmmask (a_message, l_key.substring (1, 4))
|
||||
-- l_frame.append (l_message)
|
||||
-- socket.send_message (l_frame)
|
||||
-- end
|
||||
|
||||
do_send (a_opcode:INTEGER; a_message: READABLE_STRING_8)
|
||||
local
|
||||
i: INTEGER
|
||||
l_chunk_size: INTEGER
|
||||
l_chunk: READABLE_STRING_8
|
||||
l_header_message: STRING
|
||||
l_message_count: INTEGER
|
||||
n: NATURAL_64
|
||||
retried: BOOLEAN
|
||||
l_message : STRING
|
||||
l_key: STRING
|
||||
do
|
||||
if not retried then
|
||||
create l_header_message.make_empty
|
||||
l_header_message.append_code ((0x80 | a_opcode).to_natural_32)
|
||||
l_message_count := a_message.count
|
||||
n := l_message_count.to_natural_64
|
||||
if l_message_count > 0xffff then
|
||||
--! Improve. this code needs to be checked.
|
||||
l_header_message.append_code ((0x80 | 127).to_natural_32)
|
||||
l_header_message.append_character ((n |>> 56).to_character_8)
|
||||
l_header_message.append_character ((n |>> 48).to_character_8)
|
||||
l_header_message.append_character ((n |>> 40).to_character_8)
|
||||
l_header_message.append_character ((n |>> 32).to_character_8)
|
||||
l_header_message.append_character ((n |>> 24).to_character_8)
|
||||
l_header_message.append_character ((n |>> 16).to_character_8)
|
||||
l_header_message.append_character ((n |>> 8).to_character_8)
|
||||
l_header_message.append_character ( n.to_character_8)
|
||||
elseif l_message_count > 125 then
|
||||
l_header_message.append_code ((0x80 | 126).to_natural_32)
|
||||
l_header_message.append_code ((n |>> 8).as_natural_32)
|
||||
l_header_message.append_character (n.to_character_8)
|
||||
else
|
||||
l_header_message.append_code ((0x80 | n).as_natural_32)
|
||||
end
|
||||
|
||||
l_key := new_key
|
||||
l_header_message.append (l_key.substring (1, 4))
|
||||
|
||||
socket.put_string (l_header_message)
|
||||
|
||||
l_message := implementation.unmmask (a_message, l_key.substring (1, 4))
|
||||
|
||||
|
||||
l_chunk_size := 16_384 -- 16K
|
||||
if l_message_count < l_chunk_size then
|
||||
socket.put_string (l_message)
|
||||
else
|
||||
from
|
||||
i := 0
|
||||
until
|
||||
l_chunk_size = 0
|
||||
loop
|
||||
debug ("ws")
|
||||
print ("Sending chunk " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
|
||||
end
|
||||
l_chunk := l_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
|
||||
socket.put_string (l_chunk)
|
||||
if l_chunk.count < l_chunk_size then
|
||||
l_chunk_size := 0
|
||||
end
|
||||
i := i + l_chunk_size
|
||||
end
|
||||
debug ("ws")
|
||||
print ("Sending chunk done%N")
|
||||
end
|
||||
end
|
||||
else
|
||||
-- FIXME: what should be done on rescue?
|
||||
end
|
||||
rescue
|
||||
retried := True
|
||||
io.put_string ("Internal error in " + generator + ".do_send (conn, a_opcode=" + a_opcode.out + ", a_message) !%N")
|
||||
retry
|
||||
end
|
||||
|
||||
new_key: STRING
|
||||
local
|
||||
l_random: SALT_XOR_SHIFT_64_GENERATOR
|
||||
do
|
||||
create Result.make_empty
|
||||
create l_random.make (4)
|
||||
across
|
||||
l_random.new_sequence as i
|
||||
loop
|
||||
Result.append_integer (i.item)
|
||||
end
|
||||
end
|
||||
|
||||
implementation: WEB_SOCKET_IMPL
|
||||
-- Web Socket implementation
|
||||
|
||||
crlf: STRING = "%R%N"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,90 @@
|
||||
note
|
||||
description: "Summary description for {WEB_SOCKET_HANDSHAKE_DATA}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WEB_SOCKET_HANDSHAKE_DATA
|
||||
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
reset
|
||||
end
|
||||
|
||||
|
||||
reset
|
||||
do
|
||||
has_error := False
|
||||
version := Void
|
||||
create method.make_empty
|
||||
create uri.make_empty
|
||||
create request_header.make_empty
|
||||
create request_header_map.make (10)
|
||||
end
|
||||
|
||||
|
||||
feature -- Access
|
||||
|
||||
|
||||
request_header: STRING
|
||||
-- Header' source
|
||||
|
||||
request_header_map: HASH_TABLE [STRING, STRING]
|
||||
-- Contains key:value of the header
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Error occurred during `analyze_request_message'
|
||||
|
||||
method: STRING
|
||||
-- http verb
|
||||
|
||||
uri: STRING
|
||||
-- http endpoint
|
||||
|
||||
version: detachable STRING
|
||||
-- http_version
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
set_method (a_method: STRING)
|
||||
-- Set `method' with `a_method'
|
||||
do
|
||||
method := a_method
|
||||
ensure
|
||||
method_set: method = a_method
|
||||
end
|
||||
|
||||
|
||||
set_version (a_version: STRING)
|
||||
-- Set `version' with `a_version'
|
||||
do
|
||||
version := a_version
|
||||
ensure
|
||||
version_set: attached version as l_version implies l_version = a_version
|
||||
end
|
||||
|
||||
mark_error
|
||||
do
|
||||
has_error := True
|
||||
end
|
||||
|
||||
set_request_header (a_header: STRING)
|
||||
-- Set `request_header' with `a_header'
|
||||
do
|
||||
request_header := a_header
|
||||
ensure
|
||||
request_header_set: request_header = a_header
|
||||
end
|
||||
|
||||
put_header (a_key: STRING; a_value : STRING)
|
||||
do
|
||||
request_header_map.put (a_value, a_key)
|
||||
end
|
||||
end
|
||||
739
library/network/websocket/client/src/web_socket_impl.e
Normal file
739
library/network/websocket/client/src/web_socket_impl.e
Normal file
@@ -0,0 +1,739 @@
|
||||
note
|
||||
description: "Summary description for {WEB_SOCKET_IMPL}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WEB_SOCKET_IMPL
|
||||
|
||||
inherit
|
||||
|
||||
WEB_SOCKET
|
||||
|
||||
create
|
||||
make, make_with_port, make_with_protocols, make_with_protocols_and_port
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_subscriber: WEB_SOCKET_SUBSCRIBER; a_uri: READABLE_STRING_GENERAL)
|
||||
-- Create a websocket instante with a default port.
|
||||
do
|
||||
reset
|
||||
subscriber := a_subscriber
|
||||
uri := a_uri
|
||||
create protocol.make_empty
|
||||
set_default_port
|
||||
create ready_state.make
|
||||
ensure
|
||||
uri_set: a_uri = uri
|
||||
port_wss: is_secure implies port = wss_port_default
|
||||
port_ws: not is_secure implies port = ws_port_default
|
||||
ready_state_set: ready_state.state = {WEB_SOCKET_READY_STATE}.connecting
|
||||
subscriber_set: subscriber = a_subscriber
|
||||
protocol_set: protocol.is_empty
|
||||
end
|
||||
|
||||
make_with_port (a_subscriber: WEB_SOCKET_SUBSCRIBER; a_uri: READABLE_STRING_GENERAL; a_port: INTEGER)
|
||||
-- Create a websocket instance with port `a_port',
|
||||
do
|
||||
make (a_subscriber, a_uri)
|
||||
port := a_port
|
||||
ensure
|
||||
uri_set: a_uri = uri
|
||||
port_set: port = a_port
|
||||
ready_state_set: ready_state.state = {WEB_SOCKET_READY_STATE}.connecting
|
||||
subscriber_set: subscriber = a_subscriber
|
||||
end
|
||||
|
||||
make_with_protocols (a_subscriber: WEB_SOCKET_SUBSCRIBER; a_uri: READABLE_STRING_GENERAL; a_protocols: detachable LIST [STRING])
|
||||
-- Create a web socket instance with a list of protocols `a_protocols' and default port.
|
||||
do
|
||||
reset
|
||||
subscriber := a_subscriber
|
||||
uri := a_uri
|
||||
create protocol.make_empty
|
||||
protocols := a_protocols
|
||||
set_default_port
|
||||
create ready_state.make
|
||||
ensure
|
||||
uri_set: a_uri = uri
|
||||
port_wss: is_secure implies port = wss_port_default
|
||||
port_ws: not is_secure implies port = ws_port_default
|
||||
protocols_set: protocols = a_protocols
|
||||
ready_state_set: ready_state.state = {WEB_SOCKET_READY_STATE}.connecting
|
||||
subscriber_set: subscriber = a_subscriber
|
||||
protocol_set: protocol.is_empty
|
||||
end
|
||||
|
||||
make_with_protocols_and_port (a_subscriber: WEB_SOCKET_SUBSCRIBER; a_uri: READABLE_STRING_GENERAL; a_protocols: detachable LIST [STRING]; a_port: INTEGER)
|
||||
-- Create a web socket instance with a list of protocols `a_protocols' and port `a_port'.
|
||||
do
|
||||
make_with_protocols (a_subscriber, a_uri, a_protocols)
|
||||
port := a_port
|
||||
ensure
|
||||
uri_set: a_uri = uri
|
||||
protocols_set: protocols = a_protocols
|
||||
port_set: port = a_port
|
||||
ready_state_set: ready_state.state = {WEB_SOCKET_READY_STATE}.connecting
|
||||
subscriber_set: subscriber = a_subscriber
|
||||
end
|
||||
|
||||
reset
|
||||
do
|
||||
has_error := False
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
has_error: BOOLEAN
|
||||
|
||||
is_verbose: BOOLEAN
|
||||
|
||||
feature -- Handshake
|
||||
|
||||
start_handshake (a_handshake: STRING)
|
||||
do
|
||||
subscriber.on_websocket_handshake (a_handshake)
|
||||
end
|
||||
|
||||
feature -- Receive
|
||||
|
||||
receive
|
||||
local
|
||||
l_frame: detachable WEB_SOCKET_FRAME
|
||||
l_client_message: detachable READABLE_STRING_8
|
||||
do
|
||||
l_frame := next_frame (subscriber.connection)
|
||||
if l_frame /= Void and then l_frame.is_valid then
|
||||
if attached l_frame.injected_control_frames as l_injections then
|
||||
-- Process injected control frames now.
|
||||
-- FIXME
|
||||
across
|
||||
l_injections as ic
|
||||
loop
|
||||
if ic.item.is_connection_close then
|
||||
-- FIXME: we should probably send this event .. after the `l_frame.parent' frame event.
|
||||
subscriber.on_websocket_close ("Normal Close")
|
||||
elseif ic.item.is_ping then
|
||||
-- FIXME reply only to the most recent ping ...
|
||||
subscriber.on_websocket_ping (ic.item.payload_data)
|
||||
elseif ic.item.is_pong then
|
||||
subscriber.on_websocket_pong (ic.item.payload_data)
|
||||
else
|
||||
subscriber.on_websocket_error ("Wrong Opcode")
|
||||
end
|
||||
end
|
||||
end
|
||||
l_client_message := l_frame.payload_data
|
||||
if l_client_message = Void then
|
||||
l_client_message := ""
|
||||
end
|
||||
debug ("ws")
|
||||
print ("%NExecute: %N")
|
||||
print (" [opcode: " + opcode_name (l_frame.opcode) + "]%N")
|
||||
if l_frame.is_text then
|
||||
print (" [client message: %"" + l_client_message + "%"]%N")
|
||||
elseif l_frame.is_binary then
|
||||
print (" [client binary message length: %"" + l_client_message.count.out + "%"]%N")
|
||||
end
|
||||
print (" [is_control: " + l_frame.is_control.out + "]%N")
|
||||
print (" [is_binary: " + l_frame.is_binary.out + "]%N")
|
||||
print (" [is_text: " + l_frame.is_text.out + "]%N")
|
||||
end
|
||||
if l_frame.is_connection_close then
|
||||
subscriber.on_websocket_close ("Normal Close")
|
||||
elseif l_frame.is_binary then
|
||||
subscriber.on_websocket_binary_message (l_client_message)
|
||||
elseif l_frame.is_text then
|
||||
subscriber.on_websocket_text_message (l_client_message)
|
||||
elseif l_frame.is_ping then
|
||||
subscriber.on_websocket_ping (l_client_message)
|
||||
elseif l_frame.is_pong then
|
||||
subscriber.on_websocket_pong (l_client_message)
|
||||
else
|
||||
subscriber.on_websocket_error ("Wrong Opcode")
|
||||
end
|
||||
else
|
||||
debug ("ws")
|
||||
print ("%NExecute: %N")
|
||||
print (" [ERROR: invalid frame]%N")
|
||||
if l_frame /= Void and then attached l_frame.error as err then
|
||||
print (" [Code: " + err.code.out + "]%N")
|
||||
print (" [Description: " + err.description + "]%N")
|
||||
end
|
||||
end
|
||||
subscriber.on_websocket_close ("")
|
||||
end
|
||||
|
||||
-- if l_utf.is_valid_utf_8_string_8 (l_message) then
|
||||
-- if is_data_frame_ok then
|
||||
-- if opcode = text_frame then
|
||||
-- subscriber.on_websocket_text_message (l_message)
|
||||
-- elseif opcode = binary_frame then
|
||||
-- subscriber.on_websocket_binary_message (l_message)
|
||||
-- elseif opcode = ping_frame then
|
||||
-- subscriber.on_websocket_ping (l_message)
|
||||
-- elseif opcode = pong_frame then
|
||||
-- subscriber.on_websocket_pong (l_message)
|
||||
-- elseif opcode = Connection_close_frame then
|
||||
-- subscriber.on_websocket_close ("Normal Close")
|
||||
-- elseif opcode = ping_frame then
|
||||
-- subscriber.on_websocket_ping (l_message)
|
||||
-- elseif opcode = pong_frame then
|
||||
-- subscriber.on_websocket_pong (l_message)
|
||||
-- else
|
||||
-- subscriber.on_websocket_error ("Wrong Opcode")
|
||||
-- end
|
||||
-- else
|
||||
-- subscriber.on_websocket_close ("Invalid data frame")
|
||||
-- end
|
||||
-- else
|
||||
-- subscriber.on_websocket_error ("Invalid UTF-8")
|
||||
-- end
|
||||
|
||||
end
|
||||
|
||||
feature -- Methods
|
||||
|
||||
send (a_message: STRING)
|
||||
do
|
||||
end
|
||||
|
||||
close (a_id: INTEGER)
|
||||
-- Close a websocket connection with a close id : `a_id'
|
||||
do
|
||||
end
|
||||
|
||||
close_with_description (a_id: INTEGER; a_description: READABLE_STRING_GENERAL)
|
||||
-- Close a websocket connection with a close id : `a_id' and a description `a_description'
|
||||
do
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
set_default_port
|
||||
do
|
||||
if is_secure then
|
||||
port := wss_port_default
|
||||
else
|
||||
port := ws_port_default
|
||||
end
|
||||
end
|
||||
|
||||
subscriber: WEB_SOCKET_SUBSCRIBER
|
||||
|
||||
next_frame (a_socket: HTTP_STREAM_SOCKET): detachable WEB_SOCKET_FRAME
|
||||
-- TODO Binary messages
|
||||
-- Handle error responses in a better way.
|
||||
-- IDEA:
|
||||
-- class FRAME
|
||||
-- is_fin: BOOLEAN
|
||||
-- opcode: WEB_SOCKET_STATUS_CODE (TEXT, BINARY, CLOSE, CONTINUE,PING, PONG)
|
||||
-- data/payload
|
||||
-- status_code: #see Status Codes http://tools.ietf.org/html/rfc6455#section-7.3
|
||||
-- has_error
|
||||
--
|
||||
-- See Base Framing Protocol: http://tools.ietf.org/html/rfc6455#section-5.2
|
||||
-- 0 1 2 3
|
||||
-- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
-- +-+-+-+-+-------+-+-------------+-------------------------------+
|
||||
-- |F|R|R|R| opcode|M| Payload len | Extended payload length |
|
||||
-- |I|S|S|S| (4) |A| (7) | (16/64) |
|
||||
-- |N|V|V|V| |S| | (if payload len==126/127) |
|
||||
-- | |1|2|3| |K| | |
|
||||
-- +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||||
-- | Extended payload length continued, if payload len == 127 |
|
||||
-- + - - - - - - - - - - - - - - - +-------------------------------+
|
||||
-- | |Masking-key, if MASK set to 1 |
|
||||
-- +-------------------------------+-------------------------------+
|
||||
-- | Masking-key (continued) | Payload Data |
|
||||
-- +-------------------------------- - - - - - - - - - - - - - - - +
|
||||
-- : Payload Data continued ... :
|
||||
-- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
||||
-- | Payload Data continued ... |
|
||||
-- +---------------------------------------------------------------+
|
||||
note
|
||||
EIS: "name=WebSocket RFC", "protocol=URI", "src=http://tools.ietf.org/html/rfc6455#section-5.2"
|
||||
require
|
||||
a_socket_in_blocking_mode: a_socket.is_blocking
|
||||
local
|
||||
l_opcode: INTEGER
|
||||
l_len: INTEGER
|
||||
l_remaining_len: INTEGER
|
||||
l_payload_len: NATURAL_64
|
||||
l_chunk: STRING
|
||||
l_rsv: BOOLEAN
|
||||
l_fin: BOOLEAN
|
||||
l_has_mask: BOOLEAN
|
||||
l_chunk_size: INTEGER
|
||||
l_byte: INTEGER
|
||||
l_fetch_count: INTEGER
|
||||
l_bytes_read: INTEGER
|
||||
s: STRING
|
||||
is_data_frame_ok: BOOLEAN -- Is the last process data framing ok?
|
||||
retried: BOOLEAN
|
||||
do
|
||||
if not retried then
|
||||
debug ("ws")
|
||||
print ("next_frame:%N")
|
||||
end
|
||||
from
|
||||
is_data_frame_ok := True
|
||||
until
|
||||
l_fin or not is_data_frame_ok
|
||||
loop
|
||||
-- multi-frames or continue is only valid for Binary or Text
|
||||
s := next_bytes (a_socket, 1)
|
||||
if s.is_empty then
|
||||
is_data_frame_ok := False
|
||||
debug ("ws")
|
||||
print ("[ERROR] incomplete_data!%N")
|
||||
end
|
||||
else
|
||||
l_byte := s [1].code
|
||||
debug ("ws")
|
||||
print (" fin,rsv(3),opcode(4)=")
|
||||
print (to_byte_representation (l_byte))
|
||||
print ("%N")
|
||||
end
|
||||
l_fin := l_byte & (0b10000000) /= 0
|
||||
l_rsv := l_byte & (0b01110000) = 0
|
||||
l_opcode := l_byte & 0b00001111
|
||||
if Result /= Void then
|
||||
if l_opcode = Result.opcode then
|
||||
-- should not occur in multi-fragment frame!
|
||||
create Result.make (l_opcode, l_fin)
|
||||
Result.report_error (protocol_error, "Unexpected injected frame")
|
||||
elseif l_opcode = continuation_frame then
|
||||
-- Expected
|
||||
Result.update_fin (l_fin)
|
||||
elseif is_control_frame (l_opcode) then
|
||||
-- Control frames (see Section 5.5) MAY be injected in the middle of
|
||||
-- a fragmented message. Control frames themselves MUST NOT be fragmented.
|
||||
-- if the l_opcode is a control frame then there is an error!!!
|
||||
-- CLOSE, PING, PONG
|
||||
create Result.make_as_injected_control (l_opcode, Result)
|
||||
else
|
||||
-- should not occur in multi-fragment frame!
|
||||
create Result.make (l_opcode, l_fin)
|
||||
Result.report_error (protocol_error, "Unexpected frame")
|
||||
end
|
||||
else
|
||||
create Result.make (l_opcode, l_fin)
|
||||
if Result.is_continuation then
|
||||
-- Continuation frame is not expected without parent frame!
|
||||
Result.report_error (protocol_error, "There is no message to continue!")
|
||||
end
|
||||
end
|
||||
if Result.is_valid then
|
||||
--| valid frame/fragment
|
||||
if is_verbose then
|
||||
log ("+ frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")")
|
||||
end
|
||||
|
||||
-- rsv validation
|
||||
if not l_rsv then
|
||||
-- RSV1, RSV2, RSV3: 1 bit each
|
||||
|
||||
-- MUST be 0 unless an extension is negotiated that defines meanings
|
||||
-- for non-zero values. If a nonzero value is received and none of
|
||||
-- the negotiated extensions defines the meaning of such a nonzero
|
||||
-- value, the receiving endpoint MUST _Fail the WebSocket
|
||||
-- Connection_
|
||||
|
||||
-- FIXME: add support for extension ?
|
||||
Result.report_error (protocol_error, "RSV values MUST be 0 unless an extension is negotiated that defines meanings for non-zero values")
|
||||
end
|
||||
else
|
||||
if is_verbose then
|
||||
log ("+ INVALID frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")")
|
||||
end
|
||||
end
|
||||
|
||||
-- At the moment only TEXT, (pending Binary)
|
||||
if Result.is_valid then
|
||||
if Result.is_text or Result.is_binary or Result.is_control then
|
||||
-- Reading next byte (mask+payload_len)
|
||||
s := next_bytes (a_socket, 1)
|
||||
if s.is_empty then
|
||||
Result.report_error (invalid_data, "Incomplete data for mask and payload len")
|
||||
else
|
||||
l_byte := s [1].code
|
||||
debug ("ws")
|
||||
print (" mask,payload_len(7)=")
|
||||
print (to_byte_representation (l_byte))
|
||||
io.put_new_line
|
||||
end
|
||||
l_has_mask := l_byte & (0b10000000) /= 0 -- MASK
|
||||
l_len := l_byte & 0b01111111 -- 7bits
|
||||
|
||||
debug ("ws")
|
||||
print (" payload_len=" + l_len.out)
|
||||
io.put_new_line
|
||||
end
|
||||
if Result.is_control and then l_len > 125 then
|
||||
-- All control frames MUST have a payload length of 125 bytes or less
|
||||
-- and MUST NOT be fragmented.
|
||||
Result.report_error (protocol_error, "Control frame MUST have a payload length of 125 bytes or less")
|
||||
elseif l_len = 127 then -- TODO proof of concept read 8 bytes.
|
||||
-- the following 8 bytes interpreted as a 64-bit unsigned integer
|
||||
-- (the most significant bit MUST be 0) are the payload length.
|
||||
-- Multibyte length quantities are expressed in network byte order.
|
||||
s := next_bytes (a_socket, 8) -- 64 bits
|
||||
debug ("ws")
|
||||
print (" extended payload length=" + string_to_byte_representation (s))
|
||||
io.put_new_line
|
||||
end
|
||||
if s.count < 8 then
|
||||
Result.report_error (Invalid_data, "Incomplete data for 64 bit Extended payload length")
|
||||
else
|
||||
l_payload_len := s [8].natural_32_code.to_natural_64
|
||||
l_payload_len := l_payload_len | (s [7].natural_32_code.to_natural_64 |<< 8)
|
||||
l_payload_len := l_payload_len | (s [6].natural_32_code.to_natural_64 |<< 16)
|
||||
l_payload_len := l_payload_len | (s [5].natural_32_code.to_natural_64 |<< 24)
|
||||
l_payload_len := l_payload_len | (s [4].natural_32_code.to_natural_64 |<< 32)
|
||||
l_payload_len := l_payload_len | (s [3].natural_32_code.to_natural_64 |<< 40)
|
||||
l_payload_len := l_payload_len | (s [2].natural_32_code.to_natural_64 |<< 48)
|
||||
l_payload_len := l_payload_len | (s [1].natural_32_code.to_natural_64 |<< 56)
|
||||
end
|
||||
elseif l_len = 126 then
|
||||
s := next_bytes (a_socket, 2) -- 16 bits
|
||||
debug ("ws")
|
||||
print (" extended payload length bits=" + string_to_byte_representation (s))
|
||||
io.put_new_line
|
||||
end
|
||||
if s.count < 2 then
|
||||
Result.report_error (Invalid_data, "Incomplete data for 16 bit Extended payload length")
|
||||
else
|
||||
l_payload_len := s [2].natural_32_code.to_natural_64
|
||||
l_payload_len := l_payload_len | (s [1].natural_32_code.to_natural_64 |<< 8)
|
||||
end
|
||||
else
|
||||
l_payload_len := l_len.to_natural_64
|
||||
end
|
||||
debug ("ws")
|
||||
print (" Full payload length=" + l_payload_len.out)
|
||||
io.put_new_line
|
||||
end
|
||||
if Result.is_valid then
|
||||
if l_has_mask then
|
||||
Result.report_error (protocol_error, "Server sent a masked frame!")
|
||||
else
|
||||
end
|
||||
if Result.is_valid then
|
||||
l_chunk_size := 0x4000 -- 16 K
|
||||
if l_payload_len > {INTEGER_32}.max_value.to_natural_64 then
|
||||
-- Issue .. to big to store in STRING
|
||||
-- FIXME !!!
|
||||
Result.report_error (Message_too_large, "Can not handle payload data (len=" + l_payload_len.out + ")")
|
||||
else
|
||||
l_len := l_payload_len.to_integer_32
|
||||
end
|
||||
from
|
||||
l_fetch_count := 0
|
||||
l_remaining_len := l_len
|
||||
until
|
||||
l_fetch_count >= l_len or l_len = 0 or not Result.is_valid
|
||||
loop
|
||||
if l_remaining_len < l_chunk_size then
|
||||
l_chunk_size := l_remaining_len
|
||||
end
|
||||
a_socket.read_stream (l_chunk_size)
|
||||
l_bytes_read := a_socket.bytes_read
|
||||
debug ("ws")
|
||||
print ("read chunk size=" + l_chunk_size.out + " fetch_count=" + l_fetch_count.out + " l_len=" + l_len.out + " -> " + l_bytes_read.out + "bytes%N")
|
||||
end
|
||||
if a_socket.bytes_read > 0 then
|
||||
l_remaining_len := l_remaining_len - l_bytes_read
|
||||
l_chunk := a_socket.last_string
|
||||
l_fetch_count := l_fetch_count + l_bytes_read
|
||||
Result.append_payload_data_chop (l_chunk, l_bytes_read, l_remaining_len = 0)
|
||||
else
|
||||
Result.report_error (internal_error, "Issue reading payload data...")
|
||||
end
|
||||
end
|
||||
if is_verbose then
|
||||
log (" Received " + l_fetch_count.out + " out of " + l_len.out + " bytes <===============")
|
||||
end
|
||||
debug ("ws")
|
||||
print (" -> ")
|
||||
if attached Result.payload_data as l_payload_data then
|
||||
s := l_payload_data.tail (l_fetch_count)
|
||||
if s.count > 50 then
|
||||
print (string_to_byte_hexa_representation (s.head (50) + ".."))
|
||||
else
|
||||
print (string_to_byte_hexa_representation (s))
|
||||
end
|
||||
print ("%N")
|
||||
if Result.is_text and Result.is_fin and Result.fragment_count = 0 then
|
||||
print (" -> ")
|
||||
if s.count > 50 then
|
||||
print (s.head (50) + "..")
|
||||
else
|
||||
print (s)
|
||||
end
|
||||
print ("%N")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if Result /= Void then
|
||||
if attached Result.error as err then
|
||||
if is_verbose then
|
||||
log (" !Invalid frame: " + err.string)
|
||||
end
|
||||
end
|
||||
if Result.is_injected_control then
|
||||
if attached Result.parent as l_parent then
|
||||
if not Result.is_valid then
|
||||
l_parent.report_error (protocol_error, "Invalid injected frame")
|
||||
end
|
||||
if Result.is_connection_close then
|
||||
-- Return this and process the connection close right away!
|
||||
l_parent.update_fin (True)
|
||||
l_fin := Result.is_fin
|
||||
else
|
||||
Result := l_parent
|
||||
l_fin := l_parent.is_fin
|
||||
check
|
||||
-- This is a control frame but occurs in fragmented frame.
|
||||
inside_fragmented_frame: not l_fin
|
||||
end
|
||||
end
|
||||
else
|
||||
check
|
||||
has_parent: False
|
||||
end
|
||||
l_fin := False -- This is a control frame but occurs in fragmented frame.
|
||||
end
|
||||
end
|
||||
if not Result.is_valid then
|
||||
is_data_frame_ok := False
|
||||
end
|
||||
else
|
||||
is_data_frame_ok := False
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue
|
||||
retried := True
|
||||
if Result /= Void then
|
||||
Result.report_error (internal_error, "Internal error")
|
||||
end
|
||||
retry
|
||||
end
|
||||
|
||||
next_bytes (a_socket: HTTP_STREAM_SOCKET; nb: INTEGER): STRING
|
||||
require
|
||||
nb > 0
|
||||
local
|
||||
n, l_bytes_read: INTEGER
|
||||
do
|
||||
create Result.make (nb)
|
||||
from
|
||||
n := nb
|
||||
until
|
||||
n = 0
|
||||
loop
|
||||
a_socket.read_stream (nb)
|
||||
l_bytes_read := a_socket.bytes_read
|
||||
if l_bytes_read > 0 then
|
||||
Result.append (a_socket.last_string)
|
||||
n := n - l_bytes_read
|
||||
else
|
||||
n := 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unmask (a_chunk: STRING_8; a_pos: INTEGER; a_key: READABLE_STRING_8)
|
||||
local
|
||||
i, n: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := a_chunk.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
a_chunk.put_code (a_chunk.code (i).bit_xor (a_key [((i + (a_pos - 1) - 1) \\ 4) + 1].natural_32_code), i)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
append_chunk_unmasked (a_chunk: READABLE_STRING_8; a_pos: INTEGER; a_key: READABLE_STRING_8; a_target: STRING)
|
||||
-- To convert masked data into unmasked data, or vice versa, the following
|
||||
-- algorithm is applied. The same algorithm applies regardless of the
|
||||
-- direction of the translation, e.g., the same steps are applied to
|
||||
-- mask the data as to unmask the data.
|
||||
|
||||
-- Octet i of the transformed data ("transformed-octet-i") is the XOR of
|
||||
-- octet i of the original data ("original-octet-i") with octet at index
|
||||
-- i modulo 4 of the masking key ("masking-key-octet-j"):
|
||||
|
||||
-- j = i MOD 4
|
||||
-- transformed-octet-i = original-octet-i XOR masking-key-octet-j
|
||||
|
||||
-- The payload length, indicated in the framing as frame-payload-length,
|
||||
-- does NOT include the length of the masking key. It is the length of
|
||||
-- the "Payload data", e.g., the number of bytes following the masking
|
||||
-- key.
|
||||
note
|
||||
EIS: "name=Masking", "src=http://tools.ietf.org/html/rfc6455#section-5.3", "protocol=uri"
|
||||
local
|
||||
i, n: INTEGER
|
||||
do
|
||||
-- debug ("ws")
|
||||
-- print ("append_chunk_unmasked (%"" + string_to_byte_representation (a_chunk) + "%",%N%Ta_pos=" + a_pos.out+ ", a_key, a_target #.count=" + a_target.count.out + ")%N")
|
||||
-- end
|
||||
from
|
||||
i := 1
|
||||
n := a_chunk.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
a_target.append_code (a_chunk.code (i).bit_xor (a_key [((i + (a_pos - 1) - 1) \\ 4) + 1].natural_32_code))
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Debug
|
||||
|
||||
log (m: STRING)
|
||||
do
|
||||
io.put_string (m + "%N")
|
||||
end
|
||||
|
||||
to_byte (a_integer: INTEGER): ARRAY [INTEGER]
|
||||
require
|
||||
valid: a_integer >= 0 and then a_integer <= 255
|
||||
local
|
||||
l_val: INTEGER
|
||||
l_index: INTEGER
|
||||
do
|
||||
create Result.make_filled (0, 1, 8)
|
||||
from
|
||||
l_val := a_integer
|
||||
l_index := 8
|
||||
until
|
||||
l_val < 2
|
||||
loop
|
||||
Result.put (l_val \\ 2, l_index)
|
||||
l_val := l_val // 2
|
||||
l_index := l_index - 1
|
||||
end
|
||||
Result.put (l_val, l_index)
|
||||
end
|
||||
|
||||
feature -- Masking
|
||||
|
||||
unmmask (a_frame: READABLE_STRING_8; a_key: READABLE_STRING_8): STRING
|
||||
-- To convert masked data into unmasked data, or vice versa, the following
|
||||
-- algorithm is applied. The same algorithm applies regardless of the
|
||||
-- direction of the translation, e.g., the same steps are applied to
|
||||
-- mask the data as to unmask the data.
|
||||
|
||||
-- Octet i of the transformed data ("transformed-octet-i") is the XOR of
|
||||
-- octet i of the original data ("original-octet-i") with octet at index
|
||||
-- i modulo 4 of the masking key ("masking-key-octet-j"):
|
||||
|
||||
-- j = i MOD 4
|
||||
-- transformed-octet-i = original-octet-i XOR masking-key-octet-j
|
||||
|
||||
-- The payload length, indicated in the framing as frame-payload-length,
|
||||
-- does NOT include the length of the masking key. It is the length of
|
||||
-- the "Payload data", e.g., the number of bytes following the masking
|
||||
-- key.
|
||||
note
|
||||
EIS: "name=Masking", "src=S", "protocol=uri"
|
||||
local
|
||||
l_frame: STRING
|
||||
i: INTEGER
|
||||
do
|
||||
l_frame := a_frame.twin
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > l_frame.count
|
||||
loop
|
||||
l_frame [i] := (l_frame [i].code.to_integer_8.bit_xor (a_key [((i - 1) \\ 4) + 1].code.to_integer_8)).to_character_8
|
||||
i := i + 1
|
||||
end
|
||||
Result := l_frame
|
||||
end
|
||||
|
||||
to_byte_representation (a_integer: INTEGER): STRING
|
||||
require
|
||||
valid: a_integer >= 0 and then a_integer <= 255
|
||||
local
|
||||
l_val: INTEGER
|
||||
do
|
||||
create Result.make (8)
|
||||
from
|
||||
l_val := a_integer
|
||||
until
|
||||
l_val < 2
|
||||
loop
|
||||
Result.prepend_integer (l_val \\ 2)
|
||||
l_val := l_val // 2
|
||||
end
|
||||
Result.prepend_integer (l_val)
|
||||
end
|
||||
|
||||
string_to_byte_representation (s: STRING): STRING
|
||||
require
|
||||
valid: s.count > 0
|
||||
local
|
||||
i, n: INTEGER
|
||||
do
|
||||
n := s.count
|
||||
create Result.make (8 * n)
|
||||
if n > 0 then
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
if not Result.is_empty then
|
||||
Result.append_character (':')
|
||||
end
|
||||
Result.append (to_byte_representation (s [i].code))
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
string_to_byte_hexa_representation (s: STRING): STRING
|
||||
local
|
||||
i, n: INTEGER
|
||||
c: INTEGER
|
||||
do
|
||||
n := s.count
|
||||
create Result.make (8 * n)
|
||||
if n > 0 then
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
if not Result.is_empty then
|
||||
Result.append_character (':')
|
||||
end
|
||||
c := s [i].code
|
||||
check
|
||||
c <= 0xFF
|
||||
end
|
||||
Result.append_character (((c |>> 4) & 0xF).to_hex_character)
|
||||
Result.append_character (((c) & 0xF).to_hex_character)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
note
|
||||
description: "[
|
||||
Objects that is used to create dummy web socket client object
|
||||
used for void-safety
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WEB_SOCKET_NULL_CLIENT
|
||||
|
||||
inherit
|
||||
WEB_SOCKET_SUBSCRIBER
|
||||
|
||||
feature -- Handshake
|
||||
|
||||
on_websocket_handshake (request: STRING)
|
||||
-- Default do nothing
|
||||
do
|
||||
end
|
||||
|
||||
feature -- TCP connection
|
||||
|
||||
connection: HTTP_STREAM_SOCKET
|
||||
do
|
||||
create Result.make_client_by_port (0, "null")
|
||||
end
|
||||
|
||||
end
|
||||
120
library/network/websocket/client/src/web_socket_ready_state.e
Normal file
120
library/network/websocket/client/src/web_socket_ready_state.e
Normal file
@@ -0,0 +1,120 @@
|
||||
note
|
||||
description: "Report the state of the connection: [CONNECTING, OPEN, CLOSING, CLOSED]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WEB_SOCKET_READY_STATE
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
set_state (Connecting)
|
||||
ensure
|
||||
state_connecting: state = Connecting
|
||||
end
|
||||
feature -- Access
|
||||
|
||||
state: INTEGER
|
||||
-- Current ready state
|
||||
|
||||
feature -- Status Report
|
||||
|
||||
is_valid_state (a_state: INTEGER): BOOLEAN
|
||||
-- Is `a_state' a valid ready state?
|
||||
do
|
||||
Result := Connecting = a_state or else Open = a_state or else Closing = a_state or else Closed = a_state
|
||||
end
|
||||
|
||||
is_connecting: BOOLEAN
|
||||
-- Is current `state' connecting?
|
||||
do
|
||||
Result := state = Connecting
|
||||
end
|
||||
|
||||
is_open: BOOLEAN
|
||||
-- Is current `state' open?
|
||||
do
|
||||
Result := state = Open
|
||||
end
|
||||
|
||||
is_closing: BOOLEAN
|
||||
-- Is current `state' closing?
|
||||
do
|
||||
Result := state = Closing
|
||||
end
|
||||
|
||||
is_closed: BOOLEAN
|
||||
-- Is current `state' closed?
|
||||
do
|
||||
Result := state = Closed
|
||||
end
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
set_state (a_state: INTEGER)
|
||||
-- Set `state' to `a_state'
|
||||
require
|
||||
valid_state: is_valid_state (a_state)
|
||||
do
|
||||
state := a_state
|
||||
ensure
|
||||
state_set: state = a_state
|
||||
end
|
||||
|
||||
mark_connecting
|
||||
--Set `state' to `connecting'
|
||||
do
|
||||
set_state (Connecting)
|
||||
ensure
|
||||
connecting_state: state = Connecting
|
||||
end
|
||||
|
||||
mark_open
|
||||
--Set `state' to `open'
|
||||
do
|
||||
set_state (Open)
|
||||
ensure
|
||||
open_state: state = Open
|
||||
end
|
||||
|
||||
mark_closing
|
||||
--Set `state' to `closing'
|
||||
do
|
||||
set_state (Closing)
|
||||
ensure
|
||||
open_state: state = Closing
|
||||
end
|
||||
|
||||
|
||||
mark_closed
|
||||
--Set `state' to `closed'
|
||||
do
|
||||
set_state (Closed)
|
||||
ensure
|
||||
open_state: state = Closed
|
||||
end
|
||||
|
||||
|
||||
feature --State connection
|
||||
|
||||
Connecting: INTEGER = 0
|
||||
-- The connection is in progress but has not been established.
|
||||
|
||||
Open: INTEGER = 1
|
||||
-- The connection has been stablished. Messages can flow between client and server.
|
||||
|
||||
Closing: INTEGER = 2
|
||||
-- The connection is going through the closing handshake
|
||||
|
||||
Closed: INTEGER = 3
|
||||
-- The connection has been closed or could not be opened.
|
||||
|
||||
|
||||
invariant
|
||||
is_valid_sate: is_valid_state (state)
|
||||
end
|
||||
61
library/network/websocket/client/src/web_socket_subscriber.e
Normal file
61
library/network/websocket/client/src/web_socket_subscriber.e
Normal file
@@ -0,0 +1,61 @@
|
||||
note
|
||||
description: "Summary description for {WEB_SOCKET_SUBSCRIBER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WEB_SOCKET_SUBSCRIBER
|
||||
|
||||
|
||||
feature -- Events
|
||||
|
||||
on_websocket_ping (a_message: detachable STRING)
|
||||
-- Send a ping in response to a received ping.
|
||||
do
|
||||
end
|
||||
|
||||
|
||||
on_websocket_pong (a_message: detachable STRING)
|
||||
-- Default implementation do nothing.
|
||||
do
|
||||
end
|
||||
|
||||
|
||||
on_websocket_text_message (a_message: detachable STRING)
|
||||
-- Default implementation do nothing.
|
||||
do
|
||||
end
|
||||
|
||||
on_websocket_binary_message (a_message: detachable STRING)
|
||||
-- Default implementation do nothing.
|
||||
do
|
||||
end
|
||||
|
||||
|
||||
on_websocket_open (a_message: detachable STRING)
|
||||
do
|
||||
end
|
||||
|
||||
|
||||
on_websocket_close (a_message: detachable STRING)
|
||||
do
|
||||
end
|
||||
|
||||
on_websocket_error (a_error: detachable STRING)
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Handshake
|
||||
|
||||
on_websocket_handshake (request: STRING)
|
||||
-- Default do nothing
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- TCP connection
|
||||
|
||||
connection: HTTP_STREAM_SOCKET
|
||||
deferred
|
||||
end
|
||||
end
|
||||
45
library/network/websocket/client/web_socket_client-safe.ecf
Normal file
45
library/network/websocket/client/web_socket_client-safe.ecf
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="web_socket_client" uuid="EE010507-597F-4FAD-8EFA-B7251E800911" library_target="web_socket_client">
|
||||
<target name="web_socket_client">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" void_safety="all">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
|
||||
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<library name="lib_http_network" location="..\..\http_network\http_network-safe.ecf"/>
|
||||
<library name="lib_web_socket_protocol" location="..\protocol\web_socket_protocol-safe.ecf"/>
|
||||
<cluster name="web_socket_client" location=".\src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/no_ssl$</exclude>
|
||||
<exclude>/ssl$</exclude>
|
||||
<exclude>/spec$</exclude>
|
||||
</file_rule>
|
||||
<cluster name="ssl" location="$|ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" excluded_value="true"/>
|
||||
<custom name="net_ssl_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
45
library/network/websocket/client/web_socket_client.ecf
Normal file
45
library/network/websocket/client/web_socket_client.ecf
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="web_socket_client" uuid="EE010507-597F-4FAD-8EFA-B7251E800911" library_target="web_socket_client">
|
||||
<target name="web_socket_client">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" void_safety="none">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/>
|
||||
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<library name="lib_http_network" location="..\..\http_network\http_network.ecf"/>
|
||||
<library name="lib_web_socket_protocol" location="..\protocol\web_socket_protocol.ecf"/>
|
||||
<cluster name="web_socket_client" location=".\src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/no_ssl$</exclude>
|
||||
<exclude>/ssl$</exclude>
|
||||
<exclude>/spec$</exclude>
|
||||
</file_rule>
|
||||
<cluster name="ssl" location="$|ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<condition>
|
||||
<custom name="net_ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" excluded_value="true"/>
|
||||
<custom name="net_ssl_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
15
library/network/websocket/client/www/index.html
Normal file
15
library/network/websocket/client/www/index.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebSocket prototype</title>
|
||||
<script src="js/jquery-1.10.1.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebSocket prototype</h1>
|
||||
<button id="run">Run</button>
|
||||
<p>Output :</p>
|
||||
<div id=console>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
9807
library/network/websocket/client/www/js/jquery-1.10.1.js
vendored
Normal file
9807
library/network/websocket/client/www/js/jquery-1.10.1.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
37
library/network/websocket/client/www/js/main.js
Normal file
37
library/network/websocket/client/www/js/main.js
Normal file
@@ -0,0 +1,37 @@
|
||||
$(function() {
|
||||
var wsEchoURI = "ws://localhost:9999/";
|
||||
var websocket;
|
||||
|
||||
function printConsole(text) {
|
||||
$("#console").append($(document.createElement("p")).html(text));
|
||||
}
|
||||
|
||||
function onWSOpen(evt) {
|
||||
printConsole("WebSocket open");
|
||||
if(websocket.readyState == websocket.OPEN) {
|
||||
printConsole("WebSocket ready");
|
||||
}
|
||||
}
|
||||
|
||||
function onWSClose(evt) {
|
||||
printConsole("WebSocket closed");
|
||||
printConsole(websocket.readyState);
|
||||
}
|
||||
|
||||
function onWSMessage(evt) {
|
||||
printConsole(evt.data);
|
||||
}
|
||||
|
||||
function onWSError(evt) {
|
||||
printConsole("An error occured in the WebSocket : " + evt.data);
|
||||
}
|
||||
|
||||
$("#run").click(function buttonRun() {
|
||||
printConsole("Connection");
|
||||
websocket = new WebSocket(wsEchoURI);
|
||||
websocket.onopen = function(evt) { onWSOpen(evt) };
|
||||
websocket.onclose = function(evt) { onWSClose(evt) };
|
||||
websocket.onmessage = function(evt) { onWSMessage(evt) };
|
||||
websocket.onerror = function(evt) { onWSError(evt) };
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="protocol" uuid="97582991-7BF3-4F4D-8944-8141ADE34274" library_target="protocol">
|
||||
<target name="protocol">
|
||||
<root all_classes="true"/>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
11
library/network/websocket/protocol/web_socket_protocol.ecf
Normal file
11
library/network/websocket/protocol/web_socket_protocol.ecf
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="protocol" uuid="97582991-7BF3-4F4D-8944-8141ADE34274" library_target="protocol">
|
||||
<target name="protocol">
|
||||
<root all_classes="true"/>
|
||||
<option void_safety="none">
|
||||
</option>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,40 @@
|
||||
note
|
||||
description: "Websocket server."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
WEB_SOCKET_SERVICE [APPLICATION_EXECUTION]
|
||||
redefine
|
||||
set_options
|
||||
end
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
set_options (opts: WEB_SOCKET_SERVICE_OPTIONS)
|
||||
do
|
||||
opts.set_is_verbose (True) -- For debug purpose
|
||||
opts.set_verbose_level ("debug")
|
||||
|
||||
opts.set_is_secure (True) -- If SSL is supported
|
||||
opts.set_secure_certificate ("ca.crt") -- Change to use your own crt file.
|
||||
opts.set_secure_certificate_key ("ca.key") -- Change to use your own key file.
|
||||
|
||||
opts.set_port (default_port_number)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
default_port_number: INTEGER = 9090
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
note
|
||||
description: "[
|
||||
Implementation of {WEB_SOCKET_EXECUTION} specific for this application.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WEB_SOCKET_EXECUTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Request processing
|
||||
|
||||
is_verbose: BOOLEAN = True
|
||||
|
||||
execute
|
||||
-- Process request ...
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
if
|
||||
request.is_get_request_method and then
|
||||
request.request_uri.is_case_insensitive_equal_general ("/app")
|
||||
then
|
||||
s := websocket_app_html ({APPLICATION}.default_port_number)
|
||||
else
|
||||
s := "Open the websocket client example: <a href=%"/app%">click here</a>."
|
||||
end
|
||||
response.header.put_content_type_text_html
|
||||
response.header.put_content_length (s.count)
|
||||
response.put_string (s)
|
||||
end
|
||||
|
||||
on_open (ws: WEB_SOCKET)
|
||||
do
|
||||
if is_verbose then
|
||||
log ("Connecting", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||
end
|
||||
end
|
||||
|
||||
on_binary (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||
do
|
||||
ws.send (Binary_frame, a_message)
|
||||
end
|
||||
|
||||
on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||
do
|
||||
-- if is_verbose then
|
||||
-- log ("On text (message length=" + a_message.count.out + ")", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||
-- end
|
||||
ws.send (Text_frame, a_message)
|
||||
end
|
||||
|
||||
on_close (ws: WEB_SOCKET)
|
||||
-- Called after the WebSocket connection is closed.
|
||||
do
|
||||
if is_verbose then
|
||||
log ("Connection closed", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||
end
|
||||
end
|
||||
|
||||
log (m: READABLE_STRING_8; lev: INTEGER)
|
||||
do
|
||||
response.put_error (m)
|
||||
end
|
||||
|
||||
websocket_app_html (a_port: INTEGER): STRING
|
||||
do
|
||||
Result := "[
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
|
||||
var socket;
|
||||
|
||||
function connect(){
|
||||
|
||||
var host = "ws://127.0.0.1:##PORTNUMBER##";
|
||||
|
||||
try{
|
||||
socket = new WebSocket(host);
|
||||
message('<p class="event">Socket Status: '+socket.readyState);
|
||||
socket.onopen = function(){
|
||||
message('<p class="event">Socket Status: '+socket.readyState+' (open)');
|
||||
}
|
||||
socket.onmessage = function(msg){
|
||||
message('<p class="message">Received: '+msg.data);
|
||||
}
|
||||
socket.onclose = function(){
|
||||
message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
|
||||
}
|
||||
} catch(exception){
|
||||
message('<p>Error'+exception);
|
||||
}
|
||||
}
|
||||
|
||||
function send(){
|
||||
var text = $('#text').val();
|
||||
if(text==""){
|
||||
message('<p class="warning">Please enter a message');
|
||||
return ;
|
||||
}
|
||||
try{
|
||||
socket.send(text);
|
||||
message('<p class="event">Sent: '+text)
|
||||
} catch(exception){
|
||||
message('<p class="warning">');
|
||||
}
|
||||
$('#text').val("");
|
||||
}
|
||||
|
||||
function message(msg){
|
||||
$('#chatLog').append(msg+'</p>');
|
||||
}//End message()
|
||||
|
||||
$('#text').keypress(function(event) {
|
||||
if (event.keyCode == '13') {
|
||||
send();
|
||||
}
|
||||
});
|
||||
|
||||
$('#disconnect').click(function(){
|
||||
socket.close();
|
||||
});
|
||||
|
||||
if (!("WebSocket" in window)){
|
||||
$('#chatLog, input, button, #examples').fadeOut("fast");
|
||||
$('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
|
||||
}else{
|
||||
//The user has WebSockets
|
||||
connect();
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
<meta charset="utf-8" />
|
||||
<style type="text/css">
|
||||
body {font-family:Arial, Helvetica, sans-serif;}
|
||||
#container { border:5px solid grey; width:800px; margin:0 auto; padding:10px; }
|
||||
#chatLog { padding:5px; border:1px solid black; }
|
||||
#chatLog p {margin:0;}
|
||||
.event {color:#999;}
|
||||
.warning { font-weight:bold; color:#CCC; }
|
||||
</style>
|
||||
<title>WebSockets Client</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<div id="container">
|
||||
<h1>WebSockets Client</h1>
|
||||
<div id="chatLog"></div>
|
||||
<input id="text" type="text" />
|
||||
<button id="disconnect">Disconnect</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
Result.replace_substring_all ("##PORTNUMBER##", a_port.out)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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="echo_websocket_server" uuid="C9B3DA5F-DF0D-4C0F-924A-130B5C1E6604">
|
||||
<target name="common">
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
</file_rule>
|
||||
<option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="all" syntax="transitional">
|
||||
<debug name="ws" enabled="true"/>
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="console_application" value="true"/>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf">
|
||||
<option>
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
</library>
|
||||
<library name="http_network" location="..\..\..\..\http_network\http_network-safe.ecf" readonly="false"/>
|
||||
<library name="httpd" location="..\..\..\..\..\server\httpd\httpd-safe.ecf" readonly="false"/>
|
||||
<library name="standalone_websocket_connector" location="..\..\..\..\..\server\wsf\connector\standalone_websocket-safe.ecf" readonly="false"/>
|
||||
<library name="websocket_server" location="..\..\websocket_server-safe.ecf" readonly="false">
|
||||
<option debug="true">
|
||||
<debug name="ws" enabled="true"/>
|
||||
</option>
|
||||
</library>
|
||||
<cluster name="src" location=".\" recursive="true">
|
||||
</cluster>
|
||||
</target>
|
||||
<target name="echo_websocket_server_mt" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
</target>
|
||||
<target name="echo_websocket_server_mt_ssl" extends="echo_websocket_server_mt">
|
||||
<variable name="ssl_enabled" value="true"/>
|
||||
</target>
|
||||
<target name="echo_websocket_server_scoop" extends="common">
|
||||
<root class="APPLICATION" feature="make_and_launch"/>
|
||||
<setting name="concurrency" value="scoop"/>
|
||||
</target>
|
||||
<target name="echo_websocket_server_scoop_ssl" extends="echo_websocket_server_scoop">
|
||||
<variable name="ssl_enabled" value="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,4 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Javier Velilla, Jocelyn Fiat and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
|
||||
if(!("WebSocket" in window)){
|
||||
$('#chatLog, input, button, #examples').fadeOut("fast");
|
||||
$('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
|
||||
}else{
|
||||
//The user has WebSockets
|
||||
|
||||
connect();
|
||||
|
||||
function connect(){
|
||||
var socket;
|
||||
var host = "ws://127.0.0.1:9090";
|
||||
|
||||
try{
|
||||
var socket = new WebSocket(host);
|
||||
message('<p class="event">Socket Status: '+socket.readyState);
|
||||
socket.onopen = function(){
|
||||
message('<p class="event">Socket Status: '+socket.readyState+' (open)');
|
||||
}
|
||||
|
||||
socket.onmessage = function(msg){
|
||||
message('<p class="message">Received: '+msg.data);
|
||||
}
|
||||
|
||||
socket.onclose = function(){
|
||||
message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
|
||||
}
|
||||
|
||||
} catch(exception){
|
||||
message('<p>Error'+exception);
|
||||
}
|
||||
|
||||
function send(){
|
||||
var text = $('#text').val();
|
||||
if(text==""){
|
||||
message('<p class="warning">Please enter a message');
|
||||
return ;
|
||||
}
|
||||
try{
|
||||
socket.send(text);
|
||||
message('<p class="event">Sent: '+text)
|
||||
} catch(exception){
|
||||
message('<p class="warning">');
|
||||
}
|
||||
$('#text').val("");
|
||||
}
|
||||
|
||||
function message(msg){
|
||||
$('#chatLog').append(msg+'</p>');
|
||||
}//End message()
|
||||
|
||||
$('#text').keypress(function(event) {
|
||||
if (event.keyCode == '13') {
|
||||
send();
|
||||
}
|
||||
});
|
||||
|
||||
$('#disconnect').click(function(){
|
||||
socket.close();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}//End connect()
|
||||
|
||||
});
|
||||
</script>
|
||||
<meta charset=utf-8 />
|
||||
<style type="text/css">
|
||||
body{font-family:Arial, Helvetica, sans-serif;}
|
||||
#container{
|
||||
border:5px solid grey;
|
||||
width:800px;
|
||||
margin:0 auto;
|
||||
padding:10px;
|
||||
}
|
||||
#chatLog{
|
||||
padding:5px;
|
||||
border:1px solid black;
|
||||
}
|
||||
#chatLog p{margin:0;}
|
||||
.event{color:#999;}
|
||||
.warning{
|
||||
font-weight:bold;
|
||||
color:#CCC;
|
||||
}
|
||||
</style>
|
||||
<title>WebSockets Client</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
|
||||
<div id="container">
|
||||
|
||||
<h1>WebSockets Client</h1>
|
||||
|
||||
<div id="chatLog">
|
||||
|
||||
</div>
|
||||
|
||||
<input id="text" type="text" />
|
||||
<button id="disconnect">Disconnect</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,53 @@
|
||||
<!DOCTYPE html>
|
||||
<title>WebSocket Image Drop</title>
|
||||
<h1>Drop Image Here</h1>
|
||||
<script>
|
||||
// Initialize WebSocket connection
|
||||
var wsUrl = "ws://127.0.0.1:9090";
|
||||
var ws = new WebSocket(wsUrl);
|
||||
ws.onopen = function() {
|
||||
console.log("open");
|
||||
}
|
||||
// Handle binary image data received on the WebSocket
|
||||
ws.onmessage = function(e) {
|
||||
var blob = e.data;
|
||||
console.log("message: " + blob.size + " bytes");
|
||||
// Work with prefixed URL API
|
||||
if (window.webkitURL) {
|
||||
URL = webkitURL;
|
||||
}
|
||||
var uri = URL.createObjectURL(blob);
|
||||
var img = document.createElement("img");
|
||||
img.src = uri;
|
||||
document.body.appendChild(img);
|
||||
}
|
||||
|
||||
// Handle drop event
|
||||
document.ondrop = function(e) {
|
||||
document.body.style.backgroundColor = "#fff";
|
||||
try {
|
||||
e.preventDefault();
|
||||
handleFileDrop(e.dataTransfer.files[0]);
|
||||
return false;
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
// Provide visual feedback for the drop area
|
||||
document.ondragover = function(e) {
|
||||
e.preventDefault();
|
||||
document.body.style.backgroundColor = "#6fff41";
|
||||
}
|
||||
document.ondragleave = function() {
|
||||
document.body.style.backgroundColor = "#fff";
|
||||
}
|
||||
// Read binary file contents and send them over WebSocket
|
||||
function handleFileDrop(file) {
|
||||
var reader = new FileReader();
|
||||
reader.readAsArrayBuffer(file);
|
||||
reader.onload = function() {
|
||||
console.log("sending: " + file.name);
|
||||
ws.send(reader.result);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
21
library/network/websocket/server/src/web_socket_execution.e
Normal file
21
library/network/websocket/server/src/web_socket_execution.e
Normal file
@@ -0,0 +1,21 @@
|
||||
note
|
||||
description: "Summary description for {WEB_SOCKET_EXECUTION}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WEB_SOCKET_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_WEBSOCKET_EXECUTION
|
||||
|
||||
WEB_SOCKET_EVENT_I
|
||||
|
||||
feature -- Websocket execution
|
||||
|
||||
new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
|
||||
do
|
||||
create Result.make (ws, Current)
|
||||
end
|
||||
|
||||
end
|
||||
30
library/network/websocket/server/src/web_socket_service.e
Normal file
30
library/network/websocket/server/src/web_socket_service.e
Normal file
@@ -0,0 +1,30 @@
|
||||
note
|
||||
description: "Summary description for {WEB_SOCKET_SERVICE}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WEB_SOCKET_SERVICE [G -> WEB_SOCKET_EXECUTION create make end]
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_and_launch
|
||||
local
|
||||
l_launcher: WSF_STANDALONE_WEBSOCKET_SERVICE_LAUNCHER [G]
|
||||
opts: WEB_SOCKET_SERVICE_OPTIONS
|
||||
do
|
||||
create opts
|
||||
set_options (opts)
|
||||
opts.append_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("ws.ini"))
|
||||
create l_launcher.make_and_launch (opts)
|
||||
end
|
||||
|
||||
set_options (opts: WEB_SOCKET_SERVICE_OPTIONS)
|
||||
-- Set values on `opts'.
|
||||
do
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
note
|
||||
description: "Summary description for {WEB_SOCKET_SERVICE_OPTIONS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WEB_SOCKET_SERVICE_OPTIONS
|
||||
|
||||
inherit
|
||||
WSF_STANDALONE_WEBSOCKET_SERVICE_OPTIONS
|
||||
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user