diff --git a/library/network/http_client/http_client-safe.ecf b/library/network/http_client/http_client-safe.ecf
index 4bcaca2f..4768dc03 100644
--- a/library/network/http_client/http_client-safe.ecf
+++ b/library/network/http_client/http_client-safe.ecf
@@ -10,12 +10,34 @@
-
+
+
+
+
+
+
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/library/network/http_client/http_client.ecf b/library/network/http_client/http_client.ecf
index e084ae0a..2e08d08f 100644
--- a/library/network/http_client/http_client.ecf
+++ b/library/network/http_client/http_client.ecf
@@ -1,5 +1,5 @@
-
+
@@ -9,13 +9,35 @@
-
-
+
+
+
+
+
+
+
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/library/network/http_client/src/http_client.e b/library/network/http_client/src/http_client.e
index a99953ca..bd6c2d89 100644
--- a/library/network/http_client/src/http_client.e
+++ b/library/network/http_client/src/http_client.e
@@ -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)"
diff --git a/library/network/http_client/src/http_client_response.e b/library/network/http_client/src/http_client_response.e
index 37f57aa4..1db2e774 100644
--- a/library/network/http_client/src/http_client_response.e
+++ b/library/network/http_client/src/http_client_response.e
@@ -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.
diff --git a/library/network/http_client/src/http_client_session.e b/library/network/http_client/src/http_client_session.e
index 8b46f503..9f9d5ded 100644
--- a/library/network/http_client/src/http_client_session.e
+++ b/library/network/http_client/src/http_client_session.e
@@ -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
diff --git a/library/network/http_client/src/spec/null/null_http_client.e b/library/network/http_client/src/spec/null/null_http_client.e
new file mode 100644
index 00000000..c2eec1ac
--- /dev/null
+++ b/library/network/http_client/src/spec/null/null_http_client.e
@@ -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
diff --git a/library/network/http_client/src/spec/null/null_http_client_session.e b/library/network/http_client/src/spec/null/null_http_client_session.e
new file mode 100644
index 00000000..9a766e57
--- /dev/null
+++ b/library/network/http_client/src/spec/null/null_http_client_session.e
@@ -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
diff --git a/library/network/http_client/src/spec/socket/net_http_client_request.e b/library/network/http_client/src/spec/socket/net_http_client_request.e
index 010c2477..4b29180e 100644
--- a/library/network/http_client/src/spec/socket/net_http_client_request.e
+++ b/library/network/http_client/src/spec/socket/net_http_client_request.e
@@ -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
+
+ 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
-feature {NONE} -- Helpers
+ 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
- read_header_from_socket (a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
+ 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
diff --git a/library/network/http_client/src/spec/socket/net_http_client_session.e b/library/network/http_client/src/spec/socket/net_http_client_session.e
index 0e1fd2cb..2bec47b1 100644
--- a/library/network/http_client/src/spec/socket/net_http_client_session.e
+++ b/library/network/http_client/src/spec/socket/net_http_client_session.e
@@ -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
diff --git a/library/network/http_client/tests/application.e b/library/network/http_client/tests/application.e
deleted file mode 100644
index dfb7e63b..00000000
--- a/library/network/http_client/tests/application.e
+++ /dev/null
@@ -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
diff --git a/library/network/http_client/tests/httptest.ecf b/library/network/http_client/tests/httptest.ecf
deleted file mode 100644
index 884c053d..00000000
--- a/library/network/http_client/tests/httptest.ecf
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- /EIFGENs$
- /CVS$
- /.svn$
-
-
-
-
\ No newline at end of file
diff --git a/library/network/http_client/tests/logo.jpg b/library/network/http_client/tests/logo.jpg
new file mode 100644
index 00000000..cb234734
Binary files /dev/null and b/library/network/http_client/tests/logo.jpg differ
diff --git a/library/network/http_client/tests/test.e b/library/network/http_client/tests/test.e
index 0073561a..6829c245 100644
--- a/library/network/http_client/tests/test.e
+++ b/library/network/http_client/tests/test.e
@@ -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
diff --git a/library/network/http_client/tests/test.txt b/library/network/http_client/tests/test.txt
new file mode 100644
index 00000000..08d13bd7
--- /dev/null
+++ b/library/network/http_client/tests/test.txt
@@ -0,0 +1 @@
+This is a text sample for testing HTTP Client library.
diff --git a/library/network/http_client/tests/test_http_client_i.e b/library/network/http_client/tests/test_http_client_i.e
index b42af04b..e49f6823 100644
--- a/library/network/http_client/tests/test_http_client_i.e
+++ b/library/network/http_client/tests/test_http_client_i.e
@@ -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
diff --git a/library/network/http_client/tests/test_libcurl_with_web.e b/library/network/http_client/tests/test_libcurl_with_web.e
new file mode 100644
index 00000000..52c6fc23
--- /dev/null
+++ b/library/network/http_client/tests/test_libcurl_with_web.e
@@ -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
diff --git a/library/network/http_client/tests/test_net_with_web.e b/library/network/http_client/tests/test_net_with_web.e
new file mode 100644
index 00000000..1c551aac
--- /dev/null
+++ b/library/network/http_client/tests/test_net_with_web.e
@@ -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
diff --git a/library/network/http_client/tests/test_with_web_i.e b/library/network/http_client/tests/test_with_web_i.e
new file mode 100644
index 00000000..f386d5cb
--- /dev/null
+++ b/library/network/http_client/tests/test_with_web_i.e
@@ -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 ("