Compare commits
60 Commits
es_rev1010
...
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 31fa31bd53 | |||
| a8a3ca5b97 | |||
| 7c6fe5a04a | |||
| c8e2009638 | |||
| 627ec7aefc | |||
| e7087bcbc1 | |||
| 8d881bcd7d | |||
| ed3ad962d1 | |||
| d3e865cf6c | |||
| 9fcd30b4e1 | |||
| 0baa05cf63 | |||
| f97f59b703 | |||
| dc377b84d3 | |||
| f14431fc05 | |||
| 9577d7d82a | |||
| 99f8377721 | |||
| 73d5555532 | |||
| ce3c7ac57a | |||
|
|
8754c2d67d | ||
| 5e928b9a47 | |||
|
|
9cdfbd2538 | ||
| e4fcc863ca | |||
|
|
7c8d6b9eef | ||
| a97eb4b062 | |||
| bd5aba3db6 | |||
| d43c4edb7d | |||
| 9cdd676417 | |||
| cb273c3176 | |||
| ec7d504502 | |||
| 7ed1e815b0 | |||
| da2e26f697 | |||
| bc169d6b26 | |||
| cf2f0f09fa | |||
| 207a109e44 | |||
| 2f2e2067ba | |||
| 7aa7bf1ab2 | |||
| 8e8c3602c6 | |||
|
|
ffd7dd8540 | ||
|
|
947c94644e | ||
|
|
6a779797a5 | ||
|
|
7b0ccc0aec | ||
|
|
74001fe674 | ||
|
|
c7eb12ad8e | ||
|
|
36eeff9285 | ||
|
|
e9292b3eac | ||
|
|
30625d460f | ||
|
|
24eb0a4002 | ||
|
|
7d738a164d | ||
|
|
1037256ea6 | ||
|
|
4d79bba04b | ||
|
|
5de024923e | ||
|
|
8b90241986 | ||
|
|
da1c0b8545 | ||
|
|
603bedf71d | ||
|
|
5fedad7f2e | ||
|
|
e83f5654d8 | ||
|
|
25446cac12 | ||
|
|
ccff084642 | ||
|
|
830adbe10c | ||
|
|
e6d998953e |
@@ -1,10 +1,8 @@
|
||||
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
|
||||
- curl -sSL https://www.eiffel.org/setup/install.sh | bash > eiffel.rc
|
||||
- source ./eiffel.rc
|
||||
- echo `ec -version`
|
||||
- cd $current_dir
|
||||
- echo Check projects compilation status...
|
||||
|
||||
49
CHANGELOG.md
@@ -7,13 +7,54 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
### Changed
|
||||
### Deprecated
|
||||
### Removed
|
||||
### Fixed
|
||||
### Security
|
||||
|
||||
## [v1.1.0] - 2018-10-29
|
||||
### Added
|
||||
- `examples`: Added an example to run the debug app with apache2+libfcgi inside a docker container.
|
||||
- `wsf`: Updated the default rescue response (i.e when exception or bad internal error occurs). Factorized the implementation in `WGI_RESCUE_EXECUTION`, and now by redefining the `WGI_EXECUTION.execute_rescue (...)` procedure, it is possible to have a custom response on such rescued execution.
|
||||
- `http`: Added `HTTP_COOKIE.set_expiration_from_max_age`, to add the "Expiration:" based on the max-age value.
|
||||
|
||||
### Changed
|
||||
- `wsf`: Adopted the nanoseconds timeout precision. And in config file added support for ns, us, ms, s timeout precision (without indication, it uses `seconds` precision).
|
||||
|
||||
### Deprecated
|
||||
### Removed
|
||||
### Fixed
|
||||
- `websocket`: Allow (websocket) upgrade even without persistent connection for normal http request.
|
||||
(note: this allows to use websocket in single-threaded mode, and avoid the keep-alive-timeout delay before websocket begins its execution)
|
||||
|
||||
- `http`: Fixed `HTTP_HEADER.put_raw_header (..)` by ignoring any empty line of the argument value. (note: "%R" is considered as empty line here.)
|
||||
- `jwt`: updated to make JWT algorithm support more flexible, and simple to extend with specific algorithm.
|
||||
- `httpd`, `websocket`: Fixed setting of socket.timeout in httpd (it was not correctly set).
|
||||
|
||||
### Security
|
||||
|
||||
## [v1.0.6] - 2018-02-05
|
||||
### Added
|
||||
- `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.
|
||||
- `http_client`: added convenient `get` and `custom` functions on `HTTP_CLIENT` directly.
|
||||
- `wsf`: added `WSF_EXECUTE_HANDLER`, and `WSF_CGI_HANDLER`. Demonstration of `WSF_CGI_HANDLER` in the new `tools/httpd` project.
|
||||
- `wsf_security`: new security library, providing support for XSS injection protection and similar.
|
||||
- `wsf`: 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.
|
||||
Accept -1 as value of max_keep_alive_requests to have unlimited number of request in the same persistent connection.
|
||||
- `wsf_compression`: Introduced `WSF_COMPRESSION` and applied to `WSF_*_WITH_COMPRESSION` classes. Added `simple_compression` example.
|
||||
- `websocket`: added `on_timer` solution to allow the server to check for external events and send notification to websocket clients.
|
||||
- `wsf`: Added routing condition mapping.
|
||||
- `wsf_extension`: 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).
|
||||
- `wsf_security`: added security protections such as XSS injection protection support.
|
||||
|
||||
### Changed
|
||||
- adopted ecf version 1-16-0 and use a single .ecf file (the -safe.ecf are now redirection to normal .ecf)
|
||||
- `wsf_html`: Made interface of wsf forms and widgets a bit more flexible by accepting `READABLE_STRING_GENERAL`.
|
||||
|
||||
### Deprecated
|
||||
- removed support for Eiffel version before 17.05 .
|
||||
- SSL 2 or 3 is obsolete and will raise an exception if used.
|
||||
@@ -21,8 +62,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
||||
### 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.
|
||||
- `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`.
|
||||
- `notification_email`: fixed the SMTP support for multiple recipients address.
|
||||
- `http_network`: use proper ciphers settings for libcurl implementation.
|
||||
- `http_client`: Improved support of absolute/relative https:// and http:// in `http_client`.
|
||||
- `json_encoder.e`: Properly JSON encode null character as \u0000 .
|
||||
### Security
|
||||
|
||||
|
||||
|
||||
12
README.md
@@ -13,14 +13,14 @@ Currently EWF offers a collection of Eiffel libraries designed to be integrated
|
||||
|
||||
There is a growing ecosystem around EWF, that provides useful components:
|
||||
* OpenID and OAuth consumer library
|
||||
* Various hypermedia format such as HAL, Collection+json, …
|
||||
* Various hypermedia format such as HAL, Collection+json, ...
|
||||
* Websocket server and client
|
||||
* Template engine
|
||||
* API Auto-documentation with swagger
|
||||
* A simple experimental CMS.
|
||||
* ...
|
||||
|
||||
So if you want to build a website, a web api, RESTful service, …or even if you want to consume other web api, EWF is a solution.
|
||||
So if you want to build a website, a web api, RESTful service, ... or even if you want to consume other web api, EWF is a solution.
|
||||
|
||||
EWF brings with it all the advantages of the Eiffel technology and tools with its powerful features such as Design by Contract, debugging, testing tools which enable to build efficient systems expected to be repeatedly refined, extended, and improved in a predictable and controllable way so as to become with time bugfree systems. Enjoy the full power of debugging your web server application from the IDE.
|
||||
|
||||
@@ -51,11 +51,11 @@ Tasks and issues are managed with github issue system
|
||||
## How to get the source code?
|
||||
|
||||
Using git
|
||||
* git clone https://github.com/EiffelWebFramework/EWF.git
|
||||
* `git clone https://github.com/EiffelWebFramework/EWF.git`
|
||||
|
||||
* And to build the required and related Clibs
|
||||
* cd contrib/ise_library/cURL
|
||||
* geant compile
|
||||
* `cd contrib/ise_library/cURL`
|
||||
* `geant compile`
|
||||
|
||||
## Libraries under 'library'
|
||||
|
||||
@@ -64,7 +64,7 @@ Using git
|
||||
* connectors: various web server connectors for EWSGI
|
||||
* libfcgi: Wrapper for libfcgi SDK
|
||||
* __wsf__: Web Server Framework [read more](library/server/wsf)
|
||||
* __router__: URL dispatching/routing based on uri, uri_template, or custom [read more](library/server/wsf/router)
|
||||
* __router__: URL dispatching/routing based on `uri`, `uri_template`, or custom [read more](library/server/wsf/router)
|
||||
|
||||
### protocol
|
||||
* __http__: HTTP related classes, constants for status code, content types, ... [read more](library/network/protocol/http)
|
||||
|
||||
@@ -14,3 +14,9 @@ port: 9000
|
||||
repo: https://github.com/EiffelWebFramework/EWF
|
||||
version: v1
|
||||
download: https://github.com/EiffelWebFramework/EWF/archive/v1.zip
|
||||
|
||||
plugins:
|
||||
- jekyll-relative-links
|
||||
relative_links:
|
||||
enabled: true
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 9.3 KiB |
@@ -94,11 +94,12 @@ The **WSF_RESPONSE** provides features to define the response with information s
|
||||
|
||||
**APPLICATION** is the root class of our example, it launches the application, using the corresponding connector, Which connector? this depends how do you want to run it `cgi`, `fcgi`, `standalone`. For development is recommended to use a standalone web server written in Eiffel, and run the execution within the EiffelStudio debugger. For production fcgi (or cgi) using Apache or another popular web server.
|
||||
|
||||

|
||||

|
||||
|
||||
**WS_LAUNCHABLE_SERVICE** inherit from **WS_SERVICE** class, which is a marker interface in EWF. And also provides a way to launch our application using different kind of connectors. The class **WSF_DEFAULT_SERVICE_I**, inherit from **WS_LAUNCHABLE_SERVICE** and has a formal generic that should conform to **WSF_SERVICE_LAUNCHER [WSF_EXECUTION]**. Below a [BON diagram](http://www.bon-method.com/index_normal.htm) showing one of the possible options.
|
||||
|
||||

|
||||

|
||||
|
||||
Other connectors:
|
||||
|
||||
**WSF_STANDALONE_SERVICE_LAUNCHER**
|
||||
@@ -111,7 +112,7 @@ The **APPLICATION_EXECUTION** class inherits from **WSF_EXECUTION** interface,
|
||||
|
||||
In the **APPLICATION_EXECUTION** class class you will need to implement the **execute** feature, get data from the request *req* and write the response in *res*.
|
||||
|
||||

|
||||

|
||||
|
||||
The WSF_EXECUTION instance, in this case ```APPLICATION_EXECUTION``` is created per request, with two main attributes request: ```WSF_REQUEST``` and response: ```WSF_RESPONSE```.
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
BIN
docs/workbook/basics/images/APPLICATION_EXECUTION.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
docs/workbook/basics/images/Launcher_Hierarchy.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/workbook/basics/images/WSF_SERVICE_LAUNCHER_CGI.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
docs/workbook/basics/images/WSF_SERVICE_LAUNCHER_FCGI.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
docs/workbook/basics/images/WSF_SERVICE_LAUNCHER_STANDALONE.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
50
examples/docker/Dockerfile
Normal file
@@ -0,0 +1,50 @@
|
||||
FROM debian
|
||||
#ubuntu:xenial
|
||||
MAINTAINER Jocelyn Fiat <jfiat@eiffel.com>
|
||||
LABEL description="EiffelWeb debug example hosted using apache2+libfcgi"
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install \
|
||||
curl bzip2 make gcc git-core \
|
||||
apache2 libapache2-mod-fcgid libfcgi-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
#RUN apt-get update && apt-get -y install tmux git-all vim && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
RUN a2enmod rewrite suexec include fcgid
|
||||
|
||||
RUN export uid=1000 gid=1000 && \
|
||||
mkdir -p /home/eifweb && \
|
||||
echo "eifweb:x:${uid}:${gid}:eifweb,,,:/home/eifweb:/bin/bash" >> /etc/passwd && \
|
||||
echo "eifweb:x:${uid}:" >> /etc/group && \
|
||||
chown ${uid}:${gid} -R /home/eifweb
|
||||
|
||||
USER eifweb
|
||||
ENV HOME /home/eifweb
|
||||
ENV WEBDIR /home/eifweb/www
|
||||
WORKDIR $HOME
|
||||
|
||||
# Create expected folders
|
||||
RUN mkdir $WEBDIR
|
||||
|
||||
#Build the debug EiffelWeb example and copy the executable to $HOME/
|
||||
COPY files/build_service_fcgi $HOME/build_service_fcgi
|
||||
USER root
|
||||
RUN chown eifweb:eifweb $HOME/build_service_fcgi && chmod 700 $HOME/build_service_fcgi
|
||||
USER eifweb
|
||||
RUN $HOME/build_service_fcgi $HOME/www
|
||||
|
||||
USER root
|
||||
COPY ./files/httpd.conf /etc/apache2/sites-enabled/000-default.conf
|
||||
COPY ./files/html/.htaccess $WEBDIR/html/.htaccess
|
||||
COPY ./files/html/index.html $WEBDIR/html/index.html
|
||||
RUN echo > $WEBDIR/html/service.ews
|
||||
RUN chown www-data:www-data -R $WEBDIR
|
||||
|
||||
#Setup apache as foreground (for docker purpose)
|
||||
RUN mkdir -p /etc/service/apache
|
||||
ADD ./files/apache.sh /etc/service/apache/run
|
||||
RUN chmod +x /etc/service/apache/run
|
||||
ENTRYPOINT ["/etc/service/apache/run"]
|
||||
25
examples/docker/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
Docker container for EiffelWeb debug example with apache2+libfcgi.
|
||||
==================================================================
|
||||
|
||||
This example demonstrates the use of EiffelWeb with Apache2, using the libfcgi connector.
|
||||
For that, it is using the Docker solution to show the compilation and configuration steps.
|
||||
|
||||
To build the docker image:
|
||||
|
||||
```
|
||||
docker build -t local/ewf-debug-httpd .
|
||||
```
|
||||
|
||||
To run the docker image in a self-destroyed container:
|
||||
```
|
||||
docker run --rm -dit -p 8080:80 --name my-ewf-debug local/ewf-debug-httpd
|
||||
```
|
||||
|
||||
Notes:
|
||||
- `--rm` : to remove the container after the execution
|
||||
- `-p 8080:80` : to map internal listening port 80 to localhost port 8080.
|
||||
- on Linux, you may need to use `sudo` to be able to use `docker`
|
||||
- depending on the docker installation, you may need to add an extra `-e ISE_PLATFORM=linux-x86` to force execution in 32bits.
|
||||
- This docker example is simple on purpose. For production it should be improved and optimized to keep the image smaller and more customizable.
|
||||
- For more advanced Docker usage, please refer to official https://www.docker.com/ website.
|
||||
|
||||
3
examples/docker/files/apache.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec /usr/sbin/apache2ctl -D FOREGROUND
|
||||
25
examples/docker/files/build_service_fcgi
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
web_dir=$1
|
||||
|
||||
mkdir $web_dir/bin
|
||||
mkdir $web_dir/html
|
||||
|
||||
# Install latest EiffelStudio suite.
|
||||
curl -sSL https://www.eiffel.org/setup/install.sh > install_eiffel.sh \
|
||||
&& bash ./install_eiffel.sh latest > $HOME/eiffel.rc
|
||||
|
||||
# Setup Eiffel environment
|
||||
source $HOME/eiffel.rc
|
||||
|
||||
# Get source code
|
||||
git clone https://github.com/EiffelWebFramework/EWF.git $web_dir/src
|
||||
|
||||
# Build executable
|
||||
eiffel build -v --target debug_libfcgi $web_dir/src/examples/debug/debug.ecf $web_dir/html/service.fcgi
|
||||
|
||||
# Clean files
|
||||
rm -rf $ISE_EIFFEL
|
||||
rm -rf $web_dir/src
|
||||
|
||||
exit $?
|
||||
20
examples/docker/files/html/.htaccess
Normal file
@@ -0,0 +1,20 @@
|
||||
<IfModule mod_fcgid.c>
|
||||
AddHandler fcgid-script .ews
|
||||
FcgidWrapper /home/eifweb/www/html/service.fcgi .ews
|
||||
</IfModule>
|
||||
|
||||
Options +ExecCGI +Includes +FollowSymLinks
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
|
||||
#RewriteRule ^.*$ maintenance.html [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^$ service.ews/
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||||
RewriteRule ^(.*)$ service.ews/$1 [C]
|
||||
|
||||
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||||
</IfModule>
|
||||
19
examples/docker/files/html/index.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>EiffelWeb running on Apache2+libfcgi</title>
|
||||
</head>
|
||||
<body>
|
||||
EiffelWeb debug example hosted on apache2+libfcgi .
|
||||
<ul>
|
||||
<li>Send a GET request <a href="get?foo=bar">get?foo=bar</a></li>
|
||||
<li>Send a POST request <form action="post?foo=bar" method="POST">
|
||||
<input type="text" name="one" value="value_1"/>
|
||||
<input type="text" name="two" value="value_2"/>
|
||||
<input type="submit" value="Submit"/>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
53
examples/docker/files/httpd.conf
Normal file
@@ -0,0 +1,53 @@
|
||||
<IfModule mod_fcgid.c>
|
||||
FcgidIdleTimeout 60
|
||||
FcgidBusyScanInterval 120
|
||||
FcgidProcessLifeTime 1800
|
||||
#7200
|
||||
FcgidMaxProcesses 10
|
||||
FcgidMaxProcessesPerClass 100
|
||||
FcgidMinProcessesPerClass 100
|
||||
FcgidConnectTimeout 8
|
||||
FcgidIOTimeout 3000
|
||||
FcgidBusyTimeout 300
|
||||
FcgidPassHeader Authorization
|
||||
FcgidMaxRequestLen 10000000
|
||||
FcgidMaxRequestsPerProcess 100
|
||||
</IfModule>
|
||||
|
||||
<VirtualHost *:80>
|
||||
# The ServerName directive sets the request scheme, hostname and port that
|
||||
# the server uses to identify itself. This is used when creating
|
||||
# redirection URLs. In the context of virtual hosts, the ServerName
|
||||
# specifies what hostname must appear in the request's Host: header to
|
||||
# match this virtual host. For the default virtual host (this file) this
|
||||
# value is not decisive as it is used as a last resort host regardless.
|
||||
# However, you must set it for any further virtual host explicitly.
|
||||
#ServerName www.example.com
|
||||
|
||||
ServerAdmin webmaster@localhost
|
||||
DocumentRoot /home/eifweb/www/html
|
||||
|
||||
<Directory /home/eifweb/www/html>
|
||||
Options Indexes FollowSymLinks MultiViews
|
||||
AllowOverride All
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
|
||||
# error, crit, alert, emerg.
|
||||
# It is also possible to configure the loglevel for particular
|
||||
# modules, e.g.
|
||||
#LogLevel info ssl:warn
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||
|
||||
# For most configuration files from conf-available/, which are
|
||||
# enabled or disabled at a global level, it is possible to
|
||||
# include a line for only one particular virtual host. For example the
|
||||
# following line enables the CGI configuration for this host only
|
||||
# after it has been globally disabled with "a2disconf".
|
||||
#Include conf-available/serve-cgi-bin.conf
|
||||
|
||||
|
||||
</VirtualHost>
|
||||
@@ -26,22 +26,27 @@ feature -- Basic operations
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
if request.path_info.same_string_general ("/app") then
|
||||
s := websocket_app_html (9090)
|
||||
if request.path_info.same_string_general ("/favicon.ico") then
|
||||
response.put_header ({HTTP_STATUS_CODE}.not_found, <<["Content-Length", "0"]>>)
|
||||
else
|
||||
s := "Hello World!"
|
||||
create dt.make_now_utc
|
||||
s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||||
s.append ("<p><a href=%"/app%">Websocket demo</a></p>")
|
||||
if request.path_info.same_string_general ("/app") then
|
||||
s := websocket_app_html (request.server_name, request.server_port)
|
||||
|
||||
else
|
||||
s := "Hello World!"
|
||||
create dt.make_now_utc
|
||||
s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||||
s.append ("<p><a href=%"/app%">Websocket demo</a></p>")
|
||||
end
|
||||
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
||||
response.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
response.header.put_content_type_text_html
|
||||
response.header.put_content_length (s.count)
|
||||
if request.is_keep_alive_http_connection then
|
||||
response.header.put_connection_keep_alive
|
||||
end
|
||||
response.put_string (s)
|
||||
end
|
||||
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
||||
response.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
response.header.put_content_type_text_html
|
||||
response.header.put_content_length (s.count)
|
||||
if request.is_keep_alive_http_connection then
|
||||
response.header.put_connection_keep_alive
|
||||
end
|
||||
response.put_string (s)
|
||||
end
|
||||
|
||||
feature -- Websocket execution
|
||||
@@ -184,7 +189,7 @@ feature -- Command
|
||||
|
||||
feature -- HTML Resource
|
||||
|
||||
websocket_app_html (a_port: INTEGER): STRING
|
||||
websocket_app_html (a_host: STRING; a_port: INTEGER): STRING
|
||||
do
|
||||
Result := "[
|
||||
<!DOCTYPE html>
|
||||
@@ -198,7 +203,7 @@ $(document).ready(function() {
|
||||
|
||||
function connect(){
|
||||
|
||||
var host = "##WSSCHEME##://127.0.0.1:##PORTNUMBER##/app";
|
||||
var host = "##WSSCHEME##://##HOSTNAME##:##PORTNUMBER##/app";
|
||||
|
||||
try{
|
||||
|
||||
@@ -280,6 +285,7 @@ body {font-family:Arial, Helvetica, sans-serif;}
|
||||
</body>
|
||||
</html>
|
||||
]"
|
||||
Result.replace_substring_all ("##HOSTNAME##", a_host)
|
||||
Result.replace_substring_all ("##PORTNUMBER##", a_port.out)
|
||||
if request.is_https then
|
||||
Result.replace_substring_all ("##HTTPSCHEME##", "https")
|
||||
|
||||
@@ -4,10 +4,10 @@ port=9090
|
||||
max_concurrent_connections=100
|
||||
max_tcp_clients=100
|
||||
socket_timeout=30
|
||||
socket_recv_timeout=5
|
||||
socket_recv_timeout=100 ms
|
||||
|
||||
#Persistent connections
|
||||
keep_alive_timeout=2
|
||||
keep_alive_timeout=50 ms
|
||||
max_keep_alive_requests=-1
|
||||
|
||||
#SSL
|
||||
|
||||
@@ -151,14 +151,14 @@ feature -- Settings
|
||||
|
||||
timeout: INTEGER
|
||||
-- HTTP transaction timeout in seconds.
|
||||
--| 0 means it nevers timeout
|
||||
--| 0 means it never timeouts
|
||||
do
|
||||
Result := session.timeout
|
||||
end
|
||||
|
||||
connect_timeout: INTEGER
|
||||
-- HTTP connection timeout in seconds.
|
||||
--| 0 means it nevers timeout
|
||||
-- HTTP connection timeout in milliseconds.
|
||||
--| 0 means it never timeouts
|
||||
do
|
||||
Result := session.connect_timeout
|
||||
end
|
||||
@@ -208,7 +208,7 @@ feature -- Settings
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -31,9 +31,12 @@ feature {NONE} -- Initialization
|
||||
|
||||
apply_workaround
|
||||
-- Due to issue with Eiffel cURL on Windows 32bits
|
||||
-- we need to do the following workaround
|
||||
-- we need to do the following workaround.
|
||||
local
|
||||
add: detachable INET_ADDRESS
|
||||
once
|
||||
if attached (create {INET_ADDRESS_FACTORY}).create_localhost then
|
||||
if {PLATFORM}.is_windows then
|
||||
add := (create {INET_ADDRESS_FACTORY}).create_localhost
|
||||
end
|
||||
end
|
||||
|
||||
@@ -449,7 +452,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -64,7 +64,7 @@ feature {NONE} -- Internal
|
||||
create Result.make_client_by_port (a_port, a_host)
|
||||
end
|
||||
Result.set_connect_timeout (connect_timeout)
|
||||
Result.set_timeout (timeout)
|
||||
Result.set_timeout_ns (Result.seconds_to_nanoseconds (timeout))
|
||||
Result.connect
|
||||
end
|
||||
end
|
||||
@@ -887,7 +887,7 @@ feature {NONE} -- Helpers
|
||||
|
||||
invariant
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -27,10 +27,22 @@
|
||||
<custom name="ssl_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="ssl_network" location="$|ssl\" recursive="true">
|
||||
<cluster name="ssl_network" location="$|ssl\" recursive="false">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
</condition>
|
||||
<file_rule>
|
||||
<exclude>/http_stream_secure_socket_ext\.e$</exclude>
|
||||
<condition>
|
||||
<version type="compiler" max="18.05.0.0"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
<cluster name="ssl_network_until_18_05" location="$|ssl\until_18.05" recursive="true">
|
||||
<condition>
|
||||
<custom name="ssl_enabled" value="true"/>
|
||||
<version type="compiler" max="18.05.0.0"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
</target>
|
||||
|
||||
@@ -2,22 +2,9 @@ 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
|
||||
Result := a_ssl.write (a_pointer, a_byte_count)
|
||||
if a_ssl.was_error then
|
||||
if Result >= 0 then
|
||||
Result := -1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
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
|
||||
Result := a_ssl.write (a_pointer, a_byte_count)
|
||||
if a_ssl.was_error then
|
||||
if Result >= 0 then
|
||||
Result := -1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -197,6 +197,24 @@ feature -- Change Element
|
||||
expiration_set: attached expiration as l_expiration and then l_expiration.same_string (date_to_rfc1123_http_date_format (a_date))
|
||||
end
|
||||
|
||||
set_expiration_from_max_age
|
||||
-- Set `expiration` value from `max_age`.
|
||||
local
|
||||
dt: DATE_TIME
|
||||
do
|
||||
if max_age < 0 then
|
||||
unset_expiration
|
||||
else
|
||||
if max_age = 0 then
|
||||
create dt.make_from_epoch (0)
|
||||
else
|
||||
create dt.make_now_utc
|
||||
dt.second_add (max_age)
|
||||
end
|
||||
set_expiration_date (dt)
|
||||
end
|
||||
end
|
||||
|
||||
set_path (a_path: READABLE_STRING_8)
|
||||
-- Set `path' to `a_path'.
|
||||
do
|
||||
|
||||
@@ -228,7 +228,9 @@ feature -- Header: merging
|
||||
if line [line.count] = '%R' then
|
||||
line.remove_tail (1)
|
||||
end
|
||||
put_header (line)
|
||||
if not line.is_empty then
|
||||
put_header (line)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -213,6 +213,7 @@ feature -- Content related header
|
||||
--| note: see `put_content_type_with_charset' for examples.
|
||||
local
|
||||
s: STRING_8
|
||||
v: READABLE_STRING_8
|
||||
do
|
||||
if a_params /= Void and then not a_params.is_empty then
|
||||
create s.make_from_string (a_content_type)
|
||||
@@ -224,9 +225,14 @@ feature -- Content related header
|
||||
s.append_character (' ')
|
||||
s.append (nv.name)
|
||||
s.append_character ('=')
|
||||
s.append_character ('%"')
|
||||
s.append (nv.value)
|
||||
s.append_character ('%"')
|
||||
v := nv.value
|
||||
if v.has(' ') or v.has ('%T') or v.has ('=') then
|
||||
s.append_character ('%"')
|
||||
s.append (v)
|
||||
s.append_character ('%"')
|
||||
else
|
||||
s.append (v)
|
||||
end
|
||||
end
|
||||
end
|
||||
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s)
|
||||
|
||||
@@ -95,40 +95,67 @@ feature -- Basic operation
|
||||
l_email: EMAIL
|
||||
h: STRING
|
||||
i: INTEGER
|
||||
lst: LIST [READABLE_STRING_8]
|
||||
do
|
||||
create l_email.make_with_entry (a_email.from_address, addresses_to_header_line_value (a_email.to_addresses))
|
||||
if attached a_email.reply_to_address as l_reply_to then
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_reply_to, l_reply_to)
|
||||
end
|
||||
|
||||
if attached a_email.cc_addresses as lst then
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_cc, addresses_to_header_line_value (lst))
|
||||
end
|
||||
if attached a_email.bcc_addresses as lst then
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_bcc, addresses_to_header_line_value (lst))
|
||||
end
|
||||
l_email.set_message (a_email.content)
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, a_email.subject)
|
||||
|
||||
create h.make_empty
|
||||
;(create {HTTP_DATE}.make_from_date_time (a_email.date)).append_to_rfc1123_string (h)
|
||||
l_email.add_header_entry ("Date", h)
|
||||
|
||||
if attached a_email.additional_header_lines as lst then
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
h := ic.item
|
||||
i := h.index_of (':', 1)
|
||||
if i > 0 then
|
||||
l_email.add_header_entry (h.head (i - 1), h.substring (i + 1, h.count))
|
||||
else
|
||||
check is_header_line: False end
|
||||
lst := a_email.to_addresses
|
||||
if lst.is_empty then
|
||||
-- Error ...
|
||||
else
|
||||
-- With EMAIL, there should be a unique recipient at creation.
|
||||
create l_email.make_with_entry (a_email.from_address, lst.first)
|
||||
if lst.count > 1 then
|
||||
from
|
||||
lst.start
|
||||
lst.forth
|
||||
until
|
||||
lst.off
|
||||
loop
|
||||
l_email.add_recipient_address (lst.item)
|
||||
lst.forth
|
||||
end
|
||||
end
|
||||
end
|
||||
if attached a_email.reply_to_address as l_reply_to then
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_reply_to, l_reply_to)
|
||||
end
|
||||
|
||||
smtp_send_email (l_email)
|
||||
if attached a_email.cc_addresses as l_cc_addresses then
|
||||
across
|
||||
l_cc_addresses as ic
|
||||
loop
|
||||
l_email.add_recipient_address_in_cc (ic.item)
|
||||
end
|
||||
end
|
||||
if attached a_email.bcc_addresses as l_bcc_addresses then
|
||||
across
|
||||
l_bcc_addresses as ic
|
||||
loop
|
||||
l_email.add_recipient_address_in_bcc (ic.item)
|
||||
end
|
||||
end
|
||||
l_email.set_message (a_email.content)
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, a_email.subject)
|
||||
|
||||
create h.make_empty
|
||||
;(create {HTTP_DATE}.make_from_date_time (a_email.date)).append_to_rfc1123_string (h)
|
||||
l_email.add_header_entry ("Date", h)
|
||||
|
||||
if attached a_email.additional_header_lines as l_lines then
|
||||
across
|
||||
l_lines as ic
|
||||
loop
|
||||
h := ic.item
|
||||
i := h.index_of (':', 1)
|
||||
if i > 0 then
|
||||
l_email.add_header_entry (h.head (i - 1), h.substring (i + 1, h.count))
|
||||
else
|
||||
check is_header_line: False end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
smtp_send_email (l_email)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
@@ -182,4 +209,3 @@ note
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
|
||||
|
||||
@@ -64,14 +64,16 @@ feature -- Conversion
|
||||
|
||||
encoded_string (a_secret: READABLE_STRING_8): STRING
|
||||
local
|
||||
alg, sign: READABLE_STRING_8
|
||||
sign, alg_name: READABLE_STRING_8
|
||||
alg: JWT_ALG
|
||||
l_enc_payload, l_enc_header: READABLE_STRING_8
|
||||
do
|
||||
reset_error
|
||||
alg := header.algorithm
|
||||
if not is_supporting_signature_algorithm (alg) then
|
||||
report_unsupported_alg_error (alg)
|
||||
alg := alg_hs256 -- Default ...
|
||||
alg_name := header.algorithm
|
||||
alg := algorithms [alg_name]
|
||||
if alg = Void then
|
||||
report_unsupported_alg_error (alg_name)
|
||||
alg := algorithms.hs256 -- Default ...
|
||||
end
|
||||
l_enc_header := base64url_encode (header.string)
|
||||
l_enc_payload := base64url_encode (claimset.string)
|
||||
@@ -94,12 +96,12 @@ feature -- Element change
|
||||
|
||||
set_algorithm_to_hs256
|
||||
do
|
||||
set_algorithm (alg_hs256)
|
||||
set_algorithm (algorithms.hs256.name)
|
||||
end
|
||||
|
||||
set_algorithm_to_none
|
||||
do
|
||||
set_algorithm (alg_none)
|
||||
set_algorithm (algorithms.none.name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -16,12 +16,15 @@ feature {NONE} -- Initialization
|
||||
|
||||
default_create
|
||||
do
|
||||
create algorithms
|
||||
create header
|
||||
create claimset
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
algorithms: JWT_ALGORITHMS
|
||||
|
||||
header: JWT_HEADER
|
||||
|
||||
claimset: JWT_CLAIMSET
|
||||
@@ -91,6 +94,11 @@ feature -- status report
|
||||
Result := attached errors as errs and then across errs as ic some attached {JWT_UNSUPPORTED_ALG_ERROR} ic.item end
|
||||
end
|
||||
|
||||
has_mismatched_alg_error: BOOLEAN
|
||||
do
|
||||
Result := attached errors as errs and then across errs as ic some attached {JWT_MISMATCHED_ALG_ERROR} ic.item end
|
||||
end
|
||||
|
||||
has_unverified_token_error: BOOLEAN
|
||||
do
|
||||
Result := attached errors as errs and then across errs as ic some attached {JWT_UNVERIFIED_TOKEN_ERROR} ic.item end
|
||||
|
||||
27
library/security/jwt/src/jwt_alg.e
Normal file
@@ -0,0 +1,27 @@
|
||||
note
|
||||
description: "Summary description for {JWT_ALG}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
JWT_ALG
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: READABLE_STRING_8
|
||||
deferred
|
||||
end
|
||||
|
||||
encoded_string (a_message: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_none: BOOLEAN
|
||||
-- Is Current algorithm is "none" ?
|
||||
do
|
||||
end
|
||||
|
||||
end
|
||||
57
library/security/jwt/src/jwt_alg_hs256.e
Normal file
@@ -0,0 +1,57 @@
|
||||
note
|
||||
description: "Summary description for {JWT_ALG_HS256}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
JWT_ALG_HS256
|
||||
|
||||
inherit
|
||||
JWT_ALG
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "hs256"
|
||||
|
||||
encoded_string (a_message: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING
|
||||
do
|
||||
Result := base64_hmacsha256 (a_message, a_secret)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
|
||||
local
|
||||
hs256: HMAC_SHA256
|
||||
do
|
||||
create hs256.make_ascii_key (a_secret)
|
||||
hs256.update_from_string (s)
|
||||
-- if Version >= EiffelStudio 18.01 then
|
||||
-- Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
|
||||
-- else
|
||||
Result := base64_bytes_encoded_string (hs256.digest)
|
||||
-- end
|
||||
end
|
||||
|
||||
base64_bytes_encoded_string (a_bytes: SPECIAL [NATURAL_8]): STRING_8
|
||||
-- Base64 string from `a_bytes`.
|
||||
--| Note: to be removed when 18.01 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
|
||||
|
||||
end
|
||||
30
library/security/jwt/src/jwt_alg_none.e
Normal file
@@ -0,0 +1,30 @@
|
||||
note
|
||||
description: "Summary description for {JWT_ALG_NONE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
JWT_ALG_NONE
|
||||
|
||||
inherit
|
||||
JWT_ALG
|
||||
redefine
|
||||
is_none
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "none"
|
||||
|
||||
encoded_string (a_message: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING
|
||||
do
|
||||
create Result.make_empty
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_none: BOOLEAN = True
|
||||
-- Is Current algorithm is "none" ?
|
||||
|
||||
end
|
||||
98
library/security/jwt/src/jwt_algorithms.e
Normal file
@@ -0,0 +1,98 @@
|
||||
note
|
||||
description: "Summary description for {JWT_ALGORITHMS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
JWT_ALGORITHMS
|
||||
|
||||
inherit
|
||||
ANY
|
||||
redefine
|
||||
default_create
|
||||
end
|
||||
|
||||
create
|
||||
default_create
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
default_create
|
||||
do
|
||||
create items.make_caseless (2)
|
||||
register_algorithm (hs256)
|
||||
register_algorithm (none)
|
||||
-- TODO: check if this is acceptable default.
|
||||
set_default_algorithm ({JWT_ALG_HS256}.name)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
hs256: JWT_ALG_HS256
|
||||
do
|
||||
create Result
|
||||
end
|
||||
|
||||
none: JWT_ALG_NONE
|
||||
do
|
||||
create Result
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
default_algorithm: JWT_ALG
|
||||
do
|
||||
if attached internal_default_alg_name as l_alg_name then
|
||||
Result := algorithm (l_alg_name)
|
||||
end
|
||||
if Result = Void then
|
||||
Result := none
|
||||
end
|
||||
end
|
||||
|
||||
algorithm alias "[]" (a_name: READABLE_STRING_GENERAL): detachable JWT_ALG
|
||||
do
|
||||
Result := items [a_name]
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
register_algorithm (alg: attached like algorithm)
|
||||
do
|
||||
items [alg.name] := alg
|
||||
end
|
||||
|
||||
unregister_algorithm (a_alg_name: READABLE_STRING_GENERAL)
|
||||
do
|
||||
items.remove (a_alg_name)
|
||||
end
|
||||
|
||||
set_default_algorithm (a_alg_name: detachable READABLE_STRING_GENERAL)
|
||||
do
|
||||
if
|
||||
a_alg_name = Void or else
|
||||
not is_supported_algorithm (a_alg_name)
|
||||
then
|
||||
internal_default_alg_name := Void
|
||||
else
|
||||
internal_default_alg_name := a_alg_name
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_supported_algorithm (a_name: READABLE_STRING_GENERAL): BOOLEAN
|
||||
do
|
||||
Result := items.has (a_name)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
items: STRING_TABLE [attached like algorithm]
|
||||
|
||||
internal_default_alg_name: detachable READABLE_STRING_GENERAL
|
||||
|
||||
invariant
|
||||
|
||||
end
|
||||
@@ -1,275 +0,0 @@
|
||||
note
|
||||
description: "JSON Web Token encoder"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
JWT_ENCODER
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
encoded_values (a_values: STRING_TABLE [READABLE_STRING_GENERAL]; a_secret: READABLE_STRING_8; a_algo: READABLE_STRING_8): STRING
|
||||
local
|
||||
j: JSON_OBJECT
|
||||
do
|
||||
create j.make_with_capacity (a_values.count)
|
||||
across
|
||||
a_values as ic
|
||||
loop
|
||||
j.put_string (ic.item, ic.key)
|
||||
end
|
||||
Result := encoded_json (j, a_secret, a_algo)
|
||||
end
|
||||
|
||||
encoded_json (a_json: JSON_OBJECT; a_secret: READABLE_STRING_8; a_algo: READABLE_STRING_8): STRING
|
||||
local
|
||||
vis: JSON_PRETTY_STRING_VISITOR
|
||||
s: STRING
|
||||
do
|
||||
create s.make_empty
|
||||
create vis.make (s)
|
||||
vis.visit_json_object (a_json)
|
||||
Result := encoded_string (s, a_secret, a_algo)
|
||||
end
|
||||
|
||||
encoded_string (a_payload: READABLE_STRING_8; a_secret: READABLE_STRING_8; a_algo: READABLE_STRING_8): STRING
|
||||
local
|
||||
alg, sign: STRING_8
|
||||
l_enc_payload, l_enc_header: READABLE_STRING_8
|
||||
do
|
||||
reset_error
|
||||
if a_algo.is_case_insensitive_equal_general (alg_hs256) then
|
||||
alg := alg_hs256
|
||||
elseif a_algo.is_case_insensitive_equal_general (alg_none) then
|
||||
alg := alg_none
|
||||
else
|
||||
report_unsupported_alg_error (a_algo)
|
||||
alg := alg_hs256 -- Default ...
|
||||
end
|
||||
l_enc_header := base64url_encode (header ("JWT", alg))
|
||||
l_enc_payload := base64url_encode (a_payload)
|
||||
sign := signature (l_enc_header, l_enc_payload, a_secret, alg)
|
||||
create Result.make (l_enc_header.count + 1 + l_enc_payload.count + 1 + sign.count)
|
||||
Result.append (l_enc_header)
|
||||
Result.append_character ('.')
|
||||
Result.append (l_enc_payload)
|
||||
Result.append_character ('.')
|
||||
Result.append (sign)
|
||||
end
|
||||
|
||||
decoded_string (a_token: READABLE_STRING_8; a_secret: READABLE_STRING_8; a_algo: detachable READABLE_STRING_8): detachable STRING
|
||||
local
|
||||
i,j,n: INTEGER
|
||||
alg, l_enc_payload, l_enc_header, l_signature: READABLE_STRING_8
|
||||
do
|
||||
reset_error
|
||||
n := a_token.count
|
||||
i := a_token.index_of ('.', 1)
|
||||
if i > 0 then
|
||||
j := a_token.index_of ('.', i + 1)
|
||||
if j > 0 then
|
||||
l_enc_header := a_token.substring (1, i - 1)
|
||||
l_enc_payload := a_token.substring (i + 1, j - 1)
|
||||
l_signature := a_token.substring (j + 1, n)
|
||||
Result := base64url_decode (l_enc_payload)
|
||||
alg := a_algo
|
||||
if alg = Void then
|
||||
alg := signature_algorithm_from_encoded_header (l_enc_header)
|
||||
if alg = Void then
|
||||
-- Use default
|
||||
alg := alg_hs256
|
||||
end
|
||||
end
|
||||
check alg_set: alg /= Void end
|
||||
if alg.is_case_insensitive_equal (alg_hs256) then
|
||||
alg := alg_hs256
|
||||
elseif alg.is_case_insensitive_equal (alg_none) then
|
||||
alg := alg_none
|
||||
else
|
||||
alg := alg_hs256
|
||||
report_unsupported_alg_error (alg)
|
||||
end
|
||||
|
||||
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_secret, alg)) then
|
||||
report_unverified_token_error
|
||||
end
|
||||
else
|
||||
report_invalid_token
|
||||
end
|
||||
else
|
||||
report_invalid_token
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Error status
|
||||
|
||||
error_code: INTEGER
|
||||
-- Last error, if any.
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Last `encoded_string` reported an error?
|
||||
do
|
||||
Result := error_code /= 0
|
||||
end
|
||||
|
||||
has_unsupported_alg_error: BOOLEAN
|
||||
do
|
||||
Result := error_code = unsupported_alg_error
|
||||
end
|
||||
|
||||
has_unverified_token_error: BOOLEAN
|
||||
do
|
||||
Result := error_code = unverified_token_error
|
||||
end
|
||||
|
||||
has_invalid_token_error: BOOLEAN
|
||||
do
|
||||
Result := error_code = invalid_token_error
|
||||
end
|
||||
|
||||
feature {NONE} -- Error reporting
|
||||
|
||||
reset_error
|
||||
do
|
||||
error_code := 0
|
||||
end
|
||||
|
||||
report_unsupported_alg_error (alg: READABLE_STRING_8)
|
||||
do
|
||||
error_code := unsupported_alg_error
|
||||
end
|
||||
|
||||
report_unverified_token_error
|
||||
do
|
||||
error_code := unverified_token_error
|
||||
end
|
||||
|
||||
report_invalid_token
|
||||
do
|
||||
error_code := invalid_token_error
|
||||
end
|
||||
|
||||
feature {NONE} -- Constants
|
||||
|
||||
unsupported_alg_error: INTEGER = -2
|
||||
|
||||
unverified_token_error: INTEGER = -4
|
||||
|
||||
invalid_token_error: INTEGER = -8
|
||||
|
||||
alg_hs256: STRING = "HS256"
|
||||
-- HMAC SHA256.
|
||||
|
||||
alg_none: STRING = "none"
|
||||
-- for unsecured token.
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
header (a_type: detachable READABLE_STRING_8; alg: READABLE_STRING_8): STRING
|
||||
do
|
||||
create Result.make_empty
|
||||
Result.append ("{%"typ%":%"")
|
||||
if a_type /= Void then
|
||||
Result.append (a_type)
|
||||
else
|
||||
Result.append ("JWT")
|
||||
end
|
||||
Result.append ("%",%"alg%":%"")
|
||||
Result.append (alg)
|
||||
Result.append ("%"}")
|
||||
end
|
||||
|
||||
feature {NONE} -- Conversion
|
||||
|
||||
signature_algorithm_from_encoded_header (a_enc_header: READABLE_STRING_8): detachable STRING_8
|
||||
local
|
||||
jp: JSON_PARSER
|
||||
do
|
||||
create jp.make_with_string (base64url_decode (a_enc_header))
|
||||
jp.parse_content
|
||||
if
|
||||
attached jp.parsed_json_object as jo and then
|
||||
attached {JSON_STRING} jo.item ("alg") as j_alg
|
||||
then
|
||||
Result := j_alg.unescaped_string_8
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Base64
|
||||
|
||||
base64url_encode (s: READABLE_STRING_8): STRING_8
|
||||
local
|
||||
urlencoder: URL_ENCODER
|
||||
base64: BASE64
|
||||
do
|
||||
create urlencoder
|
||||
create base64
|
||||
Result := urlsafe_encode (base64.encoded_string (s))
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
signature (a_enc_header, a_enc_payload: READABLE_STRING_8; a_secret: READABLE_STRING_8; alg: READABLE_STRING_8): STRING_8
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
if alg = alg_none then
|
||||
create Result.make_empty
|
||||
else
|
||||
create s.make (a_enc_header.count + 1 + a_enc_payload.count)
|
||||
s.append (a_enc_header)
|
||||
s.append_character ('.')
|
||||
s.append (a_enc_payload)
|
||||
if alg = alg_hs256 then
|
||||
Result := base64_hmacsha256 (s, a_secret)
|
||||
else
|
||||
Result := base64_hmacsha256 (s, a_secret)
|
||||
end
|
||||
Result := urlsafe_encode (Result)
|
||||
end
|
||||
end
|
||||
|
||||
base64url_decode (s: READABLE_STRING_8): STRING_8
|
||||
local
|
||||
urlencoder: URL_ENCODER
|
||||
base64: BASE64
|
||||
do
|
||||
create urlencoder
|
||||
create base64
|
||||
Result := base64.decoded_string (urlsafe_decode (s))
|
||||
end
|
||||
|
||||
urlsafe_encode (s: READABLE_STRING_8): STRING_8
|
||||
do
|
||||
create Result.make_from_string (s)
|
||||
Result.replace_substring_all ("=", "")
|
||||
Result.replace_substring_all ("+", "-")
|
||||
Result.replace_substring_all ("/", "_")
|
||||
end
|
||||
|
||||
urlsafe_decode (s: READABLE_STRING_8): STRING_8
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
create Result.make_from_string (s)
|
||||
Result.replace_substring_all ("-", "+")
|
||||
Result.replace_substring_all ("_", "/")
|
||||
from
|
||||
i := Result.count \\ 4
|
||||
until
|
||||
i = 0
|
||||
loop
|
||||
i := i - 1
|
||||
Result.extend ('=')
|
||||
end
|
||||
end
|
||||
|
||||
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
|
||||
local
|
||||
ut: JWT_UTILITIES
|
||||
do
|
||||
create ut
|
||||
Result := ut.base64_hmacsha256 (s, a_secret)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -9,6 +9,20 @@ class
|
||||
|
||||
inherit
|
||||
JWT_UTILITIES
|
||||
redefine
|
||||
default_create
|
||||
end
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
default_create
|
||||
do
|
||||
create algorithms
|
||||
end
|
||||
|
||||
feature -- Settings
|
||||
|
||||
algorithms: JWT_ALGORITHMS
|
||||
|
||||
feature -- Access
|
||||
|
||||
@@ -18,11 +32,12 @@ feature -- Access
|
||||
-- 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)
|
||||
a_valid_alg: a_alg /= Void implies algorithms.is_supported_algorithm (a_alg)
|
||||
local
|
||||
jws: JWS
|
||||
i,j,n: INTEGER
|
||||
alg, l_enc_payload, l_enc_header, l_signature: READABLE_STRING_8
|
||||
alg_encoder: JWT_ALG
|
||||
do
|
||||
n := a_token_input.count
|
||||
i := a_token_input.index_of ('.', 1)
|
||||
@@ -43,17 +58,18 @@ feature -- Access
|
||||
else
|
||||
if alg = Void then
|
||||
-- Use default
|
||||
alg := alg_hs256
|
||||
alg := algorithms.default_algorithm.name
|
||||
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
|
||||
alg_encoder := algorithms [alg]
|
||||
if alg_encoder = Void then
|
||||
jws.report_unsupported_alg_error (alg)
|
||||
alg := alg_hs256
|
||||
alg_encoder := algorithms.default_algorithm
|
||||
end
|
||||
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_verification_key, alg)) then
|
||||
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_verification_key, alg_encoder)) then
|
||||
jws.report_unverified_token_error
|
||||
end
|
||||
if
|
||||
|
||||
@@ -7,14 +7,6 @@ note
|
||||
class
|
||||
JWT_UTILITIES
|
||||
|
||||
feature -- Constants
|
||||
|
||||
alg_hs256: STRING = "HS256"
|
||||
-- HMAC SHA256.
|
||||
|
||||
alg_none: STRING = "none"
|
||||
-- for unsecured token.
|
||||
|
||||
feature -- Encoding
|
||||
|
||||
base64url_encode (s: READABLE_STRING_8): STRING_8
|
||||
@@ -35,61 +27,21 @@ feature -- Encoding
|
||||
Result.replace_substring_all ("/", "_")
|
||||
end
|
||||
|
||||
signature (a_enc_header, a_enc_payload: READABLE_STRING_8; a_secret: READABLE_STRING_8; alg: READABLE_STRING_8): STRING_8
|
||||
signature (a_enc_header, a_enc_payload: READABLE_STRING_8; a_secret: READABLE_STRING_8; alg: JWT_ALG): STRING_8
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
if alg.is_case_insensitive_equal (alg_none) then
|
||||
if alg.is_none then
|
||||
create Result.make_empty
|
||||
else
|
||||
create s.make (a_enc_header.count + 1 + a_enc_payload.count)
|
||||
s.append (a_enc_header)
|
||||
s.append_character ('.')
|
||||
s.append (a_enc_payload)
|
||||
if alg.is_case_insensitive_equal (alg_hs256) then
|
||||
Result := base64_hmacsha256 (s, a_secret)
|
||||
else
|
||||
Result := base64_hmacsha256 (s, a_secret)
|
||||
end
|
||||
Result := urlsafe_encode (Result)
|
||||
Result := urlsafe_encode (alg.encoded_string (s, a_secret))
|
||||
end
|
||||
end
|
||||
|
||||
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
|
||||
local
|
||||
hs256: HMAC_SHA256
|
||||
do
|
||||
create hs256.make_ascii_key (a_secret)
|
||||
hs256.update_from_string (s)
|
||||
-- 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
|
||||
|
||||
base64url_decode (s: READABLE_STRING_8): STRING_8
|
||||
@@ -119,20 +71,4 @@ feature -- Decoding
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Signature
|
||||
|
||||
supported_signature_algorithms: LIST [READABLE_STRING_8]
|
||||
-- Supported signature algorithm `alg`?
|
||||
do
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (2)
|
||||
Result.extend (alg_hs256)
|
||||
Result.extend (alg_none)
|
||||
end
|
||||
|
||||
is_supporting_signature_algorithm (alg: READABLE_STRING_8): BOOLEAN
|
||||
-- Is supporting signature algorithm `alg`?
|
||||
do
|
||||
Result := across supported_signature_algorithms as ic some alg.is_case_insensitive_equal (ic.item) end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
22
library/security/jwt/testing/jwt_alg_test.e
Normal file
@@ -0,0 +1,22 @@
|
||||
note
|
||||
description: "Summary description for {JWT_ALG_TEST}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
JWT_ALG_TEST
|
||||
|
||||
inherit
|
||||
JWT_ALG
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "test"
|
||||
|
||||
encoded_string (a_message: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING
|
||||
do
|
||||
Result := "TEST<<"+ a_message + ">>"
|
||||
end
|
||||
|
||||
end
|
||||
@@ -16,6 +16,30 @@ inherit
|
||||
|
||||
feature -- Test
|
||||
|
||||
example
|
||||
local
|
||||
jwt: JWS
|
||||
l_loader: JWT_LOADER
|
||||
tok: STRING
|
||||
do
|
||||
create jwt.make_with_json_payload ("[
|
||||
{"iss":"joe", "exp":1200819380,"http://example.com/is_root":true}
|
||||
]")
|
||||
jwt.set_algorithm_to_hs256
|
||||
tok := jwt.encoded_string ("my-secret")
|
||||
|
||||
create l_loader
|
||||
if
|
||||
attached l_loader.token (tok, Void, "my-secret", Void) as l_tok and then
|
||||
not l_tok.has_error
|
||||
then
|
||||
print (l_tok.claimset.string)
|
||||
check verified: not l_tok.has_unverified_token_error end
|
||||
check no_error: not l_tok.has_error end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
test_jwt_io
|
||||
local
|
||||
jwt: JWS
|
||||
@@ -185,7 +209,8 @@ feature -- Test
|
||||
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 ("error", l_tok.has_error)
|
||||
assert ("has_mismatched_alg_error", l_tok.has_mismatched_alg_error)
|
||||
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
||||
end
|
||||
end
|
||||
@@ -205,15 +230,50 @@ feature -- Test
|
||||
tok := jwt.encoded_string ("secret")
|
||||
|
||||
if attached (create {JWT_LOADER}).token (tok, "none", "secret", Void) as l_tok then
|
||||
assert ("no error", not jwt.has_error)
|
||||
assert ("no error", not l_tok.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 ("no error", not l_tok.has_error)
|
||||
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
||||
end
|
||||
end
|
||||
|
||||
test_additional_alg
|
||||
local
|
||||
jwt: JWS
|
||||
payload: STRING
|
||||
tok: STRING
|
||||
l_loader: JWT_LOADER
|
||||
do
|
||||
payload := "[
|
||||
{"iss":"joe","exp":1300819380,"http://example.com/is_root":true}
|
||||
]"
|
||||
|
||||
create jwt.make_with_json_payload (payload)
|
||||
jwt.algorithms.register_algorithm (create {JWT_ALG_TEST})
|
||||
jwt.set_algorithm ({JWT_ALG_TEST}.name)
|
||||
tok := jwt.encoded_string ("secret")
|
||||
|
||||
create l_loader
|
||||
l_loader.algorithms.register_algorithm (create {JWT_ALG_TEST})
|
||||
if attached l_loader.token (tok, "test", "secret", Void) as l_tok then
|
||||
assert ("no error", not l_tok.has_error)
|
||||
assert ("not has_unsupported_alg_error", not l_tok.has_unsupported_alg_error)
|
||||
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
||||
end
|
||||
if attached l_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
|
||||
|
||||
create l_loader
|
||||
if attached l_loader.token (tok, "test", "secret", Void) as l_tok then
|
||||
assert ("has error", l_tok.has_error)
|
||||
assert ("has_unsupported_alg_error", l_tok.has_unsupported_alg_error)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Implementation
|
||||
|
||||
duplicated_time (dt: DATE_TIME): DATE_TIME
|
||||
|
||||
@@ -38,11 +38,11 @@ feature -- Execution
|
||||
exec.execute
|
||||
res.push
|
||||
exec.clean
|
||||
elseif exec /= Void then
|
||||
exec.execute_rescue ((create {EXCEPTION_MANAGER}).last_exception)
|
||||
exec.clean
|
||||
else
|
||||
process_rescue (res)
|
||||
if exec /= Void then
|
||||
exec.clean
|
||||
end
|
||||
(create {WGI_RESCUE_EXECUTION}).execute (req, res, (create {EXCEPTION_MANAGER}).last_exception)
|
||||
end
|
||||
rescue
|
||||
if not rescued then
|
||||
@@ -51,24 +51,6 @@ feature -- Execution
|
||||
end
|
||||
end
|
||||
|
||||
process_rescue (res: detachable WGI_RESPONSE)
|
||||
-- Handle rescued execution of current request.
|
||||
do
|
||||
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.trace as l_trace then
|
||||
if res /= Void then
|
||||
if not res.status_is_set then
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void)
|
||||
end
|
||||
if res.message_writable then
|
||||
res.put_string ("<pre>")
|
||||
res.put_string (html_encoder.encoded_string (l_trace))
|
||||
res.put_string ("</pre>")
|
||||
end
|
||||
res.push
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
@@ -73,11 +73,11 @@ feature -- Execution
|
||||
exec.execute
|
||||
res.push
|
||||
exec.clean
|
||||
elseif exec /= Void then
|
||||
exec.execute_rescue ((create {EXCEPTION_MANAGER}).last_exception)
|
||||
exec.clean
|
||||
else
|
||||
process_rescue (res)
|
||||
if exec /= Void then
|
||||
exec.clean
|
||||
end
|
||||
(create {WGI_RESCUE_EXECUTION}).execute (req, res, (create {EXCEPTION_MANAGER}).last_exception)
|
||||
end
|
||||
rescue
|
||||
if not rescued then
|
||||
@@ -86,24 +86,6 @@ feature -- Execution
|
||||
end
|
||||
end
|
||||
|
||||
process_rescue (res: detachable WGI_RESPONSE)
|
||||
-- Handle rescued execution of current request.
|
||||
do
|
||||
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.trace as l_trace then
|
||||
if res /= Void then
|
||||
if not res.status_is_set then
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void)
|
||||
end
|
||||
if res.message_writable then
|
||||
res.put_string ("<pre>")
|
||||
res.put_string (html_encoder.encoded_string (l_trace))
|
||||
res.put_string ("</pre>")
|
||||
end
|
||||
res.push
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Input/Output
|
||||
|
||||
input: WGI_LIBFCGI_INPUT_STREAM
|
||||
|
||||
@@ -88,13 +88,15 @@ feature -- Request processing
|
||||
exec.execute
|
||||
res.push
|
||||
exec.clean
|
||||
else
|
||||
elseif exec /= Void then
|
||||
if not has_error then
|
||||
process_rescue (res)
|
||||
end
|
||||
if exec /= Void then
|
||||
exec.clean
|
||||
exec.execute_rescue ((create {EXCEPTION_MANAGER}).last_exception)
|
||||
end
|
||||
exec.clean
|
||||
elseif not has_error then
|
||||
(create {WGI_RESCUE_EXECUTION}).execute (req, res, (create {EXCEPTION_MANAGER}).last_exception)
|
||||
else
|
||||
-- Bad error.
|
||||
end
|
||||
rescue
|
||||
if l_output = Void or else not l_output.is_available then
|
||||
@@ -106,31 +108,6 @@ feature -- Request processing
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
process_rescue (res: detachable WGI_RESPONSE)
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.trace as l_trace then
|
||||
if res /= Void then
|
||||
if not res.status_is_set then
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void)
|
||||
end
|
||||
create s.make_empty
|
||||
s.append ("<pre>")
|
||||
s.append (html_encoder.encoded_string (l_trace))
|
||||
s.append ("</pre>")
|
||||
if not res.header_committed then
|
||||
res.put_header_text ("Content-Type: text/html%R%NContent-Length: " + s.count.out + "%R%N%R%N")
|
||||
end
|
||||
if res.message_writable then
|
||||
res.put_string (s)
|
||||
end
|
||||
res.push
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
httpd_environment (a_socket: HTTPD_STREAM_SOCKET): STRING_TABLE [READABLE_STRING_8]
|
||||
local
|
||||
p: INTEGER
|
||||
|
||||
@@ -110,7 +110,9 @@ feature -- Header output operation
|
||||
if
|
||||
not l_connection.is_case_insensitive_equal_general ("close")
|
||||
then
|
||||
s.replace_substring ("Connection: close", i + 1, j - 1)
|
||||
if not l_connection.is_case_insensitive_equal_general ("upgrade") then
|
||||
s.replace_substring ("Connection: close", i + 1, j - 1)
|
||||
end
|
||||
end
|
||||
elseif not is_http_version_1_0 then
|
||||
-- HTTP/1.1: always return "close" since persistent connection is not supported.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="ewsgi_spec" uuid="AA193B9F-02FD-47B9-B60D-C42B9AB35E1C" library_target="ewsgi_spec">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-18-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-18-0 http://www.eiffel.com/developers/xml/configuration-1-18-0.xsd" name="ewsgi_spec" uuid="AA193B9F-02FD-47B9-B60D-C42B9AB35E1C" library_target="ewsgi_spec">
|
||||
<target name="ewsgi_spec">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -10,6 +10,8 @@
|
||||
<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="..\..\network\protocol\http\http.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<cluster name="specification" location="specification\" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -42,6 +42,14 @@ feature -- Execution
|
||||
is_valid_end_of_execution: is_valid_end_of_execution
|
||||
end
|
||||
|
||||
feature {WGI_EXPORTER, WGI_CONNECTOR} -- Execution: rescue
|
||||
|
||||
execute_rescue (e: detachable EXCEPTION)
|
||||
-- Execute on rescue.
|
||||
do
|
||||
(create {WGI_RESCUE_EXECUTION}).execute (request, response, e)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid_end_of_execution: BOOLEAN
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
note
|
||||
description: "Execution when an exception occurred."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WGI_RESCUE_EXECUTION
|
||||
|
||||
inherit
|
||||
WGI_EXPORTER
|
||||
|
||||
SHARED_HTML_ENCODER
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: detachable WGI_REQUEST; res: detachable WGI_RESPONSE; e: detachable EXCEPTION)
|
||||
-- Exception or internal error occurred, return the eventual trace
|
||||
-- as response.
|
||||
-- `req` and `res` may be available for processing.
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
if
|
||||
res /= Void and then
|
||||
e /= Void and then
|
||||
attached e.trace as l_trace
|
||||
then
|
||||
if not res.status_is_set then
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void)
|
||||
end
|
||||
create s.make_empty
|
||||
s.append ("<pre>")
|
||||
s.append (html_encoder.encoded_string (l_trace))
|
||||
s.append ("</pre>")
|
||||
if not res.header_committed then
|
||||
-- Overwrite any header previously set.
|
||||
res.put_header_text ("Content-Type: text/html%R%NContent-Length: " + s.count.out + "%R%N%R%N")
|
||||
end
|
||||
if res.message_writable then
|
||||
res.put_string (s)
|
||||
end
|
||||
res.push
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2018, 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
|
||||
@@ -11,6 +11,8 @@ inherit
|
||||
|
||||
HTTPD_CONSTANTS
|
||||
|
||||
SOCKET_TIMEOUT_UTILITIES
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
@@ -18,9 +20,9 @@ feature {NONE} -- Initialization
|
||||
http_server_port := default_http_server_port
|
||||
max_concurrent_connections := default_max_concurrent_connections
|
||||
max_tcp_clients := default_max_tcp_clients
|
||||
socket_timeout := default_socket_timeout
|
||||
socket_recv_timeout := default_socket_recv_timeout
|
||||
keep_alive_timeout := default_keep_alive_timeout
|
||||
socket_timeout_ns := seconds_to_nanoseconds (default_socket_timeout)
|
||||
socket_recv_timeout_ns := seconds_to_nanoseconds (default_socket_recv_timeout)
|
||||
keep_alive_timeout_ns := seconds_to_nanoseconds (default_keep_alive_timeout)
|
||||
max_keep_alive_requests := default_max_keep_alive_requests
|
||||
is_secure := False
|
||||
create secure_certificate.make_empty
|
||||
@@ -39,12 +41,12 @@ feature -- Access
|
||||
max_tcp_clients: INTEGER assign set_max_tcp_clients
|
||||
-- Listen on socket for at most `queue' connections.
|
||||
|
||||
socket_timeout: INTEGER assign set_socket_timeout
|
||||
socket_timeout_ns: NATURAL_64 assign set_socket_timeout_ns
|
||||
-- Amount of seconds that the server waits for receipts and transmissions during communications.
|
||||
-- note: with timeout of 0, socket can wait for ever.
|
||||
-- By default: 60 seconds, which is appropriate for most situations.
|
||||
|
||||
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
|
||||
socket_recv_timeout_ns: NATURAL_64 assign set_socket_recv_timeout_ns
|
||||
-- Amount of seconds that the server waits for receiving data during communications.
|
||||
-- note: with timeout of 0, socket can wait for ever.
|
||||
-- By default: 5 seconds.
|
||||
@@ -65,7 +67,7 @@ feature -- Access
|
||||
verbose_level: INTEGER assign set_verbose_level
|
||||
-- Verbosity of output.
|
||||
|
||||
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
|
||||
keep_alive_timeout_ns: NATURAL_64 assign set_keep_alive_timeout_ns
|
||||
-- Persistent connection timeout.
|
||||
-- Number of seconds the server waits after a request has been served before it closes the connection.
|
||||
-- Timeout unit in Seconds.
|
||||
@@ -86,9 +88,9 @@ feature -- Access
|
||||
do
|
||||
Result.is_verbose := is_verbose
|
||||
Result.verbose_level := verbose_level
|
||||
Result.timeout := socket_timeout
|
||||
Result.socket_recv_timeout := socket_recv_timeout
|
||||
Result.keep_alive_timeout := keep_alive_timeout
|
||||
Result.timeout_ns := socket_timeout_ns
|
||||
Result.socket_recv_timeout_ns := socket_recv_timeout_ns
|
||||
Result.keep_alive_timeout_ns := keep_alive_timeout_ns
|
||||
Result.max_keep_alive_requests := max_keep_alive_requests
|
||||
Result.is_secure := is_secure
|
||||
end
|
||||
@@ -166,28 +168,52 @@ feature -- Element change
|
||||
max_concurrent_connections_set : max_concurrent_connections = v
|
||||
end
|
||||
|
||||
set_socket_timeout (a_nb_seconds: like socket_timeout)
|
||||
set_socket_timeout_ns (a_nano_seconds: like socket_timeout_ns)
|
||||
-- Set `socket_timeout_ns' with `a_nano_seconds'.
|
||||
require
|
||||
is_valid_timeout_ns: is_valid_timeout_ns (a_nano_seconds)
|
||||
do
|
||||
socket_timeout_ns := a_nano_seconds
|
||||
ensure
|
||||
socket_timeout_ns_set: socket_timeout_ns = a_nano_seconds
|
||||
end
|
||||
|
||||
set_socket_recv_timeout_ns (a_nano_seconds: like socket_recv_timeout_ns)
|
||||
-- Set `socket_recv_timeout_ns' with `a_nano_seconds'.
|
||||
require
|
||||
is_valid_timeout_ns: is_valid_timeout_ns (a_nano_seconds)
|
||||
do
|
||||
socket_recv_timeout_ns := a_nano_seconds
|
||||
ensure
|
||||
socket_recv_timeout_ns_set: socket_recv_timeout_ns = a_nano_seconds
|
||||
end
|
||||
|
||||
set_keep_alive_timeout_ns (a_nano_seconds: like keep_alive_timeout_ns)
|
||||
-- Set `keep_alive_timeout_ns' with `a_nano_seconds'.
|
||||
require
|
||||
is_valid_timeout_ns: is_valid_timeout_ns (a_nano_seconds)
|
||||
do
|
||||
keep_alive_timeout_ns := a_nano_seconds
|
||||
ensure
|
||||
keep_alive_timeout_ns_set: keep_alive_timeout_ns = a_nano_seconds
|
||||
end
|
||||
|
||||
set_socket_timeout (a_nb_seconds: INTEGER)
|
||||
-- Set `socket_timeout' with `a_nb_seconds'.
|
||||
do
|
||||
socket_timeout := a_nb_seconds
|
||||
ensure
|
||||
socket_timeout_set: socket_timeout = a_nb_seconds
|
||||
set_socket_timeout_ns (seconds_to_nanoseconds (a_nb_seconds))
|
||||
end
|
||||
|
||||
set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
|
||||
set_socket_recv_timeout (a_nb_seconds: INTEGER)
|
||||
-- Set `socket_recv_timeout' with `a_nb_seconds'.
|
||||
do
|
||||
socket_recv_timeout := a_nb_seconds
|
||||
ensure
|
||||
socket_recv_timeout_set: socket_recv_timeout = a_nb_seconds
|
||||
set_socket_recv_timeout_ns (seconds_to_nanoseconds (a_nb_seconds))
|
||||
end
|
||||
|
||||
set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
|
||||
-- Set `keep_alive_timeout' with `a_seconds'.
|
||||
set_keep_alive_timeout (a_nb_seconds: INTEGER)
|
||||
-- Set `keep_alive_timeout' with `a_nb_seconds'.
|
||||
do
|
||||
keep_alive_timeout := a_seconds
|
||||
ensure
|
||||
keep_alive_timeout_set: keep_alive_timeout = a_seconds
|
||||
set_keep_alive_timeout_ns (seconds_to_nanoseconds (a_nb_seconds))
|
||||
end
|
||||
|
||||
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
|
||||
@@ -296,6 +322,8 @@ feature -- Element change
|
||||
|
||||
set_secure_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
|
||||
-- Set `secure_protocol' with `a_ssl_version'.
|
||||
local
|
||||
err: DEVELOPER_EXCEPTION
|
||||
do
|
||||
if a_ssl_version.is_case_insensitive_equal ("tls_1_2") then
|
||||
set_secure_protocol_to_tls_1_2
|
||||
@@ -307,7 +335,9 @@ feature -- Element change
|
||||
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
|
||||
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
|
||||
else -- Default
|
||||
set_secure_protocol_to_tls_1_2
|
||||
end
|
||||
@@ -343,7 +373,7 @@ feature -- SSL Helpers
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -9,6 +9,9 @@ note
|
||||
expanded class
|
||||
HTTPD_REQUEST_SETTINGS
|
||||
|
||||
inherit
|
||||
SOCKET_TIMEOUT_UTILITIES
|
||||
|
||||
feature -- Access
|
||||
|
||||
is_verbose: BOOLEAN assign set_is_verbose
|
||||
@@ -20,13 +23,13 @@ feature -- Access
|
||||
is_secure: BOOLEAN assign set_is_secure
|
||||
-- Is using secure connection? i.e SSL?
|
||||
|
||||
timeout: INTEGER assign set_timeout
|
||||
timeout_ns: NATURAL_64 assign set_timeout_ns
|
||||
-- Amount of seconds that the server waits for receipts and transmissions during communications.
|
||||
|
||||
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
|
||||
socket_recv_timeout_ns: NATURAL_64 assign set_socket_recv_timeout_ns
|
||||
-- Amount of seconds that the server waits for receiving data on socket during communications.
|
||||
|
||||
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
|
||||
keep_alive_timeout_ns: NATURAL_64 assign set_keep_alive_timeout_ns
|
||||
-- Keep-alive timeout, also known as persistent-connection timeout.
|
||||
-- Number of seconds the server waits after a request has been served before it closes the connection.
|
||||
-- Unit in Seconds.
|
||||
@@ -34,6 +37,29 @@ feature -- Access
|
||||
max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
|
||||
-- Maximum number of requests allowed per persistent connection.
|
||||
|
||||
feature -- Access: obsolete
|
||||
|
||||
timeout: INTEGER assign set_timeout
|
||||
obsolete
|
||||
"Use `timeout_ns` [2018-10-29]"
|
||||
do
|
||||
Result := nanoseconds_to_seconds (timeout_ns)
|
||||
end
|
||||
|
||||
socket_recv_timeout: INTEGER assign set_socket_recv_timeout
|
||||
obsolete
|
||||
"Use `socket_recv_timeout_ns` [2018-10-29]"
|
||||
do
|
||||
Result := nanoseconds_to_seconds (socket_recv_timeout_ns)
|
||||
end
|
||||
|
||||
keep_alive_timeout: INTEGER assign set_keep_alive_timeout
|
||||
obsolete
|
||||
"Use `keep_alive_timeout_ns` [2018-10-29]"
|
||||
do
|
||||
Result := nanoseconds_to_seconds (keep_alive_timeout_ns)
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
set_is_verbose (b: BOOLEAN)
|
||||
@@ -54,24 +80,48 @@ feature -- Change
|
||||
is_secure := b
|
||||
end
|
||||
|
||||
feature -- Timeout change
|
||||
|
||||
set_timeout_ns (a_timeout_in_nanoseconds: NATURAL_64)
|
||||
-- Set `timeout_ns' to `a_timeout_in_nanoseconds'.
|
||||
do
|
||||
timeout_ns := a_timeout_in_nanoseconds
|
||||
end
|
||||
|
||||
set_socket_recv_timeout_ns (a_timeout_in_nanoseconds: NATURAL_64)
|
||||
-- Set `socket_recv_timeout_ns' to `a_timeout_in_nanoseconds'.
|
||||
do
|
||||
socket_recv_timeout_ns := a_timeout_in_nanoseconds
|
||||
end
|
||||
|
||||
set_keep_alive_timeout_ns (a_timeout_in_nanoseconds: NATURAL_64)
|
||||
-- Set `keep_alive_timeout_ns' to `a_timeout_in_nanoseconds'.
|
||||
do
|
||||
keep_alive_timeout_ns := a_timeout_in_nanoseconds
|
||||
end
|
||||
|
||||
feature -- Timeout change (in seconds)
|
||||
|
||||
set_timeout (a_timeout_in_seconds: INTEGER)
|
||||
-- Set `timeout' to `a_timeout_in_seconds'.
|
||||
do
|
||||
timeout := a_timeout_in_seconds
|
||||
set_timeout_ns (seconds_to_nanoseconds (a_timeout_in_seconds))
|
||||
end
|
||||
|
||||
set_socket_recv_timeout (a_timeout_in_seconds: INTEGER)
|
||||
-- Set `socket_recv_timeout' to `a_timeout_in_seconds'.
|
||||
do
|
||||
socket_recv_timeout := a_timeout_in_seconds
|
||||
set_socket_recv_timeout_ns (seconds_to_nanoseconds (a_timeout_in_seconds))
|
||||
end
|
||||
|
||||
set_keep_alive_timeout (a_timeout_in_seconds: INTEGER)
|
||||
-- Set `keep_alive_timeout' to `a_timeout_in_seconds'.
|
||||
do
|
||||
keep_alive_timeout := a_timeout_in_seconds
|
||||
set_keep_alive_timeout_ns (seconds_to_nanoseconds (a_timeout_in_seconds))
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
|
||||
-- Set `max_keep_alive_requests' with `nb'
|
||||
do
|
||||
@@ -79,7 +129,7 @@ feature -- Change
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -19,9 +19,9 @@ feature {NONE} -- Initialization
|
||||
do
|
||||
reset
|
||||
-- Import global request settings.
|
||||
timeout := a_request_settings.timeout -- seconds
|
||||
socket_recv_timeout := a_request_settings.socket_recv_timeout -- seconds
|
||||
keep_alive_timeout := a_request_settings.keep_alive_timeout -- seconds
|
||||
timeout_ns := a_request_settings.timeout_ns -- nanoseconds
|
||||
socket_recv_timeout_ns := a_request_settings.socket_recv_timeout_ns -- nanoseconds
|
||||
keep_alive_timeout_ns := a_request_settings.keep_alive_timeout_ns -- nanoseconds
|
||||
max_keep_alive_requests := a_request_settings.max_keep_alive_requests
|
||||
|
||||
is_verbose := a_request_settings.is_verbose
|
||||
@@ -148,17 +148,17 @@ feature -- Settings
|
||||
-- Is next persistent connection supported?
|
||||
-- note: it is relevant only if `is_persistent_connection_supported' is True.
|
||||
|
||||
timeout: INTEGER -- seconds
|
||||
-- Amount of seconds that the server waits for receipts and transmissions during communications.
|
||||
timeout_ns: NATURAL_64 -- nanoseconds
|
||||
-- Amount of nanoseconds that the server waits for receipts and transmissions during communications.
|
||||
|
||||
socket_recv_timeout: INTEGER -- seconds
|
||||
-- Amount of seconds that the server waits for receiving data on socket during communications.
|
||||
socket_recv_timeout_ns: NATURAL_64 -- nanoseconds
|
||||
-- Amount of nanoseconds that the server waits for receiving data on socket during communications.
|
||||
|
||||
max_keep_alive_requests: INTEGER
|
||||
-- Maximum number of requests allowed per persistent connection.
|
||||
|
||||
keep_alive_timeout: INTEGER -- seconds
|
||||
-- Number of seconds for persistent connection timeout.
|
||||
keep_alive_timeout_ns: NATURAL_64 -- nanoseconds
|
||||
-- Number of nanoseconds for persistent connection timeout.
|
||||
|
||||
feature -- Status report
|
||||
|
||||
@@ -226,6 +226,10 @@ feature -- Execution
|
||||
do
|
||||
l_socket := client_socket
|
||||
|
||||
-- Set to expected `timeout_ns`.
|
||||
l_socket.set_timeout_ns (timeout_ns)
|
||||
l_socket.set_recv_timeout_ns (socket_recv_timeout_ns)
|
||||
|
||||
-- Compute remote info once for the persistent connection.
|
||||
create l_remote_info
|
||||
if attached l_socket.peer_address as l_addr then
|
||||
@@ -299,13 +303,13 @@ feature -- Execution
|
||||
end
|
||||
|
||||
-- Try to get request header.
|
||||
-- If the request is reusing persistent connection, use `keep_alive_timeout',
|
||||
-- otherwise `socket_recv_timeout'.
|
||||
-- If the request is reusing persistent connection, use `keep_alive_timeout_ns',
|
||||
-- otherwise `socket_recv_timeout_ns'.
|
||||
get_request_header (l_socket, a_is_reusing_connection)
|
||||
|
||||
if has_error then
|
||||
if a_is_reusing_connection and then request_header.is_empty then
|
||||
-- Close persistent connection, since no new connection occurred in the delay `keep_alive_timeout'.
|
||||
-- Close persistent connection, since no new connection occurred in the delay `keep_alive_timeout_ns'.
|
||||
debug ("dbglog")
|
||||
dbglog ("execute_request socket=" + l_socket.descriptor.out + "} close persistent connection.")
|
||||
end
|
||||
@@ -407,9 +411,9 @@ feature -- Parsing
|
||||
a_socket.readable
|
||||
then
|
||||
if a_is_reusing_connection then
|
||||
a_socket.set_recv_timeout (keep_alive_timeout) -- in seconds!
|
||||
a_socket.set_recv_timeout_ns (keep_alive_timeout_ns) -- in nanoseconds!
|
||||
else
|
||||
a_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response ..
|
||||
a_socket.set_recv_timeout_ns (socket_recv_timeout_ns) -- FIXME: return a 408 Request Timeout response ..
|
||||
end
|
||||
|
||||
if
|
||||
@@ -424,7 +428,7 @@ feature -- Parsing
|
||||
if not has_error then
|
||||
if a_is_reusing_connection then
|
||||
-- Restore normal recv timeout!
|
||||
a_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response ..
|
||||
a_socket.set_recv_timeout_ns (socket_recv_timeout_ns) -- FIXME: return a 408 Request Timeout response ..
|
||||
end
|
||||
from
|
||||
line := next_line (a_socket)
|
||||
@@ -646,7 +650,7 @@ invariant
|
||||
request_header_attached: request_header /= Void
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -109,9 +109,9 @@ feature -- Execution
|
||||
log (" - port = " + configuration.http_server_port.out)
|
||||
log (" - max_tcp_clients = " + configuration.max_tcp_clients.out)
|
||||
log (" - max_concurrent_connections = " + configuration.max_concurrent_connections.out)
|
||||
log (" - socket_timeout = " + configuration.socket_timeout.out + " seconds")
|
||||
log (" - socket_recv_timeout = " + configuration.socket_recv_timeout.out + " seconds")
|
||||
log (" - keep_alive_timeout = " + configuration.keep_alive_timeout.out + " seconds")
|
||||
log (" - socket_timeout = " + timeout_representation (configuration.socket_timeout_ns))
|
||||
log (" - socket_recv_timeout = " + timeout_representation (configuration.socket_recv_timeout_ns))
|
||||
log (" - keep_alive_timeout = " + timeout_representation (configuration.keep_alive_timeout_ns))
|
||||
log (" - max_keep_alive_requests = " + configuration.max_keep_alive_requests.out)
|
||||
if configuration.has_secure_support then
|
||||
if configuration.is_secure then
|
||||
@@ -366,8 +366,25 @@ feature -- Output
|
||||
end
|
||||
end
|
||||
|
||||
timeout_representation (a_ns: NATURAL_64): STRING
|
||||
do
|
||||
if 1_000 * (a_ns // 1_000) = a_ns then
|
||||
if 1_000_000 * (a_ns // 1_000_000) = a_ns then
|
||||
if 1_000_000_000 * (a_ns // 1_000_000_000) = a_ns then
|
||||
Result := (a_ns // 1_000_000_000).out + " seconds"
|
||||
else
|
||||
Result := (a_ns // 1_000_000).out + " milliseconds"
|
||||
end
|
||||
else
|
||||
Result := (a_ns // 1_000).out + " microseconds"
|
||||
end
|
||||
else
|
||||
Result := a_ns.out + " nanoseconds"
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -13,7 +13,8 @@ CL.exe /c %CL_FLAGS% /TC libfcgi\libfcgi\fcgi_stdio.c libfcgi\libfcgi\fcgiapp
|
||||
CL.exe /c %CL_FLAGS% /TP libfcgi\libfcgi\fcgio.cpp
|
||||
link.exe %LINK_FLAGS% /DLL %E_libFCGI_OUTDIR%\fcgi_stdio.obj %E_libFCGI_OUTDIR%\fcgiapp.obj %E_libFCGI_OUTDIR%\os_win32.obj %E_libFCGI_OUTDIR%\fcgio.obj
|
||||
|
||||
copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\windows\msc
|
||||
mkdir /S %~dp0..\spec\lib\win64\%ISE_C_COMPILER%
|
||||
copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\windows\%ISE_C_COMPILER%
|
||||
|
||||
endlocal
|
||||
exit 0
|
||||
rem exit 0
|
||||
|
||||
@@ -13,7 +13,8 @@ CL.exe /c %CL_FLAGS% /TC libfcgi\libfcgi\fcgi_stdio.c libfcgi\libfcgi\fcgiapp
|
||||
CL.exe /c %CL_FLAGS% /TP libfcgi\libfcgi\fcgio.cpp
|
||||
link.exe %LINK_FLAGS% /DLL %E_libFCGI_OUTDIR%\fcgi_stdio.obj %E_libFCGI_OUTDIR%\fcgiapp.obj %E_libFCGI_OUTDIR%\os_win32.obj %E_libFCGI_OUTDIR%\fcgio.obj
|
||||
|
||||
copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\win64\msc
|
||||
mkdir /S %~dp0..\spec\lib\win64\%ISE_C_COMPILER%
|
||||
copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\win64\%ISE_C_COMPILER%
|
||||
|
||||
endlocal
|
||||
exit 0
|
||||
rem exit 0
|
||||
|
||||
@@ -12,10 +12,10 @@ note
|
||||
max_concurrent_connections: set to 1, for single threaded behavior
|
||||
max_tcp_clients: max number of open tcp connection
|
||||
|
||||
socket_timeout: connection timeout
|
||||
socket_recv_timeout: read data timeout
|
||||
socket_timeout_ns: connection timeout in nanoseconds
|
||||
socket_recv_timeout_ns: read data timeout in nanoseconds
|
||||
|
||||
keep_alive_timeout: amount of time the server will wait for subsequent
|
||||
keep_alive_timeout_ns: amount of nanoseconds the server will wait for subsequent
|
||||
requests on a persistent connection,
|
||||
max_keep_alive_requests: number of requests allowed on a persistent connection,
|
||||
|
||||
@@ -40,6 +40,8 @@ inherit
|
||||
|
||||
WGI_STANDALONE_HTTPD_LOGGER_CONSTANTS
|
||||
|
||||
WSF_TIMEOUT_UTILITIES
|
||||
|
||||
create
|
||||
make,
|
||||
make_and_launch
|
||||
@@ -57,9 +59,9 @@ feature {NONE} -- Initialization
|
||||
port_number := {WGI_STANDALONE_CONSTANTS}.default_http_server_port --| Default, but quite often, this port is already used ...
|
||||
max_concurrent_connections := {WGI_STANDALONE_CONSTANTS}.default_max_concurrent_connections
|
||||
max_tcp_clients := {WGI_STANDALONE_CONSTANTS}.default_max_tcp_clients
|
||||
socket_timeout := {WGI_STANDALONE_CONSTANTS}.default_socket_timeout -- seconds
|
||||
socket_recv_timeout := {WGI_STANDALONE_CONSTANTS}.default_socket_recv_timeout -- seconds
|
||||
keep_alive_timeout := {WGI_STANDALONE_CONSTANTS}.default_keep_alive_timeout -- seconds.
|
||||
socket_timeout_ns := seconds_to_nanoseconds ({WGI_STANDALONE_CONSTANTS}.default_socket_timeout) -- default in seconds
|
||||
socket_recv_timeout_ns := seconds_to_nanoseconds ({WGI_STANDALONE_CONSTANTS}.default_socket_recv_timeout) -- default in seconds
|
||||
keep_alive_timeout_ns := seconds_to_nanoseconds ({WGI_STANDALONE_CONSTANTS}.default_keep_alive_timeout) -- default in seconds.
|
||||
max_keep_alive_requests := {WGI_STANDALONE_CONSTANTS}.default_max_keep_alive_requests
|
||||
verbose := False
|
||||
verbose_level := notice_level
|
||||
@@ -111,9 +113,9 @@ feature {NONE} -- Initialization
|
||||
end
|
||||
max_concurrent_connections := opts.option_integer_value ("max_concurrent_connections", max_concurrent_connections)
|
||||
max_tcp_clients := opts.option_integer_value ("max_tcp_clients", max_tcp_clients)
|
||||
socket_timeout := opts.option_integer_value ("socket_timeout", socket_timeout)
|
||||
socket_recv_timeout := opts.option_integer_value ("socket_recv_timeout", socket_recv_timeout)
|
||||
keep_alive_timeout := opts.option_integer_value ("keep_alive_timeout", keep_alive_timeout)
|
||||
socket_timeout_ns := opts.option_timeout_ns_value ("socket_timeout", socket_timeout_ns)
|
||||
socket_recv_timeout_ns := opts.option_timeout_ns_value ("socket_recv_timeout", socket_recv_timeout_ns)
|
||||
keep_alive_timeout_ns := opts.option_timeout_ns_value ("keep_alive_timeout", keep_alive_timeout_ns)
|
||||
max_keep_alive_requests := opts.option_integer_value ("max_keep_alive_requests", max_keep_alive_requests)
|
||||
|
||||
if
|
||||
@@ -169,9 +171,9 @@ feature -- Execution
|
||||
cfg.http_server_port := port_number
|
||||
cfg.set_max_concurrent_connections (max_concurrent_connections)
|
||||
cfg.set_max_tcp_clients (max_tcp_clients)
|
||||
cfg.set_socket_timeout (socket_timeout)
|
||||
cfg.set_socket_recv_timeout (socket_recv_timeout)
|
||||
cfg.set_keep_alive_timeout (keep_alive_timeout)
|
||||
cfg.set_socket_timeout_ns (socket_timeout_ns)
|
||||
cfg.set_socket_recv_timeout_ns (socket_recv_timeout_ns)
|
||||
cfg.set_keep_alive_timeout_ns (keep_alive_timeout_ns)
|
||||
cfg.set_max_keep_alive_requests (max_keep_alive_requests)
|
||||
end
|
||||
|
||||
@@ -244,10 +246,10 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
max_tcp_clients: INTEGER
|
||||
socket_timeout: INTEGER
|
||||
socket_recv_timeout: INTEGER
|
||||
socket_timeout_ns: NATURAL_64
|
||||
socket_recv_timeout_ns: NATURAL_64
|
||||
|
||||
keep_alive_timeout: INTEGER
|
||||
keep_alive_timeout_ns: NATURAL_64
|
||||
max_keep_alive_requests: INTEGER
|
||||
|
||||
is_secure_connection_supported: BOOLEAN
|
||||
|
||||
@@ -23,6 +23,8 @@ deferred class
|
||||
inherit
|
||||
WEB_SOCKET_CONSTANTS
|
||||
|
||||
WSF_TIMEOUT_UTILITIES
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature -- Web Socket Interface
|
||||
@@ -90,13 +92,18 @@ feature -- Websocket events
|
||||
|
||||
feature {WEB_SOCKET} -- Timeout.
|
||||
|
||||
timer_delay: INTEGER
|
||||
-- Maximal duration in seconds between two `on_timeout` event.
|
||||
timer_delay_ns: NATURAL_64
|
||||
-- Maximal duration in nanoseconds between two `on_timeout` event.
|
||||
-- Disable timeout event, by setting it to `0` (default).
|
||||
|
||||
set_timer_delay_ns (nb_nanosecs: NATURAL_64)
|
||||
do
|
||||
timer_delay_ns := nb_nanosecs
|
||||
end
|
||||
|
||||
set_timer_delay (nb_secs: INTEGER)
|
||||
do
|
||||
timer_delay := nb_secs
|
||||
timer_delay_ns := seconds_to_nanoseconds (nb_secs)
|
||||
end
|
||||
|
||||
on_timer (ws: WEB_SOCKET)
|
||||
@@ -143,7 +150,7 @@ feature -- Websocket events: implemented
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -42,7 +42,7 @@ feature {NONE} -- Initialization
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Access
|
||||
feature {WEB_SOCKET_EVENT_I} -- Access
|
||||
|
||||
request: WSF_REQUEST
|
||||
-- Associated request.
|
||||
@@ -209,29 +209,30 @@ feature {WEB_SOCKET_HANDLER} -- Networking
|
||||
|
||||
wait_for_input (cb: detachable WEB_SOCKET_EVENT_I)
|
||||
local
|
||||
l_timeout, nb: INTEGER
|
||||
l_cb_timeout: INTEGER
|
||||
nb,
|
||||
l_timeout_ns,
|
||||
l_cb_timeout_ns: NATURAL_64
|
||||
do
|
||||
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
|
||||
l_cb_timeout_ns := cb.timer_delay_ns
|
||||
l_timeout_ns := socket.timeout_ns
|
||||
if l_cb_timeout_ns = 0 then
|
||||
-- timeout event not enabled.
|
||||
has_input := socket.ready_for_reading
|
||||
else
|
||||
cb.on_timer (Current)
|
||||
if l_cb_timeout > l_timeout then
|
||||
if l_cb_timeout_ns > l_timeout_ns 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
|
||||
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.
|
||||
l_timeout_ns := socket.timeout_ns
|
||||
nb := l_timeout_ns
|
||||
socket.set_timeout_ns (l_cb_timeout_ns)
|
||||
until
|
||||
has_input or nb <= 0
|
||||
loop
|
||||
@@ -239,13 +240,13 @@ feature {WEB_SOCKET_HANDLER} -- Networking
|
||||
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)
|
||||
socket.set_timeout_ns (l_timeout_ns)
|
||||
cb.on_timer (Current)
|
||||
socket.set_timeout (l_cb_timeout)
|
||||
socket.set_timeout_ns (l_cb_timeout_ns)
|
||||
end
|
||||
nb := nb - l_cb_timeout
|
||||
nb := nb - l_cb_timeout_ns
|
||||
end
|
||||
socket.set_timeout (l_timeout)
|
||||
socket.set_timeout_ns (l_timeout_ns)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -772,7 +773,7 @@ feature {NONE} -- Debug
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
note
|
||||
description : "Objects that ..."
|
||||
author : "$Author$"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
description: "[
|
||||
Handler that can also play the role of a filter, i.e.
|
||||
than can pre-process incoming data and post-process outgoing data.
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
WSF_EXECUTE_FILTER_HANDLER
|
||||
@@ -22,7 +25,7 @@ feature -- Execution
|
||||
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
|
||||
|
||||
@@ -8,10 +8,19 @@ deferred class
|
||||
WSF_URI_FILTER_HANDLER
|
||||
|
||||
inherit
|
||||
WSF_EXECUTE_FILTER_HANDLER
|
||||
WSF_FILTER_HANDLER [WSF_URI_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-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)"
|
||||
|
||||
@@ -11,7 +11,9 @@ inherit
|
||||
WSF_EXECUTE_RESPONSE_AGENT_HANDLER
|
||||
|
||||
WSF_URI_RESPONSE_HANDLER
|
||||
|
||||
undefine
|
||||
execute
|
||||
end
|
||||
create
|
||||
make
|
||||
|
||||
|
||||
@@ -22,6 +22,14 @@ 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-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)"
|
||||
|
||||
@@ -8,10 +8,19 @@ deferred class
|
||||
WSF_URI_TEMPLATE_FILTER_HANDLER
|
||||
|
||||
inherit
|
||||
WSF_EXECUTE_FILTER_HANDLER
|
||||
WSF_FILTER_HANDLER [WSF_URI_TEMPLATE_HANDLER]
|
||||
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute_next (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
if attached next as n then
|
||||
n.execute (req, res)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
|
||||
@@ -11,6 +11,9 @@ inherit
|
||||
WSF_EXECUTE_RESPONSE_AGENT_HANDLER
|
||||
|
||||
WSF_URI_TEMPLATE_RESPONSE_HANDLER
|
||||
undefine
|
||||
execute
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
@@ -22,6 +22,14 @@ 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-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)"
|
||||
|
||||
36
library/server/wsf/security/filter/wsf_xss_filter.e
Normal file
@@ -0,0 +1,36 @@
|
||||
note
|
||||
description: "[
|
||||
{WSF_XSS_FILTER}.
|
||||
Simple anti cross-site scripting (XSS) filter.
|
||||
Remove all suspicious strings from request parameters (query strings and form) before returning them to the application
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WSF_XSS_FILTER
|
||||
|
||||
inherit
|
||||
|
||||
WSF_FILTER
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the filter.
|
||||
do
|
||||
execute_next (create {WSF_XSS_REQUEST}.make_from_request (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
|
||||
94
library/server/wsf/security/support/wsf_protection.e
Normal file
@@ -0,0 +1,94 @@
|
||||
note
|
||||
description: "[
|
||||
Security protection on values.
|
||||
|
||||
It could be to protect against XSS, SQL ... injections.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=OWASP", "src=https://www.owasp.org/", "protocol=uri"
|
||||
EIS: "name=OWASP XSS", "src=https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet", "protocol=uri"
|
||||
EIS: "name=Regular expression protection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
|
||||
|
||||
deferred class
|
||||
WSF_PROTECTION
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid: BOOLEAN
|
||||
-- Is valid protection?
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- String Protection
|
||||
|
||||
string_8 (s: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||
-- Safe string value from `s`.
|
||||
-- If a thread is detected, either return Void, or filter out the threat.
|
||||
require
|
||||
is_valid: is_valid
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Value Protection
|
||||
|
||||
string_value (v: WSF_STRING): detachable WSF_STRING
|
||||
-- Safe string value from `v`.
|
||||
-- If a thread is detected, either return Void, or filter out the threat.
|
||||
require
|
||||
is_valid: is_valid
|
||||
deferred
|
||||
end
|
||||
|
||||
value (v: WSF_VALUE): detachable WSF_VALUE
|
||||
-- Safe value from `v`.
|
||||
-- If a thread is detected, either return Void, or filter out the threat.
|
||||
require
|
||||
is_valid: is_valid
|
||||
do
|
||||
if attached {WSF_STRING} v as s then
|
||||
Result := string_value (s)
|
||||
elseif attached {WSF_MULTIPLE_STRING} v as ms then
|
||||
Result := multiple_string_value (ms)
|
||||
else
|
||||
-- TODO
|
||||
Result := v
|
||||
end
|
||||
end
|
||||
|
||||
multiple_string_value (mv: WSF_MULTIPLE_STRING): detachable WSF_MULTIPLE_STRING
|
||||
-- Safe multiple string value from `mv`.
|
||||
-- If a thread is detected in any of the item, either return Void, or filter out the threat.
|
||||
require
|
||||
is_valid: is_valid
|
||||
local
|
||||
v: detachable WSF_STRING
|
||||
do
|
||||
-- TODO: check if the whole structure should be Void
|
||||
-- when one item is filtered out, or if the structure could have
|
||||
-- holes.
|
||||
across
|
||||
mv as ic
|
||||
loop
|
||||
v := string_value (ic.item)
|
||||
if v = Void then
|
||||
Result := Void
|
||||
elseif Result = Void then
|
||||
create Result.make_with_value (v)
|
||||
else
|
||||
Result.add_value (v)
|
||||
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
|
||||
489
library/server/wsf/security/support/wsf_protection_policy.e
Normal file
@@ -0,0 +1,489 @@
|
||||
note
|
||||
description: "Return data for WSF_REQUEST query and form parameters using different types of protection policy"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WSF_PROTECTION_POLICY
|
||||
|
||||
-- TODO add header protection.
|
||||
|
||||
feature -- Query parameters
|
||||
|
||||
custom_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
|
||||
-- Filtered Query parameter name `a_name' with custom protections.
|
||||
do
|
||||
Result := custom_wsf_value (a_req.query_parameter (a_name), a_protections)
|
||||
end
|
||||
|
||||
predefined_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Query parameter name `a_name' with all predefined protections.
|
||||
-- check {WSF_PROTECTIONS} class.
|
||||
do
|
||||
Result := predefined_value (a_req.query_parameter (a_name))
|
||||
end
|
||||
|
||||
xss_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Query parameter name `a_name' with xss protection.
|
||||
do
|
||||
Result := xss_value (a_req.query_parameter (a_name))
|
||||
end
|
||||
|
||||
xss_js_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Query parameter name `a_name' with xss protection.
|
||||
do
|
||||
Result := xss_js_value (a_req.query_parameter (a_name))
|
||||
end
|
||||
|
||||
sql_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Query parameter name `a_name' with sql injection protection.
|
||||
do
|
||||
Result := sql_value (a_req.query_parameter (a_name))
|
||||
end
|
||||
|
||||
server_side_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Query parameter name `a_name' with server side injection protection.
|
||||
do
|
||||
Result := server_side_value (a_req.query_parameter (a_name))
|
||||
end
|
||||
|
||||
xpath_abbreviated_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Query parameter name `a_name' with XPath_abbreviated injection protection.
|
||||
do
|
||||
Result := xpath_abbreviated_value (a_req.query_parameter (a_name))
|
||||
end
|
||||
|
||||
xpath_expanded_query_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Query parameter name `a_name' with XPath expanded injection protection.
|
||||
do
|
||||
Result := xpath_expanded_value (a_req.query_parameter (a_name))
|
||||
end
|
||||
|
||||
feature -- Form Parameters
|
||||
|
||||
custom_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
|
||||
-- Filtered Form parameter name `a_name' with custom protections.
|
||||
do
|
||||
Result := custom_wsf_value (a_req.form_parameter (a_name), a_protections)
|
||||
end
|
||||
|
||||
predefined_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Form parameter name `a_name' with all predefined protections.
|
||||
-- check {WSF_PROTECTIONS} class.
|
||||
do
|
||||
Result := predefined_value (a_req.form_parameter (a_name))
|
||||
end
|
||||
|
||||
xss_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Form parameter name `a_name' with xss protection.
|
||||
do
|
||||
Result := xss_value (a_req.form_parameter (a_name))
|
||||
end
|
||||
|
||||
xss_js_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Form parameter name `a_name' with xss protection.
|
||||
do
|
||||
Result := xss_js_value (a_req.form_parameter (a_name))
|
||||
end
|
||||
|
||||
sql_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Form parameter name `a_name' with sql injection protection.
|
||||
do
|
||||
Result := sql_value (a_req.form_parameter (a_name))
|
||||
end
|
||||
|
||||
server_side_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Form parameter name `a_name' with server side injection protection.
|
||||
do
|
||||
Result := server_side_value (a_req.form_parameter (a_name))
|
||||
end
|
||||
|
||||
xpath_abbreviated_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Form parameter name `a_name' with server Xpath abbreviated injection protection.
|
||||
do
|
||||
Result := xpath_abbreviated_value (a_req.form_parameter (a_name))
|
||||
end
|
||||
|
||||
xpath_expanded_form_parameter (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered Form parameter name `a_name' with server Xpath expanded injection protection.
|
||||
do
|
||||
Result := xpath_expanded_value (a_req.form_parameter (a_name))
|
||||
end
|
||||
|
||||
feature -- Meta Variables
|
||||
|
||||
custom_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
|
||||
-- Filtered CGI Meta variable name `a_name' with custom protections.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached {WSF_STRING} custom_wsf_value (a_req.meta_variable (a_name), a_protections) as l_result then
|
||||
Result := l_result
|
||||
end
|
||||
end
|
||||
|
||||
predefined_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- Filtered CGI Meta variable name `a_name' with predefined protections.
|
||||
-- check {WSF_PROTECTIONS} class.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached {WSF_STRING} predefined_value (a_req.meta_variable (a_name)) as l_result then
|
||||
Result := l_result
|
||||
end
|
||||
end
|
||||
|
||||
xss_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
|
||||
-- Filtered CGI Meta variable name `a_name' with xss protection.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached {WSF_STRING} xss_value (a_req.meta_variable (a_name)) as l_result then
|
||||
Result := l_result
|
||||
end
|
||||
end
|
||||
|
||||
xss_js_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
|
||||
-- Filtered CGI Meta variable name `a_name' with xss protection.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached {WSF_STRING} xss_js_value (a_req.meta_variable (a_name)) as l_result then
|
||||
Result := l_result
|
||||
end
|
||||
end
|
||||
|
||||
sql_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
|
||||
-- Filtered CGI Meta variable name `a_name' with sql injection protection.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached {WSF_STRING} sql_value (a_req.meta_variable (a_name)) as l_result then
|
||||
Result := l_result
|
||||
end
|
||||
end
|
||||
|
||||
server_side_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
|
||||
-- Filtered CGI Meta variable name `a_name' with server side injection protection.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached {WSF_STRING} server_side_value (a_req.meta_variable (a_name)) as l_result then
|
||||
Result := l_result
|
||||
end
|
||||
end
|
||||
|
||||
xpath_abbreviated_side_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
|
||||
-- Filtered CGI Meta variable name `a_name' with Xpath abbreviated injection protection.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached {WSF_STRING} xpath_abbreviated_value (a_req.meta_variable (a_name)) as l_result then
|
||||
Result := l_result
|
||||
end
|
||||
end
|
||||
|
||||
xpath_expanded_side_meta_variable (a_req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
|
||||
-- Filtered CGI Meta variable name `a_name' with Xpath abbreviated injection protection.
|
||||
require
|
||||
a_name_valid: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached {WSF_STRING} xpath_expanded_value (a_req.meta_variable (a_name)) as l_result then
|
||||
Result := l_result
|
||||
end
|
||||
end
|
||||
feature -- HTTP_*
|
||||
|
||||
custom_http_accept (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_accept header with custom protections `a_protections`.
|
||||
-- Contents of the Accept: header from the current wgi_request, if there is one.
|
||||
-- Example: 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
|
||||
do
|
||||
Result := custom_string_value (a_req.http_accept, a_protections)
|
||||
end
|
||||
|
||||
custom_http_accept_charset (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_accept_charset header with custom protections `a_protections`.
|
||||
-- Contents of the Accept-Charset: header from the current wgi_request, if there is one.
|
||||
-- Example: 'iso-8859-1,*,utf-8'.
|
||||
|
||||
do
|
||||
Result := custom_string_value (a_req.http_accept_charset, a_protections)
|
||||
end
|
||||
|
||||
custom_http_accept_encoding (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_accept_encoding header with custom protections `a_protections`.
|
||||
-- Contents of the Accept-Encoding: header from the current wgi_request, if there is one.
|
||||
-- Example: 'gzip'.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_accept_encoding, a_protections)
|
||||
end
|
||||
|
||||
custom_http_accept_language (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_accept_language header with custom protections `a_protections`.
|
||||
-- Contents of the Accept-Language: header from the current wgi_request, if there is one.
|
||||
-- Example: 'en'.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_accept_language, a_protections)
|
||||
end
|
||||
|
||||
custom_http_connection (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_connection header with custom protections `a_protections`.
|
||||
-- Contents of the Connection: header from the current wgi_request, if there is one.
|
||||
-- Example: 'keep-alive'.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_connection, a_protections)
|
||||
end
|
||||
|
||||
custom_http_expect (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_expect header with custom protections `a_protections`.
|
||||
-- The Expect request-header field is used to indicate that particular server behaviors are required by the client.
|
||||
-- Example: '100-continue'.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_expect, a_protections)
|
||||
end
|
||||
|
||||
custom_http_host (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_host header with custom protections `a_protections`.
|
||||
-- Contents of the Host: header from the current wgi_request, if there is one.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_host, a_protections)
|
||||
end
|
||||
|
||||
custom_http_referer (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_referer header with custom protections `a_protections`.
|
||||
-- The address of the page (if any) which referred the user agent to the current page.
|
||||
-- This is set by the user agent.
|
||||
-- Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature.
|
||||
-- In short, it cannot really be trusted.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_referer, a_protections)
|
||||
end
|
||||
|
||||
custom_http_user_agent (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_user_agent header with custom protections `a_protections`.
|
||||
-- Contents of the User-Agent: header from the current wgi_request, if there is one.
|
||||
-- This is a string denoting the user agent being which is accessing the page.
|
||||
-- A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586).
|
||||
-- Among other things, you can use this value to tailor your page's
|
||||
-- output to the capabilities of the user agent.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_user_agent, a_protections)
|
||||
end
|
||||
|
||||
custom_http_authorization (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_authorization header with custom protections `a_protections`.
|
||||
-- Contents of the Authorization: header from the current wgi_request, if there is one.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_authorization, a_protections)
|
||||
end
|
||||
|
||||
custom_http_transfer_encoding (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_transfer_encoding header with custom protections `a_protections`.
|
||||
-- Transfer-Encoding
|
||||
-- for instance chunked.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_transfer_encoding, a_protections)
|
||||
end
|
||||
|
||||
custom_http_access_control_request_headers (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_access_control_request_headers header with custom protections `a_protections`.
|
||||
-- Indicates which headers will be used in the actual request
|
||||
-- as part of the preflight request
|
||||
do
|
||||
Result := custom_string_value (a_req.http_access_control_request_headers, a_protections)
|
||||
end
|
||||
|
||||
custom_http_if_match (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_if_match header with custom protections `a_protections`.
|
||||
-- Existence check on resource.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_if_match, a_protections)
|
||||
end
|
||||
|
||||
custom_http_if_modified_since (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_if_modified_since header with custom protections `a_protections`.
|
||||
-- Modification check on resource.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_if_modified_since, a_protections)
|
||||
end
|
||||
|
||||
custom_http_if_none_match (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_if_none_match header with custom protections `a_protections`.
|
||||
-- Existence check on resource.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_if_none_match, a_protections)
|
||||
end
|
||||
|
||||
custom_http_if_range (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_if_range header with custom protections `a_protections`.
|
||||
-- Existence check on resource.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_if_range, a_protections)
|
||||
end
|
||||
|
||||
custom_http_if_unmodified_since (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_if_unmodified_since header with custom protections `a_protections`.
|
||||
-- Modification check on resource.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_if_unmodified_since, a_protections)
|
||||
end
|
||||
|
||||
custom_http_last_modified (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_last_modified header with custom protections `a_protections`.
|
||||
-- Modification check on resource.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_last_modified, a_protections)
|
||||
end
|
||||
|
||||
custom_http_range (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_range header with custom protections `a_protections`.
|
||||
-- Requested byte-range of resource.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_range, a_protections)
|
||||
end
|
||||
|
||||
custom_http_content_range (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_content_range header with custom protections `a_protections`.
|
||||
-- Partial range of selected representation enclosed in message payload.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_content_range, a_protections)
|
||||
end
|
||||
|
||||
custom_http_content_encoding (a_req: WSF_REQUEST; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Filtered http_content_encoding header with custom protections `a_protections`.
|
||||
-- Encoding (usually compression) of message payload.
|
||||
do
|
||||
Result := custom_string_value (a_req.http_content_encoding, a_protections)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
custom_wsf_value (a_value: detachable WSF_VALUE; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
|
||||
-- Return value `a_value` filtered by all protections policy.
|
||||
do
|
||||
Result := filter_wsf_value (a_value, a_protections )
|
||||
end
|
||||
|
||||
custom_string_value (a_value: detachable READABLE_STRING_8; a_protections: ITERABLE [WSF_PROTECTION]): detachable READABLE_STRING_8
|
||||
-- Return value `a_value` filtered by all protections policy.
|
||||
do
|
||||
Result := filter_string_value (a_value, a_protections )
|
||||
end
|
||||
|
||||
predefined_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
|
||||
-- Return value `a_value` filtered by all predefined protections policy.
|
||||
local
|
||||
l_wsf_xss: WSF_PROTECTIONS
|
||||
do
|
||||
Result := filter_wsf_value (a_value,
|
||||
<<
|
||||
l_wsf_xss.XSS,
|
||||
l_wsf_xss.server_side,
|
||||
l_wsf_xss.sql_injection,
|
||||
l_wsf_xss.xpath_abbreviated,
|
||||
l_wsf_xss.xpath_expanded
|
||||
>>)
|
||||
end
|
||||
|
||||
xss_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
|
||||
-- Return value `a_value` filtered by xss protection.
|
||||
local
|
||||
l_wsf_xss: WSF_PROTECTIONS
|
||||
do
|
||||
Result := filter_wsf_value (a_value, <<l_wsf_xss.XSS>>)
|
||||
end
|
||||
|
||||
xss_js_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
|
||||
-- Return value `a_value` filtered by xss-javascript protection.
|
||||
local
|
||||
l_wsf_xss: WSF_PROTECTIONS
|
||||
do
|
||||
Result := filter_wsf_value (a_value, <<l_wsf_xss.XSS_javascript>>)
|
||||
end
|
||||
|
||||
sql_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
|
||||
-- Return value `a_value` filtered by sql injection protection.
|
||||
local
|
||||
l_wsf_xss: WSF_PROTECTIONS
|
||||
do
|
||||
Result := filter_wsf_value (a_value, <<l_wsf_xss.SQL_injection>>)
|
||||
end
|
||||
|
||||
server_side_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
|
||||
-- Return value `a_value` filtered by server side injection protection.
|
||||
local
|
||||
l_wsf_xss: WSF_PROTECTIONS
|
||||
do
|
||||
Result := filter_wsf_value (a_value, <<l_wsf_xss.Server_side>>)
|
||||
end
|
||||
|
||||
xpath_abbreviated_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
|
||||
-- Return value `a_value` filtered by xpath_abbreviated injection protection.
|
||||
local
|
||||
l_wsf_xss: WSF_PROTECTIONS
|
||||
do
|
||||
Result := filter_wsf_value (a_value, <<l_wsf_xss.Xpath_abbreviated>>)
|
||||
end
|
||||
|
||||
xpath_expanded_value (a_value: detachable WSF_VALUE): detachable WSF_VALUE
|
||||
-- Return value `a_value` filtered by Xpath expanded injection protection.
|
||||
local
|
||||
l_wsf_xss: WSF_PROTECTIONS
|
||||
do
|
||||
Result := filter_wsf_value (a_value, <<l_wsf_xss.Xpath_expanded>>)
|
||||
end
|
||||
|
||||
filter_wsf_value (a_value: detachable WSF_VALUE; a_protections: ITERABLE [WSF_PROTECTION]): detachable WSF_VALUE
|
||||
-- Filter value `a_value` with an array of protections policy `a_protections`.
|
||||
require
|
||||
a_protections_valid: across a_protections as ic all ic.item.is_valid end
|
||||
local
|
||||
prot: WSF_PROTECTION
|
||||
do
|
||||
if a_value /= Void then
|
||||
Result := a_value
|
||||
across
|
||||
a_protections as ic
|
||||
until
|
||||
Result = Void
|
||||
loop
|
||||
prot := ic.item
|
||||
check is_valid: prot.is_valid end
|
||||
Result := prot.value (Result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
filter_string_value (a_value: detachable READABLE_STRING_8; a_protections: ITERABLE [WSF_PROTECTION] ): detachable READABLE_STRING_8
|
||||
-- Filter value `a_value` with an array of protections policy `a_protections`.
|
||||
require
|
||||
all_protections_valid: across a_protections as ic all ic.item.is_valid end
|
||||
local
|
||||
prot: WSF_PROTECTION
|
||||
do
|
||||
if a_value /= Void then
|
||||
Result := a_value
|
||||
across
|
||||
a_protections as ic
|
||||
until
|
||||
Result = Void
|
||||
loop
|
||||
prot := ic.item
|
||||
check is_valid: prot.is_valid end
|
||||
Result := prot.string_8 (Result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2018, 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
|
||||
114
library/server/wsf/security/support/wsf_protection_regexp.e
Normal file
@@ -0,0 +1,114 @@
|
||||
note
|
||||
description: "Security protection based on Regular expression."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=Regular expression protection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
|
||||
|
||||
class
|
||||
WSF_PROTECTION_REGEXP
|
||||
|
||||
inherit
|
||||
WSF_PROTECTION
|
||||
|
||||
create
|
||||
make,
|
||||
make_caseless,
|
||||
make_with_regexp
|
||||
|
||||
convert
|
||||
make_with_regexp ({REGULAR_EXPRESSION})
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_regexp_pattern: READABLE_STRING_8; a_caseless: BOOLEAN)
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
do
|
||||
create r
|
||||
r.set_caseless (a_caseless)
|
||||
r.compile (a_regexp_pattern)
|
||||
make_with_regexp (r)
|
||||
end
|
||||
|
||||
make_caseless (a_regexp_pattern: READABLE_STRING_8)
|
||||
do
|
||||
make (a_regexp_pattern, True)
|
||||
end
|
||||
|
||||
make_with_regexp (a_regexp: REGULAR_EXPRESSION)
|
||||
do
|
||||
regexp := a_regexp
|
||||
end
|
||||
|
||||
|
||||
feature -- Access
|
||||
|
||||
regexp: REGULAR_EXPRESSION
|
||||
|
||||
feature -- String Protection
|
||||
|
||||
string_8 (s: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||
local
|
||||
reg: like regexp
|
||||
do
|
||||
reg := regexp
|
||||
reg.match (s)
|
||||
if reg.has_matched then
|
||||
Result := Void
|
||||
else
|
||||
Result := s
|
||||
end
|
||||
end
|
||||
|
||||
string_value (v: WSF_STRING): detachable WSF_STRING
|
||||
local
|
||||
vs: READABLE_STRING_8
|
||||
do
|
||||
vs := v.url_encoded_value
|
||||
if attached string_8 (vs) as s then
|
||||
if vs = s then
|
||||
Result := v
|
||||
else
|
||||
create Result.make (v.name, s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid: BOOLEAN
|
||||
-- <Precursor>
|
||||
-- i.e: if the association regular expression is successfully compiled.
|
||||
do
|
||||
Result := is_compiled
|
||||
end
|
||||
|
||||
is_compiled: BOOLEAN
|
||||
do
|
||||
Result := regexp.is_compiled
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
compiled_regexp (p: STRING; caseless: BOOLEAN): REGULAR_EXPRESSION
|
||||
require
|
||||
p /= Void
|
||||
do
|
||||
create Result
|
||||
Result.set_caseless (caseless)
|
||||
Result.compile (p)
|
||||
ensure
|
||||
is_compiled: Result.is_compiled
|
||||
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
|
||||
100
library/server/wsf/security/support/wsf_protections.e
Normal file
@@ -0,0 +1,100 @@
|
||||
note
|
||||
description: "[
|
||||
{WSF_PROTECTIONS}
|
||||
Provide application security parterns to assist in Cross Site Scripting
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=OWASP XSS", "src=https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet", "protocol=uri"
|
||||
EIS: "name=Regular expression protection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
|
||||
|
||||
expanded class
|
||||
WSF_PROTECTIONS
|
||||
|
||||
feature -- XSS patterns
|
||||
|
||||
XSS: WSF_PROTECTION_REGEXP
|
||||
note
|
||||
EIS: "name= XSS", "src=https://community.apigee.com/questions/27198/xss-threat-protection-patterns.html#answer-27465", "protocol=uri"
|
||||
once
|
||||
create Result.make_caseless ("((\%%3C)|<)[^\n]+((\%%3E)|>)")
|
||||
ensure
|
||||
is_compiled: Result.is_compiled
|
||||
end
|
||||
|
||||
XSS_javascript: WSF_PROTECTION_REGEXP
|
||||
note
|
||||
EIS: "name=JavaScript Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
|
||||
once
|
||||
Result := compiled_regexp ("<\s*script\b[^>]*>[^<]+<\s*/\s*script\s*>", True)
|
||||
ensure
|
||||
is_compiled: Result.is_compiled
|
||||
end
|
||||
|
||||
feature -- XPath injections Patterns
|
||||
|
||||
XPath_abbreviated: WSF_PROTECTION_REGEXP
|
||||
note
|
||||
EIS: "name=XPath Abbreviated Syntax Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
|
||||
once
|
||||
Result := compiled_regexp ("(/(@?[\w_?\w:\*]+(\[[^]]+\])*)?)+", True)
|
||||
ensure
|
||||
is_compiled: Result.is_compiled
|
||||
end
|
||||
|
||||
XPath_expanded: WSF_PROTECTION_REGEXP
|
||||
note
|
||||
EIS: "name=XPath Expanded Syntax Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
|
||||
once
|
||||
Result := compiled_regexp ("/?(ancestor(-or-self)?|descendant(-or-self)?|following(-sibling))", True)
|
||||
ensure
|
||||
is_compiled: Result.is_compiled
|
||||
end
|
||||
|
||||
feature -- Server side injection
|
||||
|
||||
Server_side: WSF_PROTECTION_REGEXP
|
||||
note
|
||||
EIS: "name=Server-Side Include Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
|
||||
once
|
||||
|
||||
Result := compiled_regexp ("<!--#(include|exec|echo|config|printenv)\s+.*", True)
|
||||
ensure
|
||||
is_compiled: Result.is_compiled
|
||||
end
|
||||
|
||||
feature -- SQL injection Patterns
|
||||
|
||||
SQL_injection: WSF_PROTECTION_REGEXP
|
||||
note
|
||||
EIS: "name= SQL Injection", "src=https://docs.apigee.com/api-services/reference/regular-expression-protection", "protocol=uri"
|
||||
once
|
||||
Result := compiled_regexp ("[\s]*((delete)|(exec)|(drop\s*table)|(insert)|(shutdown)|(update)|(\bor\b))", True)
|
||||
ensure
|
||||
is_compiled: Result.is_compiled
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
compiled_regexp (p: STRING; caseless: BOOLEAN): REGULAR_EXPRESSION
|
||||
require
|
||||
p /= Void
|
||||
do
|
||||
create Result
|
||||
Result.set_caseless (caseless)
|
||||
Result.compile (p)
|
||||
ensure
|
||||
is_compiled: Result.is_compiled
|
||||
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
|
||||
260
library/server/wsf/security/wsf_xss_request.e
Normal file
@@ -0,0 +1,260 @@
|
||||
note
|
||||
description: "[
|
||||
XSS request, redefine query_parameter and form_parameters filtering the data (using XSS protection)
|
||||
before return the value.
|
||||
The XSS protection pattern used is defined here :{WSF_PROTECTIONS}.XSS: WSF_PROTECTION
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WSF_XSS_REQUEST
|
||||
|
||||
inherit
|
||||
WSF_REQUEST
|
||||
redefine
|
||||
query_parameter,
|
||||
form_parameter,
|
||||
meta_variable,
|
||||
http_accept,
|
||||
http_accept_charset,
|
||||
http_accept_encoding,
|
||||
http_accept_language,
|
||||
http_connection,
|
||||
http_expect,
|
||||
http_host,
|
||||
http_referer,
|
||||
http_user_agent,
|
||||
http_authorization,
|
||||
http_transfer_encoding,
|
||||
http_access_control_request_headers,
|
||||
http_if_match,
|
||||
http_if_modified_since,
|
||||
http_if_none_match,
|
||||
http_if_range,
|
||||
http_if_unmodified_since,
|
||||
http_last_modified,
|
||||
http_range,
|
||||
http_content_range,
|
||||
http_content_encoding
|
||||
end
|
||||
|
||||
WSF_REQUEST_EXPORTER
|
||||
|
||||
WSF_PROTECTION_POLICY
|
||||
|
||||
create
|
||||
make_from_request
|
||||
|
||||
feature {NONE} -- Creation
|
||||
|
||||
make_from_request (req: WSF_REQUEST)
|
||||
do
|
||||
make_from_wgi (req.wgi_request)
|
||||
end
|
||||
|
||||
feature -- Query parameters
|
||||
|
||||
query_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := xss_query_parameter (Current, a_name)
|
||||
end
|
||||
|
||||
feature -- Form Parameters
|
||||
|
||||
form_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := xss_form_parameter (Current, a_name)
|
||||
end
|
||||
|
||||
feature -- Meta Variable
|
||||
|
||||
meta_variable (a_name: READABLE_STRING_GENERAL): detachable WSF_STRING
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := xss_meta_variable (Current, a_name)
|
||||
end
|
||||
|
||||
feature -- HTTP_*
|
||||
|
||||
http_accept: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_accept (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_accept_charset: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_accept_charset (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_accept_encoding: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_accept_encoding (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_accept_language: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_accept_language (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_connection: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_connection (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_expect: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_expect (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_host: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_host (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_referer: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_referer (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_user_agent: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_user_agent (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_authorization: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_authorization (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_transfer_encoding: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_transfer_encoding (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_access_control_request_headers: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_access_control_request_headers (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_if_match: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_if_match (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_if_modified_since: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_if_modified_since (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_if_none_match: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_if_none_match (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_if_range: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_if_range (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_if_unmodified_since: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_if_unmodified_since (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_last_modified: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_last_modified (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_range: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_range (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_content_range: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_content_range (Current, <<l_protection.xss>>)
|
||||
end
|
||||
|
||||
http_content_encoding: detachable READABLE_STRING_8
|
||||
-- <Precursor>
|
||||
local
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
Result := custom_http_content_encoding (Current, <<l_protection.xss>>)
|
||||
end
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
note
|
||||
description : "Objects that ..."
|
||||
author : "$Author$"
|
||||
author : "$Author: jfiat $"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
|
||||
@@ -51,7 +51,6 @@ feature {NONE} -- Initialization
|
||||
create h.make
|
||||
header := h
|
||||
h.put_content_type (content_type)
|
||||
h.put_transfer_encoding_binary
|
||||
h.put_content_length (filesize (file_path))
|
||||
h.put_content_disposition ("attachment", "filename=%""+ base_name +"%"")
|
||||
if attached filedate (file_path) as dt then
|
||||
@@ -215,22 +214,14 @@ feature -- Implementation: output
|
||||
create f.make_with_path (fn)
|
||||
check f.exists and then f.is_readable end
|
||||
|
||||
f.open_read
|
||||
from
|
||||
until
|
||||
f.exhausted
|
||||
loop
|
||||
f.read_stream (4_096)
|
||||
res.put_string (f.last_string)
|
||||
end
|
||||
f.close
|
||||
res.put_file_content (f, 0, f.count)
|
||||
end
|
||||
|
||||
invariant
|
||||
status_code_set: status_code /= 0
|
||||
|
||||
note
|
||||
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
copyright: "2011-2018, 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
|
||||
|
||||
@@ -152,6 +152,80 @@ feature -- Helpers
|
||||
end
|
||||
end
|
||||
|
||||
option_natural_64_value (a_opt_name: READABLE_STRING_GENERAL; a_default: NATURAL_64): NATURAL_64
|
||||
-- NATURAL_64 value associated to option name `a_opt_name', other return `a_default'.
|
||||
local
|
||||
s: READABLE_STRING_GENERAL
|
||||
do
|
||||
Result := a_default
|
||||
if attached option (a_opt_name) as opt then
|
||||
if attached {NATURAL_64} opt as n then
|
||||
Result := n
|
||||
else
|
||||
s := opt.out
|
||||
if s.is_natural_64 then
|
||||
Result := s.to_natural_64
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
option_timeout_ns_value (a_opt_name: READABLE_STRING_GENERAL; a_default: NATURAL_64): NATURAL_64
|
||||
-- Timeout in nanoseconds associated to option name `a_opt_name', other return `a_default'.
|
||||
local
|
||||
v: detachable READABLE_STRING_GENERAL
|
||||
s: STRING_32
|
||||
i,l_count: INTEGER
|
||||
do
|
||||
if a_opt_name.ends_with ("_ns") then
|
||||
-- Option use "nanoseconds" convention.
|
||||
Result := option_natural_64_value (a_opt_name, a_default)
|
||||
elseif attached option (a_opt_name) as opt then
|
||||
if attached {READABLE_STRING_GENERAL} opt as str then
|
||||
from
|
||||
i := 1
|
||||
l_count := str.count
|
||||
until
|
||||
i > l_count or v /= Void
|
||||
loop
|
||||
if str[i].is_space then
|
||||
-- ignore
|
||||
elseif str[i].is_digit then
|
||||
-- Keep it
|
||||
else
|
||||
v := str.head (i - 1)
|
||||
create s.make_from_string_general (str.substring (i, l_count))
|
||||
s.adjust
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
if v = Void then
|
||||
v := str
|
||||
end
|
||||
if v /= Void then
|
||||
if s = Void or else s.is_whitespace or else s.is_case_insensitive_equal_general ("s") then
|
||||
-- Consider as `seconds` for backward compatibility
|
||||
Result := timeout_utilities.seconds_to_nanoseconds (v.to_integer)
|
||||
elseif s.is_case_insensitive_equal_general ("ns") then
|
||||
check v.is_natural_64 end
|
||||
Result := v.to_natural_64
|
||||
elseif s.is_case_insensitive_equal_general ("us") then
|
||||
check v.is_integer end
|
||||
Result := timeout_utilities.microseconds_to_nanoseconds (v.to_integer)
|
||||
elseif s.is_case_insensitive_equal_general ("ms") then
|
||||
check v.is_integer end
|
||||
Result := timeout_utilities.milliseconds_to_nanoseconds (v.to_integer)
|
||||
end
|
||||
else
|
||||
-- Error!
|
||||
end
|
||||
else
|
||||
-- Backward comptability with timeout in seconds!
|
||||
Result := timeout_utilities.seconds_to_nanoseconds (option_integer_value (a_opt_name, timeout_utilities.nanoseconds_to_seconds (a_default)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
option_boolean_value (a_opt_name: READABLE_STRING_GENERAL; a_default: BOOLEAN): BOOLEAN
|
||||
-- BOOLEAN value associated to option name `a_opt_name', other return `a_default'.
|
||||
local
|
||||
@@ -222,6 +296,11 @@ feature {NONE} -- Implementation
|
||||
options: STRING_TABLE [detachable ANY]
|
||||
-- Custom options which might be support (or not) by the default service
|
||||
|
||||
timeout_utilities: WSF_TIMEOUT_UTILITIES
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
invariant
|
||||
options_attached: options /= Void
|
||||
note
|
||||
|
||||
93
library/server/wsf/src/support/wsf_timeout_utilities.e
Normal file
@@ -0,0 +1,93 @@
|
||||
note
|
||||
description: "Utility for timeout conversion."
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
seealso: "See also {SOCKET_TIMEOUT_UTILITIES} from EiffelNet library."
|
||||
|
||||
class
|
||||
WSF_TIMEOUT_UTILITIES
|
||||
|
||||
feature -- Time helpers
|
||||
|
||||
one_second_in_nanoseconds: NATURAL_64 = 1_000_000_000
|
||||
-- Number of nanoseconds in one second.
|
||||
|
||||
one_millisecond_in_nanoseconds: NATURAL_64 = 1_000_000
|
||||
-- Number of nanoseconds in one millisecond.
|
||||
|
||||
one_microsecond_in_nanoseconds: NATURAL_64 = 1_000
|
||||
-- Number of nanoseconds in one microsecond.
|
||||
|
||||
max_timeout_ns_value: NATURAL_64 = 2_147_483_647_999_999_999
|
||||
-- Maximum value for a timeout in nanoseconds.
|
||||
-- See `is_valid_timeout_ns` for explanation.
|
||||
|
||||
nanoseconds_to_seconds (ns: NATURAL_64): INTEGER
|
||||
-- Number of seconds in `ns` nanoseconds (that fits in INTEGER_32 value).
|
||||
-- Warning: Result can not be more than {INTEGER}.max_value.
|
||||
local
|
||||
n64: NATURAL_64
|
||||
do
|
||||
n64 := ns // one_second_in_nanoseconds
|
||||
if n64 > {INTEGER}.max_value.to_natural_64 then
|
||||
Result := {INTEGER}.max_value
|
||||
else
|
||||
Result := n64.to_integer_32
|
||||
end
|
||||
ensure
|
||||
non_negative: Result >= 0
|
||||
coherent: Result.to_natural_64 * one_second_in_nanoseconds <= ns
|
||||
is_class: class
|
||||
end
|
||||
|
||||
seconds_to_nanoseconds (s: INTEGER): NATURAL_64
|
||||
-- Number of nanoseconds in `s` seconds (that fits in NATURAL_64 value).
|
||||
do
|
||||
Result := one_second_in_nanoseconds * s.to_natural_64
|
||||
ensure
|
||||
is_class: class
|
||||
end
|
||||
|
||||
milliseconds_to_nanoseconds (ms: INTEGER): NATURAL_64
|
||||
-- Number of nanoseconds in `ms` milliseconds (that fits in NATURAL_64 value).
|
||||
do
|
||||
Result := one_millisecond_in_nanoseconds * ms.to_natural_64
|
||||
ensure
|
||||
is_class: class
|
||||
end
|
||||
|
||||
microseconds_to_nanoseconds (us: INTEGER): NATURAL_64
|
||||
-- Number of nanoseconds in `us` microseconds (that fits in NATURAL_64 value).
|
||||
do
|
||||
Result := one_microsecond_in_nanoseconds * us.to_natural_64
|
||||
ensure
|
||||
is_class: class
|
||||
end
|
||||
|
||||
feature -- Validation
|
||||
|
||||
is_valid_timeout_ns (ns: NATURAL_64): BOOLEAN
|
||||
--| Note: internally (C API), the ns timeout is splitted into:
|
||||
--| - a long value `tv_sec` : number of seconds
|
||||
--| - a long value `tv_usec`: number of microseconds (with struct timeval)
|
||||
--| or in the future
|
||||
--| - a long value `tv_nsec`: number of nanoseconds (with struct timespec)
|
||||
--| tv_usec or tv_nsec should be >= 0 and < equivalent of 1 second.
|
||||
--| tv_usec < 1_000_000
|
||||
--| tv_nsec < 1_000_000_000
|
||||
--| so there is no issue with NATURAL_64 for usec or nsec
|
||||
--| but, then, there is restriction for the seconds value which is coded as long int
|
||||
--| and long values (32 bits) are between -2_147_483_647 and +2_147_483_647 (i.e 2e31 - 1 = {INTEGER_32}.max_value).
|
||||
--| then, the `seconds` part of the timeout <= +2_147_483_647, and then expressed in nanoseconds, and taking into account
|
||||
--| the usec or nsec part, the timeout should not be greater than:
|
||||
--| +2_147_483_647 * 1_000_000_000 + 999_999_999 = 2_147_483_647_999_999_999
|
||||
--| and as NATURAL_64's max value is bigger = 18_446_744_073_709_551_615
|
||||
--| features dealing with timeout in nanoseconds have related preconditions.
|
||||
do
|
||||
Result := 0 <= ns and then ns <= max_timeout_ns_value
|
||||
ensure
|
||||
is_class: class
|
||||
end
|
||||
|
||||
end
|
||||
129
library/server/wsf/tests/src/test_wsf_protection_policy.e
Normal file
@@ -0,0 +1,129 @@
|
||||
note
|
||||
description: "Summary description for {TEST_WSF_PROTECTION_POLICY}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
TEST_WSF_PROTECTION_POLICY
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
WSF_SERVICE
|
||||
undefine
|
||||
default_create
|
||||
end
|
||||
|
||||
feature -- Test
|
||||
|
||||
test_http_expect_attack_without_xss_protection
|
||||
local
|
||||
req: WSF_REQUEST
|
||||
|
||||
do
|
||||
--| Case HTTP header expect attack, no filtered.
|
||||
req := new_request (<<
|
||||
["REQUEST_METHOD", "GET"],
|
||||
["QUERY_STRING", ""],
|
||||
["REQUEST_URI", "/cookie"],
|
||||
["HTTP_EXPECT", "<script>alert(XSS attack)</script>"]
|
||||
>>
|
||||
)
|
||||
-- no filter
|
||||
assert ("HTTP_EXPECT <script>alert(XSS attack)</script>", attached req.http_expect as v and then v.is_case_insensitive_equal ("<script>alert(XSS attack)</script>"))
|
||||
end
|
||||
|
||||
test_http_expect_attack_with_xss_protection
|
||||
local
|
||||
req: WSF_REQUEST
|
||||
sec: WSF_PROTECTION_POLICY
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
create sec
|
||||
--| Case HTTP header expect attack, filtered using {xss_regular_expression}
|
||||
req := new_request (<<
|
||||
["REQUEST_METHOD", "GET"],
|
||||
["QUERY_STRING", ""],
|
||||
["REQUEST_URI", "/xss_example"],
|
||||
["HTTP_EXPECT", "<script>alert(XSS attack)</script>"]
|
||||
>>
|
||||
)
|
||||
assert ("HTTP_EXPECT <script>alert(XSS attack)</script>", sec.custom_http_expect (req, <<l_protection.xss>>) = Void)
|
||||
end
|
||||
|
||||
|
||||
test_http_expect_attack_with_xss_js_protection
|
||||
local
|
||||
req: WSF_REQUEST
|
||||
sec: WSF_PROTECTION_POLICY
|
||||
l_protection: WSF_PROTECTIONS
|
||||
do
|
||||
create sec
|
||||
--| Case HTTP header expect attack, filtered using {xss_javascript_expression}
|
||||
req := new_request (<<
|
||||
["REQUEST_METHOD", "GET"],
|
||||
["QUERY_STRING", ""],
|
||||
["REQUEST_URI", "/xss_example"],
|
||||
["HTTP_EXPECT", "<script>alert(XSS attack)</script>"]
|
||||
>>
|
||||
)
|
||||
assert ("HTTP_EXPECT <script>alert(XSS attack)</script>", sec.custom_http_expect (req, <<l_protection.xss_javascript>>) = Void )
|
||||
end
|
||||
|
||||
test_http_referer_attack_with_xss_js_protection_fails
|
||||
local
|
||||
req: WSF_REQUEST
|
||||
sec: WSF_PROTECTION_POLICY
|
||||
l_protection: WSF_PROTECTIONS
|
||||
l_str: STRING
|
||||
do
|
||||
l_str:= "[
|
||||
Referer: http://www.google.com/search?hl=en&q=fe525"-alert(1)-"d116a885fd5
|
||||
]"
|
||||
create sec
|
||||
--| Case HTTP header referer attack, filtered using {xss_javascript_expression}
|
||||
req := new_request (<<
|
||||
["REQUEST_METHOD", "GET"],
|
||||
["QUERY_STRING", ""],
|
||||
["REQUEST_URI", "/xss_example"],
|
||||
["HTTP_REFERER", l_str]
|
||||
>>
|
||||
)
|
||||
assert ("HTTP_REFERER", attached sec.custom_http_referer (req, <<l_protection.xss_javascript>>) as v and then not v.is_empty )
|
||||
end
|
||||
|
||||
|
||||
test_http_referer_attack_with_xss_protection
|
||||
local
|
||||
req: WSF_REQUEST
|
||||
sec: WSF_PROTECTION_POLICY
|
||||
l_protection: WSF_PROTECTIONS
|
||||
l_str: STRING
|
||||
do
|
||||
l_str:= "[
|
||||
Referer: http://www.google.com/search?hl=en&q=fe525"-alert(1)-"d116a885fd5
|
||||
]"
|
||||
create sec
|
||||
--| Case HTTP header referer attack, filtered using {xss_javascript_expression}
|
||||
req := new_request (<<
|
||||
["REQUEST_METHOD", "GET"],
|
||||
["QUERY_STRING", ""],
|
||||
["REQUEST_URI", "/xss_example"],
|
||||
["HTTP_REFERER", l_str]
|
||||
>>
|
||||
)
|
||||
assert ("HTTP_REFERER", attached {READABLE_STRING_8} sec.custom_http_referer (req, <<l_protection.xss>>) as v and then not v.is_empty )
|
||||
end
|
||||
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
new_request (a_meta: ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]): WSF_REQUEST_NULL
|
||||
local
|
||||
wgi_req: WGI_REQUEST
|
||||
do
|
||||
create {WGI_REQUEST_NULL} wgi_req.make_with_file (a_meta, io.input)
|
||||
create Result.make_from_wgi (wgi_req)
|
||||
end
|
||||
|
||||
end
|
||||
252
library/server/wsf/tests/src/test_xss_patterns.e
Normal file
@@ -0,0 +1,252 @@
|
||||
note
|
||||
description: "Summary description for {TEST_XSS_PATTERNS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=XSS Filter Evasion Cheat Sheet", "src=https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet", "protocol=uri"
|
||||
|
||||
class
|
||||
TEST_XSS_PATTERNS
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
|
||||
feature -- Tests
|
||||
|
||||
test_xss_locator
|
||||
local
|
||||
xss: WSF_XSS_REQUEST
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:= "[
|
||||
';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//";
|
||||
alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//--
|
||||
></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("XSS locator", r.has_matched)
|
||||
end
|
||||
|
||||
test_xss_locator_short
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
'';!--"<XSS>=&{()}
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("XSS locator short", r.has_matched)
|
||||
end
|
||||
|
||||
test_no_filter_evasion
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<SCRIPT SRC=http://xss.rocks/xss.js></SCRIPT>
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("No filter evasion", r.has_matched)
|
||||
end
|
||||
|
||||
test_filter_bypass_based_polyglot
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
'">><marquee><img src=x onerror=confirm(1)></marquee>"></plaintext\></|\><plaintext/onmouseover=prompt(1)>
|
||||
<script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>'-->"></script>
|
||||
<script>alert(document.cookie)</script>">
|
||||
<img/id="confirm(1)"/alt="/"src="/"onerror=eval(id)>'">
|
||||
<img src="http://www.shellypalmer.com/wp-content/images/2015/07/hacked-compressor.jpg">
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Filter bypass based polyglot", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
test_image_xss_js_directive
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG SRC="javascript:alert('XSS');">
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Image XSS using the JavaScript directive", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
test_no_quotes_no_semicolon
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG SRC=javascript:alert('XSS')>
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("No quotes and no semicolon", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
test_case_insensitive_xss_vector
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG SRC=JaVaScRiPt:alert('XSS')>
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Case insensitive XSS attack vector", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
test_html_entities
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG SRC=javascript:alert("XSS")>
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("HTML entities", r.has_matched)
|
||||
end
|
||||
|
||||
test_grave_accent_obfuscation
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG SRC=`javascript:alert("RSnake says, 'XSS'")`>
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Grave accent obfuscation", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
test_malformed_a_tags
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
-- Skip the HREF attribute and get to the meat of the XXS... Submitted by David Cross ~ Verified on Chrome
|
||||
s:="[
|
||||
<a onmouseover="alert(document.cookie)">xxs link</a>
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Malformed A tags", r.has_matched)
|
||||
end
|
||||
|
||||
test_malformed_a_tags_2
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
-- Chrome loves to replace missing quotes for you... if you ever get stuck just leave them off and Chrome will put them
|
||||
-- in the right place and fix your missing quotes on a URL or script.
|
||||
s:="[
|
||||
<a onmouseover=alert(document.cookie)>xxs link</a>
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Malformed A tags", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
test_malformed_img
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG """><SCRIPT>alert("XSS")</SCRIPT>">
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Malformed IMG tags", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
test_from_char_code
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("fromCharCode", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
test_default_src_tag
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG SRC=# onmouseover="alert('xxs')">
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Default SRC tag to get past filters that check SRC domain", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
test_default_src_tag_2
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG SRC= onmouseover="alert('xxs')">
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Default SRC tag by leaving it empty", r.has_matched)
|
||||
end
|
||||
|
||||
test_default_src_tag_3
|
||||
local
|
||||
r: REGULAR_EXPRESSION
|
||||
s: STRING
|
||||
do
|
||||
s:="[
|
||||
<IMG onmouseover="alert('xxs')">
|
||||
]"
|
||||
r:= xss_pattern.XSS.regexp
|
||||
r.match (s)
|
||||
assert ("Default SRC tag by leaving it out entirely", r.has_matched)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
xss_pattern: WSF_PROTECTIONS
|
||||
|
||||
end
|
||||
@@ -3,9 +3,9 @@
|
||||
<target name="server">
|
||||
<root class="TEST" feature="make"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
</file_rule>
|
||||
<option debug="false" warning="true">
|
||||
<assertions precondition="true" postcondition="true" check="true" loop="true" supplier_precondition="true"/>
|
||||
@@ -21,12 +21,14 @@
|
||||
</library>
|
||||
<library name="http" location="..\..\..\network\protocol\http\http.ecf" readonly="false"/>
|
||||
<library name="http_client" location="..\..\..\network\http_client\net_http_client.ecf" readonly="false"/>
|
||||
<library name="pcre" location="$ISE_LIBRARY\unstable\library\text\regexp\pcre\pcre.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="wsf" location="..\wsf.ecf" readonly="false">
|
||||
<option>
|
||||
<assertions precondition="true" postcondition="true" check="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
</library>
|
||||
<library name="wsf_security" location="..\wsf_security.ecf" readonly="false"/>
|
||||
<library name="wsf_standalone" location="..\..\wsf\connector\standalone.ecf" readonly="false"/>
|
||||
<cluster name="server" location=".\server\" recursive="true"/>
|
||||
</target>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<target name="wsf">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true">
|
||||
</option>
|
||||
|
||||
22
library/server/wsf/wsf_security.ecf
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="wsf_security" uuid="6684959A-6F63-4861-A98E-7E144AE77F2E" library_target="wsf_security">
|
||||
<target name="wsf_security">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</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="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
|
||||
<library name="http" location="..\..\network\protocol\http\http.ecf"/>
|
||||
<library name="pcre" location="$ISE_LIBRARY\unstable\library\text\regexp\pcre\pcre.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="security" location=".\security\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -111,9 +111,9 @@ feature -- Execution
|
||||
i := l_raw_header.substring_index ("%R%N", 1)
|
||||
if i > 0 then
|
||||
-- Skip the first status line.
|
||||
create h.make_from_raw_header_data (l_raw_header.substring (i + 2, l_raw_header.count).as_string_8_conversion) -- NOTE: it is string 8 per nature.
|
||||
create h.make_from_raw_header_data (l_raw_header.substring (i + 2, l_raw_header.count).to_string_8) -- NOTE: it is string 8 per nature.
|
||||
else
|
||||
create h.make_from_raw_header_data (l_raw_header.as_string_8_conversion)
|
||||
create h.make_from_raw_header_data (l_raw_header.to_string_8)
|
||||
end
|
||||
if attached l_remote_uri.host as l_remote_host then
|
||||
if l_remote_uri.port > 0 then
|
||||
|
||||
@@ -48,6 +48,7 @@ feature -- Encoder
|
||||
if uc.is_character_8 then
|
||||
c := uc.to_character_8
|
||||
inspect c
|
||||
when '%U' then Result.append_string ("\u0000")
|
||||
when '%"' then Result.append_string ("\%"")
|
||||
when '\' then Result.append_string ("\\")
|
||||
when '%B' then Result.append_string ("\b")
|
||||
|
||||
@@ -39,12 +39,16 @@
|
||||
<library name="wsf_cgi" location="..\library\server\wsf\connector\cgi.ecf" readonly="false"/>
|
||||
<library name="wsf_extension" location="..\library\server\wsf\wsf_extension.ecf" readonly="false"/>
|
||||
<library name="wsf_html" location="..\library\server\wsf_html\wsf_html.ecf" readonly="false"/>
|
||||
<library name="wsf_proxy" location="..\library\server\wsf_proxy\wsf_proxy.ecf" readonly="false"/>
|
||||
<library name="wsf_js_widget" location="..\draft\library\server\wsf_js_widget\wsf_js_widget.ecf" readonly="false"/>
|
||||
<library name="wsf_standalone" location="..\library\server\wsf\connector\standalone.ecf" readonly="false"/>
|
||||
<library name="wsf_standalone_websocket" location="..\library\server\wsf\connector\standalone_websocket.ecf" readonly="false"/>
|
||||
<library name="wsf_libfcgi" location="..\library\server\wsf\connector\libfcgi.ecf" readonly="false"/>
|
||||
<library name="wsf_openshift" location="..\library\server\wsf\connector\openshift.ecf" readonly="false"/>
|
||||
<library name="wsf_policy_driven" location="..\library\server\wsf\wsf_policy_driven.ecf" readonly="false"/>
|
||||
<library name="wsf_router_context" location="..\library\server\wsf\wsf_router_context.ecf" readonly="false"/>
|
||||
<library name="wsf_session" location="..\library\server\wsf\wsf_session.ecf" readonly="false"/>
|
||||
<library name="wsf_security" location="..\library\server\wsf\wsf_security.ecf" readonly="false"/>
|
||||
</target>
|
||||
<target name="all_none" extends="all">
|
||||
<description>Compiling with None concurrency</description>
|
||||
|
||||
@@ -65,6 +65,8 @@ echo Install library: ewf/wsf_proxy
|
||||
%COPYCMD% %TMP_DIR%\library\server\wsf_proxy %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_proxy
|
||||
echo Install library: ewf/wsf_html
|
||||
%COPYCMD% %TMP_DIR%\library\server\wsf_html %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_html
|
||||
echo Install library: ewf/wsf_security
|
||||
%COPYCMD% %TMP_DIR%\library\server\wsf_security %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_security
|
||||
echo Install library: ewf/encoder
|
||||
%COPYCMD% %TMP_DIR%\library\text\encoder %TMP_CONTRIB_DIR%\library\web\framework\ewf\text\encoder
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ echo Uninstall framework: ewf
|
||||
|
||||
echo Uninstall ewf examples
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\examples\web\ewf
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\examples\ewb\ewf_precomp
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\examples\web\ewf_precomp
|
||||
|
||||
echo Uninstall ewf wizard
|
||||
%RDCMD% %TMP_TARGET_DIR%\help\wizards\ewf
|
||||
|
||||