14186 lines
432 KiB
Diff
14186 lines
432 KiB
Diff
diff --git a/doc/readme.md b/doc/readme.md
|
||
new file mode 100644
|
||
index 0000000..26e5c0b
|
||
--- /dev/null
|
||
+++ b/doc/readme.md
|
||
@@ -0,0 +1,10 @@
|
||
+Welcome to the EiffelWeb framework documentation.
|
||
+
|
||
+The EiffelWeb framework is also known as *EWF*.
|
||
+
|
||
+The [Workbook](/doc/workbook/workbook.md) lets you discover the EiffelWeb framework.
|
||
+
|
||
+And you may found in the *old* [Wiki](/doc/wiki/readme.md) notes that were not migrated to the [Workbook](/doc/workbook/workbook.md), but consider them as obsolete.
|
||
+
|
||
+[Enter the documentation](/doc/workbook/workbook.md)
|
||
+
|
||
diff --git a/doc/wiki/Spec-server-architecture.md b/doc/wiki/Spec-server-architecture.md
|
||
index 2960af9..e44f4de 100644
|
||
--- a/doc/wiki/Spec-server-architecture.md
|
||
+++ b/doc/wiki/Spec-server-architecture.md
|
||
@@ -1,3 +1,3 @@
|
||
## Diagram: Overview of the server architecture ##
|
||
|
||
-
|
||
\ No newline at end of file
|
||
+
|
||
diff --git a/doc/workbook/basics/basics.md b/doc/workbook/basics/basics.md
|
||
index f570644..698878b 100644
|
||
--- a/doc/workbook/basics/basics.md
|
||
+++ b/doc/workbook/basics/basics.md
|
||
@@ -1,4 +1,4 @@
|
||
-Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query Parameter](/doc/workbook/handling_request/form.md)
|
||
+Nav: [Workbook](../workbook.md) :: [Handling Requests: Form/Query Parameter](../handling_request/form.md)
|
||
|
||
|
||
## EWF basic service
|
||
@@ -11,14 +11,15 @@ Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query Parameter](/doc
|
||
- [Source code](#source_2)
|
||
|
||
|
||
-<a name="structure"/>
|
||
+<a name="structure"></a>
|
||
+
|
||
## EWF service structure
|
||
|
||
The following code describes the basic structure of an EWF basic service that handles HTTP requests. We will need to define a Service Launcher and a Request Execution implementation.
|
||
|
||
```eiffel
|
||
class
|
||
- APPLICACTION
|
||
+ APPLICATION
|
||
|
||
inherit
|
||
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||
@@ -91,16 +92,15 @@ 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 you want to run it cgi, fcgi,nino or 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.
|
||
+**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**
|
||
**WSF_CGI_SERVICE_LAUNCHER**
|
||
-**WSF_NINO_SERVICE_LAUNCHER**
|
||
**WSF_LIBFCGI_SERVICE_LAUNCHER**
|
||
|
||
A basic EWF service inherits from **WSF_DEFAULT_SERVICE**, which has a formal generic that should conform to **WSF_EXECUTION** class with a `make' creation procedure, in our case the class **APPLICATION_EXECUTION**.
|
||
@@ -109,11 +109,12 @@ The **APPLICATION_EXECUTION** class inherits from **WSF_EXECUTION** interface,
|
||
|
||
In the **APPLICATION_EXECUTION** class class you will need to implement 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```.
|
||
|
||
-<a name="text"/>
|
||
+<a name="text"></a>
|
||
+
|
||
## A simple Service to Generate Plain Text.
|
||
|
||
Before to continue, it is recommended to review the getting started guided. In the example we will only shows the implementation of the WSF_EXECUTION interface.
|
||
@@ -142,16 +143,18 @@ end
|
||
|
||
```
|
||
<a name="source_1"></a>
|
||
-##### Source code
|
||
+
|
||
+### Source code
|
||
The source code is available on Github. You can get it by running the command:
|
||
|
||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||
|
||
-The example of simple service that generate plain text response is located in the directory $PATH/ewd/doc/workbook/basics/simple, where $PATH is where you run ```git clone``` . Just double click on the simple.ecf file and select the simple_nino target or if you prefer the command line, run the command:
|
||
+The example of simple service that generate plain text response is located in the directory $PATH/ewf/doc/workbook/basics/simple, where $PATH is where you run ```git clone``` . Just double click on the simple.ecf file and select the simple_nino target or if you prefer the command line, run the command:
|
||
|
||
```estudio -config simple.ecf -target simple_nino```
|
||
|
||
<a name="html"></a>
|
||
+
|
||
## A Service to Generate HTML.
|
||
To generate HTML, it's needed
|
||
|
||
@@ -193,7 +196,10 @@ feature -- Basic operations
|
||
|
||
end
|
||
```
|
||
-##### Source code
|
||
+
|
||
+<a name="source_2"></a>
|
||
+
|
||
+### Source code
|
||
The source code is available on Github. You can get it by running the command:
|
||
|
||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||
@@ -202,5 +208,5 @@ The example of the service that generates HTML is located in the directory $PATH
|
||
|
||
```estudio -config simple_html.ecf -target simple_html_nino```
|
||
|
||
-Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query Parameter](/doc/workbook/handling_request/form.md)
|
||
+Nav: [Workbook](../workbook.md) :: [Handling Requests: Form/Query Parameter](../handling_request/form.md)
|
||
|
||
diff --git a/doc/workbook/basics/simple_html/apache_config/Readme.md b/doc/workbook/basics/simple_html/apache_config/Readme.md
|
||
index 448eb14..c2fd077 100644
|
||
--- a/doc/workbook/basics/simple_html/apache_config/Readme.md
|
||
+++ b/doc/workbook/basics/simple_html/apache_config/Readme.md
|
||
@@ -1,31 +1,32 @@
|
||
-##Run simple_html example on Apache with FCGI on Windows.
|
||
+Nav: [Workbook](../../../workbook.md) :: [Basic concepts](../../../basics/basics.md)
|
||
|
||
+## Run simple_html example on Apache with FCGI on Windows.
|
||
|
||
|
||
-####Prerequisites
|
||
+#### Prerequisites
|
||
|
||
* This tutorial was written for people working under Windows environment, and using Apache Server with FCGI connector
|
||
* Compile the ewf application from command line.
|
||
-* Assuming you have installed Apache Server under C:/home/server/Apache24.
|
||
-* Assuming you have placed your current project under C:/home/server/Apache24/fcgi-bin.
|
||
-* Assuming you have setted the Listen to 8888, the defautl value is 80 .
|
||
+* Assuming you have installed Apache Server under `C:/home/server/Apache24`.
|
||
+* Assuming you have placed your current project under `C:/home/server/Apache24/fcgi-bin`.
|
||
+* Assuming you have setted the Listen to `8888`, the defautl value is `80` .
|
||
|
||
|
||
|
||
-####FCGI module
|
||
+#### FCGI module
|
||
If you don't have the FCGI module installed, you can get it from https://www.apachelounge.com/download/, download the module based on your platform [modules-2.4-win64-VC11.zip](https://www.apachelounge.com/download/VC11/modules/modules-2.4-win64-VC11.zip) or [modules-2.4-win32-VC11.zip](https://www.apachelounge.com/download/VC11/modules/modules-2.4-win32-VC11.zip), uncompress it
|
||
-and copy the _mod_fcgid.so_ to C:/home/server/Apache24/modules
|
||
+and copy the _mod_fcgid.so_ to `C:/home/server/Apache24/modules`
|
||
|
||
-####Compile the project simple_html using the fcgi connector.
|
||
+#### Compile the project simple_html using the fcgi connector.
|
||
|
||
ec -config simple_html.ecf -target simple_html_fcgi -finalize -c_compile -project_path .
|
||
|
||
-Copy the genereted exe to C:/home/server/Apache24/fcgi-bin folder.
|
||
+Copy the genereted exe to `C:/home/server/Apache24/fcgi-bin` folder.
|
||
|
||
Check if you have _libfcgi.dll_ in your PATH.
|
||
|
||
|
||
-####Apache configuration
|
||
+#### Apache configuration
|
||
Add to httpd.conf the content, you can get the configuration file [here](config.conf)
|
||
|
||
```
|
||
@@ -43,10 +44,18 @@ LoadModule fcgid_module modules/mod_fcgid.so
|
||
```
|
||
|
||
Test if your httpd.conf is ok
|
||
->httpd -t
|
||
+```
|
||
+> httpd -t
|
||
+```
|
||
|
||
-Luanch the server
|
||
->httpd
|
||
+Launch the server
|
||
+```
|
||
+> httpd
|
||
+```
|
||
|
||
Check the application
|
||
->http://localhost:8888/simple
|
||
+```
|
||
+> http://localhost:8888/simple
|
||
+```
|
||
+
|
||
+Nav: [Workbook](../../../workbook.md) :: [Basic concepts](../../../basics/basics.md)
|
||
diff --git a/doc/workbook/basics/simple_html/simple_html.rc b/doc/workbook/basics/simple_html/simple_html.rc
|
||
deleted file mode 100644
|
||
index b0ec159..0000000
|
||
--- a/doc/workbook/basics/simple_html/simple_html.rc
|
||
+++ /dev/null
|
||
@@ -1,6 +0,0 @@
|
||
-#include <windows.h>
|
||
-
|
||
-STRINGTABLE
|
||
-BEGIN
|
||
- 1 "This Program was made using EiffelStudio using Visual Studio C++"
|
||
-END
|
||
diff --git a/doc/workbook/deployment.md b/doc/workbook/deployment.md
|
||
deleted file mode 100644
|
||
index 3f86da9..0000000
|
||
--- a/doc/workbook/deployment.md
|
||
+++ /dev/null
|
||
@@ -1,163 +0,0 @@
|
||
-EWF Deployment
|
||
-==============
|
||
-
|
||
-#Apache on Windows#
|
||
-
|
||
-1. Apache Install
|
||
-2. Deploying EWF CGI
|
||
-3. CGI overview
|
||
- 1. Build EWF application
|
||
- 2. Copy the generated exe file and the www content .htaccess CGI
|
||
-4. Deploying EWF FCGI
|
||
-5. FCGI overview
|
||
- 1. Build EWF application
|
||
- 2. Copy the generated exe file and the www content.htaccess CGI
|
||
-
|
||
-
|
||
-
|
||
-##Apache on Windows
|
||
-
|
||
-###Apache Install
|
||
-
|
||
->Check the correct version (Win 32 or Win64)
|
||
->Apache Version: Apache 2.4.4
|
||
->Windows: http://www.apachelounge.com/download/
|
||
-
|
||
-####Deploying EWF CGI
|
||
-
|
||
-####CGI overview
|
||
->A new process is started for each HTTP request. So if there are N requests to the same >CGI program, the code of the CGI program is loaded into memory N times.
|
||
->When a CGI program finishes handling a request, the program terminates.
|
||
-
|
||
-* Build EWF application
|
||
-
|
||
- ec -config [app.ecf] -target [app_cgi] -finalize -c_compile -project_path
|
||
-
|
||
-
|
||
->Note: change app.ecf and target app_cgi based on your own configuration.
|
||
-
|
||
-* Copy the generated exe file and the www content
|
||
-
|
||
-Copy the app.exe and the folder _www_ into a folder served by apache2, for example under.
|
||
-
|
||
-
|
||
- <APACHE_PATH>/htdocs.
|
||
-
|
||
- <APACHE_PATH> = path to your apache installation
|
||
-
|
||
- Edit httpd.conf under c:/<APACHE_PATH>/conf
|
||
-
|
||
- DocumentRoot "c:/<APACHE_PATH>/htdocs"
|
||
-
|
||
- <Directory "c:/<APACHE_PATH>/htdocs">
|
||
- AllowOverride All --
|
||
- Require all granted -- this is required in Apache 2.4.4
|
||
- </Directory>
|
||
-
|
||
-Check that you have the following modules enabled
|
||
-
|
||
- LoadModule cgi_module modules/mod_cgi.so
|
||
- LoadModule rewrite_module modules/mod_rewrite.so
|
||
-
|
||
-####Tip:
|
||
->To check the syntax of your httpd.conf file. From command line run the following
|
||
-
|
||
- $>httpd - t
|
||
-
|
||
-
|
||
->.htaccess CGI
|
||
- http://perishablepress.com/stupid-htaccess-tricks/
|
||
-
|
||
-####.htaccess
|
||
-
|
||
- Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||
- AddHandler cgi-script exe
|
||
-
|
||
- <IfModule mod_rewrite.c>
|
||
- RewriteEngine on
|
||
-
|
||
- RewriteRule ^$ $service [L]
|
||
-
|
||
- RewriteCond %{REQUEST_FILENAME} !-f
|
||
- RewriteCond %{REQUEST_FILENAME} !-d
|
||
- RewriteCond %{REQUEST_URI} !$service
|
||
- RewriteRule ^(.*)$ $service/$1
|
||
-
|
||
- RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||
- </IfModule
|
||
-
|
||
->Replace $service with the name of your executable service, for example app_service.exe
|
||
-
|
||
-
|
||
-####Deploying EWF FCGI
|
||
->To deploy FCGI you will need to download the mod_fcgi module.
|
||
->You can get it from here http://www.apachelounge.com/download/
|
||
-
|
||
-####FCGI overview
|
||
->FastCGI allows a single, long-running process to handle more than one user request while keeping close to the CGI programming model, retaining the simplicity while eliminating the overhead of creating a new process for each request. Unlike converting an application to a web server plug-in, FastCGI applications remain independent of the web server.
|
||
-
|
||
-* Build EWF application
|
||
-
|
||
- ec -config [app.ecf] -target [app_fcgi] -finalize -c_compile -project_path .
|
||
-
|
||
->Note: change app.ecf and target app_fcgi based on your own configuration.
|
||
-
|
||
-* Copy the generated exe file and the www content
|
||
-
|
||
-Copy the app.exe and the folder "www" into a folder served by apache2, for example under
|
||
-
|
||
- <APACHE_PATH>/htdocs.
|
||
-
|
||
- <APACHE_PATH> = path to your apache installation
|
||
-
|
||
- Edit httpd.conf under c:/<APACHE_PATH>/conf
|
||
-
|
||
- DocumentRoot "c:/<APACHE_PATH>/htdocs"
|
||
-
|
||
- <Directory "c:/<APACHE_PATH>/htdocs">
|
||
- AllowOverride All --
|
||
- Require all granted -- this is required in Apache 2.4.4
|
||
- </Directory>
|
||
-
|
||
->Check that you have the following modules enabled
|
||
-
|
||
- LoadModule rewrite_module modules/mod_rewrite.so
|
||
- LoadModule fcgid_module modules/mod_fcgid.so
|
||
-
|
||
->NOTE: By default Apache does not come with fcgid module, so you will need to download it, and put the module under Apache2/modules
|
||
-
|
||
-#.htaccess FCGI
|
||
->http://perishablepress.com/stupid-htaccess-tricks/
|
||
-
|
||
-####.htaccess
|
||
-
|
||
- Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||
-
|
||
- <IfModule mod_fcgid.c>
|
||
- AddHandler fcgid-script .ews
|
||
- FcgidWrapper $FULL_PATH/$service .ews
|
||
- </IfModule>
|
||
-
|
||
-
|
||
- <IfModule mod_rewrite.c>
|
||
- RewriteEngine on
|
||
-
|
||
- RewriteBase /
|
||
- RewriteRule ^$ service.ews [L]
|
||
-
|
||
- RewriteCond %{REQUEST_FILENAME} !-f
|
||
- RewriteCond %{REQUEST_FILENAME} !-d
|
||
- RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||
- RewriteCond %{REQUEST_URI} !service.ews
|
||
- RewriteRule ^(.*)$ service.ews/$1
|
||
-
|
||
-
|
||
- RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||
- </IfModule>
|
||
-
|
||
-Replace $service with the name of your executable $service, for example app_service.exe
|
||
-You will need to create an service.ews file, this file will be located at the same place where you copy your app service executable.
|
||
-
|
||
-
|
||
-
|
||
-
|
||
diff --git a/doc/workbook/deployment/readme.md b/doc/workbook/deployment/readme.md
|
||
new file mode 100644
|
||
index 0000000..91a9179
|
||
--- /dev/null
|
||
+++ b/doc/workbook/deployment/readme.md
|
||
@@ -0,0 +1,189 @@
|
||
+Nav: [Workbook](../workbook.md)
|
||
+
|
||
+EWF Deployment
|
||
+==============
|
||
+
|
||
+# Apache on Windows#
|
||
+
|
||
+1. Apache Install
|
||
+2. Deploying EWF CGI
|
||
+3. CGI overview
|
||
+ 1. Build EWF application
|
||
+ 2. Copy the generated exe file and the www content .htaccess CGI
|
||
+4. Deploying EWF FCGI
|
||
+5. FCGI overview
|
||
+ 1. Build EWF application
|
||
+ 2. Copy the generated exe file and the www content.htaccess CGI
|
||
+
|
||
+
|
||
+
|
||
+## Apache on Windows
|
||
+
|
||
+### Apache Install
|
||
+
|
||
+>Check the correct version (Win 32 or Win64)
|
||
+>Apache Version: Apache 2.4.4
|
||
+>Windows: http://www.apachelounge.com/download/
|
||
+
|
||
+note: on linux (debian), use
|
||
+> sudo apt-get install apache2
|
||
+
|
||
+#### Deploying EWF CGI
|
||
+
|
||
+#### CGI overview
|
||
+>A new process is started for each HTTP request. So if there are N requests to the same
|
||
+>CGI program, the code of the CGI program is loaded into memory N times.
|
||
+>When a CGI program finishes handling a request, the program terminates.
|
||
+
|
||
+* Build EWF application
|
||
+
|
||
+ ec -config [app.ecf] -target [app_cgi] -finalize -c_compile -project_path
|
||
+
|
||
+
|
||
+>Note: change app.ecf and target app_cgi based on your own configuration.
|
||
+
|
||
+* Copy the generated exe file and the www content
|
||
+
|
||
+Copy the app.exe and the folder _www_ into a folder served by apache2, for example under.
|
||
+
|
||
+
|
||
+ <APACHE_PATH>/htdocs.
|
||
+
|
||
+ <APACHE_PATH> = path to your apache installation
|
||
+
|
||
+ Edit httpd.conf under c:/<APACHE_PATH>/conf
|
||
+
|
||
+ DocumentRoot "c:/<APACHE_PATH>/htdocs"
|
||
+
|
||
+ <Directory "c:/<APACHE_PATH>/htdocs">
|
||
+ AllowOverride All --
|
||
+ Require all granted -- this is required in Apache 2.4.4
|
||
+ </Directory>
|
||
+
|
||
+Check that you have the following modules enabled
|
||
+
|
||
+ LoadModule cgi_module modules/mod_cgi.so
|
||
+ LoadModule rewrite_module modules/mod_rewrite.so
|
||
+
|
||
+#### Tip:
|
||
+>To check the syntax of your httpd.conf file. From command line run the following
|
||
+
|
||
+ $>httpd - t
|
||
+
|
||
+
|
||
+>.htaccess CGI
|
||
+ http://perishablepress.com/stupid-htaccess-tricks/
|
||
+
|
||
+#### .htaccess
|
||
+
|
||
+ Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||
+ AddHandler cgi-script exe
|
||
+
|
||
+ <IfModule mod_rewrite.c>
|
||
+ RewriteEngine on
|
||
+
|
||
+ RewriteRule ^$ $service [L]
|
||
+
|
||
+ RewriteCond %{REQUEST_FILENAME} !-f
|
||
+ RewriteCond %{REQUEST_FILENAME} !-d
|
||
+ RewriteCond %{REQUEST_URI} !$service
|
||
+ RewriteRule ^(.*)$ $service/$1
|
||
+
|
||
+ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||
+ </IfModule
|
||
+
|
||
+>Replace $service with the name of your executable service, for example app_service.exe
|
||
+
|
||
+
|
||
+#### Deploying EWF FCGI
|
||
+>To deploy FCGI you will need to download the mod_fcgi module.
|
||
+>You can get it from here http://www.apachelounge.com/download/
|
||
+
|
||
+note: on linux (debian), use
|
||
+> sudo apt-get install libapache2-mod-fastcgi
|
||
+
|
||
+#### FCGI overview
|
||
+>FastCGI allows a single, long-running process to handle more than one user request while keeping close to the CGI programming model, retaining the simplicity while eliminating the overhead of creating a new process for each request. Unlike converting an application to a web server plug-in, FastCGI applications remain independent of the web server.
|
||
+
|
||
+* Build EWF application
|
||
+
|
||
+ ec -config [app.ecf] -target [app_fcgi] -finalize -c_compile -project_path .
|
||
+
|
||
+>Note: change app.ecf and target app_fcgi based on your own configuration.
|
||
+
|
||
+* Copy the generated exe file and the www content
|
||
+
|
||
+Copy the app.exe and the folder "www" into a folder served by apache2, for example under
|
||
+
|
||
+ <APACHE_PATH>/htdocs.
|
||
+
|
||
+ <APACHE_PATH> = path to your apache installation
|
||
+
|
||
+ Edit httpd.conf under c:/<APACHE_PATH>/conf
|
||
+
|
||
+ DocumentRoot "c:/<APACHE_PATH>/htdocs"
|
||
+
|
||
+ <Directory "c:/<APACHE_PATH>/htdocs">
|
||
+ AllowOverride All --
|
||
+ Require all granted -- this is required in Apache 2.4.4
|
||
+ </Directory>
|
||
+
|
||
+>Check that you have the following modules enabled
|
||
+
|
||
+ LoadModule rewrite_module modules/mod_rewrite.so
|
||
+ LoadModule fcgid_module modules/mod_fcgid.so
|
||
+
|
||
+>NOTE: By default Apache does not come with fcgid module, so you will need to download it, and put the module under Apache2/modules
|
||
+
|
||
+It is also possible to set various parameters in the apache site configuration file such as:
|
||
+```
|
||
+ <IfModule mod_fcgid.c>
|
||
+ # FcgidIdleTimeout 600
|
||
+ # FcgidBusyScanInterval 120
|
||
+ # FcgidProcessLifeTime 3600
|
||
+ # FcgidMaxProcesses 5
|
||
+ # FcgidMaxProcessesPerClass 100
|
||
+ # FcgidMinProcessesPerClass 100
|
||
+ # FcgidConnectTimeout 8
|
||
+ # FcgidIOTimeout 60
|
||
+ # FcgidBusyTimeout 1200
|
||
+ </IfModule>
|
||
+```
|
||
+See https://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html for more information.
|
||
+
|
||
+# .htaccess FCGI
|
||
+
|
||
+```
|
||
+http://perishablepress.com/stupid-htaccess-tricks/
|
||
+```
|
||
+
|
||
+#### .htaccess
|
||
+
|
||
+ Options +ExecCGI +Includes +FollowSymLinks -Indexes
|
||
+
|
||
+ <IfModule mod_fcgid.c>
|
||
+ AddHandler fcgid-script .ews
|
||
+ FcgidWrapper $FULL_PATH/$service .ews
|
||
+ </IfModule>
|
||
+
|
||
+
|
||
+ <IfModule mod_rewrite.c>
|
||
+ RewriteEngine on
|
||
+
|
||
+ RewriteBase /
|
||
+ RewriteRule ^$ service.ews [L]
|
||
+
|
||
+ RewriteCond %{REQUEST_FILENAME} !-f
|
||
+ RewriteCond %{REQUEST_FILENAME} !-d
|
||
+ RewriteCond %{REQUEST_URI} !=/favicon.ico
|
||
+ RewriteCond %{REQUEST_URI} !service.ews
|
||
+ RewriteRule ^(.*)$ service.ews/$1
|
||
+
|
||
+
|
||
+ RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||
+ </IfModule>
|
||
+
|
||
+Replace $service with the name of your executable $service, for example app_service.exe
|
||
+You will need to create an service.ews file, this file will be located at the same place where you copy your app service executable.
|
||
+
|
||
+Nav: [Workbook](../workbook.md)
|
||
diff --git a/doc/workbook/generating_response/generating_response.md b/doc/workbook/generating_response/generating_response.md
|
||
index 4d63cdf..977daf7 100644
|
||
--- a/doc/workbook/generating_response/generating_response.md
|
||
+++ b/doc/workbook/generating_response/generating_response.md
|
||
@@ -1,5 +1,5 @@
|
||
|
||
-Nav: [Workbook](../workbook.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md) | [Handling Cookies](/doc/workbook/handling_cookies/handling_cookies.md)
|
||
+Nav: [Workbook](../workbook.md) :: [Handling Requests: Header Fields](../handling_request/headers.md) :: [Handling Cookies](../handling_cookies/handling_cookies.md)
|
||
|
||
|
||
## EWF Generating Response
|
||
@@ -14,7 +14,8 @@ Nav: [Workbook](../workbook.md) | [Handling Requests: Header Fields](/doc/workbo
|
||
- [Response Header Fields](#header_fields)
|
||
|
||
|
||
-<a name="format"/>
|
||
+<a name="format"></a>
|
||
+
|
||
## Format of the HTTP response
|
||
|
||
As we saw in the previous documents, a request from a user-agent (browser or other client) consists of an HTTP command (usually GET or POST), zero or more request headers (one or more in HTTP 1.1, since Host is required), a blank line, and only in the case of POST/PUT requests, payload data. A typical request looks like the following.
|
||
@@ -49,7 +50,8 @@ looks like this:
|
||
|
||
The status line consists of the HTTP version (HTTP/1.1 in the preceding example), a status code (an integer 200 in the example), and a very short message corresponding to the status code (OK in the example). In most cases, the headers are optional except for Content-Type, which specifies the MIME type of the document that follows. Although most responses contain a document, some don’t. For example, responses to HEAD requests should never include a document, and various status codes essentially indicate failure or redirection (and thus either don’t include a document or include only a short error-message document).
|
||
|
||
-<a name="status_set"/>
|
||
+<a name="status_set"></a>
|
||
+
|
||
## How to set the status code
|
||
|
||
If you need to set an arbitrary status code, you can use the ```WSF_RESPONSE.put_header``` feature or the ```WSF_RESPONSE.set_status_code``` feature. An status code of 200 is a default value. See below examples using the mentioned features.
|
||
@@ -94,7 +96,8 @@ Example
|
||
```
|
||
Both features takes an INTEGER (the status code) as an formal argument, you can use 200, 300, 500 etc directly, but instead of using explicit numbers, it's recommended to use the constants defined in the class [HTTP_STATUS_CODE](). The name of each constant is based from the standard [HTTP 1.1](https://httpwg.github.io/).
|
||
|
||
-<a name="redirect"/>
|
||
+<a name="redirect"></a>
|
||
+
|
||
## How to redirect to a particular location.
|
||
To redirect the response to a new location, we need to send a 302 status code, to do that we use ```{HTTP_STATUS_CODE}.found```
|
||
|
||
@@ -145,7 +148,8 @@ The ```WSF_RESPONSE.redirect_now``` feature use the status code ```{HTTP_STATUS_
|
||
|
||
Using a similar approach we can build features to answer a bad request (400), internal server error (500), etc. We will build a simple example showing the most common HTTP status codes.
|
||
|
||
-<a name="status"/>
|
||
+<a name="status"></a>
|
||
+
|
||
## [HTTP 1.1 Status Codes](https://httpwg.github.io/specs/rfc7231.html#status.codes)
|
||
The status-code element is a three-digit integer code giving the result of the attempt to understand and satisfy the request. The first digit of the status-code defines the class of response.
|
||
|
||
@@ -159,7 +163,8 @@ General categories:
|
||
Note: use ```res.set_status_code({HTTP_STATUS_CODE}.bad_request)``` rather than ```res.set_status_code(400)```.
|
||
|
||
|
||
-<a name="example_1"/>
|
||
+<a name="example_1"></a>
|
||
+
|
||
### Example Staus Codes
|
||
Basic Service that builds a simple web page to show the most common status codes
|
||
```eiffel
|
||
@@ -298,7 +303,8 @@ end
|
||
|
||
|
||
|
||
-<a name="example_2"/>
|
||
+<a name="example_2"></a>
|
||
+
|
||
### Example Generic Search Engine
|
||
The following example shows a basic EWF service that builds a generic front end for the most used search engines. This example shows how
|
||
redirection works, and we will use a tools to play with the API to show differents responses.
|
||
@@ -585,7 +591,8 @@ Connection: close
|
||
</html>
|
||
```
|
||
|
||
-<a name="header_fields"/>
|
||
+<a name="header_fields"></a>
|
||
+
|
||
## [Response Header Fields](https://httpwg.github.io/specs/rfc7231.html#response.header.fields)
|
||
|
||
The response header fields allow the server to pass additional information about the response beyond what is placed in the status-line. These header fields give information about the server, about further access to the target resource, or about related resources. We can specify cookies, page modification date (for caching), reload a page after a designated period of time, size of the document.
|
||
@@ -996,4 +1003,4 @@ There are four categories for response header fields:
|
||
|
||
|
||
|
||
-Nav: [Workbook](../workbook.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md) | [Handling Cookies](/doc/workbook/handling_cookies/handling_cookies.md)
|
||
+Nav: [Workbook](../workbook.md) :: [Handling Requests: Header Fields](../handling_request/headers.md) :: [Handling Cookies](../handling_cookies/handling_cookies.md)
|
||
diff --git a/doc/workbook/handling_cookies/handling_cookies.md b/doc/workbook/handling_cookies/handling_cookies.md
|
||
index f1de9bd..316ba48 100644
|
||
--- a/doc/workbook/handling_cookies/handling_cookies.md
|
||
+++ b/doc/workbook/handling_cookies/handling_cookies.md
|
||
@@ -1,4 +1,4 @@
|
||
-Nav: [Workbook](../workbook.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||
+Nav: [Workbook](../workbook.md) :: [Generating Responses](../generating_response/generating_response.md)
|
||
|
||
# Handling Cookies
|
||
|
||
@@ -9,9 +9,10 @@ Nav: [Workbook](../workbook.md) | [Generating Responses](/doc/workbook/generatin
|
||
- [How to read a cookie](#read_cookie)
|
||
- [Examples](#examples)
|
||
|
||
-<a name="cookie"/>
|
||
-## [Cookie](http://httpwg.github.io/specs/rfc6265.html)
|
||
-A cookie is a piece of data that can be stored in a browser's cache. If you visit a web site and then revisit it, the cookie data can be used to identify you as a return visitor. Cookies enable state information, such as an online shopping cart, to be remembered. A cookie can be short term, holding data for a single web session, that is, until you close the browser, or a cookie can be longer term, holding data for a week or a year.
|
||
+<a name="cookie"></a>
|
||
+
|
||
+## Cookie
|
||
+A [cookie](http://httpwg.github.io/specs/rfc6265.html) is a piece of data that can be stored in a browser's cache. If you visit a web site and then revisit it, the cookie data can be used to identify you as a return visitor. Cookies enable state information, such as an online shopping cart, to be remembered. A cookie can be short term, holding data for a single web session, that is, until you close the browser, or a cookie can be longer term, holding data for a week or a year.
|
||
|
||
Cookies are used a lot in web client-server communication.
|
||
|
||
@@ -30,7 +31,8 @@ Client send cookies to server
|
||
|
||
|
||
|
||
-<a name="properties"/>
|
||
+<a name="properties"></a>
|
||
+
|
||
### Cookie properties
|
||
|
||
- Comment: describe the purpose of the cookie. Note that server doesn’t receive this information when client sends cookie in request header.
|
||
@@ -43,7 +45,8 @@ Client send cookies to server
|
||
- HttpOnly: Checks whether this Cookie has been marked as HttpOnly.
|
||
- Version:
|
||
|
||
-<a name="set_get"/>
|
||
+<a name="set_get"></a>
|
||
+
|
||
## Write and Read Cookies.
|
||
|
||
To send a cookie to the client we should use the [HTTP_HEADER] class, and call ```h.put_cookie``` feature or
|
||
@@ -67,7 +70,8 @@ WSF_REQUEST.cookie (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||
feature.
|
||
|
||
|
||
-<a name="set_cookie"/>
|
||
+<a name="set_cookie"></a>
|
||
+
|
||
### How to set Cookies
|
||
Here we have the feature definitions to set cookies
|
||
|
||
@@ -111,7 +115,8 @@ Example of use:
|
||
res.put_string (web_page)
|
||
end
|
||
```
|
||
-<a name="read_cookie"/>
|
||
+<a name="read_cookie"></a>
|
||
+
|
||
### How to read Cookies
|
||
|
||
Reading a particular cookie
|
||
@@ -130,7 +135,8 @@ Reading all the cookies
|
||
```
|
||
|
||
|
||
-<a name="examples"/>
|
||
+<a name="examples"></a>
|
||
+
|
||
### Example
|
||
The following EWF service shows a basic use of cookies.
|
||
1. It display a message to first-time visitors.
|
||
@@ -285,4 +291,4 @@ end
|
||
```
|
||
|
||
|
||
-Nav: [Workbook](../workbook.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||
+Nav: [Workbook](../workbook.md) :: [Generating Responses](../generating_response/generating_response.md)
|
||
diff --git a/doc/workbook/handling_request/form.md b/doc/workbook/handling_request/form.md
|
||
index c72772b..f5f7f33 100644
|
||
--- a/doc/workbook/handling_request/form.md
|
||
+++ b/doc/workbook/handling_request/form.md
|
||
@@ -1,13 +1,11 @@
|
||
-Nav: [Workbook](../workbook.md) | [Basic Concepts] (/doc/workbook/basics/basics.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md)
|
||
-
|
||
-
|
||
-#Handling Requests: Form/Query Data
|
||
+Nav: [Workbook](../workbook.md) :: [Basic Concepts](../basics/basics.md) :: [Handling Requests: Header Fields](./headers.md)
|
||
|
||
+# Handling Requests: Form/Query Data
|
||
|
||
##### Table of Contents
|
||
- [Reading Form Data](#read)
|
||
- [Query Parameters](#query)
|
||
- - [Form Parameters](#form)
|
||
+ - [Form Parameters](#form_parameters)
|
||
- [Uniform Read](#uniform)
|
||
- [Reading Parameters and Values](#reading_pv)
|
||
- [How to read all parameters names](#all_names)
|
||
@@ -34,11 +32,13 @@ Here we will show you how to read input submitted by a user using a Form (GET an
|
||
* client side validattion, server side validations, set default if it's a valid option.
|
||
* How to populate Eiffel objects from the request data.
|
||
|
||
-<a name="read"/>
|
||
+<a name="read"></a>
|
||
+
|
||
## Reading Form Data
|
||
EWF [WSF_REQUEST]() class, provides features to handling this form parsing automatically.
|
||
|
||
-<a name="query"/>
|
||
+<a name="query"></a>
|
||
+
|
||
### Query Parameters
|
||
|
||
WSF_REQUEST.query_parameters: ITERABLE [WSF_VALUE]
|
||
@@ -46,7 +46,8 @@ EWF [WSF_REQUEST]() class, provides features to handling this form parsing autom
|
||
|
||
WSF_REQUEST.query_parameter (a_name: READABLE_STRING_GENERAL): detachable WSF_VALUE
|
||
-- Query parameter for name `a_name'.
|
||
-<a name="form"/>
|
||
+<a name="form_parameters"></a>
|
||
+
|
||
### Form Parameters
|
||
|
||
WSF_REQUEST.form_parameters: ITERABLE [WSF_VALUE]
|
||
@@ -57,7 +58,8 @@ EWF [WSF_REQUEST]() class, provides features to handling this form parsing autom
|
||
|
||
The values supplied to form_parameter and query_parameter are case sensitive.
|
||
|
||
-<a name="uniform"/>
|
||
+<a name="uniform"></a>
|
||
+
|
||
### Read Data
|
||
The previous features, let you read the data one way for GET request and a different way for POST request. WSF_REQUEST provide a feature to read all the data in a uniform way.
|
||
|
||
@@ -70,7 +72,8 @@ So, you use **WSF_REQUEST.item** feature exactly the same way for GET and POST r
|
||
>Note: if a query parameter has the same name as a form paramenter req.item will retrieve the form paramenter. Remember the precedence: form > query > path
|
||
|
||
|
||
-<a name="reading_pv">
|
||
+<a name="reading_pv"></a>
|
||
+
|
||
## Reading Parameters and Values
|
||
|
||
Suppose we have the following HTML5 form using Method POST. This HTML5 form has client side form validation using the new HTML5 attribute, you can do the same using Javascript. So in this case if the user does not fill the fields as expected the form will not be submitted to the server.
|
||
@@ -111,7 +114,8 @@ Suppose we have the following HTML5 form using Method POST. This HTML5 form has
|
||
</fieldset>
|
||
</form>
|
||
```
|
||
-<a name="all_names">
|
||
+<a name="all_names"></a>
|
||
+
|
||
### How to read all parameter names
|
||
To read all the parameters names we simple call WSF_REQUEST.form_parameters.
|
||
|
||
@@ -119,7 +123,8 @@ To read all the parameters names we simple call WSF_REQUEST.form_parameters.
|
||
req: WSF_REQUEST
|
||
across req.form_parameters as ic loop show_parameter_name (ic.item.key) end
|
||
```
|
||
-<a name="single_values">
|
||
+<a name="single_values"></a>
|
||
+
|
||
### How to read single values
|
||
To read a particular parameter, a single value, for example `given-name', we simple call WSF_REQUEST.form_parameter (a_name) and we check if it's attached to WSF_STRING (represents a String parameter)
|
||
```
|
||
@@ -131,7 +136,8 @@ To read a particular parameter, a single value, for example `given-name', we sim
|
||
-- Value missing, check the name against the HTML form
|
||
end
|
||
```
|
||
-<a name="multiple_values">
|
||
+<a name="multiple_values"></a>
|
||
+
|
||
### How to read multiple values
|
||
|
||
To read multiple values, for example in the case of `languages', we simple call WSF_REQUEST.form_parameter (a_name) and we check if it's attached to WSF_MULTIPLE_STRING (represents a String parameter)
|
||
@@ -155,7 +161,8 @@ To read multiple values, for example in the case of `languages', we simple call
|
||
```
|
||
In this case we are handling strings values, but in some cases you will need to do a conversion, betweend the strings that came from the request to map them to your domain model.
|
||
|
||
-<a name="table_values">
|
||
+<a name="table_values"></a>
|
||
+
|
||
### How to read table values
|
||
This is particularly useful when you have a request with the following format
|
||
|
||
@@ -183,7 +190,8 @@ if attached {WSF_TABLE} req.query_parameter ("tab") as l_tab then
|
||
end
|
||
```
|
||
|
||
-<a name="raw_data">
|
||
+<a name="raw_data"></a>
|
||
+
|
||
## Reading Raw Data
|
||
You can also access the data in raw format, it means you will need to parse and url-decode it, and also you will not be able to use the previous features, by default, to enable that you need to call `req.set_raw_input_data_recorded (True)'. This feature (reading raw data) is useful if you are reading POST data with JSON or XML formats, but it's not convinient for HTML forms.
|
||
|
||
@@ -199,7 +207,8 @@ To read raw data you need to do this
|
||
|
||
> given-name=testr&family-name=test&dob=1976-08-26&email=test%40gmail.com&url=http%3A%2F%2Fwww.eiffelroom.com&phone=455555555555&languages=Spanish&languages=English
|
||
|
||
-<a name=upload></a>
|
||
+<a name="upload"></a>
|
||
+
|
||
## Upload Files
|
||
How can we read data when the date come from an uploaded file/s?.
|
||
HTML supports a form element ```<input type="File" ... > ``` to upload a single file and ```<input type="File" ... multiplr> ``` to upload multiple files.
|
||
@@ -290,7 +299,8 @@ The source code is available on Github. You can get it by running the command:
|
||
The example is located in the directory $PATH/ewf/doc/workbook/upload_file where $PATH is where you run git clone.
|
||
|
||
|
||
-<a name=examples>
|
||
+<a name="examples"></a>
|
||
+
|
||
## Examples
|
||
The source code is available on Github. You can get it by running the command:
|
||
|
||
@@ -303,5 +313,4 @@ The GET example is located in the directory $PATH/ewf/doc/workbook/form/get, and
|
||
>Note: replace <ecf_name> and<target_name> with the corresponding values.
|
||
|
||
|
||
-Nav: [Workbook](../workbook.md) | [Basic Concepts] (/doc/workbook/basics/basics.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md)
|
||
-
|
||
+Nav: [Workbook](../workbook.md) :: [Basic Concepts](../basics/basics.md) :: [Handling Requests: Header Fields](./headers.md)
|
||
diff --git a/doc/workbook/handling_request/headers.md b/doc/workbook/handling_request/headers.md
|
||
index bba056e..5f0945f 100644
|
||
--- a/doc/workbook/handling_request/headers.md
|
||
+++ b/doc/workbook/handling_request/headers.md
|
||
@@ -1,7 +1,7 @@
|
||
-Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query parameters] (/doc/workbook/handling_request/form.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||
+Nav: [Workbook](../workbook.md) :: [Handling Requests: Form/Query parameters](./form.md) :: [Generating Responses](../generating_response/generating_response.md)
|
||
|
||
|
||
-#Handling Requests: Headers
|
||
+# Handling Requests: Headers
|
||
|
||
##### Introduction
|
||
- The [HTTP request header fields (also known as "headers")](https://httpwg.github.io/specs/rfc7231.html#request.header.fields) are set by the client (usually web browser) and sent in the header of the http request text (see http protocol), as opposed to form or query parameters [Form Data]().
|
||
@@ -43,6 +43,7 @@ Among other, this means the header fields are exposed with associated CGI field
|
||
- For instance `X-Server` will be known as `HTTP_X_SERVER`.
|
||
|
||
<a name="read_header"></a>
|
||
+
|
||
## Reading HTTP Header fields
|
||
EWF [WSF_REQUEST]() class provides features to access HTTP headers.
|
||
|
||
@@ -72,7 +73,8 @@ Due to CGI compliance, the original header names are not available, however the
|
||
Note: CGI variables are information about the current request (and also about the server). Some are based on the HTTP request line and headers (e.g., form parameters, query parameters), others are derived from the socket itself (e.g., the name and IP address of the requesting host), and still others are taken from server installation parameters (e.g., the mapping of URLs to actual paths).
|
||
|
||
<a name="read_line"></a>
|
||
-####Retrieve information from the Request Line
|
||
+
|
||
+#### Retrieve information from the Request Line
|
||
|
||
For convenience, the following sections refer to a request starting with line:
|
||
```
|
||
@@ -102,59 +104,72 @@ Overview of the features
|
||
|
||
|
||
<a name="understand"></a>
|
||
+
|
||
#### Understanding HTTP 1.1 Request Headers
|
||
Access to the request headers permits the web server applications or APIs to perform optimizations and provide behavior that would not be possible without them for instance such as adapting the response according to the browser preferences.
|
||
This section summarizes the headers most often used; for more information, see the [HTTP 1.1 specification](https://httpwg.github.io/specs/), note that [RFC 2616 is dead](https://www.mnot.net/blog/2014/06/07/rfc2616_is_dead).
|
||
|
||
<a name="accept"></a>
|
||
+
|
||
* [Accept](https://httpwg.github.io/specs/rfc7231.html#header.accept)
|
||
- The "Accept" header field can be used by user agents (browser or other clients) to define response media types that are acceptable. Accept header fields can be used to indicate that the request is limited to a small set of desired types, as in the case of a request for an inline image.
|
||
For example, assume an APIs Learn4Kids can respond with XML or JSON data (JSON format have some advantages over XML, readability, parsing etc...), a client can define its preference using "Accept: application/json" to request data in JSON format, or "Accept: application/xml" to get XML format. In other case the server sends a not acceptable response. Note that the client can define an ordered list of accepted content types, including "*", the client will get the response and know the content type via the response header field "Content-Type". Related [Content-Negotiation]()
|
||
|
||
<a name="accept_charset"></a>
|
||
+
|
||
* [Accept-Charset](https://httpwg.github.io/specs/rfc7231.html#header.accept-charset)
|
||
- The "Accept-Charset" header field can be sent by a user agent (browser or other clients) to indicate which charsets are acceptable in textual response content (e.g., ISO-8859-1).
|
||
|
||
<a name="accept_encoding"></a>
|
||
+
|
||
* [Accept-Encoding](https://httpwg.github.io/specs/rfc7231.html#header.accept-encoding)
|
||
- The "Accept-Encoding" header field can be used by user agents (browser or other clients) to indicate which response content-codings (`gzip`, `compress`) are acceptable in the response. An "identity" token is used as a synonym for "no encoding" in order to communicate when no encoding is preferred. If the server receives this header, it is free to encode the page by using one of the content-encodings specified (usually to reduce transmission time), sending the `Content-Encoding` response header to indicate that it has done so.
|
||
|
||
<a name="accept_language"></a>
|
||
+
|
||
* [Accept-Language](https://httpwg.github.io/specs/rfc7231.html#header.accept-language)
|
||
- The "Accept-Language" header field can be used by user agents (browser or other client) to indicate the set of natural languages that are preferred in the response in case the server can produce representation in more than one language. The value of the header should be one of the standard language codes such as en, en-us, da, etc. See RFC 1766 for details (start at http://www.rfc-editor.org/ to get a current list of the RFC archive sites).
|
||
|
||
<a name="connection"></a>
|
||
+
|
||
* [Connection](https://httpwg.github.io/specs/rfc7230.html#header.connection)
|
||
- The "Connection" header field allows the sender to indicate desired control options for the current connection, for example if it can hanlde persistent HTTP connections.
|
||
By default HTTP/1.1 uses "persistent connections", allowing multiple requests and responses to be carried over a single connection. The "close" connection option is used to signal that a connection will not persist after the current request/response.
|
||
|
||
<a name="authorization"></a>
|
||
+
|
||
* [Authorization](https://httpwg.github.io/specs/rfc7235.html#header.authorization)
|
||
- The header is used by user agents to authenticate themselves when accessing password protected resources.
|
||
|
||
<a name="content-length"></a>
|
||
+
|
||
* [Content-Length](https://httpwg.github.io/specs/rfc7230.html#header.content-length)
|
||
- For messages that includes a payload body, the Content-Length field-value provides the framing information necessary to determine where the body (and message) ends.
|
||
|
||
<a name="cookie"></a>
|
||
+
|
||
* [Cookie](https://httpwg.github.io/specs/rfc6265.html)
|
||
- The Cookie header contains cookies received by the user agent in previous Set-Cookie headers. The origin server is free to ignore the Cookie header or use its contents for an application-specific purpose. (Related State Management).
|
||
|
||
<a name="host"></a>
|
||
+
|
||
* [Host](https://httpwg.github.io/specs/rfc7230.html#header.host)
|
||
- The "Host" header field provides the host and port information from the target URI, enabling the origin server to distinguish among resources while serving requests for multiple host names on a single IP address. In HTTP 1.1, browsers and other clients are required to specify this header, which indicates the host and port as given in the original URL.
|
||
|
||
<a name="if-modified-since"></a>
|
||
+
|
||
* [If-Modified-Since](https://httpwg.github.io/specs/rfc7232.html#header.if-modified-since)
|
||
- The "If-Modified-Since" header field makes a GET or HEAD request method conditional on the selected representation's modification date being more recent than the date provided in the field-value. Transfering of the selected representation's data is avoided if that data has not changed. So, indicates that the user agents wants the page only if it has been changes after the specified date. The server sends a 304 resource not modified if not has a newer result representation available.
|
||
|
||
<a name="if-unmodified-since"></a>
|
||
+
|
||
* [If-Unmodified-Since](https://httpwg.github.io/specs/rfc7232.html#header.if-unmodified-since)
|
||
- The "If-Unmodified-Since" header field makes the request method conditional on the selected representation's last modification date being earlier than or equal to the date provided in the field-value. The operation should succeed only if the document is older than the specified date.
|
||
|
||
Generally, If-Modified-Since is used for GET requests (“give me the document only if it is newer than my cached version”), whereas If-Unmodified-Since is used for PUT requests (“update this document only if nobody else has changed it since I generated it”).
|
||
|
||
<a name="referer"></a>
|
||
+
|
||
* [Referer](https://httpwg.github.io/specs/rfc7231.html#header.referer)
|
||
- The "Referer" header field allows the user agent to specify a URI reference for the resource from which the target URI was obtained (i.e., the "referrer", though the field name is misspelled). A user agent MUST NOT include the fragment and userinfo components of the URI reference [RFC3986], if any, when generating the Referer field value. This header indicates the URL of the referring Web page.
|
||
|
||
@@ -162,15 +177,17 @@ For example, if you are at Web page A and click on a link to Web page B, the URL
|
||
included in the Referer header when the browser requests Web page B.
|
||
|
||
<a name="user-agent"></a>
|
||
+
|
||
* [User-Agent](https://httpwg.github.io/specs/rfc7231.html#header.user-agent)
|
||
- The "User-Agent" header field contains information about the user agent of the request, which is often used by servers to help identify the scope of reported interoperability problems, to work around or tailor responses to avoid particular user agent limitations, and for analytics regarding browser or operating system use or device.
|
||
|
||
**Note**: the example shows the **WSF_EXECUTION** implementation, that will be used by the service launcher.
|
||
|
||
<a name="example"></a>
|
||
+
|
||
#### Building a Table of All Request Headers
|
||
|
||
-The following [EWF service](/doc/workbook/handling_request/headers/header_fields/application.e) code simply uses an ```html_template``` to fill a table (names and values) with all the headers fields it receives.
|
||
+The following [EWF service](./headers/header_fields/application.e) code simply uses an ```html_template``` to fill a table (names and values) with all the headers fields it receives.
|
||
|
||
The service accomplishes this task by calling ```req.meta_variables``` feature to get an ```ITERABLE [WSF_STRING]```, an structure that can be iterated over using ```across...loop...end```, then it checks if the name has the prefix ```HTTP_``` and if it is true, put the header name and value in a row. (the name in the left cell, the value in the right cell).
|
||
|
||
@@ -276,6 +293,7 @@ end
|
||
```
|
||
|
||
<a name="compress"></a>
|
||
+
|
||
#### How to compress pages
|
||
To be completed.
|
||
|
||
@@ -284,7 +302,7 @@ To be completed.
|
||
|
||
#### Detecting Browser Types
|
||
|
||
-The User-Agent header identifies the specific browser/client that is sending the request. The following code shows a [EWF service](/doc/workbook/handling_request/headers/browser_name/application.e) that sends browser-specific responses.
|
||
+The User-Agent header identifies the specific browser/client that is sending the request. The following code shows a [EWF service](./headers/browser_name/application.e) that sends browser-specific responses.
|
||
|
||
The examples uses the ideas based on the [Browser detection using the user agent](https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent) article.
|
||
Basically the code check if the header user_agent exist and then call the ```browser_name (a_user_agent: READABLE_STRING_8): READABLE_STRING_32```
|
||
@@ -429,10 +447,8 @@ As an exercise, try to write a similar service to retrieve the OS family using t
|
||
* [SERVER_SOFTWARE](https://tools.ietf.org/html/rfc3875#section-4.1.16)
|
||
|
||
**Example**
|
||
-An [EWF service](/doc/workbook/handling_request/headers/cgi_variables/application.e) that shows the CGI variables, creates a table showing the values of all the CGI variables.
|
||
-
|
||
-
|
||
-Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query parameters] (/doc/workbook/handling_request/form.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||
+An [EWF service](./headers/cgi_variables/application.e) that shows the CGI variables, creates a table showing the values of all the CGI variables.
|
||
|
||
|
||
+Nav: [Workbook](../workbook.md) :: [Handling Requests: Form/Query parameters](./form.md) :: [Generating Responses](../generating_response/generating_response.md)
|
||
|
||
diff --git a/doc/workbook/readme.md b/doc/workbook/readme.md
|
||
index 6353245..5274bf8 100644
|
||
--- a/doc/workbook/readme.md
|
||
+++ b/doc/workbook/readme.md
|
||
@@ -1,4 +1,4 @@
|
||
-The [Workbook](/doc/workbook.md) lets you discover the EiffelWeb framework.
|
||
+The [Workbook](./workbook.md) lets you discover the EiffelWeb framework.
|
||
|
||
-[Enter the documentation](/doc/workbook.md)
|
||
+[Enter the documentation](./workbook.md)
|
||
|
||
diff --git a/doc/workbook/workbook.md b/doc/workbook/workbook.md
|
||
index 07ddc2f..c454c49 100644
|
||
--- a/doc/workbook/workbook.md
|
||
+++ b/doc/workbook/workbook.md
|
||
@@ -5,38 +5,46 @@
|
||
* [EWF Introduction](#introduction)
|
||
* [Handling Requests: Form/Query Parameter](#form_query_parameters)
|
||
* [Handling Requests: Header Fields](#header_fields)
|
||
-* [Generating Responses](#generating responses)
|
||
+* [Generating Responses](#generating_responses)
|
||
* [Handling Cookies](#handling_cookies)
|
||
* [EWF Deployment](#deployment)
|
||
|
||
<a name="core"></a>
|
||
+
|
||
# EWF Core
|
||
Before reading (or walking throught) the workbook, to get a quick overview of EWF, it is recommended to read the following articles:
|
||
+
|
||
* [Getting Started with EWF](http://eiffelwebframework.github.io/EWF/getting-started/)
|
||
-* [EWF Documentation](http://eiffelwebframework.github.io/EWF/wiki/Documentation/)
|
||
+* [EWF Documentation](http://eiffelwebframework.github.io/EWF/workbook/workbook)
|
||
* [EWF Application Lifecyle](https://github.com/EiffelWebFramework/ewf_examples/wiki/Application-Lifecycle)
|
||
|
||
|
||
<a name="introduction"></a>
|
||
+
|
||
## Introduction
|
||
-[Basic Concepts] (/doc/workbook/basics/basics.md).
|
||
+[Basic Concepts](./basics/basics.md).
|
||
|
||
<a name="form_query_parameters"></a>
|
||
+
|
||
## Handling Requests: Form/Query Parameter
|
||
-[Handling Requests: Form/Query Parameter] (/doc/workbook/handling_request/form.md).
|
||
+[Handling Requests: Form/Query Parameter](./handling_request/form.md).
|
||
|
||
<a name="header_fields"></a>
|
||
+
|
||
## Handling Requests: Header Fields
|
||
-[Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md).
|
||
+[Handling Requests: Header Fields](./handling_request/headers.md).
|
||
|
||
<a name="generating_responses"></a>
|
||
+
|
||
## Generating Response
|
||
-[Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||
+[Generating Responses](./generating_response/generating_response.md)
|
||
|
||
<a name="handling_cookies"></a>
|
||
+
|
||
## Handling Cookies
|
||
-[Handling Cookies](/doc/workbook/handling_cookies/handling_cookies.md)
|
||
+[Handling Cookies](./handling_cookies/handling_cookies.md)
|
||
+
|
||
+<a name="deployment"></a>
|
||
|
||
-<a name="deployment"/>
|
||
## EWF Deployment
|
||
-[EWF Deployment](/doc/workbook/deployment.md)
|
||
+[EWF Deployment](./deployment/readme.md)
|
||
diff --git a/examples/proxy/application.e b/examples/proxy/application.e
|
||
new file mode 100644
|
||
index 0000000..a90f489
|
||
--- /dev/null
|
||
+++ b/examples/proxy/application.e
|
||
@@ -0,0 +1,29 @@
|
||
+note
|
||
+ description: "Launcher for reverse proxy web application."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ APPLICATION
|
||
+
|
||
+inherit
|
||
+ WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||
+ redefine
|
||
+ initialize
|
||
+ end
|
||
+
|
||
+create
|
||
+ make_and_launch
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ initialize
|
||
+ -- Initialize current service.
|
||
+ do
|
||
+ -- Specific to `standalone' connector (the EiffelWeb server).
|
||
+ -- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
|
||
+ set_service_option ("port", 9090)
|
||
+ import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("server.ini"))
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/examples/proxy/application_execution.e b/examples/proxy/application_execution.e
|
||
new file mode 100644
|
||
index 0000000..ab54495
|
||
--- /dev/null
|
||
+++ b/examples/proxy/application_execution.e
|
||
@@ -0,0 +1,49 @@
|
||
+note
|
||
+ description: "Reverse proxy example."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ APPLICATION_EXECUTION
|
||
+
|
||
+inherit
|
||
+ WSF_EXECUTION
|
||
+
|
||
+ WSF_URI_REWRITER
|
||
+ rename
|
||
+ uri as proxy_uri
|
||
+ end
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature -- Basic operations
|
||
+
|
||
+ execute
|
||
+ do
|
||
+ -- NOTE: please enter the target server uri here
|
||
+ -- replace "http://localhost:8080/foobar"
|
||
+ send_proxy_response ("http://localhost:8080/foobar", Current)
|
||
+ end
|
||
+
|
||
+ send_proxy_response (a_remote: READABLE_STRING_8; a_rewriter: detachable WSF_URI_REWRITER)
|
||
+ local
|
||
+ h: WSF_SIMPLE_REVERSE_PROXY_HANDLER
|
||
+ do
|
||
+ create h.make (a_remote)
|
||
+ h.set_uri_rewriter (a_rewriter)
|
||
+ h.set_uri_rewriter (create {WSF_AGENT_URI_REWRITER}.make (agent proxy_uri))
|
||
+ h.set_timeout (30) -- 30 seconds
|
||
+ h.set_connect_timeout (5_000) -- milliseconds = 5 seconds
|
||
+ h.execute (request, response)
|
||
+ end
|
||
+
|
||
+feature -- Helpers
|
||
+
|
||
+ proxy_uri (a_request: WSF_REQUEST): STRING
|
||
+ -- Request uri rewriten as url.
|
||
+ do
|
||
+ Result := a_request.request_uri
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/examples/proxy/proxy.ecf b/examples/proxy/proxy.ecf
|
||
new file mode 100644
|
||
index 0000000..9bf59a5
|
||
--- /dev/null
|
||
+++ b/examples/proxy/proxy.ecf
|
||
@@ -0,0 +1,28 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="proxy" uuid="B55F0D95-3793-4C90-BBAC-BF5F2DECD5E6" library_target="proxy">
|
||
+ <target name="common" abstract="true">
|
||
+ <file_rule>
|
||
+ <exclude>/.svn$</exclude>
|
||
+ <exclude>/CVS$</exclude>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
+ </file_rule>
|
||
+ <option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||
+ <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
+ </option>
|
||
+ <setting name="console_application" value="true"/>
|
||
+ <variable name="ssl_supported" value="false"/>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
+ <library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||
+ <library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||
+ <library name="wsf_proxy" location="..\..\library\server\wsf_proxy\wsf_proxy-safe.ecf" readonly="false"/>
|
||
+ </target>
|
||
+ <target name="proxy" extends="common">
|
||
+ <root class="APPLICATION" feature="make_and_launch"/>
|
||
+ <option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||
+ <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
+ </option>
|
||
+ <setting name="concurrency" value="scoop"/>
|
||
+ <library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
|
||
+ <cluster name="proxy" location=".\" recursive="true"/>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/examples/proxy/server.ini b/examples/proxy/server.ini
|
||
new file mode 100644
|
||
index 0000000..ae90215
|
||
--- /dev/null
|
||
+++ b/examples/proxy/server.ini
|
||
@@ -0,0 +1,8 @@
|
||
+verbose=true
|
||
+verbose_level=ALERT
|
||
+port=9090
|
||
+#max_concurrent_connections=100
|
||
+#keep_alive_timeout=15
|
||
+#max_tcp_clients=100
|
||
+#socket_timeout=300
|
||
+#max_keep_alive_requests=300
|
||
diff --git a/examples/simple/application.e b/examples/simple/application.e
|
||
index d1de21d..e86ee66 100644
|
||
--- a/examples/simple/application.e
|
||
+++ b/examples/simple/application.e
|
||
@@ -20,9 +20,18 @@ feature {NONE} -- Initialization
|
||
initialize
|
||
-- Initialize current service.
|
||
do
|
||
- set_service_option ("port", 9090)
|
||
+ -- Specific to `standalone' connector (the EiffelWeb server).
|
||
+ -- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
|
||
+ set_service_option ("port", 9090)
|
||
+-- set_service_option ("socket_timeout", 50)
|
||
+-- set_service_option ("max_concurrent_connections", 100)
|
||
+-- set_service_option ("keep_alive_timeout", 15)
|
||
+-- set_service_option ("max_keep_alive_requests", 300)
|
||
+
|
||
+ import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("simple.ini"))
|
||
+
|
||
+-- set_service_option ("socket_timeout", 5)
|
||
+-- set_service_option ("keep_alive_timeout", 3)
|
||
end
|
||
|
||
-
|
||
-
|
||
end
|
||
diff --git a/examples/simple/application_execution.e b/examples/simple/application_execution.e
|
||
index b02bdf5..179f215 100644
|
||
--- a/examples/simple/application_execution.e
|
||
+++ b/examples/simple/application_execution.e
|
||
@@ -17,10 +17,13 @@ feature -- Basic operations
|
||
execute
|
||
local
|
||
s: STRING
|
||
- do
|
||
- -- To send a response we need to setup, the status code and
|
||
- -- the response headers.
|
||
- s := "Hello World!"
|
||
+ dt: HTTP_DATE
|
||
+ do
|
||
+ -- To send a response we need to setup, the status code and
|
||
+ -- the response headers.
|
||
+ s := "Hello World!"
|
||
+ create dt.make_now_utc
|
||
+ s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||
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
|
||
diff --git a/examples/simple/simple.ecf b/examples/simple/simple.ecf
|
||
index f341867..fb45586 100644
|
||
--- a/examples/simple/simple.ecf
|
||
+++ b/examples/simple/simple.ecf
|
||
@@ -1,35 +1,28 @@
|
||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
-<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-13-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-13-0 http://www.eiffel.com/developers/xml/configuration-1-13-0.xsd" name="simple" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple">
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="simple" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486" library_target="simple">
|
||
<target name="common" abstract="true">
|
||
<file_rule>
|
||
- <exclude>/EIFGENs$</exclude>
|
||
- <exclude>/CVS$</exclude>
|
||
<exclude>/.svn$</exclude>
|
||
+ <exclude>/CVS$</exclude>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
</file_rule>
|
||
- <option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||
+ <option warning="true" full_class_checking="false" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="transitional" syntax="transitional">
|
||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
</option>
|
||
+ <setting name="console_application" value="true"/>
|
||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||
</target>
|
||
<target name="simple_standalone" extends="common">
|
||
<root class="APPLICATION" feature="make_and_launch"/>
|
||
- <option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||
+ <option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
</option>
|
||
- <setting name="concurrency" value="thread"/>
|
||
+ <setting name="concurrency" value="scoop"/>
|
||
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
|
||
<cluster name="simple" location=".\" recursive="true"/>
|
||
</target>
|
||
- <target name="simple_nino" extends="common">
|
||
- <root class="APPLICATION" feature="make_and_launch"/>
|
||
- <option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||
- <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
- </option>
|
||
- <library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf"/>
|
||
- <cluster name="simple" location=".\" recursive="true"/>
|
||
- </target>
|
||
<target name="simple_cgi" extends="common">
|
||
<root class="APPLICATION" feature="make_and_launch"/>
|
||
<option warning="true" is_attached_by_default="true" void_safety="transitional" syntax="transitional">
|
||
@@ -48,4 +41,7 @@
|
||
</target>
|
||
<target name="simple" extends="simple_standalone">
|
||
</target>
|
||
+ <target name="simple_standalone_st" extends="simple_standalone">
|
||
+ <setting name="concurrency" value="none"/>
|
||
+ </target>
|
||
</system>
|
||
diff --git a/examples/simple/simple.ini b/examples/simple/simple.ini
|
||
new file mode 100644
|
||
index 0000000..ae90215
|
||
--- /dev/null
|
||
+++ b/examples/simple/simple.ini
|
||
@@ -0,0 +1,8 @@
|
||
+verbose=true
|
||
+verbose_level=ALERT
|
||
+port=9090
|
||
+#max_concurrent_connections=100
|
||
+#keep_alive_timeout=15
|
||
+#max_tcp_clients=100
|
||
+#socket_timeout=300
|
||
+#max_keep_alive_requests=300
|
||
diff --git a/examples/simple_ssl/application.e b/examples/simple_ssl/application.e
|
||
new file mode 100644
|
||
index 0000000..e890c18
|
||
--- /dev/null
|
||
+++ b/examples/simple_ssl/application.e
|
||
@@ -0,0 +1,29 @@
|
||
+note
|
||
+ description : "simple application root class"
|
||
+ date : "$Date$"
|
||
+ revision : "$Revision$"
|
||
+
|
||
+class
|
||
+ APPLICATION
|
||
+
|
||
+inherit
|
||
+ WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||
+ redefine
|
||
+ initialize
|
||
+ end
|
||
+
|
||
+create
|
||
+ make_and_launch
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ initialize
|
||
+ -- Initialize current service.
|
||
+ do
|
||
+ -- Specific to `standalone' connector (the EiffelWeb server).
|
||
+ -- See `{WSF_STANDALONE_SERVICE_LAUNCHER}.initialize'
|
||
+ set_service_option ("port", 9090)
|
||
+ import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("simple.ini"))
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/examples/simple_ssl/application_execution.e b/examples/simple_ssl/application_execution.e
|
||
new file mode 100644
|
||
index 0000000..2d6d0a7
|
||
--- /dev/null
|
||
+++ b/examples/simple_ssl/application_execution.e
|
||
@@ -0,0 +1,41 @@
|
||
+note
|
||
+ description : "simple application execution"
|
||
+ date : "$Date$"
|
||
+ revision : "$Revision$"
|
||
+
|
||
+class
|
||
+ APPLICATION_EXECUTION
|
||
+
|
||
+inherit
|
||
+ WSF_EXECUTION
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature -- Basic operations
|
||
+
|
||
+ execute
|
||
+ local
|
||
+ s: STRING
|
||
+ dt: HTTP_DATE
|
||
+ do
|
||
+ -- To send a response we need to setup, the status code and
|
||
+ -- the response headers.
|
||
+ s := "Hello World!"
|
||
+ create dt.make_now_utc
|
||
+ s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||
+ if request.is_https then
|
||
+ s.append ("<p>This is a secured connection! (https)</p>%N")
|
||
+ end
|
||
+
|
||
+ response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
||
+ response.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||
+ response.header.put_content_type_text_html
|
||
+ response.header.put_content_length (s.count)
|
||
+ if attached request.http_connection as l_connection and then l_connection.is_case_insensitive_equal_general ("keep-alive") then
|
||
+ response.header.put_header_key_value ("Connection", "keep-alive")
|
||
+ end
|
||
+ response.put_string (s)
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/examples/simple_ssl/simple.crt b/examples/simple_ssl/simple.crt
|
||
new file mode 100644
|
||
index 0000000..6147c20
|
||
--- /dev/null
|
||
+++ b/examples/simple_ssl/simple.crt
|
||
@@ -0,0 +1,15 @@
|
||
+-----BEGIN CERTIFICATE-----
|
||
+MIICWDCCAcGgAwIBAgIJAJnXGtV+PtiYMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
|
||
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
|
||
+aWRnaXRzIFB0eSBMdGQwHhcNMTUwNDAzMjIxNTA0WhcNMTYwNDAyMjIxNTA0WjBF
|
||
+MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
|
||
+ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
|
||
+gQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3HtHcKIl5wSUvn
|
||
+G8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J53K0xuUlxz4d4
|
||
+4D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQABo1AwTjAdBgNV
|
||
+HQ4EFgQUbWpk2HoHa0YqpEwr7CGEatBFTMkwHwYDVR0jBBgwFoAUbWpk2HoHa0Yq
|
||
+pEwr7CGEatBFTMkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAi+h4/
|
||
+IgEocWkdRZBKHEcTrRxz5WhEDJMoVo9LhnXvCfn1G/4p6Un6sYv7Xzpi9NuSY8uV
|
||
+cjfJJXhtF3AtyZ70iTAxWaRWjGaZ03PYOjlledJ5rqJEt6CCn8m+JsfznduZvbxQ
|
||
+zQ6jCLXfyD/tvemB+yYEI3NntvRKx5/zt6Q26Q==
|
||
+-----END CERTIFICATE-----
|
||
diff --git a/examples/simple_ssl/simple.ini b/examples/simple_ssl/simple.ini
|
||
new file mode 100644
|
||
index 0000000..2ec39e1
|
||
--- /dev/null
|
||
+++ b/examples/simple_ssl/simple.ini
|
||
@@ -0,0 +1,28 @@
|
||
+##########################################################
|
||
+### EiffelWeb settings for related connector ###
|
||
+### Mostly for EiffelWeb standalone connector ###
|
||
+### See {WGI_STANDALONE_CONSTANTS} for default values. ###
|
||
+##########################################################
|
||
+
|
||
+### Connection settings
|
||
+port=9090
|
||
+#max_concurrent_connections=100
|
||
+#max_tcp_clients=100
|
||
+
|
||
+### Timeout settings
|
||
+#socket_timeout=60
|
||
+#socket_recv_timeout=5
|
||
+
|
||
+### Persistent connection settings
|
||
+#keep_alive_timeout=15
|
||
+#max_keep_alive_requests=100
|
||
+
|
||
+### SSL settings
|
||
+# enable SSL, with file certificate.
|
||
+ssl_enabled=true
|
||
+ssl_ca_key=simple.key
|
||
+ssl_ca_crt=simple.crt
|
||
+
|
||
+### App settings
|
||
+verbose=true
|
||
+verbose_level=ALERT
|
||
diff --git a/examples/simple_ssl/simple.key b/examples/simple_ssl/simple.key
|
||
new file mode 100644
|
||
index 0000000..e5e22a5
|
||
--- /dev/null
|
||
+++ b/examples/simple_ssl/simple.key
|
||
@@ -0,0 +1,15 @@
|
||
+-----BEGIN RSA PRIVATE KEY-----
|
||
+MIICXAIBAAKBgQDFMK6ojzg+KlklhTossR13c51izMgGc3B0z9ttfHIcx2kxra3H
|
||
+tHcKIl5wSUvnG8zmSyFAyQTs5LUv65q46FM9qU8tP+vTeFCfNXvjRcIEpouta3J5
|
||
+3K0xuUlxz4d44D6qvdDWAez/0AkI4y5etW5zXtg7IQorJhsI9TmfGuruzwIDAQAB
|
||
+AoGAR5efMg+dieRyLU8rieJcImxVbfOPg9gRsjdtIVkXTR+RL7ow59q7hXBo/Td/
|
||
+WU8cm1gXoJ/bK+71YYqWyB+BaLRIWvRWb7Gdw203tu4e136Ca5uuY+71qdbVTVcl
|
||
+NQ7J+T+eAQFP+a+DdT3ZQxu9eze87SMbu6i5YSpIk2kusOECQQDunv/DQ+nc+NgR
|
||
+DF+Td3sNYUVRT9a1CWi6abAG6reXwp8MS4NobWDf+Ps4JODhEEwlIdq5qL7qqYBZ
|
||
+Gc1TJJ53AkEA0404Fn6vAzzegBcS4RLlYTK7nMr0m4pMmDMCI6YzAYdMmKHp1e6f
|
||
+IwxSmQrmwyAgwcT01bc0+A8yipcC2BWQaQJBAJ01QZm635OGmos41KsKF5bsE8gL
|
||
+SpBBH69Yu/ECqGwie7iU84FUNnO4zIHjwghlPVVlZX3Vz9o4S+fn2N9DC+cCQGyZ
|
||
+QyCxGdC0r5fbwHJQS/ZQn+UGfvlVzqoXDVMVn3t6ZES6YZrT61eHnOM5qGqklIxE
|
||
+Old3vDZXPt/MU8Zvk3kCQBOgUx2VxvTrHN37hk9/QIDiM62+RenBm1M3ah8xTosf
|
||
+1mSeEb6d9Kwb3TgPBmA7YXzJuAQfRIvEPMPxT5SSr6Q=
|
||
+-----END RSA PRIVATE KEY-----
|
||
diff --git a/examples/simple_ssl/simple_ssl.ecf b/examples/simple_ssl/simple_ssl.ecf
|
||
new file mode 100644
|
||
index 0000000..51e2b9a
|
||
--- /dev/null
|
||
+++ b/examples/simple_ssl/simple_ssl.ecf
|
||
@@ -0,0 +1,27 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="simple_ssl" uuid="C2FE296C-3C18-4609-A5AB-F604BDEE4410" library_target="simple_ssl">
|
||
+ <target name="simple_ssl">
|
||
+ <description>Simple EiffelWeb standalone server with SSL support (Concurrent connection supported thanks to SCOOP).</description>
|
||
+ <root class="APPLICATION" feature="make_and_launch"/>
|
||
+ <file_rule>
|
||
+ <exclude>/.svn$</exclude>
|
||
+ <exclude>/CVS$</exclude>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
+ </file_rule>
|
||
+ <option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||
+ <assertions/>
|
||
+ </option>
|
||
+ <setting name="console_application" value="true"/>
|
||
+ <setting name="concurrency" value="scoop"/>
|
||
+ <variable name="httpd_ssl_enabled" value="true"/>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
+ <library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
|
||
+ <library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||
+ <library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||
+ <cluster name="simple" location=".\" recursive="true"/>
|
||
+ </target>
|
||
+ <target name="simple_ssl_st" extends="simple_ssl">
|
||
+ <description>Simple EiffelWeb standalone server with SSL support (Single threaded, thus no concurrent connection.)</description>
|
||
+ <setting name="concurrency" value="none"/>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/examples/upload_image/upload_image-safe.ecf b/examples/upload_image/upload_image-safe.ecf
|
||
index 0916cdc..b573ad5 100644
|
||
--- a/examples/upload_image/upload_image-safe.ecf
|
||
+++ b/examples/upload_image/upload_image-safe.ecf
|
||
@@ -1,7 +1,6 @@
|
||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="upload_image" uuid="F2400BE8-D8EB-48EB-B4E4-5D4377062A7F" library_target="upload_image">
|
||
- <target name="upload_image">
|
||
- <root class="IMAGE_UPLOADER" feature="make"/>
|
||
+ <target name="upload_image_common">
|
||
<file_rule>
|
||
<exclude>/EIFGENs$</exclude>
|
||
<exclude>/\.git$</exclude>
|
||
@@ -11,14 +10,26 @@
|
||
<debug name="standalone" enabled="true"/>
|
||
<assertions precondition="true" postcondition="true" check="true" invariant="true" supplier_precondition="true"/>
|
||
</option>
|
||
- <setting name="concurrency" value="thread"/>
|
||
+ <setting name="concurrency" value="scoop"/>
|
||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
- <library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false" use_application_options="true"/>
|
||
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf" readonly="false"/>
|
||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||
<library name="uri_template" location="..\..\library\text\parser\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false" use_application_options="true"/>
|
||
+ </target>
|
||
+ <target name="upload_image_standalone" extends="upload_image_common">
|
||
+ <root class="IMAGE_UPLOADER" feature="make"/>
|
||
+ <setting name="concurrency" value="thread"/>
|
||
+ <library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false" use_application_options="true"/>
|
||
<cluster name="src" location="src\" recursive="true"/>
|
||
</target>
|
||
+ <target name="upload_image_libfcgi" extends="upload_image_common">
|
||
+ <root class="IMAGE_UPLOADER" feature="make"/>
|
||
+ <setting name="concurrency" value="none"/>
|
||
+ <library name="default_libfcgi" location="..\..\library\server\wsf\default\libfcgi-safe.ecf" readonly="false" use_application_options="true"/>
|
||
+ <cluster name="src" location="src\" recursive="true"/>
|
||
+ </target>
|
||
+ <target name="upload_image" extends="upload_image_standalone">
|
||
+ </target>
|
||
</system>
|
||
diff --git a/examples/websocket/application.e b/examples/websocket/application.e
|
||
new file mode 100644
|
||
index 0000000..72f4f62
|
||
--- /dev/null
|
||
+++ b/examples/websocket/application.e
|
||
@@ -0,0 +1,29 @@
|
||
+note
|
||
+ description : "simple application root class"
|
||
+ date : "$Date$"
|
||
+ revision : "$Revision$"
|
||
+
|
||
+class
|
||
+ APPLICATION
|
||
+
|
||
+create
|
||
+ make_and_launch
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make_and_launch
|
||
+ local
|
||
+ l_launcher: WSF_STANDALONE_WEBSOCKET_SERVICE_LAUNCHER [APPLICATION_EXECUTION]
|
||
+ opts: WSF_SERVICE_LAUNCHER_OPTIONS
|
||
+ do
|
||
+ create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} opts.make_from_file ("ws.ini")
|
||
+ create l_launcher.make_and_launch (options)
|
||
+ end
|
||
+
|
||
+ options: WSF_SERVICE_LAUNCHER_OPTIONS
|
||
+ -- Initialize current service.
|
||
+ do
|
||
+ create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI} Result.make_from_file ("ws.ini")
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/examples/websocket/application_execution.e b/examples/websocket/application_execution.e
|
||
new file mode 100644
|
||
index 0000000..92f82be
|
||
--- /dev/null
|
||
+++ b/examples/websocket/application_execution.e
|
||
@@ -0,0 +1,184 @@
|
||
+note
|
||
+ description : "simple application execution"
|
||
+ date : "$Date$"
|
||
+ revision : "$Revision$"
|
||
+
|
||
+class
|
||
+ APPLICATION_EXECUTION
|
||
+
|
||
+inherit
|
||
+ WSF_WEBSOCKET_EXECUTION
|
||
+
|
||
+ WEB_SOCKET_EVENT_I
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature -- Basic operations
|
||
+
|
||
+ execute
|
||
+ local
|
||
+ s: STRING
|
||
+ dt: HTTP_DATE
|
||
+ do
|
||
+ -- To send a response we need to setup, the status code and
|
||
+ -- the response headers.
|
||
+ if request.path_info.same_string_general ("/app") then
|
||
+ s := websocket_app_html (9090)
|
||
+ else
|
||
+ s := "Hello World!"
|
||
+ create dt.make_now_utc
|
||
+ s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||
+ s.append ("<p><a href=%"/app%">Websocket demo</a></p>")
|
||
+ end
|
||
+ response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
||
+ response.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||
+ response.header.put_content_type_text_html
|
||
+ response.header.put_content_length (s.count)
|
||
+ if attached request.http_connection as l_connection and then l_connection.is_case_insensitive_equal_general ("keep-alive") then
|
||
+ response.header.put_header_key_value ("Connection", "keep-alive")
|
||
+ end
|
||
+ response.put_string (s)
|
||
+ end
|
||
+
|
||
+feature -- Websocket execution
|
||
+
|
||
+ new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
|
||
+ do
|
||
+ create Result.make (ws, Current)
|
||
+ end
|
||
+
|
||
+feature -- Websocket execution
|
||
+
|
||
+ on_open (ws: WEB_SOCKET)
|
||
+ do
|
||
+ ws.put_error ("Connecting")
|
||
+ ws.send (Text_frame, "Hello, this is a simple demo with Websocket using Eiffel. (/help for more information).%N")
|
||
+ end
|
||
+
|
||
+ on_binary (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||
+ do
|
||
+ ws.send (Binary_frame, a_message)
|
||
+ end
|
||
+
|
||
+ on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||
+ do
|
||
+ if a_message.same_string_general ("/help") then
|
||
+ -- Echo the message for testing.
|
||
+ ws.send (Text_frame, "Help: available commands%N - /time : return the server UTC time.%N")
|
||
+ elseif a_message.starts_with_general ("/time") then
|
||
+ ws.send (Text_frame, "Server time is " + (create {HTTP_DATE}.make_now_utc).string)
|
||
+ else
|
||
+ -- Echo the message for testing.
|
||
+ ws.send (Text_frame, a_message)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ on_close (ws: WEB_SOCKET)
|
||
+ -- Called after the WebSocket connection is closed.
|
||
+ do
|
||
+ ws.put_error ("Connection closed")
|
||
+ end
|
||
+
|
||
+feature -- HTML Resource
|
||
+
|
||
+ websocket_app_html (a_port: INTEGER): STRING
|
||
+ do
|
||
+ Result := "[
|
||
+<!DOCTYPE html>
|
||
+<html>
|
||
+<head>
|
||
+<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
|
||
+<script type="text/javascript">
|
||
+$(document).ready(function() {
|
||
+
|
||
+ var socket;
|
||
+
|
||
+ function connect(){
|
||
+
|
||
+ var host = "ws://127.0.0.1:##PORTNUMBER##";
|
||
+
|
||
+ try{
|
||
+ socket = new WebSocket(host);
|
||
+ message('<p class="event">Socket Status: '+socket.readyState);
|
||
+ socket.onopen = function(){
|
||
+ message('<p class="event">Socket Status: '+socket.readyState+' (open)');
|
||
+ }
|
||
+ socket.onmessage = function(msg){
|
||
+ message('<p class="message">Received: '+msg.data);
|
||
+ }
|
||
+ socket.onclose = function(){
|
||
+ message('<p class="event">Socket Status: '+socket.readyState+' (Closed)');
|
||
+ }
|
||
+ } catch(exception){
|
||
+ message('<p>Error'+exception);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ function send(){
|
||
+ var text = $('#text').val();
|
||
+ if(text==""){
|
||
+ message('<p class="warning">Please enter a message');
|
||
+ return ;
|
||
+ }
|
||
+ try{
|
||
+ socket.send(text);
|
||
+ message('<p class="event">Sent: '+text)
|
||
+ } catch(exception){
|
||
+ message('<p class="warning">');
|
||
+ }
|
||
+ $('#text').val("");
|
||
+ }
|
||
+
|
||
+ function message(msg){
|
||
+ $('#chatLog').append(msg+'</p>');
|
||
+ }//End message()
|
||
+
|
||
+ $('#text').keypress(function(event) {
|
||
+ if (event.keyCode == '13') {
|
||
+ send();
|
||
+ }
|
||
+ });
|
||
+
|
||
+ $('#disconnect').click(function(){
|
||
+ socket.close();
|
||
+ });
|
||
+
|
||
+ if (!("WebSocket" in window)){
|
||
+ $('#chatLog, input, button, #examples').fadeOut("fast");
|
||
+ $('<p>Oh no, you need a browser that supports WebSockets. How about <a href="http://www.google.com/chrome">Google Chrome</a>?</p>').appendTo('#container');
|
||
+ }else{
|
||
+ //The user has WebSockets
|
||
+ connect();
|
||
+ }
|
||
+
|
||
+});
|
||
+</script>
|
||
+<meta charset="utf-8" />
|
||
+<style type="text/css">
|
||
+body {font-family:Arial, Helvetica, sans-serif;}
|
||
+#container { border:5px solid grey; width:800px; margin:0 auto; padding:10px; }
|
||
+#chatLog { padding:5px; border:1px solid black; }
|
||
+#chatLog p {margin:0;}
|
||
+.event {color:#999;}
|
||
+.warning { font-weight:bold; color:#CCC; }
|
||
+</style>
|
||
+<title>WebSockets Client</title>
|
||
+</head>
|
||
+<body>
|
||
+ <div id="wrapper">
|
||
+ <div id="container">
|
||
+ <h1>WebSockets Client</h1>
|
||
+ <div id="chatLog"></div>
|
||
+ <input id="text" type="text" />
|
||
+ <button id="disconnect">Disconnect</button>
|
||
+ </div>
|
||
+ </div>
|
||
+</body>
|
||
+</html>
|
||
+ ]"
|
||
+ Result.replace_substring_all ("##PORTNUMBER##", a_port.out)
|
||
+ end
|
||
+
|
||
+
|
||
+end
|
||
diff --git a/examples/websocket/websocket_app.ecf b/examples/websocket/websocket_app.ecf
|
||
new file mode 100644
|
||
index 0000000..13f9527
|
||
--- /dev/null
|
||
+++ b/examples/websocket/websocket_app.ecf
|
||
@@ -0,0 +1,21 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="websocket_app" uuid="75D17C20-10A8-4E4C-A059-33D72A2B6AEF">
|
||
+ <target name="websocket_app">
|
||
+ <root class="APPLICATION" feature="make_and_launch"/>
|
||
+ <file_rule>
|
||
+ <exclude>/.svn$</exclude>
|
||
+ <exclude>/CVS$</exclude>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
+ </file_rule>
|
||
+ <option warning="true" is_attached_by_default="true" void_safety="all" syntax="transitional">
|
||
+ <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
+ </option>
|
||
+ <setting name="console_application" value="true"/>
|
||
+ <setting name="concurrency" value="scoop"/>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
+ <library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||
+ <library name="standalone_websocket_connector" location="..\..\library\server\wsf\connector\standalone_websocket-safe.ecf" readonly="false"/>
|
||
+ <library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf"/>
|
||
+ <cluster name="app" location=".\" recursive="true"/>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/examples/websocket/ws.ini b/examples/websocket/ws.ini
|
||
new file mode 100644
|
||
index 0000000..b04bd03
|
||
--- /dev/null
|
||
+++ b/examples/websocket/ws.ini
|
||
@@ -0,0 +1,8 @@
|
||
+verbose=true
|
||
+verbose_level=INFORMATION
|
||
+port=9090
|
||
+max_concurrent_connections=100
|
||
+keep_alive_timeout=35
|
||
+max_tcp_clients=100
|
||
+socket_timeout=30000
|
||
+max_keep_alive_requests=3000
|
||
diff --git a/library/network/protocol/http/src/http_header.e b/library/network/protocol/http/src/http_header.e
|
||
index db797f9..49dec76 100644
|
||
--- a/library/network/protocol/http/src/http_header.e
|
||
+++ b/library/network/protocol/http/src/http_header.e
|
||
@@ -180,7 +180,9 @@ feature -- Header: adding
|
||
if line [line.count] = '%R' then
|
||
line.remove_tail (1)
|
||
end
|
||
- add_header (line)
|
||
+ if not line.is_empty then
|
||
+ add_header (line)
|
||
+ end
|
||
end
|
||
end
|
||
end
|
||
diff --git a/library/security/openid/consumer/src/openid_consumer.e b/library/security/openid/consumer/src/openid_consumer.e
|
||
index 68b73a5..95123a5 100644
|
||
--- a/library/security/openid/consumer/src/openid_consumer.e
|
||
+++ b/library/security/openid/consumer/src/openid_consumer.e
|
||
@@ -508,9 +508,9 @@ feature -- Helper
|
||
|
||
new_session (a_uri: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||
local
|
||
- cl: LIBCURL_HTTP_CLIENT
|
||
+ cl: DEFAULT_HTTP_CLIENT
|
||
do
|
||
- create cl.make
|
||
+ create cl
|
||
Result := cl.new_session (a_uri)
|
||
Result.set_is_insecure (True)
|
||
Result.set_max_redirects (5)
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/none/httpd_connection_handler.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/none/httpd_connection_handler.e
|
||
new file mode 100644
|
||
index 0000000..5bc857c
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/none/httpd_connection_handler.e
|
||
@@ -0,0 +1,86 @@
|
||
+note
|
||
+ description: "[
|
||
+ Implementation of HTTPD_CONNECTION_HANDLER_I for concurrency mode: none
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_CONNECTION_HANDLER
|
||
+
|
||
+inherit
|
||
+ HTTPD_CONNECTION_HANDLER_I
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ initialize
|
||
+ do
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ is_shutdown_requested: BOOLEAN
|
||
+ -- <Precursor>
|
||
+
|
||
+ shutdown_requested (a_server: like server): BOOLEAN
|
||
+ do
|
||
+ -- FIXME: we should probably remove this possibility, check with EWF if this is needed.
|
||
+ Result := a_server.controller.shutdown_requested
|
||
+ end
|
||
+
|
||
+feature {HTTPD_SERVER_I} -- Execution
|
||
+
|
||
+ accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
+ local
|
||
+ cl: HTTPD_STREAM_SOCKET
|
||
+ do
|
||
+ is_shutdown_requested := is_shutdown_requested or shutdown_requested (server)
|
||
+ if is_shutdown_requested then
|
||
+ -- Cancel
|
||
+ elseif attached factory.new_handler as h then
|
||
+ cl := h.client_socket
|
||
+ a_listening_socket.accept_to (cl)
|
||
+ if h.is_connected then
|
||
+ h.safe_execute
|
||
+ end
|
||
+ else
|
||
+ check is_not_full: False end
|
||
+ end
|
||
+ update_is_shutdown_requested
|
||
+ end
|
||
+
|
||
+ update_is_shutdown_requested
|
||
+ do
|
||
+ is_shutdown_requested := shutdown_requested (server)
|
||
+ end
|
||
+
|
||
+ shutdown
|
||
+ do
|
||
+ if not is_shutdown_requested then
|
||
+ is_shutdown_requested := True
|
||
+ server.controller.shutdown
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {HTTPD_SERVER_I} -- Status report
|
||
+
|
||
+ wait_for_completion
|
||
+ -- Wait until Current is ready for shutdown
|
||
+ do
|
||
+ -- no concurrency, then current task should be done.
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/none/httpd_request_handler.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/none/httpd_request_handler.e
|
||
new file mode 100644
|
||
index 0000000..c033de7
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/none/httpd_request_handler.e
|
||
@@ -0,0 +1,32 @@
|
||
+note
|
||
+ description : "Concurrent specific feature to implement HTTPD_REQUEST_HANDLER"
|
||
+ date : "$Date$"
|
||
+ revision : "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_REQUEST_HANDLER
|
||
+
|
||
+inherit
|
||
+ HTTPD_REQUEST_HANDLER_I
|
||
+ redefine
|
||
+ is_persistent_connection_supported
|
||
+ end
|
||
+
|
||
+feature -- Status report
|
||
+
|
||
+ is_persistent_connection_supported: BOOLEAN = False
|
||
+ -- <Precursor>
|
||
+ -- When there is no concurrency support, do not try to support
|
||
+ -- persistent connection!
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/none/httpd_request_handler_factory.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/none/httpd_request_handler_factory.e
|
||
new file mode 100644
|
||
index 0000000..1c93e9e
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/none/httpd_request_handler_factory.e
|
||
@@ -0,0 +1,15 @@
|
||
+note
|
||
+ description: "Implementation of request handler factory for concurrency mode: none"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_REQUEST_HANDLER_FACTORY
|
||
+
|
||
+inherit
|
||
+ HTTPD_REQUEST_HANDLER_FACTORY_I
|
||
+
|
||
+note
|
||
+ copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/httpd_connection_handler.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/httpd_connection_handler.e
|
||
new file mode 100644
|
||
index 0000000..3941b86
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/httpd_connection_handler.e
|
||
@@ -0,0 +1,146 @@
|
||
+note
|
||
+ description: "[
|
||
+ Implementation of HTTPD_CONNECTION_HANDLER_I for concurrency mode: SCOOP
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_CONNECTION_HANDLER
|
||
+
|
||
+inherit
|
||
+ HTTPD_CONNECTION_HANDLER_I
|
||
+ redefine
|
||
+ initialize
|
||
+ end
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ initialize
|
||
+ local
|
||
+ n: INTEGER
|
||
+ p: like pool
|
||
+ do
|
||
+ n := max_concurrent_connections (server).max (1) -- At least one processor!
|
||
+ create p.make (n)
|
||
+ initialize_pool (p, n)
|
||
+ pool := p
|
||
+ end
|
||
+
|
||
+ initialize_pool (p: like pool; n: INTEGER)
|
||
+ -- Initialize Concurrent pool of `n' potential separate connection handlers.
|
||
+ do
|
||
+ p.set_count (n)
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ is_shutdown_requested: BOOLEAN
|
||
+ -- <Precursor>
|
||
+
|
||
+ max_concurrent_connections (a_server: like server): INTEGER
|
||
+ -- Max concurrent connection settings from server `a_server'.
|
||
+ do
|
||
+ Result := a_server.configuration.max_concurrent_connections
|
||
+ end
|
||
+
|
||
+feature {HTTPD_SERVER_I} -- Execution
|
||
+
|
||
+ shutdown
|
||
+ -- <Precursor>
|
||
+ do
|
||
+ if not is_shutdown_requested then
|
||
+ is_shutdown_requested := True
|
||
+ pool_gracefull_stop (pool)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ pool_gracefull_stop (p: like pool)
|
||
+ -- Graceful stop concurrent pool of separate connection handlers.
|
||
+ do
|
||
+ p.gracefull_stop
|
||
+ end
|
||
+
|
||
+ accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
+ -- <Precursor>
|
||
+ do
|
||
+ accept_connection_on_pool (pool, a_listening_socket) -- Wait on not pool.is_full or is_stop_requested
|
||
+ end
|
||
+
|
||
+ accept_connection_on_pool (a_pool: like pool; a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
+ -- Process accept connection
|
||
+ -- note that the precondition matters for scoop synchronization.
|
||
+ require
|
||
+ concurrency: not a_pool.is_full or is_shutdown_requested or a_pool.stop_requested
|
||
+ local
|
||
+ cl: separate HTTPD_STREAM_SOCKET
|
||
+ do
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".ENTER accept_connection_on_pool")
|
||
+ end
|
||
+ if is_shutdown_requested then
|
||
+ -- Cancel
|
||
+ elseif attached a_pool.separate_item (factory) as h then
|
||
+ cl := separate_client_socket (h)
|
||
+ a_listening_socket.accept_to (cl)
|
||
+ process_handler (h)
|
||
+ else
|
||
+ check is_not_full: False end
|
||
+ end
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".LEAVE accept_connection_on_pool")
|
||
+ end
|
||
+ end
|
||
+
|
||
+ process_handler (hdl: separate HTTPD_REQUEST_HANDLER)
|
||
+ -- Process request handler `hdl' as soon as `hdl' is connected to accepted socket.
|
||
+ require
|
||
+ hdl.is_connected
|
||
+ do
|
||
+ hdl.safe_execute
|
||
+ end
|
||
+
|
||
+feature {HTTPD_SERVER_I} -- Status report
|
||
+
|
||
+ wait_for_completion
|
||
+ -- Wait until Current is ready for shutdown.
|
||
+ do
|
||
+ wait_for_pool_completion (pool)
|
||
+ end
|
||
+
|
||
+ wait_for_pool_completion (p: like pool)
|
||
+ -- Wait until concurrent pool is empty and terminated.
|
||
+ require
|
||
+ p.is_empty -- SCOOP wait condition.
|
||
+ do
|
||
+ p.terminate
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Implementation
|
||
+
|
||
+ separate_client_socket (hdl: separate HTTPD_REQUEST_HANDLER): separate HTTPD_STREAM_SOCKET
|
||
+ -- Client socket for request handler `hdl'.
|
||
+ do
|
||
+ Result := hdl.client_socket
|
||
+ end
|
||
+
|
||
+ pool: separate CONCURRENT_POOL [HTTPD_REQUEST_HANDLER]
|
||
+ -- Pool of separate connection handlers.
|
||
+
|
||
+invariant
|
||
+ pool_attached: pool /= Void
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/httpd_request_handler.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/httpd_request_handler.e
|
||
new file mode 100644
|
||
index 0000000..2defb09
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/httpd_request_handler.e
|
||
@@ -0,0 +1,62 @@
|
||
+note
|
||
+ description: "[
|
||
+ Instance of HTTPD_REQUEST_HANDLER will process the incoming connection
|
||
+ and extract information on the request and the server
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_REQUEST_HANDLER
|
||
+
|
||
+inherit
|
||
+ HTTPD_REQUEST_HANDLER_I
|
||
+ redefine
|
||
+ release
|
||
+ end
|
||
+
|
||
+ CONCURRENT_POOL_ITEM
|
||
+ rename
|
||
+ release as release_pool_item
|
||
+ end
|
||
+
|
||
+feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
|
||
+
|
||
+ release
|
||
+ -- <Precursor>
|
||
+ local
|
||
+ d: detachable STRING
|
||
+ do
|
||
+ debug ("dbglog")
|
||
+ if
|
||
+ attached internal_client_socket as l_socket and then
|
||
+ l_socket.descriptor_available
|
||
+ then
|
||
+ d := l_socket.descriptor.out
|
||
+ else
|
||
+ d := "N/A"
|
||
+ end
|
||
+ dbglog (generator + ".release: ENTER {" + d + "}")
|
||
+ end
|
||
+ Precursor {HTTPD_REQUEST_HANDLER_I}
|
||
+ release_pool_item
|
||
+ debug ("dbglog")
|
||
+ if d /= Void then
|
||
+ dbglog (generator + ".release: LEAVE {" + d + "}")
|
||
+ else
|
||
+ dbglog (generator + ".release: LEAVE {N/A}")
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/httpd_request_handler_factory.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/httpd_request_handler_factory.e
|
||
new file mode 100644
|
||
index 0000000..e48a96a
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/httpd_request_handler_factory.e
|
||
@@ -0,0 +1,27 @@
|
||
+note
|
||
+ description: "Implementation of request handler factory for concurrency mode: SCOOP"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_REQUEST_HANDLER_FACTORY
|
||
+
|
||
+inherit
|
||
+ HTTPD_REQUEST_HANDLER_FACTORY_I
|
||
+
|
||
+ CONCURRENT_POOL_FACTORY [HTTPD_REQUEST_HANDLER]
|
||
+ rename
|
||
+ new_separate_item as new_handler
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/pool/concurrent_pool.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/pool/concurrent_pool.e
|
||
new file mode 100644
|
||
index 0000000..070f271
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/pool/concurrent_pool.e
|
||
@@ -0,0 +1,190 @@
|
||
+note
|
||
+ description: "Concurrent pool for SCOOP concurrency mode."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ CONCURRENT_POOL [G -> CONCURRENT_POOL_ITEM]
|
||
+
|
||
+inherit
|
||
+ HTTPD_DEBUG_FACILITIES
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (n: INTEGER)
|
||
+ do
|
||
+ capacity := n
|
||
+ create items.make_empty (n)
|
||
+ create busy_items.make_empty (n)
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ count: INTEGER
|
||
+ -- Number of concurrent items managed by Current pool.
|
||
+
|
||
+ capacity: INTEGER
|
||
+ -- Maximum number of concurrent items managed by Current pool.
|
||
+
|
||
+feature -- Status report
|
||
+
|
||
+ is_full: BOOLEAN
|
||
+ -- Pool is full?
|
||
+ do
|
||
+ Result := count >= capacity
|
||
+ end
|
||
+
|
||
+ is_empty: BOOLEAN
|
||
+ -- No concurrent item waiting in current pool.
|
||
+ do
|
||
+ Result := count = 0
|
||
+ end
|
||
+
|
||
+ stop_requested: BOOLEAN
|
||
+ -- Current pool received a request to terminate.
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ separate_item (a_factory: separate CONCURRENT_POOL_FACTORY [G]): detachable separate G
|
||
+ -- Reused, or new separate item of type {G} created by `a_factory'.
|
||
+ require
|
||
+ is_not_full: not is_full
|
||
+ local
|
||
+ i,n,pos: INTEGER
|
||
+ lst: like busy_items
|
||
+ l_item: detachable separate G
|
||
+ do
|
||
+ if not stop_requested then
|
||
+ from
|
||
+ lst := busy_items
|
||
+ pos := -1
|
||
+ i := 0
|
||
+ n := lst.count - 1
|
||
+ until
|
||
+ i > n or l_item /= Void or pos >= 0
|
||
+ loop
|
||
+ if not lst [i] then -- is free (i.e not busy)
|
||
+ pos := i
|
||
+
|
||
+ if items.valid_index (pos) then
|
||
+ l_item := items [pos]
|
||
+ if l_item /= Void then
|
||
+ busy_items [pos] := True
|
||
+ end
|
||
+ end
|
||
+ if l_item = Void then
|
||
+ -- Empty, then let's create one.
|
||
+ l_item := a_factory.new_separate_item
|
||
+ register_item (l_item)
|
||
+ items [pos] := l_item
|
||
+ end
|
||
+ end
|
||
+ i := i + 1
|
||
+ end
|
||
+ if l_item = Void then
|
||
+ -- Pool is FULL ...
|
||
+ check overcapacity: False end
|
||
+ else
|
||
+ debug ("pool", "dbglog")
|
||
+ dbglog ("Lock pool item #" + pos.out + " (free:"+ (capacity - count).out +"))")
|
||
+ end
|
||
+ count := count + 1
|
||
+ busy_items [pos] := True
|
||
+ Result := l_item
|
||
+ a_factory.update_item (l_item)
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Basic operation
|
||
+
|
||
+ gracefull_stop
|
||
+ -- Request the Current pool to terminate.
|
||
+ do
|
||
+ stop_requested := True
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Internal
|
||
+
|
||
+ items: SPECIAL [detachable separate G]
|
||
+ -- List of concurrent items.
|
||
+
|
||
+ busy_items: SPECIAL [BOOLEAN]
|
||
+ -- Map of items being proceed.
|
||
+
|
||
+feature {CONCURRENT_POOL_ITEM} -- Change
|
||
+
|
||
+ release_item (a_item: separate G)
|
||
+ -- Unregister `a_item' from Current pool.
|
||
+ require
|
||
+ count > 0
|
||
+ local
|
||
+ i,n,pos: INTEGER
|
||
+ lst: like items
|
||
+ do
|
||
+ -- release handler for reuse
|
||
+ from
|
||
+ lst := items
|
||
+ i := 0
|
||
+ n := lst.count - 1
|
||
+ until
|
||
+ i > n or lst [i] = a_item
|
||
+ loop
|
||
+ i := i + 1
|
||
+ end
|
||
+ if i <= n then
|
||
+ pos := i
|
||
+ busy_items [pos] := False
|
||
+ count := count - 1
|
||
+--reuse items [pos] := Void
|
||
+ debug ("pool", "dbglog")
|
||
+ dbglog ("Released pool item #" + i.out + " (free:"+ (capacity - count).out +"))")
|
||
+ end
|
||
+ else
|
||
+ check known_item: False end
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Change
|
||
+
|
||
+ set_count (n: INTEGER)
|
||
+ -- Set capacity of Current pool to `n'.
|
||
+ local
|
||
+ g: detachable separate G
|
||
+ do
|
||
+ capacity := n
|
||
+ items.fill_with (g, 0, n - 1)
|
||
+ busy_items.fill_with (False, 0, n - 1)
|
||
+ end
|
||
+
|
||
+ terminate
|
||
+ -- Terminate current pool.
|
||
+ local
|
||
+ l_items: like items
|
||
+ do
|
||
+ l_items := items
|
||
+ l_items.wipe_out
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Implementation
|
||
+
|
||
+ register_item (a_item: separate G)
|
||
+ -- Adopt `a_item' in current pool.
|
||
+ do
|
||
+ a_item.set_pool (Current)
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/pool/concurrent_pool_factory.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/pool/concurrent_pool_factory.e
|
||
new file mode 100644
|
||
index 0000000..92e0f2c
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/pool/concurrent_pool_factory.e
|
||
@@ -0,0 +1,31 @@
|
||
+note
|
||
+ description: "Factory in charge of creating new concurrent pool item."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ CONCURRENT_POOL_FACTORY [G -> CONCURRENT_POOL_ITEM]
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ update_item (a_item: separate G)
|
||
+ -- Update `a_item' for optionally purpose.
|
||
+ do
|
||
+ end
|
||
+
|
||
+ new_separate_item: separate G
|
||
+ -- New separated object of type {G}.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/pool/concurrent_pool_item.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/pool/concurrent_pool_item.e
|
||
new file mode 100644
|
||
index 0000000..2b5d1c6
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/scoop/pool/concurrent_pool_item.e
|
||
@@ -0,0 +1,52 @@
|
||
+note
|
||
+ description: "[
|
||
+ Item create by the CONCURRENT_POOL_FACTORY, and managed by the CONCURRENT_POOL
|
||
+ for SCOOP concurrency mode.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ CONCURRENT_POOL_ITEM
|
||
+
|
||
+feature {NONE} -- Access
|
||
+
|
||
+ pool: detachable separate CONCURRENT_POOL [CONCURRENT_POOL_ITEM]
|
||
+ -- Associated concurrent pool component.
|
||
+
|
||
+feature {CONCURRENT_POOL} -- Change
|
||
+
|
||
+ set_pool (p: like pool)
|
||
+ -- Set associated `pool' to `p'.
|
||
+ do
|
||
+ pool := p
|
||
+ end
|
||
+
|
||
+feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
|
||
+
|
||
+ release
|
||
+ -- Release Current pool item from associated pool.
|
||
+ do
|
||
+ if attached pool as p then
|
||
+ pool_release (p)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Implementation
|
||
+
|
||
+ pool_release (p: separate CONCURRENT_POOL [CONCURRENT_POOL_ITEM])
|
||
+ do
|
||
+ p.release_item (Current)
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/httpd_connection_handler.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/httpd_connection_handler.e
|
||
new file mode 100644
|
||
index 0000000..75115d3
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/httpd_connection_handler.e
|
||
@@ -0,0 +1,103 @@
|
||
+note
|
||
+ description: "[
|
||
+ Implementation of HTTPD_CONNECTION_HANDLER_I for concurrency mode: Thread
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_CONNECTION_HANDLER
|
||
+
|
||
+inherit
|
||
+ HTTPD_CONNECTION_HANDLER_I
|
||
+ redefine
|
||
+ initialize
|
||
+ end
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ initialize
|
||
+ local
|
||
+ n: INTEGER
|
||
+ do
|
||
+ n := max_concurrent_connections (server).max (1) -- At least one thread!
|
||
+ create pool.make (n.to_natural_32)
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ is_shutdown_requested: BOOLEAN
|
||
+
|
||
+ max_concurrent_connections (a_server: like server): INTEGER
|
||
+ do
|
||
+ Result := a_server.configuration.max_concurrent_connections
|
||
+ end
|
||
+
|
||
+feature {HTTPD_SERVER_I} -- Execution
|
||
+
|
||
+ shutdown
|
||
+ do
|
||
+ if not is_shutdown_requested then
|
||
+ is_shutdown_requested := True
|
||
+ pool_gracefull_stop (pool)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ pool_gracefull_stop (p: like pool)
|
||
+ do
|
||
+ p.terminate
|
||
+ end
|
||
+
|
||
+ accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
+ local
|
||
+ cl: separate HTTPD_STREAM_SOCKET
|
||
+ do
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".ENTER accept_connection {"+ a_listening_socket.descriptor.out +"}")
|
||
+ end
|
||
+
|
||
+ if is_shutdown_requested then
|
||
+ -- cancel
|
||
+ elseif attached factory.new_handler as h then
|
||
+ cl := h.client_socket
|
||
+ a_listening_socket.accept_to (cl)
|
||
+ if h.is_connected then
|
||
+ pool.add_work (agent h.safe_execute)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".LEAVE accept_incoming_connection {"+ a_listening_socket.descriptor.out +"}")
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {HTTPD_SERVER_I} -- Status report
|
||
+
|
||
+ wait_for_completion
|
||
+ -- Wait until Current is ready for shutdown
|
||
+ do
|
||
+ pool.wait_for_completion
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Access
|
||
+
|
||
+ pool: THREAD_POOL [HTTPD_REQUEST_HANDLER] --ANY] --POOLED_THREAD [HTTP_REQUEST_HANDLER]]
|
||
+ -- Pool of concurrent connection handlers.
|
||
+
|
||
+invariant
|
||
+ pool_attached: pool /= Void
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/httpd_request_handler.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/httpd_request_handler.e
|
||
new file mode 100644
|
||
index 0000000..57c6ecd
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/httpd_request_handler.e
|
||
@@ -0,0 +1,56 @@
|
||
+note
|
||
+ description: "[
|
||
+ Instance of HTTPD_REQUEST_HANDLER will process the incoming connection
|
||
+ and extract information on the request and the server
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_REQUEST_HANDLER
|
||
+
|
||
+inherit
|
||
+ HTTPD_REQUEST_HANDLER_I
|
||
+ redefine
|
||
+ release
|
||
+ end
|
||
+
|
||
+feature {HTTPD_CONNECTION_HANDLER_I} -- Basic operation
|
||
+
|
||
+ release
|
||
+ -- <Precursor>
|
||
+ local
|
||
+ d: detachable STRING
|
||
+ do
|
||
+ debug ("dbglog")
|
||
+ if
|
||
+ attached internal_client_socket as l_socket and then
|
||
+ l_socket.descriptor_available
|
||
+ then
|
||
+ d := l_socket.descriptor.out
|
||
+ else
|
||
+ d := "N/A"
|
||
+ end
|
||
+ dbglog (generator + ".release: ENTER {" + d + "}")
|
||
+ end
|
||
+ Precursor {HTTPD_REQUEST_HANDLER_I}
|
||
+ debug ("dbglog")
|
||
+ if d /= Void then
|
||
+ dbglog (generator + ".release: LEAVE {" + d + "}")
|
||
+ else
|
||
+ dbglog (generator + ".release: LEAVE {N/A}")
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/httpd_request_handler_factory.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/httpd_request_handler_factory.e
|
||
new file mode 100644
|
||
index 0000000..cf692e5
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/httpd_request_handler_factory.e
|
||
@@ -0,0 +1,15 @@
|
||
+note
|
||
+ description: "Implementation of request handler factory for concurrency mode: Thread"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_REQUEST_HANDLER_FACTORY
|
||
+
|
||
+inherit
|
||
+ HTTPD_REQUEST_HANDLER_FACTORY_I
|
||
+
|
||
+note
|
||
+ copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/pool/pooled_thread.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/pool/pooled_thread.e
|
||
new file mode 100644
|
||
index 0000000..393e028
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/pool/pooled_thread.e
|
||
@@ -0,0 +1,121 @@
|
||
+note
|
||
+ description: "{POOLED_THREAD} is used in combination with {THREAD_POOL} to allow for pooled threads."
|
||
+ legal: "See notice at end of class."
|
||
+ status: "Community Preview 1.0"
|
||
+ date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $"
|
||
+ revision: "$Revision: 80577 $"
|
||
+
|
||
+class
|
||
+ POOLED_THREAD [G]
|
||
+
|
||
+inherit
|
||
+ THREAD
|
||
+ rename
|
||
+ make as thread_make
|
||
+ end
|
||
+
|
||
+create {THREAD_POOL}
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (a_thread_pool: THREAD_POOL [G]; a_semaphore: SEMAPHORE)
|
||
+ -- `a_thread_pool', the pool in which this thread is managed
|
||
+ -- `a_semaphore' is used for execution suspending
|
||
+ do
|
||
+ thread_make
|
||
+ thread_pool := a_thread_pool
|
||
+ semaphore := a_semaphore
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Access
|
||
+
|
||
+ thread_pool: THREAD_POOL [G]
|
||
+ -- Pool manager in which this thread is pooled
|
||
+
|
||
+ target: detachable G
|
||
+ -- Target on which the `thread_procedure' should be applied
|
||
+ -- Depending on which launch is used, target is not used
|
||
+
|
||
+ thread_procedure: detachable PROCEDURE
|
||
+ -- Work that should be executed by the thread
|
||
+
|
||
+ semaphore: SEMAPHORE
|
||
+ -- Semaphore share with all threads in a thread pool
|
||
+ -- to suspend execution until more work is available
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ set_target (a_target: G)
|
||
+ -- Sets the target on which the work should be executed
|
||
+ do
|
||
+ target := a_target
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Implementation
|
||
+
|
||
+ execute
|
||
+ -- <Precursor>
|
||
+ local
|
||
+ done: BOOLEAN
|
||
+ do
|
||
+ from
|
||
+ semaphore.wait
|
||
+ thread_procedure := thread_pool.get_work (Current)
|
||
+ until
|
||
+ done
|
||
+ loop
|
||
+ if attached thread_procedure as l_work then
|
||
+ if attached target as t then
|
||
+ l_work.call ([t])
|
||
+ else
|
||
+ l_work.call (Void)
|
||
+ end
|
||
+ end
|
||
+ if thread_pool.over then
|
||
+ done := True
|
||
+ else
|
||
+ thread_procedure := thread_pool.get_work (Current)
|
||
+ if thread_procedure = Void then
|
||
+ semaphore.wait
|
||
+ thread_procedure := thread_pool.get_work (Current)
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ thread_pool.thread_terminated (Current)
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2012, Javier Velilla and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ licensing_options: "http://www.eiffel.com/licensing"
|
||
+ copying: "[
|
||
+ This file is part of Eiffel Software's Eiffel Development Environment.
|
||
+
|
||
+ Eiffel Software's Eiffel Development Environment is free
|
||
+ software; you can redistribute it and/or modify it under
|
||
+ the terms of the GNU General Public License as published
|
||
+ by the Free Software Foundation, version 2 of the License
|
||
+ (available at the URL listed under "license" above).
|
||
+
|
||
+ Eiffel Software's Eiffel Development Environment is
|
||
+ distributed in the hope that it will be useful, but
|
||
+ WITHOUT ANY WARRANTY; without even the implied warranty
|
||
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
+ See the GNU General Public License for more details.
|
||
+
|
||
+ You should have received a copy of the GNU General Public
|
||
+ License along with Eiffel Software's Eiffel Development
|
||
+ Environment; if not, write to the Free Software Foundation,
|
||
+ Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
+ ]"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
+
|
||
+
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/pool/thread_pool.e b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/pool/thread_pool.e
|
||
new file mode 100644
|
||
index 0000000..02e94d9
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/concurrency/thread/pool/thread_pool.e
|
||
@@ -0,0 +1,228 @@
|
||
+note
|
||
+ description: "[
|
||
+ A thread pool manager. Manages threads up to `capacity' and queue after that,
|
||
+ till threads get available again.
|
||
+ ]"
|
||
+ legal: "See notice at end of class."
|
||
+ status: "Community Preview 1.0"
|
||
+ date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $"
|
||
+ revision: "$Revision: 80577 $"
|
||
+
|
||
+class
|
||
+ THREAD_POOL [G]
|
||
+
|
||
+inherit
|
||
+ EXECUTION_ENVIRONMENT
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (n: like capacity)
|
||
+ -- Initialize current pool with capacity `n'.
|
||
+ require
|
||
+ n_positive: n > 0
|
||
+ n_not_too_large: n < {INTEGER_32}.max_value.as_natural_32
|
||
+ local
|
||
+ i: NATURAL
|
||
+ do
|
||
+ capacity := n
|
||
+ create work_queue.make (n.to_integer_32)
|
||
+ create work_queue_mutex.make
|
||
+ create over_mutex.make
|
||
+ create termination_mutex.make
|
||
+ create work_semaphore.make (capacity.as_integer_32)
|
||
+ from
|
||
+ i := 1
|
||
+ until
|
||
+ i > capacity
|
||
+ loop
|
||
+ work_semaphore.wait
|
||
+ i := i + 1
|
||
+ end
|
||
+ initialize_threads
|
||
+ terminated_count := capacity
|
||
+ is_over := False
|
||
+ ensure
|
||
+ capacity_set: capacity = n
|
||
+ work_queue_set: work_queue.is_empty
|
||
+ end
|
||
+
|
||
+ initialize_threads
|
||
+ -- Launches all threads
|
||
+ local
|
||
+ i: NATURAL
|
||
+ thread: POOLED_THREAD [G]
|
||
+ do
|
||
+ from
|
||
+ i := 1
|
||
+ until
|
||
+ i > capacity
|
||
+ loop
|
||
+ create thread.make (Current, work_semaphore)
|
||
+ thread.launch
|
||
+ i := i + 1
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ capacity: NATURAL
|
||
+ -- Maximal number of threads allowed (queuing otherwise)
|
||
+
|
||
+ queue_count: NATURAL
|
||
+ -- Number of items in queue
|
||
+ do
|
||
+ work_queue_mutex.lock
|
||
+ Result := work_queue.count.as_natural_32
|
||
+ work_queue_mutex.unlock
|
||
+ end
|
||
+
|
||
+feature -- Status report
|
||
+
|
||
+ valid_action (a_action: PROCEDURE): BOOLEAN
|
||
+ -- Is `a_action' a valid action for the current pool.
|
||
+ do
|
||
+ -- There should be no open operands.
|
||
+ Result := a_action.valid_operands (Void)
|
||
+ end
|
||
+
|
||
+feature -- Basic operations
|
||
+
|
||
+ add_work (work: PROCEDURE)
|
||
+ -- Launches a thread with the specified argument `arg'. Reuse of thread if possible.
|
||
+ require
|
||
+ valid_action: valid_action (work)
|
||
+ do
|
||
+ work_queue_mutex.lock
|
||
+ work_queue.extend (work)
|
||
+ if work_queue.count <= capacity.as_integer_32 then
|
||
+ -- Let one thread wake up and do the work
|
||
+ work_semaphore.post
|
||
+ end
|
||
+ work_queue_mutex.unlock
|
||
+ end
|
||
+
|
||
+ over: BOOLEAN
|
||
+ -- Is the thread pool being terminated?
|
||
+ do
|
||
+ over_mutex.lock
|
||
+ Result := is_over
|
||
+ over_mutex.unlock
|
||
+ end
|
||
+
|
||
+ thread_terminated (a_thread: POOLED_THREAD [G])
|
||
+ -- Notifies the thread pool that a thread has terminated its execution.
|
||
+ do
|
||
+ termination_mutex.lock
|
||
+ terminated_count := terminated_count - 1
|
||
+ termination_mutex.unlock
|
||
+ end
|
||
+
|
||
+ get_work (requester: POOLED_THREAD [G]): detachable PROCEDURE
|
||
+ -- If there is work to do, it is returned
|
||
+ -- Yields Void otherwise
|
||
+ do
|
||
+ if not over then
|
||
+ work_queue_mutex.lock
|
||
+ if not work_queue.is_empty then
|
||
+ Result := work_queue.item
|
||
+ work_queue.remove
|
||
+ end
|
||
+ work_queue_mutex.unlock
|
||
+ end
|
||
+ end
|
||
+
|
||
+ wait_for_completion
|
||
+ -- Wait until there is no more work to be completed
|
||
+ local
|
||
+ done: BOOLEAN
|
||
+ do
|
||
+ from
|
||
+
|
||
+ until
|
||
+ done
|
||
+ loop
|
||
+ work_queue_mutex.lock
|
||
+ done := work_queue.is_empty
|
||
+ work_queue_mutex.unlock
|
||
+ if not done then
|
||
+ sleep (1)
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+ terminate
|
||
+ -- Terminates all the threads after their execution
|
||
+ do
|
||
+ over_mutex.lock
|
||
+ is_over := True
|
||
+ over_mutex.unlock
|
||
+ from
|
||
+ termination_mutex.lock
|
||
+ until
|
||
+ terminated_count = 0
|
||
+ loop
|
||
+ work_semaphore.post
|
||
+ termination_mutex.unlock
|
||
+ termination_mutex.lock
|
||
+ end
|
||
+ termination_mutex.unlock
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Implementation: Access
|
||
+
|
||
+ work_queue: ARRAYED_QUEUE [PROCEDURE]
|
||
+ -- Queue that holds unprocessed requests as agents
|
||
+ -- Thread-safe access when accessor holds `queue_mutex'
|
||
+
|
||
+ work_queue_mutex: MUTEX
|
||
+ -- Mutex for the queue
|
||
+
|
||
+ work_semaphore: SEMAPHORE
|
||
+ -- Semaphore which hols the number of work to be done.
|
||
+ -- Needed to wake up worker threads
|
||
+
|
||
+ terminated_count: NATURAL
|
||
+ --
|
||
+
|
||
+ is_over: BOOLEAN
|
||
+ -- Is the thread pool being terminated?
|
||
+
|
||
+ over_mutex: MUTEX
|
||
+ -- Mutex for the `is_over' variable
|
||
+
|
||
+ termination_mutex: MUTEX
|
||
+;note
|
||
+ copyright: "2011-2012, Javier Velilla, Jocelyn Fiat and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ licensing_options: "http://www.eiffel.com/licensing"
|
||
+ copying: "[
|
||
+ This file is part of Eiffel Software's Eiffel Development Environment.
|
||
+
|
||
+ Eiffel Software's Eiffel Development Environment is free
|
||
+ software; you can redistribute it and/or modify it under
|
||
+ the terms of the GNU General Public License as published
|
||
+ by the Free Software Foundation, version 2 of the License
|
||
+ (available at the URL listed under "license" above).
|
||
+
|
||
+ Eiffel Software's Eiffel Development Environment is
|
||
+ distributed in the hope that it will be useful, but
|
||
+ WITHOUT ANY WARRANTY; without even the implied warranty
|
||
+ of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
+ See the GNU General Public License for more details.
|
||
+
|
||
+ You should have received a copy of the GNU General Public
|
||
+ License along with Eiffel Software's Eiffel Development
|
||
+ Environment; if not, write to the Free Software Foundation,
|
||
+ Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
+ ]"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/configuration/httpd_configuration_i.e b/library/server/ewsgi/connectors/standalone/lib/httpd/configuration/httpd_configuration_i.e
|
||
new file mode 100644
|
||
index 0000000..7c06b59
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/configuration/httpd_configuration_i.e
|
||
@@ -0,0 +1,348 @@
|
||
+note
|
||
+ description: "Configuration for the standalone HTTPd server."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_CONFIGURATION_I
|
||
+
|
||
+inherit
|
||
+ ANY
|
||
+
|
||
+ HTTPD_CONSTANTS
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make
|
||
+ do
|
||
+ 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
|
||
+ max_keep_alive_requests := default_max_keep_alive_requests
|
||
+ is_secure := False
|
||
+ create ca_crt.make_empty
|
||
+ create ca_key.make_empty
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ Server_details: STRING_8
|
||
+ -- Detail of the server.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ http_server_name: detachable READABLE_STRING_8 assign set_http_server_name
|
||
+ http_server_port: INTEGER assign set_http_server_port
|
||
+ max_tcp_clients: INTEGER assign set_max_tcp_clients
|
||
+ -- Listen on socket for at most `queue' connections.
|
||
+
|
||
+ socket_timeout: INTEGER assign set_socket_timeout
|
||
+ -- 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
|
||
+ -- 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.
|
||
+
|
||
+ max_concurrent_connections: INTEGER assign set_max_concurrent_connections
|
||
+ -- Max number of concurrent connections.
|
||
+
|
||
+ force_single_threaded: BOOLEAN assign set_force_single_threaded
|
||
+ do
|
||
+ Result := max_concurrent_connections = 0
|
||
+ end
|
||
+
|
||
+ is_verbose: BOOLEAN assign set_is_verbose
|
||
+ -- Display verbose message to the output?
|
||
+
|
||
+ verbose_level: INTEGER assign set_verbose_level
|
||
+ -- Verbosity of output.
|
||
+
|
||
+ keep_alive_timeout: INTEGER assign set_keep_alive_timeout
|
||
+ -- Persistent connection timeout.
|
||
+ -- Number of seconds the server waits after a request has been served before it closes the connection.
|
||
+ -- Timeout unit in Seconds.
|
||
+ -- By default: 5 seconds.
|
||
+
|
||
+ max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
|
||
+ -- Maximum number of requests allowed per persistent connection.
|
||
+ -- Recommended a high setting.
|
||
+ -- To disable KeepAlive, set `max_keep_alive_requests' to 0.
|
||
+ -- By default: 100 .
|
||
+
|
||
+ has_ssl_support: BOOLEAN
|
||
+ -- Has SSL support?
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ request_settings: HTTPD_REQUEST_SETTINGS
|
||
+ 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.max_keep_alive_requests := max_keep_alive_requests
|
||
+ Result.is_secure := is_secure
|
||
+ end
|
||
+
|
||
+feature -- Access: SSL
|
||
+
|
||
+ is_secure: BOOLEAN
|
||
+ -- Is SSL/TLS session?.
|
||
+
|
||
+ ca_crt: detachable IMMUTABLE_STRING_32
|
||
+ -- the signed certificate.
|
||
+
|
||
+ ca_key: detachable IMMUTABLE_STRING_32
|
||
+ -- private key to the certificate.
|
||
+
|
||
+ ssl_protocol: NATURAL
|
||
+ -- By default protocol is tls 1.2.
|
||
+
|
||
+feature -- Element change
|
||
+
|
||
+ set_ssl_settings (v: detachable separate TUPLE [protocol: separate READABLE_STRING_GENERAL; ca_crt, ca_key: detachable separate READABLE_STRING_GENERAL])
|
||
+ local
|
||
+ prot: STRING_32
|
||
+ do
|
||
+ is_secure := False
|
||
+ ca_crt := Void
|
||
+ ca_key := Void
|
||
+ if v /= Void then
|
||
+ is_secure := True
|
||
+ create prot.make_from_separate (v.protocol)
|
||
+ set_ssl_protocol_from_string (prot)
|
||
+ set_ca_crt (v.ca_crt)
|
||
+ set_ca_key (v.ca_key)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ set_http_server_name (v: detachable separate READABLE_STRING_8)
|
||
+ do
|
||
+ if v = Void then
|
||
+ unset_http_server_name
|
||
+ else
|
||
+ create {IMMUTABLE_STRING_8} http_server_name.make_from_separate (v)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ unset_http_server_name
|
||
+ -- Unset `http_server_name' value.
|
||
+ do
|
||
+ http_server_name := Void
|
||
+ ensure
|
||
+ unset_http_server_name: http_server_name = Void
|
||
+ end
|
||
+
|
||
+ set_http_server_port (v: like http_server_port)
|
||
+ -- Set `http_server_port' with `v'.
|
||
+ do
|
||
+ http_server_port := v
|
||
+ ensure
|
||
+ http_server_port_set: http_server_port = v
|
||
+ end
|
||
+
|
||
+ set_max_tcp_clients (v: like max_tcp_clients)
|
||
+ -- Set `max_tcp_clients' with `v'.
|
||
+ do
|
||
+ max_tcp_clients := v
|
||
+ ensure
|
||
+ max_tcp_clients_set: max_tcp_clients = v
|
||
+ end
|
||
+
|
||
+ set_max_concurrent_connections (v: like max_concurrent_connections)
|
||
+ -- Set `max_concurrent_connections' with `v'.
|
||
+ do
|
||
+ max_concurrent_connections := v
|
||
+ ensure
|
||
+ max_concurrent_connections_set : max_concurrent_connections = v
|
||
+ end
|
||
+
|
||
+ set_socket_timeout (a_nb_seconds: like socket_timeout)
|
||
+ -- Set `socket_timeout' with `a_nb_seconds'
|
||
+ do
|
||
+ socket_timeout := a_nb_seconds
|
||
+ ensure
|
||
+ socket_timeout_set: socket_timeout = a_nb_seconds
|
||
+ end
|
||
+
|
||
+ set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
|
||
+ -- 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
|
||
+ end
|
||
+
|
||
+ set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
|
||
+ -- Set `keep_alive_timeout' with `a_seconds'
|
||
+ do
|
||
+ keep_alive_timeout := a_seconds
|
||
+ ensure
|
||
+ keep_alive_timeout_set: keep_alive_timeout = a_seconds
|
||
+ end
|
||
+
|
||
+ set_max_keep_alive_requests (nb: like max_keep_alive_requests)
|
||
+ -- Set `max_keep_alive_requests' with `nb'
|
||
+ do
|
||
+ max_keep_alive_requests := nb
|
||
+ ensure
|
||
+ max_keep_alive_requests_set: max_keep_alive_requests = nb
|
||
+ end
|
||
+
|
||
+ set_force_single_threaded (v: like force_single_threaded)
|
||
+ -- Force server to handle incoming request in a single thread.
|
||
+ -- i.e set max_concurrent_connections to 0!
|
||
+ obsolete
|
||
+ "Use set_max_concurrent_connections (0) [June/2016]"
|
||
+ do
|
||
+ if v then
|
||
+ set_max_concurrent_connections (0)
|
||
+ end
|
||
+ --|Missing postcondition
|
||
+ --| force_single_thread_set: v implies max_concurrent_connections = 0
|
||
+ --| not_single_thread: not v implies max_concurrent_connections > 0
|
||
+ end
|
||
+
|
||
+ set_is_verbose (b: BOOLEAN)
|
||
+ -- Set `is_verbose' to `b'
|
||
+ do
|
||
+ is_verbose := b
|
||
+ ensure
|
||
+ is_verbose_set: is_verbose = b
|
||
+ end
|
||
+
|
||
+ set_verbose_level (lev: INTEGER)
|
||
+ -- Set `verbose_level' to `lev'.
|
||
+ do
|
||
+ verbose_level := lev
|
||
+ ensure
|
||
+ verbose_level_set: verbose_level = lev
|
||
+ end
|
||
+
|
||
+ set_is_secure (b: BOOLEAN)
|
||
+ -- Set `is_secure' to `b'.
|
||
+ do
|
||
+ if b and has_ssl_support then
|
||
+ is_secure := True
|
||
+ if
|
||
+ http_server_port = 80
|
||
+ then
|
||
+ set_http_server_port (443)
|
||
+ end
|
||
+ else
|
||
+ is_secure := False
|
||
+ if
|
||
+ http_server_port = 443
|
||
+ then
|
||
+ set_http_server_port (80)
|
||
+ end
|
||
+ end
|
||
+ ensure
|
||
+ is_secure_set: has_ssl_support implies is_secure
|
||
+ is_not_secure: not has_ssl_support implies not is_secure
|
||
+ end
|
||
+
|
||
+ mark_secure
|
||
+ -- Set is_secure in True
|
||
+ do
|
||
+ set_is_secure (True)
|
||
+ ensure
|
||
+ is_secure_set: has_ssl_support implies is_secure
|
||
+ -- http_server_port_set: has_ssl_support implies http_server_port = 443
|
||
+ is_not_secure: not has_ssl_support implies not is_secure
|
||
+ -- default_port: not has_ssl_support implies http_server_port = 80
|
||
+ end
|
||
+
|
||
+feature -- Element change
|
||
+
|
||
+ set_ca_crt (a_value: detachable separate READABLE_STRING_GENERAL)
|
||
+ -- Set `ca_crt' from `a_value'.
|
||
+ do
|
||
+ if a_value /= Void then
|
||
+ create ca_crt.make_from_separate (a_value)
|
||
+ else
|
||
+ ca_crt := Void
|
||
+ end
|
||
+ end
|
||
+
|
||
+ set_ca_key (a_value: detachable separate READABLE_STRING_GENERAL)
|
||
+ -- Set `ca_key' with `a_value'.
|
||
+ do
|
||
+ if a_value /= Void then
|
||
+ create ca_key.make_from_separate (a_value)
|
||
+ else
|
||
+ ca_key := Void
|
||
+ end
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol (a_version: NATURAL)
|
||
+ -- Set `ssl_protocol' with `a_version'
|
||
+ do
|
||
+ ssl_protocol := a_version
|
||
+ ensure
|
||
+ ssl_protocol_set: ssl_protocol = a_version
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
|
||
+ -- Set `ssl_protocol' with `a_ssl_version'
|
||
+ do
|
||
+ if a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then
|
||
+ set_ssl_protocol_to_ssl_2_or_3
|
||
+ elseif a_ssl_version.is_case_insensitive_equal ("tls_1_0") then
|
||
+ set_ssl_protocol_to_tls_1_0
|
||
+ elseif a_ssl_version.is_case_insensitive_equal ("tls_1_1") then
|
||
+ set_ssl_protocol_to_tls_1_1
|
||
+ elseif a_ssl_version.is_case_insensitive_equal ("tls_1_2") then
|
||
+ set_ssl_protocol_to_tls_1_2
|
||
+ elseif a_ssl_version.is_case_insensitive_equal ("dtls_1_0") then
|
||
+ set_ssl_protocol_to_dtls_1_0
|
||
+ else -- Default
|
||
+ set_ssl_protocol_to_tls_1_2
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- SSL Helpers
|
||
+
|
||
+ set_ssl_protocol_to_ssl_2_or_3
|
||
+ -- Set `ssl_protocol' with `Ssl_23'.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_tls_1_0
|
||
+ -- Set `ssl_protocol' with `Tls_1_0'.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_tls_1_1
|
||
+ -- Set `ssl_protocol' with `Tls_1_1'.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_tls_1_2
|
||
+ -- Set `ssl_protocol' with `Tls_1_2'.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_dtls_1_0
|
||
+ -- Set `ssl_protocol' with `Dtls_1_0'.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/configuration/httpd_constants.e b/library/server/ewsgi/connectors/standalone/lib/httpd/configuration/httpd_constants.e
|
||
new file mode 100644
|
||
index 0000000..71e4dcc
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/configuration/httpd_constants.e
|
||
@@ -0,0 +1,28 @@
|
||
+note
|
||
+ description: "[
|
||
+ Various constant values used in httpd settings.
|
||
+ ]"
|
||
+ author: "$Author$"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_CONSTANTS
|
||
+
|
||
+feature -- Default connection settings
|
||
+
|
||
+ default_http_server_port: INTEGER = 80
|
||
+ default_max_concurrent_connections: INTEGER = 100
|
||
+ default_max_tcp_clients: INTEGER = 100
|
||
+
|
||
+feature -- Default timeout settings
|
||
+
|
||
+ default_socket_timeout: INTEGER = 60 -- seconds
|
||
+ default_socket_recv_timeout: INTEGER = 5 -- seconds
|
||
+
|
||
+feature -- Default persistent connection settings
|
||
+
|
||
+ default_keep_alive_timeout: INTEGER = 15 -- seconds
|
||
+ default_max_keep_alive_requests: INTEGER = 100
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/configuration/httpd_request_settings.e b/library/server/ewsgi/connectors/standalone/lib/httpd/configuration/httpd_request_settings.e
|
||
new file mode 100644
|
||
index 0000000..a591cac
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/configuration/httpd_request_settings.e
|
||
@@ -0,0 +1,82 @@
|
||
+note
|
||
+ description: "[
|
||
+ Request settings for the standalone HTTPd server.
|
||
+ ]"
|
||
+ author: "$Author$"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+expanded class
|
||
+ HTTPD_REQUEST_SETTINGS
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ is_verbose: BOOLEAN assign set_is_verbose
|
||
+ -- Is verbose?
|
||
+
|
||
+ verbose_level: INTEGER assign set_verbose_level
|
||
+ -- Verbosity of output.
|
||
+
|
||
+ is_secure: BOOLEAN assign set_is_secure
|
||
+ -- Is using secure connection? i.e SSL?
|
||
+
|
||
+ timeout: INTEGER assign set_timeout
|
||
+ -- Amount of seconds that the server waits for receipts and transmissions during communications.
|
||
+
|
||
+ socket_recv_timeout: INTEGER assign set_socket_recv_timeout
|
||
+ -- 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, 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.
|
||
+
|
||
+ max_keep_alive_requests: INTEGER assign set_max_keep_alive_requests
|
||
+ -- Maximum number of requests allowed per persistent connection.
|
||
+
|
||
+feature -- Change
|
||
+
|
||
+ set_is_verbose (b: BOOLEAN)
|
||
+ -- Set `is_verbose' to `b'.
|
||
+ do
|
||
+ is_verbose := b
|
||
+ end
|
||
+
|
||
+ set_verbose_level (lev: INTEGER)
|
||
+ -- Set `verbose_level' to `lev'.
|
||
+ do
|
||
+ verbose_level := lev
|
||
+ end
|
||
+
|
||
+ set_is_secure (b: BOOLEAN)
|
||
+ -- Set `is_secure' to `b'.
|
||
+ do
|
||
+ is_secure := b
|
||
+ end
|
||
+
|
||
+ set_timeout (a_timeout_in_seconds: INTEGER)
|
||
+ -- Set `timeout' to `a_timeout_in_seconds'.
|
||
+ do
|
||
+ timeout := 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
|
||
+ 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
|
||
+ end
|
||
+
|
||
+ set_max_keep_alive_requests (nb: like max_keep_alive_requests)
|
||
+ -- Set `max_keep_alive_requests' with `nb'
|
||
+ do
|
||
+ max_keep_alive_requests := nb
|
||
+ end
|
||
+
|
||
+end
|
||
+
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/http_network-safe.ecf b/library/server/ewsgi/connectors/standalone/lib/httpd/http_network-safe.ecf
|
||
new file mode 100644
|
||
index 0000000..faea6dd
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/http_network-safe.ecf
|
||
@@ -0,0 +1,38 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="http_network" uuid="56DAA1CE-0A2E-451A-BFC9-7821578E79F0" library_target="http_network">
|
||
+ <target name="http_network">
|
||
+ <root all_classes="true"/>
|
||
+ <file_rule>
|
||
+ <exclude>/.git$</exclude>
|
||
+ <exclude>/.svn$</exclude>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
+ </file_rule>
|
||
+ <option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||
+ <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
+ </option>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
+ <library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf" readonly="false"/>
|
||
+ <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
|
||
+ <condition>
|
||
+ <custom name="net_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </library>
|
||
+ <cluster name="network" location=".\network\">
|
||
+ <cluster name="ssl_network" location="$|ssl\" recursive="true">
|
||
+ <condition>
|
||
+ <custom name="net_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_until_16_05" location="$|until_16_05\">
|
||
+ <condition>
|
||
+ <version type="compiler" max="16.11.0.0"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_from_16_11" location="$|from_16_11\">
|
||
+ <condition>
|
||
+ <version type="compiler" min="16.11.0.0"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ </cluster>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/http_network.ecf b/library/server/ewsgi/connectors/standalone/lib/httpd/http_network.ecf
|
||
new file mode 100644
|
||
index 0000000..25cc31e
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/http_network.ecf
|
||
@@ -0,0 +1,39 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="http_network" uuid="56DAA1CE-0A2E-451A-BFC9-7821578E79F0" library_target="http_network">
|
||
+ <target name="http_network">
|
||
+ <root all_classes="true"/>
|
||
+ <file_rule>
|
||
+ <exclude>/.git$</exclude>
|
||
+ <exclude>/.svn$</exclude>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
+ </file_rule>
|
||
+ <option warning="true" full_class_checking="false" void_safety="none" syntax="standard">
|
||
+ <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
+ </option>
|
||
+ <setting name="concurrency" value="scoop"/>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||
+ <library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||
+ <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||
+ <condition>
|
||
+ <custom name="net_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </library>
|
||
+ <cluster name="network" location=".\network\">
|
||
+ <cluster name="ssl_network" location="$|ssl\" recursive="true">
|
||
+ <condition>
|
||
+ <custom name="net_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_until_16_05" location="$|until_16_05\">
|
||
+ <condition>
|
||
+ <version type="compiler" max="16.11.0.0"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_from_16_11" location="$|from_16_11\">
|
||
+ <condition>
|
||
+ <version type="compiler" min="16.11.0.0"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ </cluster>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd-safe.ecf b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd-safe.ecf
|
||
new file mode 100644
|
||
index 0000000..d25947d
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd-safe.ecf
|
||
@@ -0,0 +1,78 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="httpd" uuid="50FE258D-CC94-4748-9223-55F1129E5FB3" library_target="httpd">
|
||
+ <target name="httpd">
|
||
+ <root all_classes="true"/>
|
||
+ <file_rule>
|
||
+ <exclude>/.git$</exclude>
|
||
+ <exclude>/.svn$</exclude>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
+ </file_rule>
|
||
+ <option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||
+ <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
+ </option>
|
||
+ <setting name="concurrency" value="scoop"/>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
+ <library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||
+ <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
|
||
+ <condition>
|
||
+ <custom name="httpd_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </library>
|
||
+ <library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf">
|
||
+ <condition>
|
||
+ <concurrency excluded_value="none"/>
|
||
+ </condition>
|
||
+ </library>
|
||
+ <library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||
+ <cluster name="network" location=".\network" recursive="false">
|
||
+ <cluster name="ssl_network" location="$|ssl" recursive="true">
|
||
+ <condition>
|
||
+ <custom name="httpd_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_until_16_05" location="$|until_16_05\">
|
||
+ <condition>
|
||
+ <version type="compiler" max="16.11.0.0"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_from_16_11" location="$|from_16_11\">
|
||
+ <condition>
|
||
+ <version type="compiler" min="16.11.0.0"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ </cluster>
|
||
+ <cluster name="httpd_server" location=".\" recursive="true">
|
||
+ <file_rule>
|
||
+ <exclude>/concurrency$</exclude>
|
||
+ <exclude>/no_ssl$</exclude>
|
||
+ <exclude>/ssl$</exclude>
|
||
+ <exclude>/network$</exclude>
|
||
+ </file_rule>
|
||
+ <cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
||
+ <condition>
|
||
+ <custom name="httpd_ssl_enabled" excluded_value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="ssl" location="$|ssl\" recursive="true">
|
||
+ <condition>
|
||
+ <custom name="httpd_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="concurrency_none" location="$|concurrency\none\" recursive="true">
|
||
+ <condition>
|
||
+ <concurrency value="none"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="concurrency_scoop" location="$|concurrency\scoop\" recursive="true">
|
||
+ <condition>
|
||
+ <concurrency value="scoop"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="concurrency_thread" location="$|concurrency\thread\" recursive="true">
|
||
+ <condition>
|
||
+ <concurrency value="thread"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ </cluster>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd.ecf b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd.ecf
|
||
new file mode 100644
|
||
index 0000000..9d62c60
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd.ecf
|
||
@@ -0,0 +1,77 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="httpd" uuid="50FE258D-CC94-4748-9223-55F1129E5FB3" library_target="httpd">
|
||
+ <target name="httpd">
|
||
+ <root all_classes="true"/>
|
||
+ <file_rule>
|
||
+ <exclude>/.git$</exclude>
|
||
+ <exclude>/.svn$</exclude>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
+ </file_rule>
|
||
+ <option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||
+ </option>
|
||
+ <setting name="concurrency" value="thread"/>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||
+ <library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||
+ <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||
+ <condition>
|
||
+ <custom name="httpd_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </library>
|
||
+ <library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf">
|
||
+ <condition>
|
||
+ <concurrency excluded_value="none"/>
|
||
+ </condition>
|
||
+ </library>
|
||
+ <library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||
+ <cluster name="network" location=".\network" recursive="false">
|
||
+ <cluster name="ssl_network" location="$|ssl" recursive="true">
|
||
+ <condition>
|
||
+ <custom name="httpd_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_until_16_05" location="$|until_16_05\">
|
||
+ <condition>
|
||
+ <version type="compiler" max="16.11.0.0"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_from_16_11" location="$|from_16_11\">
|
||
+ <condition>
|
||
+ <version type="compiler" min="16.11.0.0"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ </cluster>
|
||
+ <cluster name="httpd_server" location=".\" recursive="true">
|
||
+ <file_rule>
|
||
+ <exclude>/concurrency$</exclude>
|
||
+ <exclude>/no_ssl$</exclude>
|
||
+ <exclude>/ssl$</exclude>
|
||
+ <exclude>/network$</exclude>
|
||
+ </file_rule>
|
||
+ <cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
||
+ <condition>
|
||
+ <custom name="httpd_ssl_enabled" excluded_value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="ssl" location="$|ssl\" recursive="true">
|
||
+ <condition>
|
||
+ <custom name="httpd_ssl_enabled" value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="concurrency_none" location="$|concurrency\none\" recursive="true">
|
||
+ <condition>
|
||
+ <concurrency value="none"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="concurrency_scoop" location="$|concurrency\scoop\" recursive="true">
|
||
+ <condition>
|
||
+ <concurrency value="scoop"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="concurrency_thread" location="$|concurrency\thread\" recursive="true">
|
||
+ <condition>
|
||
+ <concurrency value="thread"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ </cluster>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_connection_handler_i.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_connection_handler_i.e
|
||
new file mode 100644
|
||
index 0000000..be3e51f
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_connection_handler_i.e
|
||
@@ -0,0 +1,97 @@
|
||
+note
|
||
+ description: "[
|
||
+ Interface for the incoming connection handler.
|
||
+
|
||
+ Each incoming socket connection is processed by
|
||
+ an implementation of HTTPD_CONNECTION_HANDLER_I.
|
||
+
|
||
+ Note there are 3 implementations, one for each concurrent mode: none, thread, scoop.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_CONNECTION_HANDLER_I
|
||
+
|
||
+inherit
|
||
+ HTTPD_DEBUG_FACILITIES
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ frozen make (a_server: like server)
|
||
+ do
|
||
+ server := a_server
|
||
+ factory := separate_factory (a_server)
|
||
+ initialize
|
||
+ end
|
||
+
|
||
+ initialize
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Access
|
||
+
|
||
+ factory: separate HTTPD_REQUEST_HANDLER_FACTORY
|
||
+ -- Request handler factory.
|
||
+
|
||
+ server: separate HTTPD_SERVER_I
|
||
+ -- Associated server.
|
||
+
|
||
+feature {HTTPD_SERVER_I} -- Execution
|
||
+
|
||
+ accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
+ -- Accept incoming connection from `a_listening_socket'.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ shutdown
|
||
+ -- Shutdown server.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ wait_for_completion
|
||
+ -- Wait until Current completed any pending task.
|
||
+ --| Used for SCOOP synchronisation.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+feature {HTTPD_SERVER} -- Status report
|
||
+
|
||
+ is_shutdown_requested: BOOLEAN
|
||
+ -- Any request to shutdown the server?
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Implementation
|
||
+
|
||
+ log (a_message: separate READABLE_STRING_8)
|
||
+ -- Log `a_message'
|
||
+ do
|
||
+ -- FIXME: Concurrency issue on `server'
|
||
+ separate_server_log (server, a_message)
|
||
+ end
|
||
+
|
||
+ separate_factory (a_server: like server): like factory
|
||
+ -- Separate factory from `a_server'.
|
||
+ --| required by SCOOP design.
|
||
+ do
|
||
+ Result := a_server.factory
|
||
+ end
|
||
+
|
||
+ separate_server_log (a_server: like server; a_message: separate READABLE_STRING_8)
|
||
+ -- Concurrent call to `a_server.log (a_message)'.
|
||
+ do
|
||
+ a_server.log (a_message)
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_controller.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_controller.e
|
||
new file mode 100644
|
||
index 0000000..f9a6667
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_controller.e
|
||
@@ -0,0 +1,28 @@
|
||
+note
|
||
+ description: "[
|
||
+ Object used to control (i.e shutdown) the server.
|
||
+ Mostly needed in SCOOP concurrency mode.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_CONTROLLER
|
||
+
|
||
+feature -- Operation
|
||
+
|
||
+ shutdown
|
||
+ -- Request the associated server to be shutdown.
|
||
+ do
|
||
+ shutdown_requested := True
|
||
+ end
|
||
+
|
||
+feature -- Status report.
|
||
+
|
||
+ shutdown_requested: BOOLEAN
|
||
+ -- Shutdown requested.
|
||
+
|
||
+;note
|
||
+ copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_debug_facilities.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_debug_facilities.e
|
||
new file mode 100644
|
||
index 0000000..3182332
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_debug_facilities.e
|
||
@@ -0,0 +1,50 @@
|
||
+note
|
||
+ description: " Routines used for debug logging."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_DEBUG_FACILITIES
|
||
+
|
||
+feature {NONE} -- Output
|
||
+
|
||
+ dbglog (m: READABLE_STRING_8)
|
||
+ require
|
||
+ not m.ends_with_general ("%N")
|
||
+ local
|
||
+ s: STRING
|
||
+ do
|
||
+ debug ("dbglog")
|
||
+ create s.make (24)
|
||
+ s.append ("[EWF/DBG] <#")
|
||
+ s.append_integer (processor_id_from_object (Current))
|
||
+ s.append ("> ")
|
||
+ s.append (generator)
|
||
+ s.append (create {STRING}.make_filled (' ', (46 - s.count).max (0)))
|
||
+ s.append (" | ")
|
||
+ s.append (m)
|
||
+ s.append ("%N")
|
||
+ print (s)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- runtime
|
||
+
|
||
+ frozen processor_id_from_object (a_object: separate ANY): INTEGER_32
|
||
+ external
|
||
+ "C inline use %"eif_scoop.h%""
|
||
+ alias
|
||
+ "RTS_PID(eif_access($a_object))"
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_logger.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_logger.e
|
||
new file mode 100644
|
||
index 0000000..057f43a
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_logger.e
|
||
@@ -0,0 +1,26 @@
|
||
+note
|
||
+ description: "Logging facilities component"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_LOGGER
|
||
+
|
||
+feature -- Logs
|
||
+
|
||
+ log (a_message: separate READABLE_STRING_8)
|
||
+ -- Log `a_message'
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_logger_constants.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_logger_constants.e
|
||
new file mode 100644
|
||
index 0000000..c75bec4
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_logger_constants.e
|
||
@@ -0,0 +1,22 @@
|
||
+note
|
||
+ description: "[
|
||
+ Constant value to define the logging level.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_LOGGER_CONSTANTS
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ alert_level: INTEGER = 1 -- 0000 0001
|
||
+ critical_level: INTEGER = 2 -- 0000 0010
|
||
+ error_level: INTEGER = 4 -- 0000 0100
|
||
+ warning_level: INTEGER = 8 -- 0000 1000
|
||
+
|
||
+ notice_level: INTEGER = 16 -- 0001 0000
|
||
+ information_level: INTEGER = 32 -- 0010 0000
|
||
+ debug_level: INTEGER = 64 -- 0100 0000
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_factory_i.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_factory_i.e
|
||
new file mode 100644
|
||
index 0000000..940d7a0
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_factory_i.e
|
||
@@ -0,0 +1,26 @@
|
||
+note
|
||
+ description: "Summary description for {HTTPD_REQUEST_HANDLER_FACTORY_I}."
|
||
+ author: ""
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_REQUEST_HANDLER_FACTORY_I
|
||
+
|
||
+feature -- Factory
|
||
+
|
||
+ new_handler: separate HTTPD_REQUEST_HANDLER
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_i.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_i.e
|
||
new file mode 100644
|
||
index 0000000..53ce713
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_request_handler_i.e
|
||
@@ -0,0 +1,527 @@
|
||
+note
|
||
+ description: "HTTPD handler interface processing request."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_REQUEST_HANDLER_I
|
||
+
|
||
+inherit
|
||
+ HTTPD_DEBUG_FACILITIES
|
||
+
|
||
+ HTTPD_LOGGER_CONSTANTS
|
||
+
|
||
+ HTTPD_SOCKET_FACTORY
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (a_request_settings: HTTPD_REQUEST_SETTINGS)
|
||
+ 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
|
||
+ max_keep_alive_requests := a_request_settings.max_keep_alive_requests
|
||
+
|
||
+ is_verbose := a_request_settings.is_verbose
|
||
+ verbose_level := a_request_settings.verbose_level
|
||
+ is_secure := a_request_settings.is_secure
|
||
+ end
|
||
+
|
||
+ reset
|
||
+ do
|
||
+ reset_request
|
||
+
|
||
+ has_error := False
|
||
+ if attached internal_client_socket as l_sock then
|
||
+ l_sock.cleanup
|
||
+ end
|
||
+ internal_client_socket := Void
|
||
+ end
|
||
+
|
||
+ reset_request
|
||
+ do
|
||
+ version := Void
|
||
+ remote_info := Void
|
||
+
|
||
+ -- FIXME: optimize to just wipe_out if needed
|
||
+ create method.make_empty
|
||
+ create uri.make_empty
|
||
+ create request_header.make_empty
|
||
+ create request_header_map.make (10)
|
||
+
|
||
+ is_persistent_connection_requested := False
|
||
+ end
|
||
+
|
||
+feature -- Status report
|
||
+
|
||
+ is_connected: BOOLEAN
|
||
+ -- Is handler connected to incoming request via `client_socket'?
|
||
+ do
|
||
+ Result := client_socket.descriptor_available
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ internal_client_socket: detachable HTTPD_STREAM_SOCKET
|
||
+
|
||
+ client_socket: HTTPD_STREAM_SOCKET
|
||
+ local
|
||
+ s: like internal_client_socket
|
||
+ do
|
||
+ s := internal_client_socket
|
||
+ if s = Void then
|
||
+ s := new_client_socket (is_secure)
|
||
+ internal_client_socket := s
|
||
+ end
|
||
+ Result := s
|
||
+ end
|
||
+
|
||
+ request_header: STRING
|
||
+ -- Header' source
|
||
+
|
||
+ request_header_map: HASH_TABLE [STRING, STRING]
|
||
+ -- Contains key:value of the header
|
||
+
|
||
+ method: STRING
|
||
+ -- http verb
|
||
+
|
||
+ uri: STRING
|
||
+ -- http endpoint
|
||
+
|
||
+ version: detachable STRING
|
||
+ -- http_version
|
||
+ --| unused for now
|
||
+
|
||
+ remote_info: detachable TUPLE [addr: STRING; hostname: STRING; port: INTEGER]
|
||
+ -- Information related to remote client
|
||
+
|
||
+ is_persistent_connection_requested: BOOLEAN
|
||
+ -- Persistent connection requested?
|
||
+ -- either has "Connection: keep-alive" header,
|
||
+ -- or is HTTP/1.1 and no header "Connection: close".
|
||
+
|
||
+ is_http_version_1_0: BOOLEAN
|
||
+ do
|
||
+ Result := not attached version as v or else v.same_string ("HTTP/1.0")
|
||
+ end
|
||
+
|
||
+ is_http_version_1_1: BOOLEAN
|
||
+ do
|
||
+ Result := not attached version as v or else v.same_string ("HTTP/1.1")
|
||
+ end
|
||
+
|
||
+ is_http_version_2: BOOLEAN
|
||
+ do
|
||
+ Result := not attached version as v or else v.same_string ("HTTP/2.0")
|
||
+ end
|
||
+
|
||
+feature -- Settings
|
||
+
|
||
+ is_verbose: BOOLEAN
|
||
+ -- Output messages?
|
||
+
|
||
+ verbose_level: INTEGER
|
||
+ -- Output verbosity.
|
||
+
|
||
+ is_secure: BOOLEAN
|
||
+ -- Is secure socket?
|
||
+ -- i.e: SSL?
|
||
+
|
||
+ is_persistent_connection_supported: BOOLEAN
|
||
+ -- Is persistent connection supported?
|
||
+ do
|
||
+ Result := {HTTPD_SERVER}.is_persistent_connection_supported and then max_keep_alive_requests > 0
|
||
+ end
|
||
+
|
||
+ is_next_persistent_connection_supported: BOOLEAN
|
||
+ -- 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.
|
||
+
|
||
+ socket_recv_timeout: INTEGER -- seconds
|
||
+ -- Amount of seconds 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.
|
||
+
|
||
+feature -- Status report
|
||
+
|
||
+ has_error: BOOLEAN
|
||
+ -- Error occurred during `analyze_request_message'
|
||
+
|
||
+feature -- Change
|
||
+
|
||
+ set_is_verbose (b: BOOLEAN)
|
||
+ -- Set `is_verbose' with `b'.
|
||
+ do
|
||
+ is_verbose := b
|
||
+ ensure
|
||
+ is_verbose_set: is_verbose = b
|
||
+ end
|
||
+
|
||
+feature -- Execution
|
||
+
|
||
+ safe_execute
|
||
+ -- Execute accepted incoming connection as request.
|
||
+ local
|
||
+ retried: BOOLEAN
|
||
+ do
|
||
+ if retried then
|
||
+ release
|
||
+ else
|
||
+ if
|
||
+ not has_error and then
|
||
+ is_connected
|
||
+ then
|
||
+ execute
|
||
+ end
|
||
+ release
|
||
+ end
|
||
+ rescue
|
||
+ retried := True
|
||
+ retry
|
||
+ end
|
||
+
|
||
+ execute
|
||
+ require
|
||
+ is_connected: is_connected
|
||
+ local
|
||
+ l_socket: like client_socket
|
||
+ l_exit: BOOLEAN
|
||
+ n,m: INTEGER
|
||
+ do
|
||
+ l_socket := client_socket
|
||
+ l_socket.set_recv_timeout (socket_recv_timeout)
|
||
+ check
|
||
+ socket_attached: l_socket /= Void
|
||
+ socket_valid: l_socket.is_open_read and then l_socket.is_open_write
|
||
+ end
|
||
+ from
|
||
+ -- Process persistent connection as long the socket is not closed.
|
||
+ n := 0
|
||
+ m := max_keep_alive_requests
|
||
+ is_next_persistent_connection_supported := True
|
||
+ until
|
||
+ l_exit
|
||
+ loop
|
||
+ n := n + 1
|
||
+ if n >= m then
|
||
+ is_next_persistent_connection_supported := False
|
||
+ elseif n > 1 and is_verbose then
|
||
+ log ("Reuse connection (" + n.out + ")", information_level)
|
||
+ end
|
||
+ -- FIXME: it seems to be called one more time, mostly to see this is done.
|
||
+ execute_request (n > 1)
|
||
+ l_exit := not is_persistent_connection_supported
|
||
+ or not is_next_persistent_connection_supported -- related to `max_keep_alive_requests'
|
||
+ or not is_persistent_connection_requested
|
||
+ or has_error or l_socket.is_closed or not l_socket.is_open_read
|
||
+ reset_request
|
||
+ end
|
||
+ if l_exit and has_error and not l_socket.is_closed then
|
||
+ l_socket.close
|
||
+ end
|
||
+ end
|
||
+
|
||
+ execute_request (a_is_reusing_connection: BOOLEAN)
|
||
+ -- Execute http request, and if `a_is_reusing_connection' is True
|
||
+ -- the execution is reusing the persistent connection.
|
||
+ require
|
||
+ is_connected: is_connected
|
||
+ reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported
|
||
+ local
|
||
+ l_remote_info: detachable like remote_info
|
||
+ l_socket: like client_socket
|
||
+ l_is_ready: BOOLEAN
|
||
+ do
|
||
+ l_socket := client_socket
|
||
+ check
|
||
+ socket_attached: l_socket /= Void
|
||
+ socket_valid: l_socket.is_open_read and then l_socket.is_open_write
|
||
+ end
|
||
+ if l_socket.is_closed then
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".execute_request {socket is Closed!}")
|
||
+ end
|
||
+ else
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
||
+ end
|
||
+
|
||
+ if a_is_reusing_connection then
|
||
+ --| set by default 5 seconds.
|
||
+ l_socket.set_recv_timeout (keep_alive_timeout) -- in seconds!
|
||
+ l_is_ready := l_socket.ready_for_reading
|
||
+ else
|
||
+ l_is_ready := True
|
||
+ end
|
||
+
|
||
+ if l_is_ready then
|
||
+ l_socket.set_recv_timeout (socket_recv_timeout) -- FIXME: return a 408 Request Timeout response ..
|
||
+ create l_remote_info
|
||
+ if attached l_socket.peer_address as l_addr then
|
||
+ l_remote_info.addr := l_addr.host_address.host_address
|
||
+ l_remote_info.hostname := l_addr.host_address.host_name
|
||
+ l_remote_info.port := l_addr.port
|
||
+ remote_info := l_remote_info
|
||
+ end
|
||
+ analyze_request_message (l_socket)
|
||
+ else
|
||
+ has_error := True
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + "} timeout!")
|
||
+ end
|
||
+ end
|
||
+
|
||
+ if has_error then
|
||
+ if l_is_ready then
|
||
+ -- check catch_bad_incoming_connection: False end
|
||
+ if is_verbose then
|
||
+ log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level)
|
||
+ end
|
||
+ end
|
||
+ process_bad_request (l_socket)
|
||
+ is_persistent_connection_requested := False
|
||
+ else
|
||
+ if is_verbose then
|
||
+ log (request_header, information_level)
|
||
+ end
|
||
+ process_request (l_socket)
|
||
+ end
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".execute_request {" + l_socket.descriptor.out + "} LEAVE")
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+ release
|
||
+ do
|
||
+ reset
|
||
+ end
|
||
+
|
||
+feature -- Request processing
|
||
+
|
||
+ process_bad_request (a_socket: HTTPD_STREAM_SOCKET)
|
||
+ require
|
||
+ has_error: has_error
|
||
+ a_socket_attached: a_socket /= Void
|
||
+ local
|
||
+ h: STRING
|
||
+ s: STRING
|
||
+ do
|
||
+ s := "{
|
||
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
|
||
+<html><head>
|
||
+<title>400 Bad Request</title>
|
||
+</head><body>
|
||
+<h1>Bad Request</h1>
|
||
+</body></html>
|
||
+ }"
|
||
+ create h.make (1_024)
|
||
+ h.append ("HTTP/1.1 400 Bad Request%R%N")
|
||
+ h.append ("Content-Length: " + s.count.out + "%R%N")
|
||
+ h.append ("Connection: close%R%N")
|
||
+ h.append ("Content-Type: text/html; charset=iso-8859-1%R%N")
|
||
+ h.append ("%R%N")
|
||
+ a_socket.put_string (h)
|
||
+ a_socket.put_string (s)
|
||
+ end
|
||
+
|
||
+ process_request (a_socket: HTTPD_STREAM_SOCKET)
|
||
+ -- Process request ...
|
||
+ require
|
||
+ no_error: not has_error
|
||
+ a_uri_attached: uri /= Void
|
||
+ a_method_attached: method /= Void
|
||
+ a_header_map_attached: request_header_map /= Void
|
||
+ a_header_text_attached: request_header /= Void
|
||
+ a_socket_attached: a_socket /= Void
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+feature -- Parsing
|
||
+
|
||
+ analyze_request_message (a_socket: HTTPD_STREAM_SOCKET)
|
||
+ -- Analyze message extracted from `a_socket' as HTTP request
|
||
+ require
|
||
+ input_readable: a_socket /= Void and then a_socket.is_open_read
|
||
+ local
|
||
+ end_of_stream: BOOLEAN
|
||
+ pos, n: INTEGER
|
||
+ line: detachable STRING
|
||
+ k, val: STRING
|
||
+ txt: STRING
|
||
+ l_is_verbose: BOOLEAN
|
||
+ do
|
||
+ create txt.make (64)
|
||
+ request_header := txt
|
||
+ if
|
||
+ not has_error and then
|
||
+ a_socket.is_readable and then
|
||
+ attached next_line (a_socket) as l_request_line and then
|
||
+ not l_request_line.is_empty
|
||
+ then
|
||
+ txt.append (l_request_line)
|
||
+ txt.append_character ('%N')
|
||
+ analyze_request_line (l_request_line)
|
||
+ else
|
||
+ has_error := True
|
||
+ end
|
||
+ l_is_verbose := is_verbose
|
||
+ if not has_error then
|
||
+ from
|
||
+ line := next_line (a_socket)
|
||
+ until
|
||
+ line = Void or end_of_stream or has_error
|
||
+ loop
|
||
+ n := line.count
|
||
+ debug ("ew_standalone")
|
||
+ if l_is_verbose then
|
||
+ log (line, debug_level)
|
||
+ end
|
||
+ end
|
||
+ pos := line.index_of (':', 1)
|
||
+ if pos > 0 then
|
||
+ k := line.substring (1, pos - 1)
|
||
+ if line [pos + 1].is_space then
|
||
+ pos := pos + 1
|
||
+ end
|
||
+ if line [n] = '%R' then
|
||
+ n := n - 1
|
||
+ end
|
||
+ val := line.substring (pos + 1, n)
|
||
+ request_header_map.put (val, k)
|
||
+ end
|
||
+ txt.append (line)
|
||
+ txt.append_character ('%N')
|
||
+ if line.is_empty or else line [1] = '%R' then
|
||
+ end_of_stream := True
|
||
+ else
|
||
+ line := next_line (a_socket)
|
||
+ end
|
||
+ end
|
||
+ -- Except for HTTP/1.0, persistent connection is the default.
|
||
+ is_persistent_connection_requested := True
|
||
+ if is_http_version_1_0 then
|
||
+ is_persistent_connection_requested := attached request_header_map.item ("Connection") as l_connection and then
|
||
+ l_connection.is_case_insensitive_equal_general ("keep-alive")
|
||
+ else
|
||
+ -- By default HTTP:1/1 support persistent connection.
|
||
+ if attached request_header_map.item ("Connection") as l_connection then
|
||
+ if l_connection.is_case_insensitive_equal_general ("close") then
|
||
+ is_persistent_connection_requested := False
|
||
+ end
|
||
+ else
|
||
+ is_persistent_connection_requested := True
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+ analyze_request_line (line: STRING)
|
||
+ -- Analyze `line' as a HTTP request line
|
||
+ require
|
||
+ valid_line: line /= Void and then not line.is_empty
|
||
+ local
|
||
+ n, pos, next_pos: INTEGER
|
||
+ do
|
||
+ debug ("ew_standalone")
|
||
+ if is_verbose then
|
||
+ log ("%N## Parse HTTP request line ##", debug_level)
|
||
+ log (line, debug_level)
|
||
+ end
|
||
+ end
|
||
+ pos := line.index_of (' ', 1)
|
||
+ method := line.substring (1, pos - 1)
|
||
+ next_pos := line.index_of (' ', pos + 1)
|
||
+ uri := line.substring (pos + 1, next_pos - 1)
|
||
+ n := line.count
|
||
+ if line[n] = '%R' then
|
||
+ n := n - 1
|
||
+ end
|
||
+ version := line.substring (next_pos + 1, n)
|
||
+ has_error := method.is_empty
|
||
+ end
|
||
+
|
||
+ next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
|
||
+ -- Next line fetched from `a_socket' is available.
|
||
+ require
|
||
+ not_has_error: not has_error or is_verbose
|
||
+ is_readable: a_socket.is_open_read
|
||
+ local
|
||
+ retried: BOOLEAN
|
||
+ do
|
||
+ if retried then
|
||
+ has_error := True
|
||
+ Result := Void
|
||
+ elseif a_socket.readable then
|
||
+ a_socket.read_line_thread_aware
|
||
+ Result := a_socket.last_string
|
||
+ -- Do no check `socket_ok' before socket operation,
|
||
+ -- otherwise it may be False, due to error during other socket operation in same thread.
|
||
+ if not a_socket.socket_ok then
|
||
+ has_error := True
|
||
+ if is_verbose then
|
||
+ log (request_header +"%N" + Result + "%N## socket_ok=False! ##", debug_level)
|
||
+ end
|
||
+ end
|
||
+ else
|
||
+ -- Error with socket...
|
||
+ has_error := True
|
||
+ if is_verbose then
|
||
+ log (request_header + "%N## Socket is not readable! ##", debug_level)
|
||
+ end
|
||
+ end
|
||
+ rescue
|
||
+ retried := True
|
||
+ retry
|
||
+ end
|
||
+
|
||
+feature -- Output
|
||
+
|
||
+ logger: detachable HTTPD_LOGGER
|
||
+
|
||
+ set_logger (a_logger: like logger)
|
||
+ -- Set `logger' with `a_logger'.
|
||
+ do
|
||
+ logger := a_logger
|
||
+ ensure
|
||
+ logger_set: logger = a_logger
|
||
+ end
|
||
+
|
||
+ log (m: STRING; a_level: INTEGER)
|
||
+ -- Log message `m'.
|
||
+ require
|
||
+ is_verbose: is_verbose
|
||
+ do
|
||
+ if is_verbose and (verbose_level & a_level) = a_level then
|
||
+ if attached logger as l_logger then
|
||
+ l_logger.log (m)
|
||
+ else
|
||
+ io.put_string (m + "%N")
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+invariant
|
||
+ request_header_attached: request_header /= Void
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_server_i.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_server_i.e
|
||
new file mode 100644
|
||
index 0000000..3b1ac5d
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_server_i.e
|
||
@@ -0,0 +1,377 @@
|
||
+note
|
||
+ description: "HTTPD server interface"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_SERVER_I
|
||
+
|
||
+inherit
|
||
+ HTTPD_DEBUG_FACILITIES
|
||
+
|
||
+ HTTPD_LOGGER
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (a_factory: like factory)
|
||
+ -- Create current httpd server with `a_factory' of connection handlers.
|
||
+ -- `a_factory': connection handler builder
|
||
+ require
|
||
+ a_factory_is_separated: {PLATFORM}.is_scoop_capable implies not attached {HTTPD_REQUEST_HANDLER_FACTORY} a_factory
|
||
+ do
|
||
+ make_configured (create {like configuration}.make, a_factory)
|
||
+ end
|
||
+
|
||
+ make_configured (a_cfg: like configuration; a_factory: like factory)
|
||
+ -- `a_cfg': server configuration
|
||
+ -- `a_factory': connection handler builder
|
||
+ do
|
||
+ configuration := a_cfg
|
||
+ factory := a_factory
|
||
+
|
||
+ build_controller
|
||
+
|
||
+ initialize
|
||
+ end
|
||
+
|
||
+ build_controller
|
||
+ -- Build `controller'.
|
||
+ do
|
||
+ create <NONE> controller
|
||
+ end
|
||
+
|
||
+ initialize
|
||
+ -- Initialize Current server.
|
||
+ do
|
||
+ is_shutdown_requested := False
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ is_verbose: BOOLEAN
|
||
+ -- Is verbose for output messages.
|
||
+
|
||
+ verbose_level: INTEGER
|
||
+ -- Verbosity of output.
|
||
+
|
||
+ configuration: HTTPD_CONFIGURATION
|
||
+ -- Associated server configuration.
|
||
+
|
||
+ controller: separate HTTPD_CONTROLLER
|
||
+
|
||
+ factory: separate HTTPD_REQUEST_HANDLER_FACTORY
|
||
+
|
||
+ is_persistent_connection_supported: BOOLEAN = True
|
||
+ -- Is persistent connection supported?
|
||
+
|
||
+feature -- Callbacks
|
||
+
|
||
+ observer: detachable separate HTTPD_SERVER_OBSERVER
|
||
+
|
||
+ set_observer (obs: like observer)
|
||
+ -- Set `observer' to `obs'.
|
||
+ do
|
||
+ observer := obs
|
||
+ end
|
||
+
|
||
+feature -- Access: listening
|
||
+
|
||
+ port: INTEGER
|
||
+ -- Effective listening port.
|
||
+ --| If 0 then it is not launched successfully!
|
||
+
|
||
+feature -- Status: listening
|
||
+
|
||
+ is_launched: BOOLEAN
|
||
+ -- Server launched and listening on `port'
|
||
+
|
||
+ is_terminated: BOOLEAN
|
||
+ -- Is terminated?
|
||
+
|
||
+ is_shutdown_requested: BOOLEAN
|
||
+ -- Set true to stop accept loop
|
||
+
|
||
+feature {NONE} -- Access: server
|
||
+
|
||
+ request_counter: INTEGER
|
||
+ -- request counter, incremented for each new incoming connection.
|
||
+
|
||
+feature -- Execution
|
||
+
|
||
+ launch
|
||
+ do
|
||
+ apply_configuration
|
||
+ is_terminated := False
|
||
+ if is_verbose then
|
||
+ log ("%N%NStarting Web Application Server ...")
|
||
+ 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 (" - max_keep_alive_requests = " + configuration.max_keep_alive_requests.out)
|
||
+ if configuration.has_ssl_support then
|
||
+ if configuration.is_secure then
|
||
+ log (" - SSL = enabled")
|
||
+ else
|
||
+ log (" - SSL = disabled")
|
||
+ end
|
||
+ else
|
||
+ log (" - SSL = not supported")
|
||
+ end
|
||
+ if configuration.verbose_level > 0 then
|
||
+ log (" - verbose_level = " + configuration.verbose_level.out)
|
||
+ end
|
||
+ end
|
||
+ is_shutdown_requested := False
|
||
+ listen
|
||
+ is_terminated := True
|
||
+ on_terminated
|
||
+ end
|
||
+
|
||
+ shutdown_server
|
||
+ do
|
||
+ debug ("dbglog")
|
||
+ dbglog ("Shutdown requested")
|
||
+ end
|
||
+ is_shutdown_requested := True
|
||
+ controller_shutdown (controller)
|
||
+ end
|
||
+
|
||
+ controller_shutdown (ctl: attached like controller)
|
||
+ do
|
||
+ ctl.shutdown
|
||
+ end
|
||
+
|
||
+feature -- Listening
|
||
+
|
||
+ listen
|
||
+ -- <Precursor>
|
||
+ -- Creates a socket and connects to the http server.
|
||
+ -- `a_server': The main server object
|
||
+ local
|
||
+ l_listening_socket: detachable HTTPD_STREAM_SOCKET
|
||
+ l_http_port: INTEGER
|
||
+ l_connection_handler: HTTPD_CONNECTION_HANDLER
|
||
+ do
|
||
+ is_terminated := False
|
||
+ is_launched := False
|
||
+ port := 0
|
||
+ is_shutdown_requested := False
|
||
+ l_http_port := configuration.http_server_port
|
||
+
|
||
+ if
|
||
+ attached configuration.http_server_name as l_servername and then
|
||
+ attached (create {INET_ADDRESS_FACTORY}).create_from_name (l_servername) as l_addr
|
||
+ then
|
||
+ l_listening_socket := new_listening_socket (l_addr, l_http_port)
|
||
+ else
|
||
+ l_listening_socket := new_listening_socket (Void, l_http_port)
|
||
+ end
|
||
+
|
||
+ if not l_listening_socket.is_bound then
|
||
+ if is_verbose then
|
||
+ log ("Socket could not be bound on port " + l_http_port.out + " !")
|
||
+ end
|
||
+ else
|
||
+ l_http_port := l_listening_socket.port
|
||
+ create l_connection_handler.make (Current)
|
||
+ from
|
||
+ l_listening_socket.listen (configuration.max_tcp_clients)
|
||
+ if is_verbose then
|
||
+ if configuration.is_secure then
|
||
+ log ("%NListening on port " + l_http_port.out +" : https://localhost:" + l_http_port.out + "/")
|
||
+ else
|
||
+ log ("%NListening on port " + l_http_port.out +" : http://localhost:" + l_http_port.out + "/")
|
||
+ end
|
||
+ end
|
||
+ on_launched (l_http_port)
|
||
+ until
|
||
+ is_shutdown_requested
|
||
+ loop
|
||
+ request_counter := request_counter + 1
|
||
+ if is_verbose then
|
||
+ log ("#" + request_counter.out + "# Waiting connection...(listening socket:" + l_listening_socket.descriptor.out + ")")
|
||
+ end
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".before process_waiting_incoming_connection")
|
||
+ end
|
||
+ l_connection_handler.accept_incoming_connection (l_listening_socket)
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".after process_waiting_incoming_connection")
|
||
+ end
|
||
+
|
||
+ update_is_shutdown_requested (l_connection_handler)
|
||
+ end
|
||
+ wait_for_connection_handler_completion (l_connection_handler)
|
||
+ l_listening_socket.cleanup
|
||
+ check
|
||
+ socket_is_closed: l_listening_socket.is_closed
|
||
+ end
|
||
+ end
|
||
+ if is_launched then
|
||
+ on_stopped
|
||
+ end
|
||
+ if is_verbose then
|
||
+ log ("HTTP Connection Server ends.")
|
||
+ end
|
||
+ rescue
|
||
+ log ("HTTP Connection Server shutdown due to exception. Please relaunch manually.")
|
||
+
|
||
+ if l_listening_socket /= Void then
|
||
+ l_listening_socket.cleanup
|
||
+ check
|
||
+ listening_socket_is_closed: l_listening_socket.is_closed
|
||
+ end
|
||
+ end
|
||
+ if is_launched then
|
||
+ on_stopped
|
||
+ end
|
||
+ is_shutdown_requested := True
|
||
+ retry
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Factory
|
||
+
|
||
+ new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET
|
||
+ do
|
||
+ if a_addr /= Void then
|
||
+ create Result.make_server_by_address_and_port (a_addr, a_http_port)
|
||
+ else
|
||
+ create Result.make_server_by_port (a_http_port)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Helpers
|
||
+
|
||
+ wait_for_connection_handler_completion (h: HTTPD_CONNECTION_HANDLER)
|
||
+ do
|
||
+ h.wait_for_completion
|
||
+ debug ("dbglog")
|
||
+ dbglog ("Shutdown ready from connection_handler point of view")
|
||
+ end
|
||
+ end
|
||
+
|
||
+ update_is_shutdown_requested (a_connection_handler: HTTPD_CONNECTION_HANDLER)
|
||
+ do
|
||
+ is_shutdown_requested := is_shutdown_requested or shutdown_requested (controller)
|
||
+ if is_shutdown_requested then
|
||
+ a_connection_handler.shutdown
|
||
+ end
|
||
+ end
|
||
+
|
||
+ shutdown_requested (a_controller: separate HTTPD_CONTROLLER): BOOLEAN
|
||
+ -- Shutdown requested on concurrent `a_controller'?
|
||
+ do
|
||
+ Result := a_controller.shutdown_requested
|
||
+ end
|
||
+
|
||
+feature -- Event
|
||
+
|
||
+ on_launched (a_port: INTEGER)
|
||
+ -- Server launched using port `a_port'
|
||
+ require
|
||
+ not_launched: not is_launched
|
||
+ do
|
||
+ is_launched := True
|
||
+ port := a_port
|
||
+ if attached observer as obs then
|
||
+ observer_on_launched (obs, a_port)
|
||
+ end
|
||
+ ensure
|
||
+ is_launched: is_launched
|
||
+ end
|
||
+
|
||
+ on_stopped
|
||
+ -- Server stopped
|
||
+ require
|
||
+ is_launched: is_launched
|
||
+ do
|
||
+ if attached observer as obs then
|
||
+ observer_on_stopped (obs)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ on_terminated
|
||
+ -- Server terminated
|
||
+ require
|
||
+ is_terminated
|
||
+ do
|
||
+ if is_terminated and is_verbose then
|
||
+ log ("%N%NTerminating Web Application Server (port="+ port.out +"):%N")
|
||
+ end
|
||
+ if attached output as o then
|
||
+ o.flush
|
||
+ o.close
|
||
+ end
|
||
+ if attached observer as obs then
|
||
+ observer_on_terminated (obs)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Separate event
|
||
+
|
||
+ observer_on_launched (obs: attached like observer; a_port: INTEGER)
|
||
+ do
|
||
+ obs.on_launched (a_port)
|
||
+ end
|
||
+
|
||
+ observer_on_stopped (obs: attached like observer)
|
||
+ do
|
||
+ obs.on_stopped
|
||
+ end
|
||
+
|
||
+ observer_on_terminated (obs: attached like observer)
|
||
+ do
|
||
+ obs.on_terminated
|
||
+ end
|
||
+
|
||
+feature -- Configuration change
|
||
+
|
||
+ apply_configuration
|
||
+ require
|
||
+ is_not_launched: not is_launched
|
||
+ do
|
||
+ is_verbose := configuration.is_verbose
|
||
+ verbose_level := configuration.verbose_level
|
||
+ end
|
||
+
|
||
+feature -- Output
|
||
+
|
||
+ output: detachable FILE
|
||
+
|
||
+ set_log_output (f: FILE)
|
||
+ -- Set `output' to `f'.
|
||
+ do
|
||
+ output := f
|
||
+ ensure
|
||
+ output_set: output = f
|
||
+ end
|
||
+
|
||
+ log (a_message: separate READABLE_STRING_8)
|
||
+ -- Log `a_message'.
|
||
+ local
|
||
+ m: STRING
|
||
+ do
|
||
+ create m.make_from_separate (a_message)
|
||
+ if attached output as o then
|
||
+ o.put_string (m)
|
||
+ o.put_new_line
|
||
+ else
|
||
+ io.error.put_string (m)
|
||
+ io.error.put_new_line
|
||
+ end
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_server_observer.e b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_server_observer.e
|
||
new file mode 100644
|
||
index 0000000..18a5998
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/httpd_server_observer.e
|
||
@@ -0,0 +1,27 @@
|
||
+note
|
||
+ description: "Summary description for {HTTPD_SERVER_OBSERVER}."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_SERVER_OBSERVER
|
||
+
|
||
+feature -- Event
|
||
+
|
||
+ on_launched (a_port: INTEGER)
|
||
+ -- Associated server launched listening on port `a_port'.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ on_stopped
|
||
+ -- Associated server stopped.
|
||
+ --| the server may restart itself after being rescued.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ on_terminated
|
||
+ -- Associated server terminated.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/from_16_11/tcp_stream_socket_ext.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/from_16_11/tcp_stream_socket_ext.e
|
||
new file mode 100644
|
||
index 0000000..65d4d70
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/from_16_11/tcp_stream_socket_ext.e
|
||
@@ -0,0 +1,9 @@
|
||
+note
|
||
+ description: "[
|
||
+ Since 16.11, the EiffelNet socket interface has recv_timeout and send_timeout.
|
||
+ ]"
|
||
+
|
||
+deferred class
|
||
+ TCP_STREAM_SOCKET_EXT
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket.e
|
||
new file mode 100644
|
||
index 0000000..76c0bb3
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/httpd_stream_socket.e
|
||
@@ -0,0 +1,358 @@
|
||
+note
|
||
+ description: "[
|
||
+ Summary description for {HTTPD_STREAM_SOCKET}
|
||
+ that can be used for http or https connection.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_STREAM_SOCKET
|
||
+
|
||
+create
|
||
+ make_server_by_address_and_port,
|
||
+ make_server_by_port,
|
||
+ make_client_by_address_and_port,
|
||
+ make_client_by_port,
|
||
+ make_from_separate,
|
||
+ make_empty
|
||
+
|
||
+create {HTTPD_STREAM_SOCKET}
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER)
|
||
+ do
|
||
+ create {TCP_STREAM_SOCKET} socket.make_server_by_address_and_port (an_address, a_port)
|
||
+ end
|
||
+
|
||
+ make_server_by_port (a_port: INTEGER)
|
||
+ do
|
||
+ create {TCP_STREAM_SOCKET} socket.make_server_by_port (a_port)
|
||
+ end
|
||
+
|
||
+ make_client_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER)
|
||
+ do
|
||
+ create {TCP_STREAM_SOCKET} socket.make_client_by_address_and_port (an_address, a_port)
|
||
+ end
|
||
+
|
||
+ make_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING)
|
||
+ do
|
||
+ create {TCP_STREAM_SOCKET} socket.make_client_by_port (a_peer_port, a_peer_host)
|
||
+ end
|
||
+
|
||
+ make_from_separate (s: separate HTTPD_STREAM_SOCKET)
|
||
+ require
|
||
+ descriptor_available: s.descriptor_available
|
||
+ do
|
||
+ create {TCP_STREAM_SOCKET} socket.make_from_separate (s.socket)
|
||
+ end
|
||
+
|
||
+ make_empty
|
||
+ do
|
||
+ create {TCP_STREAM_SOCKET} socket.make_empty
|
||
+ end
|
||
+
|
||
+ retrieve_socket (s: HTTPD_STREAM_SOCKET): INTEGER
|
||
+ do
|
||
+ Result := s.socket.descriptor
|
||
+ end
|
||
+
|
||
+feature -- Change
|
||
+
|
||
+ set_timeout (n: INTEGER)
|
||
+ -- Set timeout to `n' seconds.
|
||
+ do
|
||
+ if attached {NETWORK_STREAM_SOCKET} socket as l_socket then
|
||
+ l_socket.set_timeout (n)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ set_connect_timeout (n: INTEGER)
|
||
+ do
|
||
+ if attached {NETWORK_STREAM_SOCKET} socket as l_socket then
|
||
+ l_socket.set_connect_timeout (n)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ set_accept_timeout (n: INTEGER)
|
||
+ do
|
||
+ if attached {NETWORK_STREAM_SOCKET} socket as l_socket then
|
||
+ l_socket.set_accept_timeout (n)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ set_recv_timeout (a_timeout_seconds: INTEGER)
|
||
+ -- Set the receive timeout in seconds on Current socket.
|
||
+ do
|
||
+ if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
+ l_socket.set_recv_timeout (a_timeout_seconds)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ set_send_timeout (a_timeout_seconds: INTEGER)
|
||
+ -- Set the send timeout in seconds on Current socket.
|
||
+ do
|
||
+ if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
+ l_socket.set_send_timeout (a_timeout_seconds)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ last_string: STRING
|
||
+ do
|
||
+ Result := socket.last_string
|
||
+ end
|
||
+
|
||
+ last_character: CHARACTER
|
||
+ do
|
||
+ Result := socket.last_character
|
||
+ end
|
||
+
|
||
+ peer_address: detachable NETWORK_SOCKET_ADDRESS
|
||
+ -- Peer address of socket
|
||
+ do
|
||
+ if attached {NETWORK_SOCKET_ADDRESS} socket.peer_address as l_peer_address then
|
||
+ Result := l_peer_address
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Input
|
||
+
|
||
+ read_line_thread_aware
|
||
+ do
|
||
+ socket.read_line_thread_aware
|
||
+ end
|
||
+
|
||
+ read_stream_thread_aware (nb: INTEGER)
|
||
+ do
|
||
+ socket.read_stream_thread_aware (nb)
|
||
+ end
|
||
+
|
||
+ read_stream (nb: INTEGER)
|
||
+ do
|
||
+ socket.read_stream (nb)
|
||
+ end
|
||
+
|
||
+ read_character
|
||
+ do
|
||
+ socket.read_character
|
||
+ end
|
||
+
|
||
+ bytes_read: INTEGER
|
||
+ do
|
||
+ Result := socket.bytes_read
|
||
+ end
|
||
+
|
||
+feature -- Output
|
||
+
|
||
+ send_message (a_msg: STRING)
|
||
+ do
|
||
+ put_string (a_msg)
|
||
+ end
|
||
+
|
||
+ put_readable_string_8 (s: READABLE_STRING_8)
|
||
+ -- Write readable string `s' to socket.
|
||
+ do
|
||
+ if attached {TCP_STREAM_SOCKET} socket as l_tcp_stream_socket then
|
||
+ l_tcp_stream_socket.put_readable_string_8 (s)
|
||
+ else
|
||
+ put_string (s)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ put_string (s: STRING)
|
||
+ do
|
||
+ socket.put_string (s)
|
||
+ end
|
||
+
|
||
+ put_character (c: CHARACTER)
|
||
+ do
|
||
+ socket.put_character (c)
|
||
+ end
|
||
+
|
||
+feature -- Status Report
|
||
+
|
||
+ descriptor_available: BOOLEAN
|
||
+ -- Is descriptor available?
|
||
+ do
|
||
+ Result := socket.descriptor_available
|
||
+ end
|
||
+
|
||
+ descriptor: INTEGER
|
||
+ do
|
||
+ Result := socket.descriptor
|
||
+ end
|
||
+
|
||
+ port: INTEGER
|
||
+ do
|
||
+ if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
+ Result := l_socket.port
|
||
+ end
|
||
+ end
|
||
+
|
||
+ exists: BOOLEAN
|
||
+ do
|
||
+ Result := socket.exists
|
||
+ end
|
||
+
|
||
+ is_blocking: BOOLEAN
|
||
+ do
|
||
+ Result := socket.is_blocking
|
||
+ end
|
||
+
|
||
+ is_bound: BOOLEAN
|
||
+ do
|
||
+ if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
+ Result := l_socket.is_bound
|
||
+ end
|
||
+ end
|
||
+
|
||
+ is_connected: BOOLEAN
|
||
+ do
|
||
+ if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
+ Result := l_socket.is_connected
|
||
+ end
|
||
+ end
|
||
+
|
||
+ is_created: BOOLEAN
|
||
+ do
|
||
+ if attached {NETWORK_SOCKET} socket as l_socket then
|
||
+ Result := l_socket.is_created
|
||
+ end
|
||
+ end
|
||
+
|
||
+ socket_ok: BOOLEAN
|
||
+ do
|
||
+ Result := socket.socket_ok
|
||
+ end
|
||
+
|
||
+ is_open_read: BOOLEAN
|
||
+ do
|
||
+ Result := socket.is_open_read
|
||
+ end
|
||
+
|
||
+ is_open_write: BOOLEAN
|
||
+ do
|
||
+ Result := socket.is_open_write
|
||
+ end
|
||
+
|
||
+ is_closed: BOOLEAN
|
||
+ do
|
||
+ Result := socket.is_closed
|
||
+ end
|
||
+
|
||
+ is_readable: BOOLEAN
|
||
+ do
|
||
+ Result := socket.is_readable
|
||
+ end
|
||
+
|
||
+ cleanup
|
||
+ do
|
||
+ socket.cleanup
|
||
+ end
|
||
+
|
||
+ ready_for_writing: BOOLEAN
|
||
+ do
|
||
+ if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
+ Result := l_socket.ready_for_writing
|
||
+ end
|
||
+ end
|
||
+
|
||
+ connect
|
||
+ do
|
||
+ socket.connect
|
||
+ end
|
||
+
|
||
+ close
|
||
+ do
|
||
+ socket.close
|
||
+ end
|
||
+
|
||
+ listen (a_queue: INTEGER)
|
||
+ do
|
||
+ socket.listen (a_queue)
|
||
+ end
|
||
+
|
||
+ accept
|
||
+ do
|
||
+ socket.accept
|
||
+ end
|
||
+
|
||
+ accept_to (other: separate HTTPD_STREAM_SOCKET)
|
||
+ -- Accept a new connection on listen socket.
|
||
+ -- Socket of accepted connection is available in `other'.
|
||
+ do
|
||
+ if
|
||
+ attached {NETWORK_STREAM_SOCKET} socket as l_socket and then
|
||
+ attached {separate NETWORK_STREAM_SOCKET} other.socket as l_other_socket
|
||
+ then
|
||
+ l_socket.accept_to (l_other_socket)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ set_blocking
|
||
+ do
|
||
+ socket.set_blocking
|
||
+ end
|
||
+
|
||
+ set_non_blocking
|
||
+ do
|
||
+ socket.set_non_blocking
|
||
+ end
|
||
+
|
||
+ readable: BOOLEAN
|
||
+ do
|
||
+ Result := socket.readable
|
||
+ end
|
||
+
|
||
+ ready_for_reading: BOOLEAN
|
||
+ do
|
||
+ if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
+ Result := l_socket.ready_for_reading
|
||
+ end
|
||
+ end
|
||
+
|
||
+ try_ready_for_reading: BOOLEAN
|
||
+ do
|
||
+ if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
+ Result := l_socket.try_ready_for_reading
|
||
+ end
|
||
+ end
|
||
+
|
||
+ accepted: detachable HTTPD_STREAM_SOCKET
|
||
+ do
|
||
+ if attached {NETWORK_STREAM_SOCKET} socket.accepted as l_accepted then
|
||
+ create Result.make (l_accepted)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {HTTPD_STREAM_SOCKET} -- Implementation
|
||
+
|
||
+ make (a_socket: STREAM_SOCKET)
|
||
+ do
|
||
+ socket := a_socket
|
||
+ end
|
||
+
|
||
+ socket: STREAM_SOCKET
|
||
+
|
||
+ network_stream_socket: detachable NETWORK_STREAM_SOCKET
|
||
+ do
|
||
+ if attached {NETWORK_STREAM_SOCKET} socket as s then
|
||
+ Result := s
|
||
+ end
|
||
+ end
|
||
+
|
||
+;note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket.e
|
||
new file mode 100644
|
||
index 0000000..1217d0d
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/httpd_stream_ssl_socket.e
|
||
@@ -0,0 +1,169 @@
|
||
+note
|
||
+ description: "[
|
||
+ Summary description for {HTTPD_STREAM_SSL_SOCKET}
|
||
+ that can be used for http or https connection.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_STREAM_SSL_SOCKET
|
||
+
|
||
+inherit
|
||
+ HTTPD_STREAM_SOCKET
|
||
+ redefine
|
||
+ port,
|
||
+ is_bound,
|
||
+ ready_for_writing,
|
||
+ ready_for_reading,
|
||
+ try_ready_for_reading,
|
||
+ put_readable_string_8,
|
||
+ make_empty
|
||
+ end
|
||
+
|
||
+create
|
||
+ make_ssl_server_by_address_and_port, make_ssl_server_by_port,
|
||
+ make_server_by_address_and_port, make_server_by_port,
|
||
+ make_ssl_client_by_address_and_port, make_ssl_client_by_port,
|
||
+ make_client_by_address_and_port, make_client_by_port,
|
||
+ make_empty
|
||
+
|
||
+create {HTTPD_STREAM_SOCKET}
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make_ssl_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
|
||
+ local
|
||
+ l_socket: SSL_TCP_STREAM_SOCKET
|
||
+ do
|
||
+ create l_socket.make_server_by_address_and_port (an_address, a_port)
|
||
+ l_socket.set_tls_protocol (a_ssl_protocol)
|
||
+ socket := l_socket
|
||
+ set_certificates (a_crt_fn, a_key_fn)
|
||
+ end
|
||
+
|
||
+ make_ssl_server_by_port (a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
|
||
+ local
|
||
+ l_socket: SSL_TCP_STREAM_SOCKET
|
||
+ do
|
||
+ create l_socket.make_server_by_port (a_port)
|
||
+ l_socket.set_tls_protocol (a_ssl_protocol)
|
||
+ socket := l_socket
|
||
+ set_certificates (a_crt_fn, a_key_fn)
|
||
+ end
|
||
+
|
||
+ make_ssl_client_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
|
||
+ local
|
||
+ l_socket: SSL_TCP_STREAM_SOCKET
|
||
+ do
|
||
+ create l_socket.make_client_by_address_and_port (an_address, a_port)
|
||
+ l_socket.set_tls_protocol (a_ssl_protocol)
|
||
+ socket := l_socket
|
||
+ set_certificates (a_crt_fn, a_key_fn)
|
||
+ end
|
||
+
|
||
+ make_ssl_client_by_port (a_peer_port: INTEGER; a_peer_host: STRING; a_ssl_protocol: NATURAL; a_crt_fn, a_key_fn: detachable READABLE_STRING_GENERAL)
|
||
+ local
|
||
+ l_socket: SSL_TCP_STREAM_SOCKET
|
||
+ do
|
||
+ create l_socket.make_client_by_port (a_peer_port, a_peer_host)
|
||
+ l_socket.set_tls_protocol (a_ssl_protocol)
|
||
+ socket := l_socket
|
||
+ set_certificates (a_crt_fn, a_key_fn)
|
||
+ end
|
||
+
|
||
+ make_empty
|
||
+ -- <Precursor>.
|
||
+ do
|
||
+ create {SSL_TCP_STREAM_SOCKET} socket.make_empty
|
||
+ end
|
||
+
|
||
+feature -- Output
|
||
+
|
||
+ put_readable_string_8 (s: READABLE_STRING_8)
|
||
+ -- <Precursor>
|
||
+ do
|
||
+ if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
+ l_ssl_socket.put_readable_string_8 (s)
|
||
+ else
|
||
+ Precursor (s)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Status Report
|
||
+
|
||
+ port: INTEGER
|
||
+ -- <Precursor>
|
||
+ do
|
||
+ if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
+ Result := l_ssl_socket.port
|
||
+ else
|
||
+ Result := Precursor
|
||
+ end
|
||
+ end
|
||
+
|
||
+ is_bound: BOOLEAN
|
||
+ -- <Precursor>
|
||
+ do
|
||
+ if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
+ Result := l_ssl_socket.is_bound
|
||
+ else
|
||
+ Result := Precursor
|
||
+ end
|
||
+ end
|
||
+
|
||
+ ready_for_writing: BOOLEAN
|
||
+ -- <Precursor>
|
||
+ do
|
||
+ if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
+ Result := l_ssl_socket.ready_for_writing
|
||
+ else
|
||
+ Result := Precursor
|
||
+ end
|
||
+ end
|
||
+
|
||
+ ready_for_reading: BOOLEAN
|
||
+ -- <Precursor>
|
||
+ do
|
||
+ if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
+ Result := l_ssl_socket.ready_for_reading
|
||
+ else
|
||
+ Result := Precursor
|
||
+ end
|
||
+ end
|
||
+
|
||
+ try_ready_for_reading: BOOLEAN
|
||
+ do
|
||
+ if attached {SSL_TCP_STREAM_SOCKET} socket as l_socket then
|
||
+ Result := l_socket.try_ready_for_reading
|
||
+ else
|
||
+ Result := Precursor
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {HTTPD_STREAM_SOCKET} -- Implementation
|
||
+
|
||
+ set_certificates (a_crt_filename, a_key_filename: detachable READABLE_STRING_GENERAL)
|
||
+ do
|
||
+ if attached {SSL_NETWORK_STREAM_SOCKET} socket as l_socket then
|
||
+ if a_crt_filename /= Void then
|
||
+ l_socket.set_certificate_file_name (a_crt_filename)
|
||
+ end
|
||
+ if a_key_filename /= Void then
|
||
+ l_socket.set_key_file_name (a_key_filename)
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/ssl_tcp_stream_socket.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/ssl_tcp_stream_socket.e
|
||
new file mode 100644
|
||
index 0000000..de71f47
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/ssl/ssl_tcp_stream_socket.e
|
||
@@ -0,0 +1,73 @@
|
||
+note
|
||
+ description: "SSL tcp stream socket."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ SSL_TCP_STREAM_SOCKET
|
||
+
|
||
+inherit
|
||
+
|
||
+ SSL_NETWORK_STREAM_SOCKET
|
||
+
|
||
+create
|
||
+ make_server_by_address_and_port, make_server_by_port,
|
||
+ make_client_by_address_and_port, make_client_by_port,
|
||
+ make_empty
|
||
+
|
||
+create {SSL_NETWORK_STREAM_SOCKET}
|
||
+ make_from_descriptor_and_address
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER)
|
||
+ -- Create server socket on `an_address' and `a_port'.
|
||
+ require
|
||
+ valid_port: a_port >= 0
|
||
+ do
|
||
+ make
|
||
+ create address.make_from_address_and_port (an_address, a_port)
|
||
+ bind
|
||
+ end
|
||
+
|
||
+feature -- Basic operation
|
||
+
|
||
+ send_message (a_msg: STRING)
|
||
+ do
|
||
+ put_string (a_msg)
|
||
+ end
|
||
+
|
||
+feature -- Output
|
||
+
|
||
+ put_readable_string_8 (s: READABLE_STRING_8)
|
||
+ -- Write readable string `s' to socket.
|
||
+ local
|
||
+ ext: C_STRING
|
||
+ do
|
||
+ create ext.make (s)
|
||
+ put_managed_pointer (ext.managed_data, 0, s.count)
|
||
+ end
|
||
+
|
||
+feature -- Status report
|
||
+
|
||
+ try_ready_for_reading: BOOLEAN
|
||
+ -- Is data available for reading from the socket right now?
|
||
+ require
|
||
+ socket_exists: exists
|
||
+ local
|
||
+ retval: INTEGER
|
||
+ do
|
||
+ retval := c_select_poll_with_timeout (descriptor, True, 0)
|
||
+ Result := (retval > 0)
|
||
+ end
|
||
+
|
||
+feature {NONE}-- Implementation
|
||
+
|
||
+
|
||
+
|
||
+
|
||
+note
|
||
+ copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/tcp_stream_socket.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/tcp_stream_socket.e
|
||
new file mode 100644
|
||
index 0000000..6961a77
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/tcp_stream_socket.e
|
||
@@ -0,0 +1,98 @@
|
||
+note
|
||
+ description: "Summary description for {TCP_STREAM_SOCKET}."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ TCP_STREAM_SOCKET
|
||
+
|
||
+inherit
|
||
+ NETWORK_STREAM_SOCKET
|
||
+ redefine
|
||
+ make
|
||
+ end
|
||
+
|
||
+ TCP_STREAM_SOCKET_EXT
|
||
+
|
||
+create
|
||
+ make_server_by_address_and_port,
|
||
+ make_server_by_port,
|
||
+ make_client_by_address_and_port,
|
||
+ make_client_by_port,
|
||
+ make_from_separate,
|
||
+ make_empty
|
||
+
|
||
+create {NETWORK_STREAM_SOCKET}
|
||
+ make_from_descriptor_and_address
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make
|
||
+ -- Create a network stream socket.
|
||
+ do
|
||
+ Precursor
|
||
+ debug
|
||
+ set_reuse_address
|
||
+ end
|
||
+ end
|
||
+
|
||
+ make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER)
|
||
+ -- Create server socket on `an_address' and `a_port'.
|
||
+ require
|
||
+ valid_port: a_port >= 0
|
||
+ do
|
||
+ make
|
||
+ create address.make_from_address_and_port (an_address, a_port)
|
||
+ bind
|
||
+ end
|
||
+
|
||
+ make_from_separate (s: separate STREAM_SOCKET)
|
||
+ require
|
||
+ descriptor_available: s.descriptor_available
|
||
+ do
|
||
+ create_from_descriptor (s.descriptor)
|
||
+ end
|
||
+
|
||
+feature -- Basic operation
|
||
+
|
||
+ send_message (a_msg: STRING)
|
||
+ do
|
||
+ put_string (a_msg)
|
||
+ end
|
||
+
|
||
+feature -- Output
|
||
+
|
||
+ put_readable_string_8 (s: READABLE_STRING_8)
|
||
+ -- Write readable string `s' to socket.
|
||
+ local
|
||
+ ext: C_STRING
|
||
+ do
|
||
+ create ext.make (s)
|
||
+ put_managed_pointer (ext.managed_data, 0, s.count)
|
||
+ end
|
||
+
|
||
+feature -- Status report
|
||
+
|
||
+ try_ready_for_reading: BOOLEAN
|
||
+ -- Is data available for reading from the socket right now?
|
||
+ require
|
||
+ socket_exists: exists
|
||
+ local
|
||
+ retval: INTEGER
|
||
+ do
|
||
+ retval := c_select_poll_with_timeout (descriptor, True, 0)
|
||
+ Result := (retval > 0)
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/tcp_stream_socket_ext.e b/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/tcp_stream_socket_ext.e
|
||
new file mode 100644
|
||
index 0000000..73480ca
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/network/until_16_05/tcp_stream_socket_ext.e
|
||
@@ -0,0 +1,104 @@
|
||
+note
|
||
+ description: "[
|
||
+ Until 16.05, the EiffelNet socket interface DOES NOT have recv_timeout and send_timeout.
|
||
+ ]"
|
||
+
|
||
+deferred class
|
||
+ TCP_STREAM_SOCKET_EXT
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ descriptor: INTEGER
|
||
+ -- Socket descriptor of current socket
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+feature -- Socket Recv and Send timeout.
|
||
+
|
||
+-- recv_timeout: INTEGER
|
||
+-- -- Receive timeout in seconds on Current socket.
|
||
+-- do
|
||
+-- Result := c_get_sock_recv_timeout (descriptor, level_sol_socket)
|
||
+-- ensure
|
||
+-- result_not_negative: Result >= 0
|
||
+-- end
|
||
+--
|
||
+-- send_timeout: INTEGER
|
||
+-- -- Send timeout in seconds on Current socket.
|
||
+-- do
|
||
+-- Result := c_get_sock_send_timeout (descriptor, level_sol_socket)
|
||
+-- ensure
|
||
+-- result_not_negative: Result >= 0
|
||
+-- end
|
||
+
|
||
+ set_recv_timeout (a_timeout_seconds: INTEGER)
|
||
+ -- Set the receive timeout in seconds on Current socket.
|
||
+ -- if `0' the related operations will never timeout.
|
||
+ require
|
||
+ positive_timeout: a_timeout_seconds >= 0
|
||
+ do
|
||
+ c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds)
|
||
+ end
|
||
+
|
||
+ set_send_timeout (a_timeout_seconds: INTEGER)
|
||
+ -- Set the send timeout in milliseconds on Current socket.
|
||
+ -- if `0' the related operations will never timeout.
|
||
+ require
|
||
+ positive_timeout: a_timeout_seconds >= 0
|
||
+ do
|
||
+ c_set_sock_send_timeout (descriptor, level_sol_socket, a_timeout_seconds)
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Externals
|
||
+
|
||
+ level_sol_socket: INTEGER
|
||
+ -- SOL_SOCKET level of options
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+-- set_so_rcvtimeo (a_timeout_seconds: INTEGER)
|
||
+-- -- Set the receive timeout in seconds on Current socket.
|
||
+-- -- if `0' the related operations will never timeout.
|
||
+-- require
|
||
+-- positive_timeout: a_timeout_seconds >= 0
|
||
+-- do
|
||
+-- c_set_sock_recv_timeout (descriptor, level_sol_socket, a_timeout_seconds)
|
||
+-- end
|
||
+
|
||
+ c_set_sock_recv_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER)
|
||
+ -- C routine to set socket option `SO_RCVTIMEO' with `a_timeout_seconds' seconds.
|
||
+ external
|
||
+ "C inline"
|
||
+ alias
|
||
+ "[
|
||
+#ifdef EIF_WINDOWS
|
||
+ int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
|
||
+ setsockopt((SOCKET) $a_fd, (int) $a_level, (int) SO_RCVTIMEO, (char *) &arg, sizeof(arg));
|
||
+#else
|
||
+ struct timeval tv;
|
||
+ tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
|
||
+
|
||
+ setsockopt((int) $a_fd, (int) $a_level, (int) SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
|
||
+#endif
|
||
+ ]"
|
||
+ end
|
||
+
|
||
+ c_set_sock_send_timeout (a_fd, a_level: INTEGER; a_timeout_seconds: INTEGER)
|
||
+ -- C routine to set socket option `SO_SNDTIMEO' with `a_timeout_seconds' seconds.
|
||
+ external
|
||
+ "C inline"
|
||
+ alias
|
||
+ "[
|
||
+#ifdef EIF_WINDOWS
|
||
+ int arg = (int) 1000 * $a_timeout_seconds; /* Timeout in milliseconds */
|
||
+ setsockopt((SOCKET) $a_fd, (int) $a_level, (int) SO_SNDTIMEO, (char *) &arg, sizeof(arg));
|
||
+#else
|
||
+ struct timeval tv;
|
||
+ tv.tv_sec = $a_timeout_seconds; /* Timeout in seconds */
|
||
+
|
||
+ setsockopt((int) $a_fd, (int) $a_level, (int) SO_SNDTIMEO, (struct timeval *)&tv, sizeof(struct timeval));
|
||
+#endif
|
||
+ ]"
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/no_ssl/httpd_configuration.e b/library/server/ewsgi/connectors/standalone/lib/httpd/no_ssl/httpd_configuration.e
|
||
new file mode 100644
|
||
index 0000000..65ae18c
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/no_ssl/httpd_configuration.e
|
||
@@ -0,0 +1,66 @@
|
||
+note
|
||
+ description: "Standalone server configuration (ssl NOT supported)."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_CONFIGURATION
|
||
+
|
||
+inherit
|
||
+ HTTPD_CONFIGURATION_I
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature -- Status
|
||
+
|
||
+ Server_details: STRING_8 = "Server: Standalone Eiffel Server"
|
||
+
|
||
+ has_ssl_support: BOOLEAN = False
|
||
+ -- Precursor
|
||
+
|
||
+feature -- SSL Helpers
|
||
+
|
||
+ set_ssl_protocol_to_ssl_2_or_3
|
||
+ -- Set `ssl_protocol' with `Ssl_23'.
|
||
+ do
|
||
+ -- Ignored
|
||
+ end
|
||
+
|
||
+
|
||
+ set_ssl_protocol_to_tls_1_0
|
||
+ -- Set `ssl_protocol' with `Tls_1_0'.
|
||
+ do
|
||
+ -- Ignored
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_tls_1_1
|
||
+ -- Set `ssl_protocol' with `Tls_1_1'.
|
||
+ do
|
||
+ -- Ignored
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_tls_1_2
|
||
+ -- Set `ssl_protocol' with `Tls_1_2'.
|
||
+ do
|
||
+ -- Ignored
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_dtls_1_0
|
||
+ -- Set `ssl_protocol' with `Dtls_1_0'.
|
||
+ do
|
||
+ -- Ignored
|
||
+ end
|
||
+
|
||
+
|
||
+note
|
||
+ copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/no_ssl/httpd_server.e b/library/server/ewsgi/connectors/standalone/lib/httpd/no_ssl/httpd_server.e
|
||
new file mode 100644
|
||
index 0000000..8787e1f
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/no_ssl/httpd_server.e
|
||
@@ -0,0 +1,27 @@
|
||
+note
|
||
+ description: "[
|
||
+ httpd server
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_SERVER
|
||
+
|
||
+inherit
|
||
+ HTTPD_SERVER_I
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+note
|
||
+ copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/no_ssl/httpd_socket_factory.e b/library/server/ewsgi/connectors/standalone/lib/httpd/no_ssl/httpd_socket_factory.e
|
||
new file mode 100644
|
||
index 0000000..6c08bc3
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/no_ssl/httpd_socket_factory.e
|
||
@@ -0,0 +1,17 @@
|
||
+note
|
||
+ description: "Summary description for {HTTPD_SOCKET_FACTORY}."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_SOCKET_FACTORY
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ new_client_socket (a_is_secure: BOOLEAN): HTTPD_STREAM_SOCKET
|
||
+ do
|
||
+ check not_secure: not a_is_secure end
|
||
+ create Result.make_empty
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/package.iron b/library/server/ewsgi/connectors/standalone/lib/httpd/package.iron
|
||
new file mode 100644
|
||
index 0000000..bb34ce9
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/package.iron
|
||
@@ -0,0 +1,20 @@
|
||
+package httpd
|
||
+
|
||
+project
|
||
+ httpd = "httpd-safe.ecf"
|
||
+ httpd = "httpd.ecf"
|
||
+
|
||
+note
|
||
+ title: HTTP server
|
||
+ description: "[
|
||
+ Simple HTTP listener and handler, that can be extended easily.
|
||
+ ]"
|
||
+ tags: http,httpd,server,web
|
||
+ collection: EWF
|
||
+ copyright: 2011-2016, 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)
|
||
+ link[license]: http://www.eiffel.com/licensing/forum.txt
|
||
+ link[source]: "Github" https://github.com/EiffelWebFramework/EWF/library/server/httpd
|
||
+ link[doc]: "Documentation" http://eiffelwebframework.github.io/EWF/
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_configuration.e b/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_configuration.e
|
||
new file mode 100644
|
||
index 0000000..b7eba79
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_configuration.e
|
||
@@ -0,0 +1,79 @@
|
||
+note
|
||
+ description: "Standalone server configuration (ssl supported)."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_CONFIGURATION
|
||
+
|
||
+inherit
|
||
+ HTTPD_CONFIGURATION_I
|
||
+ redefine
|
||
+ make
|
||
+ end
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make
|
||
+ -- Create a new instance and set ssl protocol to tls_1_2.
|
||
+ do
|
||
+ Precursor
|
||
+ set_ssl_protocol_to_tls_1_2
|
||
+ ensure then
|
||
+ ssl_protocol_set: ssl_protocol = {SSL_PROTOCOL}.tls_1_2
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ Server_details: STRING_8 = "Server: Standalone Eiffel Server (https)"
|
||
+
|
||
+ has_ssl_support: BOOLEAN = True
|
||
+ -- Precursor
|
||
+
|
||
+feature -- SSL Helpers
|
||
+
|
||
+ set_ssl_protocol_to_ssl_2_or_3
|
||
+ -- Set `ssl_protocol' with `Ssl_23'.
|
||
+ do
|
||
+ set_ssl_protocol ({SSL_PROTOCOL}.Ssl_23)
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_tls_1_0
|
||
+ -- Set `ssl_protocol' with `Tls_1_0'.
|
||
+ do
|
||
+ set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_0)
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_tls_1_1
|
||
+ -- Set `ssl_protocol' with `Tls_1_1'.
|
||
+ do
|
||
+ set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_1)
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_tls_1_2
|
||
+ -- Set `ssl_protocol' with `Tls_1_2'.
|
||
+ do
|
||
+ set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_2)
|
||
+ end
|
||
+
|
||
+ set_ssl_protocol_to_dtls_1_0
|
||
+ -- Set `ssl_protocol' with `Dtls_1_0'.
|
||
+ do
|
||
+ set_ssl_protocol ({SSL_PROTOCOL}.Dtls_1_0)
|
||
+ end
|
||
+
|
||
+
|
||
+note
|
||
+ copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_server.e b/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_server.e
|
||
new file mode 100644
|
||
index 0000000..4045aa2
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_server.e
|
||
@@ -0,0 +1,35 @@
|
||
+note
|
||
+ description: "[
|
||
+ SSL enabled server
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ HTTPD_SERVER
|
||
+
|
||
+inherit
|
||
+ HTTPD_SERVER_I
|
||
+ redefine
|
||
+ new_listening_socket
|
||
+ end
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Factory
|
||
+
|
||
+ new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET
|
||
+ do
|
||
+ if configuration.is_secure then
|
||
+ if a_addr /= Void then
|
||
+ create {HTTPD_STREAM_SSL_SOCKET} Result.make_ssl_server_by_address_and_port (a_addr, a_http_port, configuration.ssl_protocol, configuration.ca_crt, configuration.ca_key)
|
||
+ else
|
||
+ create {HTTPD_STREAM_SSL_SOCKET} Result.make_ssl_server_by_port (a_http_port, configuration.ssl_protocol, configuration.ca_crt, configuration.ca_key)
|
||
+ end
|
||
+ else
|
||
+ Result := Precursor (a_addr, a_http_port)
|
||
+ end
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_socket_factory.e b/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_socket_factory.e
|
||
new file mode 100644
|
||
index 0000000..ff410db
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/lib/httpd/ssl/httpd_socket_factory.e
|
||
@@ -0,0 +1,20 @@
|
||
+note
|
||
+ description: "Summary description for {HTTPD_SOCKET_FACTORY}."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ HTTPD_SOCKET_FACTORY
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ new_client_socket (a_is_secure: BOOLEAN): HTTPD_STREAM_SOCKET
|
||
+ do
|
||
+ if a_is_secure then
|
||
+ create {HTTPD_STREAM_SSL_SOCKET} Result.make_empty
|
||
+ else
|
||
+ create Result.make_empty
|
||
+ end
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/none/httpd_connection_handler.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/none/httpd_connection_handler.e
|
||
deleted file mode 100644
|
||
index 5bc857c..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/none/httpd_connection_handler.e
|
||
+++ /dev/null
|
||
@@ -1,86 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Implementation of HTTPD_CONNECTION_HANDLER_I for concurrency mode: none
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_CONNECTION_HANDLER
|
||
-
|
||
-inherit
|
||
- HTTPD_CONNECTION_HANDLER_I
|
||
-
|
||
-create
|
||
- make
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- initialize
|
||
- do
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- is_shutdown_requested: BOOLEAN
|
||
- -- <Precursor>
|
||
-
|
||
- shutdown_requested (a_server: like server): BOOLEAN
|
||
- do
|
||
- -- FIXME: we should probably remove this possibility, check with EWF if this is needed.
|
||
- Result := a_server.controller.shutdown_requested
|
||
- end
|
||
-
|
||
-feature {HTTPD_SERVER_I} -- Execution
|
||
-
|
||
- accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
- local
|
||
- cl: HTTPD_STREAM_SOCKET
|
||
- do
|
||
- is_shutdown_requested := is_shutdown_requested or shutdown_requested (server)
|
||
- if is_shutdown_requested then
|
||
- -- Cancel
|
||
- elseif attached factory.new_handler as h then
|
||
- cl := h.client_socket
|
||
- a_listening_socket.accept_to (cl)
|
||
- if h.is_connected then
|
||
- h.safe_execute
|
||
- end
|
||
- else
|
||
- check is_not_full: False end
|
||
- end
|
||
- update_is_shutdown_requested
|
||
- end
|
||
-
|
||
- update_is_shutdown_requested
|
||
- do
|
||
- is_shutdown_requested := shutdown_requested (server)
|
||
- end
|
||
-
|
||
- shutdown
|
||
- do
|
||
- if not is_shutdown_requested then
|
||
- is_shutdown_requested := True
|
||
- server.controller.shutdown
|
||
- end
|
||
- end
|
||
-
|
||
-feature {HTTPD_SERVER_I} -- Status report
|
||
-
|
||
- wait_for_completion
|
||
- -- Wait until Current is ready for shutdown
|
||
- do
|
||
- -- no concurrency, then current task should be done.
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/none/httpd_request_handler.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/none/httpd_request_handler.e
|
||
deleted file mode 100644
|
||
index c033de7..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/none/httpd_request_handler.e
|
||
+++ /dev/null
|
||
@@ -1,32 +0,0 @@
|
||
-note
|
||
- description : "Concurrent specific feature to implement HTTPD_REQUEST_HANDLER"
|
||
- date : "$Date$"
|
||
- revision : "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_REQUEST_HANDLER
|
||
-
|
||
-inherit
|
||
- HTTPD_REQUEST_HANDLER_I
|
||
- redefine
|
||
- is_persistent_connection_supported
|
||
- end
|
||
-
|
||
-feature -- Status report
|
||
-
|
||
- is_persistent_connection_supported: BOOLEAN = False
|
||
- -- <Precursor>
|
||
- -- When there is no concurrency support, do not try to support
|
||
- -- persistent connection!
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/none/httpd_request_handler_factory.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/none/httpd_request_handler_factory.e
|
||
deleted file mode 100644
|
||
index 1c93e9e..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/none/httpd_request_handler_factory.e
|
||
+++ /dev/null
|
||
@@ -1,15 +0,0 @@
|
||
-note
|
||
- description: "Implementation of request handler factory for concurrency mode: none"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_REQUEST_HANDLER_FACTORY
|
||
-
|
||
-inherit
|
||
- HTTPD_REQUEST_HANDLER_FACTORY_I
|
||
-
|
||
-note
|
||
- copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/httpd_connection_handler.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/httpd_connection_handler.e
|
||
deleted file mode 100644
|
||
index 9a5f466..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/httpd_connection_handler.e
|
||
+++ /dev/null
|
||
@@ -1,146 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Implementation of HTTPD_CONNECTION_HANDLER_I for concurrency mode: SCOOP
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_CONNECTION_HANDLER
|
||
-
|
||
-inherit
|
||
- HTTPD_CONNECTION_HANDLER_I
|
||
- redefine
|
||
- initialize
|
||
- end
|
||
-
|
||
-create
|
||
- make
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- initialize
|
||
- local
|
||
- n: INTEGER
|
||
- p: like pool
|
||
- do
|
||
- n := max_concurrent_connections (server)
|
||
- create p.make (n)
|
||
- initialize_pool (p, n)
|
||
- pool := p
|
||
- end
|
||
-
|
||
- initialize_pool (p: like pool; n: INTEGER)
|
||
- -- Initialize Concurrent pool of `n' potential separate connection handlers.
|
||
- do
|
||
- p.set_count (n)
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- is_shutdown_requested: BOOLEAN
|
||
- -- <Precursor>
|
||
-
|
||
- max_concurrent_connections (a_server: like server): INTEGER
|
||
- -- Max concurrent connection settings from server `a_server'.
|
||
- do
|
||
- Result := a_server.configuration.max_concurrent_connections
|
||
- end
|
||
-
|
||
-feature {HTTPD_SERVER_I} -- Execution
|
||
-
|
||
- shutdown
|
||
- -- <Precursor>
|
||
- do
|
||
- if not is_shutdown_requested then
|
||
- is_shutdown_requested := True
|
||
- pool_gracefull_stop (pool)
|
||
- end
|
||
- end
|
||
-
|
||
- pool_gracefull_stop (p: like pool)
|
||
- -- Graceful stop concurrent pool of separate connection handlers.
|
||
- do
|
||
- p.gracefull_stop
|
||
- end
|
||
-
|
||
- accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
- -- <Precursor>
|
||
- do
|
||
- accept_connection_on_pool (pool, a_listening_socket) -- Wait on not pool.is_full or is_stop_requested
|
||
- end
|
||
-
|
||
- accept_connection_on_pool (a_pool: like pool; a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
- -- Process accept connection
|
||
- -- note that the precondition matters for scoop synchronization.
|
||
- require
|
||
- concurrency: not a_pool.is_full or is_shutdown_requested or a_pool.stop_requested
|
||
- local
|
||
- cl: separate HTTPD_STREAM_SOCKET
|
||
- do
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".ENTER accept_connection_on_pool")
|
||
- end
|
||
- if is_shutdown_requested then
|
||
- -- Cancel
|
||
- elseif attached a_pool.separate_item (factory) as h then
|
||
- cl := separate_client_socket (h)
|
||
- a_listening_socket.accept_to (cl)
|
||
- process_handler (h)
|
||
- else
|
||
- check is_not_full: False end
|
||
- end
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".LEAVE accept_connection_on_pool")
|
||
- end
|
||
- end
|
||
-
|
||
- process_handler (hdl: separate HTTPD_REQUEST_HANDLER)
|
||
- -- Process request handler `hdl' as soon as `hdl' is connected to accepted socket.
|
||
- require
|
||
- hdl.is_connected
|
||
- do
|
||
- hdl.safe_execute
|
||
- end
|
||
-
|
||
-feature {HTTPD_SERVER_I} -- Status report
|
||
-
|
||
- wait_for_completion
|
||
- -- Wait until Current is ready for shutdown.
|
||
- do
|
||
- wait_for_pool_completion (pool)
|
||
- end
|
||
-
|
||
- wait_for_pool_completion (p: like pool)
|
||
- -- Wait until concurrent pool is empty and terminated.
|
||
- require
|
||
- p.is_empty -- SCOOP wait condition.
|
||
- do
|
||
- p.terminate
|
||
- end
|
||
-
|
||
-feature {NONE} -- Implementation
|
||
-
|
||
- separate_client_socket (hdl: separate HTTPD_REQUEST_HANDLER): separate HTTPD_STREAM_SOCKET
|
||
- -- Client socket for request handler `hdl'.
|
||
- do
|
||
- Result := hdl.client_socket
|
||
- end
|
||
-
|
||
- pool: separate CONCURRENT_POOL [HTTPD_REQUEST_HANDLER]
|
||
- -- Pool of separate connection handlers.
|
||
-
|
||
-invariant
|
||
- pool_attached: pool /= Void
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/httpd_request_handler.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/httpd_request_handler.e
|
||
deleted file mode 100644
|
||
index 9335e76..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/httpd_request_handler.e
|
||
+++ /dev/null
|
||
@@ -1,55 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Instance of HTTPD_REQUEST_HANDLER will process the incoming connection
|
||
- and extract information on the request and the server
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_REQUEST_HANDLER
|
||
-
|
||
-inherit
|
||
- HTTPD_REQUEST_HANDLER_I
|
||
- redefine
|
||
- release
|
||
- end
|
||
-
|
||
- CONCURRENT_POOL_ITEM
|
||
- rename
|
||
- release as release_pool_item
|
||
- end
|
||
-
|
||
-feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
|
||
-
|
||
- release
|
||
- -- <Precursor>
|
||
- local
|
||
- d: STRING
|
||
- do
|
||
- if attached internal_client_socket as l_socket then
|
||
- d := l_socket.descriptor.out
|
||
- else
|
||
- d := "N/A"
|
||
- end
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".release: ENTER {" + d + "}")
|
||
- end
|
||
- Precursor {HTTPD_REQUEST_HANDLER_I}
|
||
- release_pool_item
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".release: LEAVE {" + d + "}")
|
||
- end
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/httpd_request_handler_factory.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/httpd_request_handler_factory.e
|
||
deleted file mode 100644
|
||
index e48a96a..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/httpd_request_handler_factory.e
|
||
+++ /dev/null
|
||
@@ -1,27 +0,0 @@
|
||
-note
|
||
- description: "Implementation of request handler factory for concurrency mode: SCOOP"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_REQUEST_HANDLER_FACTORY
|
||
-
|
||
-inherit
|
||
- HTTPD_REQUEST_HANDLER_FACTORY_I
|
||
-
|
||
- CONCURRENT_POOL_FACTORY [HTTPD_REQUEST_HANDLER]
|
||
- rename
|
||
- new_separate_item as new_handler
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/pool/concurrent_pool.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/pool/concurrent_pool.e
|
||
deleted file mode 100644
|
||
index 070f271..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/pool/concurrent_pool.e
|
||
+++ /dev/null
|
||
@@ -1,190 +0,0 @@
|
||
-note
|
||
- description: "Concurrent pool for SCOOP concurrency mode."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- CONCURRENT_POOL [G -> CONCURRENT_POOL_ITEM]
|
||
-
|
||
-inherit
|
||
- HTTPD_DEBUG_FACILITIES
|
||
-
|
||
-create
|
||
- make
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make (n: INTEGER)
|
||
- do
|
||
- capacity := n
|
||
- create items.make_empty (n)
|
||
- create busy_items.make_empty (n)
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- count: INTEGER
|
||
- -- Number of concurrent items managed by Current pool.
|
||
-
|
||
- capacity: INTEGER
|
||
- -- Maximum number of concurrent items managed by Current pool.
|
||
-
|
||
-feature -- Status report
|
||
-
|
||
- is_full: BOOLEAN
|
||
- -- Pool is full?
|
||
- do
|
||
- Result := count >= capacity
|
||
- end
|
||
-
|
||
- is_empty: BOOLEAN
|
||
- -- No concurrent item waiting in current pool.
|
||
- do
|
||
- Result := count = 0
|
||
- end
|
||
-
|
||
- stop_requested: BOOLEAN
|
||
- -- Current pool received a request to terminate.
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- separate_item (a_factory: separate CONCURRENT_POOL_FACTORY [G]): detachable separate G
|
||
- -- Reused, or new separate item of type {G} created by `a_factory'.
|
||
- require
|
||
- is_not_full: not is_full
|
||
- local
|
||
- i,n,pos: INTEGER
|
||
- lst: like busy_items
|
||
- l_item: detachable separate G
|
||
- do
|
||
- if not stop_requested then
|
||
- from
|
||
- lst := busy_items
|
||
- pos := -1
|
||
- i := 0
|
||
- n := lst.count - 1
|
||
- until
|
||
- i > n or l_item /= Void or pos >= 0
|
||
- loop
|
||
- if not lst [i] then -- is free (i.e not busy)
|
||
- pos := i
|
||
-
|
||
- if items.valid_index (pos) then
|
||
- l_item := items [pos]
|
||
- if l_item /= Void then
|
||
- busy_items [pos] := True
|
||
- end
|
||
- end
|
||
- if l_item = Void then
|
||
- -- Empty, then let's create one.
|
||
- l_item := a_factory.new_separate_item
|
||
- register_item (l_item)
|
||
- items [pos] := l_item
|
||
- end
|
||
- end
|
||
- i := i + 1
|
||
- end
|
||
- if l_item = Void then
|
||
- -- Pool is FULL ...
|
||
- check overcapacity: False end
|
||
- else
|
||
- debug ("pool", "dbglog")
|
||
- dbglog ("Lock pool item #" + pos.out + " (free:"+ (capacity - count).out +"))")
|
||
- end
|
||
- count := count + 1
|
||
- busy_items [pos] := True
|
||
- Result := l_item
|
||
- a_factory.update_item (l_item)
|
||
- end
|
||
- end
|
||
- end
|
||
-
|
||
-feature -- Basic operation
|
||
-
|
||
- gracefull_stop
|
||
- -- Request the Current pool to terminate.
|
||
- do
|
||
- stop_requested := True
|
||
- end
|
||
-
|
||
-feature {NONE} -- Internal
|
||
-
|
||
- items: SPECIAL [detachable separate G]
|
||
- -- List of concurrent items.
|
||
-
|
||
- busy_items: SPECIAL [BOOLEAN]
|
||
- -- Map of items being proceed.
|
||
-
|
||
-feature {CONCURRENT_POOL_ITEM} -- Change
|
||
-
|
||
- release_item (a_item: separate G)
|
||
- -- Unregister `a_item' from Current pool.
|
||
- require
|
||
- count > 0
|
||
- local
|
||
- i,n,pos: INTEGER
|
||
- lst: like items
|
||
- do
|
||
- -- release handler for reuse
|
||
- from
|
||
- lst := items
|
||
- i := 0
|
||
- n := lst.count - 1
|
||
- until
|
||
- i > n or lst [i] = a_item
|
||
- loop
|
||
- i := i + 1
|
||
- end
|
||
- if i <= n then
|
||
- pos := i
|
||
- busy_items [pos] := False
|
||
- count := count - 1
|
||
---reuse items [pos] := Void
|
||
- debug ("pool", "dbglog")
|
||
- dbglog ("Released pool item #" + i.out + " (free:"+ (capacity - count).out +"))")
|
||
- end
|
||
- else
|
||
- check known_item: False end
|
||
- end
|
||
- end
|
||
-
|
||
-feature -- Change
|
||
-
|
||
- set_count (n: INTEGER)
|
||
- -- Set capacity of Current pool to `n'.
|
||
- local
|
||
- g: detachable separate G
|
||
- do
|
||
- capacity := n
|
||
- items.fill_with (g, 0, n - 1)
|
||
- busy_items.fill_with (False, 0, n - 1)
|
||
- end
|
||
-
|
||
- terminate
|
||
- -- Terminate current pool.
|
||
- local
|
||
- l_items: like items
|
||
- do
|
||
- l_items := items
|
||
- l_items.wipe_out
|
||
- end
|
||
-
|
||
-feature {NONE} -- Implementation
|
||
-
|
||
- register_item (a_item: separate G)
|
||
- -- Adopt `a_item' in current pool.
|
||
- do
|
||
- a_item.set_pool (Current)
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/pool/concurrent_pool_factory.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/pool/concurrent_pool_factory.e
|
||
deleted file mode 100644
|
||
index 92e0f2c..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/pool/concurrent_pool_factory.e
|
||
+++ /dev/null
|
||
@@ -1,31 +0,0 @@
|
||
-note
|
||
- description: "Factory in charge of creating new concurrent pool item."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- CONCURRENT_POOL_FACTORY [G -> CONCURRENT_POOL_ITEM]
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- update_item (a_item: separate G)
|
||
- -- Update `a_item' for optionally purpose.
|
||
- do
|
||
- end
|
||
-
|
||
- new_separate_item: separate G
|
||
- -- New separated object of type {G}.
|
||
- deferred
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/pool/concurrent_pool_item.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/pool/concurrent_pool_item.e
|
||
deleted file mode 100644
|
||
index 2b5d1c6..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/scoop/pool/concurrent_pool_item.e
|
||
+++ /dev/null
|
||
@@ -1,52 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Item create by the CONCURRENT_POOL_FACTORY, and managed by the CONCURRENT_POOL
|
||
- for SCOOP concurrency mode.
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- CONCURRENT_POOL_ITEM
|
||
-
|
||
-feature {NONE} -- Access
|
||
-
|
||
- pool: detachable separate CONCURRENT_POOL [CONCURRENT_POOL_ITEM]
|
||
- -- Associated concurrent pool component.
|
||
-
|
||
-feature {CONCURRENT_POOL} -- Change
|
||
-
|
||
- set_pool (p: like pool)
|
||
- -- Set associated `pool' to `p'.
|
||
- do
|
||
- pool := p
|
||
- end
|
||
-
|
||
-feature {CONCURRENT_POOL, HTTPD_CONNECTION_HANDLER_I} -- Basic operation
|
||
-
|
||
- release
|
||
- -- Release Current pool item from associated pool.
|
||
- do
|
||
- if attached pool as p then
|
||
- pool_release (p)
|
||
- end
|
||
- end
|
||
-
|
||
-feature {NONE} -- Implementation
|
||
-
|
||
- pool_release (p: separate CONCURRENT_POOL [CONCURRENT_POOL_ITEM])
|
||
- do
|
||
- p.release_item (Current)
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/httpd_connection_handler.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/httpd_connection_handler.e
|
||
deleted file mode 100644
|
||
index 45b85bd..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/httpd_connection_handler.e
|
||
+++ /dev/null
|
||
@@ -1,103 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Implementation of HTTPD_CONNECTION_HANDLER_I for concurrency mode: Thread
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_CONNECTION_HANDLER
|
||
-
|
||
-inherit
|
||
- HTTPD_CONNECTION_HANDLER_I
|
||
- redefine
|
||
- initialize
|
||
- end
|
||
-
|
||
-create
|
||
- make
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- initialize
|
||
- local
|
||
- n: INTEGER
|
||
- do
|
||
- n := max_concurrent_connections (server)
|
||
- create pool.make (n.to_natural_32)
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- is_shutdown_requested: BOOLEAN
|
||
-
|
||
- max_concurrent_connections (a_server: like server): INTEGER
|
||
- do
|
||
- Result := a_server.configuration.max_concurrent_connections
|
||
- end
|
||
-
|
||
-feature {HTTPD_SERVER_I} -- Execution
|
||
-
|
||
- shutdown
|
||
- do
|
||
- if not is_shutdown_requested then
|
||
- is_shutdown_requested := True
|
||
- pool_gracefull_stop (pool)
|
||
- end
|
||
- end
|
||
-
|
||
- pool_gracefull_stop (p: like pool)
|
||
- do
|
||
- p.terminate
|
||
- end
|
||
-
|
||
- accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
- local
|
||
- cl: separate HTTPD_STREAM_SOCKET
|
||
- do
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".ENTER accept_connection {"+ a_listening_socket.descriptor.out +"}")
|
||
- end
|
||
-
|
||
- if is_shutdown_requested then
|
||
- -- cancel
|
||
- elseif attached factory.new_handler as h then
|
||
- cl := h.client_socket
|
||
- a_listening_socket.accept_to (cl)
|
||
- if h.is_connected then
|
||
- pool.add_work (agent h.safe_execute)
|
||
- end
|
||
- end
|
||
-
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".LEAVE accept_incoming_connection {"+ a_listening_socket.descriptor.out +"}")
|
||
- end
|
||
- end
|
||
-
|
||
-feature {HTTPD_SERVER_I} -- Status report
|
||
-
|
||
- wait_for_completion
|
||
- -- Wait until Current is ready for shutdown
|
||
- do
|
||
- pool.wait_for_completion
|
||
- end
|
||
-
|
||
-feature {NONE} -- Access
|
||
-
|
||
- pool: THREAD_POOL [HTTPD_REQUEST_HANDLER] --ANY] --POOLED_THREAD [HTTP_REQUEST_HANDLER]]
|
||
- -- Pool of concurrent connection handlers.
|
||
-
|
||
-invariant
|
||
- pool_attached: pool /= Void
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/httpd_request_handler.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/httpd_request_handler.e
|
||
deleted file mode 100644
|
||
index e053bfe..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/httpd_request_handler.e
|
||
+++ /dev/null
|
||
@@ -1,49 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Instance of HTTPD_REQUEST_HANDLER will process the incoming connection
|
||
- and extract information on the request and the server
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_REQUEST_HANDLER
|
||
-
|
||
-inherit
|
||
- HTTPD_REQUEST_HANDLER_I
|
||
- redefine
|
||
- release
|
||
- end
|
||
-
|
||
-feature {HTTPD_CONNECTION_HANDLER_I} -- Basic operation
|
||
-
|
||
- release
|
||
- local
|
||
- d: STRING
|
||
- do
|
||
- -- FIXME: for log purpose
|
||
- if attached internal_client_socket as l_socket then
|
||
- d := l_socket.descriptor.out
|
||
- else
|
||
- d := "N/A"
|
||
- end
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".release: ENTER {" + d + "}")
|
||
- end
|
||
- Precursor {HTTPD_REQUEST_HANDLER_I}
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".release: LEAVE {" + d + "}")
|
||
- end
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/httpd_request_handler_factory.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/httpd_request_handler_factory.e
|
||
deleted file mode 100644
|
||
index cf692e5..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/httpd_request_handler_factory.e
|
||
+++ /dev/null
|
||
@@ -1,15 +0,0 @@
|
||
-note
|
||
- description: "Implementation of request handler factory for concurrency mode: Thread"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_REQUEST_HANDLER_FACTORY
|
||
-
|
||
-inherit
|
||
- HTTPD_REQUEST_HANDLER_FACTORY_I
|
||
-
|
||
-note
|
||
- copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/pool/pooled_thread.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/pool/pooled_thread.e
|
||
deleted file mode 100644
|
||
index 393e028..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/pool/pooled_thread.e
|
||
+++ /dev/null
|
||
@@ -1,121 +0,0 @@
|
||
-note
|
||
- description: "{POOLED_THREAD} is used in combination with {THREAD_POOL} to allow for pooled threads."
|
||
- legal: "See notice at end of class."
|
||
- status: "Community Preview 1.0"
|
||
- date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $"
|
||
- revision: "$Revision: 80577 $"
|
||
-
|
||
-class
|
||
- POOLED_THREAD [G]
|
||
-
|
||
-inherit
|
||
- THREAD
|
||
- rename
|
||
- make as thread_make
|
||
- end
|
||
-
|
||
-create {THREAD_POOL}
|
||
- make
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make (a_thread_pool: THREAD_POOL [G]; a_semaphore: SEMAPHORE)
|
||
- -- `a_thread_pool', the pool in which this thread is managed
|
||
- -- `a_semaphore' is used for execution suspending
|
||
- do
|
||
- thread_make
|
||
- thread_pool := a_thread_pool
|
||
- semaphore := a_semaphore
|
||
- end
|
||
-
|
||
-feature {NONE} -- Access
|
||
-
|
||
- thread_pool: THREAD_POOL [G]
|
||
- -- Pool manager in which this thread is pooled
|
||
-
|
||
- target: detachable G
|
||
- -- Target on which the `thread_procedure' should be applied
|
||
- -- Depending on which launch is used, target is not used
|
||
-
|
||
- thread_procedure: detachable PROCEDURE
|
||
- -- Work that should be executed by the thread
|
||
-
|
||
- semaphore: SEMAPHORE
|
||
- -- Semaphore share with all threads in a thread pool
|
||
- -- to suspend execution until more work is available
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- set_target (a_target: G)
|
||
- -- Sets the target on which the work should be executed
|
||
- do
|
||
- target := a_target
|
||
- end
|
||
-
|
||
-feature {NONE} -- Implementation
|
||
-
|
||
- execute
|
||
- -- <Precursor>
|
||
- local
|
||
- done: BOOLEAN
|
||
- do
|
||
- from
|
||
- semaphore.wait
|
||
- thread_procedure := thread_pool.get_work (Current)
|
||
- until
|
||
- done
|
||
- loop
|
||
- if attached thread_procedure as l_work then
|
||
- if attached target as t then
|
||
- l_work.call ([t])
|
||
- else
|
||
- l_work.call (Void)
|
||
- end
|
||
- end
|
||
- if thread_pool.over then
|
||
- done := True
|
||
- else
|
||
- thread_procedure := thread_pool.get_work (Current)
|
||
- if thread_procedure = Void then
|
||
- semaphore.wait
|
||
- thread_procedure := thread_pool.get_work (Current)
|
||
- end
|
||
- end
|
||
- end
|
||
- thread_pool.thread_terminated (Current)
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2012, Javier Velilla and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- licensing_options: "http://www.eiffel.com/licensing"
|
||
- copying: "[
|
||
- This file is part of Eiffel Software's Eiffel Development Environment.
|
||
-
|
||
- Eiffel Software's Eiffel Development Environment is free
|
||
- software; you can redistribute it and/or modify it under
|
||
- the terms of the GNU General Public License as published
|
||
- by the Free Software Foundation, version 2 of the License
|
||
- (available at the URL listed under "license" above).
|
||
-
|
||
- Eiffel Software's Eiffel Development Environment is
|
||
- distributed in the hope that it will be useful, but
|
||
- WITHOUT ANY WARRANTY; without even the implied warranty
|
||
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
- See the GNU General Public License for more details.
|
||
-
|
||
- You should have received a copy of the GNU General Public
|
||
- License along with Eiffel Software's Eiffel Development
|
||
- Environment; if not, write to the Free Software Foundation,
|
||
- Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
- ]"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
-
|
||
-
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/pool/thread_pool.e b/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/pool/thread_pool.e
|
||
deleted file mode 100644
|
||
index 02e94d9..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/concurrency/thread/pool/thread_pool.e
|
||
+++ /dev/null
|
||
@@ -1,228 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- A thread pool manager. Manages threads up to `capacity' and queue after that,
|
||
- till threads get available again.
|
||
- ]"
|
||
- legal: "See notice at end of class."
|
||
- status: "Community Preview 1.0"
|
||
- date: "$Date: 2009-09-01 19:15:37 -0300 (mar 01 de sep de 2009) $"
|
||
- revision: "$Revision: 80577 $"
|
||
-
|
||
-class
|
||
- THREAD_POOL [G]
|
||
-
|
||
-inherit
|
||
- EXECUTION_ENVIRONMENT
|
||
-
|
||
-create
|
||
- make
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make (n: like capacity)
|
||
- -- Initialize current pool with capacity `n'.
|
||
- require
|
||
- n_positive: n > 0
|
||
- n_not_too_large: n < {INTEGER_32}.max_value.as_natural_32
|
||
- local
|
||
- i: NATURAL
|
||
- do
|
||
- capacity := n
|
||
- create work_queue.make (n.to_integer_32)
|
||
- create work_queue_mutex.make
|
||
- create over_mutex.make
|
||
- create termination_mutex.make
|
||
- create work_semaphore.make (capacity.as_integer_32)
|
||
- from
|
||
- i := 1
|
||
- until
|
||
- i > capacity
|
||
- loop
|
||
- work_semaphore.wait
|
||
- i := i + 1
|
||
- end
|
||
- initialize_threads
|
||
- terminated_count := capacity
|
||
- is_over := False
|
||
- ensure
|
||
- capacity_set: capacity = n
|
||
- work_queue_set: work_queue.is_empty
|
||
- end
|
||
-
|
||
- initialize_threads
|
||
- -- Launches all threads
|
||
- local
|
||
- i: NATURAL
|
||
- thread: POOLED_THREAD [G]
|
||
- do
|
||
- from
|
||
- i := 1
|
||
- until
|
||
- i > capacity
|
||
- loop
|
||
- create thread.make (Current, work_semaphore)
|
||
- thread.launch
|
||
- i := i + 1
|
||
- end
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- capacity: NATURAL
|
||
- -- Maximal number of threads allowed (queuing otherwise)
|
||
-
|
||
- queue_count: NATURAL
|
||
- -- Number of items in queue
|
||
- do
|
||
- work_queue_mutex.lock
|
||
- Result := work_queue.count.as_natural_32
|
||
- work_queue_mutex.unlock
|
||
- end
|
||
-
|
||
-feature -- Status report
|
||
-
|
||
- valid_action (a_action: PROCEDURE): BOOLEAN
|
||
- -- Is `a_action' a valid action for the current pool.
|
||
- do
|
||
- -- There should be no open operands.
|
||
- Result := a_action.valid_operands (Void)
|
||
- end
|
||
-
|
||
-feature -- Basic operations
|
||
-
|
||
- add_work (work: PROCEDURE)
|
||
- -- Launches a thread with the specified argument `arg'. Reuse of thread if possible.
|
||
- require
|
||
- valid_action: valid_action (work)
|
||
- do
|
||
- work_queue_mutex.lock
|
||
- work_queue.extend (work)
|
||
- if work_queue.count <= capacity.as_integer_32 then
|
||
- -- Let one thread wake up and do the work
|
||
- work_semaphore.post
|
||
- end
|
||
- work_queue_mutex.unlock
|
||
- end
|
||
-
|
||
- over: BOOLEAN
|
||
- -- Is the thread pool being terminated?
|
||
- do
|
||
- over_mutex.lock
|
||
- Result := is_over
|
||
- over_mutex.unlock
|
||
- end
|
||
-
|
||
- thread_terminated (a_thread: POOLED_THREAD [G])
|
||
- -- Notifies the thread pool that a thread has terminated its execution.
|
||
- do
|
||
- termination_mutex.lock
|
||
- terminated_count := terminated_count - 1
|
||
- termination_mutex.unlock
|
||
- end
|
||
-
|
||
- get_work (requester: POOLED_THREAD [G]): detachable PROCEDURE
|
||
- -- If there is work to do, it is returned
|
||
- -- Yields Void otherwise
|
||
- do
|
||
- if not over then
|
||
- work_queue_mutex.lock
|
||
- if not work_queue.is_empty then
|
||
- Result := work_queue.item
|
||
- work_queue.remove
|
||
- end
|
||
- work_queue_mutex.unlock
|
||
- end
|
||
- end
|
||
-
|
||
- wait_for_completion
|
||
- -- Wait until there is no more work to be completed
|
||
- local
|
||
- done: BOOLEAN
|
||
- do
|
||
- from
|
||
-
|
||
- until
|
||
- done
|
||
- loop
|
||
- work_queue_mutex.lock
|
||
- done := work_queue.is_empty
|
||
- work_queue_mutex.unlock
|
||
- if not done then
|
||
- sleep (1)
|
||
- end
|
||
- end
|
||
- end
|
||
-
|
||
- terminate
|
||
- -- Terminates all the threads after their execution
|
||
- do
|
||
- over_mutex.lock
|
||
- is_over := True
|
||
- over_mutex.unlock
|
||
- from
|
||
- termination_mutex.lock
|
||
- until
|
||
- terminated_count = 0
|
||
- loop
|
||
- work_semaphore.post
|
||
- termination_mutex.unlock
|
||
- termination_mutex.lock
|
||
- end
|
||
- termination_mutex.unlock
|
||
- end
|
||
-
|
||
-feature {NONE} -- Implementation: Access
|
||
-
|
||
- work_queue: ARRAYED_QUEUE [PROCEDURE]
|
||
- -- Queue that holds unprocessed requests as agents
|
||
- -- Thread-safe access when accessor holds `queue_mutex'
|
||
-
|
||
- work_queue_mutex: MUTEX
|
||
- -- Mutex for the queue
|
||
-
|
||
- work_semaphore: SEMAPHORE
|
||
- -- Semaphore which hols the number of work to be done.
|
||
- -- Needed to wake up worker threads
|
||
-
|
||
- terminated_count: NATURAL
|
||
- --
|
||
-
|
||
- is_over: BOOLEAN
|
||
- -- Is the thread pool being terminated?
|
||
-
|
||
- over_mutex: MUTEX
|
||
- -- Mutex for the `is_over' variable
|
||
-
|
||
- termination_mutex: MUTEX
|
||
-;note
|
||
- copyright: "2011-2012, Javier Velilla, Jocelyn Fiat and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- licensing_options: "http://www.eiffel.com/licensing"
|
||
- copying: "[
|
||
- This file is part of Eiffel Software's Eiffel Development Environment.
|
||
-
|
||
- Eiffel Software's Eiffel Development Environment is free
|
||
- software; you can redistribute it and/or modify it under
|
||
- the terms of the GNU General Public License as published
|
||
- by the Free Software Foundation, version 2 of the License
|
||
- (available at the URL listed under "license" above).
|
||
-
|
||
- Eiffel Software's Eiffel Development Environment is
|
||
- distributed in the hope that it will be useful, but
|
||
- WITHOUT ANY WARRANTY; without even the implied warranty
|
||
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
- See the GNU General Public License for more details.
|
||
-
|
||
- You should have received a copy of the GNU General Public
|
||
- License along with Eiffel Software's Eiffel Development
|
||
- Environment; if not, write to the Free Software Foundation,
|
||
- Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||
- ]"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/configuration/httpd_configuration_i.e b/library/server/ewsgi/connectors/standalone/src/httpd/configuration/httpd_configuration_i.e
|
||
deleted file mode 100644
|
||
index 8ae522a..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/configuration/httpd_configuration_i.e
|
||
+++ /dev/null
|
||
@@ -1,235 +0,0 @@
|
||
-note
|
||
- description: "Configuration for the standalone HTTPd server."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_CONFIGURATION_I
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make
|
||
- do
|
||
- http_server_port := 80
|
||
- max_concurrent_connections := 100
|
||
- max_tcp_clients := 100
|
||
- socket_accept_timeout := 1_000
|
||
- socket_connect_timeout := 5_000
|
||
- keep_alive_timeout := 5
|
||
- is_secure := False
|
||
- create ca_crt.make_empty
|
||
- create ca_key.make_empty
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- Server_details: STRING_8
|
||
- -- Detail of the server.
|
||
- deferred
|
||
- end
|
||
-
|
||
- http_server_name: detachable READABLE_STRING_8 assign set_http_server_name
|
||
- http_server_port: INTEGER assign set_http_server_port
|
||
- max_tcp_clients: INTEGER assign set_max_tcp_clients
|
||
- max_concurrent_connections: INTEGER assign set_max_concurrent_connections
|
||
- socket_accept_timeout: INTEGER assign set_socket_accept_timeout
|
||
- socket_connect_timeout: INTEGER assign set_socket_connect_timeout
|
||
- force_single_threaded: BOOLEAN assign set_force_single_threaded
|
||
- do
|
||
- Result := (max_concurrent_connections = 0)
|
||
- end
|
||
-
|
||
- is_verbose: BOOLEAN assign set_is_verbose
|
||
- -- Display verbose message to the output?
|
||
-
|
||
- keep_alive_timeout: INTEGER assign set_keep_alive_timeout
|
||
- -- Persistent connection timeout
|
||
- -- Timeout unit in Seconds.
|
||
-
|
||
- has_ssl_support: BOOLEAN
|
||
- -- Has SSL support?
|
||
- deferred
|
||
- end
|
||
-
|
||
-feature -- Access: SSL
|
||
-
|
||
- is_secure: BOOLEAN
|
||
- -- Is SSL/TLS session?.
|
||
-
|
||
- ca_crt: STRING
|
||
- -- the signed certificate.
|
||
-
|
||
- ca_key: STRING
|
||
- -- private key to the certificate.
|
||
-
|
||
- ssl_protocol: NATURAL
|
||
- -- By default protocol is tls 1.2.
|
||
-
|
||
-feature -- Element change
|
||
-
|
||
- set_http_server_name (v: detachable separate READABLE_STRING_8)
|
||
- do
|
||
- if v = Void then
|
||
- unset_http_server_name
|
||
- else
|
||
- create {IMMUTABLE_STRING_8} http_server_name.make_from_separate (v)
|
||
- end
|
||
- --| Missing postcondition.
|
||
- end
|
||
-
|
||
- unset_http_server_name
|
||
- -- Unset `http_server_name' value.
|
||
- do
|
||
- http_server_name := Void
|
||
- ensure
|
||
- unset_http_server_name: http_server_name = Void
|
||
- end
|
||
-
|
||
- set_http_server_port (v: like http_server_port)
|
||
- -- Set `http_server_port' with `v'.
|
||
- do
|
||
- http_server_port := v
|
||
- ensure
|
||
- http_server_port_set: http_server_port = v
|
||
- end
|
||
-
|
||
- set_max_tcp_clients (v: like max_tcp_clients)
|
||
- -- Set `max_tcp_clients' with `v'.
|
||
- do
|
||
- max_tcp_clients := v
|
||
- ensure
|
||
- max_tcp_clients_set: max_tcp_clients = v
|
||
- end
|
||
-
|
||
- set_max_concurrent_connections (v: like max_concurrent_connections)
|
||
- -- Set `max_concurrent_connections' with `v'.
|
||
- do
|
||
- max_concurrent_connections := v
|
||
- ensure
|
||
- max_concurrent_connections_set : max_concurrent_connections = v
|
||
- end
|
||
-
|
||
- set_socket_accept_timeout (v: like socket_accept_timeout)
|
||
- -- Set `socket_accept_timeout' with `v'
|
||
- do
|
||
- socket_accept_timeout := v
|
||
- ensure
|
||
- socket_accept_timeout_set: socket_accept_timeout = v
|
||
- end
|
||
-
|
||
- set_socket_connect_timeout (v: like socket_connect_timeout)
|
||
- -- Set `socket_connect_timeout' with `v'
|
||
- do
|
||
- socket_connect_timeout := v
|
||
- ensure
|
||
- socket_connect_timeout_set: socket_connect_timeout = v
|
||
- end
|
||
-
|
||
- set_force_single_threaded (v: like force_single_threaded)
|
||
- do
|
||
- if v then
|
||
- set_max_concurrent_connections (0)
|
||
- end
|
||
- --|Missing postcondition
|
||
- --| force_single_thread_set: v implies max_concurrent_connections = 0
|
||
- --| not_single_thread: not v implies max_concurrent_connections > 0
|
||
- end
|
||
-
|
||
- set_is_verbose (b: BOOLEAN)
|
||
- -- Set `is_verbose' to `b'
|
||
- do
|
||
- is_verbose := b
|
||
- ensure
|
||
- is_verbose_set: is_verbose = b
|
||
- end
|
||
-
|
||
- set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
|
||
- -- Set `keep_alive_timeout' with `a_seconds'
|
||
- do
|
||
- keep_alive_timeout := a_seconds
|
||
- ensure
|
||
- keep_alive_timeout_set: keep_alive_timeout = a_seconds
|
||
- end
|
||
-
|
||
- mark_secure
|
||
- -- Set is_secure in True
|
||
- do
|
||
- if has_ssl_support then
|
||
- is_secure := True
|
||
- if http_server_port = 80 then
|
||
- set_http_server_port (443)
|
||
- end
|
||
- else
|
||
- is_secure := False
|
||
- end
|
||
- ensure
|
||
- is_secure_set: has_ssl_support implies is_secure
|
||
- -- http_server_port_set: has_ssl_support implies http_server_port = 443
|
||
- is_not_secure: not has_ssl_support implies not is_secure
|
||
- -- default_port: not has_ssl_support implies http_server_port = 80
|
||
- end
|
||
-
|
||
-feature -- Element change
|
||
-
|
||
- set_ca_crt (a_value: STRING)
|
||
- -- Set `ca_crt' with `a_value'
|
||
- do
|
||
- ca_crt := a_value
|
||
- ensure
|
||
- ca_crt_set: ca_crt = a_value
|
||
- end
|
||
-
|
||
- set_ca_key (a_value: STRING)
|
||
- -- Set `ca_key' with `a_value'
|
||
- do
|
||
- ca_key := a_value
|
||
- ensure
|
||
- ca_key_set: ca_key = a_value
|
||
- end
|
||
-
|
||
- set_ssl_protocol (a_version: NATURAL)
|
||
- -- Set `ssl_protocol' with `a_version'
|
||
- do
|
||
- ssl_protocol := a_version
|
||
- ensure
|
||
- ssl_protocol_set: ssl_protocol = a_version
|
||
- end
|
||
-
|
||
-feature -- SSL Helpers
|
||
-
|
||
- set_ssl_protocol_to_ssl_2_or_3
|
||
- -- Set `ssl_protocol' with `Ssl_23'.
|
||
- deferred
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_tls_1_0
|
||
- -- Set `ssl_protocol' with `Tls_1_0'.
|
||
- deferred
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_tls_1_1
|
||
- -- Set `ssl_protocol' with `Tls_1_1'.
|
||
- deferred
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_tls_1_2
|
||
- -- Set `ssl_protocol' with `Tls_1_2'.
|
||
- deferred
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_dtls_1_0
|
||
- -- Set `ssl_protocol' with `Dtls_1_0'.
|
||
- deferred
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd-safe.ecf b/library/server/ewsgi/connectors/standalone/src/httpd/httpd-safe.ecf
|
||
deleted file mode 100644
|
||
index 440e3c1..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd-safe.ecf
|
||
+++ /dev/null
|
||
@@ -1,60 +0,0 @@
|
||
-<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
-<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="httpd" uuid="50FE258D-CC94-4748-9223-55F1129E5FB3" library_target="httpd">
|
||
- <target name="httpd">
|
||
- <root all_classes="true"/>
|
||
- <file_rule>
|
||
- <exclude>/.git$</exclude>
|
||
- <exclude>/.svn$</exclude>
|
||
- <exclude>/EIFGENs$</exclude>
|
||
- </file_rule>
|
||
- <option warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="standard">
|
||
- <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||
- </option>
|
||
- <setting name="concurrency" value="scoop"/>
|
||
- <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
- <library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||
- <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
|
||
- <condition>
|
||
- <custom name="httpd_ssl_enabled" value="true"/>
|
||
- </condition>
|
||
- </library>
|
||
- <library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf">
|
||
- <condition>
|
||
- <concurrency excluded_value="none"/>
|
||
- </condition>
|
||
- </library>
|
||
- <library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||
- <cluster name="httpd_server" location=".\" recursive="true">
|
||
- <file_rule>
|
||
- <exclude>/concurrency$</exclude>
|
||
- <exclude>/no_ssl$</exclude>
|
||
- <exclude>/ssl$</exclude>
|
||
- </file_rule>
|
||
- <cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
||
- <condition>
|
||
- <custom name="httpd_ssl_enabled" excluded_value="true"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- <cluster name="ssl" location="$|ssl\" recursive="true">
|
||
- <condition>
|
||
- <custom name="httpd_ssl_enabled" value="true"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- <cluster name="concurrency_none" location="$|concurrency\none\" recursive="true">
|
||
- <condition>
|
||
- <concurrency value="none"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- <cluster name="concurrency_scoop" location="$|concurrency\scoop\" recursive="true">
|
||
- <condition>
|
||
- <concurrency value="scoop"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- <cluster name="concurrency_thread" location="$|concurrency\thread\" recursive="true">
|
||
- <condition>
|
||
- <concurrency value="thread"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- </cluster>
|
||
- </target>
|
||
-</system>
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd.ecf b/library/server/ewsgi/connectors/standalone/src/httpd/httpd.ecf
|
||
deleted file mode 100644
|
||
index 88aeaa4..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd.ecf
|
||
+++ /dev/null
|
||
@@ -1,61 +0,0 @@
|
||
-<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
-<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="httpd" uuid="50FE258D-CC94-4748-9223-55F1129E5FB3" library_target="httpd">
|
||
- <target name="httpd">
|
||
- <root all_classes="true"/>
|
||
- <file_rule>
|
||
- <exclude>/.git$</exclude>
|
||
- <exclude>/.svn$</exclude>
|
||
- <exclude>/EIFGENs$</exclude>
|
||
- </file_rule>
|
||
- <option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||
- </option>
|
||
- <setting name="concurrency" value="thread"/>
|
||
- <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||
- <library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
|
||
- <library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||
- <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||
- <condition>
|
||
- <custom name="httpd_ssl_enabled" value="true"/>
|
||
- </condition>
|
||
- </library>
|
||
- <library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf">
|
||
- <condition>
|
||
- <concurrency excluded_value="none"/>
|
||
- </condition>
|
||
- </library>
|
||
- <library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||
- <cluster name="httpd_server" location=".\" recursive="true">
|
||
- <file_rule>
|
||
- <exclude>/EIFGENs$</exclude>
|
||
- <exclude>/concurrency$</exclude>
|
||
- <exclude>/no_ssl$</exclude>
|
||
- <exclude>/ssl$</exclude>
|
||
- </file_rule>
|
||
- <cluster name="no_ssl" location="$|no_ssl\" recursive="true">
|
||
- <condition>
|
||
- <custom name="httpd_ssl_enabled" excluded_value="true"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- <cluster name="ssl" location="$|ssl\" recursive="true">
|
||
- <condition>
|
||
- <custom name="httpd_ssl_enabled" value="true"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- <cluster name="concurrency_none" location="$|concurrency\none\" recursive="true">
|
||
- <condition>
|
||
- <concurrency value="none"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- <cluster name="concurrency_scoop" location="$|concurrency\scoop\" recursive="true">
|
||
- <condition>
|
||
- <concurrency value="scoop"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- <cluster name="concurrency_thread" location="$|concurrency\thread\" recursive="true">
|
||
- <condition>
|
||
- <concurrency value="thread"/>
|
||
- </condition>
|
||
- </cluster>
|
||
- </cluster>
|
||
- </target>
|
||
-</system>
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_connection_handler_i.e b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_connection_handler_i.e
|
||
deleted file mode 100644
|
||
index be3e51f..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_connection_handler_i.e
|
||
+++ /dev/null
|
||
@@ -1,97 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Interface for the incoming connection handler.
|
||
-
|
||
- Each incoming socket connection is processed by
|
||
- an implementation of HTTPD_CONNECTION_HANDLER_I.
|
||
-
|
||
- Note there are 3 implementations, one for each concurrent mode: none, thread, scoop.
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_CONNECTION_HANDLER_I
|
||
-
|
||
-inherit
|
||
- HTTPD_DEBUG_FACILITIES
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- frozen make (a_server: like server)
|
||
- do
|
||
- server := a_server
|
||
- factory := separate_factory (a_server)
|
||
- initialize
|
||
- end
|
||
-
|
||
- initialize
|
||
- deferred
|
||
- end
|
||
-
|
||
-feature {NONE} -- Access
|
||
-
|
||
- factory: separate HTTPD_REQUEST_HANDLER_FACTORY
|
||
- -- Request handler factory.
|
||
-
|
||
- server: separate HTTPD_SERVER_I
|
||
- -- Associated server.
|
||
-
|
||
-feature {HTTPD_SERVER_I} -- Execution
|
||
-
|
||
- accept_incoming_connection (a_listening_socket: HTTPD_STREAM_SOCKET)
|
||
- -- Accept incoming connection from `a_listening_socket'.
|
||
- deferred
|
||
- end
|
||
-
|
||
- shutdown
|
||
- -- Shutdown server.
|
||
- deferred
|
||
- end
|
||
-
|
||
- wait_for_completion
|
||
- -- Wait until Current completed any pending task.
|
||
- --| Used for SCOOP synchronisation.
|
||
- deferred
|
||
- end
|
||
-
|
||
-feature {HTTPD_SERVER} -- Status report
|
||
-
|
||
- is_shutdown_requested: BOOLEAN
|
||
- -- Any request to shutdown the server?
|
||
- deferred
|
||
- end
|
||
-
|
||
-feature {NONE} -- Implementation
|
||
-
|
||
- log (a_message: separate READABLE_STRING_8)
|
||
- -- Log `a_message'
|
||
- do
|
||
- -- FIXME: Concurrency issue on `server'
|
||
- separate_server_log (server, a_message)
|
||
- end
|
||
-
|
||
- separate_factory (a_server: like server): like factory
|
||
- -- Separate factory from `a_server'.
|
||
- --| required by SCOOP design.
|
||
- do
|
||
- Result := a_server.factory
|
||
- end
|
||
-
|
||
- separate_server_log (a_server: like server; a_message: separate READABLE_STRING_8)
|
||
- -- Concurrent call to `a_server.log (a_message)'.
|
||
- do
|
||
- a_server.log (a_message)
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_controller.e b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_controller.e
|
||
deleted file mode 100644
|
||
index f9a6667..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_controller.e
|
||
+++ /dev/null
|
||
@@ -1,28 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Object used to control (i.e shutdown) the server.
|
||
- Mostly needed in SCOOP concurrency mode.
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_CONTROLLER
|
||
-
|
||
-feature -- Operation
|
||
-
|
||
- shutdown
|
||
- -- Request the associated server to be shutdown.
|
||
- do
|
||
- shutdown_requested := True
|
||
- end
|
||
-
|
||
-feature -- Status report.
|
||
-
|
||
- shutdown_requested: BOOLEAN
|
||
- -- Shutdown requested.
|
||
-
|
||
-;note
|
||
- copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_debug_facilities.e b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_debug_facilities.e
|
||
deleted file mode 100644
|
||
index 3182332..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_debug_facilities.e
|
||
+++ /dev/null
|
||
@@ -1,50 +0,0 @@
|
||
-note
|
||
- description: " Routines used for debug logging."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_DEBUG_FACILITIES
|
||
-
|
||
-feature {NONE} -- Output
|
||
-
|
||
- dbglog (m: READABLE_STRING_8)
|
||
- require
|
||
- not m.ends_with_general ("%N")
|
||
- local
|
||
- s: STRING
|
||
- do
|
||
- debug ("dbglog")
|
||
- create s.make (24)
|
||
- s.append ("[EWF/DBG] <#")
|
||
- s.append_integer (processor_id_from_object (Current))
|
||
- s.append ("> ")
|
||
- s.append (generator)
|
||
- s.append (create {STRING}.make_filled (' ', (46 - s.count).max (0)))
|
||
- s.append (" | ")
|
||
- s.append (m)
|
||
- s.append ("%N")
|
||
- print (s)
|
||
- end
|
||
- end
|
||
-
|
||
-feature -- runtime
|
||
-
|
||
- frozen processor_id_from_object (a_object: separate ANY): INTEGER_32
|
||
- external
|
||
- "C inline use %"eif_scoop.h%""
|
||
- alias
|
||
- "RTS_PID(eif_access($a_object))"
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_logger.e b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_logger.e
|
||
deleted file mode 100644
|
||
index 057f43a..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_logger.e
|
||
+++ /dev/null
|
||
@@ -1,26 +0,0 @@
|
||
-note
|
||
- description: "Logging facilities component"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_LOGGER
|
||
-
|
||
-feature -- Logs
|
||
-
|
||
- log (a_message: separate READABLE_STRING_8)
|
||
- -- Log `a_message'
|
||
- deferred
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_factory_i.e b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_factory_i.e
|
||
deleted file mode 100644
|
||
index 940d7a0..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_factory_i.e
|
||
+++ /dev/null
|
||
@@ -1,26 +0,0 @@
|
||
-note
|
||
- description: "Summary description for {HTTPD_REQUEST_HANDLER_FACTORY_I}."
|
||
- author: ""
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_REQUEST_HANDLER_FACTORY_I
|
||
-
|
||
-feature -- Factory
|
||
-
|
||
- new_handler: separate HTTPD_REQUEST_HANDLER
|
||
- deferred
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_i.e b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_i.e
|
||
deleted file mode 100644
|
||
index 8eaef93..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_i.e
|
||
+++ /dev/null
|
||
@@ -1,426 +0,0 @@
|
||
-note
|
||
- description: "HTTPD handler interface processing request."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_REQUEST_HANDLER_I
|
||
-
|
||
-inherit
|
||
- HTTPD_DEBUG_FACILITIES
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make
|
||
- do
|
||
- reset
|
||
- end
|
||
-
|
||
- reset
|
||
- do
|
||
- reset_request
|
||
-
|
||
- has_error := False
|
||
- if attached internal_client_socket as l_sock then
|
||
- l_sock.cleanup
|
||
- end
|
||
- internal_client_socket := Void
|
||
- end
|
||
-
|
||
- reset_request
|
||
- do
|
||
- version := Void
|
||
- remote_info := Void
|
||
-
|
||
- -- FIXME: optimize to just wipe_out if needed
|
||
- create method.make_empty
|
||
- create uri.make_empty
|
||
- create request_header.make_empty
|
||
- create request_header_map.make (10)
|
||
-
|
||
- is_persistent_connection_requested := False
|
||
- end
|
||
-
|
||
-feature -- Status report
|
||
-
|
||
- is_connected: BOOLEAN
|
||
- -- Is handler connected to incoming request via `client_socket'?
|
||
- do
|
||
- Result := client_socket.descriptor_available
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- internal_client_socket: detachable HTTPD_STREAM_SOCKET
|
||
-
|
||
- client_socket: HTTPD_STREAM_SOCKET
|
||
- local
|
||
- s: like internal_client_socket
|
||
- do
|
||
- s := internal_client_socket
|
||
- if s = Void then
|
||
- create s.make_empty
|
||
- internal_client_socket := s
|
||
- end
|
||
- Result := s
|
||
- end
|
||
-
|
||
- request_header: STRING
|
||
- -- Header' source
|
||
-
|
||
- request_header_map: HASH_TABLE [STRING, STRING]
|
||
- -- Contains key:value of the header
|
||
-
|
||
- method: STRING
|
||
- -- http verb
|
||
-
|
||
- uri: STRING
|
||
- -- http endpoint
|
||
-
|
||
- version: detachable STRING
|
||
- -- http_version
|
||
- --| unused for now
|
||
-
|
||
- remote_info: detachable TUPLE [addr: STRING; hostname: STRING; port: INTEGER]
|
||
- -- Information related to remote client
|
||
-
|
||
- is_persistent_connection_requested: BOOLEAN
|
||
- -- Persistent connection requested?
|
||
- -- either has "Connection: keep-alive" header,
|
||
- -- or is HTTP/1.1 and no header "Connection: close".
|
||
-
|
||
- is_http_version_1_0: BOOLEAN
|
||
- do
|
||
- Result := not attached version as v or else v.same_string ("HTTP/1.0")
|
||
- end
|
||
-
|
||
- is_http_version_1_1: BOOLEAN
|
||
- do
|
||
- Result := not attached version as v or else v.same_string ("HTTP/1.1")
|
||
- end
|
||
-
|
||
- is_http_version_2: BOOLEAN
|
||
- do
|
||
- Result := not attached version as v or else v.same_string ("HTTP/2.0")
|
||
- end
|
||
-
|
||
-feature -- Settings
|
||
-
|
||
- is_verbose: BOOLEAN
|
||
-
|
||
- is_persistent_connection_supported: BOOLEAN
|
||
- -- Is persistent connection supported?
|
||
- do
|
||
- Result := {HTTPD_SERVER}.is_persistent_connection_supported
|
||
- end
|
||
-
|
||
- persistent_connection_timeout: INTEGER = 5 -- seconds
|
||
- -- Number of seconds for persistent connection timeout.
|
||
- -- Default: 5 sec.
|
||
-
|
||
-feature -- Status report
|
||
-
|
||
- has_error: BOOLEAN
|
||
- -- Error occurred during `analyze_request_message'
|
||
-
|
||
-feature -- Change
|
||
-
|
||
- set_is_verbose (b: BOOLEAN)
|
||
- -- Set `is_verbose' with `b'.
|
||
- do
|
||
- is_verbose := b
|
||
- ensure
|
||
- is_verbose_set: is_verbose = b
|
||
- end
|
||
-
|
||
-feature -- Execution
|
||
-
|
||
- safe_execute
|
||
- -- Execute accepted incoming connection as request.
|
||
- local
|
||
- retried: BOOLEAN
|
||
- do
|
||
- if retried then
|
||
- release
|
||
- else
|
||
- if
|
||
- not has_error and then
|
||
- is_connected
|
||
- then
|
||
- execute
|
||
- end
|
||
- release
|
||
- end
|
||
- rescue
|
||
- retried := True
|
||
- retry
|
||
- end
|
||
-
|
||
- execute
|
||
- require
|
||
- is_connected: is_connected
|
||
- local
|
||
- l_socket: like client_socket
|
||
- l_exit: BOOLEAN
|
||
- n: INTEGER
|
||
- do
|
||
- l_socket := client_socket
|
||
- check
|
||
- socket_attached: l_socket /= Void
|
||
- socket_valid: l_socket.is_open_read and then l_socket.is_open_write
|
||
- end
|
||
- from
|
||
- -- Process persistent connection as long the socket is not closed.
|
||
- n := 0
|
||
- until
|
||
- l_exit
|
||
- loop
|
||
- n := n + 1
|
||
- -- FIXME: it seems to be called one more time, mostly to see this is done.
|
||
- execute_request
|
||
- l_exit := not is_persistent_connection_supported
|
||
- or has_error or l_socket.is_closed or not l_socket.is_open_read
|
||
- or not is_persistent_connection_requested
|
||
- reset_request
|
||
- end
|
||
- end
|
||
-
|
||
- execute_request
|
||
- require
|
||
- is_connected: is_connected
|
||
- local
|
||
- l_remote_info: detachable like remote_info
|
||
- l_socket: like client_socket
|
||
- l_is_ready: BOOLEAN
|
||
- i: INTEGER
|
||
- do
|
||
- l_socket := client_socket
|
||
- check
|
||
- socket_attached: l_socket /= Void
|
||
- socket_valid: l_socket.is_open_read and then l_socket.is_open_write
|
||
- end
|
||
- if l_socket.is_closed then
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".execute_request {socket is Closed!}")
|
||
- end
|
||
- else
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
||
- end
|
||
-
|
||
- --| TODO: add configuration options for socket timeout.
|
||
- --| set by default 5 seconds.
|
||
--- l_socket.set_timeout (persistent_connection_timeout) -- 5 seconds!
|
||
- l_socket.set_timeout (1) -- 1 second!
|
||
- from
|
||
- i := persistent_connection_timeout -- * 1 sec
|
||
- until
|
||
- l_is_ready or i <= 0 or has_error
|
||
- loop
|
||
- l_is_ready := l_socket.ready_for_reading
|
||
- check not l_socket.is_closed end
|
||
- i := i - 1
|
||
- end
|
||
-
|
||
- if l_is_ready then
|
||
- create l_remote_info
|
||
- if attached l_socket.peer_address as l_addr then
|
||
- l_remote_info.addr := l_addr.host_address.host_address
|
||
- l_remote_info.hostname := l_addr.host_address.host_name
|
||
- l_remote_info.port := l_addr.port
|
||
- remote_info := l_remote_info
|
||
- end
|
||
- analyze_request_message (l_socket)
|
||
- else
|
||
- has_error := True
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + "} timeout!")
|
||
- end
|
||
- end
|
||
-
|
||
- if has_error then
|
||
- if l_is_ready then
|
||
- -- check catch_bad_incoming_connection: False end
|
||
- if is_verbose then
|
||
- log ("ERROR: invalid HTTP incoming request")
|
||
- end
|
||
- end
|
||
- else
|
||
- process_request (l_socket)
|
||
- end
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".execute_request {" + l_socket.descriptor.out + "} LEAVE")
|
||
- end
|
||
- end
|
||
- end
|
||
-
|
||
- release
|
||
- do
|
||
- reset
|
||
- end
|
||
-
|
||
-feature -- Request processing
|
||
-
|
||
- process_request (a_socket: HTTPD_STREAM_SOCKET)
|
||
- -- Process request ...
|
||
- require
|
||
- no_error: not has_error
|
||
- a_uri_attached: uri /= Void
|
||
- a_method_attached: method /= Void
|
||
- a_header_map_attached: request_header_map /= Void
|
||
- a_header_text_attached: request_header /= Void
|
||
- a_socket_attached: a_socket /= Void
|
||
- deferred
|
||
- end
|
||
-
|
||
-feature -- Parsing
|
||
-
|
||
- analyze_request_message (a_socket: HTTPD_STREAM_SOCKET)
|
||
- -- Analyze message extracted from `a_socket' as HTTP request
|
||
- require
|
||
- input_readable: a_socket /= Void and then a_socket.is_open_read
|
||
- local
|
||
- end_of_stream: BOOLEAN
|
||
- pos, n: INTEGER
|
||
- line: detachable STRING
|
||
- k, val: STRING
|
||
- txt: STRING
|
||
- l_is_verbose: BOOLEAN
|
||
- do
|
||
- create txt.make (64)
|
||
- request_header := txt
|
||
- if a_socket.is_readable and then attached next_line (a_socket) as l_request_line and then not l_request_line.is_empty then
|
||
- txt.append (l_request_line)
|
||
- txt.append_character ('%N')
|
||
- analyze_request_line (l_request_line)
|
||
- else
|
||
- has_error := True
|
||
- end
|
||
- l_is_verbose := is_verbose
|
||
- if not has_error or l_is_verbose then
|
||
- -- if `is_verbose' we can try to print the request, even if it is a bad HTTP request
|
||
- from
|
||
- line := next_line (a_socket)
|
||
- until
|
||
- line = Void or end_of_stream
|
||
- loop
|
||
- n := line.count
|
||
- if l_is_verbose then
|
||
- log (line)
|
||
- end
|
||
- pos := line.index_of (':', 1)
|
||
- if pos > 0 then
|
||
- k := line.substring (1, pos - 1)
|
||
- if line [pos + 1].is_space then
|
||
- pos := pos + 1
|
||
- end
|
||
- if line [n] = '%R' then
|
||
- n := n - 1
|
||
- end
|
||
- val := line.substring (pos + 1, n)
|
||
- request_header_map.put (val, k)
|
||
- end
|
||
- txt.append (line)
|
||
- txt.append_character ('%N')
|
||
- if line.is_empty or else line [1] = '%R' then
|
||
- end_of_stream := True
|
||
- else
|
||
- line := next_line (a_socket)
|
||
- end
|
||
- end
|
||
- -- Except for HTTP/1.0, persistent connection is the default.
|
||
- is_persistent_connection_requested := True
|
||
- if is_http_version_1_0 then
|
||
- is_persistent_connection_requested := attached request_header_map.item ("Connection") as l_connection and then
|
||
- l_connection.is_case_insensitive_equal_general ("keep-alive")
|
||
- else
|
||
- -- By default HTTP:1/1 support persistent connection.
|
||
- if attached request_header_map.item ("Connection") as l_connection then
|
||
- if l_connection.is_case_insensitive_equal_general ("close") then
|
||
- is_persistent_connection_requested := False
|
||
- end
|
||
- else
|
||
- is_persistent_connection_requested := True
|
||
- end
|
||
- end
|
||
- end
|
||
- end
|
||
-
|
||
- analyze_request_line (line: STRING)
|
||
- -- Analyze `line' as a HTTP request line
|
||
- require
|
||
- valid_line: line /= Void and then not line.is_empty
|
||
- local
|
||
- n, pos, next_pos: INTEGER
|
||
- do
|
||
- if is_verbose then
|
||
- log ("%N## Parse HTTP request line ##")
|
||
- log (line)
|
||
- end
|
||
- pos := line.index_of (' ', 1)
|
||
- method := line.substring (1, pos - 1)
|
||
- next_pos := line.index_of (' ', pos + 1)
|
||
- uri := line.substring (pos + 1, next_pos - 1)
|
||
- n := line.count
|
||
- if line[n] = '%R' then
|
||
- n := n - 1
|
||
- end
|
||
- version := line.substring (next_pos + 1, n)
|
||
- has_error := method.is_empty
|
||
- end
|
||
-
|
||
- next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
|
||
- -- Next line fetched from `a_socket' is available.
|
||
- require
|
||
- is_readable: a_socket.is_open_read
|
||
- local
|
||
- retried: BOOLEAN
|
||
- do
|
||
- if retried then
|
||
- Result := Void
|
||
- elseif a_socket.socket_ok then
|
||
- a_socket.read_line_thread_aware
|
||
- Result := a_socket.last_string
|
||
- end
|
||
- rescue
|
||
- retried := True
|
||
- retry
|
||
- end
|
||
-
|
||
-feature -- Output
|
||
-
|
||
- logger: detachable HTTPD_LOGGER
|
||
-
|
||
- set_logger (a_logger: like logger)
|
||
- -- Set `logger' with `a_logger'.
|
||
- do
|
||
- logger := a_logger
|
||
- ensure
|
||
- logger_set: logger = a_logger
|
||
- end
|
||
-
|
||
- log (m: STRING)
|
||
- -- Log message `m'.
|
||
- do
|
||
- if attached logger as l_logger then
|
||
- l_logger.log (m)
|
||
- else
|
||
- io.put_string (m + "%N")
|
||
- end
|
||
- end
|
||
-
|
||
-invariant
|
||
- request_header_attached: request_header /= Void
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_server_i.e b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_server_i.e
|
||
deleted file mode 100644
|
||
index d3c236d..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_server_i.e
|
||
+++ /dev/null
|
||
@@ -1,354 +0,0 @@
|
||
-note
|
||
- description: "HTTPD server interface"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_SERVER_I
|
||
-
|
||
-inherit
|
||
- HTTPD_DEBUG_FACILITIES
|
||
-
|
||
- HTTPD_LOGGER
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make (a_factory: like factory)
|
||
- -- Create current httpd server with `a_factory' of connection handlers.
|
||
- -- `a_factory': connection handler builder
|
||
- require
|
||
- a_factory_is_separated: {PLATFORM}.is_scoop_capable implies not attached {HTTPD_REQUEST_HANDLER_FACTORY} a_factory
|
||
- do
|
||
- make_configured (create {like configuration}.make, a_factory)
|
||
- end
|
||
-
|
||
- make_configured (a_cfg: like configuration; a_factory: like factory)
|
||
- -- `a_cfg': server configuration
|
||
- -- `a_factory': connection handler builder
|
||
- do
|
||
- configuration := a_cfg
|
||
- factory := a_factory
|
||
-
|
||
- build_controller
|
||
-
|
||
- initialize
|
||
- end
|
||
-
|
||
- build_controller
|
||
- -- Build `controller'.
|
||
- do
|
||
- create controller
|
||
- end
|
||
-
|
||
- initialize
|
||
- -- Initialize Current server.
|
||
- do
|
||
- is_shutdown_requested := False
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- is_verbose: BOOLEAN
|
||
- -- Is verbose for output messages.
|
||
-
|
||
- configuration: HTTPD_CONFIGURATION
|
||
- -- Associated server configuration.
|
||
-
|
||
- controller: separate HTTPD_CONTROLLER
|
||
-
|
||
- factory: separate HTTPD_REQUEST_HANDLER_FACTORY
|
||
-
|
||
- is_persistent_connection_supported: BOOLEAN = True
|
||
- -- Is persistent connection supported?
|
||
-
|
||
-feature -- Callbacks
|
||
-
|
||
- observer: detachable separate HTTPD_SERVER_OBSERVER
|
||
-
|
||
- set_observer (obs: like observer)
|
||
- -- Set `observer' to `obs'.
|
||
- do
|
||
- observer := obs
|
||
- end
|
||
-
|
||
-feature -- Access: listening
|
||
-
|
||
- port: INTEGER
|
||
- -- Effective listening port.
|
||
- --| If 0 then it is not launched successfully!
|
||
-
|
||
-feature -- Status: listening
|
||
-
|
||
- is_launched: BOOLEAN
|
||
- -- Server launched and listening on `port'
|
||
-
|
||
- is_terminated: BOOLEAN
|
||
- -- Is terminated?
|
||
-
|
||
- is_shutdown_requested: BOOLEAN
|
||
- -- Set true to stop accept loop
|
||
-
|
||
-feature {NONE} -- Access: server
|
||
-
|
||
- request_counter: INTEGER
|
||
- -- request counter, incremented for each new incoming connection.
|
||
-
|
||
-feature -- Execution
|
||
-
|
||
- launch
|
||
- do
|
||
- apply_configuration
|
||
- is_terminated := False
|
||
- if is_verbose then
|
||
- log ("%N%NStarting Web Application Server (port=" + configuration.http_server_port.out + "):%N")
|
||
- end
|
||
- is_shutdown_requested := False
|
||
- listen
|
||
- is_terminated := True
|
||
- on_terminated
|
||
- end
|
||
-
|
||
- shutdown_server
|
||
- do
|
||
- debug ("dbglog")
|
||
- dbglog ("Shutdown requested")
|
||
- end
|
||
- is_shutdown_requested := True
|
||
- controller_shutdown (controller)
|
||
- end
|
||
-
|
||
- controller_shutdown (ctl: attached like controller)
|
||
- do
|
||
- ctl.shutdown
|
||
- end
|
||
-
|
||
-feature -- Listening
|
||
-
|
||
- listen
|
||
- -- <Precursor>
|
||
- -- Creates a socket and connects to the http server.
|
||
- -- `a_server': The main server object
|
||
- local
|
||
- l_listening_socket: detachable HTTPD_STREAM_SOCKET
|
||
- l_http_port: INTEGER
|
||
- l_connection_handler: HTTPD_CONNECTION_HANDLER
|
||
- do
|
||
- is_terminated := False
|
||
- is_launched := False
|
||
- port := 0
|
||
- is_shutdown_requested := False
|
||
- l_http_port := configuration.http_server_port
|
||
-
|
||
- if
|
||
- attached configuration.http_server_name as l_servername and then
|
||
- attached (create {INET_ADDRESS_FACTORY}).create_from_name (l_servername) as l_addr
|
||
- then
|
||
- l_listening_socket := new_listening_socket (l_addr, l_http_port)
|
||
- else
|
||
- l_listening_socket := new_listening_socket (Void, l_http_port)
|
||
- end
|
||
-
|
||
- if not l_listening_socket.is_bound then
|
||
- if is_verbose then
|
||
- log ("Socket could not be bound on port " + l_http_port.out)
|
||
- end
|
||
- else
|
||
- l_http_port := l_listening_socket.port
|
||
- create l_connection_handler.make (Current)
|
||
- from
|
||
- l_listening_socket.listen (configuration.max_tcp_clients)
|
||
- if is_verbose then
|
||
- if configuration.is_secure then
|
||
- log ("%NHTTP Connection Server ready on port " + l_http_port.out +" : https://localhost:" + l_http_port.out + "/")
|
||
- else
|
||
- log ("%NHTTP Connection Server ready on port " + l_http_port.out +" : http://localhost:" + l_http_port.out + "/")
|
||
- end
|
||
- end
|
||
- on_launched (l_http_port)
|
||
- until
|
||
- is_shutdown_requested
|
||
- loop
|
||
- request_counter := request_counter + 1
|
||
- if is_verbose then
|
||
- log ("#" + request_counter.out + "# Waiting connection...(listening socket:" + l_listening_socket.descriptor.out + ")")
|
||
- end
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".before process_waiting_incoming_connection")
|
||
- end
|
||
- l_connection_handler.accept_incoming_connection (l_listening_socket)
|
||
- debug ("dbglog")
|
||
- dbglog (generator + ".after process_waiting_incoming_connection")
|
||
- end
|
||
-
|
||
- update_is_shutdown_requested (l_connection_handler)
|
||
- end
|
||
- wait_for_connection_handler_completion (l_connection_handler)
|
||
- l_listening_socket.cleanup
|
||
- check
|
||
- socket_is_closed: l_listening_socket.is_closed
|
||
- end
|
||
- end
|
||
- if is_launched then
|
||
- on_stopped
|
||
- end
|
||
- if is_verbose then
|
||
- log ("HTTP Connection Server ends.")
|
||
- end
|
||
- rescue
|
||
- log ("HTTP Connection Server shutdown due to exception. Please relaunch manually.")
|
||
-
|
||
- if l_listening_socket /= Void then
|
||
- l_listening_socket.cleanup
|
||
- check
|
||
- listening_socket_is_closed: l_listening_socket.is_closed
|
||
- end
|
||
- end
|
||
- if is_launched then
|
||
- on_stopped
|
||
- end
|
||
- is_shutdown_requested := True
|
||
- retry
|
||
- end
|
||
-
|
||
-feature {NONE} -- Factory
|
||
-
|
||
- new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET
|
||
- do
|
||
- if a_addr /= Void then
|
||
- create Result.make_server_by_address_and_port (a_addr, a_http_port)
|
||
- else
|
||
- create Result.make_server_by_port (a_http_port)
|
||
- end
|
||
- end
|
||
-
|
||
-feature {NONE} -- Helpers
|
||
-
|
||
- wait_for_connection_handler_completion (h: HTTPD_CONNECTION_HANDLER)
|
||
- do
|
||
- h.wait_for_completion
|
||
- debug ("dbglog")
|
||
- dbglog ("Shutdown ready from connection_handler point of view")
|
||
- end
|
||
- end
|
||
-
|
||
- update_is_shutdown_requested (a_connection_handler: HTTPD_CONNECTION_HANDLER)
|
||
- do
|
||
- is_shutdown_requested := is_shutdown_requested or shutdown_requested (controller)
|
||
- if is_shutdown_requested then
|
||
- a_connection_handler.shutdown
|
||
- end
|
||
- end
|
||
-
|
||
- shutdown_requested (a_controller: separate HTTPD_CONTROLLER): BOOLEAN
|
||
- -- Shutdown requested on concurrent `a_controller'?
|
||
- do
|
||
- Result := a_controller.shutdown_requested
|
||
- end
|
||
-
|
||
-feature -- Event
|
||
-
|
||
- on_launched (a_port: INTEGER)
|
||
- -- Server launched using port `a_port'
|
||
- require
|
||
- not_launched: not is_launched
|
||
- do
|
||
- is_launched := True
|
||
- port := a_port
|
||
- if attached observer as obs then
|
||
- observer_on_launched (obs, a_port)
|
||
- end
|
||
- ensure
|
||
- is_launched: is_launched
|
||
- end
|
||
-
|
||
- on_stopped
|
||
- -- Server stopped
|
||
- require
|
||
- is_launched: is_launched
|
||
- do
|
||
- if attached observer as obs then
|
||
- observer_on_stopped (obs)
|
||
- end
|
||
- end
|
||
-
|
||
- on_terminated
|
||
- -- Server terminated
|
||
- require
|
||
- is_terminated
|
||
- do
|
||
- if is_terminated and is_verbose then
|
||
- log ("%N%NTerminating Web Application Server (port="+ port.out +"):%N")
|
||
- end
|
||
- if attached output as o then
|
||
- o.flush
|
||
- o.close
|
||
- end
|
||
- if attached observer as obs then
|
||
- observer_on_terminated (obs)
|
||
- end
|
||
- end
|
||
-
|
||
-feature {NONE} -- Separate event
|
||
-
|
||
- observer_on_launched (obs: attached like observer; a_port: INTEGER)
|
||
- do
|
||
- obs.on_launched (a_port)
|
||
- end
|
||
-
|
||
- observer_on_stopped (obs: attached like observer)
|
||
- do
|
||
- obs.on_stopped
|
||
- end
|
||
-
|
||
- observer_on_terminated (obs: attached like observer)
|
||
- do
|
||
- obs.on_terminated
|
||
- end
|
||
-
|
||
-feature -- Configuration change
|
||
-
|
||
- apply_configuration
|
||
- require
|
||
- is_not_launched: not is_launched
|
||
- do
|
||
- is_verbose := configuration.is_verbose
|
||
- end
|
||
-
|
||
-feature -- Output
|
||
-
|
||
- output: detachable FILE
|
||
-
|
||
- set_log_output (f: FILE)
|
||
- -- Set `output' to `f'.
|
||
- do
|
||
- output := f
|
||
- ensure
|
||
- output_set: output = f
|
||
- end
|
||
-
|
||
- log (a_message: separate READABLE_STRING_8)
|
||
- -- Log `a_message'.
|
||
- local
|
||
- m: STRING
|
||
- do
|
||
- create m.make_from_separate (a_message)
|
||
- if attached output as o then
|
||
- o.put_string (m)
|
||
- o.put_new_line
|
||
- else
|
||
- io.error.put_string (m)
|
||
- io.error.put_new_line
|
||
- end
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_server_observer.e b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_server_observer.e
|
||
deleted file mode 100644
|
||
index 18a5998..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_server_observer.e
|
||
+++ /dev/null
|
||
@@ -1,27 +0,0 @@
|
||
-note
|
||
- description: "Summary description for {HTTPD_SERVER_OBSERVER}."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-deferred class
|
||
- HTTPD_SERVER_OBSERVER
|
||
-
|
||
-feature -- Event
|
||
-
|
||
- on_launched (a_port: INTEGER)
|
||
- -- Associated server launched listening on port `a_port'.
|
||
- deferred
|
||
- end
|
||
-
|
||
- on_stopped
|
||
- -- Associated server stopped.
|
||
- --| the server may restart itself after being rescued.
|
||
- deferred
|
||
- end
|
||
-
|
||
- on_terminated
|
||
- -- Associated server terminated.
|
||
- deferred
|
||
- end
|
||
-
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/network/httpd_stream_socket.e b/library/server/ewsgi/connectors/standalone/src/httpd/network/httpd_stream_socket.e
|
||
deleted file mode 100644
|
||
index 4f4f115..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/network/httpd_stream_socket.e
|
||
+++ /dev/null
|
||
@@ -1,307 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Summary description for {HTTPD_STREAM_SOCKET}
|
||
- that can be used for http or https connection.
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_STREAM_SOCKET
|
||
-
|
||
-create
|
||
- make_server_by_address_and_port,
|
||
- make_server_by_port,
|
||
- make_from_separate,
|
||
- make_empty
|
||
-
|
||
-create {HTTPD_STREAM_SOCKET}
|
||
- make
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER)
|
||
- do
|
||
- create {TCP_STREAM_SOCKET} socket.make_server_by_address_and_port (an_address, a_port)
|
||
- end
|
||
-
|
||
- make_server_by_port (a_port: INTEGER)
|
||
- do
|
||
- create {TCP_STREAM_SOCKET} socket.make_server_by_port (a_port)
|
||
- end
|
||
-
|
||
- make_from_separate (s: separate HTTPD_STREAM_SOCKET)
|
||
- require
|
||
- descriptor_available: s.descriptor_available
|
||
- do
|
||
- create {TCP_STREAM_SOCKET} socket.make_from_separate (s.socket)
|
||
- end
|
||
-
|
||
- make_empty
|
||
- do
|
||
- create {TCP_STREAM_SOCKET} socket.make_empty
|
||
- end
|
||
-
|
||
- retrieve_socket (s: HTTPD_STREAM_SOCKET): INTEGER
|
||
- do
|
||
- Result := s.socket.descriptor
|
||
- end
|
||
-
|
||
-feature -- Change
|
||
-
|
||
- set_timeout (n: INTEGER)
|
||
- do
|
||
- if attached {NETWORK_STREAM_SOCKET} socket as l_socket then
|
||
- l_socket.set_timeout (n)
|
||
- end
|
||
- end
|
||
-
|
||
- set_connect_timeout (n: INTEGER)
|
||
- do
|
||
- if attached {NETWORK_STREAM_SOCKET} socket as l_socket then
|
||
- l_socket.set_connect_timeout (n)
|
||
- end
|
||
- end
|
||
-
|
||
- set_accept_timeout (n: INTEGER)
|
||
- do
|
||
- if attached {NETWORK_STREAM_SOCKET} socket as l_socket then
|
||
- l_socket.set_accept_timeout (n)
|
||
- end
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- last_string: STRING
|
||
- do
|
||
- Result := socket.last_string
|
||
- end
|
||
-
|
||
- last_character: CHARACTER
|
||
- do
|
||
- Result := socket.last_character
|
||
- end
|
||
-
|
||
- peer_address: detachable NETWORK_SOCKET_ADDRESS
|
||
- -- Peer address of socket
|
||
- do
|
||
- if attached {NETWORK_SOCKET_ADDRESS} socket.peer_address as l_peer_address then
|
||
- Result := l_peer_address
|
||
- end
|
||
- end
|
||
-
|
||
-feature -- Input
|
||
-
|
||
- read_line_thread_aware
|
||
- do
|
||
- socket.read_line_thread_aware
|
||
- end
|
||
-
|
||
- read_stream_thread_aware (nb: INTEGER)
|
||
- do
|
||
- socket.read_stream_thread_aware (nb)
|
||
- end
|
||
-
|
||
- read_stream (nb: INTEGER)
|
||
- do
|
||
- socket.read_stream (nb)
|
||
- end
|
||
-
|
||
- read_character
|
||
- do
|
||
- socket.read_character
|
||
- end
|
||
-
|
||
- bytes_read: INTEGER
|
||
- do
|
||
- Result := socket.bytes_read
|
||
- end
|
||
-
|
||
-feature -- Output
|
||
-
|
||
- send_message (a_msg: STRING)
|
||
- do
|
||
- put_string (a_msg)
|
||
- end
|
||
-
|
||
- put_readable_string_8 (s: READABLE_STRING_8)
|
||
- -- Write readable string `s' to socket.
|
||
- do
|
||
- if attached {TCP_STREAM_SOCKET} socket as l_tcp_stream_socket then
|
||
- l_tcp_stream_socket.put_readable_string_8 (s)
|
||
- else
|
||
- put_string (s)
|
||
- end
|
||
- end
|
||
-
|
||
- put_string (s: STRING)
|
||
- do
|
||
- socket.put_string (s)
|
||
- end
|
||
-
|
||
- put_character (c: CHARACTER)
|
||
- do
|
||
- socket.put_character (c)
|
||
- end
|
||
-
|
||
-feature -- Status Report
|
||
-
|
||
- descriptor_available: BOOLEAN
|
||
- -- Is descriptor available?
|
||
- do
|
||
- Result := socket.descriptor_available
|
||
- end
|
||
-
|
||
- descriptor: INTEGER
|
||
- do
|
||
- Result := socket.descriptor
|
||
- end
|
||
-
|
||
- port: INTEGER
|
||
- do
|
||
- if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
- Result := l_socket.port
|
||
- end
|
||
- end
|
||
-
|
||
- is_blocking: BOOLEAN
|
||
- do
|
||
- Result := socket.is_blocking
|
||
- end
|
||
-
|
||
- is_bound: BOOLEAN
|
||
- do
|
||
- if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
- Result := l_socket.is_bound
|
||
- end
|
||
- end
|
||
-
|
||
- is_created: BOOLEAN
|
||
- do
|
||
- if attached {NETWORK_SOCKET} socket as l_socket then
|
||
- Result := l_socket.is_created
|
||
- end
|
||
- end
|
||
-
|
||
- socket_ok: BOOLEAN
|
||
- do
|
||
- Result := socket.socket_ok
|
||
- end
|
||
-
|
||
- is_open_read: BOOLEAN
|
||
- do
|
||
- Result := socket.is_open_read
|
||
- end
|
||
-
|
||
- is_open_write: BOOLEAN
|
||
- do
|
||
- Result := socket.is_open_write
|
||
- end
|
||
-
|
||
- is_closed: BOOLEAN
|
||
- do
|
||
- Result := socket.is_closed
|
||
- end
|
||
-
|
||
- is_readable: BOOLEAN
|
||
- do
|
||
- Result := socket.is_readable
|
||
- end
|
||
-
|
||
- cleanup
|
||
- do
|
||
- socket.cleanup
|
||
- end
|
||
-
|
||
- ready_for_writing: BOOLEAN
|
||
- do
|
||
- if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
- Result := l_socket.ready_for_writing
|
||
- end
|
||
- end
|
||
-
|
||
- listen (a_queue: INTEGER)
|
||
- do
|
||
- socket.listen (a_queue)
|
||
- end
|
||
-
|
||
- accept
|
||
- do
|
||
- socket.accept
|
||
- end
|
||
-
|
||
- accept_to (other: separate HTTPD_STREAM_SOCKET)
|
||
- -- Accept a new connection on listen socket.
|
||
- -- Socket of accepted connection is available in `other'.
|
||
- do
|
||
- if
|
||
- attached {NETWORK_STREAM_SOCKET} socket as l_socket and then
|
||
- attached {separate NETWORK_STREAM_SOCKET} other.socket as l_other_socket
|
||
- then
|
||
- l_socket.accept_to (l_other_socket)
|
||
- end
|
||
- end
|
||
-
|
||
- set_blocking
|
||
- do
|
||
- socket.set_blocking
|
||
- end
|
||
-
|
||
- set_non_blocking
|
||
- do
|
||
- socket.set_non_blocking
|
||
- end
|
||
-
|
||
- readable: BOOLEAN
|
||
- do
|
||
- Result := socket.readable
|
||
- end
|
||
-
|
||
- ready_for_reading: BOOLEAN
|
||
- do
|
||
- if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
- Result := l_socket.ready_for_reading
|
||
- end
|
||
- end
|
||
-
|
||
- try_ready_for_reading: BOOLEAN
|
||
- do
|
||
- if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||
- Result := l_socket.try_ready_for_reading
|
||
- end
|
||
- end
|
||
-
|
||
- accepted: detachable HTTPD_STREAM_SOCKET
|
||
- do
|
||
- if attached {NETWORK_STREAM_SOCKET} socket.accepted as l_accepted then
|
||
- create Result.make (l_accepted)
|
||
- end
|
||
- end
|
||
-
|
||
-feature {HTTPD_STREAM_SOCKET} -- Implementation
|
||
-
|
||
- make (a_socket: STREAM_SOCKET)
|
||
- do
|
||
- socket := a_socket
|
||
- end
|
||
-
|
||
- socket: STREAM_SOCKET
|
||
-
|
||
- network_stream_socket: detachable NETWORK_STREAM_SOCKET
|
||
- do
|
||
- if attached {NETWORK_STREAM_SOCKET} socket as s then
|
||
- Result := s
|
||
- end
|
||
- end
|
||
-
|
||
-;note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/network/tcp_stream_socket.e b/library/server/ewsgi/connectors/standalone/src/httpd/network/tcp_stream_socket.e
|
||
deleted file mode 100644
|
||
index 2e88f19..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/network/tcp_stream_socket.e
|
||
+++ /dev/null
|
||
@@ -1,94 +0,0 @@
|
||
-note
|
||
- description: "Summary description for {TCP_STREAM_SOCKET}."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- TCP_STREAM_SOCKET
|
||
-
|
||
-inherit
|
||
- NETWORK_STREAM_SOCKET
|
||
- redefine
|
||
- make
|
||
- end
|
||
-
|
||
-create
|
||
- make_server_by_address_and_port,
|
||
- make_server_by_port,
|
||
- make_from_separate,
|
||
- make_empty
|
||
-
|
||
-create {NETWORK_STREAM_SOCKET}
|
||
- make_from_descriptor_and_address
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make
|
||
- -- Create a network stream socket.
|
||
- do
|
||
- Precursor
|
||
- debug
|
||
- set_reuse_address
|
||
- end
|
||
- end
|
||
-
|
||
- make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER)
|
||
- -- Create server socket on `an_address' and `a_port'.
|
||
- require
|
||
- valid_port: a_port >= 0
|
||
- do
|
||
- make
|
||
- create address.make_from_address_and_port (an_address, a_port)
|
||
- bind
|
||
- end
|
||
-
|
||
- make_from_separate (s: separate STREAM_SOCKET)
|
||
- require
|
||
- descriptor_available: s.descriptor_available
|
||
- do
|
||
- create_from_descriptor (s.descriptor)
|
||
- end
|
||
-
|
||
-feature -- Basic operation
|
||
-
|
||
- send_message (a_msg: STRING)
|
||
- do
|
||
- put_string (a_msg)
|
||
- end
|
||
-
|
||
-feature -- Output
|
||
-
|
||
- put_readable_string_8 (s: READABLE_STRING_8)
|
||
- -- Write readable string `s' to socket.
|
||
- local
|
||
- ext: C_STRING
|
||
- do
|
||
- create ext.make (s)
|
||
- put_managed_pointer (ext.managed_data, 0, s.count)
|
||
- end
|
||
-
|
||
-feature -- Status report
|
||
-
|
||
- try_ready_for_reading: BOOLEAN
|
||
- -- Is data available for reading from the socket right now?
|
||
- require
|
||
- socket_exists: exists
|
||
- local
|
||
- retval: INTEGER
|
||
- do
|
||
- retval := c_select_poll_with_timeout (descriptor, True, 0)
|
||
- Result := (retval > 0)
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/no_ssl/httpd_configuration.e b/library/server/ewsgi/connectors/standalone/src/httpd/no_ssl/httpd_configuration.e
|
||
deleted file mode 100644
|
||
index 65ae18c..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/no_ssl/httpd_configuration.e
|
||
+++ /dev/null
|
||
@@ -1,66 +0,0 @@
|
||
-note
|
||
- description: "Standalone server configuration (ssl NOT supported)."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_CONFIGURATION
|
||
-
|
||
-inherit
|
||
- HTTPD_CONFIGURATION_I
|
||
-
|
||
-create
|
||
- make
|
||
-
|
||
-feature -- Status
|
||
-
|
||
- Server_details: STRING_8 = "Server: Standalone Eiffel Server"
|
||
-
|
||
- has_ssl_support: BOOLEAN = False
|
||
- -- Precursor
|
||
-
|
||
-feature -- SSL Helpers
|
||
-
|
||
- set_ssl_protocol_to_ssl_2_or_3
|
||
- -- Set `ssl_protocol' with `Ssl_23'.
|
||
- do
|
||
- -- Ignored
|
||
- end
|
||
-
|
||
-
|
||
- set_ssl_protocol_to_tls_1_0
|
||
- -- Set `ssl_protocol' with `Tls_1_0'.
|
||
- do
|
||
- -- Ignored
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_tls_1_1
|
||
- -- Set `ssl_protocol' with `Tls_1_1'.
|
||
- do
|
||
- -- Ignored
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_tls_1_2
|
||
- -- Set `ssl_protocol' with `Tls_1_2'.
|
||
- do
|
||
- -- Ignored
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_dtls_1_0
|
||
- -- Set `ssl_protocol' with `Dtls_1_0'.
|
||
- do
|
||
- -- Ignored
|
||
- end
|
||
-
|
||
-
|
||
-note
|
||
- copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/no_ssl/httpd_server.e b/library/server/ewsgi/connectors/standalone/src/httpd/no_ssl/httpd_server.e
|
||
deleted file mode 100644
|
||
index 8787e1f..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/no_ssl/httpd_server.e
|
||
+++ /dev/null
|
||
@@ -1,27 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- httpd server
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_SERVER
|
||
-
|
||
-inherit
|
||
- HTTPD_SERVER_I
|
||
-
|
||
-create
|
||
- make
|
||
-
|
||
-note
|
||
- copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/ssl/httpd_configuration.e b/library/server/ewsgi/connectors/standalone/src/httpd/ssl/httpd_configuration.e
|
||
deleted file mode 100644
|
||
index b7eba79..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/ssl/httpd_configuration.e
|
||
+++ /dev/null
|
||
@@ -1,79 +0,0 @@
|
||
-note
|
||
- description: "Standalone server configuration (ssl supported)."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_CONFIGURATION
|
||
-
|
||
-inherit
|
||
- HTTPD_CONFIGURATION_I
|
||
- redefine
|
||
- make
|
||
- end
|
||
-
|
||
-create
|
||
- make
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make
|
||
- -- Create a new instance and set ssl protocol to tls_1_2.
|
||
- do
|
||
- Precursor
|
||
- set_ssl_protocol_to_tls_1_2
|
||
- ensure then
|
||
- ssl_protocol_set: ssl_protocol = {SSL_PROTOCOL}.tls_1_2
|
||
- end
|
||
-
|
||
-feature -- Access
|
||
-
|
||
- Server_details: STRING_8 = "Server: Standalone Eiffel Server (https)"
|
||
-
|
||
- has_ssl_support: BOOLEAN = True
|
||
- -- Precursor
|
||
-
|
||
-feature -- SSL Helpers
|
||
-
|
||
- set_ssl_protocol_to_ssl_2_or_3
|
||
- -- Set `ssl_protocol' with `Ssl_23'.
|
||
- do
|
||
- set_ssl_protocol ({SSL_PROTOCOL}.Ssl_23)
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_tls_1_0
|
||
- -- Set `ssl_protocol' with `Tls_1_0'.
|
||
- do
|
||
- set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_0)
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_tls_1_1
|
||
- -- Set `ssl_protocol' with `Tls_1_1'.
|
||
- do
|
||
- set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_1)
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_tls_1_2
|
||
- -- Set `ssl_protocol' with `Tls_1_2'.
|
||
- do
|
||
- set_ssl_protocol ({SSL_PROTOCOL}.Tls_1_2)
|
||
- end
|
||
-
|
||
- set_ssl_protocol_to_dtls_1_0
|
||
- -- Set `ssl_protocol' with `Dtls_1_0'.
|
||
- do
|
||
- set_ssl_protocol ({SSL_PROTOCOL}.Dtls_1_0)
|
||
- end
|
||
-
|
||
-
|
||
-note
|
||
- copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/ssl/httpd_server.e b/library/server/ewsgi/connectors/standalone/src/httpd/ssl/httpd_server.e
|
||
deleted file mode 100644
|
||
index 4045aa2..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/ssl/httpd_server.e
|
||
+++ /dev/null
|
||
@@ -1,35 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- SSL enabled server
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_SERVER
|
||
-
|
||
-inherit
|
||
- HTTPD_SERVER_I
|
||
- redefine
|
||
- new_listening_socket
|
||
- end
|
||
-
|
||
-create
|
||
- make
|
||
-
|
||
-feature {NONE} -- Factory
|
||
-
|
||
- new_listening_socket (a_addr: detachable INET_ADDRESS; a_http_port: INTEGER): HTTPD_STREAM_SOCKET
|
||
- do
|
||
- if configuration.is_secure then
|
||
- if a_addr /= Void then
|
||
- create {HTTPD_STREAM_SSL_SOCKET} Result.make_ssl_server_by_address_and_port (a_addr, a_http_port, configuration.ssl_protocol, configuration.ca_crt, configuration.ca_key)
|
||
- else
|
||
- create {HTTPD_STREAM_SSL_SOCKET} Result.make_ssl_server_by_port (a_http_port, configuration.ssl_protocol, configuration.ca_crt, configuration.ca_key)
|
||
- end
|
||
- else
|
||
- Result := Precursor (a_addr, a_http_port)
|
||
- end
|
||
- end
|
||
-
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/ssl/httpd_stream_ssl_socket.e b/library/server/ewsgi/connectors/standalone/src/httpd/ssl/httpd_stream_ssl_socket.e
|
||
deleted file mode 100644
|
||
index e1f25ca..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/ssl/httpd_stream_ssl_socket.e
|
||
+++ /dev/null
|
||
@@ -1,139 +0,0 @@
|
||
-note
|
||
- description: "[
|
||
- Summary description for {HTTPD_STREAM_SSL_SOCKET}
|
||
- that can be used for http or https connection.
|
||
- ]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- HTTPD_STREAM_SSL_SOCKET
|
||
-
|
||
-inherit
|
||
- HTTPD_STREAM_SOCKET
|
||
- redefine
|
||
- port,
|
||
- is_bound,
|
||
- ready_for_writing,
|
||
- ready_for_reading,
|
||
- try_ready_for_reading,
|
||
- put_readable_string_8
|
||
- end
|
||
-
|
||
-create
|
||
- make_ssl_server_by_address_and_port, make_ssl_server_by_port,
|
||
- make_server_by_address_and_port, make_server_by_port
|
||
-
|
||
-create {HTTPD_STREAM_SOCKET}
|
||
- make
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make_ssl_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING)
|
||
- local
|
||
- l_socket: SSL_TCP_STREAM_SOCKET
|
||
- do
|
||
- create l_socket.make_server_by_address_and_port (an_address, a_port)
|
||
- l_socket.set_tls_protocol (a_ssl_protocol)
|
||
- socket := l_socket
|
||
- set_certificates (a_crt, a_key)
|
||
- end
|
||
-
|
||
- make_ssl_server_by_port (a_port: INTEGER; a_ssl_protocol: NATURAL; a_crt: STRING; a_key: STRING)
|
||
- local
|
||
- l_socket: SSL_TCP_STREAM_SOCKET
|
||
- do
|
||
- create l_socket.make_server_by_port (a_port)
|
||
- l_socket.set_tls_protocol (a_ssl_protocol)
|
||
- socket := l_socket
|
||
- set_certificates (a_crt, a_key)
|
||
- end
|
||
-
|
||
-feature -- Output
|
||
-
|
||
- put_readable_string_8 (s: READABLE_STRING_8)
|
||
- -- <Precursor>
|
||
- do
|
||
- if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
- l_ssl_socket.put_readable_string_8 (s)
|
||
- else
|
||
- Precursor (s)
|
||
- end
|
||
- end
|
||
-
|
||
-feature -- Status Report
|
||
-
|
||
- port: INTEGER
|
||
- -- <Precursor>
|
||
- do
|
||
- if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
- Result := l_ssl_socket.port
|
||
- else
|
||
- Result := Precursor
|
||
- end
|
||
- end
|
||
-
|
||
- is_bound: BOOLEAN
|
||
- -- <Precursor>
|
||
- do
|
||
- if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
- Result := l_ssl_socket.is_bound
|
||
- else
|
||
- Result := Precursor
|
||
- end
|
||
- end
|
||
-
|
||
- ready_for_writing: BOOLEAN
|
||
- -- <Precursor>
|
||
- do
|
||
- if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
- Result := l_ssl_socket.ready_for_writing
|
||
- else
|
||
- Result := Precursor
|
||
- end
|
||
- end
|
||
-
|
||
- ready_for_reading: BOOLEAN
|
||
- -- <Precursor>
|
||
- do
|
||
- if attached {SSL_TCP_STREAM_SOCKET} socket as l_ssl_socket then
|
||
- Result := l_ssl_socket.ready_for_reading
|
||
- else
|
||
- Result := Precursor
|
||
- end
|
||
- end
|
||
-
|
||
- try_ready_for_reading: BOOLEAN
|
||
- do
|
||
- if attached {SSL_TCP_STREAM_SOCKET} socket as l_socket then
|
||
- Result := l_socket.try_ready_for_reading
|
||
- else
|
||
- Result := Precursor
|
||
- end
|
||
- end
|
||
-
|
||
-feature {HTTPD_STREAM_SOCKET} -- Implementation
|
||
-
|
||
- set_certificates (a_crt: STRING; a_key: STRING)
|
||
- local
|
||
- a_file_name: FILE_NAME
|
||
- do
|
||
- if attached {SSL_NETWORK_STREAM_SOCKET} socket as l_socket then
|
||
- create a_file_name.make_from_string (a_crt)
|
||
- l_socket.set_certificate_file_name (a_file_name)
|
||
- create a_file_name.make_from_string (a_key)
|
||
- l_socket.set_key_file_name (a_file_name)
|
||
- end
|
||
- end
|
||
-
|
||
-note
|
||
- copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
- source: "[
|
||
- Eiffel Software
|
||
- 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
- Telephone 805-685-1006, Fax 805-685-6869
|
||
- Website http://www.eiffel.com
|
||
- Customer support http://support.eiffel.com
|
||
- ]"
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/ssl/ssl_tcp_stream_socket.e b/library/server/ewsgi/connectors/standalone/src/httpd/ssl/ssl_tcp_stream_socket.e
|
||
deleted file mode 100644
|
||
index f1043ad..0000000
|
||
--- a/library/server/ewsgi/connectors/standalone/src/httpd/ssl/ssl_tcp_stream_socket.e
|
||
+++ /dev/null
|
||
@@ -1,72 +0,0 @@
|
||
-note
|
||
- description: "SSL tcp stream socket."
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
-
|
||
-class
|
||
- SSL_TCP_STREAM_SOCKET
|
||
-
|
||
-inherit
|
||
-
|
||
- SSL_NETWORK_STREAM_SOCKET
|
||
-
|
||
-create
|
||
- make_server_by_address_and_port, make_server_by_port,
|
||
- make_empty
|
||
-
|
||
-create {SSL_NETWORK_STREAM_SOCKET}
|
||
- make_from_descriptor_and_address
|
||
-
|
||
-feature {NONE} -- Initialization
|
||
-
|
||
- make_server_by_address_and_port (an_address: INET_ADDRESS; a_port: INTEGER)
|
||
- -- Create server socket on `an_address' and `a_port'.
|
||
- require
|
||
- valid_port: a_port >= 0
|
||
- do
|
||
- make
|
||
- create address.make_from_address_and_port (an_address, a_port)
|
||
- bind
|
||
- end
|
||
-
|
||
-feature -- Basic operation
|
||
-
|
||
- send_message (a_msg: STRING)
|
||
- do
|
||
- put_string (a_msg)
|
||
- end
|
||
-
|
||
-feature -- Output
|
||
-
|
||
- put_readable_string_8 (s: READABLE_STRING_8)
|
||
- -- Write readable string `s' to socket.
|
||
- local
|
||
- ext: C_STRING
|
||
- do
|
||
- create ext.make (s)
|
||
- put_managed_pointer (ext.managed_data, 0, s.count)
|
||
- end
|
||
-
|
||
-feature -- Status report
|
||
-
|
||
- try_ready_for_reading: BOOLEAN
|
||
- -- Is data available for reading from the socket right now?
|
||
- require
|
||
- socket_exists: exists
|
||
- local
|
||
- retval: INTEGER
|
||
- do
|
||
- retval := c_select_poll_with_timeout (descriptor, True, 0)
|
||
- Result := (retval > 0)
|
||
- end
|
||
-
|
||
-feature {NONE}-- Implementation
|
||
-
|
||
-
|
||
-
|
||
-
|
||
-note
|
||
- copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
||
- license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
-
|
||
-end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler.e b/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler.e
|
||
index 3abd522..4ff9ac8 100644
|
||
--- a/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler.e
|
||
+++ b/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler.e
|
||
@@ -24,13 +24,10 @@ create
|
||
|
||
feature {NONE} -- Initialization
|
||
|
||
- make_with_connector (conn: like connector)
|
||
+ make_with_connector (a_request_settings: HTTPD_REQUEST_SETTINGS; conn: like connector)
|
||
do
|
||
- make
|
||
+ make (a_request_settings)
|
||
connector := conn
|
||
--- if conn /= Void then
|
||
--- set_is_verbose (is_connector_verbose (conn))
|
||
--- end
|
||
end
|
||
|
||
feature -- Access
|
||
@@ -56,11 +53,6 @@ feature -- SCOOP helpers
|
||
Result := conn.base
|
||
end
|
||
|
||
- is_connector_verbose (conn: separate WGI_STANDALONE_CONNECTOR [G]): BOOLEAN
|
||
- do
|
||
- Result := conn.is_verbose
|
||
- end
|
||
-
|
||
feature -- Request processing
|
||
|
||
process_request (a_socket: HTTPD_STREAM_SOCKET)
|
||
@@ -87,7 +79,7 @@ feature -- Request processing
|
||
else
|
||
l_output.set_http_version (version)
|
||
end
|
||
- res.set_is_persistent_connection_supported ({HTTPD_SERVER}.is_persistent_connection_supported)
|
||
+ res.set_is_persistent_connection_supported (is_persistent_connection_supported and is_next_persistent_connection_supported)
|
||
res.set_is_persistent_connection_requested (is_persistent_connection_requested)
|
||
|
||
req.set_meta_string_variable ("RAW_HEADER_DATA", request_header)
|
||
@@ -238,6 +230,9 @@ feature -- Request processing
|
||
set_environment_variable (l_server_port, "SERVER_PORT", Result)
|
||
set_environment_variable (version, "SERVER_PROTOCOL", Result)
|
||
set_environment_variable ({HTTPD_CONFIGURATION}.Server_details, "SERVER_SOFTWARE", Result)
|
||
+ if is_secure then
|
||
+ set_environment_variable ("on", "HTTPS", Result)
|
||
+ end
|
||
|
||
--| Apply `base' value
|
||
l_base := base
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler_factory.e b/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler_factory.e
|
||
index 48ea569..5dc7c4b 100644
|
||
--- a/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler_factory.e
|
||
+++ b/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler_factory.e
|
||
@@ -1,5 +1,5 @@
|
||
note
|
||
- description: "Implementation of WGI request handler factory for WGI_STANDALOE_CONNECTOR."
|
||
+ description: "Implementation of WGI request handler factory for WGI_STANDALONE_CONNECTOR."
|
||
date: "$Date$"
|
||
revision: "$Revision$"
|
||
|
||
@@ -14,19 +14,23 @@ feature -- Access
|
||
connector: detachable separate WGI_STANDALONE_CONNECTOR [G]
|
||
-- httpd solution.
|
||
|
||
+ request_settings: HTTPD_REQUEST_SETTINGS
|
||
+ -- Settings specific to request handling.
|
||
+
|
||
feature -- Element change
|
||
|
||
- set_connector (conn: like connector)
|
||
+ update_with (conn: like connector; a_conf: separate HTTPD_CONFIGURATION)
|
||
-- Set `connector' with `conn'.
|
||
do
|
||
connector := conn
|
||
+ request_settings := a_conf.request_settings
|
||
end
|
||
|
||
feature -- Factory
|
||
|
||
new_handler: separate WGI_HTTPD_REQUEST_HANDLER [G]
|
||
do
|
||
- create Result.make_with_connector (connector)
|
||
+ create Result.make_with_connector (request_settings, connector)
|
||
end
|
||
|
||
note
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector.e
|
||
index 9c35e71..0d227df 100644
|
||
--- a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector.e
|
||
+++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector.e
|
||
@@ -1,9 +1,9 @@
|
||
note
|
||
description: "[
|
||
- Standalone Web Server connector
|
||
+ Standalone Web Server connector.
|
||
]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
+ date: "$Date: 2016-08-06 13:34:52 +0200 (sam., 06 août 2016) $"
|
||
+ revision: "$Revision: 99106 $"
|
||
|
||
class
|
||
WGI_STANDALONE_CONNECTOR [G -> WGI_EXECUTION create make end]
|
||
@@ -20,18 +20,18 @@ feature {NONE} -- Initialization
|
||
make
|
||
-- Create current standalone connector.
|
||
local
|
||
- fac: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G]
|
||
+ fac: like request_handler_factory
|
||
do
|
||
-- Callbacks
|
||
create on_launched_actions
|
||
|
||
-- Server
|
||
- create fac
|
||
+ create <NONE> fac
|
||
+ request_handler_factory := fac
|
||
create server.make (fac)
|
||
- create observer
|
||
+ create <NONE> observer
|
||
configuration := server_configuration (server)
|
||
controller := server_controller (server)
|
||
- set_factory_connector (Current, fac)
|
||
initialize_server (server)
|
||
end
|
||
|
||
@@ -51,9 +51,9 @@ feature {NONE} -- Separate helper
|
||
a_server.set_observer (observer)
|
||
end
|
||
|
||
- set_factory_connector (conn: detachable separate WGI_STANDALONE_CONNECTOR [G]; fac: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G])
|
||
+ update_factory (conn: detachable separate WGI_STANDALONE_CONNECTOR [G]; fac: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G]; a_conf: like configuration)
|
||
do
|
||
- fac.set_connector (conn)
|
||
+ fac.update_with (conn, a_conf)
|
||
end
|
||
|
||
server_configuration (a_server: like server): like configuration
|
||
@@ -63,17 +63,26 @@ feature {NONE} -- Separate helper
|
||
|
||
feature -- Access
|
||
|
||
- name: STRING_8 = "httpd"
|
||
+ name: STRING_8
|
||
-- Name of Current connector
|
||
+ once
|
||
+ Result := "httpd"
|
||
+ end
|
||
|
||
- version: STRING_8 = "0.1"
|
||
+ version: STRING_8
|
||
-- Version of Current connector
|
||
+ once
|
||
+ Result := "1.0"
|
||
+ end
|
||
|
||
feature -- Access
|
||
|
||
server: separate HTTPD_SERVER
|
||
-- HTTPd server object.
|
||
|
||
+ request_handler_factory: separate WGI_HTTPD_REQUEST_HANDLER_FACTORY [G]
|
||
+ -- Factory for request handlers.
|
||
+
|
||
controller: separate HTTPD_CONTROLLER
|
||
-- Controller used to shutdown server.
|
||
|
||
@@ -97,9 +106,6 @@ feature -- Status report
|
||
-- Listening port.
|
||
--| 0: not launched
|
||
|
||
- is_verbose: BOOLEAN
|
||
- -- Is verbose?
|
||
-
|
||
feature -- Callbacks
|
||
|
||
on_launched_actions: ACTION_SEQUENCE [TUPLE [WGI_STANDALONE_CONNECTOR [WGI_EXECUTION]]]
|
||
@@ -149,6 +155,12 @@ feature -- Element change
|
||
set_is_verbose_on_configuration (b, configuration)
|
||
end
|
||
|
||
+ set_is_secure (b: BOOLEAN)
|
||
+ -- Set is_secure connection mode.
|
||
+ -- i.e: using SSL.
|
||
+ do
|
||
+ set_is_secure_on_configuration (b, configuration)
|
||
+ end
|
||
|
||
feature -- Server
|
||
|
||
@@ -190,18 +202,22 @@ feature {NONE} -- Implementation
|
||
Result := a_server.controller
|
||
end
|
||
|
||
- configure_server (a_configuration: like configuration)
|
||
+ apply_configuration (a_configuration: like configuration)
|
||
+ local
|
||
+ v: BOOLEAN
|
||
do
|
||
- if a_configuration.is_verbose then
|
||
- if attached base as l_base then
|
||
+ v := a_configuration.is_verbose
|
||
+ if v then
|
||
+ if attached base as l_base and then not l_base.is_whitespace then
|
||
io.error.put_string ("Base=" + l_base + "%N")
|
||
end
|
||
end
|
||
+ update_factory (Current, request_handler_factory, a_configuration)
|
||
end
|
||
|
||
launch_server (a_server: like server)
|
||
do
|
||
- configure_server (a_server.configuration)
|
||
+ apply_configuration (a_server.configuration)
|
||
a_server.launch
|
||
end
|
||
|
||
@@ -229,13 +245,17 @@ feature {NONE} -- Implementation: element change
|
||
|
||
set_is_verbose_on_configuration (b: BOOLEAN; cfg: like configuration)
|
||
do
|
||
- is_verbose := b
|
||
cfg.set_is_verbose (b)
|
||
end
|
||
|
||
+ set_is_secure_on_configuration (b: BOOLEAN; cfg: like configuration)
|
||
+ do
|
||
+ cfg.set_is_secure (b)
|
||
+ end
|
||
+
|
||
|
||
note
|
||
- copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
source: "[
|
||
Eiffel Software
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector_exporter.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector_exporter.e
|
||
new file mode 100644
|
||
index 0000000..bbfa7ca
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_connector_exporter.e
|
||
@@ -0,0 +1,11 @@
|
||
+note
|
||
+ description: "[
|
||
+ Interface to access protected feature from {WGI_STANDALONE_CONNECTOR}.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ WGI_STANDALONE_CONNECTOR_EXPORTER
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_constants.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_constants.e
|
||
new file mode 100644
|
||
index 0000000..dd5914c
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_constants.e
|
||
@@ -0,0 +1,18 @@
|
||
+note
|
||
+ description: "[
|
||
+ Constants value related to Standalone connector,
|
||
+ and indirectly to `httpd' component.
|
||
+ ]"
|
||
+ author: "$Author$"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ WGI_STANDALONE_CONSTANTS
|
||
+
|
||
+inherit
|
||
+ ANY
|
||
+
|
||
+ HTTPD_CONSTANTS
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_httpd_logger_constants.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_httpd_logger_constants.e
|
||
new file mode 100644
|
||
index 0000000..c0e7502
|
||
--- /dev/null
|
||
+++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_httpd_logger_constants.e
|
||
@@ -0,0 +1,10 @@
|
||
+note
|
||
+ description: "Export HTTPD_LOGGER_CONSTANTS to Standlone connector interfaces."
|
||
+
|
||
+class
|
||
+ WGI_STANDALONE_HTTPD_LOGGER_CONSTANTS
|
||
+
|
||
+inherit
|
||
+ HTTPD_LOGGER_CONSTANTS
|
||
+
|
||
+end
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_input_stream.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_input_stream.e
|
||
index 2013ad6..1cf333a 100644
|
||
--- a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_input_stream.e
|
||
+++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_input_stream.e
|
||
@@ -24,7 +24,7 @@ feature {NONE} -- Initialization
|
||
set_source (a_source)
|
||
end
|
||
|
||
-feature {WGI_STANDALONE_CONNECTOR, WGI_SERVICE} -- Nino
|
||
+feature {WGI_STANDALONE_CONNECTOR, WGI_SERVICE, WGI_STANDALONE_CONNECTOR_EXPORTER} -- Standalone
|
||
|
||
set_source (i: like source)
|
||
do
|
||
diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_response_stream.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_response_stream.e
|
||
index ad19d07..40a65e5 100644
|
||
--- a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_response_stream.e
|
||
+++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_response_stream.e
|
||
@@ -108,7 +108,6 @@ feature -- Header output operation
|
||
l_connection := s.substring (i + 12, j - 1)
|
||
l_connection.adjust
|
||
if
|
||
- not is_http_version_1_0 and
|
||
not l_connection.is_case_insensitive_equal_general ("close")
|
||
then
|
||
s.replace_substring ("Connection: close", i + 1, j - 1)
|
||
diff --git a/library/server/ewsgi/connectors/standalone/standalone-safe.ecf b/library/server/ewsgi/connectors/standalone/standalone-safe.ecf
|
||
index cbb239d..84514c6 100644
|
||
--- a/library/server/ewsgi/connectors/standalone/standalone-safe.ecf
|
||
+++ b/library/server/ewsgi/connectors/standalone/standalone-safe.ecf
|
||
@@ -16,7 +16,7 @@
|
||
<library name="encoder" location="..\..\..\..\text\encoder\encoder-safe.ecf"/>
|
||
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
|
||
<library name="http" location="..\..\..\..\network\protocol\http\http-safe.ecf"/>
|
||
- <library name="httpd" location="src\httpd\httpd-safe.ecf" readonly="false"/>
|
||
+ <library name="httpd" location="lib\httpd\httpd-safe.ecf" readonly="false"/>
|
||
<cluster name="src" location=".\src\">
|
||
<cluster name="implementation" location="$|implementation\" hidden="true"/>
|
||
</cluster>
|
||
diff --git a/library/server/ewsgi/connectors/standalone/standalone.ecf b/library/server/ewsgi/connectors/standalone/standalone.ecf
|
||
index 4fd3418..89c4189 100644
|
||
--- a/library/server/ewsgi/connectors/standalone/standalone.ecf
|
||
+++ b/library/server/ewsgi/connectors/standalone/standalone.ecf
|
||
@@ -15,7 +15,7 @@
|
||
<library name="encoder" location="..\..\..\..\text\encoder\encoder.ecf"/>
|
||
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
|
||
<library name="http" location="..\..\..\..\network\protocol\http\http.ecf"/>
|
||
- <library name="httpd" location="src\httpd\httpd.ecf" readonly="false"/>
|
||
+ <library name="httpd" location="lib\httpd\httpd.ecf" readonly="false"/>
|
||
<cluster name="src" location=".\src\">
|
||
<cluster name="implementation" location="$|implementation\" hidden="true"/>
|
||
</cluster>
|
||
diff --git a/library/server/ewsgi/connectors/standalone/test_standalone-safe.ecf b/library/server/ewsgi/connectors/standalone/test_standalone-safe.ecf
|
||
index 361d9d8..c0e72fa 100644
|
||
--- a/library/server/ewsgi/connectors/standalone/test_standalone-safe.ecf
|
||
+++ b/library/server/ewsgi/connectors/standalone/test_standalone-safe.ecf
|
||
@@ -10,12 +10,11 @@
|
||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
<library name="connector_standalone" location="standalone-safe.ecf" readonly="false"/>
|
||
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
|
||
- <library name="httpd_edit" location="src\httpd\httpd-safe.ecf" readonly="false">
|
||
+ <library name="httpd_edit" location="lib\httpd\httpd-safe.ecf" readonly="false">
|
||
<option debug="true">
|
||
<debug name="dbglog" enabled="true"/>
|
||
</option>
|
||
</library>
|
||
- <library name="net_ssl_edit" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf" readonly="false"/>
|
||
<library name="wsf" location="..\..\..\wsf\wsf-safe.ecf" readonly="false"/>
|
||
<cluster name="tests" location="tests\" recursive="true"/>
|
||
</target>
|
||
@@ -27,4 +26,10 @@
|
||
</target>
|
||
<target name="test_connector_standalone" extends="test_standalone_scoop">
|
||
</target>
|
||
+ <target name="test_standalone_scoop_ssl" extends="test_standalone_scoop">
|
||
+ <variable name="httpd_ssl_enabled" value="true"/>
|
||
+ <variable name="libcurl_http_client_disabled" value="true"/>
|
||
+ <variable name="net_http_client_disabled" value="false"/>
|
||
+ <variable name="netssl_http_client_enabled" value="true"/>
|
||
+ </target>
|
||
</system>
|
||
diff --git a/library/server/libfcgi/libfcgi-safe.ecf b/library/server/libfcgi/libfcgi-safe.ecf
|
||
index 0ae6aed..6958b48 100644
|
||
--- a/library/server/libfcgi/libfcgi-safe.ecf
|
||
+++ b/library/server/libfcgi/libfcgi-safe.ecf
|
||
@@ -24,11 +24,11 @@
|
||
<platform value="windows"/>
|
||
</condition>
|
||
</external_library>
|
||
- <external_library location="/usr/lib/libfcgi.so">
|
||
+ <external_linker_flag value="-lfcgi">
|
||
<condition>
|
||
<platform value="unix"/>
|
||
</condition>
|
||
- </external_library>
|
||
+ </external_linker_flag>
|
||
<external_library location="/opt/local/lib/libfcgi.dylib">
|
||
<condition>
|
||
<platform value="macintosh"/>
|
||
diff --git a/library/server/libfcgi/libfcgi.ecf b/library/server/libfcgi/libfcgi.ecf
|
||
index 1b0dbdc..9216b61 100644
|
||
--- a/library/server/libfcgi/libfcgi.ecf
|
||
+++ b/library/server/libfcgi/libfcgi.ecf
|
||
@@ -25,11 +25,11 @@
|
||
<platform value="windows"/>
|
||
</condition>
|
||
</external_library>
|
||
- <external_library location="/usr/lib/libfcgi.so">
|
||
+ <external_linker_flag value="-lfcgi">
|
||
<condition>
|
||
<platform value="unix"/>
|
||
</condition>
|
||
- </external_library>
|
||
+ </external_linker_flag>
|
||
<external_library location="/opt/local/lib/libfcgi.dylib">
|
||
<condition>
|
||
<platform value="macintosh"/>
|
||
diff --git a/library/server/wsf/connector/standalone/wsf_standalone_service_launcher.e b/library/server/wsf/connector/standalone/wsf_standalone_service_launcher.e
|
||
index 006a23e..e416960 100644
|
||
--- a/library/server/wsf/connector/standalone/wsf_standalone_service_launcher.e
|
||
+++ b/library/server/wsf/connector/standalone/wsf_standalone_service_launcher.e
|
||
@@ -2,14 +2,27 @@ note
|
||
description: "[
|
||
Component to launch the service using the default connector
|
||
|
||
- Eiffel Web httpd for this class
|
||
+ EiffelWeb httpd for this class
|
||
|
||
+ The httpd default connector support options:
|
||
+ verbose: to display verbose output
|
||
+ port: numeric such as 8099 (or equivalent string as "8099")
|
||
+ base: base_url (very specific to standalone server)
|
||
+
|
||
+ max_concurrent_connections: set one, for single threaded behavior
|
||
+ max_tcp_clients: max number of open tcp connection
|
||
+
|
||
+ socket_timeout: connection timeout
|
||
+ socket_recv_timeout: read data timeout
|
||
+
|
||
+ keep_alive_timeout: amount of time the server will wait for subsequent
|
||
+ requests on a persistent connection,
|
||
+ max_keep_alive_requests: number of requests allowed on a persistent connection,
|
||
+
|
||
+ ssl_enabled: set to True for https support.
|
||
+ ssl_ca_crt: path to the certificat crt file (relevant when ssl_enabled is True)
|
||
+ ssl_ca_key: path to the certificat key file (relevant when ssl_enabled is True)
|
||
|
||
- The httpd default connector support options:
|
||
- port: numeric such as 8099 (or equivalent string as "8099")
|
||
- base: base_url (very specific to standalone server)
|
||
- verbose: to display verbose output, useful for standalone connector
|
||
- force_single_threaded: use only one thread, useful for standalone connector
|
||
|
||
check WSF_SERVICE_LAUNCHER for more documentation
|
||
]"
|
||
@@ -25,6 +38,8 @@ inherit
|
||
launchable
|
||
end
|
||
|
||
+ WGI_STANDALONE_HTTPD_LOGGER_CONSTANTS
|
||
+
|
||
create
|
||
make,
|
||
make_and_launch
|
||
@@ -34,37 +49,77 @@ feature {NONE} -- Initialization
|
||
initialize
|
||
local
|
||
conn: like connector
|
||
+ s: READABLE_STRING_GENERAL
|
||
do
|
||
create on_launched_actions
|
||
create on_stopped_actions
|
||
|
||
- port_number := 80 --| Default, but quite often, this port is already used ...
|
||
+ 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.
|
||
+ max_keep_alive_requests := {WGI_STANDALONE_CONSTANTS}.default_max_keep_alive_requests
|
||
+ verbose := False
|
||
+ verbose_level := notice_level
|
||
+
|
||
base_url := ""
|
||
|
||
if attached options as opts then
|
||
if attached {READABLE_STRING_GENERAL} opts.option ("server_name") as l_server_name then
|
||
server_name := l_server_name.to_string_8
|
||
end
|
||
- if attached {INTEGER} opts.option ("port") as l_port then
|
||
- port_number := l_port
|
||
- elseif
|
||
- attached {READABLE_STRING_GENERAL} opts.option ("port") as l_port_str and then
|
||
- l_port_str.is_integer
|
||
- then
|
||
- port_number := l_port_str.as_string_8.to_integer
|
||
- end
|
||
if attached {READABLE_STRING_GENERAL} opts.option ("base") as l_base_str then
|
||
base_url := l_base_str.as_string_8
|
||
end
|
||
- if attached {BOOLEAN} opts.option ("force_single_threaded") as l_single_threaded then
|
||
- single_threaded := l_single_threaded
|
||
- elseif attached {READABLE_STRING_GENERAL} opts.option ("force_single_threaded") as l_single_threaded_str then
|
||
- single_threaded := l_single_threaded_str.as_lower.same_string ("true")
|
||
+
|
||
+ verbose := opts.option_boolean_value ("verbose", verbose)
|
||
+ -- See `{HTTPD_REQUEST_HANDLER_I}.*_verbose_level`
|
||
+
|
||
+ if opts.has_integer_option ("verbose_level") then
|
||
+ verbose_level := opts.option_integer_value ("verbose_level", verbose_level)
|
||
+ elseif attached {READABLE_STRING_GENERAL} opts.option ("verbose_level") as s_verbose_level then
|
||
+ verbose_level := 0 -- Reset
|
||
+ across
|
||
+ s_verbose_level.split ('+') as ic
|
||
+ loop
|
||
+ s := ic.item
|
||
+ if s.is_case_insensitive_equal ("alert") then
|
||
+ verbose_level := verbose_level | alert_level
|
||
+ elseif s.is_case_insensitive_equal ("critical") then
|
||
+ verbose_level := verbose_level | critical_level
|
||
+ elseif s.is_case_insensitive_equal ("error") then
|
||
+ verbose_level := verbose_level | error_level
|
||
+ elseif s.is_case_insensitive_equal ("warning") then
|
||
+ verbose_level := verbose_level | warning_level
|
||
+ elseif s.is_case_insensitive_equal ("notice") then
|
||
+ verbose_level := verbose_level | notice_level
|
||
+ elseif s.is_case_insensitive_equal ("information") then
|
||
+ verbose_level := verbose_level | information_level
|
||
+ elseif s.is_case_insensitive_equal ("debug") then
|
||
+ verbose_level := verbose_level | debug_level
|
||
+ else
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ port_number := opts.option_integer_value ("port", port_number)
|
||
+
|
||
+ if opts.option_boolean_value ("force_single_threaded", False) then
|
||
+ force_single_threaded
|
||
end
|
||
- if attached {BOOLEAN} opts.option ("verbose") as l_verbose then
|
||
- verbose := l_verbose
|
||
- elseif attached {READABLE_STRING_GENERAL} opts.option ("verbose") as l_verbose_str then
|
||
- verbose := l_verbose_str.as_lower.same_string ("true")
|
||
+ 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)
|
||
+ max_keep_alive_requests := opts.option_integer_value ("max_keep_alive_requests", max_keep_alive_requests)
|
||
+
|
||
+ if
|
||
+ opts.option_boolean_value ("ssl_enabled", ssl_enabled) and then
|
||
+ attached opts.option_string_32_value ("ssl_protocol", "tls_1_2") as ssl_prot
|
||
+ then
|
||
+ ssl_settings := [ssl_prot, opts.option_string_32_value ("ssl_ca_crt", Void), opts.option_string_32_value ("ssl_ca_key", Void)]
|
||
end
|
||
end
|
||
|
||
@@ -76,18 +131,27 @@ feature {NONE} -- Initialization
|
||
update_configuration (conn.configuration)
|
||
end
|
||
|
||
+ force_single_threaded
|
||
+ -- Set `single_threaded' to True.
|
||
+ do
|
||
+ max_concurrent_connections := 1
|
||
+ end
|
||
+
|
||
feature -- Execution
|
||
|
||
update_configuration (cfg: like connector.configuration)
|
||
do
|
||
- if single_threaded then
|
||
- cfg.set_force_single_threaded (True)
|
||
- end
|
||
cfg.set_is_verbose (verbose)
|
||
- if attached server_name as l_server_name then
|
||
- cfg.set_http_server_name (l_server_name)
|
||
- end
|
||
+ cfg.set_verbose_level (verbose_level)
|
||
+ cfg.set_ssl_settings (ssl_settings)
|
||
+ cfg.set_http_server_name (server_name)
|
||
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_max_keep_alive_requests (max_keep_alive_requests)
|
||
end
|
||
|
||
launch
|
||
@@ -98,14 +162,20 @@ feature -- Execution
|
||
do
|
||
conn := connector
|
||
conn.set_base (base_url)
|
||
- debug ("nino")
|
||
+ debug ("ew_standalone")
|
||
if verbose then
|
||
io.error.put_string ("Launching standalone web server on port " + port_number.out)
|
||
+ if ssl_enabled then
|
||
+ io.error.put_string ("%N https://")
|
||
+ else
|
||
+ io.error.put_string ("%N http://")
|
||
+ end
|
||
if attached server_name as l_name then
|
||
- io.error.put_string ("%N http://" + l_name + ":" + port_number.out + "/" + base_url + "%N")
|
||
+ io.error.put_string (l_name)
|
||
else
|
||
- io.error.put_string ("%N http://localhost:" + port_number.out + "/" + base_url + "%N")
|
||
+ io.error.put_string ("localhost")
|
||
end
|
||
+ io.error.put_string (":" + port_number.out + "/" + base_url + "%N")
|
||
end
|
||
end
|
||
update_configuration (conn.configuration)
|
||
@@ -114,15 +184,15 @@ feature -- Execution
|
||
|
||
feature -- Callback
|
||
|
||
- on_launched_actions: ACTION_SEQUENCE [TUPLE [WGI_STANDALONE_CONNECTOR [G]]]
|
||
+ on_launched_actions: ACTION_SEQUENCE [TUPLE [like connector]]
|
||
-- Actions triggered when launched
|
||
|
||
- on_stopped_actions: ACTION_SEQUENCE [TUPLE [WGI_STANDALONE_CONNECTOR [G]]]
|
||
+ on_stopped_actions: ACTION_SEQUENCE [TUPLE [like connector]]
|
||
-- Actions triggered when stopped
|
||
|
||
feature {NONE} -- Implementation
|
||
|
||
- on_launched (conn: WGI_STANDALONE_CONNECTOR [G])
|
||
+ on_launched (conn: like connector)
|
||
do
|
||
on_launched_actions.call ([conn])
|
||
end
|
||
@@ -134,8 +204,29 @@ feature {NONE} -- Implementation
|
||
base_url: READABLE_STRING_8
|
||
|
||
verbose: BOOLEAN
|
||
+ verbose_level: INTEGER
|
||
+ -- Help defining the verbosity.
|
||
+ -- The higher, the more output.
|
||
+
|
||
+ ssl_settings: detachable TUPLE [protocol: READABLE_STRING_GENERAL; ca_crt, ca_key: detachable READABLE_STRING_GENERAL]
|
||
+
|
||
+ ssl_enabled: BOOLEAN
|
||
+ -- Is secure server? i.e using SSL?
|
||
+ do
|
||
+ Result := attached ssl_settings as ssl and then attached ssl.protocol as prot and then not prot.is_whitespace
|
||
+ end
|
||
+
|
||
+ max_concurrent_connections: INTEGER
|
||
+ max_tcp_clients: INTEGER
|
||
+ socket_timeout: INTEGER
|
||
+ socket_recv_timeout: INTEGER
|
||
+ keep_alive_timeout: INTEGER
|
||
+ max_keep_alive_requests: INTEGER
|
||
|
||
single_threaded: BOOLEAN
|
||
+ do
|
||
+ Result := max_concurrent_connections = 0
|
||
+ end
|
||
|
||
feature -- Status report
|
||
|
||
diff --git a/library/server/wsf/connector/standalone_websocket-safe.ecf b/library/server/wsf/connector/standalone_websocket-safe.ecf
|
||
new file mode 100644
|
||
index 0000000..01484e5
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket-safe.ecf
|
||
@@ -0,0 +1,25 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="wsf_standalone_websocket" uuid="7C83D4B4-39C9-4D27-941B-0F0AAD45122E" library_target="wsf_standalone_websocket">
|
||
+ <target name="wsf_standalone_websocket">
|
||
+ <root all_classes="true"/>
|
||
+ <file_rule>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
+ <exclude>/\.git$</exclude>
|
||
+ <exclude>/\.svn$</exclude>
|
||
+ </file_rule>
|
||
+ <option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||
+ </option>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
+ <library name="connector_standalone" location="..\..\ewsgi\connectors\standalone\standalone-safe.ecf"/>
|
||
+ <library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
|
||
+ <library name="encoder" location="..\..\..\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||
+ <library name="error" location="..\..\..\utility\general\error\error-safe.ecf"/>
|
||
+ <library name="ewsgi" location="..\..\ewsgi\ewsgi-safe.ecf"/>
|
||
+ <library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
|
||
+ <library name="httpd" location="..\..\ewsgi\connectors\standalone\lib\httpd\httpd-safe.ecf"/>
|
||
+ <library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||
+ <library name="wsf" location="..\wsf-safe.ecf"/>
|
||
+ <library name="wsf_standalone" location="standalone-safe.ecf"/>
|
||
+ <cluster name="wsf_standalone_websocket" location=".\standalone_websocket\" recursive="true"/>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/library/server/wsf/connector/standalone_websocket.ecf b/library/server/wsf/connector/standalone_websocket.ecf
|
||
new file mode 100644
|
||
index 0000000..0783e77
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket.ecf
|
||
@@ -0,0 +1,25 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="wsf_standalone_websocket" uuid="7C83D4B4-39C9-4D27-941B-0F0AAD45122E" library_target="wsf_standalone_websocket">
|
||
+ <target name="wsf_standalone_websocket">
|
||
+ <root all_classes="true"/>
|
||
+ <file_rule>
|
||
+ <exclude>/EIFGENs$</exclude>
|
||
+ <exclude>/\.git$</exclude>
|
||
+ <exclude>/\.svn$</exclude>
|
||
+ </file_rule>
|
||
+ <option warning="true" full_class_checking="true" is_obsolete_routine_type="true" void_safety="none" syntax="provisional">
|
||
+ </option>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||
+ <library name="connector_standalone" location="..\..\ewsgi\connectors\standalone\standalone.ecf"/>
|
||
+ <library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/>
|
||
+ <library name="encoder" location="..\..\..\text\encoder\encoder.ecf" readonly="false"/>
|
||
+ <library name="error" location="..\..\..\utility\general\error\error.ecf"/>
|
||
+ <library name="ewsgi" location="..\..\ewsgi\ewsgi.ecf"/>
|
||
+ <library name="http" location="..\..\..\network\protocol\http\http.ecf"/>
|
||
+ <library name="httpd" location="..\..\ewsgi\connectors\standalone\lib\httpd\httpd.ecf"/>
|
||
+ <library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||
+ <library name="wsf" location="..\wsf.ecf"/>
|
||
+ <library name="wsf_standalone" location="standalone.ecf"/>
|
||
+ <cluster name="wsf_standalone_websocket" location=".\standalone_websocket\" recursive="true"/>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/websocket/event/web_socket_event_i.e b/library/server/wsf/connector/standalone_websocket/websocket/event/web_socket_event_i.e
|
||
new file mode 100644
|
||
index 0000000..9ad0fec
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/websocket/event/web_socket_event_i.e
|
||
@@ -0,0 +1,137 @@
|
||
+note
|
||
+ description: "[
|
||
+ Websocket callback events for actions like opening and closing the connection,
|
||
+ sending and receiving messages, and listening.
|
||
+
|
||
+ Define the websocket events:
|
||
+ - on_open
|
||
+ - on_binary
|
||
+ - on_text
|
||
+ - on_close
|
||
+
|
||
+ note: the following features could also be redefined:
|
||
+ - on_pong
|
||
+ - on_ping
|
||
+ - on_unsupported
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ WEB_SOCKET_EVENT_I
|
||
+
|
||
+inherit
|
||
+ WEB_SOCKET_CONSTANTS
|
||
+
|
||
+ REFACTORING_HELPER
|
||
+
|
||
+feature -- Web Socket Interface
|
||
+
|
||
+ on_event (ws: WEB_SOCKET; a_message: detachable READABLE_STRING_8; a_opcode: INTEGER)
|
||
+ -- Called when a frame from the client has been receive
|
||
+ require
|
||
+ ws_attached: ws /= Void
|
||
+ ws_valid: ws.is_open_read and then ws.is_open_write
|
||
+ local
|
||
+ l_message: READABLE_STRING_8
|
||
+ do
|
||
+ debug ("ws")
|
||
+ ws.log ("%Non_event (ws, a_message, " + opcode_name (a_opcode) + ")%N", {HTTPD_LOGGER_CONSTANTS}.debug_level)
|
||
+ end
|
||
+ if a_message = Void then
|
||
+ create {STRING} l_message.make_empty
|
||
+ else
|
||
+ l_message := a_message
|
||
+ end
|
||
+
|
||
+ if a_opcode = Binary_frame then
|
||
+ on_binary (ws, l_message)
|
||
+ elseif a_opcode = Text_frame then
|
||
+ on_text (ws, l_message)
|
||
+ elseif a_opcode = Pong_frame then
|
||
+ on_pong (ws, l_message)
|
||
+ elseif a_opcode = Ping_frame then
|
||
+ on_ping (ws, l_message)
|
||
+ elseif a_opcode = Connection_close_frame then
|
||
+ on_connection_close (ws, "")
|
||
+ else
|
||
+ on_unsupported (ws, l_message, a_opcode)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Websocket events
|
||
+
|
||
+ on_open (ws: WEB_SOCKET)
|
||
+ -- Called after handshake, indicates that a complete WebSocket connection has been established.
|
||
+ require
|
||
+ ws_attached: ws /= Void
|
||
+ ws_valid: ws.is_open_read and then ws.is_open_write
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ on_binary (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||
+ require
|
||
+ ws_attached: ws /= Void
|
||
+ ws_valid: ws.is_open_read and then ws.is_open_write
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ on_text (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||
+ require
|
||
+ ws_attached: ws /= Void
|
||
+ ws_valid: ws.is_open_read and then ws.is_open_write
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+ on_close (ws: detachable WEB_SOCKET)
|
||
+ -- Called after the WebSocket connection is closed.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+feature -- Websocket events: implemented
|
||
+
|
||
+ on_pong (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||
+ require
|
||
+ ws_attached: ws /= Void
|
||
+ ws_valid: ws.is_open_read and then ws.is_open_write
|
||
+ do
|
||
+ -- log ("Its a pong frame")
|
||
+ -- at first we ignore pong
|
||
+ -- FIXME: provide better explanation
|
||
+ end
|
||
+
|
||
+ on_ping (ws: WEB_SOCKET; a_message: READABLE_STRING_8)
|
||
+ require
|
||
+ ws_attached: ws /= Void
|
||
+ ws_valid: ws.is_open_read and then ws.is_open_write
|
||
+ do
|
||
+ ws.send (Pong_frame, a_message)
|
||
+ end
|
||
+
|
||
+ on_unsupported (ws: WEB_SOCKET; a_message: READABLE_STRING_8; a_opcode: INTEGER)
|
||
+ require
|
||
+ ws_attached: ws /= Void
|
||
+ ws_valid: ws.is_open_read and then ws.is_open_write
|
||
+ do
|
||
+ -- do nothing
|
||
+ end
|
||
+
|
||
+ on_connection_close (ws: WEB_SOCKET; a_message: detachable READABLE_STRING_8)
|
||
+ require
|
||
+ ws_attached: ws /= Void
|
||
+ ws_valid: ws.is_open_read and then ws.is_open_write
|
||
+ do
|
||
+ ws.send (Connection_close_frame, "")
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/websocket/event/web_socket_message_type.e b/library/server/wsf/connector/standalone_websocket/websocket/event/web_socket_message_type.e
|
||
new file mode 100644
|
||
index 0000000..cd48af0
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/websocket/event/web_socket_message_type.e
|
||
@@ -0,0 +1,39 @@
|
||
+note
|
||
+ description: "[
|
||
+ A web socket message has an opcode specifying the type of the message payload. The
|
||
+ opcode consists of the last four bits in the first byte of the frame header.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+ EIS: "name=Data Frame", "src=http://tools.ietf.org/html/rfc6455#section-5.6", "protocol=uri"
|
||
+ EIS: "name=Control Frame", "src=http://tools.ietf.org/html/rfc6455#section-5.5", "protocol=uri"
|
||
+
|
||
+class
|
||
+ WEB_SOCKET_MESSAGE_TYPE
|
||
+
|
||
+feature -- Data Frames
|
||
+
|
||
+ Text: INTEGER = 0x1
|
||
+ -- The data type of the message is text.
|
||
+
|
||
+ Binary: INTEGER = 0x2
|
||
+ -- The data type of the message is binary.
|
||
+
|
||
+feature -- Control Frames
|
||
+
|
||
+ Close: INTEGER = 0x8
|
||
+ -- The client or server is sending a closing
|
||
+ -- handshake to the server or client.
|
||
+
|
||
+ Ping: INTEGER = 0x9
|
||
+ -- The client or server sends a ping to the server or client.
|
||
+
|
||
+ Pong: INTEGER = 0xA
|
||
+ -- The client or server sends a pong to the server or client.
|
||
+
|
||
+feature -- Reserverd
|
||
+
|
||
+ -- Opcodes 0x3-0x7 are reserved for further non-control frames yet to be
|
||
+ -- defined.
|
||
+
|
||
+end
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/web_socket_constants.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/web_socket_constants.e
|
||
new file mode 100644
|
||
index 0000000..86e771d
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/web_socket_constants.e
|
||
@@ -0,0 +1,203 @@
|
||
+note
|
||
+ description: "Constants for WebSockets"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WEB_SOCKET_CONSTANTS
|
||
+
|
||
+feature -- Constants
|
||
+
|
||
+
|
||
+ HTTP_1_1: STRING = "HTTP/1.1 101 WebSocket Protocol Handshake"
|
||
+
|
||
+ Upgrade_ws: STRING = "Upgrade: websocket"
|
||
+
|
||
+ Connection_ws: STRING = "Connection: Upgrade"
|
||
+
|
||
+ Sec_WebSocket_Origin: STRING = "Sec-WebSocket-Origin: "
|
||
+
|
||
+ Sec_WebSocket_Protocol: STRING = "Sec-WebSocket-Protocol: "
|
||
+
|
||
+ Sec_WebSocket_Location: STRING = "Sec-WebSocket-Location: "
|
||
+
|
||
+ Sec_WebSocket_Version: STRING = "Sec-WebSocket-Version: "
|
||
+
|
||
+ Sec_WebSocket_Extensions: STRING = "Sec-WebSocket-Extensions: "
|
||
+
|
||
+ WebSocket_Origin: STRING = "WebSocket-Origin: "
|
||
+
|
||
+ WebSocket_Protocol: STRING = "WebSocket-Protocol: "
|
||
+
|
||
+ WebSocket_Location: STRING = "WebSocket-Location: "
|
||
+
|
||
+ Origin: STRING = "Origin"
|
||
+
|
||
+ Server: STRING = "EWSS"
|
||
+
|
||
+ Sec_WebSocket_Key: STRING = "Sec-WebSocket-Key"
|
||
+
|
||
+ Ws_scheme: STRING = "ws://"
|
||
+
|
||
+ Wss_scheme: STRING = "wss://"
|
||
+
|
||
+ Magic_guid: STRING = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||
+
|
||
+ -- The handshake from the client looks as follows:
|
||
+
|
||
+ -- GET /chat HTTP/1.1
|
||
+ -- Host: server.example.com
|
||
+ -- Upgrade: websocket
|
||
+ -- Connection: Upgrade
|
||
+ -- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||
+ -- Origin: http://example.com
|
||
+ -- Sec-WebSocket-Protocol: chat, superchat
|
||
+ -- Sec-WebSocket-Version: 13
|
||
+
|
||
+ -- The handshake from the server looks as follows:
|
||
+
|
||
+ -- HTTP/1.1 101 Switching Protocols
|
||
+ -- Upgrade: websocket
|
||
+ -- Connection: Upgrade
|
||
+ -- Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
|
||
+ -- Sec-WebSocket-Protocol: chat
|
||
+
|
||
+feature -- Opcodes Standard actions
|
||
+
|
||
+ --| Maybe we need an enum STANDARD_ACTIONS_OPCODES?
|
||
+ -- |Opcode | Meaning | Reference |
|
||
+ -- -+--------+-------------------------------------+-----------|
|
||
+ -- | 0 | Continuation Frame | RFC 6455 |
|
||
+ -- -+--------+-------------------------------------+-----------|
|
||
+ -- | 1 | Text Frame | RFC 6455 |
|
||
+ -- -+--------+-------------------------------------+-----------|
|
||
+ -- | 2 | Binary Frame | RFC 6455 |
|
||
+ -- -+--------+-------------------------------------+-----------|
|
||
+ -- | 8 | Connection Close Frame | RFC 6455 |
|
||
+ -- -+--------+-------------------------------------+-----------|
|
||
+ -- | 9 | Ping Frame | RFC 6455 |
|
||
+ -- -+--------+-------------------------------------+-----------|
|
||
+ -- | 10 | Pong Frame | RFC 6455 |
|
||
+ -- -+--------+-------------------------------------+-----------|
|
||
+
|
||
+ Continuation_frame: INTEGER = 0
|
||
+
|
||
+ Text_frame: INTEGER = 1
|
||
+
|
||
+ Binary_frame: INTEGER = 2
|
||
+
|
||
+ Connection_close_frame: INTEGER = 8
|
||
+
|
||
+ Ping_frame: INTEGER = 9
|
||
+
|
||
+ Pong_frame: INTEGER = 10
|
||
+
|
||
+ is_control_frame (a_opcode: INTEGER): BOOLEAN
|
||
+ -- Is `a_opcode' a control frame?
|
||
+ do
|
||
+ inspect a_opcode
|
||
+ when Connection_close_frame, Ping_frame, Pong_frame then
|
||
+ Result := True
|
||
+ else
|
||
+ end
|
||
+ end
|
||
+
|
||
+ opcode_name (a_opcode: INTEGER): STRING
|
||
+ do
|
||
+ inspect a_opcode
|
||
+ when Continuation_frame then Result := "Continuation"
|
||
+ when Text_frame then Result := "Text"
|
||
+ when Binary_frame then Result := "Binary"
|
||
+ when Connection_close_frame then Result := "Connection Close"
|
||
+ when Ping_frame then Result := "Ping"
|
||
+ when Pong_frame then Result := "Pong"
|
||
+ else
|
||
+ Result := "Unknown-Opcode"
|
||
+ end
|
||
+ Result := "0x" + a_opcode.to_hex_string + " " + Result
|
||
+ end
|
||
+
|
||
+feature -- Close code numbers
|
||
+
|
||
+ -- Maybe an ENUM CLOSE_CODES
|
||
+
|
||
+ -- |Status Code | Meaning | Contact | Reference |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1000 | Normal Closure | hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1001 | Going Away | hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1002 | Protocol error | hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1003 | Unsupported Data| hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1004 | ---Reserved---- | hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1005 | No Status Rcvd | hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1006 | Abnormal Closure| hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1007 | Invalid frame | hybi@ietf.org | RFC 6455 |
|
||
+ -- | | payload data | | |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1008 | Policy Violation| hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1009 | Message Too Big | hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1010 | Mandatory Ext. | hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1011 | Internal Server | hybi@ietf.org | RFC 6455 |
|
||
+ -- | | Error | | |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+ -- | 1015 | TLS handshake | hybi@ietf.org | RFC 6455 |
|
||
+ -- -+------------+-----------------+---------------+-----------|
|
||
+
|
||
+ Normal_closure: INTEGER = 1000
|
||
+ -- Indicates a normal closure, meaning that the purpose for
|
||
+ -- which the connection was established has been fulfilled.
|
||
+
|
||
+ Going_away: INTEGER = 1001
|
||
+ -- Indicates that an endpoint is "going away", such as a server
|
||
+ -- going down or a browser having navigated away from a page.
|
||
+
|
||
+ Protocol_error: INTEGER = 1002
|
||
+ -- Indicates that an endpoint is terminating the connection due
|
||
+ -- to a protocol error.
|
||
+
|
||
+ Unsupported_data: INTEGER = 1003
|
||
+ -- Indicates that an endpoint is terminating the connection
|
||
+ -- because it has received a type of data it cannot accept (e.g., an
|
||
+ -- endpoint that understands only text data MAY send this if it
|
||
+ -- receives a binary message).
|
||
+
|
||
+ Invalid_data: INTEGER = 1007
|
||
+ -- Indicates that an endpoint is terminating the connection
|
||
+ -- because it has received data within a message that was not
|
||
+ -- consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
|
||
+ -- data within a text message).
|
||
+
|
||
+ Policy_violation: INTEGER = 1008
|
||
+ -- Indicates that an endpoint is terminating the connection
|
||
+ -- because it has received a message that violates its policy. This
|
||
+ -- is a generic status code that can be returned when there is no
|
||
+ -- other more suitable status code (e.g., 1003 or 1009) or if there
|
||
+ -- is a need to hide specific details about the policy.
|
||
+
|
||
+ Message_too_large: INTEGER = 1009
|
||
+ -- Indicates that an endpoint is terminating the connection
|
||
+ -- because it has received a message that is too big for it to
|
||
+ -- process.
|
||
+
|
||
+ Extension_required: INTEGER = 1010
|
||
+ -- Indicates that an endpoint (client) is terminating the
|
||
+ -- connection because it has expected the server to negotiate one or
|
||
+ -- more extension, but the server didn't return them in the response
|
||
+ -- message of the WebSocket handshake.
|
||
+
|
||
+ Internal_error: INTEGER = 1011
|
||
+ -- Indicates that a server is terminating the connection because
|
||
+ -- it encountered an unexpected condition that prevented it from
|
||
+ -- fulfilling the request.
|
||
+
|
||
+
|
||
+end
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/web_socket_error_frame.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/web_socket_error_frame.e
|
||
new file mode 100644
|
||
index 0000000..af9aa83
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/web_socket_error_frame.e
|
||
@@ -0,0 +1,35 @@
|
||
+note
|
||
+ description: "Summary description for {WEB_SOCKET_ERROR_FRAME}."
|
||
+ author: ""
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WEB_SOCKET_ERROR_FRAME
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (a_code: INTEGER; a_desc: like description)
|
||
+ do
|
||
+ code := a_code
|
||
+ description := a_desc
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ code: INTEGER
|
||
+
|
||
+ description: READABLE_STRING_8
|
||
+
|
||
+feature -- Conversion
|
||
+
|
||
+ string: STRING
|
||
+ do
|
||
+ create Result.make_from_string ("Error(" + code.out + "): " + description)
|
||
+ end
|
||
+
|
||
+
|
||
+end
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/websocket/protocol/web_socket_frame.e b/library/server/wsf/connector/standalone_websocket/websocket/protocol/web_socket_frame.e
|
||
new file mode 100644
|
||
index 0000000..3f3350a
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/websocket/protocol/web_socket_frame.e
|
||
@@ -0,0 +1,437 @@
|
||
+note
|
||
+ description: "[
|
||
+ Summary description for {WEB_SOCKET_FRAME}.
|
||
+ See Base Framing Protocol: http://tools.ietf.org/html/rfc6455#section-5.2
|
||
+ 0 1 2 3
|
||
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||
+ +-+-+-+-+-------+-+-------------+-------------------------------+
|
||
+ |F|R|R|R| opcode|M| Payload len | Extended payload length |
|
||
+ |I|S|S|S| (4) |A| (7) | (16/64) |
|
||
+ |N|V|V|V| |S| | (if payload len==126/127) |
|
||
+ | |1|2|3| |K| | |
|
||
+ +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||
+ | Extended payload length continued, if payload len == 127 |
|
||
+ + - - - - - - - - - - - - - - - +-------------------------------+
|
||
+ | |Masking-key, if MASK set to 1 |
|
||
+ +-------------------------------+-------------------------------+
|
||
+ | Masking-key (continued) | Payload Data |
|
||
+ +-------------------------------- - - - - - - - - - - - - - - - +
|
||
+ : Payload Data continued ... :
|
||
+ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
||
+ | Payload Data continued ... |
|
||
+ +---------------------------------------------------------------+
|
||
+
|
||
+ Check the `check_utf_8_validity_on_chop' if there is performance issue
|
||
+ with bigger data.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+ EIS: "name=Websocket RFC6455 section-5.2", "protocol=URI", "src=http://tools.ietf.org/html/rfc6455#section-5.2", "tag=rfc"
|
||
+
|
||
+class
|
||
+ WEB_SOCKET_FRAME
|
||
+
|
||
+inherit
|
||
+ ANY
|
||
+
|
||
+ WEB_SOCKET_CONSTANTS
|
||
+
|
||
+create
|
||
+ make,
|
||
+ make_as_injected_control
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (a_opcode: INTEGER; flag_is_fin: BOOLEAN)
|
||
+ -- Create current frame with opcode `a_opcode'
|
||
+ -- and `a_fin' to indicate if this is the final fragment.
|
||
+ do
|
||
+ is_incomplete := False
|
||
+ opcode := a_opcode
|
||
+ is_fin := flag_is_fin
|
||
+
|
||
+ inspect opcode
|
||
+ when
|
||
+ Continuation_frame, -- 0
|
||
+ Text_frame, -- 1
|
||
+ Binary_frame -- 2
|
||
+ then
|
||
+ --| Supported opcode
|
||
+ when
|
||
+ Connection_close_frame, -- 8
|
||
+ Ping_frame, -- 9
|
||
+ Pong_frame -- 10
|
||
+ then
|
||
+ --| Supported control opcode
|
||
+ -- All control frames MUST have a payload length of 125 bytes or less
|
||
+ -- and MUST NOT be fragmented.
|
||
+ if flag_is_fin then
|
||
+ -- So far it is valid.
|
||
+ else
|
||
+ report_error (Protocol_error, "Control frames MUST NOT be fragmented.")
|
||
+ end
|
||
+ else
|
||
+ report_error (Protocol_error, "Unknown opcode")
|
||
+ end
|
||
+ end
|
||
+
|
||
+ make_as_injected_control (a_opcode: INTEGER; a_parent: WEB_SOCKET_FRAME)
|
||
+ require
|
||
+ parent_is_not_control_frame: not a_parent.is_control
|
||
+ a_opcode_is_control_frame: is_control_frame (a_opcode)
|
||
+ do
|
||
+ make (a_opcode, True)
|
||
+ parent := a_parent
|
||
+ a_parent.add_injected_control_frame (Current)
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ opcode: INTEGER
|
||
+ -- CONTINUOUS, TEXT, BINARY, PING, PONG, CLOSING
|
||
+
|
||
+ is_fin: BOOLEAN
|
||
+ -- is the final fragment in a message?
|
||
+
|
||
+ fragment_count: INTEGER
|
||
+
|
||
+ payload_length: NATURAL_64
|
||
+ payload_data: detachable STRING_8
|
||
+ -- Maybe we need a buffer here.
|
||
+
|
||
+ uncoded_payload_data: detachable STRING_32
|
||
+ local
|
||
+ utf: UTF_CONVERTER
|
||
+ do
|
||
+ if attached payload_data as d then
|
||
+ Result := utf.utf_8_string_8_to_string_32 (d)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ error: detachable WEB_SOCKET_ERROR_FRAME
|
||
+ -- Describe the type of error
|
||
+
|
||
+feature -- Access: injected control frames
|
||
+
|
||
+ injected_control_frames: detachable LIST [WEB_SOCKET_FRAME]
|
||
+
|
||
+ parent: detachable WEB_SOCKET_FRAME
|
||
+ -- If Current is injected, `parent' is the related fragmented frame
|
||
+
|
||
+ is_injected_control: BOOLEAN
|
||
+ do
|
||
+ Result := parent /= Void
|
||
+ ensure
|
||
+ Result implies (is_control_frame (opcode))
|
||
+ end
|
||
+
|
||
+feature -- Operation
|
||
+
|
||
+ update_fin (a_flag_is_fin: BOOLEAN)
|
||
+ do
|
||
+ is_fin := a_flag_is_fin
|
||
+ end
|
||
+
|
||
+feature {WEB_SOCKET_FRAME} -- Change: injected control frames
|
||
+
|
||
+ add_injected_control_frame (f: WEB_SOCKET_FRAME)
|
||
+ require
|
||
+ Current_is_not_control: not is_control
|
||
+ f_is_control_frame: f.is_control
|
||
+ parented_to_current: f.parent = Current
|
||
+ local
|
||
+ lst: like injected_control_frames
|
||
+ do
|
||
+ lst := injected_control_frames
|
||
+ if lst = Void then
|
||
+ create {ARRAYED_LIST [WEB_SOCKET_FRAME]} lst.make (1)
|
||
+ injected_control_frames := lst
|
||
+ end
|
||
+ lst.force (f)
|
||
+ ensure
|
||
+ parented_to_current: f.parent = Current
|
||
+ end
|
||
+
|
||
+ remove_injected_control_frame (f: WEB_SOCKET_FRAME)
|
||
+ require
|
||
+ Current_is_not_control: not is_control
|
||
+ f_is_control_frame: f.is_control
|
||
+ parented_to_current: f.parent = Current
|
||
+ local
|
||
+ lst: like injected_control_frames
|
||
+ do
|
||
+ lst := injected_control_frames
|
||
+ if lst /= Void then
|
||
+ lst.prune (f)
|
||
+ if lst.is_empty then
|
||
+ injected_control_frames := Void
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Query
|
||
+
|
||
+ is_binary: BOOLEAN
|
||
+ do
|
||
+ Result := opcode = binary_frame
|
||
+ end
|
||
+
|
||
+ is_text: BOOLEAN
|
||
+ do
|
||
+ Result := opcode = text_frame
|
||
+ end
|
||
+
|
||
+ is_continuation: BOOLEAN
|
||
+ do
|
||
+ Result := opcode = continuation_frame
|
||
+ end
|
||
+
|
||
+ is_connection_close: BOOLEAN
|
||
+ do
|
||
+ Result := opcode = connection_close_frame
|
||
+ end
|
||
+
|
||
+ is_control: BOOLEAN
|
||
+ do
|
||
+ inspect opcode
|
||
+ when connection_close_frame, Ping_frame, Pong_frame then
|
||
+ Result := True
|
||
+ else
|
||
+ end
|
||
+ end
|
||
+
|
||
+ is_ping: BOOLEAN
|
||
+ do
|
||
+ Result := opcode = ping_frame
|
||
+ end
|
||
+
|
||
+ is_pong: BOOLEAN
|
||
+ do
|
||
+ Result := opcode = pong_frame
|
||
+ end
|
||
+
|
||
+feature -- Status report
|
||
+
|
||
+ is_valid: BOOLEAN
|
||
+ do
|
||
+ Result := not has_error
|
||
+ end
|
||
+
|
||
+ is_incomplete: BOOLEAN
|
||
+
|
||
+ has_error: BOOLEAN
|
||
+ do
|
||
+ Result := error /= Void
|
||
+ end
|
||
+
|
||
+feature -- Change
|
||
+
|
||
+ increment_fragment_count
|
||
+ do
|
||
+ fragment_count := fragment_count + 1
|
||
+ end
|
||
+
|
||
+ check_utf_8_validity_on_chop: BOOLEAN = False
|
||
+ -- True: check for each chop
|
||
+ -- False: check only for each fragment
|
||
+ --| see autobahntestsuite #6.4.3 and #6.4.4
|
||
+
|
||
+ append_payload_data_chop (a_data: STRING_8; a_len: INTEGER; a_flag_chop_complete: BOOLEAN)
|
||
+ do
|
||
+ if a_flag_chop_complete then
|
||
+ increment_fragment_count
|
||
+ end
|
||
+ if attached payload_data as l_payload_data then
|
||
+ l_payload_data.append (a_data)
|
||
+ else
|
||
+ payload_data := a_data
|
||
+ end
|
||
+ payload_length := payload_length + a_len.to_natural_64
|
||
+
|
||
+ if is_text then
|
||
+ if is_fin and a_flag_chop_complete then
|
||
+ -- Check the whole message is a valid UTF-8 string
|
||
+ if attached payload_data as d then
|
||
+ if not is_valid_utf_8_string (d) then
|
||
+ report_error (invalid_data, "The text message is not a valid UTF-8 text!")
|
||
+ end
|
||
+ else
|
||
+ -- empty payload??
|
||
+ end
|
||
+ elseif check_utf_8_validity_on_chop or else a_flag_chop_complete then
|
||
+ -- Check the payload data as utf-8 stream (may be incomplete at this point)
|
||
+ if not is_valid_text_payload_stream then
|
||
+ report_error (invalid_data, "This is not a valid UTF-8 stream!")
|
||
+ -- is_valid implies the connection will be closed!
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+ report_error (a_code: INTEGER; a_description: READABLE_STRING_8)
|
||
+ require
|
||
+ not has_error
|
||
+ do
|
||
+ create error.make (a_code, a_description)
|
||
+ ensure
|
||
+ has_error: has_error
|
||
+ is_not_valid: not is_valid
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Helper
|
||
+
|
||
+ last_utf_8_stream_validation_position: INTEGER
|
||
+ -- In relation with `is_valid_utf_8 (.., a_is_stream=True)'
|
||
+
|
||
+ is_valid_text_payload_stream: BOOLEAN
|
||
+ require
|
||
+ is_text_frame: is_text
|
||
+ do
|
||
+ if attached payload_data as s then
|
||
+ Result := is_valid_utf_8 (s, not is_fin)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ is_valid_utf_8_string (s: READABLE_STRING_8): BOOLEAN
|
||
+ do
|
||
+ Result := is_valid_utf_8 (s, False)
|
||
+-- and (create {UTF_CONVERTER}).is_valid_utf_8_string_8 (s)
|
||
+ end
|
||
+
|
||
+ is_valid_utf_8 (s: READABLE_STRING_8; a_is_stream: BOOLEAN): BOOLEAN
|
||
+ -- UTF-8 validity checker.
|
||
+ note
|
||
+ EIS: "name=UTF-8 RFC3629", "protocol=URI", "src=https://tools.ietf.org/html/rfc3629", "tag=rfc"
|
||
+ require
|
||
+ is_text_frame: is_text
|
||
+ local
|
||
+ i: like {STRING_8}.count
|
||
+ n: like {STRING_8}.count
|
||
+ c,c2,c3,c4,w: NATURAL_32
|
||
+ l_is_incomplete_stream: BOOLEAN
|
||
+ do
|
||
+ Result := True
|
||
+ -- Following code also check that codepoint is between 0 and 0x10FFFF
|
||
+ -- (as expected by spec, and tested by autobahn ws testsuite)
|
||
+ from
|
||
+ if a_is_stream then
|
||
+ i := last_utf_8_stream_validation_position -- to avoid recomputing from the beginning each time.
|
||
+ else
|
||
+ i := 0
|
||
+ end
|
||
+ n := s.count
|
||
+ until
|
||
+ i >= n or not Result
|
||
+ loop
|
||
+ i := i + 1
|
||
+ c := s.code (i)
|
||
+ if c <= 0x7F then
|
||
+ -- 0xxxxxxx
|
||
+ w := c
|
||
+ elseif c <= 0xC1 then
|
||
+ -- The octet values C0, C1, F5 to FF never appear.
|
||
+ --| case 0xC0 and 0xC1
|
||
+ Result := False
|
||
+ elseif (c & 0xE0) = 0xC0 then
|
||
+ -- 110xxxxx 10xxxxxx
|
||
+ i := i + 1
|
||
+ if i <= n then
|
||
+ c2 := s.code (i)
|
||
+ if
|
||
+ (c2 & 0xC0) = 0x80
|
||
+ then
|
||
+ w := ((c & 0x1F) |<< 6)
|
||
+ | (c2 & 0x3F)
|
||
+ Result := 0x80 <= w and w <= 0x7FF
|
||
+ else
|
||
+ Result := False
|
||
+ end
|
||
+ else
|
||
+ l_is_incomplete_stream := True
|
||
+ end
|
||
+ elseif (c & 0xF0) = 0xE0 then
|
||
+ -- 1110xxxx 10xxxxxx 10xxxxxx
|
||
+ i := i + 2
|
||
+ if i <= n then
|
||
+ c2 := s.code (i - 1)
|
||
+ c3 := s.code (i)
|
||
+ if
|
||
+ (c2 & 0xC0) = 0x80 and
|
||
+ (c3 & 0xC0) = 0x80
|
||
+ then
|
||
+ w := ((c & 0xF) |<< 12)
|
||
+ | ((c2 & 0x3F) |<< 6)
|
||
+ | (c3 & 0x3F)
|
||
+ if 0x800 <= w and w <= 0xFFFF then
|
||
+ if 0xD800 <= w and w <= 0xDFFF then
|
||
+ -- The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF
|
||
+ Result := False
|
||
+ end
|
||
+ else
|
||
+ Result := False
|
||
+ end
|
||
+ else
|
||
+ Result := False
|
||
+ end
|
||
+ else
|
||
+ if i - 1 <= n then
|
||
+ Result := (s.code (i - 1) & 0xC0) = 0x80
|
||
+ end
|
||
+ l_is_incomplete_stream := True
|
||
+ end
|
||
+ elseif (c & 0xF8) = 0xF0 then -- 0001 0000-0010 FFFF
|
||
+ -- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||
+ if 0xF5 <= c and c <= 0xFF then
|
||
+ -- The octet values C0, C1, F5 to FF never appear.
|
||
+ Result := False
|
||
+ else
|
||
+ i := i + 3
|
||
+ if i <= n then
|
||
+ c2 := s.code (i - 2)
|
||
+ c3 := s.code (i - 1)
|
||
+ c4 := s.code (i)
|
||
+ if
|
||
+ (c2 & 0xC0) = 0x80 and
|
||
+ (c3 & 0xC0) = 0x80 and
|
||
+ (c4 & 0xC0) = 0x80
|
||
+ then
|
||
+ w := ((c & 0x7) |<< 18) |
|
||
+ ((c2 & 0x3F) |<< 12) |
|
||
+ ((c3 & 0x3F) |<< 6) |
|
||
+ (c4 & 0x3F)
|
||
+ Result := 0x1_0000 <= w and w <= 0x10_FFFF
|
||
+ else
|
||
+ Result := False
|
||
+ end
|
||
+ else
|
||
+ if i - 2 <= n then
|
||
+ c2 := s.code (i - 2)
|
||
+ Result := (c2 & 0xC0) = 0x80
|
||
+ if Result then
|
||
+ if c = 0xF4 and c2 >= 0x90 then
|
||
+ --| any byte 10xxxxxx (i.e >= 0x80) that would come after,
|
||
+ -- will result in out of range code point
|
||
+ -- indeed 0xF4 0x90 0x80 0x80 = 0x1100 0000 > 0x10_FFFF
|
||
+ Result := False
|
||
+ elseif i - 1 <= n then
|
||
+ Result := (s.code (i - 1) & 0xC0) = 0x80
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ l_is_incomplete_stream := True
|
||
+ end
|
||
+ end
|
||
+ else
|
||
+ -- Invalid byte in UTF-8
|
||
+ Result := False
|
||
+ end
|
||
+ if Result then
|
||
+ if l_is_incomplete_stream then
|
||
+ Result := a_is_stream
|
||
+ elseif a_is_stream then
|
||
+ last_utf_8_stream_validation_position := i
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+end
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/websocket/web_socket.e b/library/server/wsf/connector/standalone_websocket/websocket/web_socket.e
|
||
new file mode 100644
|
||
index 0000000..e301f25
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/websocket/web_socket.e
|
||
@@ -0,0 +1,805 @@
|
||
+note
|
||
+ description: "[
|
||
+ Object representing the websocket connection.
|
||
+ It contains the `request` and `response`, and more important the `socket` itself.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WEB_SOCKET
|
||
+
|
||
+inherit
|
||
+ WGI_STANDALONE_CONNECTOR_EXPORTER
|
||
+
|
||
+ WSF_RESPONSE_EXPORTER
|
||
+
|
||
+ WGI_EXPORTER
|
||
+
|
||
+ HTTPD_LOGGER_CONSTANTS
|
||
+
|
||
+ WEB_SOCKET_CONSTANTS
|
||
+
|
||
+ SHARED_BASE64
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
+ do
|
||
+ request := req
|
||
+ response := res
|
||
+ is_verbose := False
|
||
+ verbose_level := notice_level
|
||
+
|
||
+ if
|
||
+ attached {WGI_STANDALONE_INPUT_STREAM} req.input as r_input
|
||
+ then
|
||
+ socket := r_input.source
|
||
+ else
|
||
+ create socket.make_empty
|
||
+ check has_socket: False end
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ socket: HTTPD_STREAM_SOCKET
|
||
+ -- Underlying connected socket.
|
||
+
|
||
+feature {NONE} -- Access
|
||
+
|
||
+ request: WSF_REQUEST
|
||
+ -- Associated request.
|
||
+
|
||
+ response: WSF_RESPONSE
|
||
+ -- Associated response stream.
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ is_websocket: BOOLEAN
|
||
+ -- Does `open_ws_handshake' detect valid websocket upgrade handshake?
|
||
+
|
||
+feature -- Settings
|
||
+
|
||
+ is_verbose: BOOLEAN
|
||
+ -- Output verbose log messages?
|
||
+
|
||
+ verbose_level: INTEGER
|
||
+ -- Level of verbosity.
|
||
+
|
||
+feature -- Status
|
||
+
|
||
+ has_error: BOOLEAN
|
||
+ -- Error occured during processing?
|
||
+
|
||
+feature -- Socket status
|
||
+
|
||
+ is_ready_for_reading: BOOLEAN
|
||
+ -- Is `socket' ready for reading?
|
||
+ --| at this point, socket should be set to blocking.
|
||
+ do
|
||
+ Result := socket.ready_for_reading
|
||
+ end
|
||
+
|
||
+ is_open_read: BOOLEAN
|
||
+ -- Is `socket' open for reading?
|
||
+ do
|
||
+ Result := socket.is_open_read
|
||
+ end
|
||
+
|
||
+ is_open_write: BOOLEAN
|
||
+ -- Is `socket' open for writing?
|
||
+ do
|
||
+ Result := socket.is_open_write
|
||
+ end
|
||
+
|
||
+ socket_descriptor: INTEGER
|
||
+ -- Descriptor for current `socket'.
|
||
+ do
|
||
+ Result := socket.descriptor
|
||
+ end
|
||
+
|
||
+feature -- Element change
|
||
+
|
||
+ set_is_verbose (b: BOOLEAN)
|
||
+ do
|
||
+ is_verbose := b
|
||
+ end
|
||
+
|
||
+ set_verbose_level (lev: INTEGER)
|
||
+ do
|
||
+ verbose_level := lev
|
||
+ end
|
||
+
|
||
+feature -- Basic operation
|
||
+
|
||
+ put_error (a_message: READABLE_STRING_8)
|
||
+ do
|
||
+ response.put_error (a_message)
|
||
+ end
|
||
+
|
||
+ log (m: READABLE_STRING_8; lev: INTEGER)
|
||
+ -- Log `m' in the error channel, i.e stderr for standalone.
|
||
+ do
|
||
+ if is_verbose then
|
||
+ put_error (m)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Basic Operation
|
||
+
|
||
+ open_ws_handshake
|
||
+ -- The opening handshake is intended to be compatible with HTTP-based
|
||
+ -- server-side software and intermediaries, so that a single port can be
|
||
+ -- used by both HTTP clients alking to that server and WebSocket
|
||
+ -- clients talking to that server. To this end, the WebSocket client's
|
||
+ -- handshake is an HTTP Upgrade request:
|
||
+
|
||
+ -- GET /chat HTTP/1.1
|
||
+ -- Host: server.example.com
|
||
+ -- Upgrade: websocket
|
||
+ -- Connection: Upgrade
|
||
+ -- Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
|
||
+ -- Origin: http://example.com
|
||
+ -- Sec-WebSocket-Protocol: chat, superchat
|
||
+ -- Sec-WebSocket-Version: 13
|
||
+ local
|
||
+ l_sha1: SHA1
|
||
+ l_key : STRING
|
||
+ req: like request
|
||
+ res: like response
|
||
+ do
|
||
+ -- Reset values.
|
||
+ is_websocket := False
|
||
+ has_error := False
|
||
+
|
||
+ -- Local cache.
|
||
+ req := request
|
||
+ res := response
|
||
+
|
||
+ -- Reading client's opening GT
|
||
+
|
||
+ -- TODO extract to a validator handshake or something like that.
|
||
+ if is_verbose then
|
||
+ log ("%NReceive <====================", debug_level)
|
||
+ if attached req.raw_header_data as rhd then
|
||
+ log (rhd, debug_level)
|
||
+ end
|
||
+ end
|
||
+ if
|
||
+ req.is_get_request_method and then -- MUST be GET request!
|
||
+ attached req.meta_string_variable ("HTTP_UPGRADE") as l_upgrade_key and then
|
||
+ l_upgrade_key.is_case_insensitive_equal_general ("websocket") -- Upgrade header must be present with value websocket
|
||
+ then
|
||
+ is_websocket := True
|
||
+ socket.set_blocking
|
||
+ if
|
||
+ attached req.meta_string_variable ("HTTP_SEC_WEBSOCKET_KEY") as l_ws_key and then -- Sec-websocket-key must be present
|
||
+ attached req.meta_string_variable ("HTTP_CONNECTION") as l_connection_key and then -- Connection header must be present with value Upgrade
|
||
+ l_connection_key.has_substring ("Upgrade") and then
|
||
+ attached req.meta_string_variable ("HTTP_SEC_WEBSOCKET_VERSION") as l_version_key and then -- Version header must be present with value 13
|
||
+ l_version_key.is_case_insensitive_equal ("13") and then
|
||
+ attached req.http_host -- Host header must be present
|
||
+ then
|
||
+ if is_verbose then
|
||
+ log ("key " + l_ws_key, debug_level)
|
||
+ end
|
||
+ -- Sending the server's opening handshake
|
||
+ create l_sha1.make
|
||
+ l_sha1.update_from_string (l_ws_key + magic_guid)
|
||
+ l_key := Base64_encoder.encoded_string (digest (l_sha1))
|
||
+ res.header.add_header_key_value ("Upgrade", "websocket")
|
||
+ res.header.add_header_key_value ("Connection", "Upgrade")
|
||
+ res.header.add_header_key_value ("Sec-WebSocket-Accept", l_key)
|
||
+
|
||
+ if is_verbose then
|
||
+ log ("%N================> Send Handshake", debug_level)
|
||
+ if attached {HTTP_HEADER} res.header as h then
|
||
+ log (h.string, debug_level)
|
||
+ end
|
||
+ end
|
||
+ res.set_status_code_with_reason_phrase (101, "Switching Protocols")
|
||
+ res.wgi_response.push
|
||
+ else
|
||
+ has_error := True
|
||
+ if is_verbose then
|
||
+ log ("Error (opening_handshake)!!!", debug_level)
|
||
+ end
|
||
+ -- If we cannot complete the handshake, then the server MUST stop processing the client's handshake and return an HTTP response with an
|
||
+ -- appropriate error code (such as 400 Bad Request).
|
||
+ res.set_status_code_with_reason_phrase (400, "Bad Request")
|
||
+ end
|
||
+ else
|
||
+ is_websocket := False
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Response!
|
||
+
|
||
+ send (a_opcode:INTEGER; a_message: READABLE_STRING_8)
|
||
+ local
|
||
+ i: INTEGER
|
||
+ l_chunk_size: INTEGER
|
||
+ l_chunk: READABLE_STRING_8
|
||
+ l_header_message: STRING
|
||
+ l_message_count: INTEGER
|
||
+ n: NATURAL_64
|
||
+ retried: BOOLEAN
|
||
+ do
|
||
+ debug ("ws")
|
||
+ print (">>do_send (..., "+ opcode_name (a_opcode) +", ..)%N")
|
||
+ end
|
||
+ if not retried then
|
||
+ create l_header_message.make_empty
|
||
+ l_header_message.append_code ((0x80 | a_opcode).to_natural_32)
|
||
+ l_message_count := a_message.count
|
||
+ n := l_message_count.to_natural_64
|
||
+ if l_message_count > 0xffff then
|
||
+ --! Improve. this code needs to be checked.
|
||
+ l_header_message.append_code ((0 | 127).to_natural_32)
|
||
+ l_header_message.append_character ((n |>> 56).to_character_8)
|
||
+ l_header_message.append_character ((n |>> 48).to_character_8)
|
||
+ l_header_message.append_character ((n |>> 40).to_character_8)
|
||
+ l_header_message.append_character ((n |>> 32).to_character_8)
|
||
+ l_header_message.append_character ((n |>> 24).to_character_8)
|
||
+ l_header_message.append_character ((n |>> 16).to_character_8)
|
||
+ l_header_message.append_character ((n |>> 8).to_character_8)
|
||
+ l_header_message.append_character ( n.to_character_8)
|
||
+ elseif l_message_count > 125 then
|
||
+ l_header_message.append_code ((0 | 126).to_natural_32)
|
||
+ l_header_message.append_code ((n |>> 8).as_natural_32)
|
||
+ l_header_message.append_character (n.to_character_8)
|
||
+ else
|
||
+ l_header_message.append_code (n.as_natural_32)
|
||
+ end
|
||
+ socket.put_string (l_header_message)
|
||
+
|
||
+ l_chunk_size := 16_384 -- 16K TODO: see if we should make it customizable.
|
||
+ if l_message_count < l_chunk_size then
|
||
+ socket.put_string (a_message)
|
||
+ else
|
||
+ from
|
||
+ i := 0
|
||
+ until
|
||
+ l_chunk_size = 0
|
||
+ loop
|
||
+ debug ("ws")
|
||
+ print ("Sending chunk " + (i + 1).out + " -> " + (i + l_chunk_size).out +" / " + l_message_count.out + "%N")
|
||
+ end
|
||
+ l_chunk := a_message.substring (i + 1, l_message_count.min (i + l_chunk_size))
|
||
+ socket.put_string (l_chunk)
|
||
+ if l_chunk.count < l_chunk_size then
|
||
+ l_chunk_size := 0
|
||
+ end
|
||
+ i := i + l_chunk_size
|
||
+ end
|
||
+ debug ("ws")
|
||
+ print ("Sending chunk done%N")
|
||
+ end
|
||
+ end
|
||
+ else
|
||
+ -- FIXME: what should be done on rescue?
|
||
+ end
|
||
+ rescue
|
||
+ retried := True
|
||
+ io.put_string ("Internal error in " + generator + ".do_send (conn, a_opcode=" + a_opcode.out + ", a_message) !%N")
|
||
+ retry
|
||
+ end
|
||
+
|
||
+ next_frame: detachable WEB_SOCKET_FRAME
|
||
+ -- TODO Binary messages
|
||
+ -- Handle error responses in a better way.
|
||
+ -- IDEA:
|
||
+ -- class FRAME
|
||
+ -- is_fin: BOOLEAN
|
||
+ -- opcode: WEB_SOCKET_STATUS_CODE (TEXT, BINARY, CLOSE, CONTINUE,PING, PONG)
|
||
+ -- data/payload
|
||
+ -- status_code: #see Status Codes http://tools.ietf.org/html/rfc6455#section-7.3
|
||
+ -- has_error
|
||
+ --
|
||
+ -- See Base Framing Protocol: http://tools.ietf.org/html/rfc6455#section-5.2
|
||
+ -- 0 1 2 3
|
||
+ -- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||
+ -- +-+-+-+-+-------+-+-------------+-------------------------------+
|
||
+ -- |F|R|R|R| opcode|M| Payload len | Extended payload length |
|
||
+ -- |I|S|S|S| (4) |A| (7) | (16/64) |
|
||
+ -- |N|V|V|V| |S| | (if payload len==126/127) |
|
||
+ -- | |1|2|3| |K| | |
|
||
+ -- +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||
+ -- | Extended payload length continued, if payload len == 127 |
|
||
+ -- + - - - - - - - - - - - - - - - +-------------------------------+
|
||
+ -- | |Masking-key, if MASK set to 1 |
|
||
+ -- +-------------------------------+-------------------------------+
|
||
+ -- | Masking-key (continued) | Payload Data |
|
||
+ -- +-------------------------------- - - - - - - - - - - - - - - - +
|
||
+ -- : Payload Data continued ... :
|
||
+ -- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|
||
+ -- | Payload Data continued ... |
|
||
+ -- +---------------------------------------------------------------+
|
||
+ note
|
||
+ EIS: "name=WebSocket RFC", "protocol=URI", "src=http://tools.ietf.org/html/rfc6455#section-5.2"
|
||
+ require
|
||
+ socket_in_blocking_mode: socket.is_blocking
|
||
+ local
|
||
+ l_socket: like socket
|
||
+ l_opcode: INTEGER
|
||
+ l_len: INTEGER
|
||
+ l_remaining_len: INTEGER
|
||
+ l_payload_len: NATURAL_64
|
||
+ l_masking_key: detachable READABLE_STRING_8
|
||
+ l_chunk: STRING
|
||
+ l_rsv: BOOLEAN
|
||
+ l_fin: BOOLEAN
|
||
+ l_has_mask: BOOLEAN
|
||
+ l_chunk_size: INTEGER
|
||
+ l_byte: INTEGER
|
||
+ l_fetch_count: INTEGER
|
||
+ l_bytes_read: INTEGER
|
||
+ s: STRING
|
||
+ is_data_frame_ok: BOOLEAN -- Is the last process data framing ok?
|
||
+ retried: BOOLEAN
|
||
+ do
|
||
+ if not retried then
|
||
+ l_socket := socket
|
||
+ debug ("ws")
|
||
+ print ("next_frame:%N")
|
||
+ end
|
||
+ from
|
||
+ is_data_frame_ok := True
|
||
+ until
|
||
+ l_fin or not is_data_frame_ok
|
||
+ loop
|
||
+ -- multi-frames or continue is only valid for Binary or Text
|
||
+ s := next_bytes (l_socket, 1)
|
||
+ if s.is_empty then
|
||
+ is_data_frame_ok := False
|
||
+ debug ("ws")
|
||
+ print ("[ERROR] incomplete_data!%N")
|
||
+ end
|
||
+ else
|
||
+ l_byte := s [1].code
|
||
+ debug ("ws")
|
||
+ print (" fin,rsv(3),opcode(4)=")
|
||
+ print (to_byte_representation (l_byte))
|
||
+ print ("%N")
|
||
+ end
|
||
+ l_fin := l_byte & (0b10000000) /= 0
|
||
+ l_rsv := l_byte & (0b01110000) = 0
|
||
+ l_opcode := l_byte & 0b00001111
|
||
+ if Result /= Void then
|
||
+ if l_opcode = Result.opcode then
|
||
+ -- should not occur in multi-fragment frame!
|
||
+ create Result.make (l_opcode, l_fin)
|
||
+ Result.report_error (protocol_error, "Unexpected injected frame")
|
||
+ elseif l_opcode = continuation_frame then
|
||
+ -- Expected
|
||
+ Result.update_fin (l_fin)
|
||
+ elseif is_control_frame (l_opcode) then
|
||
+ -- Control frames (see Section 5.5) MAY be injected in the middle of
|
||
+ -- a fragmented message. Control frames themselves MUST NOT be fragmented.
|
||
+ -- if the l_opcode is a control frame then there is an error!!!
|
||
+ -- CLOSE, PING, PONG
|
||
+ create Result.make_as_injected_control (l_opcode, Result)
|
||
+ else
|
||
+ -- should not occur in multi-fragment frame!
|
||
+ create Result.make (l_opcode, l_fin)
|
||
+ Result.report_error (protocol_error, "Unexpected frame")
|
||
+ end
|
||
+ else
|
||
+ create Result.make (l_opcode, l_fin)
|
||
+ if Result.is_continuation then
|
||
+ -- Continuation frame is not expected without parent frame!
|
||
+ Result.report_error (protocol_error, "There is no message to continue!")
|
||
+ end
|
||
+ end
|
||
+ if Result.is_valid then
|
||
+ --| valid frame/fragment
|
||
+ if is_verbose then
|
||
+ log ("+ frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")", debug_level)
|
||
+ end
|
||
+
|
||
+ -- rsv validation
|
||
+ if not l_rsv then
|
||
+ -- RSV1, RSV2, RSV3: 1 bit each
|
||
+
|
||
+ -- MUST be 0 unless an extension is negotiated that defines meanings
|
||
+ -- for non-zero values. If a nonzero value is received and none of
|
||
+ -- the negotiated extensions defines the meaning of such a nonzero
|
||
+ -- value, the receiving endpoint MUST _Fail the WebSocket
|
||
+ -- Connection_
|
||
+
|
||
+ -- FIXME: add support for extension ?
|
||
+ Result.report_error (protocol_error, "RSV values MUST be 0 unless an extension is negotiated that defines meanings for non-zero values")
|
||
+ end
|
||
+ else
|
||
+ if is_verbose then
|
||
+ log ("+ INVALID frame " + opcode_name (l_opcode) + " (fin=" + l_fin.out + ")", debug_level)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ -- At the moment only TEXT, (pending Binary)
|
||
+ if Result.is_valid then
|
||
+ if Result.is_text or Result.is_binary or Result.is_control then
|
||
+ -- Reading next byte (mask+payload_len)
|
||
+ s := next_bytes (l_socket, 1)
|
||
+ if s.is_empty then
|
||
+ Result.report_error (invalid_data, "Incomplete data for mask and payload len")
|
||
+ else
|
||
+ l_byte := s [1].code
|
||
+ debug ("ws")
|
||
+ print (" mask,payload_len(7)=")
|
||
+ print (to_byte_representation (l_byte))
|
||
+ io.put_new_line
|
||
+ end
|
||
+ l_has_mask := l_byte & (0b10000000) /= 0 -- MASK
|
||
+ l_len := l_byte & 0b01111111 -- 7bits
|
||
+
|
||
+ debug ("ws")
|
||
+ print (" payload_len=" + l_len.out)
|
||
+ io.put_new_line
|
||
+ end
|
||
+ if Result.is_control and then l_len > 125 then
|
||
+ -- All control frames MUST have a payload length of 125 bytes or less
|
||
+ -- and MUST NOT be fragmented.
|
||
+ Result.report_error (protocol_error, "Control frame MUST have a payload length of 125 bytes or less")
|
||
+ elseif l_len = 127 then -- TODO proof of concept read 8 bytes.
|
||
+ -- the following 8 bytes interpreted as a 64-bit unsigned integer
|
||
+ -- (the most significant bit MUST be 0) are the payload length.
|
||
+ -- Multibyte length quantities are expressed in network byte order.
|
||
+ s := next_bytes (l_socket, 8) -- 64 bits
|
||
+ debug ("ws")
|
||
+ print (" extended payload length=" + string_to_byte_representation (s))
|
||
+ io.put_new_line
|
||
+ end
|
||
+ if s.count < 8 then
|
||
+ Result.report_error (Invalid_data, "Incomplete data for 64 bit Extended payload length")
|
||
+ else
|
||
+ l_payload_len := s [8].natural_32_code.to_natural_64
|
||
+ l_payload_len := l_payload_len | (s [7].natural_32_code.to_natural_64 |<< 8)
|
||
+ l_payload_len := l_payload_len | (s [6].natural_32_code.to_natural_64 |<< 16)
|
||
+ l_payload_len := l_payload_len | (s [5].natural_32_code.to_natural_64 |<< 24)
|
||
+ l_payload_len := l_payload_len | (s [4].natural_32_code.to_natural_64 |<< 32)
|
||
+ l_payload_len := l_payload_len | (s [3].natural_32_code.to_natural_64 |<< 40)
|
||
+ l_payload_len := l_payload_len | (s [2].natural_32_code.to_natural_64 |<< 48)
|
||
+ l_payload_len := l_payload_len | (s [1].natural_32_code.to_natural_64 |<< 56)
|
||
+ end
|
||
+ elseif l_len = 126 then
|
||
+ s := next_bytes (l_socket, 2) -- 16 bits
|
||
+ debug ("ws")
|
||
+ print (" extended payload length bits=" + string_to_byte_representation (s))
|
||
+ io.put_new_line
|
||
+ end
|
||
+ if s.count < 2 then
|
||
+ Result.report_error (Invalid_data, "Incomplete data for 16 bit Extended payload length")
|
||
+ else
|
||
+ l_payload_len := s [2].natural_32_code.to_natural_64
|
||
+ l_payload_len := l_payload_len | (s [1].natural_32_code.to_natural_64 |<< 8)
|
||
+ end
|
||
+ else
|
||
+ l_payload_len := l_len.to_natural_64
|
||
+ end
|
||
+ debug ("ws")
|
||
+ print (" Full payload length=" + l_payload_len.out)
|
||
+ io.put_new_line
|
||
+ end
|
||
+ if Result.is_valid then
|
||
+ if l_has_mask then
|
||
+ l_masking_key := next_bytes (l_socket, 4) -- 32 bits
|
||
+ debug ("ws")
|
||
+ print (" Masking key bits=" + string_to_byte_representation (l_masking_key))
|
||
+ io.put_new_line
|
||
+ end
|
||
+ if l_masking_key.count < 4 then
|
||
+ debug ("ws")
|
||
+ print ("masking-key read stream -> " + l_socket.bytes_read.out + " bits%N")
|
||
+ end
|
||
+ Result.report_error (Invalid_data, "Incomplete data for Masking-key")
|
||
+ l_masking_key := Void
|
||
+ end
|
||
+ else
|
||
+ Result.report_error (protocol_error, "All frames sent from client to server are masked!")
|
||
+ end
|
||
+ if Result.is_valid then
|
||
+ l_chunk_size := 0x4000 -- 16 K
|
||
+ if l_payload_len > {INTEGER_32}.max_value.to_natural_64 then
|
||
+ -- Issue .. to big to store in STRING
|
||
+ -- FIXME !!!
|
||
+ Result.report_error (Message_too_large, "Can not handle payload data (len=" + l_payload_len.out + ")")
|
||
+ else
|
||
+ l_len := l_payload_len.to_integer_32
|
||
+ end
|
||
+ from
|
||
+ l_fetch_count := 0
|
||
+ l_remaining_len := l_len
|
||
+ until
|
||
+ l_fetch_count >= l_len or l_len = 0 or not Result.is_valid
|
||
+ loop
|
||
+ if l_remaining_len < l_chunk_size then
|
||
+ l_chunk_size := l_remaining_len
|
||
+ end
|
||
+ l_socket.read_stream (l_chunk_size)
|
||
+ l_bytes_read := l_socket.bytes_read
|
||
+ debug ("ws")
|
||
+ print ("read chunk size=" + l_chunk_size.out + " fetch_count=" + l_fetch_count.out + " l_len=" + l_len.out + " -> " + l_bytes_read.out + "bytes%N")
|
||
+ end
|
||
+ if l_bytes_read > 0 then
|
||
+ l_remaining_len := l_remaining_len - l_bytes_read
|
||
+ l_chunk := l_socket.last_string
|
||
+ if l_masking_key /= Void then
|
||
+ -- Masking
|
||
+ -- http://tools.ietf.org/html/rfc6455#section-5.3
|
||
+ unmask (l_chunk, l_fetch_count + 1, l_masking_key)
|
||
+ else
|
||
+ check
|
||
+ client_frame_should_always_be_encoded: False
|
||
+ end
|
||
+ end
|
||
+ l_fetch_count := l_fetch_count + l_bytes_read
|
||
+ Result.append_payload_data_chop (l_chunk, l_bytes_read, l_remaining_len = 0)
|
||
+ else
|
||
+ Result.report_error (internal_error, "Issue reading payload data...")
|
||
+ end
|
||
+ end
|
||
+ if is_verbose then
|
||
+ log (" Received " + l_fetch_count.out + " out of " + l_len.out + " bytes <===============", debug_level)
|
||
+ end
|
||
+ debug ("ws")
|
||
+ print (" -> ")
|
||
+ if attached Result.payload_data as l_payload_data then
|
||
+ s := l_payload_data.tail (l_fetch_count)
|
||
+ if s.count > 50 then
|
||
+ print (string_to_byte_hexa_representation (s.head (50) + ".."))
|
||
+ else
|
||
+ print (string_to_byte_hexa_representation (s))
|
||
+ end
|
||
+ print ("%N")
|
||
+ if Result.is_text and Result.is_fin and Result.fragment_count = 0 then
|
||
+ print (" -> ")
|
||
+ if s.count > 50 then
|
||
+ print (s.head (50) + "..")
|
||
+ else
|
||
+ print (s)
|
||
+ end
|
||
+ print ("%N")
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ if Result /= Void then
|
||
+ if attached Result.error as err then
|
||
+ if is_verbose then
|
||
+ log (" !Invalid frame: " + err.string, debug_level)
|
||
+ end
|
||
+ end
|
||
+ if Result.is_injected_control then
|
||
+ if attached Result.parent as l_parent then
|
||
+ if not Result.is_valid then
|
||
+ l_parent.report_error (protocol_error, "Invalid injected frame")
|
||
+ end
|
||
+ if Result.is_connection_close then
|
||
+ -- Return this and process the connection close right away!
|
||
+ l_parent.update_fin (True)
|
||
+ l_fin := Result.is_fin
|
||
+ else
|
||
+ Result := l_parent
|
||
+ l_fin := l_parent.is_fin
|
||
+ check
|
||
+ -- This is a control frame but occurs in fragmented frame.
|
||
+ inside_fragmented_frame: not l_fin
|
||
+ end
|
||
+ end
|
||
+ else
|
||
+ check
|
||
+ has_parent: False
|
||
+ end
|
||
+ l_fin := False -- This is a control frame but occurs in fragmented frame.
|
||
+ end
|
||
+ end
|
||
+ if not Result.is_valid then
|
||
+ is_data_frame_ok := False
|
||
+ end
|
||
+ else
|
||
+ is_data_frame_ok := False
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ has_error := Result = Void or else Result.has_error
|
||
+ rescue
|
||
+ retried := True
|
||
+ if Result /= Void then
|
||
+ Result.report_error (internal_error, "Internal error")
|
||
+ end
|
||
+ retry
|
||
+ end
|
||
+
|
||
+
|
||
+feature -- Encoding
|
||
+
|
||
+ digest (a_sha1: SHA1): STRING
|
||
+ -- Digest of `a_sha1'.
|
||
+ -- Should by in SHA1 class
|
||
+ local
|
||
+ l_digest: SPECIAL [NATURAL_8]
|
||
+ index, l_upper: INTEGER
|
||
+ do
|
||
+ l_digest := a_sha1.digest
|
||
+ create Result.make (l_digest.count // 2)
|
||
+ from
|
||
+ index := l_digest.Lower
|
||
+ l_upper := l_digest.upper
|
||
+ until
|
||
+ index > l_upper
|
||
+ loop
|
||
+ Result.append_character (l_digest [index].to_character_8)
|
||
+ index := index + 1
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Socket helpers
|
||
+
|
||
+ next_bytes (a_socket: HTTPD_STREAM_SOCKET; nb: INTEGER): STRING
|
||
+ require
|
||
+ nb > 0
|
||
+ local
|
||
+ n, l_bytes_read: INTEGER
|
||
+ do
|
||
+ create Result.make (nb)
|
||
+ from
|
||
+ n := nb
|
||
+ until
|
||
+ n = 0
|
||
+ loop
|
||
+ a_socket.read_stream (nb)
|
||
+ l_bytes_read := a_socket.bytes_read
|
||
+ if l_bytes_read > 0 then
|
||
+ Result.append (a_socket.last_string)
|
||
+ n := n - l_bytes_read
|
||
+ else
|
||
+ n := 0
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Masking Data Client - Server
|
||
+
|
||
+ unmask (a_chunk: STRING_8; a_pos: INTEGER; a_key: READABLE_STRING_8)
|
||
+ local
|
||
+ i, n: INTEGER
|
||
+ do
|
||
+ from
|
||
+ i := 1
|
||
+ n := a_chunk.count
|
||
+ until
|
||
+ i > n
|
||
+ loop
|
||
+ a_chunk.put_code (a_chunk.code (i).bit_xor (a_key [((i + (a_pos - 1) - 1) \\ 4) + 1].natural_32_code), i)
|
||
+ i := i + 1
|
||
+ end
|
||
+ end
|
||
+
|
||
+ append_chunk_unmasked (a_chunk: READABLE_STRING_8; a_pos: INTEGER; a_key: READABLE_STRING_8; a_target: STRING)
|
||
+ -- To convert masked data into unmasked data, or vice versa, the following
|
||
+ -- algorithm is applied. The same algorithm applies regardless of the
|
||
+ -- direction of the translation, e.g., the same steps are applied to
|
||
+ -- mask the data as to unmask the data.
|
||
+
|
||
+ -- Octet i of the transformed data ("transformed-octet-i") is the XOR of
|
||
+ -- octet i of the original data ("original-octet-i") with octet at index
|
||
+ -- i modulo 4 of the masking key ("masking-key-octet-j"):
|
||
+
|
||
+ -- j = i MOD 4
|
||
+ -- transformed-octet-i = original-octet-i XOR masking-key-octet-j
|
||
+
|
||
+ -- The payload length, indicated in the framing as frame-payload-length,
|
||
+ -- does NOT include the length of the masking key. It is the length of
|
||
+ -- the "Payload data", e.g., the number of bytes following the masking
|
||
+ -- key.
|
||
+ note
|
||
+ EIS: "name=Masking", "src=http://tools.ietf.org/html/rfc6455#section-5.3", "protocol=uri"
|
||
+ local
|
||
+ i, n: INTEGER
|
||
+ do
|
||
+ -- debug ("ws")
|
||
+ -- print ("append_chunk_unmasked (%"" + string_to_byte_representation (a_chunk) + "%",%N%Ta_pos=" + a_pos.out+ ", a_key, a_target #.count=" + a_target.count.out + ")%N")
|
||
+ -- end
|
||
+ from
|
||
+ i := 1
|
||
+ n := a_chunk.count
|
||
+ until
|
||
+ i > n
|
||
+ loop
|
||
+ a_target.append_code (a_chunk.code (i).bit_xor (a_key [((i + (a_pos - 1) - 1) \\ 4) + 1].natural_32_code))
|
||
+ i := i + 1
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Debug
|
||
+
|
||
+ to_byte_representation (a_integer: INTEGER): STRING
|
||
+ require
|
||
+ valid: a_integer >= 0 and then a_integer <= 255
|
||
+ local
|
||
+ l_val: INTEGER
|
||
+ do
|
||
+ create Result.make (8)
|
||
+ from
|
||
+ l_val := a_integer
|
||
+ until
|
||
+ l_val < 2
|
||
+ loop
|
||
+ Result.prepend_integer (l_val \\ 2)
|
||
+ l_val := l_val // 2
|
||
+ end
|
||
+ Result.prepend_integer (l_val)
|
||
+ end
|
||
+
|
||
+ string_to_byte_representation (s: STRING): STRING
|
||
+ require
|
||
+ valid: s.count > 0
|
||
+ local
|
||
+ i, n: INTEGER
|
||
+ do
|
||
+ n := s.count
|
||
+ create Result.make (8 * n)
|
||
+ if n > 0 then
|
||
+ from
|
||
+ i := 1
|
||
+ until
|
||
+ i > n
|
||
+ loop
|
||
+ if not Result.is_empty then
|
||
+ Result.append_character (':')
|
||
+ end
|
||
+ Result.append (to_byte_representation (s [i].code))
|
||
+ i := i + 1
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+ string_to_byte_hexa_representation (s: STRING): STRING
|
||
+ local
|
||
+ i, n: INTEGER
|
||
+ c: INTEGER
|
||
+ do
|
||
+ n := s.count
|
||
+ create Result.make (8 * n)
|
||
+ if n > 0 then
|
||
+ from
|
||
+ i := 1
|
||
+ until
|
||
+ i > n
|
||
+ loop
|
||
+ if not Result.is_empty then
|
||
+ Result.append_character (':')
|
||
+ end
|
||
+ c := s [i].code
|
||
+ check
|
||
+ c <= 0xFF
|
||
+ end
|
||
+ Result.append_character (((c |>> 4) & 0xF).to_hex_character)
|
||
+ Result.append_character (((c) & 0xF).to_hex_character)
|
||
+ i := i + 1
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+
|
||
+note
|
||
+ copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/websocket/web_socket_handler.e b/library/server/wsf/connector/standalone_websocket/websocket/web_socket_handler.e
|
||
new file mode 100644
|
||
index 0000000..5268c60
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/websocket/web_socket_handler.e
|
||
@@ -0,0 +1,152 @@
|
||
+note
|
||
+ description: "[
|
||
+ To implement websocket handling, provide a `callbacks` object implementing the {WEB_SOCKET_EVENT_I} interface.
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WEB_SOCKET_HANDLER
|
||
+
|
||
+inherit
|
||
+ WEB_SOCKET_CONSTANTS
|
||
+
|
||
+ REFACTORING_HELPER
|
||
+
|
||
+ HTTPD_LOGGER_CONSTANTS
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (ws: WEB_SOCKET; a_callbacks: WEB_SOCKET_EVENT_I)
|
||
+ do
|
||
+ web_socket := ws
|
||
+ callbacks := a_callbacks
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ web_socket: WEB_SOCKET
|
||
+ -- Associated websocket.
|
||
+
|
||
+ callbacks: WEB_SOCKET_EVENT_I
|
||
+
|
||
+feature -- Execution
|
||
+
|
||
+ frozen execute
|
||
+ do
|
||
+ callbacks.on_open (web_socket)
|
||
+ execute_websocket
|
||
+ end
|
||
+
|
||
+ execute_websocket
|
||
+ local
|
||
+ exit: BOOLEAN
|
||
+ l_frame: detachable WEB_SOCKET_FRAME
|
||
+ l_client_message: detachable READABLE_STRING_8
|
||
+ l_utf: UTF_CONVERTER
|
||
+ ws: like web_socket
|
||
+ s: STRING
|
||
+ do
|
||
+ from
|
||
+ -- loop until ws is closed or has error.
|
||
+ ws := web_socket
|
||
+ until
|
||
+ exit
|
||
+ loop
|
||
+ debug ("dbglog")
|
||
+ dbglog (generator + ".execute_websocket (loop) WS_REQUEST_HANDLER.process_request {" + ws.socket_descriptor.out + "}")
|
||
+ end
|
||
+ if ws.is_ready_for_reading then
|
||
+ l_frame := ws.next_frame
|
||
+ if l_frame /= Void and then l_frame.is_valid then
|
||
+ if attached l_frame.injected_control_frames as l_injections then
|
||
+ -- Process injected control frames now.
|
||
+ -- FIXME
|
||
+ across
|
||
+ l_injections as ic
|
||
+ loop
|
||
+ if ic.item.is_connection_close then
|
||
+ -- FIXME: we should probably send this event .. after the `l_frame.parent' frame event.
|
||
+ callbacks.on_event (ws, ic.item.payload_data, ic.item.opcode)
|
||
+ exit := True
|
||
+ elseif ic.item.is_ping then
|
||
+ -- FIXME reply only to the most recent ping ...
|
||
+ callbacks.on_event (ws, ic.item.payload_data, ic.item.opcode)
|
||
+ else
|
||
+ callbacks.on_event (ws, ic.item.payload_data, ic.item.opcode)
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+ l_client_message := l_frame.payload_data
|
||
+ if l_client_message = Void then
|
||
+ l_client_message := ""
|
||
+ end
|
||
+
|
||
+ debug ("ws")
|
||
+ create s.make_from_string ("%NExecute: %N")
|
||
+ s.append (" [opcode: "+ opcode_name (l_frame.opcode) +"]%N")
|
||
+ if l_frame.is_text then
|
||
+ s.append (" [client message: %""+ l_client_message +"%"]%N")
|
||
+ elseif l_frame.is_binary then
|
||
+ s.append (" [client binary message length: %""+ l_client_message.count.out +"%"]%N")
|
||
+ end
|
||
+ s.append (" [is_control: " + l_frame.is_control.out + "]%N")
|
||
+ s.append (" [is_binary: " + l_frame.is_binary.out + "]%N")
|
||
+ s.append (" [is_text: " + l_frame.is_text.out + "]%N")
|
||
+ dbglog (s)
|
||
+ end
|
||
+
|
||
+ if l_frame.is_connection_close then
|
||
+ callbacks.on_event (ws, l_client_message, l_frame.opcode)
|
||
+ exit := True
|
||
+ elseif l_frame.is_binary then
|
||
+ callbacks.on_event (ws, l_client_message, l_frame.opcode)
|
||
+ elseif l_frame.is_text then
|
||
+ check is_valid_utf_8: l_utf.is_valid_utf_8_string_8 (l_client_message) end
|
||
+ callbacks.on_event (ws, l_client_message, l_frame.opcode)
|
||
+ else
|
||
+ callbacks.on_event (ws, l_client_message, l_frame.opcode)
|
||
+ end
|
||
+ else
|
||
+ debug ("ws")
|
||
+ create s.make_from_string ("%NExecute: %N")
|
||
+ s.append (" [ERROR: invalid frame]%N")
|
||
+ if l_frame /= Void and then attached l_frame.error as err then
|
||
+ s.append (" [Code: "+ err.code.out +"]%N")
|
||
+ s.append (" [Description: "+ err.description +"]%N")
|
||
+ end
|
||
+ dbglog (s)
|
||
+ end
|
||
+ callbacks.on_event (ws, "", connection_close_frame)
|
||
+ exit := True -- FIXME: check proper close protocol
|
||
+ end
|
||
+ else
|
||
+ debug ("ws")
|
||
+ dbglog (generator + ".WAITING WS_REQUEST_HANDLER.process_request {" + ws.socket_descriptor.out + "}")
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Logging
|
||
+
|
||
+ dbglog (m: READABLE_STRING_8)
|
||
+ do
|
||
+ web_socket.log (m, debug_level)
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/wgi_standalone_websocket_connector.e b/library/server/wsf/connector/standalone_websocket/wgi_standalone_websocket_connector.e
|
||
new file mode 100644
|
||
index 0000000..1567520
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/wgi_standalone_websocket_connector.e
|
||
@@ -0,0 +1,44 @@
|
||
+note
|
||
+ description: "Summary description for {WGI_STANDALONE_WEBSOCKET_CONNECTOR}."
|
||
+ author: ""
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WGI_STANDALONE_WEBSOCKET_CONNECTOR [G -> WGI_EXECUTION create make end]
|
||
+
|
||
+inherit
|
||
+ WGI_STANDALONE_CONNECTOR [G]
|
||
+ redefine
|
||
+ name, version
|
||
+ end
|
||
+
|
||
+create
|
||
+ make,
|
||
+ make_with_base
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ name: STRING_8
|
||
+ -- Name of Current connector
|
||
+ once
|
||
+ Result := "ws_httpd"
|
||
+ end
|
||
+
|
||
+ version: STRING_8
|
||
+ -- Version of Current connector
|
||
+ once
|
||
+ Result := "1.0"
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/wsf_standalone_websocket_service_launcher.e b/library/server/wsf/connector/standalone_websocket/wsf_standalone_websocket_service_launcher.e
|
||
new file mode 100644
|
||
index 0000000..0a334fb
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/wsf_standalone_websocket_service_launcher.e
|
||
@@ -0,0 +1,49 @@
|
||
+note
|
||
+ description: "[
|
||
+ Component to launch the service using the default connector
|
||
+
|
||
+ Eiffel Web httpd for this class
|
||
+
|
||
+
|
||
+ The httpd default connector support options:
|
||
+ port: numeric such as 8099 (or equivalent string as "8099")
|
||
+ base: base_url (very specific to standalone server)
|
||
+ verbose: to display verbose output, useful for standalone connector
|
||
+ force_single_threaded: use only one thread, useful for standalone connector
|
||
+
|
||
+ check WSF_SERVICE_LAUNCHER for more documentation
|
||
+ ]"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WSF_STANDALONE_WEBSOCKET_SERVICE_LAUNCHER [G -> WSF_WEBSOCKET_EXECUTION create make end]
|
||
+
|
||
+inherit
|
||
+ WSF_STANDALONE_SERVICE_LAUNCHER [G]
|
||
+ redefine
|
||
+ connector
|
||
+ end
|
||
+
|
||
+create
|
||
+ make,
|
||
+ make_and_launch
|
||
+
|
||
+feature -- Status report
|
||
+
|
||
+ connector: WGI_STANDALONE_WEBSOCKET_CONNECTOR [G]
|
||
+ -- Default connector
|
||
+
|
||
+;note
|
||
+ copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+
|
||
+end
|
||
+
|
||
diff --git a/library/server/wsf/connector/standalone_websocket/wsf_websocket_execution.e b/library/server/wsf/connector/standalone_websocket/wsf_websocket_execution.e
|
||
new file mode 100644
|
||
index 0000000..78be024
|
||
--- /dev/null
|
||
+++ b/library/server/wsf/connector/standalone_websocket/wsf_websocket_execution.e
|
||
@@ -0,0 +1,79 @@
|
||
+note
|
||
+ description: "[
|
||
+ Request execution based on attributes `request' and `response'.
|
||
+ Also support Upgrade to Websocket protocol.
|
||
+
|
||
+
|
||
+ ]"
|
||
+ author: "$Author$"
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ WSF_WEBSOCKET_EXECUTION
|
||
+
|
||
+inherit
|
||
+ WSF_EXECUTION
|
||
+ rename
|
||
+ execute as http_execute
|
||
+ end
|
||
+
|
||
+--create
|
||
+-- make
|
||
+
|
||
+feature -- Execution
|
||
+
|
||
+ frozen http_execute
|
||
+ local
|
||
+ ws: WEB_SOCKET
|
||
+ ws_h: like new_websocket_handler
|
||
+ do
|
||
+ create ws.make (request, response)
|
||
+ ws.open_ws_handshake
|
||
+ if ws.is_websocket then
|
||
+ if ws.has_error then
|
||
+ -- Upgrade to websocket raised an error
|
||
+ -- stay on standard HTTP/1.1 protocol
|
||
+ execute
|
||
+ else
|
||
+ ws_h := new_websocket_handler (ws)
|
||
+ ws_h.execute
|
||
+ end
|
||
+ else
|
||
+ execute
|
||
+ end
|
||
+ end
|
||
+
|
||
+ execute
|
||
+ -- Execute Current request,
|
||
+ -- getting data from `request'
|
||
+ -- and response to client via `response'.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+feature -- Factory
|
||
+
|
||
+ new_websocket_handler (ws: WEB_SOCKET): WEB_SOCKET_HANDLER
|
||
+ -- Websocket request specific handler on socket `ws'.
|
||
+ --| For the creation, it requires an instance of `{WEB_SOCKET_EVENT_I}'
|
||
+ --| to receive the websocket events.
|
||
+ --| One can inherit from {WEB_SOCKET_EVENT_I} and implement the related
|
||
+ --| deferred features.
|
||
+ --| Or even provide a new class implementing {WEB_SOCKET_EVENT_I}.
|
||
+ require
|
||
+ is_websocket: ws.is_websocket
|
||
+ no_error: not ws.has_error
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+note
|
||
+ copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/library/server/wsf/package.iron b/library/server/wsf/package.iron
|
||
index 99d760f..f766b25 100644
|
||
--- a/library/server/wsf/package.iron
|
||
+++ b/library/server/wsf/package.iron
|
||
@@ -1,13 +1,3 @@
|
||
-note
|
||
- title: Web Server Foundation
|
||
- description: Core of the Eiffel Web Framework, used to build web server application.
|
||
- tags: ewf,server,httpd,request,connector
|
||
- license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
|
||
- link[license]: https://github.com/EiffelWebFramework/EWF/blob/master/LICENSE
|
||
- link[source]: "Github" https://github.com/EiffelWebFramework/EWF
|
||
- link[doc]: "Documentation" http://eiffelwebframework.github.io/EWF/
|
||
-
|
||
-end
|
||
|
||
package wsf
|
||
|
||
@@ -37,6 +27,7 @@ project
|
||
default_standalone = "default/standalone-safe.ecf"
|
||
default_standalone = "default/standalone.ecf"
|
||
|
||
+
|
||
note
|
||
title: Web Server Foundation
|
||
description: "[
|
||
diff --git a/library/server/wsf/src/service/wsf_service_launcher.e b/library/server/wsf/src/service/wsf_service_launcher.e
|
||
index 2568a5e..86c41a7 100644
|
||
--- a/library/server/wsf/src/service/wsf_service_launcher.e
|
||
+++ b/library/server/wsf/src/service/wsf_service_launcher.e
|
||
@@ -22,11 +22,11 @@ note
|
||
For instance, you can use
|
||
create s.make_and_launch_and_options (agent execute, <<["port", 8099]>>)
|
||
|
||
- And if Nino is the default connector it will support:
|
||
+ And if the connector is the Standalone connector,
|
||
+ check {WSF_STANDALONE_SERVICE_LAUNCHER} for options description, such as:
|
||
port: numeric such as 8099 (or equivalent string as "8099")
|
||
base: base_url (very specific to standalone server)
|
||
- force_single_threaded: use only one thread, useful for Nino
|
||
- verbose: to display verbose output, useful for Nino
|
||
+ verbose: to display verbose output.
|
||
]"
|
||
date: "$Date$"
|
||
revision: "$Revision$"
|
||
diff --git a/library/server/wsf/src/service/wsf_service_launcher_options.e b/library/server/wsf/src/service/wsf_service_launcher_options.e
|
||
index fd690f8..1394c17 100644
|
||
--- a/library/server/wsf/src/service/wsf_service_launcher_options.e
|
||
+++ b/library/server/wsf/src/service/wsf_service_launcher_options.e
|
||
@@ -8,8 +8,8 @@ note
|
||
force_single_threaded: use only one thread, useful for Nino
|
||
verbose: to display verbose output, useful for Nino
|
||
]"
|
||
- date: "$Date$"
|
||
- revision: "$Revision$"
|
||
+ date: "$Date: 2016-08-06 13:34:52 +0200 (sam., 06 août 2016) $"
|
||
+ revision: "$Revision: 99106 $"
|
||
|
||
class
|
||
WSF_SERVICE_LAUNCHER_OPTIONS
|
||
@@ -83,6 +83,86 @@ feature -- Access
|
||
Result := options.item (a_name)
|
||
end
|
||
|
||
+feature -- Helpers
|
||
+
|
||
+ has_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
|
||
+ -- Is there any value associated to option name `a_opt_name'?
|
||
+ do
|
||
+ Result := attached option (a_opt_name)
|
||
+ end
|
||
+
|
||
+ has_integer_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
|
||
+ -- Is there any INTEGER value associated to option name `a_opt_name'?
|
||
+ local
|
||
+ s: READABLE_STRING_GENERAL
|
||
+ do
|
||
+ if attached option (a_opt_name) as opt then
|
||
+ if attached {INTEGER} opt as i then
|
||
+ Result := True
|
||
+ else
|
||
+ s := opt.out
|
||
+ Result := s.is_integer
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+ has_string_32_option (a_opt_name: READABLE_STRING_GENERAL): BOOLEAN
|
||
+ -- Is there any string 32 value associated to option name `a_opt_name'?
|
||
+ do
|
||
+ if attached option (a_opt_name) as opt then
|
||
+ Result := attached {READABLE_STRING_GENERAL} opt
|
||
+ end
|
||
+ end
|
||
+
|
||
+ option_string_32_value (a_opt_name: READABLE_STRING_GENERAL; a_default: detachable READABLE_STRING_GENERAL): detachable IMMUTABLE_STRING_32
|
||
+ -- Unicode String value associated to option name `a_opt_name', other return `a_default'.
|
||
+ do
|
||
+ if attached option (a_opt_name) as opt then
|
||
+ if attached {READABLE_STRING_32} opt as s32 then
|
||
+ create Result.make_from_string (s32)
|
||
+ elseif attached {READABLE_STRING_GENERAL} opt as s then
|
||
+ create Result.make_from_string_general (s)
|
||
+ end
|
||
+ end
|
||
+ if Result = Void and a_default /= Void then
|
||
+ create Result.make_from_string_general (a_default)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ option_integer_value (a_opt_name: READABLE_STRING_GENERAL; a_default: INTEGER): INTEGER
|
||
+ -- INTEGER 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 {INTEGER} opt as i then
|
||
+ Result := i
|
||
+ else
|
||
+ s := opt.out
|
||
+ if s.is_integer then
|
||
+ Result := s.to_integer
|
||
+ end
|
||
+ 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
|
||
+ s: READABLE_STRING_GENERAL
|
||
+ do
|
||
+ Result := a_default
|
||
+ if attached option (a_opt_name) as opt then
|
||
+ if attached {BOOLEAN} opt as b then
|
||
+ Result := b
|
||
+ else
|
||
+ s := opt.out
|
||
+ Result := s.is_case_insensitive_equal ("true")
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
feature -- Access
|
||
|
||
new_cursor: TABLE_ITERATION_CURSOR [detachable ANY, READABLE_STRING_GENERAL]
|
||
diff --git a/library/server/wsf/src/wsf_execution.e b/library/server/wsf/src/wsf_execution.e
|
||
index 8bc0a97..352207f 100644
|
||
--- a/library/server/wsf/src/wsf_execution.e
|
||
+++ b/library/server/wsf/src/wsf_execution.e
|
||
@@ -14,7 +14,6 @@ inherit
|
||
make_from_execution as make_from_wgi_execution
|
||
redefine
|
||
make,
|
||
- execute,
|
||
clean,
|
||
is_valid_end_of_execution
|
||
end
|
||
diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e
|
||
index 055e35b..00708aa 100644
|
||
--- a/library/server/wsf/src/wsf_request.e
|
||
+++ b/library/server/wsf/src/wsf_request.e
|
||
@@ -261,10 +261,12 @@ feature -- Access: Input
|
||
local
|
||
l_input: WGI_INPUT_STREAM
|
||
n: INTEGER
|
||
+ buf_initial_size: INTEGER
|
||
do
|
||
if raw_input_data_recorded and then attached raw_input_data as d then
|
||
buf.append (d)
|
||
else
|
||
+ buf_initial_size := buf.count
|
||
l_input := input
|
||
if is_chunked_input then
|
||
from
|
||
@@ -286,73 +288,53 @@ feature -- Access: Input
|
||
end
|
||
end
|
||
if raw_input_data_recorded then
|
||
- set_raw_input_data (buf)
|
||
+ set_raw_input_data (buf.substring (buf_initial_size + 1, buf.count))
|
||
+ -- Only the input data! And differente reference.
|
||
end
|
||
end
|
||
end
|
||
|
||
- read_input_data_into_file (a_file: FILE)
|
||
+ read_input_data_into_file (a_medium: IO_MEDIUM)
|
||
-- retrieve the content from the `input' stream into `s'
|
||
-- warning: if the input data has already been retrieved
|
||
-- you might not get anything
|
||
require
|
||
- a_file_is_open_write: a_file.is_open_write
|
||
+ a_medium_is_open_write: a_medium.is_open_write
|
||
local
|
||
s: STRING
|
||
l_input: WGI_INPUT_STREAM
|
||
l_raw_data: detachable STRING_8
|
||
- len: NATURAL_64
|
||
nb, l_step: INTEGER
|
||
- l_size: NATURAL_64
|
||
do
|
||
if raw_input_data_recorded and then attached raw_input_data as d then
|
||
- a_file.put_string (d)
|
||
+ a_medium.put_string (d)
|
||
else
|
||
if raw_input_data_recorded then
|
||
create l_raw_data.make_empty
|
||
end
|
||
l_input := input
|
||
- len := content_length_value
|
||
-
|
||
- debug ("wsf")
|
||
- io.error.put_string (generator + ".read_input_data_into_file (a_file) content_length=" + len.out + "%N")
|
||
- end
|
||
|
||
from
|
||
- l_size := 0
|
||
l_step := 8_192
|
||
create s.make (l_step)
|
||
until
|
||
l_step = 0 or l_input.end_of_input
|
||
loop
|
||
- if len < l_step.to_natural_64 then
|
||
- l_step := len.to_integer_32
|
||
- end
|
||
- if l_step > 0 then
|
||
- l_input.append_to_string (s, l_step)
|
||
- nb := l_input.last_appended_count
|
||
- l_size := l_size + nb.to_natural_64
|
||
- len := len - nb.to_natural_64
|
||
-
|
||
- debug ("wsf")
|
||
- io.error.put_string (" append (s, " + l_step.out + ") -> " + nb.out + " (" + l_size.out + " / "+ content_length_value.out + ")%N")
|
||
- end
|
||
+ l_input.append_to_string (s, l_step)
|
||
+ nb := l_input.last_appended_count
|
||
|
||
- a_file.put_string (s)
|
||
- if l_raw_data /= Void then
|
||
- l_raw_data.append (s)
|
||
- end
|
||
- s.wipe_out
|
||
- if nb < l_step then
|
||
- l_step := 0
|
||
- end
|
||
+ a_medium.put_string (s)
|
||
+ if l_raw_data /= Void then
|
||
+ l_raw_data.append (s)
|
||
+ end
|
||
+ s.wipe_out
|
||
+ if nb < l_step then
|
||
+ l_step := 0
|
||
end
|
||
end
|
||
- a_file.flush
|
||
- debug ("wsf")
|
||
- io.error.put_string ("offset =" + len.out + "%N")
|
||
+ if attached {FILE} a_medium as f then
|
||
+ f.flush
|
||
end
|
||
- check got_all_data: len = 0 end
|
||
if l_raw_data /= Void then
|
||
set_raw_input_data (l_raw_data)
|
||
end
|
||
diff --git a/library/server/wsf/tests/src/wsf_request_null.e b/library/server/wsf/tests/src/wsf_request_null.e
|
||
index 3d6bbea..960cbed 100644
|
||
--- a/library/server/wsf/tests/src/wsf_request_null.e
|
||
+++ b/library/server/wsf/tests/src/wsf_request_null.e
|
||
@@ -10,7 +10,7 @@ class
|
||
inherit
|
||
WSF_REQUEST
|
||
|
||
-create {WSF_TO_WGI_SERVICE, WSF_SERVICE}
|
||
+create {WSF_SERVICE}
|
||
make_from_wgi
|
||
|
||
end
|
||
diff --git a/library/server/wsf_proxy/network/no_ssl/wsf_proxy_socket_factory.e b/library/server/wsf_proxy/network/no_ssl/wsf_proxy_socket_factory.e
|
||
new file mode 100644
|
||
index 0000000..21c2e7e
|
||
--- /dev/null
|
||
+++ b/library/server/wsf_proxy/network/no_ssl/wsf_proxy_socket_factory.e
|
||
@@ -0,0 +1,20 @@
|
||
+note
|
||
+ description: "Summary description for {WSF_PROXY_SOCKET_FACTORY}."
|
||
+ author: ""
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WSF_PROXY_SOCKET_FACTORY
|
||
+
|
||
+inherit
|
||
+ WSF_PROXY_SOCKET_FACTORY_I
|
||
+
|
||
+feature {NONE} -- Implementation
|
||
+
|
||
+ ssl_socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable NETWORK_STREAM_SOCKET
|
||
+ do
|
||
+ check supported: False end
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/wsf_proxy/network/ssl/wsf_proxy_socket_factory.e b/library/server/wsf_proxy/network/ssl/wsf_proxy_socket_factory.e
|
||
new file mode 100644
|
||
index 0000000..ad26490
|
||
--- /dev/null
|
||
+++ b/library/server/wsf_proxy/network/ssl/wsf_proxy_socket_factory.e
|
||
@@ -0,0 +1,30 @@
|
||
+note
|
||
+ description: "Summary description for {WSF_PROXY_SOCKET_FACTORY}."
|
||
+ author: ""
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WSF_PROXY_SOCKET_FACTORY
|
||
+
|
||
+inherit
|
||
+ WSF_PROXY_SOCKET_FACTORY_I
|
||
+ redefine
|
||
+ is_ssl_supported
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Implementation
|
||
+
|
||
+ ssl_socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable SSL_NETWORK_STREAM_SOCKET
|
||
+ do
|
||
+ if attached create_from_name (a_host) as l_peer_address then
|
||
+ create Result.make_client_by_address_and_port (l_peer_address, a_port)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Status
|
||
+
|
||
+ is_ssl_supported: BOOLEAN = True
|
||
+ -- Is https:// supported?
|
||
+
|
||
+end
|
||
diff --git a/library/server/wsf_proxy/network/wsf_proxy_socket_factory_i.e b/library/server/wsf_proxy/network/wsf_proxy_socket_factory_i.e
|
||
new file mode 100644
|
||
index 0000000..900db00
|
||
--- /dev/null
|
||
+++ b/library/server/wsf_proxy/network/wsf_proxy_socket_factory_i.e
|
||
@@ -0,0 +1,67 @@
|
||
+note
|
||
+ description: "Summary description for {WSF_PROXY_SOCKET_FACTORY_I}."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ WSF_PROXY_SOCKET_FACTORY_I
|
||
+
|
||
+inherit
|
||
+ INET_ADDRESS_FACTORY
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ socket_from_uri (a_uri: URI): like socket
|
||
+ local
|
||
+ l_port: INTEGER
|
||
+ do
|
||
+ if a_uri.is_valid and then attached a_uri.host as l_host then
|
||
+ l_port := a_uri.port
|
||
+ if a_uri.scheme.is_case_insensitive_equal_general ("https") then
|
||
+ if is_ssl_supported then
|
||
+ if l_port <= 0 then
|
||
+ l_port := 443
|
||
+ end
|
||
+ Result := ssl_socket (l_host, l_port)
|
||
+ end
|
||
+ elseif a_uri.scheme.is_case_insensitive_equal_general ("http") then
|
||
+ if l_port <= 0 then
|
||
+ l_port := 80
|
||
+ end
|
||
+ Result := socket (l_host, l_port)
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Status
|
||
+
|
||
+ is_uri_supported (a_uri: URI): BOOLEAN
|
||
+ do
|
||
+ Result := a_uri.scheme.is_case_insensitive_equal_general ("http")
|
||
+ or else (
|
||
+ a_uri.scheme.is_case_insensitive_equal_general ("https")
|
||
+ and is_ssl_supported
|
||
+ )
|
||
+ end
|
||
+
|
||
+ is_ssl_supported: BOOLEAN
|
||
+ -- Is https:// supported?
|
||
+ do
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Implementation
|
||
+
|
||
+ socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable NETWORK_STREAM_SOCKET
|
||
+ do
|
||
+ if attached create_from_name (a_host) as l_peer_address then
|
||
+ create Result.make_client_by_address_and_port (l_peer_address, a_port)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ ssl_socket (a_host: READABLE_STRING_8; a_port: INTEGER): detachable NETWORK_STREAM_SOCKET
|
||
+ require
|
||
+ is_ssl_supported: is_ssl_supported
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/wsf_proxy/reverse_proxy/wsf_simple_reverse_proxy_handler.e b/library/server/wsf_proxy/reverse_proxy/wsf_simple_reverse_proxy_handler.e
|
||
new file mode 100644
|
||
index 0000000..a0ab3c6
|
||
--- /dev/null
|
||
+++ b/library/server/wsf_proxy/reverse_proxy/wsf_simple_reverse_proxy_handler.e
|
||
@@ -0,0 +1,303 @@
|
||
+note
|
||
+ description: "Summary description for {WSF_SIMPLE_REVERSE_PROXY_HANDLER}."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WSF_SIMPLE_REVERSE_PROXY_HANDLER
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (a_remote_uri: READABLE_STRING_8)
|
||
+ do
|
||
+ create remote_uri.make_from_string (a_remote_uri)
|
||
+ timeout := 30 -- seconds. See {NETWORK_SOCKET}.default_timeout
|
||
+ connect_timeout := 5_000 -- 5 seconds.
|
||
+ is_via_header_supported := True
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ remote_uri: URI
|
||
+ -- Url for the targetted service.
|
||
+
|
||
+ uri_rewriter: detachable WSF_URI_REWRITER assign set_uri_rewriter
|
||
+ -- URI rewriter component, to compute the URI on targetted service
|
||
+ -- based on current request.
|
||
+
|
||
+feature -- Settings
|
||
+
|
||
+ connect_timeout: INTEGER assign set_connect_timeout
|
||
+ -- In milliseconds.
|
||
+
|
||
+ timeout: INTEGER assign set_timeout
|
||
+ -- In seconds.
|
||
+
|
||
+ is_via_header_supported: BOOLEAN
|
||
+ -- Via: header supported.
|
||
+ -- Default: True.
|
||
+
|
||
+feature -- Change
|
||
+
|
||
+ set_uri_rewriter (a_rewriter: like uri_rewriter)
|
||
+ do
|
||
+ uri_rewriter := a_rewriter
|
||
+ end
|
||
+
|
||
+ set_timeout (a_timeout_in_seconds: INTEGER)
|
||
+ -- in seconds.
|
||
+ do
|
||
+ timeout := a_timeout_in_seconds
|
||
+ end
|
||
+
|
||
+ set_connect_timeout (a_timeout_in_milliseconds: INTEGER)
|
||
+ -- in milliseconds.
|
||
+ do
|
||
+ connect_timeout := a_timeout_in_milliseconds
|
||
+ end
|
||
+
|
||
+ set_is_via_header_supported (b: BOOLEAN)
|
||
+ -- Set `is_via_header_supported' to `b'.
|
||
+ do
|
||
+ is_via_header_supported := b
|
||
+ end
|
||
+
|
||
+feature -- Execution
|
||
+
|
||
+ proxy_uri (request: WSF_REQUEST): STRING
|
||
+ -- URI to query on proxyfied host.
|
||
+ do
|
||
+ if attached uri_rewriter as r then
|
||
+ Result := r.uri (request)
|
||
+ else
|
||
+ Result := request.request_uri
|
||
+ end
|
||
+ end
|
||
+
|
||
+ execute (request: WSF_REQUEST; response: WSF_RESPONSE)
|
||
+ -- Execute reverse proxy request.
|
||
+ local
|
||
+ h: HTTP_HEADER
|
||
+ l_http_query: STRING
|
||
+ l_status_line: STRING
|
||
+ l_max_forward: INTEGER
|
||
+ l_via: detachable STRING
|
||
+ l_protocol: STRING
|
||
+ i: INTEGER
|
||
+ l_completed: BOOLEAN
|
||
+ l_remote_uri: like remote_uri
|
||
+ l_socket_factory: WSF_PROXY_SOCKET_FACTORY
|
||
+ do
|
||
+ l_remote_uri := remote_uri
|
||
+ create l_socket_factory
|
||
+ if not l_socket_factory.is_uri_supported (l_remote_uri) then
|
||
+ send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, l_remote_uri.scheme + " is not supported! [for remote " + l_remote_uri.string + "]")
|
||
+ elseif attached l_socket_factory.socket_from_uri (l_remote_uri) as l_socket then
|
||
+ l_socket.set_connect_timeout (connect_timeout) -- milliseconds
|
||
+ l_socket.set_timeout (timeout) -- seconds
|
||
+
|
||
+ l_socket.connect
|
||
+ if l_socket.is_connected then
|
||
+ create l_http_query.make_from_string (request.request_method)
|
||
+ l_http_query.append_character (' ')
|
||
+ l_http_query.append (l_remote_uri.path)
|
||
+ l_http_query.append (proxy_uri (request))
|
||
+ l_http_query.append_character (' ')
|
||
+ l_http_query.append (request.server_protocol)
|
||
+ if attached request.raw_header_data as l_raw_header then
|
||
+ 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))
|
||
+ else
|
||
+ create h.make_from_raw_header_data (l_raw_header)
|
||
+ end
|
||
+ if attached l_remote_uri.host as l_remote_host then
|
||
+ if l_remote_uri.port > 0 then
|
||
+ h.put_header_key_value ("Host", l_remote_host + ":" + l_remote_uri.port.out)
|
||
+ else
|
||
+ h.put_header_key_value ("Host", l_remote_host)
|
||
+ end
|
||
+ end
|
||
+
|
||
+ -- Via header
|
||
+ if is_via_header_supported then
|
||
+ if attached h.item ("Via") as v then
|
||
+ l_via := v
|
||
+ l_via.append (", ")
|
||
+ else
|
||
+ create l_via.make_empty
|
||
+ end
|
||
+ l_via.append (request.server_protocol + " " + request.server_name + " (PROXY-" + request.server_software + ")")
|
||
+ h.put_header_key_value ("Via", l_via)
|
||
+ end
|
||
+
|
||
+ -- Max-Forwards header handling
|
||
+ if attached h.item ("Max-Forwards") as h_max_forward then
|
||
+ -- Max-Forwards: 0 stop, otherwise decrement by one.
|
||
+ -- see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.31
|
||
+ if h_max_forward.is_integer then
|
||
+ l_max_forward := h_max_forward.to_integer - 1
|
||
+ if l_max_forward >= 0 then
|
||
+ h.put_header_key_value ("Max-Forwards", l_max_forward.out)
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ if l_max_forward < 0 then
|
||
+ -- i.e previous Max-Forwards was '0'
|
||
+ send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, "Reached maximum number of Forwards, not forwarded to " + l_remote_uri.string)
|
||
+ else
|
||
+ l_socket.put_string (l_http_query)
|
||
+ l_socket.put_string ("%R%N")
|
||
+ l_socket.put_string (h.string)
|
||
+ l_socket.put_string ("%R%N")
|
||
+ if request.content_length_value > 0 then
|
||
+ request.read_input_data_into_file (l_socket)
|
||
+ end
|
||
+
|
||
+ -- Get HTTP status
|
||
+ l_socket.read_line_thread_aware
|
||
+ create l_status_line.make_from_string (l_socket.last_string)
|
||
+ -- Get HTTP header block
|
||
+ if attached next_http_header_block (l_socket) as l_resp_header then
|
||
+ create h.make_from_raw_header_data (l_resp_header)
|
||
+ if attached status_line_info (l_status_line) as l_status_info then
|
||
+ l_protocol := l_status_info.protocol
|
||
+ if attached l_status_info.reason_phrase as l_phrase then
|
||
+ response.set_status_code_with_reason_phrase (l_status_info.status_code, l_phrase)
|
||
+ else
|
||
+ response.set_status_code (l_status_info.status_code)
|
||
+ end
|
||
+ else
|
||
+ check has_status_line: False end
|
||
+ l_protocol := "1.0" -- Default?
|
||
+ response.set_status_code (80)
|
||
+ end
|
||
+
|
||
+ if is_via_header_supported then
|
||
+ if attached h.item ("Via") as v then
|
||
+ l_via := v
|
||
+ l_via.append (", ")
|
||
+ else
|
||
+ create l_via.make_empty
|
||
+ end
|
||
+ l_via.append (l_protocol + " " + request.server_name + " (PROXY-" + request.server_software + ")")
|
||
+ h.put_header_key_value ("Via", l_via)
|
||
+ end
|
||
+
|
||
+ response.add_header_lines (h)
|
||
+ from
|
||
+ l_socket.read_stream (2_048)
|
||
+ until
|
||
+ l_socket.was_error
|
||
+ or not l_socket.is_connected
|
||
+ or l_socket.bytes_read <= 0
|
||
+ or l_completed
|
||
+ loop
|
||
+ response.put_string (l_socket.last_string)
|
||
+ if l_socket.bytes_read = 2_048 then
|
||
+ l_socket.read_stream (2_048)
|
||
+ else
|
||
+ l_completed := True
|
||
+ end
|
||
+ end
|
||
+ else
|
||
+ send_error (request, response, {HTTP_STATUS_CODE}.internal_server_error, "Invalid response header!")
|
||
+ end
|
||
+ end
|
||
+ else
|
||
+ send_error (request, response, {HTTP_STATUS_CODE}.internal_server_error, "Can not access request header!")
|
||
+ end
|
||
+ else
|
||
+ send_error (request, response, {HTTP_STATUS_CODE}.gateway_timeout, "Unable to connect " + l_remote_uri.string)
|
||
+ end
|
||
+ else
|
||
+ send_error (request, response, {HTTP_STATUS_CODE}.bad_gateway, "Unable to connect " + l_remote_uri.string)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature {NONE} -- Implementation
|
||
+
|
||
+ status_line_info (a_line: READABLE_STRING_8): detachable TUPLE [protocol: READABLE_STRING_8; status_code: INTEGER; reason_phrase: detachable READABLE_STRING_8]
|
||
+ -- Info from status line
|
||
+ --| Such as "HTTP/1.1 200 OK" -> ["1.1", 200, "OK"]
|
||
+ local
|
||
+ i,j: INTEGER
|
||
+ p,s: detachable READABLE_STRING_8
|
||
+ c: INTEGER
|
||
+ do
|
||
+ i := a_line.index_of (' ', 1)
|
||
+ if i > 0 then
|
||
+ p := a_line.substring (1, i - 1)
|
||
+ if p.starts_with_general ("HTTP/") then
|
||
+ p := p.substring (6, p.count) -- We could also keep HTTP/
|
||
+ end
|
||
+ j := i + 1
|
||
+ i := a_line.index_of (' ', j)
|
||
+ if i > 0 then
|
||
+ s := a_line.substring (j, i - 1)
|
||
+ if s.is_integer then
|
||
+ c := s.to_integer
|
||
+ s := a_line.substring (i + 1, a_line.count)
|
||
+ if s.is_whitespace then
|
||
+ s := Void
|
||
+ elseif s[s.count].is_space then
|
||
+ s := s.substring (1, s.count - 1)
|
||
+ end
|
||
+ Result := [p, c, s]
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+ next_http_header_block (a_socket: NETWORK_STREAM_SOCKET): detachable STRING
|
||
+ local
|
||
+ h: STRING
|
||
+ do
|
||
+ create h.make_empty
|
||
+ from
|
||
+ a_socket.read_line_thread_aware
|
||
+ until
|
||
+ Result /= Void
|
||
+ or a_socket.was_error
|
||
+ or (a_socket.bytes_read = 0 or a_socket.bytes_read = -1)
|
||
+ or not a_socket.is_connected
|
||
+ loop
|
||
+ if a_socket.last_string.same_string ("%R") then
|
||
+ -- End of header
|
||
+ Result := h
|
||
+ else
|
||
+ h.append (a_socket.last_string)
|
||
+ h.append ("%N")
|
||
+ a_socket.read_line_thread_aware
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+
|
||
+ send_error (request: WSF_REQUEST; response: WSF_RESPONSE; a_status_code: INTEGER; a_message: READABLE_STRING_8)
|
||
+ local
|
||
+ s: STRING
|
||
+ do
|
||
+ -- To send a response we need to setup, the status code and
|
||
+ -- the response headers.
|
||
+ create s.make_from_string (a_message)
|
||
+ debug
|
||
+ s.append ("%N(UTC time is " + (create {HTTP_DATE}.make_now_utc).rfc850_string + ").%N")
|
||
+ end
|
||
+ response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "plain/text"], ["Content-Length", s.count.out]>>)
|
||
+ response.set_status_code (a_status_code)
|
||
+ response.header.put_content_type_text_html
|
||
+ response.header.put_content_length (s.count)
|
||
+ if
|
||
+ attached request.http_connection as l_connection and then
|
||
+ l_connection.is_case_insensitive_equal_general ("keep-alive")
|
||
+ then
|
||
+ response.header.put_header_key_value ("Connection", "keep-alive")
|
||
+ end
|
||
+ response.put_string (s)
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/wsf_proxy/rewriter/wsf_agent_uri_rewriter.e b/library/server/wsf_proxy/rewriter/wsf_agent_uri_rewriter.e
|
||
new file mode 100644
|
||
index 0000000..fc054b3
|
||
--- /dev/null
|
||
+++ b/library/server/wsf_proxy/rewriter/wsf_agent_uri_rewriter.e
|
||
@@ -0,0 +1,38 @@
|
||
+note
|
||
+ description: "Summary description for {WSF_AGENT_URI_REWRITER}."
|
||
+ author: ""
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+class
|
||
+ WSF_AGENT_URI_REWRITER
|
||
+
|
||
+inherit
|
||
+ WSF_URI_REWRITER
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+--convert
|
||
+-- make ({FUNCTION [TUPLE [WSF_REQUEST], STRING]})
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ make (a_rewriter_function: like rewriter_function)
|
||
+ do
|
||
+ rewriter_function := a_rewriter_function
|
||
+ end
|
||
+
|
||
+feature -- Access
|
||
+
|
||
+ rewriter_function: FUNCTION [TUPLE [WSF_REQUEST], STRING]
|
||
+
|
||
+feature -- Conversion
|
||
+
|
||
+ uri (a_request: WSF_REQUEST): STRING
|
||
+ -- <Precursor>.
|
||
+ do
|
||
+ Result := rewriter_function (a_request)
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/wsf_proxy/rewriter/wsf_uri_rewriter.e b/library/server/wsf_proxy/rewriter/wsf_uri_rewriter.e
|
||
new file mode 100644
|
||
index 0000000..6cbc1ca
|
||
--- /dev/null
|
||
+++ b/library/server/wsf_proxy/rewriter/wsf_uri_rewriter.e
|
||
@@ -0,0 +1,16 @@
|
||
+note
|
||
+ description: "Summary description for {WSF_URI_REWRITER}."
|
||
+ date: "$Date$"
|
||
+ revision: "$Revision$"
|
||
+
|
||
+deferred class
|
||
+ WSF_URI_REWRITER
|
||
+
|
||
+feature -- Conversion
|
||
+
|
||
+ uri (a_request: WSF_REQUEST): STRING
|
||
+ -- Rewritten request uri based on `a_request'.
|
||
+ deferred
|
||
+ end
|
||
+
|
||
+end
|
||
diff --git a/library/server/wsf_proxy/wsf_proxy-safe.ecf b/library/server/wsf_proxy/wsf_proxy-safe.ecf
|
||
new file mode 100644
|
||
index 0000000..9dd777c
|
||
--- /dev/null
|
||
+++ b/library/server/wsf_proxy/wsf_proxy-safe.ecf
|
||
@@ -0,0 +1,34 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="wsf_proxy" uuid="A39CCC27-BF63-4959-B881-7D0713F4C84A" library_target="wsf_proxy">
|
||
+ <target name="wsf_proxy">
|
||
+ <root all_classes="true"/>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
+ <library name="http" location="..\..\..\library\network\protocol\http\http-safe.ecf"/>
|
||
+ <library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||
+ <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
|
||
+ <condition>
|
||
+ <custom name="ssl_supported" value="true"/>
|
||
+ </condition>
|
||
+ </library>
|
||
+ <library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||
+ <library name="wsf" location="..\wsf\wsf-safe.ecf"/>
|
||
+ <cluster name="network" location=".\network\" recursive="true">
|
||
+ <file_rule>
|
||
+ <exclude>no_ssl</exclude>
|
||
+ <exclude>ssl</exclude>
|
||
+ </file_rule>
|
||
+ <cluster name="network_ssl" location="$|ssl\">
|
||
+ <condition>
|
||
+ <custom name="ssl_supported" value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_no_ssl" location="$|no_ssl\">
|
||
+ <condition>
|
||
+ <custom name="ssl_supported" excluded_value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ </cluster>
|
||
+ <cluster name="reverse_proxy" location=".\reverse_proxy\" recursive="true"/>
|
||
+ <cluster name="rewriter" location=".\rewriter\" recursive="true"/>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/library/server/wsf_proxy/wsf_proxy.ecf b/library/server/wsf_proxy/wsf_proxy.ecf
|
||
new file mode 100644
|
||
index 0000000..17ffa57
|
||
--- /dev/null
|
||
+++ b/library/server/wsf_proxy/wsf_proxy.ecf
|
||
@@ -0,0 +1,36 @@
|
||
+<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
+<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="wsf_proxy" uuid="A39CCC27-BF63-4959-B881-7D0713F4C84A" library_target="wsf_proxy">
|
||
+ <target name="wsf_proxy">
|
||
+ <root all_classes="true"/>
|
||
+ <option void_safety="none">
|
||
+ </option>
|
||
+ <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||
+ <library name="http" location="..\..\..\library\network\protocol\http\http.ecf"/>
|
||
+ <library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||
+ <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||
+ <condition>
|
||
+ <custom name="ssl_supported" value="true"/>
|
||
+ </condition>
|
||
+ </library>
|
||
+ <library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||
+ <library name="wsf" location="..\wsf\wsf.ecf"/>
|
||
+ <cluster name="network" location=".\network\" recursive="true">
|
||
+ <file_rule>
|
||
+ <exclude>no_ssl</exclude>
|
||
+ <exclude>ssl</exclude>
|
||
+ </file_rule>
|
||
+ <cluster name="network_ssl" location="$|ssl\">
|
||
+ <condition>
|
||
+ <custom name="ssl_supported" value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ <cluster name="network_no_ssl" location="$|no_ssl\">
|
||
+ <condition>
|
||
+ <custom name="ssl_supported" excluded_value="true"/>
|
||
+ </condition>
|
||
+ </cluster>
|
||
+ </cluster>
|
||
+ <cluster name="reverse_proxy" location=".\reverse_proxy\" recursive="true"/>
|
||
+ <cluster name="rewriter" location=".\rewriter\" recursive="true"/>
|
||
+ </target>
|
||
+</system>
|
||
diff --git a/library/text/parser/feed/feed-safe.ecf b/library/text/parser/feed/feed-safe.ecf
|
||
index 3c5a7dd..48591ec 100644
|
||
--- a/library/text/parser/feed/feed-safe.ecf
|
||
+++ b/library/text/parser/feed/feed-safe.ecf
|
||
@@ -5,7 +5,7 @@
|
||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
|
||
<library name="encoders" location="..\..\encoder\encoder-safe.ecf"/>
|
||
- <library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||
+ <library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
|
||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||
<library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid-safe.ecf"/>
|
||
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser-safe.ecf"/>
|
||
diff --git a/library/text/parser/feed/feed.ecf b/library/text/parser/feed/feed.ecf
|
||
index a69dd4e..9d4031e 100644
|
||
--- a/library/text/parser/feed/feed.ecf
|
||
+++ b/library/text/parser/feed/feed.ecf
|
||
@@ -7,7 +7,7 @@
|
||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
|
||
<library name="encoders" location="..\..\encoder\encoder.ecf"/>
|
||
- <library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
|
||
+ <library name="http" location="..\..\..\network\protocol\http\http.ecf"/>
|
||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||
<library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid.ecf"/>
|
||
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser.ecf"/>
|
||
diff --git a/library/text/parser/feed/src/atom/atom_feed_generator.e b/library/text/parser/feed/src/atom/atom_feed_generator.e
|
||
index 4f15a22..f4d8791 100644
|
||
--- a/library/text/parser/feed/src/atom/atom_feed_generator.e
|
||
+++ b/library/text/parser/feed/src/atom/atom_feed_generator.e
|
||
@@ -72,6 +72,13 @@ feature -- Visitor
|
||
if attached a_entry.date as dt then
|
||
append_content_tag_to ("updated", Void, date_to_string (dt), buffer)
|
||
end
|
||
+ if attached a_entry.categories as cats then
|
||
+ across
|
||
+ cats as ic
|
||
+ loop
|
||
+ append_content_tag_to ("category", <<["term", ic.item]>>, Void, buffer)
|
||
+ end
|
||
+ end
|
||
|
||
append_content_tag_to ("summary", Void, a_entry.description, buffer)
|
||
if attached a_entry.content as l_content then
|
||
diff --git a/library/text/parser/feed/src/atom/atom_feed_parser.e b/library/text/parser/feed/src/atom/atom_feed_parser.e
|
||
index d486598..0c16b2c 100644
|
||
--- a/library/text/parser/feed/src/atom/atom_feed_parser.e
|
||
+++ b/library/text/parser/feed/src/atom/atom_feed_parser.e
|
||
@@ -81,6 +81,7 @@ feature -- Access
|
||
if attached x_entry.element_by_name ("content") as x_content then
|
||
e.set_content (xml_element_code (x_content), xml_attribute_text (x_content, "type"))
|
||
end
|
||
+
|
||
if attached x_entry.element_by_name ("author") as x_author then
|
||
if attached x_author.element_by_name ("name") as x_name and then
|
||
attached x_name.text as l_author_name
|
||
@@ -92,6 +93,17 @@ feature -- Access
|
||
e.set_author (l_author)
|
||
end
|
||
end
|
||
+
|
||
+ -- Optional "category"
|
||
+ if attached x_entry.elements_by_name ("category") as x_categories then
|
||
+ across
|
||
+ x_categories as cats_ic
|
||
+ loop
|
||
+ if attached xml_attribute_text (cats_ic.item, "term") as l_term then
|
||
+ e.set_category (l_term)
|
||
+ end
|
||
+ end
|
||
+ end
|
||
Result.extend (e)
|
||
end
|
||
end
|
||
diff --git a/library/text/parser/feed/src/support/feed_generator.e b/library/text/parser/feed/src/support/feed_generator.e
|
||
index efbb8b8..cad90cc 100644
|
||
--- a/library/text/parser/feed/src/support/feed_generator.e
|
||
+++ b/library/text/parser/feed/src/support/feed_generator.e
|
||
@@ -66,7 +66,7 @@ feature {NONE} -- Helpers
|
||
end
|
||
end
|
||
if a_content = Void then
|
||
- a_output.append ("/>")
|
||
+ a_output.append ("/>%N")
|
||
else
|
||
a_output.append (">")
|
||
a_output.append (escaped_unicode_xml (a_content.as_string_32))
|
||
diff --git a/library/text/parser/feed/tests/atom_test_set.e b/library/text/parser/feed/tests/atom_test_set.e
|
||
index 68eab06..1ffaa2f 100644
|
||
--- a/library/text/parser/feed/tests/atom_test_set.e
|
||
+++ b/library/text/parser/feed/tests/atom_test_set.e
|
||
@@ -65,6 +65,7 @@ feature {NONE} -- Data
|
||
<name>John Doe</name>
|
||
<email>johndoe@example.com</email>
|
||
</author>
|
||
+ <category term="foo"/><category term="bar"/>
|
||
</entry>
|
||
|
||
</feed>
|
||
diff --git a/library/text/parser/feed/tests/msys-iconv-2.dll b/library/text/parser/feed/tests/msys-iconv-2.dll
|
||
new file mode 100644
|
||
index 0000000..111ded9
|
||
Binary files /dev/null and b/library/text/parser/feed/tests/msys-iconv-2.dll differ
|
||
diff --git a/library/text/parser/feed/tests/tests-safe.ecf b/library/text/parser/feed/tests/tests-safe.ecf
|
||
index 6620d47..abc3309 100644
|
||
--- a/library/text/parser/feed/tests/tests-safe.ecf
|
||
+++ b/library/text/parser/feed/tests/tests-safe.ecf
|
||
@@ -6,7 +6,7 @@
|
||
<setting name="concurrency" value="none"/>
|
||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||
<library name="feed" location="..\feed-safe.ecf" readonly="false"/>
|
||
- <library name="http_client" location="$ISE_LIBRARY\contrib\library\network\http_client\http_client-safe.ecf"/>
|
||
+ <library name="http_client" location="..\..\..\..\network\http_client\http_client-safe.ecf"/>
|
||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser-safe.ecf"/>
|
||
<tests name="src" location=".\" recursive="true"/>
|
||
diff --git a/tests/all-safe.ecf b/tests/all-safe.ecf
|
||
index 0516825..727ff92 100644
|
||
--- a/tests/all-safe.ecf
|
||
+++ b/tests/all-safe.ecf
|
||
@@ -17,6 +17,7 @@
|
||
<library name="connector_libfcgi" location="..\library\server\ewsgi\connectors\libfcgi\libfcgi-safe.ecf" readonly="false"/>
|
||
<library name="connector_nino" location="..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false"/>
|
||
<library name="connector_null" location="..\library\server\ewsgi\connectors\null\null-safe.ecf" readonly="false"/>
|
||
+ <library name="connector_standalone" location="..\library\server\ewsgi\connectors\standalone\standalone-safe.ecf" readonly="false"/>
|
||
<library name="conneg" location="..\library\network\protocol\content_negotiation\conneg-safe.ecf" readonly="false"/>
|
||
<library name="default_cgi" location="..\library\server\wsf\default\cgi-safe.ecf" readonly="false"/>
|
||
<library name="default_libfcgi" location="..\library\server\wsf\default\libfcgi-safe.ecf" readonly="false"/>
|
||
@@ -30,6 +31,7 @@
|
||
<library name="filter" location="..\examples\filter\filter-safe.ecf" readonly="false"/>
|
||
<library name="hello_world" location="..\library\server\ewsgi\examples\hello_world\hello-safe.ecf" readonly="false"/>
|
||
<library name="http" location="..\library\network\protocol\http\http-safe.ecf" readonly="false"/>
|
||
+ <library name="httpd" location="..\library\server\ewsgi\connectors\standalone\lib\httpd\httpd-safe.ecf" readonly="false"/>
|
||
<library name="http_authorization" location="..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
|
||
<library name="http_client" location="..\library\network\http_client\http_client-safe.ecf" readonly="false"/>
|
||
<library name="libfcgi" location="..\library\server\libfcgi\libfcgi-safe.ecf" readonly="false"/>
|
||
diff --git a/tests/dev/hello-safe.ecf b/tests/dev/hello-safe.ecf
|
||
index 94095cc..e61bc49 100644
|
||
--- a/tests/dev/hello-safe.ecf
|
||
+++ b/tests/dev/hello-safe.ecf
|
||
@@ -1,14 +1,15 @@
|
||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-9-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-9-0 http://www.eiffel.com/developers/xml/configuration-1-9-0.xsd" name="hello_dev" uuid="7C9887BD-4AE4-47F2-A0AA-4BBB6736D433">
|
||
<target name="hello_dev" abstract="true">
|
||
- <root class="HELLO_ROUTED_WORLD" feature="make"/>
|
||
+ <root class="HELLO_ROUTED_WORLD" feature="make_and_launch"/>
|
||
<file_rule>
|
||
<exclude>/EIFGENs$</exclude>
|
||
<exclude>/\.git$</exclude>
|
||
<exclude>/\.svn$</exclude>
|
||
</file_rule>
|
||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||
- <debug name="nino" enabled="true"/>
|
||
+ <debug name="standalone" enabled="true"/>
|
||
+ <debug name="ew_standalone" enabled="true"/>
|
||
<assertions precondition="true" postcondition="true" check="true" invariant="true" supplier_precondition="true"/>
|
||
</option>
|
||
<setting name="concurrency" value="thread"/>
|
||
@@ -16,20 +17,20 @@
|
||
<library name="http" location="../../library/network/protocol/http/http-safe.ecf" readonly="false"/>
|
||
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
|
||
</target>
|
||
- <target name="hello_nino" extends="hello_connector">
|
||
+ <target name="hello_standalone" extends="hello_dev">
|
||
<option debug="true" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||
- <debug name="nino" enabled="true"/>
|
||
+ <debug name="standalone" enabled="true"/>
|
||
<assertions precondition="true" postcondition="true" check="true" invariant="true" supplier_precondition="true"/>
|
||
</option>
|
||
- <library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="false" use_application_options="true"/>
|
||
+ <library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false" use_application_options="true"/>
|
||
<cluster name="src" location="src\" recursive="true"/>
|
||
<override name="override" location="override\" recursive="true"/>
|
||
</target>
|
||
- <target name="hello_cgi" extends="hello_connector">
|
||
+ <target name="hello_cgi" extends="hello_dev">
|
||
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf"/>
|
||
<cluster name="src" location="src\" recursive="true"/>
|
||
</target>
|
||
- <target name="hello_libfcgi" extends="hello_connector">
|
||
+ <target name="hello_libfcgi" extends="hello_dev">
|
||
<library name="default_libfcgi" location="..\..\library\server\wsf\default\libfcgi-safe.ecf"/>
|
||
<cluster name="src" location="src\" recursive="true"/>
|
||
</target>
|
||
diff --git a/tests/dev/src/hello_routed_world.e b/tests/dev/src/hello_routed_world.e
|
||
index fa588fe..f0d3a62 100644
|
||
--- a/tests/dev/src/hello_routed_world.e
|
||
+++ b/tests/dev/src/hello_routed_world.e
|
||
@@ -8,243 +8,24 @@ class
|
||
HELLO_ROUTED_WORLD
|
||
|
||
inherit
|
||
- WSF_URI_TEMPLATE_ROUTED_SERVICE
|
||
-
|
||
- WSF_HANDLER_HELPER
|
||
-
|
||
- WSF_DEFAULT_SERVICE
|
||
+ WSF_DEFAULT_SERVICE [HELLO_ROUTED_WORLD_EXECUTION]
|
||
+ redefine
|
||
+ initialize
|
||
+ end
|
||
|
||
create
|
||
- make
|
||
+ make_and_launch
|
||
|
||
feature {NONE} -- Initialization
|
||
|
||
- make
|
||
+ initialize
|
||
do
|
||
- initialize_router
|
||
+ Precursor
|
||
set_service_option ("port", 8099)
|
||
- make_and_launch
|
||
- end
|
||
-
|
||
- create_router
|
||
- do
|
||
- create router.make (5)
|
||
- end
|
||
-
|
||
- setup_router
|
||
- local
|
||
- ra: WSF_AGENT_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
|
||
- hello: WSF_URI_TEMPLATE_ROUTING_HANDLER
|
||
- www: WSF_FILE_SYSTEM_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT]
|
||
- do
|
||
- router.map_agent ("/refresh", agent execute_refresh)
|
||
- router.map_agent ("/home", agent execute_home)
|
||
- create www.make (document_root)
|
||
- www.set_directory_index (<<"index.html">>)
|
||
-
|
||
- router.map ("/www{/path}{?query}", www)
|
||
-
|
||
- --| Map all "/hello*" using a ROUTING_HANDLER
|
||
- create hello.make (3)
|
||
- router.map ("/hello", hello)
|
||
-
|
||
- create ra.make (agent handle_hello)
|
||
- hello.map ("/hello/{name}.{format}", ra)
|
||
- hello.map ("/hello.{format}/{name}", ra)
|
||
- hello.map ("/hello/{name}", ra)
|
||
-
|
||
- create ra.make (agent handle_anonymous_hello)
|
||
- hello.map ("/hello", ra)
|
||
- hello.map ("/hello.{format}", ra)
|
||
-
|
||
- --| Various various route, directly on the "router"
|
||
- router.map_agent_with_request_methods ("/method/any", agent handle_method_any, Void)
|
||
- router.map_agent_with_request_methods ("/method/guess", agent handle_method_get_or_post, <<"GET", "POST">>)
|
||
- router.map_agent_with_request_methods ("/method/custom", agent handle_method_get, <<"GET">>)
|
||
- router.map_agent_with_request_methods ("/method/custom", agent handle_method_post, <<"POST">>)
|
||
- end
|
||
-
|
||
-
|
||
- document_root: READABLE_STRING_8
|
||
- local
|
||
- e: EXECUTION_ENVIRONMENT
|
||
- dn: DIRECTORY_NAME
|
||
- once
|
||
- create e
|
||
- create dn.make_from_string (e.current_working_directory)
|
||
- dn.extend ("htdocs")
|
||
- Result := dn.string
|
||
- if Result[Result.count] = Operating_environment.directory_separator then
|
||
- Result := Result.substring (1, Result.count - 1)
|
||
- end
|
||
- end
|
||
-
|
||
-feature -- Execution
|
||
-
|
||
-
|
||
- execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
- local
|
||
- l_url: STRING
|
||
- do
|
||
- l_url := req.absolute_script_url ("/home")
|
||
- res.redirect_now_with_content (l_url, "You are now being redirected to " + l_url, {HTTP_MIME_TYPES}.text_html)
|
||
- end
|
||
-
|
||
- execute_refresh (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
- local
|
||
- h: HTTP_HEADER
|
||
- l_url: STRING
|
||
- e: EXECUTION_ENVIRONMENT
|
||
- n: INTEGER
|
||
- i: INTEGER
|
||
- s: STRING_8
|
||
- do
|
||
- l_url := req.absolute_script_url ("/home")
|
||
-
|
||
-
|
||
-
|
||
- n := 3
|
||
- create h.make
|
||
- h.put_refresh (l_url, 5)
|
||
- h.put_location (l_url)
|
||
- h.put_content_type_text_plain
|
||
- h.put_transfer_encoding_chunked
|
||
--- h.put_content_length (0)
|
||
--- res.set_status_code ({HTTP_STATUS_CODE}.moved_permanently)
|
||
- res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||
- res.put_header_text (h.string)
|
||
-
|
||
- from
|
||
- create e
|
||
- create s.make (255)
|
||
- until
|
||
- n = 0
|
||
- loop
|
||
- if n > 1 then
|
||
- s.append ("%NRedirected to " + l_url + " in " + n.out + " seconds :%N")
|
||
- else
|
||
- s.append ("%NRedirected to " + l_url + " in 1 second :%N")
|
||
- end
|
||
- res.put_chunk (s, Void); s.wipe_out
|
||
- from
|
||
- i := 1
|
||
- until
|
||
- i = 1001
|
||
- loop
|
||
- s.append_character ('.')
|
||
- if i \\ 100 = 0 then
|
||
- s.append_character ('%N')
|
||
- end
|
||
- res.put_chunk (s, Void); s.wipe_out
|
||
- e.sleep (1_000_000)
|
||
- i := i + 1
|
||
- end
|
||
- n := n - 1
|
||
- end
|
||
- s.append ("%NYou are now being redirected...%N")
|
||
- res.put_chunk (s, Void); s.wipe_out
|
||
- res.put_chunk_end
|
||
end
|
||
|
||
- execute_home (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
- local
|
||
- l_body: STRING_8
|
||
- do
|
||
- create l_body.make (255)
|
||
- l_body.append ("<html><body>Hello World ?!%N")
|
||
- l_body.append ("<h3>Please try the following links</h3><ul>%N")
|
||
- l_body.append ("<li><a href=%""+ req.script_url ("/") + "%">default</a></li>%N")
|
||
- l_body.append ("<li><a href=%""+ req.script_url ("/refresh") + "%">redirect using refresh and chunked encoding</a></li>%N")
|
||
- l_body.append ("<li><a href=%""+ req.script_url ("/hello") + "%">/hello</a></li>%N")
|
||
- l_body.append ("<li><a href=%""+ req.script_url ("/hello.html/Joce") + "%">/hello.html/Joce</a></li>%N")
|
||
- l_body.append ("<li><a href=%""+ req.script_url ("/hello.json/Joce") + "%">/hello.json/Joce</a></li>%N")
|
||
- l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce.html") + "%">/hello/Joce.html</a></li>%N")
|
||
- l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce.xml") + "%">/hello/Joce.xml</a></li>%N")
|
||
- l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce") + "%">/hello/Joce</a></li>%N")
|
||
- l_body.append ("</ul>%N")
|
||
- if attached req.item ("REQUEST_COUNT") as rqc then
|
||
- l_body.append ("request #"+ rqc.as_string.string + "%N")
|
||
- end
|
||
- l_body.append ("</body></html>%N")
|
||
-
|
||
- res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_body.count.out]>>)
|
||
- res.put_string (l_body)
|
||
- end
|
||
-
|
||
- execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE; a_name: detachable READABLE_STRING_32; ctx: WSF_HANDLER_CONTEXT)
|
||
- local
|
||
- l_response_content_type: detachable STRING
|
||
- h: HTTP_HEADER
|
||
- content_type_supported: ARRAY [STRING]
|
||
- l_body: STRING_8
|
||
- do
|
||
- if a_name /= Void then
|
||
- l_body := "Hello %"" + a_name + "%" !%N"
|
||
- else
|
||
- l_body := "Hello anonymous visitor !%N"
|
||
- end
|
||
- content_type_supported := <<{HTTP_MIME_TYPES}.application_json, {HTTP_MIME_TYPES}.text_html, {HTTP_MIME_TYPES}.text_xml, {HTTP_MIME_TYPES}.text_plain>>
|
||
- inspect ctx.request_format_id ("format", content_type_supported)
|
||
- when {HTTP_FORMAT_CONSTANTS}.json then
|
||
- l_response_content_type := {HTTP_MIME_TYPES}.application_json
|
||
- l_body := "{%N%"application%": %"/hello%",%N %"message%": %"" + l_body + "%" %N}"
|
||
- when {HTTP_FORMAT_CONSTANTS}.html then
|
||
- l_response_content_type := {HTTP_MIME_TYPES}.text_html
|
||
- when {HTTP_FORMAT_CONSTANTS}.xml then
|
||
- l_response_content_type := {HTTP_MIME_TYPES}.text_xml
|
||
- l_body := "<response><application>/hello</application><message>" + l_body + "</message></response>%N"
|
||
- when {HTTP_FORMAT_CONSTANTS}.text then
|
||
- l_response_content_type := {HTTP_MIME_TYPES}.text_plain
|
||
- else
|
||
- execute_content_type_not_allowed (req, res, content_type_supported,
|
||
- <<{HTTP_FORMAT_CONSTANTS}.json_name, {HTTP_FORMAT_CONSTANTS}.html_name, {HTTP_FORMAT_CONSTANTS}.xml_name, {HTTP_FORMAT_CONSTANTS}.text_name>>
|
||
- )
|
||
- end
|
||
- if l_response_content_type /= Void then
|
||
- create h.make
|
||
- h.put_content_type (l_response_content_type)
|
||
- h.put_content_length (l_body.count)
|
||
- res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||
- res.put_header_text (h.string)
|
||
- res.put_string (l_body)
|
||
- end
|
||
- end
|
||
-
|
||
- handle_hello (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
- do
|
||
- execute_hello (req, res, ctx.string_parameter ("name"), ctx)
|
||
- end
|
||
-
|
||
- handle_anonymous_hello (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
- do
|
||
- execute_hello (req, res, ctx.string_parameter ("name"), ctx)
|
||
- end
|
||
-
|
||
- handle_method_any (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
- do
|
||
- execute_hello (req, res, req.request_method, ctx)
|
||
- end
|
||
-
|
||
- handle_method_get (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
- do
|
||
- execute_hello (req, res, "GET", ctx)
|
||
- end
|
||
-
|
||
-
|
||
- handle_method_post (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
- do
|
||
- execute_hello (req, res, "POST", ctx)
|
||
- end
|
||
-
|
||
- handle_method_get_or_post (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
- do
|
||
- execute_hello (req, res, "GET or POST", ctx)
|
||
- end
|
||
-
|
||
-
|
||
-
|
||
note
|
||
- copyright: "2011-2011, Eiffel Software and others"
|
||
+ copyright: "2011-2016, Eiffel Software and others"
|
||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
source: "[
|
||
Eiffel Software
|
||
diff --git a/tests/dev/src/hello_routed_world_execution.e b/tests/dev/src/hello_routed_world_execution.e
|
||
new file mode 100644
|
||
index 0000000..6dd3e6d
|
||
--- /dev/null
|
||
+++ b/tests/dev/src/hello_routed_world_execution.e
|
||
@@ -0,0 +1,272 @@
|
||
+note
|
||
+ description : "Objects that ..."
|
||
+ author : "$Author$"
|
||
+ date : "$Date$"
|
||
+ revision : "$Revision$"
|
||
+
|
||
+class
|
||
+ HELLO_ROUTED_WORLD_EXECUTION
|
||
+
|
||
+inherit
|
||
+ WSF_ROUTED_EXECUTION
|
||
+ redefine
|
||
+ execute_default
|
||
+ end
|
||
+
|
||
+ WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_EXECUTION
|
||
+
|
||
+create
|
||
+ make
|
||
+
|
||
+feature {NONE} -- Initialization
|
||
+
|
||
+ setup_router
|
||
+ local
|
||
+ ra: WSF_URI_TEMPLATE_AGENT_HANDLER
|
||
+ hello: WSF_URI_TEMPLATE_ROUTING_HANDLER
|
||
+ www: WSF_FILE_SYSTEM_HANDLER
|
||
+ do
|
||
+ map_uri_template_agent ("/refresh", agent execute_refresh, Void)
|
||
+ map_uri_template_agent ("/home", agent execute_home, Void)
|
||
+ create www.make (document_root)
|
||
+ www.set_directory_index (<<"index.html">>)
|
||
+ router.handle ("/www{/path}{?query}", www, Void)
|
||
+
|
||
+ --| Map all "/hello*" using a ROUTING_HANDLER
|
||
+ create hello.make (3)
|
||
+ router.handle ("/hello", hello, Void)
|
||
+
|
||
+ create ra.make (agent handle_hello)
|
||
+ hello.router.handle ("/hello/{name}.{format}", ra, Void)
|
||
+ hello.router.handle ("/hello.{format}/{name}", ra, Void)
|
||
+ hello.router.handle ("/hello/{name}", ra, Void)
|
||
+
|
||
+ create ra.make (agent handle_anonymous_hello)
|
||
+ hello.router.handle ("/hello", ra, Void)
|
||
+ hello.router.handle ("/hello.{format}", ra, Void)
|
||
+
|
||
+ --| Various various route, directly on the "router"
|
||
+ map_uri_template_agent ("/method/any", agent handle_method_any, Void)
|
||
+ map_uri_template_agent ("/method/guess", agent handle_method_get_or_post, <<"GET", "POST">>)
|
||
+ map_uri_template_agent ("/method/custom", agent handle_method_get, <<"GET">>)
|
||
+ map_uri_template_agent ("/method/custom", agent handle_method_post, <<"POST">>)
|
||
+ end
|
||
+
|
||
+
|
||
+ document_root: READABLE_STRING_8
|
||
+ local
|
||
+ e: EXECUTION_ENVIRONMENT
|
||
+ dn: DIRECTORY_NAME
|
||
+ once
|
||
+ create e
|
||
+ create dn.make_from_string (e.current_working_directory)
|
||
+ dn.extend ("htdocs")
|
||
+ Result := dn.string
|
||
+ if Result[Result.count] = Operating_environment.directory_separator then
|
||
+ Result := Result.substring (1, Result.count - 1)
|
||
+ end
|
||
+ end
|
||
+
|
||
+feature -- Execution
|
||
+
|
||
+
|
||
+ execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
+ local
|
||
+ l_url: STRING
|
||
+ do
|
||
+ l_url := req.absolute_script_url ("/home")
|
||
+ res.redirect_now_with_content (l_url, "You are now being redirected to " + l_url, {HTTP_MIME_TYPES}.text_html)
|
||
+ end
|
||
+
|
||
+ execute_refresh (req: WSF_REQUEST; res: WSF_RESPONSE) --ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT;
|
||
+ local
|
||
+ h: HTTP_HEADER
|
||
+ l_url: STRING
|
||
+ e: EXECUTION_ENVIRONMENT
|
||
+ n: INTEGER
|
||
+ i: INTEGER
|
||
+ s: STRING_8
|
||
+ do
|
||
+ l_url := req.absolute_script_url ("/home")
|
||
+
|
||
+
|
||
+
|
||
+ n := 3
|
||
+ create h.make
|
||
+ h.put_refresh (l_url, 5)
|
||
+ h.put_location (l_url)
|
||
+ h.put_content_type_text_plain
|
||
+ h.put_transfer_encoding_chunked
|
||
+-- h.put_content_length (0)
|
||
+-- res.set_status_code ({HTTP_STATUS_CODE}.moved_permanently)
|
||
+ res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||
+ res.put_header_text (h.string)
|
||
+
|
||
+ from
|
||
+ create e
|
||
+ create s.make (255)
|
||
+ until
|
||
+ n = 0
|
||
+ loop
|
||
+ if n > 1 then
|
||
+ s.append ("%NRedirected to " + l_url + " in " + n.out + " seconds :%N")
|
||
+ else
|
||
+ s.append ("%NRedirected to " + l_url + " in 1 second :%N")
|
||
+ end
|
||
+ res.put_chunk (s, Void); s.wipe_out
|
||
+ from
|
||
+ i := 1
|
||
+ until
|
||
+ i = 1001
|
||
+ loop
|
||
+ s.append_character ('.')
|
||
+ if i \\ 100 = 0 then
|
||
+ s.append_character ('%N')
|
||
+ end
|
||
+ res.put_chunk (s, Void); s.wipe_out
|
||
+ e.sleep (1_000_000)
|
||
+ i := i + 1
|
||
+ end
|
||
+ n := n - 1
|
||
+ end
|
||
+ s.append ("%NYou are now being redirected...%N")
|
||
+ res.put_chunk (s, Void); s.wipe_out
|
||
+ res.put_chunk_end
|
||
+ end
|
||
+
|
||
+ execute_home (req: WSF_REQUEST; res: WSF_RESPONSE) -- ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT;
|
||
+ local
|
||
+ l_body: STRING_8
|
||
+ do
|
||
+ create l_body.make (255)
|
||
+ l_body.append ("<html><body>Hello World ?!%N")
|
||
+ l_body.append ("<h3>Please try the following links</h3><ul>%N")
|
||
+ l_body.append ("<li><a href=%""+ req.script_url ("/") + "%">default</a></li>%N")
|
||
+ l_body.append ("<li><a href=%""+ req.script_url ("/refresh") + "%">redirect using refresh and chunked encoding</a></li>%N")
|
||
+ l_body.append ("<li><a href=%""+ req.script_url ("/hello") + "%">/hello</a></li>%N")
|
||
+ l_body.append ("<li><a href=%""+ req.script_url ("/hello.html/Joce") + "%">/hello.html/Joce</a></li>%N")
|
||
+ l_body.append ("<li><a href=%""+ req.script_url ("/hello.json/Joce") + "%">/hello.json/Joce</a></li>%N")
|
||
+ l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce.html") + "%">/hello/Joce.html</a></li>%N")
|
||
+ l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce.xml") + "%">/hello/Joce.xml</a></li>%N")
|
||
+ l_body.append ("<li><a href=%""+ req.script_url ("/hello/Joce") + "%">/hello/Joce</a></li>%N")
|
||
+ l_body.append ("</ul>%N")
|
||
+ if attached req.item ("REQUEST_COUNT") as rqc then
|
||
+ l_body.append ("request #"+ rqc.as_string.url_encoded_value + "%N")
|
||
+ end
|
||
+ l_body.append ("</body></html>%N")
|
||
+
|
||
+ res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_body.count.out]>>)
|
||
+ res.put_string (l_body)
|
||
+ end
|
||
+
|
||
+ execute_hello (req: WSF_REQUEST; res: WSF_RESPONSE; a_name: detachable READABLE_STRING_32)
|
||
+ local
|
||
+ l_response_content_type: detachable STRING
|
||
+ h: HTTP_HEADER
|
||
+ content_type_supported: ARRAY [STRING]
|
||
+ l_body: STRING_8
|
||
+ l_format: detachable READABLE_STRING_GENERAL
|
||
+ l_http_format_constants: HTTP_FORMAT_CONSTANTS
|
||
+ do
|
||
+ if a_name /= Void then
|
||
+ l_body := "Hello %"" + a_name + "%" !%N"
|
||
+ else
|
||
+ l_body := "Hello anonymous visitor !%N"
|
||
+ end
|
||
+ content_type_supported := <<{HTTP_MIME_TYPES}.application_json, {HTTP_MIME_TYPES}.text_html, {HTTP_MIME_TYPES}.text_xml, {HTTP_MIME_TYPES}.text_plain>>
|
||
+ if attached {WSF_STRING} req.path_parameter ("format") as s_format then
|
||
+ l_format := s_format.value
|
||
+ end
|
||
+ if l_format = Void then
|
||
+ across
|
||
+ content_type_supported as ic
|
||
+ until
|
||
+ l_format /= Void
|
||
+ loop
|
||
+ if req.is_content_type_accepted (ic.item) then
|
||
+ l_format := ic.item
|
||
+ end
|
||
+ end
|
||
+ end
|
||
+ if l_format /= Void then
|
||
+ create l_http_format_constants
|
||
+ inspect
|
||
+ l_http_format_constants.format_id (l_format)
|
||
+ when {HTTP_FORMAT_CONSTANTS}.json then
|
||
+ l_response_content_type := {HTTP_MIME_TYPES}.application_json
|
||
+ l_body := "{%N%"application%": %"/hello%",%N %"message%": %"" + l_body + "%" %N}"
|
||
+ when {HTTP_FORMAT_CONSTANTS}.html then
|
||
+ l_response_content_type := {HTTP_MIME_TYPES}.text_html
|
||
+ when {HTTP_FORMAT_CONSTANTS}.xml then
|
||
+ l_response_content_type := {HTTP_MIME_TYPES}.text_xml
|
||
+ l_body := "<response><application>/hello</application><message>" + l_body + "</message></response>%N"
|
||
+ when {HTTP_FORMAT_CONSTANTS}.text then
|
||
+ l_response_content_type := {HTTP_MIME_TYPES}.text_plain
|
||
+ else
|
||
+ l_response_content_type := Void
|
||
+ end
|
||
+ end
|
||
+
|
||
+ if l_response_content_type /= Void then
|
||
+ create h.make
|
||
+ h.put_content_type (l_response_content_type)
|
||
+ h.put_content_length (l_body.count)
|
||
+ res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||
+ res.put_header_text (h.string)
|
||
+ res.put_string (l_body)
|
||
+ else
|
||
+ res.send (create {WSF_PRECONDITION_FAILED_MESSAGE}.make (req)) -- FIXME: better error message!
|
||
+ end
|
||
+ end
|
||
+
|
||
+ string_path_parameter (req: WSF_REQUEST; a_name: READABLE_STRING_GENERAL): detachable STRING_32
|
||
+ do
|
||
+ if attached {WSF_STRING} req.path_parameter (a_name) as s then
|
||
+ Result := s.value
|
||
+ end
|
||
+ end
|
||
+
|
||
+ handle_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
+ do
|
||
+ execute_hello (req, res, string_path_parameter (req, "name"))
|
||
+ end
|
||
+
|
||
+ handle_anonymous_hello (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
+ do
|
||
+ execute_hello (req, res, string_path_parameter (req, "name"))
|
||
+ end
|
||
+
|
||
+ handle_method_any (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
+ do
|
||
+ execute_hello (req, res, req.request_method)
|
||
+ end
|
||
+
|
||
+ handle_method_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
+ do
|
||
+ execute_hello (req, res, "GET")
|
||
+ end
|
||
+
|
||
+
|
||
+ handle_method_post (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
+ do
|
||
+ execute_hello (req, res, "POST")
|
||
+ end
|
||
+
|
||
+ handle_method_get_or_post (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||
+ do
|
||
+ execute_hello (req, res, "GET or POST")
|
||
+ end
|
||
+
|
||
+
|
||
+
|
||
+note
|
||
+ copyright: "2011-2016, Eiffel Software and others"
|
||
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||
+ source: "[
|
||
+ Eiffel Software
|
||
+ 5949 Hollister Ave., Goleta, CA 93117 USA
|
||
+ Telephone 805-685-1006, Fax 805-685-6869
|
||
+ Website http://www.eiffel.com
|
||
+ Customer support http://support.eiffel.com
|
||
+ ]"
|
||
+end
|
||
diff --git a/tools/install_ewf.bat b/tools/install_ewf.bat
|
||
index ae26e20..dd1fa88 100644
|
||
--- a/tools/install_ewf.bat
|
||
+++ b/tools/install_ewf.bat
|
||
@@ -59,6 +59,8 @@ echo Install library: ewf/wsf
|
||
%COPYCMD% %TMP_DIR%\library\server\wsf %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf
|
||
echo Install library: ewf/wsf_extension
|
||
%COPYCMD% %TMP_DIR%\library\server\wsf_extension %TMP_CONTRIB_DIR%\library\web\framework\ewf\wsf_extension
|
||
+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/encoder
|