diff --git a/examples/websocket/stack.txt b/examples/websocket/stack.txt new file mode 100644 index 00000000..70bad84d --- /dev/null +++ b/examples/websocket/stack.txt @@ -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 ( @ 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) ( @ 4 ) + diff --git a/examples/websocket/websocket_app.rc b/examples/websocket/websocket_app.rc new file mode 100644 index 00000000..b0ec159c --- /dev/null +++ b/examples/websocket/websocket_app.rc @@ -0,0 +1,6 @@ +#include + +STRINGTABLE +BEGIN + 1 "This Program was made using EiffelStudio using Visual Studio C++" +END diff --git a/examples/websocket_compress/application.e b/examples/websocket_compress/application.e new file mode 100644 index 00000000..6caccefa --- /dev/null +++ b/examples/websocket_compress/application.e @@ -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 diff --git a/examples/websocket_compress/application_execution_ws.e b/examples/websocket_compress/application_execution_ws.e new file mode 100644 index 00000000..195d899b --- /dev/null +++ b/examples/websocket_compress/application_execution_ws.e @@ -0,0 +1,196 @@ +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 ("

Websocket demo

") +-- 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) + -- + 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") + end + +feature -- HTML Resource + + websocket_app_html (a_port: INTEGER): STRING + do + Result := "[ + + + + + + + +WebSockets Client + + +
+
+

WebSockets Client

+
+ + +
+
+ + + ]" + Result.replace_substring_all ("##PORTNUMBER##", a_port.out) + end + + +end diff --git a/examples/websocket_compress/cert.crt b/examples/websocket_compress/cert.crt new file mode 100644 index 00000000..4d3bcb33 --- /dev/null +++ b/examples/websocket_compress/cert.crt @@ -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----- diff --git a/examples/websocket_compress/priv.key b/examples/websocket_compress/priv.key new file mode 100644 index 00000000..9b56c44d --- /dev/null +++ b/examples/websocket_compress/priv.key @@ -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----- diff --git a/examples/websocket_compress/server.crt b/examples/websocket_compress/server.crt new file mode 100644 index 00000000..083881fe --- /dev/null +++ b/examples/websocket_compress/server.crt @@ -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----- diff --git a/examples/websocket_compress/server.csr b/examples/websocket_compress/server.csr new file mode 100644 index 00000000..afd1dcef --- /dev/null +++ b/examples/websocket_compress/server.csr @@ -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----- diff --git a/examples/websocket_compress/server.key b/examples/websocket_compress/server.key new file mode 100644 index 00000000..ea706538 --- /dev/null +++ b/examples/websocket_compress/server.key @@ -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----- diff --git a/examples/websocket_compress/server.pass.key b/examples/websocket_compress/server.pass.key new file mode 100644 index 00000000..45784bdb --- /dev/null +++ b/examples/websocket_compress/server.pass.key @@ -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----- diff --git a/examples/websocket_compress/simple.crt b/examples/websocket_compress/simple.crt new file mode 100644 index 00000000..6147c200 --- /dev/null +++ b/examples/websocket_compress/simple.crt @@ -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----- diff --git a/examples/websocket_compress/simple.key b/examples/websocket_compress/simple.key new file mode 100644 index 00000000..e5e22a52 --- /dev/null +++ b/examples/websocket_compress/simple.key @@ -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----- diff --git a/examples/websocket_compress/websocket_app.ecf b/examples/websocket_compress/websocket_app.ecf new file mode 100644 index 00000000..4d84a96f --- /dev/null +++ b/examples/websocket_compress/websocket_app.ecf @@ -0,0 +1,28 @@ + + + + + + /.svn$ + /CVS$ + /EIFGENs$ + + + + + + + + + + + + + + diff --git a/examples/websocket_compress/websocket_app.rc b/examples/websocket_compress/websocket_app.rc new file mode 100644 index 00000000..b0ec159c --- /dev/null +++ b/examples/websocket_compress/websocket_app.rc @@ -0,0 +1,6 @@ +#include + +STRINGTABLE +BEGIN + 1 "This Program was made using EiffelStudio using Visual Studio C++" +END diff --git a/examples/websocket_compress/ws.ini b/examples/websocket_compress/ws.ini new file mode 100644 index 00000000..3fddc175 --- /dev/null +++ b/examples/websocket_compress/ws.ini @@ -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 + diff --git a/examples/websocket_ssl/ws.ini b/examples/websocket_ssl/ws.ini deleted file mode 100644 index ee3de0cf..00000000 --- a/examples/websocket_ssl/ws.ini +++ /dev/null @@ -1,15 +0,0 @@ -verbose=true -verbose_level=INFORMATION -port=9090 -max_concurrent_connections=100 -keep_alive_timeout=35 -max_tcp_clients=100 -socket_timeout=30000 -max_keep_alive_requests=3000 - -### SSL settings -# enable SSL, with file certificate. -ssl_enabled=true -ssl_ca_key=simple.key -ssl_ca_crt=simple.crt - diff --git a/library/network/websocket/protocol/web_socket_frame.e b/library/network/websocket/protocol/web_socket_frame.e index a8d3c769..5eb78004 100644 --- a/library/network/websocket/protocol/web_socket_frame.e +++ b/library/network/websocket/protocol/web_socket_frame.e @@ -114,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] @@ -259,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!") @@ -276,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 diff --git a/library/server/wsf/connector/standalone_websocket-safe.ecf b/library/server/wsf/connector/standalone_websocket-safe.ecf index 2f1e36d9..94664231 100644 --- a/library/server/wsf/connector/standalone_websocket-safe.ecf +++ b/library/server/wsf/connector/standalone_websocket-safe.ecf @@ -18,6 +18,7 @@ + diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/compression_extensions_parser.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/compression_extensions_parser.e deleted file mode 100644 index 13b74f17..00000000 --- a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/compression_extensions_parser.e +++ /dev/null @@ -1,111 +0,0 @@ -note - description: "{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 - COMPRESSION_EXTENSIONS_PARSER - -create - make - -feature {NONE} -- Initialize - - make (a_header: STRING_32) - do - header := a_header - create {ARRAYED_LIST [WEBSOCKET_PCME]} last_offers.make (0) - ensure - header_set: header = a_header - end - -feature -- Access - - header: STRING_32 - -- Content raw header `Sec-Websocket-Extensions'. - - last_offers: LIST [WEBSOCKET_PCME] - -- List of potential offered PMCE - --| From Sec-Websocket-Extensions header. - --| The order of elements is important as it specifies the client's preferences. - -feature -- Parse - - parse - -- Parse `SEC-WEBSOCKET-EXTENSIONS' header. - -- The result is available in `last_offer'. - local - l_offers: ARRAYED_LIST [WEBSOCKET_PCME] - l_pcme: WEBSOCKET_PCME - 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 loop - -- Shared code extract to an external feature. - l_offers.force (parse_parameters (ic.item)) - end - else - -- we should raise an Issue. - end - last_offers := l_offers - end - -feature {NONE}-- Parse Compression Extension. - - parse_parameters (a_string: STRING_32): WEBSOCKET_PCME - local - l_first: BOOLEAN - l_str: STRING_32 - l_key: STRING_32 - l_value: STRING_32 - do - 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 - loop - if l_first then - l_str := ip.item - l_str.adjust - Result.set_name (l_str) - l_first := False - else - l_str := ip.item - l_str.adjust - 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 = - l_value := l_str.substring (l_str.index_of ('=', 1) + 1, l_str.count) -- Exclude = - Result.force (l_value, l_key) - else - Result.force (Void, l_str) - end - end - end - else - -- Compression Extension simple - --| like - --| permessage-deflate - l_str := a_string - l_str.adjust - 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 diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_compression_extensions_parser.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_compression_extensions_parser.e new file mode 100644 index 00000000..cee96b2f --- /dev/null +++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_compression_extensions_parser.e @@ -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 diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/websocket_pcme.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce.e similarity index 84% rename from library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/websocket_pcme.e rename to library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce.e index 1f29f6a4..76376d1a 100644 --- a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/websocket_pcme.e +++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce.e @@ -4,7 +4,7 @@ note revision: "$Revision$" class - WEBSOCKET_PCME + WEB_SOCKET_PMCE feature -- Access @@ -14,6 +14,16 @@ feature -- Access 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) diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_constants.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_constants.e new file mode 100644 index 00000000..fd21c395 --- /dev/null +++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_constants.e @@ -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 diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_accept.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_accept.e new file mode 100644 index 00000000..cc3937c5 --- /dev/null +++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_accept.e @@ -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 + Result.append_character (';') + Result.append ("client_max_window_bits") + if client_max_window_bits > 0 then + 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 diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_accept_factory.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_accept_factory.e new file mode 100644 index 00000000..7d944232 --- /dev/null +++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_accept_factory.e @@ -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 diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_offer.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_offer.e new file mode 100644 index 00000000..ef7bd622 --- /dev/null +++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_offer.e @@ -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 diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_server_support.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_server_support.e new file mode 100644 index 00000000..3038a509 --- /dev/null +++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_server_support.e @@ -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 diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_server_support_factory.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_server_support_factory.e new file mode 100644 index 00000000..99187e30 --- /dev/null +++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_server_support_factory.e @@ -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 diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_validator.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_validator.e index 946f1cbd..9036fd97 100644 --- a/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_validator.e +++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/extension/web_socket_pmce_deflate_validator.e @@ -1,7 +1,8 @@ note - description: "[Object that validate a PMCE permessage defalate extension, + description: "[ + Object that validate a PMCE permessage defalate extension, using the DEFLATE algorithm - ]" + ]" date: "$Date$" revision: "$Revision$" @@ -9,10 +10,6 @@ class WEB_SOCKET_PMCE_DEFLATE_VALIDATOR -feature -- Validate - - - feature -- Access name: STRING = "permessage-deflate" @@ -27,8 +24,8 @@ feature -- Access create Result.make_caseless (4) Result.force (False, "server_no_context_takeover") Result.force (False, "client_no_context_takeover") - Result.force (True, "server_max_windows_bits") - Result.force (True, "client_max_windows_bits") + Result.force (True, "server_max_window_bits") + Result.force (True, "client_max_window_bits") end diff --git a/library/server/wsf/connector/standalone_websocket/websocket/web_socket.e b/library/server/wsf/connector/standalone_websocket/websocket/web_socket.e index 513c4bb3..26f9f0b6 100644 --- a/library/server/wsf/connector/standalone_websocket/websocket/web_socket.e +++ b/library/server/wsf/connector/standalone_websocket/websocket/web_socket.e @@ -44,11 +44,6 @@ feature {NONE} -- Initialization end end - set_pcme_deflate - do - - end - feature -- Access socket: HTTPD_STREAM_SOCKET @@ -119,14 +114,20 @@ feature -- Element change verbose_level := lev end - mark_pcme_supported - -- Set the websocket to handle pcme. + configure_pcme (a_pcme_server: WEB_SOCKET_PMCE_DEFLATE_SERVER_SUPPORT) + -- Set `pcme_server_support` with `a_pcme_server`. do - is_pcme_supported := True + pmce_server_support := a_pcme_server ensure - pmce_supported_true: is_pcme_supported + 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) @@ -202,7 +203,7 @@ feature -- Basic Operation 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 is_pcme_supported and then attached req.meta_string_variable ("HTTP_SEC_WEBSOCKET_EXTENSIONS") as l_ws_extension then + 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. --| @@ -224,10 +225,9 @@ feature -- Basic Operation res.header.add_header_key_value ("Sec-WebSocket-Accept", l_key) -- Sec-WebSocket-Extensions - if is_pcme_supported and then attached accepted_offer as l_offer - and then attached extension_response(l_offer) as l_extension_response + if attached pmce_server_support and then attached accepted_offer as l_offer then - res.header.add_header_key_value ("Sec-WebSocket-Extensions", l_extension_response) + res.header.add_header_key_value ("Sec-WebSocket-Extensions", l_offer.extension_response) end if is_verbose then @@ -267,20 +267,20 @@ feature -- Response! l_opcode: NATURAL_32 do l_message := a_message --- if attached accepted_offer and then not on_handshake then --- l_message := compress_string (l_message) --- end + if attached accepted_offer and then not on_handshake then + l_message := compress_string (l_message) + end debug ("ws") print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N") end if not retried then create l_header_message.make_empty --- if attached accepted_offer and then not on_handshake then --- l_opcode := (0x80 | a_opcode).to_natural_32 --- l_header_message.append_code ((l_opcode.bit_xor (0b1000000))) --- else + if attached accepted_offer and then not on_handshake then + l_opcode := (0x80 | a_opcode).to_natural_32 + l_header_message.append_code ((l_opcode.bit_xor (0b1000000))) + else l_header_message.append_code ((0x80 | a_opcode).to_natural_32) --- end + end l_message_count := l_message.count n := l_message_count.to_natural_64 if l_message_count > 0xffff then @@ -305,7 +305,7 @@ feature -- Response! if not socket.was_error then l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable. if l_message_count < l_chunk_size then - socket.put_string_8_noexception (a_message) + socket.put_string_8_noexception (l_message) else from i := 0 @@ -390,6 +390,7 @@ feature -- Response! 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 @@ -600,7 +601,10 @@ feature -- Response! if attached accepted_offer then -- Uncompress data. --- Result.append_payload_data_chop (uncompress_string (l_chunk), l_bytes_read, l_remaining_len = 0) + Result.append_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 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 @@ -859,56 +863,110 @@ feature {NONE} -- Debug feature -- PCME --- uncompress_string (a_string: STRING): STRING --- local --- di: ZLIB_STRING_UNCOMPRESS --- l_string: STRING --- l_array: ARRAY [NATURAL_8] --- l_byte: SPECIAL [INTEGER_8] --- do --- create l_string.make_from_string (a_string) --- --Prepend 0x78 and 09c --- l_string.prepend_character ((156).to_character_8) --- l_string.prepend_character ((120).to_character_8) + uncompress_string (a_string: STRING): STRING + local + di: ZLIB_STRING_UNCOMPRESS + l_string: STRING + l_array: ARRAY [NATURAL_8] + l_byte: SPECIAL [INTEGER_8] + do + create l_string.make_from_string (a_string) --- -- Append 4 octects 0x00 0x00 0xff 0xff to the tail of the paiload message +---- 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_PCME_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_PCME_CONSTANTS}.default_window_size) --- l_array := string_to_array (l_string) --- l_byte := byte_array (l_array) - - - - --- create di.string_stream (l_string) --- Result := di.to_string -- debug ("ws") -- print ("%NBytes uncompresses:" + di.total_bytes_uncompressed.out) --- print ("%NUncompress message:" + Result) -- end --- end + Result := do_uncompress (a_string) + end --- compress_string (a_string: STRING): STRING --- local --- dc: ZLIB_STRING_COMPRESS --- l_string: STRING --- do + + 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 (Result) +-- create dc.string_stream_with_size (Result, {WEB_SOCKET_PCME_CONSTANTS}.default_chunk_size) -- dc.mark_sync_flush --- dc.put_string (a_string) +-- dc.put_string_with_options (a_string, {ZLIB_CONSTANTS}.Z_default_compression, -{WEB_SOCKET_PCME_CONSTANTS}.default_window_size, {WEB_SOCKET_PCME_CONSTANTS}.default_value_memory, {ZLIB_CONSTANTS}.z_default_strategy.to_integer_32) +-- Result := Result.substring (1, Result.count - 4) --- Result := Result.substring (3, Result.count - 4) + Result := do_compress (a_string) --- debug ("ws") --- print ("%NBytes uncompresses:" + dc.total_bytes_compressed.out ) --- end --- end + 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 @@ -963,28 +1021,17 @@ feature -- PCME feature {NONE} -- Extensions - is_pcme_supported: BOOLEAN - --| Temporary hack to test websocket compression - on_handshake: BOOLEAN - permessage_compression: STRING = "permessage-deflate" - --| Temporary hack to test websocket compression - - extension_response (a_offer: WEBSOCKET_PCME): detachable STRING_8 - do - if attached a_offer.name as l_name then - create Result.make_from_string (l_name) - end - end handle_extensions (a_extension: READABLE_STRING_32) - -- handle WebSocket extensions. local - l_parse: COMPRESSION_EXTENSIONS_PARSER - l_offers: LIST [WEBSOCKET_PCME] + l_parse: WEB_SOCKET_COMPRESSION_EXTENSIONS_PARSER + l_offers: LIST [WEB_SOCKET_PMCE] l_accepted: BOOLEAN - l_offer: WEBSOCKET_PCME + 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 @@ -994,26 +1041,22 @@ feature {NONE} -- Extensions l_parse.parse l_offers := l_parse.last_offers if not l_offers.is_empty then - -- filter by permessage-deflate. - --| TODO: validate if it's a valid extension. - --| validate params. + create l_accept_offer_accept across l_offers as ic until l_accepted loop - if attached {STRING_32} ic.item.name as l_name and then - l_name.is_case_insensitive_equal_general (permessage_compression) - then - l_accepted := True - create l_offer - l_offer.set_name (permessage_compression) + 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 - accepted_offer := l_offer end end - accepted_offer: detachable WEBSOCKET_PCME + accepted_offer: detachable WEB_SOCKET_PMCE_DEFLATE_ACCEPT -- Accepted compression extension. ;note diff --git a/tests/server/wsf/connector/standalone_websocket/application.e b/tests/server/wsf/connector/standalone_websocket/application.e new file mode 100644 index 00000000..116c5e19 --- /dev/null +++ b/tests/server/wsf/connector/standalone_websocket/application.e @@ -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 diff --git a/tests/server/wsf/connector/standalone_websocket/extension_parameter_test_set.e b/tests/server/wsf/connector/standalone_websocket/extension_parameter_test_set.e new file mode 100644 index 00000000..5581c342 --- /dev/null +++ b/tests/server/wsf/connector/standalone_websocket/extension_parameter_test_set.e @@ -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 + + diff --git a/tests/server/wsf/connector/standalone_websocket/standalone_websocket.ecf b/tests/server/wsf/connector/standalone_websocket/standalone_websocket.ecf new file mode 100644 index 00000000..3081d135 --- /dev/null +++ b/tests/server/wsf/connector/standalone_websocket/standalone_websocket.ecf @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + /.svn$ + /CVS$ + /EIFGENs$ + + + +