Compare commits
38 Commits
es_rev1009
...
es_rev1010
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
603bedf71d | ||
|
|
5fedad7f2e | ||
|
|
e83f5654d8 | ||
|
|
ccff084642 | ||
|
|
830adbe10c | ||
|
|
e6d998953e | ||
|
|
6ca3cca88b | ||
|
|
f91a676f41 | ||
|
|
1c75e11e34 | ||
|
|
b5b4fa6b2f | ||
|
|
211fc425a3 | ||
|
|
95cebe26bb | ||
|
|
f770c236d5 | ||
|
|
503e5f7915 | ||
|
|
39f01e95fd | ||
|
|
c725159d7e | ||
|
|
e66f1cf7be | ||
|
|
c03d28cabc | ||
|
|
e834b2b360 | ||
|
|
d089fd3a03 | ||
|
|
a0c1ab5232 | ||
|
|
a8ddd10b46 | ||
|
|
db39068ceb | ||
|
|
a1b4337438 | ||
|
|
74121be470 | ||
|
|
1c9f5ac0e7 | ||
|
|
8ff20d34a7 | ||
|
|
97fe16b4c2 | ||
|
|
cdada71f7e | ||
|
|
a7d0398ec6 | ||
|
|
267655d7bc | ||
|
|
e735da1bcb | ||
|
|
67bdcfb6ef | ||
|
|
ca4043b102 | ||
|
|
310e96e185 | ||
| e14bb568d2 | |||
| 05d37439bc | |||
|
|
99bf552b89 |
@@ -1,10 +1,8 @@
|
|||||||
language: eiffel
|
language: eiffel
|
||||||
before_script:
|
before_script:
|
||||||
- export current_dir=$PWD ; echo current_dir=$current_dir ; cd ..
|
- export current_dir=$PWD ; echo current_dir=$current_dir ; cd ..
|
||||||
- export ISE_VERSION=17.05; export ISE_BUILD=100416
|
- curl -sSL https://www.eiffel.org/setup/install.sh | bash
|
||||||
- curl -sSL http://downloads.sourceforge.net/eiffelstudio/Eiffel_${ISE_VERSION}_gpl_${ISE_BUILD}-linux-x86-64.tar.bz2 | tar -x --bzip2
|
- source eiffel_latest.rc
|
||||||
- export ISE_EIFFEL=$PWD/Eiffel_${ISE_VERSION} ; export ISE_PLATFORM=linux-x86-64
|
|
||||||
- export PATH=$PATH:$ISE_EIFFEL/studio/spec/$ISE_PLATFORM/bin:$PATH:$ISE_EIFFEL/tools/spec/$ISE_PLATFORM/bin
|
|
||||||
- echo `ec -version`
|
- echo `ec -version`
|
||||||
- cd $current_dir
|
- cd $current_dir
|
||||||
- echo Check projects compilation status...
|
- echo Check projects compilation status...
|
||||||
@@ -13,6 +11,7 @@ branches:
|
|||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
- v1
|
- v1
|
||||||
|
- develop
|
||||||
|
|
||||||
script: compile_all -ecb -melt -list_failures -log_verbose -clean -options dotnet=false
|
script: compile_all -ecb -melt -list_failures -log_verbose -clean -options dotnet=false
|
||||||
group: stable
|
group: stable
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||||||
- `jwt`: new JSON Web Token (JWT) library (supports for claim exp, iat, nbf, iss, aud).
|
- `jwt`: new JSON Web Token (JWT) library (supports for claim exp, iat, nbf, iss, aud).
|
||||||
- `http_client`: added support for ciphers setting in the libcurl implementation only.
|
- `http_client`: added support for ciphers setting in the libcurl implementation only.
|
||||||
- `http_client`: added convenient `get` and `custom` functions on HTTP_CLIENT directly.
|
- `http_client`: added convenient `get` and `custom` functions on HTTP_CLIENT directly.
|
||||||
|
- `websocket`: added `on_timer` solution to allow the server to check for external events and send notification to websocket clients.
|
||||||
|
- `wsf`: added `WSF_EXECUTE_HANDLER`, and `WSF_CGI_HANDLER`. Demonstration of `WSF_CGI_HANDLER` in the new `tools/httpd` project.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- adopted ecf version 1-16-0 and use a single .ecf file (the -safe.ecf are now redirection to normal .ecf)
|
- adopted ecf version 1-16-0 and use a single .ecf file (the -safe.ecf are now redirection to normal .ecf)
|
||||||
@@ -22,6 +24,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||||||
- `http_client`: Added support for multiple file in form data. Made clear what is the meaning of `upload_filename`, `upload_data` and `form_data`.
|
- `http_client`: Added support for multiple file in form data. Made clear what is the meaning of `upload_filename`, `upload_data` and `form_data`.
|
||||||
- `authentication`: HTTP_AUTHORIZATION acceps now READABLE_STRING_GENERAL for username and password argument.
|
- `authentication`: HTTP_AUTHORIZATION acceps now READABLE_STRING_GENERAL for username and password argument.
|
||||||
- `http_client`: fixed curl implementation by setting `Content-Type` to `x-www-form-urlencoded` (if not set) when POST send data as `x-www-form-urlencoded`.
|
- `http_client`: fixed curl implementation by setting `Content-Type` to `x-www-form-urlencoded` (if not set) when POST send data as `x-www-form-urlencoded`.
|
||||||
|
- `notification_email`: fixed the SMTP support for multiple recipients address.
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -13,14 +13,14 @@ Currently EWF offers a collection of Eiffel libraries designed to be integrated
|
|||||||
|
|
||||||
There is a growing ecosystem around EWF, that provides useful components:
|
There is a growing ecosystem around EWF, that provides useful components:
|
||||||
* OpenID and OAuth consumer library
|
* OpenID and OAuth consumer library
|
||||||
* Various hypermedia format such as HAL, Collection+json, …
|
* Various hypermedia format such as HAL, Collection+json, ...
|
||||||
* Websocket server and client
|
* Websocket server and client
|
||||||
* Template engine
|
* Template engine
|
||||||
* API Auto-documentation with swagger
|
* API Auto-documentation with swagger
|
||||||
* A simple experimental CMS.
|
* A simple experimental CMS.
|
||||||
* ...
|
* ...
|
||||||
|
|
||||||
So if you want to build a website, a web api, RESTful service, …or even if you want to consume other web api, EWF is a solution.
|
So if you want to build a website, a web api, RESTful service, ... or even if you want to consume other web api, EWF is a solution.
|
||||||
|
|
||||||
EWF brings with it all the advantages of the Eiffel technology and tools with its powerful features such as Design by Contract, debugging, testing tools which enable to build efficient systems expected to be repeatedly refined, extended, and improved in a predictable and controllable way so as to become with time bugfree systems. Enjoy the full power of debugging your web server application from the IDE.
|
EWF brings with it all the advantages of the Eiffel technology and tools with its powerful features such as Design by Contract, debugging, testing tools which enable to build efficient systems expected to be repeatedly refined, extended, and improved in a predictable and controllable way so as to become with time bugfree systems. Enjoy the full power of debugging your web server application from the IDE.
|
||||||
|
|
||||||
@@ -51,11 +51,11 @@ Tasks and issues are managed with github issue system
|
|||||||
## How to get the source code?
|
## How to get the source code?
|
||||||
|
|
||||||
Using git
|
Using git
|
||||||
* git clone https://github.com/EiffelWebFramework/EWF.git
|
* `git clone https://github.com/EiffelWebFramework/EWF.git`
|
||||||
|
|
||||||
* And to build the required and related Clibs
|
* And to build the required and related Clibs
|
||||||
* cd contrib/ise_library/cURL
|
* `cd contrib/ise_library/cURL`
|
||||||
* geant compile
|
* `geant compile`
|
||||||
|
|
||||||
## Libraries under 'library'
|
## Libraries under 'library'
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ Using git
|
|||||||
* connectors: various web server connectors for EWSGI
|
* connectors: various web server connectors for EWSGI
|
||||||
* libfcgi: Wrapper for libfcgi SDK
|
* libfcgi: Wrapper for libfcgi SDK
|
||||||
* __wsf__: Web Server Framework [read more](library/server/wsf)
|
* __wsf__: Web Server Framework [read more](library/server/wsf)
|
||||||
* __router__: URL dispatching/routing based on uri, uri_template, or custom [read more](library/server/wsf/router)
|
* __router__: URL dispatching/routing based on `uri`, `uri_template`, or custom [read more](library/server/wsf/router)
|
||||||
|
|
||||||
### protocol
|
### protocol
|
||||||
* __http__: HTTP related classes, constants for status code, content types, ... [read more](library/network/protocol/http)
|
* __http__: HTTP related classes, constants for status code, content types, ... [read more](library/network/protocol/http)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ feature -- Basic operations
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
When using the "standalone" connector (or the deprecated "nino" connector), by default the service listens on port 80, but often this port is already used by other applications, so it is recommended to use another port.
|
When using the [standalone](../connectors/standalone.md) connector (or the deprecated "nino" connector), by default the service listens on port 80, but often this port is already used by other applications, so it is recommended to use another port.
|
||||||
To define another port, redefine the feature `initialize` and set up a new port number using the service options (see below).
|
To define another port, redefine the feature `initialize` and set up a new port number using the service options (see below).
|
||||||
|
|
||||||
|
|
||||||
@@ -86,6 +86,8 @@ feature {NONE} -- Initialization
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Learn more about the [Standalone](../connectors/standalone.md) connector.
|
||||||
|
|
||||||
The **WSF_REQUEST** gives access to the incoming data; the class provides features to get information such as request method, form data, query parameters, uploaded files, HTTP request headers, and hostname of the client among others.
|
The **WSF_REQUEST** gives access to the incoming data; the class provides features to get information such as request method, form data, query parameters, uploaded files, HTTP request headers, and hostname of the client among others.
|
||||||
|
|
||||||
The **WSF_RESPONSE** provides features to define the response with information such as HTTP status codes (10x,20x, 30x, 40x, and 50x), response headers (Content-Type, Content-Length, etc.) and obviously the body of the message itself.
|
The **WSF_RESPONSE** provides features to define the response with information such as HTTP status codes (10x,20x, 30x, 40x, and 50x), response headers (Content-Type, Content-Length, etc.) and obviously the body of the message itself.
|
||||||
|
|||||||
27
docs/workbook/connectors/standalone.md
Normal file
27
docs/workbook/connectors/standalone.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
Nav: [Workbook](../workbook.md)
|
||||||
|
|
||||||
|
## The EiffelWeb standalone connector
|
||||||
|
|
||||||
|
It provides a standalone httpd server for the EiffelWeb framework.
|
||||||
|
It implements HTTP/1.1 with persistent connection, concurrent connection, ...
|
||||||
|
|
||||||
|
To easily set the standalone connector, see class `WSF_STANDALONE_SERVICE_OPTIONS`.
|
||||||
|
|
||||||
|
### Main settings:
|
||||||
|
|
||||||
|
* `port`: Listening port number (defaut: 80).
|
||||||
|
* `max_concurrent_connections`: maximum of concurrent connections (default: 100)
|
||||||
|
* `max_tcp_clients`: Listen on socket for at most `max_tcp_clients` connections (default: 100)
|
||||||
|
* `socket_timeout`: Amount of seconds the server waits for receipts and transmissions during communications. With timeout of 0, socket can wait for ever. (default: 60)
|
||||||
|
* `socket_recv_timeout`: Amount of seconds the server waits for receiving data during communications. With timeout of 0, socket can waits for ever. (default: 5)
|
||||||
|
* `keep_alive_timeout`: Persistent connection timeout. Number of seconds the server waits after a request has been served before it closes the connection (default: 5)
|
||||||
|
* `max_keep_alive_requests`: Maximum number of requests allowed per persistent connection. To disable KeepAlive, set `max_keep_alive_requests` to `0`. To have no limit, set `max_keep_alive_requests` to `-1` (default: 300).
|
||||||
|
|
||||||
|
* `is_secure`: check SSL certificate?
|
||||||
|
* `secure_certificate`: path to SSL certificate.
|
||||||
|
* `secure_certificate_key`: certificate key
|
||||||
|
|
||||||
|
* `verbose`: display verbose output (Default: false)
|
||||||
|
|
||||||
|
See also `WGI_STANDALONE_CONSTANTS` for default values.
|
||||||
|
|
||||||
@@ -2,8 +2,8 @@ verbose=true
|
|||||||
verbose_level=ALERT
|
verbose_level=ALERT
|
||||||
port=9090
|
port=9090
|
||||||
#max_concurrent_connections=100
|
#max_concurrent_connections=100
|
||||||
keep_alive_timeout=3
|
#keep_alive_timeout=5
|
||||||
#max_tcp_clients=100
|
|
||||||
socket_timeout=60
|
|
||||||
socket_recv_timeout=15
|
|
||||||
#max_keep_alive_requests=300
|
#max_keep_alive_requests=300
|
||||||
|
#max_tcp_clients=100
|
||||||
|
#socket_timeout=60
|
||||||
|
#socket_recv_timeout=15
|
||||||
|
|||||||
4
examples/simple_compression/service.ini
Normal file
4
examples/simple_compression/service.ini
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
port=9090
|
||||||
|
verbose=true
|
||||||
|
socket_recv_timeout=15
|
||||||
|
keep_alive_timeout=30
|
||||||
26
examples/simple_compression/service_compression.e
Normal file
26
examples/simple_compression/service_compression.e
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
note
|
||||||
|
description : "simple application root class"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
SERVICE_COMPRESSION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_DEFAULT_SERVICE [SERVICE_COMPRESSION_EXECUTION]
|
||||||
|
redefine
|
||||||
|
initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make_and_launch
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("service.ini"))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
22
examples/simple_compression/service_compression.ecf
Normal file
22
examples/simple_compression/service_compression.ecf
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="service_compression" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486">
|
||||||
|
<target name="service_compression">
|
||||||
|
<root class="SERVICE_COMPRESSION" feature="make_and_launch"/>
|
||||||
|
<option warning="true" void_safety="all">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<setting name="concurrency" value="scoop"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
|
||||||
|
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
|
||||||
|
<library name="wsf_compression" location="..\..\library\server\wsf\wsf_compression-safe.ecf" readonly="false"/>
|
||||||
|
<cluster name="service_compression" location=".\" recursive="true">
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
68
examples/simple_compression/service_compression_execution.e
Normal file
68
examples/simple_compression/service_compression_execution.e
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
note
|
||||||
|
description: "Simple file execution, serving home.html, ewf.png and 404.html"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
SERVICE_COMPRESSION_EXECUTION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTED_EXECUTION
|
||||||
|
redefine
|
||||||
|
initialize,
|
||||||
|
execute_default
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
|
||||||
|
initialize
|
||||||
|
-- Initialize current service.
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
initialize_router
|
||||||
|
end
|
||||||
|
|
||||||
|
setup_router
|
||||||
|
local
|
||||||
|
fhdl_with_compression: WSF_FILE_SYSTEM_HANDLER_WITH_COMPRESSION
|
||||||
|
fhdl: WSF_FILE_SYSTEM_HANDLER
|
||||||
|
do
|
||||||
|
create fhdl_with_compression.make_hidden ("www")
|
||||||
|
fhdl_with_compression.set_directory_index (<<"index.html">>)
|
||||||
|
fhdl_with_compression.compression.set_default_compression_format
|
||||||
|
fhdl_with_compression.compression.enable_compression_for_media_type ({HTTP_MIME_TYPES}.image_jpg)
|
||||||
|
fhdl_with_compression.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
execute_default (ia_req, ia_res)
|
||||||
|
end)
|
||||||
|
router.handle ("/compressed/", fhdl_with_compression, router.methods_GET)
|
||||||
|
|
||||||
|
create fhdl.make_hidden ("www")
|
||||||
|
fhdl.set_directory_index (<<"index.html">>)
|
||||||
|
fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
execute_default (ia_req, ia_res)
|
||||||
|
end)
|
||||||
|
router.handle ("/", fhdl, router.methods_GET)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
-- Dispatch requests without a matching handler.
|
||||||
|
local
|
||||||
|
not_found: WSF_NOT_FOUND_RESPONSE
|
||||||
|
mesg: WSF_RESPONSE_MESSAGE
|
||||||
|
do
|
||||||
|
create not_found.make (request)
|
||||||
|
not_found.add_suggested_location (request.absolute_script_url (""), "Home", "Back to home page")
|
||||||
|
mesg := not_found
|
||||||
|
res.send (mesg)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
77705
examples/simple_compression/www/big_file.html
Normal file
77705
examples/simple_compression/www/big_file.html
Normal file
File diff suppressed because it is too large
Load Diff
88332
examples/simple_compression/www/big_file2.html
Normal file
88332
examples/simple_compression/www/big_file2.html
Normal file
File diff suppressed because it is too large
Load Diff
BIN
examples/simple_compression/www/eiffel.jpg
Normal file
BIN
examples/simple_compression/www/eiffel.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
examples/simple_compression/www/ewf.png
Normal file
BIN
examples/simple_compression/www/ewf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
27
examples/simple_compression/www/index.html
Normal file
27
examples/simple_compression/www/index.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>EWF simple_file example</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>EWF simple_file example</h1>
|
||||||
|
<p>This is a static html file served by EWF.</p>
|
||||||
|
<p>Try to <a href="nowhere.html">get lost</a>.</p>
|
||||||
|
<div width="45%" style="display: inline-block; border: solid 1px black; padding: 10px; margin: 10px;">
|
||||||
|
<h2>Without any compression</h2>
|
||||||
|
<a href="ewf.png"><img src="ewf.png"/></a>
|
||||||
|
<p>This is the real Eiffel tower.</p>
|
||||||
|
<a href="eiffel.jpg"><img src="eiffel.jpg"/></a>
|
||||||
|
<p>Try to <a href="big_file2.html">load a big file</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div width="45%" style="display: inline-block; border: solid 1px black; padding: 10px; margin: 10px;">
|
||||||
|
<h2>With gzip compression</h2>
|
||||||
|
<a href="compressed/ewf.png"><img src="compressed/ewf.png"/></a>
|
||||||
|
<p>This is the real Eiffel tower.</p>
|
||||||
|
<a href="compressed/eiffel.jpg"><img src="compressed/eiffel.jpg"/></a>
|
||||||
|
<p>Try to <a href="compressed/big_file2.html">load a compressed big file</a>.</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -10,6 +10,9 @@ inherit
|
|||||||
WSF_WEBSOCKET_EXECUTION
|
WSF_WEBSOCKET_EXECUTION
|
||||||
|
|
||||||
WEB_SOCKET_EVENT_I
|
WEB_SOCKET_EVENT_I
|
||||||
|
redefine
|
||||||
|
on_timer
|
||||||
|
end
|
||||||
|
|
||||||
create
|
create
|
||||||
make
|
make
|
||||||
@@ -52,6 +55,9 @@ feature -- Websocket execution
|
|||||||
|
|
||||||
on_open (ws: WEB_SOCKET)
|
on_open (ws: WEB_SOCKET)
|
||||||
do
|
do
|
||||||
|
initialize_commands
|
||||||
|
set_timer_delay (1) -- Every 1 second.
|
||||||
|
|
||||||
ws.put_error ("Connecting")
|
ws.put_error ("Connecting")
|
||||||
ws.send (Text_frame, "Hello, this is a simple demo with Websocket using Eiffel. (/help for more information).%N")
|
ws.send (Text_frame, "Hello, this is a simple demo with Websocket using Eiffel. (/help for more information).%N")
|
||||||
end
|
end
|
||||||
@@ -62,12 +68,26 @@ feature -- Websocket execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
|
local
|
||||||
|
i: INTEGER
|
||||||
|
cmd_name: READABLE_STRING_8
|
||||||
do
|
do
|
||||||
if a_message.same_string_general ("/help") then
|
if a_message.starts_with_general ("/") then
|
||||||
-- Echo the message for testing.
|
from
|
||||||
ws.send (Text_frame, "Help: available commands%N - /time : return the server UTC time.%N")
|
i := 1
|
||||||
elseif a_message.starts_with_general ("/time") then
|
until
|
||||||
ws.send (Text_frame, "Server time is " + (create {HTTP_DATE}.make_now_utc).string)
|
i >= a_message.count or else a_message[i + 1].is_space
|
||||||
|
loop
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
cmd_name := a_message.substring (2, i)
|
||||||
|
if attached command (cmd_name) as cmd then
|
||||||
|
cmd (ws, a_message.substring (i + 1, a_message.count))
|
||||||
|
elseif a_message.same_string_general ("/help") then
|
||||||
|
on_help_command (ws, Void)
|
||||||
|
else
|
||||||
|
ws.send (Text_frame, "Error: unknown command '/" + cmd_name + "'!%N")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
-- Echo the message for testing.
|
-- Echo the message for testing.
|
||||||
ws.send (Text_frame, a_message)
|
ws.send (Text_frame, a_message)
|
||||||
@@ -80,6 +100,88 @@ feature -- Websocket execution
|
|||||||
ws.put_error ("Connection closed")
|
ws.put_error ("Connection closed")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
on_timer (ws: WEB_SOCKET)
|
||||||
|
-- <Precursor>.
|
||||||
|
-- If ever the file ".stop" exists, stop gracefully the connection.
|
||||||
|
local
|
||||||
|
fut: FILE_UTILITIES
|
||||||
|
f: RAW_FILE
|
||||||
|
do
|
||||||
|
if fut.file_exists (".stop") then
|
||||||
|
ws.send_text ("End of the communication ...%N")
|
||||||
|
ws.send_connection_close ("")
|
||||||
|
create f.make_with_name (".stop")
|
||||||
|
f.delete
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Command
|
||||||
|
|
||||||
|
initialize_commands
|
||||||
|
do
|
||||||
|
register_command (agent on_help_command, "help", "Display this help.")
|
||||||
|
register_command (agent on_time_command, "time", "Return the server UTC time.")
|
||||||
|
register_command (agent on_shutdown_command, "shutdown", "Shutdown the service (ends the websocket).")
|
||||||
|
end
|
||||||
|
|
||||||
|
register_command (a_cmd: attached like command; a_name: READABLE_STRING_8; a_description: READABLE_STRING_8)
|
||||||
|
local
|
||||||
|
tb: like commands
|
||||||
|
do
|
||||||
|
tb := commands
|
||||||
|
if tb = Void then
|
||||||
|
create tb.make_caseless (1)
|
||||||
|
commands := tb
|
||||||
|
end
|
||||||
|
tb.force ([a_cmd, a_name, a_description], a_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
commands: detachable STRING_TABLE [TUPLE [cmd: attached like command; name, description: READABLE_STRING_8]]
|
||||||
|
|
||||||
|
command (a_name: READABLE_STRING_GENERAL): detachable PROCEDURE [TUPLE [ws: WEB_SOCKET; args: detachable READABLE_STRING_GENERAL]]
|
||||||
|
do
|
||||||
|
if
|
||||||
|
attached commands as tb and then
|
||||||
|
attached tb.item (a_name) as d
|
||||||
|
then
|
||||||
|
Result := d.cmd
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
on_help_command (ws: WEB_SOCKET; args: detachable READABLE_STRING_GENERAL)
|
||||||
|
local
|
||||||
|
s: STRING
|
||||||
|
do
|
||||||
|
create s.make_from_string ("Help: available commands:%N")
|
||||||
|
if attached commands as tb then
|
||||||
|
across
|
||||||
|
tb as ic
|
||||||
|
loop
|
||||||
|
s.append ("<li> /")
|
||||||
|
s.append (ic.item.name)
|
||||||
|
s.append (" : ")
|
||||||
|
s.append (ic.item.description)
|
||||||
|
s.append ("</li>%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ws.send_text (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
on_time_command (ws: WEB_SOCKET; args: detachable READABLE_STRING_GENERAL)
|
||||||
|
do
|
||||||
|
ws.send_text ("Server time is " + (create {HTTP_DATE}.make_now_utc).string)
|
||||||
|
end
|
||||||
|
|
||||||
|
on_shutdown_command (ws: WEB_SOCKET; args: detachable READABLE_STRING_GENERAL)
|
||||||
|
local
|
||||||
|
f: RAW_FILE
|
||||||
|
do
|
||||||
|
ws.send_text ("Active websockets will end soon.%N")
|
||||||
|
create f.make_create_read_write (".stop")
|
||||||
|
f.put_string ("stop%N")
|
||||||
|
f.close
|
||||||
|
end
|
||||||
|
|
||||||
feature -- HTML Resource
|
feature -- HTML Resource
|
||||||
|
|
||||||
websocket_app_html (a_port: INTEGER): STRING
|
websocket_app_html (a_port: INTEGER): STRING
|
||||||
@@ -188,5 +290,4 @@ body {font-family:Arial, Helvetica, sans-serif;}
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
<target name="websocket_app">
|
<target name="websocket_app">
|
||||||
<root class="APPLICATION" feature="make_and_launch"/>
|
<root class="APPLICATION" feature="make_and_launch"/>
|
||||||
<file_rule>
|
<file_rule>
|
||||||
<exclude>/\.svn$</exclude>
|
|
||||||
<exclude>/CVS$</exclude>
|
<exclude>/CVS$</exclude>
|
||||||
<exclude>/EIFGENs$</exclude>
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/\.svn$</exclude>
|
||||||
</file_rule>
|
</file_rule>
|
||||||
<option warning="true">
|
<option warning="true">
|
||||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
@@ -17,6 +17,14 @@
|
|||||||
<library name="wsf" location="..\..\library\server\wsf\wsf.ecf"/>
|
<library name="wsf" location="..\..\library\server\wsf\wsf.ecf"/>
|
||||||
<cluster name="app" location=".\" recursive="true"/>
|
<cluster name="app" location=".\" recursive="true"/>
|
||||||
</target>
|
</target>
|
||||||
|
<target name="websocket_app_st" extends="websocket_app">
|
||||||
|
<description>Single thread solution.
|
||||||
|
Warning: as it can not handle concurrent request, it is recommended to set Keep-Alive-Timeout to very low value, as browser will keep persistent connection open too long.
|
||||||
|
</description>
|
||||||
|
<capability>
|
||||||
|
<concurrency use="none"/>
|
||||||
|
</capability>
|
||||||
|
</target>
|
||||||
<target name="websocket_app_ssl" extends="websocket_app">
|
<target name="websocket_app_ssl" extends="websocket_app">
|
||||||
<variable name="ssl_enabled" value="true"/>
|
<variable name="ssl_enabled" value="true"/>
|
||||||
</target>
|
</target>
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
verbose=true
|
|
||||||
verbose_level=INFORMATION
|
|
||||||
port=9090
|
port=9090
|
||||||
|
|
||||||
|
#Socket and timeout
|
||||||
max_concurrent_connections=100
|
max_concurrent_connections=100
|
||||||
keep_alive_timeout=35
|
|
||||||
max_tcp_clients=100
|
max_tcp_clients=100
|
||||||
socket_timeout=30
|
socket_timeout=30
|
||||||
socket_recv_timeout=5
|
socket_recv_timeout=5
|
||||||
max_keep_alive_requests=300
|
|
||||||
|
|
||||||
|
#Persistent connections
|
||||||
|
keep_alive_timeout=2
|
||||||
|
max_keep_alive_requests=-1
|
||||||
|
|
||||||
|
#SSL
|
||||||
is_secure=false
|
is_secure=false
|
||||||
secure_certificate=ca.crt
|
secure_certificate=ca.crt
|
||||||
secure_certificate_key=ca.key
|
secure_certificate_key=ca.key
|
||||||
|
|
||||||
|
#Debug
|
||||||
|
verbose=true
|
||||||
|
verbose_level=INFORMATION
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ feature {NONE} -- Initialization
|
|||||||
i := a_url.substring_index ("://", 1)
|
i := a_url.substring_index ("://", 1)
|
||||||
if i > 0 then
|
if i > 0 then
|
||||||
check
|
check
|
||||||
a_url.substring (1, i).same_string ("http")
|
a_url.head (i - 1).same_string ("http")
|
||||||
or a_url.substring (1, i).same_string ("https")
|
or a_url.head (i - 1).same_string ("https")
|
||||||
end
|
end
|
||||||
url := a_url
|
url := a_url
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -61,12 +61,40 @@ feature -- Access
|
|||||||
url (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): STRING_8
|
url (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): STRING_8
|
||||||
-- Url computed from Current and `ctx' data.
|
-- Url computed from Current and `ctx' data.
|
||||||
do
|
do
|
||||||
Result := base_url + a_path
|
if is_absolute_url (a_path) then
|
||||||
|
-- Is Absolute url
|
||||||
|
Result := a_path
|
||||||
|
else
|
||||||
|
Result := base_url + a_path
|
||||||
|
end
|
||||||
if ctx /= Void then
|
if ctx /= Void then
|
||||||
ctx.append_query_parameters_to_url (Result)
|
ctx.append_query_parameters_to_url (Result)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
is_absolute_url (s: READABLE_STRING_GENERAL): BOOLEAN
|
||||||
|
-- Does `s` represent an absolute url?
|
||||||
|
local
|
||||||
|
i, pos: INTEGER
|
||||||
|
sch: READABLE_STRING_GENERAL
|
||||||
|
do
|
||||||
|
pos := s.substring_index ("://", 1)
|
||||||
|
if pos > 0 then
|
||||||
|
sch := s.head (pos - 1)
|
||||||
|
if not sch.is_whitespace then
|
||||||
|
from
|
||||||
|
i := 1
|
||||||
|
Result := True
|
||||||
|
until
|
||||||
|
not Result or i > sch.count
|
||||||
|
loop
|
||||||
|
Result := sch[i].is_alpha_numeric
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
feature {NONE} -- Access: verbose
|
feature {NONE} -- Access: verbose
|
||||||
|
|
||||||
verbose_mode: INTEGER
|
verbose_mode: INTEGER
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ note
|
|||||||
deferred class
|
deferred class
|
||||||
HTTP_CLIENT_REQUEST_PARAMETER
|
HTTP_CLIENT_REQUEST_PARAMETER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
DEBUG_OUTPUT
|
||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
|
|
||||||
name: READABLE_STRING_32
|
name: READABLE_STRING_32
|
||||||
@@ -18,6 +21,15 @@ feature -- Access
|
|||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
debug_output: STRING_32
|
||||||
|
do
|
||||||
|
create Result.make_empty
|
||||||
|
Result.append (name)
|
||||||
|
Result.append ("=...")
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Conversion
|
feature -- Conversion
|
||||||
|
|
||||||
append_form_url_encoded_to (a_output: STRING_8)
|
append_form_url_encoded_to (a_output: STRING_8)
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ feature -- Access
|
|||||||
Result := items.count
|
Result := items.count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
has (a_parameter_name: READABLE_STRING_GENERAL): BOOLEAN
|
||||||
|
do
|
||||||
|
Result := across items as ic some a_parameter_name.same_string (ic.item.name) end
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
extend, force (i: G)
|
extend, force (i: G)
|
||||||
|
|||||||
@@ -74,6 +74,22 @@ feature -- Test routines
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test_abs_url
|
||||||
|
local
|
||||||
|
sess: like new_session
|
||||||
|
h: STRING_8
|
||||||
|
l_url: STRING
|
||||||
|
do
|
||||||
|
sess := new_session ("https://www.eiffel.org")
|
||||||
|
l_url := "/foo/bar"
|
||||||
|
assert ("abs rel", sess.url (l_url, Void).same_string (sess.base_url + l_url))
|
||||||
|
|
||||||
|
l_url := "https://www.eiffel.org/foo/bar"
|
||||||
|
assert ("abs 1", sess.url (l_url, Void).same_string (l_url))
|
||||||
|
l_url := "https://example.com/foo/bar"
|
||||||
|
assert ("abs 2", sess.url (l_url, Void).same_string (l_url))
|
||||||
|
end
|
||||||
|
|
||||||
test_headers
|
test_headers
|
||||||
local
|
local
|
||||||
res: HTTP_CLIENT_RESPONSE
|
res: HTTP_CLIENT_RESPONSE
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ feature -- Tests
|
|||||||
test_http_client_ssl
|
test_http_client_ssl
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test_libcurl_abs_url
|
||||||
|
do
|
||||||
|
test_abs_url
|
||||||
|
end
|
||||||
|
|
||||||
test_libcurl_headers
|
test_libcurl_headers
|
||||||
do
|
do
|
||||||
test_headers
|
test_headers
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ feature -- Tests
|
|||||||
test_http_client_ssl
|
test_http_client_ssl
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test_net_abs_url
|
||||||
|
do
|
||||||
|
test_abs_url
|
||||||
|
end
|
||||||
|
|
||||||
test_net_headers
|
test_net_headers
|
||||||
do
|
do
|
||||||
test_headers
|
test_headers
|
||||||
|
|||||||
@@ -95,40 +95,67 @@ feature -- Basic operation
|
|||||||
l_email: EMAIL
|
l_email: EMAIL
|
||||||
h: STRING
|
h: STRING
|
||||||
i: INTEGER
|
i: INTEGER
|
||||||
|
lst: LIST [READABLE_STRING_8]
|
||||||
do
|
do
|
||||||
create l_email.make_with_entry (a_email.from_address, addresses_to_header_line_value (a_email.to_addresses))
|
lst := a_email.to_addresses
|
||||||
if attached a_email.reply_to_address as l_reply_to then
|
if lst.is_empty then
|
||||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_reply_to, l_reply_to)
|
-- Error ...
|
||||||
end
|
else
|
||||||
|
-- With EMAIL, there should be a unique recipient at creation.
|
||||||
if attached a_email.cc_addresses as lst then
|
create l_email.make_with_entry (a_email.from_address, lst.first)
|
||||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_cc, addresses_to_header_line_value (lst))
|
if lst.count > 1 then
|
||||||
end
|
from
|
||||||
if attached a_email.bcc_addresses as lst then
|
lst.start
|
||||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_bcc, addresses_to_header_line_value (lst))
|
lst.forth
|
||||||
end
|
until
|
||||||
l_email.set_message (a_email.content)
|
lst.off
|
||||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, a_email.subject)
|
loop
|
||||||
|
l_email.add_recipient_address (lst.item)
|
||||||
create h.make_empty
|
lst.forth
|
||||||
;(create {HTTP_DATE}.make_from_date_time (a_email.date)).append_to_rfc1123_string (h)
|
|
||||||
l_email.add_header_entry ("Date", h)
|
|
||||||
|
|
||||||
if attached a_email.additional_header_lines as lst then
|
|
||||||
across
|
|
||||||
lst as ic
|
|
||||||
loop
|
|
||||||
h := ic.item
|
|
||||||
i := h.index_of (':', 1)
|
|
||||||
if i > 0 then
|
|
||||||
l_email.add_header_entry (h.head (i - 1), h.substring (i + 1, h.count))
|
|
||||||
else
|
|
||||||
check is_header_line: False end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
if attached a_email.reply_to_address as l_reply_to then
|
||||||
|
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_reply_to, l_reply_to)
|
||||||
|
end
|
||||||
|
|
||||||
smtp_send_email (l_email)
|
if attached a_email.cc_addresses as l_cc_addresses then
|
||||||
|
across
|
||||||
|
l_cc_addresses as ic
|
||||||
|
loop
|
||||||
|
l_email.add_recipient_address_in_cc (ic.item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if attached a_email.bcc_addresses as l_bcc_addresses then
|
||||||
|
across
|
||||||
|
l_bcc_addresses as ic
|
||||||
|
loop
|
||||||
|
l_email.add_recipient_address_in_bcc (ic.item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
l_email.set_message (a_email.content)
|
||||||
|
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, a_email.subject)
|
||||||
|
|
||||||
|
create h.make_empty
|
||||||
|
;(create {HTTP_DATE}.make_from_date_time (a_email.date)).append_to_rfc1123_string (h)
|
||||||
|
l_email.add_header_entry ("Date", h)
|
||||||
|
|
||||||
|
if attached a_email.additional_header_lines as l_lines then
|
||||||
|
across
|
||||||
|
l_lines as ic
|
||||||
|
loop
|
||||||
|
h := ic.item
|
||||||
|
i := h.index_of (':', 1)
|
||||||
|
if i > 0 then
|
||||||
|
l_email.add_header_entry (h.head (i - 1), h.substring (i + 1, h.count))
|
||||||
|
else
|
||||||
|
check is_header_line: False end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
smtp_send_email (l_email)
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
feature {NONE} -- Implementation
|
feature {NONE} -- Implementation
|
||||||
@@ -182,4 +209,3 @@ note
|
|||||||
Customer support http://support.eiffel.com
|
Customer support http://support.eiffel.com
|
||||||
]"
|
]"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -8,19 +8,9 @@ deferred class
|
|||||||
|
|
||||||
inherit
|
inherit
|
||||||
HTTPD_REQUEST_HANDLER_I
|
HTTPD_REQUEST_HANDLER_I
|
||||||
redefine
|
|
||||||
is_persistent_connection_supported
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Status report
|
|
||||||
|
|
||||||
is_persistent_connection_supported: BOOLEAN = False
|
|
||||||
-- <Precursor>
|
|
||||||
-- When there is no concurrency support, do not try to support
|
|
||||||
-- persistent connection!
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ feature -- Default timeout settings
|
|||||||
|
|
||||||
feature -- Default persistent connection settings
|
feature -- Default persistent connection settings
|
||||||
|
|
||||||
default_keep_alive_timeout: INTEGER = 15 -- seconds
|
default_keep_alive_timeout: INTEGER = 5 -- seconds
|
||||||
default_max_keep_alive_requests: INTEGER = 100
|
default_max_keep_alive_requests: INTEGER = 300
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
|||||||
@@ -140,7 +140,8 @@ feature -- Settings
|
|||||||
is_persistent_connection_supported: BOOLEAN
|
is_persistent_connection_supported: BOOLEAN
|
||||||
-- Is persistent connection supported?
|
-- Is persistent connection supported?
|
||||||
do
|
do
|
||||||
Result := {HTTPD_SERVER}.is_persistent_connection_supported and then max_keep_alive_requests > 0
|
Result := {HTTPD_SERVER}.is_persistent_connection_supported and then
|
||||||
|
max_keep_alive_requests /= 0 --| `-1` no limit
|
||||||
end
|
end
|
||||||
|
|
||||||
is_next_persistent_connection_supported: BOOLEAN
|
is_next_persistent_connection_supported: BOOLEAN
|
||||||
@@ -247,7 +248,8 @@ feature -- Execution
|
|||||||
l_exit
|
l_exit
|
||||||
loop
|
loop
|
||||||
n := n + 1
|
n := n + 1
|
||||||
if n >= m then
|
-- If `m` is `-1`, no limit for the number of keep_alive requests.
|
||||||
|
if m >= 0 and n >= m then
|
||||||
is_next_persistent_connection_supported := False
|
is_next_persistent_connection_supported := False
|
||||||
elseif n > 1 and is_verbose then
|
elseif n > 1 and is_verbose then
|
||||||
log ("Reuse connection (" + n.out + ")", information_level)
|
log ("Reuse connection (" + n.out + ")", information_level)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<library name="encoder" location="..\..\..\..\text\encoder\encoder.ecf"/>
|
<library name="encoder" location="..\..\..\..\text\encoder\encoder.ecf"/>
|
||||||
<library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
|
<library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
|
||||||
<library name="http" location="..\..\..\..\network\protocol\http\http.ecf"/>
|
<library name="http" location="..\..\..\..\network\protocol\http\http.ecf"/>
|
||||||
|
<library name="process" location="$ISE_LIBRARY\library\process\base\base_process.ecf"/>
|
||||||
<library name="wsf" location="wsf.ecf"/>
|
<library name="wsf" location="wsf.ecf"/>
|
||||||
<library name="wsf_router_context" location="wsf_router_context.ecf" readonly="true"/>
|
<library name="wsf_router_context" location="wsf_router_context.ecf" readonly="true"/>
|
||||||
<cluster name="extension" location="..\..\..\wsf\extension\" recursive="true"/>
|
<cluster name="extension" location="..\..\..\wsf\extension\" recursive="true"/>
|
||||||
|
|||||||
205
library/server/wsf/compression/wsf_compression.e
Normal file
205
library/server/wsf/compression/wsf_compression.e
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_COMPRESSION}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_COMPRESSION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make
|
||||||
|
-- Initialize compression support, by default no compression
|
||||||
|
-- Gzip with the following media types
|
||||||
|
-- applications/javascript
|
||||||
|
-- application/json
|
||||||
|
-- application/xml
|
||||||
|
-- text/css
|
||||||
|
-- text/html
|
||||||
|
--
|
||||||
|
do
|
||||||
|
-- compression algorithms
|
||||||
|
create {ARRAYED_LIST [STRING]} compression_supported_formats.make (0)
|
||||||
|
compression_supported_formats.compare_objects
|
||||||
|
|
||||||
|
-- media types supported by compression.
|
||||||
|
create {ARRAYED_LIST [STRING]} compression_enabled_media_types.make (0)
|
||||||
|
compression_enabled_media_types.compare_objects
|
||||||
|
set_default_compression_enabled_media_types
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Query
|
||||||
|
|
||||||
|
encoding_variants (req: WSF_REQUEST; ct: STRING): detachable HTTP_ACCEPT_ENCODING_VARIANTS
|
||||||
|
-- If the client support compression and the server support one of the algorithms
|
||||||
|
-- compress it and update the response header.
|
||||||
|
local
|
||||||
|
conneg : SERVER_CONTENT_NEGOTIATION
|
||||||
|
do
|
||||||
|
if
|
||||||
|
attached req.http_accept_encoding as l_http_encoding and then
|
||||||
|
not compression_supported_formats.is_empty and then
|
||||||
|
compression_enabled_media_types.has (ct)
|
||||||
|
then
|
||||||
|
create conneg.make ("", "", "", "")
|
||||||
|
Result := conneg.encoding_preference (compression_supported_formats, l_http_encoding)
|
||||||
|
if not Result.is_acceptable then
|
||||||
|
Result := Void
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Compression: constants
|
||||||
|
|
||||||
|
gzip_compression_format: STRING = "gzip"
|
||||||
|
-- RFC 1952 (gzip compressed format).
|
||||||
|
|
||||||
|
deflate_compression_format: STRING = "deflate"
|
||||||
|
-- RFC 1951 (deflate compressed format).
|
||||||
|
|
||||||
|
compress_compression_format: STRING = "compress"
|
||||||
|
-- RFC 1950 (zlib compressed format).
|
||||||
|
|
||||||
|
feature -- Compression
|
||||||
|
|
||||||
|
compression_supported_formats : LIST [STRING]
|
||||||
|
-- Server side compression supported formats.
|
||||||
|
-- Supported compression agorithms: `gzip_compression_format', `deflate_compression_format', `compress_compression_format'.
|
||||||
|
-- identity, means no compression at all.
|
||||||
|
|
||||||
|
compression_enabled_media_types: LIST [STRING]
|
||||||
|
-- List of media types supported by compression.
|
||||||
|
|
||||||
|
set_default_compression_format
|
||||||
|
-- gzip default format.
|
||||||
|
do
|
||||||
|
enable_gzip_compression
|
||||||
|
end
|
||||||
|
|
||||||
|
disable_all_compression_formats
|
||||||
|
-- Remove all items.
|
||||||
|
do
|
||||||
|
compression_supported_formats.wipe_out
|
||||||
|
end
|
||||||
|
|
||||||
|
enable_gzip_compression
|
||||||
|
-- add 'gzip' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.force (gzip_compression_format)
|
||||||
|
ensure
|
||||||
|
has_gzip: compression_supported_formats.has (gzip_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
disable_gzip_compression
|
||||||
|
-- remove 'gzip' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.prune (gzip_compression_format)
|
||||||
|
ensure
|
||||||
|
not_gzip: not compression_supported_formats.has (gzip_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
enable_deflate_compression
|
||||||
|
-- add 'deflate' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.force (deflate_compression_format)
|
||||||
|
ensure
|
||||||
|
has_deflate: compression_supported_formats.has (deflate_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
disable_deflate_compression
|
||||||
|
-- remove 'deflate' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.prune (deflate_compression_format)
|
||||||
|
ensure
|
||||||
|
not_deflate: not compression_supported_formats.has (deflate_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
enable_compress_compression
|
||||||
|
-- add 'compress' format to the list of 'compression_supported' formats
|
||||||
|
do
|
||||||
|
compression_supported_formats.force (compress_compression_format)
|
||||||
|
ensure
|
||||||
|
has_compress: compression_supported_formats.has (compress_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
disable_compress_compression
|
||||||
|
-- remove 'deflate' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.prune (compress_compression_format)
|
||||||
|
ensure
|
||||||
|
no_compress: not compression_supported_formats.has (compress_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Compression: media types
|
||||||
|
|
||||||
|
set_default_compression_enabled_media_types
|
||||||
|
-- Default media types
|
||||||
|
-- applications/javascript
|
||||||
|
-- application/json
|
||||||
|
-- application/xml
|
||||||
|
-- text/css
|
||||||
|
-- text/html
|
||||||
|
-- text/plain
|
||||||
|
do
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_javascript)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_json)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_xml)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_css)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_html)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_plain)
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_all_compression_enabled_media_types
|
||||||
|
-- Remove all items.
|
||||||
|
do
|
||||||
|
compression_enabled_media_types.wipe_out
|
||||||
|
end
|
||||||
|
|
||||||
|
enable_compression_for_media_type (a_media_type: STRING)
|
||||||
|
do
|
||||||
|
compression_enabled_media_types.force (a_media_type)
|
||||||
|
ensure
|
||||||
|
has_media_type: compression_enabled_media_types.has (a_media_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Compress Data
|
||||||
|
|
||||||
|
compressed_string (a_string: STRING; a_encoding: STRING): STRING
|
||||||
|
-- Compress `a_string' using `deflate_compression_format'
|
||||||
|
local
|
||||||
|
dc: ZLIB_STRING_COMPRESS
|
||||||
|
do
|
||||||
|
create Result.make_empty
|
||||||
|
create dc.string_stream_with_size (Result, 32_768) -- chunk size 32k
|
||||||
|
dc.put_string_with_options (a_string, {ZLIB_CONSTANTS}.Z_default_compression, zlb_strategy (a_encoding), {ZLIB_CONSTANTS}.Z_mem_level_9, {ZLIB_CONSTANTS}.z_default_strategy.to_integer_32)
|
||||||
|
-- We use the default compression level
|
||||||
|
-- We use the default value for windows bits, the range is 8..15. Higher values use more memory, but produce smaller output.
|
||||||
|
-- Memory: Higher values use more memory, but are faster and produce smaller output. The default is 8, we use 9.
|
||||||
|
end
|
||||||
|
|
||||||
|
zlb_strategy (a_encoding: STRING): INTEGER
|
||||||
|
do
|
||||||
|
if a_encoding.is_case_insensitive_equal_general (gzip_compression_format) then
|
||||||
|
Result := {ZLIB_CONSTANTS}.z_default_window_bits + 16
|
||||||
|
elseif a_encoding.is_case_insensitive_equal_general (deflate_compression_format) then
|
||||||
|
Result := -{ZLIB_CONSTANTS}.z_default_window_bits
|
||||||
|
else
|
||||||
|
check compress: a_encoding.is_case_insensitive_equal_general (compress_compression_format) end
|
||||||
|
Result := {ZLIB_CONSTANTS}.z_default_window_bits
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_FILE_RESPONSE_WITH_COMPRESSION}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_FILE_RESPONSE_WITH_COMPRESSION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_FILE_RESPONSE
|
||||||
|
redefine
|
||||||
|
send_to
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make_with_path,
|
||||||
|
make_with_content_type_and_path,
|
||||||
|
make_html_with_path,
|
||||||
|
make,
|
||||||
|
make_with_content_type,
|
||||||
|
make_html
|
||||||
|
|
||||||
|
feature {NONE} -- Access
|
||||||
|
|
||||||
|
compression_variants: detachable HTTP_ACCEPT_ENCODING_VARIANTS
|
||||||
|
|
||||||
|
compression: detachable WSF_COMPRESSION
|
||||||
|
|
||||||
|
feature -- Compression setting
|
||||||
|
|
||||||
|
apply_compression (a_compression: WSF_COMPRESSION; req: WSF_REQUEST)
|
||||||
|
do
|
||||||
|
compression := a_compression
|
||||||
|
compression_variants := a_compression.encoding_variants (req, content_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
reset_compression
|
||||||
|
do
|
||||||
|
compression := Void
|
||||||
|
compression_variants := Void
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {WSF_RESPONSE} -- Output
|
||||||
|
|
||||||
|
send_to (res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
if status_code = {HTTP_STATUS_CODE}.not_found then
|
||||||
|
-- File not found, then no more data.
|
||||||
|
elseif
|
||||||
|
attached compression as l_compression and then
|
||||||
|
attached compression_variants as l_compression_variants and then
|
||||||
|
attached l_compression_variants.encoding as l_encoding and then
|
||||||
|
attached l_compression_variants.vary_header_value as l_vary_header
|
||||||
|
then
|
||||||
|
send_compressed_to (res, l_compression, l_encoding, l_vary_header)
|
||||||
|
else
|
||||||
|
-- Send uncompressed...
|
||||||
|
Precursor (res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send_compressed_to (res: WSF_RESPONSE; a_compression: WSF_COMPRESSION; a_comp_encoding, a_comp_vary_header: READABLE_STRING_8)
|
||||||
|
local
|
||||||
|
s: detachable READABLE_STRING_8
|
||||||
|
l_content, l_compressed_content: STRING_8
|
||||||
|
f: RAW_FILE
|
||||||
|
l_count: INTEGER
|
||||||
|
do
|
||||||
|
res.set_status_code (status_code)
|
||||||
|
create f.make_with_path (file_path)
|
||||||
|
l_count := f.count
|
||||||
|
f.open_read
|
||||||
|
f.read_stream (l_count)
|
||||||
|
f.close
|
||||||
|
l_content := f.last_string
|
||||||
|
|
||||||
|
s := head
|
||||||
|
if s /= Void then
|
||||||
|
l_content.prepend (s)
|
||||||
|
end
|
||||||
|
s := bottom
|
||||||
|
if s /= Void then
|
||||||
|
l_content.append_string (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
l_compressed_content := a_compression.compressed_string (l_content, a_comp_encoding)
|
||||||
|
|
||||||
|
debug
|
||||||
|
res.put_error (l_content.count.out + " -(compression-> " + l_compressed_content.count.out + "%N")
|
||||||
|
end
|
||||||
|
header.put_content_encoding (a_comp_encoding)
|
||||||
|
header.add_header ("Vary:" + a_comp_vary_header)
|
||||||
|
header.put_content_length (l_compressed_content.count)
|
||||||
|
|
||||||
|
res.put_header_text (header.string)
|
||||||
|
if not answer_head_request_method then
|
||||||
|
res.put_string (l_compressed_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_FILE_SYSTEM_HANDLER_WITH_COMPRESSION}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_FILE_SYSTEM_HANDLER_WITH_COMPRESSION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_FILE_SYSTEM_HANDLER
|
||||||
|
redefine
|
||||||
|
initialize,
|
||||||
|
process_transfert
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make_with_path,
|
||||||
|
make_hidden_with_path,
|
||||||
|
make,
|
||||||
|
make_hidden
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
create compression.make
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access: compression
|
||||||
|
|
||||||
|
compression: WSF_COMPRESSION
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
process_transfert (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
local
|
||||||
|
ext: READABLE_STRING_32
|
||||||
|
ct: detachable READABLE_STRING_8
|
||||||
|
fres: WSF_FILE_RESPONSE_WITH_COMPRESSION
|
||||||
|
dt: DATE_TIME
|
||||||
|
do
|
||||||
|
ext := extension (f.path.name)
|
||||||
|
ct := extension_mime_mapping.mime_type (ext)
|
||||||
|
if ct = Void then
|
||||||
|
ct := {HTTP_MIME_TYPES}.application_force_download
|
||||||
|
end
|
||||||
|
|
||||||
|
create fres.make_with_content_type_and_path (ct, f.path)
|
||||||
|
-- Apply compression based on request `req` header.
|
||||||
|
fres.apply_compression (compression, req)
|
||||||
|
|
||||||
|
-- Prepare response
|
||||||
|
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||||
|
|
||||||
|
-- cache control
|
||||||
|
create dt.make_now_utc
|
||||||
|
fres.header.put_utc_date (dt)
|
||||||
|
if max_age >= 0 then
|
||||||
|
fres.set_max_age (max_age)
|
||||||
|
if max_age > 0 then
|
||||||
|
dt := dt.twin
|
||||||
|
dt.second_add (max_age)
|
||||||
|
end
|
||||||
|
fres.set_expires_date (dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- send
|
||||||
|
fres.set_answer_head_request_method (req.request_method.same_string ({HTTP_REQUEST_METHODS}.method_head))
|
||||||
|
res.send (fres)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
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
|
||||||
@@ -66,7 +66,7 @@ feature -- Access: connection
|
|||||||
end
|
end
|
||||||
|
|
||||||
max_tcp_clients: INTEGER assign set_max_tcp_clients
|
max_tcp_clients: INTEGER assign set_max_tcp_clients
|
||||||
-- Listen on socket for at most `queue' connections.
|
-- Listen on socket for at most `max_tcp_clients' connections.
|
||||||
do
|
do
|
||||||
Result := option_integer_value ("max_tcp_clients", 0)
|
Result := option_integer_value ("max_tcp_clients", 0)
|
||||||
end
|
end
|
||||||
@@ -104,11 +104,26 @@ feature -- Access: persistent connection
|
|||||||
-- Maximum number of requests allowed per persistent connection.
|
-- Maximum number of requests allowed per persistent connection.
|
||||||
-- Recommended a high setting.
|
-- Recommended a high setting.
|
||||||
-- To disable KeepAlive, set `max_keep_alive_requests' to 0.
|
-- To disable KeepAlive, set `max_keep_alive_requests' to 0.
|
||||||
|
-- To have no limit, set `max_keep_alive_requests' to -1.
|
||||||
-- By default: {HTTPD_CONFIGURATION_I}.default_max_keep_alive_requests .
|
-- By default: {HTTPD_CONFIGURATION_I}.default_max_keep_alive_requests .
|
||||||
do
|
do
|
||||||
Result := option_integer_value ("max_keep_alive_requests", 0)
|
Result := option_integer_value ("max_keep_alive_requests", 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
persistent_connection_disabled: BOOLEAN
|
||||||
|
-- Persistent connection disabled?
|
||||||
|
-- (or Keep-Alive disabled).
|
||||||
|
do
|
||||||
|
Result := max_keep_alive_requests = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
has_unlimited_keep_alive_requests: BOOLEAN
|
||||||
|
-- Has unlimited count of keep alive requests.
|
||||||
|
-- i.e no limit of number of requests allowed per persistent connection.
|
||||||
|
do
|
||||||
|
Result := max_keep_alive_requests < 0
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Access: SSL
|
feature -- Access: SSL
|
||||||
|
|
||||||
is_secure: BOOLEAN assign set_is_secure
|
is_secure: BOOLEAN assign set_is_secure
|
||||||
@@ -205,6 +220,16 @@ feature -- Element change
|
|||||||
set_numeric_option ("max_keep_alive_requests", nb)
|
set_numeric_option ("max_keep_alive_requests", nb)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_unlimited_keep_alive_requests
|
||||||
|
do
|
||||||
|
set_max_keep_alive_requests (-1)
|
||||||
|
end
|
||||||
|
|
||||||
|
disable_persistent_connection
|
||||||
|
do
|
||||||
|
set_max_keep_alive_requests (0)
|
||||||
|
end
|
||||||
|
|
||||||
set_is_secure (b: BOOLEAN)
|
set_is_secure (b: BOOLEAN)
|
||||||
-- Set secured connection enabled to `b'.
|
-- Set secured connection enabled to `b'.
|
||||||
-- i.e if connection is using SSL/TLS.
|
-- i.e if connection is using SSL/TLS.
|
||||||
|
|||||||
@@ -88,6 +88,23 @@ feature -- Websocket events
|
|||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
|
feature {WEB_SOCKET} -- Timeout.
|
||||||
|
|
||||||
|
timer_delay: INTEGER
|
||||||
|
-- Maximal duration in seconds between two `on_timeout` event.
|
||||||
|
-- Disable timeout event, by setting it to `0` (default).
|
||||||
|
|
||||||
|
set_timer_delay (nb_secs: INTEGER)
|
||||||
|
do
|
||||||
|
timer_delay := nb_secs
|
||||||
|
end
|
||||||
|
|
||||||
|
on_timer (ws: WEB_SOCKET)
|
||||||
|
-- Called every `timer_delay` seconds.
|
||||||
|
-- Note: redefine to use.
|
||||||
|
do
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Websocket events: implemented
|
feature -- Websocket events: implemented
|
||||||
|
|
||||||
on_pong (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
on_pong (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||||||
@@ -126,7 +143,7 @@ feature -- Websocket events: implemented
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
note
|
note
|
||||||
description: "[
|
description: "[
|
||||||
Object representing the websocket connection.
|
Object representing the websocket connection.
|
||||||
It contains the `request` and `response`, and more important the `socket` itself.
|
It contains internally the `request` and `response`, and more important the `socket` itself.
|
||||||
]"
|
]"
|
||||||
date: "$Date$"
|
date: "$Date$"
|
||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
@@ -10,16 +10,14 @@ class
|
|||||||
WEB_SOCKET
|
WEB_SOCKET
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
|
WEB_SOCKET_WRITER
|
||||||
|
|
||||||
WGI_STANDALONE_CONNECTOR_EXPORTER
|
WGI_STANDALONE_CONNECTOR_EXPORTER
|
||||||
|
|
||||||
WSF_RESPONSE_EXPORTER
|
WSF_RESPONSE_EXPORTER
|
||||||
|
|
||||||
WGI_EXPORTER
|
WGI_EXPORTER
|
||||||
|
|
||||||
HTTPD_LOGGER_CONSTANTS
|
|
||||||
|
|
||||||
WEB_SOCKET_CONSTANTS
|
|
||||||
|
|
||||||
SHARED_BASE64
|
SHARED_BASE64
|
||||||
|
|
||||||
create
|
create
|
||||||
@@ -32,7 +30,7 @@ feature {NONE} -- Initialization
|
|||||||
request := req
|
request := req
|
||||||
response := res
|
response := res
|
||||||
is_verbose := False
|
is_verbose := False
|
||||||
verbose_level := notice_level
|
verbose_level := {HTTPD_LOGGER_CONSTANTS}.notice_level
|
||||||
|
|
||||||
if
|
if
|
||||||
attached {WGI_STANDALONE_INPUT_STREAM} req.input as r_input
|
attached {WGI_STANDALONE_INPUT_STREAM} req.input as r_input
|
||||||
@@ -44,11 +42,6 @@ feature {NONE} -- Initialization
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
socket: HTTPD_STREAM_SOCKET
|
|
||||||
-- Underlying connected socket.
|
|
||||||
|
|
||||||
feature {NONE} -- Access
|
feature {NONE} -- Access
|
||||||
|
|
||||||
request: WSF_REQUEST
|
request: WSF_REQUEST
|
||||||
@@ -75,14 +68,7 @@ feature -- Status
|
|||||||
has_error: BOOLEAN
|
has_error: BOOLEAN
|
||||||
-- Error occured during processing?
|
-- Error occured during processing?
|
||||||
|
|
||||||
feature -- Socket status
|
feature -- Status report
|
||||||
|
|
||||||
is_ready_for_reading: BOOLEAN
|
|
||||||
-- Is `socket' ready for reading?
|
|
||||||
--| at this point, socket should be set to blocking.
|
|
||||||
do
|
|
||||||
Result := socket.ready_for_reading
|
|
||||||
end
|
|
||||||
|
|
||||||
is_open_read: BOOLEAN
|
is_open_read: BOOLEAN
|
||||||
-- Is `socket' open for reading?
|
-- Is `socket' open for reading?
|
||||||
@@ -96,12 +82,6 @@ feature -- Socket status
|
|||||||
Result := socket.is_open_write
|
Result := socket.is_open_write
|
||||||
end
|
end
|
||||||
|
|
||||||
socket_descriptor: INTEGER
|
|
||||||
-- Descriptor for current `socket'.
|
|
||||||
do
|
|
||||||
Result := socket.descriptor
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
set_is_verbose (b: BOOLEAN)
|
set_is_verbose (b: BOOLEAN)
|
||||||
@@ -129,7 +109,7 @@ feature -- Basic operation
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Basic Operation
|
feature {WSF_WEBSOCKET_EXECUTION} -- Basic Operation
|
||||||
|
|
||||||
open_ws_handshake
|
open_ws_handshake
|
||||||
-- The opening handshake is intended to be compatible with HTTP-based
|
-- The opening handshake is intended to be compatible with HTTP-based
|
||||||
@@ -164,10 +144,10 @@ feature -- Basic Operation
|
|||||||
|
|
||||||
-- TODO extract to a validator handshake or something like that.
|
-- TODO extract to a validator handshake or something like that.
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("%NReceive <====================", debug_level)
|
log ("%NReceive <====================", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
if attached req.raw_header_data as rhd then
|
if attached req.raw_header_data as rhd then
|
||||||
check raw_header_is_valid_as_string_8: rhd.is_valid_as_string_8 end
|
check raw_header_is_valid_as_string_8: rhd.is_valid_as_string_8 end
|
||||||
log (rhd.to_string_8, debug_level)
|
log (rhd.to_string_8, {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if
|
if
|
||||||
@@ -186,7 +166,7 @@ feature -- Basic Operation
|
|||||||
attached req.http_host -- Host header must be present
|
attached req.http_host -- Host header must be present
|
||||||
then
|
then
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("key " + l_ws_key, debug_level)
|
log ("key " + l_ws_key, {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
end
|
end
|
||||||
-- Sending the server's opening handshake
|
-- Sending the server's opening handshake
|
||||||
create l_sha1.make
|
create l_sha1.make
|
||||||
@@ -198,9 +178,9 @@ feature -- Basic Operation
|
|||||||
res.header.add_header_key_value ("Sec-WebSocket-Accept", l_key)
|
res.header.add_header_key_value ("Sec-WebSocket-Accept", l_key)
|
||||||
|
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("%N================> Send Handshake", debug_level)
|
log ("%N================> Send Handshake", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
if attached {HTTP_HEADER} res.header as h then
|
if attached {HTTP_HEADER} res.header as h then
|
||||||
log (h.string, debug_level)
|
log (h.string, {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
res.set_status_code_with_reason_phrase (101, "Switching Protocols")
|
res.set_status_code_with_reason_phrase (101, "Switching Protocols")
|
||||||
@@ -208,7 +188,7 @@ feature -- Basic Operation
|
|||||||
else
|
else
|
||||||
has_error := True
|
has_error := True
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("Error (opening_handshake)!!!", debug_level)
|
log ("Error (opening_handshake)!!!", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
end
|
end
|
||||||
-- If we cannot complete the handshake, then the server MUST stop processing the client's handshake and return an HTTP response with an
|
-- If we cannot complete the handshake, then the server MUST stop processing the client's handshake and return an HTTP response with an
|
||||||
-- appropriate error code (such as 400 Bad Request).
|
-- appropriate error code (such as 400 Bad Request).
|
||||||
@@ -219,80 +199,77 @@ feature -- Basic Operation
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Response!
|
feature {WEB_SOCKET_HANDLER} -- Networking
|
||||||
|
|
||||||
send (a_opcode:INTEGER; a_message: READABLE_STRING_8)
|
socket: HTTPD_STREAM_SOCKET
|
||||||
|
-- Underlying connected socket.
|
||||||
|
|
||||||
|
has_input: BOOLEAN
|
||||||
|
-- Set by `wait_for_input`.
|
||||||
|
|
||||||
|
wait_for_input (cb: detachable WEB_SOCKET_EVENT_I)
|
||||||
local
|
local
|
||||||
i: INTEGER
|
l_timeout, nb: INTEGER
|
||||||
l_chunk_size: INTEGER
|
l_cb_timeout: INTEGER
|
||||||
l_chunk: READABLE_STRING_8
|
|
||||||
l_header_message: STRING
|
|
||||||
l_message_count: INTEGER
|
|
||||||
n: NATURAL_64
|
|
||||||
retried: BOOLEAN
|
|
||||||
do
|
do
|
||||||
debug ("ws")
|
has_input := False
|
||||||
print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N")
|
if cb = Void then
|
||||||
end
|
has_input := socket.ready_for_reading
|
||||||
if not retried then
|
else
|
||||||
create l_header_message.make_empty
|
l_cb_timeout := cb.timer_delay
|
||||||
l_header_message.append_code ((0x80 | a_opcode).to_natural_32)
|
l_timeout := socket.timeout
|
||||||
l_message_count := a_message.count
|
if l_cb_timeout = 0 then
|
||||||
n := l_message_count.to_natural_64
|
-- timeout event not enabled.
|
||||||
if l_message_count > 0xffff then
|
has_input := socket.ready_for_reading
|
||||||
--! Improve. this code needs to be checked.
|
|
||||||
l_header_message.append_code ((0 | 127).to_natural_32)
|
|
||||||
l_header_message.append_character ((n |>> 56).to_character_8)
|
|
||||||
l_header_message.append_character ((n |>> 48).to_character_8)
|
|
||||||
l_header_message.append_character ((n |>> 40).to_character_8)
|
|
||||||
l_header_message.append_character ((n |>> 32).to_character_8)
|
|
||||||
l_header_message.append_character ((n |>> 24).to_character_8)
|
|
||||||
l_header_message.append_character ((n |>> 16).to_character_8)
|
|
||||||
l_header_message.append_character ((n |>> 8).to_character_8)
|
|
||||||
l_header_message.append_character ( n.to_character_8)
|
|
||||||
elseif l_message_count > 125 then
|
|
||||||
l_header_message.append_code ((0 | 126).to_natural_32)
|
|
||||||
l_header_message.append_code ((n |>> 8).as_natural_32)
|
|
||||||
l_header_message.append_character (n.to_character_8)
|
|
||||||
else
|
else
|
||||||
l_header_message.append_code (n.as_natural_32)
|
cb.on_timer (Current)
|
||||||
end
|
if l_cb_timeout > l_timeout then
|
||||||
socket.put_string_8_noexception (l_header_message)
|
-- event timeout duration is bigger than socket timeout
|
||||||
if not socket.was_error then
|
-- thus, no on_timeout before next frame waiting
|
||||||
l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable.
|
has_input := socket.ready_for_reading
|
||||||
if l_message_count < l_chunk_size then
|
|
||||||
socket.put_string_8_noexception (a_message)
|
|
||||||
else
|
else
|
||||||
from
|
from
|
||||||
i := 0
|
l_timeout := socket.timeout
|
||||||
|
nb := l_timeout
|
||||||
|
socket.set_timeout (l_cb_timeout) -- FIXME: for now 1 sec is the smaller timeout we can use.
|
||||||
until
|
until
|
||||||
l_chunk_size = 0 or socket.was_error
|
has_input or nb <= 0
|
||||||
loop
|
loop
|
||||||
debug ("ws")
|
has_input := socket.ready_for_reading
|
||||||
print ("Sending chunk " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
|
if not has_input then
|
||||||
|
-- Call on_timeout only if there is no input,
|
||||||
|
-- otherwise it was called once before the initial wait.
|
||||||
|
socket.set_timeout (l_timeout)
|
||||||
|
cb.on_timer (Current)
|
||||||
|
socket.set_timeout (l_cb_timeout)
|
||||||
end
|
end
|
||||||
l_chunk := a_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
|
nb := nb - l_cb_timeout
|
||||||
socket.put_string_8_noexception (l_chunk)
|
|
||||||
if l_chunk.count < l_chunk_size then
|
|
||||||
l_chunk_size := 0
|
|
||||||
end
|
|
||||||
i := i + l_chunk_size
|
|
||||||
end
|
|
||||||
debug ("ws")
|
|
||||||
print ("Sending chunk done%N")
|
|
||||||
end
|
end
|
||||||
|
socket.set_timeout (l_timeout)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
|
||||||
-- FIXME: what should be done on rescue?
|
|
||||||
end
|
end
|
||||||
rescue
|
|
||||||
retried := True
|
|
||||||
io.put_string ("Internal error in " + generator + ".do_send (conn, a_opcode=" + a_opcode.out + ", a_message) !%N")
|
|
||||||
retry
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
socket_descriptor: INTEGER
|
||||||
|
-- Descriptor for current `socket'.
|
||||||
|
do
|
||||||
|
Result := socket.descriptor
|
||||||
|
end
|
||||||
|
|
||||||
|
socket_put_string (s: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
socket.put_string_8_noexception (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
socket_was_error: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := socket.was_error
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {WEB_SOCKET_HANDLER} -- Frame
|
||||||
|
|
||||||
next_frame: detachable WEB_SOCKET_FRAME
|
next_frame: detachable WEB_SOCKET_FRAME
|
||||||
-- TODO Binary messages
|
-- TODO Binary messages
|
||||||
-- Handle error responses in a better way.
|
-- Handle error responses in a better way.
|
||||||
@@ -402,7 +379,7 @@ feature -- Response!
|
|||||||
if Result.is_valid then
|
if Result.is_valid then
|
||||||
--| valid frame/fragment
|
--| valid frame/fragment
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("+ frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")", debug_level)
|
log ("+ frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- rsv validation
|
-- rsv validation
|
||||||
@@ -420,7 +397,7 @@ feature -- Response!
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log ("+ INVALID frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")", debug_level)
|
log ("+ INVALID frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -548,7 +525,7 @@ feature -- Response!
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log (" Received " + l_fetch_count.out + " out of " + l_len.out + " bytes <===============", debug_level)
|
log (" Received " + l_fetch_count.out + " out of " + l_len.out + " bytes <===============", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
end
|
end
|
||||||
debug ("ws")
|
debug ("ws")
|
||||||
print (" -> ")
|
print (" -> ")
|
||||||
@@ -580,7 +557,7 @@ feature -- Response!
|
|||||||
if Result /= Void then
|
if Result /= Void then
|
||||||
if attached Result.error as err then
|
if attached Result.error as err then
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log (" !Invalid frame: " + err.string, debug_level)
|
log (" !Invalid frame: " + err.string, {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if Result.is_injected_control then
|
if Result.is_injected_control then
|
||||||
@@ -624,8 +601,7 @@ feature -- Response!
|
|||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Encoding
|
||||||
feature -- Encoding
|
|
||||||
|
|
||||||
digest (a_sha1: SHA1): STRING
|
digest (a_sha1: SHA1): STRING
|
||||||
-- Digest of `a_sha1'.
|
-- Digest of `a_sha1'.
|
||||||
@@ -672,7 +648,7 @@ feature {NONE} -- Socket helpers
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Masking Data Client - Server
|
feature {NONE} -- Masking Data Client - Server
|
||||||
|
|
||||||
unmask (a_chunk: STRING_8; a_pos: INTEGER; a_key: READABLE_STRING_8)
|
unmask (a_chunk: STRING_8; a_pos: INTEGER; a_key: READABLE_STRING_8)
|
||||||
local
|
local
|
||||||
@@ -795,7 +771,6 @@ feature {NONE} -- Debug
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ feature -- Execution
|
|||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
dbglog (generator + ".execute_websocket (loop) WS_REQUEST_HANDLER.process_request {" + ws.socket_descriptor.out + "}")
|
dbglog (generator + ".execute_websocket (loop) WS_REQUEST_HANDLER.process_request {" + ws.socket_descriptor.out + "}")
|
||||||
end
|
end
|
||||||
if ws.is_ready_for_reading then
|
ws.wait_for_input (callbacks)
|
||||||
|
if ws.has_input then
|
||||||
l_frame := ws.next_frame
|
l_frame := ws.next_frame
|
||||||
if l_frame /= Void and then l_frame.is_valid then
|
if l_frame /= Void and then l_frame.is_valid then
|
||||||
if attached l_frame.injected_control_frames as l_injections then
|
if attached l_frame.injected_control_frames as l_injections then
|
||||||
@@ -140,7 +141,7 @@ feature {NONE} -- Logging
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WEB_SOCKET_WRITER}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WEB_SOCKET_WRITER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WEB_SOCKET_CONSTANTS
|
||||||
|
|
||||||
|
feature -- Messages
|
||||||
|
|
||||||
|
send_text (a_message: READABLE_STRING_8)
|
||||||
|
-- Send text frame `a_message`.
|
||||||
|
do
|
||||||
|
send (text_frame, a_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
send_connection_close (a_message: detachable READABLE_STRING_8)
|
||||||
|
-- Send connection close frame `a_message`.
|
||||||
|
do
|
||||||
|
send (connection_close_frame, a_message)
|
||||||
|
end
|
||||||
|
|
||||||
|
send_binary (a_data: READABLE_STRING_8)
|
||||||
|
-- Send binary frame `a_data`.
|
||||||
|
do
|
||||||
|
send (Binary_frame, a_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Custom Message
|
||||||
|
|
||||||
|
send (a_opcode: INTEGER; a_message: detachable READABLE_STRING_8)
|
||||||
|
local
|
||||||
|
i: INTEGER
|
||||||
|
l_chunk_size: INTEGER
|
||||||
|
l_chunk: READABLE_STRING_8
|
||||||
|
l_header_message: STRING
|
||||||
|
l_message_count: INTEGER
|
||||||
|
n: NATURAL_64
|
||||||
|
retried: BOOLEAN
|
||||||
|
do
|
||||||
|
debug ("ws")
|
||||||
|
print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N")
|
||||||
|
end
|
||||||
|
if not retried then
|
||||||
|
create l_header_message.make_empty
|
||||||
|
l_header_message.append_code ((0x80 | a_opcode).to_natural_32)
|
||||||
|
if a_message /= Void then
|
||||||
|
l_message_count := a_message.count
|
||||||
|
else
|
||||||
|
l_message_count := 0
|
||||||
|
end
|
||||||
|
n := l_message_count.to_natural_64
|
||||||
|
if l_message_count > 0xffff then
|
||||||
|
--! Improve. this code needs to be checked.
|
||||||
|
l_header_message.append_code ((0 | 127).to_natural_32)
|
||||||
|
l_header_message.append_character ((n |>> 56).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 48).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 40).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 32).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 24).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 16).to_character_8)
|
||||||
|
l_header_message.append_character ((n |>> 8).to_character_8)
|
||||||
|
l_header_message.append_character ( n.to_character_8)
|
||||||
|
elseif l_message_count > 125 then
|
||||||
|
l_header_message.append_code ((0 | 126).to_natural_32)
|
||||||
|
l_header_message.append_code ((n |>> 8).as_natural_32)
|
||||||
|
l_header_message.append_character (n.to_character_8)
|
||||||
|
else
|
||||||
|
l_header_message.append_code (n.as_natural_32)
|
||||||
|
end
|
||||||
|
socket_put_string (l_header_message)
|
||||||
|
if not socket_was_error then
|
||||||
|
l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable.
|
||||||
|
if a_message = Void or else l_message_count < l_chunk_size then
|
||||||
|
if a_message /= Void then
|
||||||
|
socket_put_string (a_message)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
from
|
||||||
|
i := 0
|
||||||
|
until
|
||||||
|
l_chunk_size = 0 or socket_was_error
|
||||||
|
loop
|
||||||
|
debug ("ws")
|
||||||
|
print ("Sending chunk " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
|
||||||
|
end
|
||||||
|
l_chunk := a_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
|
||||||
|
socket_put_string (l_chunk)
|
||||||
|
if l_chunk.count < l_chunk_size then
|
||||||
|
l_chunk_size := 0
|
||||||
|
end
|
||||||
|
i := i + l_chunk_size
|
||||||
|
end
|
||||||
|
debug ("ws")
|
||||||
|
print ("Sending chunk done%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
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")
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Networking
|
||||||
|
|
||||||
|
socket_put_string (s: READABLE_STRING_8)
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
socket_was_error: BOOLEAN
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, 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
|
||||||
237
library/server/wsf/extension/handler/cgi/wsf_cgi_handler.e
Normal file
237
library/server/wsf/extension/handler/cgi/wsf_cgi_handler.e
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
note
|
||||||
|
description: "Handler to process CGI script."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_CGI_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_EXECUTE_HANDLER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Creation
|
||||||
|
|
||||||
|
make (a_dir: PATH)
|
||||||
|
do
|
||||||
|
working_direction := a_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Settings
|
||||||
|
|
||||||
|
buffer_size: INTEGER = 1_024
|
||||||
|
|
||||||
|
working_direction: PATH
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
cgi_file_path (req: WSF_REQUEST): PATH
|
||||||
|
local
|
||||||
|
l_path_info: READABLE_STRING_32
|
||||||
|
do
|
||||||
|
-- Path to CGI executable.
|
||||||
|
l_path_info := req.path_info
|
||||||
|
if l_path_info.starts_with_general ("/") then
|
||||||
|
l_path_info := l_path_info.substring (2, l_path_info.count)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Process
|
||||||
|
Result := working_direction.extended (l_path_info)
|
||||||
|
end
|
||||||
|
|
||||||
|
exists (req: WSF_REQUEST): BOOLEAN
|
||||||
|
-- CGI file exists?
|
||||||
|
do
|
||||||
|
Result := (create {FILE_UTILITIES}).file_path_exists (cgi_file_path (req))
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
-- Execute `req' responding in `res'.
|
||||||
|
local
|
||||||
|
fut: FILE_UTILITIES
|
||||||
|
l_exec_path: PATH
|
||||||
|
proc: BASE_PROCESS
|
||||||
|
l_input_env: STRING_TABLE [READABLE_STRING_GENERAL]
|
||||||
|
l_input_header: detachable STRING
|
||||||
|
l_input_buf: STRING
|
||||||
|
l_output: STRING
|
||||||
|
l_output_header_sent: BOOLEAN
|
||||||
|
l_error: STRING
|
||||||
|
s: STRING
|
||||||
|
i, j, n: INTEGER
|
||||||
|
do
|
||||||
|
-- Header
|
||||||
|
if attached req.raw_header_data as l_header then
|
||||||
|
l_input_header := l_header
|
||||||
|
end
|
||||||
|
-- Input data
|
||||||
|
create l_input_buf.make (req.content_length_value.to_integer_32)
|
||||||
|
req.read_input_data_into (l_input_buf)
|
||||||
|
|
||||||
|
-- Input environment
|
||||||
|
create l_input_env.make (10)
|
||||||
|
across
|
||||||
|
req.meta_variables as ic
|
||||||
|
loop
|
||||||
|
if attached {WSF_STRING} ic.item as var then
|
||||||
|
l_input_env.force (var.value, var.name)
|
||||||
|
else
|
||||||
|
check supported: False end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- No need to import `l_input_header` in environment
|
||||||
|
-- As current connector already did the job.
|
||||||
|
|
||||||
|
-- Process
|
||||||
|
l_exec_path := cgi_file_path (req)
|
||||||
|
if fut.file_path_exists (l_exec_path) then
|
||||||
|
proc := (create {BASE_PROCESS_FACTORY}).process_launcher (l_exec_path.name, Void, working_direction.name)
|
||||||
|
proc.set_hidden (True)
|
||||||
|
proc.set_environment_variable_table (l_input_env)
|
||||||
|
proc.set_separate_console (False)
|
||||||
|
proc.redirect_input_to_stream
|
||||||
|
proc.redirect_output_to_stream
|
||||||
|
proc.redirect_error_to_stream
|
||||||
|
|
||||||
|
-- Launch CGI execution
|
||||||
|
proc.launch
|
||||||
|
if proc.launched then
|
||||||
|
-- Do not send the header to CGI script
|
||||||
|
-- value are passed via environment variables
|
||||||
|
-- proc.put_string (l_input_header)
|
||||||
|
-- Send payload.
|
||||||
|
proc.put_string (l_input_buf)
|
||||||
|
|
||||||
|
create l_output.make_empty
|
||||||
|
create l_error.make_empty
|
||||||
|
get_output_and_error_from_process (proc, l_output, l_error)
|
||||||
|
if l_error /= Void and then not l_error.is_whitespace then
|
||||||
|
res.put_error (l_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Wait for process exit
|
||||||
|
if not proc.has_exited then
|
||||||
|
proc.wait_for_exit
|
||||||
|
end
|
||||||
|
if proc.exit_code /= 0 then
|
||||||
|
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
|
||||||
|
s := "CGI script execution failed [exit code=" + proc.exit_code.out+ "]!"
|
||||||
|
res.header.put_content_type_utf_8_text_plain
|
||||||
|
res.header.put_content_length (s.count)
|
||||||
|
res.put_string (s)
|
||||||
|
else
|
||||||
|
-- Send the response
|
||||||
|
-- error already sent via `res.put_error (l_error)`
|
||||||
|
from
|
||||||
|
i := 1
|
||||||
|
n := l_output.count
|
||||||
|
until
|
||||||
|
i > n or l_output_header_sent
|
||||||
|
loop
|
||||||
|
j := l_output.index_of ('%N', i)
|
||||||
|
if j > 0 then
|
||||||
|
s := l_output.substring (i, j)
|
||||||
|
s.right_adjust
|
||||||
|
if s.is_empty then
|
||||||
|
-- Reached end of header
|
||||||
|
l_output_header_sent := True
|
||||||
|
else
|
||||||
|
res.add_header_line (s)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- ERROR
|
||||||
|
l_output_header_sent := True
|
||||||
|
end
|
||||||
|
i := j + 1
|
||||||
|
end
|
||||||
|
if l_output_header_sent then
|
||||||
|
if i <= n then
|
||||||
|
res.put_string (l_output.substring (i, n))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
|
||||||
|
s := "Internal server error!"
|
||||||
|
res.header.put_content_type_utf_8_text_plain
|
||||||
|
res.header.put_content_length (s.count)
|
||||||
|
res.put_string (s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error)
|
||||||
|
s := "Could not launch CGI script!"
|
||||||
|
res.header.put_content_type_utf_8_text_plain
|
||||||
|
res.header.put_content_length (s.count)
|
||||||
|
res.put_string (s)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
res.set_status_code ({HTTP_STATUS_CODE}.not_found)
|
||||||
|
s := "Not found!"
|
||||||
|
res.header.put_content_type_utf_8_text_plain
|
||||||
|
res.header.put_content_length (s.count)
|
||||||
|
res.put_string (s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
get_output_and_error_from_process (proc: BASE_PROCESS; a_output: STRING; a_error: STRING)
|
||||||
|
local
|
||||||
|
output_buf, error_buf: SPECIAL [NATURAL_8]
|
||||||
|
do
|
||||||
|
from
|
||||||
|
create output_buf.make_filled (0, buffer_size)
|
||||||
|
create error_buf.make_filled (0, buffer_size)
|
||||||
|
until
|
||||||
|
not attached output_buf and not attached error_buf
|
||||||
|
loop
|
||||||
|
if attached output_buf then
|
||||||
|
if proc.has_output_stream_error or proc.has_output_stream_closed then
|
||||||
|
output_buf := Void
|
||||||
|
end
|
||||||
|
if attached output_buf then
|
||||||
|
-- Try reading from standard output.
|
||||||
|
proc.read_output_to_special (output_buf)
|
||||||
|
across
|
||||||
|
output_buf as ic
|
||||||
|
loop
|
||||||
|
a_output.append_code (ic.item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if attached output_buf implies output_buf.count = 0 and then attached error_buf then
|
||||||
|
-- Nothing is read from standard output, switch to standard error.
|
||||||
|
if proc.has_error_stream_error or proc.has_error_stream_closed then
|
||||||
|
error_buf := Void
|
||||||
|
end
|
||||||
|
if attached error_buf then
|
||||||
|
-- Try reading from standard error.
|
||||||
|
proc.read_error_to_special (error_buf)
|
||||||
|
across
|
||||||
|
error_buf as ic
|
||||||
|
loop
|
||||||
|
a_error.append_code (ic.item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if attached output_buf then
|
||||||
|
output_buf.extend_filled (0)
|
||||||
|
end
|
||||||
|
if attached error_buf then
|
||||||
|
error_buf.extend_filled (0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
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
|
||||||
@@ -221,17 +221,19 @@ feature {WSF_RESPONSE} -- Output
|
|||||||
|
|
||||||
l_url := Void
|
l_url := Void
|
||||||
s.append ("<li>")
|
s.append ("<li>")
|
||||||
s.append ("<code>")
|
if attached m.associated_resource as l_associated_resource then
|
||||||
if doc_url_supported then
|
s.append ("<code>")
|
||||||
s.append ("<a class=%"mappingresource%" href=%"")
|
if doc_url_supported then
|
||||||
s.append (doc_url (m.associated_resource))
|
s.append ("<a class=%"mappingresource%" href=%"")
|
||||||
s.append ("%">")
|
s.append (doc_url (l_associated_resource))
|
||||||
s.append (m.associated_resource)
|
s.append ("%">")
|
||||||
s.append ("</a>")
|
s.append (l_associated_resource)
|
||||||
else
|
s.append ("</a>")
|
||||||
s.append (m.associated_resource)
|
else
|
||||||
|
s.append (l_associated_resource)
|
||||||
|
end
|
||||||
|
s.append ("</code>")
|
||||||
end
|
end
|
||||||
s.append ("</code>")
|
|
||||||
|
|
||||||
if meths /= Void then
|
if meths /= Void then
|
||||||
s.append (" [")
|
s.append (" [")
|
||||||
@@ -291,7 +293,11 @@ feature {WSF_RESPONSE} -- Output
|
|||||||
s.append ("</li>%N")
|
s.append ("</li>%N")
|
||||||
else
|
else
|
||||||
debug
|
debug
|
||||||
s.append ("<li>" + m.associated_resource + " is HIDDEN</li>%N")
|
if attached m.associated_resource as l_associated_resource then
|
||||||
|
s.append ("<li>" + l_associated_resource + " is HIDDEN</li>%N")
|
||||||
|
else
|
||||||
|
s.append ("<li>HIDDEN</li>%N")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_ROUTING_AGENT_CONDITION}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_ROUTING_AGENT_CONDITION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTING_CONDITION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (act: like condition)
|
||||||
|
do
|
||||||
|
condition := act
|
||||||
|
end
|
||||||
|
|
||||||
|
condition: FUNCTION [TUPLE [request: WSF_REQUEST], BOOLEAN]
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
accepted (req: WSF_REQUEST): BOOLEAN
|
||||||
|
-- Does `req` satisfy Current condition?
|
||||||
|
do
|
||||||
|
Result := condition (req)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Request path info ends with one of the specified extensions.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_ROUTING_EXTENSION_CONDITION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTING_CONDITION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Creation
|
||||||
|
|
||||||
|
make (a_extension_list: ITERABLE [READABLE_STRING_GENERAL])
|
||||||
|
do
|
||||||
|
extension_list := a_extension_list
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
extension_list: ITERABLE [READABLE_STRING_GENERAL]
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
accepted (req: WSF_REQUEST): BOOLEAN
|
||||||
|
-- Does `req` satisfy Current condition?
|
||||||
|
local
|
||||||
|
l_path: READABLE_STRING_GENERAL
|
||||||
|
i: INTEGER
|
||||||
|
do
|
||||||
|
l_path := req.percent_encoded_path_info
|
||||||
|
i := l_path.last_index_of ('.', l_path.count)
|
||||||
|
if i > 0 then
|
||||||
|
i := i + 1
|
||||||
|
Result := across extension_list as ic some ic.item.same_caseless_characters (l_path, i, l_path.count, 1) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Request path info is associated with existing file.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_ROUTING_FILE_EXISTS_CONDITION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTING_PATH_EXISTS_CONDITION
|
||||||
|
redefine
|
||||||
|
path_exists
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
path_exists (p: PATH): BOOLEAN
|
||||||
|
local
|
||||||
|
fut: FILE_UTILITIES
|
||||||
|
do
|
||||||
|
Result := fut.file_path_exists (p)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Request path info is associated with existing file or folder.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_ROUTING_PATH_EXISTS_CONDITION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTING_CONDITION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Creation
|
||||||
|
|
||||||
|
make (a_parent_location: PATH)
|
||||||
|
do
|
||||||
|
parent_location := a_parent_location
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
parent_location: PATH
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
path_exists (p: PATH): BOOLEAN
|
||||||
|
local
|
||||||
|
fut: FILE_UTILITIES
|
||||||
|
do
|
||||||
|
Result := fut.file_path_exists (p) or fut.directory_path_exists (p)
|
||||||
|
end
|
||||||
|
|
||||||
|
accepted (req: WSF_REQUEST): BOOLEAN
|
||||||
|
-- Does `req` satisfy Current condition?
|
||||||
|
local
|
||||||
|
l_path: READABLE_STRING_GENERAL
|
||||||
|
p: PATH
|
||||||
|
do
|
||||||
|
l_path := req.path_info
|
||||||
|
if not l_path.is_empty then
|
||||||
|
if l_path[1] = '/' then
|
||||||
|
l_path := l_path.substring (2, l_path.count)
|
||||||
|
end
|
||||||
|
p := parent_location.extended (l_path)
|
||||||
|
Result := path_exists (p)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_ROUTING_AND_CONDITION}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_ROUTING_AND_CONDITION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTING_CONDITION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Creation
|
||||||
|
|
||||||
|
make (a_left, a_right: WSF_ROUTING_CONDITION)
|
||||||
|
do
|
||||||
|
left := a_left
|
||||||
|
right := a_right
|
||||||
|
end
|
||||||
|
|
||||||
|
left, right: WSF_ROUTING_CONDITION
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
accepted (req: WSF_REQUEST): BOOLEAN
|
||||||
|
-- Does `req` satisfy Current condition?
|
||||||
|
do
|
||||||
|
Result := left.accepted (req) and then right.accepted (req)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_ROUTING_CONDITION}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WSF_ROUTING_CONDITION
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
accepted (req: WSF_REQUEST): BOOLEAN
|
||||||
|
-- Does `req` satisfy Current condition?
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
conjuncted alias "and" (cond: WSF_ROUTING_CONDITION): WSF_ROUTING_AND_CONDITION
|
||||||
|
do
|
||||||
|
create Result.make (Current, cond)
|
||||||
|
end
|
||||||
|
|
||||||
|
disjuncted alias "or" (cond: WSF_ROUTING_CONDITION): WSF_ROUTING_OR_CONDITION
|
||||||
|
do
|
||||||
|
create Result.make (Current, cond)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_ROUTING_OR_CONDITION}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_ROUTING_OR_CONDITION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTING_CONDITION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Creation
|
||||||
|
|
||||||
|
make (a_left, a_right: WSF_ROUTING_CONDITION)
|
||||||
|
do
|
||||||
|
left := a_left
|
||||||
|
right := a_right
|
||||||
|
end
|
||||||
|
|
||||||
|
left, right: WSF_ROUTING_CONDITION
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
accepted (req: WSF_REQUEST): BOOLEAN
|
||||||
|
-- Does `req` satisfy Current condition?
|
||||||
|
do
|
||||||
|
Result := left.accepted (req) or else right.accepted (req)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_WITH_CONDITION_HANDLER}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WSF_WITH_CONDITION_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_EXECUTE_HANDLER
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for WSF_WITH_CONDITION_MAPPING."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_WITH_CONDITION_MAPPING
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_WITH_CONDITION_MAPPING_I
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
handler: WSF_EXECUTE_HANDLER
|
||||||
|
|
||||||
|
feature -- change
|
||||||
|
|
||||||
|
set_handler (h: like handler)
|
||||||
|
do
|
||||||
|
handler := h
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Execution
|
||||||
|
|
||||||
|
execute_handler (h: like handler; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
-- Execute handler `h' with `req' and `res' for Current mapping
|
||||||
|
do
|
||||||
|
h.execute (req, res)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for WSF_WITH_CONDITION_MAPPING_I."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WSF_WITH_CONDITION_MAPPING_I
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTER_MAPPING
|
||||||
|
|
||||||
|
WSF_SELF_DOCUMENTED_ROUTER_MAPPING
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_condition: like condition; h: like handler)
|
||||||
|
do
|
||||||
|
set_handler (h)
|
||||||
|
condition := a_condition
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
condition: WSF_ROUTING_CONDITION
|
||||||
|
|
||||||
|
associated_resource: READABLE_STRING_8
|
||||||
|
-- Name (URI, or URI template or regular expression or ...) of handled resource
|
||||||
|
do
|
||||||
|
if attached condition_description as desc and then desc.is_valid_as_string_8 then
|
||||||
|
Result := desc.to_string_8
|
||||||
|
else
|
||||||
|
Result := description
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
condition_description: detachable READABLE_STRING_32
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_condition_description (desc: detachable READABLE_STRING_GENERAL)
|
||||||
|
do
|
||||||
|
if desc = Void then
|
||||||
|
condition_description := Void
|
||||||
|
else
|
||||||
|
condition_description := desc.as_string_32
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_handler (h: like handler)
|
||||||
|
-- Set `handler' to `h'.
|
||||||
|
require
|
||||||
|
h_attached: h /= Void
|
||||||
|
deferred
|
||||||
|
ensure
|
||||||
|
h_aliased: handler = h
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Documentation
|
||||||
|
|
||||||
|
description: STRING_32 = "With-Condition"
|
||||||
|
|
||||||
|
feature -- Status
|
||||||
|
|
||||||
|
is_mapping (a_path: READABLE_STRING_8; req: WSF_REQUEST; a_router: WSF_ROUTER): BOOLEAN
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := condition.accepted (req)
|
||||||
|
end
|
||||||
|
|
||||||
|
try (a_path: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE; sess: WSF_ROUTER_SESSION; a_router: WSF_ROUTER)
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
if condition.accepted (req) then
|
||||||
|
sess.set_dispatched_handler (handler)
|
||||||
|
a_router.execute_before (Current)
|
||||||
|
execute_handler (handler, req, res)
|
||||||
|
a_router.execute_after (Current)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Execution
|
||||||
|
|
||||||
|
execute_handler (h: like handler; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
-- Execute handler `h' with `req' and `res' for Current mapping.
|
||||||
|
require
|
||||||
|
h_attached: h /= Void
|
||||||
|
req_attached: req /= Void
|
||||||
|
res_attached: res /= Void
|
||||||
|
path_validate_condition: condition.accepted (req)
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
invariant
|
||||||
|
|
||||||
|
condition_attached: condition /= Void
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_EXECUTE_AGENT_HANDLER}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_EXECUTE_AGENT_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_HANDLER
|
||||||
|
|
||||||
|
WSF_EXECUTE_HANDLER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_action: like action)
|
||||||
|
do
|
||||||
|
action := a_action
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
action: PROCEDURE [TUPLE [request: WSF_REQUEST; response: WSF_RESPONSE]]
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
action (req, res)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Handler that can also play the role of a filter, i.e.
|
||||||
|
than can pre-process incoming data and post-process outgoing data.
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WSF_EXECUTE_FILTER_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_FILTER_HANDLER [WSF_EXECUTE_HANDLER]
|
||||||
|
|
||||||
|
WSF_EXECUTE_HANDLER
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
execute_next (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
if attached next as n then
|
||||||
|
n.execute (req, res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_EXECUTE_RESPONSE_AGENT_HANDLER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_EXECUTE_RESPONSE_AGENT_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_EXECUTE_RESPONSE_HANDLER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Initialization
|
||||||
|
|
||||||
|
make (act: like action)
|
||||||
|
do
|
||||||
|
action := act
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
action: FUNCTION [TUPLE [req: WSF_REQUEST], WSF_RESPONSE_MESSAGE]
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
response (req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
|
||||||
|
do
|
||||||
|
Result := action.item ([req])
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_EXECUTE_RESPONSE_HANDLER}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WSF_EXECUTE_RESPONSE_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_EXECUTE_HANDLER
|
||||||
|
|
||||||
|
feature -- Response
|
||||||
|
|
||||||
|
response (req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
|
||||||
|
require
|
||||||
|
is_valid_context: is_valid_context (req)
|
||||||
|
deferred
|
||||||
|
ensure
|
||||||
|
Result_attached: Result /= Void
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
-- Execute request handler
|
||||||
|
do
|
||||||
|
res.send (response (req))
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_EXECUTE_ROUTING_HANDLER}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_EXECUTE_ROUTING_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTING_HANDLER
|
||||||
|
|
||||||
|
WSF_EXECUTE_HANDLER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_SELF_DOCUMENTED_EXECUTE_AGENT_HANDLER}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_SELF_DOCUMENTED_EXECUTE_AGENT_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_EXECUTE_AGENT_HANDLER
|
||||||
|
rename
|
||||||
|
make as make_handler
|
||||||
|
end
|
||||||
|
|
||||||
|
WSF_SELF_DOCUMENTED_AGENT_HANDLER
|
||||||
|
|
||||||
|
create
|
||||||
|
make,
|
||||||
|
make_with_descriptions,
|
||||||
|
make_hidden
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_action: like action; a_self_doc: like self_documentation_builder)
|
||||||
|
-- <Precursor>
|
||||||
|
-- and using `a_self_doc' function to build the `mapping_documentation'.
|
||||||
|
do
|
||||||
|
set_self_documentation_builder (a_self_doc)
|
||||||
|
make_handler (a_action)
|
||||||
|
end
|
||||||
|
|
||||||
|
make_with_descriptions (a_action: like action; a_descriptions: ITERABLE [READABLE_STRING_GENERAL])
|
||||||
|
do
|
||||||
|
across
|
||||||
|
a_descriptions as c
|
||||||
|
loop
|
||||||
|
add_description (c.item)
|
||||||
|
end
|
||||||
|
make_handler (a_action)
|
||||||
|
end
|
||||||
|
|
||||||
|
make_hidden (a_action: like action)
|
||||||
|
-- <Precursor>
|
||||||
|
-- and using `a_self_doc' function to build the `mapping_documentation'
|
||||||
|
-- mark it as `hidden'.
|
||||||
|
do
|
||||||
|
is_hidden := True
|
||||||
|
make (a_action, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Represents the ancestor of all the WSF_ROUTER handlers with `execute (WSF_REQUEST, WSF_RESPONSE)` routine.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
WSF_EXECUTE_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_HANDLER
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
-- Execute `req' responding in `res'.
|
||||||
|
require
|
||||||
|
req_attached: req /= Void
|
||||||
|
res_attached: res /= Void
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
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
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
note
|
note
|
||||||
description: "Summary description for {WSF_SELF_DOCUMENTED_URI_AGENT_HANDLER}."
|
description: "Summary description for {WSF_SELF_DOCUMENTED_URI_AGENT_HANDLER}."
|
||||||
author: ""
|
|
||||||
date: "$Date$"
|
date: "$Date$"
|
||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
|
|
||||||
@@ -50,7 +49,7 @@ feature {NONE} -- Initialization
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -8,31 +8,14 @@ class
|
|||||||
WSF_URI_AGENT_HANDLER
|
WSF_URI_AGENT_HANDLER
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
|
WSF_EXECUTE_AGENT_HANDLER
|
||||||
WSF_URI_HANDLER
|
WSF_URI_HANDLER
|
||||||
|
|
||||||
create
|
create
|
||||||
make
|
make
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
|
||||||
|
|
||||||
make (a_action: like action)
|
|
||||||
do
|
|
||||||
action := a_action
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
action: PROCEDURE [TUPLE [request: WSF_REQUEST; response: WSF_RESPONSE]]
|
|
||||||
|
|
||||||
feature -- Execution
|
|
||||||
|
|
||||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
|
||||||
do
|
|
||||||
action.call ([req, res])
|
|
||||||
end
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -8,31 +8,17 @@ class
|
|||||||
WSF_URI_RESPONSE_AGENT_HANDLER
|
WSF_URI_RESPONSE_AGENT_HANDLER
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
WSF_URI_RESPONSE_HANDLER
|
WSF_EXECUTE_RESPONSE_AGENT_HANDLER
|
||||||
|
|
||||||
|
WSF_URI_RESPONSE_HANDLER
|
||||||
|
undefine
|
||||||
|
execute
|
||||||
|
end
|
||||||
create
|
create
|
||||||
make
|
make
|
||||||
|
|
||||||
feature -- Initialization
|
|
||||||
|
|
||||||
make (act: like action)
|
|
||||||
do
|
|
||||||
action := act
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
action: FUNCTION [TUPLE [req: WSF_REQUEST], WSF_RESPONSE_MESSAGE]
|
|
||||||
|
|
||||||
feature -- Execution
|
|
||||||
|
|
||||||
response (req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
|
|
||||||
do
|
|
||||||
Result := action.item ([req])
|
|
||||||
end
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ deferred class
|
|||||||
WSF_URI_RESPONSE_HANDLER
|
WSF_URI_RESPONSE_HANDLER
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
|
WSF_HANDLER
|
||||||
|
|
||||||
WSF_URI_HANDLER
|
WSF_URI_HANDLER
|
||||||
|
|
||||||
feature -- Response
|
feature -- Response
|
||||||
@@ -29,7 +31,7 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ inherit
|
|||||||
feature -- Execution
|
feature -- Execution
|
||||||
|
|
||||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
-- Execute handler for `req' and respond in `res'.
|
-- Execute `req' responding in `res'.
|
||||||
require
|
require
|
||||||
req_attached: req /= Void
|
req_attached: req /= Void
|
||||||
res_attached: res /= Void
|
res_attached: res /= Void
|
||||||
@@ -30,7 +30,7 @@ feature {WSF_ROUTER} -- Mapping
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ class
|
|||||||
WSF_URI_TEMPLATE_AGENT_HANDLER
|
WSF_URI_TEMPLATE_AGENT_HANDLER
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
|
WSF_EXECUTE_AGENT_HANDLER
|
||||||
|
|
||||||
WSF_URI_TEMPLATE_HANDLER
|
WSF_URI_TEMPLATE_HANDLER
|
||||||
|
|
||||||
create
|
create
|
||||||
@@ -16,26 +18,8 @@ create
|
|||||||
convert
|
convert
|
||||||
make ({PROCEDURE [WSF_REQUEST, WSF_RESPONSE]})
|
make ({PROCEDURE [WSF_REQUEST, WSF_RESPONSE]})
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
|
||||||
|
|
||||||
make (a_action: like action)
|
|
||||||
do
|
|
||||||
action := a_action
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
action: PROCEDURE [TUPLE [request: WSF_REQUEST; response: WSF_RESPONSE]]
|
|
||||||
|
|
||||||
feature -- Execution
|
|
||||||
|
|
||||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
|
||||||
do
|
|
||||||
action.call ([req, res])
|
|
||||||
end
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -8,31 +8,18 @@ class
|
|||||||
WSF_URI_TEMPLATE_RESPONSE_AGENT_HANDLER
|
WSF_URI_TEMPLATE_RESPONSE_AGENT_HANDLER
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
|
WSF_EXECUTE_RESPONSE_AGENT_HANDLER
|
||||||
|
|
||||||
WSF_URI_TEMPLATE_RESPONSE_HANDLER
|
WSF_URI_TEMPLATE_RESPONSE_HANDLER
|
||||||
|
undefine
|
||||||
|
execute
|
||||||
|
end
|
||||||
|
|
||||||
create
|
create
|
||||||
make
|
make
|
||||||
|
|
||||||
feature -- Initialization
|
|
||||||
|
|
||||||
make (act: like action)
|
|
||||||
do
|
|
||||||
action := act
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
action: FUNCTION [TUPLE [req: WSF_REQUEST], WSF_RESPONSE_MESSAGE]
|
|
||||||
|
|
||||||
feature -- Execution
|
|
||||||
|
|
||||||
response (req: WSF_REQUEST): WSF_RESPONSE_MESSAGE
|
|
||||||
do
|
|
||||||
Result := action.item ([req])
|
|
||||||
end
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ deferred class
|
|||||||
WSF_URI_TEMPLATE_RESPONSE_HANDLER
|
WSF_URI_TEMPLATE_RESPONSE_HANDLER
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
|
WSF_RESPONSE_HANDLER
|
||||||
|
|
||||||
WSF_URI_TEMPLATE_HANDLER
|
WSF_URI_TEMPLATE_HANDLER
|
||||||
|
|
||||||
feature -- Response
|
feature -- Response
|
||||||
@@ -29,7 +31,7 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ feature {WSF_ROUTER} -- Mapping
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ feature {NONE} -- Initialization
|
|||||||
|
|
||||||
make_with_path (d: like document_root)
|
make_with_path (d: like document_root)
|
||||||
do
|
do
|
||||||
|
initialize
|
||||||
max_age := -1
|
max_age := -1
|
||||||
if d.is_empty then
|
if d.is_empty then
|
||||||
document_root := execution_environment.current_working_path
|
document_root := execution_environment.current_working_path
|
||||||
@@ -73,6 +74,11 @@ feature {NONE} -- Initialization
|
|||||||
is_hidden: BOOLEAN
|
is_hidden: BOOLEAN
|
||||||
-- Current mapped handler should be hidden from self documentation
|
-- Current mapped handler should be hidden from self documentation
|
||||||
|
|
||||||
|
initialize
|
||||||
|
-- Initialize Current handler.
|
||||||
|
do
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Documentation
|
feature -- Documentation
|
||||||
|
|
||||||
mapping_documentation (m: WSF_ROUTER_MAPPING; a_request_methods: detachable WSF_REQUEST_METHODS): WSF_ROUTER_MAPPING_DOCUMENTATION
|
mapping_documentation (m: WSF_ROUTER_MAPPING; a_request_methods: detachable WSF_REQUEST_METHODS): WSF_ROUTER_MAPPING_DOCUMENTATION
|
||||||
@@ -97,7 +103,7 @@ feature -- Access
|
|||||||
-- Function to evaluate if a path is ignored or not during autoindex.
|
-- Function to evaluate if a path is ignored or not during autoindex.
|
||||||
-- If `index_ignores' is Void and `index_ignores_function' is Void, use default ignore rules.
|
-- If `index_ignores' is Void and `index_ignores_function' is Void, use default ignore rules.
|
||||||
|
|
||||||
directory_index: detachable ARRAY [READABLE_STRING_8]
|
directory_index: detachable ITERABLE [READABLE_STRING_GENERAL]
|
||||||
-- File serve if a directory index is requested.
|
-- File serve if a directory index is requested.
|
||||||
|
|
||||||
not_found_handler: detachable PROCEDURE [TUPLE [uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE]]
|
not_found_handler: detachable PROCEDURE [TUPLE [uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE]]
|
||||||
@@ -124,7 +130,7 @@ feature -- Element change
|
|||||||
set_directory_index (idx: like directory_index)
|
set_directory_index (idx: like directory_index)
|
||||||
-- Set `directory_index' as `idx'
|
-- Set `directory_index' as `idx'
|
||||||
do
|
do
|
||||||
if idx = Void or else idx.is_empty then
|
if idx = Void then
|
||||||
directory_index := Void
|
directory_index := Void
|
||||||
else
|
else
|
||||||
directory_index := idx
|
directory_index := idx
|
||||||
@@ -211,7 +217,7 @@ feature -- Execution
|
|||||||
fn := resource_filename (uri)
|
fn := resource_filename (uri)
|
||||||
create f.make_with_path (fn)
|
create f.make_with_path (fn)
|
||||||
if f.exists then
|
if f.exists then
|
||||||
if f.is_readable then
|
if f.is_access_readable then
|
||||||
if f.is_directory then
|
if f.is_directory then
|
||||||
if index_disabled then
|
if index_disabled then
|
||||||
process_directory_index_disabled (uri, req, res)
|
process_directory_index_disabled (uri, req, res)
|
||||||
@@ -341,6 +347,8 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
process_file (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
process_file (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
require
|
||||||
|
f_valid: f.exists and then f.is_access_readable
|
||||||
do
|
do
|
||||||
if
|
if
|
||||||
attached req.meta_string_variable ("HTTP_IF_MODIFIED_SINCE") as s_if_modified_since and then
|
attached req.meta_string_variable ("HTTP_IF_MODIFIED_SINCE") as s_if_modified_since and then
|
||||||
@@ -355,6 +363,8 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
process_transfert (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
process_transfert (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
require
|
||||||
|
f_valid: f.exists and then f.is_access_readable
|
||||||
local
|
local
|
||||||
ext: READABLE_STRING_32
|
ext: READABLE_STRING_32
|
||||||
ct: detachable READABLE_STRING_8
|
ct: detachable READABLE_STRING_8
|
||||||
@@ -366,7 +376,7 @@ feature -- Execution
|
|||||||
if ct = Void then
|
if ct = Void then
|
||||||
ct := {HTTP_MIME_TYPES}.application_force_download
|
ct := {HTTP_MIME_TYPES}.application_force_download
|
||||||
end
|
end
|
||||||
create fres.make_with_content_type (ct, f.path.name)
|
create fres.make_with_content_type_and_path (ct, f.path)
|
||||||
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
|
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||||
|
|
||||||
-- cache control
|
-- cache control
|
||||||
|
|||||||
24
library/server/wsf/router/wsf_response_handler.e
Normal file
24
library/server/wsf/router/wsf_response_handler.e
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Represents the ancestor of all the WSF_ROUTER handler based on message response.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_RESPONSE_HANDLER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_HANDLER
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
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
|
||||||
@@ -11,16 +11,6 @@ deferred class
|
|||||||
inherit
|
inherit
|
||||||
DEBUG_OUTPUT
|
DEBUG_OUTPUT
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
|
||||||
|
|
||||||
make (a_resource: READABLE_STRING_8; h: like handler)
|
|
||||||
-- Create mapping based on resource `a_resource' and handler `h'.
|
|
||||||
require
|
|
||||||
a_resource_attached: a_resource /= Void
|
|
||||||
h_attached: h /= Void
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
|
|
||||||
associated_resource: READABLE_STRING_8
|
associated_resource: READABLE_STRING_8
|
||||||
|
|||||||
@@ -48,7 +48,10 @@ feature -- Element change
|
|||||||
feature -- Execution
|
feature -- Execution
|
||||||
|
|
||||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
-- Execute request handler
|
-- Execute `req' responding in `res'.
|
||||||
|
require
|
||||||
|
req_attached: req /= Void
|
||||||
|
res_attached: res /= Void
|
||||||
local
|
local
|
||||||
sess: WSF_ROUTER_SESSION
|
sess: WSF_ROUTER_SESSION
|
||||||
do
|
do
|
||||||
@@ -60,7 +63,7 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -104,6 +104,15 @@ feature {NONE} -- Initialization
|
|||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
|
set_content_type (a_content_type: detachable like content_type)
|
||||||
|
do
|
||||||
|
if a_content_type = Void then
|
||||||
|
get_content_type
|
||||||
|
else
|
||||||
|
content_type := a_content_type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
set_max_age (sec: INTEGER)
|
set_max_age (sec: INTEGER)
|
||||||
do
|
do
|
||||||
header.put_cache_control ("max-age=" + sec.out)
|
header.put_cache_control ("max-age=" + sec.out)
|
||||||
@@ -227,6 +236,7 @@ feature {WSF_RESPONSE} -- Output
|
|||||||
do
|
do
|
||||||
res.set_status_code (status_code)
|
res.set_status_code (status_code)
|
||||||
if status_code = {HTTP_STATUS_CODE}.not_found then
|
if status_code = {HTTP_STATUS_CODE}.not_found then
|
||||||
|
-- File not found, then no more data.
|
||||||
else
|
else
|
||||||
res.put_header_text (header.string)
|
res.put_header_text (header.string)
|
||||||
s := head
|
s := head
|
||||||
|
|||||||
3
library/server/wsf/wsf_compression-safe.ecf
Normal file
3
library/server/wsf/wsf_compression-safe.ecf
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<redirection 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" uuid="C558E537-1259-4C94-8C49-117D7E821820" message="Obsolete: use wsf_compression.ecf !" location="wsf_compression.ecf">
|
||||||
|
</redirection>
|
||||||
20
library/server/wsf/wsf_compression.ecf
Normal file
20
library/server/wsf/wsf_compression.ecf
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?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="wsf_compression" uuid="C558E537-1259-4C94-8C49-117D7E821820" library_target="wsf_compression">
|
||||||
|
<target name="wsf_compression">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true">
|
||||||
|
</option>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||||
|
<library name="conneg" location="..\..\network\protocol\content_negotiation\conneg.ecf"/>
|
||||||
|
<library name="http" location="..\..\network\protocol\http\http.ecf"/>
|
||||||
|
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||||
|
<library name="wsf" location="wsf.ecf" readonly="false"/>
|
||||||
|
<library name="zlib" location="$ISE_LIBRARY\unstable\library\compression\zlib\zlib.ecf" readonly="false"/>
|
||||||
|
<cluster name="compression" location=".\compression\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
<target name="wsf_extension">
|
<target name="wsf_extension">
|
||||||
<root all_classes="true"/>
|
<root all_classes="true"/>
|
||||||
<file_rule>
|
<file_rule>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
<exclude>/\.git$</exclude>
|
<exclude>/\.git$</exclude>
|
||||||
<exclude>/\.svn$</exclude>
|
<exclude>/\.svn$</exclude>
|
||||||
<exclude>/EIFGENs$</exclude>
|
|
||||||
</file_rule>
|
</file_rule>
|
||||||
<option warning="true">
|
<option warning="true">
|
||||||
</option>
|
</option>
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||||
<library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
|
<library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
|
||||||
<library name="http" location="..\..\network\protocol\http\http.ecf"/>
|
<library name="http" location="..\..\network\protocol\http\http.ecf"/>
|
||||||
|
<library name="process" location="$ISE_LIBRARY\library\process\base\base_process.ecf"/>
|
||||||
<library name="wsf" location="wsf.ecf"/>
|
<library name="wsf" location="wsf.ecf"/>
|
||||||
<library name="wsf_router_context" location="wsf_router_context.ecf" readonly="true"/>
|
<library name="wsf_router_context" location="wsf_router_context.ecf" readonly="true"/>
|
||||||
<cluster name="extension" location=".\extension\" recursive="true"/>
|
<cluster name="extension" location=".\extension\" recursive="true"/>
|
||||||
|
|||||||
@@ -69,8 +69,6 @@ feature -- Process
|
|||||||
|
|
||||||
process_group (g: ERROR_GROUP)
|
process_group (g: ERROR_GROUP)
|
||||||
-- <Precursor>
|
-- <Precursor>
|
||||||
local
|
|
||||||
l_errors: LIST [ERROR]
|
|
||||||
do
|
do
|
||||||
across
|
across
|
||||||
g.sub_errors as s
|
g.sub_errors as s
|
||||||
|
|||||||
12
tools/httpd/README.md
Normal file
12
tools/httpd/README.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
Simple web server
|
||||||
|
=================
|
||||||
|
|
||||||
|
This server is very simple and limited, it is, for now:
|
||||||
|
- a file server
|
||||||
|
- a CGI handler (for `*.cgi` executables)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
httpd (--root path)
|
||||||
|
|
||||||
|
--root <path>: document directory for file server (default: www)
|
||||||
|
|
||||||
56
tools/httpd/httpd.ecf
Normal file
56
tools/httpd/httpd.ecf
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-17-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-17-0 http://www.eiffel.com/developers/xml/configuration-1-17-0.xsd" name="httpd" uuid="60DBA808-B014-47BB-BDEF-11B87C1DC6DE">
|
||||||
|
<target name="common" abstract="true">
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/\.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="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.ecf"/>
|
||||||
|
<library name="http" location="..\..\library\network\protocol\http\http.ecf"/>
|
||||||
|
<library name="process" location="$ISE_LIBRARY\library\process\base\base_process.ecf"/>
|
||||||
|
<library name="wsf" location="..\..\library\server\wsf\wsf.ecf" readonly="false"/>
|
||||||
|
<library name="wsf_extension" location="..\..\library\server\wsf\wsf_extension.ecf" readonly="false"/>
|
||||||
|
</target>
|
||||||
|
<target name="httpd_standalone" extends="common">
|
||||||
|
<root class="APPLICATION" feature="make_and_launch"/>
|
||||||
|
<option warning="true">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone.ecf"/>
|
||||||
|
<cluster name="httpd" location=".\src" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
<target name="httpd_standalone_mt" extends="httpd_standalone">
|
||||||
|
<capability>
|
||||||
|
<concurrency support="thread" use="thread"/>
|
||||||
|
</capability>
|
||||||
|
</target>
|
||||||
|
<target name="httpd_standalone_st" extends="httpd_standalone">
|
||||||
|
<capability>
|
||||||
|
<concurrency support="none" use="none"/>
|
||||||
|
</capability>
|
||||||
|
</target>
|
||||||
|
<target name="httpd_cgi" extends="common">
|
||||||
|
<root class="APPLICATION" feature="make_and_launch"/>
|
||||||
|
<option warning="true">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<capability>
|
||||||
|
<concurrency use="scoop"/>
|
||||||
|
</capability>
|
||||||
|
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi.ecf"/>
|
||||||
|
<cluster name="httpd" location=".\src" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
<target name="httpd_libfcgi" extends="common">
|
||||||
|
<root class="APPLICATION" feature="make_and_launch"/>
|
||||||
|
<option warning="true">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<library name="default_libfcgi" location="..\..\library\server\wsf\default\libfcgi.ecf"/>
|
||||||
|
<cluster name="httpd" location=".\src" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
9
tools/httpd/server.ini
Normal file
9
tools/httpd/server.ini
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
verbose=true
|
||||||
|
verbose_level=ALERT
|
||||||
|
port=9090
|
||||||
|
#max_concurrent_connections=100
|
||||||
|
#keep_alive_timeout=5
|
||||||
|
#max_keep_alive_requests=300
|
||||||
|
#max_tcp_clients=100
|
||||||
|
#socket_timeout=60
|
||||||
|
#socket_recv_timeout=15
|
||||||
31
tools/httpd/src/application.e
Normal file
31
tools/httpd/src/application.e
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
note
|
||||||
|
description : "simple application root class"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||||
|
redefine
|
||||||
|
initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
SHARED_EXECUTION_ENVIRONMENT
|
||||||
|
|
||||||
|
create
|
||||||
|
make_and_launch
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
-- Initialize current service.
|
||||||
|
do
|
||||||
|
-- Specific to `standalone' connector (the EiffelWeb server).
|
||||||
|
-- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
|
||||||
|
set_service_option ("port", 9090)
|
||||||
|
import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("server.ini"))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
95
tools/httpd/src/application_execution.e
Normal file
95
tools/httpd/src/application_execution.e
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
note
|
||||||
|
description : "simple application execution"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
APPLICATION_EXECUTION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTED_EXECUTION
|
||||||
|
|
||||||
|
SHARED_EXECUTION_ENVIRONMENT
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Setup
|
||||||
|
|
||||||
|
cgi_file_extensions: ITERABLE [READABLE_STRING_GENERAL]
|
||||||
|
once
|
||||||
|
Result := <<"cgi">>
|
||||||
|
end
|
||||||
|
|
||||||
|
directory_index_file_names: ITERABLE [READABLE_STRING_GENERAL]
|
||||||
|
once
|
||||||
|
Result := <<"index.html">>
|
||||||
|
end
|
||||||
|
|
||||||
|
document_location: PATH
|
||||||
|
-- Root folder for the httpd files server.
|
||||||
|
local
|
||||||
|
i,n: INTEGER
|
||||||
|
d: detachable READABLE_STRING_GENERAL
|
||||||
|
once
|
||||||
|
Result := execution_environment.current_working_path.extended ("www")
|
||||||
|
if attached execution_environment.arguments as args then
|
||||||
|
from
|
||||||
|
i := 1
|
||||||
|
n := args.argument_count
|
||||||
|
until
|
||||||
|
i > n or d /= Void
|
||||||
|
loop
|
||||||
|
if
|
||||||
|
attached args.argument (i) as v and then
|
||||||
|
v.same_string_general ("--root") and then
|
||||||
|
i < n
|
||||||
|
then
|
||||||
|
i := i + 1
|
||||||
|
d := args.argument (i)
|
||||||
|
end
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
if d /= Void then
|
||||||
|
create Result.make_from_string (d)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
setup_router
|
||||||
|
-- Setup `router'
|
||||||
|
local
|
||||||
|
cgi: WSF_CGI_HANDLER
|
||||||
|
cgi_cond: WSF_ROUTING_CONDITION
|
||||||
|
fs: WSF_FILE_SYSTEM_HANDLER
|
||||||
|
m: WSF_STARTS_WITH_MAPPING
|
||||||
|
cond: WSF_WITH_CONDITION_MAPPING
|
||||||
|
s: STRING_32
|
||||||
|
do
|
||||||
|
create cgi.make (document_location)
|
||||||
|
cgi_cond := create {WSF_ROUTING_FILE_EXISTS_CONDITION}.make (document_location) and create {WSF_ROUTING_EXTENSION_CONDITION}.make (cgi_file_extensions)
|
||||||
|
create cond.make (cgi_cond, cgi)
|
||||||
|
create s.make_empty
|
||||||
|
across
|
||||||
|
cgi_file_extensions as ic
|
||||||
|
loop
|
||||||
|
if not s.is_empty then
|
||||||
|
s.append_string_general (", ")
|
||||||
|
end
|
||||||
|
s.append_string_general ("*.")
|
||||||
|
s.append_string_general (ic.item)
|
||||||
|
end
|
||||||
|
cond.set_condition_description (s)
|
||||||
|
|
||||||
|
router.map (cond, Void)
|
||||||
|
|
||||||
|
create fs.make_with_path (document_location)
|
||||||
|
fs.enable_index
|
||||||
|
fs.set_directory_index (directory_index_file_names)
|
||||||
|
fs.set_not_found_handler (agent (i_uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE) do execute_default (req, res) end)
|
||||||
|
|
||||||
|
create m.make ("", fs)
|
||||||
|
router.map (m, router.methods_get)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
1
tools/httpd/www/home.html
Normal file
1
tools/httpd/www/home.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello EiffelWeb httpd server.
|
||||||
Reference in New Issue
Block a user