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