Merge branch 'master' of https://github.com/EiffelWebFramework/EWF into ewf_ws_compression

# Conflicts:
#	library/server/wsf/connector/standalone_websocket/websocket/web_socket.e
This commit is contained in:
jvelilla
2016-10-17 10:37:44 -03:00
151 changed files with 14370 additions and 781 deletions
+3 -6
View File
@@ -18,13 +18,10 @@
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf"/>
<library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
<library name="standalone" location="standalone-safe.ecf" readonly="false"/>
<library name="nino" location="nino-safe.ecf" readonly="false">
<renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/>
</library>
<library name="standalone_ws" location="standalone_websocket-safe.ecf" readonly="false"/>
<library name="openshift" location="openshift-safe.ecf" readonly="false"/>
<library name="nino" location="nino-safe.ecf" readonly="false"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="..\wsf-safe.ecf"/>
<cluster name="wsf_cgi" location=".\cgi\" recursive="true"/>
<cluster name="wsf_libfcgi" location=".\libfcgi\" recursive="true"/>
<cluster name="wsf_standalone" location=".\standalone\" 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-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="wsf_standalone" uuid="9BF2D71A-0986-4025-9C97-15B65F07C568" library_target="wsf_standalone">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="wsf_standalone" uuid="9BF2D71A-0986-4025-9C97-15B65F07C568" library_target="wsf_standalone">
<target name="wsf_standalone">
<root all_classes="true"/>
<file_rule>
@@ -7,16 +7,17 @@
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<option warning="true" full_class_checking="true" is_attached_by_default="true" is_obsolete_routine_type="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="connector_standalone" location="..\..\ewsgi\connectors\standalone\standalone-safe.ecf"/>
<library name="connector_standalone" location="..\..\ewsgi\connectors\standalone\standalone-safe.ecf" use_application_options="true"/>
<library name="encoder" location="..\..\..\text\encoder\encoder-safe.ecf" readonly="false"/>
<library name="error" location="..\..\..\utility\general\error\error-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf" use_application_options="true"/>
<library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="..\wsf-safe.ecf"/>
<library name="wsf" location="..\wsf-safe.ecf" use_application_options="true"/>
<cluster name="wsf_standalone" location=".\standalone\" recursive="true"/>
</target>
</system>
@@ -0,0 +1,24 @@
note
description: "[
Service using standalone connector launcher.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
class
WSF_STANDALONE_SERVICE [G -> WSF_EXECUTION create make end]
inherit
WSF_LAUNCHABLE_SERVICE
feature {NONE} -- Initialization
launch (opts: detachable WSF_SERVICE_LAUNCHER_OPTIONS)
local
l_launcher: WSF_STANDALONE_SERVICE_LAUNCHER [G]
do
create l_launcher.make_and_launch (opts)
end
end
@@ -76,7 +76,7 @@ feature {NONE} -- Initialization
verbose := opts.option_boolean_value ("verbose", verbose)
-- See `{HTTPD_REQUEST_HANDLER_I}.*_verbose_level`
if opts.has_integer_option ("verbose_level") then
verbose_level := opts.option_integer_value ("verbose_level", verbose_level)
elseif attached {READABLE_STRING_GENERAL} opts.option ("verbose_level") as s_verbose_level then
@@ -115,11 +115,17 @@ feature {NONE} -- Initialization
keep_alive_timeout := opts.option_integer_value ("keep_alive_timeout", keep_alive_timeout)
max_keep_alive_requests := opts.option_integer_value ("max_keep_alive_requests", max_keep_alive_requests)
if
opts.option_boolean_value ("ssl_enabled", ssl_enabled) and then
if
opts.option_boolean_value ("is_secure", is_secure) and then
attached opts.option_string_32_value ("secure_protocol", "tls_1_2") as l_secure_prot
then
secure_settings := [l_secure_prot, opts.option_string_32_value ("secure_certificate", Void), opts.option_string_32_value ("secure_certificate_key", Void)]
elseif
-- OBSOLETE: backward compatible with old settings name [oct/2016].
opts.option_boolean_value ("ssl_enabled", is_secure) and then
attached opts.option_string_32_value ("ssl_protocol", "tls_1_2") as ssl_prot
then
ssl_settings := [ssl_prot, opts.option_string_32_value ("ssl_ca_crt", Void), opts.option_string_32_value ("ssl_ca_key", Void)]
secure_settings := [ssl_prot, opts.option_string_32_value ("ssl_ca_crt", Void), opts.option_string_32_value ("ssl_ca_key", Void)]
end
end
@@ -135,7 +141,7 @@ feature {NONE} -- Initialization
-- Set `single_threaded' to True.
do
max_concurrent_connections := 1
end
end
feature -- Execution
@@ -143,7 +149,7 @@ feature -- Execution
do
cfg.set_is_verbose (verbose)
cfg.set_verbose_level (verbose_level)
cfg.set_ssl_settings (ssl_settings)
cfg.set_secure_settings (secure_settings)
cfg.set_http_server_name (server_name)
cfg.http_server_port := port_number
cfg.set_max_concurrent_connections (max_concurrent_connections)
@@ -165,7 +171,7 @@ feature -- Execution
debug ("ew_standalone")
if verbose then
io.error.put_string ("Launching standalone web server on port " + port_number.out)
if ssl_enabled then
if is_secure then
io.error.put_string ("%N https://")
else
io.error.put_string ("%N http://")
@@ -213,26 +219,36 @@ feature {NONE} -- Implementation
-- Help defining the verbosity.
-- The higher, the more output.
ssl_settings: detachable TUPLE [protocol: READABLE_STRING_GENERAL; ca_crt, ca_key: detachable READABLE_STRING_GENERAL]
ssl_enabled: BOOLEAN
-- Is secure server? i.e using SSL?
do
Result := attached ssl_settings as ssl and then attached ssl.protocol as prot and then not prot.is_whitespace
end
max_concurrent_connections: INTEGER
max_tcp_clients: INTEGER
socket_timeout: INTEGER
socket_recv_timeout: INTEGER
keep_alive_timeout: INTEGER
max_keep_alive_requests: INTEGER
single_threaded: BOOLEAN
do
Result := max_concurrent_connections = 0
end
max_tcp_clients: INTEGER
socket_timeout: INTEGER
socket_recv_timeout: INTEGER
keep_alive_timeout: INTEGER
max_keep_alive_requests: INTEGER
is_secure_connection_supported: BOOLEAN
-- Is SSL supported in current compiled system?
do
Result := {WGI_STANDALONE_CONSTANTS}.is_secure_connection_supported
end
is_secure: BOOLEAN
-- Is secure server? i.e using SSL?
do
Result := attached secure_settings as l_secure_settings and then
attached l_secure_settings.protocol as prot and then not prot.is_whitespace
end
secure_settings: detachable TUPLE [protocol: READABLE_STRING_GENERAL; ca_crt, ca_key: detachable READABLE_STRING_GENERAL]
feature -- Status report
connector: WGI_STANDALONE_CONNECTOR [G]
@@ -244,7 +260,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
@@ -0,0 +1,273 @@
note
description: "[
Options specific to standalone connector.
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_STANDALONE_SERVICE_OPTIONS
inherit
WSF_SERVICE_LAUNCHER_OPTIONS
feature -- Status report
is_secure_connection_supported: BOOLEAN
-- Is SSL/TLS supported by current compiled system?
do
Result := {WGI_STANDALONE_CONSTANTS}.is_secure_connection_supported
end
feature -- Access: output
is_verbose: BOOLEAN
-- Send verbose message to the output?
do
Result := option_boolean_value ("verbose", False)
end
verbose_level: detachable READABLE_STRING_8
-- Verbosity of output.
do
if attached {READABLE_STRING_GENERAL} option ("verbose_level") as l_verbose_level and then l_verbose_level.is_valid_as_string_8 then
Result := l_verbose_level.to_string_8
end
end
feature -- Access: connection
port: INTEGER assign set_port
-- Listening port number.
do
Result := option_integer_value ("port", 0)
end
server_name: detachable READABLE_STRING_8 assign set_server_name
-- Listening only for connection on `server_name' if defined.
do
if attached {READABLE_STRING_GENERAL} option ("server_name") as l_server_name and then l_server_name.is_valid_as_string_8 then
Result := l_server_name.to_string_8
end
end
base_url: detachable READABLE_STRING_8 assign set_base_url
do
if attached {READABLE_STRING_GENERAL} option ("base") as l_base and then l_base.is_valid_as_string_8 then
Result := l_base.to_string_8
end
end
max_concurrent_connections: INTEGER assign set_max_concurrent_connections
-- Maximum of concurrent connections.
-- Define the size of the concurrent pool.
do
Result := option_integer_value ("max_concurrent_connections", 0)
end
max_tcp_clients: INTEGER assign set_max_tcp_clients
-- Listen on socket for at most `queue' connections.
do
Result := option_integer_value ("max_tcp_clients", 0)
end
feature -- Access: network
socket_timeout: INTEGER assign set_socket_timeout
-- Amount of seconds that the server waits for receipts and transmissions during communications.
-- note: with timeout of 0, socket can wait for ever.
-- By default: {HTTPD_CONFIGURATION_I}.default_socket_timeout seconds, which is appropriate for most situations.
do
Result := option_integer_value ("socket_timeout", 0)
end
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
-- Amount of seconds that the server waits for receiving data during communications.
-- note: with timeout of 0, socket can wait for ever.
-- By default: {HTTPD_CONFIGURATION_I}.default_socket_recv_timeout seconds.
do
Result := option_integer_value ("socket_recv_timeout", 0)
end
feature -- Access: persistent connection
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
-- Persistent connection timeout.
-- Number of seconds the server waits after a request has been served before it closes the connection.
-- Timeout unit in Seconds.
-- By default: {HTTPD_CONFIGURATION_I}.default_keep_alive_timeout .
do
Result := option_integer_value ("keep_alive_timeout", 0)
end
max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
-- Maximum number of requests allowed per persistent connection.
-- Recommended a high setting.
-- To disable KeepAlive, set `max_keep_alive_requests' to 0.
-- By default: {HTTPD_CONFIGURATION_I}.default_max_keep_alive_requests .
do
Result := option_integer_value ("max_keep_alive_requests", 0)
end
feature -- Access: SSL
is_secure: BOOLEAN assign set_is_secure
-- Is SSL/TLS session?
do
Result := option_boolean_value ("is_secure", False)
end
secure_protocol: detachable READABLE_STRING_GENERAL assign set_secure_protocol
-- SSL protocol name, by default TLS 1.2
do
if attached {READABLE_STRING_GENERAL} option ("secure_protocol") as l_prot and then l_prot.is_valid_as_string_8 then
Result := l_prot.to_string_8
end
end
secure_certificate: detachable READABLE_STRING_GENERAL assign set_secure_certificate
-- Signed certificate.
do
if attached {READABLE_STRING_GENERAL} option ("secure_certificate") as l_ssl_ca_crt then
Result := l_ssl_ca_crt
end
end
secure_certificate_key: detachable READABLE_STRING_GENERAL assign set_secure_certificate_key
-- Private key for the certificate.
do
if attached {READABLE_STRING_GENERAL} option ("secure_certificate_key") as l_ssl_ca_key then
Result := l_ssl_ca_key
end
end
feature -- Element change
set_is_verbose (b: BOOLEAN)
do
set_boolean_option ("verbose", b)
end
set_verbose_level (lev: detachable READABLE_STRING_GENERAL)
-- Set `verbose_level' to `lev'.
do
set_string_option ("verbose_level", lev)
end
set_port (a_port_number: INTEGER)
do
set_numeric_option ("port", a_port_number)
end
set_server_name (v: detachable READABLE_STRING_8)
do
set_string_option ("server_name", v)
end
set_base_url (v: detachable READABLE_STRING_8)
do
set_string_option ("base_url", v)
end
set_max_tcp_clients (v: like max_tcp_clients)
-- Set `max_tcp_clients' with `v'.
do
set_numeric_option ("max_tcp_clients", v)
end
set_max_concurrent_connections (v: like max_concurrent_connections)
-- Set `max_concurrent_connections' with `v'.
do
set_numeric_option ("max_concurrent_connections", v)
end
set_socket_timeout (a_nb_seconds: like socket_timeout)
-- Set `socket_timeout' with `a_nb_seconds'
do
set_numeric_option ("socket_timeout", a_nb_seconds)
end
set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
-- Set `socket_recv_timeout' with `a_nb_seconds'
do
set_numeric_option ("socket_recv_timeout", a_nb_seconds)
end
set_keep_alive_timeout (a_nb_seconds: like keep_alive_timeout)
-- Set `keep_alive_timeout' with `a_nb_seconds'
do
set_numeric_option ("keep_alive_timeout", a_nb_seconds)
end
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
-- Set `max_keep_alive_requests' with `nb'
do
set_numeric_option ("max_keep_alive_requests", nb)
end
set_is_secure (b: BOOLEAN)
-- Set secured connection enabled to `b'.
-- i.e if connection is using SSL/TLS.
do
set_boolean_option ("is_secure", b)
end
set_secure_protocol_to_ssl_2_or_3
-- Set `ssl_protocol' with `Ssl_23'.
do
set_secure_protocol ("ssl_2_3")
end
set_secure_protocol_to_tls_1_0
-- Set `ssl_protocol' with `Tls_1_0'.
do
set_secure_protocol ("tls_1_0")
end
set_secure_protocol_to_tls_1_1
-- Set `ssl_protocol' with `Tls_1_1'.
do
set_secure_protocol ("tls_1_1")
end
set_secure_protocol_to_tls_1_2
-- Set `ssl_protocol' with `Tls_1_2'.
do
set_secure_protocol ("tls_1_2")
end
set_secure_protocol_to_dtls_1_0
-- Set `ssl_protocol' with `Dtls_1_0'.
do
set_secure_protocol ("dtls_1_0")
end
set_secure_protocol (a_prot: detachable READABLE_STRING_GENERAL)
-- Set `secure_protocol' with `a_version'
do
set_string_option ("secure_protocol", a_prot)
end
set_secure_certificate (a_value: detachable READABLE_STRING_GENERAL)
-- Set `secure_certificate' from `a_value'.
do
set_string_option ("secure_certificate", a_value)
end
set_secure_certificate_key (a_value: detachable READABLE_STRING_GENERAL)
-- Set `secure_certificate_key' with `a_value'.
do
set_string_option ("secure_certificate_key", a_value)
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end
@@ -16,10 +16,11 @@
<library name="error" location="..\..\..\utility\general\error\error-safe.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf"/>
<library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
<library name="httpd" location="..\..\ewsgi\connectors\standalone\lib\httpd\httpd-safe.ecf"/>
<library name="httpd" location="..\..\httpd\httpd-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="..\wsf-safe.ecf"/>
<library name="wsf_standalone" location="standalone-safe.ecf"/>
<library name="web_socket_protocol" location="..\..\..\network\websocket\protocol\web_socket_protocol-safe.ecf"/>
<cluster name="wsf_standalone_websocket" location=".\standalone_websocket\" recursive="true"/>
</target>
</system>
@@ -16,10 +16,11 @@
<library name="error" location="..\..\..\utility\general\error\error.ecf"/>
<library name="ewsgi" location="..\..\ewsgi\ewsgi.ecf"/>
<library name="http" location="..\..\..\network\protocol\http\http.ecf"/>
<library name="httpd" location="..\..\ewsgi\connectors\standalone\lib\httpd\httpd.ecf"/>
<library name="httpd" location="..\..\httpd\httpd.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="wsf" location="..\wsf.ecf"/>
<library name="wsf_standalone" location="standalone.ecf"/>
<library name="web_socket_protocol" location="..\..\..\network\websocket\protocol\web_socket_protocol.ecf"/>
<cluster name="wsf_standalone_websocket" location=".\standalone_websocket\" recursive="true"/>
</target>
</system>
@@ -122,6 +122,7 @@ feature -- Websocket events: implemented
ws_valid: ws.is_open_read and then ws.is_open_write
do
ws.send (Connection_close_frame, "")
on_close (ws)
end
note
@@ -1,203 +0,0 @@
note
description: "Constants for WebSockets"
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_CONSTANTS
feature -- Constants
HTTP_1_1: STRING = "HTTP/1.1 101 WebSocket Protocol Handshake"
Upgrade_ws: STRING = "Upgrade: websocket"
Connection_ws: STRING = "Connection: Upgrade"
Sec_WebSocket_Origin: STRING = "Sec-WebSocket-Origin: "
Sec_WebSocket_Protocol: STRING = "Sec-WebSocket-Protocol: "
Sec_WebSocket_Location: STRING = "Sec-WebSocket-Location: "
Sec_WebSocket_Version: STRING = "Sec-WebSocket-Version: "
Sec_WebSocket_Extensions: STRING = "Sec-WebSocket-Extensions: "
WebSocket_Origin: STRING = "WebSocket-Origin: "
WebSocket_Protocol: STRING = "WebSocket-Protocol: "
WebSocket_Location: STRING = "WebSocket-Location: "
Origin: STRING = "Origin"
Server: STRING = "EWSS"
Sec_WebSocket_Key: STRING = "Sec-WebSocket-Key"
Ws_scheme: STRING = "ws://"
Wss_scheme: STRING = "wss://"
Magic_guid: STRING = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
-- The handshake from the client looks as follows:
-- GET /chat HTTP/1.1
-- Host: server.example.com
-- Upgrade: websocket
-- Connection: Upgrade
-- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-- Origin: http://example.com
-- Sec-WebSocket-Protocol: chat, superchat
-- Sec-WebSocket-Version: 13
-- The handshake from the server looks as follows:
-- HTTP/1.1 101 Switching Protocols
-- Upgrade: websocket
-- Connection: Upgrade
-- Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-- Sec-WebSocket-Protocol: chat
feature -- Opcodes Standard actions
--| Maybe we need an enum STANDARD_ACTIONS_OPCODES?
-- |Opcode | Meaning | Reference |
-- -+--------+-------------------------------------+-----------|
-- | 0 | Continuation Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 1 | Text Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 2 | Binary Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 8 | Connection Close Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 9 | Ping Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
-- | 10 | Pong Frame | RFC 6455 |
-- -+--------+-------------------------------------+-----------|
Continuation_frame: INTEGER = 0
Text_frame: INTEGER = 1
Binary_frame: INTEGER = 2
Connection_close_frame: INTEGER = 8
Ping_frame: INTEGER = 9
Pong_frame: INTEGER = 10
is_control_frame (a_opcode: INTEGER): BOOLEAN
-- Is `a_opcode' a control frame?
do
inspect a_opcode
when Connection_close_frame, Ping_frame, Pong_frame then
Result := True
else
end
end
opcode_name (a_opcode: INTEGER): STRING
do
inspect a_opcode
when Continuation_frame then Result := "Continuation"
when Text_frame then Result := "Text"
when Binary_frame then Result := "Binary"
when Connection_close_frame then Result := "Connection Close"
when Ping_frame then Result := "Ping"
when Pong_frame then Result := "Pong"
else
Result := "Unknown-Opcode"
end
Result := "0x" + a_opcode.to_hex_string + " " + Result
end
feature -- Close code numbers
-- Maybe an ENUM CLOSE_CODES
-- |Status Code | Meaning | Contact | Reference |
-- -+------------+-----------------+---------------+-----------|
-- | 1000 | Normal Closure | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1001 | Going Away | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1002 | Protocol error | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1003 | Unsupported Data| hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1004 | ---Reserved---- | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1005 | No Status Rcvd | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1006 | Abnormal Closure| hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1007 | Invalid frame | hybi@ietf.org | RFC 6455 |
-- | | payload data | | |
-- -+------------+-----------------+---------------+-----------|
-- | 1008 | Policy Violation| hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1009 | Message Too Big | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1010 | Mandatory Ext. | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
-- | 1011 | Internal Server | hybi@ietf.org | RFC 6455 |
-- | | Error | | |
-- -+------------+-----------------+---------------+-----------|
-- | 1015 | TLS handshake | hybi@ietf.org | RFC 6455 |
-- -+------------+-----------------+---------------+-----------|
Normal_closure: INTEGER = 1000
-- Indicates a normal closure, meaning that the purpose for
-- which the connection was established has been fulfilled.
Going_away: INTEGER = 1001
-- Indicates that an endpoint is "going away", such as a server
-- going down or a browser having navigated away from a page.
Protocol_error: INTEGER = 1002
-- Indicates that an endpoint is terminating the connection due
-- to a protocol error.
Unsupported_data: INTEGER = 1003
-- Indicates that an endpoint is terminating the connection
-- because it has received a type of data it cannot accept (e.g., an
-- endpoint that understands only text data MAY send this if it
-- receives a binary message).
Invalid_data: INTEGER = 1007
-- Indicates that an endpoint is terminating the connection
-- because it has received data within a message that was not
-- consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
-- data within a text message).
Policy_violation: INTEGER = 1008
-- Indicates that an endpoint is terminating the connection
-- because it has received a message that violates its policy. This
-- is a generic status code that can be returned when there is no
-- other more suitable status code (e.g., 1003 or 1009) or if there
-- is a need to hide specific details about the policy.
Message_too_large: INTEGER = 1009
-- Indicates that an endpoint is terminating the connection
-- because it has received a message that is too big for it to
-- process.
Extension_required: INTEGER = 1010
-- Indicates that an endpoint (client) is terminating the
-- connection because it has expected the server to negotiate one or
-- more extension, but the server didn't return them in the response
-- message of the WebSocket handshake.
Internal_error: INTEGER = 1011
-- Indicates that a server is terminating the connection because
-- it encountered an unexpected condition that prevented it from
-- fulfilling the request.
end
@@ -1,35 +0,0 @@
note
description: "Summary description for {WEB_SOCKET_ERROR_FRAME}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WEB_SOCKET_ERROR_FRAME
create
make
feature {NONE} -- Initialization
make (a_code: INTEGER; a_desc: like description)
do
code := a_code
description := a_desc
end
feature -- Access
code: INTEGER
description: READABLE_STRING_8
feature -- Conversion
string: STRING
do
create Result.make_from_string ("Error(" + code.out + "): " + description)
end
end
@@ -1,455 +0,0 @@
note
description: "[
Summary description for {WEB_SOCKET_FRAME}.
See Base Framing Protocol: http://tools.ietf.org/html/rfc6455#section-5.2
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
Check the `check_utf_8_validity_on_chop' if there is performance issue
with bigger data.
]"
date: "$Date$"
revision: "$Revision$"
EIS: "name=Websocket RFC6455 section-5.2", "protocol=URI", "src=http://tools.ietf.org/html/rfc6455#section-5.2", "tag=rfc"
class
WEB_SOCKET_FRAME
inherit
ANY
WEB_SOCKET_CONSTANTS
create
make,
make_as_injected_control
feature {NONE} -- Initialization
make (a_opcode: INTEGER; flag_is_fin: BOOLEAN)
-- Create current frame with opcode `a_opcode'
-- and `a_fin' to indicate if this is the final fragment.
do
is_incomplete := False
opcode := a_opcode
is_fin := flag_is_fin
inspect opcode
when
Continuation_frame, -- 0
Text_frame, -- 1
Binary_frame -- 2
then
--| Supported opcode
when
Connection_close_frame, -- 8
Ping_frame, -- 9
Pong_frame -- 10
then
--| Supported control opcode
-- All control frames MUST have a payload length of 125 bytes or less
-- and MUST NOT be fragmented.
if flag_is_fin then
-- So far it is valid.
else
report_error (Protocol_error, "Control frames MUST NOT be fragmented.")
end
else
report_error (Protocol_error, "Unknown opcode")
end
end
make_as_injected_control (a_opcode: INTEGER; a_parent: WEB_SOCKET_FRAME)
require
parent_is_not_control_frame: not a_parent.is_control
a_opcode_is_control_frame: is_control_frame (a_opcode)
do
make (a_opcode, True)
parent := a_parent
a_parent.add_injected_control_frame (Current)
end
feature -- Access
opcode: INTEGER
-- CONTINUOUS, TEXT, BINARY, PING, PONG, CLOSING
is_fin: BOOLEAN
-- is the final fragment in a message?
is_rsv1: BOOLEAN
-- is extension negotiation in a message?
fragment_count: INTEGER
payload_length: NATURAL_64
payload_data: detachable STRING_8
-- Maybe we need a buffer here.
uncoded_payload_data: detachable STRING_32
local
utf: UTF_CONVERTER
do
if attached payload_data as d then
Result := utf.utf_8_string_8_to_string_32 (d)
end
end
error: detachable WEB_SOCKET_ERROR_FRAME
-- Describe the type of error
feature -- Access: injected control frames
injected_control_frames: detachable LIST [WEB_SOCKET_FRAME]
parent: detachable WEB_SOCKET_FRAME
-- If Current is injected, `parent' is the related fragmented frame
is_injected_control: BOOLEAN
do
Result := parent /= Void
ensure
Result implies (is_control_frame (opcode))
end
feature -- Operation
update_fin (a_flag_is_fin: BOOLEAN)
do
is_fin := a_flag_is_fin
end
update_rsv1 (a_flag_rsv1: BOOLEAN)
do
is_rsv1 := a_flag_rsv1
end
feature {WEB_SOCKET_FRAME} -- Change: injected control frames
add_injected_control_frame (f: WEB_SOCKET_FRAME)
require
Current_is_not_control: not is_control
f_is_control_frame: f.is_control
parented_to_current: f.parent = Current
local
lst: like injected_control_frames
do
lst := injected_control_frames
if lst = Void then
create {ARRAYED_LIST [WEB_SOCKET_FRAME]} lst.make (1)
injected_control_frames := lst
end
lst.force (f)
ensure
parented_to_current: f.parent = Current
end
remove_injected_control_frame (f: WEB_SOCKET_FRAME)
require
Current_is_not_control: not is_control
f_is_control_frame: f.is_control
parented_to_current: f.parent = Current
local
lst: like injected_control_frames
do
lst := injected_control_frames
if lst /= Void then
lst.prune (f)
if lst.is_empty then
injected_control_frames := Void
end
end
end
feature -- Query
is_binary: BOOLEAN
do
Result := opcode = binary_frame
end
is_text: BOOLEAN
do
Result := opcode = text_frame
end
is_continuation: BOOLEAN
do
Result := opcode = continuation_frame
end
is_connection_close: BOOLEAN
do
Result := opcode = connection_close_frame
end
is_control: BOOLEAN
do
inspect opcode
when connection_close_frame, Ping_frame, Pong_frame then
Result := True
else
end
end
is_ping: BOOLEAN
do
Result := opcode = ping_frame
end
is_pong: BOOLEAN
do
Result := opcode = pong_frame
end
feature -- Status report
is_valid: BOOLEAN
do
Result := not has_error
end
is_incomplete: BOOLEAN
has_error: BOOLEAN
do
Result := error /= Void
end
feature -- Change
increment_fragment_count
do
fragment_count := fragment_count + 1
end
check_utf_8_validity_on_chop: BOOLEAN = False
-- True: check for each chop
-- False: check only for each fragment
--| see autobahntestsuite #6.4.3 and #6.4.4
append_payload_data_chop (a_data: STRING_8; a_len: INTEGER; a_flag_chop_complete: BOOLEAN)
do
if a_flag_chop_complete then
increment_fragment_count
end
if attached payload_data as l_payload_data then
l_payload_data.append (a_data)
else
payload_data := a_data
end
payload_length := payload_length + a_len.to_natural_64
if is_text then
if is_fin and a_flag_chop_complete then
-- Check the whole message is a valid UTF-8 string
if attached payload_data as d then
if not is_valid_utf_8_string (d) then
report_error (invalid_data, "The text message is not a valid UTF-8 text!")
end
else
-- empty payload??
end
elseif check_utf_8_validity_on_chop or else a_flag_chop_complete then
-- Check the payload data as utf-8 stream (may be incomplete at this point)
if not is_valid_text_payload_stream then
report_error (invalid_data, "This is not a valid UTF-8 stream!")
-- is_valid implies the connection will be closed!
end
end
end
end
report_error (a_code: INTEGER; a_description: READABLE_STRING_8)
require
not has_error
do
create error.make (a_code, a_description)
ensure
has_error: has_error
is_not_valid: not is_valid
end
feature {NONE} -- Helper
last_utf_8_stream_validation_position: INTEGER
-- In relation with `is_valid_utf_8 (.., a_is_stream=True)'
is_valid_text_payload_stream: BOOLEAN
require
is_text_frame: is_text
do
if attached payload_data as s then
Result := is_valid_utf_8 (s, not is_fin)
end
end
is_valid_utf_8_string (s: READABLE_STRING_8): BOOLEAN
do
Result := is_valid_utf_8 (s, False)
-- and (create {UTF_CONVERTER}).is_valid_utf_8_string_8 (s)
end
is_valid_utf_8 (s: READABLE_STRING_8; a_is_stream: BOOLEAN): BOOLEAN
-- UTF-8 validity checker.
note
EIS: "name=UTF-8 RFC3629", "protocol=URI", "src=https://tools.ietf.org/html/rfc3629", "tag=rfc"
require
is_text_frame: is_text
local
i: like {STRING_8}.count
n: like {STRING_8}.count
c,c2,c3,c4,w: NATURAL_32
l_is_incomplete_stream: BOOLEAN
do
Result := True
-- Following code also check that codepoint is between 0 and 0x10FFFF
-- (as expected by spec, and tested by autobahn ws testsuite)
from
if a_is_stream then
i := last_utf_8_stream_validation_position -- to avoid recomputing from the beginning each time.
else
i := 0
end
n := s.count
until
i >= n or not Result
loop
i := i + 1
c := s.code (i)
if c <= 0x7F then
-- 0xxxxxxx
w := c
elseif c <= 0xC1 then
-- The octet values C0, C1, F5 to FF never appear.
--| case 0xC0 and 0xC1
Result := False
elseif (c & 0xE0) = 0xC0 then
-- 110xxxxx 10xxxxxx
i := i + 1
if i <= n then
c2 := s.code (i)
if
(c2 & 0xC0) = 0x80
then
w := ((c & 0x1F) |<< 6)
| (c2 & 0x3F)
Result := 0x80 <= w and w <= 0x7FF
else
Result := False
end
else
l_is_incomplete_stream := True
end
elseif (c & 0xF0) = 0xE0 then
-- 1110xxxx 10xxxxxx 10xxxxxx
i := i + 2
if i <= n then
c2 := s.code (i - 1)
c3 := s.code (i)
if
(c2 & 0xC0) = 0x80 and
(c3 & 0xC0) = 0x80
then
w := ((c & 0xF) |<< 12)
| ((c2 & 0x3F) |<< 6)
| (c3 & 0x3F)
if 0x800 <= w and w <= 0xFFFF then
if 0xD800 <= w and w <= 0xDFFF then
-- The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF
Result := False
end
else
Result := False
end
else
Result := False
end
else
if i - 1 <= n then
Result := (s.code (i - 1) & 0xC0) = 0x80
end
l_is_incomplete_stream := True
end
elseif (c & 0xF8) = 0xF0 then -- 0001 0000-0010 FFFF
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
if 0xF5 <= c and c <= 0xFF then
-- The octet values C0, C1, F5 to FF never appear.
Result := False
else
i := i + 3
if i <= n then
c2 := s.code (i - 2)
c3 := s.code (i - 1)
c4 := s.code (i)
if
(c2 & 0xC0) = 0x80 and
(c3 & 0xC0) = 0x80 and
(c4 & 0xC0) = 0x80
then
w := ((c & 0x7) |<< 18) |
((c2 & 0x3F) |<< 12) |
((c3 & 0x3F) |<< 6) |
(c4 & 0x3F)
Result := 0x1_0000 <= w and w <= 0x10_FFFF
else
Result := False
end
else
if i - 2 <= n then
c2 := s.code (i - 2)
Result := (c2 & 0xC0) = 0x80
if Result then
if c = 0xF4 and c2 >= 0x90 then
--| any byte 10xxxxxx (i.e >= 0x80) that would come after,
-- will result in out of range code point
-- indeed 0xF4 0x90 0x80 0x80 = 0x1100 0000 > 0x10_FFFF
Result := False
elseif i - 1 <= n then
Result := (s.code (i - 1) & 0xC0) = 0x80
end
end
end
l_is_incomplete_stream := True
end
end
else
-- Invalid byte in UTF-8
Result := False
end
if Result then
if l_is_incomplete_stream then
Result := a_is_stream
elseif a_is_stream then
last_utf_8_stream_validation_position := i
end
end
end
end
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end
@@ -267,20 +267,20 @@ feature -- Response!
l_opcode: NATURAL_32
do
l_message := a_message
if attached accepted_offer and then not on_handshake then
l_message := compress_string (l_message)
end
-- if attached accepted_offer and then not on_handshake then
-- l_message := compress_string (l_message)
-- end
debug ("ws")
print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N")
end
if not retried then
create l_header_message.make_empty
if attached accepted_offer and then not on_handshake then
l_opcode := (0x80 | a_opcode).to_natural_32
l_header_message.append_code ((l_opcode.bit_xor (0b1000000)))
else
-- if attached accepted_offer and then not on_handshake then
-- l_opcode := (0x80 | a_opcode).to_natural_32
-- l_header_message.append_code ((l_opcode.bit_xor (0b1000000)))
-- else
l_header_message.append_code ((0x80 | a_opcode).to_natural_32)
end
-- end
l_message_count := l_message.count
n := l_message_count.to_natural_64
if l_message_count > 0xffff then
@@ -301,31 +301,33 @@ feature -- Response!
else
l_header_message.append_code (n.as_natural_32)
end
socket.put_string (l_header_message)
l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable.
if l_message_count < l_chunk_size then
socket.put_string (l_message)
else
from
i := 0
until
l_chunk_size = 0
loop
socket.put_string_8_noexception (l_header_message)
if not socket.was_error then
l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable.
if l_message_count < l_chunk_size then
socket.put_string_8_noexception (a_message)
else
from
i := 0
until
l_chunk_size = 0 or socket.was_error
loop
debug ("ws")
print ("Sending chunk " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
end
l_chunk := a_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
socket.put_string_8_noexception (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 " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
print ("Sending chunk done%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
@@ -598,7 +600,7 @@ feature -- Response!
if attached accepted_offer then
-- Uncompress data.
Result.append_payload_data_chop (uncompress_string (l_chunk), l_bytes_read, l_remaining_len = 0)
-- Result.append_payload_data_chop (uncompress_string (l_chunk), l_bytes_read, l_remaining_len = 0)
else
Result.append_payload_data_chop (l_chunk, l_bytes_read, l_remaining_len = 0)
end
@@ -857,54 +859,54 @@ feature {NONE} -- Debug
feature -- PCME
uncompress_string (a_string: STRING): STRING
local
di: ZLIB_STRING_UNCOMPRESS
l_string: STRING
l_array: ARRAY [NATURAL_8]
l_byte: SPECIAL [INTEGER_8]
do
create l_string.make_from_string (a_string)
--Prepend 0x78 and 09c
l_string.prepend_character ((156).to_character_8)
l_string.prepend_character ((120).to_character_8)
-- uncompress_string (a_string: STRING): STRING
-- local
-- di: ZLIB_STRING_UNCOMPRESS
-- l_string: STRING
-- l_array: ARRAY [NATURAL_8]
-- l_byte: SPECIAL [INTEGER_8]
-- do
-- create l_string.make_from_string (a_string)
-- --Prepend 0x78 and 09c
-- l_string.prepend_character ((156).to_character_8)
-- l_string.prepend_character ((120).to_character_8)
-- Append 4 octects 0x00 0x00 0xff 0xff to the tail of the paiload message
l_string.append_character ((0x00).to_character_8)
l_string.append_character ((0x00).to_character_8)
l_string.append_character ((0xff).to_character_8)
l_string.append_character ((0xff).to_character_8)
-- -- Append 4 octects 0x00 0x00 0xff 0xff to the tail of the paiload message
-- l_string.append_character ((0x00).to_character_8)
-- l_string.append_character ((0x00).to_character_8)
-- l_string.append_character ((0xff).to_character_8)
-- l_string.append_character ((0xff).to_character_8)
l_array := string_to_array (l_string)
l_byte := byte_array (l_array)
-- l_array := string_to_array (l_string)
-- l_byte := byte_array (l_array)
create di.string_stream (l_string)
Result := di.to_string
debug ("ws")
print ("%NBytes uncompresses:" + di.total_bytes_uncompressed.out)
print ("%NUncompress message:" + Result)
end
end
-- create di.string_stream (l_string)
-- Result := di.to_string
-- debug ("ws")
-- print ("%NBytes uncompresses:" + di.total_bytes_uncompressed.out)
-- print ("%NUncompress message:" + Result)
-- end
-- end
compress_string (a_string: STRING): STRING
local
dc: ZLIB_STRING_COMPRESS
l_string: STRING
do
create Result.make_empty
create dc.string_stream (Result)
dc.mark_sync_flush
dc.put_string (a_string)
-- compress_string (a_string: STRING): STRING
-- local
-- dc: ZLIB_STRING_COMPRESS
-- l_string: STRING
-- do
-- create Result.make_empty
-- create dc.string_stream (Result)
-- dc.mark_sync_flush
-- dc.put_string (a_string)
Result := Result.substring (3, Result.count - 4)
-- Result := Result.substring (3, Result.count - 4)
debug ("ws")
print ("%NBytes uncompresses:" + dc.total_bytes_compressed.out )
end
end
-- debug ("ws")
-- print ("%NBytes uncompresses:" + dc.total_bytes_compressed.out )
-- end
-- end
byte_array (a_bytes: SPECIAL [NATURAL_8]) : SPECIAL [INTEGER_8]
@@ -0,0 +1,14 @@
note
description: "[
Options specific to websocket standalone connector.
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_STANDALONE_WEBSOCKET_SERVICE_OPTIONS
inherit
WSF_STANDALONE_SERVICE_OPTIONS
end
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="default_standalone" uuid="5CBA8C5A-3191-434A-8DE1-C0C3CAC9C4F4" library_target="default_standalone">
<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="default_standalone" uuid="5CBA8C5A-3191-434A-8DE1-C0C3CAC9C4F4" library_target="default_standalone">
<target name="default_standalone">
<root all_classes="true"/>
<file_rule>
@@ -7,10 +7,11 @@
<exclude>/\.git$</exclude>
<exclude>/\.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<option warning="true" full_class_checking="true" is_attached_by_default="true" is_obsolete_routine_type="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="wsf" location="..\wsf-safe.ecf"/>
<library name="wsf" location="..\wsf-safe.ecf" use_application_options="true"/>
<library name="wsf_standalone" location="..\connector\standalone-safe.ecf"/>
<cluster name="default_standalone" location=".\standalone\" recursive="true"/>
</target>
@@ -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="wsf" location="..\wsf.ecf"/>
<library name="wsf_standalone" location="..\connector\standalone.ecf"/>
@@ -75,6 +75,12 @@ feature -- Merging
end
end
import_ini_file_options (a_filename: READABLE_STRING_GENERAL)
-- Import options from ini file `a_filename'.
do
append_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file (a_filename))
end
feature -- Access
option (a_name: READABLE_STRING_GENERAL): detachable ANY
@@ -157,7 +163,7 @@ feature -- Helpers
Result := b
else
s := opt.out
Result := s.is_case_insensitive_equal ("true")
Result := s.is_case_insensitive_equal ("true") or s.is_case_insensitive_equal ("yes")
end
end
end
@@ -177,6 +183,34 @@ feature -- Element change
options.force (a_value, a_name)
end
unset_option (a_name: READABLE_STRING_GENERAL)
do
options.remove (a_name)
end
set_string_option (a_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL)
do
if a_value = Void then
unset_option (a_name)
else
set_option (a_name, a_value)
end
end
set_numeric_option (a_name: READABLE_STRING_GENERAL; a_value: NUMERIC)
do
set_option (a_name, a_value)
end
set_boolean_option (a_name: READABLE_STRING_GENERAL; a_value: BOOLEAN)
do
if a_value then
set_option (a_name, "true")
else
set_option (a_name, "false")
end
end
set_verbose (b: BOOLEAN)
-- Set option "verbose" to `b'
do
+17 -4
View File
@@ -288,7 +288,7 @@ feature -- Access: Input
end
end
if raw_input_data_recorded then
set_raw_input_data (buf.substring (buf_initial_size + 1, buf.count))
set_raw_input_data (buf.substring (buf_initial_size + 1, buf.count))
-- Only the input data! And differente reference.
end
end
@@ -833,6 +833,7 @@ feature -- Access: CGI meta parameters - 1.1
local
l_result: like internal_percent_encoded_path_info
r: READABLE_STRING_8
pi: READABLE_STRING_32
i,m,n,spos: INTEGER
do
l_result := internal_percent_encoded_path_info
@@ -844,11 +845,23 @@ feature -- Access: CGI meta parameters - 1.1
else
l_result := r.string
end
if attached script_name as s then
pi := path_info
i := 0
if pi.is_valid_as_string_8 then
i := l_result.substring_index (pi.to_string_8, 1)
if i > 0 then
-- Path info is included in REQUEST_URI
-- so let's get the percent encoded path info from request uri.
l_result := l_result.substring (i, l_result.count)
end
end
--| here "i = 0" means request_uri does not start with `path_info',
--| thus let's compute it from `script_name'.
if i = 0 and attached script_name as s then
if l_result.starts_with (s) then
l_result := l_result.substring (s.count + 1, l_result.count)
else
--| Handle Rewrite url engine, to have clean path
--| Handle Rewrite url engine, to have clean path
from
i := 1
m := l_result.count
@@ -2083,7 +2096,7 @@ invariant
wgi_request.content_type /= Void implies content_type /= Void
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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
@@ -51,7 +51,7 @@ feature -- Helper
if b = Void then
b := ""
end
Result := "/" + b + a_query_url
Result := b + a_query_url
end
end
@@ -11,6 +11,6 @@ feature -- Access
port_number: INTEGER = 9091
base_url: STRING = "" --"/test/"
base_url: STRING = "/test/"
end
@@ -32,7 +32,7 @@ feature -- Execution
feature -- Tests
test_script_url
test_script_url_1
local
req: WSF_REQUEST
s: READABLE_STRING_8
@@ -48,7 +48,13 @@ feature -- Tests
)
s := req.script_url ("/new/path/")
assert ("script_url (/new/path/) = %""+s+"%" but should be %"/foo/bar/new/path/%"", s.same_string ("/foo/bar/new/path/"))
end
test_script_url_2
local
req: WSF_REQUEST
s: READABLE_STRING_8
do
--| Case #2
req := new_request (<<
["REQUEST_METHOD", "GET"],
@@ -60,7 +66,13 @@ feature -- Tests
)
s := req.script_url ("/new/path/")
assert ("script_url (/new/path/) = %""+s+"%" but should be %"/foo/bar/new/path/%"", s.same_string ("/foo/bar/new/path/"))
end
test_script_url_3
local
req: WSF_REQUEST
s: READABLE_STRING_8
do
--| Case #3
req := new_request (<<
["REQUEST_METHOD", "GET"],
@@ -44,17 +44,17 @@ feature -- Test Cases
l_cookie.set_path ("/")
l_cookie.set_secure (True)
l_cookie.set_http_only (True)
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly"))
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly"))
w_res.put_header_text (l_header.string)
w_res.add_cookie (l_cookie)
w_res.set_status_code ({HTTP_STATUS_CODE}.ok)
w_res.put_string ("Test")
assert ("Expected", l_res.output.same_string("200 %R%NContent-Type: text/html%R%NSet-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly%R%N%R%NTest") )
assert ("Expected", l_res.output.same_string("200 %R%NContent-Type: text/html%R%NSet-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly%R%N%R%NTest") )
end
test_add_multiple_cookiewith_similar_cookie_name
test_add_multiple_cookies_with_similar_cookie_name
local
w_res: WSF_RESPONSE
l_cookie: WSF_COOKIE
@@ -73,7 +73,7 @@ feature -- Test Cases
l_cookie.set_path ("/")
l_cookie.set_secure (True)
l_cookie.set_http_only (True)
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly"))
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly"))
w_res.put_header_text (l_header.string)
w_res.add_cookie (l_cookie)
@@ -84,16 +84,16 @@ feature -- Test Cases
l_cookie.set_path ("/")
l_cookie.set_secure (True)
l_cookie.set_http_only (True)
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=newUser; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly"))
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=newUser; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly"))
w_res.add_cookie (l_cookie) -- Ignored
w_res.set_status_code ({HTTP_STATUS_CODE}.ok)
w_res.put_string ("Test")
assert ("Expected", l_res.output.same_string("200 %R%NContent-Type: text/html%R%NSet-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly%R%N%R%NTest") )
assert ("Expected", l_res.output.same_string("200 %R%NContent-Type: text/html%R%NSet-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly%R%N%R%NTest") )
end
test_add_multiple_cookie_with_similar_cookie_name_2
test_add_multiple_cookies_with_similar_cookie_name_2
local
w_res: WSF_RESPONSE
l_cookie: WSF_COOKIE
@@ -113,7 +113,7 @@ feature -- Test Cases
l_cookie.set_path ("/")
l_cookie.set_secure (True)
l_cookie.set_http_only (True)
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly"))
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly"))
w_res.add_cookie (l_cookie)
@@ -123,7 +123,7 @@ feature -- Test Cases
l_cookie.set_path ("/")
l_cookie.set_secure (True)
l_cookie.set_http_only (True)
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=newUser; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly"))
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: user_id=newUser; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly"))
w_res.add_cookie (l_cookie) -- Ignored
@@ -134,11 +134,11 @@ feature -- Test Cases
l_cookie.set_path ("/")
l_cookie.set_secure (True)
l_cookie.set_http_only (True)
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: ewf_sessionid=test; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly"))
assert("Expected", l_cookie.header_line.same_string ("Set-Cookie: ewf_sessionid=test; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly"))
w_res.add_cookie (l_cookie)
w_res.set_status_code ({HTTP_STATUS_CODE}.ok)
w_res.put_string ("Test")
assert ("Expected", l_res.output.same_string("200 %R%NContent-Type: text/html%R%NSet-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly%R%NSet-Cookie: ewf_sessionid=test; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Max-Age=-1; Secure; HttpOnly%R%N%R%NTest") )
assert ("Expected", l_res.output.same_string("200 %R%NContent-Type: text/html%R%NSet-Cookie: user_id=u12345; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly%R%NSet-Cookie: ewf_sessionid=test; Domain=www.example.com; Path=/; Expires=Sat, 18 Apr 2015 21:22:05 GMT; Secure; HttpOnly%R%N%R%NTest") )
end
end