Compare commits
6 Commits
es_rev1005
...
ewf_ws_com
| Author | SHA1 | Date | |
|---|---|---|---|
| d4f1839024 | |||
|
|
4a21dcd2d6 | ||
|
|
4c912912a6 | ||
|
|
406559f1c6 | ||
|
|
bbca08ab5e | ||
|
|
82c3e2aebb |
27
examples/websocket/stack.txt
Normal file
27
examples/websocket/stack.txt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
Call stack:
|
||||||
|
|
||||||
|
ObjectClassRoutine
|
||||||
|
------------------
|
||||||
|
[0x2405ABBA510] TCP_STREAM_SOCKET c_put_stream (From SOCKET) ( @ 0 )
|
||||||
|
[0x2405ABBA510] TCP_STREAM_SOCKET put_pointer_content (From SOCKET) ( @ 4 )
|
||||||
|
[0x2405ABBA510] TCP_STREAM_SOCKET put_managed_pointer (From SOCKET) ( @ 5 )
|
||||||
|
[0x2405ABBA510] TCP_STREAM_SOCKET put_readable_string_8 ( @ 2 )
|
||||||
|
[0x2405ABBA520] HTTPD_STREAM_SOCKET put_readable_string_8 ( @ 2 )
|
||||||
|
[0x2405ABBA528] WGI_STANDALONE_OUTPUT_STREAM put_string ( @ 4 )
|
||||||
|
[0x2405ABBA528] WGI_STANDALONE_OUTPUT_STREAM put_crlf (From WGI_OUTPUT_STREAM) ( @ 1 )
|
||||||
|
[0x2405ABBA528] WGI_STANDALONE_OUTPUT_STREAM put_header_line (From WGI_OUTPUT_STREAM) ( @ 2 )
|
||||||
|
[0x2405ABBA528] WGI_STANDALONE_OUTPUT_STREAM put_status_line ( @ 14 )
|
||||||
|
[0x2405ABBA530] WGI_STANDALONE_RESPONSE_STREAM set_status_code (From WGI_RESPONSE_STREAM) ( @ 6 )
|
||||||
|
[0x2405ABBA538] WSF_WGI_DELAYED_HEADER_RESPONSE set_status_code (From WGI_FILTER_RESPONSE) ( @ 4 )
|
||||||
|
[0x2405ABBA540] WSF_RESPONSE process_header ( @ 3 )
|
||||||
|
[0x2405ABBA538] WSF_WGI_DELAYED_HEADER_RESPONSE process_header ( @ 3 )
|
||||||
|
[0x2405ABBA538] WSF_WGI_DELAYED_HEADER_RESPONSE put_string ( @ 2 )
|
||||||
|
[0x2405ABBA540] WSF_RESPONSE put_string ( @ 2 )
|
||||||
|
[0x2405ABBA548] APPLICATION_EXECUTION execute ( @ 13 )
|
||||||
|
[0x2405ABBA548] APPLICATION_EXECUTION http_execute (From WSF_WEBSOCKET_EXECUTION) ( @ 8 )
|
||||||
|
[0x2405ABBA550] WGI_HTTPD_REQUEST_HANDLER process_request <R> ( @ 21 )
|
||||||
|
[0x2405ABBA550] WGI_HTTPD_REQUEST_HANDLER execute_request (From HTTPD_REQUEST_HANDLER_I) ( @ 27 )
|
||||||
|
[0x2405ABBA550] WGI_HTTPD_REQUEST_HANDLER execute (From HTTPD_REQUEST_HANDLER_I) ( @ 14 )
|
||||||
|
[0x2405ABBA550] WGI_HTTPD_REQUEST_HANDLER safe_execute (From HTTPD_REQUEST_HANDLER_I) <R> ( @ 4 )
|
||||||
|
|
||||||
6
examples/websocket/websocket_app.rc
Normal file
6
examples/websocket/websocket_app.rc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
1 "This Program was made using EiffelStudio using Visual Studio C++"
|
||||||
|
END
|
||||||
29
examples/websocket_compress/application.e
Normal file
29
examples/websocket_compress/application.e
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
note
|
||||||
|
description : "simple application root class"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION
|
||||||
|
|
||||||
|
create
|
||||||
|
make_and_launch
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make_and_launch
|
||||||
|
local
|
||||||
|
l_launcher: WSF_STANDALONE_WEBSOCKET_SERVICE_LAUNCHER [APPLICATION_EXECUTION_WS]
|
||||||
|
opts: WSF_SERVICE_LAUNCHER_OPTIONS
|
||||||
|
do
|
||||||
|
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} opts.make_from_file ("ws.ini")
|
||||||
|
create l_launcher.make_and_launch (options)
|
||||||
|
end
|
||||||
|
|
||||||
|
options: WSF_SERVICE_LAUNCHER_OPTIONS
|
||||||
|
-- Initialize current service.
|
||||||
|
do
|
||||||
|
create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} Result.make_from_file ("ws.ini")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
197
examples/websocket_compress/application_execution_ws.e
Normal file
197
examples/websocket_compress/application_execution_ws.e
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
note
|
||||||
|
description : "simple application execution"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION_EXECUTION_WS
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_WEBSOCKET_EXECUTION
|
||||||
|
redefine
|
||||||
|
initialize_websocket_options
|
||||||
|
end
|
||||||
|
|
||||||
|
WEB_SOCKET_EVENT_I
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Basic operations
|
||||||
|
|
||||||
|
execute
|
||||||
|
local
|
||||||
|
s: STRING
|
||||||
|
dt: HTTP_DATE
|
||||||
|
do
|
||||||
|
-- To send a response we need to setup, the status code and
|
||||||
|
-- the response headers.
|
||||||
|
-- if request.path_info.same_string_general ("/app") then
|
||||||
|
s := websocket_app_html (9090)
|
||||||
|
-- else
|
||||||
|
-- s := "Hello World!"
|
||||||
|
-- create dt.make_now_utc
|
||||||
|
-- s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||||||
|
-- s.append ("<p><a href=%"/app%">Websocket demo</a></p>")
|
||||||
|
-- end
|
||||||
|
if attached request.string_item ("exit") as s_exit and then s_exit.is_case_insensitive_equal_general ("now") then
|
||||||
|
(create {EXCEPTIONS}).die (-1)
|
||||||
|
end
|
||||||
|
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
||||||
|
response.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||||
|
response.header.put_content_type_text_html
|
||||||
|
response.header.put_content_length (s.count)
|
||||||
|
if attached request.http_connection as l_connection and then l_connection.is_case_insensitive_equal_general ("keep-alive") then
|
||||||
|
response.header.put_header_key_value ("Connection", "keep-alive")
|
||||||
|
end
|
||||||
|
response.put_string (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Websocket execution
|
||||||
|
|
||||||
|
new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
|
||||||
|
do
|
||||||
|
create Result.make (ws, Current)
|
||||||
|
end
|
||||||
|
|
||||||
|
initialize_websocket_options (ws: WEB_SOCKET)
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
ws.configure_pcme ((create {WEB_SOCKET_PMCE_DEFLATE_SERVER_SUPPORT_FACTORY}).basic_support)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Websocket execution
|
||||||
|
|
||||||
|
on_open (ws: WEB_SOCKET)
|
||||||
|
do
|
||||||
|
ws.put_error ("Connecting")
|
||||||
|
-- ws.send (Text_frame, "Hello, this is a simple demo with Websocket using Eiffel. (/help for more information).%N")
|
||||||
|
end
|
||||||
|
|
||||||
|
on_binary (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
ws.send (Binary_frame, a_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
if a_message.same_string_general ("/help") then
|
||||||
|
-- Echo the message for testing.
|
||||||
|
ws.send (Text_frame, "Help: available commands%N - /time : return the server UTC time.%N")
|
||||||
|
elseif a_message.starts_with_general ("/time") then
|
||||||
|
ws.send (Text_frame, "Server time is " + (create {HTTP_DATE}.make_now_utc).string)
|
||||||
|
else
|
||||||
|
-- Echo the message for testing.
|
||||||
|
ws.send (Text_frame, a_message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
on_close (ws: WEB_SOCKET)
|
||||||
|
-- Called after the WebSocket connection is closed.
|
||||||
|
do
|
||||||
|
ws.put_error ("Connection closed")
|
||||||
|
ws.send (Connection_close_frame, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- HTML Resource
|
||||||
|
|
||||||
|
websocket_app_html (a_port: INTEGER): STRING
|
||||||
|
do
|
||||||
|
Result := "[
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
var socket;
|
||||||
|
|
||||||
|
function connect(){
|
||||||
|
|
||||||
|
var host = "ws://127.0.0.1:##PORTNUMBER##";
|
||||||
|
|
||||||
|
try{
|
||||||
|
socket = new WebSocket(host);
|
||||||
|
message('<p class="event">Socket Status: '+socket.readyState);
|
||||||
|
socket.onopen = function(){
|
||||||
|
message('<p class="event">Socket Status: '+socket.readyState+' (open)');
|
||||||
|
}
|
||||||
|
socket.onmessage = function(msg){
|
||||||
|
message('<p class="message">Received: '+msg.data);
|
||||||
|
}
|
||||||
|
socket.onclose = function(){
|
||||||
|
message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
|
||||||
|
}
|
||||||
|
} catch(exception){
|
||||||
|
message('<p>Error'+exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function send(){
|
||||||
|
var text = $('#text').val();
|
||||||
|
if(text==""){
|
||||||
|
message('<p class="warning">Please enter a message');
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
socket.send(text);
|
||||||
|
message('<p class="event">Sent: '+text)
|
||||||
|
} catch(exception){
|
||||||
|
message('<p class="warning">');
|
||||||
|
}
|
||||||
|
$('#text').val("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function message(msg){
|
||||||
|
$('#chatLog').append(msg+'</p>');
|
||||||
|
}//End message()
|
||||||
|
|
||||||
|
$('#text').keypress(function(event) {
|
||||||
|
if (event.keyCode == '13') {
|
||||||
|
send();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#disconnect').click(function(){
|
||||||
|
socket.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!("WebSocket" in window)){
|
||||||
|
$('#chatLog, input, button, #examples').fadeOut("fast");
|
||||||
|
$('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
|
||||||
|
}else{
|
||||||
|
//The user has WebSockets
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<style type="text/css">
|
||||||
|
body {font-family:Arial, Helvetica, sans-serif;}
|
||||||
|
#container { border:5px solid grey; width:800px; margin:0 auto; padding:10px; }
|
||||||
|
#chatLog { padding:5px; border:1px solid black; }
|
||||||
|
#chatLog p {margin:0;}
|
||||||
|
.event {color:#999;}
|
||||||
|
.warning { font-weight:bold; color:#CCC; }
|
||||||
|
</style>
|
||||||
|
<title>WebSockets Client</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="wrapper">
|
||||||
|
<div id="container">
|
||||||
|
<h1>WebSockets Client</h1>
|
||||||
|
<div id="chatLog"></div>
|
||||||
|
<input id="text" type="text" />
|
||||||
|
<button id="disconnect">Disconnect</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
]"
|
||||||
|
Result.replace_substring_all ("##PORTNUMBER##", a_port.out)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
31
examples/websocket_compress/cert.crt
Normal file
31
examples/websocket_compress/cert.crt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFajCCBFKgAwIBAgIQdsKX6kswrkbK7NZ/kc31vTANBgkqhkiG9w0BAQsFADBC
|
||||||
|
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMS
|
||||||
|
UmFwaWRTU0wgU0hBMjU2IENBMB4XDTE2MDYxMDAwMDAwMFoXDTE3MDcxMDIzNTk1
|
||||||
|
OVowHzEdMBsGA1UEAwwUbG9jYWxob3N0LmRhcGxpZS5jb20wggEiMA0GCSqGSIb3
|
||||||
|
DQEBAQUAA4IBDwAwggEKAoIBAQCd49Z2PuWyX9qFlURgq8E0OzMP6szDLutkYBmW
|
||||||
|
sDdnekEw0mAUgmXcrhKcDog8ugDvcVqqOlice8rumL9OLMmRG3ObSzLV++2ETgBe
|
||||||
|
xpEawSJKj7UpCpw2EJtMFvSPXrHIMhkN4rUkh1Pzoo7+i4/MVIoDPljPgxPOtFNS
|
||||||
|
tECA/3kD2DlkIY/wOlkF8T1lg7A8Q92aVXiyIHmXubrHdT4bhr4YRbyvltEB2eA+
|
||||||
|
z4LLyqz+kMKHN1TYhMJUGur/C/Le3sNrhF2veqOCdPBomTwpWwJ4PPmN0kqeT0N3
|
||||||
|
D1CJVrt4Uj8W9N7fPsguYAehs5e06MCcAT3Dl1EqNNEJw/elAgMBAAGjggJ9MIIC
|
||||||
|
eTAfBgNVHREEGDAWghRsb2NhbGhvc3QuZGFwbGllLmNvbTAJBgNVHRMEAjAAMCsG
|
||||||
|
A1UdHwQkMCIwIKAeoByGGmh0dHA6Ly9ncC5zeW1jYi5jb20vZ3AuY3JsMG8GA1Ud
|
||||||
|
IARoMGYwZAYGZ4EMAQIBMFowKgYIKwYBBQUHAgEWHmh0dHBzOi8vd3d3LnJhcGlk
|
||||||
|
c3NsLmNvbS9sZWdhbDAsBggrBgEFBQcCAjAgDB5odHRwczovL3d3dy5yYXBpZHNz
|
||||||
|
bC5jb20vbGVnYWwwHwYDVR0jBBgwFoAUl8InUJ7CyewMiDLIfK3ipgFP2m8wDgYD
|
||||||
|
VR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBXBggr
|
||||||
|
BgEFBQcBAQRLMEkwHwYIKwYBBQUHMAGGE2h0dHA6Ly9ncC5zeW1jZC5jb20wJgYI
|
||||||
|
KwYBBQUHMAKGGmh0dHA6Ly9ncC5zeW1jYi5jb20vZ3AuY3J0MIIBAgYKKwYBBAHW
|
||||||
|
eQIEAgSB8wSB8ADuAHYA3esdK3oNT6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvswA
|
||||||
|
AAFVOonOzQAABAMARzBFAiEAzh4K7ZOSGCCFFvzAvrfl+o5AKcnmV7NHPgQZe3x4
|
||||||
|
hZgCIH/M2LZI1OSdkQbF2wgD/xH4PvQ4i8TTOdGB0WAYVr1eAHQApLkJkLQYWBSH
|
||||||
|
uxOizGdwCjw1mAT5G9+443fNDsgN3BAAAAFVOonO8gAABAMARTBDAh84cfLQthSR
|
||||||
|
Pe6hFgL8TPSuCUxIFBcEbnIPNB7ZxQwYAiBTClbmIn81bBkwAjasJu2u+UdxGE0i
|
||||||
|
Wx5lFe5X9pqsUTANBgkqhkiG9w0BAQsFAAOCAQEAAWYuT/fTBZdXb4kwoVaUnc82
|
||||||
|
2CEnGuOHr9QMdGRMqWJRe068StADdw1u3V6bcB7+mBiGl8C+WOLhv9WxYKqNFvyj
|
||||||
|
Eeaeekb4GqfrfuxNvoOU/vHdYaww2J9N1ESgIV4BdFF8aNgOnjpRcKSMsMgzNJdU
|
||||||
|
lh6l7jhnTeNYCyMnn+2dVQBcRQvptKmpkS4sK6NAVSMWDioImEoGj0PCdLqG8k21
|
||||||
|
d3vNddCEQmcNUTHs38nswUKZxfQKpjo+z9jBFmurmaNqSFnd8ySmBELZjXEOXEQz
|
||||||
|
KBlUSDj3UYVmH49t0toGyHVfKHPCBLyUZhvUTy0tNVgn9Nc+/MXJnv+c5rxVeQ==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
27
examples/websocket_compress/priv.key
Normal file
27
examples/websocket_compress/priv.key
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEAnePWdj7lsl/ahZVEYKvBNDszD+rMwy7rZGAZlrA3Z3pBMNJg
|
||||||
|
FIJl3K4SnA6IPLoA73FaqjpYnHvK7pi/TizJkRtzm0sy1fvthE4AXsaRGsEiSo+1
|
||||||
|
KQqcNhCbTBb0j16xyDIZDeK1JIdT86KO/ouPzFSKAz5Yz4MTzrRTUrRAgP95A9g5
|
||||||
|
ZCGP8DpZBfE9ZYOwPEPdmlV4siB5l7m6x3U+G4a+GEW8r5bRAdngPs+Cy8qs/pDC
|
||||||
|
hzdU2ITCVBrq/wvy3t7Da4Rdr3qjgnTwaJk8KVsCeDz5jdJKnk9Ddw9QiVa7eFI/
|
||||||
|
FvTe3z7ILmAHobOXtOjAnAE9w5dRKjTRCcP3pQIDAQABAoIBAD9lu7hxGvQbrvfS
|
||||||
|
bslOTd62IpOymROKZHRCbiPmj+iZ21FKN9AkZ9hLgSduYl/X5AZBAsG1ed0ji+Fw
|
||||||
|
Leiq7Si52Bq0AC6R4NYuJ9Hmc19Fy4oa2AgpvX2r/193HC3xPPuAujSsIkYPnLMI
|
||||||
|
Q9iLm2rVSzFwOGLiY/Ksz4Q24mupB6d2LvaKVlzX5p6T4E+lkHZchHwRKKDGZkjJ
|
||||||
|
AxrvF54ff3Y11gv4oBadeMMcZbypYuttYWh558SlHDocLHyh/DJ4bz+v9pWqQBTD
|
||||||
|
ifw09pMQwTGkbIjSYGx4n5GqqUIhQP32UK9JSTe6uLuj+HsnHVyZqUZIc2OgjBXu
|
||||||
|
iUhpceECgYEAzPAv6FAumdd6nKhgmOA1U6QTCfCUc4WuCDSI79puVufyJU8CzvyY
|
||||||
|
MIOEncIKTa3IT9+vdjA3jMrwqbsuRSv9O7QTaLETHj+H2ZM0dgTAJwBqENvJ7I6c
|
||||||
|
2mOhuzFTqBSFgZI0thtvGIMVB4uB0NDmDDxO2u36tIZdCh3wxGPahY0CgYEAxTq3
|
||||||
|
2UHanMKQP1ysxyh4YIt+2WWozzid6wc55kDQmC0tmQNEslHP67rRcAyNjaxctGoW
|
||||||
|
KdDGXNoFDXJUEDwAb57xqsp7dbmLZiRBWyerVScAhqeInd5FBU6Z14QoQXSHukzu
|
||||||
|
Qk9y5aVjd0gHMr99kEKb4gq9I6pSQrRV4RhUOHkCgYEAqG05oj7LncnWzgsfoGSb
|
||||||
|
zwrITuH2z15tIfczF5S4HBpyCrF9yGkWFsDv68t+Nkr9lo9qn8KO1Nowof3g0B6P
|
||||||
|
91JOpTrqUHC8EFeHxPqwhR01Dnfj4tNuG5nIYJTwgPGkq1Fimsu/KTEJxnKkFN6q
|
||||||
|
QiIKWKk+4sCOlHL43h6GiD0CgYEApBPSSOqRLOJr0FqP3mtboa3LDJ6XDjViotjz
|
||||||
|
IQuAvWiwS2A9sT+AgvJ7NAUtadQfl3O+FnF55Fr34xTevFa+Yh3PnK8wgGKohoPY
|
||||||
|
3zWkQoXc2Sjc7cCwmNaoA3QNbU69b456lC22PzgVBHGmk2Bc3DudpTIb4WTbZGTU
|
||||||
|
IRv1TLkCgYEAqlqBb3IXNWB2xLM8MkcAi8JA+m3LmmDYsEcPzjF7NEKPM8z0j4uT
|
||||||
|
IoX9skpZnvgDdsN6Lg0DNkXHFwq2zdrM2aM3nOVogAWd0qwoNB1HS2Pi3Bo/xth+
|
||||||
|
11eiPU3HZr4J48KG+6HV0FXWaa7OTfRE8oWU2UlyJvRTwhKp2QIjRe4=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
20
examples/websocket_compress/server.crt
Normal file
20
examples/websocket_compress/server.crt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDLjCCAhYCCQD0M/P4Fq0WWjANBgkqhkiG9w0BAQsFADBZMQswCQYDVQQGEwJB
|
||||||
|
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
||||||
|
cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTYxMDE3MTQ1MzM2WhcN
|
||||||
|
MTcxMDE3MTQ1MzM2WjBZMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0
|
||||||
|
ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAls
|
||||||
|
b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDArAIQLgOC
|
||||||
|
mpG7YsN+G/nvFCvV5yWMg97ydAb4mnDKs2yspYcPWHWVa83tBxvESlbxL2AtLqIE
|
||||||
|
zhOwyrS9/+LcOpjdgwx/ierkJvuENCAw1YLL+qZOtA0f4F9hMLSGl4taTgTZIF9v
|
||||||
|
IvsUCGUvkD+SfJYlFufjKV1zmZu9w4YumiaK5Og5X02ca/XvhAktKolP1cAiYJJy
|
||||||
|
oRi9G2OX0wvdElIzNM/1y9Qk4fHKPvTJwpocrdrQsYfywEtmgRbQBh3ptexkQ+a8
|
||||||
|
ajbgtzgWOt78ZP40p2lcVtl0C51BeyOO9qJq39Rdj226tEvxu7yvCCfvDZjjUDe2
|
||||||
|
BM5DUie1WDszAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEPFSvc639ObaL2w79It
|
||||||
|
Bi4wuIXjnmzEfQzeCot4AF/ukGYpwrDGM9ZIrUzB/3lF0sMRgc9tS1bGwsEGDNyQ
|
||||||
|
q7uXkLqrUDGUfBIeabhIQRNdEOvROBPF6nH9IFaSG7fitrMwZSrr/0i10dGLqnM6
|
||||||
|
aKOC3DSWvgcaFNfkEWZIceZtfjBjmqTsq1wAH0ktzi8hF+DlsdFzqmigkDykaqIr
|
||||||
|
Pig6zPxO0mhrEmvstI0W5JGlh9xyMzzKvyWvSsgRC5d9ilyGfp7BcPvOYTfW+Ktc
|
||||||
|
lEm8ySbCgERKy/KQUgRYeU3f1ilJbmqdIg8ZPvtj7imMkIw9lXg9fLRsvia2iv7J
|
||||||
|
eGk=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
11
examples/websocket_compress/server.csr
Normal file
11
examples/websocket_compress/server.csr
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIBmTCCAQICAQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
|
||||||
|
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9j
|
||||||
|
YWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLPusnlaVysrjHXpUk
|
||||||
|
4ls2fqd0IW25VEC4Vsl0x76dpDE3u5v1bWRi6Bd8i3PnIU4lmJnQFokVjW8e4etZ
|
||||||
|
xC+T/iNvacIxw/5unBsJ0vazrAjaPLaAEP+25weTYv8eqD/LQDcwVWvgEBGDZFOz
|
||||||
|
RGzm9kYpS3YZjt2G20OiJqDW0wIDAQABoAAwDQYJKoZIhvcNAQELBQADgYEANfOx
|
||||||
|
FzdJEYU+byESJsnZxDsu+c35jd49HDo6C4WjwHeadFEjqSYedyt1ymhJuhTp8+2c
|
||||||
|
sb+l263QeeAV7+XmROYrEx9AA/QPSHCepGWEen9e2gZHUH82fxzjoJSAbsJMSpPI
|
||||||
|
ITRMz7jLqvwZWSEjGGn1SMJ2a7PCB3AtxPJXdV4=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
27
examples/websocket_compress/server.key
Normal file
27
examples/websocket_compress/server.key
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAwKwCEC4DgpqRu2LDfhv57xQr1ecljIPe8nQG+JpwyrNsrKWH
|
||||||
|
D1h1lWvN7QcbxEpW8S9gLS6iBM4TsMq0vf/i3DqY3YMMf4nq5Cb7hDQgMNWCy/qm
|
||||||
|
TrQNH+BfYTC0hpeLWk4E2SBfbyL7FAhlL5A/knyWJRbn4yldc5mbvcOGLpomiuTo
|
||||||
|
OV9NnGv174QJLSqJT9XAImCScqEYvRtjl9ML3RJSMzTP9cvUJOHxyj70ycKaHK3a
|
||||||
|
0LGH8sBLZoEW0AYd6bXsZEPmvGo24Lc4Fjre/GT+NKdpXFbZdAudQXsjjvaiat/U
|
||||||
|
XY9turRL8bu8rwgn7w2Y41A3tgTOQ1IntVg7MwIDAQABAoIBAGw27/24al5LhHiT
|
||||||
|
0ysceXdPhANjDRLr6ieRBW1SCPHAbtRVMBNtYrVm1EVBzdWkz0otir41x+gc+rA8
|
||||||
|
WeGxO/DntH4NodJsWxKD1pZ9VGs5MHpysD/aHtj9g3SqD2rc6PbyhQSSiyd3pT+u
|
||||||
|
K27YfOxAr2/reph63jMUXoXpsI6xKKD+ouz7BfNsgXFdog8NYJimsyA9ol5IU6y5
|
||||||
|
g8qkO1rbxVza314Sitd1QDXIb0pb1QTQvsz01iXtJpjdsSJ5NG+5s8h6sFMuoqBw
|
||||||
|
IRbTxFnI7WyVkyRU4eZNPlj1yQzDN/TIPnFcF0D1KnhUJiYi//aJCuOMIVQNgfCq
|
||||||
|
ufhnr6kCgYEA+hquMFCqOe/KxK7bs8gUNOjurQf1DF/n+2pSdnCPwgFtJI2kaNtZ
|
||||||
|
B0eAkW/2QydHEEobkU4r7Yxi3enO0W+5Pxmf851/JY/HqLXHhB6HqwwMiFa8mdvw
|
||||||
|
ePJ75h2coCSFQGwAF+c4pfmO+SVFxPJBOTpgWYftYo5GwwMCro//VM0CgYEAxTa8
|
||||||
|
vQQ6WHmrI4K/qf7ZK+PStJvEdvX850qAF4crbvmpTtSubNdnk2WnCcWZbMiNaqWD
|
||||||
|
Rjx376khECrNJPj2k1i4IkD0XJg58Te/snj78vaMHMds43LaDEZlTq1izQkkoaAQ
|
||||||
|
gHc9SV8CppKwlGLESfaUoaU2G2eU8OXvU0o1z/8CgYB4mbj5D0ymV1zdbrv+ezJT
|
||||||
|
OScuRLIDX5PLhj9xYWHQ2UM55xsqfOr3OaFjgCutwhjgX/vfG2TPTWy3OXFDSK55
|
||||||
|
IPAiVwXipOxQDCfDK72b21mHvNuBDa9tgu//zLOj/ThMEkgRPqvwTOCGf1SfXXna
|
||||||
|
EK45PfRYG+c/TgpTBpjG2QKBgQCvf9uL1YRakcODcWpAzILUtQQgJ3I5bwSliz3c
|
||||||
|
MjSrqE9L0KZ8Q2Bv4gk4//hdv0dhvXLKASu2QesjaQNwQIGldFFAYk9TS0Bc8gCM
|
||||||
|
wW9ssozTW4ECE4YJYGftEQ0Ya2eG0Vt+Bx6p+XvxYh7zncUSEPYJt20kDBSWH/jP
|
||||||
|
RP4IXwKBgAkWzAQolIeEbIe/PNwR+KytnMTmVcdZJEz1bU5VdURSsIN/FgxOABIO
|
||||||
|
WoCFB/nv5+hq8lul9RrRE3SBHuqvhN2RxrdlNqbcag3nhEU4haQpnYlKQoAk0+0Q
|
||||||
|
PPdgFWXKrHQVdFxbIwO6KVL3MZ51toYaoB49EVtMkH1B6CACmMDq
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
30
examples/websocket_compress/server.pass.key
Normal file
30
examples/websocket_compress/server.pass.key
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: DES-EDE3-CBC,A5A863D1830AA8FC
|
||||||
|
|
||||||
|
9ZqIMHcqh8uavZ8R1/N57q48VIkHGz6KItSzKUrRAPrqhRAw4UZ7RSV3OpmeN3SC
|
||||||
|
CevtU9BTWnymInVTFKAM+cp/nLBdidBEjdT2V8XP6/FXhlvsSNBlqu1qENioR2xe
|
||||||
|
VoOhYkpRC3DHVh3hhzShbeFtJM3iktGQ1gVwv2FyVkn1ktimclOSecYLe1dq6BqM
|
||||||
|
HU3GX3Q5PczpJDExT3cxDrOsWtSKaznhrSzczian0b/vNohYnADHHxJCEC2Ayhtc
|
||||||
|
AYjxNCadv6hkhiFIWuEK1bHVVK5Lj8LvODNZjSfPM4+R4ghWeEUpGr0OeF301UZp
|
||||||
|
mYdsO9pzqIXaO6tDpBAgLAeEz1YpSqL4olO2spuFMR5vQjjeNikgrm4UjOfGfqlm
|
||||||
|
P/x4JjJMCahMjLsIU0Cmk6fQ8FlVYZGFTLJzlP5bb5ztlEoiS/0buehDAS6qnQBD
|
||||||
|
4Bs/KWB4OtT7hD7LHeFnylU65MwujUMMPIivB1eKN6DlFYc64FB8pJi2kWXEGggu
|
||||||
|
5EhLK5HPxnql4iQjV2tqL6TZ3V+XEYgicosEWPSMGR0iF94zA2BQcU3+upE/vT+A
|
||||||
|
W/LhMOkroEBffa9bgfiZ3S2/NUyyi7LSDmwPrLSgawJTN08zFPT16bZGk0P2+j+q
|
||||||
|
WU3A2UzU7BhqswsSb1q7lorhIYFaIXpFSVGHeYfnZit3kbzt3/KJx+wiJAo2fGrK
|
||||||
|
njrb7rpJcuafi0oN4lul7y0e8ap7iZ8Zj5nGbKu6037xlPTPXsuzBtnL2OS8/mSU
|
||||||
|
vJkuODJotddVjYAXp9aVtT129zxxkjwCsFb3AFeuFgKXSN2ynXwGNOFohQCBxnR5
|
||||||
|
pzVsnRT+/u4rlqv5/aefb3/R4j+NpYP8aKl3Bzv+fp880PAFtxBMPEFzNtmCbUcb
|
||||||
|
+RGDWt8iS5GJ2cHjGj59OwvOvJ9Rf/yfw84/zYvCdMmx+GxV7qWC2eAuy5OQkyzp
|
||||||
|
aB/qtaii2BStdI099LUBnFlmdDxn59EIdvIxyPZ5sd9Asfyd/hHs/rlCBy2jx35V
|
||||||
|
XQiSJuw3XzV/1XqNCfUs9K4rpmSM4Rc7SJuzIN1uz38izezcA3QbR0w6/+lMV/3Z
|
||||||
|
fjrJONGc3TFMs19WVXGPYs9/BgHkxkD2pwfNDvA6JlXEnpdP84vrQwaohOPmWbV/
|
||||||
|
WOu9HXhkbD2pNYq93sPtMcsi+jW5gcUyod8ldIF0am80n+fpEPpyyXtwOpY26sqa
|
||||||
|
i+E3Xq96sXb1G/Yi7B/4OI2NgDhOWHSkZmHmAdAlnK8QYPNwMsUBGZ0ox/+9ziQY
|
||||||
|
ilWrBSXZSsn92Q+Z5j03E/pfZkBhnwGpPn6/y1uyp4FG8nL6L1RoV05nAzR+td6G
|
||||||
|
1KBWN9iQOKfnnVbwaDKMHCE1lvcVlVe3EqsTAASp3gj7L4EzdqAyEo0u5L9wEIkw
|
||||||
|
M5L2ztzGyXCRXCgUzQ5FBvqrM/cBYqRdKjl0OHzn4ESTKSxqvzZu8A4eB45O+QMp
|
||||||
|
hfcLcGoPAOXDeIxQHginoSq1SDVI2xnpQspN2sL/WRjvpu5GySDBLQo1v2YJeUi5
|
||||||
|
vMRBMWeWCFjNl/aERJO+diMD3s29QqN+TYzlwuugJIAvr58hY1+9sw==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
15
examples/websocket_compress/simple.crt
Normal file
15
examples/websocket_compress/simple.crt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICWDCCAcGgAwIBAgIJAJnXGtV+PtiYMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
|
||||||
|
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||||||
|
aWRnaXRzIFB0eSBMdGQwHhcNMTUwNDAzMjIxNTA0WhcNMTYwNDAyMjIxNTA0WjBF
|
||||||
|
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||||||
|
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
|
||||||
|
gQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3HtHcKIl5wSUvn
|
||||||
|
G8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J53K0xuUlxz4d4
|
||||||
|
4D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQABo1AwTjAdBgNV
|
||||||
|
HQ4EFgQUbWpk2HoHa0YqpEwr7CGEatBFTMkwHwYDVR0jBBgwFoAUbWpk2HoHa0Yq
|
||||||
|
pEwr7CGEatBFTMkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAi+h4/
|
||||||
|
IgEocWkdRZBKHEcTrRxz5WhEDJMoVo9LhnXvCfn1G/4p6Un6sYv7Xzpi9NuSY8uV
|
||||||
|
cjfJJXhtF3AtyZ70iTAxWaRWjGaZ03PYOjlledJ5rqJEt6CCn8m+JsfznduZvbxQ
|
||||||
|
zQ6jCLXfyD/tvemB+yYEI3NntvRKx5/zt6Q26Q==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
15
examples/websocket_compress/simple.key
Normal file
15
examples/websocket_compress/simple.key
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXAIBAAKBgQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3H
|
||||||
|
tHcKIl5wSUvnG8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J5
|
||||||
|
3K0xuUlxz4d44D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQAB
|
||||||
|
AoGAR5efMg+dieRyLU8rieJcImxVbfOPg9gRsjdtIVkXTR+RL7ow59q7hXBo/Td/
|
||||||
|
WU8cm1gXoJ/bK+71YYqWyB+BaLRIWvRWb7Gdw203tu4e136Ca5uuY+71qdbVTVcl
|
||||||
|
NQ7J+T+eAQFP+a+DdT3ZQxu9eze87SMbu6i5YSpIk2kusOECQQDunv/DQ+nc+NgR
|
||||||
|
DF+Td3sNYUVRT9a1CWi6abAG6reXwp8MS4NobWDf+Ps4JODhEEwlIdq5qL7qqYBZ
|
||||||
|
Gc1TJJ53AkEA0404Fn6vAzzegBcS4RLlYTK7nMr0m4pMmDMCI6YzAYdMmKHp1e6f
|
||||||
|
IwxSmQrmwyAgwcT01bc0+A8yipcC2BWQaQJBAJ01QZm635OGmos41KsKF5bsE8gL
|
||||||
|
SpBBH69Yu/ECqGwie7iU84FUNnO4zIHjwghlPVVlZX3Vz9o4S+fn2N9DC+cCQGyZ
|
||||||
|
QyCxGdC0r5fbwHJQS/ZQn+UGfvlVzqoXDVMVn3t6ZES6YZrT61eHnOM5qGqklIxE
|
||||||
|
Old3vDZXPt/MU8Zvk3kCQBOgUx2VxvTrHN37hk9/QIDiM62+RenBm1M3ah8xTosf
|
||||||
|
1mSeEb6d9Kwb3TgPBmA7YXzJuAQfRIvEPMPxT5SSr6Q=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
28
examples/websocket_compress/websocket_app.ecf
Normal file
28
examples/websocket_compress/websocket_app.ecf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="websocket_app" uuid="75D17C20-10A8-4E4C-A059-33D72A2B6AEF">
|
||||||
|
<target name="websocket_app">
|
||||||
|
<root class="APPLICATION" feature="make_and_launch"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option concurrency="scoop" root_catcall_detection="none" root_concurrency="scoop" root_void_safety="all" profile="true" debug="true" warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||||||
|
<debug name="ws" enabled="true"/>
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<setting name="console_application" value="true"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="standalone_websocket_connector" location="..\..\library\server\wsf\connector\standalone_websocket-safe.ecf" readonly="false">
|
||||||
|
<option profile="true" debug="true">
|
||||||
|
<debug name="ws" enabled="true"/>
|
||||||
|
</option>
|
||||||
|
</library>
|
||||||
|
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||||||
|
<cluster name="app" location=".\" recursive="true">
|
||||||
|
<option profile="true">
|
||||||
|
</option>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
6
examples/websocket_compress/websocket_app.rc
Normal file
6
examples/websocket_compress/websocket_app.rc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
BEGIN
|
||||||
|
1 "This Program was made using EiffelStudio using Visual Studio C++"
|
||||||
|
END
|
||||||
15
examples/websocket_compress/ws.ini
Normal file
15
examples/websocket_compress/ws.ini
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
verbose=false
|
||||||
|
verbose_level=INFORMATION
|
||||||
|
port=9090
|
||||||
|
max_concurrent_connections=10
|
||||||
|
keep_alive_timeout=30
|
||||||
|
max_tcp_clients=30
|
||||||
|
socket_timeout=30000
|
||||||
|
max_keep_alive_requests=3000
|
||||||
|
|
||||||
|
### SSL settings
|
||||||
|
# enable SSL, with file certificate.
|
||||||
|
ssl_enabled=false
|
||||||
|
ssl_ca_key=server.key
|
||||||
|
ssl_ca_crt=server.crt
|
||||||
|
|
||||||
@@ -93,6 +93,9 @@ feature -- Access
|
|||||||
is_fin: BOOLEAN
|
is_fin: BOOLEAN
|
||||||
-- is the final fragment in a message?
|
-- is the final fragment in a message?
|
||||||
|
|
||||||
|
is_rsv1: BOOLEAN
|
||||||
|
-- is extension negotiation in a message?
|
||||||
|
|
||||||
fragment_count: INTEGER
|
fragment_count: INTEGER
|
||||||
|
|
||||||
payload_length: NATURAL_64
|
payload_length: NATURAL_64
|
||||||
@@ -111,6 +114,10 @@ feature -- Access
|
|||||||
error: detachable WEB_SOCKET_ERROR_FRAME
|
error: detachable WEB_SOCKET_ERROR_FRAME
|
||||||
-- Describe the type of error
|
-- Describe the type of error
|
||||||
|
|
||||||
|
raw_data_length: NATURAL_64
|
||||||
|
raw_data: detachable STRING_8
|
||||||
|
-- Contains raw data to be uncompressed.
|
||||||
|
|
||||||
feature -- Access: injected control frames
|
feature -- Access: injected control frames
|
||||||
|
|
||||||
injected_control_frames: detachable LIST [WEB_SOCKET_FRAME]
|
injected_control_frames: detachable LIST [WEB_SOCKET_FRAME]
|
||||||
@@ -132,6 +139,11 @@ feature -- Operation
|
|||||||
is_fin := a_flag_is_fin
|
is_fin := a_flag_is_fin
|
||||||
end
|
end
|
||||||
|
|
||||||
|
update_rsv1 (a_flag_rsv1: BOOLEAN)
|
||||||
|
do
|
||||||
|
is_rsv1 := a_flag_rsv1
|
||||||
|
end
|
||||||
|
|
||||||
feature {WEB_SOCKET_FRAME} -- Change: injected control frames
|
feature {WEB_SOCKET_FRAME} -- Change: injected control frames
|
||||||
|
|
||||||
add_injected_control_frame (f: WEB_SOCKET_FRAME)
|
add_injected_control_frame (f: WEB_SOCKET_FRAME)
|
||||||
@@ -251,6 +263,7 @@ feature -- Change
|
|||||||
if is_text then
|
if is_text then
|
||||||
if is_fin and a_flag_chop_complete then
|
if is_fin and a_flag_chop_complete then
|
||||||
-- Check the whole message is a valid UTF-8 string
|
-- Check the whole message is a valid UTF-8 string
|
||||||
|
-- iff is not is_rsv1
|
||||||
if attached payload_data as d then
|
if attached payload_data as d then
|
||||||
if not is_valid_utf_8_string (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!")
|
report_error (invalid_data, "The text message is not a valid UTF-8 text!")
|
||||||
@@ -268,6 +281,20 @@ feature -- Change
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
append_raw_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 raw_data as l_raw_data then
|
||||||
|
l_raw_data.append (a_data)
|
||||||
|
else
|
||||||
|
raw_data := a_data
|
||||||
|
end
|
||||||
|
raw_data_length := raw_data_length + a_len.to_natural_64
|
||||||
|
end
|
||||||
|
|
||||||
report_error (a_code: INTEGER; a_description: READABLE_STRING_8)
|
report_error (a_code: INTEGER; a_description: READABLE_STRING_8)
|
||||||
require
|
require
|
||||||
not has_error
|
not has_error
|
||||||
@@ -434,4 +461,14 @@ feature {NONE} -- Helper
|
|||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
|
<library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
|
||||||
<library name="httpd" location="..\..\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="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||||
|
<library name="zlib" location="$ISE_LIBRARY\unstable\library\compression\zlib\zlib-safe.ecf" readonly="false"/>
|
||||||
<library name="wsf" location="..\wsf-safe.ecf"/>
|
<library name="wsf" location="..\wsf-safe.ecf"/>
|
||||||
<library name="wsf_standalone" location="standalone-safe.ecf"/>
|
<library name="wsf_standalone" location="standalone-safe.ecf"/>
|
||||||
<library name="web_socket_protocol" location="..\..\..\network\websocket\protocol\web_socket_protocol-safe.ecf"/>
|
<library name="web_socket_protocol" location="..\..\..\network\websocket\protocol\web_socket_protocol-safe.ecf"/>
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
note
|
||||||
|
description: "{WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER} Parse the SEC_WEBSOCKET_EXTENSION header as par of websocket opening handshake."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
EIS: "name=Compression Extension for WebSocket"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialize
|
||||||
|
|
||||||
|
make (a_header: STRING_32)
|
||||||
|
do
|
||||||
|
header := a_header
|
||||||
|
create {ARRAYED_LIST [WEB_SOCKET_PMCE]} last_offers.make (0)
|
||||||
|
ensure
|
||||||
|
header_set: header = a_header
|
||||||
|
no_error: not has_error
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
header: STRING_32
|
||||||
|
-- Content raw header `Sec-Websocket-Extensions'.
|
||||||
|
|
||||||
|
last_offers: LIST [WEB_SOCKET_PMCE]
|
||||||
|
-- List of potential offered PMCE
|
||||||
|
--| From Sec-Websocket-Extensions header.
|
||||||
|
--| The order of elements is important as it specifies the client's preferences.
|
||||||
|
|
||||||
|
error_message: detachable STRING
|
||||||
|
-- last error message if any?
|
||||||
|
|
||||||
|
has_error: BOOLEAN
|
||||||
|
-- Has the extension header errors?
|
||||||
|
do
|
||||||
|
Result := attached error_message
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
feature -- Parse
|
||||||
|
|
||||||
|
parse
|
||||||
|
-- Parse `SEC-WEBSOCKET-EXTENSIONS' header.
|
||||||
|
-- The result is available in `last_offer'.
|
||||||
|
local
|
||||||
|
l_offers: ARRAYED_LIST [WEB_SOCKET_PMCE]
|
||||||
|
l_pcme: WEB_SOCKET_PMCE
|
||||||
|
do
|
||||||
|
create l_offers.make (1)
|
||||||
|
if attached header.split (',') as l_list then
|
||||||
|
-- Multiple offers separated by ',', if l_list.count > 1
|
||||||
|
across l_list as ic until has_error loop
|
||||||
|
-- Shared code extract to an external feature.
|
||||||
|
l_offers.force (parse_parameters (ic.item))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- we should raise an Issue.
|
||||||
|
end
|
||||||
|
if not has_error then
|
||||||
|
last_offers := l_offers
|
||||||
|
end
|
||||||
|
check
|
||||||
|
has_errors: has_error implies last_offers.is_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE}-- Parse Compression Extension.
|
||||||
|
|
||||||
|
parse_parameters (a_string: STRING_32): WEB_SOCKET_PMCE
|
||||||
|
local
|
||||||
|
l_validator: WEB_SOCKET_PMCE_DEFLATE_VALIDATOR
|
||||||
|
l_first: BOOLEAN
|
||||||
|
l_str: STRING_32
|
||||||
|
l_key: STRING_32
|
||||||
|
l_value: STRING_32
|
||||||
|
do
|
||||||
|
create l_validator
|
||||||
|
if attached a_string.split (';') as l_parameters then
|
||||||
|
-- parameters for the current offer.
|
||||||
|
create Result
|
||||||
|
across
|
||||||
|
l_parameters as ip
|
||||||
|
from
|
||||||
|
l_first := True
|
||||||
|
until
|
||||||
|
has_error
|
||||||
|
loop
|
||||||
|
if l_first then
|
||||||
|
l_str := ip.item
|
||||||
|
l_str.adjust
|
||||||
|
if not l_validator.name.same_string (l_str) then
|
||||||
|
create error_message.make_from_string ("Invalid PCME name: expected: `permessage-deflate` got `" + l_str + "`")
|
||||||
|
end
|
||||||
|
Result.set_name (l_str)
|
||||||
|
l_first := False
|
||||||
|
else
|
||||||
|
l_str := ip.item
|
||||||
|
l_str.adjust
|
||||||
|
-- valid parameter
|
||||||
|
if l_str.has ('=') then
|
||||||
|
-- The parameter has a value
|
||||||
|
-- server_max_window_bits = 10
|
||||||
|
l_key := l_str.substring (1, l_str.index_of ('=', 1) - 1) -- Exclude =
|
||||||
|
if l_validator.parameters.has (l_key) and then l_validator.parameters.at (l_key) then
|
||||||
|
l_value := l_str.substring (l_str.index_of ('=', 1) + 1, l_str.count) -- Exclude =
|
||||||
|
if Result.has (l_key) then
|
||||||
|
-- Unexpected value for parameter name
|
||||||
|
create error_message.make_from_string ("Invalid PCME value multiple occurences of parameter `" + l_str + "`")
|
||||||
|
else
|
||||||
|
if l_validator.sliding_windows_size.has (l_value) then
|
||||||
|
Result.force (l_value, l_key)
|
||||||
|
else
|
||||||
|
-- Unexpected value sliding window, value must be 8..15
|
||||||
|
create error_message.make_from_string ("Invalid PCME value for parameters with windows bits: expected a value between:`8 .. 15 ` got `" + l_value + "`")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Unexpected value for parameter name
|
||||||
|
create error_message.make_from_string ("Invalid PCME value for parameters: expected: `server_max_window_bits, client_max_window_bits ` got `" + l_str + "`")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if l_validator.parameters.has (l_str) then
|
||||||
|
if Result.has (l_str) then
|
||||||
|
-- Unexpected value for parameter name
|
||||||
|
create error_message.make_from_string ("Invalid PCME value multiple occurences of parameter `" + l_str + "`")
|
||||||
|
else
|
||||||
|
Result.force (Void, l_str)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Unexpected parameter name
|
||||||
|
create error_message.make_from_string ("Invalid PCME parameters: expected: `server_no_context_takeover, client_no_context_takeover, server_max_window_bits, client_max_window_bits ` got `" + l_str + "`")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Compression Extension simple
|
||||||
|
--| like
|
||||||
|
--| permessage-deflate
|
||||||
|
l_str := a_string
|
||||||
|
l_str.adjust
|
||||||
|
if not l_validator.name.same_string (l_str) then
|
||||||
|
create error_message.make_from_string ("Invalid PCME name: expected: `permessage-deflate` got `" + l_str + "`")
|
||||||
|
end
|
||||||
|
create Result
|
||||||
|
Result.set_name (l_str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
note
|
||||||
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
note
|
||||||
|
description: "{WEBSOCKET_PCME}, object that represent a websocket per-message compression extension."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_PMCE
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
name: detachable STRING_32
|
||||||
|
-- Compression extension name.
|
||||||
|
|
||||||
|
parameters: detachable STRING_TABLE [detachable STRING_32]
|
||||||
|
-- Compression extensions parameter.
|
||||||
|
|
||||||
|
feature -- Status Report
|
||||||
|
|
||||||
|
has (a_key: STRING_32): BOOLEAN
|
||||||
|
-- Is there an item in the table with key `a_key'?
|
||||||
|
do
|
||||||
|
if attached parameters as l_parameters then
|
||||||
|
Result := l_parameters.has (a_key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Change Element
|
||||||
|
|
||||||
|
set_name (a_name: STRING_32)
|
||||||
|
-- Set name with `a_name'.
|
||||||
|
do
|
||||||
|
name := a_name
|
||||||
|
ensure
|
||||||
|
name_set: name = a_name
|
||||||
|
end
|
||||||
|
|
||||||
|
force (a_value: detachable STRING_32; a_key: STRING_32)
|
||||||
|
-- Update table `parameters' so that `a_value' will be the item associated
|
||||||
|
-- with `a_key'.
|
||||||
|
local
|
||||||
|
l_parameters: like parameters
|
||||||
|
do
|
||||||
|
l_parameters := parameters
|
||||||
|
if attached l_parameters then
|
||||||
|
l_parameters.force (a_value, a_key)
|
||||||
|
else
|
||||||
|
create l_parameters.make_caseless (1)
|
||||||
|
l_parameters.force (a_value, a_key)
|
||||||
|
end
|
||||||
|
parameters := l_parameters
|
||||||
|
end
|
||||||
|
note
|
||||||
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WEB_SOCKET_PMCE_CONSTANTS_2}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_PMCE_CONSTANTS
|
||||||
|
|
||||||
|
|
||||||
|
feature -- Extension Parameters
|
||||||
|
|
||||||
|
Permessage_deflate: STRING = "permessage-deflate"
|
||||||
|
-- PCME permessage deflate name.
|
||||||
|
|
||||||
|
Default_window_size: INTEGER = 15
|
||||||
|
-- Default value for windows size.
|
||||||
|
|
||||||
|
Default_value_memory: INTEGER = 8
|
||||||
|
-- Default value for memory level.
|
||||||
|
|
||||||
|
Default_chunk_size: INTEGER = 32768
|
||||||
|
-- Default chunk size 2^15
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
note
|
||||||
|
description: "Object representing compression parameter client offer accepts by the server."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_PMCE_DEFLATE_ACCEPT
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_server_ctx: BOOLEAN; a_accept_server_window: BOOLEAN; a_server_window: INTEGER; a_client_ctx: BOOLEAN; a_accept_client_window: BOOLEAN; a_client_window: INTEGER; )
|
||||||
|
-- Create accepted offer.
|
||||||
|
do
|
||||||
|
accept_server_no_context_takeover := a_server_ctx
|
||||||
|
accept_server_max_window_bits := a_accept_server_window
|
||||||
|
server_max_window_bits := a_server_window
|
||||||
|
|
||||||
|
accept_client_no_context_takeover := a_client_ctx
|
||||||
|
accept_client_max_window_bits := a_accept_client_window
|
||||||
|
client_max_window_bits := a_client_window
|
||||||
|
end
|
||||||
|
|
||||||
|
accept_server_no_context_takeover: BOOLEAN
|
||||||
|
-- Does the response include this parameter?
|
||||||
|
|
||||||
|
accept_server_max_window_bits: BOOLEAN
|
||||||
|
-- Does the response include this parameter?
|
||||||
|
|
||||||
|
server_max_window_bits: INTEGER
|
||||||
|
-- Server sliding window.
|
||||||
|
|
||||||
|
accept_client_no_context_takeover: BOOLEAN
|
||||||
|
-- Does the response include this parameter?
|
||||||
|
|
||||||
|
accept_client_max_window_bits: BOOLEAN
|
||||||
|
-- Does the response include this parameter?
|
||||||
|
|
||||||
|
client_max_window_bits: INTEGER
|
||||||
|
-- Client sliding window.
|
||||||
|
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
extension_response: STRING
|
||||||
|
-- Generate response
|
||||||
|
do
|
||||||
|
create Result.make_from_string ({WEB_SOCKET_PMCE_CONSTANTS}.Permessage_deflate)
|
||||||
|
if accept_server_no_context_takeover then
|
||||||
|
Result.append_character (';')
|
||||||
|
Result.append ("server_no_context_takeover")
|
||||||
|
end
|
||||||
|
|
||||||
|
if accept_client_no_context_takeover then
|
||||||
|
Result.append_character (';')
|
||||||
|
Result.append ("client_no_context_takeover")
|
||||||
|
end
|
||||||
|
|
||||||
|
if accept_server_max_window_bits then
|
||||||
|
Result.append_character (';')
|
||||||
|
Result.append ("server_max_window_bits")
|
||||||
|
if server_max_window_bits > 0 then
|
||||||
|
Result.append_character ('=')
|
||||||
|
Result.append_integer (server_max_window_bits)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if accept_client_max_window_bits then
|
||||||
|
if client_max_window_bits > 0 then
|
||||||
|
Result.append_character (';')
|
||||||
|
Result.append ("client_max_window_bits")
|
||||||
|
Result.append_character ('=')
|
||||||
|
Result.append_integer (client_max_window_bits)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
note
|
||||||
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
note
|
||||||
|
description: "Object representing the accept offer from client to server"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_PMCE_DEFLATE_ACCEPT_FACTORY
|
||||||
|
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
accept_offer (a_server_conf: WEB_SOCKET_PMCE_DEFLATE_SERVER_SUPPORT; a_client: WEB_SOCKET_PMCE_DEFLATE_OFFER): detachable WEB_SOCKET_PMCE_DEFLATE_ACCEPT
|
||||||
|
-- Does the server accept the client offer?
|
||||||
|
-- Return the accepted offer or void in other case.
|
||||||
|
local
|
||||||
|
l_request_window: BOOLEAN
|
||||||
|
l_request_context: BOOLEAN
|
||||||
|
l_invalid: BOOLEAN
|
||||||
|
l_server_max_window_bits: INTEGER
|
||||||
|
l_client_max_window_bits: INTEGER
|
||||||
|
l_client_context: BOOLEAN
|
||||||
|
do
|
||||||
|
-- server_max_windows_bits
|
||||||
|
-- client request max_windows and server accept
|
||||||
|
if a_client.request_max_window_bits > 0 and then a_server_conf.accept_max_window_bits then
|
||||||
|
l_request_window := True
|
||||||
|
l_server_max_window_bits := a_client.request_max_window_bits
|
||||||
|
elseif a_client.request_max_window_bits = 0 then
|
||||||
|
l_request_window := False
|
||||||
|
else
|
||||||
|
-- Server does not support client offer
|
||||||
|
l_invalid := True
|
||||||
|
end
|
||||||
|
|
||||||
|
-- server_no_content_takeover
|
||||||
|
if a_client.request_no_context_take_over and then a_server_conf.accept_no_context_take_over then
|
||||||
|
l_request_context := True
|
||||||
|
elseif not a_client.request_no_context_take_over and then not a_server_conf.accept_no_context_take_over then
|
||||||
|
l_request_context := False
|
||||||
|
else
|
||||||
|
l_invalid := True
|
||||||
|
end
|
||||||
|
if not l_invalid then
|
||||||
|
create Result.make (l_request_context, l_request_window, l_server_max_window_bits, a_client.accept_no_context_take_over, a_client.accept_max_window_bits, a_server_conf.request_max_window_bits )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
note
|
||||||
|
description: "Object representing a permessage-deflate` WebSocket extension offered by a client to a server."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_PMCE_DEFLATE_OFFER
|
||||||
|
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make
|
||||||
|
-- create an object with default settings.
|
||||||
|
do
|
||||||
|
mark_accept_no_context_take_over
|
||||||
|
mark_accept_max_window_bits
|
||||||
|
mark_request_no_context_take_over
|
||||||
|
set_request_max_window_bits (0)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
accept_no_context_take_over: BOOLEAN
|
||||||
|
-- Does client accepts `no context takeover` feature?
|
||||||
|
-- Default value: True
|
||||||
|
|
||||||
|
accept_max_window_bits: BOOLEAN
|
||||||
|
-- Does client accepts setting `max window size`?
|
||||||
|
-- Default value: True.
|
||||||
|
|
||||||
|
request_no_context_take_over: BOOLEAN
|
||||||
|
-- Does client request `no context takeover` feature?
|
||||||
|
|
||||||
|
request_max_window_bits: INTEGER
|
||||||
|
-- Does client requests given `max window size?`
|
||||||
|
-- Valid value must be 8-15
|
||||||
|
-- Default 0.
|
||||||
|
|
||||||
|
feature -- Parse Params
|
||||||
|
|
||||||
|
configure (a_pcme: WEB_SOCKET_PMCE)
|
||||||
|
-- Configure websocket extension parameters an returns the provided offer by a client.
|
||||||
|
do
|
||||||
|
-- extension paramet defaults
|
||||||
|
accept_max_window_bits := False
|
||||||
|
accept_no_context_take_over := False
|
||||||
|
request_max_window_bits := 0
|
||||||
|
request_no_context_take_over := False
|
||||||
|
|
||||||
|
if attached a_pcme.parameters as l_parameters then
|
||||||
|
across l_parameters as ic loop
|
||||||
|
if ic.key.same_string ("client_max_window_bits") then
|
||||||
|
mark_accept_max_window_bits
|
||||||
|
elseif ic.key.same_string ("client_no_context_takeover") then
|
||||||
|
mark_accept_no_context_take_over
|
||||||
|
elseif ic.key.same_string ("server_max_window_bits") then
|
||||||
|
if attached ic.item as l_value then
|
||||||
|
set_request_max_window_bits (l_value.to_integer)
|
||||||
|
end
|
||||||
|
elseif ic.key.same_string ("server_no_context_takeover") then
|
||||||
|
mark_request_no_context_take_over
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Element Change
|
||||||
|
|
||||||
|
mark_accept_no_context_take_over
|
||||||
|
-- Set `accept_no_context_take_over` to True.
|
||||||
|
do
|
||||||
|
accept_no_context_take_over := True
|
||||||
|
ensure
|
||||||
|
accept_no_context_take_over_true: accept_no_context_take_over
|
||||||
|
end
|
||||||
|
|
||||||
|
unmark_accept_no_context_take_over
|
||||||
|
-- Set `accept_no_context_take_over` to False
|
||||||
|
do
|
||||||
|
accept_no_context_take_over := False
|
||||||
|
ensure
|
||||||
|
accept_no_context_take_over_false: not accept_no_context_take_over
|
||||||
|
end
|
||||||
|
|
||||||
|
mark_accept_max_window_bits
|
||||||
|
-- Set `accept_max_window_bits` to True
|
||||||
|
do
|
||||||
|
accept_max_window_bits := True
|
||||||
|
ensure
|
||||||
|
accept_max_window_bits_true: accept_max_window_bits
|
||||||
|
end
|
||||||
|
|
||||||
|
unmark_accept_max_window_bits
|
||||||
|
-- Set `accept_max_window_bits` to False
|
||||||
|
do
|
||||||
|
accept_max_window_bits := False
|
||||||
|
ensure
|
||||||
|
accept_max_window_bits_false: not accept_max_window_bits
|
||||||
|
end
|
||||||
|
|
||||||
|
mark_request_no_context_take_over
|
||||||
|
-- Set `request_no_context_take_over` to True.
|
||||||
|
do
|
||||||
|
request_no_context_take_over := True
|
||||||
|
ensure
|
||||||
|
request_no_context_take_over_true: request_no_context_take_over
|
||||||
|
end
|
||||||
|
|
||||||
|
unmark_request_no_context_take_over
|
||||||
|
-- Set `request_no_context_take_over` to False.
|
||||||
|
do
|
||||||
|
request_no_context_take_over := False
|
||||||
|
ensure
|
||||||
|
request_no_context_take_over_false: request_no_context_take_over
|
||||||
|
end
|
||||||
|
|
||||||
|
set_request_max_window_bits (a_bits: INTEGER)
|
||||||
|
-- Set `request_max_window_bits` to `a_bits`.
|
||||||
|
require
|
||||||
|
valid_range: (a_bits >= 8 and then a_bits <= 15) or else (a_bits = 0)
|
||||||
|
do
|
||||||
|
request_max_window_bits := a_bits
|
||||||
|
ensure
|
||||||
|
request_max_window_bits_set: request_max_window_bits = a_bits
|
||||||
|
end
|
||||||
|
|
||||||
|
;note
|
||||||
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
note
|
||||||
|
description: "Object representing serever support"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_PMCE_DEFLATE_SERVER_SUPPORT
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make
|
||||||
|
-- create an object with default settings.
|
||||||
|
do
|
||||||
|
mark_accept_no_context_take_over
|
||||||
|
mark_accept_max_window_bits
|
||||||
|
mark_request_no_context_take_over
|
||||||
|
set_request_max_window_bits (0)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
accept_no_context_take_over: BOOLEAN
|
||||||
|
-- Does server accepts `no context takeover` feature?
|
||||||
|
-- Default value: True
|
||||||
|
|
||||||
|
accept_max_window_bits: BOOLEAN
|
||||||
|
-- Does server accepts setting `max window size`?
|
||||||
|
-- Default value: True.
|
||||||
|
|
||||||
|
request_no_context_take_over: BOOLEAN
|
||||||
|
-- Does server request `no context takeover` feature?
|
||||||
|
|
||||||
|
request_max_window_bits: INTEGER
|
||||||
|
-- Does server requests given `max window size?`
|
||||||
|
-- Valid value must be 8-15
|
||||||
|
-- Default 0.
|
||||||
|
|
||||||
|
feature -- Element Change
|
||||||
|
|
||||||
|
mark_accept_no_context_take_over
|
||||||
|
-- Set `accept_no_context_take_over` to True.
|
||||||
|
do
|
||||||
|
accept_no_context_take_over := True
|
||||||
|
ensure
|
||||||
|
accept_no_context_take_over_true: accept_no_context_take_over
|
||||||
|
end
|
||||||
|
|
||||||
|
unmark_accept_no_context_take_over
|
||||||
|
-- Set `accept_no_context_take_over` to False
|
||||||
|
do
|
||||||
|
accept_no_context_take_over := False
|
||||||
|
ensure
|
||||||
|
accept_no_context_take_over_false: not accept_no_context_take_over
|
||||||
|
end
|
||||||
|
|
||||||
|
mark_accept_max_window_bits
|
||||||
|
-- Set `accept_max_window_bits` to True
|
||||||
|
do
|
||||||
|
accept_max_window_bits := True
|
||||||
|
ensure
|
||||||
|
accept_max_window_bits_true: accept_max_window_bits
|
||||||
|
end
|
||||||
|
|
||||||
|
unmark_accept_max_window_bits
|
||||||
|
-- Set `accept_max_window_bits` to False
|
||||||
|
do
|
||||||
|
accept_max_window_bits := False
|
||||||
|
ensure
|
||||||
|
accept_max_window_bits_false: not accept_max_window_bits
|
||||||
|
end
|
||||||
|
|
||||||
|
mark_request_no_context_take_over
|
||||||
|
-- Set `request_no_context_take_over` to True.
|
||||||
|
do
|
||||||
|
request_no_context_take_over := True
|
||||||
|
ensure
|
||||||
|
request_no_context_take_over_true: request_no_context_take_over
|
||||||
|
end
|
||||||
|
|
||||||
|
unmark_request_no_context_take_over
|
||||||
|
-- Set `request_no_context_take_over` to False.
|
||||||
|
do
|
||||||
|
request_no_context_take_over := False
|
||||||
|
ensure
|
||||||
|
request_no_context_take_over_false: request_no_context_take_over
|
||||||
|
end
|
||||||
|
|
||||||
|
set_request_max_window_bits (a_bits: INTEGER)
|
||||||
|
-- Set `request_max_window_bits` to `a_bits`.
|
||||||
|
require
|
||||||
|
valid_range: (a_bits >= 8 and then a_bits <= 15) or else (a_bits = 0)
|
||||||
|
do
|
||||||
|
request_max_window_bits := a_bits
|
||||||
|
ensure
|
||||||
|
request_max_window_bits_set: request_max_window_bits = a_bits
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WEB_SOCKET_PCME_DEFLATE_SERVER_SUPPORT_FACTORY}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_PMCE_DEFLATE_SERVER_SUPPORT_FACTORY
|
||||||
|
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
basic_support: WEB_SOCKET_PMCE_DEFLATE_SERVER_SUPPORT
|
||||||
|
-- client_max_window_bits: True
|
||||||
|
-- server_max_window_bits: False
|
||||||
|
-- client_content_take_over: False
|
||||||
|
-- server_content_take_over: False
|
||||||
|
do
|
||||||
|
create Result.make
|
||||||
|
Result.mark_accept_max_window_bits
|
||||||
|
Result.unmark_accept_no_context_take_over
|
||||||
|
Result.unmark_request_no_context_take_over
|
||||||
|
Result.set_request_max_window_bits ({WEB_SOCKET_PMCE_CONSTANTS}.default_window_size)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Object that validate a PMCE permessage defalate extension,
|
||||||
|
using the DEFLATE algorithm
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WEB_SOCKET_PMCE_DEFLATE_VALIDATOR
|
||||||
|
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
name: STRING = "permessage-deflate"
|
||||||
|
-- registered extension name.
|
||||||
|
|
||||||
|
|
||||||
|
parameters: STRING_TABLE [BOOLEAN]
|
||||||
|
-- extension parameters
|
||||||
|
note
|
||||||
|
EIS: "name=Extension Parameters", "src=https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-28#section-7.1", "protocol=url"
|
||||||
|
once
|
||||||
|
create Result.make_caseless (4)
|
||||||
|
Result.force (False, "server_no_context_takeover")
|
||||||
|
Result.force (False, "client_no_context_takeover")
|
||||||
|
Result.force (True, "server_max_window_bits")
|
||||||
|
Result.force (True, "client_max_window_bits")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
sliding_windows_size: STRING_TABLE [INTEGER]
|
||||||
|
-- LZ77 sliding window size.
|
||||||
|
--! Map with valid windows and the
|
||||||
|
--! context parameter, and integer value
|
||||||
|
--! between 8 and 15.
|
||||||
|
note
|
||||||
|
EIS:"name=Limiting the LZ77 sliding window size", "src=https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-28#section-7.1.2", "protocol=url"
|
||||||
|
once
|
||||||
|
create Result.make (7)
|
||||||
|
Result.force (256, "8")
|
||||||
|
Result.force (512, "9")
|
||||||
|
Result.force (1024, "10")
|
||||||
|
Result.force (2048, "11")
|
||||||
|
Result.force (4096, "12")
|
||||||
|
Result.force (8192, "13")
|
||||||
|
Result.force (16384, "14")
|
||||||
|
Result.force (32768, "15")
|
||||||
|
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
|
||||||
@@ -114,6 +114,20 @@ feature -- Element change
|
|||||||
verbose_level := lev
|
verbose_level := lev
|
||||||
end
|
end
|
||||||
|
|
||||||
|
configure_pcme (a_pcme_server: WEB_SOCKET_PMCE_DEFLATE_SERVER_SUPPORT)
|
||||||
|
-- Set `pcme_server_support` with `a_pcme_server`.
|
||||||
|
do
|
||||||
|
pmce_server_support := a_pcme_server
|
||||||
|
ensure
|
||||||
|
pcme_server_support_set: pmce_server_support = a_pcme_server
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
feature -- PMCE Compression
|
||||||
|
|
||||||
|
pmce_server_support : detachable WEB_SOCKET_PMCE_DEFLATE_SERVER_SUPPORT
|
||||||
|
-- Compression extension supported by the server.
|
||||||
|
|
||||||
feature -- Basic operation
|
feature -- Basic operation
|
||||||
|
|
||||||
put_error (a_message: READABLE_STRING_8)
|
put_error (a_message: READABLE_STRING_8)
|
||||||
@@ -142,10 +156,13 @@ feature -- Basic Operation
|
|||||||
-- Host: server.example.com
|
-- Host: server.example.com
|
||||||
-- Upgrade: websocket
|
-- Upgrade: websocket
|
||||||
-- Connection: Upgrade
|
-- Connection: Upgrade
|
||||||
|
--! Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
|
||||||
-- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
-- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||||||
-- Origin: http://example.com
|
-- Origin: http://example.com
|
||||||
-- Sec-WebSocket-Protocol: chat, superchat
|
-- Sec-WebSocket-Protocol: chat, superchat
|
||||||
-- Sec-WebSocket-Version: 13
|
-- Sec-WebSocket-Version: 13
|
||||||
|
note
|
||||||
|
EIS: "name=Compression Extensions for WebSocket", "src=https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-28", "protocol=url"
|
||||||
local
|
local
|
||||||
l_sha1: SHA1
|
l_sha1: SHA1
|
||||||
l_key : STRING
|
l_key : STRING
|
||||||
@@ -155,6 +172,7 @@ feature -- Basic Operation
|
|||||||
-- Reset values.
|
-- Reset values.
|
||||||
is_websocket := False
|
is_websocket := False
|
||||||
has_error := False
|
has_error := False
|
||||||
|
on_handshake:= True
|
||||||
|
|
||||||
-- Local cache.
|
-- Local cache.
|
||||||
req := request
|
req := request
|
||||||
@@ -184,9 +202,20 @@ feature -- Basic Operation
|
|||||||
l_version_key.is_case_insensitive_equal ("13") and then
|
l_version_key.is_case_insensitive_equal ("13") and then
|
||||||
attached req.http_host -- Host header must be present
|
attached req.http_host -- Host header must be present
|
||||||
then
|
then
|
||||||
|
-- here we can check for Sec-WebSocket-Extensions, it could be a collection of extensions.
|
||||||
|
if attached pmce_server_support and then attached req.meta_string_variable ("HTTP_SEC_WEBSOCKET_EXTENSIONS") as l_ws_extension then
|
||||||
|
-- at the moment we only handle web socket compression extension (PMCE permessage-deflate).
|
||||||
|
--| We need a way to define which compression algorithm the server support.
|
||||||
|
--|
|
||||||
|
handle_extensions (l_ws_extension)
|
||||||
|
end
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("key " + l_ws_key, debug_level)
|
log ("key " + l_ws_key, debug_level)
|
||||||
end
|
end
|
||||||
|
--! Before to send the response we need to check the Sec-Web-Socket extension.
|
||||||
|
--! We can write the compression extension here or just build
|
||||||
|
--! a set of classes and call them where it's needed.
|
||||||
|
|
||||||
-- Sending the server's opening handshake
|
-- Sending the server's opening handshake
|
||||||
create l_sha1.make
|
create l_sha1.make
|
||||||
l_sha1.update_from_string (l_ws_key + magic_guid)
|
l_sha1.update_from_string (l_ws_key + magic_guid)
|
||||||
@@ -195,6 +224,12 @@ feature -- Basic Operation
|
|||||||
res.header.add_header_key_value ("Connection", "Upgrade")
|
res.header.add_header_key_value ("Connection", "Upgrade")
|
||||||
res.header.add_header_key_value ("Sec-WebSocket-Accept", l_key)
|
res.header.add_header_key_value ("Sec-WebSocket-Accept", l_key)
|
||||||
|
|
||||||
|
-- Sec-WebSocket-Extensions
|
||||||
|
if attached pmce_server_support and then attached accepted_offer as l_offer
|
||||||
|
then
|
||||||
|
res.header.add_header_key_value ("Sec-WebSocket-Extensions", l_offer.extension_response)
|
||||||
|
end
|
||||||
|
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("%N================> Send Handshake", debug_level)
|
log ("%N================> Send Handshake", debug_level)
|
||||||
if attached {HTTP_HEADER} res.header as h then
|
if attached {HTTP_HEADER} res.header as h then
|
||||||
@@ -228,14 +263,26 @@ feature -- Response!
|
|||||||
l_message_count: INTEGER
|
l_message_count: INTEGER
|
||||||
n: NATURAL_64
|
n: NATURAL_64
|
||||||
retried: BOOLEAN
|
retried: BOOLEAN
|
||||||
|
l_message: STRING_8
|
||||||
|
l_opcode: NATURAL_32
|
||||||
|
l_remaning_len: INTEGER
|
||||||
do
|
do
|
||||||
|
l_message := a_message
|
||||||
|
if attached accepted_offer and then not on_handshake then
|
||||||
|
l_message := compress_string (l_message)
|
||||||
|
end
|
||||||
debug ("ws")
|
debug ("ws")
|
||||||
print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N")
|
print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N")
|
||||||
end
|
end
|
||||||
if not retried then
|
if not retried then
|
||||||
create l_header_message.make_empty
|
create l_header_message.make_empty
|
||||||
l_header_message.append_code ((0x80 | a_opcode).to_natural_32)
|
if attached accepted_offer and then not on_handshake then
|
||||||
l_message_count := a_message.count
|
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
|
||||||
|
l_message_count := l_message.count
|
||||||
n := l_message_count.to_natural_64
|
n := l_message_count.to_natural_64
|
||||||
if l_message_count > 0xffff then
|
if l_message_count > 0xffff then
|
||||||
--! Improve. this code needs to be checked.
|
--! Improve. this code needs to be checked.
|
||||||
@@ -257,27 +304,36 @@ feature -- Response!
|
|||||||
end
|
end
|
||||||
socket.put_string_8_noexception (l_header_message)
|
socket.put_string_8_noexception (l_header_message)
|
||||||
if not socket.was_error then
|
if not socket.was_error then
|
||||||
l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable.
|
if attached accepted_offer and then not on_handshake then
|
||||||
if l_message_count < l_chunk_size then
|
socket.put_string_8_noexception (l_message)
|
||||||
socket.put_string_8_noexception (a_message)
|
|
||||||
else
|
else
|
||||||
from
|
l_chunk_size := 0x8000 --16_384 -- 16K TODO: see if we should make it customizable.
|
||||||
i := 0
|
if l_message_count < l_chunk_size then
|
||||||
until
|
socket.put_string_8_noexception (l_message)
|
||||||
l_chunk_size = 0 or socket.was_error
|
else
|
||||||
loop
|
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_remaning_len := l_message_count - i*l_chunk_size
|
||||||
|
if l_remaning_len < l_chunk_size then
|
||||||
|
l_chunk := a_message.substring (i + 1, l_message_count.min (i + l_remaning_len))
|
||||||
|
else
|
||||||
|
l_chunk := a_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
|
||||||
|
end
|
||||||
|
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")
|
debug ("ws")
|
||||||
print ("Sending chunk " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
|
print ("Sending chunk done%N")
|
||||||
end
|
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 done%N")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -287,7 +343,7 @@ feature -- Response!
|
|||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
retried := True
|
retried := True
|
||||||
io.put_string ("Internal error in " + generator + ".do_send (conn, a_opcode=" + a_opcode.out + ", a_message) !%N")
|
io.put_string ("Internal error in " + generator + ".do_send (conn, a_opcode=" + a_opcode.out + ", l_message) !%N")
|
||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -343,6 +399,8 @@ feature -- Response!
|
|||||||
s: STRING
|
s: STRING
|
||||||
is_data_frame_ok: BOOLEAN -- Is the last process data framing ok?
|
is_data_frame_ok: BOOLEAN -- Is the last process data framing ok?
|
||||||
retried: BOOLEAN
|
retried: BOOLEAN
|
||||||
|
l_frame_rsv: INTEGER
|
||||||
|
-- l_ps: PROFILING_SETTING
|
||||||
do
|
do
|
||||||
if not retried then
|
if not retried then
|
||||||
l_socket := socket
|
l_socket := socket
|
||||||
@@ -370,6 +428,7 @@ feature -- Response!
|
|||||||
end
|
end
|
||||||
l_fin := l_byte & (0b10000000) /= 0
|
l_fin := l_byte & (0b10000000) /= 0
|
||||||
l_rsv := l_byte & (0b01110000) = 0
|
l_rsv := l_byte & (0b01110000) = 0
|
||||||
|
l_frame_rsv := (l_byte & 0x70) |>> 4
|
||||||
l_opcode := l_byte & 0b00001111
|
l_opcode := l_byte & 0b00001111
|
||||||
if Result /= Void then
|
if Result /= Void then
|
||||||
if l_opcode = Result.opcode then
|
if l_opcode = Result.opcode then
|
||||||
@@ -404,7 +463,15 @@ feature -- Response!
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- rsv validation
|
-- rsv validation
|
||||||
if not l_rsv then
|
if l_frame_rsv /= 0 then
|
||||||
|
if attached accepted_offer and then l_frame_rsv = 4 then
|
||||||
|
if Result.is_continuation then
|
||||||
|
Result.report_error (protocol_error, "RSV is set and no extension is negotiated")
|
||||||
|
else
|
||||||
|
on_handshake := False
|
||||||
|
Result.update_rsv1 (True)
|
||||||
|
end
|
||||||
|
elseif not l_rsv then
|
||||||
-- RSV1, RSV2, RSV3: 1 bit each
|
-- RSV1, RSV2, RSV3: 1 bit each
|
||||||
|
|
||||||
-- MUST be 0 unless an extension is negotiated that defines meanings
|
-- MUST be 0 unless an extension is negotiated that defines meanings
|
||||||
@@ -414,7 +481,8 @@ feature -- Response!
|
|||||||
-- Connection_
|
-- Connection_
|
||||||
|
|
||||||
-- FIXME: add support for extension ?
|
-- 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")
|
Result.report_error (protocol_error, "RSV values MUST be 0 unless an extension is negotiated that defines meanings for non-zero values")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
@@ -505,7 +573,7 @@ feature -- Response!
|
|||||||
Result.report_error (protocol_error, "All frames sent from client to server are masked!")
|
Result.report_error (protocol_error, "All frames sent from client to server are masked!")
|
||||||
end
|
end
|
||||||
if Result.is_valid then
|
if Result.is_valid then
|
||||||
l_chunk_size := 0x4000 -- 16 K
|
l_chunk_size := 0x8000 -- 16 K
|
||||||
if l_payload_len > {INTEGER_32}.max_value.to_natural_64 then
|
if l_payload_len > {INTEGER_32}.max_value.to_natural_64 then
|
||||||
-- Issue .. to big to store in STRING
|
-- Issue .. to big to store in STRING
|
||||||
-- FIXME !!!
|
-- FIXME !!!
|
||||||
@@ -540,7 +608,16 @@ feature -- Response!
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
l_fetch_count := l_fetch_count + l_bytes_read
|
l_fetch_count := l_fetch_count + l_bytes_read
|
||||||
Result.append_payload_data_chop (l_chunk, l_bytes_read, l_remaining_len = 0)
|
|
||||||
|
if attached accepted_offer then
|
||||||
|
-- Uncompress data.
|
||||||
|
Result.append_raw_data_chop (l_chunk, l_bytes_read, l_remaining_len = 0)
|
||||||
|
if Result.is_fin and then attached Result.raw_data as l_raw_data and then l_remaining_len = 0 then
|
||||||
|
Result.append_payload_data_chop (uncompress_string (l_raw_data), Result.raw_data_length.to_integer_32, l_remaining_len = 0)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result.append_payload_data_chop (l_chunk, l_bytes_read, l_remaining_len = 0)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
Result.report_error (internal_error, "Issue reading payload data...")
|
Result.report_error (internal_error, "Issue reading payload data...")
|
||||||
end
|
end
|
||||||
@@ -794,7 +871,205 @@ feature {NONE} -- Debug
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
note
|
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)
|
||||||
|
|
||||||
|
---- l_array := string_to_array (l_string)
|
||||||
|
---- l_byte := byte_array (l_array)
|
||||||
|
|
||||||
|
-- TODO add logic to compute window size based on extension negotiation.
|
||||||
|
|
||||||
|
create di.string_stream_with_size (l_string, {WEB_SOCKET_PMCE_CONSTANTS}.default_chunk_size)
|
||||||
|
|
||||||
|
-- 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)
|
||||||
|
Result := di.to_string_with_options (-{WEB_SOCKET_PMCE_CONSTANTS}.default_window_size)
|
||||||
|
|
||||||
|
debug ("ws")
|
||||||
|
print ("%NBytes uncompresses:" + di.total_bytes_uncompressed.out)
|
||||||
|
end
|
||||||
|
-- Result := do_uncompress (a_string)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
do_uncompress (a_string: STRING): STRING
|
||||||
|
local
|
||||||
|
di: ZLIB_STRING_UNCOMPRESS
|
||||||
|
l_string: STRING
|
||||||
|
do
|
||||||
|
create l_string.make_from_string (a_string)
|
||||||
|
di := string_uncompress
|
||||||
|
if attached di then
|
||||||
|
-- create di.string_stream_with_size (l_string, {WEB_SOCKET_PCME_CONSTANTS}.default_chunk_size)
|
||||||
|
di.set_string (l_string)
|
||||||
|
|
||||||
|
-- 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)
|
||||||
|
Result := di.to_string_with_options (-{WEB_SOCKET_PMCE_CONSTANTS}.default_window_size)
|
||||||
|
else
|
||||||
|
create di.string_stream_with_size(l_string, {WEB_SOCKET_PMCE_CONSTANTS}.default_chunk_size)
|
||||||
|
|
||||||
|
-- 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)
|
||||||
|
Result := di.to_string_with_options (-{WEB_SOCKET_PMCE_CONSTANTS}.default_window_size)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
string_uncompress: detachable ZLIB_STRING_UNCOMPRESS
|
||||||
|
|
||||||
|
compress_string (a_string: STRING): STRING
|
||||||
|
local
|
||||||
|
dc: ZLIB_STRING_COMPRESS
|
||||||
|
l_string: STRING
|
||||||
|
do
|
||||||
|
|
||||||
|
debug ("ws")
|
||||||
|
print ("%NBegin compresses:" + a_string.count.out)
|
||||||
|
end
|
||||||
|
-- TODO add logic to compute window size based on extension negotiation.
|
||||||
|
create Result.make_empty
|
||||||
|
create dc.string_stream_with_size (Result, {WEB_SOCKET_PMCE_CONSTANTS}.default_chunk_size)
|
||||||
|
dc.mark_full_flush
|
||||||
|
dc.put_string_with_options (a_string, {ZLIB_CONSTANTS}.Z_default_compression, -{WEB_SOCKET_PMCE_CONSTANTS}.default_window_size, {WEB_SOCKET_PMCE_CONSTANTS}.default_value_memory, {ZLIB_CONSTANTS}.z_default_strategy.to_integer_32)
|
||||||
|
Result := Result.substring (1, Result.count - 4)
|
||||||
|
|
||||||
|
-- Result := do_compress (a_string)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
do_compress (a_string: STRING): STRING
|
||||||
|
local
|
||||||
|
dc: ZLIB_STRING_COMPRESS
|
||||||
|
do
|
||||||
|
create Result.make_empty
|
||||||
|
dc := string_compress
|
||||||
|
|
||||||
|
if attached dc then
|
||||||
|
dc.set_string (Result)
|
||||||
|
dc.mark_sync_flush
|
||||||
|
dc.put_string_with_options (a_string, {ZLIB_CONSTANTS}.Z_default_compression, -{WEB_SOCKET_PMCE_CONSTANTS}.default_window_size, 9, {ZLIB_CONSTANTS}.z_default_strategy.to_integer_32)
|
||||||
|
Result := Result.substring (1, Result.count - 4)
|
||||||
|
else
|
||||||
|
create dc.string_stream_with_size(Result, {WEB_SOCKET_PMCE_CONSTANTS}.default_chunk_size)
|
||||||
|
dc.mark_sync_flush
|
||||||
|
dc.put_string_with_options (a_string, {ZLIB_CONSTANTS}.Z_default_compression, -{WEB_SOCKET_PMCE_CONSTANTS}.default_window_size, 9, {ZLIB_CONSTANTS}.z_default_strategy.to_integer_32)
|
||||||
|
Result := Result.substring (1, Result.count - 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
string_compress: detachable ZLIB_STRING_COMPRESS
|
||||||
|
|
||||||
|
byte_array (a_bytes: SPECIAL [NATURAL_8]) : SPECIAL [INTEGER_8]
|
||||||
|
local
|
||||||
|
i: INTEGER
|
||||||
|
do
|
||||||
|
|
||||||
|
create Result.make_filled (0,a_bytes.count)
|
||||||
|
across a_bytes as c
|
||||||
|
loop
|
||||||
|
Result.put(to_byte(c.item.as_integer_8), i)
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
to_byte (a_val : INTEGER) : INTEGER_8
|
||||||
|
-- takes a value between 0 and 255
|
||||||
|
-- Result :-128 to 127
|
||||||
|
do
|
||||||
|
if a_val >= 128 then
|
||||||
|
Result := (-256 + a_val).to_integer_8
|
||||||
|
else
|
||||||
|
Result := a_val.to_integer_8
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
result_value : 127 >= Result and Result >= -128
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
string_to_array (s: STRING): ARRAY [NATURAL_8]
|
||||||
|
local
|
||||||
|
i, n: INTEGER
|
||||||
|
c: INTEGER
|
||||||
|
do
|
||||||
|
n := s.count
|
||||||
|
create Result.make_empty
|
||||||
|
if n > 0 then
|
||||||
|
from
|
||||||
|
i := 1
|
||||||
|
until
|
||||||
|
i > n
|
||||||
|
loop
|
||||||
|
c := s [i].code
|
||||||
|
check
|
||||||
|
c <= 0xFF
|
||||||
|
end
|
||||||
|
Result.force (c.as_natural_8, i)
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
feature {NONE} -- Extensions
|
||||||
|
|
||||||
|
on_handshake: BOOLEAN
|
||||||
|
|
||||||
|
|
||||||
|
handle_extensions (a_extension: READABLE_STRING_32)
|
||||||
|
local
|
||||||
|
l_parse: WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER
|
||||||
|
l_offers: LIST [WEB_SOCKET_PMCE]
|
||||||
|
l_accepted: BOOLEAN
|
||||||
|
l_offer:WEB_SOCKET_PMCE_DEFLATE_OFFER
|
||||||
|
l_client_offer: WEB_SOCKET_PMCE_DEFLATE_OFFER
|
||||||
|
l_accept_offer_accept: WEB_SOCKET_PMCE_DEFLATE_ACCEPT_FACTORY
|
||||||
|
do
|
||||||
|
-- TODO improve handle
|
||||||
|
-- at the moment only check we have permessage_compression
|
||||||
|
--| TODO add validation and select the best offer.
|
||||||
|
|
||||||
|
create l_parse.make (a_extension)
|
||||||
|
l_parse.parse
|
||||||
|
l_offers := l_parse.last_offers
|
||||||
|
if not l_offers.is_empty then
|
||||||
|
create l_accept_offer_accept
|
||||||
|
across l_offers as ic
|
||||||
|
until
|
||||||
|
l_accepted
|
||||||
|
loop
|
||||||
|
create l_client_offer.make
|
||||||
|
l_client_offer.configure (ic.item)
|
||||||
|
if attached pmce_server_support as l_server_suppor then
|
||||||
|
accepted_offer := l_accept_offer_accept.accept_offer (l_server_suppor, l_client_offer)
|
||||||
|
l_accepted := attached accepted_offer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
accepted_offer: detachable WEB_SOCKET_PMCE_DEFLATE_ACCEPT
|
||||||
|
-- Accepted compression extension.
|
||||||
|
|
||||||
|
;note
|
||||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ note
|
|||||||
description: "[
|
description: "[
|
||||||
Request execution based on attributes `request' and `response'.
|
Request execution based on attributes `request' and `response'.
|
||||||
Also support Upgrade to Websocket protocol.
|
Also support Upgrade to Websocket protocol.
|
||||||
|
|
||||||
|
|
||||||
]"
|
]"
|
||||||
author: "$Author$"
|
author: "$Author$"
|
||||||
@@ -29,6 +29,7 @@ feature -- Execution
|
|||||||
ws_h: like new_websocket_handler
|
ws_h: like new_websocket_handler
|
||||||
do
|
do
|
||||||
create ws.make (request, response)
|
create ws.make (request, response)
|
||||||
|
initialize_websocket_options (ws)
|
||||||
ws.open_ws_handshake
|
ws.open_ws_handshake
|
||||||
if ws.is_websocket then
|
if ws.is_websocket then
|
||||||
if ws.has_error then
|
if ws.has_error then
|
||||||
@@ -51,6 +52,18 @@ feature -- Execution
|
|||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
|
feature -- WebSocket Options
|
||||||
|
|
||||||
|
initialize_websocket_options (ws: WEB_SOCKET)
|
||||||
|
-- Set web socket options (extensions) to be used as part of the ws opem handshake
|
||||||
|
--| for example set pcme algorithm etc.
|
||||||
|
--| Other option is create a new Class WEB_SOCKET_OPTION/ or
|
||||||
|
--| WEB_SOCKET_EXTENTIONS
|
||||||
|
--| defining all potenial extensions to the protocol.
|
||||||
|
do
|
||||||
|
-- To be redefined
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Factory
|
feature -- Factory
|
||||||
|
|
||||||
new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
|
new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
note
|
||||||
|
description: "standalone_websocket application root class"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
ARGUMENTS
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make
|
||||||
|
-- Run application.
|
||||||
|
do
|
||||||
|
--| Add your code here
|
||||||
|
print ("Hello Eiffel World!%N")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Eiffel tests that can be executed by testing tool.
|
||||||
|
]"
|
||||||
|
author: "EiffelStudio test wizard"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
testing: "type/manual"
|
||||||
|
|
||||||
|
class
|
||||||
|
EXTENSION_PARAMETER_TEST_SET
|
||||||
|
|
||||||
|
inherit
|
||||||
|
EQA_TEST_SET
|
||||||
|
|
||||||
|
feature -- Test routines
|
||||||
|
|
||||||
|
basic_extension
|
||||||
|
-- Test basic extension
|
||||||
|
local
|
||||||
|
l_pcme_parser: WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER
|
||||||
|
l_offers: LIST [WEB_SOCKET_PCME]
|
||||||
|
do
|
||||||
|
create l_pcme_parser.make ("permessage-deflate")
|
||||||
|
l_pcme_parser.parse
|
||||||
|
l_offers := l_pcme_parser.last_offers
|
||||||
|
assert ("one element", l_offers.count = 1)
|
||||||
|
assert ("extension name: permessage-defalte", attached l_offers.at (1).name as l_name and then l_name.same_string (("permessage-deflate")))
|
||||||
|
assert ("no parameters", l_offers.at (1).parameters = Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
extension_with_client_and_windows_bits
|
||||||
|
local
|
||||||
|
l_pcme_parser: WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER
|
||||||
|
l_offers: LIST [WEB_SOCKET_PCME]
|
||||||
|
do
|
||||||
|
create l_pcme_parser.make ("permessage-deflate; client_max_window_bits; server_max_window_bits=10")
|
||||||
|
l_pcme_parser.parse
|
||||||
|
l_offers := l_pcme_parser.last_offers
|
||||||
|
assert ("one element", l_offers.count = 1)
|
||||||
|
assert ("extension name: permessage-defalte", attached l_offers.at (1).name as l_name and then l_name.same_string (("permessage-deflate")))
|
||||||
|
if attached l_offers.at (1).parameters as l_parameters then
|
||||||
|
assert ("two parameters", l_parameters.count = 2)
|
||||||
|
assert ("exist client_max_window_bits", l_parameters.has ("client_max_window_bits"))
|
||||||
|
assert ("empty value for client_max_window_bits", l_parameters.item ("client_max_window_bits") = Void)
|
||||||
|
assert ("exist server_max_window_bits", l_parameters.has ("server_max_window_bits"))
|
||||||
|
assert ("value 10 for server_max_window_bits", attached l_parameters.item ("server_max_window_bits") as l_val and then l_val.same_string ("10"))
|
||||||
|
else
|
||||||
|
assert("Unexpected case", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
extension_with_wrong_pcme_name
|
||||||
|
local
|
||||||
|
l_pcme_parser: WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER
|
||||||
|
l_offers: LIST [WEB_SOCKET_PCME]
|
||||||
|
do
|
||||||
|
create l_pcme_parser.make ("permessage-7z")
|
||||||
|
l_pcme_parser.parse
|
||||||
|
l_offers := l_pcme_parser.last_offers
|
||||||
|
assert ("empty offers", l_offers.is_empty)
|
||||||
|
assert ("has_errors ", l_pcme_parser.has_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
extension_with_parameter_not_defined
|
||||||
|
local
|
||||||
|
l_pcme_parser: WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER
|
||||||
|
l_offers: LIST [WEB_SOCKET_PCME]
|
||||||
|
do
|
||||||
|
create l_pcme_parser.make ("permessage-deflate; max_window_bits; server_max_window_bits=10")
|
||||||
|
l_pcme_parser.parse
|
||||||
|
l_offers := l_pcme_parser.last_offers
|
||||||
|
assert ("empty offers", l_offers.is_empty)
|
||||||
|
assert ("has_errors ", l_pcme_parser.has_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
extension_with_parameter_and_not_needed_value
|
||||||
|
local
|
||||||
|
l_pcme_parser: WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER
|
||||||
|
l_offers: LIST [WEB_SOCKET_PCME]
|
||||||
|
do
|
||||||
|
create l_pcme_parser.make ("permessage-deflate; client_no_context_takeover=10")
|
||||||
|
l_pcme_parser.parse
|
||||||
|
l_offers := l_pcme_parser.last_offers
|
||||||
|
assert ("empty offers", l_offers.is_empty)
|
||||||
|
assert ("has_errors ", l_pcme_parser.has_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
extension_with_multiple_parameters_with_same_name
|
||||||
|
local
|
||||||
|
l_pcme_parser: WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER
|
||||||
|
l_offers: LIST [WEB_SOCKET_PCME]
|
||||||
|
do
|
||||||
|
create l_pcme_parser.make ("permessage-deflate; client_no_context_takeover; client_no_context_takeover")
|
||||||
|
l_pcme_parser.parse
|
||||||
|
l_offers := l_pcme_parser.last_offers
|
||||||
|
assert ("empty offers", l_offers.is_empty)
|
||||||
|
assert ("has_errors ", l_pcme_parser.has_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
extension_with_parameter_with_wrong_value
|
||||||
|
local
|
||||||
|
l_pcme_parser: WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER
|
||||||
|
l_offers: LIST [WEB_SOCKET_PCME]
|
||||||
|
do
|
||||||
|
create l_pcme_parser.make ("permessage-deflate; client_no_context_takeover; server_max_window_bits=0")
|
||||||
|
l_pcme_parser.parse
|
||||||
|
l_offers := l_pcme_parser.last_offers
|
||||||
|
assert ("empty offers", l_offers.is_empty)
|
||||||
|
assert ("has_errors ", l_pcme_parser.has_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="standalone_websocket" uuid="DCC8967B-6B94-439C-AB71-300D41D5AD2E">
|
||||||
|
<target name="standalone_websocket">
|
||||||
|
<root class="APPLICATION" feature="make"/>
|
||||||
|
<option concurrency="scoop" root_concurrency="scoop" warning="true">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<setting name="console_application" value="true"/>
|
||||||
|
<precompile name="base_pre" location="$ISE_PRECOMP\base-scoop-safe.ecf"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||||
|
<library name="standalone_websocket_connector" location="..\..\..\..\..\library\server\wsf\connector\standalone_websocket-safe.ecf" readonly="false">
|
||||||
|
<option debug="false">
|
||||||
|
<debug name="ws" enabled="true"/>
|
||||||
|
</option>
|
||||||
|
</library>
|
||||||
|
<cluster name="standalone_websocket" location=".\" recursive="true">
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
Reference in New Issue
Block a user