Added null http client for upcoming changes.
Refactored NET request implementation. - fixed potential issue with header conflict. - simplified, and extract parts of the code into routine. - Implemented read of chunked Transfer-Encoding - Fixed potential issue with socket handling. First steps to be able to exclude net or libcurl implementation when using http_client lib. Removed from NET implementation the hack related to PUT and upload data (it was used to workaround an issue with libcurl).
This commit is contained in:
@@ -10,12 +10,34 @@
|
|||||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||||
</option>
|
</option>
|
||||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf"/>
|
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL-safe.ecf">
|
||||||
|
<condition>
|
||||||
|
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</library>
|
||||||
|
|
||||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||||
<library name="http" location="..\protocol\http\http-safe.ecf"/>
|
<library name="http" location="..\protocol\http\http-safe.ecf"/>
|
||||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
|
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
|
||||||
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
|
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf">
|
||||||
|
<condition>
|
||||||
|
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</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\" recursive="true"/>
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="spec_null" location="$|spec/null" recursive="true"/>
|
||||||
|
<cluster name="spec_net" location="$|spec/socket" recursive="true">
|
||||||
|
<condition>
|
||||||
|
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="spec_libcurl" location="$|spec/libcurl" recursive="true">
|
||||||
|
<condition>
|
||||||
|
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
</cluster>
|
||||||
</target>
|
</target>
|
||||||
</system>
|
</system>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
|
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="http_client" uuid="628F5A96-021B-4191-926B-B3BF49272866" library_target="http_client">
|
||||||
<target name="http_client">
|
<target name="http_client">
|
||||||
<root all_classes="true"/>
|
<root all_classes="true"/>
|
||||||
<file_rule>
|
<file_rule>
|
||||||
@@ -9,13 +9,35 @@
|
|||||||
</file_rule>
|
</file_rule>
|
||||||
<option warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
<option warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
|
||||||
</option>
|
</option>
|
||||||
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
|
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||||
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
|
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf">
|
||||||
|
<condition>
|
||||||
|
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</library>
|
||||||
|
|
||||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||||
<library name="http" location="..\protocol\http\http.ecf"/>
|
<library name="http" location="..\protocol\http\http.ecf"/>
|
||||||
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
|
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
|
||||||
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
|
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf">
|
||||||
|
<condition>
|
||||||
|
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</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\" recursive="true"/>
|
<cluster name="src" location=".\src\">
|
||||||
|
<cluster name="spec_null" location="$|spec/null" recursive="true"/>
|
||||||
|
<cluster name="spec_net" location="$|spec/socket" recursive="true">
|
||||||
|
<condition>
|
||||||
|
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
<cluster name="spec_libcurl" location="$|spec/libcurl" recursive="true">
|
||||||
|
<condition>
|
||||||
|
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||||
|
</condition>
|
||||||
|
</cluster>
|
||||||
|
</cluster>
|
||||||
</target>
|
</target>
|
||||||
</system>
|
</system>
|
||||||
@@ -16,7 +16,6 @@ feature -- Status
|
|||||||
deferred
|
deferred
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
note
|
note
|
||||||
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ feature -- Access
|
|||||||
|
|
||||||
status_line: detachable READABLE_STRING_8
|
status_line: detachable READABLE_STRING_8
|
||||||
|
|
||||||
|
http_version: detachable READABLE_STRING_8
|
||||||
|
-- http version associated with `status_line'.
|
||||||
|
|
||||||
raw_header: READABLE_STRING_8
|
raw_header: READABLE_STRING_8
|
||||||
-- Raw http header of the response.
|
-- Raw http header of the response.
|
||||||
|
|
||||||
@@ -190,15 +193,54 @@ feature -- Access
|
|||||||
|
|
||||||
feature -- Change
|
feature -- Change
|
||||||
|
|
||||||
|
set_http_version (v: like http_version)
|
||||||
|
-- Set `http_version' to `v'.
|
||||||
|
do
|
||||||
|
http_version := v
|
||||||
|
end
|
||||||
|
|
||||||
set_status (s: INTEGER)
|
set_status (s: INTEGER)
|
||||||
-- Set response `status' code to `s'
|
-- Set response `status' code to `s'
|
||||||
do
|
do
|
||||||
status := s
|
status := s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
set_status_line (a_line: detachable READABLE_STRING_8)
|
||||||
|
-- Set status line to `a_line',
|
||||||
|
-- and also `status' extracted from `a_line' if possible.
|
||||||
|
local
|
||||||
|
i,j: INTEGER
|
||||||
|
s: READABLE_STRING_8
|
||||||
|
do
|
||||||
|
status_line := a_line
|
||||||
|
http_version := Void
|
||||||
|
|
||||||
|
if a_line /= Void then
|
||||||
|
if a_line.starts_with ("HTTP/") then
|
||||||
|
i := a_line.index_of (' ', 1)
|
||||||
|
if i > 0 then
|
||||||
|
http_version := a_line.substring (1 + 5, i - 1) -- ("HTTP/").count = 5
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
i := 1
|
||||||
|
end
|
||||||
|
-- Get status code token.
|
||||||
|
if i > 0 then
|
||||||
|
j := a_line.index_of (' ', i)
|
||||||
|
if j > i then
|
||||||
|
s := a_line.substring (i, j - 1)
|
||||||
|
if s.is_integer then
|
||||||
|
set_status (s.to_integer)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
set_response_message (a_source: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT)
|
set_response_message (a_source: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT)
|
||||||
-- Parse `a_source' response message
|
-- Parse `a_source' response message
|
||||||
-- and set `header' and `body'.
|
-- and set `status_line', `status', `header' and `body'.
|
||||||
--| ctx is the context associated with the request
|
--| ctx is the context associated with the request
|
||||||
--| it might be useful to deal with redirection customization...
|
--| it might be useful to deal with redirection customization...
|
||||||
local
|
local
|
||||||
@@ -234,7 +276,7 @@ feature -- Change
|
|||||||
|
|
||||||
j := i + 2
|
j := i + 2
|
||||||
pos := j
|
pos := j
|
||||||
status_line := l_status_line
|
set_status_line (l_status_line)
|
||||||
set_raw_header (h)
|
set_raw_header (h)
|
||||||
|
|
||||||
-- libcURL does not cache redirection content.
|
-- libcURL does not cache redirection content.
|
||||||
|
|||||||
@@ -218,9 +218,9 @@ feature -- Element change
|
|||||||
cookie := Void
|
cookie := Void
|
||||||
end
|
end
|
||||||
|
|
||||||
set_cookie (c: READABLE_STRING_8)
|
set_cookie (a_cookie: detachable READABLE_STRING_8)
|
||||||
do
|
do
|
||||||
cookie := c
|
cookie := a_cookie
|
||||||
end
|
end
|
||||||
|
|
||||||
set_timeout (n_seconds: like timeout)
|
set_timeout (n_seconds: like timeout)
|
||||||
@@ -253,12 +253,26 @@ feature -- Element change
|
|||||||
headers.prune (k)
|
headers.prune (k)
|
||||||
end
|
end
|
||||||
|
|
||||||
set_credentials (u: like username; p: like password)
|
set_credentials (u,p: detachable READABLE_STRING_GENERAL)
|
||||||
|
local
|
||||||
|
s: STRING_32
|
||||||
do
|
do
|
||||||
username := u
|
if u = Void then
|
||||||
password := p
|
username := Void
|
||||||
|
else
|
||||||
|
create {STRING_32} username.make_from_string_general (u)
|
||||||
|
end
|
||||||
|
if p = Void then
|
||||||
|
password := Void
|
||||||
|
else
|
||||||
|
create {STRING_32} password.make_from_string_general (p)
|
||||||
|
end
|
||||||
if u /= Void and p /= Void then
|
if u /= Void and p /= Void then
|
||||||
credentials := u + ":" + p
|
create s.make (u.count + 1 + p.count)
|
||||||
|
s.append_string_general (u)
|
||||||
|
s.append_character (':')
|
||||||
|
s.append_string_general (p)
|
||||||
|
credentials := s
|
||||||
else
|
else
|
||||||
credentials := Void
|
credentials := Void
|
||||||
end
|
end
|
||||||
|
|||||||
33
library/network/http_client/src/spec/null/null_http_client.e
Normal file
33
library/network/http_client/src/spec/null/null_http_client.e
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
note
|
||||||
|
description : "[
|
||||||
|
Instantiate one of the descendant of HTTP_CLIENT
|
||||||
|
then use `new_session' to create a session of http requests
|
||||||
|
]"
|
||||||
|
date : "$Date$"
|
||||||
|
revision : "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
NULL_HTTP_CLIENT
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT
|
||||||
|
|
||||||
|
feature -- Status
|
||||||
|
|
||||||
|
new_session (a_base_url: READABLE_STRING_8): NULL_HTTP_CLIENT_SESSION
|
||||||
|
-- Create a new session using `a_base_url'.
|
||||||
|
do
|
||||||
|
create Result.make (a_base_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
note
|
||||||
|
description : "[
|
||||||
|
HTTP_CLIENT_SESSION represents a session
|
||||||
|
and is used to call get, post, .... request
|
||||||
|
with predefined settings such as
|
||||||
|
base_url
|
||||||
|
specific common headers
|
||||||
|
timeout and so on ...
|
||||||
|
]"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
NULL_HTTP_CLIENT_SESSION
|
||||||
|
|
||||||
|
inherit
|
||||||
|
HTTP_CLIENT_SESSION
|
||||||
|
|
||||||
|
create
|
||||||
|
make
|
||||||
|
|
||||||
|
feature {NONE} -- Initialization
|
||||||
|
|
||||||
|
initialize
|
||||||
|
do
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Custom
|
||||||
|
|
||||||
|
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
create Result.make (base_url + a_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
custom_with_upload_data (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom (a_method, a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
custom_with_upload_file (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Helper
|
||||||
|
|
||||||
|
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := custom ("GET", a_path, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := custom ("HEAD", a_path, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("POST", a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
|
||||||
|
end
|
||||||
|
|
||||||
|
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
|
||||||
|
end
|
||||||
|
|
||||||
|
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := custom ("DELETE", a_path, ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
|
impl_custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
|
||||||
|
do
|
||||||
|
Result := custom (a_method, a_path, a_ctx)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Status report
|
||||||
|
|
||||||
|
is_available: BOOLEAN = False
|
||||||
|
-- Is interface usable?
|
||||||
|
|
||||||
|
note
|
||||||
|
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
|
||||||
|
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||||
|
source: "[
|
||||||
|
Eiffel Software
|
||||||
|
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||||
|
Telephone 805-685-1006, Fax 805-685-6869
|
||||||
|
Website http://www.eiffel.com
|
||||||
|
Customer support http://support.eiffel.com
|
||||||
|
]"
|
||||||
|
end
|
||||||
@@ -18,6 +18,8 @@ inherit
|
|||||||
|
|
||||||
REFACTORING_HELPER
|
REFACTORING_HELPER
|
||||||
|
|
||||||
|
SHARED_EXECUTION_ENVIRONMENT
|
||||||
|
|
||||||
create
|
create
|
||||||
make
|
make
|
||||||
|
|
||||||
@@ -33,19 +35,19 @@ feature -- Access
|
|||||||
local
|
local
|
||||||
redirection_response: detachable like response
|
redirection_response: detachable like response
|
||||||
l_uri: URI
|
l_uri: URI
|
||||||
|
l_header_key: READABLE_STRING_8
|
||||||
l_host: READABLE_STRING_8
|
l_host: READABLE_STRING_8
|
||||||
|
l_cookie: detachable READABLE_STRING_8
|
||||||
l_request_uri: STRING
|
l_request_uri: STRING
|
||||||
l_url: HTTP_URL
|
l_url: HTTP_URL
|
||||||
socket: NETWORK_STREAM_SOCKET
|
socket: NETWORK_STREAM_SOCKET
|
||||||
s: STRING
|
s: STRING
|
||||||
l_message: STRING
|
l_message: STRING
|
||||||
i,j: INTEGER
|
|
||||||
l_content_length: INTEGER
|
l_content_length: INTEGER
|
||||||
l_location: detachable READABLE_STRING_8
|
l_location: detachable READABLE_STRING_8
|
||||||
l_port: INTEGER
|
l_port: INTEGER
|
||||||
l_authorization: HTTP_AUTHORIZATION
|
l_authorization: HTTP_AUTHORIZATION
|
||||||
l_platform: STRING
|
l_platform: STRING
|
||||||
l_useragent: 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]
|
l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
|
||||||
ctx: like context
|
ctx: like context
|
||||||
@@ -58,7 +60,9 @@ feature -- Access
|
|||||||
l_prev_header: READABLE_STRING_8
|
l_prev_header: READABLE_STRING_8
|
||||||
l_boundary: READABLE_STRING_8
|
l_boundary: READABLE_STRING_8
|
||||||
l_is_http_1_0: BOOLEAN
|
l_is_http_1_0: BOOLEAN
|
||||||
|
retried: BOOLEAN
|
||||||
do
|
do
|
||||||
|
if not retried then
|
||||||
ctx := context
|
ctx := context
|
||||||
if ctx /= Void then
|
if ctx /= Void then
|
||||||
l_is_http_1_0 := attached ctx.http_version as l_http_version and then l_http_version.same_string ("HTTP/1.0")
|
l_is_http_1_0 := attached ctx.http_version as l_http_version and then l_http_version.same_string ("HTTP/1.0")
|
||||||
@@ -77,7 +81,6 @@ feature -- Access
|
|||||||
l_port := 80
|
l_port := 80
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if attached l_uri.host as h then
|
if attached l_uri.host as h then
|
||||||
l_host := h
|
l_host := h
|
||||||
else
|
else
|
||||||
@@ -86,9 +89,11 @@ feature -- Access
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- add headers for authorization
|
-- add headers for authorization
|
||||||
if attached l_uri.userinfo as l_userinfo then
|
if not headers.has ("Authorization") then
|
||||||
if attached l_uri.username as u_name then
|
if
|
||||||
if attached l_uri.password as u_pass then
|
attached username as u_name and
|
||||||
|
attached password as u_pass
|
||||||
|
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.extend (auth, "Authorization")
|
||||||
@@ -96,7 +101,6 @@ feature -- Access
|
|||||||
check headers.has_key ("Authorization") end
|
check headers.has_key ("Authorization") end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
create l_request_uri.make_from_string (l_uri.path)
|
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 then
|
||||||
@@ -104,24 +108,26 @@ feature -- Access
|
|||||||
l_request_uri.append (l_query)
|
l_request_uri.append (l_query)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add headers for user agent
|
-- add computed header User-Agent if not yet set.
|
||||||
|
if not headers.has ("User-Agent") then
|
||||||
if {PLATFORM}.is_unix then
|
if {PLATFORM}.is_unix then
|
||||||
l_platform := "Unix"
|
l_platform := "Unix"
|
||||||
else
|
elseif {PLATFORM}.is_windows then
|
||||||
if {PLATFORM}.is_mac then
|
|
||||||
l_platform := "Mac"
|
|
||||||
else
|
|
||||||
l_platform := "Windows"
|
l_platform := "Windows"
|
||||||
|
elseif {PLATFORM}.is_mac then
|
||||||
|
l_platform := "Mac"
|
||||||
|
elseif {PLATFORM}.is_vms then
|
||||||
|
l_platform := "VMS"
|
||||||
|
elseif {PLATFORM}.is_vxworks then
|
||||||
|
l_platform := "VxWorks"
|
||||||
|
else
|
||||||
|
l_platform := "Unknown"
|
||||||
end
|
end
|
||||||
|
headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
|
||||||
end
|
end
|
||||||
if l_platform /= Void then
|
|
||||||
l_useragent := "eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")"
|
|
||||||
headers.extend (l_useragent, "User-Agent")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- handle sending data
|
-- handle sending data
|
||||||
if attached ctx then
|
if ctx /= Void then
|
||||||
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
|
||||||
@@ -181,10 +187,10 @@ feature -- Access
|
|||||||
l_form_string.append (http_end_of_header_line)
|
l_form_string.append (http_end_of_header_line)
|
||||||
|
|
||||||
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_access_readable then
|
||||||
l_upload_file.open_read
|
append_file_content_to (l_upload_file, l_upload_file.count, l_form_string)
|
||||||
l_upload_file.read_stream (l_upload_file.count)
|
-- Reset l_upload_file to Void, since the related content is already processed.
|
||||||
l_form_string.append (l_upload_file.last_string)
|
l_upload_file := Void
|
||||||
end
|
end
|
||||||
l_form_string.append (http_end_of_header_line)
|
l_form_string.append (http_end_of_header_line)
|
||||||
end
|
end
|
||||||
@@ -194,53 +200,36 @@ feature -- Access
|
|||||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
elseif
|
||||||
if request_method.is_case_insensitive_equal ("POST") then
|
request_method.is_case_insensitive_equal ("POST")
|
||||||
if ctx /= Void then
|
or else request_method.is_case_insensitive_equal ("PUT")
|
||||||
if ctx.has_upload_data then
|
then
|
||||||
l_upload_data := ctx.upload_data
|
|
||||||
if l_upload_data /= Void then
|
if l_upload_data /= Void then
|
||||||
|
check ctx.has_upload_data end
|
||||||
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
|
||||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||||
end
|
elseif l_upload_filename /= Void then
|
||||||
elseif ctx.has_upload_filename then
|
check ctx.has_upload_filename end
|
||||||
if l_upload_filename /= Void then
|
|
||||||
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.readable then
|
||||||
headers.extend (l_upload_file.count.out, "Content-Length")
|
headers.extend (l_upload_file.count.out, "Content-Length")
|
||||||
l_upload_file.open_read
|
|
||||||
end
|
end
|
||||||
check l_upload_file /= Void end
|
check l_upload_file /= Void end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle put requests
|
|
||||||
if request_method.is_case_insensitive_equal ("PUT") then
|
|
||||||
if ctx /= Void then
|
|
||||||
if ctx.has_upload_filename then
|
|
||||||
if l_upload_filename /= Void then
|
|
||||||
create l_upload_file.make_with_name (l_upload_filename)
|
|
||||||
if l_upload_file.exists and then l_upload_file.is_readable then
|
|
||||||
headers.extend (l_upload_file.count.out, "Content-Length")
|
|
||||||
l_upload_file.open_read
|
|
||||||
end
|
|
||||||
check l_upload_filename /= Void end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Connect
|
-- Connect
|
||||||
create socket.make_client_by_port (l_port, l_host)
|
create socket.make_client_by_port (l_port, l_host)
|
||||||
socket.set_timeout (timeout)
|
|
||||||
socket.set_connect_timeout (connect_timeout)
|
socket.set_connect_timeout (connect_timeout)
|
||||||
|
socket.set_timeout (timeout)
|
||||||
socket.connect
|
socket.connect
|
||||||
if socket.is_connected then
|
if socket.is_connected then
|
||||||
|
-- FIXME: check usage of headers and specific header variable.
|
||||||
|
--| only one Cookie: is allowed, so merge multiple into one;
|
||||||
|
--| if Host is in header, use that one.
|
||||||
|
-- Compute Request line.
|
||||||
create s.make_from_string (request_method.as_upper)
|
create s.make_from_string (request_method.as_upper)
|
||||||
s.append_character (' ')
|
s.append_character (' ')
|
||||||
s.append (l_request_uri)
|
s.append (l_request_uri)
|
||||||
@@ -252,49 +241,74 @@ feature -- Access
|
|||||||
end
|
end
|
||||||
s.append (Http_end_of_header_line)
|
s.append (Http_end_of_header_line)
|
||||||
|
|
||||||
|
-- Compute Header Host:
|
||||||
s.append (Http_host_header)
|
s.append (Http_host_header)
|
||||||
s.append (": ")
|
s.append (": ")
|
||||||
|
if attached headers [Http_host_header] as h_host then
|
||||||
|
s.append (h_host)
|
||||||
|
else
|
||||||
s.append (l_host)
|
s.append (l_host)
|
||||||
s.append (http_end_of_header_line)
|
|
||||||
if attached session.cookie as cookie then
|
|
||||||
s.append ("Cookie: " + cookie + http_end_of_header_line)
|
|
||||||
end
|
end
|
||||||
|
s.append (http_end_of_header_line)
|
||||||
|
|
||||||
|
-- Append the given request headers
|
||||||
|
l_cookie := Void
|
||||||
if headers.is_empty then
|
if headers.is_empty then
|
||||||
s.append (Http_end_of_command)
|
s.append (Http_end_of_command)
|
||||||
else
|
else
|
||||||
across
|
across
|
||||||
headers as ic
|
headers as ic
|
||||||
loop
|
loop
|
||||||
|
l_header_key := ic.key
|
||||||
|
if l_header_key.same_string_general ("Host") then
|
||||||
|
-- FIXME: already handled elsewhere!
|
||||||
|
elseif l_header_key.same_string_general ("Cookie") then
|
||||||
|
-- FIXME: need cookie merging.
|
||||||
|
l_cookie := ic.item
|
||||||
|
else
|
||||||
s.append (ic.key)
|
s.append (ic.key)
|
||||||
s.append (": ")
|
s.append (": ")
|
||||||
s.append (ic.item)
|
s.append (ic.item)
|
||||||
s.append (Http_end_of_header_line)
|
s.append (Http_end_of_header_line)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
s.append (Http_end_of_header_line)
|
s.append (Http_end_of_header_line)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Compute Header Cookie: if needed
|
||||||
|
-- Get session cookie
|
||||||
|
if l_cookie = Void then
|
||||||
|
l_cookie := session.cookie
|
||||||
|
else
|
||||||
|
-- Overwrite potential session cookie, if specified by the user.
|
||||||
|
end
|
||||||
|
if l_cookie /= Void then
|
||||||
|
s.append ("Cookie: ")
|
||||||
|
s.append (l_cookie)
|
||||||
|
s.append (http_end_of_header_line)
|
||||||
|
end
|
||||||
|
|
||||||
if l_upload_data /= Void then
|
if l_upload_data /= Void then
|
||||||
s.append (l_upload_data)
|
s.append (l_upload_data)
|
||||||
s.append (http_end_of_header_line)
|
s.append (http_end_of_header_line)
|
||||||
end
|
end
|
||||||
if attached l_upload_file then
|
--| Note that any remaining file to upload will be done directly via the socket
|
||||||
if not l_upload_file.after then
|
--| to optimize memory usage
|
||||||
from
|
|
||||||
until
|
|
||||||
l_upload_file.after
|
|
||||||
loop
|
|
||||||
l_upload_file.read_line_thread_aware
|
|
||||||
s.append (l_upload_file.last_string)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
--| Send request
|
||||||
|
if socket.ready_for_writing then
|
||||||
socket.put_string (s)
|
socket.put_string (s)
|
||||||
|
--| Send remaining payload data, if needed.
|
||||||
|
if l_upload_file /= Void then
|
||||||
|
-- i.e: not yet processed
|
||||||
|
append_file_content_to_socket (l_upload_file, l_upload_file.count, socket)
|
||||||
|
end
|
||||||
|
|
||||||
--| Get response.
|
--| Get response.
|
||||||
--| Get header message
|
--| Get header message
|
||||||
|
if socket.ready_for_reading then
|
||||||
create l_message.make_empty
|
create l_message.make_empty
|
||||||
read_header_from_socket (socket, l_message)
|
append_socket_header_content_to (socket, l_message)
|
||||||
l_prev_header := Result.raw_header
|
l_prev_header := Result.raw_header
|
||||||
Result.set_raw_header (l_message.string)
|
Result.set_raw_header (l_message.string)
|
||||||
l_content_length := -1
|
l_content_length := -1
|
||||||
@@ -312,29 +326,9 @@ feature -- Access
|
|||||||
-- Restore previous header
|
-- Restore previous header
|
||||||
Result.set_raw_header (l_prev_header)
|
Result.set_raw_header (l_prev_header)
|
||||||
-- Set message
|
-- Set message
|
||||||
Result.set_response_message (l_message, context)
|
Result.set_response_message (l_message, ctx)
|
||||||
|
-- Check status code.
|
||||||
-- Get status code.
|
check status_coherent: attached Result.status_line as l_status_line implies l_status_line.has_substring (Result.status.out) end
|
||||||
if attached Result.status_line as l_status_line then
|
|
||||||
if l_status_line.starts_with ("HTTP/") then
|
|
||||||
i := l_status_line.index_of (' ', 1)
|
|
||||||
if i > 0 then
|
|
||||||
i := i + 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
i := 1
|
|
||||||
end
|
|
||||||
-- Get status code token.
|
|
||||||
if i > 0 then
|
|
||||||
j := l_status_line.index_of (' ', i)
|
|
||||||
if j > i then
|
|
||||||
s := l_status_line.substring (i, j - 1)
|
|
||||||
if s.is_integer then
|
|
||||||
Result.set_status (s.to_integer)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- follow redirect
|
-- follow redirect
|
||||||
if l_location /= Void then
|
if l_location /= Void then
|
||||||
@@ -344,28 +338,110 @@ feature -- Access
|
|||||||
redirection_response := response
|
redirection_response := response
|
||||||
redirection_response.add_redirection (Result.status_line, Result.raw_header, Result.body)
|
redirection_response.add_redirection (Result.status_line, Result.raw_header, Result.body)
|
||||||
Result := redirection_response
|
Result := redirection_response
|
||||||
-- Result.add_redirection (redirection_response.status_line, redirection_response.raw_header, redirection_response.body)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
current_redirects := 0
|
current_redirects := current_redirects - 1
|
||||||
|
else
|
||||||
|
Result.set_error_message ("Read Timeout")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result.set_error_message ("Write Timeout")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
Result.set_error_message ("Could not connect")
|
Result.set_error_message ("Could not connect")
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
create Result.make (url)
|
||||||
|
Result.set_error_message ("Error: internal error")
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
retried := True
|
||||||
|
retry
|
||||||
end
|
end
|
||||||
|
|
||||||
feature {NONE} -- Helpers
|
feature {NONE} -- Helpers
|
||||||
|
|
||||||
read_header_from_socket (a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
|
append_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: NETWORK_STREAM_SOCKET)
|
||||||
|
-- Append `a_file' content to `a_output'.
|
||||||
|
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||||
|
require
|
||||||
|
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||||
|
local
|
||||||
|
l_was_open: BOOLEAN
|
||||||
|
l_count: INTEGER
|
||||||
|
do
|
||||||
|
if a_len >= 0 then
|
||||||
|
l_count := a_len
|
||||||
|
else
|
||||||
|
l_count := a_file.count
|
||||||
|
end
|
||||||
|
if l_count > 0 then
|
||||||
|
l_was_open := a_file.is_open_read
|
||||||
|
if a_file.is_open_read then
|
||||||
|
l_was_open := True
|
||||||
|
else
|
||||||
|
a_file.open_read
|
||||||
|
end
|
||||||
|
from
|
||||||
|
until
|
||||||
|
l_count = 0 or a_file.exhausted
|
||||||
|
loop
|
||||||
|
a_file.read_stream_thread_aware (l_count.min (2_048))
|
||||||
|
a_output.put_string (a_file.last_string)
|
||||||
|
l_count := l_count - a_file.bytes_read
|
||||||
|
end
|
||||||
|
if not l_was_open then
|
||||||
|
a_file.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append_file_content_to (a_file: FILE; a_len: INTEGER; a_output: STRING)
|
||||||
|
-- Append `a_file' content to `a_output'.
|
||||||
|
-- If `a_len' >= 0 then read only `a_len' characters.
|
||||||
|
require
|
||||||
|
a_file_readable: a_file.exists and then a_file.is_access_readable
|
||||||
|
local
|
||||||
|
l_was_open: BOOLEAN
|
||||||
|
l_count: INTEGER
|
||||||
|
do
|
||||||
|
if a_len >= 0 then
|
||||||
|
l_count := a_len
|
||||||
|
else
|
||||||
|
l_count := a_file.count
|
||||||
|
end
|
||||||
|
if l_count > 0 then
|
||||||
|
l_was_open := a_file.is_open_read
|
||||||
|
if a_file.is_open_read then
|
||||||
|
l_was_open := True
|
||||||
|
else
|
||||||
|
a_file.open_read
|
||||||
|
end
|
||||||
|
from
|
||||||
|
|
||||||
|
until
|
||||||
|
l_count = 0 or a_file.exhausted
|
||||||
|
loop
|
||||||
|
a_file.read_stream_thread_aware (l_count.min (2_048))
|
||||||
|
a_output.append (a_file.last_string)
|
||||||
|
l_count := l_count - a_file.bytes_read
|
||||||
|
end
|
||||||
|
if not l_was_open then
|
||||||
|
a_file.close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
append_socket_header_content_to (a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
|
||||||
-- Get header from `a_socket' into `a_output'.
|
-- Get header from `a_socket' into `a_output'.
|
||||||
local
|
local
|
||||||
s: READABLE_STRING_8
|
s: READABLE_STRING_8
|
||||||
do
|
do
|
||||||
if a_socket.is_readable then
|
|
||||||
from
|
from
|
||||||
s := ""
|
s := ""
|
||||||
until
|
until
|
||||||
s.same_string ("%R") or not a_socket.is_readable
|
s.same_string ("%R") or not a_socket.readable
|
||||||
loop
|
loop
|
||||||
a_socket.read_line_thread_aware
|
a_socket.read_line_thread_aware
|
||||||
s := a_socket.last_string
|
s := a_socket.last_string
|
||||||
@@ -378,7 +454,6 @@ feature {NONE} -- Helpers
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_len: INTEGER; a_buffer: STRING)
|
append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_len: INTEGER; a_buffer: STRING)
|
||||||
-- Get content from `a_socket' and append it to `a_buffer'.
|
-- Get content from `a_socket' and append it to `a_buffer'.
|
||||||
@@ -386,22 +461,32 @@ feature {NONE} -- Helpers
|
|||||||
-- this is probably HTTP/1.0 without any Content-Length.
|
-- this is probably HTTP/1.0 without any Content-Length.
|
||||||
local
|
local
|
||||||
s: STRING_8
|
s: STRING_8
|
||||||
n,pos: INTEGER
|
n,pos, l_chunk_size, l_count: INTEGER
|
||||||
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
|
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
|
||||||
do
|
do
|
||||||
if a_socket.is_readable then
|
if a_socket.readable then
|
||||||
if a_len >= 0 then
|
if a_len >= 0 then
|
||||||
a_socket.read_stream_thread_aware (a_len)
|
from
|
||||||
s := a_socket.last_string
|
n := a_len
|
||||||
check full_content_read: a_socket.bytes_read = a_len end
|
until
|
||||||
a_buffer.append (s)
|
n = 0 or else not a_socket.readable or else a_response.error_occurred
|
||||||
|
loop
|
||||||
|
if a_socket.ready_for_reading then
|
||||||
|
a_socket.read_stream_thread_aware (n)
|
||||||
|
l_count := l_count + a_socket.bytes_read
|
||||||
|
n := n - a_socket.bytes_read
|
||||||
|
a_buffer.append (a_socket.last_string)
|
||||||
else
|
else
|
||||||
if attached a_response.header ("Transfer-Encoding") as l_enc and then l_enc.is_case_insensitive_equal ("chunked") then
|
a_response.set_error_message ("Could not read chunked data, timeout")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
check full_content_read: l_count = a_len end
|
||||||
|
elseif attached a_response.header ("Transfer-Encoding") as l_enc and then l_enc.is_case_insensitive_equal ("chunked") then
|
||||||
from
|
from
|
||||||
create hexa2int.make
|
create hexa2int.make
|
||||||
n := 1
|
n := 1
|
||||||
until
|
until
|
||||||
n = 0 or not a_socket.is_readable
|
n = 0 or not a_socket.readable
|
||||||
loop
|
loop
|
||||||
a_socket.read_line_thread_aware -- Read chunk info
|
a_socket.read_line_thread_aware -- Read chunk info
|
||||||
s := a_socket.last_string
|
s := a_socket.last_string
|
||||||
@@ -421,26 +506,49 @@ feature {NONE} -- Helpers
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if n > 0 then
|
if n > 0 then
|
||||||
|
from
|
||||||
|
until
|
||||||
|
n = 0 or else not a_socket.readable or else a_response.error_occurred
|
||||||
|
loop
|
||||||
|
if a_socket.ready_for_reading then
|
||||||
a_socket.read_stream_thread_aware (n)
|
a_socket.read_stream_thread_aware (n)
|
||||||
check a_socket.bytes_read = n end
|
l_count := l_count + a_socket.bytes_read
|
||||||
|
n := n - a_socket.bytes_read
|
||||||
a_buffer.append (a_socket.last_string)
|
a_buffer.append (a_socket.last_string)
|
||||||
|
else
|
||||||
|
a_response.set_error_message ("Could not read chunked data, timeout")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if a_socket.ready_for_reading then
|
||||||
a_socket.read_character
|
a_socket.read_character
|
||||||
check a_socket.last_character = '%R' end
|
check a_socket.last_character = '%R' end
|
||||||
a_socket.read_character
|
a_socket.read_character
|
||||||
check a_socket.last_character = '%N' end
|
check a_socket.last_character = '%N' end
|
||||||
|
else
|
||||||
|
a_response.set_error_message ("Could not read chunked data, timeout")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- HTTP/1.0
|
-- No Content-Length and no chunked transfer encoding!
|
||||||
|
-- maybe HTTP/1.0 ?
|
||||||
|
-- FIXME: check solution!
|
||||||
from
|
from
|
||||||
n := 1_024
|
l_count := 0
|
||||||
|
l_chunk_size := 1_024
|
||||||
|
n := l_chunk_size --| value to satisfy until condition on first loop.
|
||||||
until
|
until
|
||||||
n < 1_024 or not a_socket.is_readable
|
n < l_chunk_size or not a_socket.readable
|
||||||
loop
|
loop
|
||||||
a_socket.read_stream_thread_aware (1_024)
|
if a_socket.ready_for_reading then
|
||||||
|
a_socket.read_stream_thread_aware (l_chunk_size)
|
||||||
s := a_socket.last_string
|
s := a_socket.last_string
|
||||||
n := a_socket.bytes_read
|
n := a_socket.bytes_read
|
||||||
|
l_count := l_count + n
|
||||||
a_buffer.append (s)
|
a_buffer.append (s)
|
||||||
|
else
|
||||||
|
a_response.set_error_message ("Could not read data, timeout")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ feature {NONE} -- Implementation
|
|||||||
local
|
local
|
||||||
req: HTTP_CLIENT_REQUEST
|
req: HTTP_CLIENT_REQUEST
|
||||||
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
f: detachable RAW_FILE
|
|
||||||
l_data: detachable READABLE_STRING_8
|
l_data: detachable READABLE_STRING_8
|
||||||
do
|
do
|
||||||
ctx := a_ctx
|
ctx := a_ctx
|
||||||
@@ -117,34 +116,12 @@ feature {NONE} -- Implementation
|
|||||||
if ctx /= Void then
|
if ctx /= Void then
|
||||||
l_data := ctx.upload_data
|
l_data := ctx.upload_data
|
||||||
if l_data /= Void and a_method.is_case_insensitive_equal_general ("PUT") then
|
if l_data /= Void and a_method.is_case_insensitive_equal_general ("PUT") then
|
||||||
--| Quick and dirty hack using real file, for PUT uploaded data
|
check put_conflict_file_and_data: not ctx.has_upload_filename end
|
||||||
--| FIXME [2012-05-23]: better use libcurl for that purpose
|
|
||||||
|
|
||||||
if ctx.has_upload_filename then
|
|
||||||
check put_conflict_file_and_data: False end
|
|
||||||
end
|
|
||||||
create f.make_open_write (create {FILE_NAME}.make_temporary_name)
|
|
||||||
f.put_string (l_data)
|
|
||||||
f.close
|
|
||||||
check ctx /= Void then
|
|
||||||
ctx.set_upload_data (Void)
|
|
||||||
ctx.set_upload_filename (f.path.name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
|
||||||
Result := req.response
|
Result := req.response
|
||||||
|
|
||||||
-- FIXME file should be deleted, but uncommenting the following leads to a PERMISSION DENIED exception..
|
|
||||||
--if f /= Void then
|
|
||||||
-- f.delete
|
|
||||||
--end
|
|
||||||
|
|
||||||
if l_data /= Void and a_ctx /= Void then
|
|
||||||
a_ctx.set_upload_filename (Void)
|
|
||||||
a_ctx.set_upload_data (l_data)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
note
|
note
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
note
|
|
||||||
description : "httptest application root class"
|
|
||||||
date : "$Date$"
|
|
||||||
revision : "$Revision$"
|
|
||||||
|
|
||||||
class
|
|
||||||
APPLICATION
|
|
||||||
|
|
||||||
inherit
|
|
||||||
ARGUMENTS
|
|
||||||
|
|
||||||
create
|
|
||||||
make
|
|
||||||
|
|
||||||
feature {NONE} -- Initialization
|
|
||||||
|
|
||||||
make
|
|
||||||
-- Run application.
|
|
||||||
do
|
|
||||||
requestbin_path := "/15u47xi2"
|
|
||||||
test_1
|
|
||||||
test_2
|
|
||||||
test_3
|
|
||||||
test_4
|
|
||||||
test_5
|
|
||||||
test_6
|
|
||||||
test_7
|
|
||||||
end
|
|
||||||
|
|
||||||
requestbin_path: STRING
|
|
||||||
|
|
||||||
feature -- Tests
|
|
||||||
|
|
||||||
test_1
|
|
||||||
local
|
|
||||||
sess: NET_HTTP_CLIENT_SESSION
|
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
|
||||||
do
|
|
||||||
-- URL ENCODED POST REQUEST
|
|
||||||
-- check requestbin to ensure the "Hello World" has been received in the raw body
|
|
||||||
-- also check that User-Agent was sent
|
|
||||||
create h.make_empty
|
|
||||||
create sess.make("http://requestb.in")
|
|
||||||
if attached sess.post (requestbin_path, Void, "Hello World").headers as hds then
|
|
||||||
across
|
|
||||||
hds as c
|
|
||||||
loop
|
|
||||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print (h)
|
|
||||||
end
|
|
||||||
|
|
||||||
test_2
|
|
||||||
local
|
|
||||||
sess: NET_HTTP_CLIENT_SESSION
|
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
|
||||||
do
|
|
||||||
-- POST REQUEST WITH FORM DATA
|
|
||||||
-- check requestbin to ensure the form parameters are correctly received
|
|
||||||
create sess.make("http://requestb.in")
|
|
||||||
create l_ctx.make
|
|
||||||
l_ctx.form_parameters.extend ("First Value", "First Key")
|
|
||||||
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
|
||||||
create sess.make("http://requestb.in")
|
|
||||||
create h.make_empty
|
|
||||||
if attached sess.post (requestbin_path, l_ctx, "").headers as hds then
|
|
||||||
across
|
|
||||||
hds as c
|
|
||||||
loop
|
|
||||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print (h)
|
|
||||||
end
|
|
||||||
|
|
||||||
test_3
|
|
||||||
local
|
|
||||||
sess: NET_HTTP_CLIENT_SESSION
|
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
|
||||||
do
|
|
||||||
-- POST REQUEST WITH A FILE
|
|
||||||
-- check requestbin to ensure the form parameters are correctly received
|
|
||||||
-- set filename to a local file
|
|
||||||
create sess.make("http://requestb.in")
|
|
||||||
create l_ctx.make
|
|
||||||
l_ctx.set_upload_filename ("C:\temp\test.txt")
|
|
||||||
create h.make_empty
|
|
||||||
if attached sess.post (requestbin_path, l_ctx, "").headers as hds then
|
|
||||||
across
|
|
||||||
hds as c
|
|
||||||
loop
|
|
||||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print (h)
|
|
||||||
end
|
|
||||||
|
|
||||||
test_4
|
|
||||||
local
|
|
||||||
sess: NET_HTTP_CLIENT_SESSION
|
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
|
||||||
do
|
|
||||||
-- PUT REQUEST WITH A FILE
|
|
||||||
-- check requestbin to ensure the file is correctly received
|
|
||||||
-- set filename to a local file
|
|
||||||
create sess.make("http://requestb.in")
|
|
||||||
create l_ctx.make
|
|
||||||
l_ctx.set_upload_filename ("C:\temp\test.txt")
|
|
||||||
create h.make_empty
|
|
||||||
if attached sess.put (requestbin_path, l_ctx, "").headers as hds then
|
|
||||||
across
|
|
||||||
hds as c
|
|
||||||
loop
|
|
||||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print (h)
|
|
||||||
end
|
|
||||||
|
|
||||||
test_5
|
|
||||||
local
|
|
||||||
sess: NET_HTTP_CLIENT_SESSION
|
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
|
||||||
do
|
|
||||||
-- 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
|
|
||||||
create sess.make("http://requestb.in")
|
|
||||||
create l_ctx.make
|
|
||||||
l_ctx.set_upload_filename ("C:\temp\logo.png")
|
|
||||||
l_ctx.form_parameters.extend ("First Value", "First Key")
|
|
||||||
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
|
||||||
create h.make_empty
|
|
||||||
if attached sess.post (requestbin_path, l_ctx, "").headers as hds then
|
|
||||||
across
|
|
||||||
hds as c
|
|
||||||
loop
|
|
||||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print (h)
|
|
||||||
end
|
|
||||||
|
|
||||||
test_6
|
|
||||||
local
|
|
||||||
sess: NET_HTTP_CLIENT_SESSION
|
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
|
||||||
do
|
|
||||||
-- GET REQUEST, Forwarding (google's first answer is a forward)
|
|
||||||
-- check headers received (printed in console)
|
|
||||||
create sess.make("http://google.com")
|
|
||||||
create h.make_empty
|
|
||||||
if attached sess.get ("/", Void).headers as hds then
|
|
||||||
across
|
|
||||||
hds as c
|
|
||||||
loop
|
|
||||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print (h)
|
|
||||||
end
|
|
||||||
|
|
||||||
test_7
|
|
||||||
local
|
|
||||||
sess: NET_HTTP_CLIENT_SESSION
|
|
||||||
h: STRING_8
|
|
||||||
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
|
||||||
do
|
|
||||||
-- GET REQUEST WITH AUTHENTICATION, see http://browserspy.dk/password.php
|
|
||||||
-- check header WWW-Authenticate is received (authentication successful)
|
|
||||||
create sess.make("http://test:test@browserspy.dk")
|
|
||||||
create h.make_empty
|
|
||||||
if attached sess.get ("/password-ok.php", Void).headers as hds then
|
|
||||||
across
|
|
||||||
hds as c
|
|
||||||
loop
|
|
||||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print (h)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
||||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-14-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-14-0 http://www.eiffel.com/developers/xml/configuration-1-14-0.xsd" name="httptest" uuid="C8DFC1BA-78CA-4518-B0F2-2241E42808FF">
|
|
||||||
<target name="httptest">
|
|
||||||
<root class="APPLICATION" feature="make"/>
|
|
||||||
<option warning="true" void_safety="all">
|
|
||||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
|
||||||
</option>
|
|
||||||
<setting name="console_application" value="true"/>
|
|
||||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
|
||||||
<library name="http_client" location="..\http_client-safe.ecf"/>
|
|
||||||
<cluster name="httptest" location=".\" recursive="true">
|
|
||||||
<file_rule>
|
|
||||||
<exclude>/EIFGENs$</exclude>
|
|
||||||
<exclude>/CVS$</exclude>
|
|
||||||
<exclude>/.svn$</exclude>
|
|
||||||
</file_rule>
|
|
||||||
</cluster>
|
|
||||||
</target>
|
|
||||||
</system>
|
|
||||||
BIN
library/network/http_client/tests/logo.jpg
Normal file
BIN
library/network/http_client/tests/logo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
@@ -6,7 +6,13 @@ create
|
|||||||
feature -- Init
|
feature -- Init
|
||||||
|
|
||||||
make
|
make
|
||||||
|
local
|
||||||
|
null: NULL_HTTP_CLIENT
|
||||||
do
|
do
|
||||||
|
create null
|
||||||
|
if attached null.new_session ("http://example.com/") as l_sess then
|
||||||
|
check not l_sess.is_available end
|
||||||
|
end
|
||||||
test_http_client
|
test_http_client
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -42,7 +48,7 @@ feature -- Init
|
|||||||
do
|
do
|
||||||
if not b then
|
if not b then
|
||||||
create e
|
create e
|
||||||
e.set_message (m)
|
e.set_description (m)
|
||||||
e.raise
|
e.raise
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
1
library/network/http_client/tests/test.txt
Normal file
1
library/network/http_client/tests/test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
This is a text sample for testing HTTP Client library.
|
||||||
@@ -44,8 +44,6 @@ feature -- Test routines
|
|||||||
assert ("missing body", False)
|
assert ("missing body", False)
|
||||||
end
|
end
|
||||||
assert ("same headers", h.same_string (res.raw_header))
|
assert ("same headers", h.same_string (res.raw_header))
|
||||||
else
|
|
||||||
assert ("Not found", False)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -54,7 +52,6 @@ feature -- Test routines
|
|||||||
sess: like new_session
|
sess: like new_session
|
||||||
h: STRING_8
|
h: STRING_8
|
||||||
do
|
do
|
||||||
--| Add your code here
|
|
||||||
sess := new_session ("http://requestb.in")
|
sess := new_session ("http://requestb.in")
|
||||||
create h.make_empty
|
create h.make_empty
|
||||||
if attached sess.get ("/1a0q2h61", Void).headers as hds then
|
if attached sess.get ("/1a0q2h61", Void).headers as hds then
|
||||||
@@ -64,7 +61,6 @@ feature -- Test routines
|
|||||||
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
print (h)
|
print (h)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
67
library/network/http_client/tests/test_libcurl_with_web.e
Normal file
67
library/network/http_client/tests/test_libcurl_with_web.e
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Objects that ...
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
TEST_LIBCURL_WITH_WEB
|
||||||
|
|
||||||
|
inherit
|
||||||
|
TEST_WITH_WEB_I
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
local
|
||||||
|
cl: LIBCURL_HTTP_CLIENT
|
||||||
|
do
|
||||||
|
create cl
|
||||||
|
Result := cl.new_session (a_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Tests
|
||||||
|
|
||||||
|
libcurl_test_post_url_encoded
|
||||||
|
do
|
||||||
|
test_post_url_encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_post_with_form_data
|
||||||
|
do
|
||||||
|
test_post_with_form_data
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_post_with_file
|
||||||
|
do
|
||||||
|
test_post_with_file
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_put_with_file
|
||||||
|
do
|
||||||
|
test_put_with_file
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_put_with_data
|
||||||
|
do
|
||||||
|
test_put_with_data
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_post_with_file_and_form_data
|
||||||
|
do
|
||||||
|
test_post_with_file_and_form_data
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_get_with_redirection
|
||||||
|
do
|
||||||
|
test_get_with_redirection
|
||||||
|
end
|
||||||
|
|
||||||
|
libcurl_test_get_with_authentication
|
||||||
|
do
|
||||||
|
test_get_with_authentication
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
67
library/network/http_client/tests/test_net_with_web.e
Normal file
67
library/network/http_client/tests/test_net_with_web.e
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Objects that ...
|
||||||
|
]"
|
||||||
|
author: "$Author$"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
|
||||||
|
class
|
||||||
|
TEST_NET_WITH_WEB
|
||||||
|
|
||||||
|
inherit
|
||||||
|
TEST_WITH_WEB_I
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
local
|
||||||
|
cl: NET_HTTP_CLIENT
|
||||||
|
do
|
||||||
|
create cl
|
||||||
|
Result := cl.new_session (a_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Tests
|
||||||
|
|
||||||
|
net_test_post_url_encoded
|
||||||
|
do
|
||||||
|
test_post_url_encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_post_with_form_data
|
||||||
|
do
|
||||||
|
test_post_with_form_data
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_post_with_file
|
||||||
|
do
|
||||||
|
test_post_with_file
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_put_with_file
|
||||||
|
do
|
||||||
|
test_put_with_file
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_put_with_data
|
||||||
|
do
|
||||||
|
test_put_with_data
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_post_with_file_and_form_data
|
||||||
|
do
|
||||||
|
test_post_with_file_and_form_data
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_get_with_redirection
|
||||||
|
do
|
||||||
|
test_get_with_redirection
|
||||||
|
end
|
||||||
|
|
||||||
|
net_test_get_with_authentication
|
||||||
|
do
|
||||||
|
test_get_with_authentication
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
302
library/network/http_client/tests/test_with_web_i.e
Normal file
302
library/network/http_client/tests/test_with_web_i.e
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
note
|
||||||
|
description: "[
|
||||||
|
Eiffel tests that can be executed by testing tool.
|
||||||
|
]"
|
||||||
|
author: "EiffelStudio test wizard"
|
||||||
|
date: "$Date$"
|
||||||
|
revision: "$Revision$"
|
||||||
|
testing: "type/manual"
|
||||||
|
|
||||||
|
deferred class
|
||||||
|
TEST_WITH_WEB_I
|
||||||
|
|
||||||
|
inherit
|
||||||
|
EQA_TEST_SET
|
||||||
|
redefine
|
||||||
|
on_prepare
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Initialization
|
||||||
|
|
||||||
|
on_prepare
|
||||||
|
do
|
||||||
|
Precursor
|
||||||
|
global_requestbin_path := new_requestbin_path
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
new_session (a_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||||
|
deferred
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Requestbin
|
||||||
|
|
||||||
|
global_requestbin_path: detachable READABLE_STRING_8
|
||||||
|
|
||||||
|
new_requestbin_path: detachable STRING
|
||||||
|
local
|
||||||
|
i,j: INTEGER
|
||||||
|
do
|
||||||
|
if
|
||||||
|
attached new_session ("http://requestb.in") as sess and then
|
||||||
|
attached sess.post ("/api/v1/bins", Void, Void) as resp
|
||||||
|
then
|
||||||
|
if resp.error_occurred then
|
||||||
|
print ("Error occurred!%N")
|
||||||
|
elseif attached resp.body as l_content then
|
||||||
|
|
||||||
|
i := l_content.substring_index ("%"name%":", 1)
|
||||||
|
if i > 0 then
|
||||||
|
j := l_content.index_of (',', i + 1)
|
||||||
|
if j > 0 then
|
||||||
|
Result := l_content.substring (i + 7, j - 1)
|
||||||
|
Result.adjust
|
||||||
|
if Result.starts_with ("%"") then
|
||||||
|
Result.remove_head (1)
|
||||||
|
end
|
||||||
|
if Result.ends_with ("%"") then
|
||||||
|
Result.remove_tail (1)
|
||||||
|
end
|
||||||
|
if not Result.starts_with ("/") then
|
||||||
|
Result.prepend_character ('/')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
feature -- Factory
|
||||||
|
|
||||||
|
test_post_url_encoded
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
-- URL ENCODED POST REQUEST
|
||||||
|
-- check requestbin to ensure the "Hello World" has been received in the raw body
|
||||||
|
-- also check that User-Agent was sent
|
||||||
|
create h.make_empty
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
if
|
||||||
|
attached sess.post (requestbin_path, Void, "Hello World") as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_post_with_form_data
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- POST REQUEST WITH FORM DATA
|
||||||
|
-- check requestbin to ensure the form parameters are correctly received
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.form_parameters.extend ("First Value", "First Key")
|
||||||
|
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.post (requestbin_path, l_ctx, "") as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_post_with_file
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- POST REQUEST WITH A FILE
|
||||||
|
-- check requestbin to ensure the form parameters are correctly received
|
||||||
|
-- set filename to a local file
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.set_upload_filename ("test.txt")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.post (requestbin_path, l_ctx, "") as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_put_with_file
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- PUT REQUEST WITH A FILE
|
||||||
|
-- check requestbin to ensure the file is correctly received
|
||||||
|
-- set filename to a local file
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.set_upload_filename ("test.txt")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.put (requestbin_path, l_ctx, Void) as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_put_with_data
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- PUT REQUEST WITH A FILE
|
||||||
|
-- check requestbin to ensure the file is correctly received
|
||||||
|
-- set filename to a local file
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.set_upload_data ("This is a test for http client.%N")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.put (requestbin_path, l_ctx, Void) as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_post_with_file_and_form_data
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
l_ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- POST REQUEST WITH A FILE AND FORM DATA
|
||||||
|
-- check requestbin to ensure the file and form parameters are correctly received
|
||||||
|
-- set filename to a local file
|
||||||
|
sess := new_session ("http://requestb.in")
|
||||||
|
create l_ctx.make
|
||||||
|
l_ctx.set_upload_filename ("logo.jpg")
|
||||||
|
l_ctx.form_parameters.extend ("First Value", "First Key")
|
||||||
|
l_ctx.form_parameters.extend ("Second Value", "Second Key")
|
||||||
|
create h.make_empty
|
||||||
|
if
|
||||||
|
attached sess.post (requestbin_path, l_ctx, Void) as res and then
|
||||||
|
attached res.headers as hds
|
||||||
|
then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_get_with_redirection
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
h: STRING_8
|
||||||
|
do
|
||||||
|
if attached global_requestbin_path as requestbin_path then
|
||||||
|
|
||||||
|
-- GET REQUEST, Forwarding (google's first answer is a forward)
|
||||||
|
-- check headers received (printed in console)
|
||||||
|
sess := new_session ("http://google.com")
|
||||||
|
create h.make_empty
|
||||||
|
if attached sess.get ("/", Void) as res and then attached res.headers as hds then
|
||||||
|
across
|
||||||
|
hds as c
|
||||||
|
loop
|
||||||
|
h.append (c.item.name + ": " + c.item.value + "%R%N")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print (h)
|
||||||
|
else
|
||||||
|
assert ("Has requestbin path", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_get_with_authentication
|
||||||
|
local
|
||||||
|
sess: HTTP_CLIENT_SESSION
|
||||||
|
ctx: HTTP_CLIENT_REQUEST_CONTEXT
|
||||||
|
do
|
||||||
|
-- GET REQUEST WITH AUTHENTICATION, see http://browserspy.dk/password.php
|
||||||
|
-- check header WWW-Authenticate is received (authentication successful)
|
||||||
|
sess := new_session ("http://browserspy.dk")
|
||||||
|
sess.set_credentials ("test", "test")
|
||||||
|
create ctx.make_with_credentials_required
|
||||||
|
if attached sess.get ("/password-ok.php", ctx) as res then
|
||||||
|
if attached {READABLE_STRING_8} res.body as l_body then
|
||||||
|
assert ("Fetch all body, including closing html tag", l_body.has_substring ("</html>"))
|
||||||
|
else
|
||||||
|
assert ("has body", False)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user