Compare commits

...

65 Commits

Author SHA1 Message Date
dde6a0b7de Added specific configuration file, so that it is easier to use either libcurl or net implementation. 2015-10-19 08:46:31 +02:00
b64a281d75 Fixed timeout issue due to too many "ready_for_reading".
Fixed Connection behavior.
Fixed Content-Type settings.
Removed condition on POST or PUT, since code also applied to any request methods.
Added verbose output implementation.
2015-10-19 08:46:30 +02:00
b69b8aaaf9 Added first support for persistent connection in NET http client implementation.
Various improvement related to eventual errors.
2015-10-19 08:46:29 +02:00
65b28ed877 Updated README.md with configuration topics related to libcurl or net disabling.
Fixed ssl test by precising insecure ssl.
2015-10-19 08:46:27 +02:00
6c7637716b Updated a few comments
Removed useless NULL_HTTP_CLIENT.
Extracted mime code from NET_HTTP_CLIENT_REQUEST.response into specific routine.
2015-10-19 08:46:26 +02:00
ff9a238f5c Added https support with Net implementation.
Added notion of default HTTP_CLIENT, to be able to build portable code among http client implementation.
2015-10-19 08:46:25 +02:00
eec3cbdba1 Added null http client for upcoming changes.
Refactored NET request implementation.
  - fixed potential issue with header conflict.
  - simplified, and extract parts of the code into routine.
  - Implemented read of chunked Transfer-Encoding
  - Fixed potential issue with socket handling.
First steps to be able to exclude net or libcurl implementation when using http_client lib.
Removed from NET implementation the hack related to PUT and upload data (it was used to workaround an issue with libcurl).
2015-10-19 08:46:23 +02:00
29c4931dc0 Added support for chunked transfer-encoding response.
Implemented correctly the redirection support for NET_HTTP_CLIENT...
Added the possibility to use HTTP/1.0 .
Splitted the manual tests that were using during development.
First step to redesign and clean the new code.
2015-10-19 08:46:22 +02:00
Florian Jacky
9cd0f0b117 Fixed configuration files 2015-10-19 08:46:21 +02:00
Florian Jacky
aa0eb4fc43 Fixed configuration files 2015-10-19 08:46:20 +02:00
Florian Jacky
dbdc594b59 config files 2015-10-19 08:46:18 +02:00
Florian Jacky
4176a8c68b correct password for authentication test 2015-10-19 08:46:17 +02:00
Florian Jacky
0557d1ee2d added remaining features 2015-10-19 08:46:16 +02:00
Florian Jacky
eed8af9a0a now supports sending requests, receiving headers, receiving message text, redirection, agent header, cookies, basic http authorization, sending data using post using url-encoding, sending file as post as data, sending put data 2015-10-19 08:46:15 +02:00
Florian Jacky
1b881c4f60 implemented http authorization, support for redirection and user-agent 2015-10-19 08:46:14 +02:00
Florian Jacky
770488dbd3 implemented http authorization, support for redirection and user-agent 2015-10-19 08:46:12 +02:00
3f69081d32 Added postcondition to ensure the result of {HTTP_CLIENT_REQUEST}.response is attached.
(useless with void-safety compilation, but keep it for non void-safe execution).
2015-10-19 08:46:11 +02:00
7033db7dc4 Removed useless redefination of is_equal. 2015-10-19 08:46:10 +02:00
a1a16b4a22 Fixing http_client.ecf file with correct locations. 2015-10-19 08:46:09 +02:00
98e92ee0fe Basic initial Eiffel NET implementation. 2015-10-19 08:46:07 +02:00
29b55f36cf Added skeleton for Eiffel Net implementation of HTTP_CLIENT solution.
This is work in progress.
2015-10-19 08:46:06 +02:00
061e88c9fe Added FEED.prune (a_item: FEED_ITEM). 2015-10-14 17:40:38 +02:00
66f204b1f2 Make custom error interface more flexible with READABLE_STRING_... instead of STRING_... 2015-10-10 00:58:07 +02:00
c92b1b8c3b Added feed to xhtml visitor.
Updated interfaces, mainly related to date attributes.
2015-10-09 19:08:53 +02:00
98c12b8fb9 Made HTTP_DATE more flexible and support UTC+0000, GMT+0000 and now also +0000.
Added comments.
2015-10-08 11:00:01 +02:00
5fee483fd9 Added FEED + FEED operator to merge two feeds.
Added FEED sorting routine.
Added FEED_ITEM.link: detachable FEED_LINK that represents the main feed link.
Comments.
2015-10-08 10:10:08 +02:00
f7a7afccd6 Fixed compilation of non void-safe feed.ecf 2015-10-05 22:58:58 +02:00
e2c70e6d70 Updated a few comments.
Renamed generator to follow *_FEED_GENERATOR naming.
Renamed feed entry as feed item.
Made FEED conforms to ITERABLE [FEED_ITEM] for convenience.
2015-09-16 10:02:09 +02:00
a5e150d1c0 Improved feed library with comments, bug fixes and code factorization. 2015-09-08 21:45:27 +02:00
39887c8bdb Added initial ATOM and RSS feed parser and generator.
(work in progress)
2015-09-07 19:22:50 +02:00
jvelilla
1f1e2abbda Removed support for SSLv3 2015-08-26 11:56:24 -03:00
1796d9631f Added target "all_stable_with_ssl" to check compilation with ssl enabled. 2015-08-26 13:38:50 +02:00
389975e409 Merge branch 'v1' 2015-08-24 16:13:01 +02:00
6c51590369 Updated installation location of openid and http_authorization in ISE package.
Added iron package file for ewsgi.
2015-08-24 16:12:25 +02:00
jvelilla
cc65bae644 Fixed typo: Aug instead of Aou. 2015-08-06 10:45:47 +02:00
jvelilla
c824f707cf Fixed typo: Aug instead of Aou. 2015-08-06 10:42:15 +02:00
47c5b798b3 Cosmetic true -> True 2015-08-04 13:24:03 +02:00
f0cba1d536 Fixing script_url' that wrongly used path_info' instead of `percent_encoded_path_info'.
(issue on script_url when path info contains unicode character).
2015-08-04 13:21:36 +02:00
ed891546bc Updated set_value for WSF_FORM_SELECTABLE_INPUT (for example a checkbox).
Call the feature set_checked_by_value iff the the current value exist in the
list of values, in other case set checked in Flase.
If we call set_checked_by_value without filter, previous checked values will be
set in False.
2015-08-04 13:21:07 +02:00
8651ff6e1e Fixing script_url' that wrongly used path_info' instead of `percent_encoded_path_info'.
(issue on script_url when path info contains unicode character).
2015-08-04 13:03:51 +02:00
629edea991 Merge remote-tracking branch 'javier/ewf_html_form' into v1 2015-08-04 13:00:08 +02:00
jvelilla
1e10ce8518 Updated set_value for WSF_FORM_SELECTABLE_INPUT (for example a checkbox).
Call the feature set_checked_by_value iff the the current value exist in the
list of values, in other case set checked in Flase.
If we call set_checked_by_value without filter, previous checked values will be
set in False.
2015-07-31 11:55:23 -03:00
4f8f17ad48 Fixed various compilation issues.
Ensure the obsolete/v0 ecf has new UUID.
2015-07-03 20:02:13 +02:00
148518984e Added the possibility to provide the sendmail location in NOTIFICATION_SENDMAIL_MAILER.
Added NOTIFICATION_STORAGE_MAILER which allow to store the email in a storage (could be just output, file, database ...)
Added SMTP implementation, based on EiffelNet SMTP_PROTOCOL.
   note: it is possible to exclude this by setting ecf variable "smtp_notification_email_disabled" to "True"
   this way help to manage dependencies, since the Eiffel Net library would not be included neither.
Fixed Date header value computation.
2015-07-03 10:02:56 +02:00
33150e34d6 Reverted previous changed related to redefinition of set_status_code which was against existing assertions. 2015-07-02 15:11:33 +02:00
af60a5719e Updated eiffelstudio locations for EWF libraries. 2015-07-02 13:06:38 +02:00
31557cfc33 Fixed WGI_HTTPD_REQUEST_HANDLER.process_rescue
Fixed WGI_STANDALONE_OUTPUT_STREAM.is_available
Added WGI_STANDALONE_RESPONSE_STREAM.is_persistent_connection_supported
2015-07-02 10:50:41 +02:00
78c0cd5b0d Merge branch 'v1' of https://github.com/EiffelWebFramework/EWF into v1 2015-07-01 21:48:47 +02:00
412534d0be Fixed compilation of all*-safe.ecf files.
Corrected a few comments.
2015-07-01 21:43:54 +02:00
jvelilla
0f6aa8d7ae Merge branch 'jvelilla-ewf_v1_workbook' into v1 2015-06-30 09:58:40 -03:00
jvelilla
2c745c63d3 Updated workbook: generating response, handling cookies and headers documents. 2015-06-30 09:21:12 -03:00
jvelilla
efd80c1287 Updated workbook form document 2015-06-30 09:00:47 -03:00
jvelilla
01f649fd88 Updated: workbook headers document.
Removed: unnecessary files.
2015-06-30 08:57:00 -03:00
jvelilla
f23aeb6412 Updated Workbook basic documentation. 2015-06-30 08:42:29 -03:00
jvelilla
1a4596c79b Merge branch 'ewf_v1_workbook' of https://github.com/jvelilla/EWF into ewf_v1_workbook 2015-06-29 19:05:25 -03:00
jvelilla
b16e4aa570 Updated basic documentation 2015-06-29 19:03:54 -03:00
Javier Velilla
5255b15fa9 Update basics.md 2015-06-29 18:36:44 -03:00
jvelilla
57048373f4 Update basic document 2015-06-29 18:30:11 -03:00
Javier Velilla
9e06fb2ab8 Update basics.md 2015-06-29 16:24:09 -03:00
Javier Velilla
f2405e0ccd Update basics.md 2015-06-29 16:22:01 -03:00
Javier Velilla
6e3a7deb6e Update workbook.md 2015-06-29 16:20:19 -03:00
jvelilla
f254b599c0 Update basic document to the new EWF concurrent design 2015-06-29 16:17:43 -03:00
99a05b95ba Improved code related to cookie management (avoid duplicated cookie). 2015-06-22 22:06:16 +02:00
54dd43c38a Synchronized wsf-safe.ecf and wsf.ecf 2015-06-18 14:53:19 +02:00
d0836d49a4 Merge branch 'v1' 2015-06-10 09:49:28 +02:00
130 changed files with 5738 additions and 449 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -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. Lets describe them in a few words.
![Launcher Hierarchy](/doc/workbook/basics/Launcher Hierarchy.png "Launcher Hierarchy")
![Service Template Hierarchy](/workbook/SERVICE_TEMPLATE.png "Service Template")
**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.
![Standalone Launcher](/doc/workbook/basics/WSF_SERVICE_LAUNCHER_STANDALONE.png "Standalone Hierarchy")
Other connectors:
![Launcher Hierarchy](/app/doc/WSF_SERVICE_LAUNCHER.png "Launcher")
**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*.
![Execution Hierarchy](/doc/workbook/basics/APPLICATION_EXECUTION.png "Application Execution ")
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,56 @@
# simple HTTP client
## Overview
It provides simple routine to perform http requests, and get response.
## Requirements
* Eiffel cURL library
* cURL dynamic libraries in the PATH or the current directory (.dll or .so)
* One of the following
- Eiffel cURL library
- cURL dynamic libraries in the PATH or the current directory (.dll or .so)
- Eiffel Net library
- and optionally Eiffel NetSSL library to support https://
This means on Windows, do not forget to copy the libcurl.dll (and related) either in the same directory of the executable, or ensure the .dll are in the PATH environment.
It is possible to exclude the libcurl implementation xor the Eiffel Net implementation:
In the .ecf configuration file of your project, you can use the following custom variables:
* Disable the libcurl implementation
```
<variable name="libcurl_http_client_disabled" value="True"/>
```
* Disable the net implementation
```
<variable name="net_http_client_disabled" value="True"/>
```
* If you disabled both, the http client will not work as expected.
For the net implementation (using EiffelNet), if you need https:// support, you need to enabled the ssl support with the custom variables :
```
<variable name="netssl_http_client_enabled" value="True"/>
```
* By default, SSL is not included (mostly because it is sometime a pain to get the needed dynamic libraries .dll or .so)
## Usage
* To build code that is portable across the libcurl or net implementation of http_client library, use the DEFAULT_HTTP_CLIENT
```
cl: DEFAULT_HTTP_CLIENT
sess: HTTP_CLIENT_SESSION
create cl
sess := cl.new_session ("http://example.com")
if attached sess.get ("/path-to-test") as l_response then
if not l_response.error_occurred then
if attached l_response.body as l_body then
print (l_body)
end
end
end
```
## Examples
* See the tests/test-safe.ecf project to see how to use.
* Examples will come in the future.

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-12-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-12-0 http://www.eiffel.com/developers/xml/configuration-1-12-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
<target name="http_client">
<root all_classes="true"/>
<file_rule>
@@ -7,12 +7,78 @@
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
<option debug="false" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf">
<condition>
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
</condition>
</library>
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
<library name="http" location="..\protocol\http\http-safe.ecf"/>
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<cluster name="src" location=".\src\" recursive="true"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
</library>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
<cluster name="src" location=".\src\">
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
<cluster name="spec_net" location="$|spec\net\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
</condition>
<cluster name="net_implementation" location="$|implementation\" hidden="true">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="net_ssl_disabled" location="$|no_ssl\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="net_ssl_enabled" location="$|ssl\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
</cluster>
</cluster>
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true">
<condition>
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="default_null" location="$|default\null\">
<condition>
<custom name="net_http_client_disabled" value="true"/>
<custom name="libcurl_http_client_disabled" value="true"/>
</condition>
</cluster>
<cluster name="default_net" location="$|default\net\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="libcurl_http_client_disabled" value="true"/>
</condition>
</cluster>
<cluster name="default_libcurl" location="$|default\libcurl\">
<condition>
<custom name="net_http_client_disabled" value="true"/>
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="default_libcurl_or_net" location="$|default\libcurl_or_net\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
</condition>
</cluster>
</cluster>
</target>
</system>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
<target name="http_client">
<root all_classes="true"/>
<file_rule>
@@ -9,10 +9,76 @@
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf">
<condition>
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
</condition>
</library>
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
<library name="http" location="..\protocol\http\http.ecf"/>
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
<library name="encoder" location="../../text/encoder/encoder.ecf"/>
<cluster name="src" location=".\src\" recursive="true"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
</library>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<cluster name="src" location=".\src\">
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
<cluster name="spec_net" location="$|spec\net\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
</condition>
<cluster name="net_implementation" location="$|implementation\" hidden="true">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="net_ssl_disabled" location="$|no_ssl\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="net_ssl_enabled" location="$|ssl\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
</cluster>
</cluster>
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true">
<condition>
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="default_null" location="$|default\null\">
<condition>
<custom name="net_http_client_disabled" value="true"/>
<custom name="libcurl_http_client_disabled" value="true"/>
</condition>
</cluster>
<cluster name="default_net" location="$|default\net\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="libcurl_http_client_disabled" value="true"/>
</condition>
</cluster>
<cluster name="default_libcurl" location="$|default\libcurl\">
<condition>
<custom name="net_http_client_disabled" value="true"/>
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="default_libcurl_or_net" location="$|default\libcurl_or_net\">
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
</condition>
</cluster>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="libcurl_http_client" uuid="FA686EE7-D01D-43A3-8E95-A00120658040" library_target="libcurl_http_client">
<target name="libcurl_http_client">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option debug="false" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
<library name="http" location="..\protocol\http\http-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
<cluster name="src" location=".\src\">
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
<cluster name="default_libcurl" location="$|default\libcurl\"/>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="libcurl_http_client" uuid="FA686EE7-D01D-43A3-8E95-A00120658040" library_target="libcurl_http_client">
<target name="libcurl_http_client">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
<library name="http" location="..\protocol\http\http.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<cluster name="src" location=".\src\">
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
<cluster name="default_libcurl" location="$|default\libcurl\"/>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="7897B317-7AD3-44E4-A933-0544A169AB1B" library_target="net_http_client">
<target name="net_http_client">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option debug="false" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
<library name="http" location="..\protocol\http\http-safe.ecf"/>
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
<condition>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
</library>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
<cluster name="src" location=".\src\">
<cluster name="spec_net" location="$|spec\net\">
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
<cluster name="net_ssl_disabled" location="$|no_ssl\">
<condition>
<custom name="netssl_http_client_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="net_ssl_enabled" location="$|ssl\">
<condition>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
</cluster>
</cluster>
<cluster name="default_net" location="$|default\net\"/>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="7897B317-7AD3-44E4-A933-0544A169AB1B" library_target="net_http_client">
<target name="net_http_client">
<root all_classes="true"/>
<file_rule>
<exclude>/.git$</exclude>
<exclude>/EIFGENs$</exclude>
<exclude>/.svn$</exclude>
</file_rule>
<option debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
<library name="http" location="..\protocol\http\http.ecf"/>
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
<condition>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
</library>
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<cluster name="src" location=".\src\">
<cluster name="spec_net" location="$|spec\net\">
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
<cluster name="net_ssl_disabled" location="$|no_ssl\">
<condition>
<custom name="netssl_http_client_enabled" excluded_value="true"/>
</condition>
</cluster>
<cluster name="net_ssl_enabled" location="$|ssl\">
<condition>
<custom name="netssl_http_client_enabled" value="true"/>
</condition>
</cluster>
</cluster>
<cluster name="default_net" location="$|default\net\"/>
</cluster>
</target>
</system>

View File

@@ -0,0 +1,15 @@
note
description: "[
Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_HTTP_CLIENT
inherit
LIBCURL_HTTP_CLIENT
end

View File

@@ -0,0 +1,46 @@
note
description: "[
Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT or NET_HTTP_CLIENT.
The preference goes to libcurl implementation for now,
since the net implementation has currently less functionalities.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_HTTP_CLIENT
inherit
HTTP_CLIENT
feature -- Access
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
-- Create a new session using `a_base_url'.
local
libcurl: LIBCURL_HTTP_CLIENT
net: NET_HTTP_CLIENT
do
--| For now, try libcurl first, and then net
--| the reason is the net implementation is still in progress.
create libcurl
Result := libcurl.new_session (a_base_url)
if not Result.is_available then
create net
Result := net.new_session (a_base_url)
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,15 @@
note
description: "[
Default HTTP_CLIENT based on NET_HTTP_CLIENT.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_HTTP_CLIENT
inherit
NET_HTTP_CLIENT
end

View File

@@ -0,0 +1,33 @@
note
description: "[
Default HTTP_CLIENT based on NULL_HTTP_CLIENT.
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
class
DEFAULT_HTTP_CLIENT
inherit
HTTP_CLIENT
feature -- Access
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
-- Create a new session using `a_base_url'.
do
create {NULL_HTTP_CLIENT_SESSION} Result.make (a_base_url)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -9,7 +9,7 @@ note
deferred class
HTTP_CLIENT
feature -- Status
feature -- Access
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
-- Create a new session using `a_base_url'.
@@ -17,7 +17,7 @@ feature -- Status
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -14,24 +14,41 @@ inherit
feature {NONE} -- Initialization
make (a_url: READABLE_STRING_8; a_session: like session; ctx: like context)
-- Initialize `Current'.
make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context)
-- Initialize `Current' with request url `a_url', method `a_request_method' within the session `a_session'
-- and optional context `ctx' which can be used to pass additional parameters.
do
request_method := a_request_method
session := a_session
url := a_url
headers := session.headers.twin
if ctx /= Void then
context := ctx
import (ctx)
end
initialize (a_url, ctx)
ensure
context_set: context = ctx
ctx_header_set: ctx /= Void implies across ctx.headers as ctx_h all attached headers.item (ctx_h.key) as v and then v.same_string (ctx_h.item) end
end
initialize (a_url: READABLE_STRING_8; ctx: like context)
-- Initialize Current with `a_url' and `ctx'.
-- This can be used to reset/reinitialize Current with new url
-- in the case of redirection.
do
url := a_url
headers := session.headers.twin
if ctx /= Void then
context := ctx
import (ctx)
else
context := Void
end
end
feature {NONE} -- Internal
session: HTTP_CLIENT_SESSION
-- Session related to Current request.
-- It provides a few parameters related to session.
context: detachable HTTP_CLIENT_REQUEST_CONTEXT
-- Potential additional parameters for this specific request.
feature -- Status report
@@ -44,16 +61,27 @@ feature -- Status report
feature -- Access
request_method: READABLE_STRING_8
deferred
end
-- Request method associated with Current request.
url: READABLE_STRING_8
-- URL associated with current request.
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
-- Specific headers to be used for current request.
response: HTTP_CLIENT_RESPONSE
-- Response received from request execution.
-- Check `error_occurred' for eventual error.
-- note: two consecutive calls will trigger two executions!
deferred
ensure
Result_set: Result /= Void
end
feature {HTTP_CLIENT_SESSION} -- Execution
import (ctx: HTTP_CLIENT_REQUEST_CONTEXT)
-- Import `ctx' parameters.
local
l_headers: like headers
do
@@ -67,10 +95,6 @@ feature {HTTP_CLIENT_SESSION} -- Execution
end
end
execute: HTTP_CLIENT_RESPONSE
deferred
end
feature -- Authentication
auth_type: STRING
@@ -88,21 +112,26 @@ feature -- Authentication
end
username: detachable READABLE_STRING_32
-- Username specified for the `session'.
do
Result := session.username
end
password: detachable READABLE_STRING_32
-- Password specified for the `session'.
do
Result := session.password
end
credentials: detachable READABLE_STRING_32
-- Credentials specified for the `session'.
--| Usually `username':`password'
do
Result := session.credentials
end
proxy: detachable TUPLE [host: READABLE_STRING_8; port: INTEGER]
-- Optional proxy settings.
do
Result := session.proxy
end
@@ -220,7 +249,7 @@ feature {NONE} -- Utilities: encoding
end
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -81,7 +81,10 @@ feature -- Access
-- Optional output file to get downloaded content and header
output_content_file: detachable FILE
-- Optional output file to get downloaded content
-- Optional output file to get downloaded content
http_version: detachable IMMUTABLE_STRING_8
-- Overwrite default http version if set.
feature -- Status report
@@ -209,6 +212,17 @@ feature -- Element change
output_content_file := f
end
set_http_version (v: detachable READABLE_STRING_8)
require
valid_version: v = Void or else v.starts_with_general ("HTTP/")
do
if v = Void then
http_version := Void
else
create http_version.make_from_string (v)
end
end
feature -- Status setting
set_proxy (a_host: detachable READABLE_STRING_8; a_port: INTEGER)
@@ -264,7 +278,7 @@ feature {NONE} -- Implementation
end
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -13,7 +13,7 @@ create
feature {NONE} -- Initialization
make (a_url: like url)
make (a_url: READABLE_STRING_8)
-- Initialize `Current'.
do
--| Default values
@@ -54,12 +54,23 @@ feature -- Access
status_line: detachable READABLE_STRING_8
http_version: detachable READABLE_STRING_8
-- http version associated with `status_line'.
raw_header: READABLE_STRING_8
-- Raw http header of the response.
redirections: detachable ARRAYED_LIST [TUPLE [status_line: detachable READABLE_STRING_8; raw_header: READABLE_STRING_8; body: detachable READABLE_STRING_8]]
-- Header of previous redirection if any.
redirections_count: INTEGER
-- Number of redirections.
do
if attached redirections as lst then
Result := lst.count
end
end
header (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
-- Header entry value related to `a_name'
-- if multiple entries, just concatenate them using comma character
@@ -92,6 +103,24 @@ feature -- Access
Result := s
end
multiple_header (a_name: READABLE_STRING_8): detachable LIST [READABLE_STRING_8]
-- Header multiple entries related to `a_name'
local
k: READABLE_STRING_8
do
across
headers as hds
loop
k := hds.item.name
if k.same_string (a_name) then
if Result = Void then
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (1)
end
Result.force (hds.item.value)
end
end
end
headers: LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
-- Computed table of http headers of the response.
--| We use a LIST since one might have multiple message-header fields with the same field-name
@@ -115,7 +144,7 @@ feature -- Access
loop
l_start := pos
--| Left justify
from until not h[l_start].is_space loop
from until not h [l_start].is_space loop
l_start := l_start + 1
end
pos := h.index_of ('%N', l_start)
@@ -129,7 +158,7 @@ feature -- Access
end
if l_end > 0 then
--| Right justify
from until not h[l_end].is_space loop
from until not h [l_end].is_space loop
l_end := l_end - 1
end
c := h.index_of (':', l_start)
@@ -188,17 +217,72 @@ feature -- Access
end
end
feature -- Status report
is_http_1_0: BOOLEAN
-- Is response using HTTP/1.0 protocole?
--| Note: it is relevant once the raw header are set.
do
Result := attached http_version as v and then v.same_string ("1.0")
end
is_http_1_1: BOOLEAN
-- Is response using HTTP/1.1 protocole?
--| Note: it is relevant once the raw header are set.
do
Result := attached http_version as v and then v.same_string ("1.1")
end
feature -- Change
set_http_version (v: like http_version)
-- Set `http_version' to `v'.
do
http_version := v
end
set_status (s: INTEGER)
-- Set response `status' code to `s'
do
status := s
end
set_status_line (a_line: detachable READABLE_STRING_8)
-- Set status line to `a_line',
-- and also `status' extracted from `a_line' if possible.
local
i,j: INTEGER
s: READABLE_STRING_8
do
status_line := a_line
http_version := Void
if a_line /= Void then
if a_line.starts_with ("HTTP/") then
i := a_line.index_of (' ', 1)
if i > 0 then
http_version := a_line.substring (1 + 5, i - 1) -- ("HTTP/").count = 5
i := i + 1
end
else
i := 1
end
-- Get status code token.
if i > 0 then
j := a_line.index_of (' ', i)
if j > i then
s := a_line.substring (i, j - 1)
if s.is_integer then
set_status (s.to_integer)
end
end
end
end
end
set_response_message (a_source: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT)
-- Parse `a_source' response message
-- and set `header' and `body'.
-- and set `status_line', `status', `header' and `body'.
--| ctx is the context associated with the request
--| it might be useful to deal with redirection customization...
local
@@ -234,7 +318,7 @@ feature -- Change
j := i + 2
pos := j
status_line := l_status_line
set_status_line (l_status_line)
set_raw_header (h)
-- libcURL does not cache redirection content.
@@ -277,10 +361,23 @@ feature -- Change
set_raw_header (h: READABLE_STRING_8)
-- Set http header `raw_header' to `h'
local
i: INTEGER
s: STRING_8
do
raw_header := h
--| Reset internal headers
internal_headers := Void
--| Set status line, right away.
i := h.index_of ('%N', 1)
if i > 0 then
s := h.substring (1, i - 1)
if s.starts_with ("HTTP/") then
s.right_adjust
set_status_line (s)
end
end
end
add_redirection (s: detachable READABLE_STRING_8; h: READABLE_STRING_8; a_body: detachable READABLE_STRING_8)
@@ -308,7 +405,7 @@ feature {NONE} -- Implementation
-- Internal cached value for the headers
;note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -50,6 +50,14 @@ feature {NONE} -- Initialization
feature -- Basic operation
close
-- Close session.
--| useful to disconnect persistent connection.
do
end
feature -- Access
url (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): STRING_8
-- Url computed from Current and `ctx' data.
local
@@ -77,6 +85,61 @@ feature -- Basic operation
end
end
feature {NONE} -- Access: verbose
verbose_mode: INTEGER
-- Internal verbose mode.
verbose_header_sent_mode: INTEGER = 1 --| 0001
verbose_header_received_mode: INTEGER = 2 --| 0010
verbose_debug_mode: INTEGER = 4 --| 0100
feature -- Access: verbose
is_header_sent_verbose: BOOLEAN
do
Result := verbose_mode & verbose_header_sent_mode = verbose_header_sent_mode
end
is_header_received_verbose: BOOLEAN
do
Result := verbose_mode & verbose_header_received_mode = verbose_header_received_mode
end
is_debug_verbose: BOOLEAN
do
Result := verbose_mode & verbose_debug_mode = verbose_debug_mode
end
feature -- Element change: verbose
set_header_sent_verbose (b: BOOLEAN)
do
if b then
verbose_mode := verbose_mode | verbose_header_sent_mode
else
verbose_mode := verbose_mode & verbose_header_sent_mode.bit_not
end
end
set_header_received_verbose (b: BOOLEAN)
do
if b then
verbose_mode := verbose_mode | verbose_header_received_mode
else
verbose_mode := verbose_mode & verbose_header_received_mode.bit_not
end
end
set_debug_verbose (b: BOOLEAN)
do
if b then
verbose_mode := verbose_mode | verbose_debug_mode
else
verbose_mode := verbose_mode & verbose_debug_mode.bit_not
end
end
feature -- Custom
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
@@ -88,17 +151,23 @@ feature -- Helper
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- Response for GET request based on Current, `a_path' and `ctx'.
require
is_available: is_available
deferred
end
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- Response for HEAD request based on Current, `a_path' and `ctx'.
require
is_available: is_available
deferred
end
post (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- Response for POST request based on Current, `a_path' and `ctx'
-- with input `data'
require
is_available: is_available
deferred
end
@@ -111,29 +180,39 @@ feature -- Helper
patch (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- Response for PATCH request based on Current, `a_path' and `ctx'
-- with input `data'
require
is_available: is_available
deferred
end
patch_file (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- Response for PATCH request based on Current, `a_path' and `ctx'
-- with uploaded data file `fn'
require
is_available: is_available
deferred
end
put (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- Response for PUT request based on Current, `a_path' and `ctx'
-- with input `data'
require
is_available: is_available
deferred
end
put_file (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- Response for PUT request based on Current, `a_path' and `ctx'
-- with uploaded file `fn'
require
is_available: is_available
deferred
end
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- Response for DELETE request based on Current, `a_path' and `ctx'
require
is_available: is_available
deferred
end
@@ -185,6 +264,9 @@ feature -- Access
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
-- Headers common to any request created by Current session.
cookie: detachable READABLE_STRING_8
-- Cookie for the current base_url
feature -- Authentication
auth_type: STRING
@@ -194,11 +276,15 @@ feature -- Authentication
auth_type_id: INTEGER
-- See {HTTP_CLIENT_CONSTANTS}.Auth_type_*
username,
username: detachable READABLE_STRING_32
-- Associated optional username value.
password: detachable READABLE_STRING_32
-- Associated optional password value.
credentials: detachable READABLE_STRING_32
-- Associated optional credentials value.
-- Computed as `username':`password'.
feature -- Status setting
@@ -212,6 +298,12 @@ feature -- Element change
set_base_url (u: like base_url)
do
base_url := u
cookie := Void
end
set_cookie (a_cookie: detachable READABLE_STRING_8)
do
cookie := a_cookie
end
set_timeout (n_seconds: like timeout)
@@ -244,12 +336,26 @@ feature -- Element change
headers.prune (k)
end
set_credentials (u: like username; p: like password)
set_credentials (u,p: detachable READABLE_STRING_GENERAL)
local
s: STRING_32
do
username := u
password := p
if u = Void then
username := Void
else
create {STRING_32} username.make_from_string_general (u)
end
if p = Void then
password := Void
else
create {STRING_32} password.make_from_string_general (p)
end
if u /= Void and p /= Void then
credentials := u + ":" + p
create s.make (u.count + 1 + p.count)
s.append_string_general (u)
s.append_character (':')
s.append_string_general (p)
credentials := s
else
credentials := Void
end
@@ -300,7 +406,7 @@ feature -- Element change
end
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -1,6 +1,9 @@
note
description : "[
Specific implementation of HTTP_CLIENT based on Eiffel cURL library
WARNING: Do not forget to have the dynamic libraries libcurl (.dll or .so)
and related accessible to the executable (i.e in same directory, or in the PATH)
]"
author : "$Author$"
date : "$Date$"
@@ -13,6 +16,7 @@ inherit
HTTP_CLIENT
create
default_create,
make
feature {NONE} -- Initialization
@@ -20,6 +24,7 @@ feature {NONE} -- Initialization
make
-- Initialize `Current'.
do
default_create
end
feature -- Status
@@ -30,7 +35,7 @@ feature -- Status
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -1,6 +1,9 @@
note
description: "[
Specific implementation of HTTP_CLIENT_REQUEST based on Eiffel cURL library
WARNING: Do not forget to have the dynamic libraries libcurl (.dll or .so)
and related accessible to the executable (i.e in same directory, or in the PATH)
]"
date: "$Date$"
revision: "$Revision$"
@@ -10,9 +13,8 @@ class
inherit
HTTP_CLIENT_REQUEST
rename
make as make_request
redefine
make,
session
end
@@ -23,8 +25,7 @@ feature {NONE} -- Initialization
make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context)
do
make_request (a_url, a_session, ctx)
request_method := a_request_method
Precursor (a_url, a_request_method, a_session, ctx)
apply_workaround
end
@@ -38,13 +39,10 @@ feature {NONE} -- Initialization
session: LIBCURL_HTTP_CLIENT_SESSION
feature -- Access
request_method: READABLE_STRING_8
feature -- Execution
execute: HTTP_CLIENT_RESPONSE
response: HTTP_CLIENT_RESPONSE
-- <Precursor>
local
l_result: INTEGER
l_curl_string: detachable CURL_STRING
@@ -63,6 +61,8 @@ feature -- Execution
l_upload_data: detachable READABLE_STRING_8
l_upload_filename: detachable READABLE_STRING_GENERAL
l_headers: like headers
l_is_http_1_0: BOOLEAN
l_uri: URI
do
if not retried then
curl := session.curl
@@ -72,6 +72,10 @@ feature -- Execution
ctx := context
if ctx /= Void then
l_is_http_1_0 := attached ctx.http_version as l_http_version and then l_http_version.same_string ("HTTP/1.0")
end
--| Configure cURL session
initialize_curl_session (ctx, curl, curl_easy, curl_handle)
@@ -81,12 +85,48 @@ feature -- Execution
append_parameters_to_url (ctx.query_parameters, l_url)
end
if session.is_header_sent_verbose then
io.error.put_string ("> Sending:%N")
create l_uri.make_from_string (l_url)
io.error.put_string ("> ")
io.error.put_string (request_method + " " + l_uri.path)
if attached l_uri.query as q then
io.error.put_string (q)
end
if l_is_http_1_0 then
io.error.put_string (" HTTP/1.0")
else
io.error.put_string (" HTTP/1.1")
end
io.error.put_new_line
if attached l_uri.host as l_host then
io.error.put_string ("> ")
io.error.put_string ("Host: " + l_host)
io.error.put_new_line
end
across
headers as ic
loop
io.error.put_string ("> ")
io.error.put_string (ic.key)
io.error.put_string (": ")
io.error.put_string (ic.item)
io.error.put_new_line
end
io.error.put_string ("> ... ")
io.error.put_new_line
end
debug ("service")
io.put_string ("SERVICE: " + l_url)
io.put_new_line
end
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_url, l_url)
if l_is_http_1_0 then
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_http_version, {CURL_OPT_CONSTANTS}.curl_http_version_1_0)
else
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_http_version, {CURL_OPT_CONSTANTS}.curl_http_version_none)
end
l_headers := headers
-- Context
@@ -241,6 +281,10 @@ feature -- Execution
Result.status := response_status_code (curl_easy, curl_handle)
if l_curl_string /= Void then
Result.set_response_message (l_curl_string.string, ctx)
if session.is_header_received_verbose then
io.error.put_string ("< Receiving:%N")
io.error.put_string (Result.raw_header)
end
end
else
Result.set_error_message ("Error: cURL Error[" + l_result.out + "]")
@@ -390,7 +434,7 @@ feature {NONE} -- Implementation
end
note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -1,6 +1,9 @@
note
description: "[
Specific implementation of HTTP_CLIENT_SESSION based on Eiffel cURL library
WARNING: Do not forget to have the dynamic libraries libcurl (.dll or .so)
and related accessible to the executable (i.e in same directory, or in the PATH)
]"
date: "$Date$"
revision: "$Revision$"
@@ -34,19 +37,22 @@ feature -- Status report
feature -- Custom
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
local
req: HTTP_CLIENT_REQUEST
do
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
Result := req.execute
Result := req.response
end
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- Same as `custom' but including upload data `a_data'.
do
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
end
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- Same as `custom' but including upload file `fn'.
do
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
end
@@ -54,46 +60,55 @@ feature -- Custom
feature -- Helper
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := custom ("GET", a_path, ctx)
end
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := custom ("HEAD", a_path, ctx)
end
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
end
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
end
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
end
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
end
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
end
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
end
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := custom ("DELETE", a_path, ctx)
end
@@ -140,7 +155,7 @@ feature {NONE} -- Implementation
end
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
Result := req.execute
Result := req.response
if f /= Void then
f.delete
@@ -161,7 +176,7 @@ feature {LIBCURL_HTTP_CLIENT_REQUEST} -- Curl implementation
;note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -0,0 +1,52 @@
note
description: "Socket connection information, used for peristent connection implementation."
date: "$Date$"
revision: "$Revision$"
class
NET_HTTP_CLIENT_CONNECTION
create
make
feature {NONE} -- Initialization
make (a_socket: NETWORK_STREAM_SOCKET; a_host: READABLE_STRING_GENERAL; a_port: INTEGER)
do
socket := a_socket
host := a_host
port := a_port
end
feature -- Access
socket: NETWORK_STREAM_SOCKET
-- Persistent connection socket.
host: READABLE_STRING_GENERAL
-- Host used for this connection.
port: INTEGER
-- Port used for this connection.
feature -- Status report
is_reusable (a_host: READABLE_STRING_GENERAL; a_port: INTEGER): BOOLEAN
-- Is Current connection reusable for new connection `a_host:a_port'?
do
if a_host.same_string (host) and port = a_port then
Result := socket.is_connected
end
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,34 @@
note
description : "[
Specific implementation of HTTP_CLIENT based on Eiffel NET library
WARNING: this is work in progress
]"
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
NET_HTTP_CLIENT
inherit
HTTP_CLIENT
feature -- Status
new_session (a_base_url: READABLE_STRING_8): NET_HTTP_CLIENT_SESSION
do
create Result.make (a_base_url)
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,739 @@
note
description: "[
Specific implementation of HTTP_CLIENT_REQUEST based on Eiffel NET library
]"
date: "$Date$"
revision: "$Revision$"
class
NET_HTTP_CLIENT_REQUEST
inherit
HTTP_CLIENT_REQUEST
redefine
session
end
TRANSFER_COMMAND_CONSTANTS
REFACTORING_HELPER
SHARED_EXECUTION_ENVIRONMENT
create
make
feature {NONE} -- Internal
session: NET_HTTP_CLIENT_SESSION
net_http_client_version: STRING = "0.1"
session_socket (a_host: READABLE_STRING_8; a_port: INTEGER; a_is_https: BOOLEAN; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): NETWORK_STREAM_SOCKET
-- Session socket to use for connection.
-- Eventually reuse the persistent connection if any.
local
l_socket: detachable NETWORK_STREAM_SOCKET
do
if
attached session.persistent_connection as l_persistent_connection and then
l_persistent_connection.is_reusable (a_host, a_port)
then
l_socket := l_persistent_connection.socket
if a_is_https then
if attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
Result := l_ssl_socket
else
l_socket := Void
end
elseif attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
l_socket := Void
end
if l_socket /= Void and then not l_socket.is_connected then
-- Reset persistent connection
l_socket := Void
end
end
if l_socket /= Void then
-- Reuse persistent connection.
Result := l_socket
else
session.set_persistent_connection (Void)
if a_is_https then
create {SSL_NETWORK_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
else
create Result.make_client_by_port (a_port, a_host)
end
Result.set_connect_timeout (connect_timeout)
Result.set_timeout (timeout)
Result.connect
end
end
feature -- Access
response: HTTP_CLIENT_RESPONSE
-- <Precursor>
local
redirection_response: detachable like response
l_uri: URI
l_header_key: READABLE_STRING_8
l_host: READABLE_STRING_8
l_cookie: detachable READABLE_STRING_8
l_request_uri: STRING
l_url: HTTP_URL
l_socket: NETWORK_STREAM_SOCKET
s: STRING
l_message: STRING
l_content_length: INTEGER
l_location: detachable READABLE_STRING_8
l_port: INTEGER
l_is_https: BOOLEAN
l_authorization: HTTP_AUTHORIZATION
l_platform: STRING
l_upload_data: detachable READABLE_STRING_8
l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
ctx: like context
l_upload_file: detachable RAW_FILE
l_upload_filename: detachable READABLE_STRING_GENERAL
l_form_string: STRING
l_prev_header: READABLE_STRING_8
l_boundary: READABLE_STRING_8
l_is_http_1_0_request: BOOLEAN
l_is_keep_alive: BOOLEAN
retried: BOOLEAN
do
if not retried then
ctx := context
if ctx /= Void then
l_is_http_1_0_request := attached ctx.http_version as l_http_version and then l_http_version.same_string ("HTTP/1.0")
end
create Result.make (url)
-- Get URL data
l_is_https := url.starts_with_general ("https://")
create l_uri.make_from_string (url)
l_port := l_uri.port
if l_port = 0 then
if l_is_https then
l_port := 443
else
l_port := 80
end
end
if attached l_uri.host as h then
l_host := h
else
create l_url.make (url)
l_host := l_url.host
end
if attached session.proxy as l_proxy_settings then
-- For now, so proxy support.
check
not_supported: False
end
end
-- Connect
l_socket := session_socket (l_host, l_port, l_is_https, ctx)
if l_socket.is_connected then
create l_form_string.make_empty
-- add headers for authorization
if not headers.has ("Authorization") then
if
attached username as u_name and
attached password as u_pass
then
create l_authorization.make_basic_auth (u_name, u_pass)
if attached l_authorization.http_authorization as auth then
headers.extend (auth, "Authorization")
end
check headers.has_key ("Authorization") end
end
end
create l_request_uri.make_from_string (l_uri.path)
if attached l_uri.query as l_query then
l_request_uri.append_character ('?')
l_request_uri.append (l_query)
end
-- add computed header User-Agent if not yet set.
if not headers.has ("User-Agent") then
if {PLATFORM}.is_unix then
l_platform := "Unix"
elseif {PLATFORM}.is_windows then
l_platform := "Windows"
elseif {PLATFORM}.is_mac then
l_platform := "Mac"
elseif {PLATFORM}.is_vms then
l_platform := "VMS"
elseif {PLATFORM}.is_vxworks then
l_platform := "VxWorks"
else
l_platform := "Unknown"
end
headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
end
-- handle sending data
if ctx /= Void then
if ctx.has_upload_filename then
l_upload_filename := ctx.upload_filename
end
if ctx.has_upload_data then
l_upload_data := ctx.upload_data
end
if ctx.has_form_data then
l_form_data := ctx.form_parameters
if l_upload_data = Void and l_upload_filename = Void then
-- Send as form-urlencoded
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
l_upload_data := ctx.form_parameters_to_url_encoded_string
headers.force (l_upload_data.count.out, "Content-Length")
else
-- create form using multipart/form-data encoding
l_boundary := new_mime_boundary
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
if l_form_data /= Void then
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
headers.extend (l_upload_data.count.out, "Content-Length")
end
end
elseif l_upload_data /= Void then
check ctx.has_upload_data end
if not headers.has ("Content-Type") then
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
end
headers.extend (l_upload_data.count.out, "Content-Length")
elseif l_upload_filename /= Void then
check ctx.has_upload_filename end
create l_upload_file.make_with_name (l_upload_filename)
if l_upload_file.exists and then l_upload_file.readable then
headers.extend (l_upload_file.count.out, "Content-Length")
end
check l_upload_file /= Void end
end
end
-- FIXME: check usage of headers and specific header variable.
--| only one Cookie: is allowed, so merge multiple into one;
--| if Host is in header, use that one.
-- Compute Request line.
create s.make_from_string (request_method.as_upper)
s.append_character (' ')
s.append (l_request_uri)
s.append_character (' ')
if l_is_http_1_0_request then
s.append ("HTTP/1.0")
else
s.append ("HTTP/1.1")
end
s.append (Http_end_of_header_line)
-- Compute Header Host:
s.append (Http_host_header)
s.append (": ")
if attached headers [Http_host_header] as h_host then
s.append (h_host)
else
s.append (l_host)
end
s.append (http_end_of_header_line)
if not headers.has ("Connection") then
if l_is_http_1_0_request then
s.append ("Connection: keep-alive")
s.append (http_end_of_header_line)
end
end
-- Append the given request headers
l_cookie := Void
if not headers.is_empty then
across
headers as ic
loop
l_header_key := ic.key
if l_header_key.same_string_general ("Host") then
-- FIXME: already handled elsewhere!
elseif l_header_key.same_string_general ("Cookie") then
-- FIXME: need cookie merging.
l_cookie := ic.item
else
s.append (ic.key)
s.append (": ")
s.append (ic.item)
s.append (Http_end_of_header_line)
end
end
end
-- Compute Header Cookie: if needed
-- Use session cookie
if l_cookie = Void then
l_cookie := session.cookie
else
-- Overwrite potential session cookie, if specified by the user.
end
if l_cookie /= Void then
s.append ("Cookie: ")
s.append (l_cookie)
s.append (http_end_of_header_line)
end
--| End of client header.
s.append (Http_end_of_header_line)
if l_upload_data /= Void then
s.append (l_upload_data)
s.append (http_end_of_header_line)
end
--| Note that any remaining file to upload will be done directly via the socket
--| to optimize memory usage
--|-----------------------------|--
--| Request preparation is done |--
--|-----------------------------|--
if l_socket.ready_for_writing then
--| Socket is ready for writing, so let's send the request.
--|-------------------------|--
--| Send request |--
--|-------------------------|--
if session.is_header_sent_verbose then
log ("> Sending:%N")
log (s)
end
l_socket.put_string (s)
--| Send remaining payload data, if needed.
if l_upload_file /= Void then
-- i.e: not yet processed
append_file_content_to_socket (l_upload_file, l_upload_file.count, l_socket)
end
--|-------------------------|--
--| Get response. |--
--| Get header message |--
--|-------------------------|--
if is_ready_for_reading (l_socket) then
create l_message.make_empty
append_socket_header_content_to (Result, l_socket, l_message)
if session.is_header_received_verbose then
log ("< Receiving:%N")
log (l_message)
end
l_prev_header := Result.raw_header
Result.set_raw_header (l_message.string)
l_message.append (http_end_of_header_line)
if not Result.error_occurred then
-- Get information from header
l_content_length := -1
if attached Result.header ("Content-Length") as s_len and then s_len.is_integer then
l_content_length := s_len.to_integer
end
l_location := Result.header ("Location")
if attached Result.header ("Set-Cookie") as s_cookies then
session.set_cookie (s_cookies)
end
-- Keep-alive connection?
-- with HTTP/1.1, this is the default, and could be changed by Connection: close
-- with HTTP/1.0, it requires "Connection: keep-alive" header line.
if attached Result.header ("Connection") as s_connection then
l_is_keep_alive := s_connection.same_string ("keep-alive")
else
l_is_keep_alive := not Result.is_http_1_0
end
-- Get content if any.
append_socket_content_to (Result, l_socket, l_content_length, l_message)
-- Restore previous header
Result.set_raw_header (l_prev_header)
-- Set message
Result.set_response_message (l_message, ctx)
-- Check status code.
check status_coherent: attached Result.status_line as l_status_line implies l_status_line.has_substring (Result.status.out) end
if l_is_keep_alive then
session.set_persistent_connection (create {NET_HTTP_CLIENT_CONNECTION}.make (l_socket, l_host, l_port))
else
session.set_persistent_connection (Void)
end
-- follow redirect
if l_location /= Void then
if Result.redirections_count < max_redirects then
initialize (l_location, ctx)
redirection_response := response
redirection_response.add_redirection (Result.status_line, Result.raw_header, Result.body)
Result := redirection_response
end
end
if not l_is_keep_alive then
l_socket.cleanup
end
end
else
if session.is_debug_verbose then
log ("Debug: Read Timeout!%N")
end
Result.set_error_message ("Read Timeout")
end
else
if session.is_debug_verbose then
log ("Debug: Write Timeout!%N")
end
Result.set_error_message ("Write Timeout")
end
else
if session.is_debug_verbose then
log ("Debug: Could not connect!%N")
end
Result.set_error_message ("Could not connect")
end
else
create Result.make (url)
Result.set_error_message ("Error: internal error")
end
rescue
retried := True
retry
end
feature {NONE} -- Helpers
log (m: READABLE_STRING_8)
-- Output log messages.
do
io.error.put_string (m)
end
is_ready_for_reading (a_socket: NETWORK_STREAM_SOCKET): BOOLEAN
-- Is `a_socket' ready for reading?
do
Result := a_socket.ready_for_reading
end
form_date_and_uploaded_files_to_mime_string (a_form_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]; a_upload_filename: detachable READABLE_STRING_GENERAL; a_mime_boundary: READABLE_STRING_8): STRING
-- Form data and uploaded files converted to mime string.
-- TODO: design a proper MIME... component.
local
l_path: PATH
l_mime_type: READABLE_STRING_8
l_upload_file: detachable RAW_FILE
l_mime_type_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING
do
create Result.make (100)
across
a_form_parameters as ic
loop
Result.append (a_mime_boundary)
Result.append (http_end_of_header_line)
Result.append ("Content-Disposition: form-data; name=")
Result.append_character ('%"')
Result.append (string_to_mime_encoded_string (ic.key))
Result.append_character ('%"')
Result.append (http_end_of_header_line)
Result.append (http_end_of_header_line)
Result.append (string_to_mime_encoded_string (ic.item))
Result.append (http_end_of_header_line)
end
if a_upload_filename /= Void then
-- get file extension, otherwise set default
create l_mime_type_mapping.make_default
create l_path.make_from_string (a_upload_filename)
if
attached l_path.extension as ext and then
attached l_mime_type_mapping.mime_type (ext) as l_mt
then
l_mime_type := l_mt
else
l_mime_type := "application/octet-stream"
end
Result.append (a_mime_boundary)
Result.append (http_end_of_header_line)
Result.append ("Content-Disposition: form-data; name=%"")
Result.append (string_to_mime_encoded_string (a_upload_filename))
Result.append_character ('%"')
Result.append ("; filename=%"")
Result.append (string_to_mime_encoded_string (a_upload_filename))
Result.append_character ('%"')
Result.append (http_end_of_header_line)
Result.append ("Content-Type: ")
Result.append (l_mime_type)
Result.append (http_end_of_header_line)
Result.append (http_end_of_header_line)
create l_upload_file.make_with_path (l_path)
if l_upload_file.exists and then l_upload_file.is_access_readable then
append_file_content_to (l_upload_file, l_upload_file.count, Result)
-- Reset l_upload_file to Void, since the related content is already processed.
l_upload_file := Void
end
Result.append (http_end_of_header_line)
end
Result.append (a_mime_boundary)
Result.append ("--") --| end
end
string_to_mime_encoded_string (s: READABLE_STRING_GENERAL): STRING
-- Encoded unicode string for mime value.
-- For instance uploaded filename, or form data key or values.
local
utf: UTF_CONVERTER
do
-- FIXME: find the proper encoding!
Result := utf.utf_32_string_to_utf_8_string_8 (s)
end
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: NETWORK_STREAM_SOCKET)
-- Append `a_file' content to `a_output'.
-- If `a_len' >= 0 then read only `a_len' characters.
require
a_file_readable: a_file.exists and then a_file.is_access_readable
local
l_was_open: BOOLEAN
l_count: INTEGER
do
if a_len >= 0 then
l_count := a_len
else
l_count := a_file.count
end
if l_count > 0 then
l_was_open := a_file.is_open_read
if a_file.is_open_read then
l_was_open := True
else
a_file.open_read
end
from
until
l_count = 0 or a_file.exhausted
loop
a_file.read_stream_thread_aware (l_count.min (2_048))
a_output.put_string (a_file.last_string)
l_count := l_count - a_file.bytes_read
end
if not l_was_open then
a_file.close
end
end
end
append_file_content_to (a_file: FILE; a_len: INTEGER; a_output: STRING)
-- Append `a_file' content to `a_output'.
-- If `a_len' >= 0 then read only `a_len' characters.
require
a_file_readable: a_file.exists and then a_file.is_access_readable
local
l_was_open: BOOLEAN
l_count: INTEGER
do
if a_len >= 0 then
l_count := a_len
else
l_count := a_file.count
end
if l_count > 0 then
l_was_open := a_file.is_open_read
if a_file.is_open_read then
l_was_open := True
else
a_file.open_read
end
from
until
l_count = 0 or a_file.exhausted
loop
a_file.read_stream_thread_aware (l_count.min (2_048))
a_output.append (a_file.last_string)
l_count := l_count - a_file.bytes_read
end
if not l_was_open then
a_file.close
end
end
end
append_socket_header_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
-- Get header from `a_socket' into `a_output'.
local
s: READABLE_STRING_8
do
from
s := ""
until
s.same_string ("%R") or not a_socket.readable or a_response.error_occurred
loop
a_socket.read_line_thread_aware
s := a_socket.last_string
if s.is_empty then
if session.is_debug_verbose then
log ("Debug: ERROR: zero byte read when receiving header.%N")
end
a_response.set_error_message ("Read zero byte, expecting header line")
elseif s.same_string ("%R") then
-- Reach end of header
else
a_output.append (s)
a_output.append_character ('%N')
end
end
end
append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_len: INTEGER; a_output: STRING)
-- Get content from `a_socket' and append it to `a_output'.
-- If `a_len' is negative, try to get as much as possible,
-- this is probably HTTP/1.0 without any Content-Length.
local
s: STRING_8
r: INTEGER -- remaining count
n,l_chunk_size, l_count: INTEGER
do
if a_socket.readable then
if a_len >= 0 then
if session.is_debug_verbose then
log ("Debug: Content-Length="+ a_len.out +"%N")
end
from
r := a_len
until
r = 0 or else not a_socket.readable or else a_response.error_occurred
loop
a_socket.read_stream_thread_aware (r)
l_count := l_count + a_socket.bytes_read
if session.is_debug_verbose then
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
log ("Debug: - current count=" + l_count.out + "%N")
end
r := r - a_socket.bytes_read
a_output.append (a_socket.last_string)
end
check full_content_read: not a_response.error_occurred implies l_count = a_len end
elseif attached a_response.header ("Transfer-Encoding") as l_enc and then l_enc.is_case_insensitive_equal ("chunked") then
append_socket_chunked_content_to (a_response, a_socket, a_output)
else
-- No Content-Length and no chunked transfer encoding!
-- maybe HTTP/1.0 ?
-- FIXME: check solution!
from
l_count := 0
l_chunk_size := 1_024
n := l_chunk_size --| value to satisfy until condition on first loop.
until
n < l_chunk_size or not a_socket.readable
loop
a_socket.read_stream_thread_aware (l_chunk_size)
s := a_socket.last_string
n := a_socket.bytes_read
l_count := l_count + n
a_output.append (s)
end
end
end
end
append_socket_chunked_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
-- Get chunked content from `a_socket' and append it to `a_output'.
require
socket_readable: a_socket.readable
has_chunked_transfer_encoding: attached a_response.header ("Transfer-Encoding") as l_enc and then
l_enc.is_case_insensitive_equal ("chunked")
local
s: STRING_8
r: INTEGER -- remaining count
n,pos, l_count: INTEGER
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
do
if session.is_debug_verbose then
log ("Debug: Chunked encoding%N")
end
from
create hexa2int.make
n := 1
until
n = 0 or not a_socket.readable
loop
a_socket.read_line_thread_aware -- Read chunk info
s := a_socket.last_string
s.right_adjust
if session.is_debug_verbose then
log ("Debug: - chunk info='" + s + "'%N")
end
pos := s.index_of (';', 1)
if pos > 0 then
s.keep_head (pos - 1)
end
if s.is_empty then
n := 0
else
hexa2int.parse_string_with_type (s, hexa2int.type_integer)
if hexa2int.parse_successful then
n := hexa2int.parsed_integer
else
n := 0
end
end
if session.is_debug_verbose then
log ("Debug: - chunk size=" + n.out + "%N")
end
if n > 0 then
from
r := n
until
r = 0 or else not a_socket.readable or else a_response.error_occurred
loop
a_socket.read_stream_thread_aware (r)
l_count := l_count + a_socket.bytes_read
if session.is_debug_verbose then
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
log ("Debug: - current count=" + l_count.out + "%N")
end
r := r - a_socket.bytes_read
a_output.append (a_socket.last_string)
end
a_socket.read_character
check a_socket.last_character = '%R' end
a_socket.read_character
check a_socket.last_character = '%N' end
if session.is_debug_verbose then
log ("Debug: - Found CRNL %N")
end
end
end
end
new_mime_boundary: STRING
-- New MIME boundary.
do
-- FIXME: better boundary creation
Result := "----------------------------5eadfcf3bb3e"
end
invariant
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,184 @@
note
description: "[
Specific implementation of HTTP_CLIENT_SESSION based on Eiffel NET library
]"
date: "$Date$"
revision: "$Revision$"
class
NET_HTTP_CLIENT_SESSION
inherit
HTTP_CLIENT_SESSION
redefine
close
end
NET_HTTP_CLIENT_INFO
create
make
feature {NONE} -- Initialization
initialize
do
end
feature -- Status report
is_available: BOOLEAN
-- Is interface usable?
do
Result := True
if base_url.starts_with_general ("https://") then
Result := has_https_support
end
end
feature -- Access: persistent connection
persistent_connection: detachable NET_HTTP_CLIENT_CONNECTION
-- Socket used for persistent connection purpose.
feature -- Element change: persistent connection
set_persistent_connection (a_connection: like persistent_connection)
-- Set `persistent_connection' to `a_connection'.
do
persistent_connection := a_connection
end
feature -- Basic operation
close
-- <Precursor>
do
if attached persistent_connection as l_connection then
persistent_connection := Void
l_connection.socket.cleanup
end
end
feature -- Custom
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
local
req: HTTP_CLIENT_REQUEST
do
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
Result := req.response
end
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- Same as `custom' but including upload data `a_data'.
do
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
end
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- Same as `custom' but including upload file `fn'.
do
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
end
feature -- Helper
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := custom ("GET", a_path, ctx)
end
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := custom ("HEAD", a_path, ctx)
end
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
end
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
end
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
end
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
end
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
end
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
end
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do
Result := custom ("DELETE", a_path, ctx)
end
feature {NONE} -- Implementation
impl_custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
local
req: HTTP_CLIENT_REQUEST
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
l_data: detachable READABLE_STRING_8
do
ctx := a_ctx
if data /= Void then
if ctx = Void then
create ctx.make
end
ctx.set_upload_data (data)
end
if fn /= Void then
if ctx = Void then
create ctx.make
end
ctx.set_upload_filename (fn)
end
if ctx /= Void then
l_data := ctx.upload_data
if l_data /= Void and a_method.is_case_insensitive_equal_general ("PUT") then
check put_conflict_file_and_data: not ctx.has_upload_filename end
end
end
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
Result := req.response
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,24 @@
note
description: "Additional information related to NET HTTP Client.."
date: "$Date$"
revision: "$Revision$"
class
NET_HTTP_CLIENT_INFO
feature -- Access
has_https_support: BOOLEAN = False
-- Is HTTPS supported?
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,22 @@
note
description: "[
A fake SSL network stream socket... when SSL is disabled at compilation time.
Its behavior is similar to NETWORK_STREAM_SOCKET.
]"
date: "$Date$"
revision: "$Revision$"
class
SSL_NETWORK_STREAM_SOCKET
inherit
NETWORK_STREAM_SOCKET
create
make, make_empty, make_client_by_port, make_client_by_address_and_port, make_server_by_port, make_loopback_server_by_port
create {SSL_NETWORK_STREAM_SOCKET}
make_from_descriptor_and_address, create_from_descriptor
end

View File

@@ -0,0 +1,24 @@
note
description: "Additional information related to NET HTTP Client.."
date: "$Date$"
revision: "$Revision$"
class
NET_HTTP_CLIENT_INFO
feature -- Access
has_https_support: BOOLEAN = True
-- Is HTTPS supported?
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

View File

@@ -0,0 +1,110 @@
note
description : "[
NULL version of HTTP_CLIENT_SESSION.
It is used if no implementation is available (libcurl or net)
]"
date: "$Date$"
revision: "$Revision$"
class
NULL_HTTP_CLIENT_SESSION
inherit
HTTP_CLIENT_SESSION
create
make
feature {NONE} -- Initialization
initialize
do
end
feature -- Custom
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
do
create Result.make (base_url + a_path)
end
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
do
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
end
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
do
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
end
feature -- Helper
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
do
Result := custom ("GET", a_path, ctx)
end
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
do
Result := custom ("HEAD", a_path, ctx)
end
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
do
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
end
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
do
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
end
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
do
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
end
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
do
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
end
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
do
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
end
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
do
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
end
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
do
Result := custom ("DELETE", a_path, ctx)
end
feature {NONE} -- Implementation
impl_custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
do
Result := custom (a_method, a_path, a_ctx)
end
feature -- Status report
is_available: BOOLEAN = False
-- Is interface usable?
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-10-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-10-0 http://www.eiffel.com/developers/xml/configuration-1-10-0.xsd" name="test_http_client" uuid="920E5C50-41E1-4DAC-8D48-D9C860E49228">
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="test_http_client" uuid="920E5C50-41E1-4DAC-8D48-D9C860E49228">
<target name="test_http_client">
<root class="TEST" feature="make"/>
<file_rule>
@@ -10,6 +10,9 @@
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option>
<variable name="netssl_http_client_enabled" value="false"/>
<variable name="net_http_client_disabled" value="false"/>
<variable name="libcurl_http_client_disabled" value="false"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="http_client" location="..\http_client-safe.ecf" readonly="false" use_application_options="true">
<option>
@@ -17,6 +20,19 @@
</option>
</library>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
<tests name="tests" location=".\"/>
<tests name="tests" location=".\">
<file_rule>
<exclude>.*libcurl_.*.e$</exclude>
<condition>
<custom name="libcurl_http_client_disabled" value="true"/>
</condition>
</file_rule>
<file_rule>
<exclude>.*net_.*.e$</exclude>
<condition>
<custom name="net_http_client_disabled" value="true"/>
</condition>
</file_rule>
</tests>
</target>
</system>

View File

@@ -6,17 +6,46 @@ create
feature -- Init
make
local
null: NULL_HTTP_CLIENT_SESSION
do
create null.make ("http://example.com/")
check not null.is_available end
test_get_with_authentication
test_http_client
end
test_get_with_authentication
local
cl: DEFAULT_HTTP_CLIENT
sess: HTTP_CLIENT_SESSION
ctx: HTTP_CLIENT_REQUEST_CONTEXT
do
-- GET REQUEST WITH AUTHENTICATION, see http://browserspy.dk/password.php
-- check header WWW-Authenticate is received (authentication successful)
create cl
sess := cl.new_session ("http://browserspy.dk")
sess.set_credentials ("test", "test")
create ctx.make_with_credentials_required
if attached sess.get ("/password-ok.php", ctx) as res then
if attached {READABLE_STRING_8} res.body as l_body then
assert ("Fetch all body, including closing html tag", l_body.has_substring ("</html>"))
else
assert ("has body", False)
end
end
end
test_http_client
-- New test routine
local
sess: LIBCURL_HTTP_CLIENT_SESSION
cl: DEFAULT_HTTP_CLIENT
sess: HTTP_CLIENT_SESSION
h: STRING_8
do
create sess.make ("http://www.google.com")
create cl
sess := cl.new_session ("http://www.google.com")
if attached sess.get ("/search?q=eiffel", Void) as res then
assert ("Get returned without error", not res.error_occurred)
create h.make_empty
@@ -42,7 +71,7 @@ feature -- Init
do
if not b then
create e
e.set_message (m)
e.set_description (m)
e.raise
end
end

View File

@@ -0,0 +1 @@
This is a text sample for testing HTTP Client library.

View File

@@ -7,21 +7,27 @@ note
revision: "$Revision$"
testing: "type/manual"
class
TEST_HTTP_CLIENT
deferred class
TEST_HTTP_CLIENT_I
inherit
EQA_TEST_SET
feature -- Factory
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
deferred
end
feature -- Test routines
test_http_client
-- New test routine
local
sess: LIBCURL_HTTP_CLIENT_SESSION
sess: like new_session
h: STRING_8
do
create sess.make ("http://www.google.com")
sess := new_session ("http://www.google.com")
if attached sess.get ("/search?q=eiffel", Void) as res then
assert ("Get returned without error", not res.error_occurred)
create h.make_empty
@@ -38,8 +44,33 @@ feature -- Test routines
assert ("missing body", False)
end
assert ("same headers", h.same_string (res.raw_header))
else
assert ("Not found", False)
end
end
test_http_client_ssl
-- New test routine
local
sess: like new_session
h: STRING_8
do
sess := new_session ("https://www.eiffel.org")
sess.set_is_insecure (True)
if attached sess.get ("/welcome", Void) as res then
assert ("Get returned without error", not res.error_occurred)
create h.make_empty
if attached res.headers as hds then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
if attached res.body as l_body then
assert ("body not empty", not l_body.is_empty)
else
assert ("missing body", False)
end
assert ("same headers", h.same_string (res.raw_header))
end
end

View File

@@ -0,0 +1,43 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date$"
revision: "$Revision$"
testing: "type/manual"
class
TEST_LIBCURL_HTTP_CLIENT
inherit
TEST_HTTP_CLIENT_I
feature -- Factory
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
do
create {LIBCURL_HTTP_CLIENT_SESSION} Result.make (a_url)
end
feature -- Tests
test_libcurl_http_client
do
test_http_client
end
test_libcurl_http_client_ssl
do
test_http_client_ssl
end
test_libcurl_headers
do
test_headers
end
end

View File

@@ -0,0 +1,67 @@
note
description: "[
Objects that ...
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
class
TEST_LIBCURL_WITH_WEB
inherit
TEST_WITH_WEB_I
feature -- Factory
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
local
cl: LIBCURL_HTTP_CLIENT
do
create cl
Result := cl.new_session (a_url)
end
feature -- Tests
libcurl_test_post_url_encoded
do
test_post_url_encoded
end
libcurl_test_post_with_form_data
do
test_post_with_form_data
end
libcurl_test_post_with_file
do
test_post_with_file
end
libcurl_test_put_with_file
do
test_put_with_file
end
libcurl_test_put_with_data
do
test_put_with_data
end
libcurl_test_post_with_file_and_form_data
do
test_post_with_file_and_form_data
end
libcurl_test_get_with_redirection
do
test_get_with_redirection
end
libcurl_test_get_with_authentication
do
test_get_with_authentication
end
end

View File

@@ -0,0 +1,86 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date$"
revision: "$Revision$"
testing: "type/manual"
class
TEST_NET_HTTP_CLIENT
inherit
TEST_HTTP_CLIENT_I
feature -- Factory
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
do
create {NET_HTTP_CLIENT_SESSION} Result.make (a_url)
end
feature -- Tests
test_net_http_client
do
test_http_client
end
test_net_http_client_ssl
do
test_http_client_ssl
end
test_net_headers
do
test_headers
end
test_persistent_connection
local
sess: like new_session
h: STRING_8
do
sess := new_session ("http://www.google.fr")
if attached sess.get ("/", Void) as res then
assert ("Get returned without error", not res.error_occurred)
create h.make_empty
if attached res.headers as hds then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
if attached res.body as l_body then
assert ("body not empty", not l_body.is_empty)
else
assert ("missing body", False)
end
assert ("same headers", h.same_string (res.raw_header))
end
if attached sess.get ("/", Void) as res then
assert ("Get returned without error", not res.error_occurred)
create h.make_empty
if attached res.headers as hds then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
if attached res.body as l_body then
assert ("body not empty", not l_body.is_empty)
else
assert ("missing body", False)
end
assert ("same headers", h.same_string (res.raw_header))
end
sess.close
end
end

View File

@@ -0,0 +1,67 @@
note
description: "[
Objects that ...
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
class
TEST_NET_WITH_WEB
inherit
TEST_WITH_WEB_I
feature -- Factory
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
local
cl: NET_HTTP_CLIENT
do
create cl
Result := cl.new_session (a_url)
end
feature -- Tests
net_test_post_url_encoded
do
test_post_url_encoded
end
net_test_post_with_form_data
do
test_post_with_form_data
end
net_test_post_with_file
do
test_post_with_file
end
net_test_put_with_file
do
test_put_with_file
end
net_test_put_with_data
do
test_put_with_data
end
net_test_post_with_file_and_form_data
do
test_post_with_file_and_form_data
end
net_test_get_with_redirection
do
test_get_with_redirection
end
net_test_get_with_authentication
do
test_get_with_authentication
end
end

View File

@@ -0,0 +1,302 @@
note
description: "[
Eiffel tests that can be executed by testing tool.
]"
author: "EiffelStudio test wizard"
date: "$Date$"
revision: "$Revision$"
testing: "type/manual"
deferred class
TEST_WITH_WEB_I
inherit
EQA_TEST_SET
redefine
on_prepare
end
feature -- Initialization
on_prepare
do
Precursor
global_requestbin_path := new_requestbin_path
end
feature -- Factory
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
deferred
end
feature -- Requestbin
global_requestbin_path: detachable READABLE_STRING_8
new_requestbin_path: detachable STRING
local
i,j: INTEGER
do
if
attached new_session ("http://requestb.in") as sess and then
attached sess.post ("/api/v1/bins", Void, Void) as resp
then
if resp.error_occurred then
print ("Error occurred!%N")
elseif attached resp.body as l_content then
i := l_content.substring_index ("%"name%":", 1)
if i > 0 then
j := l_content.index_of (',', i + 1)
if j > 0 then
Result := l_content.substring (i + 7, j - 1)
Result.adjust
if Result.starts_with ("%"") then
Result.remove_head (1)
end
if Result.ends_with ("%"") then
Result.remove_tail (1)
end
if not Result.starts_with ("/") then
Result.prepend_character ('/')
end
end
end
end
end
end
feature -- Factory
test_post_url_encoded
local
sess: HTTP_CLIENT_SESSION
h: STRING_8
do
if attached global_requestbin_path as requestbin_path then
-- URL ENCODED POST REQUEST
-- check requestbin to ensure the "Hello World" has been received in the raw body
-- also check that User-Agent was sent
create h.make_empty
sess := new_session ("http://requestb.in")
if
attached sess.post (requestbin_path, Void, "Hello World") as res and then
attached res.headers as hds
then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
print (h)
else
assert ("Has requestbin path", False)
end
end
test_post_with_form_data
local
sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do
if attached global_requestbin_path as requestbin_path then
-- POST REQUEST WITH FORM DATA
-- check requestbin to ensure the form parameters are correctly received
sess := new_session ("http://requestb.in")
create l_ctx.make
l_ctx.form_parameters.extend ("First Value", "First Key")
l_ctx.form_parameters.extend ("Second Value", "Second Key")
create h.make_empty
if
attached sess.post (requestbin_path, l_ctx, "") as res and then
attached res.headers as hds
then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
print (h)
else
assert ("Has requestbin path", False)
end
end
test_post_with_file
local
sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do
if attached global_requestbin_path as requestbin_path then
-- POST REQUEST WITH A FILE
-- check requestbin to ensure the form parameters are correctly received
-- set filename to a local file
sess := new_session ("http://requestb.in")
create l_ctx.make
l_ctx.set_upload_filename ("test.txt")
create h.make_empty
if
attached sess.post (requestbin_path, l_ctx, "") as res and then
attached res.headers as hds
then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
print (h)
else
assert ("Has requestbin path", False)
end
end
test_put_with_file
local
sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do
if attached global_requestbin_path as requestbin_path then
-- PUT REQUEST WITH A FILE
-- check requestbin to ensure the file is correctly received
-- set filename to a local file
sess := new_session ("http://requestb.in")
create l_ctx.make
l_ctx.set_upload_filename ("test.txt")
create h.make_empty
if
attached sess.put (requestbin_path, l_ctx, Void) as res and then
attached res.headers as hds
then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
print (h)
else
assert ("Has requestbin path", False)
end
end
test_put_with_data
local
sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do
if attached global_requestbin_path as requestbin_path then
-- PUT REQUEST WITH A FILE
-- check requestbin to ensure the file is correctly received
-- set filename to a local file
sess := new_session ("http://requestb.in")
create l_ctx.make
l_ctx.set_upload_data ("This is a test for http client.%N")
create h.make_empty
if
attached sess.put (requestbin_path, l_ctx, Void) as res and then
attached res.headers as hds
then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
print (h)
else
assert ("Has requestbin path", False)
end
end
test_post_with_file_and_form_data
local
sess: HTTP_CLIENT_SESSION
h: STRING_8
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
do
if attached global_requestbin_path as requestbin_path then
-- POST REQUEST WITH A FILE AND FORM DATA
-- check requestbin to ensure the file and form parameters are correctly received
-- set filename to a local file
sess := new_session ("http://requestb.in")
create l_ctx.make
l_ctx.set_upload_filename ("logo.jpg")
l_ctx.form_parameters.extend ("First Value", "First Key")
l_ctx.form_parameters.extend ("Second Value", "Second Key")
create h.make_empty
if
attached sess.post (requestbin_path, l_ctx, Void) as res and then
attached res.headers as hds
then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
print (h)
else
assert ("Has requestbin path", False)
end
end
test_get_with_redirection
local
sess: HTTP_CLIENT_SESSION
h: STRING_8
do
if attached global_requestbin_path as requestbin_path then
-- GET REQUEST, Forwarding (google's first answer is a forward)
-- check headers received (printed in console)
sess := new_session ("http://google.com")
create h.make_empty
if attached sess.get ("/", Void) as res and then attached res.headers as hds then
across
hds as c
loop
h.append (c.item.name + ": " + c.item.value + "%R%N")
end
end
print (h)
else
assert ("Has requestbin path", False)
end
end
test_get_with_authentication
local
sess: HTTP_CLIENT_SESSION
ctx: HTTP_CLIENT_REQUEST_CONTEXT
do
-- GET REQUEST WITH AUTHENTICATION, see http://browserspy.dk/password.php
-- check header WWW-Authenticate is received (authentication successful)
sess := new_session ("http://browserspy.dk")
sess.set_credentials ("test", "test")
create ctx.make_with_credentials_required
if attached sess.get ("/password-ok.php", ctx) as res then
if attached {READABLE_STRING_8} res.body as l_body then
assert ("Fetch all body, including closing html tag", l_body.has_substring ("</html>"))
else
assert ("has body", False)
end
end
end
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -60,7 +60,6 @@ feature -- Access
is_persistent_connection_supported: BOOLEAN = True
-- Is persistent connection supported?
--| For now, disabled during dev.
feature -- Callbacks

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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