diff --git a/library/network/http_client/http_client-safe.ecf b/library/network/http_client/http_client-safe.ecf
index 4768dc03..d09ef84b 100644
--- a/library/network/http_client/http_client-safe.ecf
+++ b/library/network/http_client/http_client-safe.ecf
@@ -24,20 +24,60 @@
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library/network/http_client/src/default/libcurl/default_http_client.e b/library/network/http_client/src/default/libcurl/default_http_client.e
new file mode 100644
index 00000000..288dc20d
--- /dev/null
+++ b/library/network/http_client/src/default/libcurl/default_http_client.e
@@ -0,0 +1,15 @@
+note
+ description: "[
+ Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT.
+ ]"
+ author: "$Author$"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ DEFAULT_HTTP_CLIENT
+
+inherit
+ LIBCURL_HTTP_CLIENT
+
+end
diff --git a/library/network/http_client/src/default/libcurl_or_net/default_http_client.e b/library/network/http_client/src/default/libcurl_or_net/default_http_client.e
new file mode 100644
index 00000000..9f50c575
--- /dev/null
+++ b/library/network/http_client/src/default/libcurl_or_net/default_http_client.e
@@ -0,0 +1,43 @@
+note
+ description: "[
+ Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT.
+ ]"
+ author: "$Author$"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ DEFAULT_HTTP_CLIENT
+
+inherit
+ HTTP_CLIENT
+
+feature -- Access
+
+ new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
+ -- Create a new session using `a_base_url'.
+ local
+ libcurl: LIBCURL_HTTP_CLIENT
+ net: NET_HTTP_CLIENT
+ do
+ --| For now, try libcurl first, and then net
+ --| the reason is the net implementation is still in progress.
+ create libcurl
+ Result := libcurl.new_session (a_base_url)
+ if not Result.is_available then
+ create net
+ Result := net.new_session (a_base_url)
+ end
+ 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/default/net/default_http_client.e b/library/network/http_client/src/default/net/default_http_client.e
new file mode 100644
index 00000000..1a9b936d
--- /dev/null
+++ b/library/network/http_client/src/default/net/default_http_client.e
@@ -0,0 +1,15 @@
+note
+ description: "[
+ Default HTTP_CLIENT based on NET_HTTP_CLIENT.
+ ]"
+ author: "$Author$"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ DEFAULT_HTTP_CLIENT
+
+inherit
+ NET_HTTP_CLIENT
+
+end
diff --git a/library/network/http_client/src/default/null/default_http_client.e b/library/network/http_client/src/default/null/default_http_client.e
new file mode 100644
index 00000000..70b4a992
--- /dev/null
+++ b/library/network/http_client/src/default/null/default_http_client.e
@@ -0,0 +1,15 @@
+note
+ description: "[
+ Default HTTP_CLIENT based on NULL_HTTP_CLIENT.
+ ]"
+ author: "$Author$"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ DEFAULT_HTTP_CLIENT
+
+inherit
+ NULL_HTTP_CLIENT
+
+end
diff --git a/library/network/http_client/src/http_client.e b/library/network/http_client/src/http_client.e
index bd6c2d89..2b3ba358 100644
--- a/library/network/http_client/src/http_client.e
+++ b/library/network/http_client/src/http_client.e
@@ -9,7 +9,7 @@ note
deferred class
HTTP_CLIENT
-feature -- Status
+feature -- Access
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
-- Create a new session using `a_base_url'.
diff --git a/library/network/http_client/src/http_client_response.e b/library/network/http_client/src/http_client_response.e
index 1db2e774..92e64fe0 100644
--- a/library/network/http_client/src/http_client_response.e
+++ b/library/network/http_client/src/http_client_response.e
@@ -95,6 +95,24 @@ feature -- Access
Result := s
end
+ multiple_header (a_name: READABLE_STRING_8): detachable LIST [READABLE_STRING_8]
+ -- Header multiple entries related to `a_name'
+ local
+ k: READABLE_STRING_8
+ do
+ across
+ headers as hds
+ loop
+ k := hds.item.name
+ if k.same_string (a_name) then
+ if Result = Void then
+ create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (1)
+ end
+ Result.force (hds.item.value)
+ end
+ end
+ end
+
headers: LIST [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]
-- Computed table of http headers of the response.
--| We use a LIST since one might have multiple message-header fields with the same field-name
diff --git a/library/network/http_client/src/spec/socket/net_http_client.e b/library/network/http_client/src/spec/net/net_http_client.e
similarity index 85%
rename from library/network/http_client/src/spec/socket/net_http_client.e
rename to library/network/http_client/src/spec/net/net_http_client.e
index 863451f5..753d3b3a 100644
--- a/library/network/http_client/src/spec/socket/net_http_client.e
+++ b/library/network/http_client/src/spec/net/net_http_client.e
@@ -14,18 +14,6 @@ class
inherit
HTTP_CLIENT
-create
- default_create,
- make
-
-feature {NONE} -- Initialization
-
- make
- -- Initialize `Current'.
- do
- default_create
- end
-
feature -- Status
new_session (a_base_url: READABLE_STRING_8): NET_HTTP_CLIENT_SESSION
diff --git a/library/network/http_client/src/spec/socket/net_http_client_request.e b/library/network/http_client/src/spec/net/net_http_client_request.e
similarity index 58%
rename from library/network/http_client/src/spec/socket/net_http_client_request.e
rename to library/network/http_client/src/spec/net/net_http_client_request.e
index 4b29180e..41c91294 100644
--- a/library/network/http_client/src/spec/socket/net_http_client_request.e
+++ b/library/network/http_client/src/spec/net/net_http_client_request.e
@@ -28,6 +28,16 @@ feature {NONE} -- Internal
session: NET_HTTP_CLIENT_SESSION
net_http_client_version: STRING = "0.1"
+
+ new_socket (a_host: READABLE_STRING_8; a_port: INTEGER; a_is_https: BOOLEAN; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): NETWORK_STREAM_SOCKET
+ do
+ if a_is_https then
+ create {SSL_NETWORK_STREAM_SOCKET} Result.make_client_by_port (a_port, a_host)
+ else
+ create Result.make_client_by_port (a_port, a_host)
+ end
+ end
+
feature -- Access
response: HTTP_CLIENT_RESPONSE
@@ -40,12 +50,13 @@ feature -- Access
l_cookie: detachable READABLE_STRING_8
l_request_uri: STRING
l_url: HTTP_URL
- socket: NETWORK_STREAM_SOCKET
+ l_socket: NETWORK_STREAM_SOCKET
s: STRING
l_message: STRING
l_content_length: INTEGER
l_location: detachable READABLE_STRING_8
l_port: INTEGER
+ l_is_https: BOOLEAN
l_authorization: HTTP_AUTHORIZATION
l_platform: STRING
l_upload_data: detachable READABLE_STRING_8
@@ -69,13 +80,13 @@ feature -- Access
end
create Result.make (url)
- create l_form_string.make_empty
-- Get URL data
+ l_is_https := url.starts_with_general ("https://")
create l_uri.make_from_string (url)
l_port := l_uri.port
if l_port = 0 then
- if url.starts_with_general ("https://") then
+ if l_is_https then
l_port := 443
else
l_port := 80
@@ -88,144 +99,147 @@ feature -- Access
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")
- end
- check headers.has_key ("Authorization") 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 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
- l_platform := "Unknown"
- end
- headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
- end
-
- -- handle sending data
- if ctx /= Void then
- if ctx.has_upload_filename then
- 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
- 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
-
-- 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
+ l_socket := new_socket (l_host, l_port, l_is_https, ctx)
+ l_socket.set_connect_timeout (connect_timeout)
+ l_socket.set_timeout (timeout)
+ l_socket.connect
+ if l_socket.is_connected then
+
+ create l_form_string.make_empty
+
+ -- 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")
+ end
+ check headers.has_key ("Authorization") 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 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
+ l_platform := "Unknown"
+ end
+ headers.extend ("eiffelhttpclient/" + net_http_client_version + " (" + l_platform + ")", "User-Agent")
+ end
+
+ -- handle sending data
+ if ctx /= Void then
+ if ctx.has_upload_filename then
+ 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
+ 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
+
-- 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.
@@ -295,20 +309,32 @@ feature -- Access
--| 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)
+
+ --|-----------------------------|--
+ --| Request preparation is done |--
+ --|-----------------------------|--
+
+ if l_socket.ready_for_writing then
+ --| Socket is ready for writing, so let's send the request.
+
+ --|-------------------------|--
+ --| Send request |--
+ --|-------------------------|--
+
+ l_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)
+ append_file_content_to_socket (l_upload_file, l_upload_file.count, l_socket)
end
- --| Get response.
- --| Get header message
- if socket.ready_for_reading then
+ --|-------------------------|--
+ --| Get response. |--
+ --| Get header message |--
+ --|-------------------------|--
+ if l_socket.ready_for_reading then
create l_message.make_empty
- append_socket_header_content_to (socket, l_message)
+ append_socket_header_content_to (l_socket, l_message)
l_prev_header := Result.raw_header
Result.set_raw_header (l_message.string)
l_content_length := -1
@@ -316,13 +342,13 @@ feature -- Access
l_content_length := s_len.to_integer
end
l_location := Result.header ("Location")
- if attached Result.header ("Set-Cookies") as s_cookies then
+ if attached Result.header ("Set-Cookie") 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)
+ append_socket_content_to (Result, l_socket, l_content_length, l_message)
-- Restore previous header
Result.set_raw_header (l_prev_header)
-- Set message
@@ -455,33 +481,47 @@ feature {NONE} -- Helpers
end
end
- append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_len: INTEGER; a_buffer: STRING)
- -- Get content from `a_socket' and append it to `a_buffer'.
+ append_socket_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_len: INTEGER; a_output: STRING)
+ -- Get content from `a_socket' and append it to `a_output'.
-- If `a_len' is negative, try to get as much as possible,
-- this is probably HTTP/1.0 without any Content-Length.
local
s: STRING_8
+ r: INTEGER -- remaining count
n,pos, l_chunk_size, l_count: INTEGER
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
do
if a_socket.readable then
if a_len >= 0 then
+ debug ("socket_content")
+ io.error.put_string ("Content-Length="+ a_len.out +"%N")
+ end
from
- n := a_len
+ r := a_len
until
- n = 0 or else not a_socket.readable or else a_response.error_occurred
+ r = 0 or else not a_socket.readable or else a_response.error_occurred
loop
if a_socket.ready_for_reading then
- a_socket.read_stream_thread_aware (n)
+ a_socket.read_stream_thread_aware (r)
l_count := l_count + a_socket.bytes_read
- n := n - a_socket.bytes_read
- a_buffer.append (a_socket.last_string)
+ debug ("socket_content")
+ io.error.put_string (" - byte read=" + a_socket.bytes_read.out + "%N")
+ io.error.put_string (" - current count=" + l_count.out + "%N")
+ end
+ r := r - a_socket.bytes_read
+ a_output.append (a_socket.last_string)
else
+ debug ("socket_content")
+ io.error.put_string (" -! TIMEOUT%N")
+ end
a_response.set_error_message ("Could not read chunked data, timeout")
end
end
- check full_content_read: l_count = a_len end
+ check full_content_read: not a_response.error_occurred implies 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
+ debug ("socket_content")
+ io.error.put_string ("Chunked encoding%N")
+ end
from
create hexa2int.make
n := 1
@@ -491,6 +531,9 @@ feature {NONE} -- Helpers
a_socket.read_line_thread_aware -- Read chunk info
s := a_socket.last_string
s.right_adjust
+ debug ("socket_content")
+ io.error.put_string (" - chunk info='" + s + "'%N")
+ end
pos := s.index_of (';', 1)
if pos > 0 then
s.keep_head (pos - 1)
@@ -505,17 +548,28 @@ feature {NONE} -- Helpers
n := 0
end
end
+ debug ("socket_content")
+ io.error.put_string (" - chunk size=" + n.out + "%N")
+ end
if n > 0 then
from
+ r := n
until
- n = 0 or else not a_socket.readable or else a_response.error_occurred
+ r = 0 or else not a_socket.readable or else a_response.error_occurred
loop
if a_socket.ready_for_reading then
- a_socket.read_stream_thread_aware (n)
+ a_socket.read_stream_thread_aware (r)
l_count := l_count + a_socket.bytes_read
- n := n - a_socket.bytes_read
- a_buffer.append (a_socket.last_string)
+ debug ("socket_content")
+ io.error.put_string (" - byte read=" + a_socket.bytes_read.out + "%N")
+ io.error.put_string (" - current count=" + l_count.out + "%N")
+ end
+ r := r - a_socket.bytes_read
+ a_output.append (a_socket.last_string)
else
+ debug ("socket_content")
+ io.error.put_string (" -! TIMEOUT%N")
+ end
a_response.set_error_message ("Could not read chunked data, timeout")
end
end
@@ -525,7 +579,13 @@ feature {NONE} -- Helpers
check a_socket.last_character = '%R' end
a_socket.read_character
check a_socket.last_character = '%N' end
+ debug ("socket_content")
+ io.error.put_string (" - Found CRNL %N")
+ end
else
+ debug ("socket_content")
+ io.error.put_string (" -! TIMEOUT%N")
+ end
a_response.set_error_message ("Could not read chunked data, timeout")
end
end
@@ -546,7 +606,7 @@ feature {NONE} -- Helpers
s := a_socket.last_string
n := a_socket.bytes_read
l_count := l_count + n
- a_buffer.append (s)
+ a_output.append (s)
else
a_response.set_error_message ("Could not read data, timeout")
end
diff --git a/library/network/http_client/src/spec/socket/net_http_client_session.e b/library/network/http_client/src/spec/net/net_http_client_session.e
similarity index 96%
rename from library/network/http_client/src/spec/socket/net_http_client_session.e
rename to library/network/http_client/src/spec/net/net_http_client_session.e
index 2bec47b1..2c375574 100644
--- a/library/network/http_client/src/spec/socket/net_http_client_session.e
+++ b/library/network/http_client/src/spec/net/net_http_client_session.e
@@ -11,6 +11,8 @@ class
inherit
HTTP_CLIENT_SESSION
+ NET_HTTP_CLIENT_INFO
+
create
make
@@ -22,8 +24,14 @@ feature {NONE} -- Initialization
feature -- Status report
- is_available: BOOLEAN = True
+ is_available: BOOLEAN
-- Is interface usable?
+ do
+ Result := True
+ if base_url.starts_with_general ("https://") then
+ Result := has_https_support
+ end
+ end
feature -- Custom
diff --git a/library/network/http_client/src/spec/net/no_ssl/net_http_client_info.e b/library/network/http_client/src/spec/net/no_ssl/net_http_client_info.e
new file mode 100644
index 00000000..47517b32
--- /dev/null
+++ b/library/network/http_client/src/spec/net/no_ssl/net_http_client_info.e
@@ -0,0 +1,24 @@
+note
+ description: "Additional information related to NET HTTP Client.."
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ NET_HTTP_CLIENT_INFO
+
+feature -- Access
+
+ has_https_support: BOOLEAN = False
+ -- Is HTTPS supported?
+
+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/net/no_ssl/ssl_network_stream_socket.e b/library/network/http_client/src/spec/net/no_ssl/ssl_network_stream_socket.e
new file mode 100644
index 00000000..5a3264c7
--- /dev/null
+++ b/library/network/http_client/src/spec/net/no_ssl/ssl_network_stream_socket.e
@@ -0,0 +1,22 @@
+note
+ description: "[
+ A fake SSL network stream socket... when SSL is disabled at compilation time.
+ Its behavior is similar to NETWORK_STREAM_SOCKET.
+ ]"
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ SSL_NETWORK_STREAM_SOCKET
+
+inherit
+ NETWORK_STREAM_SOCKET
+
+create
+ make, make_empty, make_client_by_port, make_client_by_address_and_port, make_server_by_port, make_loopback_server_by_port
+
+create {SSL_NETWORK_STREAM_SOCKET}
+ make_from_descriptor_and_address, create_from_descriptor
+
+
+end
diff --git a/library/network/http_client/src/spec/net/ssl/net_http_client_info.e b/library/network/http_client/src/spec/net/ssl/net_http_client_info.e
new file mode 100644
index 00000000..0648f04d
--- /dev/null
+++ b/library/network/http_client/src/spec/net/ssl/net_http_client_info.e
@@ -0,0 +1,24 @@
+note
+ description: "Additional information related to NET HTTP Client.."
+ date: "$Date$"
+ revision: "$Revision$"
+
+class
+ NET_HTTP_CLIENT_INFO
+
+feature -- Access
+
+ has_https_support: BOOLEAN = True
+ -- Is HTTPS supported?
+
+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/tests/test-safe.ecf b/library/network/http_client/tests/test-safe.ecf
index 8d9005e2..75c48706 100644
--- a/library/network/http_client/tests/test-safe.ecf
+++ b/library/network/http_client/tests/test-safe.ecf
@@ -10,6 +10,8 @@
+
+
-
+
+
+ .*libcurl_.*.e$
+
+
+
+
+
+ .*net_.*.e$
+
+
+
+
+
diff --git a/library/network/http_client/tests/test.e b/library/network/http_client/tests/test.e
index 6829c245..f5020cb7 100644
--- a/library/network/http_client/tests/test.e
+++ b/library/network/http_client/tests/test.e
@@ -13,16 +13,40 @@ feature -- Init
if attached null.new_session ("http://example.com/") as l_sess then
check not l_sess.is_available end
end
+ test_get_with_authentication
test_http_client
end
+ test_get_with_authentication
+ local
+ cl: DEFAULT_HTTP_CLIENT
+ 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)
+ create cl
+ sess := cl.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 ("