Updated a few comments
Removed useless NULL_HTTP_CLIENT. Extracted mime code from NET_HTTP_CLIENT_REQUEST.response into specific routine.
This commit is contained in:
@@ -40,11 +40,13 @@
|
||||
</condition>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
|
||||
@@ -24,20 +24,63 @@
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</library>
|
||||
|
||||
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
|
||||
<cluster name="src" location=".\src\">
|
||||
<cluster name="spec_null" location="$|spec/null" recursive="true"/>
|
||||
<cluster name="spec_net" location="$|spec/socket" recursive="true">
|
||||
<cluster name="spec_net" location="$|spec/net">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
<cluster name="net_ssl_disabled" location="$|no_ssl">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="net_ssl_enabled" location="$|ssl">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="netssl_http_client_enabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
<cluster name="spec_libcurl" location="$|spec/libcurl" recursive="true">
|
||||
<condition>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_null" location="$|default/null">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_net" location="$|default/net">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_libcurl" location="$|default/libcurl">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
<cluster name="default_libcurl_or_net" location="$|default/libcurl_or_net">
|
||||
<condition>
|
||||
<custom name="net_http_client_disabled" excluded_value="true"/>
|
||||
<custom name="libcurl_http_client_disabled" excluded_value="true"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</cluster>
|
||||
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
note
|
||||
description: "[
|
||||
Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT.
|
||||
Default HTTP_CLIENT based on LIBCURL_HTTP_CLIENT or NET_HTTP_CLIENT.
|
||||
|
||||
The preference goes to libcurl implementation for now,
|
||||
since the net implementation has currently less functionalities.
|
||||
]"
|
||||
author: "$Author$"
|
||||
date: "$Date$"
|
||||
|
||||
@@ -10,6 +10,24 @@ class
|
||||
DEFAULT_HTTP_CLIENT
|
||||
|
||||
inherit
|
||||
NULL_HTTP_CLIENT
|
||||
HTTP_CLIENT
|
||||
|
||||
feature -- Access
|
||||
|
||||
new_session (a_base_url: READABLE_STRING_8): HTTP_CLIENT_SESSION
|
||||
-- Create a new session using `a_base_url'.
|
||||
do
|
||||
create {NULL_HTTP_CLIENT_SESSION} 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
|
||||
|
||||
@@ -15,9 +15,9 @@ inherit
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context)
|
||||
-- Initialize `Current'.
|
||||
-- Initialize `Current' with request url `a_url', method `a_request_method' within the session `a_session'
|
||||
-- and optional context `ctx' which can be used to pass additional parameters.
|
||||
do
|
||||
current_redirects := 0
|
||||
request_method := a_request_method
|
||||
session := a_session
|
||||
initialize (a_url, ctx)
|
||||
@@ -27,6 +27,9 @@ feature {NONE} -- Initialization
|
||||
end
|
||||
|
||||
initialize (a_url: READABLE_STRING_8; ctx: like context)
|
||||
-- Initialize Current with `a_url' and `ctx'.
|
||||
-- This can be used to reset/reinitialize Current with new url
|
||||
-- in the case of redirection.
|
||||
do
|
||||
url := a_url
|
||||
headers := session.headers.twin
|
||||
@@ -41,10 +44,11 @@ feature {NONE} -- Initialization
|
||||
feature {NONE} -- Internal
|
||||
|
||||
session: HTTP_CLIENT_SESSION
|
||||
-- Session related to Current request.
|
||||
-- It provides a few parameters related to session.
|
||||
|
||||
context: detachable HTTP_CLIENT_REQUEST_CONTEXT
|
||||
|
||||
current_redirects: INTEGER_32
|
||||
-- Potential additional parameters for this specific request.
|
||||
|
||||
feature -- Status report
|
||||
|
||||
@@ -60,11 +64,14 @@ feature -- Access
|
||||
-- Request method associated with Current request.
|
||||
|
||||
url: READABLE_STRING_8
|
||||
-- URL associated with current request.
|
||||
|
||||
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
||||
-- Specific headers to be used for current request.
|
||||
|
||||
response: HTTP_CLIENT_RESPONSE
|
||||
-- Execute the request and return the response.
|
||||
-- Response received from request execution.
|
||||
-- Check `error_occurred' for eventual error.
|
||||
-- note: two consecutive calls will trigger two executions!
|
||||
deferred
|
||||
ensure
|
||||
@@ -74,6 +81,7 @@ feature -- Access
|
||||
feature {HTTP_CLIENT_SESSION} -- Execution
|
||||
|
||||
import (ctx: HTTP_CLIENT_REQUEST_CONTEXT)
|
||||
-- Import `ctx' parameters.
|
||||
local
|
||||
l_headers: like headers
|
||||
do
|
||||
@@ -104,21 +112,26 @@ feature -- Authentication
|
||||
end
|
||||
|
||||
username: detachable READABLE_STRING_32
|
||||
-- Username specified for the `session'.
|
||||
do
|
||||
Result := session.username
|
||||
end
|
||||
|
||||
password: detachable READABLE_STRING_32
|
||||
-- Password specified for the `session'.
|
||||
do
|
||||
Result := session.password
|
||||
end
|
||||
|
||||
credentials: detachable READABLE_STRING_32
|
||||
-- Credentials specified for the `session'.
|
||||
--| Usually `username':`password'
|
||||
do
|
||||
Result := session.credentials
|
||||
end
|
||||
|
||||
proxy: detachable TUPLE [host: READABLE_STRING_8; port: INTEGER]
|
||||
-- Optional proxy settings.
|
||||
do
|
||||
Result := session.proxy
|
||||
end
|
||||
|
||||
@@ -63,6 +63,14 @@ feature -- Access
|
||||
redirections: detachable ARRAYED_LIST [TUPLE [status_line: detachable READABLE_STRING_8; raw_header: READABLE_STRING_8; body: detachable READABLE_STRING_8]]
|
||||
-- Header of previous redirection if any.
|
||||
|
||||
redirections_count: INTEGER
|
||||
-- Number of redirections.
|
||||
do
|
||||
if attached redirections as lst then
|
||||
Result := lst.count
|
||||
end
|
||||
end
|
||||
|
||||
header (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||
-- Header entry value related to `a_name'
|
||||
-- if multiple entries, just concatenate them using comma character
|
||||
|
||||
@@ -197,11 +197,15 @@ feature -- Authentication
|
||||
auth_type_id: INTEGER
|
||||
-- See {HTTP_CLIENT_CONSTANTS}.Auth_type_*
|
||||
|
||||
username,
|
||||
username: detachable READABLE_STRING_32
|
||||
-- Associated optional username value.
|
||||
|
||||
password: detachable READABLE_STRING_32
|
||||
-- Associated optional password value.
|
||||
|
||||
credentials: detachable READABLE_STRING_32
|
||||
|
||||
-- Associated optional credentials value.
|
||||
-- Computed as `username':`password'.
|
||||
|
||||
feature -- Status setting
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ feature -- Status report
|
||||
feature -- Custom
|
||||
|
||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
local
|
||||
req: HTTP_CLIENT_REQUEST
|
||||
do
|
||||
@@ -45,11 +46,13 @@ feature -- Custom
|
||||
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
|
||||
-- Same as `custom' but including upload data `a_data'.
|
||||
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
|
||||
-- Same as `custom' but including upload file `fn'.
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||
end
|
||||
@@ -57,46 +60,55 @@ feature -- Custom
|
||||
feature -- Helper
|
||||
|
||||
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("GET", a_path, ctx)
|
||||
end
|
||||
|
||||
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("DELETE", a_path, ctx)
|
||||
end
|
||||
|
||||
@@ -65,9 +65,6 @@ feature -- Access
|
||||
l_upload_file: detachable RAW_FILE
|
||||
l_upload_filename: detachable READABLE_STRING_GENERAL
|
||||
l_form_string: STRING
|
||||
l_mime_type_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING
|
||||
l_mime_type: STRING
|
||||
l_fn_extension: READABLE_STRING_GENERAL
|
||||
l_prev_header: READABLE_STRING_8
|
||||
l_boundary: READABLE_STRING_8
|
||||
l_is_http_1_0: BOOLEAN
|
||||
@@ -80,7 +77,6 @@ feature -- Access
|
||||
end
|
||||
create Result.make (url)
|
||||
|
||||
|
||||
-- Get URL data
|
||||
l_is_https := url.starts_with_general ("https://")
|
||||
create l_uri.make_from_string (url)
|
||||
@@ -158,7 +154,6 @@ feature -- Access
|
||||
|
||||
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")
|
||||
@@ -166,57 +161,12 @@ feature -- Access
|
||||
headers.force (l_upload_data.count.out, "Content-Length")
|
||||
|
||||
else
|
||||
-- create form
|
||||
-- create form using multipart/form-data encoding
|
||||
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
|
||||
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
|
||||
headers.extend (l_upload_data.count.out, "Content-Length")
|
||||
end
|
||||
end
|
||||
@@ -236,7 +186,6 @@ feature -- Access
|
||||
end
|
||||
check l_upload_file /= Void end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -358,16 +307,13 @@ feature -- Access
|
||||
|
||||
-- follow redirect
|
||||
if l_location /= Void then
|
||||
if current_redirects < max_redirects then
|
||||
current_redirects := current_redirects + 1
|
||||
if Result.redirections_count < max_redirects then
|
||||
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
|
||||
@@ -388,6 +334,78 @@ feature -- Access
|
||||
|
||||
feature {NONE} -- Helpers
|
||||
|
||||
form_date_and_uploaded_files_to_mime_string (a_form_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_32]; a_upload_filename: detachable READABLE_STRING_GENERAL; a_mime_boundary: READABLE_STRING_8): STRING
|
||||
local
|
||||
l_path: PATH
|
||||
l_mime_type: READABLE_STRING_8
|
||||
l_upload_file: detachable RAW_FILE
|
||||
l_mime_type_mapping: HTTP_FILE_EXTENSION_MIME_MAPPING
|
||||
do
|
||||
create Result.make (100)
|
||||
across
|
||||
a_form_parameters as ic
|
||||
loop
|
||||
Result.append (a_mime_boundary)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append ("Content-Disposition: form-data; name=")
|
||||
Result.append_character ('%"')
|
||||
Result.append (string_to_mime_encoded_string (ic.key))
|
||||
Result.append_character ('%"')
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append (string_to_mime_encoded_string (ic.item))
|
||||
Result.append (http_end_of_header_line)
|
||||
end
|
||||
|
||||
if a_upload_filename /= Void then
|
||||
-- get file extension, otherwise set default
|
||||
create l_mime_type_mapping.make_default
|
||||
create l_path.make_from_string (a_upload_filename)
|
||||
if
|
||||
attached l_path.extension as ext and then
|
||||
attached l_mime_type_mapping.mime_type (ext) as l_mt
|
||||
then
|
||||
l_mime_type := l_mt
|
||||
else
|
||||
l_mime_type := "application/octet-stream"
|
||||
end
|
||||
|
||||
Result.append (a_mime_boundary)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append ("Content-Disposition: form-data; name=%"")
|
||||
Result.append (string_to_mime_encoded_string (a_upload_filename))
|
||||
Result.append_character ('%"')
|
||||
Result.append ("; filename=%"")
|
||||
Result.append (string_to_mime_encoded_string (a_upload_filename))
|
||||
Result.append_character ('%"')
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append ("Content-Type: ")
|
||||
Result.append (l_mime_type)
|
||||
Result.append (http_end_of_header_line)
|
||||
Result.append (http_end_of_header_line)
|
||||
|
||||
create l_upload_file.make_with_path (l_path)
|
||||
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, Result)
|
||||
-- Reset l_upload_file to Void, since the related content is already processed.
|
||||
l_upload_file := Void
|
||||
end
|
||||
Result.append (http_end_of_header_line)
|
||||
end
|
||||
Result.append (a_mime_boundary)
|
||||
Result.append ("--") --| end
|
||||
end
|
||||
|
||||
string_to_mime_encoded_string (s: READABLE_STRING_GENERAL): STRING
|
||||
-- Encoded unicode string for mime value.
|
||||
-- For instance uploaded filename, or form data key or values.
|
||||
local
|
||||
utf: UTF_CONVERTER
|
||||
do
|
||||
-- FIXME: find the proper encoding!
|
||||
Result := utf.utf_32_string_to_utf_8_string_8 (s)
|
||||
end
|
||||
|
||||
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.
|
||||
@@ -488,8 +506,7 @@ feature {NONE} -- Helpers
|
||||
local
|
||||
s: STRING_8
|
||||
r: INTEGER -- remaining count
|
||||
n,pos, l_chunk_size, l_count: INTEGER
|
||||
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
|
||||
n,l_chunk_size, l_count: INTEGER
|
||||
do
|
||||
if a_socket.readable then
|
||||
if a_len >= 0 then
|
||||
@@ -519,77 +536,7 @@ feature {NONE} -- Helpers
|
||||
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
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
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 (r)
|
||||
l_count := l_count + a_socket.bytes_read
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
end
|
||||
append_socket_chunked_content_to (a_response, a_socket, a_output)
|
||||
else
|
||||
-- No Content-Length and no chunked transfer encoding!
|
||||
-- maybe HTTP/1.0 ?
|
||||
@@ -615,6 +562,91 @@ feature {NONE} -- Helpers
|
||||
end
|
||||
end
|
||||
|
||||
append_socket_chunked_content_to (a_response: HTTP_CLIENT_RESPONSE; a_socket: NETWORK_STREAM_SOCKET; a_output: STRING)
|
||||
-- Get chunked content from `a_socket' and append it to `a_output'.
|
||||
require
|
||||
socket_readable: a_socket.readable
|
||||
has_chunked_transfer_encoding: attached a_response.header ("Transfer-Encoding") as l_enc and then
|
||||
l_enc.is_case_insensitive_equal ("chunked")
|
||||
local
|
||||
s: STRING_8
|
||||
r: INTEGER -- remaining count
|
||||
n,pos, l_count: INTEGER
|
||||
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
|
||||
do
|
||||
debug ("socket_content")
|
||||
io.error.put_string ("Chunked encoding%N")
|
||||
end
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
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 (r)
|
||||
l_count := l_count + a_socket.bytes_read
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
new_mime_boundary: STRING
|
||||
-- New MIME boundary.
|
||||
do
|
||||
|
||||
@@ -36,6 +36,7 @@ feature -- Status report
|
||||
feature -- Custom
|
||||
|
||||
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
local
|
||||
req: HTTP_CLIENT_REQUEST
|
||||
do
|
||||
@@ -44,11 +45,13 @@ feature -- Custom
|
||||
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
|
||||
-- Same as `custom' but including upload data `a_data'.
|
||||
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
|
||||
-- Same as `custom' but including upload file `fn'.
|
||||
do
|
||||
Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
|
||||
end
|
||||
@@ -56,46 +59,55 @@ feature -- Custom
|
||||
feature -- Helper
|
||||
|
||||
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("GET", a_path, ctx)
|
||||
end
|
||||
|
||||
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
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
|
||||
-- <Precursor>
|
||||
do
|
||||
Result := custom ("DELETE", a_path, ctx)
|
||||
end
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
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
|
||||
@@ -1,11 +1,7 @@
|
||||
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 ...
|
||||
NULL version of HTTP_CLIENT_SESSION.
|
||||
It is used if no implementation is available (libcurl or net)
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<variable name="netssl_http_client_enabled" value="false"/>
|
||||
<variable name="net_http_client_disabled" value="false"/>
|
||||
<variable name="libcurl_http_client_disabled" value="false"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="http_client" location="..\http_client-safe.ecf" readonly="false" use_application_options="true">
|
||||
|
||||
@@ -7,12 +7,11 @@ feature -- Init
|
||||
|
||||
make
|
||||
local
|
||||
null: NULL_HTTP_CLIENT
|
||||
null: NULL_HTTP_CLIENT_SESSION
|
||||
do
|
||||
create null
|
||||
if attached null.new_session ("http://example.com/") as l_sess then
|
||||
check not l_sess.is_available end
|
||||
end
|
||||
create null.make ("http://example.com/")
|
||||
check not null.is_available end
|
||||
|
||||
test_get_with_authentication
|
||||
test_http_client
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user