Compare commits

...

12 Commits

Author SHA1 Message Date
Jocelyn Fiat
ca4043b102 Merge branch 'master' into v1 2017-06-20 18:17:30 +02:00
Jocelyn Fiat
310e96e185 Updated EOL for 2 ecf files. 2017-06-20 18:16:43 +02:00
Jocelyn Fiat
7feb45b549 Updated error library (cosmetic, and loop iteration). 2017-06-20 18:08:50 +02:00
Jocelyn Fiat
5bbd031275 Update EOL on ecf files. 2017-06-20 17:49:28 +02:00
Jocelyn Fiat
90e60fad26 Updated changelog. 2017-06-20 17:47:17 +02:00
Jocelyn Fiat
98c20ee7c1 Fixed specific ecf files for http_client library. 2017-06-20 17:37:24 +02:00
Jocelyn Fiat
64027f56bd Merge branch 'master' into v1 2017-06-20 09:53:29 +02:00
Jocelyn Fiat
0b99e84728 Added installation of JWT into Eiffel installation. 2017-06-20 09:53:13 +02:00
Jocelyn Fiat
a6806c676a Updated ecf files to version 1-16-0 .
Minor cosmetic changes.
2017-06-20 09:48:10 +02:00
Jocelyn Fiat
9e5e8bb1bf Added simple way to set the issued_at claim value to current UTC date time. 2017-06-14 16:27:24 +02:00
Jocelyn Fiat
10a83c6ad8 Added possibility to create JWS object with specific algo hs256 or none easily. 2017-06-14 16:26:31 +02:00
Jocelyn Fiat
1ec3b8e7a4 Added support for multiple file in form data.
Made clear what is the meaning of upload_filename, upload_data and form_data.
2017-06-14 16:19:43 +02:00
36 changed files with 923 additions and 582 deletions

View File

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

View File

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

View File

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

View File

@@ -113,14 +113,13 @@ feature -- Implementation
across across
l_values as c l_values as c
loop loop
s.replace_substring_all ({STRING_32} "${" + c.key.as_string_32 + "}", c.item) s.replace_substring_all ({STRING_32} "${" + c.key.to_string_32 + "}", c.item.to_string_32)
end end
end end
end end
note note
copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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

View File

@@ -17,6 +17,7 @@
<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="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>

View File

@@ -17,6 +17,7 @@
<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="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>

View File

@@ -26,6 +26,7 @@
</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="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>

View File

@@ -26,6 +26,7 @@
</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="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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,15 +1,3 @@
<?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="jwt" uuid="A75C2D84-D543-4708-BAF3-254C308376CC" library_target="jwt"> <redirection xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" uuid="A75C2D84-D543-4708-BAF3-254C308376CC" message="Obsolete: use jwt.ecf !" location="jwt.ecf">
<target name="jwt"> </redirection>
<root all_classes="true"/>
<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="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto-safe.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -1,10 +1,13 @@
<?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="jwt" uuid="A75C2D84-D543-4708-BAF3-254C308376CC" library_target="jwt"> <system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="jwt" uuid="A75C2D84-D543-4708-BAF3-254C308376CC" library_target="jwt">
<target name="jwt"> <target name="jwt">
<root all_classes="true"/> <root all_classes="true"/>
<option warning="true" void_safety="none"> <option warning="true">
</option> </option>
<setting name="concurrency" value="scoop"/> <capability>
<concurrency support="scoop" use="scoop"/>
<void_safety support="all" use="all"/>
</capability>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/> <library name="crypto" location="$ISE_LIBRARY\unstable\library\text\encryption\crypto\crypto.ecf"/>
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/> <library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>

View File

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

View File

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

View File

@@ -1,13 +1,16 @@
<?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="testing" uuid="DB49E98A-0048-414A-A469-EE9B5B903BF3"> <system xmlns="http://www.eiffel.com/developers/xml/configuration-1-16-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-16-0 http://www.eiffel.com/developers/xml/configuration-1-16-0.xsd" name="testing" uuid="DB49E98A-0048-414A-A469-EE9B5B903BF3">
<target name="testing"> <target name="testing">
<root class="ANY" feature="default_create"/> <root class="ANY" feature="default_create"/>
<setting name="console_application" value="true"/> <option warning="true">
<option warning="true" void_safety="all">
</option> </option>
<setting name="concurrency" value="none"/> <setting name="console_application" value="true"/>
<capability>
<concurrency support="none" use="none"/>
<void_safety support="all" use="all"/>
</capability>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/> <library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="jwt" location="..\jwt-safe.ecf" readonly="false"/> <library name="jwt" location="..\jwt.ecf" readonly="false"/>
<library name="testing" location="$ISE_LIBRARY\library\testing\testing.ecf"/> <library name="testing" location="$ISE_LIBRARY\library\testing\testing.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/> <library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<tests name="src" location=".\" recursive="true"/> <tests name="src" location=".\" recursive="true"/>

View File

@@ -8,7 +8,6 @@ note
title: Eiffel OpenID title: Eiffel OpenID
description: OpenID consumer library description: OpenID consumer library
tags: openid,security,web,authentication,sso tags: openid,security,web,authentication,sso
license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
copyright: 2011-2016, Jocelyn Fiat, Eiffel Software and others copyright: 2011-2016, 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)
link[license]: http://www.eiffel.com/licensing/forum.txt link[license]: http://www.eiffel.com/licensing/forum.txt

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,19 @@ feature -- Process
end end
process_group (g: ERROR_GROUP) process_group (g: ERROR_GROUP)
-- <Precursor>
local local
l_errors: LIST [ERROR] 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

View File

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

View File

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

View File

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