Improved query and form data encoding (based on a very early version of the general URI percent-encoding rules).
- 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. Updated autotest cases. Code cleaning.
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
</library>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
||||
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
|
||||
<cluster name="spec_net" location="$|spec\net\">
|
||||
<condition>
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
</library>
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="implementation" location="$|implementation" recursive="true" hidden="true"/>
|
||||
<cluster name="spec_null" location="$|spec\null\" recursive="true"/>
|
||||
<cluster name="spec_net" location="$|spec\net\">
|
||||
<condition>
|
||||
|
||||
@@ -196,73 +196,8 @@ feature -- Settings
|
||||
Result := session.is_insecure
|
||||
end
|
||||
|
||||
feature {NONE} -- Utilities
|
||||
|
||||
append_parameters_to_url (a_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]; a_url: STRING)
|
||||
-- Append parameters `a_parameters' to `a_url'
|
||||
require
|
||||
a_url_attached: a_url /= Void
|
||||
local
|
||||
l_first_param: BOOLEAN
|
||||
do
|
||||
if a_parameters.count > 0 then
|
||||
if a_url.index_of ('?', 1) > 0 then
|
||||
l_first_param := False
|
||||
elseif a_url.index_of ('&', 1) > 0 then
|
||||
l_first_param := False
|
||||
else
|
||||
l_first_param := True
|
||||
end
|
||||
|
||||
from
|
||||
a_parameters.start
|
||||
until
|
||||
a_parameters.after
|
||||
loop
|
||||
if l_first_param then
|
||||
a_url.append_character ('?')
|
||||
else
|
||||
a_url.append_character ('&')
|
||||
end
|
||||
a_url.append (urlencode (a_parameters.key_for_iteration))
|
||||
a_url.append_character ('=')
|
||||
a_url.append (urlencode (a_parameters.item_for_iteration))
|
||||
l_first_param := False
|
||||
a_parameters.forth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Utilities: encoding
|
||||
|
||||
url_encoder: URL_ENCODER
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
urlencode (s: READABLE_STRING_32): READABLE_STRING_8
|
||||
-- URL encode `s'
|
||||
do
|
||||
Result := url_encoder.encoded_string (s)
|
||||
end
|
||||
|
||||
urldecode (s: READABLE_STRING_8): READABLE_STRING_32
|
||||
-- URL decode `s'
|
||||
do
|
||||
Result := url_encoder.decoded_string (s)
|
||||
end
|
||||
|
||||
stripslashes (s: STRING): STRING
|
||||
do
|
||||
Result := s.string
|
||||
Result.replace_substring_all ("\%"", "%"")
|
||||
Result.replace_substring_all ("\'", "'")
|
||||
Result.replace_substring_all ("\/", "/")
|
||||
Result.replace_substring_all ("\\", "\")
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2016, 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)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -142,16 +142,16 @@ feature -- Element change
|
||||
end
|
||||
end
|
||||
|
||||
add_query_parameter (k: READABLE_STRING_32; v: READABLE_STRING_32)
|
||||
add_query_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL)
|
||||
-- Add a query parameter `k=v'.
|
||||
do
|
||||
query_parameters.force (v, k)
|
||||
query_parameters.force (v.to_string_32, k.to_string_32)
|
||||
end
|
||||
|
||||
add_form_parameter (k: READABLE_STRING_32; v: READABLE_STRING_32)
|
||||
add_form_parameter (k: READABLE_STRING_GENERAL; v: READABLE_STRING_GENERAL)
|
||||
-- Add a form parameter `k'= `v'.
|
||||
do
|
||||
form_parameters.force (v, k)
|
||||
form_parameters.force (v.to_string_32, k.to_string_32)
|
||||
end
|
||||
|
||||
set_credentials_required (b: BOOLEAN)
|
||||
@@ -236,18 +236,62 @@ feature -- Status setting
|
||||
end
|
||||
end
|
||||
|
||||
feature -- URL helpers
|
||||
|
||||
append_query_parameters_to_url (a_url: STRING)
|
||||
-- Append parameters `a_parameters' to `a_url'
|
||||
require
|
||||
a_url_attached: a_url /= Void
|
||||
local
|
||||
l_first_param: BOOLEAN
|
||||
do
|
||||
if
|
||||
attached query_parameters as l_query_parameters and then
|
||||
not l_query_parameters.is_empty
|
||||
then
|
||||
if a_url.index_of ('?', 1) > 0 then
|
||||
l_first_param := False
|
||||
elseif a_url.index_of ('&', 1) > 0 then
|
||||
l_first_param := False
|
||||
else
|
||||
l_first_param := True
|
||||
end
|
||||
|
||||
across
|
||||
query_parameters as ic
|
||||
loop
|
||||
if l_first_param then
|
||||
a_url.append_character ('?')
|
||||
else
|
||||
a_url.append_character ('&')
|
||||
end
|
||||
l_first_param := False
|
||||
uri_percent_encoder.append_query_name_encoded_string_to (ic.key, a_url)
|
||||
a_url.append_character ('=')
|
||||
uri_percent_encoder.append_query_value_encoded_string_to (ic.item, a_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Conversion helpers
|
||||
|
||||
query_parameters_to_url_encoded_string: STRING_8
|
||||
-- `query_parameters' as url-encoded string.
|
||||
do
|
||||
Result := parameters_to_url_encoded_string (query_parameters)
|
||||
Result := parameters_to_uri_percent_encoded_string (query_parameters)
|
||||
end
|
||||
|
||||
form_parameters_to_x_www_form_url_encoded_string: STRING_8
|
||||
-- `form_parameters' as x-www-form-urlencoded string.
|
||||
do
|
||||
Result := parameters_to_x_www_form_urlencoded_string (form_parameters)
|
||||
end
|
||||
|
||||
form_parameters_to_url_encoded_string: STRING_8
|
||||
-- `form_parameters' as url-encoded string.
|
||||
obsolete "Use form_parameters_to_x_www_form_url_encoded_string [2017-05-31]"
|
||||
do
|
||||
Result := parameters_to_url_encoded_string (form_parameters)
|
||||
Result := form_parameters_to_x_www_form_url_encoded_string
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
@@ -271,6 +315,52 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
end
|
||||
|
||||
parameters_to_uri_percent_encoded_string (ht: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING_8
|
||||
-- Build query urlencoded string using parameters from `ht'.
|
||||
do
|
||||
create Result.make (64)
|
||||
across
|
||||
ht as ic
|
||||
loop
|
||||
if not Result.is_empty then
|
||||
Result.append_character ('&')
|
||||
end
|
||||
uri_percent_encoder.append_query_name_encoded_string_to (ic.key, Result)
|
||||
Result.append_character ('=')
|
||||
uri_percent_encoder.append_query_value_encoded_string_to (ic.item, Result)
|
||||
end
|
||||
end
|
||||
|
||||
parameters_to_x_www_form_urlencoded_string (ht: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]): STRING_8
|
||||
-- Build x-www-form-urlencoded string using parameters from `ht'.
|
||||
do
|
||||
create Result.make (64)
|
||||
from
|
||||
ht.start
|
||||
until
|
||||
ht.after
|
||||
loop
|
||||
if not Result.is_empty then
|
||||
Result.append_character ('&')
|
||||
end
|
||||
Result.append (x_www_form_url_encoder.encoded_string (ht.key_for_iteration))
|
||||
Result.append_character ('=')
|
||||
Result.append (x_www_form_url_encoder.encoded_string (ht.item_for_iteration))
|
||||
ht.forth
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
url_encoder: URL_ENCODER
|
||||
-- Shared URL encoder.
|
||||
once
|
||||
@@ -278,7 +368,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
|
||||
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)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -60,28 +60,10 @@ feature -- Access
|
||||
|
||||
url (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): STRING_8
|
||||
-- Url computed from Current and `ctx' data.
|
||||
local
|
||||
s: STRING_8
|
||||
url_encoder: URL_ENCODER
|
||||
do
|
||||
Result := base_url + a_path
|
||||
if ctx /= Void then
|
||||
create s.make_empty
|
||||
create url_encoder
|
||||
across
|
||||
ctx.query_parameters as q
|
||||
loop
|
||||
if not s.is_empty then
|
||||
s.append_character ('&')
|
||||
end
|
||||
s.append (url_encoder.encoded_string (q.key))
|
||||
s.append_character ('=')
|
||||
s.append (url_encoder.encoded_string (q.item))
|
||||
end
|
||||
if not s.is_empty then
|
||||
Result.append_character ('?')
|
||||
Result.append (s)
|
||||
end
|
||||
ctx.append_query_parameters_to_url (Result)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -420,7 +402,7 @@ feature -- Element change
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2016, 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)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -0,0 +1,628 @@
|
||||
note
|
||||
description: "[
|
||||
Component to handle percent encoding
|
||||
|
||||
WARNING: THIS IS A COPY FROM $ISE_LIBRARY/library/text/uri library.
|
||||
In the future, http_client will use directly the `uri` library.
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
EIS: "name=Percent-encoding", "protocol=URI", "src=http://en.wikipedia.org/wiki/Percent-encoding"
|
||||
|
||||
class
|
||||
URI_PERCENT_ENCODER
|
||||
|
||||
feature -- Percent encoding
|
||||
|
||||
append_percent_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
|
||||
-- Append `s' as percent-encoded value to `a_result'
|
||||
local
|
||||
i,n: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := s.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
append_encoded_character_code_to (s.code (i), a_result)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
append_query_name_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
|
||||
-- Append `s' as encoded for URI query name to `a_result'
|
||||
local
|
||||
i,n: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := s.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
append_query_name_encoded_character_code_to (s.code (i), a_result)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
append_query_value_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
|
||||
-- Append `s' as encoded for URI query value to `a_result'.
|
||||
local
|
||||
i,n: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := s.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
append_query_value_encoded_character_code_to (s.code (i), a_result)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
append_path_segment_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
|
||||
-- Append `a_string' as encoded for URI path segment to `a_result'
|
||||
local
|
||||
i,n: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := s.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
append_path_segment_encoded_character_code_to (s.code (i), a_result)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
append_www_form_url_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
|
||||
-- Append `a_string' as www-form-urlencoded value to `a_result'.
|
||||
-- The main difference with `append_percent_encoded_string_to` is the encoding of space using '+'.
|
||||
local
|
||||
i,n: INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
n := s.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
inspect s.code (i)
|
||||
when 32 then -- space: 32 ' '
|
||||
a_result.append_code (43) -- 43 '+'
|
||||
else
|
||||
append_encoded_character_code_to (s.code (i), a_result)
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- URI building helpers
|
||||
|
||||
append_encoded_character_code_to (c: NATURAL_32; a_result: STRING_GENERAL)
|
||||
-- Append character code `c' as query name encoded content into `a_result'.
|
||||
do
|
||||
if
|
||||
--| unreserved ALPHA / DIGIT
|
||||
(48 <= c and c <= 57) -- DIGIT: 0 .. 9
|
||||
or (65 <= c and c <= 90) -- ALPHA: A .. Z
|
||||
or (97 <= c and c <= 122) -- ALPHA: a .. z
|
||||
then
|
||||
a_result.append_code (c)
|
||||
else
|
||||
inspect c
|
||||
when
|
||||
45, 46, 95, 126 -- unreserved characters: -._~
|
||||
then
|
||||
a_result.append_code (c)
|
||||
when
|
||||
58, 64, -- reserved =+ gen-delims: : @
|
||||
33, 36, 38, 39, 40, 41, 42, -- reserved =+ sub-delims: ! $ & ' ( ) *
|
||||
43, 44, 59, 61, -- reserved = sub-delims: + , ; =
|
||||
37 -- percent encoding: %
|
||||
then
|
||||
append_percent_encoded_character_code_to (c, a_result)
|
||||
else
|
||||
append_percent_encoded_character_code_to (c, a_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
append_query_name_encoded_character_code_to (c: NATURAL_32; a_result: STRING_GENERAL)
|
||||
-- Append character code `a_code' as query name encoded content into `a_result'.
|
||||
do
|
||||
inspect c
|
||||
when 61 then -- equal sign: =
|
||||
append_percent_encoded_character_code_to (c, a_result)
|
||||
else
|
||||
append_query_value_encoded_character_code_to (c, a_result)
|
||||
end
|
||||
end
|
||||
|
||||
append_query_value_encoded_character_code_to (c: NATURAL_32; a_result: STRING_GENERAL)
|
||||
-- Append character code `a_code' as query value encoded content into `a_result'.
|
||||
do
|
||||
inspect c
|
||||
when 32 then -- Space
|
||||
a_result.append_code (43) -- 43 '+'
|
||||
when
|
||||
39, -- '
|
||||
58, 64, -- reserved =+ gen-delims: : @
|
||||
33, 36, 40, 41, 42, -- reserved =+ sub-delims: ! $ ( ) *
|
||||
44, 59, 61 -- reserved = sub-delims: , ; =
|
||||
then
|
||||
a_result.append_code (c)
|
||||
when
|
||||
47, -- slash: /
|
||||
63 -- question mark ?
|
||||
then
|
||||
a_result.append_code (c)
|
||||
else
|
||||
append_encoded_character_code_to (c, a_result)
|
||||
end
|
||||
end
|
||||
|
||||
append_path_segment_encoded_character_code_to (c: NATURAL_32; a_result: STRING_GENERAL)
|
||||
-- Append character code `a_code' as query name encoded content into `a_result'.
|
||||
do
|
||||
append_encoded_character_code_to (c, a_result)
|
||||
end
|
||||
|
||||
feature -- Percent encoding: character
|
||||
|
||||
append_percent_encoded_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
|
||||
-- Append character code `a_code' as percent-encoded content into `a_result'
|
||||
do
|
||||
if a_code > 0xFF then
|
||||
-- Unicode
|
||||
append_percent_encoded_unicode_character_code_to (a_code, a_result)
|
||||
elseif a_code > 0x7F then
|
||||
-- Extended ASCII
|
||||
-- This requires percent-encoding on UTF-8 converted character.
|
||||
append_percent_encoded_unicode_character_code_to (a_code, a_result)
|
||||
else
|
||||
-- ASCII
|
||||
append_percent_encoded_ascii_character_code_to (a_code, a_result)
|
||||
end
|
||||
ensure
|
||||
appended: a_result.count > old a_result.count
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: character encoding
|
||||
|
||||
append_percent_encoded_ascii_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
|
||||
-- Append extended ascii character code `a_code' as percent-encoded content into `a_result'
|
||||
-- Note: it does not UTF-8 convert this extended ASCII.
|
||||
require
|
||||
is_extended_ascii: a_code <= 0xFF
|
||||
local
|
||||
c: INTEGER
|
||||
do
|
||||
if a_code > 0xFF then
|
||||
-- Unicode
|
||||
append_percent_encoded_unicode_character_code_to (a_code, a_result)
|
||||
else
|
||||
-- Extended ASCII
|
||||
c := a_code.to_integer_32
|
||||
a_result.append_code (37) -- 37 '%%'
|
||||
a_result.append_code (hex_digit [c |>> 4])
|
||||
a_result.append_code (hex_digit [c & 0xF])
|
||||
end
|
||||
ensure
|
||||
appended: a_result.count > old a_result.count
|
||||
end
|
||||
|
||||
append_percent_encoded_unicode_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
|
||||
-- Append Unicode character code `a_code' as UTF-8 and percent-encoded content into `a_result'
|
||||
-- Note: it does include UTF-8 conversion of extended ASCII and Unicode.
|
||||
do
|
||||
if a_code <= 0x7F then
|
||||
-- 0xxxxxxx
|
||||
append_percent_encoded_ascii_character_code_to (a_code, a_result)
|
||||
elseif a_code <= 0x7FF then
|
||||
-- 110xxxxx 10xxxxxx
|
||||
append_percent_encoded_ascii_character_code_to ((a_code |>> 6) | 0xC0, a_result)
|
||||
append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result)
|
||||
elseif a_code <= 0xFFFF then
|
||||
-- 1110xxxx 10xxxxxx 10xxxxxx
|
||||
append_percent_encoded_ascii_character_code_to ((a_code |>> 12) | 0xE0, a_result)
|
||||
append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result)
|
||||
append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result)
|
||||
else
|
||||
-- c <= 1FFFFF - there are no higher code points
|
||||
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
append_percent_encoded_ascii_character_code_to ((a_code |>> 18) | 0xF0, a_result)
|
||||
append_percent_encoded_ascii_character_code_to (((a_code |>> 12) & 0x3F) | 0x80, a_result)
|
||||
append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result)
|
||||
append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result)
|
||||
end
|
||||
ensure
|
||||
appended: a_result.count > old a_result.count
|
||||
end
|
||||
|
||||
feature -- Percent decoding
|
||||
|
||||
append_percent_decoded_string_to (v: READABLE_STRING_GENERAL; a_result: STRING_GENERAL)
|
||||
-- Append to `a_result' a string equivalent to the percent-encoded string `v'
|
||||
--| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8
|
||||
local
|
||||
i,n: INTEGER
|
||||
c: NATURAL_32
|
||||
pr: CELL [INTEGER]
|
||||
a_result_is_string_32: BOOLEAN
|
||||
do
|
||||
a_result_is_string_32 := attached {STRING_32} a_result
|
||||
from
|
||||
i := 1
|
||||
create pr.put (i)
|
||||
n := v.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
c := v.code (i)
|
||||
inspect c
|
||||
when 43 then -- 43 '+'
|
||||
-- Some implementation are replacing spaces with "+" instead of "%20"
|
||||
a_result.append_code (32) -- 32 ' '
|
||||
when 37 then -- 37 '%%'
|
||||
-- An escaped character ?
|
||||
if i = n then -- Error?
|
||||
a_result.append_code (c)
|
||||
else
|
||||
if a_result_is_string_32 then
|
||||
-- Convert UTF-8 to UTF-32
|
||||
pr.replace (i)
|
||||
c := next_percent_decoded_unicode_character_code (v, pr)
|
||||
a_result.append_code (c)
|
||||
i := pr.item
|
||||
else
|
||||
-- Keep UTF-8
|
||||
pr.replace (i)
|
||||
c := next_percent_decoded_character_code (v, pr)
|
||||
a_result.append_code (c)
|
||||
i := pr.item
|
||||
end
|
||||
end
|
||||
else
|
||||
if c <= 0x7F then
|
||||
a_result.append_code (c)
|
||||
else
|
||||
if a_result_is_string_32 then
|
||||
a_result.append_code (c)
|
||||
else
|
||||
-- Keep the percent encoded char for non string 32.
|
||||
append_percent_encoded_character_code_to (c, a_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: decoding
|
||||
|
||||
next_percent_decoded_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32
|
||||
-- Character decoded from string `v' starting from index `a_position.item'
|
||||
-- note: it also updates `a_position.item' to indicate the new index position.
|
||||
require
|
||||
valid_start: a_position.item <= v.count
|
||||
is_percent_char: v.code (a_position.item) = 37 -- 37 '%%'
|
||||
local
|
||||
c: NATURAL_32
|
||||
i, n: INTEGER
|
||||
not_a_digit: BOOLEAN
|
||||
ascii_pos: NATURAL_32
|
||||
ival: NATURAL_32
|
||||
pos: INTEGER
|
||||
c_is_digit: BOOLEAN
|
||||
do
|
||||
--| pos is index in stream of escape character ('%')
|
||||
pos := a_position.item
|
||||
c := v.code (pos + 1)
|
||||
if c = 85 or c = 117 then -- 117 'u' 85 'U'
|
||||
-- NOTE: this is not a standard, but it can occur, so use this for decoding only
|
||||
-- An escaped Unicode (ucs2) value, from ECMA scripts
|
||||
-- has the form: %u<n> where <n> is the UCS value
|
||||
-- of the character (two byte integer, one to 4 chars
|
||||
-- after escape sequence).
|
||||
-- See: http://en.wikipedia.org/wiki/Percent-encoding#Non-standard_implementations
|
||||
-- UTF-8 result can be 1 to 4 characters.
|
||||
from
|
||||
i := pos + 2
|
||||
n := v.count
|
||||
until
|
||||
(i > n) or not_a_digit
|
||||
loop
|
||||
c := v.code (i)
|
||||
c_is_digit := (48 <= c and c <= 57) -- DIGIT: 0 .. 9
|
||||
if
|
||||
c_is_digit
|
||||
or (97 <= c and c <= 102) -- ALPHA: a..f
|
||||
or (65 <= c and c <= 70) -- ALPHA: A..F
|
||||
then
|
||||
ival := ival * 16
|
||||
if c_is_digit then
|
||||
ival := ival + (c - 48) -- 48 '0'
|
||||
else
|
||||
if c > 70 then -- a..f
|
||||
ival := ival + (c - 97) + 10 -- 97 'a'
|
||||
else -- A..F
|
||||
ival := ival + (c - 65) + 10 -- 65 'A'
|
||||
end
|
||||
end
|
||||
i := i + 1
|
||||
else
|
||||
not_a_digit := True
|
||||
i := i - 1
|
||||
end
|
||||
end
|
||||
a_position.replace (i)
|
||||
Result := ival
|
||||
else
|
||||
-- ASCII char?
|
||||
ascii_pos := hexadecimal_string_to_natural_32 (v.substring (pos + 1, pos + 2))
|
||||
Result := ascii_pos
|
||||
a_position.replace (pos + 2)
|
||||
end
|
||||
end
|
||||
|
||||
next_percent_decoded_unicode_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32
|
||||
-- Next decoded character from `v' at position `a_position.item'
|
||||
-- note: it also updates `a_position' to indicate the new index position.
|
||||
require
|
||||
valid_start: a_position.item <= v.count
|
||||
is_percent_char: v.code (a_position.item) = 37 -- 37 '%%'
|
||||
local
|
||||
n, j: INTEGER
|
||||
c: NATURAL_32
|
||||
c1, c2, c3, c4: NATURAL_32
|
||||
pr: CELL [INTEGER]
|
||||
do
|
||||
create pr.put (a_position.item)
|
||||
c1 := next_percent_decoded_character_code (v, pr)
|
||||
|
||||
j := pr.item
|
||||
n := v.count
|
||||
|
||||
Result := c1
|
||||
a_position.replace (j)
|
||||
|
||||
if c1 <= 0x7F then
|
||||
-- 0xxxxxxx
|
||||
Result := c1
|
||||
elseif c1 <= 0xDF then
|
||||
-- 110xxxxx 10xxxxxx
|
||||
if j + 2 <= n then
|
||||
c := v.code (j + 1)
|
||||
if c = 37 then -- 37 '%%'
|
||||
pr.replace (j + 1)
|
||||
c2 := next_percent_decoded_character_code (v, pr)
|
||||
j := pr.item
|
||||
Result := (
|
||||
((c1 & 0x1F) |<< 6) |
|
||||
( c2 & 0x3F )
|
||||
)
|
||||
a_position.replace (j)
|
||||
else
|
||||
-- Do not try to decode
|
||||
end
|
||||
end
|
||||
elseif c1 <= 0xEF then
|
||||
-- 1110xxxx 10xxxxxx 10xxxxxx
|
||||
if j + 2 <= n then
|
||||
c := v.code (j + 1)
|
||||
if c = 37 then -- 37 '%%'
|
||||
pr.replace (j + 1)
|
||||
c2 := next_percent_decoded_character_code (v, pr)
|
||||
j := pr.item
|
||||
if j + 2 <= n then
|
||||
c := v.code (j + 1)
|
||||
if c = 37 then -- 37 '%%'
|
||||
pr.replace (j + 1)
|
||||
c3 := next_percent_decoded_character_code (v, pr)
|
||||
j := pr.item
|
||||
|
||||
Result := (
|
||||
((c1 & 0xF) |<< 12) |
|
||||
((c2 & 0x3F) |<< 6) |
|
||||
( c3 & 0x3F )
|
||||
)
|
||||
a_position.replace (j)
|
||||
else
|
||||
-- Do not try to decode
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Do not try to decode
|
||||
end
|
||||
end
|
||||
elseif c1 <= 0xF7 then
|
||||
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
|
||||
if j + 2 <= n then
|
||||
c := v.code (j + 1)
|
||||
if c = 37 then -- 37 '%%'
|
||||
pr.replace (j + 1)
|
||||
c2 := next_percent_decoded_character_code (v, pr)
|
||||
j := pr.item
|
||||
if j + 2 <= n then
|
||||
c := v.code (j + 1)
|
||||
if c = 37 then -- 37 '%%'
|
||||
pr.replace (j + 1)
|
||||
c3 := next_percent_decoded_character_code (v, pr)
|
||||
j := pr.item
|
||||
if j + 2 <= n then
|
||||
c := v.code (j + 1)
|
||||
if c = 37 then -- 37 '%%'
|
||||
pr.replace (j + 1)
|
||||
c4 := next_percent_decoded_character_code (v, pr)
|
||||
j := pr.item
|
||||
|
||||
a_position.replace (j)
|
||||
|
||||
Result := (
|
||||
((c1 & 0x7) |<< 18 ) |
|
||||
((c2 & 0x3F) |<< 12) |
|
||||
((c3 & 0x3F) |<< 6) |
|
||||
( c4 & 0x3F )
|
||||
)
|
||||
else
|
||||
-- Do not try to decode
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Do not try to decode
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Do not try to decode
|
||||
end
|
||||
end
|
||||
else
|
||||
Result := c1
|
||||
end
|
||||
end
|
||||
|
||||
feature -- RFC and characters
|
||||
|
||||
is_hexa_decimal_character (c: CHARACTER_32): BOOLEAN
|
||||
-- Is hexadecimal character ?
|
||||
do
|
||||
Result := ('a' <= c and c <= 'f') or ('A' <= c and c <= 'F') -- HEXA
|
||||
or ('0' <= c and c <= '9') -- DIGIT
|
||||
end
|
||||
|
||||
is_alpha_or_digit_character (c: CHARACTER_32): BOOLEAN
|
||||
-- Is ALPHA or DIGIT character ?
|
||||
do
|
||||
Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') -- ALPHA
|
||||
or ('0' <= c and c <= '9') -- DIGIT
|
||||
end
|
||||
|
||||
is_alpha_character (c: CHARACTER_32): BOOLEAN
|
||||
-- Is ALPHA character ?
|
||||
do
|
||||
Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z')
|
||||
end
|
||||
|
||||
is_digit_character (c: CHARACTER_32): BOOLEAN
|
||||
-- Is DIGIT character ?
|
||||
do
|
||||
Result := ('0' <= c and c <= '9')
|
||||
end
|
||||
|
||||
is_unreserved_character (c: CHARACTER_32): BOOLEAN
|
||||
-- unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
do
|
||||
if
|
||||
('a' <= c and c <= 'z') -- ALPHA
|
||||
or ('A' <= c and c <= 'Z') -- ALPHA
|
||||
or ('0' <= c and c <= '9') -- DIGIT
|
||||
then
|
||||
Result := True
|
||||
else
|
||||
inspect c
|
||||
when '-', '_', '.', '~' then -- unreserved
|
||||
Result := True
|
||||
else
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
is_reserved_character (c: CHARACTER_32): BOOLEAN
|
||||
-- reserved = gen-delims / sub-delims
|
||||
do
|
||||
Result := is_gen_delims_character (c) or is_sub_delims_character (c)
|
||||
end
|
||||
|
||||
is_gen_delims_character (c: CHARACTER_32): BOOLEAN
|
||||
-- gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
|
||||
do
|
||||
inspect c
|
||||
when ':' , '/', '?' , '#' , '[' , ']' , '@' then
|
||||
Result := True
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
is_sub_delims_character (c: CHARACTER_32): BOOLEAN
|
||||
-- sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
-- / "*" / "+" / "," / ";" / "="
|
||||
do
|
||||
inspect c
|
||||
when '!' , '$' , '&' , '%'' , '(' , ')' , '*' , '+' , ',' , ';' , '=' then -- sub-delims
|
||||
Result := True
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
hex_digit: SPECIAL [NATURAL_32]
|
||||
-- Hexadecimal digits.
|
||||
once
|
||||
create Result.make_filled (0, 16)
|
||||
Result [0] := {NATURAL_32} 48 -- 48 '0'
|
||||
Result [1] := {NATURAL_32} 49 -- 49 '1'
|
||||
Result [2] := {NATURAL_32} 50 -- 50 '2'
|
||||
Result [3] := {NATURAL_32} 51 -- 51 '3'
|
||||
Result [4] := {NATURAL_32} 52 -- 52 '4'
|
||||
Result [5] := {NATURAL_32} 53 -- 53 '5'
|
||||
Result [6] := {NATURAL_32} 54 -- 54 '6'
|
||||
Result [7] := {NATURAL_32} 55 -- 55 '7'
|
||||
Result [8] := {NATURAL_32} 56 -- 56 '8'
|
||||
Result [9] := {NATURAL_32} 57 -- 57 '9'
|
||||
Result [10] := {NATURAL_32} 65 -- 65 'A'
|
||||
Result [11] := {NATURAL_32} 66 -- 66 'B'
|
||||
Result [12] := {NATURAL_32} 67 -- 67 'C'
|
||||
Result [13] := {NATURAL_32} 68 -- 68 'D'
|
||||
Result [14] := {NATURAL_32} 69 -- 69 'E'
|
||||
Result [15] := {NATURAL_32} 70 -- 70 'F'
|
||||
end
|
||||
|
||||
is_hexa_decimal (a_string: READABLE_STRING_GENERAL): BOOLEAN
|
||||
-- Is `a_string' a valid hexadecimal sequence?
|
||||
local
|
||||
l_convertor: like ctoi_convertor
|
||||
do
|
||||
l_convertor := ctoi_convertor
|
||||
l_convertor.parse_string_with_type (a_string, {NUMERIC_INFORMATION}.type_natural_32)
|
||||
Result := l_convertor.is_integral_integer
|
||||
end
|
||||
|
||||
hexadecimal_string_to_natural_32 (a_hex_string: READABLE_STRING_GENERAL): NATURAL_32
|
||||
-- Convert hexadecimal value `a_hex_string' to its corresponding NATURAL_32 value.
|
||||
require
|
||||
is_hexa: is_hexa_decimal (a_hex_string)
|
||||
local
|
||||
l_convertor: like ctoi_convertor
|
||||
do
|
||||
l_convertor := ctoi_convertor
|
||||
l_convertor.parse_string_with_type (a_hex_string, {NUMERIC_INFORMATION}.type_no_limitation)
|
||||
Result := l_convertor.parsed_natural_32
|
||||
end
|
||||
|
||||
ctoi_convertor: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
|
||||
-- Converter used to convert string to integer or natural.
|
||||
once
|
||||
create Result.make
|
||||
Result.set_leading_separators_acceptable (False)
|
||||
Result.set_trailing_separators_acceptable (False)
|
||||
ensure
|
||||
ctoi_convertor_not_void: Result /= Void
|
||||
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
|
||||
@@ -82,9 +82,6 @@ feature -- Execution
|
||||
|
||||
--| URL
|
||||
l_url := url
|
||||
if ctx /= Void then
|
||||
append_parameters_to_url (ctx.query_parameters, l_url)
|
||||
end
|
||||
|
||||
if session.is_header_sent_verbose then
|
||||
io.error.put_string ("> Sending:%N")
|
||||
@@ -171,7 +168,7 @@ feature -- Execution
|
||||
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_url_encoded_string
|
||||
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
|
||||
@@ -179,7 +176,7 @@ feature -- Execution
|
||||
l_use_curl_form := True
|
||||
end
|
||||
else
|
||||
l_upload_data := ctx.form_parameters_to_url_encoded_string
|
||||
l_upload_data := ctx.form_parameters_to_x_www_form_url_encoded_string
|
||||
end
|
||||
else
|
||||
l_use_curl_form := True
|
||||
@@ -199,6 +196,14 @@ feature -- Execution
|
||||
)
|
||||
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
|
||||
|
||||
@@ -41,7 +41,7 @@ feature -- Custom
|
||||
local
|
||||
req: HTTP_CLIENT_REQUEST
|
||||
do
|
||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (url (a_path, ctx), a_method, Current, ctx)
|
||||
Result := req.response
|
||||
end
|
||||
|
||||
@@ -154,7 +154,7 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
end
|
||||
|
||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||
create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (url (a_path, ctx), a_method, Current, ctx)
|
||||
Result := req.response
|
||||
|
||||
if f /= Void then
|
||||
@@ -176,7 +176,7 @@ feature {LIBCURL_HTTP_CLIENT_REQUEST} -- Curl implementation
|
||||
|
||||
|
||||
;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)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -156,7 +156,7 @@ feature -- Access
|
||||
end
|
||||
|
||||
create l_request_uri.make_from_string (l_uri.path)
|
||||
if attached l_uri.query as l_query then
|
||||
if attached l_uri.query as l_query and then not l_query.is_empty then
|
||||
l_request_uri.append_character ('?')
|
||||
l_request_uri.append (l_query)
|
||||
end
|
||||
@@ -198,7 +198,7 @@ feature -- Access
|
||||
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_url_encoded_string
|
||||
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)
|
||||
@@ -208,12 +208,12 @@ feature -- Access
|
||||
-- not supported !
|
||||
-- Send as form-urlencoded
|
||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||
l_upload_data := ctx.form_parameters_to_url_encoded_string
|
||||
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_url_encoded_string
|
||||
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
|
||||
@@ -246,7 +246,7 @@ feature -- Access
|
||||
elseif l_upload_filename /= Void then
|
||||
check ctx.has_upload_filename end
|
||||
create l_upload_file.make_with_name (l_upload_filename)
|
||||
if l_upload_file.exists and then l_upload_file.readable then
|
||||
if l_upload_file.exists and then l_upload_file.is_access_readable then
|
||||
if not l_is_chunked_transfer_encoding then
|
||||
headers.extend (l_upload_file.count.out, "Content-Length")
|
||||
end
|
||||
@@ -495,6 +495,7 @@ feature {NONE} -- Helpers
|
||||
across
|
||||
a_form_parameters as ic
|
||||
loop
|
||||
Result.append ("--")
|
||||
Result.append (a_mime_boundary)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append ("Content-Disposition: form-data; name=")
|
||||
@@ -519,7 +520,7 @@ feature {NONE} -- Helpers
|
||||
else
|
||||
l_mime_type := "application/octet-stream"
|
||||
end
|
||||
|
||||
Result.append ("--")
|
||||
Result.append (a_mime_boundary)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append ("Content-Disposition: form-data; name=%"")
|
||||
@@ -542,6 +543,7 @@ feature {NONE} -- Helpers
|
||||
end
|
||||
Result.append (http_end_of_header_line)
|
||||
end
|
||||
Result.append ("--")
|
||||
Result.append (a_mime_boundary)
|
||||
Result.append ("--") --| end
|
||||
end
|
||||
@@ -907,7 +909,7 @@ feature {NONE} -- Helpers
|
||||
create ran.set_seed (i) -- FIXME: use a real random seed.
|
||||
ran.start
|
||||
ran.forth
|
||||
n := (20 * ran.real_item).truncated_to_integer
|
||||
n := (10 * ran.real_item).truncated_to_integer
|
||||
create Result.make_filled ('-', 3 + n)
|
||||
s := "_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
from
|
||||
|
||||
@@ -67,7 +67,7 @@ feature -- Custom
|
||||
local
|
||||
req: HTTP_CLIENT_REQUEST
|
||||
do
|
||||
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||
create {NET_HTTP_CLIENT_REQUEST} req.make (url (a_path, ctx), a_method, Current, ctx)
|
||||
Result := req.response
|
||||
end
|
||||
|
||||
@@ -167,12 +167,12 @@ feature {NONE} -- Implementation
|
||||
end
|
||||
end
|
||||
|
||||
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||
create {NET_HTTP_CLIENT_REQUEST} req.make (url (a_path, ctx), a_method, Current, ctx)
|
||||
Result := req.response
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2016, 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)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -25,7 +25,7 @@ feature -- Custom
|
||||
|
||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
do
|
||||
create Result.make (base_url + a_path)
|
||||
create Result.make (url (a_path, ctx))
|
||||
end
|
||||
|
||||
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||
@@ -98,7 +98,7 @@ feature -- Status report
|
||||
-- Is interface usable?
|
||||
|
||||
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)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
|
||||
@@ -34,6 +34,11 @@ feature -- Tests
|
||||
test_post_with_form_data
|
||||
end
|
||||
|
||||
libcurl_test_post_with_uncommon_form_data
|
||||
do
|
||||
test_post_with_uncommon_form_data
|
||||
end
|
||||
|
||||
libcurl_test_post_with_file
|
||||
do
|
||||
test_post_with_file
|
||||
|
||||
@@ -34,6 +34,11 @@ feature -- Tests
|
||||
test_post_with_form_data
|
||||
end
|
||||
|
||||
net_test_post_with_uncommon_form_data
|
||||
do
|
||||
test_post_with_uncommon_form_data
|
||||
end
|
||||
|
||||
net_test_post_with_file
|
||||
do
|
||||
test_post_with_file
|
||||
@@ -69,4 +74,9 @@ feature -- Tests
|
||||
test_post_with_file_using_chunked_transfer_encoding
|
||||
end
|
||||
|
||||
net_test_get_with_query_parameters
|
||||
do
|
||||
test_get_with_query_parameters
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
note
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
@@ -21,7 +21,10 @@ feature -- Initialization
|
||||
on_prepare
|
||||
do
|
||||
Precursor
|
||||
global_requestbin_path := new_requestbin_path
|
||||
global_requestbin_path := "/s0jkhhs0"
|
||||
if global_requestbin_path = Void then
|
||||
global_requestbin_path := new_requestbin_path
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Factory
|
||||
@@ -49,6 +52,9 @@ feature -- Requestbin
|
||||
i := l_content.substring_index ("%"name%":", 1)
|
||||
if i > 0 then
|
||||
j := l_content.index_of (',', i + 1)
|
||||
if j = 0 then
|
||||
j := l_content.index_of ('}', i + 1)
|
||||
end
|
||||
if j > 0 then
|
||||
Result := l_content.substring (i + 7, j - 1)
|
||||
Result.adjust
|
||||
@@ -61,6 +67,7 @@ feature -- Requestbin
|
||||
if not Result.starts_with ("/") then
|
||||
Result.prepend_character ('/')
|
||||
end
|
||||
print ("new_requestbin_path => http://requestb.in" + Result + "?inspect%N")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -108,8 +115,55 @@ feature -- Factory
|
||||
-- check requestbin to ensure the form parameters are correctly received
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
l_ctx.form_parameters.extend ("First Value", "First Key")
|
||||
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
||||
l_ctx.add_form_parameter ("First Key", "First Value")
|
||||
l_ctx.add_form_parameter ("Second Key", "Second Value")
|
||||
l_ctx.add_form_parameter ("unicode", {STRING_32} "Hello / 你好 !")
|
||||
l_ctx.add_form_parameter ({STRING_32} "Field 你好 !", "How are you?")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.post (requestbin_path, l_ctx, "") as res and then
|
||||
attached res.headers as hds
|
||||
then
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||
end
|
||||
end
|
||||
print (h)
|
||||
else
|
||||
assert ("Has requestbin path", False)
|
||||
end
|
||||
end
|
||||
|
||||
test_post_with_uncommon_form_data
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
do
|
||||
if attached global_requestbin_path as requestbin_path then
|
||||
|
||||
-- POST REQUEST WITH FORM DATA
|
||||
-- check requestbin to ensure the form parameters are correctly received
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
|
||||
l_ctx.add_form_parameter ("title", "Eiffel World!") -- space and !
|
||||
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"))
|
||||
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.post (requestbin_path, l_ctx, "") as res and then
|
||||
@@ -143,7 +197,7 @@ feature -- Factory
|
||||
l_ctx.set_upload_filename ("test.txt")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.post (requestbin_path, l_ctx, "") as res and then
|
||||
attached sess.post (requestbin_path, l_ctx, Void) as res and then
|
||||
attached res.headers as hds
|
||||
then
|
||||
across
|
||||
@@ -202,7 +256,7 @@ feature -- Factory
|
||||
-- set filename to a local file
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
l_ctx.set_upload_data ("This is a test for http client.%N")
|
||||
l_ctx.set_upload_data ("name=This is a test for http client.%N")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.put (requestbin_path, l_ctx, Void) as res and then
|
||||
@@ -232,10 +286,12 @@ feature -- Factory
|
||||
-- check requestbin to ensure the file and form parameters are correctly received
|
||||
-- set filename to a local file
|
||||
sess := new_session ("http://requestb.in")
|
||||
-- sess := new_session ("http://localhost:9090")
|
||||
create l_ctx.make
|
||||
l_ctx.set_upload_filename ("logo.jpg")
|
||||
l_ctx.form_parameters.extend ("First Value", "First Key")
|
||||
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
||||
-- l_ctx.set_upload_filename ("logo.jpg")
|
||||
l_ctx.set_upload_filename ("test.txt")
|
||||
l_ctx.add_form_parameter ("First", "Value")
|
||||
l_ctx.add_form_parameter ("Second", "and last value")
|
||||
create h.make_empty
|
||||
if
|
||||
attached sess.post (requestbin_path, l_ctx, Void) as res and then
|
||||
@@ -297,6 +353,7 @@ feature -- Factory
|
||||
sess := new_session ("http://google.com")
|
||||
create h.make_empty
|
||||
if attached sess.get ("/", Void) as res and then attached res.headers as hds then
|
||||
assert("was redirected", res.redirections_count > 0)
|
||||
across
|
||||
hds as c
|
||||
loop
|
||||
@@ -328,6 +385,53 @@ feature -- Factory
|
||||
end
|
||||
end
|
||||
|
||||
test_get_with_query_parameters
|
||||
local
|
||||
sess: HTTP_CLIENT_SESSION
|
||||
h: STRING_8
|
||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||
q: STRING
|
||||
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
|
||||
-- set filename to a local file
|
||||
sess := new_session ("http://requestb.in")
|
||||
create l_ctx.make
|
||||
l_ctx.add_query_parameter ("?", "?first&arg")
|
||||
l_ctx.add_query_parameter ("title", "Eiffel World!")
|
||||
l_ctx.add_query_parameter ("path", "foo/bar")
|
||||
l_ctx.add_query_parameter ("reserved", "+=&?")
|
||||
l_ctx.add_query_parameter ("unreserved", ":!@'()*")
|
||||
l_ctx.add_query_parameter ("unsafe", "%"[]{}")
|
||||
l_ctx.add_query_parameter ("test", "!$&'()*")
|
||||
l_ctx.add_query_parameter ("a&b", "a&b")
|
||||
l_ctx.add_query_parameter ("lst[a][b]", "[abc][123]")
|
||||
l_ctx.add_query_parameter ("foo(a,b)", "bar(1,2)*pi")
|
||||
create q.make_empty
|
||||
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
|
||||
attached sess.get (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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user