diff --git a/library/network/http_client/http_client-safe.ecf b/library/network/http_client/http_client-safe.ecf
index 7f249cee..652bd060 100644
--- a/library/network/http_client/http_client-safe.ecf
+++ b/library/network/http_client/http_client-safe.ecf
@@ -33,6 +33,7 @@
+
diff --git a/library/network/http_client/http_client.ecf b/library/network/http_client/http_client.ecf
index b4694aeb..704ee655 100644
--- a/library/network/http_client/http_client.ecf
+++ b/library/network/http_client/http_client.ecf
@@ -33,6 +33,7 @@
+
diff --git a/library/network/http_client/src/http_client_request.e b/library/network/http_client/src/http_client_request.e
index 6fcd7307..087484d9 100644
--- a/library/network/http_client/src/http_client_request.e
+++ b/library/network/http_client/src/http_client_request.e
@@ -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
diff --git a/library/network/http_client/src/http_client_request_context.e b/library/network/http_client/src/http_client_request_context.e
index fc91c0f5..d8e7fe60 100644
--- a/library/network/http_client/src/http_client_request_context.e
+++ b/library/network/http_client/src/http_client_request_context.e
@@ -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
diff --git a/library/network/http_client/src/http_client_session.e b/library/network/http_client/src/http_client_session.e
index f5342695..5272173e 100644
--- a/library/network/http_client/src/http_client_session.e
+++ b/library/network/http_client/src/http_client_session.e
@@ -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
diff --git a/library/network/http_client/src/implementation/uri_percent_encoder.e b/library/network/http_client/src/implementation/uri_percent_encoder.e
new file mode 100644
index 00000000..9e0d0396
--- /dev/null
+++ b/library/network/http_client/src/implementation/uri_percent_encoder.e
@@ -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 where 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
diff --git a/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e b/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e
index c46c8e9b..1ad66b1d 100644
--- a/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e
+++ b/library/network/http_client/src/spec/libcurl/libcurl_http_client_request.e
@@ -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
diff --git a/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e b/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e
index af8e4572..a17f2204 100644
--- a/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e
+++ b/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e
@@ -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
diff --git a/library/network/http_client/src/spec/net/net_http_client_request.e b/library/network/http_client/src/spec/net/net_http_client_request.e
index 8c9b1776..5808b031 100644
--- a/library/network/http_client/src/spec/net/net_http_client_request.e
+++ b/library/network/http_client/src/spec/net/net_http_client_request.e
@@ -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
diff --git a/library/network/http_client/src/spec/net/net_http_client_session.e b/library/network/http_client/src/spec/net/net_http_client_session.e
index e3269fc1..d414d66c 100644
--- a/library/network/http_client/src/spec/net/net_http_client_session.e
+++ b/library/network/http_client/src/spec/net/net_http_client_session.e
@@ -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
diff --git a/library/network/http_client/src/spec/null/null_http_client_session.e b/library/network/http_client/src/spec/null/null_http_client_session.e
index bf5b20ea..1ceeeab5 100644
--- a/library/network/http_client/src/spec/null/null_http_client_session.e
+++ b/library/network/http_client/src/spec/null/null_http_client_session.e
@@ -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
diff --git a/library/network/http_client/tests/test_libcurl_with_web.e b/library/network/http_client/tests/test_libcurl_with_web.e
index 52c6fc23..69ad2337 100644
--- a/library/network/http_client/tests/test_libcurl_with_web.e
+++ b/library/network/http_client/tests/test_libcurl_with_web.e
@@ -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
diff --git a/library/network/http_client/tests/test_net_with_web.e b/library/network/http_client/tests/test_net_with_web.e
index 94ca734b..6a1a112c 100644
--- a/library/network/http_client/tests/test_net_with_web.e
+++ b/library/network/http_client/tests/test_net_with_web.e
@@ -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
diff --git a/library/network/http_client/tests/test_with_web_i.e b/library/network/http_client/tests/test_with_web_i.e
index 3f3411a1..56a1604a 100644
--- a/library/network/http_client/tests/test_with_web_i.e
+++ b/library/network/http_client/tests/test_with_web_i.e
@@ -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
diff --git a/library/text/encoder/src/x_www_form_url_encoder.e b/library/text/encoder/src/x_www_form_url_encoder.e
new file mode 100644
index 00000000..1d91dfff
--- /dev/null
+++ b/library/text/encoder/src/x_www_form_url_encoder.e
@@ -0,0 +1,47 @@
+note
+ description: "Summary description for {X_WWW_FORM_URL_ENCODER}."
+ author: ""
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ X_WWW_FORM_URL_ENCODER
+
+inherit
+ URL_ENCODER
+ redefine
+ append_percent_encoded_ascii_character_code_to
+ end
+
+feature {NONE} -- Implementation: character encoding
+
+ append_percent_encoded_ascii_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL)
+ --
+ -- Note: space is encoded with '+' for x-www-form-urlencoding.
+ do
+ inspect a_code
+ when
+ 32 -- 32 ' '
+ then
+ a_result.append_code (43) -- 43 '+'
+ when
+ 33, 39, -- ! '
+ 40, 41, 42 -- ( ) *
+ then
+ a_result.append_code (a_code) -- Keep slash
+ else
+ Precursor (a_code, a_result)
+ end
+ end
+
+note
+ copyright: "Copyright (c) 2011-2017, 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