Compare commits
68 Commits
v1_executo
...
es_rev_980
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d94413297 | |||
| 35855941e6 | |||
| 50ba8ca703 | |||
| dde6a0b7de | |||
| b64a281d75 | |||
| b69b8aaaf9 | |||
| 65b28ed877 | |||
| 6c7637716b | |||
| ff9a238f5c | |||
| eec3cbdba1 | |||
| 29c4931dc0 | |||
|
|
9cd0f0b117 | ||
|
|
aa0eb4fc43 | ||
|
|
dbdc594b59 | ||
|
|
4176a8c68b | ||
|
|
0557d1ee2d | ||
|
|
eed8af9a0a | ||
|
|
1b881c4f60 | ||
|
|
770488dbd3 | ||
| 3f69081d32 | |||
| 7033db7dc4 | |||
| a1a16b4a22 | |||
| 98e92ee0fe | |||
| 29b55f36cf | |||
| 061e88c9fe | |||
| 66f204b1f2 | |||
| c92b1b8c3b | |||
| 98c12b8fb9 | |||
| 5fee483fd9 | |||
| f7a7afccd6 | |||
| e2c70e6d70 | |||
| a5e150d1c0 | |||
| 39887c8bdb | |||
|
|
1f1e2abbda | ||
| 1796d9631f | |||
| 389975e409 | |||
| 6c51590369 | |||
|
|
cc65bae644 | ||
|
|
c824f707cf | ||
| 47c5b798b3 | |||
| f0cba1d536 | |||
| ed891546bc | |||
| 8651ff6e1e | |||
| 629edea991 | |||
|
|
1e10ce8518 | ||
| 4f8f17ad48 | |||
| 148518984e | |||
| 33150e34d6 | |||
| af60a5719e | |||
| 31557cfc33 | |||
| 78c0cd5b0d | |||
| 412534d0be | |||
|
|
0f6aa8d7ae | ||
|
|
2c745c63d3 | ||
|
|
efd80c1287 | ||
|
|
01f649fd88 | ||
|
|
f23aeb6412 | ||
|
|
1a4596c79b | ||
|
|
b16e4aa570 | ||
|
|
5255b15fa9 | ||
|
|
57048373f4 | ||
|
|
9e06fb2ab8 | ||
|
|
f2405e0ccd | ||
|
|
6e3a7deb6e | ||
|
|
f254b599c0 | ||
| 99a05b95ba | |||
| 54dd43c38a | |||
| d0836d49a4 |
|
Before Width: | Height: | Size: 13 KiB |
BIN
doc/workbook/basics/APPLICATION_EXECUTION.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
doc/workbook/basics/Launcher Hierarchy.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_CGI.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_FCGI.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_NINO.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
doc/workbook/basics/WSF_SERVICE_LAUNCHER_STANDALONE.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
@@ -1,4 +1,4 @@
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query Parameter](/workbook/handling_request/form.md)
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query Parameter](/doc/workbook/handling_request/form.md)
|
||||
|
||||
|
||||
## EWF basic service
|
||||
@@ -14,133 +14,140 @@ Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query Parameter](/wor
|
||||
<a name="structure"/>
|
||||
## EWF service structure
|
||||
|
||||
The following code describes the basic structure of an EWF basic service that handles HTTP requests.
|
||||
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
|
||||
SERVICE_TEMPLATE
|
||||
APPLICACTION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE -- Todo explain this, and the concept of launchers and connectors ()
|
||||
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
make_and_launch
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
-- To read incoming HTTP request, we need to use `req'
|
||||
|
||||
-- May require talking to databases or other services.
|
||||
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers and the content we want to send out our client
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
When using the "nino" connector, by default the service listens on port 80, but often this port is already used by other applications, so it is recommended to use another port.
|
||||
To define another port, redefine the feature `initialize' and set up a new port number using the service options (see below).
|
||||
The class ```APPLICATION``` inherit from
|
||||
```WSF_DEFAULT_SERVICE [G ->WSF_EXECUTION create make end]``` it will be responsible to launch the service and set optional options.
|
||||
|
||||
The class ```APPLICATION_EXECUTION``` is an implementation of ```WSF_EXECUTION``` interface, which is instantiated for each incoming request.
|
||||
|
||||
```eiffel
|
||||
class
|
||||
SERVICE_TEMPLATE
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE
|
||||
redefine
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
do
|
||||
-- To read incoming HTTP request, we need to use `req'
|
||||
|
||||
-- May require talking to databases or other services.
|
||||
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers and the content we want to send out our client
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
When using the "nino" connector or the new "standalone" connector, by default the service listens on port 80, but often this port is already used by other applications, so it is recommended to use another port.
|
||||
To define another port, redefine the feature `initialize' and set up a new port number using the service options (see below).
|
||||
|
||||
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE [APPLICATION_EXECUTION]
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
-- on port 9090
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
end
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute the incoming request.
|
||||
do
|
||||
-- To read incoming HTTP requires, we need to use `req'
|
||||
|
||||
-- May require talking to databases or other services.
|
||||
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers and the content we want to send out client
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The **WSF_REQUEST** gives access to the incoming data; the class provides features to get information such as request method, form data, query parameters, uploaded files, HTTP request headers, and hostname of the client among others.
|
||||
|
||||
The **WSF_RESPONSE** provides features to define the response with information such as HTTP status codes (10x,20x, 30x, 40x, and 50x), response headers (Content-Type, Content-Length, etc.) and obviously the body of the message itself.
|
||||
|
||||
**SERVICE_TEMPLATE** 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 or nino. For development is recommended to use Nino, 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.
|
||||
**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.
|
||||
|
||||
The **SERVICE_TEMPLATE** class inherits from _WSF_DEFAULT_SERVICE_ class, and this one also inherits from other interfaces. Let’s describe them in a few words.
|
||||

|
||||
|
||||

|
||||
**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 the low level entry point in EWF, handling each incoming request with a single procedure ```execute (req: WSF_REQUEST; res: WSF_RESPONSE) ...```. And also provides a way to launch our application using different kind of connectors. Below a [BON diagram] (http://www.bon-method.com/index_normal.htm) showing the different kind of connectors.
|
||||

|
||||
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** (for other options see [?]).
|
||||
And then you only need to implement the **execute** feature, get data from the request *req* and write the response in *res*.
|
||||
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**.
|
||||
|
||||
The **APPLICATION_EXECUTION** class inherits from **WSF_EXECUTION** interface, which is instantiated for each incoming request. **WSF_EXECUTION** inherit from **WGI_EXECUTION** which is the low level entry point in EWF, handling each incoming request with a single procedure ```execute (req: WSF_REQUEST; res: WSF_RESPONSE) ...```.
|
||||
|
||||
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 simple Service to Generate Plain Text.
|
||||
|
||||
Before to continue, it is recommended to review the getting started guided.
|
||||
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.
|
||||
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
end
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
execute
|
||||
-- Execute the incomming request
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/plain"], ["Content-Length", "11"]>>)
|
||||
res.put_string ("Hello World")
|
||||
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/plain"], ["Content-Length", "11"]>>)
|
||||
response.put_string ("Hello World")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
```
|
||||
<a name="source_1"></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_examples.git```
|
||||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||||
|
||||
The example of simple service that generate plain text response is located in the directory $PATH/ewf_examples/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/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:
|
||||
|
||||
```estudio -config simple.ecf -target simple_nino```
|
||||
|
||||
@@ -153,37 +160,25 @@ To generate HTML, it's needed
|
||||
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
end
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
execute
|
||||
-- Execute the incomming request
|
||||
do
|
||||
-- To send a response we need to setup, the status code and
|
||||
-- the response headers.
|
||||
res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
|
||||
res.put_string (web_page)
|
||||
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", web_page.count.out]>>)
|
||||
response.put_string (web_page)
|
||||
end
|
||||
|
||||
|
||||
web_page: STRING = "[
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@@ -201,11 +196,11 @@ end
|
||||
##### Source code
|
||||
The source code is available on Github. You can get it by running the command:
|
||||
|
||||
```git clone https://github.com/EiffelWebFramework/ewf_examples.git```
|
||||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||||
|
||||
The example of the service that generates HTML is located in the directory $PATH/ewf_examples/workbook/basics/simple_html, where $PATH is where you run ```git clone``` . Just double click on the simple_html.ecf file and select the simple_html_nino target or if you prefer the command line, run the command:
|
||||
The example of the service that generates HTML is located in the directory $PATH/ewf/doc/workbook/basics/simple_html, where $PATH is where you run ```git clone``` . Just double click on the simple_html.ecf file and select the simple_html_nino target or if you prefer the command line, run the command:
|
||||
|
||||
```estudio -config simple_html.ecf -target simple_html_nino```
|
||||
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query Parameter](/workbook/handling_request/form.md)
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query Parameter](/doc/workbook/handling_request/form.md)
|
||||
|
||||
|
||||
@@ -22,5 +22,4 @@ feature -- Basic operations
|
||||
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/plain"], ["Content-Length", "11"]>>)
|
||||
response.put_string ("Hello World")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -37,6 +37,14 @@
|
||||
<library name="default_libfcgi" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\libfcgi-safe.ecf"/>
|
||||
<cluster name="simple" location=".\" recursive="true"/>
|
||||
</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">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<library name="default_standalone" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\default\standalone-safe.ecf"/>
|
||||
<cluster name="simple" location=".\" recursive="true"/>
|
||||
</target>
|
||||
<target name="simple" extends="simple_nino">
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Header Fields](/workbook/handling_request/headers.md) | [Handling Cookies](/workbook/handling_cookies/handling_cookies.md)
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md) | [Handling Cookies](/doc/workbook/handling_cookies/handling_cookies.md)
|
||||
|
||||
|
||||
## EWF Generating Response
|
||||
@@ -164,24 +164,13 @@ Note: use ```res.set_status_code({HTTP_STATUS_CODE}.bad_request)``` rather than
|
||||
Basic Service that builds a simple web page to show the most common status codes
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
end
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
@@ -321,24 +310,13 @@ note
|
||||
revision : "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
end
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
@@ -1018,4 +996,4 @@ There are four categories for response header fields:
|
||||
|
||||
|
||||
|
||||
| [Handling Requests: Header Fields](/workbook/handling_request/headers.md) | [Handling Cookies](/workbook/handling_cookies/handling_cookies.md)
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md) | [Handling Cookies](/doc/workbook/handling_cookies/handling_cookies.md)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Nav: [Workbook](../workbook.md) | [Generating Responses](/workbook/generating_response/generating_response.md)
|
||||
Nav: [Workbook](../workbook.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
|
||||
# Handling Cookies
|
||||
|
||||
@@ -146,25 +146,13 @@ note
|
||||
revision : "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
set_service_option ("verbose",True)
|
||||
end
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
@@ -297,4 +285,4 @@ end
|
||||
```
|
||||
|
||||
|
||||
Nav: [Workbook](../workbook.md) | [Generating Responses](/workbook/generating_response/generating_response.md)
|
||||
Nav: [Workbook](../workbook.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Nav: [Workbook](../workbook.md) | [Basic Concepts] (/workbook/basics/basics.md) | [Handling Requests: Header Fields](/workbook/handling_request/headers.md)
|
||||
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
|
||||
@@ -285,25 +285,23 @@ and a simple message.
|
||||
```
|
||||
The source code is available on Github. You can get it by running the command:
|
||||
|
||||
```git clone https://github.com/EiffelWebFramework/ewf_examples.git```
|
||||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||||
|
||||
The example is located in the directory $PATH/ewf_examples/workbook/upload_file where $PATH is where you run git clone.
|
||||
The example is located in the directory $PATH/ewf/doc/workbook/upload_file where $PATH is where you run git clone.
|
||||
|
||||
|
||||
<a name=examples>
|
||||
## Examples
|
||||
The source code is available on Github. You can get it by running the command:
|
||||
|
||||
```git clone https://github.com/EiffelWebFramework/ewf_examples.git```
|
||||
```git clone https://github.com/EiffelWebFramework/ewf.git```
|
||||
|
||||
The GET example is located in the directory $PATH/ewf_examples/workbook/form/get, and the post example is located in the directory $PATH/ewf_examples/workbook/form/post where $PATH is where you run git clone . To run open it using Eiffel Studio or just run theg following command
|
||||
The GET example is located in the directory $PATH/ewf/doc/workbook/form/get, and the post example is located in the directory $PATH/ewf_examples/workbook/form/post where $PATH is where you run git clone . To run open it using Eiffel Studio or just run theg following command
|
||||
|
||||
```estudio -config <ecf_name>.ecf -target <target_name>```
|
||||
|
||||
>Note: replace <ecf_name> and<target_name> with the corresponding values.
|
||||
|
||||
|
||||
|
||||
|
||||
Nav: [Workbook](../workbook.md) | [Basic Concepts] (/workbook/basics/basics.md) | [Handling Requests: Header Fields](/workbook/handling_request/headers.md)
|
||||
Nav: [Workbook](../workbook.md) | [Basic Concepts] (/doc/workbook/basics/basics.md) | [Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query parameters] (/workbook/handling_request/form.md) | [Generating Responses](/workbook/generating_response/generating_response.md)
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query parameters] (/doc/workbook/handling_request/form.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
|
||||
|
||||
#Handling Requests: Headers
|
||||
@@ -165,35 +165,26 @@ included in the Referer header when the browser requests Web page B.
|
||||
* [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](./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](/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 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).
|
||||
|
||||
The service also writes three components of the main request line (method, URI, and protocol), and also the raw header.
|
||||
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
set_service_option ("verbose", true)
|
||||
end
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
@@ -293,32 +284,21 @@ 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](./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](/doc/workbook/handling_request/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```
|
||||
feature to retrieve the current browser name or Unknown in other case.
|
||||
|
||||
```eiffel
|
||||
class
|
||||
APPLICATION
|
||||
APPLICATION_EXECUTION
|
||||
|
||||
inherit
|
||||
WSF_DEFAULT_SERVICE
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
WSF_EXECUTION
|
||||
|
||||
create
|
||||
make_and_launch
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
-- Initialize current service.
|
||||
do
|
||||
set_service_option ("port", 9090)
|
||||
set_service_option ("verbose", true)
|
||||
end
|
||||
make
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
@@ -329,8 +309,8 @@ feature -- Basic operations
|
||||
l_page_response: STRING
|
||||
l_rows: STRING
|
||||
do
|
||||
create l_page_response.make_from_string (html_template)
|
||||
if req.path_info.same_string ("/") then
|
||||
create l_page_response.make_from_string (html_template)
|
||||
if req.path_info.same_string ("/") then
|
||||
|
||||
-- retrieve the user-agent
|
||||
if attached req.http_user_agent as l_user_agent then
|
||||
@@ -404,7 +384,7 @@ end
|
||||
```
|
||||
Let see some results, we will show the html returned
|
||||
|
||||
Internet Explorer
|
||||
**Internet Explorer**
|
||||
---
|
||||
```
|
||||
<h1>EWF service example: Showing Browser Dectection Using User-Agent</h1></br>
|
||||
@@ -414,7 +394,7 @@ Internet Explorer
|
||||
<h2> Enjoy using Internet Explorer </h2>
|
||||
```
|
||||
|
||||
Chrome
|
||||
**Chrome**
|
||||
---
|
||||
```
|
||||
<h1>EWF service example: Showing Browser Dectection Using User-Agent</h1></br>
|
||||
@@ -448,10 +428,11 @@ As an exercise, try to write a similar service to retrieve the OS family using t
|
||||
* [SERVER_PROTOCOL](https://tools.ietf.org/html/rfc3875#section-4.1.15)
|
||||
* [SERVER_SOFTWARE](https://tools.ietf.org/html/rfc3875#section-4.1.16)
|
||||
|
||||
An [EWF service](./headers/cgi_variables/application.e) that shows the CGI variables, creates a table showing the values of all the CGI variables.
|
||||
**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] (/workbook/handling_request/form.md) | [Generating Responses](/workbook/generating_response/generating_response.md)
|
||||
Nav: [Workbook](../workbook.md) | [Handling Requests: Form/Query parameters] (/doc/workbook/handling_request/form.md) | [Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,20 +19,20 @@ Before reading (or walking throught) the workbook, to get a quick overview of EW
|
||||
|
||||
<a name="introduction"></a>
|
||||
## Introduction
|
||||
[Basic Concepts] (/workbook/basics/basics.md).
|
||||
[Basic Concepts] (/doc/workbook/basics/basics.md).
|
||||
|
||||
<a name="form_query_parameters"></a>
|
||||
## Handling Requests: Form/Query Parameter
|
||||
[Handling Requests: Form/Query Parameter] (/workbook/handling_request/form.md).
|
||||
[Handling Requests: Form/Query Parameter] (/doc/workbook/handling_request/form.md).
|
||||
|
||||
<a name="header_fields"></a>
|
||||
## Handling Requests: Header Fields
|
||||
[Handling Requests: Header Fields](/workbook/handling_request/headers.md).
|
||||
[Handling Requests: Header Fields](/doc/workbook/handling_request/headers.md).
|
||||
|
||||
<a name="header_fields"></a>
|
||||
## Generating Response
|
||||
[Generating Responses](/workbook/generating_response/generating_response.md)
|
||||
[Generating Responses](/doc/workbook/generating_response/generating_response.md)
|
||||
|
||||
## Handling Cookies
|
||||
[Handling Cookies](/workbook/handling_cookies/handling_cookies.md)
|
||||
[Handling Cookies](/doc/workbook/handling_cookies/handling_cookies.md)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ feature {NONE} -- Initialization
|
||||
setup_router
|
||||
do
|
||||
-- router.map (create {WSF_URI_MAPPING}.make ("/hello", create {WSF_AGENT_URI_HANDLER}.make (agent execute_hello)))
|
||||
map_uri_agent ("/hello", agent execute_hello)
|
||||
map_uri_agent ("/hello", agent execute_hello, Void)
|
||||
|
||||
-- router.map (create {WSF_URI_TEMPLATE_MAPPING}.make ("/users/{user}/message/{mesgid}", create {USER_MESSAGE_HANDLER}), router.methods_HEAD_GET_POST)
|
||||
map_uri_template ("/users/{user}/message/{mesgid}", create {USER_MESSAGE_HANDLER}, router.methods_HEAD_GET_POST)
|
||||
|
||||
@@ -1,13 +1,56 @@
|
||||
# simple HTTP client
|
||||
|
||||
## Overview
|
||||
It provides simple routine to perform http requests, and get response.
|
||||
|
||||
## Requirements
|
||||
* Eiffel cURL library
|
||||
* cURL dynamic libraries in the PATH or the current directory (.dll or .so)
|
||||
* One of the following
|
||||
- Eiffel cURL library
|
||||
- cURL dynamic libraries in the PATH or the current directory (.dll or .so)
|
||||
- Eiffel Net library
|
||||
- and optionally Eiffel NetSSL library to support https://
|
||||
|
||||
This means on Windows, do not forget to copy the libcurl.dll (and related) either in the same directory of the executable, or ensure the .dll are in the PATH environment.
|
||||
|
||||
It is possible to exclude the libcurl implementation xor the Eiffel Net implementation:
|
||||
In the .ecf configuration file of your project, you can use the following custom variables:
|
||||
|
||||
* Disable the libcurl implementation
|
||||
```
|
||||
<variable name="libcurl_http_client_disabled" value="True"/>
|
||||
```
|
||||
|
||||
* Disable the net implementation
|
||||
```
|
||||
<variable name="net_http_client_disabled" value="True"/>
|
||||
```
|
||||
|
||||
* If you disabled both, the http client will not work as expected.
|
||||
|
||||
For the net implementation (using EiffelNet), if you need https:// support, you need to enabled the ssl support with the custom variables :
|
||||
```
|
||||
<variable name="netssl_http_client_enabled" value="True"/>
|
||||
```
|
||||
* By default, SSL is not included (mostly because it is sometime a pain to get the needed dynamic libraries .dll or .so)
|
||||
|
||||
## Usage
|
||||
* To build code that is portable across the libcurl or net implementation of http_client library, use the DEFAULT_HTTP_CLIENT
|
||||
|
||||
```
|
||||
cl: DEFAULT_HTTP_CLIENT
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
create cl
|
||||
sess := cl.new_session ("http://example.com")
|
||||
if attached sess.get ("/path-to-test") as l_response then
|
||||
if not l_response.error_occurred then
|
||||
if attached l_response.body as l_body then
|
||||
print (l_body)
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
* See the tests/test-safe.ecf project to see how to use.
|
||||
* Examples will come in the future.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
|
||||
<target name="http_client">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -7,12 +7,78 @@
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<option debug="false" 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="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf">
|
||||
<condition>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http-safe.ecf"/>
|
||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
|
||||
<cluster name="spec_net" location="$|spec\net\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
<cluster name="net_implementation" location="$|implementation\" hidden="true">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_null" location="$|default\null\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_net" location="$|default\net\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_libcurl" location="$|default\libcurl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_libcurl_or_net" location="$|default\libcurl_or_net\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
|
||||
<target name="http_client">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -9,10 +9,76 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf">
|
||||
<condition>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http.ecf"/>
|
||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
|
||||
<library name="encoder" location="../../text/encoder/encoder.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
|
||||
<cluster name="spec_net" location="$|spec\net\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
<cluster name="net_implementation" location="$|implementation\" hidden="true">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true">
|
||||
<condition>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_null" location="$|default\null\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_net" location="$|default\net\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_libcurl" location="$|default\libcurl\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_libcurl_or_net" location="$|default\libcurl_or_net\">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
23
library/network/http_client/libcurl_http_client-safe.ecf
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="libcurl_http_client" uuid="FA686EE7-D01D-43A3-8E95-A00120658040" library_target="libcurl_http_client">
|
||||
<target name="libcurl_http_client">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option debug="false" 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="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http-safe.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
|
||||
<cluster name="default_libcurl" location="$|default\libcurl\"/>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
23
library/network/http_client/libcurl_http_client.ecf
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="libcurl_http_client" uuid="FA686EE7-D01D-43A3-8E95-A00120658040" library_target="libcurl_http_client">
|
||||
<target name="libcurl_http_client">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
|
||||
<cluster name="default_libcurl" location="$|default\libcurl\"/>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
40
library/network/http_client/net_http_client-safe.ecf
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="7897B317-7AD3-44E4-A933-0544A169AB1B" library_target="net_http_client">
|
||||
<target name="net_http_client">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option debug="false" 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="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http-safe.ecf"/>
|
||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-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="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="spec_net" location="$|spec\net\">
|
||||
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="default_net" location="$|default\net\"/>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
40
library/network/http_client/net_http_client.ecf
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="7897B317-7AD3-44E4-A933-0544A169AB1B" library_target="net_http_client">
|
||||
<target name="net_http_client">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="http" location="..\protocol\http\http.ecf"/>
|
||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.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="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="spec_net" location="$|spec\net\">
|
||||
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||
<condition>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="default_net" location="$|default\net\"/>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,15 @@
|
||||
note
|
||||
description: "[
|
||||
Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT.
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
DEFAULT_HTTP_CLIENT
|
||||
|
||||
inherit
|
||||
LIBCURL_HTTP_CLIENT
|
||||
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
note
|
||||
description: "[
|
||||
Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT or NET_HTTP_CLIENT.
|
||||
|
||||
The preference goes to libcurl implementation for now,
|
||||
since the net implementation has currently less functionalities.
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
DEFAULT_HTTP_CLIENT
|
||||
|
||||
inherit
|
||||
HTTP_CLIENT
|
||||
|
||||
feature -- Access
|
||||
|
||||
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
-- Create a new session using `a_base_url'.
|
||||
local
|
||||
libcurl: LIBCURL_HTTP_CLIENT
|
||||
net: NET_HTTP_CLIENT
|
||||
do
|
||||
--| For now, try libcurl first, and then net
|
||||
--| the reason is the net implementation is still in progress.
|
||||
create libcurl
|
||||
Result := libcurl.new_session (a_base_url)
|
||||
if not Result.is_available then
|
||||
create net
|
||||
Result := net.new_session (a_base_url)
|
||||
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
|
||||
@@ -0,0 +1,15 @@
|
||||
note
|
||||
description: "[
|
||||
Default HTTP_CLIENT based on NET_HTTP_CLIENT.
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
DEFAULT_HTTP_CLIENT
|
||||
|
||||
inherit
|
||||
NET_HTTP_CLIENT
|
||||
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
note
|
||||
description: "[
|
||||
Default HTTP_CLIENT based on NULL_HTTP_CLIENT.
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
DEFAULT_HTTP_CLIENT
|
||||
|
||||
inherit
|
||||
HTTP_CLIENT
|
||||
|
||||
feature -- Access
|
||||
|
||||
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
-- Create a new session using `a_base_url'.
|
||||
do
|
||||
create {NULL_HTTP_CLIENT_SESSION} Result.make (a_base_url)
|
||||
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
|
||||
@@ -9,7 +9,7 @@ note
|
||||
deferred class
|
||||
HTTP_CLIENT
|
||||
|
||||
feature -- Status
|
||||
feature -- Access
|
||||
|
||||
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
-- Create a new session using `a_base_url'.
|
||||
@@ -17,7 +17,7 @@ feature -- Status
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -14,24 +14,41 @@ inherit
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_url: READABLE_STRING_8; a_session: like session; ctx: like context)
|
||||
-- Initialize `Current'.
|
||||
make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context)
|
||||
-- Initialize `Current' with request url `a_url', method `a_request_method' within the session `a_session'
|
||||
-- and optional context `ctx' which can be used to pass additional parameters.
|
||||
do
|
||||
request_method := a_request_method
|
||||
session := a_session
|
||||
url := a_url
|
||||
headers := session.headers.twin
|
||||
if ctx /= Void then
|
||||
context := ctx
|
||||
import (ctx)
|
||||
end
|
||||
initialize (a_url, ctx)
|
||||
ensure
|
||||
context_set: context = ctx
|
||||
ctx_header_set: ctx /= Void implies across ctx.headers as ctx_h all attached headers.item (ctx_h.key) as v and then v.same_string (ctx_h.item) end
|
||||
end
|
||||
|
||||
initialize (a_url: READABLE_STRING_8; ctx: like context)
|
||||
-- Initialize Current with `a_url' and `ctx'.
|
||||
-- This can be used to reset/reinitialize Current with new url
|
||||
-- in the case of redirection.
|
||||
do
|
||||
url := a_url
|
||||
headers := session.headers.twin
|
||||
if ctx /= Void then
|
||||
context := ctx
|
||||
import (ctx)
|
||||
else
|
||||
context := Void
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Internal
|
||||
|
||||
session: HTTP_CLIENT_SESSION
|
||||
-- Session related to Current request.
|
||||
-- It provides a few parameters related to session.
|
||||
|
||||
context: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
||||
-- Potential additional parameters for this specific request.
|
||||
|
||||
feature -- Status report
|
||||
|
||||
@@ -44,16 +61,27 @@ feature -- Status report
|
||||
feature -- Access
|
||||
|
||||
request_method: READABLE_STRING_8
|
||||
deferred
|
||||
end
|
||||
-- Request method associated with Current request.
|
||||
|
||||
url: READABLE_STRING_8
|
||||
-- URL associated with current request.
|
||||
|
||||
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
||||
-- Specific headers to be used for current request.
|
||||
|
||||
response: HTTP_CLIENT_RESPONSE
|
||||
-- Response received from request execution.
|
||||
-- Check `error_occurred' for eventual error.
|
||||
-- note: two consecutive calls will trigger two executions!
|
||||
deferred
|
||||
ensure
|
||||
Result_set: Result /= Void
|
||||
end
|
||||
|
||||
feature {HTTP_CLIENT_SESSION} -- Execution
|
||||
|
||||
import (ctx: HTTP_CLIENT_REQUEST_CONTEXT)
|
||||
-- Import `ctx' parameters.
|
||||
local
|
||||
l_headers: like headers
|
||||
do
|
||||
@@ -67,10 +95,6 @@ feature {HTTP_CLIENT_SESSION} -- Execution
|
||||
end
|
||||
end
|
||||
|
||||
execute: HTTP_CLIENT_RESPONSE
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Authentication
|
||||
|
||||
auth_type: STRING
|
||||
@@ -88,21 +112,26 @@ feature -- Authentication
|
||||
end
|
||||
|
||||
username: detachable READABLE_STRING_32
|
||||
-- Username specified for the `session'.
|
||||
do
|
||||
Result := session.username
|
||||
end
|
||||
|
||||
password: detachable READABLE_STRING_32
|
||||
-- Password specified for the `session'.
|
||||
do
|
||||
Result := session.password
|
||||
end
|
||||
|
||||
credentials: detachable READABLE_STRING_32
|
||||
-- Credentials specified for the `session'.
|
||||
--| Usually `username':`password'
|
||||
do
|
||||
Result := session.credentials
|
||||
end
|
||||
|
||||
proxy: detachable TUPLE [host: READABLE_STRING_8; port: INTEGER]
|
||||
-- Optional proxy settings.
|
||||
do
|
||||
Result := session.proxy
|
||||
end
|
||||
@@ -220,7 +249,7 @@ feature {NONE} -- Utilities: encoding
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -81,7 +81,10 @@ feature -- Access
|
||||
-- Optional output file to get downloaded content and header
|
||||
|
||||
output_content_file: detachable FILE
|
||||
-- Optional output file to get downloaded content
|
||||
-- Optional output file to get downloaded content
|
||||
|
||||
http_version: detachable IMMUTABLE_STRING_8
|
||||
-- Overwrite default http version if set.
|
||||
|
||||
feature -- Status report
|
||||
|
||||
@@ -209,6 +212,17 @@ feature -- Element change
|
||||
output_content_file := f
|
||||
end
|
||||
|
||||
set_http_version (v: detachable READABLE_STRING_8)
|
||||
require
|
||||
valid_version: v = Void or else v.starts_with_general ("HTTP/")
|
||||
do
|
||||
if v = Void then
|
||||
http_version := Void
|
||||
else
|
||||
create http_version.make_from_string (v)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Status setting
|
||||
|
||||
set_proxy (a_host: detachable READABLE_STRING_8; a_port: INTEGER)
|
||||
@@ -264,7 +278,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -13,7 +13,7 @@ create
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_url: like url)
|
||||
make (a_url: READABLE_STRING_8)
|
||||
-- Initialize `Current'.
|
||||
do
|
||||
--| Default values
|
||||
@@ -54,12 +54,23 @@ feature -- Access
|
||||
|
||||
status_line: detachable READABLE_STRING_8
|
||||
|
||||
http_version: detachable READABLE_STRING_8
|
||||
-- http version associated with `status_line'.
|
||||
|
||||
raw_header: READABLE_STRING_8
|
||||
-- Raw http header of the response.
|
||||
|
||||
redirections: detachable ARRAYED_LIST [TUPLE [status_line: detachable READABLE_STRING_8; raw_header: READABLE_STRING_8; body: detachable READABLE_STRING_8]]
|
||||
-- Header of previous redirection if any.
|
||||
|
||||
redirections_count: INTEGER
|
||||
-- Number of redirections.
|
||||
do
|
||||
if attached redirections as lst then
|
||||
Result := lst.count
|
||||
end
|
||||
end
|
||||
|
||||
header (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||
-- Header entry value related to `a_name'
|
||||
-- if multiple entries, just concatenate them using comma character
|
||||
@@ -92,6 +103,24 @@ feature -- Access
|
||||
Result := s
|
||||
end
|
||||
|
||||
multiple_header (a_name: READABLE_STRING_8): detachable LIST [READABLE_STRING_8]
|
||||
-- Header multiple entries related to `a_name'
|
||||
local
|
||||
k: READABLE_STRING_8
|
||||
do
|
||||
across
|
||||
headers as hds
|
||||
loop
|
||||
k := hds.item.name
|
||||
if k.same_string (a_name) then
|
||||
if Result = Void then
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (1)
|
||||
end
|
||||
Result.force (hds.item.value)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
headers: LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
|
||||
-- Computed table of http headers of the response.
|
||||
--| We use a LIST since one might have multiple message-header fields with the same field-name
|
||||
@@ -115,7 +144,7 @@ feature -- Access
|
||||
loop
|
||||
l_start := pos
|
||||
--| Left justify
|
||||
from until not h[l_start].is_space loop
|
||||
from until not h [l_start].is_space loop
|
||||
l_start := l_start + 1
|
||||
end
|
||||
pos := h.index_of ('%N', l_start)
|
||||
@@ -129,7 +158,7 @@ feature -- Access
|
||||
end
|
||||
if l_end > 0 then
|
||||
--| Right justify
|
||||
from until not h[l_end].is_space loop
|
||||
from until not h [l_end].is_space loop
|
||||
l_end := l_end - 1
|
||||
end
|
||||
c := h.index_of (':', l_start)
|
||||
@@ -188,17 +217,72 @@ feature -- Access
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_http_1_0: BOOLEAN
|
||||
-- Is response using HTTP/1.0 protocole?
|
||||
--| Note: it is relevant once the raw header are set.
|
||||
do
|
||||
Result := attached http_version as v and then v.same_string ("1.0")
|
||||
end
|
||||
|
||||
is_http_1_1: BOOLEAN
|
||||
-- Is response using HTTP/1.1 protocole?
|
||||
--| Note: it is relevant once the raw header are set.
|
||||
do
|
||||
Result := attached http_version as v and then v.same_string ("1.1")
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
set_http_version (v: like http_version)
|
||||
-- Set `http_version' to `v'.
|
||||
do
|
||||
http_version := v
|
||||
end
|
||||
|
||||
set_status (s: INTEGER)
|
||||
-- Set response `status' code to `s'
|
||||
do
|
||||
status := s
|
||||
end
|
||||
|
||||
set_status_line (a_line: detachable READABLE_STRING_8)
|
||||
-- Set status line to `a_line',
|
||||
-- and also `status' extracted from `a_line' if possible.
|
||||
local
|
||||
i,j: INTEGER
|
||||
s: READABLE_STRING_8
|
||||
do
|
||||
status_line := a_line
|
||||
http_version := Void
|
||||
|
||||
if a_line /= Void then
|
||||
if a_line.starts_with ("HTTP/") then
|
||||
i := a_line.index_of (' ', 1)
|
||||
if i > 0 then
|
||||
http_version := a_line.substring (1 + 5, i - 1) -- ("HTTP/").count = 5
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
i := 1
|
||||
end
|
||||
-- Get status code token.
|
||||
if i > 0 then
|
||||
j := a_line.index_of (' ', i)
|
||||
if j > i then
|
||||
s := a_line.substring (i, j - 1)
|
||||
if s.is_integer then
|
||||
set_status (s.to_integer)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set_response_message (a_source: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT)
|
||||
-- Parse `a_source' response message
|
||||
-- and set `header' and `body'.
|
||||
-- and set `status_line', `status', `header' and `body'.
|
||||
--| ctx is the context associated with the request
|
||||
--| it might be useful to deal with redirection customization...
|
||||
local
|
||||
@@ -234,7 +318,7 @@ feature -- Change
|
||||
|
||||
j := i + 2
|
||||
pos := j
|
||||
status_line := l_status_line
|
||||
set_status_line (l_status_line)
|
||||
set_raw_header (h)
|
||||
|
||||
-- libcURL does not cache redirection content.
|
||||
@@ -277,10 +361,23 @@ feature -- Change
|
||||
|
||||
set_raw_header (h: READABLE_STRING_8)
|
||||
-- Set http header `raw_header' to `h'
|
||||
local
|
||||
i: INTEGER
|
||||
s: STRING_8
|
||||
do
|
||||
raw_header := h
|
||||
--| Reset internal headers
|
||||
internal_headers := Void
|
||||
|
||||
--| Set status line, right away.
|
||||
i := h.index_of ('%N', 1)
|
||||
if i > 0 then
|
||||
s := h.substring (1, i - 1)
|
||||
if s.starts_with ("HTTP/") then
|
||||
s.right_adjust
|
||||
set_status_line (s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
add_redirection (s: detachable READABLE_STRING_8; h: READABLE_STRING_8; a_body: detachable READABLE_STRING_8)
|
||||
@@ -308,7 +405,7 @@ feature {NONE} -- Implementation
|
||||
-- Internal cached value for the headers
|
||||
|
||||
;note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -50,6 +50,14 @@ feature {NONE} -- Initialization
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
close
|
||||
-- Close session.
|
||||
--| useful to disconnect persistent connection.
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
url (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): STRING_8
|
||||
-- Url computed from Current and `ctx' data.
|
||||
local
|
||||
@@ -77,6 +85,61 @@ feature -- Basic operation
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Access: verbose
|
||||
|
||||
verbose_mode: INTEGER
|
||||
-- Internal verbose mode.
|
||||
|
||||
verbose_header_sent_mode: INTEGER = 1 --| 0001
|
||||
verbose_header_received_mode: INTEGER = 2 --| 0010
|
||||
verbose_debug_mode: INTEGER = 4 --| 0100
|
||||
|
||||
feature -- Access: verbose
|
||||
|
||||
is_header_sent_verbose: BOOLEAN
|
||||
do
|
||||
Result := verbose_mode & verbose_header_sent_mode = verbose_header_sent_mode
|
||||
end
|
||||
|
||||
is_header_received_verbose: BOOLEAN
|
||||
do
|
||||
Result := verbose_mode & verbose_header_received_mode = verbose_header_received_mode
|
||||
end
|
||||
|
||||
is_debug_verbose: BOOLEAN
|
||||
do
|
||||
Result := verbose_mode & verbose_debug_mode = verbose_debug_mode
|
||||
end
|
||||
|
||||
feature -- Element change: verbose
|
||||
|
||||
set_header_sent_verbose (b: BOOLEAN)
|
||||
do
|
||||
if b then
|
||||
verbose_mode := verbose_mode | verbose_header_sent_mode
|
||||
else
|
||||
verbose_mode := verbose_mode & verbose_header_sent_mode.bit_not
|
||||
end
|
||||
end
|
||||
|
||||
set_header_received_verbose (b: BOOLEAN)
|
||||
do
|
||||
if b then
|
||||
verbose_mode := verbose_mode | verbose_header_received_mode
|
||||
else
|
||||
verbose_mode := verbose_mode & verbose_header_received_mode.bit_not
|
||||
end
|
||||
end
|
||||
|
||||
set_debug_verbose (b: BOOLEAN)
|
||||
do
|
||||
if b then
|
||||
verbose_mode := verbose_mode | verbose_debug_mode
|
||||
else
|
||||
verbose_mode := verbose_mode & verbose_debug_mode.bit_not
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Custom
|
||||
|
||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
@@ -88,17 +151,23 @@ feature -- Helper
|
||||
|
||||
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- Response for GET request based on Current, `a_path' and `ctx'.
|
||||
require
|
||||
is_available: is_available
|
||||
deferred
|
||||
end
|
||||
|
||||
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- Response for HEAD request based on Current, `a_path' and `ctx'.
|
||||
require
|
||||
is_available: is_available
|
||||
deferred
|
||||
end
|
||||
|
||||
post (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Response for POST request based on Current, `a_path' and `ctx'
|
||||
-- with input `data'
|
||||
require
|
||||
is_available: is_available
|
||||
deferred
|
||||
end
|
||||
|
||||
@@ -111,29 +180,39 @@ feature -- Helper
|
||||
patch (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Response for PATCH request based on Current, `a_path' and `ctx'
|
||||
-- with input `data'
|
||||
require
|
||||
is_available: is_available
|
||||
deferred
|
||||
end
|
||||
|
||||
patch_file (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Response for PATCH request based on Current, `a_path' and `ctx'
|
||||
-- with uploaded data file `fn'
|
||||
require
|
||||
is_available: is_available
|
||||
deferred
|
||||
end
|
||||
|
||||
put (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Response for PUT request based on Current, `a_path' and `ctx'
|
||||
-- with input `data'
|
||||
require
|
||||
is_available: is_available
|
||||
deferred
|
||||
end
|
||||
|
||||
put_file (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Response for PUT request based on Current, `a_path' and `ctx'
|
||||
-- with uploaded file `fn'
|
||||
require
|
||||
is_available: is_available
|
||||
deferred
|
||||
end
|
||||
|
||||
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- Response for DELETE request based on Current, `a_path' and `ctx'
|
||||
require
|
||||
is_available: is_available
|
||||
deferred
|
||||
end
|
||||
|
||||
@@ -185,6 +264,9 @@ feature -- Access
|
||||
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
||||
-- Headers common to any request created by Current session.
|
||||
|
||||
cookie: detachable READABLE_STRING_8
|
||||
-- Cookie for the current base_url
|
||||
|
||||
feature -- Authentication
|
||||
|
||||
auth_type: STRING
|
||||
@@ -194,11 +276,15 @@ feature -- Authentication
|
||||
auth_type_id: INTEGER
|
||||
-- See {HTTP_CLIENT_CONSTANTS}.Auth_type_*
|
||||
|
||||
username,
|
||||
username: detachable READABLE_STRING_32
|
||||
-- Associated optional username value.
|
||||
|
||||
password: detachable READABLE_STRING_32
|
||||
-- Associated optional password value.
|
||||
|
||||
credentials: detachable READABLE_STRING_32
|
||||
|
||||
-- Associated optional credentials value.
|
||||
-- Computed as `username':`password'.
|
||||
|
||||
feature -- Status setting
|
||||
|
||||
@@ -212,6 +298,12 @@ feature -- Element change
|
||||
set_base_url (u: like base_url)
|
||||
do
|
||||
base_url := u
|
||||
cookie := Void
|
||||
end
|
||||
|
||||
set_cookie (a_cookie: detachable READABLE_STRING_8)
|
||||
do
|
||||
cookie := a_cookie
|
||||
end
|
||||
|
||||
set_timeout (n_seconds: like timeout)
|
||||
@@ -244,12 +336,26 @@ feature -- Element change
|
||||
headers.prune (k)
|
||||
end
|
||||
|
||||
set_credentials (u: like username; p: like password)
|
||||
set_credentials (u,p: detachable READABLE_STRING_GENERAL)
|
||||
local
|
||||
s: STRING_32
|
||||
do
|
||||
username := u
|
||||
password := p
|
||||
if u = Void then
|
||||
username := Void
|
||||
else
|
||||
create {STRING_32} username.make_from_string_general (u)
|
||||
end
|
||||
if p = Void then
|
||||
password := Void
|
||||
else
|
||||
create {STRING_32} password.make_from_string_general (p)
|
||||
end
|
||||
if u /= Void and p /= Void then
|
||||
credentials := u + ":" + p
|
||||
create s.make (u.count + 1 + p.count)
|
||||
s.append_string_general (u)
|
||||
s.append_character (':')
|
||||
s.append_string_general (p)
|
||||
credentials := s
|
||||
else
|
||||
credentials := Void
|
||||
end
|
||||
@@ -300,7 +406,7 @@ feature -- Element change
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
note
|
||||
description : "[
|
||||
Specific implementation of HTTP_CLIENT based on Eiffel cURL library
|
||||
|
||||
WARNING: Do not forget to have the dynamic libraries libcurl (.dll or .so)
|
||||
and related accessible to the executable (i.e in same directory, or in the PATH)
|
||||
]"
|
||||
author : "$Author$"
|
||||
date : "$Date$"
|
||||
@@ -13,6 +16,7 @@ inherit
|
||||
HTTP_CLIENT
|
||||
|
||||
create
|
||||
default_create,
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
@@ -20,6 +24,7 @@ feature {NONE} -- Initialization
|
||||
make
|
||||
-- Initialize `Current'.
|
||||
do
|
||||
default_create
|
||||
end
|
||||
|
||||
feature -- Status
|
||||
@@ -30,7 +35,7 @@ feature -- Status
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
note
|
||||
description: "[
|
||||
Specific implementation of HTTP_CLIENT_REQUEST based on Eiffel cURL library
|
||||
|
||||
WARNING: Do not forget to have the dynamic libraries libcurl (.dll or .so)
|
||||
and related accessible to the executable (i.e in same directory, or in the PATH)
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
@@ -10,9 +13,8 @@ class
|
||||
|
||||
inherit
|
||||
HTTP_CLIENT_REQUEST
|
||||
rename
|
||||
make as make_request
|
||||
redefine
|
||||
make,
|
||||
session
|
||||
end
|
||||
|
||||
@@ -23,8 +25,7 @@ feature {NONE} -- Initialization
|
||||
|
||||
make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context)
|
||||
do
|
||||
make_request (a_url, a_session, ctx)
|
||||
request_method := a_request_method
|
||||
Precursor (a_url, a_request_method, a_session, ctx)
|
||||
apply_workaround
|
||||
end
|
||||
|
||||
@@ -38,13 +39,10 @@ feature {NONE} -- Initialization
|
||||
|
||||
session: LIBCURL_HTTP_CLIENT_SESSION
|
||||
|
||||
feature -- Access
|
||||
|
||||
request_method: READABLE_STRING_8
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute: HTTP_CLIENT_RESPONSE
|
||||
response: HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
local
|
||||
l_result: INTEGER
|
||||
l_curl_string: detachable CURL_STRING
|
||||
@@ -63,6 +61,8 @@ feature -- Execution
|
||||
l_upload_data: detachable READABLE_STRING_8
|
||||
l_upload_filename: detachable READABLE_STRING_GENERAL
|
||||
l_headers: like headers
|
||||
l_is_http_1_0: BOOLEAN
|
||||
l_uri: URI
|
||||
do
|
||||
if not retried then
|
||||
curl := session.curl
|
||||
@@ -72,6 +72,10 @@ feature -- Execution
|
||||
|
||||
ctx := context
|
||||
|
||||
if ctx /= Void then
|
||||
l_is_http_1_0 := attached ctx.http_version as l_http_version and then l_http_version.same_string ("HTTP/1.0")
|
||||
end
|
||||
|
||||
--| Configure cURL session
|
||||
initialize_curl_session (ctx, curl, curl_easy, curl_handle)
|
||||
|
||||
@@ -81,12 +85,48 @@ feature -- Execution
|
||||
append_parameters_to_url (ctx.query_parameters, l_url)
|
||||
end
|
||||
|
||||
if session.is_header_sent_verbose then
|
||||
io.error.put_string ("> Sending:%N")
|
||||
create l_uri.make_from_string (l_url)
|
||||
io.error.put_string ("> ")
|
||||
io.error.put_string (request_method + " " + l_uri.path)
|
||||
if attached l_uri.query as q then
|
||||
io.error.put_string (q)
|
||||
end
|
||||
if l_is_http_1_0 then
|
||||
io.error.put_string (" HTTP/1.0")
|
||||
else
|
||||
io.error.put_string (" HTTP/1.1")
|
||||
end
|
||||
io.error.put_new_line
|
||||
if attached l_uri.host as l_host then
|
||||
io.error.put_string ("> ")
|
||||
io.error.put_string ("Host: " + l_host)
|
||||
io.error.put_new_line
|
||||
end
|
||||
across
|
||||
headers as ic
|
||||
loop
|
||||
io.error.put_string ("> ")
|
||||
io.error.put_string (ic.key)
|
||||
io.error.put_string (": ")
|
||||
io.error.put_string (ic.item)
|
||||
io.error.put_new_line
|
||||
end
|
||||
io.error.put_string ("> ... ")
|
||||
io.error.put_new_line
|
||||
end
|
||||
|
||||
debug ("service")
|
||||
io.put_string ("SERVICE: " + l_url)
|
||||
io.put_new_line
|
||||
end
|
||||
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_url, l_url)
|
||||
|
||||
if l_is_http_1_0 then
|
||||
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_http_version, {CURL_OPT_CONSTANTS}.curl_http_version_1_0)
|
||||
else
|
||||
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_http_version, {CURL_OPT_CONSTANTS}.curl_http_version_none)
|
||||
end
|
||||
l_headers := headers
|
||||
|
||||
-- Context
|
||||
@@ -241,6 +281,10 @@ feature -- Execution
|
||||
Result.status := response_status_code (curl_easy, curl_handle)
|
||||
if l_curl_string /= Void then
|
||||
Result.set_response_message (l_curl_string.string, ctx)
|
||||
if session.is_header_received_verbose then
|
||||
io.error.put_string ("< Receiving:%N")
|
||||
io.error.put_string (Result.raw_header)
|
||||
end
|
||||
end
|
||||
else
|
||||
Result.set_error_message ("Error: cURL Error[" + l_result.out + "]")
|
||||
@@ -390,7 +434,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
note
|
||||
description: "[
|
||||
Specific implementation of HTTP_CLIENT_SESSION based on Eiffel cURL library
|
||||
|
||||
WARNING: Do not forget to have the dynamic libraries libcurl (.dll or .so)
|
||||
and related accessible to the executable (i.e in same directory, or in the PATH)
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
@@ -34,19 +37,22 @@ feature -- Status report
|
||||
feature -- Custom
|
||||
|
||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
local
|
||||
req: HTTP_CLIENT_REQUEST
|
||||
do
|
||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||
Result := req.execute
|
||||
Result := req.response
|
||||
end
|
||||
|
||||
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Same as `custom' but including upload data `a_data'.
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Same as `custom' but including upload file `fn'.
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||
end
|
||||
@@ -54,46 +60,55 @@ feature -- Custom
|
||||
feature -- Helper
|
||||
|
||||
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("GET", a_path, ctx)
|
||||
end
|
||||
|
||||
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("HEAD", a_path, ctx)
|
||||
end
|
||||
|
||||
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("DELETE", a_path, ctx)
|
||||
end
|
||||
@@ -140,7 +155,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||
Result := req.execute
|
||||
Result := req.response
|
||||
|
||||
if f /= Void then
|
||||
f.delete
|
||||
@@ -161,7 +176,7 @@ feature {LIBCURL_HTTP_CLIENT_REQUEST} -- Curl implementation
|
||||
|
||||
|
||||
;note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||
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
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
note
|
||||
description: "Socket connection information, used for peristent connection implementation."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
NET_HTTP_CLIENT_CONNECTION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_socket: NETWORK_STREAM_SOCKET; a_host: READABLE_STRING_GENERAL; a_port: INTEGER)
|
||||
do
|
||||
socket := a_socket
|
||||
host := a_host
|
||||
port := a_port
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
socket: NETWORK_STREAM_SOCKET
|
||||
-- Persistent connection socket.
|
||||
|
||||
host: READABLE_STRING_GENERAL
|
||||
-- Host used for this connection.
|
||||
|
||||
port: INTEGER
|
||||
-- Port used for this connection.
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_reusable (a_host: READABLE_STRING_GENERAL; a_port: INTEGER): BOOLEAN
|
||||
-- Is Current connection reusable for new connection `a_host:a_port'?
|
||||
do
|
||||
if a_host.same_string (host) and port = a_port then
|
||||
Result := socket.is_connected
|
||||
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
|
||||
34
library/network/http_client/src/spec/net/net_http_client.e
Normal file
@@ -0,0 +1,34 @@
|
||||
note
|
||||
description : "[
|
||||
Specific implementation of HTTP_CLIENT based on Eiffel NET library
|
||||
|
||||
WARNING: this is work in progress
|
||||
]"
|
||||
author : "$Author$"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
|
||||
class
|
||||
NET_HTTP_CLIENT
|
||||
|
||||
inherit
|
||||
HTTP_CLIENT
|
||||
|
||||
feature -- Status
|
||||
|
||||
new_session (a_base_url: READABLE_STRING_8): NET_HTTP_CLIENT_SESSION
|
||||
do
|
||||
create Result.make (a_base_url)
|
||||
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
|
||||
@@ -0,0 +1,739 @@
|
||||
note
|
||||
description: "[
|
||||
Specific implementation of HTTP_CLIENT_REQUEST based on Eiffel NET library
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
NET_HTTP_CLIENT_REQUEST
|
||||
|
||||
inherit
|
||||
HTTP_CLIENT_REQUEST
|
||||
redefine
|
||||
session
|
||||
end
|
||||
|
||||
TRANSFER_COMMAND_CONSTANTS
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
SHARED_EXECUTION_ENVIRONMENT
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Internal
|
||||
|
||||
session: NET_HTTP_CLIENT_SESSION
|
||||
net_http_client_version: STRING = "0.1"
|
||||
|
||||
session_socket (a_host: READABLE_STRING_8; a_port: INTEGER; a_is_https: BOOLEAN; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): NETWORK_STREAM_SOCKET
|
||||
-- Session socket to use for connection.
|
||||
-- Eventually reuse the persistent connection if any.
|
||||
local
|
||||
l_socket: detachable NETWORK_STREAM_SOCKET
|
||||
do
|
||||
if
|
||||
attached session.persistent_connection as l_persistent_connection and then
|
||||
l_persistent_connection.is_reusable (a_host, a_port)
|
||||
then
|
||||
l_socket := l_persistent_connection.socket
|
||||
if a_is_https then
|
||||
if attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
|
||||
Result := l_ssl_socket
|
||||
else
|
||||
l_socket := Void
|
||||
end
|
||||
elseif attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
|
||||
l_socket := Void
|
||||
end
|
||||
if l_socket /= Void and then not l_socket.is_connected then
|
||||
-- Reset persistent connection
|
||||
l_socket := Void
|
||||
end
|
||||
end
|
||||
if l_socket /= Void then
|
||||
-- Reuse persistent connection.
|
||||
Result := l_socket
|
||||
else
|
||||
session.set_persistent_connection (Void)
|
||||
if a_is_https then
|
||||
create {SSL_NETWORK_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
|
||||
else
|
||||
create Result.make_client_by_port (a_port, a_host)
|
||||
end
|
||||
Result.set_connect_timeout (connect_timeout)
|
||||
Result.set_timeout (timeout)
|
||||
Result.connect
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
response: HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
local
|
||||
redirection_response: detachable like response
|
||||
l_uri: URI
|
||||
l_header_key: READABLE_STRING_8
|
||||
l_host: READABLE_STRING_8
|
||||
l_cookie: detachable READABLE_STRING_8
|
||||
l_request_uri: STRING
|
||||
l_url: HTTP_URL
|
||||
l_socket: NETWORK_STREAM_SOCKET
|
||||
s: STRING
|
||||
l_message: STRING
|
||||
l_content_length: INTEGER
|
||||
l_location: detachable READABLE_STRING_8
|
||||
l_port: INTEGER
|
||||
l_is_https: BOOLEAN
|
||||
l_authorization: HTTP_AUTHORIZATION
|
||||
l_platform: STRING
|
||||
l_upload_data: detachable READABLE_STRING_8
|
||||
l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
|
||||
ctx: like context
|
||||
l_upload_file: detachable RAW_FILE
|
||||
l_upload_filename: detachable READABLE_STRING_GENERAL
|
||||
l_form_string: STRING
|
||||
l_prev_header: READABLE_STRING_8
|
||||
l_boundary: READABLE_STRING_8
|
||||
l_is_http_1_0_request: BOOLEAN
|
||||
l_is_keep_alive: BOOLEAN
|
||||
retried: BOOLEAN
|
||||
do
|
||||
if not retried then
|
||||
ctx := context
|
||||
if ctx /= Void then
|
||||
l_is_http_1_0_request := attached ctx.http_version as l_http_version and then l_http_version.same_string ("HTTP/1.0")
|
||||
end
|
||||
create Result.make (url)
|
||||
|
||||
-- Get URL data
|
||||
l_is_https := url.starts_with_general ("https://")
|
||||
create l_uri.make_from_string (url)
|
||||
l_port := l_uri.port
|
||||
if l_port = 0 then
|
||||
if l_is_https then
|
||||
l_port := 443
|
||||
else
|
||||
l_port := 80
|
||||
end
|
||||
end
|
||||
if attached l_uri.host as h then
|
||||
l_host := h
|
||||
else
|
||||
create l_url.make (url)
|
||||
l_host := l_url.host
|
||||
end
|
||||
|
||||
if attached session.proxy as l_proxy_settings then
|
||||
-- For now, so proxy support.
|
||||
check
|
||||
not_supported: False
|
||||
end
|
||||
end
|
||||
|
||||
-- Connect
|
||||
l_socket := session_socket (l_host, l_port, l_is_https, ctx)
|
||||
if l_socket.is_connected then
|
||||
|
||||
create l_form_string.make_empty
|
||||
|
||||
-- add headers for authorization
|
||||
if not headers.has ("Authorization") then
|
||||
if
|
||||
attached username as u_name and
|
||||
attached password as u_pass
|
||||
then
|
||||
create l_authorization.make_basic_auth (u_name, u_pass)
|
||||
if attached l_authorization.http_authorization as auth then
|
||||
headers.extend (auth, "Authorization")
|
||||
end
|
||||
check headers.has_key ("Authorization") end
|
||||
end
|
||||
end
|
||||
|
||||
create l_request_uri.make_from_string (l_uri.path)
|
||||
if attached l_uri.query as l_query then
|
||||
l_request_uri.append_character ('?')
|
||||
l_request_uri.append (l_query)
|
||||
end
|
||||
|
||||
-- add computed header User-Agent if not yet set.
|
||||
if not headers.has ("User-Agent") then
|
||||
if {PLATFORM}.is_unix then
|
||||
l_platform := "Unix"
|
||||
elseif {PLATFORM}.is_windows then
|
||||
l_platform := "Windows"
|
||||
elseif {PLATFORM}.is_mac then
|
||||
l_platform := "Mac"
|
||||
elseif {PLATFORM}.is_vms then
|
||||
l_platform := "VMS"
|
||||
elseif {PLATFORM}.is_vxworks then
|
||||
l_platform := "VxWorks"
|
||||
else
|
||||
l_platform := "Unknown"
|
||||
end
|
||||
headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
|
||||
end
|
||||
|
||||
-- handle sending data
|
||||
if ctx /= Void then
|
||||
if ctx.has_upload_filename then
|
||||
l_upload_filename := ctx.upload_filename
|
||||
end
|
||||
|
||||
if ctx.has_upload_data then
|
||||
l_upload_data := ctx.upload_data
|
||||
end
|
||||
|
||||
if ctx.has_form_data then
|
||||
l_form_data := ctx.form_parameters
|
||||
if l_upload_data = Void and l_upload_filename = Void then
|
||||
-- Send as form-urlencoded
|
||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||
l_upload_data := ctx.form_parameters_to_url_encoded_string
|
||||
headers.force (l_upload_data.count.out, "Content-Length")
|
||||
|
||||
else
|
||||
-- create form using multipart/form-data encoding
|
||||
l_boundary := new_mime_boundary
|
||||
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
|
||||
if l_form_data /= Void then
|
||||
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
|
||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||
end
|
||||
end
|
||||
elseif l_upload_data /= Void then
|
||||
check ctx.has_upload_data end
|
||||
if not headers.has ("Content-Type") then
|
||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||
end
|
||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||
elseif l_upload_filename /= Void then
|
||||
check ctx.has_upload_filename end
|
||||
create l_upload_file.make_with_name (l_upload_filename)
|
||||
if l_upload_file.exists and then l_upload_file.readable then
|
||||
headers.extend (l_upload_file.count.out, "Content-Length")
|
||||
end
|
||||
check l_upload_file /= Void end
|
||||
end
|
||||
end
|
||||
|
||||
-- FIXME: check usage of headers and specific header variable.
|
||||
--| only one Cookie: is allowed, so merge multiple into one;
|
||||
--| if Host is in header, use that one.
|
||||
-- Compute Request line.
|
||||
create s.make_from_string (request_method.as_upper)
|
||||
s.append_character (' ')
|
||||
s.append (l_request_uri)
|
||||
s.append_character (' ')
|
||||
if l_is_http_1_0_request then
|
||||
s.append ("HTTP/1.0")
|
||||
else
|
||||
s.append ("HTTP/1.1")
|
||||
end
|
||||
s.append (Http_end_of_header_line)
|
||||
|
||||
-- Compute Header Host:
|
||||
s.append (Http_host_header)
|
||||
s.append (": ")
|
||||
if attached headers [Http_host_header] as h_host then
|
||||
s.append (h_host)
|
||||
else
|
||||
s.append (l_host)
|
||||
end
|
||||
s.append (http_end_of_header_line)
|
||||
if not headers.has ("Connection") then
|
||||
if l_is_http_1_0_request then
|
||||
s.append ("Connection: keep-alive")
|
||||
s.append (http_end_of_header_line)
|
||||
end
|
||||
end
|
||||
|
||||
-- Append the given request headers
|
||||
l_cookie := Void
|
||||
if not headers.is_empty then
|
||||
across
|
||||
headers as ic
|
||||
loop
|
||||
l_header_key := ic.key
|
||||
if l_header_key.same_string_general ("Host") then
|
||||
-- FIXME: already handled elsewhere!
|
||||
elseif l_header_key.same_string_general ("Cookie") then
|
||||
-- FIXME: need cookie merging.
|
||||
l_cookie := ic.item
|
||||
else
|
||||
s.append (ic.key)
|
||||
s.append (": ")
|
||||
s.append (ic.item)
|
||||
s.append (Http_end_of_header_line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Compute Header Cookie: if needed
|
||||
-- Use session cookie
|
||||
if l_cookie = Void then
|
||||
l_cookie := session.cookie
|
||||
else
|
||||
-- Overwrite potential session cookie, if specified by the user.
|
||||
end
|
||||
if l_cookie /= Void then
|
||||
s.append ("Cookie: ")
|
||||
s.append (l_cookie)
|
||||
s.append (http_end_of_header_line)
|
||||
end
|
||||
|
||||
--| End of client header.
|
||||
s.append (Http_end_of_header_line)
|
||||
|
||||
if l_upload_data /= Void then
|
||||
s.append (l_upload_data)
|
||||
s.append (http_end_of_header_line)
|
||||
end
|
||||
|
||||
--| Note that any remaining file to upload will be done directly via the socket
|
||||
--| to optimize memory usage
|
||||
|
||||
|
||||
--|-----------------------------|--
|
||||
--| Request preparation is done |--
|
||||
--|-----------------------------|--
|
||||
|
||||
if l_socket.ready_for_writing then
|
||||
--| Socket is ready for writing, so let's send the request.
|
||||
|
||||
--|-------------------------|--
|
||||
--| Send request |--
|
||||
--|-------------------------|--
|
||||
|
||||
if session.is_header_sent_verbose then
|
||||
log ("> Sending:%N")
|
||||
log (s)
|
||||
end
|
||||
l_socket.put_string (s)
|
||||
--| Send remaining payload data, if needed.
|
||||
if l_upload_file /= Void then
|
||||
-- i.e: not yet processed
|
||||
append_file_content_to_socket (l_upload_file, l_upload_file.count, l_socket)
|
||||
end
|
||||
|
||||
--|-------------------------|--
|
||||
--| Get response. |--
|
||||
--| Get header message |--
|
||||
--|-------------------------|--
|
||||
if is_ready_for_reading (l_socket) then
|
||||
create l_message.make_empty
|
||||
append_socket_header_content_to (Result, l_socket, l_message)
|
||||
if session.is_header_received_verbose then
|
||||
log ("< Receiving:%N")
|
||||
log (l_message)
|
||||
end
|
||||
l_prev_header := Result.raw_header
|
||||
Result.set_raw_header (l_message.string)
|
||||
l_message.append (http_end_of_header_line)
|
||||
|
||||
if not Result.error_occurred then
|
||||
-- Get information from header
|
||||
l_content_length := -1
|
||||
if attached Result.header ("Content-Length") as s_len and then s_len.is_integer then
|
||||
l_content_length := s_len.to_integer
|
||||
end
|
||||
l_location := Result.header ("Location")
|
||||
if attached Result.header ("Set-Cookie") as s_cookies then
|
||||
session.set_cookie (s_cookies)
|
||||
end
|
||||
|
||||
-- Keep-alive connection?
|
||||
-- with HTTP/1.1, this is the default, and could be changed by Connection: close
|
||||
-- with HTTP/1.0, it requires "Connection: keep-alive" header line.
|
||||
if attached Result.header ("Connection") as s_connection then
|
||||
l_is_keep_alive := s_connection.same_string ("keep-alive")
|
||||
else
|
||||
l_is_keep_alive := not Result.is_http_1_0
|
||||
end
|
||||
|
||||
-- Get content if any.
|
||||
append_socket_content_to (Result, l_socket, l_content_length, l_message)
|
||||
-- Restore previous header
|
||||
Result.set_raw_header (l_prev_header)
|
||||
-- Set message
|
||||
Result.set_response_message (l_message, ctx)
|
||||
-- Check status code.
|
||||
check status_coherent: attached Result.status_line as l_status_line implies l_status_line.has_substring (Result.status.out) end
|
||||
|
||||
if l_is_keep_alive then
|
||||
session.set_persistent_connection (create {NET_HTTP_CLIENT_CONNECTION}.make (l_socket, l_host, l_port))
|
||||
else
|
||||
session.set_persistent_connection (Void)
|
||||
end
|
||||
|
||||
-- follow redirect
|
||||
if l_location /= Void then
|
||||
if Result.redirections_count < max_redirects then
|
||||
initialize (l_location, ctx)
|
||||
redirection_response := response
|
||||
redirection_response.add_redirection (Result.status_line, Result.raw_header, Result.body)
|
||||
Result := redirection_response
|
||||
end
|
||||
end
|
||||
if not l_is_keep_alive then
|
||||
l_socket.cleanup
|
||||
end
|
||||
end
|
||||
else
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: Read Timeout!%N")
|
||||
end
|
||||
Result.set_error_message ("Read Timeout")
|
||||
end
|
||||
else
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: Write Timeout!%N")
|
||||
end
|
||||
Result.set_error_message ("Write Timeout")
|
||||
end
|
||||
else
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: Could not connect!%N")
|
||||
end
|
||||
Result.set_error_message ("Could not connect")
|
||||
end
|
||||
else
|
||||
create Result.make (url)
|
||||
Result.set_error_message ("Error: internal error")
|
||||
end
|
||||
rescue
|
||||
retried := True
|
||||
retry
|
||||
end
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
log (m: READABLE_STRING_8)
|
||||
-- Output log messages.
|
||||
do
|
||||
io.error.put_string (m)
|
||||
end
|
||||
|
||||
is_ready_for_reading (a_socket: NETWORK_STREAM_SOCKET): BOOLEAN
|
||||
-- Is `a_socket' ready for reading?
|
||||
do
|
||||
Result := a_socket.ready_for_reading
|
||||
end
|
||||
|
||||
form_date_and_uploaded_files_to_mime_string (a_form_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]; a_upload_filename: detachable READABLE_STRING_GENERAL; a_mime_boundary: READABLE_STRING_8): STRING
|
||||
-- Form data and uploaded files converted to mime string.
|
||||
-- TODO: design a proper MIME... component.
|
||||
local
|
||||
l_path: PATH
|
||||
l_mime_type: READABLE_STRING_8
|
||||
l_upload_file: detachable RAW_FILE
|
||||
l_mime_type_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING
|
||||
do
|
||||
create Result.make (100)
|
||||
across
|
||||
a_form_parameters as ic
|
||||
loop
|
||||
Result.append (a_mime_boundary)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append ("Content-Disposition: form-data; name=")
|
||||
Result.append_character ('%"')
|
||||
Result.append (string_to_mime_encoded_string (ic.key))
|
||||
Result.append_character ('%"')
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append (string_to_mime_encoded_string (ic.item))
|
||||
Result.append (http_end_of_header_line)
|
||||
end
|
||||
|
||||
if a_upload_filename /= Void then
|
||||
-- get file extension, otherwise set default
|
||||
create l_mime_type_mapping.make_default
|
||||
create l_path.make_from_string (a_upload_filename)
|
||||
if
|
||||
attached l_path.extension as ext and then
|
||||
attached l_mime_type_mapping.mime_type (ext) as l_mt
|
||||
then
|
||||
l_mime_type := l_mt
|
||||
else
|
||||
l_mime_type := "application/octet-stream"
|
||||
end
|
||||
|
||||
Result.append (a_mime_boundary)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append ("Content-Disposition: form-data; name=%"")
|
||||
Result.append (string_to_mime_encoded_string (a_upload_filename))
|
||||
Result.append_character ('%"')
|
||||
Result.append ("; filename=%"")
|
||||
Result.append (string_to_mime_encoded_string (a_upload_filename))
|
||||
Result.append_character ('%"')
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append ("Content-Type: ")
|
||||
Result.append (l_mime_type)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append (http_end_of_header_line)
|
||||
|
||||
create l_upload_file.make_with_path (l_path)
|
||||
if l_upload_file.exists and then l_upload_file.is_access_readable then
|
||||
append_file_content_to (l_upload_file, l_upload_file.count, Result)
|
||||
-- Reset l_upload_file to Void, since the related content is already processed.
|
||||
l_upload_file := Void
|
||||
end
|
||||
Result.append (http_end_of_header_line)
|
||||
end
|
||||
Result.append (a_mime_boundary)
|
||||
Result.append ("--") --| end
|
||||
end
|
||||
|
||||
string_to_mime_encoded_string (s: READABLE_STRING_GENERAL): STRING
|
||||
-- Encoded unicode string for mime value.
|
||||
-- For instance uploaded filename, or form data key or values.
|
||||
local
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
-- FIXME: find the proper encoding!
|
||||
Result := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
|
||||
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: NETWORK_STREAM_SOCKET)
|
||||
-- Append `a_file' content to `a_output'.
|
||||
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||
require
|
||||
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||
local
|
||||
l_was_open: BOOLEAN
|
||||
l_count: INTEGER
|
||||
do
|
||||
if a_len >= 0 then
|
||||
l_count := a_len
|
||||
else
|
||||
l_count := a_file.count
|
||||
end
|
||||
if l_count > 0 then
|
||||
l_was_open := a_file.is_open_read
|
||||
if a_file.is_open_read then
|
||||
l_was_open := True
|
||||
else
|
||||
a_file.open_read
|
||||
end
|
||||
from
|
||||
until
|
||||
l_count = 0 or a_file.exhausted
|
||||
loop
|
||||
a_file.read_stream_thread_aware (l_count.min (2_048))
|
||||
a_output.put_string (a_file.last_string)
|
||||
l_count := l_count - a_file.bytes_read
|
||||
end
|
||||
if not l_was_open then
|
||||
a_file.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
append_file_content_to (a_file: FILE; a_len: INTEGER; a_output: STRING)
|
||||
-- Append `a_file' content to `a_output'.
|
||||
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||
require
|
||||
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||
local
|
||||
l_was_open: BOOLEAN
|
||||
l_count: INTEGER
|
||||
do
|
||||
if a_len >= 0 then
|
||||
l_count := a_len
|
||||
else
|
||||
l_count := a_file.count
|
||||
end
|
||||
if l_count > 0 then
|
||||
l_was_open := a_file.is_open_read
|
||||
if a_file.is_open_read then
|
||||
l_was_open := True
|
||||
else
|
||||
a_file.open_read
|
||||
end
|
||||
from
|
||||
|
||||
until
|
||||
l_count = 0 or a_file.exhausted
|
||||
loop
|
||||
a_file.read_stream_thread_aware (l_count.min (2_048))
|
||||
a_output.append (a_file.last_string)
|
||||
l_count := l_count - a_file.bytes_read
|
||||
end
|
||||
if not l_was_open then
|
||||
a_file.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
append_socket_header_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
|
||||
-- Get header from `a_socket' into `a_output'.
|
||||
local
|
||||
s: READABLE_STRING_8
|
||||
do
|
||||
from
|
||||
s := ""
|
||||
until
|
||||
s.same_string ("%R") or not a_socket.readable or a_response.error_occurred
|
||||
loop
|
||||
a_socket.read_line_thread_aware
|
||||
s := a_socket.last_string
|
||||
if s.is_empty then
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: ERROR: zero byte read when receiving header.%N")
|
||||
end
|
||||
a_response.set_error_message ("Read zero byte, expecting header line")
|
||||
elseif s.same_string ("%R") then
|
||||
-- Reach end of header
|
||||
else
|
||||
a_output.append (s)
|
||||
a_output.append_character ('%N')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_len: INTEGER; a_output: STRING)
|
||||
-- Get content from `a_socket' and append it to `a_output'.
|
||||
-- If `a_len' is negative, try to get as much as possible,
|
||||
-- this is probably HTTP/1.0 without any Content-Length.
|
||||
local
|
||||
s: STRING_8
|
||||
r: INTEGER -- remaining count
|
||||
n,l_chunk_size, l_count: INTEGER
|
||||
do
|
||||
if a_socket.readable then
|
||||
if a_len >= 0 then
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: Content-Length="+ a_len.out +"%N")
|
||||
end
|
||||
from
|
||||
r := a_len
|
||||
until
|
||||
r = 0 or else not a_socket.readable or else a_response.error_occurred
|
||||
loop
|
||||
a_socket.read_stream_thread_aware (r)
|
||||
l_count := l_count + a_socket.bytes_read
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
|
||||
log ("Debug: - current count=" + l_count.out + "%N")
|
||||
end
|
||||
r := r - a_socket.bytes_read
|
||||
a_output.append (a_socket.last_string)
|
||||
end
|
||||
check full_content_read: not a_response.error_occurred implies l_count = a_len end
|
||||
elseif attached a_response.header ("Transfer-Encoding") as l_enc and then l_enc.is_case_insensitive_equal ("chunked") then
|
||||
append_socket_chunked_content_to (a_response, a_socket, a_output)
|
||||
else
|
||||
-- No Content-Length and no chunked transfer encoding!
|
||||
-- maybe HTTP/1.0 ?
|
||||
-- FIXME: check solution!
|
||||
from
|
||||
l_count := 0
|
||||
l_chunk_size := 1_024
|
||||
n := l_chunk_size --| value to satisfy until condition on first loop.
|
||||
until
|
||||
n < l_chunk_size or not a_socket.readable
|
||||
loop
|
||||
a_socket.read_stream_thread_aware (l_chunk_size)
|
||||
s := a_socket.last_string
|
||||
n := a_socket.bytes_read
|
||||
l_count := l_count + n
|
||||
a_output.append (s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
append_socket_chunked_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
|
||||
-- Get chunked content from `a_socket' and append it to `a_output'.
|
||||
require
|
||||
socket_readable: a_socket.readable
|
||||
has_chunked_transfer_encoding: attached a_response.header ("Transfer-Encoding") as l_enc and then
|
||||
l_enc.is_case_insensitive_equal ("chunked")
|
||||
local
|
||||
s: STRING_8
|
||||
r: INTEGER -- remaining count
|
||||
n,pos, l_count: INTEGER
|
||||
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
|
||||
do
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: Chunked encoding%N")
|
||||
end
|
||||
from
|
||||
create hexa2int.make
|
||||
n := 1
|
||||
until
|
||||
n = 0 or not a_socket.readable
|
||||
loop
|
||||
a_socket.read_line_thread_aware -- Read chunk info
|
||||
s := a_socket.last_string
|
||||
s.right_adjust
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: - chunk info='" + s + "'%N")
|
||||
end
|
||||
pos := s.index_of (';', 1)
|
||||
if pos > 0 then
|
||||
s.keep_head (pos - 1)
|
||||
end
|
||||
if s.is_empty then
|
||||
n := 0
|
||||
else
|
||||
hexa2int.parse_string_with_type (s, hexa2int.type_integer)
|
||||
if hexa2int.parse_successful then
|
||||
n := hexa2int.parsed_integer
|
||||
else
|
||||
n := 0
|
||||
end
|
||||
end
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: - chunk size=" + n.out + "%N")
|
||||
end
|
||||
if n > 0 then
|
||||
from
|
||||
r := n
|
||||
until
|
||||
r = 0 or else not a_socket.readable or else a_response.error_occurred
|
||||
loop
|
||||
a_socket.read_stream_thread_aware (r)
|
||||
l_count := l_count + a_socket.bytes_read
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
|
||||
log ("Debug: - current count=" + l_count.out + "%N")
|
||||
end
|
||||
r := r - a_socket.bytes_read
|
||||
a_output.append (a_socket.last_string)
|
||||
end
|
||||
|
||||
a_socket.read_character
|
||||
check a_socket.last_character = '%R' end
|
||||
a_socket.read_character
|
||||
check a_socket.last_character = '%N' end
|
||||
if session.is_debug_verbose then
|
||||
log ("Debug: - Found CRNL %N")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
new_mime_boundary: STRING
|
||||
-- New MIME boundary.
|
||||
do
|
||||
-- FIXME: better boundary creation
|
||||
Result := "----------------------------5eadfcf3bb3e"
|
||||
end
|
||||
|
||||
invariant
|
||||
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
|
||||
@@ -0,0 +1,184 @@
|
||||
note
|
||||
description: "[
|
||||
Specific implementation of HTTP_CLIENT_SESSION based on Eiffel NET library
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
NET_HTTP_CLIENT_SESSION
|
||||
|
||||
inherit
|
||||
HTTP_CLIENT_SESSION
|
||||
redefine
|
||||
close
|
||||
end
|
||||
|
||||
NET_HTTP_CLIENT_INFO
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_available: BOOLEAN
|
||||
-- Is interface usable?
|
||||
do
|
||||
Result := True
|
||||
if base_url.starts_with_general ("https://") then
|
||||
Result := has_https_support
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access: persistent connection
|
||||
|
||||
persistent_connection: detachable NET_HTTP_CLIENT_CONNECTION
|
||||
-- Socket used for persistent connection purpose.
|
||||
|
||||
feature -- Element change: persistent connection
|
||||
|
||||
set_persistent_connection (a_connection: like persistent_connection)
|
||||
-- Set `persistent_connection' to `a_connection'.
|
||||
do
|
||||
persistent_connection := a_connection
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
close
|
||||
-- <Precursor>
|
||||
do
|
||||
if attached persistent_connection as l_connection then
|
||||
persistent_connection := Void
|
||||
l_connection.socket.cleanup
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Custom
|
||||
|
||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
local
|
||||
req: HTTP_CLIENT_REQUEST
|
||||
do
|
||||
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||
Result := req.response
|
||||
end
|
||||
|
||||
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Same as `custom' but including upload data `a_data'.
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- Same as `custom' but including upload file `fn'.
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
feature -- Helper
|
||||
|
||||
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("GET", a_path, ctx)
|
||||
end
|
||||
|
||||
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("HEAD", a_path, ctx)
|
||||
end
|
||||
|
||||
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("DELETE", a_path, ctx)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
impl_custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
local
|
||||
req: HTTP_CLIENT_REQUEST
|
||||
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
||||
l_data: detachable READABLE_STRING_8
|
||||
do
|
||||
ctx := a_ctx
|
||||
if data /= Void then
|
||||
if ctx = Void then
|
||||
create ctx.make
|
||||
end
|
||||
ctx.set_upload_data (data)
|
||||
end
|
||||
if fn /= Void then
|
||||
if ctx = Void then
|
||||
create ctx.make
|
||||
end
|
||||
ctx.set_upload_filename (fn)
|
||||
end
|
||||
if ctx /= Void then
|
||||
l_data := ctx.upload_data
|
||||
if l_data /= Void and a_method.is_case_insensitive_equal_general ("PUT") then
|
||||
check put_conflict_file_and_data: not ctx.has_upload_filename end
|
||||
end
|
||||
end
|
||||
|
||||
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||
Result := req.response
|
||||
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
|
||||
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Additional information related to NET HTTP Client.."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
NET_HTTP_CLIENT_INFO
|
||||
|
||||
feature -- Access
|
||||
|
||||
has_https_support: BOOLEAN = False
|
||||
-- Is HTTPS supported?
|
||||
|
||||
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
|
||||
@@ -0,0 +1,22 @@
|
||||
note
|
||||
description: "[
|
||||
A fake SSL network stream socket... when SSL is disabled at compilation time.
|
||||
Its behavior is similar to NETWORK_STREAM_SOCKET.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
SSL_NETWORK_STREAM_SOCKET
|
||||
|
||||
inherit
|
||||
NETWORK_STREAM_SOCKET
|
||||
|
||||
create
|
||||
make, make_empty, make_client_by_port, make_client_by_address_and_port, make_server_by_port, make_loopback_server_by_port
|
||||
|
||||
create {SSL_NETWORK_STREAM_SOCKET}
|
||||
make_from_descriptor_and_address, create_from_descriptor
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Additional information related to NET HTTP Client.."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
NET_HTTP_CLIENT_INFO
|
||||
|
||||
feature -- Access
|
||||
|
||||
has_https_support: BOOLEAN = True
|
||||
-- Is HTTPS supported?
|
||||
|
||||
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
|
||||
@@ -0,0 +1,110 @@
|
||||
note
|
||||
description : "[
|
||||
NULL version of HTTP_CLIENT_SESSION.
|
||||
It is used if no implementation is available (libcurl or net)
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
NULL_HTTP_CLIENT_SESSION
|
||||
|
||||
inherit
|
||||
HTTP_CLIENT_SESSION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
end
|
||||
|
||||
feature -- Custom
|
||||
|
||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
create Result.make (base_url + a_path)
|
||||
end
|
||||
|
||||
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
feature -- Helper
|
||||
|
||||
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := custom ("GET", a_path, ctx)
|
||||
end
|
||||
|
||||
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := custom ("HEAD", a_path, ctx)
|
||||
end
|
||||
|
||||
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
|
||||
end
|
||||
|
||||
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
|
||||
end
|
||||
|
||||
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := custom ("DELETE", a_path, ctx)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
impl_custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
Result := custom (a_method, a_path, a_ctx)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_available: BOOLEAN = False
|
||||
-- Is interface usable?
|
||||
|
||||
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
|
||||
BIN
library/network/http_client/tests/logo.jpg
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="test_http_client" uuid="920E5C50-41E1-4DAC-8D48-D9C860E49228">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="test_http_client" uuid="920E5C50-41E1-4DAC-8D48-D9C860E49228">
|
||||
<target name="test_http_client">
|
||||
<root class="TEST" feature="make"/>
|
||||
<file_rule>
|
||||
@@ -10,6 +10,9 @@
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<variable name="netssl_http_client_enabled" value="false"/>
|
||||
<variable name="net_http_client_disabled" value="false"/>
|
||||
<variable name="libcurl_http_client_disabled" value="false"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http_client" location="..\http_client-safe.ecf" readonly="false" use_application_options="true">
|
||||
<option>
|
||||
@@ -17,6 +20,19 @@
|
||||
</option>
|
||||
</library>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<tests name="tests" location=".\"/>
|
||||
<tests name="tests" location=".\">
|
||||
<file_rule>
|
||||
<exclude>.*libcurl_.*.e$</exclude>
|
||||
<condition>
|
||||
<custom name="libcurl_http_client_disabled" value="true"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
<file_rule>
|
||||
<exclude>.*net_.*.e$</exclude>
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" value="true"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
</tests>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -6,17 +6,46 @@ create
|
||||
feature -- Init
|
||||
|
||||
make
|
||||
local
|
||||
null: NULL_HTTP_CLIENT_SESSION
|
||||
do
|
||||
create null.make ("http://example.com/")
|
||||
check not null.is_available end
|
||||
|
||||
test_get_with_authentication
|
||||
test_http_client
|
||||
end
|
||||
|
||||
test_get_with_authentication
|
||||
local
|
||||
cl: DEFAULT_HTTP_CLIENT
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
-- GET REQUEST WITH AUTHENTICATION, see http://browserspy.dk/password.php
|
||||
-- check header WWW-Authenticate is received (authentication successful)
|
||||
create cl
|
||||
sess := cl.new_session ("http://browserspy.dk")
|
||||
sess.set_credentials ("test", "test")
|
||||
create ctx.make_with_credentials_required
|
||||
if attached sess.get ("/password-ok.php", ctx) as res then
|
||||
if attached {READABLE_STRING_8} res.body as l_body then
|
||||
assert ("Fetch all body, including closing html tag", l_body.has_substring ("</html>"))
|
||||
else
|
||||
assert ("has body", False)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test_http_client
|
||||
-- New test routine
|
||||
local
|
||||
sess: LIBCURL_HTTP_CLIENT_SESSION
|
||||
cl: DEFAULT_HTTP_CLIENT
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
do
|
||||
create sess.make ("http://www.google.com")
|
||||
create cl
|
||||
sess := cl.new_session ("http://www.google.com")
|
||||
if attached sess.get ("/search?q=eiffel", Void) as res then
|
||||
assert ("Get returned without error", not res.error_occurred)
|
||||
create h.make_empty
|
||||
@@ -42,7 +71,7 @@ feature -- Init
|
||||
do
|
||||
if not b then
|
||||
create e
|
||||
e.set_message (m)
|
||||
e.set_description (m)
|
||||
e.raise
|
||||
end
|
||||
end
|
||||
|
||||
1
library/network/http_client/tests/test.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a text sample for testing HTTP Client library.
|
||||
@@ -7,21 +7,27 @@ note
|
||||
revision: "$Revision$"
|
||||
testing: "type/manual"
|
||||
|
||||
class
|
||||
TEST_HTTP_CLIENT
|
||||
deferred class
|
||||
TEST_HTTP_CLIENT_I
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
test_http_client
|
||||
-- New test routine
|
||||
local
|
||||
sess: LIBCURL_HTTP_CLIENT_SESSION
|
||||
sess: like new_session
|
||||
h: STRING_8
|
||||
do
|
||||
create sess.make ("http://www.google.com")
|
||||
sess := new_session ("http://www.google.com")
|
||||
if attached sess.get ("/search?q=eiffel", Void) as res then
|
||||
assert ("Get returned without error", not res.error_occurred)
|
||||
create h.make_empty
|
||||
@@ -38,8 +44,33 @@ feature -- Test routines
|
||||
assert ("missing body", False)
|
||||
end
|
||||
assert ("same headers", h.same_string (res.raw_header))
|
||||
else
|
||||
assert ("Not found", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_http_client_ssl
|
||||
-- New test routine
|
||||
local
|
||||
sess: like new_session
|
||||
h: STRING_8
|
||||
do
|
||||
sess := new_session ("https://www.eiffel.org")
|
||||
sess.set_is_insecure (True)
|
||||
if attached sess.get ("/welcome", Void) as res then
|
||||
assert ("Get returned without error", not res.error_occurred)
|
||||
create h.make_empty
|
||||
if attached res.headers as hds then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
if attached res.body as l_body then
|
||||
assert ("body not empty", not l_body.is_empty)
|
||||
else
|
||||
assert ("missing body", False)
|
||||
end
|
||||
assert ("same headers", h.same_string (res.raw_header))
|
||||
end
|
||||
end
|
||||
|
||||
43
library/network/http_client/tests/test_libcurl_http_client.e
Normal file
@@ -0,0 +1,43 @@
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
author: "EiffelStudio test wizard"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
testing: "type/manual"
|
||||
|
||||
class
|
||||
TEST_LIBCURL_HTTP_CLIENT
|
||||
|
||||
inherit
|
||||
TEST_HTTP_CLIENT_I
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
do
|
||||
create {LIBCURL_HTTP_CLIENT_SESSION} Result.make (a_url)
|
||||
end
|
||||
|
||||
feature -- Tests
|
||||
|
||||
test_libcurl_http_client
|
||||
do
|
||||
test_http_client
|
||||
end
|
||||
|
||||
test_libcurl_http_client_ssl
|
||||
do
|
||||
test_http_client_ssl
|
||||
end
|
||||
|
||||
test_libcurl_headers
|
||||
do
|
||||
test_headers
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
67
library/network/http_client/tests/test_libcurl_with_web.e
Normal file
@@ -0,0 +1,67 @@
|
||||
note
|
||||
description: "[
|
||||
Objects that ...
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
TEST_LIBCURL_WITH_WEB
|
||||
|
||||
inherit
|
||||
TEST_WITH_WEB_I
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
local
|
||||
cl: LIBCURL_HTTP_CLIENT
|
||||
do
|
||||
create cl
|
||||
Result := cl.new_session (a_url)
|
||||
end
|
||||
|
||||
feature -- Tests
|
||||
|
||||
libcurl_test_post_url_encoded
|
||||
do
|
||||
test_post_url_encoded
|
||||
end
|
||||
|
||||
libcurl_test_post_with_form_data
|
||||
do
|
||||
test_post_with_form_data
|
||||
end
|
||||
|
||||
libcurl_test_post_with_file
|
||||
do
|
||||
test_post_with_file
|
||||
end
|
||||
|
||||
libcurl_test_put_with_file
|
||||
do
|
||||
test_put_with_file
|
||||
end
|
||||
|
||||
libcurl_test_put_with_data
|
||||
do
|
||||
test_put_with_data
|
||||
end
|
||||
|
||||
libcurl_test_post_with_file_and_form_data
|
||||
do
|
||||
test_post_with_file_and_form_data
|
||||
end
|
||||
|
||||
libcurl_test_get_with_redirection
|
||||
do
|
||||
test_get_with_redirection
|
||||
end
|
||||
|
||||
libcurl_test_get_with_authentication
|
||||
do
|
||||
test_get_with_authentication
|
||||
end
|
||||
|
||||
end
|
||||
86
library/network/http_client/tests/test_net_http_client.e
Normal file
@@ -0,0 +1,86 @@
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
author: "EiffelStudio test wizard"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
testing: "type/manual"
|
||||
|
||||
class
|
||||
TEST_NET_HTTP_CLIENT
|
||||
|
||||
inherit
|
||||
TEST_HTTP_CLIENT_I
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
do
|
||||
create {NET_HTTP_CLIENT_SESSION} Result.make (a_url)
|
||||
end
|
||||
|
||||
feature -- Tests
|
||||
|
||||
test_net_http_client
|
||||
do
|
||||
test_http_client
|
||||
end
|
||||
|
||||
test_net_http_client_ssl
|
||||
do
|
||||
test_http_client_ssl
|
||||
end
|
||||
|
||||
test_net_headers
|
||||
do
|
||||
test_headers
|
||||
end
|
||||
|
||||
test_persistent_connection
|
||||
local
|
||||
sess: like new_session
|
||||
h: STRING_8
|
||||
do
|
||||
sess := new_session ("http://www.google.fr")
|
||||
if attached sess.get ("/", Void) as res then
|
||||
assert ("Get returned without error", not res.error_occurred)
|
||||
create h.make_empty
|
||||
if attached res.headers as hds then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
if attached res.body as l_body then
|
||||
assert ("body not empty", not l_body.is_empty)
|
||||
else
|
||||
assert ("missing body", False)
|
||||
end
|
||||
assert ("same headers", h.same_string (res.raw_header))
|
||||
end
|
||||
if attached sess.get ("/", Void) as res then
|
||||
assert ("Get returned without error", not res.error_occurred)
|
||||
create h.make_empty
|
||||
if attached res.headers as hds then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
if attached res.body as l_body then
|
||||
assert ("body not empty", not l_body.is_empty)
|
||||
else
|
||||
assert ("missing body", False)
|
||||
end
|
||||
assert ("same headers", h.same_string (res.raw_header))
|
||||
end
|
||||
sess.close
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
67
library/network/http_client/tests/test_net_with_web.e
Normal file
@@ -0,0 +1,67 @@
|
||||
note
|
||||
description: "[
|
||||
Objects that ...
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
TEST_NET_WITH_WEB
|
||||
|
||||
inherit
|
||||
TEST_WITH_WEB_I
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
local
|
||||
cl: NET_HTTP_CLIENT
|
||||
do
|
||||
create cl
|
||||
Result := cl.new_session (a_url)
|
||||
end
|
||||
|
||||
feature -- Tests
|
||||
|
||||
net_test_post_url_encoded
|
||||
do
|
||||
test_post_url_encoded
|
||||
end
|
||||
|
||||
net_test_post_with_form_data
|
||||
do
|
||||
test_post_with_form_data
|
||||
end
|
||||
|
||||
net_test_post_with_file
|
||||
do
|
||||
test_post_with_file
|
||||
end
|
||||
|
||||
net_test_put_with_file
|
||||
do
|
||||
test_put_with_file
|
||||
end
|
||||
|
||||
net_test_put_with_data
|
||||
do
|
||||
test_put_with_data
|
||||
end
|
||||
|
||||
net_test_post_with_file_and_form_data
|
||||
do
|
||||
test_post_with_file_and_form_data
|
||||
end
|
||||
|
||||
net_test_get_with_redirection
|
||||
do
|
||||
test_get_with_redirection
|
||||
end
|
||||
|
||||
net_test_get_with_authentication
|
||||
do
|
||||
test_get_with_authentication
|
||||
end
|
||||
|
||||
end
|
||||
302
library/network/http_client/tests/test_with_web_i.e
Normal file
@@ -0,0 +1,302 @@
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
author: "EiffelStudio test wizard"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
testing: "type/manual"
|
||||
|
||||
deferred class
|
||||
TEST_WITH_WEB_I
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
redefine
|
||||
on_prepare
|
||||
end
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
on_prepare
|
||||
do
|
||||
Precursor
|
||||
global_requestbin_path := new_requestbin_path
|
||||
end
|
||||
|
||||
feature -- Factory
|
||||
|
||||
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Requestbin
|
||||
|
||||
global_requestbin_path: detachable READABLE_STRING_8
|
||||
|
||||
new_requestbin_path: detachable STRING
|
||||
local
|
||||
i,j: INTEGER
|
||||
do
|
||||
if
|
||||
attached new_session ("http://requestb.in") as sess and then
|
||||
attached sess.post ("/api/v1/bins", Void, Void) as resp
|
||||
then
|
||||
if resp.error_occurred then
|
||||
print ("Error occurred!%N")
|
||||
elseif attached resp.body as l_content then
|
||||
|
||||
i := l_content.substring_index ("%"name%":", 1)
|
||||
if i > 0 then
|
||||
j := l_content.index_of (',', i + 1)
|
||||
if j > 0 then
|
||||
Result := l_content.substring (i + 7, j - 1)
|
||||
Result.adjust
|
||||
if Result.starts_with ("%"") then
|
||||
Result.remove_head (1)
|
||||
end
|
||||
if Result.ends_with ("%"") then
|
||||
Result.remove_tail (1)
|
||||
end
|
||||
if not Result.starts_with ("/") then
|
||||
Result.prepend_character ('/')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Factory
|
||||
|
||||
test_post_url_encoded
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
do
|
||||
if attached global_requestbin_path as requestbin_path then
|
||||
-- URL ENCODED POST REQUEST
|
||||
-- check requestbin to ensure the "Hello World" has been received in the raw body
|
||||
-- also check that User-Agent was sent
|
||||
create h.make_empty
|
||||
sess := new_session ("http://requestb.in")
|
||||
if
|
||||
attached sess.post (requestbin_path, Void, "Hello World") as res and then
|
||||
attached res.headers as hds
|
||||
then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
print (h)
|
||||
else
|
||||
assert ("Has requestbin path", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_post_with_form_data
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
if attached global_requestbin_path as requestbin_path then
|
||||
|
||||
-- POST REQUEST WITH FORM DATA
|
||||
-- check requestbin to ensure the form parameters are correctly received
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
l_ctx.form_parameters.extend ("First Value", "First Key")
|
||||
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.post (requestbin_path, l_ctx, "") as res and then
|
||||
attached res.headers as hds
|
||||
then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
print (h)
|
||||
else
|
||||
assert ("Has requestbin path", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_post_with_file
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
if attached global_requestbin_path as requestbin_path then
|
||||
|
||||
-- POST REQUEST WITH A FILE
|
||||
-- check requestbin to ensure the form parameters are correctly received
|
||||
-- set filename to a local file
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
l_ctx.set_upload_filename ("test.txt")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.post (requestbin_path, l_ctx, "") as res and then
|
||||
attached res.headers as hds
|
||||
then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
print (h)
|
||||
else
|
||||
assert ("Has requestbin path", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_put_with_file
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
if attached global_requestbin_path as requestbin_path then
|
||||
|
||||
-- PUT REQUEST WITH A FILE
|
||||
-- check requestbin to ensure the file is correctly received
|
||||
-- set filename to a local file
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
l_ctx.set_upload_filename ("test.txt")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.put (requestbin_path, l_ctx, Void) as res and then
|
||||
attached res.headers as hds
|
||||
then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
print (h)
|
||||
else
|
||||
assert ("Has requestbin path", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_put_with_data
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
if attached global_requestbin_path as requestbin_path then
|
||||
|
||||
-- PUT REQUEST WITH A FILE
|
||||
-- check requestbin to ensure the file is correctly received
|
||||
-- set filename to a local file
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
l_ctx.set_upload_data ("This is a test for http client.%N")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.put (requestbin_path, l_ctx, Void) as res and then
|
||||
attached res.headers as hds
|
||||
then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
print (h)
|
||||
else
|
||||
assert ("Has requestbin path", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_post_with_file_and_form_data
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
if attached global_requestbin_path as requestbin_path then
|
||||
|
||||
-- POST REQUEST WITH A FILE AND FORM DATA
|
||||
-- check requestbin to ensure the file and form parameters are correctly received
|
||||
-- set filename to a local file
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
l_ctx.set_upload_filename ("logo.jpg")
|
||||
l_ctx.form_parameters.extend ("First Value", "First Key")
|
||||
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.post (requestbin_path, l_ctx, Void) as res and then
|
||||
attached res.headers as hds
|
||||
then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
print (h)
|
||||
else
|
||||
assert ("Has requestbin path", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_get_with_redirection
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
do
|
||||
if attached global_requestbin_path as requestbin_path then
|
||||
|
||||
-- GET REQUEST, Forwarding (google's first answer is a forward)
|
||||
-- check headers received (printed in console)
|
||||
sess := new_session ("http://google.com")
|
||||
create h.make_empty
|
||||
if attached sess.get ("/", Void) as res and then attached res.headers as hds then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
print (h)
|
||||
else
|
||||
assert ("Has requestbin path", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_get_with_authentication
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
-- GET REQUEST WITH AUTHENTICATION, see http://browserspy.dk/password.php
|
||||
-- check header WWW-Authenticate is received (authentication successful)
|
||||
sess := new_session ("http://browserspy.dk")
|
||||
sess.set_credentials ("test", "test")
|
||||
create ctx.make_with_credentials_required
|
||||
if attached sess.get ("/password-ok.php", ctx) as res then
|
||||
if attached {READABLE_STRING_8} res.body as l_body then
|
||||
assert ("Fetch all body, including closing html tag", l_body.has_substring ("</html>"))
|
||||
else
|
||||
assert ("has body", False)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -122,8 +122,8 @@ feature {NONE} -- Internal
|
||||
feature -- Conversion to string
|
||||
|
||||
yyyy_mmm_dd_string: STRING
|
||||
-- String representation YYYY mmm dd
|
||||
-- 2012 Dec 25
|
||||
-- String representation [YYYY mmm dd]
|
||||
-- ex: 2012 Dec 25
|
||||
do
|
||||
create Result.make (11)
|
||||
append_date_time_to_yyyy_mmm_dd_string (date_time, Result)
|
||||
@@ -131,7 +131,8 @@ feature -- Conversion to string
|
||||
|
||||
rfc1123_string: STRING
|
||||
-- String representation following RFC 1123.
|
||||
--| Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
|
||||
-- format: [ddd, dd mmm yyyy hh:mi:ss GMT]
|
||||
-- ex: Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
|
||||
local
|
||||
s: like internal_rfc1123_string
|
||||
do
|
||||
@@ -145,7 +146,8 @@ feature -- Conversion to string
|
||||
end
|
||||
|
||||
rfc850_string: STRING
|
||||
-- String representation following RFC 850
|
||||
-- String representation following RFC 850.
|
||||
-- format: [mmm, dd-mmm-yy hh:mi:ss GMT]
|
||||
do
|
||||
create Result.make (32)
|
||||
append_date_time_to_rfc850_string (date_time, Result)
|
||||
@@ -153,6 +155,7 @@ feature -- Conversion to string
|
||||
|
||||
ansi_c_string: STRING
|
||||
-- ANSI C's asctime() format
|
||||
--| Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
|
||||
do
|
||||
create Result.make (32)
|
||||
append_date_time_to_ansi_c_string (date_time, Result)
|
||||
@@ -161,74 +164,34 @@ feature -- Conversion to string
|
||||
feature -- Conversion into string
|
||||
|
||||
append_to_yyyy_mmm_dd_string (s: STRING_GENERAL)
|
||||
local
|
||||
dt: DATE_TIME
|
||||
-- Append `datetime' as [yyyy mmm dd] format to `s'.
|
||||
do
|
||||
dt := date_time
|
||||
append_integer_to (dt.year, s) -- yyyy
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_month_mmm_to (dt.month, s) -- mmm
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_2_digits_integer_to (dt.day, s) -- dd
|
||||
append_date_time_to_yyyy_mmm_dd_string (date_time, s)
|
||||
end
|
||||
|
||||
append_to_rfc1123_string (s: STRING_GENERAL)
|
||||
local
|
||||
dt: DATE_TIME
|
||||
-- Append `date_time' as [ddd, dd mmm yyyy hh:mi:ss GMT] format to `s'.
|
||||
do
|
||||
dt := date_time
|
||||
append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd
|
||||
s.append_code (44) -- 44 ',' -- ','
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_2_digits_integer_to (dt.day, s) -- dd
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_month_mmm_to (dt.month, s) -- mmm
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_integer_to (dt.year, s) -- YYYY
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_2_digits_time_to (dt.time, s) -- hh:mi:ss
|
||||
s.append (" GMT") -- SPace + GMT
|
||||
append_date_time_to_rfc1123_string (date_time, s)
|
||||
end
|
||||
|
||||
append_rfc850_string (s: STRING_GENERAL)
|
||||
local
|
||||
dt: DATE_TIME
|
||||
-- Append `date_time' as [mmm, dd-mmm-yy hh:mi:ss GMT] format to `s'.
|
||||
do
|
||||
dt := date_time
|
||||
append_day_name_to (dt.date.day_of_the_week, s) -- mmm
|
||||
s.append_code (44) -- 44 ',' -- ','
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_2_digits_integer_to (dt.day, s) -- dd
|
||||
s.append_code (45) -- 45 '-' -- '-'
|
||||
append_month_mmm_to (dt.month, s) -- mmm
|
||||
s.append_code (45) -- 45 '-' -- '-'
|
||||
append_integer_to (dt.year \\ 100, s) -- yy
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_2_digits_time_to (dt.time, s) -- hh:mi:ss
|
||||
s.append (" GMT") -- SPace + GMT
|
||||
append_date_time_to_rfc850_string (date_time, s)
|
||||
end
|
||||
|
||||
append_to_ansi_c_string (s: STRING_GENERAL)
|
||||
-- Append `date_time' as ANSI C's asctime format to `s'.
|
||||
--| Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
|
||||
local
|
||||
dt: DATE_TIME
|
||||
do
|
||||
dt := date_time
|
||||
append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_month_mmm_to (dt.month, s) -- mmm
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_integer_to (dt.day, s) -- d
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_2_digits_time_to (dt.time, s) -- hh:mi:ss
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
append_integer_to (dt.year, s) -- yyyy
|
||||
append_date_time_to_ansi_c_string (date_time, s)
|
||||
end
|
||||
|
||||
feature -- Conversion into string
|
||||
|
||||
append_date_time_to_yyyy_mmm_dd_string (dt: DATE_TIME; s: STRING_GENERAL)
|
||||
-- Append `dt' as [yyyy mmm dd] format to `s'.
|
||||
do
|
||||
append_integer_to (dt.year, s) -- yyyy
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
@@ -238,6 +201,7 @@ feature -- Conversion into string
|
||||
end
|
||||
|
||||
append_date_time_to_rfc1123_string (dt: DATE_TIME; s: STRING_GENERAL)
|
||||
-- Append `dt' as [ddd, dd mmm yyyy hh:mi:ss GMT] format to `s'.
|
||||
do
|
||||
append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd
|
||||
s.append_code (44) -- 44 ',' -- ','
|
||||
@@ -253,6 +217,7 @@ feature -- Conversion into string
|
||||
end
|
||||
|
||||
append_date_time_to_rfc850_string (dt: DATE_TIME; s: STRING_GENERAL)
|
||||
-- Append `dt' as [mmm, dd-mmm-yy hh:mi:ss GMT] format to `s'.
|
||||
do
|
||||
append_day_name_to (dt.date.day_of_the_week, s) -- mmm
|
||||
s.append_code (44) -- 44 ',' -- ','
|
||||
@@ -268,7 +233,8 @@ feature -- Conversion into string
|
||||
end
|
||||
|
||||
append_date_time_to_ansi_c_string (dt: DATE_TIME; s: STRING_GENERAL)
|
||||
--| Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
|
||||
-- Append `dt' as ANSI C's asctime format to `s'.
|
||||
-- Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
|
||||
do
|
||||
append_day_ddd_to (dt.date.day_of_the_week, s) -- ddd
|
||||
s.append_code (32) -- 32 ' ' -- SPace
|
||||
@@ -294,7 +260,7 @@ feature -- Status report
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
feature -- Helper routines.
|
||||
|
||||
append_2_digits_integer_to (i: INTEGER; s: STRING_GENERAL)
|
||||
require
|
||||
@@ -361,7 +327,7 @@ feature {NONE} -- Implementation
|
||||
when 5 then s.append ("May")
|
||||
when 6 then s.append ("Jun")
|
||||
when 7 then s.append ("Jul")
|
||||
when 8 then s.append ("Aou")
|
||||
when 8 then s.append ("Aug")
|
||||
when 9 then s.append ("Sep")
|
||||
when 10 then s.append ("Oct")
|
||||
when 11 then s.append ("Nov")
|
||||
@@ -487,7 +453,7 @@ feature {NONE} -- Implementation
|
||||
elseif l_mmm.same_string ("MAY") then l_mo := 05
|
||||
elseif l_mmm.same_string ("JUN") then l_mo := 06
|
||||
elseif l_mmm.same_string ("JUL") then l_mo := 07
|
||||
elseif l_mmm.same_string ("AOU") then l_mo := 08
|
||||
elseif l_mmm.same_string ("AUG") then l_mo := 08
|
||||
elseif l_mmm.same_string ("SEP") then l_mo := 09
|
||||
elseif l_mmm.same_string ("OCT") then l_mo := 10
|
||||
elseif l_mmm.same_string ("NOV") then l_mo := 11
|
||||
@@ -599,7 +565,11 @@ feature {NONE} -- Implementation
|
||||
t.extend (s[i].as_upper)
|
||||
i := i + 1
|
||||
end
|
||||
if t.same_string ("GMT") or t.same_string ("UTC") then
|
||||
if
|
||||
t.same_string ("GMT") -- for instance: GMT+0002
|
||||
or t.same_string ("UTC") -- for instance: UTC+0002
|
||||
or t.is_empty -- for instance: +0002
|
||||
then
|
||||
from until i > n or else not s[i].is_space loop i := i + 1 end
|
||||
if i <= n then
|
||||
t.wipe_out
|
||||
@@ -718,7 +688,7 @@ feature {NONE} -- Implementation
|
||||
elseif l_mmm.same_string ("MAY") then l_mo := 05
|
||||
elseif l_mmm.same_string ("JUN") then l_mo := 06
|
||||
elseif l_mmm.same_string ("JUL") then l_mo := 07
|
||||
elseif l_mmm.same_string ("AOU") then l_mo := 08
|
||||
elseif l_mmm.same_string ("AUG") then l_mo := 08
|
||||
elseif l_mmm.same_string ("SEP") then l_mo := 09
|
||||
elseif l_mmm.same_string ("OCT") then l_mo := 10
|
||||
elseif l_mmm.same_string ("NOV") then l_mo := 11
|
||||
@@ -905,7 +875,7 @@ feature {NONE} -- Implementation
|
||||
invariant
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others"
|
||||
copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="notification_email" uuid="99D9A065-CD45-4E20-9C86-579C8AD42E5E" library_target="notification_email">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="notification_email" uuid="99D9A065-CD45-4E20-9C86-579C8AD42E5E" library_target="notification_email">
|
||||
<target name="notification_email">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -11,8 +11,20 @@
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http" location="..\..\..\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf">
|
||||
<condition>
|
||||
<custom name="smtp_notification_email_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="process" location="$ISE_LIBRARY\library\process\process-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
<cluster name="src" location=".\" >
|
||||
<cluster name="storage" location="$|storage"/>
|
||||
<cluster name="smtp" location="$|smtp">
|
||||
<condition>
|
||||
<custom name="smtp_notification_email_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -2,9 +2,9 @@ note
|
||||
description : "[
|
||||
Component representing an email
|
||||
]"
|
||||
author : "$Author$"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
author : "$Author: jfiat $"
|
||||
date : "$Date: 2015-06-30 11:07:17 +0200 (mar., 30 juin 2015) $"
|
||||
revision : "$Revision: 97586 $"
|
||||
|
||||
class
|
||||
NOTIFICATION_EMAIL
|
||||
@@ -14,15 +14,17 @@ create
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_from: like from_address; a_to_address: READABLE_STRING_8; a_subject: like subject; a_body: like body)
|
||||
make (a_from: like from_address; a_to_address: READABLE_STRING_8; a_subject: like subject; a_content: like content)
|
||||
-- Initialize `Current'.
|
||||
require
|
||||
well_formed_from_address: is_valid_address (a_from)
|
||||
well_formed_to_address: a_to_address.has ('@')
|
||||
do
|
||||
initialize
|
||||
from_address := a_from
|
||||
subject := a_subject
|
||||
body := a_body
|
||||
content := a_content
|
||||
to_addresses.extend (a_to_address)
|
||||
|
||||
end
|
||||
|
||||
initialize
|
||||
@@ -37,11 +39,36 @@ feature -- Access
|
||||
|
||||
from_address: READABLE_STRING_8
|
||||
|
||||
reply_to_address: detachable READABLE_STRING_8
|
||||
|
||||
to_addresses: ARRAYED_LIST [READABLE_STRING_8]
|
||||
|
||||
cc_addresses: detachable ARRAYED_LIST [READABLE_STRING_8]
|
||||
|
||||
bcc_addresses: detachable ARRAYED_LIST [READABLE_STRING_8]
|
||||
|
||||
subject: READABLE_STRING_8
|
||||
|
||||
body: READABLE_STRING_8
|
||||
content: READABLE_STRING_8
|
||||
|
||||
additional_header_lines: detachable ARRAYED_LIST [READABLE_STRING_8]
|
||||
-- Additional header lines.
|
||||
|
||||
body: like content
|
||||
obsolete
|
||||
"Use `content' [June/2015]"
|
||||
do
|
||||
Result := body
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid: BOOLEAN
|
||||
-- Is current email ready to be sent?
|
||||
do
|
||||
Result := is_valid_address (from_address) and
|
||||
across to_addresses as ic all is_valid_address (ic.item) end
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
@@ -50,13 +77,90 @@ feature -- Change
|
||||
date := d
|
||||
end
|
||||
|
||||
set_subject (s: READABLE_STRING_8)
|
||||
-- Set `subject' to `s'.
|
||||
do
|
||||
subject := s
|
||||
end
|
||||
|
||||
set_content (s: READABLE_STRING_8)
|
||||
-- Set `content' to `s'.
|
||||
do
|
||||
content := s
|
||||
end
|
||||
|
||||
set_from_address (add: READABLE_STRING_8)
|
||||
require
|
||||
well_formed_address: add.has ('@')
|
||||
do
|
||||
from_address := add
|
||||
end
|
||||
|
||||
add_cc_address (add: READABLE_STRING_8)
|
||||
require
|
||||
well_formed_address: add.has ('@')
|
||||
local
|
||||
lst: like cc_addresses
|
||||
do
|
||||
lst := cc_addresses
|
||||
if lst = Void then
|
||||
create lst.make (1)
|
||||
cc_addresses := lst
|
||||
end
|
||||
lst.force (add)
|
||||
end
|
||||
|
||||
add_bcc_address (add: READABLE_STRING_8)
|
||||
require
|
||||
well_formed_address: add.has ('@')
|
||||
local
|
||||
lst: like bcc_addresses
|
||||
do
|
||||
lst := bcc_addresses
|
||||
if lst = Void then
|
||||
create lst.make (1)
|
||||
bcc_addresses := lst
|
||||
end
|
||||
lst.force (add)
|
||||
end
|
||||
|
||||
add_header_line (a_line: READABLE_STRING_8)
|
||||
require
|
||||
well_formed_header_line: a_line.has (':')
|
||||
local
|
||||
lst: like additional_header_lines
|
||||
do
|
||||
lst := additional_header_lines
|
||||
if lst = Void then
|
||||
create lst.make (1)
|
||||
additional_header_lines := lst
|
||||
end
|
||||
lst.force (a_line)
|
||||
end
|
||||
|
||||
feature -- Reset
|
||||
|
||||
reset
|
||||
do
|
||||
reset_addresses
|
||||
additional_header_lines := Void
|
||||
end
|
||||
|
||||
reset_addresses
|
||||
-- Reset all addresses.
|
||||
do
|
||||
to_addresses.wipe_out
|
||||
cc_addresses := Void
|
||||
bcc_addresses := Void
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
message: STRING_8
|
||||
do
|
||||
Result := header
|
||||
Result.append_character ('%N')
|
||||
Result.append (body)
|
||||
Result.append (content)
|
||||
Result.append_character ('%N')
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
@@ -66,13 +170,14 @@ feature -- Conversion
|
||||
hdate: HTTP_DATE
|
||||
do
|
||||
create Result.make (20)
|
||||
if attached reply_to_address as l_reply_to then
|
||||
Result.append ("Reply-To: ")
|
||||
Result.append (l_reply_to)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
Result.append ("From: ")
|
||||
Result.append (from_address)
|
||||
Result.append_character ('%N')
|
||||
Result.append ("Date: ")
|
||||
create hdate.make_from_date_time (date)
|
||||
hdate.append_to_rfc1123_string (Result)
|
||||
Result.append (" GMT%N")
|
||||
Result.append ("To: ")
|
||||
across
|
||||
to_addresses as c
|
||||
@@ -81,18 +186,67 @@ feature -- Conversion
|
||||
Result.append_character (';')
|
||||
end
|
||||
Result.append_character ('%N')
|
||||
if
|
||||
attached cc_addresses as l_cc and then
|
||||
not l_cc.is_empty
|
||||
then
|
||||
Result.append ("Cc: ")
|
||||
across
|
||||
l_cc as c
|
||||
loop
|
||||
Result.append (c.item)
|
||||
Result.append_character (';')
|
||||
end
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
if
|
||||
attached bcc_addresses as l_bcc and then
|
||||
not l_bcc.is_empty
|
||||
then
|
||||
Result.append ("Bcc: ")
|
||||
across
|
||||
l_bcc as c
|
||||
loop
|
||||
Result.append (c.item)
|
||||
Result.append_character (';')
|
||||
end
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
Result.append ("Subject: ")
|
||||
Result.append (subject)
|
||||
Result.append_character ('%N')
|
||||
Result.append ("Date: ")
|
||||
create hdate.make_from_date_time (date)
|
||||
hdate.append_to_rfc1123_string (Result)
|
||||
Result.append_character ('%N')
|
||||
if attached additional_header_lines as l_lines and then
|
||||
not l_lines.is_empty
|
||||
then
|
||||
across
|
||||
l_lines as ic
|
||||
loop
|
||||
Result.append (ic.item)
|
||||
Result.append_character ('%N')
|
||||
end
|
||||
end
|
||||
ensure
|
||||
Result.ends_with ("%N")
|
||||
end
|
||||
|
||||
feature -- Helpers
|
||||
|
||||
is_valid_address (add: READABLE_STRING_8): BOOLEAN
|
||||
-- Is `add' a valid email address?
|
||||
do
|
||||
-- FIXME: improve email validation
|
||||
Result := add.has ('@')
|
||||
end
|
||||
|
||||
invariant
|
||||
-- invariant_clause: True
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -11,8 +11,20 @@
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="http" location="..\..\..\network\protocol\http\http.ecf"/>
|
||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf">
|
||||
<condition>
|
||||
<custom name="smtp_notification_email_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="process" location="$ISE_LIBRARY\library\process\process.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<cluster name="src" location="." recursive="true"/>
|
||||
<cluster name="src" location=".">
|
||||
<cluster name="storage" location="$|storage"/>
|
||||
<cluster name="smtp" location="$|smtp">
|
||||
<condition>
|
||||
<custom name="smtp_notification_email_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -2,9 +2,9 @@ note
|
||||
description: "[
|
||||
Component responsible to send email
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
author: "$Author: jfiat $"
|
||||
date: "$Date: 2015-06-30 11:07:17 +0200 (mar., 30 juin 2015) $"
|
||||
revision: "$Revision: 97586 $"
|
||||
|
||||
deferred class
|
||||
NOTIFICATION_MAILER
|
||||
@@ -45,8 +45,40 @@ feature -- Basic operation
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Error
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Previous operation reported error?
|
||||
-- Use `reset_errors', to reset this state.
|
||||
do
|
||||
Result := attached last_errors as lst and then not lst.is_empty
|
||||
end
|
||||
|
||||
reset_errors
|
||||
-- Reset last errors.
|
||||
do
|
||||
last_errors := Void
|
||||
end
|
||||
|
||||
last_errors: detachable ARRAYED_LIST [READABLE_STRING_32]
|
||||
-- Last reported errors since previous `reset_errors' call.
|
||||
|
||||
report_error (a_msg: READABLE_STRING_GENERAL)
|
||||
-- Report error message `a_msg'.
|
||||
local
|
||||
lst: like last_errors
|
||||
do
|
||||
lst := last_errors
|
||||
if lst = Void then
|
||||
create lst.make (1)
|
||||
last_errors := lst
|
||||
end
|
||||
lst.force (a_msg.to_string_32)
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
note
|
||||
description: "Mailer that does nothing."
|
||||
date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
|
||||
revision: "$Revision: 97588 $"
|
||||
|
||||
class
|
||||
NOTIFICATION_NULL_MAILER
|
||||
|
||||
inherit
|
||||
NOTIFICATION_MAILER
|
||||
|
||||
feature -- Status
|
||||
|
||||
is_available: BOOLEAN = True
|
||||
-- <Precursor>
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
process_email (a_email: NOTIFICATION_EMAIL)
|
||||
-- <Precursor>
|
||||
do
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -2,9 +2,9 @@ note
|
||||
description : "[
|
||||
NOTIFICATION_MAILER using sendmail as mailtool
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
author: "$Author: jfiat $"
|
||||
date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
|
||||
revision: "$Revision: 97588 $"
|
||||
|
||||
class
|
||||
NOTIFICATION_SENDMAIL_MAILER
|
||||
@@ -16,23 +16,29 @@ inherit
|
||||
end
|
||||
|
||||
create
|
||||
default_create
|
||||
default_create,
|
||||
make_with_location
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_location (a_path: READABLE_STRING_GENERAL)
|
||||
do
|
||||
make (a_path, <<"-t">>)
|
||||
set_stdin_mode (True, "%N.%N%N")
|
||||
end
|
||||
|
||||
default_create
|
||||
do
|
||||
Precursor
|
||||
make ("/usr/sbin/sendmail", <<"-t">>)
|
||||
make_with_location ("/usr/sbin/sendmail")
|
||||
if not is_available then
|
||||
make ("/usr/bin/sendmail", <<"-t">>)
|
||||
make_with_location ("/usr/bin/sendmail")
|
||||
end
|
||||
set_stdin_mode (True, "%N.%N%N")
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
note
|
||||
description: "Summary description for {NOTIFICATION_STORAGE_MAILER}."
|
||||
author: ""
|
||||
date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
|
||||
revision: "$Revision: 97588 $"
|
||||
|
||||
class
|
||||
NOTIFICATION_STORAGE_MAILER
|
||||
|
||||
inherit
|
||||
NOTIFICATION_MAILER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_storage: NOTIFICATION_EMAIL_STORAGE)
|
||||
do
|
||||
storage := a_storage
|
||||
end
|
||||
|
||||
storage: NOTIFICATION_EMAIL_STORAGE
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_available: BOOLEAN
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := storage.is_available
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
process_email (a_email: NOTIFICATION_EMAIL)
|
||||
-- <Precursor>
|
||||
do
|
||||
storage.put (a_email)
|
||||
if storage.has_error then
|
||||
report_error ("Issue storing email.")
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,181 @@
|
||||
note
|
||||
description: "[
|
||||
Notification mailer based on STMP protocol.
|
||||
|
||||
Note: it is based on EiffelNet {SMTP_PROTOCOL} implementation, and may not be complete.
|
||||
]"
|
||||
author: "$Author: jfiat $"
|
||||
date: "$Date: 2015-06-30 11:07:17 +0200 (mar., 30 juin 2015) $"
|
||||
revision: "$Revision: 97586 $"
|
||||
|
||||
class
|
||||
NOTIFICATION_SMTP_MAILER
|
||||
|
||||
inherit
|
||||
NOTIFICATION_MAILER
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_user
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_smtp_server: READABLE_STRING_8)
|
||||
do
|
||||
make_with_user (a_smtp_server, Void, Void)
|
||||
end
|
||||
|
||||
make_with_user (a_smtp_server: READABLE_STRING_8; a_user: detachable READABLE_STRING_8; a_password: detachable READABLE_STRING_8)
|
||||
-- Initialize `Current'.
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
i := a_smtp_server.index_of (':', 1)
|
||||
if i > 0 then
|
||||
smtp_host := a_smtp_server.substring (1, i - 1)
|
||||
smtp_port := a_smtp_server.substring (i + 1, a_smtp_server.count).to_integer
|
||||
else
|
||||
smtp_host := a_smtp_server
|
||||
smtp_port := 0
|
||||
end
|
||||
username := a_user
|
||||
initialize
|
||||
end
|
||||
|
||||
initialize
|
||||
-- Initialize service.
|
||||
local
|
||||
l_address_factory: INET_ADDRESS_FACTORY
|
||||
do
|
||||
if attached username as u then
|
||||
create smtp_protocol.make (smtp_host, u)
|
||||
else
|
||||
-- Get local host name needed in creation of SMTP_PROTOCOL.
|
||||
create l_address_factory
|
||||
create smtp_protocol.make (smtp_host, l_address_factory.create_localhost.host_name)
|
||||
end
|
||||
if smtp_port > 0 then
|
||||
smtp_protocol.set_default_port (smtp_port)
|
||||
end
|
||||
reset_errors
|
||||
end
|
||||
|
||||
smtp_protocol: SMTP_PROTOCOL
|
||||
-- SMTP protocol.
|
||||
|
||||
feature -- Access
|
||||
|
||||
smtp_host: READABLE_STRING_8
|
||||
|
||||
smtp_port: INTEGER
|
||||
|
||||
username: detachable READABLE_STRING_8
|
||||
|
||||
feature -- Status
|
||||
|
||||
is_available: BOOLEAN
|
||||
do
|
||||
Result := True
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
process_email (a_email: NOTIFICATION_EMAIL)
|
||||
-- Process the sending of `a_email'
|
||||
local
|
||||
l_email: EMAIL
|
||||
h: STRING
|
||||
k,v: STRING
|
||||
i: INTEGER
|
||||
hdate: HTTP_DATE
|
||||
do
|
||||
create l_email.make_with_entry (a_email.from_address, addresses_to_header_line_value (a_email.to_addresses))
|
||||
if attached a_email.reply_to_address as l_reply_to then
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_reply_to, l_reply_to)
|
||||
end
|
||||
|
||||
if attached a_email.cc_addresses as lst then
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_cc, addresses_to_header_line_value (lst))
|
||||
end
|
||||
if attached a_email.bcc_addresses as lst then
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.h_bcc, addresses_to_header_line_value (lst))
|
||||
end
|
||||
l_email.set_message (a_email.content)
|
||||
l_email.add_header_entry ({EMAIL_CONSTANTS}.H_subject, a_email.subject)
|
||||
|
||||
create h.make_empty
|
||||
create hdate.make_from_date_time (a_email.date)
|
||||
hdate.append_to_rfc1123_string (h)
|
||||
l_email.add_header_entry ("Date", h)
|
||||
|
||||
if attached a_email.additional_header_lines as lst then
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
h := ic.item
|
||||
i := h.index_of (':', 1)
|
||||
if i > 0 then
|
||||
k := h.head (i - 1)
|
||||
v := h.substring (i + 1, h.count)
|
||||
l_email.add_header_entry (k, v)
|
||||
else
|
||||
check is_header_line: False end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
smtp_send_email (l_email)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
addresses_to_header_line_value (lst: ITERABLE [READABLE_STRING_8]): STRING
|
||||
local
|
||||
l_need_separator: BOOLEAN
|
||||
do
|
||||
create Result.make (10)
|
||||
l_need_separator := False
|
||||
across
|
||||
lst as ic
|
||||
loop
|
||||
if l_need_separator then
|
||||
Result.append_character (',')
|
||||
Result.append_character (' ')
|
||||
else
|
||||
l_need_separator := True
|
||||
end
|
||||
Result.append (ic.item)
|
||||
end
|
||||
end
|
||||
|
||||
smtp_send_email (a_email: EMAIL)
|
||||
-- Send the email represented by `a_email'.
|
||||
local
|
||||
retried: BOOLEAN
|
||||
do
|
||||
if not retried then
|
||||
smtp_protocol.initiate_protocol
|
||||
smtp_protocol.transfer (a_email)
|
||||
smtp_protocol.close_protocol
|
||||
if smtp_protocol.error then
|
||||
report_error ("smtp_protocol reported an error.")
|
||||
end
|
||||
end
|
||||
rescue
|
||||
report_error ("smtp_protocol raised an exception.")
|
||||
retried := True
|
||||
retry
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
note
|
||||
description: "Store email in specific file (could also be stderr, ...)."
|
||||
date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
|
||||
revision: "$Revision: 97588 $"
|
||||
|
||||
class
|
||||
NOTIFICATION_EMAIL_FILE_STORAGE
|
||||
|
||||
inherit
|
||||
NOTIFICATION_EMAIL_STORAGE
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_output_file: FILE)
|
||||
require
|
||||
a_output_file_valid: a_output_file.exists
|
||||
do
|
||||
output := a_output_file
|
||||
end
|
||||
|
||||
output: FILE
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_available: BOOLEAN
|
||||
-- Is associated storage available?
|
||||
do
|
||||
Result := output.exists and output.is_access_writable
|
||||
end
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Last operation reported an error?
|
||||
|
||||
feature -- Storage
|
||||
|
||||
put (a_email: NOTIFICATION_EMAIL)
|
||||
-- Store `a_email'.
|
||||
local
|
||||
retried: BOOLEAN
|
||||
l_close_needed: BOOLEAN
|
||||
do
|
||||
if not retried then
|
||||
has_error := False
|
||||
if not output.is_open_write then
|
||||
output.open_write
|
||||
l_close_needed := True
|
||||
end
|
||||
output.put_string ("%N----%N" + a_email.message)
|
||||
if l_close_needed then
|
||||
output.close
|
||||
else
|
||||
output.flush
|
||||
end
|
||||
end
|
||||
rescue
|
||||
retried := True
|
||||
has_error := True
|
||||
retry
|
||||
end
|
||||
|
||||
;note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
note
|
||||
description: "Abtract interface of email storage."
|
||||
date: "$Date: 2015-06-30 15:49:56 +0200 (mar., 30 juin 2015) $"
|
||||
revision: "$Revision: 97588 $"
|
||||
|
||||
deferred class
|
||||
NOTIFICATION_EMAIL_STORAGE
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_available: BOOLEAN
|
||||
-- Is associated storage available?
|
||||
deferred
|
||||
end
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Last operation reported an error?
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Storage
|
||||
|
||||
put (a_email: NOTIFICATION_EMAIL)
|
||||
-- Store `a_email'.
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -81,7 +81,7 @@ feature -- Element change
|
||||
-- Unset `http_server_name' value.
|
||||
do
|
||||
http_server_name := Void
|
||||
ensure
|
||||
ensure
|
||||
unset_http_server_name: http_server_name = Void
|
||||
end
|
||||
|
||||
@@ -202,11 +202,6 @@ feature -- SSL Helpers
|
||||
deferred
|
||||
end
|
||||
|
||||
set_ssl_protocol_to_ssl_3
|
||||
-- Set `ssl_protocol' with `Ssl_3'.
|
||||
deferred
|
||||
end
|
||||
|
||||
set_ssl_protocol_to_tls_1_0
|
||||
-- Set `ssl_protocol' with `Tls_1_0'.
|
||||
deferred
|
||||
|
||||
@@ -60,7 +60,6 @@ feature -- Access
|
||||
|
||||
is_persistent_connection_supported: BOOLEAN = True
|
||||
-- Is persistent connection supported?
|
||||
--| For now, disabled during dev.
|
||||
|
||||
feature -- Callbacks
|
||||
|
||||
|
||||
@@ -27,11 +27,6 @@ feature -- SSL Helpers
|
||||
-- Ignored
|
||||
end
|
||||
|
||||
set_ssl_protocol_to_ssl_3
|
||||
-- Set `ssl_protocol' with `Ssl_3'.
|
||||
do
|
||||
-- Ignored
|
||||
end
|
||||
|
||||
set_ssl_protocol_to_tls_1_0
|
||||
-- Set `ssl_protocol' with `Tls_1_0'.
|
||||
|
||||
@@ -41,12 +41,6 @@ feature -- SSL Helpers
|
||||
set_ssl_protocol ({SSL_PROTOCOL}.Ssl_23)
|
||||
end
|
||||
|
||||
set_ssl_protocol_to_ssl_3
|
||||
-- Set `ssl_protocol' with `Ssl_3'.
|
||||
do
|
||||
set_ssl_protocol ({SSL_PROTOCOL}.Ssl_3)
|
||||
end
|
||||
|
||||
set_ssl_protocol_to_tls_1_0
|
||||
-- Set `ssl_protocol' with `Tls_1_0'.
|
||||
do
|
||||
|
||||
@@ -87,6 +87,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_requested (is_persistent_connection_requested)
|
||||
|
||||
req.set_meta_string_variable ("RAW_HEADER_DATA", request_header)
|
||||
@@ -111,22 +112,30 @@ feature -- Request processing
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
process_rescue (res: detachable WGI_RESPONSE)
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
if attached (create {EXCEPTION_MANAGER}).last_exception as e and then attached e.trace as l_trace then
|
||||
if res /= Void then
|
||||
if not res.status_is_set then
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error, Void)
|
||||
end
|
||||
create s.make_empty
|
||||
s.append ("<pre>")
|
||||
s.append (html_encoder.encoded_string (l_trace))
|
||||
s.append ("</pre>")
|
||||
if not res.header_committed then
|
||||
res.put_header_text ("Content-Type: text/html%R%NContent-Length: " + s.count.out + "%R%N%R%N")
|
||||
end
|
||||
if res.message_writable then
|
||||
res.put_string ("<pre>")
|
||||
res.put_string (html_encoder.encoded_string (l_trace))
|
||||
res.put_string ("</pre>")
|
||||
res.put_string (s)
|
||||
end
|
||||
res.push
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
httpd_environment (a_socket: HTTPD_STREAM_SOCKET): STRING_TABLE [READABLE_STRING_8]
|
||||
local
|
||||
|
||||
@@ -29,7 +29,7 @@ feature {NONE} -- Initialization
|
||||
last_target_call_succeed := True
|
||||
end
|
||||
|
||||
feature {WGI_STANDALONE_CONNECTOR, WGI_SERVICE} -- Nino
|
||||
feature {WGI_STANDALONE_CONNECTOR, WGI_SERVICE} -- Server
|
||||
|
||||
set_target (o: like target)
|
||||
do
|
||||
@@ -100,7 +100,7 @@ feature -- Status report
|
||||
-- <Precursor>
|
||||
-- for instance IO failure due to socket disconnection.
|
||||
do
|
||||
Result := not last_target_call_succeed
|
||||
Result := last_target_call_succeed
|
||||
end
|
||||
|
||||
is_open_write: BOOLEAN
|
||||
|
||||
@@ -23,6 +23,9 @@ feature -- Settings
|
||||
is_http_version_1_0: BOOLEAN
|
||||
-- Is associated request using HTTP/1.0 ?
|
||||
|
||||
is_persistent_connection_supported: BOOLEAN
|
||||
-- Is persistent connection supported?
|
||||
|
||||
is_persistent_connection_requested: BOOLEAN
|
||||
-- Is persistent connection requested?
|
||||
|
||||
@@ -34,6 +37,12 @@ feature -- Settings change
|
||||
is_http_version_1_0 := True
|
||||
end
|
||||
|
||||
set_is_persistent_connection_supported (b: BOOLEAN)
|
||||
-- Set `is_persistent_connection_supported' to `b'.
|
||||
do
|
||||
is_persistent_connection_supported := b
|
||||
end
|
||||
|
||||
set_is_persistent_connection_requested (b: BOOLEAN)
|
||||
-- Set `is_persistent_connection_requested' to `b'.
|
||||
do
|
||||
@@ -53,7 +62,7 @@ feature -- Header output operation
|
||||
create s.make_from_string (a_text)
|
||||
|
||||
i := s.substring_index ("%NConnection:", 1)
|
||||
if {HTTPD_SERVER}.is_persistent_connection_supported then
|
||||
if is_persistent_connection_supported then
|
||||
-- Current standalone support persistent connection.
|
||||
-- If HTTP/1.1:
|
||||
-- by default all connection are persistent
|
||||
|
||||
23
library/server/ewsgi/package.iron
Normal file
@@ -0,0 +1,23 @@
|
||||
package ewsgi
|
||||
|
||||
project
|
||||
ewsgi = "ewsgi-safe.ecf"
|
||||
ewsgi = "ewsgi.ecf"
|
||||
ewsgi_spec = "ewsgi_spec-safe.ecf"
|
||||
ewsgi_spec = "ewsgi_spec.ecf"
|
||||
connector_cgi = "connectors/cgi/cgi-safe.ecf"
|
||||
connector_cgi = "connectors/cgi/cgi.ecf"
|
||||
connector_libfcgi = "connectors/libfcgi/libfcgi-safe.ecf"
|
||||
connector_libfcgi = "connectors/libfcgi/libfcgi.ecf"
|
||||
connector_nino = "connectors/nino/nino-safe.ecf"
|
||||
connector_nino = "connectors/nino/nino.ecf"
|
||||
connector_null = "connectors/null/null-safe.ecf"
|
||||
connector_null = "connectors/null/null.ecf"
|
||||
|
||||
note
|
||||
title: EWSGI
|
||||
description: EWSGI specification, and a few connectors.
|
||||
tags: web, httpd, ewf
|
||||
license: Eiffel Forum v2
|
||||
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="connector_libfcgi_v0" uuid="59C57E56-3EE6-4EF7-873F-7ED084B0EB22" library_target="connector_libfcgi_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="connector_libfcgi_v0" uuid="5E1C9860-2D9E-4A94-A11D-DA0FD9B31470" library_target="connector_libfcgi_v0">
|
||||
<target name="connector_libfcgi_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="connector_libfcgi_v0" uuid="59C57E56-3EE6-4EF7-873F-7ED084B0EB22" library_target="connector_libfcgi_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="connector_libfcgi_v0" uuid="5E1C9860-2D9E-4A94-A11D-DA0FD9B31470" library_target="connector_libfcgi_v0">
|
||||
<target name="connector_libfcgi_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="connector_nino_v0" uuid="F91861FB-4FEA-455F-9570-828D7903DC64" library_target="connector_nino_v0">
|
||||
<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="connector_nino_v0" uuid="6E00FB27-C0E2-4859-93A0-BF516C44C95F" library_target="connector_nino_v0">
|
||||
<target name="connector_nino_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="connector_nino_v0" uuid="F91861FB-4FEA-455F-9570-828D7903DC64" library_target="connector_nino_v0">
|
||||
<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="connector_nino_v0" uuid="6E00FB27-C0E2-4859-93A0-BF516C44C95F" library_target="connector_nino_v0">
|
||||
<target name="connector_nino_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_libfcgi_v0" uuid="00B169F1-2BE2-4986-8B93-825FEB944FFD" library_target="wsf_libfcgi_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_libfcgi_v0" uuid="36DB043B-E4E9-4BB6-936C-3564335C34EF" library_target="wsf_libfcgi_v0">
|
||||
<target name="wsf_libfcgi_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_libfcgi_v0" uuid="00B169F1-2BE2-4986-8B93-825FEB944FFD" library_target="wsf_libfcgi_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_libfcgi_v0" uuid="36DB043B-E4E9-4BB6-936C-3564335C34EF" library_target="wsf_libfcgi_v0">
|
||||
<target name="wsf_libfcgi_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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_nino_v0" uuid="BACF0220-900B-4409-8CB2-30A09836A650" library_target="wsf_nino_v0">
|
||||
<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_nino_v0" uuid="81525F4F-7BCA-471B-B1E3-2601EFD242E0" library_target="wsf_nino_v0">
|
||||
<target name="wsf_nino_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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_nino_v0" uuid="BACF0220-900B-4409-8CB2-30A09836A650" library_target="wsf_nino_v0">
|
||||
<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_nino_v0" uuid="81525F4F-7BCA-471B-B1E3-2601EFD242E0" library_target="wsf_nino_v0">
|
||||
<target name="wsf_nino_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="default_libfcgi_v0" uuid="B853CC3A-D173-4DA4-9832-6D0148C8F01F" library_target="default_libfcgi_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="default_libfcgi_v0" uuid="757C822A-6A23-42E6-9A30-2B0977960E3D" library_target="default_libfcgi_v0">
|
||||
<target name="default_libfcgi_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="default_libfcgi_v0" uuid="B853CC3A-D173-4DA4-9832-6D0148C8F01F" library_target="default_libfcgi_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="default_libfcgi_v0" uuid="757C822A-6A23-42E6-9A30-2B0977960E3D" library_target="default_libfcgi_v0">
|
||||
<target name="default_libfcgi_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="default_nino_v0" uuid="ACBEDC97-956C-45F5-97E3-65A6D9987625" library_target="default_nino_v0">
|
||||
<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="default_nino_v0" uuid="D3606AED-7593-460A-94FD-2859C71386FF" library_target="default_nino_v0">
|
||||
<target name="default_nino_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="default_nino_v0" uuid="ACBEDC97-956C-45F5-97E3-65A6D9987625" library_target="default_nino_v0">
|
||||
<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="default_nino_v0" uuid="D3606AED-7593-460A-94FD-2859C71386FF" library_target="default_nino_v0">
|
||||
<target name="default_nino_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_extension_v0" uuid="11055B92-CBEF-4272-8B50-0450BDA154E5" library_target="wsf_extension_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_extension_v0" uuid="66FC8180-27E6-4335-B571-4D38D3BC633A" library_target="wsf_extension_v0">
|
||||
<target name="wsf_extension_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_extension_v0" uuid="11055B92-CBEF-4272-8B50-0450BDA154E5" library_target="wsf_extension_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_extension_v0" uuid="66FC8180-27E6-4335-B571-4D38D3BC633A" library_target="wsf_extension_v0">
|
||||
<target name="wsf_extension_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="wsf_policy_driven_v0" uuid="3FC00449-5101-461D-94C6-10920C30EBF4" library_target="wsf_policy_driven_v0">
|
||||
<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="wsf_policy_driven_v0" uuid="E175F999-1164-48AA-83E1-6C14FC5E8C82" library_target="wsf_policy_driven_v0">
|
||||
<target name="wsf_policy_driven_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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="wsf_policy_driven_v0" uuid="3FC00449-5101-461D-94C6-10920C30EBF4" library_target="wsf_policy_driven_v0">
|
||||
<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="wsf_policy_driven_v0" uuid="E175F999-1164-48AA-83E1-6C14FC5E8C82" library_target="wsf_policy_driven_v0">
|
||||
<target name="wsf_policy_driven_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_router_context_v0" uuid="1A0F9B0E-26CE-4DE0-BE47-C74D1AB2B389" library_target="wsf_router_context_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_router_context_v0" uuid="BA6468F6-2130-45FC-A2F7-26A7781B09CE" library_target="wsf_router_context_v0">
|
||||
<target name="wsf_router_context_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_router_context_v0" uuid="1A0F9B0E-26CE-4DE0-BE47-C74D1AB2B389" library_target="wsf_router_context_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_router_context_v0" uuid="BA6468F6-2130-45FC-A2F7-26A7781B09CE" library_target="wsf_router_context_v0">
|
||||
<target name="wsf_router_context_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<redirection 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" uuid="C41D0367-9852-4AA7-9E7E-DEA162A4CBB0" location="../../../wsf/wsf_session-safe.ecf">
|
||||
<redirection 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" uuid="74D6AAE4-1AF5-4D5C-B19D-D24383B7DE93" location="../../../wsf/wsf_session-safe.ecf">
|
||||
</redirection>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<redirection 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" uuid="C41D0367-9852-4AA7-9E7E-DEA162A4CBB0" location="../../../wsf/wsf_session.ecf">
|
||||
<redirection 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" uuid="74D6AAE4-1AF5-4D5C-B19D-D24383B7DE93" location="../../../wsf/wsf_session.ecf">
|
||||
</redirection>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_html_v0" uuid="6AAAE037-7E66-4F5D-BED0-0042245C26BC" library_target="wsf_html_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-11-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-11-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_html_v0" uuid="9D0DC2A2-BE67-4499-B730-87C7DDE25860" library_target="wsf_html_v0">
|
||||
<target name="wsf_html_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="wsf_html_v0" uuid="6AAAE037-7E66-4F5D-BED0-0042245C26BC" library_target="wsf_html_v0">
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-11-0.xsd" name="wsf_html_v0" uuid="9D0DC2A2-BE67-4499-B730-87C7DDE25860" library_target="wsf_html_v0">
|
||||
<target name="wsf_html_v0">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -9,13 +9,14 @@
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
||||
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="encoder" location="../../../../text/encoder/encoder.ecf"/>
|
||||
<library name="uri_template" location="../../../../text/parser/uri_template/uri_template.ecf"/>
|
||||
<library name="wsf" location="../wsf/wsf.ecf"/>
|
||||
<cluster name="form" location="./form" recursive="true"/>
|
||||
<cluster name="widget" location="./widget" recursive="true"/>
|
||||
<cluster name="css" location="./css" recursive="true"/>
|
||||
<cluster name="api" location="./api" recursive="true"/>
|
||||
<library name="wsf" location="..\wsf\wsf.ecf"/>
|
||||
<cluster name="api" location="..\..\..\wsf_html\api\" recursive="true"/>
|
||||
<cluster name="css" location="..\..\..\wsf_html\css\" recursive="true"/>
|
||||
<cluster name="form" location="..\..\..\wsf_html\form\" recursive="true"/>
|
||||
<cluster name="widget" location="..\..\..\wsf_html\widget\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -59,6 +59,7 @@ feature -- Access
|
||||
|
||||
create dbg.make
|
||||
dbg.set_is_verbose (True)
|
||||
dbg.set_unicode_output_enabled (True)
|
||||
dbg.append_cgi_variables_to (req, res, s)
|
||||
dbg.append_information_to (req, res, s)
|
||||
|
||||
|
||||
@@ -42,6 +42,9 @@ feature -- Settings
|
||||
is_verbose: BOOLEAN
|
||||
-- Has verbose output (default: True)?
|
||||
|
||||
unicode_output_enabled: BOOLEAN
|
||||
-- Display names and values as unicode.
|
||||
|
||||
feature -- Settings change
|
||||
|
||||
set_is_verbose (b: BOOLEAN)
|
||||
@@ -50,6 +53,12 @@ feature -- Settings change
|
||||
is_verbose := b
|
||||
end
|
||||
|
||||
set_unicode_output_enabled (b: BOOLEAN)
|
||||
-- if `b' then enable unicode output, otherwise disable.
|
||||
do
|
||||
unicode_output_enabled := b
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
append_connector_informations_to (req: WSF_REQUEST; res: WSF_RESPONSE; a_output: STRING)
|
||||
@@ -232,7 +241,7 @@ feature {NONE} -- Implementation
|
||||
it as c
|
||||
loop
|
||||
s.append (" - ")
|
||||
s.append (utf.escaped_utf_32_string_to_utf_8_string_8 (c.key))
|
||||
append_unicode (c.key, s)
|
||||
s.append_character (' ')
|
||||
if attached c.item as l_item then
|
||||
s.append_character ('{')
|
||||
@@ -302,7 +311,11 @@ feature {NONE} -- Implementation
|
||||
it as c
|
||||
loop
|
||||
a_output.append (" - ")
|
||||
a_output.append (c.item.url_encoded_name)
|
||||
if unicode_output_enabled then
|
||||
append_unicode (c.item.name, a_output)
|
||||
else
|
||||
a_output.append (c.item.url_encoded_name)
|
||||
end
|
||||
t := c.item.generating_type
|
||||
if t.same_string ("WSF_STRING") then
|
||||
else
|
||||
@@ -313,15 +326,15 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
a_output.append_character ('=')
|
||||
if attached {WSF_STRING} c.item as l_str then
|
||||
s := l_str.url_encoded_value
|
||||
if unicode_output_enabled then
|
||||
s := l_str.value
|
||||
else
|
||||
s := l_str.url_encoded_value
|
||||
end
|
||||
else
|
||||
s := c.item.string_representation
|
||||
end
|
||||
if s.is_valid_as_string_8 then
|
||||
v := s.as_string_8
|
||||
else
|
||||
v := utf.escaped_utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
v := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
if v.has ('%N') then
|
||||
a_output.append (eol)
|
||||
across
|
||||
@@ -349,7 +362,6 @@ feature {NONE} -- Implementation
|
||||
local
|
||||
n: INTEGER
|
||||
v: READABLE_STRING_8
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
if is_verbose then
|
||||
a_output.append (a_title)
|
||||
@@ -368,11 +380,7 @@ feature {NONE} -- Implementation
|
||||
it as c
|
||||
loop
|
||||
a_output.append (" - ")
|
||||
if c.key.is_valid_as_string_8 then
|
||||
a_output.append (c.key.as_string_8)
|
||||
else
|
||||
a_output.append (utf.utf_32_string_to_utf_8_string_8 (c.key))
|
||||
end
|
||||
append_unicode (c.key, a_output)
|
||||
a_output.append_character ('=')
|
||||
v := c.item
|
||||
if v.has ('%N') then
|
||||
@@ -398,6 +406,13 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
end
|
||||
|
||||
append_unicode (s: READABLE_STRING_GENERAL; a_output: STRING)
|
||||
local
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
a_output.append (utf.utf_32_string_to_utf_8_string_8 (s))
|
||||
end
|
||||
|
||||
feature -- Constants
|
||||
|
||||
eol: STRING = "%N"
|
||||
|
||||
@@ -116,7 +116,7 @@ feature -- Output operation
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {WSF_APPLICATION_X_WWW_FORM_URLENCODED_HANDLER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
@@ -12,6 +11,11 @@ inherit
|
||||
|
||||
WSF_MIME_HANDLER_HELPER
|
||||
|
||||
WSF_VALUE_UTILITIES
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
valid_content_type (a_content_type: HTTP_CONTENT_TYPE): BOOLEAN
|
||||
@@ -54,7 +58,7 @@ feature -- Execution
|
||||
if j > 0 then
|
||||
l_name := s.substring (1, j - 1)
|
||||
l_value := s.substring (j + 1, s.count)
|
||||
add_string_value_to_table (l_name, l_value, a_vars)
|
||||
add_percent_encoded_string_value_to_table (l_name, l_value, a_vars)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -62,7 +66,7 @@ feature -- Execution
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
note
|
||||
description: "Summary description for {WSF_MULTIPART_FORM_DATA_HANDLER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
@@ -139,10 +138,12 @@ feature {NONE} -- Implementation: Form analyzer
|
||||
local
|
||||
n, i,p, b,e: INTEGER
|
||||
l_name, l_filename, l_content_type: detachable STRING_8
|
||||
l_unicode_name: READABLE_STRING_32
|
||||
l_header: detachable STRING_8
|
||||
l_content: detachable STRING_8
|
||||
l_line: detachable STRING_8
|
||||
l_up_file: WSF_UPLOADED_FILE
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
from
|
||||
p := 1
|
||||
@@ -235,12 +236,13 @@ feature {NONE} -- Implementation: Form analyzer
|
||||
if l_content_type = Void then
|
||||
l_content_type := default_content_type
|
||||
end
|
||||
create l_up_file.make (l_name, l_filename, l_content_type, l_content.count)
|
||||
add_value_to_table (l_name, l_up_file, vars)
|
||||
l_unicode_name := utf.utf_8_string_8_to_string_32 (l_name)
|
||||
create l_up_file.make (l_unicode_name, utf.utf_8_string_8_to_escaped_string_32 (l_filename), l_content_type, l_content.count)
|
||||
add_value_to_table (l_unicode_name, l_up_file, vars)
|
||||
--| `l_up_file' might have a new name
|
||||
req.save_uploaded_file (l_up_file, l_content)
|
||||
else
|
||||
add_string_value_to_table (l_name, l_content, vars)
|
||||
add_utf_8_string_value_to_table (l_name, l_content, vars)
|
||||
end
|
||||
else
|
||||
req.error_handler.add_custom_error (0, "unamed multipart entry", Void)
|
||||
@@ -254,7 +256,7 @@ feature {NONE} -- Implementation: Form analyzer
|
||||
-- Default content type
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
|
||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||