Compare commits
41 Commits
v1_executo
...
es_rev_979
| Author | SHA1 | Date | |
|---|---|---|---|
| 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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1902,7 +1902,7 @@ feature -- URL Utility
|
||||
elseif spos > 0 then
|
||||
i := spos
|
||||
end
|
||||
spos := l_rq_uri.substring_index (path_info, i)
|
||||
spos := l_rq_uri.substring_index (percent_encoded_path_info, i)
|
||||
if spos > 0 then
|
||||
l_base_url := l_rq_uri.substring (1, spos - 1)
|
||||
else
|
||||
|
||||
@@ -319,8 +319,8 @@ feature -- Header output operation: helpers
|
||||
feature -- Header add cookie
|
||||
|
||||
add_cookie (a_cookie: WSF_COOKIE)
|
||||
-- Add a Set-Cookie header field to the response, iff there is not exist
|
||||
-- a Set-Cookie header field with the same cookie-name.
|
||||
-- Add a Set-Cookie header field to the response,
|
||||
-- if no Set-Cookie header field already use same cookie-name.
|
||||
--| Servers SHOULD NOT include more than one Set-Cookie header field in
|
||||
--| the same response with the same cookie-name.
|
||||
local
|
||||
@@ -328,7 +328,8 @@ feature -- Header add cookie
|
||||
do
|
||||
across
|
||||
internal_header.headers as ic
|
||||
until l_same_cookie_name
|
||||
until
|
||||
l_same_cookie_name
|
||||
loop
|
||||
if ic.item.starts_with ("Set-Cookie:") then
|
||||
l_same_cookie_name := has_cookie_name (ic.item, a_cookie.name)
|
||||
@@ -544,24 +545,29 @@ feature -- Error reporting
|
||||
|
||||
feature {NONE} -- Implemenation
|
||||
|
||||
has_cookie_name (a_cookie_line, a_cookie_name: READABLE_STRING_32 ): BOOLEAN
|
||||
-- Has the cookie line `a_cookie_line', the cookie name `a_cookie_name'?
|
||||
local
|
||||
i,j: INTEGER
|
||||
do
|
||||
Result := False
|
||||
i := a_cookie_line.index_of ('=', 1)
|
||||
has_cookie_name (a_cookie_line, a_cookie_name: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Has the cookie line `a_cookie_line', the cookie name `a_cookie_name'?
|
||||
local
|
||||
i,j,n: INTEGER
|
||||
do
|
||||
j := a_cookie_line.index_of (':', 1)
|
||||
|
||||
if i > j and j > 0 then
|
||||
i := i - 1
|
||||
j := j + 1
|
||||
from until not a_cookie_line[j].is_space loop
|
||||
j := j + 1
|
||||
end
|
||||
if a_cookie_name.same_characters (a_cookie_line, j, i, 1) then
|
||||
Result := True
|
||||
end
|
||||
if j > 0 then
|
||||
i := a_cookie_line.index_of ('=', 1)
|
||||
if i > j then
|
||||
i := i - 1
|
||||
j := j + 1
|
||||
-- Skip spaces.
|
||||
from
|
||||
n := a_cookie_line.count
|
||||
until
|
||||
j > n or not a_cookie_line[j].is_space
|
||||
loop
|
||||
j := j + 1
|
||||
end
|
||||
if j > n then
|
||||
Result := a_cookie_name.same_characters (a_cookie_line, j, i, 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -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" uuid="A37CE5AA-4D2A-4441-BC6A-0A1D7EC49647" library_target="wsf">
|
||||
<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" uuid="A37CE5AA-4D2A-4441-BC6A-0A1D7EC49647" library_target="wsf">
|
||||
<target name="wsf">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
@@ -9,23 +9,23 @@
|
||||
</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"/>
|
||||
<mapping old_name="WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_EXECUTION" new_name="WSF_ROUTED_URI_TEMPLATE_HELPER"/>
|
||||
<mapping old_name="WSF_URI_HELPER_FOR_ROUTED_EXECUTION" new_name="WSF_ROUTED_URI_HELPER"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
|
||||
<library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="error" location="../../utility/general/error/error.ecf"/>
|
||||
<library name="http" location="../../network/protocol/http/http.ecf"/>
|
||||
<library name="conneg" location="..\..\network\protocol\content_negotiation\conneg.ecf"/>
|
||||
<library name="uri_template"
|
||||
location="../../text/parser/uri_template/uri_template.ecf"/>
|
||||
<library name="encoder"
|
||||
location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf" readonly="true"/>
|
||||
<cluster name="src" location=".\src" recursive="true"/>
|
||||
<cluster name="router" location=".\router" recursive="true">
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="error" location="..\..\utility\general\error\error.ecf"/>
|
||||
<library name="ewsgi" location="..\ewsgi\ewsgi.ecf"/>
|
||||
<library name="http" location="..\..\network\protocol\http\http.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<library name="uri_template" location="..\..\text\parser\uri_template\uri_template.ecf"/>
|
||||
<cluster name="router" location=".\router\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/policy_driven$</exclude>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -72,18 +72,28 @@ feature -- Change
|
||||
|
||||
set_value (v: detachable WSF_VALUE)
|
||||
-- Set value `v' if applicable to Current
|
||||
local
|
||||
l_found: BOOLEAN
|
||||
do
|
||||
if attached {ITERABLE [WSF_VALUE]} v as lst then
|
||||
across
|
||||
lst as c
|
||||
until
|
||||
l_found
|
||||
loop
|
||||
set_checked_by_value (c.item)
|
||||
if attached {WSF_STRING} c.item as s and then is_same_value (s.value) then
|
||||
set_checked_by_value (c.item)
|
||||
l_found := True
|
||||
end
|
||||
end
|
||||
if not l_found then
|
||||
set_checked (False)
|
||||
end
|
||||
else
|
||||
set_checked_by_value (v)
|
||||
Precursor (v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
|
||||
14
library/text/parser/feed/feed-safe.ecf
Normal file
@@ -0,0 +1,14 @@
|
||||
<?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="feed" uuid="71364A69-1549-472E-AF78-FDA4FDA016EB" library_target="feed">
|
||||
<target name="feed">
|
||||
<root all_classes="true"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid-safe.ecf"/>
|
||||
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser-safe.ecf"/>
|
||||
<library name="xml_tree" location="$ISE_LIBRARY\library\text\parser\xml\tree\xml_tree-safe.ecf"/>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
16
library/text/parser/feed/feed.ecf
Normal file
@@ -0,0 +1,16 @@
|
||||
<?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="feed" uuid="71364A69-1549-472E-AF78-FDA4FDA016EB" library_target="feed">
|
||||
<target name="feed">
|
||||
<root all_classes="true"/>
|
||||
<option void_safety="none">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
|
||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid.ecf"/>
|
||||
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser.ecf"/>
|
||||
<library name="xml_tree" location="$ISE_LIBRARY\library\text\parser\xml\tree\xml_tree.ecf"/>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
186
library/text/parser/feed/src/atom/atom_feed_generator.e
Normal file
@@ -0,0 +1,186 @@
|
||||
note
|
||||
description: "Convert a FEED into an ATOM content."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ATOM_FEED_GENERATOR
|
||||
|
||||
inherit
|
||||
FEED_VISITOR
|
||||
|
||||
FEED_GENERATOR
|
||||
rename
|
||||
process_feed as visit_feed
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
visit_feed (a_feed: FEED)
|
||||
do
|
||||
buffer.append ("[
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
]")
|
||||
buffer.append_character ('%N')
|
||||
indent
|
||||
append_content_tag_to ("title", Void, a_feed.title, buffer)
|
||||
append_content_tag_to ("subtitle", Void, a_feed.description, buffer)
|
||||
if attached a_feed.id as l_id then
|
||||
append_content_tag_to ("id", Void, l_id, buffer)
|
||||
else
|
||||
append_content_tag_to ("id", Void, "urn:uuid:" + new_uuid, buffer)
|
||||
end
|
||||
|
||||
across
|
||||
a_feed.links as tb
|
||||
loop
|
||||
tb.item.accept (Current)
|
||||
end
|
||||
if attached a_feed.date as dt then
|
||||
append_content_tag_to ("updated", Void, date_to_string (dt), buffer)
|
||||
end
|
||||
across
|
||||
a_feed.items as ic
|
||||
loop
|
||||
ic.item.accept (Current)
|
||||
end
|
||||
|
||||
exdent
|
||||
buffer.append ("</feed>")
|
||||
end
|
||||
|
||||
visit_item (a_entry: FEED_ITEM)
|
||||
do
|
||||
buffer.append (indentation)
|
||||
buffer.append ("<entry>%N")
|
||||
indent
|
||||
append_content_tag_to ("title", Void, a_entry.title, buffer)
|
||||
across
|
||||
a_entry.links as tb
|
||||
loop
|
||||
tb.item.accept (Current)
|
||||
end
|
||||
if attached a_entry.id as l_id then
|
||||
append_content_tag_to ("id", Void, l_id, buffer)
|
||||
else
|
||||
append_content_tag_to ("id", Void, "urn:uuid:" + new_uuid, buffer)
|
||||
end
|
||||
if attached a_entry.date as dt then
|
||||
append_content_tag_to ("updated", Void, date_to_string (dt), buffer)
|
||||
end
|
||||
|
||||
append_content_tag_to ("summary", Void, a_entry.description, buffer)
|
||||
if attached a_entry.content as l_content then
|
||||
if attached a_entry.content_type_or_default ("xhtml").is_case_insensitive_equal_general ("xhtml") then
|
||||
-- if l_content.has_substring ("<div xmlns=%"http://www.w3.org/1999/xhtml%">") then
|
||||
append_content_tag_to ("content", <<["type", "xhtml"]>>, l_content, buffer)
|
||||
-- else
|
||||
-- append_content_tag_to ("content", <<["type", "xhtml"]>>, {STRING_32} "<div xmlns=%"http://www.w3.org/1999/xhtml%">" + l_content + {STRING_32} "</div>", buffer)
|
||||
-- end
|
||||
else
|
||||
append_content_tag_to ("content", <<["type", a_entry.content_type]>>, a_entry.content, buffer)
|
||||
end
|
||||
end
|
||||
|
||||
if attached a_entry.author as u then
|
||||
u.accept (Current)
|
||||
end
|
||||
exdent
|
||||
buffer.append (indentation)
|
||||
buffer.append ("</entry>%N")
|
||||
end
|
||||
|
||||
visit_link (a_link: FEED_LINK)
|
||||
local
|
||||
attr: detachable ARRAYED_LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_32]]
|
||||
tu: TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_32]
|
||||
do
|
||||
create attr.make (2)
|
||||
if attached a_link.relation as rel and then not rel.is_whitespace then
|
||||
tu := ["rel", rel]
|
||||
attr.force (tu)
|
||||
end
|
||||
if attached a_link.type as t and then not t.is_whitespace then
|
||||
tu := ["type", t.as_string_32]
|
||||
attr.force (tu)
|
||||
end
|
||||
tu := ["href", a_link.href.as_string_32]
|
||||
attr.force (tu)
|
||||
if attr.is_empty then
|
||||
attr := Void
|
||||
end
|
||||
append_content_tag_to ("link", attr, Void, buffer)
|
||||
end
|
||||
|
||||
visit_author (a_author: FEED_AUTHOR)
|
||||
do
|
||||
buffer.append (indentation)
|
||||
buffer.append ("<author>%N")
|
||||
indent
|
||||
append_content_tag_to ("name", Void, a_author.name, buffer)
|
||||
append_content_tag_to ("email", Void, a_author.email, buffer)
|
||||
exdent
|
||||
buffer.append (indentation)
|
||||
buffer.append ("</author>%N")
|
||||
end
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
new_uuid: STRING
|
||||
local
|
||||
gen: UUID_GENERATOR
|
||||
do
|
||||
create gen
|
||||
Result := gen.generate_uuid.out.as_lower
|
||||
end
|
||||
|
||||
date_to_string (dt: DATE_TIME): STRING
|
||||
do
|
||||
Result := date_to_rfc3339_string (dt)
|
||||
end
|
||||
|
||||
date_to_rfc3339_string (d: DATE_TIME): STRING
|
||||
-- 2003-12-13T18:30:02Z
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
create Result.make_empty
|
||||
Result.append_integer (d.year)
|
||||
Result.append_character ('-')
|
||||
i := d.month
|
||||
if i < 10 then
|
||||
Result.append_integer (0)
|
||||
end
|
||||
Result.append_integer (i)
|
||||
Result.append_character ('-')
|
||||
i := d.day
|
||||
if i < 10 then
|
||||
Result.append_integer (0)
|
||||
end
|
||||
Result.append_integer (i)
|
||||
Result.append_character ('T')
|
||||
i := d.hour
|
||||
if i < 10 then
|
||||
Result.append_integer (0)
|
||||
end
|
||||
Result.append_integer (i)
|
||||
Result.append_character (':')
|
||||
i := d.minute
|
||||
if i < 10 then
|
||||
Result.append_integer (0)
|
||||
end
|
||||
Result.append_integer (i)
|
||||
Result.append_character (':')
|
||||
i := d.second
|
||||
if i < 10 then
|
||||
Result.append_integer (0)
|
||||
end
|
||||
Result.append_integer (i)
|
||||
Result.append_character ('Z')
|
||||
end
|
||||
|
||||
end
|
||||
104
library/text/parser/feed/src/atom/atom_feed_parser.e
Normal file
@@ -0,0 +1,104 @@
|
||||
note
|
||||
description: "[
|
||||
ATOM Parser.
|
||||
|
||||
Warning: the implementation may not support the full ATOM specification.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=ATOM at wikipedia", "protocol=URI", "src=https://en.wikipedia.org/wiki/Atom_(standard)"
|
||||
EIS: "name=RSS at wikipedia", "protocol=URI", "src=https://en.wikipedia.org/wiki/RSS"
|
||||
EIS: "name=ATOM 1.0 RFC4287", "protocol=URI", "src=https://tools.ietf.org/html/rfc4287"
|
||||
|
||||
class
|
||||
ATOM_FEED_PARSER
|
||||
|
||||
inherit
|
||||
FEED_PARSER
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "atom1"
|
||||
-- Associated name.
|
||||
|
||||
is_detected (xdoc: XML_DOCUMENT): BOOLEAN
|
||||
-- Is `xdoc' an ATOM feed representation?
|
||||
do
|
||||
Result := attached {XML_ELEMENT} xdoc.element_by_name ("feed") as x_feed and then
|
||||
(
|
||||
not attached xml_attribute_text (x_feed, "xmlns") as l_xmlns
|
||||
or else l_xmlns.same_string ("http://www.w3.org/2005/Atom")
|
||||
)
|
||||
end
|
||||
|
||||
feed (xdoc: XML_DOCUMENT): detachable FEED
|
||||
-- Feed from `xdoc' XML document.
|
||||
local
|
||||
l_title: READABLE_STRING_32
|
||||
x_entry, x_link: detachable XML_ELEMENT
|
||||
e: FEED_ITEM
|
||||
l_author: FEED_AUTHOR
|
||||
lnk: FEED_LINK
|
||||
s: STRING_32
|
||||
do
|
||||
if
|
||||
attached xdoc.element_by_name ("feed") as x_feed and then
|
||||
-- (not attached xml_attribute_text (x_feed, "xmlns") as l_xmlns or else l_xmlns.same_string ("http://www.w3.org/2005/Atom"))
|
||||
attached xml_element_text (x_feed, "title") as t
|
||||
then
|
||||
l_title := t
|
||||
create Result.make (l_title)
|
||||
Result.set_description (xml_element_text (x_feed, "subtitle"), "plain")
|
||||
Result.set_id (xml_element_text (x_feed, "id"))
|
||||
Result.set_updated_date_with_text (xml_element_text (x_feed, "updated"))
|
||||
if attached links_from_xml (x_feed, "link") as l_links then
|
||||
across
|
||||
l_links as link_ic
|
||||
loop
|
||||
lnk := link_ic.item
|
||||
Result.links.force (lnk, lnk.relation)
|
||||
end
|
||||
end
|
||||
if attached x_feed.elements_by_name ("entry") as x_entries then
|
||||
across
|
||||
x_entries as ic
|
||||
loop
|
||||
x_entry := ic.item
|
||||
if attached xml_element_text (x_entry, "title") as e_title then
|
||||
create e.make (e_title)
|
||||
e.set_description (xml_element_text (x_entry, "summary"))
|
||||
e.set_id (xml_element_text (x_entry, "id"))
|
||||
e.set_updated_date_with_text (xml_element_text (x_entry, "updated"))
|
||||
|
||||
if attached links_from_xml (x_entry, "link") as l_links then
|
||||
across
|
||||
l_links as link_ic
|
||||
loop
|
||||
lnk := link_ic.item
|
||||
e.links.force (lnk, lnk.relation)
|
||||
end
|
||||
end
|
||||
if attached x_entry.element_by_name ("content") as x_content then
|
||||
e.set_content (xml_element_code (x_content), xml_attribute_text (x_content, "type"))
|
||||
end
|
||||
if attached x_entry.element_by_name ("author") as x_author then
|
||||
if attached x_author.element_by_name ("name") as x_name and then
|
||||
attached x_name.text as l_author_name
|
||||
then
|
||||
create l_author.make (l_author_name)
|
||||
if attached x_author.element_by_name ("email") as x_email then
|
||||
l_author.set_email (x_email.text)
|
||||
end
|
||||
e.set_author (l_author)
|
||||
end
|
||||
end
|
||||
Result.extend (e)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
69
library/text/parser/feed/src/feed_default_parsers.e
Normal file
@@ -0,0 +1,69 @@
|
||||
note
|
||||
description: "[
|
||||
Collection of default feed parsers provided by the current library.
|
||||
A new parser can be easily added via `parsers.extend (...)'.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FEED_DEFAULT_PARSERS
|
||||
|
||||
inherit
|
||||
ANY
|
||||
redefine
|
||||
default_create
|
||||
end
|
||||
|
||||
create
|
||||
default_create
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
default_create
|
||||
do
|
||||
Precursor
|
||||
create {ARRAYED_LIST [FEED_PARSER]} parsers.make (2)
|
||||
parsers.force (create {RSS_2_FEED_PARSER})
|
||||
parsers.force (create {ATOM_FEED_PARSER})
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
parsers: LIST [FEED_PARSER]
|
||||
-- Available Feed parsers.
|
||||
|
||||
feature -- Access
|
||||
|
||||
feed_from_string (a_content: READABLE_STRING_8): detachable FEED
|
||||
-- Feed object from `a_content' string, if a parser is able to parse.it.
|
||||
local
|
||||
p: XML_STANDARD_PARSER
|
||||
cb_tree: XML_CALLBACKS_FILTER_DOCUMENT
|
||||
xdoc: XML_DOCUMENT
|
||||
do
|
||||
create p.make
|
||||
create cb_tree.make_null
|
||||
p.set_callbacks (cb_tree)
|
||||
p.parse_from_string_8 (a_content)
|
||||
if p.is_correct then
|
||||
xdoc := cb_tree.document
|
||||
Result := feed (xdoc)
|
||||
end
|
||||
end
|
||||
|
||||
feed (xdoc: XML_DOCUMENT): like feed_from_string
|
||||
-- Feed from `xdoc' XML document.
|
||||
do
|
||||
across
|
||||
parsers as ic
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
if ic.item.is_detected (xdoc) then
|
||||
Result := ic.item.feed (xdoc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
62
library/text/parser/feed/src/feed_parser.e
Normal file
@@ -0,0 +1,62 @@
|
||||
note
|
||||
description: "[
|
||||
Interface common to any FEED parser.
|
||||
|
||||
Usage:
|
||||
create parser
|
||||
if attached parser.feed_from_string (l_feed_content) as l_feed then
|
||||
...
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
FEED_PARSER
|
||||
|
||||
inherit
|
||||
FEED_PARSER_UTILITIES
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING
|
||||
-- Associated name.
|
||||
deferred
|
||||
ensure
|
||||
not_blanc: not Result.is_whitespace
|
||||
end
|
||||
|
||||
is_detected (xdoc: XML_DOCUMENT): BOOLEAN
|
||||
-- Is `xdoc' an feed representation or Current supported format?
|
||||
deferred
|
||||
end
|
||||
|
||||
feed (xdoc: XML_DOCUMENT): detachable FEED
|
||||
-- Feed from `xdoc' XML document.
|
||||
require
|
||||
is_detected: is_detected (xdoc)
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Basic operations
|
||||
|
||||
feed_from_string (a_content: READABLE_STRING_8): like feed
|
||||
-- Feed from `a_content' document.
|
||||
local
|
||||
p: XML_STANDARD_PARSER
|
||||
cb_tree: XML_CALLBACKS_FILTER_DOCUMENT
|
||||
xdoc: XML_DOCUMENT
|
||||
do
|
||||
create p.make
|
||||
create cb_tree.make_null
|
||||
p.set_callbacks (cb_tree)
|
||||
p.parse_from_string_8 (a_content)
|
||||
if p.is_correct then
|
||||
xdoc := cb_tree.document
|
||||
if is_detected (xdoc) then
|
||||
Result := feed (xdoc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
139
library/text/parser/feed/src/kernel/feed.e
Normal file
@@ -0,0 +1,139 @@
|
||||
note
|
||||
description: "FEED interface, could be RSS, ATOM, ..."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FEED
|
||||
|
||||
inherit
|
||||
FEED_HELPERS
|
||||
|
||||
ITERABLE [FEED_ITEM]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_title: READABLE_STRING_GENERAL)
|
||||
do
|
||||
create title.make_from_string_general (a_title)
|
||||
create items.make (1)
|
||||
create links.make (1)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
title: IMMUTABLE_STRING_32
|
||||
-- Title of the feed/channel.
|
||||
|
||||
description: detachable IMMUTABLE_STRING_32
|
||||
-- Associated description/subtitle.
|
||||
|
||||
description_content_type: detachable READABLE_STRING_8
|
||||
-- Optional content type for `description'.
|
||||
-- By default, this should be text/plain.
|
||||
|
||||
id: detachable IMMUTABLE_STRING_32
|
||||
-- Id associated with Current feed if any.
|
||||
|
||||
date: detachable DATE_TIME
|
||||
-- Build date.
|
||||
|
||||
links: STRING_TABLE [FEED_LINK]
|
||||
-- Url indexed by relation
|
||||
|
||||
items: ARRAYED_LIST [FEED_ITEM]
|
||||
-- List of feed items.
|
||||
|
||||
feature -- Access
|
||||
|
||||
new_cursor: ITERATION_CURSOR [FEED_ITEM]
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := items.new_cursor
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_description (a_description: detachable READABLE_STRING_GENERAL; a_description_content_type: like description_content_type)
|
||||
-- Set `description' with `a_description' and optional content type `text:$a_description_content_type'.
|
||||
do
|
||||
if a_description = Void then
|
||||
description := Void
|
||||
description_content_type := Void
|
||||
else
|
||||
create description.make_from_string_general (a_description)
|
||||
description_content_type := a_description_content_type
|
||||
end
|
||||
end
|
||||
|
||||
set_id (a_id: detachable READABLE_STRING_GENERAL)
|
||||
do
|
||||
if a_id = Void then
|
||||
id := Void
|
||||
else
|
||||
create id.make_from_string_general (a_id)
|
||||
end
|
||||
end
|
||||
|
||||
set_updated_date_with_text (a_date_text: detachable READABLE_STRING_32)
|
||||
-- Set `date' from date string representation `a_date_text'.
|
||||
do
|
||||
if a_date_text = Void then
|
||||
date := Void
|
||||
else
|
||||
date := date_time (a_date_text)
|
||||
end
|
||||
end
|
||||
|
||||
extend (a_item: FEED_ITEM)
|
||||
-- Add item `a_item' to feed `items'.
|
||||
do
|
||||
items.force (a_item)
|
||||
end
|
||||
|
||||
extended alias "+" (a_feed: FEED): FEED
|
||||
-- New feed object made from Current merged with a_feed.
|
||||
local
|
||||
l_title: STRING_32
|
||||
do
|
||||
create l_title.make (title.count + a_feed.title.count)
|
||||
l_title.append_character ('(')
|
||||
l_title.append (title)
|
||||
l_title.append_character (')')
|
||||
l_title.append_character ('+')
|
||||
l_title.append_character ('(')
|
||||
l_title.append (a_feed.title)
|
||||
l_title.append_character (')')
|
||||
create Result.make (l_title)
|
||||
Result.items.append (items)
|
||||
across
|
||||
a_feed.items as ic
|
||||
loop
|
||||
-- FIXME jfiat [2015/10/07] : check there is no duplication! (same id, or link, ...)
|
||||
Result.extend (ic.item)
|
||||
end
|
||||
Result.sort
|
||||
end
|
||||
|
||||
sort
|
||||
-- Sort `items', (recent first).
|
||||
local
|
||||
s: QUICK_SORTER [FEED_ITEM]
|
||||
comp: COMPARABLE_COMPARATOR [FEED_ITEM]
|
||||
do
|
||||
create comp
|
||||
create s.make (comp)
|
||||
s.reverse_sort (items)
|
||||
end
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
accept (vis: FEED_VISITOR)
|
||||
do
|
||||
vis.visit_feed (Current)
|
||||
end
|
||||
|
||||
end
|
||||
48
library/text/parser/feed/src/kernel/feed_author.e
Normal file
@@ -0,0 +1,48 @@
|
||||
note
|
||||
description: "[
|
||||
Author of feed or feed entry.
|
||||
- name and email information.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FEED_AUTHOR
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_name: READABLE_STRING_GENERAL)
|
||||
do
|
||||
create name.make_from_string_general (a_name)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: IMMUTABLE_STRING_32
|
||||
|
||||
email: detachable READABLE_STRING_8
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_email (a_email: detachable READABLE_STRING_GENERAL)
|
||||
do
|
||||
if a_email = Void then
|
||||
email := Void
|
||||
elseif a_email.is_valid_as_string_8 then
|
||||
email := a_email.as_string_8
|
||||
else
|
||||
email := Void
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
accept (vis: FEED_VISITOR)
|
||||
do
|
||||
vis.visit_author (Current)
|
||||
end
|
||||
|
||||
end
|
||||
193
library/text/parser/feed/src/kernel/feed_item.e
Normal file
@@ -0,0 +1,193 @@
|
||||
note
|
||||
description: "[
|
||||
A feed contains a list of items.
|
||||
This FEED_ITEM interface provides
|
||||
- title, description, content, id, date, ...
|
||||
- could be compared with other item to sort by date+title.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FEED_ITEM
|
||||
|
||||
inherit
|
||||
FEED_HELPERS
|
||||
undefine
|
||||
is_equal
|
||||
end
|
||||
|
||||
COMPARABLE
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_title: READABLE_STRING_GENERAL)
|
||||
do
|
||||
create title.make_from_string_general (a_title)
|
||||
create links.make (1)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
title: IMMUTABLE_STRING_32
|
||||
-- Title of associated feed item.
|
||||
|
||||
description: detachable IMMUTABLE_STRING_32
|
||||
-- Optional description (or summary).
|
||||
|
||||
content: detachable IMMUTABLE_STRING_32
|
||||
-- Content of Current feed item.
|
||||
|
||||
content_type: detachable READABLE_STRING_8
|
||||
-- Optional content type for `content'.
|
||||
-- By default, this should be text/html.
|
||||
|
||||
content_type_or_default (dft: READABLE_STRING_8): READABLE_STRING_8
|
||||
-- Associated content type, and if none, return given value `dft'.
|
||||
do
|
||||
if attached content_type as l_type then
|
||||
Result := l_type
|
||||
else
|
||||
Result := dft
|
||||
end
|
||||
end
|
||||
|
||||
id: detachable IMMUTABLE_STRING_32
|
||||
-- Identifier of current feed item, if any/
|
||||
|
||||
date: detachable DATE_TIME
|
||||
-- Publishing date.
|
||||
|
||||
link: detachable FEED_LINK
|
||||
-- Main link for the entry, if any.
|
||||
do
|
||||
if attached links as l_links then
|
||||
Result := l_links.item ("")
|
||||
across
|
||||
l_links as ic
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
Result := ic.item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
links: STRING_TABLE [FEED_LINK]
|
||||
-- Url indexed by relation
|
||||
|
||||
categories: detachable LIST [READABLE_STRING_32]
|
||||
-- Categories
|
||||
|
||||
author: detachable FEED_AUTHOR
|
||||
-- Author information.
|
||||
|
||||
feature -- Status report
|
||||
|
||||
has_category (cat: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Has category `cat'?
|
||||
--| note: case insensitive.
|
||||
do
|
||||
if attached categories as cats then
|
||||
Result := across cats as ic some cat.is_case_insensitive_equal (ic.item) end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Comparison
|
||||
|
||||
is_less alias "<" (other: like Current): BOOLEAN
|
||||
-- Is current object less than `other'?
|
||||
local
|
||||
d1,d2: like date
|
||||
do
|
||||
d1 := date
|
||||
d2 := other.date
|
||||
if d1 = Void and d2 = Void then
|
||||
Result := title < other.title
|
||||
elseif d1 = Void then
|
||||
Result := True
|
||||
elseif d2 = Void then
|
||||
Result := False
|
||||
else
|
||||
if d1 ~ d2 then
|
||||
Result := title < other.title
|
||||
else
|
||||
Result := d1 < d2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_id (a_id: detachable READABLE_STRING_GENERAL)
|
||||
do
|
||||
if a_id = Void then
|
||||
id := Void
|
||||
else
|
||||
create id.make_from_string_general (a_id)
|
||||
end
|
||||
end
|
||||
|
||||
set_description (a_description: detachable READABLE_STRING_GENERAL)
|
||||
do
|
||||
if a_description = Void then
|
||||
description := Void
|
||||
else
|
||||
create description.make_from_string_general (a_description)
|
||||
end
|
||||
end
|
||||
|
||||
set_content (a_content: detachable READABLE_STRING_GENERAL; a_type: detachable READABLE_STRING_GENERAL)
|
||||
do
|
||||
if a_content = Void then
|
||||
content := Void
|
||||
content_type := Void
|
||||
else
|
||||
create content.make_from_string_general (a_content)
|
||||
if a_type = Void then
|
||||
content_type := Void
|
||||
else
|
||||
content_type := a_type.as_string_8
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
set_updated_date_with_text (a_date_text: detachable READABLE_STRING_32)
|
||||
do
|
||||
if a_date_text = Void then
|
||||
date := Void
|
||||
else
|
||||
date := date_time (a_date_text)
|
||||
end
|
||||
end
|
||||
|
||||
set_author (a_author: detachable FEED_AUTHOR)
|
||||
do
|
||||
author := a_author
|
||||
end
|
||||
|
||||
set_category (cat: READABLE_STRING_GENERAL)
|
||||
local
|
||||
cats: like categories
|
||||
do
|
||||
cats := categories
|
||||
if cats = Void then
|
||||
create {ARRAYED_LIST [READABLE_STRING_32]} cats.make (1)
|
||||
categories := cats
|
||||
end
|
||||
cats.force (cat.as_string_32)
|
||||
ensure
|
||||
cat_set: has_category (cat)
|
||||
end
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
accept (vis: FEED_VISITOR)
|
||||
do
|
||||
vis.visit_item (Current)
|
||||
end
|
||||
|
||||
end
|
||||
58
library/text/parser/feed/src/kernel/feed_link.e
Normal file
@@ -0,0 +1,58 @@
|
||||
note
|
||||
description: "Link mentioned in feed and feed entry."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FEED_LINK
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_href: READABLE_STRING_8)
|
||||
do
|
||||
href := a_href
|
||||
set_relation (Void)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
href: READABLE_STRING_8
|
||||
-- Location of Current link.
|
||||
|
||||
relation: READABLE_STRING_32
|
||||
-- Relation associated with Current link.
|
||||
|
||||
type: detachable READABLE_STRING_8
|
||||
-- Optional type of link.
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_relation (rel: detachable READABLE_STRING_GENERAL)
|
||||
do
|
||||
if rel = Void then
|
||||
relation := ""
|
||||
else
|
||||
relation := rel.as_string_8
|
||||
end
|
||||
end
|
||||
|
||||
set_type (a_type: detachable READABLE_STRING_GENERAL)
|
||||
do
|
||||
if a_type = Void then
|
||||
type := Void
|
||||
else
|
||||
type := a_type.as_string_8
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
accept (vis: FEED_VISITOR)
|
||||
do
|
||||
vis.visit_link (Current)
|
||||
end
|
||||
|
||||
end
|
||||
126
library/text/parser/feed/src/rss/rss_2_feed_generator.e
Normal file
@@ -0,0 +1,126 @@
|
||||
note
|
||||
description: "Convert a FEED into an RSS 2.0 content."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
RSS_2_FEED_GENERATOR
|
||||
|
||||
inherit
|
||||
FEED_VISITOR
|
||||
|
||||
FEED_GENERATOR
|
||||
rename
|
||||
process_feed as visit_feed
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
visit_feed (a_feed: FEED)
|
||||
do
|
||||
buffer.append ("[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss
|
||||
xmlns:content="http://purl.org/rss/1.0/modules/content/"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
version="2.0">
|
||||
<channel>
|
||||
]")
|
||||
buffer.append_character ('%N')
|
||||
indent
|
||||
indent
|
||||
append_content_tag_to ("title", Void, a_feed.title, buffer)
|
||||
append_content_tag_to ("description", Void, a_feed.description, buffer)
|
||||
across
|
||||
a_feed.links as tb
|
||||
loop
|
||||
tb.item.accept (Current)
|
||||
end
|
||||
if attached a_feed.date as dt then
|
||||
append_content_tag_to ("lastBuildDate", Void, date_to_string (dt), buffer)
|
||||
end
|
||||
across
|
||||
a_feed.items as ic
|
||||
loop
|
||||
ic.item.accept (Current)
|
||||
end
|
||||
exdent
|
||||
exdent
|
||||
buffer.append ("[
|
||||
</channel>
|
||||
</rss>
|
||||
]")
|
||||
end
|
||||
|
||||
visit_item (a_item: FEED_ITEM)
|
||||
do
|
||||
buffer.append (indentation)
|
||||
buffer.append ("<item>%N")
|
||||
indent
|
||||
append_content_tag_to ("title", Void, a_item.title, buffer)
|
||||
if attached a_item.date as dt then
|
||||
append_content_tag_to ("pubDate", Void, date_to_string (dt), buffer)
|
||||
end
|
||||
across
|
||||
a_item.links as tb
|
||||
loop
|
||||
tb.item.accept (Current)
|
||||
end
|
||||
if attached a_item.author as u then
|
||||
u.accept (Current)
|
||||
end
|
||||
if attached a_item.categories as cats then
|
||||
across
|
||||
cats as ic
|
||||
loop
|
||||
append_content_tag_to ("category", Void, ic.item, buffer)
|
||||
end
|
||||
end
|
||||
append_content_tag_to ("guid", Void, a_item.id, buffer)
|
||||
append_content_tag_to ("description", Void, a_item.description, buffer)
|
||||
append_cdata_content_tag_to ("content:encoded", Void, a_item.content, buffer)
|
||||
|
||||
exdent
|
||||
buffer.append (indentation)
|
||||
buffer.append ("</item>%N")
|
||||
end
|
||||
|
||||
visit_link (a_link: FEED_LINK)
|
||||
local
|
||||
attr: detachable ARRAYED_LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_32]]
|
||||
tu: TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_32]
|
||||
do
|
||||
create attr.make (2)
|
||||
if attached a_link.relation as rel and then not rel.is_whitespace then
|
||||
tu := ["rel", rel]
|
||||
attr.force (tu)
|
||||
end
|
||||
if attached a_link.type as t and then not t.is_whitespace then
|
||||
tu := ["type", t.as_string_32]
|
||||
attr.force (tu)
|
||||
end
|
||||
if attr.is_empty then
|
||||
attr := Void
|
||||
end
|
||||
append_content_tag_to ("link", attr, a_link.href, buffer)
|
||||
end
|
||||
|
||||
visit_author (a_author: FEED_AUTHOR)
|
||||
do
|
||||
append_content_tag_to ("dc:creator", Void, a_author.name, buffer)
|
||||
end
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
date_to_string (dt: DATE_TIME): STRING
|
||||
local
|
||||
htdate: HTTP_DATE
|
||||
do
|
||||
create htdate.make_from_date_time (dt)
|
||||
Result := htdate.rfc850_string
|
||||
end
|
||||
|
||||
end
|
||||
125
library/text/parser/feed/src/rss/rss_2_feed_parser.e
Normal file
@@ -0,0 +1,125 @@
|
||||
note
|
||||
description: "[
|
||||
RSS 2.0 Parser.
|
||||
|
||||
Warning: the implementation may not support the full RSS 2.0 specification.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=RSS at wikipedia", "protocol=URI", "src=https://en.wikipedia.org/wiki/RSS"
|
||||
EIS: "name=RDF Site Summary (RSS) 1.0", "protocol=URI", "src=http://purl.org/rss/1.0/spec"
|
||||
|
||||
class
|
||||
RSS_2_FEED_PARSER
|
||||
|
||||
inherit
|
||||
FEED_PARSER
|
||||
|
||||
feature -- Access
|
||||
|
||||
name: STRING = "rss2"
|
||||
-- Associated name.
|
||||
|
||||
is_detected (xdoc: XML_DOCUMENT): BOOLEAN
|
||||
-- Is `xdoc' an ATOM feed representation?
|
||||
do
|
||||
if attached {XML_ELEMENT} xdoc.element_by_name ("rss") as x_rss then
|
||||
if attached xml_attribute_text (x_rss, "version") as l_version and then
|
||||
l_version.starts_with ("2.")
|
||||
then
|
||||
Result := True
|
||||
else
|
||||
-- Let's default to RSS 2.0 for now.
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feed (xdoc: XML_DOCUMENT): detachable FEED
|
||||
-- Feed from `xdoc' XML RSS 2.0 document.
|
||||
local
|
||||
lnk: FEED_LINK
|
||||
x_item, x_content, x_author: detachable XML_ELEMENT
|
||||
e: FEED_ITEM
|
||||
l_author: FEED_AUTHOR
|
||||
do
|
||||
if attached xdoc.element_by_name ("rss") as x_rss then
|
||||
if
|
||||
attached xml_attribute_text (x_rss, "version") as l_version and then
|
||||
l_version.starts_with ("2.")
|
||||
then
|
||||
if attached x_rss.element_by_name ("channel") as x_channel then
|
||||
if attached xml_element_text (x_channel, "title") as x_title then
|
||||
create Result.make (x_title)
|
||||
Result.set_description (xml_element_text (x_channel, "description"), "xhtml")
|
||||
Result.set_updated_date_with_text (xml_element_text (x_channel, "lastBuildDate"))
|
||||
if attached links_from_xml (x_channel, "link") as l_links then
|
||||
across
|
||||
l_links as link_ic
|
||||
loop
|
||||
lnk := link_ic.item
|
||||
Result.links.force (lnk, lnk.relation)
|
||||
end
|
||||
end
|
||||
if attached x_channel.elements_by_name ("item") as x_items then
|
||||
across
|
||||
x_items as ic
|
||||
loop
|
||||
x_item := ic.item
|
||||
if attached xml_element_text (x_item, "title") as e_title then
|
||||
create e.make (e_title)
|
||||
e.set_description (xml_element_text (x_item, "description"))
|
||||
e.set_updated_date_with_text (xml_element_text (x_item, "pubDate"))
|
||||
|
||||
e.set_id (xml_element_text (x_item, "guid"))
|
||||
|
||||
x_author := x_item.element_by_name ("creator")
|
||||
if x_author = Void then
|
||||
x_author := element_by_prefixed_name (x_item, "dc" , "creator")
|
||||
end
|
||||
|
||||
if
|
||||
x_author /= Void and then
|
||||
attached x_author.text as l_author_name
|
||||
then
|
||||
create l_author.make (l_author_name)
|
||||
e.set_author (l_author)
|
||||
end
|
||||
|
||||
if attached links_from_xml (x_item, "link") as l_links then
|
||||
across
|
||||
l_links as link_ic
|
||||
loop
|
||||
lnk := link_ic.item
|
||||
e.links.force (lnk, lnk.relation)
|
||||
end
|
||||
end
|
||||
if attached x_item.elements_by_name ("category") as x_categories then
|
||||
across
|
||||
x_categories as cats
|
||||
loop
|
||||
if attached cats.item.text as cat then
|
||||
e.set_category (cat)
|
||||
end
|
||||
end
|
||||
end
|
||||
x_content := x_item.element_by_name ("content")
|
||||
if x_content = Void then
|
||||
x_content := element_by_prefixed_name (x_item, "content" , "encoded")
|
||||
if x_content /= Void then
|
||||
e.set_content (x_content.text, Void)
|
||||
end
|
||||
else
|
||||
e.set_content (xml_element_code (x_content), Void)
|
||||
end
|
||||
Result.extend (e)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
119
library/text/parser/feed/src/support/feed_generator.e
Normal file
@@ -0,0 +1,119 @@
|
||||
note
|
||||
description: "Common ancestor for feed generator."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
FEED_GENERATOR
|
||||
|
||||
inherit
|
||||
XML_UTILITIES
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_buffer: STRING_8)
|
||||
do
|
||||
buffer := a_buffer
|
||||
create indentation.make_empty
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
buffer: STRING_8
|
||||
-- Output of feed conversion.
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
process_feed (a_feed: FEED)
|
||||
-- Convert `a_feed' into string representation in `buffer'.
|
||||
deferred
|
||||
end
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
indent
|
||||
do
|
||||
indentation.append ("%T")
|
||||
end
|
||||
|
||||
exdent
|
||||
require
|
||||
has_indentation: indentation.count > 0
|
||||
do
|
||||
indentation.remove_tail (1)
|
||||
end
|
||||
|
||||
indentation: STRING
|
||||
|
||||
append_content_tag_to (a_tagname: READABLE_STRING_8; a_attr: detachable ITERABLE [TUPLE [name: READABLE_STRING_8; value: detachable READABLE_STRING_GENERAL]]; a_content: detachable READABLE_STRING_GENERAL; a_output: STRING)
|
||||
do
|
||||
if a_content /= Void or a_attr /= Void then
|
||||
a_output.append (indentation)
|
||||
a_output.append ("<")
|
||||
a_output.append (a_tagname)
|
||||
if a_attr /= Void then
|
||||
across
|
||||
a_attr as ic
|
||||
loop
|
||||
if attached ic.item.value as l_att_value then
|
||||
a_output.append_character (' ')
|
||||
a_output.append (ic.item.name)
|
||||
a_output.append_character ('=')
|
||||
a_output.append_character ('%"')
|
||||
a_output.append (escaped_unicode_xml (l_att_value.as_string_32))
|
||||
a_output.append_character ('%"')
|
||||
end
|
||||
end
|
||||
end
|
||||
if a_content = Void then
|
||||
a_output.append ("/>")
|
||||
else
|
||||
a_output.append (">")
|
||||
a_output.append (escaped_unicode_xml (a_content.as_string_32))
|
||||
a_output.append ("</" + a_tagname + ">%N")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
append_cdata_content_tag_to (a_tagname: READABLE_STRING_8; a_attr: detachable ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_32]]; a_content: detachable READABLE_STRING_32; a_output: STRING)
|
||||
do
|
||||
if a_content /= Void then
|
||||
a_output.append (indentation)
|
||||
a_output.append ("<")
|
||||
a_output.append (a_tagname)
|
||||
if a_attr /= Void then
|
||||
across
|
||||
a_attr as ic
|
||||
loop
|
||||
a_output.append_character (' ')
|
||||
a_output.append (ic.item.name)
|
||||
a_output.append_character ('=')
|
||||
a_output.append_character ('%"')
|
||||
a_output.append (escaped_unicode_xml (ic.item.value))
|
||||
a_output.append_character ('%"')
|
||||
end
|
||||
end
|
||||
a_output.append (">")
|
||||
a_output.append (to_cdata_element (a_content))
|
||||
a_output.append ("</" + a_tagname + ">%N")
|
||||
end
|
||||
end
|
||||
|
||||
to_cdata_element (a_value: READABLE_STRING_GENERAL): STRING
|
||||
local
|
||||
cdata: XML_CHARACTER_DATA
|
||||
xdoc: XML_DOCUMENT
|
||||
pprinter: XML_NODE_PRINTER
|
||||
l_output: XML_STRING_8_OUTPUT_STREAM
|
||||
do
|
||||
create xdoc.make
|
||||
create cdata.make (xdoc.root_element, a_value.as_string_32)
|
||||
create pprinter.make
|
||||
create Result.make (cdata.content_count)
|
||||
create l_output.make (Result)
|
||||
pprinter.set_output (l_output)
|
||||
pprinter.process_character_data (cdata)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
86
library/text/parser/feed/src/support/feed_helpers.e
Normal file
@@ -0,0 +1,86 @@
|
||||
note
|
||||
description: "Helpers routine for feed library."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FEED_HELPERS
|
||||
|
||||
feature -- Helpers
|
||||
|
||||
date_time (a_date_string: READABLE_STRING_32): DATE_TIME
|
||||
-- "2015-08-14T10:34:13.493740Z"
|
||||
-- "Sat, 07 Sep 2002 00:00:01 GMT"
|
||||
local
|
||||
i,j: INTEGER
|
||||
s: READABLE_STRING_GENERAL
|
||||
y,m,d,h,min: INTEGER
|
||||
sec: REAL_64
|
||||
htdate: HTTP_DATE
|
||||
str: STRING_32
|
||||
do
|
||||
if a_date_string.count > 0 and then a_date_string.item (1).is_digit then
|
||||
i := a_date_string.index_of ('-', 1)
|
||||
if i > 0 then
|
||||
s := a_date_string.substring (1, i - 1)
|
||||
y := s.to_integer_32 -- Year
|
||||
j := i + 1
|
||||
i := a_date_string.index_of ('-', j)
|
||||
if i > 0 then
|
||||
s := a_date_string.substring (j, i - 1)
|
||||
m := s.to_integer_32 -- Month
|
||||
j := i + 1
|
||||
i := a_date_string.index_of ('T', j)
|
||||
if i = 0 then
|
||||
i := a_date_string.index_of (' ', j)
|
||||
end
|
||||
if i = 0 then
|
||||
i := a_date_string.count + 1
|
||||
end
|
||||
if i > 0 then
|
||||
s := a_date_string.substring (j, i - 1)
|
||||
if s.is_integer then
|
||||
d := s.to_integer_32 -- Day
|
||||
j := i + 1
|
||||
i := a_date_string.index_of (':', j)
|
||||
if i > 0 then
|
||||
s := a_date_string.substring (j, i - 1)
|
||||
h := s.to_integer
|
||||
j := i + 1
|
||||
i := a_date_string.index_of (':', j)
|
||||
if i > 0 then
|
||||
s := a_date_string.substring (j, i - 1)
|
||||
min := s.to_integer
|
||||
j := i + 1
|
||||
i := a_date_string.index_of ('Z', j)
|
||||
if i = 0 then
|
||||
i := a_date_string.count + 1
|
||||
end
|
||||
s := a_date_string.substring (j, i - 1)
|
||||
sec := s.to_double
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
create Result.make (y,m,d,h,m,0)
|
||||
Result.fine_second_add (sec)
|
||||
else
|
||||
i := a_date_string.index_of ('+', 1)
|
||||
if i > 0 then
|
||||
str := a_date_string.substring (1, i - 1)
|
||||
str.append (" GMT")
|
||||
create htdate.make_from_string (str)
|
||||
Result := htdate.date_time
|
||||
if a_date_string.substring (i + 1, a_date_string.count).is_case_insensitive_equal ("0000") then
|
||||
|
||||
end
|
||||
else
|
||||
create htdate.make_from_string (a_date_string)
|
||||
Result := htdate.date_time
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
83
library/text/parser/feed/src/support/feed_parser_utilities.e
Normal file
@@ -0,0 +1,83 @@
|
||||
note
|
||||
description: "Helpers routine for feed xml parsers."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
FEED_PARSER_UTILITIES
|
||||
|
||||
feature -- Access
|
||||
|
||||
xml_element_text (a_parent: XML_ELEMENT; a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
do
|
||||
if attached a_parent.element_by_name (a_name) as elt then
|
||||
if attached elt.text as t then
|
||||
t.left_adjust
|
||||
t.right_adjust
|
||||
Result := t
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xml_attribute_text (a_elt: XML_ELEMENT; a_att_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32
|
||||
do
|
||||
if attached a_elt.attribute_by_name (a_att_name) as att then
|
||||
Result := att.value
|
||||
end
|
||||
end
|
||||
|
||||
xml_element_code (elt: XML_ELEMENT): STRING_32
|
||||
local
|
||||
xprinter: XML_NODE_PRINTER
|
||||
do
|
||||
create xprinter.make
|
||||
create Result.make_empty
|
||||
xprinter.set_output (create {XML_STRING_32_OUTPUT_STREAM}.make (Result))
|
||||
xprinter.process_element (elt)
|
||||
end
|
||||
|
||||
links_from_xml (elt: XML_ELEMENT; a_link_elt_name: READABLE_STRING_GENERAL): detachable ARRAYED_LIST [FEED_LINK]
|
||||
local
|
||||
x_link: XML_ELEMENT
|
||||
lnk: FEED_LINK
|
||||
do
|
||||
if attached elt.elements_by_name (a_link_elt_name) as x_links then
|
||||
create Result.make (0)
|
||||
across
|
||||
x_links as ic
|
||||
loop
|
||||
x_link := ic.item
|
||||
if attached xml_attribute_text (x_link, "href") as l_href and then
|
||||
l_href.is_valid_as_string_8
|
||||
then
|
||||
create lnk.make (l_href.as_string_8)
|
||||
lnk.set_relation (xml_attribute_text (x_link, "rel"))
|
||||
lnk.set_type (xml_attribute_text (x_link, "type"))
|
||||
Result.force (lnk)
|
||||
elseif attached x_link.text as l_url and then not l_url.is_whitespace then
|
||||
create lnk.make (l_url)
|
||||
Result.force (lnk)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
element_by_prefixed_name (elt: XML_ELEMENT; a_ns_prefix: READABLE_STRING_GENERAL; a_name: READABLE_STRING_GENERAL): detachable XML_ELEMENT
|
||||
do
|
||||
across
|
||||
elt as ic
|
||||
until
|
||||
Result /= Void
|
||||
loop
|
||||
if attached {XML_ELEMENT} ic.item as x_item then
|
||||
if
|
||||
attached x_item.ns_prefix as l_ns_prefix and then a_ns_prefix.same_string (l_ns_prefix) and then
|
||||
a_name.same_string (x_item.name)
|
||||
then
|
||||
Result := x_item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,200 @@
|
||||
note
|
||||
description: "[
|
||||
Convert a FEED to STRING_32 representation.
|
||||
Mostly for debug output!
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FEED_TO_STRING_32_DEBUG_VISITOR
|
||||
|
||||
inherit
|
||||
FEED_VISITOR
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_buffer: STRING_32)
|
||||
do
|
||||
buffer := a_buffer
|
||||
create indentation.make_empty
|
||||
end
|
||||
|
||||
buffer: STRING_32
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
visit_feed (a_feed: FEED)
|
||||
do
|
||||
if attached a_feed.id as l_id then
|
||||
append_text ("#")
|
||||
append (l_id)
|
||||
append_new_line
|
||||
end
|
||||
if attached a_feed.date as dt then
|
||||
append_text ("date:")
|
||||
append (dt.out)
|
||||
append_new_line
|
||||
end
|
||||
|
||||
append_text (a_feed.title)
|
||||
append_new_line
|
||||
indent
|
||||
if attached a_feed.description as l_desc then
|
||||
append_text (l_desc)
|
||||
append_new_line
|
||||
end
|
||||
|
||||
across
|
||||
a_feed.links as ic
|
||||
loop
|
||||
ic.item.accept (Current)
|
||||
append_new_line
|
||||
end
|
||||
|
||||
append_new_line
|
||||
|
||||
across
|
||||
a_feed.items as ic
|
||||
loop
|
||||
exdent
|
||||
append_text (create {STRING_32}.make_filled ('-', 40))
|
||||
append_new_line
|
||||
indent
|
||||
ic.item.accept (Current)
|
||||
append_new_line
|
||||
end
|
||||
end
|
||||
|
||||
visit_item (a_entry: FEED_ITEM)
|
||||
do
|
||||
if attached a_entry.id as l_id then
|
||||
append_text ("#")
|
||||
append (l_id)
|
||||
append_new_line
|
||||
end
|
||||
if attached a_entry.date as dt then
|
||||
append_text ("date:")
|
||||
append (dt.out)
|
||||
append_new_line
|
||||
end
|
||||
append_text (a_entry.title)
|
||||
append_new_line
|
||||
indent
|
||||
if attached a_entry.author as l_author then
|
||||
l_author.accept (Current)
|
||||
append_new_line
|
||||
end
|
||||
if attached a_entry.categories as cats then
|
||||
append_text ("Categories: ")
|
||||
from
|
||||
cats.start
|
||||
until
|
||||
cats.after
|
||||
loop
|
||||
if not cats.isfirst then
|
||||
append (", ")
|
||||
end
|
||||
append (cats.item)
|
||||
cats.forth
|
||||
end
|
||||
append_new_line
|
||||
end
|
||||
if attached a_entry.description as l_summary then
|
||||
append_text (l_summary)
|
||||
append_new_line
|
||||
end
|
||||
|
||||
across
|
||||
a_entry.links as ic
|
||||
loop
|
||||
ic.item.accept (Current)
|
||||
append_new_line
|
||||
end
|
||||
|
||||
if attached a_entry.content as l_content then
|
||||
append_text (l_content)
|
||||
append_new_line
|
||||
end
|
||||
exdent
|
||||
end
|
||||
|
||||
visit_link (a_link: FEED_LINK)
|
||||
local
|
||||
s: STRING_32
|
||||
do
|
||||
create s.make_empty
|
||||
s.append_string_general ("@")
|
||||
s.append_string (a_link.relation)
|
||||
s.append_string (" -> ")
|
||||
s.append_string (a_link.href)
|
||||
append_text (s)
|
||||
end
|
||||
|
||||
visit_author (a_author: FEED_AUTHOR)
|
||||
local
|
||||
s: STRING_32
|
||||
do
|
||||
create s.make_empty
|
||||
s.append_string_general ("by ")
|
||||
s.append_string (a_author.name)
|
||||
if attached a_author.email as l_email then
|
||||
s.append_character (' ')
|
||||
s.append_character ('(')
|
||||
s.append_string_general (l_email)
|
||||
s.append_character (')')
|
||||
end
|
||||
append_text (s)
|
||||
end
|
||||
|
||||
feature -- Helper
|
||||
|
||||
indentation: STRING_32
|
||||
|
||||
indent
|
||||
do
|
||||
indentation.append (" ")
|
||||
end
|
||||
|
||||
exdent
|
||||
do
|
||||
indentation.remove_tail (2)
|
||||
end
|
||||
|
||||
append_new_line
|
||||
do
|
||||
append ("%N")
|
||||
end
|
||||
|
||||
append_text (s: READABLE_STRING_GENERAL)
|
||||
local
|
||||
lst: LIST [READABLE_STRING_GENERAL]
|
||||
do
|
||||
if indentation.is_empty then
|
||||
append (s)
|
||||
else
|
||||
lst := s.split ('%N')
|
||||
from
|
||||
lst.start
|
||||
until
|
||||
lst.after
|
||||
loop
|
||||
append (indentation)
|
||||
append (lst.item)
|
||||
if not lst.islast then
|
||||
append ("%N")
|
||||
end
|
||||
lst.forth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
append (s: READABLE_STRING_GENERAL)
|
||||
do
|
||||
buffer.append_string_general (s)
|
||||
end
|
||||
|
||||
end
|
||||
27
library/text/parser/feed/src/support/feed_visitor.e
Normal file
@@ -0,0 +1,27 @@
|
||||
note
|
||||
description: "Interface to visit Feed objects."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
FEED_VISITOR
|
||||
|
||||
feature -- Visit
|
||||
|
||||
visit_feed (a_feed: FEED)
|
||||
deferred
|
||||
end
|
||||
|
||||
visit_link (a_link: FEED_LINK)
|
||||
deferred
|
||||
end
|
||||
|
||||
visit_item (a_item: FEED_ITEM)
|
||||
deferred
|
||||
end
|
||||
|
||||
visit_author (a_author: FEED_AUTHOR)
|
||||
deferred
|
||||
end
|
||||
|
||||
end
|
||||
91
library/text/parser/feed/tests/application.e
Normal file
@@ -0,0 +1,91 @@
|
||||
note
|
||||
description: "Summary description for {APPLICATION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
APPLICATION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- New test routine
|
||||
do
|
||||
test_file ("data_rss_1_0.rss")
|
||||
test_web ("https://bertrandmeyer.com/feed/")
|
||||
end
|
||||
|
||||
test_file (fn: READABLE_STRING_GENERAL)
|
||||
local
|
||||
t: STRING
|
||||
f: PLAIN_TEXT_FILE
|
||||
do
|
||||
create f.make_with_name (fn)
|
||||
f.open_read
|
||||
create t.make_empty
|
||||
from
|
||||
f.read_stream_thread_aware (1_024)
|
||||
until
|
||||
f.last_string.count < 1024
|
||||
loop
|
||||
t.append (f.last_string)
|
||||
f.read_stream_thread_aware (1_024)
|
||||
end
|
||||
t.append (f.last_string)
|
||||
f.close
|
||||
test_feed (t)
|
||||
end
|
||||
|
||||
test_feed (t: READABLE_STRING_8)
|
||||
local
|
||||
feed_parser: FEED_DEFAULT_PARSERS
|
||||
vis: FEED_TO_STRING_32_DEBUG_VISITOR
|
||||
gen: RSS_2_FEED_GENERATOR
|
||||
atom_gen: ATOM_FEED_GENERATOR
|
||||
s: STRING_32
|
||||
s8: STRING_8
|
||||
pp: XML_PRETTY_PRINT_FILTER
|
||||
do
|
||||
create feed_parser
|
||||
if attached feed_parser.feed_from_string (t) as l_feed then
|
||||
create s.make_empty
|
||||
create vis.make (s)
|
||||
l_feed.accept (vis)
|
||||
print (s)
|
||||
|
||||
create s8.make_empty
|
||||
create gen.make (s8)
|
||||
l_feed.accept (gen)
|
||||
print (s8)
|
||||
|
||||
create s8.make_empty
|
||||
create atom_gen.make (s8)
|
||||
l_feed.accept (atom_gen)
|
||||
print (s8)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
test_web (a_url: READABLE_STRING_8)
|
||||
local
|
||||
cl: LIBCURL_HTTP_CLIENT
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
do
|
||||
create cl.make
|
||||
sess := cl.new_session (a_url)
|
||||
sess.set_is_insecure (True)
|
||||
if attached sess.get ("", Void) as resp then
|
||||
if
|
||||
not resp.error_occurred and then
|
||||
attached resp.body as l_feed
|
||||
then
|
||||
test_feed (l_feed)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
74
library/text/parser/feed/tests/atom_test_set.e
Normal file
@@ -0,0 +1,74 @@
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
author: "EiffelStudio test wizard"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
testing: "type/manual"
|
||||
|
||||
class
|
||||
ATOM_TEST_SET
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
test_atom
|
||||
-- New test routine
|
||||
local
|
||||
feed_parser: FEED_DEFAULT_PARSERS
|
||||
vis: FEED_TO_STRING_32_DEBUG_VISITOR
|
||||
s: STRING_32
|
||||
do
|
||||
create feed_parser
|
||||
if attached feed_parser.feed_from_string (atom_string_1) as l_feed then
|
||||
create s.make_empty
|
||||
create vis.make (s)
|
||||
l_feed.accept (vis)
|
||||
print (s)
|
||||
assert ("not_implemented", False)
|
||||
end
|
||||
assert ("not_implemented", False)
|
||||
end
|
||||
|
||||
feature {NONE} -- Data
|
||||
|
||||
atom_string_1: STRING = "[
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
|
||||
<title>Example Feed</title>
|
||||
<subtitle>A subtitle.</subtitle>
|
||||
<link href="http://example.org/feed/" rel="self" />
|
||||
<link href="http://example.org/" />
|
||||
<id>urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6</id>
|
||||
<updated>2003-12-13T18:30:02Z</updated>
|
||||
|
||||
|
||||
<entry>
|
||||
<title>Atom-Powered Robots Run Amok</title>
|
||||
<link href="http://example.org/2003/12/13/atom03" />
|
||||
<link rel="alternate" type="text/html" href="http://example.org/2003/12/13/atom03.html"/>
|
||||
<link rel="edit" href="http://example.org/2003/12/13/atom03/edit"/>
|
||||
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
|
||||
<updated>2003-12-13T18:30:02Z</updated>
|
||||
<summary>Some text.</summary>
|
||||
<content type="xhtml">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p>This is the entry content.</p>
|
||||
</div>
|
||||
</content>
|
||||
<author>
|
||||
<name>John Doe</name>
|
||||
<email>johndoe@example.com</email>
|
||||
</author>
|
||||
</entry>
|
||||
|
||||
</feed>
|
||||
]"
|
||||
end
|
||||
|
||||
|
||||
60
library/text/parser/feed/tests/rss_test_set.e
Normal file
@@ -0,0 +1,60 @@
|
||||
note
|
||||
description: "Summary description for {RSS_TEST_SET}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
RSS_TEST_SET
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
test_rss_2
|
||||
-- New test routine
|
||||
local
|
||||
feed_parser: FEED_DEFAULT_PARSERS
|
||||
vis: FEED_TO_STRING_32_DEBUG_VISITOR
|
||||
s: STRING_32
|
||||
do
|
||||
create feed_parser
|
||||
if attached feed_parser.feed_from_string (rss_2_string_1) as l_feed then
|
||||
create s.make_empty
|
||||
create vis.make (s)
|
||||
l_feed.accept (vis)
|
||||
print (s)
|
||||
assert ("not_implemented", False)
|
||||
end
|
||||
assert ("not_implemented", False)
|
||||
end
|
||||
|
||||
feature {NONE} -- Data
|
||||
|
||||
rss_2_string_1: STRING = "[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>Mon site</title>
|
||||
<description>Ceci est un exemple de flux RSS 2.0</description>
|
||||
<lastBuildDate>Sat, 07 Sep 2002 00:00:01 GMT</lastBuildDate>
|
||||
<link>http://www.example.org</link>
|
||||
<item>
|
||||
<title>Post N1</title>
|
||||
<description>This is my first post</description>
|
||||
<pubDate>Sat, 07 Sep 2002 00:00:01 GMT</pubDate>
|
||||
<link>http://www.example.org/actu1</link>
|
||||
</item>
|
||||
<item>
|
||||
<title>Post N2</title>
|
||||
<description>This is my second post</description>
|
||||
<pubDate>Sat, 07 Sep 2002 00:00:01 GMT</pubDate>
|
||||
<link>http://www.example.org/actu2</link>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
]"
|
||||
end
|
||||
|
||||
|
||||
14
library/text/parser/feed/tests/tests-safe.ecf
Normal file
@@ -0,0 +1,14 @@
|
||||
<?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="tests" uuid="C68BD5DC-F756-484E-A9FE-F2D1FD432B2A">
|
||||
<target name="tests">
|
||||
<root class="APPLICATION" feature="make"/>
|
||||
<setting name="console_application" value="false"/>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="feed" location="..\feed-safe.ecf" readonly="false"/>
|
||||
<library name="http_client" location="$ISE_LIBRARY\contrib\library\network\http_client\http_client-safe.ecf"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<library name="xml_parser" location="$ISE_LIBRARY\library\text\parser\xml\parser\xml_parser-safe.ecf"/>
|
||||
<tests name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
12
library/text/parser/feed/tests/tests.ecf
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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="tests" uuid="C68BD5DC-F756-484E-A9FE-F2D1FD432B2A">
|
||||
<target name="tests">
|
||||
<root class="ANY" feature="default_create"/>
|
||||
<setting name="console_application" value="false"/>
|
||||
<setting name="concurrency" value="none"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="feed" location="..\feed.ecf"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing.ecf"/>
|
||||
<tests name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -45,7 +45,8 @@
|
||||
<library name="rss" location="..\draft\library\protocol\syndication\rss\rss-safe.ecf" readonly="false"/>
|
||||
<library name="upload_image" location="..\examples\upload_image\upload_image-safe.ecf" readonly="false"/>
|
||||
<library name="uri_template" location="..\library\text\parser\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||||
<library name="wizard" location="..\tools\estudio_wizard\ewf_ise_wizard-safe.ecf" readonly="false"/>
|
||||
<library name="wizard" location="..\tools\estudio_wizard\wizard.ecf" readonly="false"/>
|
||||
<library name="console_wizard" location="..\tools\estudio_wizard\console_wizard.ecf" readonly="false"/>
|
||||
<library name="wsf" location="..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_all" location="..\library\server\wsf\connector\all-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_cgi" location="..\library\server\wsf\connector\cgi-safe.ecf" readonly="false"/>
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
<library name="restbucks" location="..\examples\restbucksCRUD\restbucks-safe.ecf" readonly="false"/>
|
||||
<library name="upload_image" location="..\examples\upload_image\upload_image-safe.ecf" readonly="false"/>
|
||||
<library name="uri_template" location="..\library\text\parser\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||||
<library name="wizard" location="..\tools\estudio_wizard\ewf_ise_wizard-safe.ecf" readonly="false"/>
|
||||
<library name="wizard" location="..\tools\estudio_wizard\wizard.ecf" readonly="false"/>
|
||||
<library name="console_wizard" location="..\tools\estudio_wizard\console_wizard.ecf" readonly="false"/>
|
||||
<library name="wsf" location="..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_all" location="..\library\server\wsf\connector\all-safe.ecf" readonly="false"/>
|
||||
<library name="wsf_cgi" location="..\library\server\wsf\connector\cgi-safe.ecf" readonly="false"/>
|
||||
@@ -63,4 +64,9 @@
|
||||
<root all_classes="true"/>
|
||||
<setting name="platform" value="unix"/>
|
||||
</target>
|
||||
<target name="all_stable_with_ssl" extends="all_stable">
|
||||
<description>Compiling with ssl enabled</description>
|
||||
<root all_classes="true"/>
|
||||
<variable name="httpd_ssl_enabled" value="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -38,7 +38,7 @@ feature {NONE} -- Initialization
|
||||
|
||||
feature -- UI change
|
||||
|
||||
set_title (a_title: READABLE_STRING_GENERAL)
|
||||
set_title (a_title: separate READABLE_STRING_GENERAL)
|
||||
deferred
|
||||
end
|
||||
|
||||
|
||||
@@ -89,15 +89,17 @@ echo Install library: http
|
||||
echo Install library: content_negotiation
|
||||
%COPYCMD% %TMP_DIR%\library\network\protocol\content_negotiation %TMP_CONTRIB_DIR%\library\network\protocol\content_negotiation
|
||||
echo Install library: http_authorization
|
||||
%SAFE_MD% %TMP_CONTRIB_DIR%\library\network\authentication
|
||||
%COPYCMD% %TMP_DIR%\library\server\authentication\http_authorization %TMP_CONTRIB_DIR%\library\network\authentication\http_authorization
|
||||
%SAFE_MD% %TMP_CONTRIB_DIR%\library\web\authentication
|
||||
%COPYCMD% %TMP_DIR%\library\server\authentication\http_authorization %TMP_CONTRIB_DIR%\library\web\authentication\http_authorization
|
||||
|
||||
echo Install library: openid
|
||||
%SAFE_MD% %TMP_CONTRIB_DIR%\library\security
|
||||
%COPYCMD% %TMP_DIR%\library\security\openid %TMP_CONTRIB_DIR%\library\security\openid
|
||||
%SAFE_MD% %TMP_CONTRIB_DIR%\library\web\authentication
|
||||
%COPYCMD% %TMP_DIR%\library\security\openid %TMP_CONTRIB_DIR%\library\web\authentication\openid
|
||||
|
||||
echo Install library: uri_template
|
||||
%COPYCMD% %TMP_DIR%\library\text\parser\uri_template %TMP_CONTRIB_DIR%\library\text\parser\uri_template
|
||||
echo Install library: feed
|
||||
%COPYCMD% %TMP_DIR%\library\text\parser\feed %TMP_CONTRIB_DIR%\library\text\parser\feed
|
||||
|
||||
echo Install library: notification_email
|
||||
%SAFE_MD% %TMP_CONTRIB_DIR%\library\runtime
|
||||
|
||||
@@ -60,11 +60,13 @@ echo Uninstall library: http
|
||||
echo Uninstall library: content_negotiation
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\library\network\protocol\content_negotiation
|
||||
echo Uninstall library: http_authorization
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\library\network\authentication\http_authorization
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\library\web\authentication\http_authorization
|
||||
echo Uninstall library: security\openid
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\library\security\openid
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\library\web\authentication\openid
|
||||
echo Uninstall library: uri_template
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\library\text\parser\uri_template
|
||||
echo Uninstall library: feed
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\library\text\parser\feed
|
||||
echo Uninstall library: runtime\process\notification_email
|
||||
%RDCMD% %TMP_CONTRIB_DIR%\library\runtime\process\notification_email
|
||||
|
||||
|
||||