Compare commits
27 Commits
dev_jwt
...
with_compr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97fe16b4c2 | ||
|
|
cdada71f7e | ||
|
|
a7d0398ec6 | ||
|
|
267655d7bc | ||
|
|
e735da1bcb | ||
|
|
6425482070 | ||
|
|
818c3fb460 | ||
|
|
dac50b490d | ||
|
|
16d5076fe5 | ||
|
|
2748e1d9ee | ||
|
|
27ee20f99b | ||
|
|
9a3164df70 | ||
|
|
02383810b4 | ||
|
|
dbf5e76047 | ||
|
|
5c31905427 | ||
|
|
c7ef652322 | ||
|
|
7feb45b549 | ||
|
|
5bbd031275 | ||
|
|
90e60fad26 | ||
|
|
98c20ee7c1 | ||
|
|
0b99e84728 | ||
|
|
9e5e8bb1bf | ||
|
|
10a83c6ad8 | ||
|
|
1ec3b8e7a4 | ||
| e14bb568d2 | |||
| 05d37439bc | |||
|
|
99bf552b89 |
19
.travis.yml
19
.travis.yml
@@ -0,0 +1,19 @@
|
|||||||
|
language: eiffel
|
||||||
|
before_script:
|
||||||
|
- export current_dir=$PWD ; echo current_dir=$current_dir ; cd ..
|
||||||
|
- export ISE_VERSION=17.05; export ISE_BUILD=100416
|
||||||
|
- curl -sSL http://downloads.sourceforge.net/eiffelstudio/Eiffel_${ISE_VERSION}_gpl_${ISE_BUILD}-linux-x86-64.tar.bz2 | tar -x --bzip2
|
||||||
|
- export ISE_EIFFEL=$PWD/Eiffel_${ISE_VERSION} ; export ISE_PLATFORM=linux-x86-64
|
||||||
|
- export PATH=$PATH:$ISE_EIFFEL/studio/spec/$ISE_PLATFORM/bin:$PATH:$ISE_EIFFEL/tools/spec/$ISE_PLATFORM/bin
|
||||||
|
- echo `ec -version`
|
||||||
|
- cd $current_dir
|
||||||
|
- echo Check projects compilation status...
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- v1
|
||||||
|
|
||||||
|
script: compile_all -ecb -melt -list_failures -log_verbose -clean -options dotnet=false
|
||||||
|
group: stable
|
||||||
|
os: linux
|
||||||
|
|||||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -7,20 +7,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
- jwt: new JSON Web Token (JWT) library (supports for claim exp, iat, nbf, iss, aud).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- http_network:
|
|
||||||
Integrated changes on SOCKET so that EiffelWeb can be compiled with 16.05 to 17.05 and after.
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
### Removed
|
### Removed
|
||||||
### Fixed
|
### Fixed
|
||||||
- http_client:
|
- Removed a few obsolete calls.
|
||||||
Improved query and form data encoding (based on a very early version of the general URI percent-encoding rules).
|
- `http_client`: Added support for multiple file in form data. Made clear what is the meaning of `upload_filename`, `upload_data` and `form_data`.
|
||||||
- now correct encoding of space by '%20' in path segment, and '+' in query parameters.
|
|
||||||
Unify and fixed query parameters handling for libcurl and net implementation.
|
|
||||||
Fixed file uploading (various issue in libcurl, and net implementation).
|
|
||||||
Fixed form multipart encoding by using correctly the boundary.
|
|
||||||
- Code cleaning:
|
|
||||||
Removed many obsolete calls, and added timestamp on EiffelWeb obsolete features to benefit from upcoming improvement on the EiffelStudio Inspector tool.
|
|
||||||
### Security
|
### Security
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
<?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-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="web_server" uuid="B1D3254D-A58E-4259-9796-8A2843A511A9">
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="web_server" uuid="B1D3254D-A58E-4259-9796-8A2843A511A9">
|
||||||
<target name="web_server">
|
<target name="web_server">
|
||||||
<root class="APPLICATION" feature="make"/>
|
<root class="APPLICATION" feature="make"/>
|
||||||
<file_rule>
|
<file_rule>
|
||||||
<exclude>/.git$</exclude>
|
<exclude>/.git$</exclude>
|
||||||
<exclude>/.svn$</exclude>
|
<exclude>/.svn$</exclude>
|
||||||
<exclude>/CVS$</exclude>
|
<exclude>/CVS$</exclude>
|
||||||
<exclude>/EIFGENs$</exclude>
|
<exclude>/EIFGENs$</exclude>
|
||||||
</file_rule>
|
</file_rule>
|
||||||
<option debug="true" warning="true" void_safety="all">
|
<option debug="true" warning="true" void_safety="all">
|
||||||
<debug name="nino" enabled="true"/>
|
<debug name="nino" enabled="true"/>
|
||||||
<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>
|
||||||
<setting name="concurrency" value="thread"/>
|
<setting name="concurrency" value="thread"/>
|
||||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
||||||
<library name="nino" location="..\..\nino-safe.ecf"/>
|
<library name="nino" location="..\..\nino-safe.ecf"/>
|
||||||
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
|
<library name="thread" location="$ISE_LIBRARY\library\thread\thread-safe.ecf"/>
|
||||||
<cluster name="src" location=".\" recursive="true"/>
|
<cluster name="src" location=".\" recursive="true"/>
|
||||||
</target>
|
</target>
|
||||||
</system>
|
</system>
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<?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-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="web_server" uuid="B1D3254D-A58E-4259-9796-8A2843A511A9">
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="web_server" uuid="B1D3254D-A58E-4259-9796-8A2843A511A9">
|
||||||
<target name="web_server">
|
<target name="web_server">
|
||||||
<root class="APPLICATION" feature="make"/>
|
<root class="APPLICATION" feature="make"/>
|
||||||
<file_rule>
|
<file_rule>
|
||||||
<exclude>/.git$</exclude>
|
<exclude>/.git$</exclude>
|
||||||
<exclude>/.svn$</exclude>
|
<exclude>/.svn$</exclude>
|
||||||
<exclude>/CVS$</exclude>
|
<exclude>/CVS$</exclude>
|
||||||
<exclude>/EIFGENs$</exclude>
|
<exclude>/EIFGENs$</exclude>
|
||||||
</file_rule>
|
</file_rule>
|
||||||
<option warning="true" void_safety="none">
|
<option warning="true" void_safety="none">
|
||||||
<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>
|
||||||
<setting name="concurrency" value="thread"/>
|
<setting name="concurrency" value="thread"/>
|
||||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||||
<library name="nino" location="..\..\nino.ecf"/>
|
<library name="nino" location="..\..\nino.ecf"/>
|
||||||
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf"/>
|
<library name="thread" location="$ISE_LIBRARY\library\thread\thread.ecf"/>
|
||||||
<cluster name="src" location=".\" recursive="true"/>
|
<cluster name="src" location=".\" recursive="true"/>
|
||||||
</target>
|
</target>
|
||||||
</system>
|
</system>
|
||||||
|
|||||||
4
examples/simple_compression/service.ini
Normal file
4
examples/simple_compression/service.ini
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
port=9090
|
||||||
|
verbose=true
|
||||||
|
socket_recv_timeout=15
|
||||||
|
keep_alive_timeout=30
|
||||||
26
examples/simple_compression/service_compression.e
Normal file
26
examples/simple_compression/service_compression.e
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
note
|
||||||
|
description : "simple application root class"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
SERVICE_COMPRESSION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_DEFAULT_SERVICE [SERVICE_COMPRESSION_EXECUTION]
|
||||||
|
redefine
|
||||||
|
initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make_and_launch
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
import_service_options (create {WSF_SERVICE_LAUNCHER_OPTIONS_FROM_INI}.make_from_file ("service.ini"))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
22
examples/simple_compression/service_compression.ecf
Normal file
22
examples/simple_compression/service_compression.ecf
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="service_compression" uuid="C28C4F53-9963-46C0-A080-8F13E94E7486">
|
||||||
|
<target name="service_compression">
|
||||||
|
<root class="SERVICE_COMPRESSION" feature="make_and_launch"/>
|
||||||
|
<option warning="true" void_safety="all">
|
||||||
|
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||||
|
</option>
|
||||||
|
<setting name="concurrency" value="scoop"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf"/>
|
||||||
|
<library name="http" location="..\..\library\network\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
|
||||||
|
<library name="wsf_compression" location="..\..\library\server\wsf\wsf_compression-safe.ecf" readonly="false"/>
|
||||||
|
<cluster name="service_compression" location=".\" recursive="true">
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
<exclude>/CVS$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
</cluster>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
68
examples/simple_compression/service_compression_execution.e
Normal file
68
examples/simple_compression/service_compression_execution.e
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
note
|
||||||
|
description: "Simple file execution, serving home.html, ewf.png and 404.html"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
SERVICE_COMPRESSION_EXECUTION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_ROUTED_EXECUTION
|
||||||
|
redefine
|
||||||
|
initialize,
|
||||||
|
execute_default
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
|
||||||
|
initialize
|
||||||
|
-- Initialize current service.
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
initialize_router
|
||||||
|
end
|
||||||
|
|
||||||
|
setup_router
|
||||||
|
local
|
||||||
|
fhdl_with_compression: WSF_FILE_SYSTEM_HANDLER_WITH_COMPRESSION
|
||||||
|
fhdl: WSF_FILE_SYSTEM_HANDLER
|
||||||
|
do
|
||||||
|
create fhdl_with_compression.make_hidden ("www")
|
||||||
|
fhdl_with_compression.set_directory_index (<<"index.html">>)
|
||||||
|
fhdl_with_compression.compression.set_default_compression_format
|
||||||
|
fhdl_with_compression.compression.enable_compression_for_media_type ({HTTP_MIME_TYPES}.image_jpg)
|
||||||
|
fhdl_with_compression.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
execute_default (ia_req, ia_res)
|
||||||
|
end)
|
||||||
|
router.handle ("/compressed/", fhdl_with_compression, router.methods_GET)
|
||||||
|
|
||||||
|
create fhdl.make_hidden ("www")
|
||||||
|
fhdl.set_directory_index (<<"index.html">>)
|
||||||
|
fhdl.set_not_found_handler (agent (ia_uri: READABLE_STRING_8; ia_req: WSF_REQUEST; ia_res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
execute_default (ia_req, ia_res)
|
||||||
|
end)
|
||||||
|
router.handle ("/", fhdl, router.methods_GET)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
-- Dispatch requests without a matching handler.
|
||||||
|
local
|
||||||
|
not_found: WSF_NOT_FOUND_RESPONSE
|
||||||
|
mesg: WSF_RESPONSE_MESSAGE
|
||||||
|
do
|
||||||
|
create not_found.make (request)
|
||||||
|
not_found.add_suggested_location (request.absolute_script_url (""), "Home", "Back to home page")
|
||||||
|
mesg := not_found
|
||||||
|
res.send (mesg)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
77705
examples/simple_compression/www/big_file.html
Normal file
77705
examples/simple_compression/www/big_file.html
Normal file
File diff suppressed because it is too large
Load Diff
88332
examples/simple_compression/www/big_file2.html
Normal file
88332
examples/simple_compression/www/big_file2.html
Normal file
File diff suppressed because it is too large
Load Diff
BIN
examples/simple_compression/www/eiffel.jpg
Normal file
BIN
examples/simple_compression/www/eiffel.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
examples/simple_compression/www/ewf.png
Normal file
BIN
examples/simple_compression/www/ewf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
27
examples/simple_compression/www/index.html
Normal file
27
examples/simple_compression/www/index.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>EWF simple_file example</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>EWF simple_file example</h1>
|
||||||
|
<p>This is a static html file served by EWF.</p>
|
||||||
|
<p>Try to <a href="nowhere.html">get lost</a>.</p>
|
||||||
|
<div width="45%" style="display: inline-block; border: solid 1px black; padding: 10px; margin: 10px;">
|
||||||
|
<h2>Without any compression</h2>
|
||||||
|
<a href="ewf.png"><img src="ewf.png"/></a>
|
||||||
|
<p>This is the real Eiffel tower.</p>
|
||||||
|
<a href="eiffel.jpg"><img src="eiffel.jpg"/></a>
|
||||||
|
<p>Try to <a href="big_file2.html">load a big file</a>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div width="45%" style="display: inline-block; border: solid 1px black; padding: 10px; margin: 10px;">
|
||||||
|
<h2>With gzip compression</h2>
|
||||||
|
<a href="compressed/ewf.png"><img src="compressed/ewf.png"/></a>
|
||||||
|
<p>This is the real Eiffel tower.</p>
|
||||||
|
<a href="compressed/eiffel.jpg"><img src="compressed/eiffel.jpg"/></a>
|
||||||
|
<p>Try to <a href="compressed/big_file2.html">load a compressed big file</a>.</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||||
<cluster name="src" location=".\src\">
|
<cluster name="src" location=".\src\">
|
||||||
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
||||||
|
<cluster name="parameters" location="$|parameters" recursive="true"/>
|
||||||
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
|
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
|
||||||
<cluster name="spec_net" location="$|spec\net\">
|
<cluster name="spec_net" location="$|spec\net\">
|
||||||
<condition>
|
<condition>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||||
<cluster name="src" location=".\src\">
|
<cluster name="src" location=".\src\">
|
||||||
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
||||||
|
<cluster name="parameters" location="$|parameters" recursive="true"/>
|
||||||
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
|
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
|
||||||
<cluster name="spec_net" location="$|spec\net\">
|
<cluster name="spec_net" location="$|spec\net\">
|
||||||
<condition>
|
<condition>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
<library name="net" location="$ISE_LIBRARY\library\net\net-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"/>
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||||
<cluster name="src" location=".\src\">
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
||||||
|
<cluster name="parameters" location="$|parameters" recursive="true"/>
|
||||||
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
|
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
|
||||||
<cluster name="default_libcurl" location="$|default\libcurl\"/>
|
<cluster name="default_libcurl" location="$|default\libcurl\"/>
|
||||||
</cluster>
|
</cluster>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
||||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||||
<cluster name="src" location=".\src\">
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
||||||
|
<cluster name="parameters" location="$|parameters" recursive="true"/>
|
||||||
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
|
<cluster name="spec_libcurl" location="$|spec\libcurl\" recursive="true"/>
|
||||||
<cluster name="default_libcurl" location="$|default\libcurl\"/>
|
<cluster name="default_libcurl" location="$|default\libcurl\"/>
|
||||||
</cluster>
|
</cluster>
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
</library>
|
</library>
|
||||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||||
<cluster name="src" location=".\src\">
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
||||||
|
<cluster name="parameters" location="$|parameters" recursive="true"/>
|
||||||
<cluster name="spec_net" location="$|spec\net\">
|
<cluster name="spec_net" location="$|spec\net\">
|
||||||
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
||||||
</cluster>
|
</cluster>
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
</library>
|
</library>
|
||||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||||
<cluster name="src" location=".\src\">
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
||||||
|
<cluster name="parameters" location="$|parameters" recursive="true"/>
|
||||||
<cluster name="spec_net" location="$|spec\net\">
|
<cluster name="spec_net" location="$|spec\net\">
|
||||||
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
<cluster name="net_implementation" location="$|implementation\" hidden="true"/>
|
||||||
</cluster>
|
</cluster>
|
||||||
|
|||||||
@@ -16,8 +16,19 @@ feature -- Access
|
|||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get (a_url: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := new_session (a_url).get ("", ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
custom (a_method: READABLE_STRING_8; a_url: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
-- Response for `a_method' request based on `a_url' and optional `ctx'.
|
||||||
|
do
|
||||||
|
Result := new_session (a_url).custom (a_method, "", ctx)
|
||||||
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2017, 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
|
||||||
|
|||||||
@@ -58,11 +58,11 @@ feature -- Access
|
|||||||
-- Specific headers to use in addition to the one set in the related HTTP_CLIENT_SESSION
|
-- Specific headers to use in addition to the one set in the related HTTP_CLIENT_SESSION
|
||||||
--| note: the value from Current context override the one from the session in case of conflict
|
--| note: the value from Current context override the one from the session in case of conflict
|
||||||
|
|
||||||
query_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
|
query_parameters: HTTP_CLIENT_REQUEST_QUERY_PARAMETERS
|
||||||
-- Query parameters to be appended to the url
|
-- Query parameters to be appended to the url
|
||||||
--| note: if the url already contains a query_string, the `query_parameters' will be appended to the url
|
--| note: if the url already contains a query_string, the `query_parameters' will be appended to the url
|
||||||
|
|
||||||
form_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
|
form_parameters: HTTP_CLIENT_REQUEST_FORM_PARAMETERS
|
||||||
-- Form parameters
|
-- Form parameters
|
||||||
|
|
||||||
upload_data: detachable READABLE_STRING_8
|
upload_data: detachable READABLE_STRING_8
|
||||||
@@ -145,13 +145,25 @@ feature -- Element change
|
|||||||
add_query_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL)
|
add_query_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL)
|
||||||
-- Add a query parameter `k=v'.
|
-- Add a query parameter `k=v'.
|
||||||
do
|
do
|
||||||
query_parameters.force (v.to_string_32, k.to_string_32)
|
query_parameters.force (create {HTTP_CLIENT_REQUEST_STRING_PARAMETER}.make (k, v))
|
||||||
end
|
end
|
||||||
|
|
||||||
add_form_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL)
|
add_form_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL)
|
||||||
-- Add a form parameter `k'= `v'.
|
-- Add a form parameter `k'= `v'.
|
||||||
do
|
do
|
||||||
form_parameters.force (v.to_string_32, k.to_string_32)
|
form_parameters.force (create {HTTP_CLIENT_REQUEST_STRING_PARAMETER}.make (k, v))
|
||||||
|
end
|
||||||
|
|
||||||
|
add_file_form_parameter (k: READABLE_STRING_GENERAL; a_location: READABLE_STRING_GENERAL; a_content_type: detachable READABLE_STRING_8)
|
||||||
|
-- Add a form file parameter named `k`, located at `a_location`, with optional content type `a_content_type`.
|
||||||
|
require
|
||||||
|
has_no_upload_data_or_filename: not has_upload_data and not has_upload_filename
|
||||||
|
local
|
||||||
|
param: HTTP_CLIENT_REQUEST_FILE_PARAMETER
|
||||||
|
do
|
||||||
|
create param.make_with_path (k, create {PATH}.make_from_string (a_location))
|
||||||
|
param.set_content_type (a_content_type)
|
||||||
|
form_parameters.force (param)
|
||||||
end
|
end
|
||||||
|
|
||||||
set_credentials_required (b: BOOLEAN)
|
set_credentials_required (b: BOOLEAN)
|
||||||
@@ -164,7 +176,8 @@ feature -- Element change
|
|||||||
-- Set `upload_data' to `a_data'
|
-- Set `upload_data' to `a_data'
|
||||||
--| note: the Current context can have upload_data XOR upload_filename, but not both.
|
--| note: the Current context can have upload_data XOR upload_filename, but not both.
|
||||||
require
|
require
|
||||||
has_upload_filename: (a_data /= Void and then not a_data.is_empty) implies not has_upload_filename
|
has_no_upload_filename: (a_data /= Void and then not a_data.is_empty) implies not has_upload_filename
|
||||||
|
has_no_form_data: (a_data /= Void and then not a_data.is_empty) implies not has_form_data
|
||||||
do
|
do
|
||||||
if a_data = Void or else a_data.is_empty then
|
if a_data = Void or else a_data.is_empty then
|
||||||
upload_data := Void
|
upload_data := Void
|
||||||
@@ -180,6 +193,7 @@ feature -- Element change
|
|||||||
--| note: the Current context can have upload_data XOR upload_filename, but not both.
|
--| note: the Current context can have upload_data XOR upload_filename, but not both.
|
||||||
require
|
require
|
||||||
has_no_upload_data: (a_fn /= Void and then not a_fn.is_empty) implies not has_upload_data
|
has_no_upload_data: (a_fn /= Void and then not a_fn.is_empty) implies not has_upload_data
|
||||||
|
has_no_form_data: (a_fn /= Void and then not a_fn.is_empty) implies not has_form_data
|
||||||
do
|
do
|
||||||
if a_fn = Void or else a_fn.is_empty then
|
if a_fn = Void or else a_fn.is_empty then
|
||||||
upload_filename := Void
|
upload_filename := Void
|
||||||
@@ -266,9 +280,9 @@ feature -- URL helpers
|
|||||||
a_url.append_character ('&')
|
a_url.append_character ('&')
|
||||||
end
|
end
|
||||||
l_first_param := False
|
l_first_param := False
|
||||||
uri_percent_encoder.append_query_name_encoded_string_to (ic.key, a_url)
|
uri_percent_encoder.append_query_name_encoded_string_to (ic.item.name, a_url)
|
||||||
a_url.append_character ('=')
|
a_url.append_character ('=')
|
||||||
uri_percent_encoder.append_query_value_encoded_string_to (ic.item, a_url)
|
ic.item.append_query_value_encoded_to (a_url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -315,38 +329,35 @@ feature {NONE} -- Implementation
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
parameters_to_uri_percent_encoded_string (ht: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING_8
|
parameters_to_uri_percent_encoded_string (a_params: HTTP_CLIENT_REQUEST_PARAMETERS [HTTP_CLIENT_REQUEST_PARAMETER]): STRING_8
|
||||||
-- Build query urlencoded string using parameters from `ht'.
|
-- Build query urlencoded string using parameters from `a_params'.
|
||||||
do
|
do
|
||||||
create Result.make (64)
|
create Result.make (64)
|
||||||
across
|
across
|
||||||
ht as ic
|
a_params as ic
|
||||||
loop
|
loop
|
||||||
if not Result.is_empty then
|
if not Result.is_empty then
|
||||||
Result.append_character ('&')
|
Result.append_character ('&')
|
||||||
end
|
end
|
||||||
uri_percent_encoder.append_query_name_encoded_string_to (ic.key, Result)
|
uri_percent_encoder.append_query_name_encoded_string_to (ic.item.name, Result)
|
||||||
Result.append_character ('=')
|
Result.append_character ('=')
|
||||||
uri_percent_encoder.append_query_value_encoded_string_to (ic.item, Result)
|
ic.item.append_query_value_encoded_to (Result)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
parameters_to_x_www_form_urlencoded_string (ht: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING_8
|
parameters_to_x_www_form_urlencoded_string (a_params: HTTP_CLIENT_REQUEST_PARAMETERS [HTTP_CLIENT_REQUEST_PARAMETER]): STRING_8
|
||||||
-- Build x-www-form-urlencoded string using parameters from `ht'.
|
-- Build x-www-form-urlencoded string using parameters from `a_params'.
|
||||||
do
|
do
|
||||||
create Result.make (64)
|
create Result.make (64)
|
||||||
from
|
across
|
||||||
ht.start
|
a_params as ic
|
||||||
until
|
|
||||||
ht.after
|
|
||||||
loop
|
loop
|
||||||
if not Result.is_empty then
|
if not Result.is_empty then
|
||||||
Result.append_character ('&')
|
Result.append_character ('&')
|
||||||
end
|
end
|
||||||
Result.append (x_www_form_url_encoder.encoded_string (ht.key_for_iteration))
|
x_www_form_url_encoder.append_percent_encoded_string_to (ic.item.name, Result)
|
||||||
Result.append_character ('=')
|
Result.append_character ('=')
|
||||||
Result.append (x_www_form_url_encoder.encoded_string (ht.item_for_iteration))
|
ic.item.append_form_url_encoded_to (Result)
|
||||||
ht.forth
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_CLIENT_REQUEST_FORM_PARAMETERS}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
HTTP_CLIENT_REQUEST_FORM_PARAMETERS
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT_REQUEST_PARAMETERS [HTTP_CLIENT_REQUEST_PARAMETER]
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
has_file_parameter: BOOLEAN
|
||||||
|
-- Has any file parameter?
|
||||||
|
do
|
||||||
|
Result := across items as ic some attached {HTTP_CLIENT_REQUEST_FILE_PARAMETER} ic.item end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, 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,71 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_CLIENT_REQUEST_PARAMETER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
HTTP_CLIENT_REQUEST_PARAMETER
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
name: READABLE_STRING_32
|
||||||
|
|
||||||
|
content_type: detachable READABLE_STRING_8
|
||||||
|
|
||||||
|
count: INTEGER
|
||||||
|
-- Integer representing the length of source value.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Conversion
|
||||||
|
|
||||||
|
append_form_url_encoded_to (a_output: STRING_8)
|
||||||
|
-- Append as form url encoded string to `a_output`.
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
append_query_value_encoded_to (a_output: STRING_8)
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
append_as_mime_encoded_to (a_output: STRING_8)
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_name (a_name: READABLE_STRING_GENERAL)
|
||||||
|
do
|
||||||
|
name := a_name.as_string_32
|
||||||
|
end
|
||||||
|
|
||||||
|
set_content_type (ct: detachable READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
content_type := ct
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
x_www_form_url_encoder: X_WWW_FORM_URL_ENCODER
|
||||||
|
-- Shared x-www-form-urlencoded encoder.
|
||||||
|
once
|
||||||
|
create Result
|
||||||
|
end
|
||||||
|
|
||||||
|
uri_percent_encoder: URI_PERCENT_ENCODER
|
||||||
|
once
|
||||||
|
create Result
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, 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,62 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_CLIENT_REQUEST_PARAMETERS}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
HTTP_CLIENT_REQUEST_PARAMETERS [G -> HTTP_CLIENT_REQUEST_PARAMETER]
|
||||||
|
|
||||||
|
inherit
|
||||||
|
ITERABLE [G]
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (nb: INTEGER)
|
||||||
|
do
|
||||||
|
create items.make (nb)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
is_empty: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := items.is_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
count: INTEGER
|
||||||
|
do
|
||||||
|
Result := items.count
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
extend, force (i: G)
|
||||||
|
do
|
||||||
|
items.force (i)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Iteration
|
||||||
|
|
||||||
|
new_cursor: ARRAYED_LIST_ITERATION_CURSOR [G]
|
||||||
|
-- <Precursor>
|
||||||
|
do
|
||||||
|
Result := items.new_cursor
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
items: ARRAYED_LIST [G]
|
||||||
|
|
||||||
|
invariant
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, 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,26 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_CLIENT_REQUEST_QUERY_PARAMETERS}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
HTTP_CLIENT_REQUEST_QUERY_PARAMETERS
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT_REQUEST_PARAMETERS [HTTP_CLIENT_REQUEST_STRING_PARAMETER]
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, 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,155 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_CLIENT_REQUEST_FILE_PARAMETER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
HTTP_CLIENT_REQUEST_FILE_PARAMETER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT_REQUEST_PARAMETER
|
||||||
|
|
||||||
|
create
|
||||||
|
make_with_path
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make_with_path (a_name: READABLE_STRING_GENERAL; a_path: PATH)
|
||||||
|
do
|
||||||
|
set_name (a_name)
|
||||||
|
location := a_path
|
||||||
|
if attached a_path.entry as e then
|
||||||
|
file_name := e.name
|
||||||
|
end
|
||||||
|
set_content_type ("application/octet-stream") -- Default
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
count: INTEGER
|
||||||
|
local
|
||||||
|
f: RAW_FILE
|
||||||
|
do
|
||||||
|
create f.make_with_path (location)
|
||||||
|
if f.exists and then f.is_access_readable then
|
||||||
|
Result := f.count
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
location: PATH
|
||||||
|
|
||||||
|
file_name: detachable READABLE_STRING_32
|
||||||
|
|
||||||
|
feature -- Element change
|
||||||
|
|
||||||
|
set_file_name (fn: detachable READABLE_STRING_GENERAL)
|
||||||
|
do
|
||||||
|
if fn = Void then
|
||||||
|
file_name := Void
|
||||||
|
else
|
||||||
|
file_name := fn.to_string_32
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
exists: BOOLEAN
|
||||||
|
local
|
||||||
|
fut: FILE_UTILITIES
|
||||||
|
do
|
||||||
|
Result := fut.file_path_exists (location)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Data
|
||||||
|
|
||||||
|
file_content: detachable STRING_8
|
||||||
|
require
|
||||||
|
exists: exists
|
||||||
|
local
|
||||||
|
f: RAW_FILE
|
||||||
|
do
|
||||||
|
create f.make_with_path (location)
|
||||||
|
if f.exists and then f.is_access_readable then
|
||||||
|
create Result.make (f.count)
|
||||||
|
f.open_read
|
||||||
|
from
|
||||||
|
until
|
||||||
|
f.exhausted or f.end_of_file
|
||||||
|
loop
|
||||||
|
f.read_stream_thread_aware (2_048)
|
||||||
|
Result.append (f.last_string)
|
||||||
|
end
|
||||||
|
f.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Data
|
||||||
|
|
||||||
|
append_file_content_to (a_output: STRING)
|
||||||
|
-- Append content of file located at `location`to `a_output'.
|
||||||
|
require
|
||||||
|
exists: exists
|
||||||
|
local
|
||||||
|
f: RAW_FILE
|
||||||
|
l_buffer_size: INTEGER
|
||||||
|
do
|
||||||
|
create f.make_with_path (location)
|
||||||
|
if f.exists and then f.is_access_readable then
|
||||||
|
f.open_read
|
||||||
|
from
|
||||||
|
l_buffer_size := 2_048
|
||||||
|
until
|
||||||
|
f.exhausted or f.end_of_file
|
||||||
|
loop
|
||||||
|
f.read_stream_thread_aware (l_buffer_size)
|
||||||
|
a_output.append (f.last_string)
|
||||||
|
end
|
||||||
|
f.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Conversion
|
||||||
|
|
||||||
|
append_form_url_encoded_to (a_output: STRING_8)
|
||||||
|
-- Append as form url encoded string to `a_output`.
|
||||||
|
do
|
||||||
|
if exists and then attached file_content as s then
|
||||||
|
x_www_form_url_encoder.append_percent_encoded_string_to (s, a_output)
|
||||||
|
else
|
||||||
|
check exists: False end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append_query_value_encoded_to (a_output: STRING_8)
|
||||||
|
do
|
||||||
|
if exists and then attached file_content as s then
|
||||||
|
uri_percent_encoder.append_query_value_encoded_string_to (s, a_output)
|
||||||
|
else
|
||||||
|
check exists: False end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append_as_mime_encoded_to (a_output: STRING_8)
|
||||||
|
-- Encoded unicode string for mime value.
|
||||||
|
-- For instance uploaded filename, or form data key or values.
|
||||||
|
do
|
||||||
|
-- FIXME: find the proper encoding!
|
||||||
|
if exists then
|
||||||
|
append_file_content_to (a_output)
|
||||||
|
else
|
||||||
|
check exists: False end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, 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,68 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {HTTP_CLIENT_REQUEST_STRING_PARAMETER}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
HTTP_CLIENT_REQUEST_STRING_PARAMETER
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT_REQUEST_PARAMETER
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_name, a_value: READABLE_STRING_GENERAL)
|
||||||
|
do
|
||||||
|
set_name (a_name)
|
||||||
|
value := a_value.as_string_32
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
value: READABLE_STRING_32
|
||||||
|
|
||||||
|
count: INTEGER
|
||||||
|
do
|
||||||
|
Result := value.count
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Conversion
|
||||||
|
|
||||||
|
append_form_url_encoded_to (a_output: STRING_8)
|
||||||
|
-- Append as form url encoded string to `a_output`.
|
||||||
|
do
|
||||||
|
x_www_form_url_encoder.append_percent_encoded_string_to (value, a_output)
|
||||||
|
end
|
||||||
|
|
||||||
|
append_query_value_encoded_to (a_output: STRING_8)
|
||||||
|
do
|
||||||
|
uri_percent_encoder.append_query_value_encoded_string_to (value, a_output)
|
||||||
|
end
|
||||||
|
|
||||||
|
append_as_mime_encoded_to (a_output: STRING_8)
|
||||||
|
-- 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!
|
||||||
|
utf.utf_32_string_into_utf_8_string_8 (value, a_output)
|
||||||
|
end
|
||||||
|
|
||||||
|
invariant
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, 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
|
||||||
@@ -58,7 +58,6 @@ feature -- Execution
|
|||||||
ctx: like context
|
ctx: like context
|
||||||
p_slist: POINTER
|
p_slist: POINTER
|
||||||
retried: BOOLEAN
|
retried: BOOLEAN
|
||||||
l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
|
|
||||||
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
|
||||||
@@ -151,70 +150,19 @@ feature -- Execution
|
|||||||
--| Credentials not provided ...
|
--| Credentials not provided ...
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ctx.has_upload_data then
|
if ctx.has_upload_data then
|
||||||
l_upload_data := ctx.upload_data
|
l_upload_data := ctx.upload_data
|
||||||
end
|
end
|
||||||
if ctx.has_upload_filename then
|
if ctx.has_upload_filename then
|
||||||
l_upload_filename := ctx.upload_filename
|
l_upload_filename := ctx.upload_filename
|
||||||
end
|
end
|
||||||
if ctx.has_form_data then
|
|
||||||
l_form_data := ctx.form_parameters
|
|
||||||
check non_empty_form_data: not l_form_data.is_empty end
|
|
||||||
if l_upload_data = Void and l_upload_filename = Void then
|
|
||||||
-- Send as form-urlencoded
|
|
||||||
if
|
|
||||||
attached l_headers.item ("Content-Type") as l_ct
|
|
||||||
then
|
|
||||||
if l_ct.starts_with ("application/x-www-form-urlencoded") then
|
|
||||||
-- Content-Type is already application/x-www-form-urlencoded
|
|
||||||
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
|
||||||
elseif l_ct.starts_with ("multipart/form-data") then
|
|
||||||
l_use_curl_form := True
|
|
||||||
else
|
|
||||||
-- Not supported, use libcurl form.
|
|
||||||
l_use_curl_form := True
|
|
||||||
end
|
|
||||||
else
|
|
||||||
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
|
||||||
end
|
|
||||||
else
|
|
||||||
l_use_curl_form := True
|
|
||||||
end
|
|
||||||
if l_use_curl_form then
|
|
||||||
create l_form.make
|
|
||||||
create l_last.make
|
|
||||||
from
|
|
||||||
l_form_data.start
|
|
||||||
until
|
|
||||||
l_form_data.after
|
|
||||||
loop
|
|
||||||
curl.formadd_string_string (l_form, l_last,
|
|
||||||
{CURL_FORM_CONSTANTS}.curlform_copyname, l_form_data.key_for_iteration,
|
|
||||||
{CURL_FORM_CONSTANTS}.curlform_copycontents, l_form_data.item_for_iteration,
|
|
||||||
{CURL_FORM_CONSTANTS}.curlform_end
|
|
||||||
)
|
|
||||||
l_form_data.forth
|
|
||||||
end
|
|
||||||
if l_upload_filename /= Void then
|
|
||||||
curl.formadd_string_string (l_form, l_last,
|
|
||||||
{CURL_FORM_CONSTANTS}.curlform_copyname, "file",
|
|
||||||
{CURL_FORM_CONSTANTS}.curlform_file, l_upload_filename,
|
|
||||||
{CURL_FORM_CONSTANTS}.curlform_end
|
|
||||||
)
|
|
||||||
l_upload_filename := Void
|
|
||||||
end
|
|
||||||
l_last.release_item
|
|
||||||
curl_easy.setopt_form (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httppost, l_form)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if l_upload_data /= Void then
|
if l_upload_data /= Void then
|
||||||
check
|
check
|
||||||
post_or_put_request_method: request_method.is_case_insensitive_equal ("POST")
|
post_or_put_request_method: request_method.is_case_insensitive_equal ("POST")
|
||||||
or request_method.is_case_insensitive_equal ("PUT")
|
or request_method.is_case_insensitive_equal ("PUT")
|
||||||
or request_method.is_case_insensitive_equal ("PATCH")
|
or request_method.is_case_insensitive_equal ("PATCH")
|
||||||
end
|
end
|
||||||
|
check no_form_data: not ctx.has_form_data end
|
||||||
|
|
||||||
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfields, l_upload_data)
|
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfields, l_upload_data)
|
||||||
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfieldsize, l_upload_data.count)
|
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_postfieldsize, l_upload_data.count)
|
||||||
@@ -224,6 +172,7 @@ feature -- Execution
|
|||||||
or request_method.is_case_insensitive_equal ("PUT")
|
or request_method.is_case_insensitive_equal ("PUT")
|
||||||
or request_method.is_case_insensitive_equal ("PATCH")
|
or request_method.is_case_insensitive_equal ("PATCH")
|
||||||
end
|
end
|
||||||
|
check no_form_data: not ctx.has_form_data end
|
||||||
|
|
||||||
create l_upload_file.make_with_name (l_upload_filename)
|
create l_upload_file.make_with_name (l_upload_filename)
|
||||||
if l_upload_file.exists and then l_upload_file.is_readable then
|
if l_upload_file.exists and then l_upload_file.is_readable then
|
||||||
@@ -238,12 +187,59 @@ feature -- Execution
|
|||||||
l_upload_file.open_read
|
l_upload_file.open_read
|
||||||
curl_easy.set_curl_function (l_custom_function)
|
curl_easy.set_curl_function (l_custom_function)
|
||||||
end
|
end
|
||||||
|
elseif
|
||||||
|
ctx.has_form_data and
|
||||||
|
attached ctx.form_parameters as l_form_data
|
||||||
|
then
|
||||||
|
check non_empty_form_data: not l_form_data.is_empty end
|
||||||
|
-- Send as form-urlencoded
|
||||||
|
if
|
||||||
|
attached l_headers.item ("Content-Type") as l_ct
|
||||||
|
then
|
||||||
|
if l_ct.starts_with ("application/x-www-form-urlencoded") then
|
||||||
|
-- Content-Type is already application/x-www-form-urlencoded
|
||||||
|
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
||||||
|
elseif l_ct.starts_with ("multipart/form-data") or l_form_data.has_file_parameter then
|
||||||
|
l_use_curl_form := True
|
||||||
|
else
|
||||||
|
-- Not supported, use libcurl form.
|
||||||
|
l_use_curl_form := True
|
||||||
|
end
|
||||||
|
else
|
||||||
|
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
||||||
|
end
|
||||||
|
if l_use_curl_form then
|
||||||
|
create l_form.make
|
||||||
|
create l_last.make
|
||||||
|
across
|
||||||
|
l_form_data as ic
|
||||||
|
loop
|
||||||
|
if attached {HTTP_CLIENT_REQUEST_STRING_PARAMETER} ic.item as strparam then
|
||||||
|
curl.formadd_string_string (l_form, l_last,
|
||||||
|
{CURL_FORM_CONSTANTS}.curlform_copyname, strparam.name,
|
||||||
|
{CURL_FORM_CONSTANTS}.curlform_copycontents, strparam.value,
|
||||||
|
{CURL_FORM_CONSTANTS}.curlform_end
|
||||||
|
)
|
||||||
|
elseif attached {HTTP_CLIENT_REQUEST_FILE_PARAMETER} ic.item as fileparam then
|
||||||
|
curl.formadd_string_string (l_form, l_last,
|
||||||
|
{CURL_FORM_CONSTANTS}.curlform_copyname, "file",
|
||||||
|
{CURL_FORM_CONSTANTS}.curlform_file, fileparam.location.name,
|
||||||
|
{CURL_FORM_CONSTANTS}.curlform_end
|
||||||
|
)
|
||||||
|
else
|
||||||
|
check supported_parameter_type: False end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
l_last.release_item
|
||||||
|
curl_easy.setopt_form (curl_handle, {CURL_OPT_CONSTANTS}.curlopt_httppost, l_form)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
check no_upload_data: l_upload_data = Void and l_upload_filename = Void end
|
-- No form, or upload data to send!
|
||||||
|
check no_data: not (ctx.has_upload_data or ctx.has_upload_filename or ctx.has_form_data) end
|
||||||
end
|
end
|
||||||
end -- ctx /= Void
|
end -- ctx /= Void
|
||||||
|
|
||||||
--| Header
|
--| Header
|
||||||
across
|
across
|
||||||
l_headers as curs
|
l_headers as curs
|
||||||
loop
|
loop
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ feature -- Access
|
|||||||
l_authorization: HTTP_AUTHORIZATION
|
l_authorization: HTTP_AUTHORIZATION
|
||||||
l_platform: STRING
|
l_platform: STRING
|
||||||
l_upload_data: detachable READABLE_STRING_8
|
l_upload_data: detachable READABLE_STRING_8
|
||||||
l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
|
|
||||||
ctx: like context
|
ctx: like context
|
||||||
|
l_ct: detachable READABLE_STRING_8
|
||||||
l_upload_file: detachable RAW_FILE
|
l_upload_file: detachable RAW_FILE
|
||||||
l_upload_filename: detachable READABLE_STRING_GENERAL
|
l_upload_filename: detachable READABLE_STRING_GENERAL
|
||||||
l_form_string: STRING
|
l_form_string: STRING
|
||||||
@@ -149,7 +149,7 @@ feature -- Access
|
|||||||
then
|
then
|
||||||
create l_authorization.make_basic_auth (u_name, u_pass)
|
create l_authorization.make_basic_auth (u_name, u_pass)
|
||||||
if attached l_authorization.http_authorization as auth then
|
if attached l_authorization.http_authorization as auth then
|
||||||
headers.extend (auth, "Authorization")
|
headers.force (auth, "Authorization")
|
||||||
end
|
end
|
||||||
check headers.has_key ("Authorization") end
|
check headers.has_key ("Authorization") end
|
||||||
end
|
end
|
||||||
@@ -176,7 +176,7 @@ feature -- Access
|
|||||||
else
|
else
|
||||||
l_platform := "Unknown"
|
l_platform := "Unknown"
|
||||||
end
|
end
|
||||||
headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
|
headers.force ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- handle sending data
|
-- handle sending data
|
||||||
@@ -191,67 +191,52 @@ feature -- Access
|
|||||||
l_upload_data := ctx.upload_data
|
l_upload_data := ctx.upload_data
|
||||||
end
|
end
|
||||||
|
|
||||||
if ctx.has_form_data then
|
if l_upload_data /= Void then
|
||||||
l_form_data := ctx.form_parameters
|
|
||||||
if l_upload_data = Void and l_upload_filename = Void then
|
|
||||||
if
|
|
||||||
attached headers.item ("Content-Type") as l_ct
|
|
||||||
then
|
|
||||||
if l_ct.starts_with ("application/x-www-form-urlencoded") then
|
|
||||||
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
|
||||||
elseif l_ct.starts_with ("multipart/form-data") then
|
|
||||||
-- create form using multipart/form-data encoding
|
|
||||||
l_boundary := new_mime_boundary (l_form_data)
|
|
||||||
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
|
|
||||||
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
|
|
||||||
else
|
|
||||||
-- not supported !
|
|
||||||
-- Send as form-urlencoded
|
|
||||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
|
||||||
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Send as form-urlencoded
|
|
||||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
|
||||||
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
|
||||||
end
|
|
||||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
|
||||||
if l_is_chunked_transfer_encoding then
|
|
||||||
-- Discard chunked transfer encoding
|
|
||||||
headers.remove ("Transfer-Encoding")
|
|
||||||
l_is_chunked_transfer_encoding := False
|
|
||||||
end
|
|
||||||
elseif l_form_data /= Void then
|
|
||||||
check l_upload_data = Void end
|
|
||||||
|
|
||||||
-- create form using multipart/form-data encoding
|
|
||||||
l_boundary := new_mime_boundary (l_form_data)
|
|
||||||
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
|
|
||||||
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")
|
|
||||||
if l_is_chunked_transfer_encoding then
|
|
||||||
-- Discard chunked transfer encoding
|
|
||||||
headers.remove ("Transfer-Encoding")
|
|
||||||
l_is_chunked_transfer_encoding := False
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif l_upload_data /= Void then
|
|
||||||
check ctx.has_upload_data end
|
check ctx.has_upload_data end
|
||||||
|
check no_form_data: not ctx.has_form_data end
|
||||||
if not headers.has ("Content-Type") then
|
if not headers.has ("Content-Type") then
|
||||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
headers.force ("application/x-www-form-urlencoded", "Content-Type")
|
||||||
end
|
end
|
||||||
if not l_is_chunked_transfer_encoding then
|
if not l_is_chunked_transfer_encoding then
|
||||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
headers.force (l_upload_data.count.out, "Content-Length")
|
||||||
end
|
end
|
||||||
elseif l_upload_filename /= Void then
|
elseif l_upload_filename /= Void then
|
||||||
check ctx.has_upload_filename end
|
check ctx.has_upload_filename end
|
||||||
|
check no_form_data: not ctx.has_form_data end
|
||||||
create l_upload_file.make_with_name (l_upload_filename)
|
create l_upload_file.make_with_name (l_upload_filename)
|
||||||
if l_upload_file.exists and then l_upload_file.is_access_readable then
|
if l_upload_file.exists and then l_upload_file.is_access_readable then
|
||||||
if not l_is_chunked_transfer_encoding then
|
if not l_is_chunked_transfer_encoding then
|
||||||
headers.extend (l_upload_file.count.out, "Content-Length")
|
headers.force (l_upload_file.count.out, "Content-Length")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
check l_upload_file /= Void end
|
check l_upload_file /= Void end
|
||||||
|
elseif
|
||||||
|
ctx.has_form_data and
|
||||||
|
attached ctx.form_parameters as l_form_data
|
||||||
|
then
|
||||||
|
l_ct := headers.item ("Content-Type")
|
||||||
|
if l_ct /= Void and then l_ct.starts_with ("application/x-www-form-urlencoded") then
|
||||||
|
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
||||||
|
elseif
|
||||||
|
(l_ct /= Void and then l_ct.starts_with ("multipart/form-data"))
|
||||||
|
or l_form_data.has_file_parameter
|
||||||
|
then
|
||||||
|
-- create form using multipart/form-data encoding
|
||||||
|
l_boundary := new_mime_boundary (l_form_data)
|
||||||
|
headers.force ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
|
||||||
|
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_boundary)
|
||||||
|
else
|
||||||
|
-- not supported !
|
||||||
|
-- Send as form-urlencoded
|
||||||
|
headers.force ("application/x-www-form-urlencoded", "Content-Type")
|
||||||
|
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
||||||
|
end
|
||||||
|
headers.force (l_upload_data.count.out, "Content-Length")
|
||||||
|
if l_is_chunked_transfer_encoding then
|
||||||
|
-- Discard chunked transfer encoding
|
||||||
|
headers.remove ("Transfer-Encoding")
|
||||||
|
l_is_chunked_transfer_encoding := False
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -482,14 +467,9 @@ feature {NONE} -- Helpers
|
|||||||
Result := a_status >= 300 and a_status < 400
|
Result := a_status >= 300 and a_status < 400
|
||||||
end
|
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_date_and_uploaded_files_to_mime_string (a_form_parameters: ITERABLE [HTTP_CLIENT_REQUEST_PARAMETER]; a_mime_boundary: READABLE_STRING_8): STRING
|
||||||
-- Form data and uploaded files converted to mime string.
|
-- Form data and uploaded files converted to mime string.
|
||||||
-- TODO: design a proper MIME... component.
|
-- 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
|
do
|
||||||
create Result.make (100)
|
create Result.make (100)
|
||||||
across
|
across
|
||||||
@@ -500,48 +480,26 @@ feature {NONE} -- Helpers
|
|||||||
Result.append (http_end_of_header_line)
|
Result.append (http_end_of_header_line)
|
||||||
Result.append ("Content-Disposition: form-data; name=")
|
Result.append ("Content-Disposition: form-data; name=")
|
||||||
Result.append_character ('%"')
|
Result.append_character ('%"')
|
||||||
Result.append (string_to_mime_encoded_string (ic.key))
|
Result.append (string_to_mime_encoded_string (ic.item.name))
|
||||||
Result.append_character ('%"')
|
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
|
if
|
||||||
attached l_path.extension as ext and then
|
attached {HTTP_CLIENT_REQUEST_FILE_PARAMETER} ic.item as fileparam and then
|
||||||
attached l_mime_type_mapping.mime_type (ext) as l_mt
|
attached fileparam.file_name as fn
|
||||||
then
|
then
|
||||||
l_mime_type := l_mt
|
Result.append ("; filename=")
|
||||||
else
|
Result.append_character ('%"')
|
||||||
l_mime_type := "application/octet-stream"
|
Result.append (string_to_mime_encoded_string (fn))
|
||||||
|
Result.append_character ('%"')
|
||||||
end
|
end
|
||||||
Result.append ("--")
|
if attached ic.item.content_type as ct then
|
||||||
Result.append (a_mime_boundary)
|
Result.append (http_end_of_header_line)
|
||||||
Result.append (http_end_of_header_line)
|
Result.append ("Content-Type: ")
|
||||||
Result.append ("Content-Disposition: form-data; name=%"")
|
Result.append (ct)
|
||||||
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
|
end
|
||||||
Result.append (http_end_of_header_line)
|
Result.append (http_end_of_header_line)
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
|
ic.item.append_as_mime_encoded_to (Result)
|
||||||
|
Result.append (http_end_of_header_line)
|
||||||
end
|
end
|
||||||
Result.append ("--")
|
Result.append ("--")
|
||||||
Result.append (a_mime_boundary)
|
Result.append (a_mime_boundary)
|
||||||
@@ -893,7 +851,7 @@ feature {NONE} -- Helpers
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
new_mime_boundary (a_data: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING
|
new_mime_boundary (a_data: ITERABLE [HTTP_CLIENT_REQUEST_PARAMETER]): STRING
|
||||||
-- New MIME boundary.
|
-- New MIME boundary.
|
||||||
local
|
local
|
||||||
s: STRING
|
s: STRING
|
||||||
@@ -904,7 +862,7 @@ feature {NONE} -- Helpers
|
|||||||
across
|
across
|
||||||
a_data as ic
|
a_data as ic
|
||||||
loop
|
loop
|
||||||
i := i + ic.item.count + ic.key.count
|
i := i + ic.item.count + ic.item.name.count
|
||||||
end
|
end
|
||||||
create ran.set_seed (i) -- FIXME: use a real random seed.
|
create ran.set_seed (i) -- FIXME: use a real random seed.
|
||||||
ran.start
|
ran.start
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?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-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="test_http_client" uuid="920E5C50-41E1-4DAC-8D48-D9C860E49228">
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="testing_http_client" uuid="920E5C50-41E1-4DAC-8D48-D9C860E49228">
|
||||||
<target name="test_http_client">
|
<target name="testing_http_client">
|
||||||
<root class="TEST" feature="make"/>
|
<root class="TEST" feature="make"/>
|
||||||
<file_rule>
|
<file_rule>
|
||||||
<exclude>/.git$</exclude>
|
<exclude>/.git$</exclude>
|
||||||
@@ -10,7 +10,8 @@
|
|||||||
<option warning="true" void_safety="all">
|
<option warning="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="ssl_enabled" value="true"/>
|
||||||
|
<variable name="netssl_http_client_enabled" value="true"/>
|
||||||
<variable name="net_http_client_disabled" value="false"/>
|
<variable name="net_http_client_disabled" value="false"/>
|
||||||
<variable name="libcurl_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"/>
|
||||||
@@ -59,6 +59,11 @@ feature -- Tests
|
|||||||
test_post_with_file_and_form_data
|
test_post_with_file_and_form_data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
libcurl_test_post_with_multiple_file_and_form_data
|
||||||
|
do
|
||||||
|
test_post_with_multiple_file_and_form_data
|
||||||
|
end
|
||||||
|
|
||||||
libcurl_test_get_with_redirection
|
libcurl_test_get_with_redirection
|
||||||
do
|
do
|
||||||
test_get_with_redirection
|
test_get_with_redirection
|
||||||
|
|||||||
@@ -59,6 +59,11 @@ feature -- Tests
|
|||||||
test_post_with_file_and_form_data
|
test_post_with_file_and_form_data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
net_test_post_with_multiple_file_and_form_data
|
||||||
|
do
|
||||||
|
test_post_with_multiple_file_and_form_data
|
||||||
|
end
|
||||||
|
|
||||||
net_test_get_with_redirection
|
net_test_get_with_redirection
|
||||||
do
|
do
|
||||||
test_get_with_redirection
|
test_get_with_redirection
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ feature -- Initialization
|
|||||||
on_prepare
|
on_prepare
|
||||||
do
|
do
|
||||||
Precursor
|
Precursor
|
||||||
global_requestbin_path := "/s0jkhhs0"
|
if is_using_requestbin and global_requestbin_path = Void then
|
||||||
if global_requestbin_path = Void then
|
|
||||||
global_requestbin_path := new_requestbin_path
|
global_requestbin_path := new_requestbin_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -33,7 +32,13 @@ feature -- Factory
|
|||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Requestbin
|
feature -- Requestbin
|
||||||
|
|
||||||
|
is_using_requestbin: BOOLEAN = False
|
||||||
|
is_using_mockbincom: BOOLEAN
|
||||||
|
do
|
||||||
|
Result := not is_using_requestbin
|
||||||
|
end
|
||||||
|
|
||||||
global_requestbin_path: detachable READABLE_STRING_8
|
global_requestbin_path: detachable READABLE_STRING_8
|
||||||
|
|
||||||
@@ -42,7 +47,7 @@ feature -- Requestbin
|
|||||||
i,j: INTEGER
|
i,j: INTEGER
|
||||||
do
|
do
|
||||||
if
|
if
|
||||||
attached new_session ("http://requestb.in") as sess and then
|
attached new_session ("https://requestb.in") as sess and then
|
||||||
attached sess.post ("/api/v1/bins", Void, Void) as resp
|
attached sess.post ("/api/v1/bins", Void, Void) as resp
|
||||||
then
|
then
|
||||||
if resp.error_occurred then
|
if resp.error_occurred then
|
||||||
@@ -67,13 +72,30 @@ feature -- Requestbin
|
|||||||
if not Result.starts_with ("/") then
|
if not Result.starts_with ("/") then
|
||||||
Result.prepend_character ('/')
|
Result.prepend_character ('/')
|
||||||
end
|
end
|
||||||
print ("new_requestbin_path => http://requestb.in" + Result + "?inspect%N")
|
print ("new_requestbin_path => " + sess.base_url + Result + "?inspect%N")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
new_web_session: like new_session
|
||||||
|
do
|
||||||
|
if is_using_mockbincom then
|
||||||
|
Result := new_session ("http://mockbin.com/request")
|
||||||
|
end
|
||||||
|
if Result = Void and is_using_requestbin then
|
||||||
|
if attached global_requestbin_path as l_path then
|
||||||
|
Result := new_session ("https://requestb.in" + l_path)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if Result = Void then
|
||||||
|
Result := new_session ("http://mockbin.com/request") -- Default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Factory
|
feature -- Factory
|
||||||
|
|
||||||
test_post_url_encoded
|
test_post_url_encoded
|
||||||
@@ -81,288 +103,200 @@ feature -- Factory
|
|||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
h: STRING_8
|
||||||
do
|
do
|
||||||
if attached global_requestbin_path as requestbin_path then
|
-- URL ENCODED POST REQUEST
|
||||||
-- URL ENCODED POST REQUEST
|
-- check requestbin to ensure the "Hello World" has been received in the raw body
|
||||||
-- check requestbin to ensure the "Hello World" has been received in the raw body
|
-- also check that User-Agent was sent
|
||||||
-- also check that User-Agent was sent
|
create h.make_empty
|
||||||
create h.make_empty
|
sess := new_web_session
|
||||||
sess := new_session ("http://requestb.in")
|
if
|
||||||
if
|
attached sess.post ("", Void, "Hello World") as res
|
||||||
attached sess.post (requestbin_path, Void, "Hello World") as res and then
|
then
|
||||||
attached res.headers as hds
|
check_response (res)
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
test_post_with_form_data
|
test_post_with_form_data
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
do
|
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
|
||||||
-- POST REQUEST WITH FORM DATA
|
sess := new_web_session
|
||||||
-- check requestbin to ensure the form parameters are correctly received
|
create l_ctx.make
|
||||||
sess := new_session ("http://requestb.in")
|
l_ctx.add_form_parameter ("First Key", "First Value")
|
||||||
create l_ctx.make
|
l_ctx.add_form_parameter ("Second Key", "Second Value")
|
||||||
l_ctx.add_form_parameter ("First Key", "First Value")
|
l_ctx.add_form_parameter ("unicode", {STRING_32} "Hello / 你好 !")
|
||||||
l_ctx.add_form_parameter ("Second Key", "Second Value")
|
l_ctx.add_form_parameter ({STRING_32} "Field 你好 !", "How are you?")
|
||||||
l_ctx.add_form_parameter ("unicode", {STRING_32} "Hello / 你好 !")
|
if
|
||||||
l_ctx.add_form_parameter ({STRING_32} "Field 你好 !", "How are you?")
|
attached sess.post ("", l_ctx, "") as res
|
||||||
create h.make_empty
|
then
|
||||||
if
|
check_response (res)
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
test_post_with_uncommon_form_data
|
test_post_with_uncommon_form_data
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
do
|
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_web_session
|
||||||
|
create l_ctx.make
|
||||||
|
|
||||||
-- POST REQUEST WITH FORM DATA
|
l_ctx.add_form_parameter ("title", "Eiffel World!") -- space and !
|
||||||
-- check requestbin to ensure the form parameters are correctly received
|
l_ctx.add_form_parameter ("path", "foo/bar") -- slash
|
||||||
sess := new_session ("http://requestb.in")
|
l_ctx.add_form_parameter ("unreserved", ":!@[]{}()*") -- ...
|
||||||
create l_ctx.make
|
l_ctx.add_form_parameter ("reserved", "+=?&_#_") -- ...
|
||||||
|
l_ctx.add_form_parameter ("a=b", "a=b") -- equal sign
|
||||||
|
l_ctx.add_form_parameter ("test", "!$&'()*") --
|
||||||
|
l_ctx.add_form_parameter ("lst[a][b]", "[123][456]") -- brackets
|
||||||
|
l_ctx.add_form_parameter ("pos{1,2}", "loc{a,b}") -- curly brackets
|
||||||
|
l_ctx.add_form_parameter ("?foo", "?bar") -- question mark
|
||||||
|
l_ctx.add_form_parameter ("?", "?") -- question mark
|
||||||
|
l_ctx.add_form_parameter ("&bar", "&bar") -- ampersand
|
||||||
|
l_ctx.add_form_parameter ("&", "&") -- ampersand
|
||||||
|
|
||||||
l_ctx.add_form_parameter ("title", "Eiffel World!") -- space and !
|
assert ("form data well generated", l_ctx.form_parameters_to_x_www_form_url_encoded_string.same_string ("title=Eiffel+World!&path=foo%%2Fbar&unreserved=%%3A!%%40%%5B%%5D%%7B%%7D()*&reserved=%%2B%%3D%%3F%%26_%%23_&a%%3Db=a%%3Db&test=!%%24%%26'()*&lst%%5Ba%%5D%%5Bb%%5D=%%5B123%%5D%%5B456%%5D&pos%%7B1%%2C2%%7D=loc%%7Ba%%2Cb%%7D&%%3Ffoo=%%3Fbar&%%3F=%%3F&%%26bar=%%26bar&%%26=%%26"))
|
||||||
l_ctx.add_form_parameter ("path", "foo/bar") -- slash
|
|
||||||
l_ctx.add_form_parameter ("unreserved", ":!@[]{}()*") -- ...
|
|
||||||
l_ctx.add_form_parameter ("reserved", "+=?&_#_") -- ...
|
|
||||||
l_ctx.add_form_parameter ("a=b", "a=b") -- equal sign
|
|
||||||
l_ctx.add_form_parameter ("test", "!$&'()*") --
|
|
||||||
l_ctx.add_form_parameter ("lst[a][b]", "[123][456]") -- brackets
|
|
||||||
l_ctx.add_form_parameter ("pos{1,2}", "loc{a,b}") -- curly brackets
|
|
||||||
l_ctx.add_form_parameter ("?foo", "?bar") -- question mark
|
|
||||||
l_ctx.add_form_parameter ("?", "?") -- question mark
|
|
||||||
l_ctx.add_form_parameter ("&bar", "&bar") -- ampersand
|
|
||||||
l_ctx.add_form_parameter ("&", "&") -- ampersand
|
|
||||||
|
|
||||||
assert ("form data well generated", l_ctx.form_parameters_to_x_www_form_url_encoded_string.same_string ("title=Eiffel+World!&path=foo%%2Fbar&unreserved=%%3A!%%40%%5B%%5D%%7B%%7D()*&reserved=%%2B%%3D%%3F%%26_%%23_&a%%3Db=a%%3Db&test=!%%24%%26'()*&lst%%5Ba%%5D%%5Bb%%5D=%%5B123%%5D%%5B456%%5D&pos%%7B1%%2C2%%7D=loc%%7Ba%%2Cb%%7D&%%3Ffoo=%%3Fbar&%%3F=%%3F&%%26bar=%%26bar&%%26=%%26"))
|
if
|
||||||
|
attached sess.post ("", l_ctx, "") as res
|
||||||
create h.make_empty
|
then
|
||||||
if
|
check_response (res)
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
test_post_with_file
|
test_post_with_file
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
do
|
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
|
||||||
-- POST REQUEST WITH A FILE
|
-- set filename to a local file
|
||||||
-- check requestbin to ensure the form parameters are correctly received
|
sess := new_web_session
|
||||||
-- set filename to a local file
|
create l_ctx.make
|
||||||
sess := new_session ("http://requestb.in")
|
l_ctx.set_upload_filename ("test.txt")
|
||||||
create l_ctx.make
|
if
|
||||||
l_ctx.set_upload_filename ("test.txt")
|
attached sess.post ("", l_ctx, Void) as res
|
||||||
create h.make_empty
|
then
|
||||||
if
|
check_response (res)
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
test_put_with_file
|
test_put_with_file
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
do
|
do
|
||||||
if attached global_requestbin_path as requestbin_path then
|
-- PUT REQUEST WITH A FILE
|
||||||
|
-- check requestbin to ensure the file is correctly received
|
||||||
-- PUT REQUEST WITH A FILE
|
-- set filename to a local file
|
||||||
-- check requestbin to ensure the file is correctly received
|
sess := new_web_session
|
||||||
-- set filename to a local file
|
create l_ctx.make
|
||||||
sess := new_session ("http://requestb.in")
|
l_ctx.set_upload_filename ("test.txt")
|
||||||
create l_ctx.make
|
if
|
||||||
l_ctx.set_upload_filename ("test.txt")
|
attached sess.put ("", l_ctx, Void) as res
|
||||||
create h.make_empty
|
then
|
||||||
if
|
check_response (res)
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
test_put_with_data
|
test_put_with_data
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
do
|
do
|
||||||
if attached global_requestbin_path as requestbin_path then
|
-- PUT REQUEST WITH A FILE
|
||||||
|
-- check requestbin to ensure the file is correctly received
|
||||||
-- PUT REQUEST WITH A FILE
|
-- set filename to a local file
|
||||||
-- check requestbin to ensure the file is correctly received
|
sess := new_web_session
|
||||||
-- set filename to a local file
|
create l_ctx.make
|
||||||
sess := new_session ("http://requestb.in")
|
l_ctx.set_upload_data ("name=This is a test for http client.%N")
|
||||||
create l_ctx.make
|
if
|
||||||
l_ctx.set_upload_data ("name=This is a test for http client.%N")
|
attached sess.put ("", l_ctx, Void) as res
|
||||||
create h.make_empty
|
then
|
||||||
if
|
check_response (res)
|
||||||
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test_post_with_file_and_form_data
|
test_post_with_file_and_form_data
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
do
|
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_web_session
|
||||||
|
create l_ctx.make
|
||||||
|
-- l_ctx.add_file_form_parameter ("image", "test.txt", "image/jpeg")
|
||||||
|
l_ctx.add_file_form_parameter ("text", "test.txt", "plain/text")
|
||||||
|
l_ctx.add_form_parameter ("First", "Value")
|
||||||
|
l_ctx.add_form_parameter ("Second", "and last value")
|
||||||
|
if
|
||||||
|
attached sess.post ("", l_ctx, Void) as res
|
||||||
|
then
|
||||||
|
check_response (res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- POST REQUEST WITH A FILE AND FORM DATA
|
test_post_with_multiple_file_and_form_data
|
||||||
-- check requestbin to ensure the file and form parameters are correctly received
|
local
|
||||||
-- set filename to a local file
|
sess: HTTP_CLIENT_SESSION
|
||||||
sess := new_session ("http://requestb.in")
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
-- sess := new_session ("http://localhost:9090")
|
do
|
||||||
create l_ctx.make
|
-- POST REQUEST WITH A FILE AND FORM DATA
|
||||||
-- l_ctx.set_upload_filename ("logo.jpg")
|
-- check requestbin to ensure the file and form parameters are correctly received
|
||||||
l_ctx.set_upload_filename ("test.txt")
|
-- set filename to a local file
|
||||||
l_ctx.add_form_parameter ("First", "Value")
|
sess := new_web_session
|
||||||
l_ctx.add_form_parameter ("Second", "and last value")
|
create l_ctx.make
|
||||||
create h.make_empty
|
l_ctx.add_header ("Content-Type", "multipart/form-data")
|
||||||
if
|
|
||||||
attached sess.post (requestbin_path, l_ctx, Void) as res and then
|
l_ctx.add_file_form_parameter ("first_file", "test.txt", "plain/text")
|
||||||
attached res.headers as hds
|
l_ctx.add_file_form_parameter ("image", "logo.jpg", "image/jpeg")
|
||||||
then
|
l_ctx.add_form_parameter ("First", "Value")
|
||||||
across
|
l_ctx.add_form_parameter ("Second", "and last value")
|
||||||
hds as c
|
l_ctx.add_file_form_parameter ("last_file", "test.txt", Void)
|
||||||
loop
|
|
||||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
if
|
||||||
end
|
attached sess.post ("", l_ctx, Void) as res
|
||||||
end
|
then
|
||||||
print (h)
|
check_response (res)
|
||||||
else
|
|
||||||
assert ("Has requestbin path", False)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test_post_with_file_using_chunked_transfer_encoding
|
test_post_with_file_using_chunked_transfer_encoding
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
do
|
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
|
||||||
-- POST REQUEST WITH A FILE AND FORM DATA
|
-- set filename to a local file
|
||||||
-- check requestbin to ensure the file and form parameters are correctly received
|
sess := new_web_session
|
||||||
-- set filename to a local file
|
create l_ctx.make
|
||||||
sess := new_session ("http://requestb.in")
|
l_ctx.add_header ("Transfer-Encoding", "chunked")
|
||||||
create l_ctx.make
|
l_ctx.set_upload_filename ("logo.jpg")
|
||||||
l_ctx.add_header ("Transfer-Encoding", "chunked")
|
if
|
||||||
l_ctx.set_upload_filename ("logo.jpg")
|
attached sess.post ("", l_ctx, Void) as res
|
||||||
create h.make_empty
|
then
|
||||||
if
|
check_response (res)
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
test_get_with_redirection
|
test_get_with_redirection
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
|
||||||
do
|
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)
|
||||||
-- GET REQUEST, Forwarding (google's first answer is a forward)
|
sess := new_session ("http://google.com")
|
||||||
-- check headers received (printed in console)
|
if attached sess.get ("/", Void) as res then
|
||||||
sess := new_session ("http://google.com")
|
check_response (res)
|
||||||
create h.make_empty
|
assert("was redirected", res.redirections_count > 0)
|
||||||
if attached sess.get ("/", Void) as res and then attached res.headers as hds then
|
|
||||||
assert("was redirected", res.redirections_count > 0)
|
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -377,6 +311,7 @@ feature -- Factory
|
|||||||
sess.set_credentials ("test", "test")
|
sess.set_credentials ("test", "test")
|
||||||
create ctx.make_with_credentials_required
|
create ctx.make_with_credentials_required
|
||||||
if attached sess.get ("/password-ok.php", ctx) as res then
|
if attached sess.get ("/password-ok.php", ctx) as res then
|
||||||
|
check_response (res)
|
||||||
if attached {READABLE_STRING_8} res.body as l_body 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>"))
|
assert ("Fetch all body, including closing html tag", l_body.has_substring ("</html>"))
|
||||||
else
|
else
|
||||||
@@ -388,50 +323,58 @@ feature -- Factory
|
|||||||
test_get_with_query_parameters
|
test_get_with_query_parameters
|
||||||
local
|
local
|
||||||
sess: HTTP_CLIENT_SESSION
|
sess: HTTP_CLIENT_SESSION
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
q: STRING
|
q: STRING
|
||||||
do
|
do
|
||||||
if attached global_requestbin_path as requestbin_path then
|
-- GET REQUEST WITH A FILE AND FORM DATA
|
||||||
|
-- check requestbin to ensure the file and form parameters are correctly received
|
||||||
-- GET REQUEST WITH A FILE AND FORM DATA
|
-- set filename to a local file
|
||||||
-- check requestbin to ensure the file and form parameters are correctly received
|
sess := new_web_session
|
||||||
-- set filename to a local file
|
create l_ctx.make
|
||||||
sess := new_session ("http://requestb.in")
|
l_ctx.add_query_parameter ("?", "?first&arg")
|
||||||
create l_ctx.make
|
l_ctx.add_query_parameter ("title", "Eiffel World!")
|
||||||
l_ctx.add_query_parameter ("?", "?first&arg")
|
l_ctx.add_query_parameter ("path", "foo/bar")
|
||||||
l_ctx.add_query_parameter ("title", "Eiffel World!")
|
l_ctx.add_query_parameter ("reserved", "+=&?")
|
||||||
l_ctx.add_query_parameter ("path", "foo/bar")
|
l_ctx.add_query_parameter ("unreserved", ":!@'()*")
|
||||||
l_ctx.add_query_parameter ("reserved", "+=&?")
|
l_ctx.add_query_parameter ("unsafe", "%"[]{}")
|
||||||
l_ctx.add_query_parameter ("unreserved", ":!@'()*")
|
l_ctx.add_query_parameter ("test", "!$&'()*")
|
||||||
l_ctx.add_query_parameter ("unsafe", "%"[]{}")
|
l_ctx.add_query_parameter ("a&b", "a&b")
|
||||||
l_ctx.add_query_parameter ("test", "!$&'()*")
|
l_ctx.add_query_parameter ("lst[a][b]", "[abc][123]")
|
||||||
l_ctx.add_query_parameter ("a&b", "a&b")
|
l_ctx.add_query_parameter ("foo(a,b)", "bar(1,2)*pi")
|
||||||
l_ctx.add_query_parameter ("lst[a][b]", "[abc][123]")
|
create q.make_empty
|
||||||
l_ctx.add_query_parameter ("foo(a,b)", "bar(1,2)*pi")
|
l_ctx.append_query_parameters_to_url (q)
|
||||||
create q.make_empty
|
assert("query", q.same_string ("??=?first%%26arg&title=Eiffel+World!&path=foo/bar&reserved=%%2B=%%26?&unreserved=:!@'()*&unsafe=%%22%%5B%%5D%%7B%%7D&test=!$%%26'()*&a%%26b=a%%26b&lst%%5Ba%%5D%%5Bb%%5D=%%5Babc%%5D%%5B123%%5D&foo(a,b)=bar(1,2)*pi"))
|
||||||
l_ctx.append_query_parameters_to_url (q)
|
|
||||||
assert("query", q.same_string ("??=?first%%26arg&title=Eiffel+World!&path=foo/bar&reserved=%%2B=%%26?&unreserved=:!@'()*&unsafe=%%22%%5B%%5D%%7B%%7D&test=!$%%26'()*&a%%26b=a%%26b&lst%%5Ba%%5D%%5Bb%%5D=%%5Babc%%5D%%5B123%%5D&foo(a,b)=bar(1,2)*pi"))
|
|
||||||
|
|
||||||
|
|
||||||
create h.make_empty
|
if
|
||||||
if
|
attached sess.get ("", l_ctx) as res
|
||||||
attached sess.get (requestbin_path, l_ctx) as res and then
|
then
|
||||||
attached res.headers as hds
|
check_response (res)
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
check_response (res: HTTP_CLIENT_RESPONSE)
|
||||||
|
local
|
||||||
|
h: STRING
|
||||||
|
do
|
||||||
|
assert ("ok", 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
|
||||||
|
print (h)
|
||||||
|
if attached res.body as b then
|
||||||
|
print (b)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -55,12 +55,19 @@ feature -- Secure connection Helpers
|
|||||||
end
|
end
|
||||||
|
|
||||||
set_secure_protocol_to_ssl_2_or_3
|
set_secure_protocol_to_ssl_2_or_3
|
||||||
-- Set `ssl_protocol' with `Ssl_23'.
|
-- Set `ssl_protocol' with `Ssl_23'.
|
||||||
do
|
-- Protocol not supported anymore.
|
||||||
set_secure_protocol ({SSL_PROTOCOL}.Ssl_23)
|
obsolete
|
||||||
end
|
"Use set_secure_protocol_to_tls_1_2 [2017-06-23]."
|
||||||
|
local
|
||||||
|
err: DEVELOPER_EXCEPTION
|
||||||
|
do
|
||||||
|
create err
|
||||||
|
err.set_description ("SSL_2 or SSL_3 are not supported anymore, upgrate to TLS set_secure_protocol_to_tls_1_2")
|
||||||
|
err.raise
|
||||||
|
end
|
||||||
|
|
||||||
set_secure_protocol_to_tls_1_0
|
set_secure_protocol_to_tls_1_0
|
||||||
-- Set `ssl_protocol' with `Tls_1_0'.
|
-- Set `ssl_protocol' with `Tls_1_0'.
|
||||||
do
|
do
|
||||||
set_secure_protocol ({SSL_PROTOCOL}.Tls_1_0)
|
set_secure_protocol ({SSL_PROTOCOL}.Tls_1_0)
|
||||||
@@ -176,7 +183,14 @@ feature -- Output
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2013, Javier Velilla, Jocelyn Fiat and others"
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
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
|
end
|
||||||
|
|||||||
36
library/security/jwt/src/errors/jwt_mismatched_alg_error.e
Normal file
36
library/security/jwt/src/errors/jwt_mismatched_alg_error.e
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {JWT_MISMATCHED_ALG_ERROR}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
JWT_MISMATCHED_ALG_ERROR
|
||||||
|
|
||||||
|
inherit
|
||||||
|
JWT_ERROR
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make (a_alg, a_header_alg: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
alg := a_alg
|
||||||
|
header_alg := a_header_alg
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access
|
||||||
|
|
||||||
|
alg: READABLE_STRING_8
|
||||||
|
|
||||||
|
header_alg: READABLE_STRING_8
|
||||||
|
|
||||||
|
id: STRING = "ALG_MISMATCH"
|
||||||
|
|
||||||
|
message: READABLE_STRING_8
|
||||||
|
do
|
||||||
|
Result := "Header alg [" + header_alg + "] does not match given alg [" + alg + "]!"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
note
|
note
|
||||||
description: "Summary description for {JWS}."
|
description: "Summary description for {JWS}."
|
||||||
author: ""
|
|
||||||
date: "$Date$"
|
date: "$Date$"
|
||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
|
|
||||||
@@ -9,19 +8,35 @@ class
|
|||||||
|
|
||||||
inherit
|
inherit
|
||||||
JWT
|
JWT
|
||||||
|
redefine
|
||||||
|
default_create
|
||||||
|
end
|
||||||
|
|
||||||
JWT_UTILITIES
|
JWT_UTILITIES
|
||||||
undefine
|
redefine
|
||||||
default_create
|
default_create
|
||||||
end
|
end
|
||||||
|
|
||||||
create
|
create
|
||||||
default_create,
|
default_create,
|
||||||
|
make_with_algorithm,
|
||||||
make_with_claims,
|
make_with_claims,
|
||||||
make_with_json_payload
|
make_with_json_payload
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
default_create
|
||||||
|
do
|
||||||
|
Precursor {JWT}
|
||||||
|
set_algorithm_to_hs256
|
||||||
|
end
|
||||||
|
|
||||||
|
make_with_algorithm (alg: like algorithm)
|
||||||
|
do
|
||||||
|
default_create
|
||||||
|
set_algorithm (alg)
|
||||||
|
end
|
||||||
|
|
||||||
make_with_claims (tb: STRING_TABLE [READABLE_STRING_GENERAL])
|
make_with_claims (tb: STRING_TABLE [READABLE_STRING_GENERAL])
|
||||||
do
|
do
|
||||||
default_create
|
default_create
|
||||||
@@ -77,4 +92,14 @@ feature -- Element change
|
|||||||
header.set_algorithm (alg)
|
header.set_algorithm (alg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_algorithm_to_hs256
|
||||||
|
do
|
||||||
|
set_algorithm (alg_hs256)
|
||||||
|
end
|
||||||
|
|
||||||
|
set_algorithm_to_none
|
||||||
|
do
|
||||||
|
set_algorithm (alg_none)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -118,6 +118,11 @@ feature {JWT_UTILITIES} -- Error reporting
|
|||||||
l_errors.extend (err)
|
l_errors.extend (err)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
report_mismatched_alg_error (alg, a_header_alg: READABLE_STRING_8)
|
||||||
|
do
|
||||||
|
report_error (create {JWT_MISMATCHED_ALG_ERROR}.make (alg, a_header_alg))
|
||||||
|
end
|
||||||
|
|
||||||
report_unsupported_alg_error (alg: READABLE_STRING_8)
|
report_unsupported_alg_error (alg: READABLE_STRING_8)
|
||||||
do
|
do
|
||||||
report_error (create {JWT_UNSUPPORTED_ALG_ERROR}.make (alg))
|
report_error (create {JWT_UNSUPPORTED_ALG_ERROR}.make (alg))
|
||||||
|
|||||||
@@ -259,6 +259,11 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_issued_at_now_utc
|
||||||
|
do
|
||||||
|
set_issued_at (create {DATE_TIME}.make_now_utc)
|
||||||
|
end
|
||||||
|
|
||||||
set_jwt_id (jti: detachable READABLE_STRING_8)
|
set_jwt_id (jti: detachable READABLE_STRING_8)
|
||||||
-- The "jti" (JWT ID) claim provides a unique identifier for the JWT.
|
-- The "jti" (JWT ID) claim provides a unique identifier for the JWT.
|
||||||
-- The identifier value MUST be assigned in a manner that ensures that
|
-- The identifier value MUST be assigned in a manner that ensures that
|
||||||
|
|||||||
@@ -266,11 +266,10 @@ feature {NONE} -- Implementation
|
|||||||
|
|
||||||
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
|
base64_hmacsha256 (s: READABLE_STRING_8; a_secret: READABLE_STRING_8): STRING_8
|
||||||
local
|
local
|
||||||
hs256: HMAC_SHA256
|
ut: JWT_UTILITIES
|
||||||
do
|
do
|
||||||
create hs256.make_ascii_key (a_secret)
|
create ut
|
||||||
hs256.update_from_string (s)
|
Result := ut.base64_hmacsha256 (s, a_secret)
|
||||||
Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
note
|
note
|
||||||
description: "Summary description for {JWT_LOADER}."
|
description: "Loader and verifier to JWT token."
|
||||||
author: ""
|
|
||||||
date: "$Date$"
|
date: "$Date$"
|
||||||
revision: "$Revision$"
|
revision: "$Revision$"
|
||||||
|
EIS: "name=Known Critical vulnerabilities in JWT libs", "protocol=URI", "src=https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/"
|
||||||
|
|
||||||
class
|
class
|
||||||
JWT_LOADER
|
JWT_LOADER
|
||||||
@@ -12,9 +12,13 @@ inherit
|
|||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
|
|
||||||
token (a_token_input: READABLE_STRING_8; a_secret: READABLE_STRING_8; ctx: detachable JWT_CONTEXT): detachable JWT
|
token (a_token_input: READABLE_STRING_8; a_alg: detachable READABLE_STRING_8; a_verification_key: READABLE_STRING_8; ctx: detachable JWT_CONTEXT): detachable JWT
|
||||||
-- Decoded token from `a_token_input` given the secret `a_secret`, and optional context `ctx`
|
-- Decoded token from `a_token_input` given the verification key `a_verification_key` and optional (but recommended) signature algorithm `a_alg`, and optional context `ctx`
|
||||||
-- used to specify eventual issuer and various parameters.
|
-- used to specify eventual issuer and various parameters.
|
||||||
|
-- WARNING: passing Void for `a_alg` is not safe, as the server should know which alg he used for tokens,
|
||||||
|
-- leaving the possibility to use the header alg is dangerous as client may use "none" and then bypass verification!
|
||||||
|
require
|
||||||
|
a_valid_alg: a_alg /= Void implies is_supporting_signature_algorithm (a_alg)
|
||||||
local
|
local
|
||||||
jws: JWS
|
jws: JWS
|
||||||
i,j,n: INTEGER
|
i,j,n: INTEGER
|
||||||
@@ -29,20 +33,27 @@ feature -- Access
|
|||||||
l_enc_payload := a_token_input.substring (i + 1, j - 1)
|
l_enc_payload := a_token_input.substring (i + 1, j - 1)
|
||||||
l_signature := a_token_input.substring (j + 1, n)
|
l_signature := a_token_input.substring (j + 1, n)
|
||||||
create jws.make_with_json_payload (base64url_decode (l_enc_payload))
|
create jws.make_with_json_payload (base64url_decode (l_enc_payload))
|
||||||
|
|
||||||
alg := signature_algorithm_from_encoded_header (l_enc_header)
|
alg := signature_algorithm_from_encoded_header (l_enc_header)
|
||||||
jws.set_algorithm (alg)
|
if a_alg /= Void then
|
||||||
if alg = Void then
|
if alg /= Void and then not alg.is_case_insensitive_equal_general (a_alg) then
|
||||||
-- Use default
|
jws.report_mismatched_alg_error (a_alg, alg)
|
||||||
alg := alg_hs256
|
else
|
||||||
|
alg := a_alg
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if alg = Void then
|
||||||
|
-- Use default
|
||||||
|
alg := alg_hs256
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
jws.set_algorithm (alg)
|
||||||
check alg_set: alg /= Void end
|
check alg_set: alg /= Void end
|
||||||
if ctx = Void or else not ctx.validation_ignored then
|
if ctx = Void or else not ctx.validation_ignored then
|
||||||
if not is_supporting_signature_algorithm (alg) then
|
if not is_supporting_signature_algorithm (alg) then
|
||||||
jws.report_unsupported_alg_error (alg)
|
jws.report_unsupported_alg_error (alg)
|
||||||
alg := alg_hs256
|
alg := alg_hs256
|
||||||
end
|
end
|
||||||
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_secret, alg)) then
|
if not l_signature.same_string (signature (l_enc_header, l_enc_payload, a_verification_key, alg)) then
|
||||||
jws.report_unverified_token_error
|
jws.report_unverified_token_error
|
||||||
end
|
end
|
||||||
if
|
if
|
||||||
|
|||||||
@@ -61,7 +61,33 @@ feature -- Encoding
|
|||||||
do
|
do
|
||||||
create hs256.make_ascii_key (a_secret)
|
create hs256.make_ascii_key (a_secret)
|
||||||
hs256.update_from_string (s)
|
hs256.update_from_string (s)
|
||||||
Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
|
-- if Version >= EiffelStudio 17.11 then
|
||||||
|
-- Result := hs256.base64_digest --lowercase_hexadecimal_string_digest
|
||||||
|
-- else
|
||||||
|
Result := base64_bytes_encoded_string (hs256.digest)
|
||||||
|
-- end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
base64_bytes_encoded_string (a_bytes: SPECIAL [NATURAL_8]): STRING_8
|
||||||
|
-- Base64 string from `a_bytes`.
|
||||||
|
--| Note: to be removed when 17.11 is not latest release anymore.
|
||||||
|
local
|
||||||
|
s: STRING
|
||||||
|
i,n: INTEGER
|
||||||
|
do
|
||||||
|
from
|
||||||
|
i := 1
|
||||||
|
n := a_bytes.count
|
||||||
|
create s.make (n)
|
||||||
|
until
|
||||||
|
i > n
|
||||||
|
loop
|
||||||
|
s.append_code (a_bytes[i - 1])
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
Result := (create {BASE64}).encoded_string (s)
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Decoding
|
feature -- Decoding
|
||||||
|
|||||||
@@ -54,7 +54,14 @@ feature -- Test
|
|||||||
|
|
||||||
create jwt_loader
|
create jwt_loader
|
||||||
|
|
||||||
if attached jwt_loader.token (tok, "secret", Void) as l_tok then
|
-- Use header alg!
|
||||||
|
if attached jwt_loader.token (tok, Void, "secret", Void) as l_tok then
|
||||||
|
assert ("no error", not l_tok.has_error)
|
||||||
|
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Use given alg!
|
||||||
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", Void) as l_tok then
|
||||||
assert ("no error", not l_tok.has_error)
|
assert ("no error", not l_tok.has_error)
|
||||||
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
||||||
end
|
end
|
||||||
@@ -96,21 +103,21 @@ feature -- Test
|
|||||||
create jwt_loader
|
create jwt_loader
|
||||||
|
|
||||||
-- Test with validation + exp
|
-- Test with validation + exp
|
||||||
if attached jwt_loader.token (tok, "secret", Void) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", Void) as l_tok then
|
||||||
assert ("no error", not l_tok.has_error)
|
assert ("no error", not l_tok.has_error)
|
||||||
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
||||||
end
|
end
|
||||||
|
|
||||||
create ctx
|
create ctx
|
||||||
ctx.set_time (now)
|
ctx.set_time (now)
|
||||||
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
|
||||||
assert ("no error", not l_tok.has_error)
|
assert ("no error", not l_tok.has_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
dt := duplicated_time (now)
|
dt := duplicated_time (now)
|
||||||
dt.hour_add (5)
|
dt.hour_add (5)
|
||||||
ctx.set_time (dt)
|
ctx.set_time (dt)
|
||||||
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
|
||||||
assert ("exp error", l_tok.has_error)
|
assert ("exp error", l_tok.has_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -122,7 +129,7 @@ feature -- Test
|
|||||||
tok := jwt.encoded_string ("secret")
|
tok := jwt.encoded_string ("secret")
|
||||||
|
|
||||||
ctx.set_time (now)
|
ctx.set_time (now)
|
||||||
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
|
||||||
assert ("has nbf error", l_tok.has_error)
|
assert ("has nbf error", l_tok.has_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -130,7 +137,7 @@ feature -- Test
|
|||||||
dt.second_add (15)
|
dt.second_add (15)
|
||||||
ctx.set_time (dt)
|
ctx.set_time (dt)
|
||||||
|
|
||||||
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
|
||||||
assert ("has nbf error", l_tok.has_error)
|
assert ("has nbf error", l_tok.has_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -138,31 +145,51 @@ feature -- Test
|
|||||||
dt.minute_add (45)
|
dt.minute_add (45)
|
||||||
ctx.set_time (dt)
|
ctx.set_time (dt)
|
||||||
|
|
||||||
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
|
||||||
assert ("no error", not l_tok.has_error)
|
assert ("no error", not l_tok.has_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Test Issuer
|
-- Test Issuer
|
||||||
ctx.set_issuer ("urn:foobar")
|
ctx.set_issuer ("urn:foobar")
|
||||||
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
|
||||||
assert ("has iss error", l_tok.has_error)
|
assert ("has iss error", l_tok.has_error)
|
||||||
end
|
end
|
||||||
ctx.set_issuer ("urn:foo")
|
ctx.set_issuer ("urn:foo")
|
||||||
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
|
||||||
assert ("no error", not l_tok.has_error)
|
assert ("no error", not l_tok.has_error)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Test Audience
|
-- Test Audience
|
||||||
ctx.set_audience ("urn:foobar")
|
ctx.set_audience ("urn:foobar")
|
||||||
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
|
||||||
assert ("has aud error", l_tok.has_error)
|
assert ("has aud error", l_tok.has_error)
|
||||||
end
|
end
|
||||||
ctx.set_audience ("urn:foo")
|
ctx.set_audience ("urn:foo")
|
||||||
if attached jwt_loader.token (tok, "secret", ctx) as l_tok then
|
if attached jwt_loader.token (tok, jwt.algorithm, "secret", ctx) as l_tok then
|
||||||
assert ("no error", not l_tok.has_error)
|
assert ("no error", not l_tok.has_error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test_mismatched_alg_jwt
|
||||||
|
local
|
||||||
|
jwt: JWS
|
||||||
|
payload: STRING
|
||||||
|
tok: STRING
|
||||||
|
do
|
||||||
|
payload := "[
|
||||||
|
{"iss":"joe","exp":1300819380,"http://example.com/is_root":true}
|
||||||
|
]"
|
||||||
|
|
||||||
|
create jwt.make_with_json_payload (payload)
|
||||||
|
jwt.set_algorithm ("none")
|
||||||
|
tok := jwt.encoded_string ("secret")
|
||||||
|
|
||||||
|
if attached (create {JWT_LOADER}).token (tok, "HS256", "secret", Void) as l_tok then
|
||||||
|
assert ("no error", not jwt.has_error)
|
||||||
|
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test_unsecured_jwt
|
test_unsecured_jwt
|
||||||
local
|
local
|
||||||
jwt: JWS
|
jwt: JWS
|
||||||
@@ -177,7 +204,11 @@ feature -- Test
|
|||||||
jwt.set_algorithm ("none")
|
jwt.set_algorithm ("none")
|
||||||
tok := jwt.encoded_string ("secret")
|
tok := jwt.encoded_string ("secret")
|
||||||
|
|
||||||
if attached (create {JWT_LOADER}).token (tok, "secret", Void) as l_tok then
|
if attached (create {JWT_LOADER}).token (tok, "none", "secret", Void) as l_tok then
|
||||||
|
assert ("no error", not jwt.has_error)
|
||||||
|
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
||||||
|
end
|
||||||
|
if attached (create {JWT_LOADER}).token (tok, Void, "secret", Void) as l_tok then
|
||||||
assert ("no error", not jwt.has_error)
|
assert ("no error", not jwt.has_error)
|
||||||
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
assert ("same payload", l_tok.claimset.string.same_string (payload))
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
|
|
||||||
set_socket_timeout (a_nb_seconds: like socket_timeout)
|
set_socket_timeout (a_nb_seconds: like socket_timeout)
|
||||||
-- Set `socket_timeout' with `a_nb_seconds'
|
-- Set `socket_timeout' with `a_nb_seconds'.
|
||||||
do
|
do
|
||||||
socket_timeout := a_nb_seconds
|
socket_timeout := a_nb_seconds
|
||||||
ensure
|
ensure
|
||||||
@@ -175,7 +175,7 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
|
|
||||||
set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
|
set_socket_recv_timeout (a_nb_seconds: like socket_recv_timeout)
|
||||||
-- Set `socket_recv_timeout' with `a_nb_seconds'
|
-- Set `socket_recv_timeout' with `a_nb_seconds'.
|
||||||
do
|
do
|
||||||
socket_recv_timeout := a_nb_seconds
|
socket_recv_timeout := a_nb_seconds
|
||||||
ensure
|
ensure
|
||||||
@@ -183,7 +183,7 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
|
|
||||||
set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
|
set_keep_alive_timeout (a_seconds: like keep_alive_timeout)
|
||||||
-- Set `keep_alive_timeout' with `a_seconds'
|
-- Set `keep_alive_timeout' with `a_seconds'.
|
||||||
do
|
do
|
||||||
keep_alive_timeout := a_seconds
|
keep_alive_timeout := a_seconds
|
||||||
ensure
|
ensure
|
||||||
@@ -191,7 +191,7 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
|
|
||||||
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
|
set_max_keep_alive_requests (nb: like max_keep_alive_requests)
|
||||||
-- Set `max_keep_alive_requests' with `nb'
|
-- Set `max_keep_alive_requests' with `nb'.
|
||||||
do
|
do
|
||||||
max_keep_alive_requests := nb
|
max_keep_alive_requests := nb
|
||||||
ensure
|
ensure
|
||||||
@@ -254,7 +254,7 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
|
|
||||||
mark_secure
|
mark_secure
|
||||||
-- Set is_secure in True
|
-- Set is_secure in True.
|
||||||
do
|
do
|
||||||
set_is_secure (True)
|
set_is_secure (True)
|
||||||
ensure
|
ensure
|
||||||
@@ -287,7 +287,7 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
|
|
||||||
set_secure_protocol (a_version: NATURAL)
|
set_secure_protocol (a_version: NATURAL)
|
||||||
-- Set `secure_protocol' with `a_version'
|
-- Set `secure_protocol' with `a_version'.
|
||||||
do
|
do
|
||||||
secure_protocol := a_version
|
secure_protocol := a_version
|
||||||
ensure
|
ensure
|
||||||
@@ -295,7 +295,7 @@ feature -- Element change
|
|||||||
end
|
end
|
||||||
|
|
||||||
set_secure_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
|
set_secure_protocol_from_string (a_ssl_version: READABLE_STRING_GENERAL)
|
||||||
-- Set `secure_protocol' with `a_ssl_version'
|
-- Set `secure_protocol' with `a_ssl_version'.
|
||||||
do
|
do
|
||||||
if a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then
|
if a_ssl_version.is_case_insensitive_equal ("ssl_2_3") then
|
||||||
set_secure_protocol_to_ssl_2_or_3
|
set_secure_protocol_to_ssl_2_or_3
|
||||||
@@ -316,6 +316,8 @@ feature -- SSL Helpers
|
|||||||
|
|
||||||
set_secure_protocol_to_ssl_2_or_3
|
set_secure_protocol_to_ssl_2_or_3
|
||||||
-- Set `secure_protocol' with `Ssl_23'.
|
-- Set `secure_protocol' with `Ssl_23'.
|
||||||
|
obsolete
|
||||||
|
"Use set_secure_protocol_to_tls_1_2 [2017-06-23]."
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -36,9 +36,16 @@ feature -- Access
|
|||||||
feature -- SSL Helpers
|
feature -- SSL Helpers
|
||||||
|
|
||||||
set_secure_protocol_to_ssl_2_or_3
|
set_secure_protocol_to_ssl_2_or_3
|
||||||
-- Set `secure_protocol' with `Ssl_23'.
|
-- Set `ssl_protocol' with `Ssl_23'.
|
||||||
|
-- Protocol not supported anymore.
|
||||||
|
obsolete
|
||||||
|
"Use set_secure_protocol_to_tls_1_2 [2017-06-23]."
|
||||||
|
local
|
||||||
|
err: DEVELOPER_EXCEPTION
|
||||||
do
|
do
|
||||||
set_secure_protocol ({SSL_PROTOCOL}.Ssl_23)
|
create err
|
||||||
|
err.set_description ("SSL_2 or SSL_3 are not supported anymore, upgrate to TLS set_secure_protocol_to_tls_1_2")
|
||||||
|
err.raise
|
||||||
end
|
end
|
||||||
|
|
||||||
set_secure_protocol_to_tls_1_0
|
set_secure_protocol_to_tls_1_0
|
||||||
@@ -67,7 +74,7 @@ feature -- SSL Helpers
|
|||||||
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2017, 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
|
||||||
|
|||||||
205
library/server/wsf/compression/wsf_compression.e
Normal file
205
library/server/wsf/compression/wsf_compression.e
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_COMPRESSION}."
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_COMPRESSION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
make
|
||||||
|
-- Initialize compression support, by default no compression
|
||||||
|
-- Gzip with the following media types
|
||||||
|
-- applications/javascript
|
||||||
|
-- application/json
|
||||||
|
-- application/xml
|
||||||
|
-- text/css
|
||||||
|
-- text/html
|
||||||
|
--
|
||||||
|
do
|
||||||
|
-- compression algorithms
|
||||||
|
create {ARRAYED_LIST [STRING]} compression_supported_formats.make (0)
|
||||||
|
compression_supported_formats.compare_objects
|
||||||
|
|
||||||
|
-- media types supported by compression.
|
||||||
|
create {ARRAYED_LIST [STRING]} compression_enabled_media_types.make (0)
|
||||||
|
compression_enabled_media_types.compare_objects
|
||||||
|
set_default_compression_enabled_media_types
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Query
|
||||||
|
|
||||||
|
encoding_variants (req: WSF_REQUEST; ct: STRING): detachable HTTP_ACCEPT_ENCODING_VARIANTS
|
||||||
|
-- If the client support compression and the server support one of the algorithms
|
||||||
|
-- compress it and update the response header.
|
||||||
|
local
|
||||||
|
conneg : SERVER_CONTENT_NEGOTIATION
|
||||||
|
do
|
||||||
|
if
|
||||||
|
attached req.http_accept_encoding as l_http_encoding and then
|
||||||
|
not compression_supported_formats.is_empty and then
|
||||||
|
compression_enabled_media_types.has (ct)
|
||||||
|
then
|
||||||
|
create conneg.make ("", "", "", "")
|
||||||
|
Result := conneg.encoding_preference (compression_supported_formats, l_http_encoding)
|
||||||
|
if not Result.is_acceptable then
|
||||||
|
Result := Void
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Compression: constants
|
||||||
|
|
||||||
|
gzip_compression_format: STRING = "gzip"
|
||||||
|
-- RFC 1952 (gzip compressed format).
|
||||||
|
|
||||||
|
deflate_compression_format: STRING = "deflate"
|
||||||
|
-- RFC 1951 (deflate compressed format).
|
||||||
|
|
||||||
|
compress_compression_format: STRING = "compress"
|
||||||
|
-- RFC 1950 (zlib compressed format).
|
||||||
|
|
||||||
|
feature -- Compression
|
||||||
|
|
||||||
|
compression_supported_formats : LIST [STRING]
|
||||||
|
-- Server side compression supported formats.
|
||||||
|
-- Supported compression agorithms: `gzip_compression_format', `deflate_compression_format', `compress_compression_format'.
|
||||||
|
-- identity, means no compression at all.
|
||||||
|
|
||||||
|
compression_enabled_media_types: LIST [STRING]
|
||||||
|
-- List of media types supported by compression.
|
||||||
|
|
||||||
|
set_default_compression_format
|
||||||
|
-- gzip default format.
|
||||||
|
do
|
||||||
|
enable_gzip_compression
|
||||||
|
end
|
||||||
|
|
||||||
|
disable_all_compression_formats
|
||||||
|
-- Remove all items.
|
||||||
|
do
|
||||||
|
compression_supported_formats.wipe_out
|
||||||
|
end
|
||||||
|
|
||||||
|
enable_gzip_compression
|
||||||
|
-- add 'gzip' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.force (gzip_compression_format)
|
||||||
|
ensure
|
||||||
|
has_gzip: compression_supported_formats.has (gzip_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
disable_gzip_compression
|
||||||
|
-- remove 'gzip' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.prune (gzip_compression_format)
|
||||||
|
ensure
|
||||||
|
not_gzip: not compression_supported_formats.has (gzip_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
enable_deflate_compression
|
||||||
|
-- add 'deflate' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.force (deflate_compression_format)
|
||||||
|
ensure
|
||||||
|
has_deflate: compression_supported_formats.has (deflate_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
disable_deflate_compression
|
||||||
|
-- remove 'deflate' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.prune (deflate_compression_format)
|
||||||
|
ensure
|
||||||
|
not_deflate: not compression_supported_formats.has (deflate_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
enable_compress_compression
|
||||||
|
-- add 'compress' format to the list of 'compression_supported' formats
|
||||||
|
do
|
||||||
|
compression_supported_formats.force (compress_compression_format)
|
||||||
|
ensure
|
||||||
|
has_compress: compression_supported_formats.has (compress_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
disable_compress_compression
|
||||||
|
-- remove 'deflate' format to the list of 'compression_supported' formats.
|
||||||
|
do
|
||||||
|
compression_supported_formats.prune (compress_compression_format)
|
||||||
|
ensure
|
||||||
|
no_compress: not compression_supported_formats.has (compress_compression_format)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Compression: media types
|
||||||
|
|
||||||
|
set_default_compression_enabled_media_types
|
||||||
|
-- Default media types
|
||||||
|
-- applications/javascript
|
||||||
|
-- application/json
|
||||||
|
-- application/xml
|
||||||
|
-- text/css
|
||||||
|
-- text/html
|
||||||
|
-- text/plain
|
||||||
|
do
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_javascript)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_json)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.application_xml)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_css)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_html)
|
||||||
|
compression_enabled_media_types.force ({HTTP_MIME_TYPES}.text_plain)
|
||||||
|
end
|
||||||
|
|
||||||
|
remove_all_compression_enabled_media_types
|
||||||
|
-- Remove all items.
|
||||||
|
do
|
||||||
|
compression_enabled_media_types.wipe_out
|
||||||
|
end
|
||||||
|
|
||||||
|
enable_compression_for_media_type (a_media_type: STRING)
|
||||||
|
do
|
||||||
|
compression_enabled_media_types.force (a_media_type)
|
||||||
|
ensure
|
||||||
|
has_media_type: compression_enabled_media_types.has (a_media_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Compress Data
|
||||||
|
|
||||||
|
compressed_string (a_string: STRING; a_encoding: STRING): STRING
|
||||||
|
-- Compress `a_string' using `deflate_compression_format'
|
||||||
|
local
|
||||||
|
dc: ZLIB_STRING_COMPRESS
|
||||||
|
do
|
||||||
|
create Result.make_empty
|
||||||
|
create dc.string_stream_with_size (Result, 32_768) -- chunk size 32k
|
||||||
|
dc.put_string_with_options (a_string, {ZLIB_CONSTANTS}.Z_default_compression, zlb_strategy (a_encoding), {ZLIB_CONSTANTS}.Z_mem_level_9, {ZLIB_CONSTANTS}.z_default_strategy.to_integer_32)
|
||||||
|
-- We use the default compression level
|
||||||
|
-- We use the default value for windows bits, the range is 8..15. Higher values use more memory, but produce smaller output.
|
||||||
|
-- Memory: Higher values use more memory, but are faster and produce smaller output. The default is 8, we use 9.
|
||||||
|
end
|
||||||
|
|
||||||
|
zlb_strategy (a_encoding: STRING): INTEGER
|
||||||
|
do
|
||||||
|
if a_encoding.is_case_insensitive_equal_general (gzip_compression_format) then
|
||||||
|
Result := {ZLIB_CONSTANTS}.z_default_window_bits + 16
|
||||||
|
elseif a_encoding.is_case_insensitive_equal_general (deflate_compression_format) then
|
||||||
|
Result := -{ZLIB_CONSTANTS}.z_default_window_bits
|
||||||
|
else
|
||||||
|
check compress: a_encoding.is_case_insensitive_equal_general (compress_compression_format) end
|
||||||
|
Result := {ZLIB_CONSTANTS}.z_default_window_bits
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
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,113 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_FILE_RESPONSE_WITH_COMPRESSION}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_FILE_RESPONSE_WITH_COMPRESSION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_FILE_RESPONSE
|
||||||
|
redefine
|
||||||
|
send_to
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make_with_path,
|
||||||
|
make_with_content_type_and_path,
|
||||||
|
make_html_with_path,
|
||||||
|
make,
|
||||||
|
make_with_content_type,
|
||||||
|
make_html
|
||||||
|
|
||||||
|
feature {NONE} -- Access
|
||||||
|
|
||||||
|
compression_variants: detachable HTTP_ACCEPT_ENCODING_VARIANTS
|
||||||
|
|
||||||
|
compression: detachable WSF_COMPRESSION
|
||||||
|
|
||||||
|
feature -- Compression setting
|
||||||
|
|
||||||
|
apply_compression (a_compression: WSF_COMPRESSION; req: WSF_REQUEST)
|
||||||
|
do
|
||||||
|
compression := a_compression
|
||||||
|
compression_variants := a_compression.encoding_variants (req, content_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
reset_compression
|
||||||
|
do
|
||||||
|
compression := Void
|
||||||
|
compression_variants := Void
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {WSF_RESPONSE} -- Output
|
||||||
|
|
||||||
|
send_to (res: WSF_RESPONSE)
|
||||||
|
do
|
||||||
|
if status_code = {HTTP_STATUS_CODE}.not_found then
|
||||||
|
-- File not found, then no more data.
|
||||||
|
elseif
|
||||||
|
attached compression as l_compression and then
|
||||||
|
attached compression_variants as l_compression_variants and then
|
||||||
|
attached l_compression_variants.encoding as l_encoding and then
|
||||||
|
attached l_compression_variants.vary_header_value as l_vary_header
|
||||||
|
then
|
||||||
|
send_compressed_to (res, l_compression, l_encoding, l_vary_header)
|
||||||
|
else
|
||||||
|
-- Send uncompressed...
|
||||||
|
Precursor (res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
send_compressed_to (res: WSF_RESPONSE; a_compression: WSF_COMPRESSION; a_comp_encoding, a_comp_vary_header: READABLE_STRING_8)
|
||||||
|
local
|
||||||
|
s: detachable READABLE_STRING_8
|
||||||
|
l_content, l_compressed_content: STRING_8
|
||||||
|
f: RAW_FILE
|
||||||
|
l_count: INTEGER
|
||||||
|
do
|
||||||
|
res.set_status_code (status_code)
|
||||||
|
create f.make_with_path (file_path)
|
||||||
|
l_count := f.count
|
||||||
|
f.open_read
|
||||||
|
f.read_stream (l_count)
|
||||||
|
f.close
|
||||||
|
l_content := f.last_string
|
||||||
|
|
||||||
|
s := head
|
||||||
|
if s /= Void then
|
||||||
|
l_content.prepend (s)
|
||||||
|
end
|
||||||
|
s := bottom
|
||||||
|
if s /= Void then
|
||||||
|
l_content.append_string (s)
|
||||||
|
end
|
||||||
|
|
||||||
|
l_compressed_content := a_compression.compressed_string (l_content, a_comp_encoding)
|
||||||
|
|
||||||
|
debug
|
||||||
|
res.put_error (l_content.count.out + " -(compression-> " + l_compressed_content.count.out + "%N")
|
||||||
|
end
|
||||||
|
header.put_content_encoding (a_comp_encoding)
|
||||||
|
header.add_header ("Vary:" + a_comp_vary_header)
|
||||||
|
header.put_content_length (l_compressed_content.count)
|
||||||
|
|
||||||
|
res.put_header_text (header.string)
|
||||||
|
if not answer_head_request_method then
|
||||||
|
res.put_string (l_compressed_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
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,84 @@
|
|||||||
|
note
|
||||||
|
description: "Summary description for {WSF_FILE_SYSTEM_HANDLER_WITH_COMPRESSION}."
|
||||||
|
author: ""
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
WSF_FILE_SYSTEM_HANDLER_WITH_COMPRESSION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
WSF_FILE_SYSTEM_HANDLER
|
||||||
|
redefine
|
||||||
|
initialize,
|
||||||
|
process_transfert
|
||||||
|
end
|
||||||
|
|
||||||
|
create
|
||||||
|
make_with_path,
|
||||||
|
make_hidden_with_path,
|
||||||
|
make,
|
||||||
|
make_hidden
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
create compression.make
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Access: compression
|
||||||
|
|
||||||
|
compression: WSF_COMPRESSION
|
||||||
|
|
||||||
|
feature -- Execution
|
||||||
|
|
||||||
|
process_transfert (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
local
|
||||||
|
ext: READABLE_STRING_32
|
||||||
|
ct: detachable READABLE_STRING_8
|
||||||
|
fres: WSF_FILE_RESPONSE_WITH_COMPRESSION
|
||||||
|
dt: DATE_TIME
|
||||||
|
do
|
||||||
|
ext := extension (f.path.name)
|
||||||
|
ct := extension_mime_mapping.mime_type (ext)
|
||||||
|
if ct = Void then
|
||||||
|
ct := {HTTP_MIME_TYPES}.application_force_download
|
||||||
|
end
|
||||||
|
|
||||||
|
create fres.make_with_content_type_and_path (ct, f.path)
|
||||||
|
-- Apply compression based on request `req` header.
|
||||||
|
fres.apply_compression (compression, req)
|
||||||
|
|
||||||
|
-- Prepare response
|
||||||
|
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||||
|
|
||||||
|
-- cache control
|
||||||
|
create dt.make_now_utc
|
||||||
|
fres.header.put_utc_date (dt)
|
||||||
|
if max_age >= 0 then
|
||||||
|
fres.set_max_age (max_age)
|
||||||
|
if max_age > 0 then
|
||||||
|
dt := dt.twin
|
||||||
|
dt.second_add (max_age)
|
||||||
|
end
|
||||||
|
fres.set_expires_date (dt)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- send
|
||||||
|
fres.set_answer_head_request_method (req.request_method.same_string ({HTTP_REQUEST_METHODS}.method_head))
|
||||||
|
res.send (fres)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
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
|
||||||
@@ -39,6 +39,7 @@ feature {NONE} -- Initialization
|
|||||||
|
|
||||||
make_with_path (d: like document_root)
|
make_with_path (d: like document_root)
|
||||||
do
|
do
|
||||||
|
initialize
|
||||||
max_age := -1
|
max_age := -1
|
||||||
if d.is_empty then
|
if d.is_empty then
|
||||||
document_root := execution_environment.current_working_path
|
document_root := execution_environment.current_working_path
|
||||||
@@ -73,6 +74,11 @@ feature {NONE} -- Initialization
|
|||||||
is_hidden: BOOLEAN
|
is_hidden: BOOLEAN
|
||||||
-- Current mapped handler should be hidden from self documentation
|
-- Current mapped handler should be hidden from self documentation
|
||||||
|
|
||||||
|
initialize
|
||||||
|
-- Initialize Current handler.
|
||||||
|
do
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Documentation
|
feature -- Documentation
|
||||||
|
|
||||||
mapping_documentation (m: WSF_ROUTER_MAPPING; a_request_methods: detachable WSF_REQUEST_METHODS): WSF_ROUTER_MAPPING_DOCUMENTATION
|
mapping_documentation (m: WSF_ROUTER_MAPPING; a_request_methods: detachable WSF_REQUEST_METHODS): WSF_ROUTER_MAPPING_DOCUMENTATION
|
||||||
@@ -211,7 +217,7 @@ feature -- Execution
|
|||||||
fn := resource_filename (uri)
|
fn := resource_filename (uri)
|
||||||
create f.make_with_path (fn)
|
create f.make_with_path (fn)
|
||||||
if f.exists then
|
if f.exists then
|
||||||
if f.is_readable then
|
if f.is_access_readable then
|
||||||
if f.is_directory then
|
if f.is_directory then
|
||||||
if index_disabled then
|
if index_disabled then
|
||||||
process_directory_index_disabled (uri, req, res)
|
process_directory_index_disabled (uri, req, res)
|
||||||
@@ -341,6 +347,8 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
process_file (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
process_file (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
require
|
||||||
|
f_valid: f.exists and then f.is_access_readable
|
||||||
do
|
do
|
||||||
if
|
if
|
||||||
attached req.meta_string_variable ("HTTP_IF_MODIFIED_SINCE") as s_if_modified_since and then
|
attached req.meta_string_variable ("HTTP_IF_MODIFIED_SINCE") as s_if_modified_since and then
|
||||||
@@ -355,6 +363,8 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
|
|
||||||
process_transfert (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
process_transfert (f: FILE; req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||||
|
require
|
||||||
|
f_valid: f.exists and then f.is_access_readable
|
||||||
local
|
local
|
||||||
ext: READABLE_STRING_32
|
ext: READABLE_STRING_32
|
||||||
ct: detachable READABLE_STRING_8
|
ct: detachable READABLE_STRING_8
|
||||||
@@ -366,7 +376,7 @@ feature -- Execution
|
|||||||
if ct = Void then
|
if ct = Void then
|
||||||
ct := {HTTP_MIME_TYPES}.application_force_download
|
ct := {HTTP_MIME_TYPES}.application_force_download
|
||||||
end
|
end
|
||||||
create fres.make_with_content_type (ct, f.path.name)
|
create fres.make_with_content_type_and_path (ct, f.path)
|
||||||
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
|
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||||
|
|
||||||
-- cache control
|
-- cache control
|
||||||
|
|||||||
@@ -104,6 +104,15 @@ feature {NONE} -- Initialization
|
|||||||
|
|
||||||
feature -- Element change
|
feature -- Element change
|
||||||
|
|
||||||
|
set_content_type (a_content_type: detachable like content_type)
|
||||||
|
do
|
||||||
|
if a_content_type = Void then
|
||||||
|
get_content_type
|
||||||
|
else
|
||||||
|
content_type := a_content_type
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
set_max_age (sec: INTEGER)
|
set_max_age (sec: INTEGER)
|
||||||
do
|
do
|
||||||
header.put_cache_control ("max-age=" + sec.out)
|
header.put_cache_control ("max-age=" + sec.out)
|
||||||
@@ -227,6 +236,7 @@ feature {WSF_RESPONSE} -- Output
|
|||||||
do
|
do
|
||||||
res.set_status_code (status_code)
|
res.set_status_code (status_code)
|
||||||
if status_code = {HTTP_STATUS_CODE}.not_found then
|
if status_code = {HTTP_STATUS_CODE}.not_found then
|
||||||
|
-- File not found, then no more data.
|
||||||
else
|
else
|
||||||
res.put_header_text (header.string)
|
res.put_header_text (header.string)
|
||||||
s := head
|
s := head
|
||||||
|
|||||||
21
library/server/wsf/wsf_compression-safe.ecf
Normal file
21
library/server/wsf/wsf_compression-safe.ecf
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="wsf_compression" uuid="C558E537-1259-4C94-8C49-117D7E821820" library_target="wsf_compression">
|
||||||
|
<target name="wsf_compression">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" void_safety="all">
|
||||||
|
</option>
|
||||||
|
<setting name="concurrency" value="scoop"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
|
<library name="conneg" location="..\..\network\protocol\content_negotiation\conneg-safe.ecf"/>
|
||||||
|
<library name="http" location="..\..\network\protocol\http\http-safe.ecf"/>
|
||||||
|
<library name="wsf" location="wsf-safe.ecf" readonly="false"/>
|
||||||
|
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||||
|
<library name="zlib" location="$ISE_LIBRARY\unstable\library\compression\zlib\zlib-safe.ecf" readonly="false"/>
|
||||||
|
<cluster name="compression" location=".\compression\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
21
library/server/wsf/wsf_compression.ecf
Normal file
21
library/server/wsf/wsf_compression.ecf
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="wsf_compression" uuid="C558E537-1259-4C94-8C49-117D7E821820" library_target="wsf_compression">
|
||||||
|
<target name="wsf_compression">
|
||||||
|
<root all_classes="true"/>
|
||||||
|
<file_rule>
|
||||||
|
<exclude>/.git$</exclude>
|
||||||
|
<exclude>/.svn$</exclude>
|
||||||
|
<exclude>/EIFGENs$</exclude>
|
||||||
|
</file_rule>
|
||||||
|
<option warning="true" void_safety="none">
|
||||||
|
</option>
|
||||||
|
<setting name="concurrency" value="scoop"/>
|
||||||
|
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||||
|
<library name="conneg" location="..\..\network\protocol\content_negotiation\conneg.ecf"/>
|
||||||
|
<library name="http" location="..\..\network\protocol\http\http.ecf"/>
|
||||||
|
<library name="wsf" location="wsf.ecf"/>
|
||||||
|
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||||
|
<library name="zlib" location="$ISE_LIBRARY\unstable\library\compression\zlib\zlib.ecf" readonly="false"/>
|
||||||
|
<cluster name="compression" location=".\compression\" recursive="true"/>
|
||||||
|
</target>
|
||||||
|
</system>
|
||||||
@@ -28,17 +28,17 @@ feature -- Access
|
|||||||
end
|
end
|
||||||
|
|
||||||
message: detachable READABLE_STRING_32
|
message: detachable READABLE_STRING_32
|
||||||
-- Potential error message
|
-- Potential error message.
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
parent: detachable ERROR
|
parent: detachable ERROR
|
||||||
-- Eventual error prior to Current
|
-- Eventual error prior to Current.
|
||||||
|
|
||||||
feature -- String representation
|
feature -- String representation
|
||||||
|
|
||||||
string_representation: STRING_32
|
string_representation: STRING_32
|
||||||
-- String representation for Current
|
-- String representation for Current.
|
||||||
do
|
do
|
||||||
create Result.make_from_string (name.as_string_32)
|
create Result.make_from_string (name.as_string_32)
|
||||||
Result.append_character (' ')
|
Result.append_character (' ')
|
||||||
@@ -62,7 +62,7 @@ feature -- Status report
|
|||||||
feature -- Change
|
feature -- Change
|
||||||
|
|
||||||
set_parent (a_parent: like parent)
|
set_parent (a_parent: like parent)
|
||||||
-- Set `parent' to `a_parent'
|
-- Set `parent' to `a_parent'.
|
||||||
do
|
do
|
||||||
parent := a_parent
|
parent := a_parent
|
||||||
end
|
end
|
||||||
@@ -80,7 +80,7 @@ invariant
|
|||||||
name_attached: name /= Void
|
name_attached: name /= Void
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, 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
|
||||||
|
|||||||
@@ -30,28 +30,24 @@ feature -- Access
|
|||||||
|
|
||||||
name: STRING
|
name: STRING
|
||||||
|
|
||||||
message: detachable STRING_32
|
message: STRING_32
|
||||||
do
|
do
|
||||||
create Result.make_from_string (name)
|
create Result.make_from_string (name)
|
||||||
from
|
across
|
||||||
sub_errors.start
|
sub_errors as s
|
||||||
until
|
|
||||||
sub_errors.after
|
|
||||||
loop
|
loop
|
||||||
if
|
if
|
||||||
attached sub_errors.item as e and then
|
attached s.item as e and then
|
||||||
attached e.message as m
|
attached e.message as m
|
||||||
then
|
then
|
||||||
|
|
||||||
Result.append_character ('%N')
|
Result.append_character ('%N')
|
||||||
Result.append_string (m)
|
Result.append_string (m)
|
||||||
end
|
end
|
||||||
sub_errors.forth
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sub_errors: LIST [ERROR]
|
sub_errors: LIST [ERROR]
|
||||||
-- Error contained by Current
|
-- Error contained by Current.
|
||||||
|
|
||||||
feature -- Visitor
|
feature -- Visitor
|
||||||
|
|
||||||
@@ -61,9 +57,8 @@ feature -- Visitor
|
|||||||
a_visitor.process_group (Current)
|
a_visitor.process_group (Current)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, 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,19 +1,14 @@
|
|||||||
note
|
note
|
||||||
description : "[
|
description : "Error handler or receiver."
|
||||||
Error handler or receiver.
|
|
||||||
|
|
||||||
]"
|
|
||||||
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: 2015-10-10 00:55:41 +0200 (sam., 10 oct. 2015) $"
|
date: "$Date$"
|
||||||
revision: "$Revision: 97980 $"
|
revision: "$Revision$"
|
||||||
|
|
||||||
class
|
class
|
||||||
ERROR_HANDLER
|
ERROR_HANDLER
|
||||||
|
|
||||||
inherit
|
inherit
|
||||||
ANY
|
|
||||||
|
|
||||||
DEBUG_OUTPUT
|
DEBUG_OUTPUT
|
||||||
|
|
||||||
create
|
create
|
||||||
@@ -46,7 +41,7 @@ feature -- Access
|
|||||||
-- Optional identifier for Current handler.
|
-- Optional identifier for Current handler.
|
||||||
|
|
||||||
primary_error_code: INTEGER
|
primary_error_code: INTEGER
|
||||||
-- Code of first error in `errors'
|
-- Code of first error in `errors'.
|
||||||
require
|
require
|
||||||
at_least_one_error: has_error
|
at_least_one_error: has_error
|
||||||
do
|
do
|
||||||
@@ -62,7 +57,7 @@ feature -- Status
|
|||||||
end
|
end
|
||||||
|
|
||||||
count: INTEGER
|
count: INTEGER
|
||||||
-- Number of error
|
-- Number of error.
|
||||||
do
|
do
|
||||||
Result := errors.count
|
Result := errors.count
|
||||||
end
|
end
|
||||||
@@ -83,7 +78,7 @@ feature -- Status
|
|||||||
feature {ERROR_HANDLER, ERROR_VISITOR} -- Restricted access
|
feature {ERROR_HANDLER, ERROR_VISITOR} -- Restricted access
|
||||||
|
|
||||||
errors: LIST [ERROR]
|
errors: LIST [ERROR]
|
||||||
-- Errors container
|
-- Errors container.
|
||||||
|
|
||||||
feature -- Status report
|
feature -- Status report
|
||||||
|
|
||||||
@@ -119,12 +114,12 @@ feature -- Status report
|
|||||||
feature -- Events
|
feature -- Events
|
||||||
|
|
||||||
error_added_actions: ACTION_SEQUENCE [TUPLE [ERROR]]
|
error_added_actions: ACTION_SEQUENCE [TUPLE [ERROR]]
|
||||||
-- Actions triggered when a new error is added
|
-- Actions triggered when a new error is added.
|
||||||
|
|
||||||
feature -- Synchronization
|
feature -- Synchronization
|
||||||
|
|
||||||
add_synchronization (h: ERROR_HANDLER)
|
add_synchronization (h: ERROR_HANDLER)
|
||||||
-- Add synchronization between `h' and `Current'
|
-- Add synchronization between `h' and `Current`.
|
||||||
--| the same handler can be added more than once
|
--| the same handler can be added more than once
|
||||||
--| it will be synchronized only once
|
--| it will be synchronized only once
|
||||||
do
|
do
|
||||||
@@ -133,7 +128,7 @@ feature -- Synchronization
|
|||||||
end
|
end
|
||||||
|
|
||||||
remove_synchronization (h: ERROR_HANDLER)
|
remove_synchronization (h: ERROR_HANDLER)
|
||||||
-- Remove synchronization between `h' and `Current'
|
-- Remove synchronization between `h' and `Current'.
|
||||||
do
|
do
|
||||||
remove_propagation (h)
|
remove_propagation (h)
|
||||||
h.remove_propagation (Current)
|
h.remove_propagation (Current)
|
||||||
@@ -347,7 +342,7 @@ feature {NONE} -- Event: implementation
|
|||||||
end
|
end
|
||||||
|
|
||||||
on_reset
|
on_reset
|
||||||
-- `reset' was just called
|
-- `reset' was just called.
|
||||||
local
|
local
|
||||||
sync_list: detachable ARRAYED_LIST [ERROR_HANDLER]
|
sync_list: detachable ARRAYED_LIST [ERROR_HANDLER]
|
||||||
lst: detachable LIST [ERROR_HANDLER]
|
lst: detachable LIST [ERROR_HANDLER]
|
||||||
@@ -389,7 +384,7 @@ feature {NONE} -- Event: implementation
|
|||||||
feature -- Basic operation
|
feature -- Basic operation
|
||||||
|
|
||||||
add_error (a_error: ERROR)
|
add_error (a_error: ERROR)
|
||||||
-- Add `a_error' to the stack of error
|
-- Add `a_error' to the stack of error.
|
||||||
do
|
do
|
||||||
errors.force (a_error)
|
errors.force (a_error)
|
||||||
on_error_added (a_error)
|
on_error_added (a_error)
|
||||||
@@ -406,28 +401,22 @@ feature -- Basic operation
|
|||||||
end
|
end
|
||||||
|
|
||||||
add_error_details, add_custom_error (a_code: INTEGER; a_name: STRING; a_message: detachable READABLE_STRING_GENERAL)
|
add_error_details, add_custom_error (a_code: INTEGER; a_name: STRING; a_message: detachable READABLE_STRING_GENERAL)
|
||||||
-- Add custom error to the stack of error
|
-- Add custom error to the stack of error.
|
||||||
local
|
|
||||||
e: ERROR_CUSTOM
|
|
||||||
do
|
do
|
||||||
create e.make (a_code, a_name, a_message)
|
add_error (create {ERROR_CUSTOM}.make (a_code, a_name, a_message))
|
||||||
add_error (e)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
append (other: ERROR_HANDLER)
|
append (other: ERROR_HANDLER)
|
||||||
-- Append errors from `a_err_handler'
|
-- Append errors from `a_err_handler'.
|
||||||
local
|
local
|
||||||
other_errs: LIST [ERROR]
|
other_errs: LIST [ERROR]
|
||||||
do
|
do
|
||||||
other_errs := other.errors
|
other_errs := other.errors
|
||||||
if other_errs.count > 0 then
|
if other_errs /= errors and then other_errs.count > 0 then
|
||||||
from
|
across
|
||||||
other_errs.start
|
other_errs as e
|
||||||
until
|
|
||||||
other_errs.after
|
|
||||||
loop
|
loop
|
||||||
add_error (other_errs.item)
|
add_error (e.item)
|
||||||
other_errs.forth
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
@@ -435,7 +424,7 @@ feature -- Basic operation
|
|||||||
new_count: count = old count + other.count
|
new_count: count = old count + other.count
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Access
|
feature -- Conversion
|
||||||
|
|
||||||
as_single_error: detachable ERROR
|
as_single_error: detachable ERROR
|
||||||
-- All error(s) concatenated into one single error.
|
-- All error(s) concatenated into one single error.
|
||||||
@@ -465,7 +454,7 @@ feature -- Access
|
|||||||
feature -- Element changes
|
feature -- Element changes
|
||||||
|
|
||||||
concatenate
|
concatenate
|
||||||
-- Concatenate into a single error if any
|
-- Concatenate into a single error if any.
|
||||||
do
|
do
|
||||||
if count > 1 and then attached as_single_error as e then
|
if count > 1 and then attached as_single_error as e then
|
||||||
reset
|
reset
|
||||||
@@ -516,7 +505,7 @@ invariant
|
|||||||
propagators_not_empty: attached propagators as lst implies not lst.is_empty
|
propagators_not_empty: attached propagators as lst implies not lst.is_empty
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2016, Jocelyn Fiat, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, 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
|
||||||
|
|||||||
@@ -25,19 +25,16 @@ feature -- Access
|
|||||||
process_group (g: ERROR_GROUP)
|
process_group (g: ERROR_GROUP)
|
||||||
do
|
do
|
||||||
if attached g.sub_errors as err then
|
if attached g.sub_errors as err then
|
||||||
from
|
across
|
||||||
err.start
|
err as e
|
||||||
until
|
|
||||||
err.after
|
|
||||||
loop
|
loop
|
||||||
process_error (err.item)
|
process_error (e.item)
|
||||||
err.forth
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, 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
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ inherit
|
|||||||
create
|
create
|
||||||
make
|
make
|
||||||
|
|
||||||
feature -- Initialization
|
feature {NONE} -- Creation
|
||||||
|
|
||||||
make (f: like file)
|
make (f: like file)
|
||||||
require
|
require
|
||||||
@@ -32,7 +32,7 @@ feature -- Access
|
|||||||
feature -- Output
|
feature -- Output
|
||||||
|
|
||||||
output_string (a_str: detachable READABLE_STRING_GENERAL)
|
output_string (a_str: detachable READABLE_STRING_GENERAL)
|
||||||
-- Output Unicode string
|
-- Output Unicode string.
|
||||||
do
|
do
|
||||||
if a_str /= Void then
|
if a_str /= Void then
|
||||||
to_implement ("Convert into UTF-8 or console encoding before output")
|
to_implement ("Convert into UTF-8 or console encoding before output")
|
||||||
@@ -51,7 +51,7 @@ feature -- Output
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2012, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, 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,12 +14,12 @@ inherit
|
|||||||
feature -- Output
|
feature -- Output
|
||||||
|
|
||||||
output_string (a_str: detachable READABLE_STRING_GENERAL)
|
output_string (a_str: detachable READABLE_STRING_GENERAL)
|
||||||
-- Output Unicode string
|
-- Output Unicode string.
|
||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
output_any (obj: detachable ANY)
|
output_any (obj: detachable ANY)
|
||||||
-- Output Unicode string
|
-- Output Unicode string.
|
||||||
do
|
do
|
||||||
if attached {READABLE_STRING_GENERAL} obj as l_str then
|
if attached {READABLE_STRING_GENERAL} obj as l_str then
|
||||||
to_implement ("Convert into UTF-8 or console encoding before output")
|
to_implement ("Convert into UTF-8 or console encoding before output")
|
||||||
@@ -42,6 +42,7 @@ feature -- Output
|
|||||||
feature -- Process
|
feature -- Process
|
||||||
|
|
||||||
process_error (e: ERROR)
|
process_error (e: ERROR)
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
output_string ({STRING_32}"Error Name: ")
|
output_string ({STRING_32}"Error Name: ")
|
||||||
output_string (e.name)
|
output_string (e.name)
|
||||||
@@ -54,6 +55,7 @@ feature -- Process
|
|||||||
end
|
end
|
||||||
|
|
||||||
process_custom (e: ERROR_CUSTOM)
|
process_custom (e: ERROR_CUSTOM)
|
||||||
|
-- <Precursor>
|
||||||
do
|
do
|
||||||
output_string ({STRING_32}"Error Name: ")
|
output_string ({STRING_32}"Error Name: ")
|
||||||
output_string (e.name)
|
output_string (e.name)
|
||||||
@@ -66,22 +68,17 @@ feature -- Process
|
|||||||
end
|
end
|
||||||
|
|
||||||
process_group (g: ERROR_GROUP)
|
process_group (g: ERROR_GROUP)
|
||||||
local
|
-- <Precursor>
|
||||||
l_errors: LIST [ERROR]
|
|
||||||
do
|
do
|
||||||
from
|
across
|
||||||
l_errors := g.sub_errors
|
g.sub_errors as s
|
||||||
l_errors.start
|
|
||||||
until
|
|
||||||
l_errors.after
|
|
||||||
loop
|
loop
|
||||||
l_errors.item.process (Current)
|
s.item.process (Current)
|
||||||
l_errors.forth
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, 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
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ inherit
|
|||||||
create
|
create
|
||||||
make
|
make
|
||||||
|
|
||||||
feature -- Initialization
|
feature {NONE} -- Creation
|
||||||
|
|
||||||
make (buf: like buffer)
|
make (buf: like buffer)
|
||||||
require
|
require
|
||||||
@@ -32,7 +32,7 @@ feature -- Access
|
|||||||
feature -- Output
|
feature -- Output
|
||||||
|
|
||||||
output_string (a_str: detachable READABLE_STRING_GENERAL)
|
output_string (a_str: detachable READABLE_STRING_GENERAL)
|
||||||
-- Output Unicode string
|
-- Output Unicode string.
|
||||||
do
|
do
|
||||||
if a_str /= Void then
|
if a_str /= Void then
|
||||||
to_implement ("Convert into UTF-8 or console encoding before output")
|
to_implement ("Convert into UTF-8 or console encoding before output")
|
||||||
@@ -51,7 +51,7 @@ feature -- Output
|
|||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2012, Eiffel Software and others"
|
copyright: "2011-2017, Jocelyn Fiat, 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
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
</option>
|
</option>
|
||||||
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false" use_application_options="true"/>
|
<library name="default_standalone" location="..\..\library\server\wsf\default\standalone-safe.ecf" readonly="false" use_application_options="true"/>
|
||||||
<cluster name="src" location="src\" recursive="true"/>
|
<cluster name="src" location="src\" recursive="true"/>
|
||||||
<override name="override" location="override\" recursive="true"/>
|
|
||||||
</target>
|
</target>
|
||||||
<target name="hello_cgi" extends="hello_dev">
|
<target name="hello_cgi" extends="hello_dev">
|
||||||
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf"/>
|
<library name="default_cgi" location="..\..\library\server\wsf\default\cgi-safe.ecf"/>
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ echo Install library: http_authorization
|
|||||||
%SAFE_MD% %TMP_CONTRIB_DIR%\library\web\authentication
|
%SAFE_MD% %TMP_CONTRIB_DIR%\library\web\authentication
|
||||||
%COPYCMD% %TMP_DIR%\library\server\authentication\http_authorization %TMP_CONTRIB_DIR%\library\web\authentication\http_authorization
|
%COPYCMD% %TMP_DIR%\library\server\authentication\http_authorization %TMP_CONTRIB_DIR%\library\web\authentication\http_authorization
|
||||||
|
|
||||||
|
echo Install library: jwt
|
||||||
|
%SAFE_MD% %TMP_CONTRIB_DIR%\library\web\authentication
|
||||||
|
%COPYCMD% %TMP_DIR%\library\security\jwt %TMP_CONTRIB_DIR%\library\web\authentication\jwt
|
||||||
|
|
||||||
echo Install library: openid
|
echo Install library: openid
|
||||||
%SAFE_MD% %TMP_CONTRIB_DIR%\library\web\authentication
|
%SAFE_MD% %TMP_CONTRIB_DIR%\library\web\authentication
|
||||||
%COPYCMD% %TMP_DIR%\library\security\openid %TMP_CONTRIB_DIR%\library\web\authentication\openid
|
%COPYCMD% %TMP_DIR%\library\security\openid %TMP_CONTRIB_DIR%\library\web\authentication\openid
|
||||||
|
|||||||
@@ -65,7 +65,9 @@ echo Uninstall library: content_negotiation
|
|||||||
%RDCMD% %TMP_CONTRIB_DIR%\library\network\protocol\content_negotiation
|
%RDCMD% %TMP_CONTRIB_DIR%\library\network\protocol\content_negotiation
|
||||||
echo Uninstall library: http_authorization
|
echo Uninstall library: http_authorization
|
||||||
%RDCMD% %TMP_CONTRIB_DIR%\library\web\authentication\http_authorization
|
%RDCMD% %TMP_CONTRIB_DIR%\library\web\authentication\http_authorization
|
||||||
echo Uninstall library: security\openid
|
echo Uninstall library: jwt
|
||||||
|
%RDCMD% %TMP_CONTRIB_DIR%\library\web\authentication\jwt
|
||||||
|
echo Uninstall library: openid
|
||||||
%RDCMD% %TMP_CONTRIB_DIR%\library\web\authentication\openid
|
%RDCMD% %TMP_CONTRIB_DIR%\library\web\authentication\openid
|
||||||
echo Uninstall library: uri_template
|
echo Uninstall library: uri_template
|
||||||
%RDCMD% %TMP_CONTRIB_DIR%\library\text\parser\uri_template
|
%RDCMD% %TMP_CONTRIB_DIR%\library\text\parser\uri_template
|
||||||
|
|||||||
Reference in New Issue
Block a user