Compare commits

...

63 Commits

Author SHA1 Message Date
Jocelyn Fiat
6ca3cca88b Reverted change that made WSF_URI_HANDLER and WSF_URI_TEMPLATE_HANDLER inheriting from WSF_EXECUTE_HANDLER, as it breaks existing project using EiffelWeb. 2017-11-07 23:05:22 +01:00
Jocelyn Fiat
f91a676f41 fixed obsolete v0 wsf_extension ecf file. 2017-11-04 22:44:27 +01:00
Jocelyn Fiat
1c75e11e34 removed unused local variable. 2017-11-03 18:54:46 +01:00
Jocelyn Fiat
b5b4fa6b2f added develop branch for CI 2017-11-03 18:06:47 +01:00
Jocelyn Fiat
211fc425a3 Added handler to add support for CGI scripts.
Added a new tool `httpd` which is a basic httpd server product (with file server and CGI handler).
2017-11-03 18:00:39 +01:00
Jocelyn Fiat
95cebe26bb Added routing condition mapping.
Added WSF_EXECUTE_HANDLER as common ancestor for handler with `execute (WSF_REQUEST, WSF_RESPONSE) ..` routine.
Made more flexible a few routine by accepting ITERABLE instead of ARRAY, and READABLE_STRING_GENERAL when possible.
2017-11-03 17:59:10 +01:00
Jocelyn Fiat
f770c236d5 Improved support for absolute url passed tp HTTP_REQUEST_SESSION . 2017-10-27 19:24:52 +02:00
Jocelyn Fiat
503e5f7915 Merge branch 'master' into v1 2017-10-26 10:23:33 +02:00
Jocelyn Fiat
39f01e95fd Use single ecf file. 2017-10-26 10:19:56 +02:00
Jocelyn Fiat
c725159d7e Merge branch 'master' into with_compression 2017-10-26 10:13:27 +02:00
Jocelyn Fiat
e66f1cf7be updated changelog 2017-10-24 17:51:55 +02:00
Jocelyn Fiat
c03d28cabc Use new on_timer solution, to check every 1 second, the presence of ".stop" file.
If this file exists, close all active websockets.

Redesigned the commands implementation for this example.
2017-10-24 17:45:08 +02:00
Jocelyn Fiat
e834b2b360 Added on_timer callback event so that server can check regularly external state.
This is a basic solution to implement a way to check for time to time for events to notify websocket clients.
2017-10-24 17:43:06 +02:00
Jocelyn Fiat
d089fd3a03 Merge branch 'master' into v1 2017-10-19 11:20:55 +02:00
Jocelyn Fiat
a0c1ab5232 updated simple.ini settings. 2017-10-19 11:20:14 +02:00
Jocelyn Fiat
a8ddd10b46 Merge branch 'master' into v1 2017-10-19 10:57:45 +02:00
Jocelyn Fiat
db39068ceb Updated documentation for standalone connector.
Changed `default_max_keep_alive_requests` from 100 to 300.
2017-10-19 00:14:23 +02:00
Jocelyn Fiat
a1b4337438 Set keep_alive_timeout to 2, this way for single threaded case, browser does not wait too much to start the websocket connection.
Set max_keep_alive_requests to -1, to allow unlimited number of requests within a same websocket connection.
2017-10-18 23:41:03 +02:00
Jocelyn Fiat
74121be470 Support persistent connection, even in single thread mode (i.e concurrency=none).
Warning: as there is no concurrent request handling in single threaded mode,
            it is recommended to either set the keep_alive_timeout to a small value,
            or disable persistent connection by setting max_keep_alive_requests to 0.
Change the default keep_alive_timeout from 15 to 5 seconds.
Accept -1 as value of max_keep_alive_requests to have unlimited number of request in the same persistent connection.
2017-10-18 23:29:16 +02:00
Jocelyn Fiat
1c9f5ac0e7 Merge branch 'master' into v1 2017-10-17 14:52:17 +02:00
Jocelyn Fiat
edec837c4e Made interface of wsf forms and widgets a bit more flexible by accepting READABLE_STRING_GENERAL. 2017-10-17 14:34:50 +02:00
Jocelyn Fiat
f1642a444a Improved support of absolute/relative https:// and http:// in http_client. 2017-10-17 14:30:44 +02:00
Jocelyn Fiat
48af63af83 Fixed typo to process relative or absolute url. 2017-10-09 14:23:45 +02:00
Jocelyn Fiat
2f98d7031f Updated a few package.iron files. 2017-10-06 14:06:46 +02:00
Jocelyn Fiat
70f00651c7 update test case. 2017-10-06 13:58:54 +02:00
Jocelyn Fiat
199f84c7ef Updated Readme file with Build Status on Master Branch. 2017-10-06 10:37:11 +02:00
Jocelyn Fiat
9b97627c76 Update package.iron files. 2017-10-06 10:27:32 +02:00
Jocelyn Fiat
72c87cd74d Fixed curl implementation by setting Content-Type to x-www-form-urlencoded (if not set) when POST send data as x-www-form-urlencoded. 2017-10-06 10:24:48 +02:00
Jocelyn Fiat
2ed4d03168 Renamed ciphers_settings as ciphers_setting . 2017-10-06 09:34:08 +02:00
Jocelyn Fiat
18ed92a61d Moved to unique .ecf from ecf version 1-16-0 .
Requires 17.05 or newer.
2017-10-06 09:02:10 +02:00
Jocelyn Fiat
0a6a4281e7 Merge branch 'master' into es17.05 2017-10-06 08:18:50 +02:00
Jocelyn Fiat
38cf5d7a6f Updated requirements, now EiffelWeb requires 17.05 or newer.
Improved support for future 17.11 version with new openssl library.
2017-10-05 22:07:05 +02:00
jvelilla
96648a16dc Updated Readme file with Build Status on Master Branch.
Update code style: refactor rename ciphers_settings to ciphers_setting.
2017-09-25 14:21:35 -03:00
Javier Velilla
6f35ad7b16 Merge pull request #181 from jvelilla/ewf_http_client
Update HTTP Client cURL implementation:
2017-09-25 11:05:45 -03:00
Jocelyn Fiat
8ff20d34a7 Merge branch 'master' into v1 2017-09-21 21:22:38 +02:00
jvelilla
85c8a46c89 Update Readme.md with a note about ciphers implementation. 2017-09-21 08:07:11 -03:00
Jocelyn Fiat
498e4a6ec2 Fixed validation of iss and aud when issuer and audience are not set. 2017-09-21 10:46:08 +02:00
Jocelyn Fiat
ab507d543a Now HTTP_AUTHORIZATION acceps READABLE_STRING_GENERAL for username and password argument. 2017-09-21 10:45:40 +02:00
Jocelyn Fiat
20a90db2e3 If url is relative, use session.url (...) to get valid url.
It could happen with relative url in `Location: ...` header (for redirection).
2017-09-21 10:45:08 +02:00
Jocelyn Fiat
97fe16b4c2 Code cleaning. 2017-09-21 10:26:29 +02:00
jvelilla
6ed91699b8 Renamed feature 'set_ciphers' to 'set_ciphers_settings' and added description. 2017-09-19 10:32:17 -03:00
jvelilla
bb334aef80 Updated HTTP client cURL implementation.
Refactor rename cipher_list by ciphers_settings and description.
Updated ciphers_settings representation to STIRNG_8
Refactor rename set_cipher_list by set_ciphers.
2017-09-14 11:58:43 -03:00
jvelilla
c2764e25ff Update HTTP Client cURL implementation:
Added the option to set cipher list used to negotiate security settings
(SSL handshake)
2017-09-14 10:21:32 -03:00
Jocelyn Fiat
cdada71f7e Corrected wsf_compression.ecf which was missing a few libraries.
The -safe.ecf was correct.
2017-09-02 21:49:41 +02:00
Jocelyn Fiat
a7d0398ec6 Introduce WSF_COMPRESSION and applied to WSF_*_WITH_COMPRESSION classes.
Modified the example to send the file with or without compression.
2017-09-01 18:59:18 +02:00
jvelilla
267655d7bc Update code, comment style and removed hardcoded value. 2017-08-18 13:55:54 -03:00
jvelilla
e735da1bcb Merge branch 'ewf_compression' of https://github.com/jocelyn/EWF into ewf_compression_2017 2017-08-18 09:52:03 -03:00
Jocelyn Fiat
6425482070 Fixed ecf by removing the override declaration. 2017-08-08 15:55:52 +02:00
Jocelyn Fiat
818c3fb460 Made compilable with EiffelStudio 17.05 and probably before as well. 2017-08-08 15:54:21 +02:00
Jocelyn Fiat
dac50b490d Added output for the travis CI job. 2017-08-08 14:10:12 +02:00
Jocelyn Fiat
16d5076fe5 Added Travis CI support with 17.05. 2017-08-08 14:03:39 +02:00
Jocelyn Fiat
2748e1d9ee Now JWT_LOADER takes the alg as argument, to avoid security issue where the lib is taking alg from the header (which may be a bad security weakness). 2017-07-11 23:32:11 +02:00
Jocelyn Fiat
27ee20f99b Added convenient get and custom functions on HTTP_CLIENT directly. 2017-07-11 23:29:42 +02:00
Javier Velilla
9a3164df70 Merge pull request #178 from jvelilla/ewf_ssl
Updated EWF  http_network, websocket, httpd to use the latest EiffelN…
2017-06-23 09:53:26 -03:00
jvelilla
02383810b4 Fixed bad identation
Updated date to current date in obsolte message.
2017-06-23 09:51:59 -03:00
jvelilla
dbf5e76047 Updated EWF network and httpd libraries.
Updated features using ssl_2 and ssl_3 as obsolete and raise a
developer exception.
2017-06-22 10:23:56 -03:00
jvelilla
5c31905427 Updated EWF http_network, websocket, httpd to use the latest EiffelNet SSL
version.
2017-06-21 18:34:07 -03:00
Jocelyn Fiat
67bdcfb6ef Made the parameters not hidden implementation classes. 2017-06-21 08:58:15 +02:00
Jocelyn Fiat
ca4043b102 Merge branch 'master' into v1 2017-06-20 18:17:30 +02:00
Jocelyn Fiat
310e96e185 Updated EOL for 2 ecf files. 2017-06-20 18:16:43 +02:00
e14bb568d2 Extracted compression code from wsf, and provided new wsf_compression library.
Renamed features.
2016-12-06 14:18:51 +01:00
05d37439bc Merge branch 'ewf_compression' of https://github.com/jvelilla/EWF into ewf_compression 2016-12-06 12:53:41 +01:00
jvelilla
99bf552b89 Added compression support to WSF_FILE_SYSTEM_HANDLER.
Added a simple example using eiffel web compression.
2016-11-29 18:17:44 -03:00
134 changed files with 169111 additions and 913 deletions

View File

@@ -0,0 +1,20 @@
language: eiffel
before_script:
- export current_dir=$PWD ; echo current_dir=$current_dir ; cd ..
- export ISE_VERSION=17.05; export ISE_BUILD=100416
- curl -sSL http://downloads.sourceforge.net/eiffelstudio/Eiffel_${ISE_VERSION}_gpl_${ISE_BUILD}-linux-x86-64.tar.bz2 | tar -x --bzip2
- 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`
- cd $current_dir
- echo Check projects compilation status...
branches:
only:
- master
- v1
- develop
script: compile_all -ecb -melt -list_failures -log_verbose -clean -options dotnet=false
group: stable
os: linux

View File

@@ -7,14 +7,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
## [Unreleased]
### Added
- 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 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.
### Changed
- adopted ecf version 1-16-0 and use a single .ecf file (the -safe.ecf are now redirection to normal .ecf)
### Deprecated
- removed support for Eiffel version before 17.05 .
- SSL 2 or 3 is obsolete and will raise an exception if used.
### Removed
### Fixed
- Removed a few obsolete calls.
- `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.
- `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`.
### Security

View File

@@ -1,5 +1,7 @@
# Eiffel Web Framework
[![Build Status](https://api.travis-ci.org/EiffelWebFramework/EWF.svg?branch=master)](https://travis-ci.org/EiffelWebFramework/EWF/)
## Overview

View File

@@ -1,7 +1,6 @@
package nino
project
nino = "nino-safe.ecf"
nino = "nino.ecf"
note

View File

@@ -59,7 +59,7 @@ feature -- Basic operations
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).
@@ -86,6 +86,8 @@ feature {NONE} -- Initialization
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_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.

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

View File

@@ -2,8 +2,8 @@ verbose=true
verbose_level=ALERT
port=9090
#max_concurrent_connections=100
keep_alive_timeout=3
#max_tcp_clients=100
socket_timeout=60
socket_recv_timeout=15
#keep_alive_timeout=5
#max_keep_alive_requests=300
#max_tcp_clients=100
#socket_timeout=60
#socket_recv_timeout=15

View File

@@ -0,0 +1,4 @@
port=9090
verbose=true
socket_recv_timeout=15
keep_alive_timeout=30

View 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

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

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

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

View File

@@ -10,6 +10,9 @@ inherit
WSF_WEBSOCKET_EXECUTION
WEB_SOCKET_EVENT_I
redefine
on_timer
end
create
make
@@ -52,6 +55,9 @@ feature -- Websocket execution
on_open (ws: WEB_SOCKET)
do
initialize_commands
set_timer_delay (1) -- Every 1 second.
ws.put_error ("Connecting")
ws.send (Text_frame, "Hello, this is a simple demo with Websocket using Eiffel. (/help for more information).%N")
end
@@ -62,12 +68,26 @@ feature -- Websocket execution
end
on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
local
i: INTEGER
cmd_name: READABLE_STRING_8
do
if a_message.same_string_general ("/help") then
-- Echo the message for testing.
ws.send (Text_frame, "Help: available commands%N - /time : return the server UTC time.%N")
elseif a_message.starts_with_general ("/time") then
ws.send (Text_frame, "Server time is " + (create {HTTP_DATE}.make_now_utc).string)
if a_message.starts_with_general ("/") then
from
i := 1
until
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
-- Echo the message for testing.
ws.send (Text_frame, a_message)
@@ -80,6 +100,88 @@ feature -- Websocket execution
ws.put_error ("Connection closed")
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
websocket_app_html (a_port: INTEGER): STRING
@@ -188,5 +290,4 @@ body {font-family:Arial, Helvetica, sans-serif;}
end
end
end

View File

@@ -3,9 +3,9 @@
<target name="websocket_app">
<root class="APPLICATION" feature="make_and_launch"/>
<file_rule>
<exclude>/\.svn$</exclude>
<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"/>
@@ -17,6 +17,14 @@
<library name="wsf" location="..\..\library\server\wsf\wsf.ecf"/>
<cluster name="app" location=".\" recursive="true"/>
</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">
<variable name="ssl_enabled" value="true"/>
</target>

View File

@@ -1,13 +1,20 @@
verbose=true
verbose_level=INFORMATION
port=9090
#Socket and timeout
max_concurrent_connections=100
keep_alive_timeout=35
max_tcp_clients=100
socket_timeout=30
socket_recv_timeout=5
max_keep_alive_requests=300
#Persistent connections
keep_alive_timeout=2
max_keep_alive_requests=-1
#SSL
is_secure=false
secure_certificate=ca.crt
secure_certificate_key=ca.key
#Debug
verbose=true
verbose_level=INFORMATION

View File

@@ -10,6 +10,9 @@ It provides simple routine to perform http requests, and get response.
- Eiffel Net library
- and optionally Eiffel NetSSL library to support `https://...`
* Note: set ciphers setting is supported only with libcurl implementation for now, net implementation
set all the ciphers as part of the OpenSSL initialization.
This means on Windows, do not forget to copy the libcurl.dll (and related) either in the same directory of the executable, or ensure the .dll are in the PATH environment.
It is possible to exclude the libcurl implementation xor the Eiffel Net implementation:

View File

@@ -7,8 +7,6 @@
<exclude>/\.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf">
<condition>
@@ -32,8 +30,8 @@
</library>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<cluster name="src" location=".\src\">
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
<cluster name="parameters" location="$|parameters" recursive="true"/>
<cluster name="implementation" location="$|implementation\" recursive="true" hidden="true"/>
<cluster name="parameters" location="$|parameters\" recursive="true"/>
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
<cluster name="spec_net" location="$|spec\net\">
<condition>

View File

@@ -7,8 +7,6 @@
<exclude>/\.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
@@ -16,8 +14,8 @@
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<cluster name="src" location=".\src\">
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
<cluster name="parameters" location="$|parameters" recursive="true"/>
<cluster name="implementation" location="$|implementation\" recursive="true" hidden="true"/>
<cluster name="parameters" location="$|parameters\" recursive="true"/>
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
<cluster name="default_libcurl" location="$|default\libcurl\"/>
</cluster>

View File

@@ -7,8 +7,6 @@
<exclude>/\.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
<library name="http" location="..\protocol\http\http.ecf"/>
@@ -25,8 +23,8 @@
</library>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<cluster name="src" location=".\src\">
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
<cluster name="parameters" location="$|parameters" recursive="true"/>
<cluster name="implementation" location="$|implementation\" recursive="true" hidden="true"/>
<cluster name="parameters" location="$|parameters\" recursive="true"/>
<cluster name="spec_net" location="$|spec\net\">
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
</cluster>

View File

@@ -1,22 +1,20 @@
package http_client
project
http_client = "http_client-safe.ecf"
http_client = "http_client.ecf"
libcurl_http_client = "libcurl_http_client-safe.ecf"
libcurl_http_client = "libcurl_http_client.ecf"
net_http_client = "net_http_client-safe.ecf"
net_http_client = "net_http_client.ecf"
note
title: HTTP client
description: "[
Provides simple routines to perform http requests, and get associated response.
It has two implementations:
- using Eiffel cURL (i.e libcurl)
- using EiffelNET (and the EiffelNET SSL extension)
]"
collection:EWF
description: "[
Provides simple routines to perform http requests, and get associated response.
It has two implementations:
- using Eiffel cURL (i.e libcurl)
- using EiffelNET (and the EiffelNET SSL extension)
]"
collection: EWF
tags: http,client,network,request,web,curl,EWF
copyright: 1984-2016 Eiffel Software and others
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)

View File

@@ -16,8 +16,19 @@ feature -- Access
deferred
end
get (a_url: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
do
Result := new_session (a_url).get ("", ctx)
end
custom (a_method: READABLE_STRING_8; a_url: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- Response for `a_method' request based on `a_url' and optional `ctx'.
do
Result := new_session (a_url).custom (a_method, "", ctx)
end
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)"
source: "[
Eiffel Software

View File

@@ -30,8 +30,19 @@ feature {NONE} -- Initialization
-- Initialize Current with `a_url' and `ctx'.
-- This can be used to reset/reinitialize Current with new url
-- in the case of redirection.
local
i: INTEGER
do
url := a_url
i := a_url.substring_index ("://", 1)
if i > 0 then
check
a_url.head (i - 1).same_string ("http")
or a_url.head (i - 1).same_string ("https")
end
url := a_url
else
url := session.url (a_url, Void)
end
headers := session.headers.twin
if ctx /= Void then
context := ctx

View File

@@ -61,12 +61,40 @@ feature -- Access
url (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): STRING_8
-- Url computed from Current and `ctx' data.
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
ctx.append_query_parameters_to_url (Result)
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
verbose_mode: INTEGER
@@ -272,6 +300,14 @@ feature -- Authentication
-- Associated optional credentials value.
-- Computed as `username':`password'.
ciphers_setting: detachable READABLE_STRING_8
-- SSL cipher preference lists
-- examples: DEFAULT, ALL, TLSv1
-- check https://www.openssl.org/docs/man1.1.0/apps/ciphers.html
-- Warning: At the moment only used for LIB_CURL_HTTP_CLIENT
-- Net implementation set all the ciphers using the OpenSSL at
-- initialization time.
feature -- Status setting
set_is_debug (b: BOOLEAN)
@@ -401,6 +437,14 @@ feature -- Element change
chunk_size := a_size
end
set_ciphers_setting (a_ciphers_setting: READABLE_STRING_8)
-- Set 'ciphers_setting' with 'a_ciphers_setting'.
do
create {STRING_8} ciphers_setting.make_from_string (a_ciphers_setting)
ensure
ciphers_setting_set: attached ciphers_setting as c_setting and then c_setting.same_string (a_ciphers_setting)
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)"

View File

@@ -7,6 +7,9 @@ note
deferred class
HTTP_CLIENT_REQUEST_PARAMETER
inherit
DEBUG_OUTPUT
feature -- Access
name: READABLE_STRING_32
@@ -18,6 +21,15 @@ feature -- Access
deferred
end
feature -- Status report
debug_output: STRING_32
do
create Result.make_empty
Result.append (name)
Result.append ("=...")
end
feature -- Conversion
append_form_url_encoded_to (a_output: STRING_8)

View File

@@ -29,6 +29,11 @@ feature -- Access
Result := items.count
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
extend, force (i: G)

View File

@@ -206,7 +206,10 @@ feature -- Execution
l_use_curl_form := True
end
else
l_headers.force ("application/x-www-form-urlencoded", "Content-Type")
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfields, l_upload_data)
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfieldsize, l_upload_data.count)
end
if l_use_curl_form then
create l_form.make
@@ -372,6 +375,11 @@ feature -- Execution
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_ssl_verifypeer, 0)
end
--| Cipher List
if attached session.ciphers_setting as c_list then
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_ssl_cipher_list, c_list )
end
--| Request method
if request_method.is_case_insensitive_equal ("GET") then
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httpget, 1)

View File

@@ -113,6 +113,7 @@ feature -- Access
-- Get URL data
l_is_https := url.starts_with_general ("https://")
create l_uri.make_from_string (url)
check valid_url: l_uri.is_valid end
l_port := l_uri.port
if l_port = 0 then
if l_is_https then

View File

@@ -74,6 +74,22 @@ feature -- Test routines
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
local
res: HTTP_CLIENT_RESPONSE

View File

@@ -32,6 +32,11 @@ feature -- Tests
test_http_client_ssl
end
test_libcurl_abs_url
do
test_abs_url
end
test_libcurl_headers
do
test_headers

View File

@@ -32,6 +32,11 @@ feature -- Tests
test_http_client_ssl
end
test_net_abs_url
do
test_abs_url
end
test_net_headers
do
test_headers

View File

@@ -7,117 +7,32 @@
<exclude>/\.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true">
</option>
<external_include location="$ECF_CONFIG_PATH/spec/include">
<condition>
<version type="compiler" min="16.9.9.9124"/>
</condition>
</external_include>
<external_include location="$ECF_CONFIG_PATH/spec/include_until_16_05">
<condition>
<version type="compiler" max="16.9.9.9124"/>
</condition>
</external_include>
<external_include location="$(ISE_LIBRARY)/unstable/library/network/socket/netssl/spec/include">
<condition>
<platform excluded_value="windows"/>
<version type="compiler" max="16.9.9.9124"/>
<custom name="ssl_enabled" value="true"/>
</condition>
</external_include>
<external_include location="$(ISE_LIBRARY)\unstable\library\network\socket\netssl\spec\include">
<condition>
<platform value="windows"/>
<version type="compiler" max="16.9.9.9124"/>
<custom name="ssl_enabled" value="true"/>
</condition>
</external_include>
<external_cflag value="-D_WINSOCKAPI_">
<condition>
<platform value="windows"/>
<version type="compiler" max="16.9.9.9124"/>
<custom name="ssl_enabled" value="true"/>
</condition>
</external_cflag>
<external_include location="$ECF_CONFIG_PATH/spec/include"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
</library>
<library name="openssl" location="$ISE_LIBRARY\unstable\library\network\openssl\openssl.ecf">
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
<version type="compiler" min="17.10.0.0"/>
<custom name="ssl_enabled" value="true"/>
</condition>
</library>
<cluster name="network" location=".\src\">
<file_rule>
<exclude>/http_stream_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="17.02"/>
</condition>
</file_rule>
<cluster name="disabled_ssl_network" location="$|no_ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="net_ssl_enabled" excluded_value="true"/>
<custom name="httpd_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl_network" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
<file_rule>
<exclude>/http_stream_secure_socket_ext.e$</exclude>
<condition>
<version type="compiler" max="16.9.9.9124"/>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<version type="compiler" max="16.9.9.9124"/>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<version type="compiler" max="16.9.9.9124"/>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</file_rule>
</cluster>
</cluster>
<cluster name="network_until_16_05" location=".\src\until_16_05\">
<condition>
<version type="compiler" max="16.9.9.9124"/>
</condition>
<cluster name="ssl_network_until_16_05" location="$|ssl\" recursive="true">
<condition>
<version type="compiler" max="16.9.9.9124"/>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<version type="compiler" max="16.9.9.9124"/>
<custom name="net_ssl_enabled" value="true"/>
</condition>
<condition>
<version type="compiler" max="16.9.9.9124"/>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</cluster>
</cluster>
<cluster name="network_until_17_01" location=".\src\until_17_01\">
<condition>
<version type="compiler" min="16.9.9.9124" max="17.02"/>
</condition>
</cluster>
</target>
<target name="http_network_ssl" extends="http_network">
<variable name="ssl_enabled" value="true"/>

View File

@@ -1,52 +0,0 @@
/*
indexing
description: "Functions used by the EiffelWeb http networking classes. "
copyright: "Copyright (c) 2011-2016, Jocelyn Fiat, 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
]"
*/
#ifndef _ew_network_h_
#define _ew_network_h_
#include "eif_config.h"
#ifdef EIF_WINDOWS
# ifndef _WINSOCKAPI_
# define FD_SETSIZE 256
# include <winsock2.h>
# include <Ws2tcpip.h>
# include <stdio.h>
# endif
#else /* unix-specific */
# include <sys/socket.h>
# include <unistd.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* extern declarations ... */
#ifdef EIF_WINDOWS
extern int setsockopt(int, int, int, char*, int);
extern int recv(int, char *, int, int);
extern int send(int, char *, int, int);
#else
extern int setsockopt(int, int, int, const void*, socklen_t);
extern ssize_t recv(int, void *, size_t, int);
extern ssize_t send(int, const void *, size_t, int);
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -9,8 +9,6 @@ class
inherit
NETWORK_STREAM_SOCKET
HTTP_STREAM_SOCKET_EXT
create
make, make_empty,
make_client_by_port, make_client_by_address_and_port,

View File

@@ -55,12 +55,19 @@ feature -- Secure connection Helpers
end
set_secure_protocol_to_ssl_2_or_3
-- Set `ssl_protocol' with `Ssl_23'.
do
set_secure_protocol ({SSL_PROTOCOL}.Ssl_23)
end
-- Set `ssl_protocol' with `Ssl_23'.
-- Protocol not supported anymore.
obsolete
"Use set_secure_protocol_to_tls_1_2 [2017-06-23]."
local
err: DEVELOPER_EXCEPTION
do
create err
err.set_description ("SSL_2 or SSL_3 are not supported anymore, upgrate to TLS set_secure_protocol_to_tls_1_2")
err.raise
end
set_secure_protocol_to_tls_1_0
set_secure_protocol_to_tls_1_0
-- Set `ssl_protocol' with `Tls_1_0'.
do
set_secure_protocol ({SSL_PROTOCOL}.Tls_1_0)
@@ -176,7 +183,14 @@ feature -- Output
end
note
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat 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)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -1,174 +0,0 @@
note
description: "[
Until 16.05, the EiffelNet socket interface DOES NOT have
- make_server_by_address_and_port
- recv_timeout
- send_timeout.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTP_STREAM_SOCKET_EXT
inherit
PLATFORM
feature -- Initialization
make
deferred
end
make_server_by_address_and_port (a_address: INET_ADDRESS; a_port: INTEGER)
-- Create server socket on `a_address' and `a_port'.
require
valid_port: a_port >= 0
do
make
set_address (create {like address_type}.make_from_address_and_port (a_address, a_port))
bind
end
feature -- Basic operation
bind
deferred
end
feature -- Access
set_address (addr: detachable like address_type)
deferred
end
address_type: NETWORK_SOCKET_ADDRESS
deferred
end
descriptor: INTEGER
-- Socket descriptor of current socket
deferred
end
socket_buffer: MANAGED_POINTER
deferred
end
read_socket_buffer: MANAGED_POINTER
do
Result := socket_buffer
end
put_socket_buffer: MANAGED_POINTER
do
Result := socket_buffer
end
feature -- Socket Recv and Send timeout.
set_recv_timeout (a_timeout_seconds: INTEGER)
-- Set the receive timeout in seconds on Current socket.
-- if `0' the related operations will never timeout.
require
positive_timeout: a_timeout_seconds >= 0
do
c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds)
end
set_send_timeout (a_timeout_seconds: INTEGER)
-- Set the send timeout in milliseconds on Current socket.
-- if `0' the related operations will never timeout.
require
positive_timeout: a_timeout_seconds >= 0
do
c_set_sock_send_timeout (descriptor, level_sol_socket, a_timeout_seconds)
end
feature {NONE} -- Externals
level_sol_socket: INTEGER
-- SOL_SOCKET level of options
deferred
end
c_set_sock_recv_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER)
-- C routine to set socket option `SO_RCVTIMEO' with `a_timeout_seconds' seconds.
external
"C inline use %"ew_network.h%""
alias
"[
#ifdef SO_RCVTIMEO
int flag = SO_RCVTIMEO;
#else
int flag = 0x1006;
#endif
#ifdef EIF_WINDOWS
int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
setsockopt((int) $a_fd, (int) $a_level, flag, (char *) &arg, sizeof(arg));
#else
struct timeval tv;
tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
tv.tv_usec = 0;
setsockopt((int) $a_fd, (int) $a_level, flag, (struct timeval *)&tv, sizeof(struct timeval));
#endif
]"
end
c_set_sock_send_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER)
-- C routine to set socket option `SO_SNDTIMEO' with `a_timeout_seconds' seconds.
external
"C inline use %"ew_network.h%""
alias
"[
#ifdef SO_RCVTIMEO
int flag = SO_SNDTIMEO;
#else
int flag = 0x1005;
#endif
#ifdef EIF_WINDOWS
int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
setsockopt((int) $a_fd, (int) $a_level, flag, (char *) &arg, sizeof(arg));
#else
struct timeval tv;
tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
tv.tv_usec = 0;
setsockopt((int) $a_fd, (int) $a_level, flag, (struct timeval *)&tv, sizeof(struct timeval));
#endif
]"
end
feature {NONE} -- No-Exception network operation
c_recv_noexception (a_fd: INTEGER; buf: POINTER; len: INTEGER; flags: INTEGER): INTEGER
-- External routine to read a `len' number of characters
-- into buffer `buf' from socket `a_fd' with options `flags'.
external
"C inline use %"ew_network.h%""
alias
"[
recv((int) $a_fd, (char *) $buf, (int) $len, (int) $flags)
]"
end
c_read_stream_noexception (a_fd: INTEGER; len: INTEGER; buf: POINTER): INTEGER
-- External routine to read a `len' number of characters
-- into buffer `buf' from socket `a_fd'.
do
Result := c_recv_noexception (a_fd, buf, len, 0)
end
c_put_stream_noexception (a_fd: INTEGER; buf: POINTER; len: INTEGER): INTEGER
-- External routine to write stream pointed by `s' of
-- length `length' to socket `fd'.
-- Note: does not raise exception on error, but return error value as Result.
external
"C inline use %"ew_network.h%""
alias
"[
send((int) $a_fd, (char *) $buf, (int) $len, (int) 0)
]"
end
end

View File

@@ -1,41 +0,0 @@
note
description: "[
Extension to HTTP_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, WHEN 16.05 IS OLD.
]"
deferred class
HTTP_STREAM_SECURE_SOCKET_EXT
feature {NONE} -- SSL bridge
ssl_write (a_ssl: SSL; a_pointer: POINTER; a_byte_count: INTEGER): INTEGER
do
-- In delivery until 16.05
-- SSL.write does not return any value!
-- So let's use `c_ssl_write' from Current class
-- instead of:
-- a_ssl.write (a_pointer, a_byte_count)
Result := c_ssl_write (a_ssl.ptr, a_pointer, a_byte_count)
if a_ssl.was_error then
-- Until 16.05, there is no error check for `SSL.write'
-- so nothing can be done here.
if Result >= 0 then
Result := -1
end
end
end
c_ssl_write (an_ssl_ptr: POINTER; buffer: POINTER; nb_bytes: INTEGER_32): INTEGER_32
-- External call to SSL_write
-- (export status {NONE})
external
"C use %"eif_openssl.h%""
alias
"SSL_write"
end
end

View File

@@ -1,29 +0,0 @@
note
description: "[
Extension to HTTPD_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, WHEN 17.01 IS OLD.
]"
deferred class
HTTP_STREAM_SOCKET_EXT
feature -- Access
socket_buffer: MANAGED_POINTER
deferred
end
read_socket_buffer: MANAGED_POINTER
do
Result := socket_buffer
end
put_socket_buffer: MANAGED_POINTER
do
Result := socket_buffer
end
feature {NONE} -- No-Exception network operation
end

View File

@@ -1,14 +1,14 @@
package content_negotiation
project
conneg = "conneg-safe.ecf"
conneg = "conneg.ecf"
note
title: CONneg Content Negotiation
description: "[
CONneg is a library that provides utilities to select the best repesentation of a resource for a client where there are multiple representations available.
]"
CONneg is a library that provides utilities to select the best repesentation of a resource for a client where there are multiple representations available.
]"
collection: EWF
tags: content,accept,conneg,negotiation,EWF,web,request
copyright: 2011-2016, Javier Velilla, Jocelyn Fiat, Eiffel Software and others

View File

@@ -1,20 +1,20 @@
package http
project
http = "http-safe.ecf"
http = "http.ecf"
note
title: HTTP protocol
description: "[
Collection of interfaces related to HTTP protocol:
- header
- status codes, request methods
- content type, media type, mime type.
- cookie
- date used in web protocol
- file extension mime mapping
]"
Collection of interfaces related to HTTP protocol:
- header
- status codes, request methods
- content type, media type, mime type.
- cookie
- date used in web protocol
- file extension mime mapping
]"
collection: EWF
tags: http,web,header,status,method,type,mime,cookie
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)

View File

@@ -1,25 +1,24 @@
package notification_email
project
notification_email = "notification_email-safe.ecf"
notification_email = "notification_email.ecf"
note
title: Notification Email
description: "[
Abstract interface to send message via various mailers:
- smtp
- sendmail
- external script
- store on local file
- ...
]"
Abstract interface to send message via various mailers:
- smtp
- sendmail
- external script
- store on local file
- ...
]"
collection: EWF
tags: message,smtp,sendmail,mailer
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
copyright: 2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
link[license]: http://www.eiffel.com/licensing/forum.txt
link[source]: "github" https://github.com/EiffelWebFramework/EWF/tree/master/library/runtime/process/notification_email
end

View File

@@ -4,10 +4,6 @@
<root all_classes="true"/>
<option warning="true">
</option>
<capability>
<concurrency support="scoop" use="scoop"/>
<void_safety support="all" use="all"/>
</capability>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>

View File

@@ -6,7 +6,7 @@ project
note
title: JSON Web Token
description: JSON Web Token
tags:jwt,web,jws,jwe,token,jose
tags: jwt,web,jws,jwe,token,jose
copyright: 2011-2017, Jocelyn Fiat, Eiffel Software and others
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
link[license]: http://www.eiffel.com/licensing/forum.txt

View File

@@ -0,0 +1,36 @@
note
description: "Summary description for {JWT_MISMATCHED_ALG_ERROR}."
date: "$Date$"
revision: "$Revision$"
class
JWT_MISMATCHED_ALG_ERROR
inherit
JWT_ERROR
create
make
feature {NONE} -- Initialization
make (a_alg, a_header_alg: READABLE_STRING_8)
do
alg := a_alg
header_alg := a_header_alg
end
feature -- Access
alg: READABLE_STRING_8
header_alg: READABLE_STRING_8
id: STRING = "ALG_MISMATCH"
message: READABLE_STRING_8
do
Result := "Header alg [" + header_alg + "] does not match given alg [" + alg + "]!"
end
end

View File

@@ -59,6 +59,8 @@ feature -- Status report
do
if attached claimset.issuer as iss then
Result := a_issuer = Void or else a_issuer.same_string (iss)
else
Result := a_issuer = Void
end
end
@@ -66,6 +68,8 @@ feature -- Status report
do
if attached claimset.audience as aud then
Result := a_audience = Void or else a_audience.same_string (aud)
else
Result := a_audience = Void
end
end
@@ -118,6 +122,11 @@ feature {JWT_UTILITIES} -- Error reporting
l_errors.extend (err)
end
report_mismatched_alg_error (alg, a_header_alg: READABLE_STRING_8)
do
report_error (create {JWT_MISMATCHED_ALG_ERROR}.make (alg, a_header_alg))
end
report_unsupported_alg_error (alg: READABLE_STRING_8)
do
report_error (create {JWT_UNSUPPORTED_ALG_ERROR}.make (alg))

View File

@@ -266,11 +266,10 @@ feature {NONE} -- Implementation
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
local
hs256: HMAC_SHA256
ut: JWT_UTILITIES
do
create hs256.make_ascii_key (a_secret)
hs256.update_from_string (s)
Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
create ut
Result := ut.base64_hmacsha256 (s, a_secret)
end
end

View File

@@ -1,8 +1,8 @@
note
description: "Summary description for {JWT_LOADER}."
author: ""
description: "Loader and verifier to JWT token."
date: "$Date$"
revision: "$Revision$"
EIS: "name=Known Critical vulnerabilities in JWT libs", "protocol=URI", "src=https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/"
class
JWT_LOADER
@@ -12,9 +12,13 @@ inherit
feature -- Access
token (a_token_input: READABLE_STRING_8; a_secret: READABLE_STRING_8; ctx: detachable JWT_CONTEXT): detachable JWT
-- Decoded token from `a_token_input` given the secret `a_secret`, and optional context `ctx`
token (a_token_input: READABLE_STRING_8; a_alg: detachable READABLE_STRING_8; a_verification_key: READABLE_STRING_8; ctx: detachable JWT_CONTEXT): detachable JWT
-- Decoded token from `a_token_input` given the verification key `a_verification_key` and optional (but recommended) signature algorithm `a_alg`, and optional context `ctx`
-- used to specify eventual issuer and various parameters.
-- WARNING: passing Void for `a_alg` is not safe, as the server should know which alg he used for tokens,
-- leaving the possibility to use the header alg is dangerous as client may use "none" and then bypass verification!
require
a_valid_alg: a_alg /= Void implies is_supporting_signature_algorithm (a_alg)
local
jws: JWS
i,j,n: INTEGER
@@ -29,20 +33,27 @@ feature -- Access
l_enc_payload := a_token_input.substring (i + 1, j - 1)
l_signature := a_token_input.substring (j + 1, n)
create jws.make_with_json_payload (base64url_decode (l_enc_payload))
alg := signature_algorithm_from_encoded_header (l_enc_header)
jws.set_algorithm (alg)
if alg = Void then
-- Use default
alg := alg_hs256
if a_alg /= Void then
if alg /= Void and then not alg.is_case_insensitive_equal_general (a_alg) then
jws.report_mismatched_alg_error (a_alg, alg)
else
alg := a_alg
end
else
if alg = Void then
-- Use default
alg := alg_hs256
end
end
jws.set_algorithm (alg)
check alg_set: alg /= Void end
if ctx = Void or else not ctx.validation_ignored then
if not is_supporting_signature_algorithm (alg) then
jws.report_unsupported_alg_error (alg)
alg := alg_hs256
end
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_secret, alg)) then
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_verification_key, alg)) then
jws.report_unverified_token_error
end
if

View File

@@ -61,7 +61,33 @@ feature -- Encoding
do
create hs256.make_ascii_key (a_secret)
hs256.update_from_string (s)
Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
-- if Version >= EiffelStudio 17.11 then
-- Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
-- else
Result := base64_bytes_encoded_string (hs256.digest)
-- end
end
feature {NONE} -- Implementation
base64_bytes_encoded_string (a_bytes: SPECIAL [NATURAL_8]): STRING_8
-- Base64 string from `a_bytes`.
--| Note: to be removed when 17.11 is not latest release anymore.
local
s: STRING
i,n: INTEGER
do
from
i := 1
n := a_bytes.count
create s.make (n)
until
i > n
loop
s.append_code (a_bytes[i - 1])
i := i + 1
end
Result := (create {BASE64}).encoded_string (s)
end
feature -- Decoding

View File

@@ -54,7 +54,14 @@ feature -- Test
create jwt_loader
if attached jwt_loader.token (tok, "secret", Void) as l_tok then
-- Use header alg!
if attached jwt_loader.token (tok, Void, "secret", Void) as l_tok then
assert ("no error", not l_tok.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
-- Use given alg!
if attached jwt_loader.token (tok, jwt.algorithm, "secret", Void) as l_tok then
assert ("no error", not l_tok.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
@@ -96,21 +103,21 @@ feature -- Test
create jwt_loader
-- Test with validation + exp
if attached jwt_loader.token (tok, "secret", Void) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", Void) as l_tok then
assert ("no error", not l_tok.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
create ctx
ctx.set_time (now)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("no error", not l_tok.has_error)
end
dt := duplicated_time (now)
dt.hour_add (5)
ctx.set_time (dt)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("exp error", l_tok.has_error)
end
@@ -122,7 +129,7 @@ feature -- Test
tok := jwt.encoded_string ("secret")
ctx.set_time (now)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("has nbf error", l_tok.has_error)
end
@@ -130,7 +137,7 @@ feature -- Test
dt.second_add (15)
ctx.set_time (dt)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("has nbf error", l_tok.has_error)
end
@@ -138,31 +145,51 @@ feature -- Test
dt.minute_add (45)
ctx.set_time (dt)
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("no error", not l_tok.has_error)
end
-- Test Issuer
ctx.set_issuer ("urn:foobar")
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("has iss error", l_tok.has_error)
end
ctx.set_issuer ("urn:foo")
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("no error", not l_tok.has_error)
end
-- Test Audience
ctx.set_audience ("urn:foobar")
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("has aud error", l_tok.has_error)
end
ctx.set_audience ("urn:foo")
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
assert ("no error", not l_tok.has_error)
end
end
test_mismatched_alg_jwt
local
jwt: JWS
payload: STRING
tok: STRING
do
payload := "[
{"iss":"joe","exp":1300819380,"http://example.com/is_root":true}
]"
create jwt.make_with_json_payload (payload)
jwt.set_algorithm ("none")
tok := jwt.encoded_string ("secret")
if attached (create {JWT_LOADER}).token (tok, "HS256", "secret", Void) as l_tok then
assert ("no error", not jwt.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
end
test_unsecured_jwt
local
jwt: JWS
@@ -177,7 +204,11 @@ feature -- Test
jwt.set_algorithm ("none")
tok := jwt.encoded_string ("secret")
if attached (create {JWT_LOADER}).token (tok, "secret", Void) as l_tok then
if attached (create {JWT_LOADER}).token (tok, "none", "secret", Void) as l_tok then
assert ("no error", not jwt.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end
if attached (create {JWT_LOADER}).token (tok, Void, "secret", Void) as l_tok then
assert ("no error", not jwt.has_error)
assert ("same payload", l_tok.claimset.string.same_string (payload))
end

View File

@@ -2,11 +2,10 @@ package openid
project
openid = "consumer/openid.ecf"
openid = "consumer/openid-safe.ecf"
note
title: Eiffel OpenID
description: OpenID consumer library
description: OpenID consumer library
tags: openid,security,web,authentication,sso
copyright: 2011-2016, Jocelyn Fiat, Eiffel Software and others
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
@@ -15,4 +14,3 @@ note
link[doc]: "Documentation" https://github.com/EiffelWebFramework/EWF/tree/master/library/security/http_authorization/README.md
end

View File

@@ -1,14 +1,14 @@
package http_authorization
project
http_authorization = "http_authorization-safe.ecf"
http_authorization = "http_authorization.ecf"
note
title: HTTP Authorization
description: "[
Class to manipulate HTTP 'Authorization' header value.
]"
Class to manipulate HTTP 'Authorization' header value.
]"
collection: EWF
tags: http,authorization,authentication,web
copyright: 2011-2016, Jocelyn Fiat, Eiffel Software and others
@@ -17,4 +17,3 @@ note
link[source]: "github" https://github.com/EiffelWebFramework/EWF/tree/master/library/server/authentication/http_authorization
end

View File

@@ -76,13 +76,13 @@ feature -- Initialization
a_http_authorization /= Void implies http_authorization /= Void
end
make_basic_auth (u: READABLE_STRING_32; p: READABLE_STRING_32)
make_basic_auth (u: READABLE_STRING_GENERAL; p: READABLE_STRING_GENERAL)
-- Create a Basic authentication.
do
make_custom_auth (u, p, Basic_auth_type)
end
make_custom_auth (u: READABLE_STRING_32; p: READABLE_STRING_32; a_type: READABLE_STRING_8)
make_custom_auth (u: READABLE_STRING_GENERAL; p: READABLE_STRING_GENERAL; a_type: READABLE_STRING_8)
-- Create a custom `a_type' authentication.
require
a_type_accepted: a_type.is_case_insensitive_equal (Basic_auth_type)
@@ -90,15 +90,20 @@ feature -- Initialization
local
t: STRING_8
utf: UTF_CONVERTER
s: STRING_32
do
login := u
password := p
create login.make_from_string_general (u)
create password.make_from_string_general (p)
create t.make_from_string (a_type)
t.left_adjust; t.right_adjust
type := t
if t.is_case_insensitive_equal (Basic_auth_type) then
type := Basic_auth_type
create http_authorization.make_from_string ("Basic " + (create {BASE64}).encoded_string (utf.string_32_to_utf_8_string_8 (u + {STRING_32} ":" + p)))
create s.make_from_string_general (u)
s.extend (':')
s.append_string_general (p)
create http_authorization.make_from_string ("Basic " + (create {BASE64}).encoded_string (utf.string_32_to_utf_8_string_8 (s)))
elseif t.is_case_insensitive_equal (Digest_auth_type) then
type := Digest_auth_type
to_implement ("HTTP Authorization %""+ t +"%", not yet implemented")
@@ -115,9 +120,9 @@ feature -- Access
type: READABLE_STRING_8
login: detachable READABLE_STRING_32
login: detachable IMMUTABLE_STRING_32
password: detachable READABLE_STRING_32
password: detachable IMMUTABLE_STRING_32
feature -- Status report

View File

@@ -1,24 +1,20 @@
package ewsgi
project
ewsgi = "ewsgi-safe.ecf"
ewsgi = "ewsgi.ecf"
connector_cgi = "connectors/cgi/cgi-safe.ecf"
connector_cgi = "connectors/cgi/cgi.ecf"
connector_libfcgi = "connectors/libfcgi/libfcgi-safe.ecf"
connector_libfcgi = "connectors/libfcgi/libfcgi.ecf"
connector_null = "connectors/null/null-safe.ecf"
connector_null = "connectors/null/null.ecf"
connector_standalone = "connectors/standalone/standalone-safe.ecf"
connector_standalone = "connectors/standalone/standalone.ecf"
httpd = "connectors/standalone/src/httpd/httpd-safe.ecf"
httpd = "connectors/standalone/src/httpd/httpd.ecf"
ewsgi_spec = "ewsgi_spec.ecf"
connector_nino = "connectors/nino/nino.ecf"
note
title: EWSGI
description: "[
Eiffel Web Server Gateway Interface (EWSGI) specification, and a few connectors.
]"
Eiffel Web Server Gateway Interface (EWSGI) specification, and a few connectors.
]"
collection: EWF
tags: ewsgi,cgi,web,httpd,ewf
copyright: 2011-2016, Jocelyn Fiat, Eiffel Software and others
@@ -26,5 +22,5 @@ note
link[license]: http://www.eiffel.com/licensing/forum.txt
link[source]: "github" https://github.com/EiffelWebFramework/EWF/tree/master/library/server/ewsgi
link[doc]: "Documentation" https://github.com/EiffelWebFramework/EWF/tree/master/library/server/ewsgi/doc
end
end

View File

@@ -8,19 +8,9 @@ deferred class
inherit
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
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)"
source: "[
Eiffel Software

View File

@@ -167,7 +167,7 @@ feature -- Element change
end
set_socket_timeout (a_nb_seconds: like socket_timeout)
-- Set `socket_timeout' with `a_nb_seconds'
-- Set `socket_timeout' with `a_nb_seconds'.
do
socket_timeout := a_nb_seconds
ensure
@@ -175,7 +175,7 @@ feature -- Element change
end
set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
-- Set `socket_recv_timeout' with `a_nb_seconds'
-- Set `socket_recv_timeout' with `a_nb_seconds'.
do
socket_recv_timeout := a_nb_seconds
ensure
@@ -183,7 +183,7 @@ feature -- Element change
end
set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
-- Set `keep_alive_timeout' with `a_seconds'
-- Set `keep_alive_timeout' with `a_seconds'.
do
keep_alive_timeout := a_seconds
ensure
@@ -191,7 +191,7 @@ feature -- Element change
end
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
-- Set `max_keep_alive_requests' with `nb'
-- Set `max_keep_alive_requests' with `nb'.
do
max_keep_alive_requests := nb
ensure
@@ -254,7 +254,7 @@ feature -- Element change
end
mark_secure
-- Set is_secure in True
-- Set is_secure in True.
do
set_is_secure (True)
ensure
@@ -287,7 +287,7 @@ feature -- Element change
end
set_secure_protocol (a_version: NATURAL)
-- Set `secure_protocol' with `a_version'
-- Set `secure_protocol' with `a_version'.
do
secure_protocol := a_version
ensure
@@ -295,18 +295,19 @@ feature -- Element change
end
set_secure_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
-- Set `secure_protocol' with `a_ssl_version'
-- Set `secure_protocol' with `a_ssl_version'.
do
if a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then
set_secure_protocol_to_ssl_2_or_3
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_0") then
set_secure_protocol_to_tls_1_0
if a_ssl_version.is_case_insensitive_equal ("tls_1_2") then
set_secure_protocol_to_tls_1_2
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_1") then
set_secure_protocol_to_tls_1_1
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_2") then
set_secure_protocol_to_tls_1_2
elseif a_ssl_version.is_case_insensitive_equal ("tls_1_0") then
set_secure_protocol_to_tls_1_0
elseif a_ssl_version.is_case_insensitive_equal ("dtls_1_0") then
set_secure_protocol_to_dtls_1_0
elseif a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then
-- Obsolete!
set_secure_protocol_to_ssl_2_or_3
else -- Default
set_secure_protocol_to_tls_1_2
end
@@ -316,6 +317,8 @@ feature -- SSL Helpers
set_secure_protocol_to_ssl_2_or_3
-- Set `secure_protocol' with `Ssl_23'.
obsolete
"Use `set_secure_protocol_to_tls_1_2` [2017-06-23]."
deferred
end

View File

@@ -25,8 +25,8 @@ feature -- Default timeout settings
feature -- Default persistent connection settings
default_keep_alive_timeout: INTEGER = 15 -- seconds
default_max_keep_alive_requests: INTEGER = 100
default_keep_alive_timeout: INTEGER = 5 -- seconds
default_max_keep_alive_requests: INTEGER = 300
note
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"

View File

@@ -7,8 +7,6 @@
<exclude>/\.svn$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="http_network" location="..\..\network\http_network\http_network.ecf" readonly="false"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
@@ -16,8 +14,11 @@
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
</library>
<library name="openssl" location="$ISE_LIBRARY\unstable\library\network\openssl\openssl.ecf">
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
<version type="compiler" min="17.10.0.0"/>
<custom name="ssl_enabled" value="true"/>
</condition>
</library>
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf">
@@ -35,16 +36,12 @@
<cluster name="no_ssl" location="$|no_ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" excluded_value="true"/>
<custom name="httpd_ssl_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="ssl" location="$|ssl\" recursive="true">
<condition>
<custom name="ssl_enabled" value="true"/>
</condition>
<condition>
<custom name="httpd_ssl_enabled" value="true"/>
</condition>
</cluster>
<cluster name="concurrency_none" location="$|concurrency\none\" recursive="true">
<condition>

View File

@@ -140,7 +140,8 @@ feature -- Settings
is_persistent_connection_supported: BOOLEAN
-- Is persistent connection supported?
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
is_next_persistent_connection_supported: BOOLEAN
@@ -247,7 +248,8 @@ feature -- Execution
l_exit
loop
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
elseif n > 1 and is_verbose then
log ("Reuse connection (" + n.out + ")", information_level)

View File

@@ -1,14 +1,14 @@
package httpd
project
httpd = "httpd-safe.ecf"
httpd = "httpd.ecf"
note
title: HTTP server
description: "[
Simple HTTP listener and handler, that can be extended easily.
]"
Simple HTTP listener and handler, that can be extended easily.
]"
tags: http,httpd,server,web
collection: EWF
copyright: 2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others

View File

@@ -36,9 +36,16 @@ feature -- Access
feature -- SSL Helpers
set_secure_protocol_to_ssl_2_or_3
-- Set `secure_protocol' with `Ssl_23'.
-- Set `ssl_protocol' with `Ssl_23'.
-- Protocol not supported anymore.
obsolete
"Use set_secure_protocol_to_tls_1_2 [2017-06-23]."
local
err: DEVELOPER_EXCEPTION
do
set_secure_protocol ({SSL_PROTOCOL}.Ssl_23)
create err
err.set_description ("SSL_2 or SSL_3 are not supported anymore, upgrate to TLS set_secure_protocol_to_tls_1_2")
err.raise
end
set_secure_protocol_to_tls_1_0
@@ -67,7 +74,7 @@ feature -- SSL Helpers
note
copyright: "2011-2014, 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)"
source: "[
Eiffel Software

View File

@@ -4,16 +4,16 @@ setup
compile_library = Clib
project
libfcgi = "libfcgi-safe.ecf"
libfcgi = "libfcgi.ecf"
note
title: Eiffel libfcgi wrapper
description: "[
Wrapper on modified libfcgi.
(modification: added 64 bits support)
It brings implementation for FCGI protocol.
]"
Wrapper on modified libfcgi.
(modification: added 64 bits support)
It brings implementation for FCGI protocol.
]"
collection: EWF
tags: fcgi,libfcgi,cgi,http,web,ewf
copyright: 2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others

View File

@@ -13,6 +13,7 @@
<library name="encoder" location="..\..\..\..\text\encoder\encoder.ecf"/>
<library name="ewsgi" location="..\ewsgi\ewsgi.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_router_context" location="wsf_router_context.ecf" readonly="true"/>
<cluster name="extension" location="..\..\..\wsf\extension\" recursive="true"/>

View 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

View File

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

View File

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

View File

@@ -66,7 +66,7 @@ feature -- Access: connection
end
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
Result := option_integer_value ("max_tcp_clients", 0)
end
@@ -104,11 +104,26 @@ feature -- Access: persistent connection
-- Maximum number of requests allowed per persistent connection.
-- Recommended a high setting.
-- 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 .
do
Result := option_integer_value ("max_keep_alive_requests", 0)
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
is_secure: BOOLEAN assign set_is_secure
@@ -205,6 +220,16 @@ feature -- Element change
set_numeric_option ("max_keep_alive_requests", nb)
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 secured connection enabled to `b'.
-- i.e if connection is using SSL/TLS.

View File

@@ -88,6 +88,23 @@ feature -- Websocket events
deferred
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
on_pong (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
@@ -126,7 +143,7 @@ feature -- Websocket events: implemented
end
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)"
source: "[
Eiffel Software

View File

@@ -1,7 +1,7 @@
note
description: "[
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$"
revision: "$Revision$"
@@ -10,16 +10,14 @@ class
WEB_SOCKET
inherit
WEB_SOCKET_WRITER
WGI_STANDALONE_CONNECTOR_EXPORTER
WSF_RESPONSE_EXPORTER
WGI_EXPORTER
HTTPD_LOGGER_CONSTANTS
WEB_SOCKET_CONSTANTS
SHARED_BASE64
create
@@ -32,7 +30,7 @@ feature {NONE} -- Initialization
request := req
response := res
is_verbose := False
verbose_level := notice_level
verbose_level := {HTTPD_LOGGER_CONSTANTS}.notice_level
if
attached {WGI_STANDALONE_INPUT_STREAM} req.input as r_input
@@ -44,12 +42,7 @@ feature {NONE} -- Initialization
end
end
feature -- Access
socket: HTTPD_STREAM_SOCKET
-- Underlying connected socket.
feature {NONE} -- Access
feature {NONE} -- Access
request: WSF_REQUEST
-- Associated request.
@@ -75,14 +68,7 @@ feature -- Status
has_error: BOOLEAN
-- Error occured during processing?
feature -- Socket status
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
feature -- Status report
is_open_read: BOOLEAN
-- Is `socket' open for reading?
@@ -96,12 +82,6 @@ feature -- Socket status
Result := socket.is_open_write
end
socket_descriptor: INTEGER
-- Descriptor for current `socket'.
do
Result := socket.descriptor
end
feature -- Element change
set_is_verbose (b: BOOLEAN)
@@ -129,7 +109,7 @@ feature -- Basic operation
end
end
feature -- Basic Operation
feature {WSF_WEBSOCKET_EXECUTION} -- Basic Operation
open_ws_handshake
-- 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.
if is_verbose then
log ("%NReceive <====================", debug_level)
log ("%NReceive <====================", {HTTPD_LOGGER_CONSTANTS}.debug_level)
if attached req.raw_header_data as rhd then
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
if
@@ -186,7 +166,7 @@ feature -- Basic Operation
attached req.http_host -- Host header must be present
then
if is_verbose then
log ("key " + l_ws_key, debug_level)
log ("key " + l_ws_key, {HTTPD_LOGGER_CONSTANTS}.debug_level)
end
-- Sending the server's opening handshake
create l_sha1.make
@@ -198,9 +178,9 @@ feature -- Basic Operation
res.header.add_header_key_value ("Sec-WebSocket-Accept", l_key)
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
log (h.string, debug_level)
log (h.string, {HTTPD_LOGGER_CONSTANTS}.debug_level)
end
end
res.set_status_code_with_reason_phrase (101, "Switching Protocols")
@@ -208,7 +188,7 @@ feature -- Basic Operation
else
has_error := True
if is_verbose then
log ("Error (opening_handshake)!!!", debug_level)
log ("Error (opening_handshake)!!!", {HTTPD_LOGGER_CONSTANTS}.debug_level)
end
-- 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).
@@ -219,80 +199,77 @@ feature -- Basic Operation
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
i: INTEGER
l_chunk_size: INTEGER
l_chunk: READABLE_STRING_8
l_header_message: STRING
l_message_count: INTEGER
n: NATURAL_64
retried: BOOLEAN
l_timeout, nb: INTEGER
l_cb_timeout: INTEGER
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)
l_message_count := a_message.count
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)
has_input := False
if cb = Void then
has_input := socket.ready_for_reading
else
l_cb_timeout := cb.timer_delay
l_timeout := socket.timeout
if l_cb_timeout = 0 then
-- timeout event not enabled.
has_input := socket.ready_for_reading
else
l_header_message.append_code (n.as_natural_32)
end
socket.put_string_8_noexception (l_header_message)
if not socket.was_error then
l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable.
if l_message_count < l_chunk_size then
socket.put_string_8_noexception (a_message)
cb.on_timer (Current)
if l_cb_timeout > l_timeout then
-- event timeout duration is bigger than socket timeout
-- thus, no on_timeout before next frame waiting
has_input := socket.ready_for_reading
else
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
l_chunk_size = 0 or socket.was_error
has_input or nb <= 0
loop
debug ("ws")
print ("Sending chunk " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
has_input := socket.ready_for_reading
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
l_chunk := a_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
socket.put_string_8_noexception (l_chunk)
if l_chunk.count < l_chunk_size then
l_chunk_size := 0
end
i := i + l_chunk_size
end
debug ("ws")
print ("Sending chunk done%N")
nb := nb - l_cb_timeout
end
socket.set_timeout (l_timeout)
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
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
-- TODO Binary messages
-- Handle error responses in a better way.
@@ -402,7 +379,7 @@ feature -- Response!
if Result.is_valid then
--| valid frame/fragment
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
-- rsv validation
@@ -420,7 +397,7 @@ feature -- Response!
end
else
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
@@ -548,7 +525,7 @@ feature -- Response!
end
end
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
debug ("ws")
print (" -> ")
@@ -580,7 +557,7 @@ feature -- Response!
if Result /= Void then
if attached Result.error as err then
if is_verbose then
log (" !Invalid frame: " + err.string, debug_level)
log (" !Invalid frame: " + err.string, {HTTPD_LOGGER_CONSTANTS}.debug_level)
end
end
if Result.is_injected_control then
@@ -624,8 +601,7 @@ feature -- Response!
retry
end
feature -- Encoding
feature {NONE} -- Encoding
digest (a_sha1: SHA1): STRING
-- Digest of `a_sha1'.
@@ -672,7 +648,7 @@ feature {NONE} -- Socket helpers
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)
local
@@ -795,7 +771,6 @@ feature {NONE} -- Debug
end
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)"

View File

@@ -59,7 +59,8 @@ feature -- Execution
debug ("dbglog")
dbglog (generator + ".execute_websocket (loop) WS_REQUEST_HANDLER.process_request {" + ws.socket_descriptor.out + "}")
end
if ws.is_ready_for_reading then
ws.wait_for_input (callbacks)
if ws.has_input then
l_frame := ws.next_frame
if l_frame /= Void and then l_frame.is_valid then
if attached l_frame.injected_control_frames as l_injections then
@@ -140,7 +141,7 @@ feature {NONE} -- Logging
end
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)"
source: "[
Eiffel Software

View File

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

View 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

View File

@@ -1,40 +1,32 @@
package wsf
project
wsf = "wsf-safe.ecf"
wsf = "wsf.ecf"
wsf_extension = "wsf_extension-safe.ecf"
wsf_extension = "wsf_extension.ecf"
wsf_policy_driven = "wsf_policy_driven-safe.ecf"
wsf_policy_driven = "wsf_policy_driven.ecf"
wsf_router_context = "wsf_router_context-safe.ecf"
wsf_router_context = "wsf_router_context.ecf"
wsf_session = "wsf_session-safe.ecf"
wsf_session = "wsf_session.ecf"
wsf_cgi = "connector/cgi-safe.ecf"
wsf_cgi = "connector/cgi.ecf"
wsf_libfcgi = "connector/libfcgi-safe.ecf"
wsf_libfcgi = "connector/libfcgi.ecf"
wsf_openshift = "connector/openshift-safe.ecf"
default_cgi = "default/cgi-safe.ecf"
default_cgi = "default/cgi.ecf"
default_libfcgi = "default/libfcgi-safe.ecf"
default_libfcgi = "default/libfcgi.ecf"
default_openshift = "default/openshift-safe.ecf"
wsf_standalone = "connector/standalone-safe.ecf"
default_openshift = "default/openshift.ecf"
wsf_standalone = "connector/standalone.ecf"
default_standalone = "default/standalone-safe.ecf"
default_standalone = "default/standalone.ecf"
wsf_all = "connector/all.ecf"
wsf_nino = "connector/nino.ecf"
wsf_openshift = "connector/openshift.ecf"
wsf_standalone_websocket = "connector/standalone_websocket.ecf"
default_nino = "default/nino.ecf"
note
title: Web Server Foundation
description: "[
Core of the Eiffel Web Framework (EWF).
Provide the request, response, router, ... interfaces.
The foundation to build web server application.
]"
Core of the Eiffel Web Framework (EWF).
Provide the request, response, router, ... interfaces.
The foundation to build web server application.
]"
tags: ewf,server,httpd,request,connector,web
collection: EWF
copyright: 2011-2016, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others
@@ -44,4 +36,3 @@ note
link[doc]: "Documentation" http://eiffelwebframework.github.io/EWF/
end

View File

@@ -221,17 +221,19 @@ feature {WSF_RESPONSE} -- Output
l_url := Void
s.append ("<li>")
s.append ("<code>")
if doc_url_supported then
s.append ("<a class=%"mappingresource%" href=%"")
s.append (doc_url (m.associated_resource))
s.append ("%">")
s.append (m.associated_resource)
s.append ("</a>")
else
s.append (m.associated_resource)
if attached m.associated_resource as l_associated_resource then
s.append ("<code>")
if doc_url_supported then
s.append ("<a class=%"mappingresource%" href=%"")
s.append (doc_url (l_associated_resource))
s.append ("%">")
s.append (l_associated_resource)
s.append ("</a>")
else
s.append (l_associated_resource)
end
s.append ("</code>")
end
s.append ("</code>")
if meths /= Void then
s.append (" [")
@@ -291,7 +293,11 @@ feature {WSF_RESPONSE} -- Output
s.append ("</li>%N")
else
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,13 @@
note
description: "[
Extension to HTTPD_STREAM_SOCKET to support backward compatibility.
TO BE REMOVED IN THE FUTURE, When there is no need to support older compilers.
]"
description: "Summary description for {WSF_WITH_CONDITION_HANDLER}."
date: "$Date$"
revision: "$Revision$"
deferred class
HTTP_STREAM_SOCKET_EXT
WSF_WITH_CONDITION_HANDLER
inherit
WSF_EXECUTE_HANDLER
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,34 @@
note
description : "Objects that ..."
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-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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
note
description: "Summary description for {WSF_SELF_DOCUMENTED_URI_AGENT_HANDLER}."
author: ""
date: "$Date$"
revision: "$Revision$"
@@ -50,7 +49,7 @@ feature {NONE} -- Initialization
end
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)"
source: "[
Eiffel Software

View File

@@ -8,31 +8,14 @@ class
WSF_URI_AGENT_HANDLER
inherit
WSF_EXECUTE_AGENT_HANDLER
WSF_URI_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.call ([req, res])
end
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)"
source: "[
Eiffel Software

View File

@@ -8,21 +8,12 @@ deferred class
WSF_URI_FILTER_HANDLER
inherit
WSF_FILTER_HANDLER [WSF_URI_HANDLER]
WSF_EXECUTE_FILTER_HANDLER
WSF_URI_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-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)"
source: "[
Eiffel Software

View File

@@ -8,31 +8,15 @@ class
WSF_URI_RESPONSE_AGENT_HANDLER
inherit
WSF_EXECUTE_RESPONSE_AGENT_HANDLER
WSF_URI_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-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)"
source: "[
Eiffel Software

View File

@@ -8,6 +8,8 @@ deferred class
WSF_URI_RESPONSE_HANDLER
inherit
WSF_HANDLER
WSF_URI_HANDLER
feature -- Response
@@ -20,16 +22,8 @@ feature -- Response
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"
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

View File

@@ -15,13 +15,13 @@ inherit
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute handler for `req' and respond in `res'.
-- Execute `req' responding in `res'.
require
req_attached: req /= Void
res_attached: res /= Void
deferred
end
feature {WSF_ROUTER} -- Mapping
new_mapping (a_uri: READABLE_STRING_8): WSF_ROUTER_MAPPING
@@ -30,7 +30,7 @@ feature {WSF_ROUTER} -- Mapping
end
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)"
source: "[
Eiffel Software

View File

@@ -8,6 +8,8 @@ class
WSF_URI_TEMPLATE_AGENT_HANDLER
inherit
WSF_EXECUTE_AGENT_HANDLER
WSF_URI_TEMPLATE_HANDLER
create
@@ -16,26 +18,8 @@ create
convert
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
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)"
source: "[
Eiffel Software

Some files were not shown because too many files have changed in this diff Show More