Compare commits
24 Commits
es_rev_979
...
httpclient
| Author | SHA1 | Date | |
|---|---|---|---|
| dde6a0b7de | |||
| b64a281d75 | |||
| b69b8aaaf9 | |||
| 65b28ed877 | |||
| 6c7637716b | |||
| ff9a238f5c | |||
| eec3cbdba1 | |||
| 29c4931dc0 | |||
|
|
9cd0f0b117 | ||
|
|
aa0eb4fc43 | ||
|
|
dbdc594b59 | ||
|
|
4176a8c68b | ||
|
|
0557d1ee2d | ||
|
|
eed8af9a0a | ||
|
|
1b881c4f60 | ||
|
|
770488dbd3 | ||
| 3f69081d32 | |||
| 7033db7dc4 | |||
| a1a16b4a22 | |||
| 98e92ee0fe | |||
| 29b55f36cf | |||
| 061e88c9fe | |||
| 66f204b1f2 | |||
| c92b1b8c3b |
@@ -1,13 +1,56 @@
|
|||||||
# simple HTTP client
|
# simple HTTP client
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
It provides simple routine to perform http requests, and get response.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
* Eiffel cURL library
|
* One of the following
|
||||||
* cURL dynamic libraries in the PATH or the current directory (.dll or .so)
|
- 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
|
## 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
|
## Examples
|
||||||
|
* See the tests/test-safe.ecf project to see how to use.
|
||||||
|
* Examples will come in the future.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
<?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">
|
<target name="http_client">
|
||||||
<root all_classes="true"/>
|
<root all_classes="true"/>
|
||||||
<file_rule>
|
<file_rule>
|
||||||
@@ -7,12 +7,78 @@
|
|||||||
<exclude>/EIFGENs$</exclude>
|
<exclude>/EIFGENs$</exclude>
|
||||||
<exclude>/.svn$</exclude>
|
<exclude>/.svn$</exclude>
|
||||||
</file_rule>
|
</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>
|
</option>
|
||||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
<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="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" 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>
|
</target>
|
||||||
</system>
|
</system>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
<?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">
|
<target name="http_client">
|
||||||
<root all_classes="true"/>
|
<root all_classes="true"/>
|
||||||
<file_rule>
|
<file_rule>
|
||||||
@@ -9,10 +9,76 @@
|
|||||||
</file_rule>
|
</file_rule>
|
||||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
<option warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||||
</option>
|
</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="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
|
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||||||
<library name="encoder" location="../../text/encoder/encoder.ecf"/>
|
<condition>
|
||||||
<cluster name="src" location=".\src\" recursive="true"/>
|
<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>
|
</target>
|
||||||
</system>
|
</system>
|
||||||
|
|||||||
23
library/network/http_client/libcurl_http_client-safe.ecf
Normal file
23
library/network/http_client/libcurl_http_client-safe.ecf
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="libcurl_http_client" uuid="FA686EE7-D01D-43A3-8E95-A00120658040" library_target="libcurl_http_client">
|
||||||
|
<target name="libcurl_http_client">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option debug="false" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||||
|
</option>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
|
||||||
|
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||||
|
<library name="http" location="..\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||||
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||||
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
|
||||||
|
<cluster name="default_libcurl" location="$|default\libcurl\"/>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
23
library/network/http_client/libcurl_http_client.ecf
Normal file
23
library/network/http_client/libcurl_http_client.ecf
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="libcurl_http_client" uuid="FA686EE7-D01D-43A3-8E95-A00120658040" library_target="libcurl_http_client">
|
||||||
|
<target name="libcurl_http_client">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||||
|
</option>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||||
|
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
|
||||||
|
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||||
|
<library name="http" location="..\protocol\http\http.ecf"/>
|
||||||
|
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||||
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||||
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
|
||||||
|
<cluster name="default_libcurl" location="$|default\libcurl\"/>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
40
library/network/http_client/net_http_client-safe.ecf
Normal file
40
library/network/http_client/net_http_client-safe.ecf
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="7897B317-7AD3-44E4-A933-0544A169AB1B" library_target="net_http_client">
|
||||||
|
<target name="net_http_client">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option debug="false" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||||
|
</option>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||||
|
<library name="http" location="..\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
|
||||||
|
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||||
|
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
|
||||||
|
<condition>
|
||||||
|
<custom name="netssl_http_client_enabled" value="true"/>
|
||||||
|
</condition>
|
||||||
|
</library>
|
||||||
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||||
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="spec_net" location="$|spec\net\">
|
||||||
|
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
||||||
|
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||||
|
<condition>
|
||||||
|
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||||
|
<condition>
|
||||||
|
<custom name="netssl_http_client_enabled" value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="default_net" location="$|default\net\"/>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
40
library/network/http_client/net_http_client.ecf
Normal file
40
library/network/http_client/net_http_client.ecf
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="7897B317-7AD3-44E4-A933-0544A169AB1B" library_target="net_http_client">
|
||||||
|
<target name="net_http_client">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||||
|
</option>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||||
|
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||||
|
<library name="http" location="..\protocol\http\http.ecf"/>
|
||||||
|
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
|
||||||
|
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||||
|
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||||||
|
<condition>
|
||||||
|
<custom name="netssl_http_client_enabled" value="true"/>
|
||||||
|
</condition>
|
||||||
|
</library>
|
||||||
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||||
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="spec_net" location="$|spec\net\">
|
||||||
|
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
||||||
|
<cluster name="net_ssl_disabled" location="$|no_ssl\">
|
||||||
|
<condition>
|
||||||
|
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="net_ssl_enabled" location="$|ssl\">
|
||||||
|
<condition>
|
||||||
|
<custom name="netssl_http_client_enabled" value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="default_net" location="$|default\net\"/>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT.
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
DEFAULT_HTTP_CLIENT
|
||||||
|
|
||||||
|
inherit
|
||||||
|
LIBCURL_HTTP_CLIENT
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT or NET_HTTP_CLIENT.
|
||||||
|
|
||||||
|
The preference goes to libcurl implementation for now,
|
||||||
|
since the net implementation has currently less functionalities.
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
DEFAULT_HTTP_CLIENT
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
-- Create a new session using `a_base_url'.
|
||||||
|
local
|
||||||
|
libcurl: LIBCURL_HTTP_CLIENT
|
||||||
|
net: NET_HTTP_CLIENT
|
||||||
|
do
|
||||||
|
--| For now, try libcurl first, and then net
|
||||||
|
--| the reason is the net implementation is still in progress.
|
||||||
|
create libcurl
|
||||||
|
Result := libcurl.new_session (a_base_url)
|
||||||
|
if not Result.is_available then
|
||||||
|
create net
|
||||||
|
Result := net.new_session (a_base_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Default HTTP_CLIENT based on NET_HTTP_CLIENT.
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
DEFAULT_HTTP_CLIENT
|
||||||
|
|
||||||
|
inherit
|
||||||
|
NET_HTTP_CLIENT
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Default HTTP_CLIENT based on NULL_HTTP_CLIENT.
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
DEFAULT_HTTP_CLIENT
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
-- Create a new session using `a_base_url'.
|
||||||
|
do
|
||||||
|
create {NULL_HTTP_CLIENT_SESSION} Result.make (a_base_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -9,7 +9,7 @@ note
|
|||||||
deferred class
|
deferred class
|
||||||
HTTP_CLIENT
|
HTTP_CLIENT
|
||||||
|
|
||||||
feature -- Status
|
feature -- Access
|
||||||
|
|
||||||
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
-- Create a new session using `a_base_url'.
|
-- Create a new session using `a_base_url'.
|
||||||
@@ -17,7 +17,7 @@ feature -- Status
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -14,24 +14,41 @@ inherit
|
|||||||
|
|
||||||
feature {NONE} -- Initialization
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
make (a_url: READABLE_STRING_8; a_session: like session; ctx: like context)
|
make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context)
|
||||||
-- Initialize `Current'.
|
-- 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
|
do
|
||||||
|
request_method := a_request_method
|
||||||
session := a_session
|
session := a_session
|
||||||
url := a_url
|
initialize (a_url, ctx)
|
||||||
headers := session.headers.twin
|
|
||||||
if ctx /= Void then
|
|
||||||
context := ctx
|
|
||||||
import (ctx)
|
|
||||||
end
|
|
||||||
ensure
|
ensure
|
||||||
context_set: context = ctx
|
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
|
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
|
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: HTTP_CLIENT_SESSION
|
||||||
|
-- Session related to Current request.
|
||||||
|
-- It provides a few parameters related to session.
|
||||||
|
|
||||||
context: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
context: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
-- Potential additional parameters for this specific request.
|
||||||
|
|
||||||
feature -- Status report
|
feature -- Status report
|
||||||
|
|
||||||
@@ -44,16 +61,27 @@ feature -- Status report
|
|||||||
feature -- Access
|
feature -- Access
|
||||||
|
|
||||||
request_method: READABLE_STRING_8
|
request_method: READABLE_STRING_8
|
||||||
deferred
|
-- Request method associated with Current request.
|
||||||
end
|
|
||||||
|
|
||||||
url: READABLE_STRING_8
|
url: READABLE_STRING_8
|
||||||
|
-- URL associated with current request.
|
||||||
|
|
||||||
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
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
|
feature {HTTP_CLIENT_SESSION} -- Execution
|
||||||
|
|
||||||
import (ctx: HTTP_CLIENT_REQUEST_CONTEXT)
|
import (ctx: HTTP_CLIENT_REQUEST_CONTEXT)
|
||||||
|
-- Import `ctx' parameters.
|
||||||
local
|
local
|
||||||
l_headers: like headers
|
l_headers: like headers
|
||||||
do
|
do
|
||||||
@@ -67,10 +95,6 @@ feature {HTTP_CLIENT_SESSION} -- Execution
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
execute: HTTP_CLIENT_RESPONSE
|
|
||||||
deferred
|
|
||||||
end
|
|
||||||
|
|
||||||
feature -- Authentication
|
feature -- Authentication
|
||||||
|
|
||||||
auth_type: STRING
|
auth_type: STRING
|
||||||
@@ -88,21 +112,26 @@ feature -- Authentication
|
|||||||
end
|
end
|
||||||
|
|
||||||
username: detachable READABLE_STRING_32
|
username: detachable READABLE_STRING_32
|
||||||
|
-- Username specified for the `session'.
|
||||||
do
|
do
|
||||||
Result := session.username
|
Result := session.username
|
||||||
end
|
end
|
||||||
|
|
||||||
password: detachable READABLE_STRING_32
|
password: detachable READABLE_STRING_32
|
||||||
|
-- Password specified for the `session'.
|
||||||
do
|
do
|
||||||
Result := session.password
|
Result := session.password
|
||||||
end
|
end
|
||||||
|
|
||||||
credentials: detachable READABLE_STRING_32
|
credentials: detachable READABLE_STRING_32
|
||||||
|
-- Credentials specified for the `session'.
|
||||||
|
--| Usually `username':`password'
|
||||||
do
|
do
|
||||||
Result := session.credentials
|
Result := session.credentials
|
||||||
end
|
end
|
||||||
|
|
||||||
proxy: detachable TUPLE [host: READABLE_STRING_8; port: INTEGER]
|
proxy: detachable TUPLE [host: READABLE_STRING_8; port: INTEGER]
|
||||||
|
-- Optional proxy settings.
|
||||||
do
|
do
|
||||||
Result := session.proxy
|
Result := session.proxy
|
||||||
end
|
end
|
||||||
@@ -220,7 +249,7 @@ feature {NONE} -- Utilities: encoding
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -81,7 +81,10 @@ feature -- Access
|
|||||||
-- Optional output file to get downloaded content and header
|
-- Optional output file to get downloaded content and header
|
||||||
|
|
||||||
output_content_file: detachable FILE
|
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
|
feature -- Status report
|
||||||
|
|
||||||
@@ -209,6 +212,17 @@ feature -- Element change
|
|||||||
output_content_file := f
|
output_content_file := f
|
||||||
end
|
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
|
feature -- Status setting
|
||||||
|
|
||||||
set_proxy (a_host: detachable READABLE_STRING_8; a_port: INTEGER)
|
set_proxy (a_host: detachable READABLE_STRING_8; a_port: INTEGER)
|
||||||
@@ -264,7 +278,7 @@ feature {NONE} -- Implementation
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ create
|
|||||||
|
|
||||||
feature {NONE} -- Initialization
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
make (a_url: like url)
|
make (a_url: READABLE_STRING_8)
|
||||||
-- Initialize `Current'.
|
-- Initialize `Current'.
|
||||||
do
|
do
|
||||||
--| Default values
|
--| Default values
|
||||||
@@ -54,12 +54,23 @@ feature -- Access
|
|||||||
|
|
||||||
status_line: detachable READABLE_STRING_8
|
status_line: detachable READABLE_STRING_8
|
||||||
|
|
||||||
|
http_version: detachable READABLE_STRING_8
|
||||||
|
-- http version associated with `status_line'.
|
||||||
|
|
||||||
raw_header: READABLE_STRING_8
|
raw_header: READABLE_STRING_8
|
||||||
-- Raw http header of the response.
|
-- 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]]
|
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.
|
-- 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 (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||||
-- Header entry value related to `a_name'
|
-- Header entry value related to `a_name'
|
||||||
-- if multiple entries, just concatenate them using comma character
|
-- if multiple entries, just concatenate them using comma character
|
||||||
@@ -92,6 +103,24 @@ feature -- Access
|
|||||||
Result := s
|
Result := s
|
||||||
end
|
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]]
|
headers: LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
|
||||||
-- Computed table of http headers of the response.
|
-- 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
|
--| We use a LIST since one might have multiple message-header fields with the same field-name
|
||||||
@@ -115,7 +144,7 @@ feature -- Access
|
|||||||
loop
|
loop
|
||||||
l_start := pos
|
l_start := pos
|
||||||
--| Left justify
|
--| 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
|
l_start := l_start + 1
|
||||||
end
|
end
|
||||||
pos := h.index_of ('%N', l_start)
|
pos := h.index_of ('%N', l_start)
|
||||||
@@ -129,7 +158,7 @@ feature -- Access
|
|||||||
end
|
end
|
||||||
if l_end > 0 then
|
if l_end > 0 then
|
||||||
--| Right justify
|
--| 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
|
l_end := l_end - 1
|
||||||
end
|
end
|
||||||
c := h.index_of (':', l_start)
|
c := h.index_of (':', l_start)
|
||||||
@@ -188,17 +217,72 @@ feature -- Access
|
|||||||
end
|
end
|
||||||
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
|
feature -- Change
|
||||||
|
|
||||||
|
set_http_version (v: like http_version)
|
||||||
|
-- Set `http_version' to `v'.
|
||||||
|
do
|
||||||
|
http_version := v
|
||||||
|
end
|
||||||
|
|
||||||
set_status (s: INTEGER)
|
set_status (s: INTEGER)
|
||||||
-- Set response `status' code to `s'
|
-- Set response `status' code to `s'
|
||||||
do
|
do
|
||||||
status := s
|
status := s
|
||||||
end
|
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)
|
set_response_message (a_source: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT)
|
||||||
-- Parse `a_source' response message
|
-- 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
|
--| ctx is the context associated with the request
|
||||||
--| it might be useful to deal with redirection customization...
|
--| it might be useful to deal with redirection customization...
|
||||||
local
|
local
|
||||||
@@ -234,7 +318,7 @@ feature -- Change
|
|||||||
|
|
||||||
j := i + 2
|
j := i + 2
|
||||||
pos := j
|
pos := j
|
||||||
status_line := l_status_line
|
set_status_line (l_status_line)
|
||||||
set_raw_header (h)
|
set_raw_header (h)
|
||||||
|
|
||||||
-- libcURL does not cache redirection content.
|
-- libcURL does not cache redirection content.
|
||||||
@@ -277,10 +361,23 @@ feature -- Change
|
|||||||
|
|
||||||
set_raw_header (h: READABLE_STRING_8)
|
set_raw_header (h: READABLE_STRING_8)
|
||||||
-- Set http header `raw_header' to `h'
|
-- Set http header `raw_header' to `h'
|
||||||
|
local
|
||||||
|
i: INTEGER
|
||||||
|
s: STRING_8
|
||||||
do
|
do
|
||||||
raw_header := h
|
raw_header := h
|
||||||
--| Reset internal headers
|
--| Reset internal headers
|
||||||
internal_headers := Void
|
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
|
end
|
||||||
|
|
||||||
add_redirection (s: detachable READABLE_STRING_8; h: READABLE_STRING_8; a_body: detachable READABLE_STRING_8)
|
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
|
-- Internal cached value for the headers
|
||||||
|
|
||||||
;note
|
;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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -50,6 +50,14 @@ feature {NONE} -- Initialization
|
|||||||
|
|
||||||
feature -- Basic operation
|
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 (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): STRING_8
|
||||||
-- Url computed from Current and `ctx' data.
|
-- Url computed from Current and `ctx' data.
|
||||||
local
|
local
|
||||||
@@ -77,6 +85,61 @@ feature -- Basic operation
|
|||||||
end
|
end
|
||||||
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
|
feature -- Custom
|
||||||
|
|
||||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
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
|
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'.
|
-- Response for GET request based on Current, `a_path' and `ctx'.
|
||||||
|
require
|
||||||
|
is_available: is_available
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
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'.
|
-- Response for HEAD request based on Current, `a_path' and `ctx'.
|
||||||
|
require
|
||||||
|
is_available: is_available
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
post (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
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'
|
-- Response for POST request based on Current, `a_path' and `ctx'
|
||||||
-- with input `data'
|
-- with input `data'
|
||||||
|
require
|
||||||
|
is_available: is_available
|
||||||
deferred
|
deferred
|
||||||
end
|
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
|
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'
|
-- Response for PATCH request based on Current, `a_path' and `ctx'
|
||||||
-- with input `data'
|
-- with input `data'
|
||||||
|
require
|
||||||
|
is_available: is_available
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
patch_file (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
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'
|
-- Response for PATCH request based on Current, `a_path' and `ctx'
|
||||||
-- with uploaded data file `fn'
|
-- with uploaded data file `fn'
|
||||||
|
require
|
||||||
|
is_available: is_available
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
put (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
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'
|
-- Response for PUT request based on Current, `a_path' and `ctx'
|
||||||
-- with input `data'
|
-- with input `data'
|
||||||
|
require
|
||||||
|
is_available: is_available
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
put_file (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
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'
|
-- Response for PUT request based on Current, `a_path' and `ctx'
|
||||||
-- with uploaded file `fn'
|
-- with uploaded file `fn'
|
||||||
|
require
|
||||||
|
is_available: is_available
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
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'
|
-- Response for DELETE request based on Current, `a_path' and `ctx'
|
||||||
|
require
|
||||||
|
is_available: is_available
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -185,6 +264,9 @@ feature -- Access
|
|||||||
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
||||||
-- Headers common to any request created by Current session.
|
-- Headers common to any request created by Current session.
|
||||||
|
|
||||||
|
cookie: detachable READABLE_STRING_8
|
||||||
|
-- Cookie for the current base_url
|
||||||
|
|
||||||
feature -- Authentication
|
feature -- Authentication
|
||||||
|
|
||||||
auth_type: STRING
|
auth_type: STRING
|
||||||
@@ -194,11 +276,15 @@ feature -- Authentication
|
|||||||
auth_type_id: INTEGER
|
auth_type_id: INTEGER
|
||||||
-- See {HTTP_CLIENT_CONSTANTS}.Auth_type_*
|
-- See {HTTP_CLIENT_CONSTANTS}.Auth_type_*
|
||||||
|
|
||||||
username,
|
username: detachable READABLE_STRING_32
|
||||||
|
-- Associated optional username value.
|
||||||
|
|
||||||
password: detachable READABLE_STRING_32
|
password: detachable READABLE_STRING_32
|
||||||
|
-- Associated optional password value.
|
||||||
|
|
||||||
credentials: detachable READABLE_STRING_32
|
credentials: detachable READABLE_STRING_32
|
||||||
|
-- Associated optional credentials value.
|
||||||
|
-- Computed as `username':`password'.
|
||||||
|
|
||||||
feature -- Status setting
|
feature -- Status setting
|
||||||
|
|
||||||
@@ -212,6 +298,12 @@ feature -- Element change
|
|||||||
set_base_url (u: like base_url)
|
set_base_url (u: like base_url)
|
||||||
do
|
do
|
||||||
base_url := u
|
base_url := u
|
||||||
|
cookie := Void
|
||||||
|
end
|
||||||
|
|
||||||
|
set_cookie (a_cookie: detachable READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
cookie := a_cookie
|
||||||
end
|
end
|
||||||
|
|
||||||
set_timeout (n_seconds: like timeout)
|
set_timeout (n_seconds: like timeout)
|
||||||
@@ -244,12 +336,26 @@ feature -- Element change
|
|||||||
headers.prune (k)
|
headers.prune (k)
|
||||||
end
|
end
|
||||||
|
|
||||||
set_credentials (u: like username; p: like password)
|
set_credentials (u,p: detachable READABLE_STRING_GENERAL)
|
||||||
|
local
|
||||||
|
s: STRING_32
|
||||||
do
|
do
|
||||||
username := u
|
if u = Void then
|
||||||
password := p
|
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
|
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
|
else
|
||||||
credentials := Void
|
credentials := Void
|
||||||
end
|
end
|
||||||
@@ -300,7 +406,7 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
note
|
note
|
||||||
description : "[
|
description : "[
|
||||||
Specific implementation of HTTP_CLIENT based on Eiffel cURL library
|
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$"
|
author : "$Author$"
|
||||||
date : "$Date$"
|
date : "$Date$"
|
||||||
@@ -13,6 +16,7 @@ inherit
|
|||||||
HTTP_CLIENT
|
HTTP_CLIENT
|
||||||
|
|
||||||
create
|
create
|
||||||
|
default_create,
|
||||||
make
|
make
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
feature {NONE} -- Initialization
|
||||||
@@ -20,6 +24,7 @@ feature {NONE} -- Initialization
|
|||||||
make
|
make
|
||||||
-- Initialize `Current'.
|
-- Initialize `Current'.
|
||||||
do
|
do
|
||||||
|
default_create
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Status
|
feature -- Status
|
||||||
@@ -30,7 +35,7 @@ feature -- Status
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
note
|
note
|
||||||
description: "[
|
description: "[
|
||||||
Specific implementation of HTTP_CLIENT_REQUEST based on Eiffel cURL library
|
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$"
|
date: "$Date$"
|
||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
@@ -10,9 +13,8 @@ class
|
|||||||
|
|
||||||
inherit
|
inherit
|
||||||
HTTP_CLIENT_REQUEST
|
HTTP_CLIENT_REQUEST
|
||||||
rename
|
|
||||||
make as make_request
|
|
||||||
redefine
|
redefine
|
||||||
|
make,
|
||||||
session
|
session
|
||||||
end
|
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)
|
make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context)
|
||||||
do
|
do
|
||||||
make_request (a_url, a_session, ctx)
|
Precursor (a_url, a_request_method, a_session, ctx)
|
||||||
request_method := a_request_method
|
|
||||||
apply_workaround
|
apply_workaround
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -38,13 +39,10 @@ feature {NONE} -- Initialization
|
|||||||
|
|
||||||
session: LIBCURL_HTTP_CLIENT_SESSION
|
session: LIBCURL_HTTP_CLIENT_SESSION
|
||||||
|
|
||||||
feature -- Access
|
|
||||||
|
|
||||||
request_method: READABLE_STRING_8
|
|
||||||
|
|
||||||
feature -- Execution
|
feature -- Execution
|
||||||
|
|
||||||
execute: HTTP_CLIENT_RESPONSE
|
response: HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
local
|
local
|
||||||
l_result: INTEGER
|
l_result: INTEGER
|
||||||
l_curl_string: detachable CURL_STRING
|
l_curl_string: detachable CURL_STRING
|
||||||
@@ -63,6 +61,8 @@ feature -- Execution
|
|||||||
l_upload_data: detachable READABLE_STRING_8
|
l_upload_data: detachable READABLE_STRING_8
|
||||||
l_upload_filename: detachable READABLE_STRING_GENERAL
|
l_upload_filename: detachable READABLE_STRING_GENERAL
|
||||||
l_headers: like headers
|
l_headers: like headers
|
||||||
|
l_is_http_1_0: BOOLEAN
|
||||||
|
l_uri: URI
|
||||||
do
|
do
|
||||||
if not retried then
|
if not retried then
|
||||||
curl := session.curl
|
curl := session.curl
|
||||||
@@ -72,6 +72,10 @@ feature -- Execution
|
|||||||
|
|
||||||
ctx := context
|
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
|
--| Configure cURL session
|
||||||
initialize_curl_session (ctx, curl, curl_easy, curl_handle)
|
initialize_curl_session (ctx, curl, curl_easy, curl_handle)
|
||||||
|
|
||||||
@@ -81,12 +85,48 @@ feature -- Execution
|
|||||||
append_parameters_to_url (ctx.query_parameters, l_url)
|
append_parameters_to_url (ctx.query_parameters, l_url)
|
||||||
end
|
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")
|
debug ("service")
|
||||||
io.put_string ("SERVICE: " + l_url)
|
io.put_string ("SERVICE: " + l_url)
|
||||||
io.put_new_line
|
io.put_new_line
|
||||||
end
|
end
|
||||||
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_url, l_url)
|
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
|
l_headers := headers
|
||||||
|
|
||||||
-- Context
|
-- Context
|
||||||
@@ -241,6 +281,10 @@ feature -- Execution
|
|||||||
Result.status := response_status_code (curl_easy, curl_handle)
|
Result.status := response_status_code (curl_easy, curl_handle)
|
||||||
if l_curl_string /= Void then
|
if l_curl_string /= Void then
|
||||||
Result.set_response_message (l_curl_string.string, ctx)
|
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
|
end
|
||||||
else
|
else
|
||||||
Result.set_error_message ("Error: cURL Error[" + l_result.out + "]")
|
Result.set_error_message ("Error: cURL Error[" + l_result.out + "]")
|
||||||
@@ -390,7 +434,7 @@ feature {NONE} -- Implementation
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
note
|
note
|
||||||
description: "[
|
description: "[
|
||||||
Specific implementation of HTTP_CLIENT_SESSION based on Eiffel cURL library
|
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$"
|
date: "$Date$"
|
||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
@@ -34,19 +37,22 @@ feature -- Status report
|
|||||||
feature -- Custom
|
feature -- Custom
|
||||||
|
|
||||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
local
|
local
|
||||||
req: HTTP_CLIENT_REQUEST
|
req: HTTP_CLIENT_REQUEST
|
||||||
do
|
do
|
||||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||||
Result := req.execute
|
Result := req.response
|
||||||
end
|
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
|
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
|
do
|
||||||
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
|
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
|
||||||
end
|
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
|
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
|
do
|
||||||
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||||
end
|
end
|
||||||
@@ -54,46 +60,55 @@ feature -- Custom
|
|||||||
feature -- Helper
|
feature -- Helper
|
||||||
|
|
||||||
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
Result := custom ("GET", a_path, ctx)
|
Result := custom ("GET", a_path, ctx)
|
||||||
end
|
end
|
||||||
|
|
||||||
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
Result := custom ("HEAD", a_path, ctx)
|
Result := custom ("HEAD", a_path, ctx)
|
||||||
end
|
end
|
||||||
|
|
||||||
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
|
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
|
||||||
end
|
end
|
||||||
|
|
||||||
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
|
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
|
||||||
end
|
end
|
||||||
|
|
||||||
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
|
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
|
||||||
end
|
end
|
||||||
|
|
||||||
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
|
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
|
||||||
end
|
end
|
||||||
|
|
||||||
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
|
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
|
||||||
end
|
end
|
||||||
|
|
||||||
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
|
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
|
||||||
end
|
end
|
||||||
|
|
||||||
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
Result := custom ("DELETE", a_path, ctx)
|
Result := custom ("DELETE", a_path, ctx)
|
||||||
end
|
end
|
||||||
@@ -140,7 +155,7 @@ feature {NONE} -- Implementation
|
|||||||
end
|
end
|
||||||
|
|
||||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
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
|
if f /= Void then
|
||||||
f.delete
|
f.delete
|
||||||
@@ -161,7 +176,7 @@ feature {LIBCURL_HTTP_CLIENT_REQUEST} -- Curl implementation
|
|||||||
|
|
||||||
|
|
||||||
;note
|
;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)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
source: "[
|
source: "[
|
||||||
Eiffel Software
|
Eiffel Software
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
note
|
||||||
|
description: "Socket connection information, used for peristent connection implementation."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
NET_HTTP_CLIENT_CONNECTION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_socket: NETWORK_STREAM_SOCKET; a_host: READABLE_STRING_GENERAL; a_port: INTEGER)
|
||||||
|
do
|
||||||
|
socket := a_socket
|
||||||
|
host := a_host
|
||||||
|
port := a_port
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
socket: NETWORK_STREAM_SOCKET
|
||||||
|
-- Persistent connection socket.
|
||||||
|
|
||||||
|
host: READABLE_STRING_GENERAL
|
||||||
|
-- Host used for this connection.
|
||||||
|
|
||||||
|
port: INTEGER
|
||||||
|
-- Port used for this connection.
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
is_reusable (a_host: READABLE_STRING_GENERAL; a_port: INTEGER): BOOLEAN
|
||||||
|
-- Is Current connection reusable for new connection `a_host:a_port'?
|
||||||
|
do
|
||||||
|
if a_host.same_string (host) and port = a_port then
|
||||||
|
Result := socket.is_connected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
34
library/network/http_client/src/spec/net/net_http_client.e
Normal file
34
library/network/http_client/src/spec/net/net_http_client.e
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
note
|
||||||
|
description : "[
|
||||||
|
Specific implementation of HTTP_CLIENT based on Eiffel NET library
|
||||||
|
|
||||||
|
WARNING: this is work in progress
|
||||||
|
]"
|
||||||
|
author : "$Author$"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
NET_HTTP_CLIENT
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT
|
||||||
|
|
||||||
|
feature -- Status
|
||||||
|
|
||||||
|
new_session (a_base_url: READABLE_STRING_8): NET_HTTP_CLIENT_SESSION
|
||||||
|
do
|
||||||
|
create Result.make (a_base_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,739 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Specific implementation of HTTP_CLIENT_REQUEST based on Eiffel NET library
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
NET_HTTP_CLIENT_REQUEST
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT_REQUEST
|
||||||
|
redefine
|
||||||
|
session
|
||||||
|
end
|
||||||
|
|
||||||
|
TRANSFER_COMMAND_CONSTANTS
|
||||||
|
|
||||||
|
REFACTORING_HELPER
|
||||||
|
|
||||||
|
SHARED_EXECUTION_ENVIRONMENT
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Internal
|
||||||
|
|
||||||
|
session: NET_HTTP_CLIENT_SESSION
|
||||||
|
net_http_client_version: STRING = "0.1"
|
||||||
|
|
||||||
|
session_socket (a_host: READABLE_STRING_8; a_port: INTEGER; a_is_https: BOOLEAN; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): NETWORK_STREAM_SOCKET
|
||||||
|
-- Session socket to use for connection.
|
||||||
|
-- Eventually reuse the persistent connection if any.
|
||||||
|
local
|
||||||
|
l_socket: detachable NETWORK_STREAM_SOCKET
|
||||||
|
do
|
||||||
|
if
|
||||||
|
attached session.persistent_connection as l_persistent_connection and then
|
||||||
|
l_persistent_connection.is_reusable (a_host, a_port)
|
||||||
|
then
|
||||||
|
l_socket := l_persistent_connection.socket
|
||||||
|
if a_is_https then
|
||||||
|
if attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
|
||||||
|
Result := l_ssl_socket
|
||||||
|
else
|
||||||
|
l_socket := Void
|
||||||
|
end
|
||||||
|
elseif attached {SSL_NETWORK_STREAM_SOCKET} l_socket as l_ssl_socket then
|
||||||
|
l_socket := Void
|
||||||
|
end
|
||||||
|
if l_socket /= Void and then not l_socket.is_connected then
|
||||||
|
-- Reset persistent connection
|
||||||
|
l_socket := Void
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if l_socket /= Void then
|
||||||
|
-- Reuse persistent connection.
|
||||||
|
Result := l_socket
|
||||||
|
else
|
||||||
|
session.set_persistent_connection (Void)
|
||||||
|
if a_is_https then
|
||||||
|
create {SSL_NETWORK_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
|
||||||
|
else
|
||||||
|
create Result.make_client_by_port (a_port, a_host)
|
||||||
|
end
|
||||||
|
Result.set_connect_timeout (connect_timeout)
|
||||||
|
Result.set_timeout (timeout)
|
||||||
|
Result.connect
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
response: HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
local
|
||||||
|
redirection_response: detachable like response
|
||||||
|
l_uri: URI
|
||||||
|
l_header_key: READABLE_STRING_8
|
||||||
|
l_host: READABLE_STRING_8
|
||||||
|
l_cookie: detachable READABLE_STRING_8
|
||||||
|
l_request_uri: STRING
|
||||||
|
l_url: HTTP_URL
|
||||||
|
l_socket: NETWORK_STREAM_SOCKET
|
||||||
|
s: STRING
|
||||||
|
l_message: STRING
|
||||||
|
l_content_length: INTEGER
|
||||||
|
l_location: detachable READABLE_STRING_8
|
||||||
|
l_port: INTEGER
|
||||||
|
l_is_https: BOOLEAN
|
||||||
|
l_authorization: HTTP_AUTHORIZATION
|
||||||
|
l_platform: STRING
|
||||||
|
l_upload_data: detachable READABLE_STRING_8
|
||||||
|
l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
|
||||||
|
ctx: like context
|
||||||
|
l_upload_file: detachable RAW_FILE
|
||||||
|
l_upload_filename: detachable READABLE_STRING_GENERAL
|
||||||
|
l_form_string: STRING
|
||||||
|
l_prev_header: READABLE_STRING_8
|
||||||
|
l_boundary: READABLE_STRING_8
|
||||||
|
l_is_http_1_0_request: BOOLEAN
|
||||||
|
l_is_keep_alive: BOOLEAN
|
||||||
|
retried: BOOLEAN
|
||||||
|
do
|
||||||
|
if not retried then
|
||||||
|
ctx := context
|
||||||
|
if ctx /= Void then
|
||||||
|
l_is_http_1_0_request := attached ctx.http_version as l_http_version and then l_http_version.same_string ("HTTP/1.0")
|
||||||
|
end
|
||||||
|
create Result.make (url)
|
||||||
|
|
||||||
|
-- Get URL data
|
||||||
|
l_is_https := url.starts_with_general ("https://")
|
||||||
|
create l_uri.make_from_string (url)
|
||||||
|
l_port := l_uri.port
|
||||||
|
if l_port = 0 then
|
||||||
|
if l_is_https then
|
||||||
|
l_port := 443
|
||||||
|
else
|
||||||
|
l_port := 80
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if attached l_uri.host as h then
|
||||||
|
l_host := h
|
||||||
|
else
|
||||||
|
create l_url.make (url)
|
||||||
|
l_host := l_url.host
|
||||||
|
end
|
||||||
|
|
||||||
|
if attached session.proxy as l_proxy_settings then
|
||||||
|
-- For now, so proxy support.
|
||||||
|
check
|
||||||
|
not_supported: False
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Connect
|
||||||
|
l_socket := session_socket (l_host, l_port, l_is_https, ctx)
|
||||||
|
if l_socket.is_connected then
|
||||||
|
|
||||||
|
create l_form_string.make_empty
|
||||||
|
|
||||||
|
-- add headers for authorization
|
||||||
|
if not headers.has ("Authorization") then
|
||||||
|
if
|
||||||
|
attached username as u_name and
|
||||||
|
attached password as u_pass
|
||||||
|
then
|
||||||
|
create l_authorization.make_basic_auth (u_name, u_pass)
|
||||||
|
if attached l_authorization.http_authorization as auth then
|
||||||
|
headers.extend (auth, "Authorization")
|
||||||
|
end
|
||||||
|
check headers.has_key ("Authorization") end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
create l_request_uri.make_from_string (l_uri.path)
|
||||||
|
if attached l_uri.query as l_query then
|
||||||
|
l_request_uri.append_character ('?')
|
||||||
|
l_request_uri.append (l_query)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- add computed header User-Agent if not yet set.
|
||||||
|
if not headers.has ("User-Agent") then
|
||||||
|
if {PLATFORM}.is_unix then
|
||||||
|
l_platform := "Unix"
|
||||||
|
elseif {PLATFORM}.is_windows then
|
||||||
|
l_platform := "Windows"
|
||||||
|
elseif {PLATFORM}.is_mac then
|
||||||
|
l_platform := "Mac"
|
||||||
|
elseif {PLATFORM}.is_vms then
|
||||||
|
l_platform := "VMS"
|
||||||
|
elseif {PLATFORM}.is_vxworks then
|
||||||
|
l_platform := "VxWorks"
|
||||||
|
else
|
||||||
|
l_platform := "Unknown"
|
||||||
|
end
|
||||||
|
headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle sending data
|
||||||
|
if ctx /= Void then
|
||||||
|
if ctx.has_upload_filename then
|
||||||
|
l_upload_filename := ctx.upload_filename
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctx.has_upload_data then
|
||||||
|
l_upload_data := ctx.upload_data
|
||||||
|
end
|
||||||
|
|
||||||
|
if ctx.has_form_data then
|
||||||
|
l_form_data := ctx.form_parameters
|
||||||
|
if l_upload_data = Void and l_upload_filename = Void then
|
||||||
|
-- Send as form-urlencoded
|
||||||
|
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||||
|
l_upload_data := ctx.form_parameters_to_url_encoded_string
|
||||||
|
headers.force (l_upload_data.count.out, "Content-Length")
|
||||||
|
|
||||||
|
else
|
||||||
|
-- create form using multipart/form-data encoding
|
||||||
|
l_boundary := new_mime_boundary
|
||||||
|
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
|
||||||
|
if l_form_data /= Void then
|
||||||
|
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
|
||||||
|
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif l_upload_data /= Void then
|
||||||
|
check ctx.has_upload_data end
|
||||||
|
if not headers.has ("Content-Type") then
|
||||||
|
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||||
|
end
|
||||||
|
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||||
|
elseif l_upload_filename /= Void then
|
||||||
|
check ctx.has_upload_filename end
|
||||||
|
create l_upload_file.make_with_name (l_upload_filename)
|
||||||
|
if l_upload_file.exists and then l_upload_file.readable then
|
||||||
|
headers.extend (l_upload_file.count.out, "Content-Length")
|
||||||
|
end
|
||||||
|
check l_upload_file /= Void end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- FIXME: check usage of headers and specific header variable.
|
||||||
|
--| only one Cookie: is allowed, so merge multiple into one;
|
||||||
|
--| if Host is in header, use that one.
|
||||||
|
-- Compute Request line.
|
||||||
|
create s.make_from_string (request_method.as_upper)
|
||||||
|
s.append_character (' ')
|
||||||
|
s.append (l_request_uri)
|
||||||
|
s.append_character (' ')
|
||||||
|
if l_is_http_1_0_request then
|
||||||
|
s.append ("HTTP/1.0")
|
||||||
|
else
|
||||||
|
s.append ("HTTP/1.1")
|
||||||
|
end
|
||||||
|
s.append (Http_end_of_header_line)
|
||||||
|
|
||||||
|
-- Compute Header Host:
|
||||||
|
s.append (Http_host_header)
|
||||||
|
s.append (": ")
|
||||||
|
if attached headers [Http_host_header] as h_host then
|
||||||
|
s.append (h_host)
|
||||||
|
else
|
||||||
|
s.append (l_host)
|
||||||
|
end
|
||||||
|
s.append (http_end_of_header_line)
|
||||||
|
if not headers.has ("Connection") then
|
||||||
|
if l_is_http_1_0_request then
|
||||||
|
s.append ("Connection: keep-alive")
|
||||||
|
s.append (http_end_of_header_line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Append the given request headers
|
||||||
|
l_cookie := Void
|
||||||
|
if not headers.is_empty then
|
||||||
|
across
|
||||||
|
headers as ic
|
||||||
|
loop
|
||||||
|
l_header_key := ic.key
|
||||||
|
if l_header_key.same_string_general ("Host") then
|
||||||
|
-- FIXME: already handled elsewhere!
|
||||||
|
elseif l_header_key.same_string_general ("Cookie") then
|
||||||
|
-- FIXME: need cookie merging.
|
||||||
|
l_cookie := ic.item
|
||||||
|
else
|
||||||
|
s.append (ic.key)
|
||||||
|
s.append (": ")
|
||||||
|
s.append (ic.item)
|
||||||
|
s.append (Http_end_of_header_line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Compute Header Cookie: if needed
|
||||||
|
-- Use session cookie
|
||||||
|
if l_cookie = Void then
|
||||||
|
l_cookie := session.cookie
|
||||||
|
else
|
||||||
|
-- Overwrite potential session cookie, if specified by the user.
|
||||||
|
end
|
||||||
|
if l_cookie /= Void then
|
||||||
|
s.append ("Cookie: ")
|
||||||
|
s.append (l_cookie)
|
||||||
|
s.append (http_end_of_header_line)
|
||||||
|
end
|
||||||
|
|
||||||
|
--| End of client header.
|
||||||
|
s.append (Http_end_of_header_line)
|
||||||
|
|
||||||
|
if l_upload_data /= Void then
|
||||||
|
s.append (l_upload_data)
|
||||||
|
s.append (http_end_of_header_line)
|
||||||
|
end
|
||||||
|
|
||||||
|
--| Note that any remaining file to upload will be done directly via the socket
|
||||||
|
--| to optimize memory usage
|
||||||
|
|
||||||
|
|
||||||
|
--|-----------------------------|--
|
||||||
|
--| Request preparation is done |--
|
||||||
|
--|-----------------------------|--
|
||||||
|
|
||||||
|
if l_socket.ready_for_writing then
|
||||||
|
--| Socket is ready for writing, so let's send the request.
|
||||||
|
|
||||||
|
--|-------------------------|--
|
||||||
|
--| Send request |--
|
||||||
|
--|-------------------------|--
|
||||||
|
|
||||||
|
if session.is_header_sent_verbose then
|
||||||
|
log ("> Sending:%N")
|
||||||
|
log (s)
|
||||||
|
end
|
||||||
|
l_socket.put_string (s)
|
||||||
|
--| Send remaining payload data, if needed.
|
||||||
|
if l_upload_file /= Void then
|
||||||
|
-- i.e: not yet processed
|
||||||
|
append_file_content_to_socket (l_upload_file, l_upload_file.count, l_socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
--|-------------------------|--
|
||||||
|
--| Get response. |--
|
||||||
|
--| Get header message |--
|
||||||
|
--|-------------------------|--
|
||||||
|
if is_ready_for_reading (l_socket) then
|
||||||
|
create l_message.make_empty
|
||||||
|
append_socket_header_content_to (Result, l_socket, l_message)
|
||||||
|
if session.is_header_received_verbose then
|
||||||
|
log ("< Receiving:%N")
|
||||||
|
log (l_message)
|
||||||
|
end
|
||||||
|
l_prev_header := Result.raw_header
|
||||||
|
Result.set_raw_header (l_message.string)
|
||||||
|
l_message.append (http_end_of_header_line)
|
||||||
|
|
||||||
|
if not Result.error_occurred then
|
||||||
|
-- Get information from header
|
||||||
|
l_content_length := -1
|
||||||
|
if attached Result.header ("Content-Length") as s_len and then s_len.is_integer then
|
||||||
|
l_content_length := s_len.to_integer
|
||||||
|
end
|
||||||
|
l_location := Result.header ("Location")
|
||||||
|
if attached Result.header ("Set-Cookie") as s_cookies then
|
||||||
|
session.set_cookie (s_cookies)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Keep-alive connection?
|
||||||
|
-- with HTTP/1.1, this is the default, and could be changed by Connection: close
|
||||||
|
-- with HTTP/1.0, it requires "Connection: keep-alive" header line.
|
||||||
|
if attached Result.header ("Connection") as s_connection then
|
||||||
|
l_is_keep_alive := s_connection.same_string ("keep-alive")
|
||||||
|
else
|
||||||
|
l_is_keep_alive := not Result.is_http_1_0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get content if any.
|
||||||
|
append_socket_content_to (Result, l_socket, l_content_length, l_message)
|
||||||
|
-- Restore previous header
|
||||||
|
Result.set_raw_header (l_prev_header)
|
||||||
|
-- Set message
|
||||||
|
Result.set_response_message (l_message, ctx)
|
||||||
|
-- Check status code.
|
||||||
|
check status_coherent: attached Result.status_line as l_status_line implies l_status_line.has_substring (Result.status.out) end
|
||||||
|
|
||||||
|
if l_is_keep_alive then
|
||||||
|
session.set_persistent_connection (create {NET_HTTP_CLIENT_CONNECTION}.make (l_socket, l_host, l_port))
|
||||||
|
else
|
||||||
|
session.set_persistent_connection (Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- follow redirect
|
||||||
|
if l_location /= Void then
|
||||||
|
if Result.redirections_count < max_redirects then
|
||||||
|
initialize (l_location, ctx)
|
||||||
|
redirection_response := response
|
||||||
|
redirection_response.add_redirection (Result.status_line, Result.raw_header, Result.body)
|
||||||
|
Result := redirection_response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not l_is_keep_alive then
|
||||||
|
l_socket.cleanup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: Read Timeout!%N")
|
||||||
|
end
|
||||||
|
Result.set_error_message ("Read Timeout")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: Write Timeout!%N")
|
||||||
|
end
|
||||||
|
Result.set_error_message ("Write Timeout")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: Could not connect!%N")
|
||||||
|
end
|
||||||
|
Result.set_error_message ("Could not connect")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
create Result.make (url)
|
||||||
|
Result.set_error_message ("Error: internal error")
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
retried := True
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Helpers
|
||||||
|
|
||||||
|
log (m: READABLE_STRING_8)
|
||||||
|
-- Output log messages.
|
||||||
|
do
|
||||||
|
io.error.put_string (m)
|
||||||
|
end
|
||||||
|
|
||||||
|
is_ready_for_reading (a_socket: NETWORK_STREAM_SOCKET): BOOLEAN
|
||||||
|
-- Is `a_socket' ready for reading?
|
||||||
|
do
|
||||||
|
Result := a_socket.ready_for_reading
|
||||||
|
end
|
||||||
|
|
||||||
|
form_date_and_uploaded_files_to_mime_string (a_form_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]; a_upload_filename: detachable READABLE_STRING_GENERAL; a_mime_boundary: READABLE_STRING_8): STRING
|
||||||
|
-- Form data and uploaded files converted to mime string.
|
||||||
|
-- TODO: design a proper MIME... component.
|
||||||
|
local
|
||||||
|
l_path: PATH
|
||||||
|
l_mime_type: READABLE_STRING_8
|
||||||
|
l_upload_file: detachable RAW_FILE
|
||||||
|
l_mime_type_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING
|
||||||
|
do
|
||||||
|
create Result.make (100)
|
||||||
|
across
|
||||||
|
a_form_parameters as ic
|
||||||
|
loop
|
||||||
|
Result.append (a_mime_boundary)
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
Result.append ("Content-Disposition: form-data; name=")
|
||||||
|
Result.append_character ('%"')
|
||||||
|
Result.append (string_to_mime_encoded_string (ic.key))
|
||||||
|
Result.append_character ('%"')
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
Result.append (string_to_mime_encoded_string (ic.item))
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
end
|
||||||
|
|
||||||
|
if a_upload_filename /= Void then
|
||||||
|
-- get file extension, otherwise set default
|
||||||
|
create l_mime_type_mapping.make_default
|
||||||
|
create l_path.make_from_string (a_upload_filename)
|
||||||
|
if
|
||||||
|
attached l_path.extension as ext and then
|
||||||
|
attached l_mime_type_mapping.mime_type (ext) as l_mt
|
||||||
|
then
|
||||||
|
l_mime_type := l_mt
|
||||||
|
else
|
||||||
|
l_mime_type := "application/octet-stream"
|
||||||
|
end
|
||||||
|
|
||||||
|
Result.append (a_mime_boundary)
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
Result.append ("Content-Disposition: form-data; name=%"")
|
||||||
|
Result.append (string_to_mime_encoded_string (a_upload_filename))
|
||||||
|
Result.append_character ('%"')
|
||||||
|
Result.append ("; filename=%"")
|
||||||
|
Result.append (string_to_mime_encoded_string (a_upload_filename))
|
||||||
|
Result.append_character ('%"')
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
Result.append ("Content-Type: ")
|
||||||
|
Result.append (l_mime_type)
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
|
||||||
|
create l_upload_file.make_with_path (l_path)
|
||||||
|
if l_upload_file.exists and then l_upload_file.is_access_readable then
|
||||||
|
append_file_content_to (l_upload_file, l_upload_file.count, Result)
|
||||||
|
-- Reset l_upload_file to Void, since the related content is already processed.
|
||||||
|
l_upload_file := Void
|
||||||
|
end
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
end
|
||||||
|
Result.append (a_mime_boundary)
|
||||||
|
Result.append ("--") --| end
|
||||||
|
end
|
||||||
|
|
||||||
|
string_to_mime_encoded_string (s: READABLE_STRING_GENERAL): STRING
|
||||||
|
-- Encoded unicode string for mime value.
|
||||||
|
-- For instance uploaded filename, or form data key or values.
|
||||||
|
local
|
||||||
|
utf: UTF_CONVERTER
|
||||||
|
do
|
||||||
|
-- FIXME: find the proper encoding!
|
||||||
|
Result := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: NETWORK_STREAM_SOCKET)
|
||||||
|
-- Append `a_file' content to `a_output'.
|
||||||
|
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||||
|
require
|
||||||
|
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||||
|
local
|
||||||
|
l_was_open: BOOLEAN
|
||||||
|
l_count: INTEGER
|
||||||
|
do
|
||||||
|
if a_len >= 0 then
|
||||||
|
l_count := a_len
|
||||||
|
else
|
||||||
|
l_count := a_file.count
|
||||||
|
end
|
||||||
|
if l_count > 0 then
|
||||||
|
l_was_open := a_file.is_open_read
|
||||||
|
if a_file.is_open_read then
|
||||||
|
l_was_open := True
|
||||||
|
else
|
||||||
|
a_file.open_read
|
||||||
|
end
|
||||||
|
from
|
||||||
|
until
|
||||||
|
l_count = 0 or a_file.exhausted
|
||||||
|
loop
|
||||||
|
a_file.read_stream_thread_aware (l_count.min (2_048))
|
||||||
|
a_output.put_string (a_file.last_string)
|
||||||
|
l_count := l_count - a_file.bytes_read
|
||||||
|
end
|
||||||
|
if not l_was_open then
|
||||||
|
a_file.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append_file_content_to (a_file: FILE; a_len: INTEGER; a_output: STRING)
|
||||||
|
-- Append `a_file' content to `a_output'.
|
||||||
|
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||||
|
require
|
||||||
|
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||||
|
local
|
||||||
|
l_was_open: BOOLEAN
|
||||||
|
l_count: INTEGER
|
||||||
|
do
|
||||||
|
if a_len >= 0 then
|
||||||
|
l_count := a_len
|
||||||
|
else
|
||||||
|
l_count := a_file.count
|
||||||
|
end
|
||||||
|
if l_count > 0 then
|
||||||
|
l_was_open := a_file.is_open_read
|
||||||
|
if a_file.is_open_read then
|
||||||
|
l_was_open := True
|
||||||
|
else
|
||||||
|
a_file.open_read
|
||||||
|
end
|
||||||
|
from
|
||||||
|
|
||||||
|
until
|
||||||
|
l_count = 0 or a_file.exhausted
|
||||||
|
loop
|
||||||
|
a_file.read_stream_thread_aware (l_count.min (2_048))
|
||||||
|
a_output.append (a_file.last_string)
|
||||||
|
l_count := l_count - a_file.bytes_read
|
||||||
|
end
|
||||||
|
if not l_was_open then
|
||||||
|
a_file.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append_socket_header_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
|
||||||
|
-- Get header from `a_socket' into `a_output'.
|
||||||
|
local
|
||||||
|
s: READABLE_STRING_8
|
||||||
|
do
|
||||||
|
from
|
||||||
|
s := ""
|
||||||
|
until
|
||||||
|
s.same_string ("%R") or not a_socket.readable or a_response.error_occurred
|
||||||
|
loop
|
||||||
|
a_socket.read_line_thread_aware
|
||||||
|
s := a_socket.last_string
|
||||||
|
if s.is_empty then
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: ERROR: zero byte read when receiving header.%N")
|
||||||
|
end
|
||||||
|
a_response.set_error_message ("Read zero byte, expecting header line")
|
||||||
|
elseif s.same_string ("%R") then
|
||||||
|
-- Reach end of header
|
||||||
|
else
|
||||||
|
a_output.append (s)
|
||||||
|
a_output.append_character ('%N')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_len: INTEGER; a_output: STRING)
|
||||||
|
-- Get content from `a_socket' and append it to `a_output'.
|
||||||
|
-- If `a_len' is negative, try to get as much as possible,
|
||||||
|
-- this is probably HTTP/1.0 without any Content-Length.
|
||||||
|
local
|
||||||
|
s: STRING_8
|
||||||
|
r: INTEGER -- remaining count
|
||||||
|
n,l_chunk_size, l_count: INTEGER
|
||||||
|
do
|
||||||
|
if a_socket.readable then
|
||||||
|
if a_len >= 0 then
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: Content-Length="+ a_len.out +"%N")
|
||||||
|
end
|
||||||
|
from
|
||||||
|
r := a_len
|
||||||
|
until
|
||||||
|
r = 0 or else not a_socket.readable or else a_response.error_occurred
|
||||||
|
loop
|
||||||
|
a_socket.read_stream_thread_aware (r)
|
||||||
|
l_count := l_count + a_socket.bytes_read
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
|
||||||
|
log ("Debug: - current count=" + l_count.out + "%N")
|
||||||
|
end
|
||||||
|
r := r - a_socket.bytes_read
|
||||||
|
a_output.append (a_socket.last_string)
|
||||||
|
end
|
||||||
|
check full_content_read: not a_response.error_occurred implies l_count = a_len end
|
||||||
|
elseif attached a_response.header ("Transfer-Encoding") as l_enc and then l_enc.is_case_insensitive_equal ("chunked") then
|
||||||
|
append_socket_chunked_content_to (a_response, a_socket, a_output)
|
||||||
|
else
|
||||||
|
-- No Content-Length and no chunked transfer encoding!
|
||||||
|
-- maybe HTTP/1.0 ?
|
||||||
|
-- FIXME: check solution!
|
||||||
|
from
|
||||||
|
l_count := 0
|
||||||
|
l_chunk_size := 1_024
|
||||||
|
n := l_chunk_size --| value to satisfy until condition on first loop.
|
||||||
|
until
|
||||||
|
n < l_chunk_size or not a_socket.readable
|
||||||
|
loop
|
||||||
|
a_socket.read_stream_thread_aware (l_chunk_size)
|
||||||
|
s := a_socket.last_string
|
||||||
|
n := a_socket.bytes_read
|
||||||
|
l_count := l_count + n
|
||||||
|
a_output.append (s)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append_socket_chunked_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
|
||||||
|
-- Get chunked content from `a_socket' and append it to `a_output'.
|
||||||
|
require
|
||||||
|
socket_readable: a_socket.readable
|
||||||
|
has_chunked_transfer_encoding: attached a_response.header ("Transfer-Encoding") as l_enc and then
|
||||||
|
l_enc.is_case_insensitive_equal ("chunked")
|
||||||
|
local
|
||||||
|
s: STRING_8
|
||||||
|
r: INTEGER -- remaining count
|
||||||
|
n,pos, l_count: INTEGER
|
||||||
|
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
|
||||||
|
do
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: Chunked encoding%N")
|
||||||
|
end
|
||||||
|
from
|
||||||
|
create hexa2int.make
|
||||||
|
n := 1
|
||||||
|
until
|
||||||
|
n = 0 or not a_socket.readable
|
||||||
|
loop
|
||||||
|
a_socket.read_line_thread_aware -- Read chunk info
|
||||||
|
s := a_socket.last_string
|
||||||
|
s.right_adjust
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: - chunk info='" + s + "'%N")
|
||||||
|
end
|
||||||
|
pos := s.index_of (';', 1)
|
||||||
|
if pos > 0 then
|
||||||
|
s.keep_head (pos - 1)
|
||||||
|
end
|
||||||
|
if s.is_empty then
|
||||||
|
n := 0
|
||||||
|
else
|
||||||
|
hexa2int.parse_string_with_type (s, hexa2int.type_integer)
|
||||||
|
if hexa2int.parse_successful then
|
||||||
|
n := hexa2int.parsed_integer
|
||||||
|
else
|
||||||
|
n := 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: - chunk size=" + n.out + "%N")
|
||||||
|
end
|
||||||
|
if n > 0 then
|
||||||
|
from
|
||||||
|
r := n
|
||||||
|
until
|
||||||
|
r = 0 or else not a_socket.readable or else a_response.error_occurred
|
||||||
|
loop
|
||||||
|
a_socket.read_stream_thread_aware (r)
|
||||||
|
l_count := l_count + a_socket.bytes_read
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
|
||||||
|
log ("Debug: - current count=" + l_count.out + "%N")
|
||||||
|
end
|
||||||
|
r := r - a_socket.bytes_read
|
||||||
|
a_output.append (a_socket.last_string)
|
||||||
|
end
|
||||||
|
|
||||||
|
a_socket.read_character
|
||||||
|
check a_socket.last_character = '%R' end
|
||||||
|
a_socket.read_character
|
||||||
|
check a_socket.last_character = '%N' end
|
||||||
|
if session.is_debug_verbose then
|
||||||
|
log ("Debug: - Found CRNL %N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
new_mime_boundary: STRING
|
||||||
|
-- New MIME boundary.
|
||||||
|
do
|
||||||
|
-- FIXME: better boundary creation
|
||||||
|
Result := "----------------------------5eadfcf3bb3e"
|
||||||
|
end
|
||||||
|
|
||||||
|
invariant
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Specific implementation of HTTP_CLIENT_SESSION based on Eiffel NET library
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
NET_HTTP_CLIENT_SESSION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT_SESSION
|
||||||
|
redefine
|
||||||
|
close
|
||||||
|
end
|
||||||
|
|
||||||
|
NET_HTTP_CLIENT_INFO
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
do
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
is_available: BOOLEAN
|
||||||
|
-- Is interface usable?
|
||||||
|
do
|
||||||
|
Result := True
|
||||||
|
if base_url.starts_with_general ("https://") then
|
||||||
|
Result := has_https_support
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access: persistent connection
|
||||||
|
|
||||||
|
persistent_connection: detachable NET_HTTP_CLIENT_CONNECTION
|
||||||
|
-- Socket used for persistent connection purpose.
|
||||||
|
|
||||||
|
feature -- Element change: persistent connection
|
||||||
|
|
||||||
|
set_persistent_connection (a_connection: like persistent_connection)
|
||||||
|
-- Set `persistent_connection' to `a_connection'.
|
||||||
|
do
|
||||||
|
persistent_connection := a_connection
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Basic operation
|
||||||
|
|
||||||
|
close
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
if attached persistent_connection as l_connection then
|
||||||
|
persistent_connection := Void
|
||||||
|
l_connection.socket.cleanup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Custom
|
||||||
|
|
||||||
|
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
local
|
||||||
|
req: HTTP_CLIENT_REQUEST
|
||||||
|
do
|
||||||
|
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||||
|
Result := req.response
|
||||||
|
end
|
||||||
|
|
||||||
|
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- Same as `custom' but including upload data `a_data'.
|
||||||
|
do
|
||||||
|
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- Same as `custom' but including upload file `fn'.
|
||||||
|
do
|
||||||
|
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Helper
|
||||||
|
|
||||||
|
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := custom ("GET", a_path, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := custom ("HEAD", a_path, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := custom ("DELETE", a_path, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
impl_custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
local
|
||||||
|
req: HTTP_CLIENT_REQUEST
|
||||||
|
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
l_data: detachable READABLE_STRING_8
|
||||||
|
do
|
||||||
|
ctx := a_ctx
|
||||||
|
if data /= Void then
|
||||||
|
if ctx = Void then
|
||||||
|
create ctx.make
|
||||||
|
end
|
||||||
|
ctx.set_upload_data (data)
|
||||||
|
end
|
||||||
|
if fn /= Void then
|
||||||
|
if ctx = Void then
|
||||||
|
create ctx.make
|
||||||
|
end
|
||||||
|
ctx.set_upload_filename (fn)
|
||||||
|
end
|
||||||
|
if ctx /= Void then
|
||||||
|
l_data := ctx.upload_data
|
||||||
|
if l_data /= Void and a_method.is_case_insensitive_equal_general ("PUT") then
|
||||||
|
check put_conflict_file_and_data: not ctx.has_upload_filename end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||||
|
Result := req.response
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
note
|
||||||
|
description: "Additional information related to NET HTTP Client.."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
NET_HTTP_CLIENT_INFO
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
has_https_support: BOOLEAN = False
|
||||||
|
-- Is HTTPS supported?
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
A fake SSL network stream socket... when SSL is disabled at compilation time.
|
||||||
|
Its behavior is similar to NETWORK_STREAM_SOCKET.
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
SSL_NETWORK_STREAM_SOCKET
|
||||||
|
|
||||||
|
inherit
|
||||||
|
NETWORK_STREAM_SOCKET
|
||||||
|
|
||||||
|
create
|
||||||
|
make, make_empty, make_client_by_port, make_client_by_address_and_port, make_server_by_port, make_loopback_server_by_port
|
||||||
|
|
||||||
|
create {SSL_NETWORK_STREAM_SOCKET}
|
||||||
|
make_from_descriptor_and_address, create_from_descriptor
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
note
|
||||||
|
description: "Additional information related to NET HTTP Client.."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
NET_HTTP_CLIENT_INFO
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
has_https_support: BOOLEAN = True
|
||||||
|
-- Is HTTPS supported?
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
note
|
||||||
|
description : "[
|
||||||
|
NULL version of HTTP_CLIENT_SESSION.
|
||||||
|
It is used if no implementation is available (libcurl or net)
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
NULL_HTTP_CLIENT_SESSION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT_SESSION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
do
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Custom
|
||||||
|
|
||||||
|
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
create Result.make (base_url + a_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Helper
|
||||||
|
|
||||||
|
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := custom ("GET", a_path, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := custom ("HEAD", a_path, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := custom ("DELETE", a_path, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
impl_custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := custom (a_method, a_path, a_ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
is_available: BOOLEAN = False
|
||||||
|
-- Is interface usable?
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
BIN
library/network/http_client/tests/logo.jpg
Normal file
BIN
library/network/http_client/tests/logo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
<?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">
|
<target name="test_http_client">
|
||||||
<root class="TEST" feature="make"/>
|
<root class="TEST" feature="make"/>
|
||||||
<file_rule>
|
<file_rule>
|
||||||
@@ -10,6 +10,9 @@
|
|||||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
<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"/>
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
</option>
|
</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="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
<library name="http_client" location="..\http_client-safe.ecf" readonly="false" use_application_options="true">
|
<library name="http_client" location="..\http_client-safe.ecf" readonly="false" use_application_options="true">
|
||||||
<option>
|
<option>
|
||||||
@@ -17,6 +20,19 @@
|
|||||||
</option>
|
</option>
|
||||||
</library>
|
</library>
|
||||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
<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>
|
</target>
|
||||||
</system>
|
</system>
|
||||||
|
|||||||
@@ -6,17 +6,46 @@ create
|
|||||||
feature -- Init
|
feature -- Init
|
||||||
|
|
||||||
make
|
make
|
||||||
|
local
|
||||||
|
null: NULL_HTTP_CLIENT_SESSION
|
||||||
do
|
do
|
||||||
|
create null.make ("http://example.com/")
|
||||||
|
check not null.is_available end
|
||||||
|
|
||||||
|
test_get_with_authentication
|
||||||
test_http_client
|
test_http_client
|
||||||
end
|
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
|
test_http_client
|
||||||
-- New test routine
|
-- New test routine
|
||||||
local
|
local
|
||||||
sess: LIBCURL_HTTP_CLIENT_SESSION
|
cl: DEFAULT_HTTP_CLIENT
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
h: STRING_8
|
||||||
do
|
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
|
if attached sess.get ("/search?q=eiffel", Void) as res then
|
||||||
assert ("Get returned without error", not res.error_occurred)
|
assert ("Get returned without error", not res.error_occurred)
|
||||||
create h.make_empty
|
create h.make_empty
|
||||||
@@ -42,7 +71,7 @@ feature -- Init
|
|||||||
do
|
do
|
||||||
if not b then
|
if not b then
|
||||||
create e
|
create e
|
||||||
e.set_message (m)
|
e.set_description (m)
|
||||||
e.raise
|
e.raise
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
1
library/network/http_client/tests/test.txt
Normal file
1
library/network/http_client/tests/test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This is a text sample for testing HTTP Client library.
|
||||||
@@ -7,21 +7,27 @@ note
|
|||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
testing: "type/manual"
|
testing: "type/manual"
|
||||||
|
|
||||||
class
|
deferred class
|
||||||
TEST_HTTP_CLIENT
|
TEST_HTTP_CLIENT_I
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
EQA_TEST_SET
|
EQA_TEST_SET
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Test routines
|
feature -- Test routines
|
||||||
|
|
||||||
test_http_client
|
test_http_client
|
||||||
-- New test routine
|
-- New test routine
|
||||||
local
|
local
|
||||||
sess: LIBCURL_HTTP_CLIENT_SESSION
|
sess: like new_session
|
||||||
h: STRING_8
|
h: STRING_8
|
||||||
do
|
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
|
if attached sess.get ("/search?q=eiffel", Void) as res then
|
||||||
assert ("Get returned without error", not res.error_occurred)
|
assert ("Get returned without error", not res.error_occurred)
|
||||||
create h.make_empty
|
create h.make_empty
|
||||||
@@ -38,8 +44,33 @@ feature -- Test routines
|
|||||||
assert ("missing body", False)
|
assert ("missing body", False)
|
||||||
end
|
end
|
||||||
assert ("same headers", h.same_string (res.raw_header))
|
assert ("same headers", h.same_string (res.raw_header))
|
||||||
else
|
end
|
||||||
assert ("Not found", False)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
43
library/network/http_client/tests/test_libcurl_http_client.e
Normal file
43
library/network/http_client/tests/test_libcurl_http_client.e
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Eiffel tests that can be executed by testing tool.
|
||||||
|
]"
|
||||||
|
author: "EiffelStudio test wizard"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
testing: "type/manual"
|
||||||
|
|
||||||
|
class
|
||||||
|
TEST_LIBCURL_HTTP_CLIENT
|
||||||
|
|
||||||
|
inherit
|
||||||
|
TEST_HTTP_CLIENT_I
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
do
|
||||||
|
create {LIBCURL_HTTP_CLIENT_SESSION} Result.make (a_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Tests
|
||||||
|
|
||||||
|
test_libcurl_http_client
|
||||||
|
do
|
||||||
|
test_http_client
|
||||||
|
end
|
||||||
|
|
||||||
|
test_libcurl_http_client_ssl
|
||||||
|
do
|
||||||
|
test_http_client_ssl
|
||||||
|
end
|
||||||
|
|
||||||
|
test_libcurl_headers
|
||||||
|
do
|
||||||
|
test_headers
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
67
library/network/http_client/tests/test_libcurl_with_web.e
Normal file
67
library/network/http_client/tests/test_libcurl_with_web.e
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Objects that ...
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
TEST_LIBCURL_WITH_WEB
|
||||||
|
|
||||||
|
inherit
|
||||||
|
TEST_WITH_WEB_I
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
local
|
||||||
|
cl: LIBCURL_HTTP_CLIENT
|
||||||
|
do
|
||||||
|
create cl
|
||||||
|
Result := cl.new_session (a_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Tests
|
||||||
|
|
||||||
|
libcurl_test_post_url_encoded
|
||||||
|
do
|
||||||
|
test_post_url_encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_post_with_form_data
|
||||||
|
do
|
||||||
|
test_post_with_form_data
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_post_with_file
|
||||||
|
do
|
||||||
|
test_post_with_file
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_put_with_file
|
||||||
|
do
|
||||||
|
test_put_with_file
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_put_with_data
|
||||||
|
do
|
||||||
|
test_put_with_data
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_post_with_file_and_form_data
|
||||||
|
do
|
||||||
|
test_post_with_file_and_form_data
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_get_with_redirection
|
||||||
|
do
|
||||||
|
test_get_with_redirection
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_get_with_authentication
|
||||||
|
do
|
||||||
|
test_get_with_authentication
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
86
library/network/http_client/tests/test_net_http_client.e
Normal file
86
library/network/http_client/tests/test_net_http_client.e
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Eiffel tests that can be executed by testing tool.
|
||||||
|
]"
|
||||||
|
author: "EiffelStudio test wizard"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
testing: "type/manual"
|
||||||
|
|
||||||
|
class
|
||||||
|
TEST_NET_HTTP_CLIENT
|
||||||
|
|
||||||
|
inherit
|
||||||
|
TEST_HTTP_CLIENT_I
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
do
|
||||||
|
create {NET_HTTP_CLIENT_SESSION} Result.make (a_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Tests
|
||||||
|
|
||||||
|
test_net_http_client
|
||||||
|
do
|
||||||
|
test_http_client
|
||||||
|
end
|
||||||
|
|
||||||
|
test_net_http_client_ssl
|
||||||
|
do
|
||||||
|
test_http_client_ssl
|
||||||
|
end
|
||||||
|
|
||||||
|
test_net_headers
|
||||||
|
do
|
||||||
|
test_headers
|
||||||
|
end
|
||||||
|
|
||||||
|
test_persistent_connection
|
||||||
|
local
|
||||||
|
sess: like new_session
|
||||||
|
h: STRING_8
|
||||||
|
do
|
||||||
|
sess := new_session ("http://www.google.fr")
|
||||||
|
if attached sess.get ("/", Void) as res then
|
||||||
|
assert ("Get returned without error", not res.error_occurred)
|
||||||
|
create h.make_empty
|
||||||
|
if attached res.headers as hds then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if attached res.body as l_body then
|
||||||
|
assert ("body not empty", not l_body.is_empty)
|
||||||
|
else
|
||||||
|
assert ("missing body", False)
|
||||||
|
end
|
||||||
|
assert ("same headers", h.same_string (res.raw_header))
|
||||||
|
end
|
||||||
|
if attached sess.get ("/", Void) as res then
|
||||||
|
assert ("Get returned without error", not res.error_occurred)
|
||||||
|
create h.make_empty
|
||||||
|
if attached res.headers as hds then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if attached res.body as l_body then
|
||||||
|
assert ("body not empty", not l_body.is_empty)
|
||||||
|
else
|
||||||
|
assert ("missing body", False)
|
||||||
|
end
|
||||||
|
assert ("same headers", h.same_string (res.raw_header))
|
||||||
|
end
|
||||||
|
sess.close
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
67
library/network/http_client/tests/test_net_with_web.e
Normal file
67
library/network/http_client/tests/test_net_with_web.e
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Objects that ...
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
TEST_NET_WITH_WEB
|
||||||
|
|
||||||
|
inherit
|
||||||
|
TEST_WITH_WEB_I
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
local
|
||||||
|
cl: NET_HTTP_CLIENT
|
||||||
|
do
|
||||||
|
create cl
|
||||||
|
Result := cl.new_session (a_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Tests
|
||||||
|
|
||||||
|
net_test_post_url_encoded
|
||||||
|
do
|
||||||
|
test_post_url_encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_post_with_form_data
|
||||||
|
do
|
||||||
|
test_post_with_form_data
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_post_with_file
|
||||||
|
do
|
||||||
|
test_post_with_file
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_put_with_file
|
||||||
|
do
|
||||||
|
test_put_with_file
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_put_with_data
|
||||||
|
do
|
||||||
|
test_put_with_data
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_post_with_file_and_form_data
|
||||||
|
do
|
||||||
|
test_post_with_file_and_form_data
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_get_with_redirection
|
||||||
|
do
|
||||||
|
test_get_with_redirection
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_get_with_authentication
|
||||||
|
do
|
||||||
|
test_get_with_authentication
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
302
library/network/http_client/tests/test_with_web_i.e
Normal file
302
library/network/http_client/tests/test_with_web_i.e
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Eiffel tests that can be executed by testing tool.
|
||||||
|
]"
|
||||||
|
author: "EiffelStudio test wizard"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
testing: "type/manual"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
TEST_WITH_WEB_I
|
||||||
|
|
||||||
|
inherit
|
||||||
|
EQA_TEST_SET
|
||||||
|
redefine
|
||||||
|
on_prepare
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Initialization
|
||||||
|
|
||||||
|
on_prepare
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
global_requestbin_path := new_requestbin_path
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Requestbin
|
||||||
|
|
||||||
|
global_requestbin_path: detachable READABLE_STRING_8
|
||||||
|
|
||||||
|
new_requestbin_path: detachable STRING
|
||||||
|
local
|
||||||
|
i,j: INTEGER
|
||||||
|
do
|
||||||
|
if
|
||||||
|
attached new_session ("http://requestb.in") as sess and then
|
||||||
|
attached sess.post ("/api/v1/bins", Void, Void) as resp
|
||||||
|
then
|
||||||
|
if resp.error_occurred then
|
||||||
|
print ("Error occurred!%N")
|
||||||
|
elseif attached resp.body as l_content then
|
||||||
|
|
||||||
|
i := l_content.substring_index ("%"name%":", 1)
|
||||||
|
if i > 0 then
|
||||||
|
j := l_content.index_of (',', i + 1)
|
||||||
|
if j > 0 then
|
||||||
|
Result := l_content.substring (i + 7, j - 1)
|
||||||
|
Result.adjust
|
||||||
|
if Result.starts_with ("%"") then
|
||||||
|
Result.remove_head (1)
|
||||||
|
end
|
||||||
|
if Result.ends_with ("%"") then
|
||||||
|
Result.remove_tail (1)
|
||||||
|
end
|
||||||
|
if not Result.starts_with ("/") then
|
||||||
|
Result.prepend_character ('/')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
test_post_url_encoded
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
-- URL ENCODED POST REQUEST
|
||||||
|
-- check requestbin to ensure the "Hello World" has been received in the raw body
|
||||||
|
-- also check that User-Agent was sent
|
||||||
|
create h.make_empty
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
if
|
||||||
|
attached sess.post (requestbin_path, Void, "Hello World") as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_post_with_form_data
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- POST REQUEST WITH FORM DATA
|
||||||
|
-- check requestbin to ensure the form parameters are correctly received
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.form_parameters.extend ("First Value", "First Key")
|
||||||
|
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.post (requestbin_path, l_ctx, "") as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_post_with_file
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- POST REQUEST WITH A FILE
|
||||||
|
-- check requestbin to ensure the form parameters are correctly received
|
||||||
|
-- set filename to a local file
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.set_upload_filename ("test.txt")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.post (requestbin_path, l_ctx, "") as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_put_with_file
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- PUT REQUEST WITH A FILE
|
||||||
|
-- check requestbin to ensure the file is correctly received
|
||||||
|
-- set filename to a local file
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.set_upload_filename ("test.txt")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.put (requestbin_path, l_ctx, Void) as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_put_with_data
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- PUT REQUEST WITH A FILE
|
||||||
|
-- check requestbin to ensure the file is correctly received
|
||||||
|
-- set filename to a local file
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.set_upload_data ("This is a test for http client.%N")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.put (requestbin_path, l_ctx, Void) as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_post_with_file_and_form_data
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- POST REQUEST WITH A FILE AND FORM DATA
|
||||||
|
-- check requestbin to ensure the file and form parameters are correctly received
|
||||||
|
-- set filename to a local file
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.set_upload_filename ("logo.jpg")
|
||||||
|
l_ctx.form_parameters.extend ("First Value", "First Key")
|
||||||
|
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.post (requestbin_path, l_ctx, Void) as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_get_with_redirection
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- GET REQUEST, Forwarding (google's first answer is a forward)
|
||||||
|
-- check headers received (printed in console)
|
||||||
|
sess := new_session ("http://google.com")
|
||||||
|
create h.make_empty
|
||||||
|
if attached sess.get ("/", Void) as res and then attached res.headers as hds then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_get_with_authentication
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
-- GET REQUEST WITH AUTHENTICATION, see http://browserspy.dk/password.php
|
||||||
|
-- check header WWW-Authenticate is received (authentication successful)
|
||||||
|
sess := new_session ("http://browserspy.dk")
|
||||||
|
sess.set_credentials ("test", "test")
|
||||||
|
create ctx.make_with_credentials_required
|
||||||
|
if attached sess.get ("/password-ok.php", ctx) as res then
|
||||||
|
if attached {READABLE_STRING_8} res.body as l_body then
|
||||||
|
assert ("Fetch all body, including closing html tag", l_body.has_substring ("</html>"))
|
||||||
|
else
|
||||||
|
assert ("has body", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
<root all_classes="true"/>
|
<root all_classes="true"/>
|
||||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
|
<library name="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension-safe.ecf"/>
|
||||||
|
<library name="encoders" location="..\..\encoder\encoder-safe.ecf"/>
|
||||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
|
||||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||||
<library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid-safe.ecf"/>
|
<library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid-safe.ecf"/>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
</option>
|
</option>
|
||||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
<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="base_extension" location="$ISE_LIBRARY\library\base_extension\base_extension.ecf"/>
|
||||||
|
<library name="encoders" location="..\..\encoder\encoder.ecf"/>
|
||||||
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
|
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
|
||||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||||
<library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid.ecf"/>
|
<library name="uuid" location="$ISE_LIBRARY\library\uuid\uuid.ecf"/>
|
||||||
|
|||||||
@@ -80,20 +80,40 @@ feature -- Element change
|
|||||||
|
|
||||||
set_updated_date_with_text (a_date_text: detachable READABLE_STRING_32)
|
set_updated_date_with_text (a_date_text: detachable READABLE_STRING_32)
|
||||||
-- Set `date' from date string representation `a_date_text'.
|
-- Set `date' from date string representation `a_date_text'.
|
||||||
|
obsolete
|
||||||
|
"Use set_date_with_text [oct/2015]"
|
||||||
|
do
|
||||||
|
set_date_with_text (a_date_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
set_date_with_text (a_date_text: detachable READABLE_STRING_32)
|
||||||
|
-- Set `date' from date string representation `a_date_text'.
|
||||||
do
|
do
|
||||||
if a_date_text = Void then
|
if a_date_text = Void then
|
||||||
date := Void
|
set_date (Void)
|
||||||
else
|
else
|
||||||
date := date_time (a_date_text)
|
set_date (date_time (a_date_text))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_date (a_date: detachable DATE_TIME)
|
||||||
|
-- Set `date' from `a_date'.
|
||||||
|
do
|
||||||
|
date := a_date
|
||||||
|
end
|
||||||
|
|
||||||
extend (a_item: FEED_ITEM)
|
extend (a_item: FEED_ITEM)
|
||||||
-- Add item `a_item' to feed `items'.
|
-- Add item `a_item' to feed `items'.
|
||||||
do
|
do
|
||||||
items.force (a_item)
|
items.force (a_item)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
prune (a_item: FEED_ITEM)
|
||||||
|
-- Remove feed item `a_item' from Current list of feed items.
|
||||||
|
do
|
||||||
|
items.prune (a_item)
|
||||||
|
end
|
||||||
|
|
||||||
extended alias "+" (a_feed: FEED): FEED
|
extended alias "+" (a_feed: FEED): FEED
|
||||||
-- New feed object made from Current merged with a_feed.
|
-- New feed object made from Current merged with a_feed.
|
||||||
local
|
local
|
||||||
|
|||||||
@@ -156,14 +156,29 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
|
|
||||||
set_updated_date_with_text (a_date_text: detachable READABLE_STRING_32)
|
set_updated_date_with_text (a_date_text: detachable READABLE_STRING_32)
|
||||||
|
-- Set `date' from date string representation `a_date_text'.
|
||||||
|
obsolete
|
||||||
|
"Use set_date_with_text [oct/2015]"
|
||||||
|
do
|
||||||
|
set_date_with_text (a_date_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
set_date_with_text (a_date_text: detachable READABLE_STRING_32)
|
||||||
|
-- Set `date' from date string representation `a_date_text'.
|
||||||
do
|
do
|
||||||
if a_date_text = Void then
|
if a_date_text = Void then
|
||||||
date := Void
|
set_date (Void)
|
||||||
else
|
else
|
||||||
date := date_time (a_date_text)
|
set_date (date_time (a_date_text))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_date (a_date: detachable DATE_TIME)
|
||||||
|
-- Set `date' from `a_date'.
|
||||||
|
do
|
||||||
|
date := a_date
|
||||||
|
end
|
||||||
|
|
||||||
set_author (a_author: detachable FEED_AUTHOR)
|
set_author (a_author: detachable FEED_AUTHOR)
|
||||||
do
|
do
|
||||||
author := a_author
|
author := a_author
|
||||||
|
|||||||
229
library/text/parser/feed/src/support/feed_to_xhtml_visitor.e
Normal file
229
library/text/parser/feed/src/support/feed_to_xhtml_visitor.e
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Convert a FEED to XHTML representation.
|
||||||
|
]"
|
||||||
|
date: "$Date: 2015-10-08 10:45:13 +0200 (jeu., 08 oct. 2015) $"
|
||||||
|
revision: "$Revision: 97964 $"
|
||||||
|
|
||||||
|
class
|
||||||
|
FEED_TO_XHTML_VISITOR
|
||||||
|
|
||||||
|
inherit
|
||||||
|
FEED_VISITOR
|
||||||
|
|
||||||
|
SHARED_HTML_ENCODER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_buffer: STRING_8)
|
||||||
|
do
|
||||||
|
buffer := a_buffer
|
||||||
|
create today.make_now_utc
|
||||||
|
description_enabled := False
|
||||||
|
limit := -1
|
||||||
|
end
|
||||||
|
|
||||||
|
buffer: STRING_8
|
||||||
|
-- Output buffer.
|
||||||
|
|
||||||
|
today: DATE_TIME
|
||||||
|
-- Current date.
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
header: detachable READABLE_STRING_8
|
||||||
|
-- Optional header.
|
||||||
|
|
||||||
|
footer: detachable READABLE_STRING_8
|
||||||
|
-- Optional footer.
|
||||||
|
|
||||||
|
feature -- Settings
|
||||||
|
|
||||||
|
limit: INTEGER
|
||||||
|
-- Number of item to include in XHTML generation.
|
||||||
|
-- Default: -1 => No limit
|
||||||
|
|
||||||
|
description_enabled: BOOLEAN
|
||||||
|
-- Generate description?
|
||||||
|
-- Default: False
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_limit (nb: INTEGER)
|
||||||
|
-- Set `limit' to `nb'.
|
||||||
|
do
|
||||||
|
limit := nb
|
||||||
|
end
|
||||||
|
|
||||||
|
set_description_enabled (b: BOOLEAN)
|
||||||
|
-- Set `description_enabled' to `b'.
|
||||||
|
do
|
||||||
|
description_enabled := b
|
||||||
|
end
|
||||||
|
|
||||||
|
set_header (h: like header)
|
||||||
|
do
|
||||||
|
header := h
|
||||||
|
end
|
||||||
|
|
||||||
|
set_footer (f: like footer)
|
||||||
|
do
|
||||||
|
footer := f
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Visitor
|
||||||
|
|
||||||
|
visit_feed (a_feed: FEED)
|
||||||
|
local
|
||||||
|
nb: INTEGER
|
||||||
|
do
|
||||||
|
append ("<div class=%"feed%">")
|
||||||
|
if attached header as h then
|
||||||
|
append (h)
|
||||||
|
append ("%N")
|
||||||
|
end
|
||||||
|
if attached a_feed.date as dt then
|
||||||
|
append ("<!-- date:")
|
||||||
|
append (dt.out)
|
||||||
|
append (" -->%N")
|
||||||
|
end
|
||||||
|
|
||||||
|
append ("<ul>%N")
|
||||||
|
if
|
||||||
|
description_enabled and then
|
||||||
|
attached a_feed.description as l_desc and then
|
||||||
|
l_desc.is_valid_as_string_8
|
||||||
|
then
|
||||||
|
append ("<div class=%"description%">")
|
||||||
|
append (l_desc.to_string_8)
|
||||||
|
append ("</div>")
|
||||||
|
end
|
||||||
|
|
||||||
|
nb := limit
|
||||||
|
across
|
||||||
|
a_feed as ic
|
||||||
|
until
|
||||||
|
nb = 0
|
||||||
|
loop
|
||||||
|
ic.item.accept (Current)
|
||||||
|
nb := nb - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if attached footer as f then
|
||||||
|
append (f)
|
||||||
|
append ("%N")
|
||||||
|
end
|
||||||
|
append ("</ul>%N")
|
||||||
|
end
|
||||||
|
|
||||||
|
visit_item (a_entry: FEED_ITEM)
|
||||||
|
local
|
||||||
|
lnk: detachable FEED_LINK
|
||||||
|
do
|
||||||
|
append ("<li>%N")
|
||||||
|
lnk := a_entry.link
|
||||||
|
|
||||||
|
if attached a_entry.date as dt then
|
||||||
|
append ("<div class=%"date%">")
|
||||||
|
append_date_time_to (dt, today.date, buffer)
|
||||||
|
append ("</div>%N")
|
||||||
|
end
|
||||||
|
if lnk /= Void then
|
||||||
|
append ("<a href=%"" + lnk.href + "%">")
|
||||||
|
else
|
||||||
|
check has_link: False end
|
||||||
|
append ("<a href=%"#%">")
|
||||||
|
end
|
||||||
|
append_as_html_encoded (a_entry.title)
|
||||||
|
append ("</a>%N")
|
||||||
|
debug
|
||||||
|
if attached a_entry.categories as l_categories and then not l_categories.is_empty then
|
||||||
|
append ("<div class=%"category%">")
|
||||||
|
across
|
||||||
|
l_categories as cats_ic
|
||||||
|
loop
|
||||||
|
append_as_html_encoded (cats_ic.item)
|
||||||
|
append (" ")
|
||||||
|
end
|
||||||
|
append ("</div>%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if
|
||||||
|
description_enabled and then
|
||||||
|
attached a_entry.description as l_entry_desc
|
||||||
|
then
|
||||||
|
if l_entry_desc.is_valid_as_string_8 then
|
||||||
|
append ("<div class=%"description%">")
|
||||||
|
append (l_entry_desc.as_string_8)
|
||||||
|
append ("</div>%N")
|
||||||
|
else
|
||||||
|
check is_html: False end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
append ("</li>%N")
|
||||||
|
end
|
||||||
|
|
||||||
|
visit_link (a_link: FEED_LINK)
|
||||||
|
do
|
||||||
|
append ("<a href=%"" + a_link.href + "%">")
|
||||||
|
append_as_html_encoded (a_link.relation)
|
||||||
|
append ("</a>%N")
|
||||||
|
end
|
||||||
|
|
||||||
|
visit_author (a_author: FEED_AUTHOR)
|
||||||
|
do
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Helper
|
||||||
|
|
||||||
|
append_as_html_encoded (s: READABLE_STRING_GENERAL)
|
||||||
|
do
|
||||||
|
buffer.append (html_encoder.general_encoded_string (s))
|
||||||
|
end
|
||||||
|
|
||||||
|
append (s: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
buffer.append (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
append_date_time_to (dt: DATE_TIME; a_today: DATE; a_output: STRING_GENERAL)
|
||||||
|
do
|
||||||
|
if dt.year /= a_today.year then
|
||||||
|
a_output.append (dt.year.out)
|
||||||
|
a_output.append (",")
|
||||||
|
end
|
||||||
|
a_output.append (" ")
|
||||||
|
append_month_mmm_to (dt.month, a_output)
|
||||||
|
a_output.append (" ")
|
||||||
|
if dt.day < 10 then
|
||||||
|
a_output.append ("0")
|
||||||
|
end
|
||||||
|
a_output.append (dt.day.out)
|
||||||
|
end
|
||||||
|
|
||||||
|
append_month_mmm_to (m: INTEGER; s: STRING_GENERAL)
|
||||||
|
require
|
||||||
|
1 <= m and m <= 12
|
||||||
|
do
|
||||||
|
inspect m
|
||||||
|
when 1 then s.append ("Jan")
|
||||||
|
when 2 then s.append ("Feb")
|
||||||
|
when 3 then s.append ("Mar")
|
||||||
|
when 4 then s.append ("Apr")
|
||||||
|
when 5 then s.append ("May")
|
||||||
|
when 6 then s.append ("Jun")
|
||||||
|
when 7 then s.append ("Jul")
|
||||||
|
when 8 then s.append ("Aug")
|
||||||
|
when 9 then s.append ("Sep")
|
||||||
|
when 10 then s.append ("Oct")
|
||||||
|
when 11 then s.append ("Nov")
|
||||||
|
when 12 then s.append ("Dec")
|
||||||
|
else
|
||||||
|
-- Error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -2,8 +2,8 @@ note
|
|||||||
description : "Objects that represent a custom error"
|
description : "Objects that represent a custom error"
|
||||||
legal: "See notice at end of class."
|
legal: "See notice at end of class."
|
||||||
status: "See notice at end of class."
|
status: "See notice at end of class."
|
||||||
date: "$Date$"
|
date: "$Date: 2015-10-10 00:55:41 +0200 (sam., 10 oct. 2015) $"
|
||||||
revision: "$Revision$"
|
revision: "$Revision: 97980 $"
|
||||||
|
|
||||||
class
|
class
|
||||||
ERROR_CUSTOM
|
ERROR_CUSTOM
|
||||||
@@ -24,7 +24,7 @@ feature {NONE} -- Initialization
|
|||||||
if a_message /= Void then
|
if a_message /= Void then
|
||||||
message := a_message
|
message := a_message
|
||||||
else
|
else
|
||||||
message := "Error: " + a_name + " (code=" + a_code.out + ")"
|
message := {STRING_32} "Error: " + a_name + " (code=" + a_code.out + ")"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ note
|
|||||||
description : "Objects that handle error..."
|
description : "Objects that handle error..."
|
||||||
legal: "See notice at end of class."
|
legal: "See notice at end of class."
|
||||||
status: "See notice at end of class."
|
status: "See notice at end of class."
|
||||||
date: "$Date$"
|
date: "$Date: 2015-10-10 00:55:41 +0200 (sam., 10 oct. 2015) $"
|
||||||
revision: "$Revision$"
|
revision: "$Revision: 97980 $"
|
||||||
|
|
||||||
class
|
class
|
||||||
ERROR_HANDLER
|
ERROR_HANDLER
|
||||||
@@ -216,7 +216,7 @@ feature -- Basic operation
|
|||||||
on_error_added (a_error)
|
on_error_added (a_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
add_error_details, add_custom_error (a_code: INTEGER; a_name: STRING; a_message: detachable STRING_32)
|
add_error_details, add_custom_error (a_code: INTEGER; a_name: STRING; a_message: detachable READABLE_STRING_32)
|
||||||
-- Add custom error to the stack of error
|
-- Add custom error to the stack of error
|
||||||
local
|
local
|
||||||
e: ERROR_CUSTOM
|
e: ERROR_CUSTOM
|
||||||
|
|||||||
Reference in New Issue
Block a user