Compare commits

...

6 Commits

Author SHA1 Message Date
d4f1839024 Merge branch 'master' into ewf_ws_compression 2016-11-07 11:56:15 +01:00
jvelilla
4a21dcd2d6 Update WebSocket compress code. Increase the chunk to 32k for reading.
Update send data to the client, when the pmce compression extension was accepted
send all the data at once.
2016-10-27 09:51:40 -03:00
jvelilla
4c912912a6 Initial import WebSocket Compression Protocol extension Permessage-deflate.
Added test cases for Permessage-delate valid parameters
Added a simple websocket example with compression.
2016-10-26 17:35:05 -03:00
jvelilla
406559f1c6 Merge branch 'master' of https://github.com/EiffelWebFramework/EWF into ewf_ws_compression 2016-10-25 10:33:42 -03:00
jvelilla
bbca08ab5e Merge branch 'master' of https://github.com/EiffelWebFramework/EWF into ewf_ws_compression
# Conflicts:
#	library/server/wsf/connector/standalone_websocket/websocket/web_socket.e
2016-10-17 10:37:44 -03:00
jvelilla
82c3e2aebb WebSocket compression update with new classes to
parse the compression header.
2016-10-17 10:15:12 -03:00
31 changed files with 1763 additions and 27 deletions

View 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 )

View File

@@ -0,0 +1,6 @@
#include <windows.h>
STRINGTABLE
BEGIN
1 "This Program was made using EiffelStudio using Visual Studio C++"
END

View 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

View 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

View 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-----

View 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-----

View 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-----

View 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-----

View 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-----

View 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-----

View 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-----

View 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-----

View 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>

View File

@@ -0,0 +1,6 @@
#include <windows.h>
STRINGTABLE
BEGIN
1 "This Program was made using EiffelStudio using Visual Studio C++"
END

View 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

View File

@@ -93,6 +93,9 @@ feature -- Access
is_fin: BOOLEAN
-- is the final fragment in a message?
is_rsv1: BOOLEAN
-- is extension negotiation in a message?
fragment_count: INTEGER
payload_length: NATURAL_64
@@ -111,6 +114,10 @@ feature -- Access
error: detachable WEB_SOCKET_ERROR_FRAME
-- 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
injected_control_frames: detachable LIST [WEB_SOCKET_FRAME]
@@ -132,6 +139,11 @@ feature -- Operation
is_fin := a_flag_is_fin
end
update_rsv1 (a_flag_rsv1: BOOLEAN)
do
is_rsv1 := a_flag_rsv1
end
feature {WEB_SOCKET_FRAME} -- Change: injected control frames
add_injected_control_frame (f: WEB_SOCKET_FRAME)
@@ -251,6 +263,7 @@ feature -- Change
if is_text then
if is_fin and a_flag_chop_complete then
-- Check the whole message is a valid UTF-8 string
-- iff is not is_rsv1
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!")
@@ -268,6 +281,20 @@ feature -- Change
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)
require
not has_error
@@ -434,4 +461,14 @@ feature {NONE} -- Helper
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

View File

@@ -19,6 +19,7 @@
<library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
<library name="httpd" location="..\..\httpd\httpd-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_standalone" location="standalone-safe.ecf"/>
<library name="web_socket_protocol" location="..\..\..\network\websocket\protocol\web_socket_protocol-safe.ecf"/>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -114,6 +114,20 @@ feature -- Element change
verbose_level := lev
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
put_error (a_message: READABLE_STRING_8)
@@ -142,10 +156,13 @@ feature -- Basic Operation
-- Host: server.example.com
-- Upgrade: websocket
-- Connection: Upgrade
--! Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
-- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-- Origin: http://example.com
-- Sec-WebSocket-Protocol: chat, superchat
-- 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
l_sha1: SHA1
l_key : STRING
@@ -155,6 +172,7 @@ feature -- Basic Operation
-- Reset values.
is_websocket := False
has_error := False
on_handshake:= True
-- Local cache.
req := request
@@ -184,9 +202,20 @@ feature -- Basic Operation
l_version_key.is_case_insensitive_equal ("13") and then
attached req.http_host -- Host header must be present
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
log ("key " + l_ws_key, debug_level)
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
create l_sha1.make
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 ("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
log ("%N================> Send Handshake", debug_level)
if attached {HTTP_HEADER} res.header as h then
@@ -228,14 +263,26 @@ feature -- Response!
l_message_count: INTEGER
n: NATURAL_64
retried: BOOLEAN
l_message: STRING_8
l_opcode: NATURAL_32
l_remaning_len: INTEGER
do
l_message := a_message
if attached accepted_offer and then not on_handshake then
l_message := compress_string (l_message)
end
debug ("ws")
print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N")
end
if not retried then
create l_header_message.make_empty
if attached accepted_offer and then not on_handshake then
l_opcode := (0x80 | a_opcode).to_natural_32
l_header_message.append_code ((l_opcode.bit_xor (0b1000000)))
else
l_header_message.append_code ((0x80 | a_opcode).to_natural_32)
l_message_count := a_message.count
end
l_message_count := l_message.count
n := l_message_count.to_natural_64
if l_message_count > 0xffff then
--! Improve. this code needs to be checked.
@@ -257,9 +304,12 @@ feature -- Response!
end
socket.put_string_8_noexception (l_header_message)
if not socket.was_error then
l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable.
if attached accepted_offer and then not on_handshake then
socket.put_string_8_noexception (l_message)
else
l_chunk_size := 0x8000 --16_384 -- 16K TODO: see if we should make it customizable.
if l_message_count < l_chunk_size then
socket.put_string_8_noexception (a_message)
socket.put_string_8_noexception (l_message)
else
from
i := 0
@@ -269,7 +319,12 @@ feature -- Response!
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
@@ -281,13 +336,14 @@ feature -- Response!
end
end
end
end
else
-- FIXME: what should be done on rescue?
end
rescue
retried := True
io.put_string ("Internal error in " + generator + ".do_send (conn, a_opcode=" + a_opcode.out + ", a_message) !%N")
io.put_string ("Internal error in " + generator + ".do_send (conn, a_opcode=" + a_opcode.out + ", l_message) !%N")
retry
end
@@ -343,6 +399,8 @@ feature -- Response!
s: STRING
is_data_frame_ok: BOOLEAN -- Is the last process data framing ok?
retried: BOOLEAN
l_frame_rsv: INTEGER
-- l_ps: PROFILING_SETTING
do
if not retried then
l_socket := socket
@@ -370,6 +428,7 @@ feature -- Response!
end
l_fin := l_byte & (0b10000000) /= 0
l_rsv := l_byte & (0b01110000) = 0
l_frame_rsv := (l_byte & 0x70) |>> 4
l_opcode := l_byte & 0b00001111
if Result /= Void then
if l_opcode = Result.opcode then
@@ -404,7 +463,15 @@ feature -- Response!
end
-- 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
-- MUST be 0 unless an extension is negotiated that defines meanings
@@ -416,6 +483,7 @@ feature -- Response!
-- FIXME: add support for extension ?
Result.report_error (protocol_error, "RSV values MUST be 0 unless an extension is negotiated that defines meanings for non-zero values")
end
end
else
if is_verbose then
log ("+ INVALID frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")", debug_level)
@@ -505,7 +573,7 @@ feature -- Response!
Result.report_error (protocol_error, "All frames sent from client to server are masked!")
end
if Result.is_valid then
l_chunk_size := 0x4000 -- 16 K
l_chunk_size := 0x8000 -- 16 K
if l_payload_len > {INTEGER_32}.max_value.to_natural_64 then
-- Issue .. to big to store in STRING
-- FIXME !!!
@@ -540,7 +608,16 @@ feature -- Response!
end
end
l_fetch_count := l_fetch_count + l_bytes_read
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
Result.report_error (internal_error, "Issue reading payload data...")
end
@@ -794,7 +871,205 @@ feature {NONE} -- Debug
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"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[

View File

@@ -29,6 +29,7 @@ feature -- Execution
ws_h: like new_websocket_handler
do
create ws.make (request, response)
initialize_websocket_options (ws)
ws.open_ws_handshake
if ws.is_websocket then
if ws.has_error then
@@ -51,6 +52,18 @@ feature -- Execution
deferred
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
new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER

View File

@@ -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

View File

@@ -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

View File

@@ -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>