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:
2015-09-14 21:34:37 +02:00
parent 29c4931dc0
commit eec3cbdba1
18 changed files with 1131 additions and 570 deletions

View File

@@ -10,12 +10,34 @@
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
</option>
<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="http" location="..\protocol\http\http-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"/>
<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>
</system>

View File

@@ -1,5 +1,5 @@
<?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">
<root all_classes="true"/>
<file_rule>
@@ -9,13 +9,35 @@
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="provisional">
</option>
<library name="base" location="$ISE_LIBRARY/library/base/base.ecf"/>
<library name="curl" location="$ISE_LIBRARY\library\cURL\cURL.ecf"/>
<library name="base" location="$ISE_LIBRARY\library\base\base.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="http" location="..\protocol\http\http.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"/>
<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>
</system>

View File

@@ -16,7 +16,6 @@ feature -- Status
deferred
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)"

View File

@@ -54,6 +54,9 @@ feature -- Access
status_line: detachable READABLE_STRING_8
http_version: detachable READABLE_STRING_8
-- http version associated with `status_line'.
raw_header: READABLE_STRING_8
-- Raw http header of the response.
@@ -190,15 +193,54 @@ feature -- Access
feature -- Change
set_http_version (v: like http_version)
-- Set `http_version' to `v'.
do
http_version := v
end
set_status (s: INTEGER)
-- Set response `status' code to `s'
do
status := s
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)
-- 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
--| it might be useful to deal with redirection customization...
local
@@ -234,7 +276,7 @@ feature -- Change
j := i + 2
pos := j
status_line := l_status_line
set_status_line (l_status_line)
set_raw_header (h)
-- libcURL does not cache redirection content.

View File

@@ -218,9 +218,9 @@ feature -- Element change
cookie := Void
end
set_cookie (c: READABLE_STRING_8)
set_cookie (a_cookie: detachable READABLE_STRING_8)
do
cookie := c
cookie := a_cookie
end
set_timeout (n_seconds: like timeout)
@@ -253,12 +253,26 @@ feature -- Element change
headers.prune (k)
end
set_credentials (u: like username; p: like password)
set_credentials (u,p: detachable READABLE_STRING_GENERAL)
local
s: STRING_32
do
username := u
password := p
if u = Void then
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
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
credentials := Void
end

View 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

View File

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

View File

@@ -18,6 +18,8 @@ inherit
REFACTORING_HELPER
SHARED_EXECUTION_ENVIRONMENT
create
make
@@ -33,19 +35,19 @@ feature -- Access
local
redirection_response: detachable like response
l_uri: URI
l_header_key: READABLE_STRING_8
l_host: READABLE_STRING_8
l_cookie: detachable READABLE_STRING_8
l_request_uri: STRING
l_url: HTTP_URL
socket: NETWORK_STREAM_SOCKET
s: STRING
l_message: STRING
i,j: INTEGER
l_content_length: INTEGER
l_location: detachable READABLE_STRING_8
l_port: INTEGER
l_authorization: HTTP_AUTHORIZATION
l_platform: STRING
l_useragent: STRING
l_upload_data: detachable READABLE_STRING_8
l_form_data: detachable HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]
ctx: like context
@@ -58,37 +60,40 @@ feature -- Access
l_prev_header: READABLE_STRING_8
l_boundary: READABLE_STRING_8
l_is_http_1_0: BOOLEAN
retried: BOOLEAN
do
ctx := context
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")
end
create Result.make (url)
create l_form_string.make_empty
-- Get URL data
create l_uri.make_from_string (url)
l_port := l_uri.port
if l_port = 0 then
if url.starts_with_general ("https://") then
l_port := 443
else
l_port := 80
if not retried then
ctx := context
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")
end
end
create Result.make (url)
if attached l_uri.host as h then
l_host := h
else
create l_url.make (url)
l_host := l_url.host
end
create l_form_string.make_empty
-- add headers for authorization
if attached l_uri.userinfo as l_userinfo then
if attached l_uri.username as u_name then
if attached l_uri.password as u_pass then
-- Get URL data
create l_uri.make_from_string (url)
l_port := l_uri.port
if l_port = 0 then
if url.starts_with_general ("https://") then
l_port := 443
else
l_port := 80
end
end
if attached l_uri.host as h then
l_host := h
else
create l_url.make (url)
l_host := l_url.host
end
-- add headers for authorization
if not headers.has ("Authorization") then
if
attached username as u_name and
attached password as u_pass
then
create l_authorization.make_basic_auth (u_name, u_pass)
if attached l_authorization.http_authorization as auth then
headers.extend (auth, "Authorization")
@@ -96,286 +101,356 @@ feature -- Access
check headers.has_key ("Authorization") end
end
end
end
create l_request_uri.make_from_string (l_uri.path)
if attached l_uri.query as l_query then
l_request_uri.append_character ('?')
l_request_uri.append (l_query)
end
-- add headers for user agent
if {PLATFORM}.is_unix then
l_platform := "Unix"
else
if {PLATFORM}.is_mac then
l_platform := "Mac"
else
l_platform := "Windows"
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
if attached ctx then
if ctx.has_upload_filename then
l_upload_filename := ctx.upload_filename
create l_request_uri.make_from_string (l_uri.path)
if attached l_uri.query as l_query then
l_request_uri.append_character ('?')
l_request_uri.append (l_query)
end
if ctx.has_upload_data then
l_upload_data := ctx.upload_data
end
if ctx.has_form_data then
l_form_data := ctx.form_parameters
check non_empty_form_data: not l_form_data.is_empty end
if l_upload_data = Void and l_upload_filename = Void then
-- Send as form-urlencoded
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
l_upload_data := ctx.form_parameters_to_url_encoded_string
headers.force (l_upload_data.count.out, "Content-Length")
-- add computed header User-Agent if not yet set.
if not headers.has ("User-Agent") then
if {PLATFORM}.is_unix then
l_platform := "Unix"
elseif {PLATFORM}.is_windows then
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
-- create form
l_boundary := new_mime_boundary
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
if l_form_data /= Void then
headers.extend ("*/*", "Accept")
from
l_form_data.start
until
l_form_data.after
loop
l_form_string.append (l_boundary)
l_form_string.append (http_end_of_header_line)
l_form_string.append ("Content-Disposition: form-data; name=")
l_form_string.append ("%"" + l_form_data.key_for_iteration + "%"")
l_form_string.append (http_end_of_header_line)
l_form_string.append (http_end_of_header_line)
l_form_string.append (l_form_data.item_for_iteration)
l_form_string.append (http_end_of_header_line)
l_form_data.forth
end
if l_upload_filename /= Void then
-- get file extension, otherwise set default
l_mime_type := "application/octet-stream"
create l_mime_type_mapping.make_default
l_fn_extension := l_upload_filename.tail (l_upload_filename.count - l_upload_filename.last_index_of ('.', l_upload_filename.count))
if attached l_mime_type_mapping.mime_type (l_fn_extension) as mime then
l_mime_type := mime
end
l_form_string.append (l_boundary)
l_form_string.append (http_end_of_header_line)
l_form_string.append ("Content-Disposition: form-data; name=%"" + l_upload_filename.as_string_32 + "%"")
l_form_string.append ("; filename=%"" + l_upload_filename + "%"")
l_form_string.append (http_end_of_header_line)
l_form_string.append ("Content-Type: ")
l_form_string.append (l_mime_type)
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)
if l_upload_file.exists and then l_upload_file.is_readable then
l_upload_file.open_read
l_upload_file.read_stream (l_upload_file.count)
l_form_string.append (l_upload_file.last_string)
end
l_form_string.append (http_end_of_header_line)
end
l_form_string.append (l_boundary + "--") --| end
l_upload_data := l_form_string
headers.extend (l_upload_data.count.out, "Content-Length")
end
end
else
if request_method.is_case_insensitive_equal ("POST") then
if ctx /= Void then
if ctx.has_upload_data then
l_upload_data := ctx.upload_data
if l_upload_data /= Void then
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
headers.extend (l_upload_data.count.out, "Content-Length")
end
elseif 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_file /= Void end
end
end
end
l_platform := "Unknown"
end
headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
end
end
-- handle put requests
if request_method.is_case_insensitive_equal ("PUT") then
-- handle sending data
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
l_upload_filename := ctx.upload_filename
end
if ctx.has_upload_data then
l_upload_data := ctx.upload_data
end
if ctx.has_form_data then
l_form_data := ctx.form_parameters
check non_empty_form_data: not l_form_data.is_empty end
if l_upload_data = Void and l_upload_filename = Void then
-- Send as form-urlencoded
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
l_upload_data := ctx.form_parameters_to_url_encoded_string
headers.force (l_upload_data.count.out, "Content-Length")
else
-- create form
l_boundary := new_mime_boundary
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
if l_form_data /= Void then
headers.extend ("*/*", "Accept")
from
l_form_data.start
until
l_form_data.after
loop
l_form_string.append (l_boundary)
l_form_string.append (http_end_of_header_line)
l_form_string.append ("Content-Disposition: form-data; name=")
l_form_string.append ("%"" + l_form_data.key_for_iteration + "%"")
l_form_string.append (http_end_of_header_line)
l_form_string.append (http_end_of_header_line)
l_form_string.append (l_form_data.item_for_iteration)
l_form_string.append (http_end_of_header_line)
l_form_data.forth
end
if l_upload_filename /= Void then
-- get file extension, otherwise set default
l_mime_type := "application/octet-stream"
create l_mime_type_mapping.make_default
l_fn_extension := l_upload_filename.tail (l_upload_filename.count - l_upload_filename.last_index_of ('.', l_upload_filename.count))
if attached l_mime_type_mapping.mime_type (l_fn_extension) as mime then
l_mime_type := mime
end
l_form_string.append (l_boundary)
l_form_string.append (http_end_of_header_line)
l_form_string.append ("Content-Disposition: form-data; name=%"" + l_upload_filename.as_string_32 + "%"")
l_form_string.append ("; filename=%"" + l_upload_filename + "%"")
l_form_string.append (http_end_of_header_line)
l_form_string.append ("Content-Type: ")
l_form_string.append (l_mime_type)
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)
if l_upload_file.exists and then l_upload_file.is_access_readable then
append_file_content_to (l_upload_file, l_upload_file.count, l_form_string)
-- Reset l_upload_file to Void, since the related content is already processed.
l_upload_file := Void
end
l_form_string.append (http_end_of_header_line)
end
l_form_string.append (l_boundary + "--") --| end
l_upload_data := l_form_string
headers.extend (l_upload_data.count.out, "Content-Length")
end
check l_upload_filename /= Void end
end
elseif
request_method.is_case_insensitive_equal ("POST")
or else request_method.is_case_insensitive_equal ("PUT")
then
if l_upload_data /= Void then
check ctx.has_upload_data end
headers.extend ("application/x-www-form-urlencoded", "Content-Type")
headers.extend (l_upload_data.count.out, "Content-Length")
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
headers.extend (l_upload_file.count.out, "Content-Length")
end
check l_upload_file /= Void end
end
end
end
end
-- Connect
create socket.make_client_by_port (l_port, l_host)
socket.set_timeout (timeout)
socket.set_connect_timeout (connect_timeout)
socket.connect
if socket.is_connected then
create s.make_from_string (request_method.as_upper)
s.append_character (' ')
s.append (l_request_uri)
s.append_character (' ')
if l_is_http_1_0 then
s.append ("HTTP/1.0")
else
s.append ("HTTP/1.1")
end
s.append (Http_end_of_header_line)
s.append (Http_host_header)
s.append (": ")
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
if headers.is_empty then
s.append (Http_end_of_command)
else
across
headers as ic
loop
s.append (ic.key)
s.append (": ")
s.append (ic.item)
s.append (Http_end_of_header_line)
-- Connect
create socket.make_client_by_port (l_port, l_host)
socket.set_connect_timeout (connect_timeout)
socket.set_timeout (timeout)
socket.connect
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)
s.append_character (' ')
s.append (l_request_uri)
s.append_character (' ')
if l_is_http_1_0 then
s.append ("HTTP/1.0")
else
s.append ("HTTP/1.1")
end
s.append (Http_end_of_header_line)
end
if l_upload_data /= Void then
s.append (l_upload_data)
s.append (http_end_of_header_line)
end
if attached l_upload_file then
if not l_upload_file.after then
from
until
l_upload_file.after
loop
l_upload_file.read_line_thread_aware
s.append (l_upload_file.last_string)
end
end
end
socket.put_string (s)
--| Get response.
--| Get header message
create l_message.make_empty
read_header_from_socket (socket, l_message)
l_prev_header := Result.raw_header
Result.set_raw_header (l_message.string)
l_content_length := -1
if attached Result.header ("Content-Length") as s_len and then s_len.is_integer then
l_content_length := s_len.to_integer
end
l_location := Result.header ("Location")
if attached Result.header ("Set-Cookies") as s_cookies then
session.set_cookie (s_cookies)
end
l_message.append (http_end_of_header_line)
-- Get content if any.
append_socket_content_to (Result, socket, l_content_length, l_message)
-- Restore previous header
Result.set_raw_header (l_prev_header)
-- Set message
Result.set_response_message (l_message, context)
-- Get status code.
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
-- Compute Header Host:
s.append (Http_host_header)
s.append (": ")
if attached headers [Http_host_header] as h_host then
s.append (h_host)
else
i := 1
s.append (l_host)
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)
s.append (http_end_of_header_line)
-- Append the given request headers
l_cookie := Void
if headers.is_empty then
s.append (Http_end_of_command)
else
across
headers as ic
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 (": ")
s.append (ic.item)
s.append (Http_end_of_header_line)
end
end
s.append (Http_end_of_header_line)
end
end
-- follow redirect
if l_location /= Void then
if current_redirects < max_redirects then
current_redirects := current_redirects + 1
initialize (l_location, ctx)
redirection_response := response
redirection_response.add_redirection (Result.status_line, Result.raw_header, Result.body)
Result := redirection_response
-- Result.add_redirection (redirection_response.status_line, redirection_response.raw_header, redirection_response.body)
-- 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
end
current_redirects := 0
if l_upload_data /= Void then
s.append (l_upload_data)
s.append (http_end_of_header_line)
end
--| Note that any remaining file to upload will be done directly via the socket
--| to optimize memory usage
--| Send request
if socket.ready_for_writing then
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 header message
if socket.ready_for_reading then
create l_message.make_empty
append_socket_header_content_to (socket, l_message)
l_prev_header := Result.raw_header
Result.set_raw_header (l_message.string)
l_content_length := -1
if attached Result.header ("Content-Length") as s_len and then s_len.is_integer then
l_content_length := s_len.to_integer
end
l_location := Result.header ("Location")
if attached Result.header ("Set-Cookies") as s_cookies then
session.set_cookie (s_cookies)
end
l_message.append (http_end_of_header_line)
-- Get content if any.
append_socket_content_to (Result, socket, l_content_length, l_message)
-- Restore previous header
Result.set_raw_header (l_prev_header)
-- Set message
Result.set_response_message (l_message, ctx)
-- Check status code.
check status_coherent: attached Result.status_line as l_status_line implies l_status_line.has_substring (Result.status.out) end
-- follow redirect
if l_location /= Void then
if current_redirects < max_redirects then
current_redirects := current_redirects + 1
initialize (l_location, ctx)
redirection_response := response
redirection_response.add_redirection (Result.status_line, Result.raw_header, Result.body)
Result := redirection_response
end
end
current_redirects := current_redirects - 1
else
Result.set_error_message ("Read Timeout")
end
else
Result.set_error_message ("Write Timeout")
end
else
Result.set_error_message ("Could not connect")
end
else
Result.set_error_message ("Could not connect")
create Result.make (url)
Result.set_error_message ("Error: internal error")
end
rescue
retried := True
retry
end
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'.
local
s: READABLE_STRING_8
do
if a_socket.is_readable then
from
s := ""
until
s.same_string ("%R") or not a_socket.is_readable
loop
a_socket.read_line_thread_aware
s := a_socket.last_string
if s.same_string ("%R") then
-- Reach end of header
-- a_output.append (http_end_of_header_line)
else
a_output.append (s)
a_output.append_character ('%N')
end
from
s := ""
until
s.same_string ("%R") or not a_socket.readable
loop
a_socket.read_line_thread_aware
s := a_socket.last_string
if s.same_string ("%R") then
-- Reach end of header
-- a_output.append (http_end_of_header_line)
else
a_output.append (s)
a_output.append_character ('%N')
end
end
end
@@ -386,61 +461,94 @@ feature {NONE} -- Helpers
-- this is probably HTTP/1.0 without any Content-Length.
local
s: STRING_8
n,pos: INTEGER
n,pos, l_chunk_size, l_count: INTEGER
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
do
if a_socket.is_readable then
if a_socket.readable then
if a_len >= 0 then
a_socket.read_stream_thread_aware (a_len)
s := a_socket.last_string
check full_content_read: a_socket.bytes_read = a_len end
a_buffer.append (s)
else
if attached a_response.header ("Transfer-Encoding") as l_enc and then l_enc.is_case_insensitive_equal ("chunked") then
from
create hexa2int.make
n := 1
until
n = 0 or not a_socket.is_readable
loop
a_socket.read_line_thread_aware -- Read chunk info
s := a_socket.last_string
s.right_adjust
pos := s.index_of (';', 1)
if pos > 0 then
s.keep_head (pos - 1)
end
if s.is_empty then
n := 0
from
n := a_len
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)
l_count := l_count + a_socket.bytes_read
n := n - a_socket.bytes_read
a_buffer.append (a_socket.last_string)
else
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
create hexa2int.make
n := 1
until
n = 0 or not a_socket.readable
loop
a_socket.read_line_thread_aware -- Read chunk info
s := a_socket.last_string
s.right_adjust
pos := s.index_of (';', 1)
if pos > 0 then
s.keep_head (pos - 1)
end
if s.is_empty then
n := 0
else
hexa2int.parse_string_with_type (s, hexa2int.type_integer)
if hexa2int.parse_successful then
n := hexa2int.parsed_integer
else
hexa2int.parse_string_with_type (s, hexa2int.type_integer)
if hexa2int.parse_successful then
n := hexa2int.parsed_integer
n := 0
end
end
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)
l_count := l_count + a_socket.bytes_read
n := n - a_socket.bytes_read
a_buffer.append (a_socket.last_string)
else
n := 0
a_response.set_error_message ("Could not read chunked data, timeout")
end
end
if n > 0 then
a_socket.read_stream_thread_aware (n)
check a_socket.bytes_read = n end
a_buffer.append (a_socket.last_string)
if a_socket.ready_for_reading then
a_socket.read_character
check a_socket.last_character = '%R' end
a_socket.read_character
check a_socket.last_character = '%N' end
else
a_response.set_error_message ("Could not read chunked data, timeout")
end
end
else
-- HTTP/1.0
from
n := 1_024
until
n < 1_024 or not a_socket.is_readable
loop
a_socket.read_stream_thread_aware (1_024)
end
else
-- No Content-Length and no chunked transfer encoding!
-- maybe HTTP/1.0 ?
-- FIXME: check solution!
from
l_count := 0
l_chunk_size := 1_024
n := l_chunk_size --| value to satisfy until condition on first loop.
until
n < l_chunk_size or not a_socket.readable
loop
if a_socket.ready_for_reading then
a_socket.read_stream_thread_aware (l_chunk_size)
s := a_socket.last_string
n := a_socket.bytes_read
l_count := l_count + n
a_buffer.append (s)
else
a_response.set_error_message ("Could not read data, timeout")
end
end
end

View File

@@ -98,7 +98,6 @@ feature {NONE} -- Implementation
local
req: HTTP_CLIENT_REQUEST
ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT
f: detachable RAW_FILE
l_data: detachable READABLE_STRING_8
do
ctx := a_ctx
@@ -117,34 +116,12 @@ feature {NONE} -- Implementation
if ctx /= Void then
l_data := ctx.upload_data
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
--| 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
check put_conflict_file_and_data: not ctx.has_upload_filename end
end
end
create {NET_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx)
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
note

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -6,7 +6,13 @@ create
feature -- Init
make
local
null: NULL_HTTP_CLIENT
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
end
@@ -42,7 +48,7 @@ feature -- Init
do
if not b then
create e
e.set_message (m)
e.set_description (m)
e.raise
end
end

View File

@@ -0,0 +1 @@
This is a text sample for testing HTTP Client library.

View File

@@ -44,8 +44,6 @@ feature -- Test routines
assert ("missing body", False)
end
assert ("same headers", h.same_string (res.raw_header))
else
assert ("Not found", False)
end
end
@@ -54,7 +52,6 @@ feature -- Test routines
sess: like new_session
h: STRING_8
do
--| Add your code here
sess := new_session ("http://requestb.in")
create h.make_empty
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")
end
end
print (h)
end

View 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

View 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

View 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