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:
2015-09-15 23:45:41 +02:00
parent ff9a238f5c
commit 6c7637716b
14 changed files with 294 additions and 184 deletions

View File

@@ -40,11 +40,13 @@
</condition> </condition>
<cluster name="net_ssl_disabled" location="$|no_ssl"> <cluster name="net_ssl_disabled" location="$|no_ssl">
<condition> <condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" excluded_value="true"/> <custom name="netssl_http_client_enabled" excluded_value="true"/>
</condition> </condition>
</cluster> </cluster>
<cluster name="net_ssl_enabled" location="$|ssl"> <cluster name="net_ssl_enabled" location="$|ssl">
<condition> <condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
<custom name="netssl_http_client_enabled" value="true"/> <custom name="netssl_http_client_enabled" value="true"/>
</condition> </condition>
</cluster> </cluster>

View File

@@ -24,20 +24,63 @@
<custom name="net_http_client_disabled" excluded_value="true"/> <custom name="net_http_client_disabled" excluded_value="true"/>
</condition> </condition>
</library> </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"/> <library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
<cluster name="src" location=".\src\"> <cluster name="src" location=".\src\">
<cluster name="spec_null" location="$|spec/null" recursive="true"/> <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> <condition>
<custom name="net_http_client_disabled" excluded_value="true"/> <custom name="net_http_client_disabled" excluded_value="true"/>
</condition> </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>
<cluster name="spec_libcurl" location="$|spec/libcurl" recursive="true"> <cluster name="spec_libcurl" location="$|spec/libcurl" recursive="true">
<condition> <condition>
<custom name="libcurl_http_client_disabled" excluded_value="true"/> <custom name="libcurl_http_client_disabled" excluded_value="true"/>
</condition> </condition>
</cluster> </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> </cluster>
</target> </target>
</system> </system>

View File

@@ -1,6 +1,9 @@
note note
description: "[ 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$" author: "$Author$"
date: "$Date$" date: "$Date$"

View File

@@ -10,6 +10,24 @@ class
DEFAULT_HTTP_CLIENT DEFAULT_HTTP_CLIENT
inherit 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 end

View File

@@ -15,9 +15,9 @@ inherit
feature {NONE} -- Initialization feature {NONE} -- Initialization
make (a_url: READABLE_STRING_8; a_request_method: like request_method; a_session: like session; ctx: like context) 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 do
current_redirects := 0
request_method := a_request_method request_method := a_request_method
session := a_session session := a_session
initialize (a_url, ctx) initialize (a_url, ctx)
@@ -27,6 +27,9 @@ feature {NONE} -- Initialization
end end
initialize (a_url: READABLE_STRING_8; ctx: like context) 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 do
url := a_url url := a_url
headers := session.headers.twin headers := session.headers.twin
@@ -41,10 +44,11 @@ feature {NONE} -- Initialization
feature {NONE} -- Internal feature {NONE} -- Internal
session: HTTP_CLIENT_SESSION session: HTTP_CLIENT_SESSION
-- Session related to Current request.
-- It provides a few parameters related to session.
context: detachable HTTP_CLIENT_REQUEST_CONTEXT context: detachable HTTP_CLIENT_REQUEST_CONTEXT
-- Potential additional parameters for this specific request.
current_redirects: INTEGER_32
feature -- Status report feature -- Status report
@@ -60,11 +64,14 @@ feature -- Access
-- Request method associated with Current request. -- Request method associated with Current request.
url: READABLE_STRING_8 url: READABLE_STRING_8
-- URL associated with current request.
headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8] headers: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
-- Specific headers to be used for current request.
response: HTTP_CLIENT_RESPONSE 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! -- note: two consecutive calls will trigger two executions!
deferred deferred
ensure ensure
@@ -74,6 +81,7 @@ feature -- Access
feature {HTTP_CLIENT_SESSION} -- Execution feature {HTTP_CLIENT_SESSION} -- Execution
import (ctx: HTTP_CLIENT_REQUEST_CONTEXT) import (ctx: HTTP_CLIENT_REQUEST_CONTEXT)
-- Import `ctx' parameters.
local local
l_headers: like headers l_headers: like headers
do do
@@ -104,21 +112,26 @@ feature -- Authentication
end end
username: detachable READABLE_STRING_32 username: detachable READABLE_STRING_32
-- Username specified for the `session'.
do do
Result := session.username Result := session.username
end end
password: detachable READABLE_STRING_32 password: detachable READABLE_STRING_32
-- Password specified for the `session'.
do do
Result := session.password Result := session.password
end end
credentials: detachable READABLE_STRING_32 credentials: detachable READABLE_STRING_32
-- Credentials specified for the `session'.
--| Usually `username':`password'
do do
Result := session.credentials Result := session.credentials
end end
proxy: detachable TUPLE [host: READABLE_STRING_8; port: INTEGER] proxy: detachable TUPLE [host: READABLE_STRING_8; port: INTEGER]
-- Optional proxy settings.
do do
Result := session.proxy Result := session.proxy
end end

View File

@@ -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]] 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. -- 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 (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
-- Header entry value related to `a_name' -- Header entry value related to `a_name'
-- if multiple entries, just concatenate them using comma character -- if multiple entries, just concatenate them using comma character

View File

@@ -197,11 +197,15 @@ feature -- Authentication
auth_type_id: INTEGER auth_type_id: INTEGER
-- See {HTTP_CLIENT_CONSTANTS}.Auth_type_* -- See {HTTP_CLIENT_CONSTANTS}.Auth_type_*
username, username: detachable READABLE_STRING_32
-- Associated optional username value.
password: detachable READABLE_STRING_32 password: detachable READABLE_STRING_32
-- Associated optional password value.
credentials: detachable READABLE_STRING_32 credentials: detachable READABLE_STRING_32
-- Associated optional credentials value.
-- Computed as `username':`password'.
feature -- Status setting feature -- Status setting

View File

@@ -37,6 +37,7 @@ feature -- Status report
feature -- Custom feature -- Custom
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
local local
req: HTTP_CLIENT_REQUEST req: HTTP_CLIENT_REQUEST
do do
@@ -45,11 +46,13 @@ feature -- Custom
end 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 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 do
Result := impl_custom (a_method, a_path, a_ctx, data, Void) Result := impl_custom (a_method, a_path, a_ctx, data, Void)
end 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 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 do
Result := impl_custom (a_method, a_path, a_ctx, Void, fn) Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
end end
@@ -57,46 +60,55 @@ feature -- Custom
feature -- Helper feature -- Helper
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := custom ("GET", a_path, ctx) Result := custom ("GET", a_path, ctx)
end end
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := custom ("HEAD", a_path, ctx) Result := custom ("HEAD", a_path, ctx)
end end
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("POST", a_path, a_ctx, data, Void) Result := impl_custom ("POST", a_path, a_ctx, data, Void)
end end
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("POST", a_path, a_ctx, Void, fn) Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
end end
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void) Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
end end
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn) Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
end end
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("PUT", a_path, a_ctx, data, Void) Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
end end
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn) Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
end end
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := custom ("DELETE", a_path, ctx) Result := custom ("DELETE", a_path, ctx)
end end

View File

@@ -65,9 +65,6 @@ feature -- Access
l_upload_file: detachable RAW_FILE l_upload_file: detachable RAW_FILE
l_upload_filename: detachable READABLE_STRING_GENERAL l_upload_filename: detachable READABLE_STRING_GENERAL
l_form_string: STRING 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_prev_header: READABLE_STRING_8
l_boundary: READABLE_STRING_8 l_boundary: READABLE_STRING_8
l_is_http_1_0: BOOLEAN l_is_http_1_0: BOOLEAN
@@ -80,7 +77,6 @@ feature -- Access
end end
create Result.make (url) create Result.make (url)
-- Get URL data -- Get URL data
l_is_https := url.starts_with_general ("https://") l_is_https := url.starts_with_general ("https://")
create l_uri.make_from_string (url) create l_uri.make_from_string (url)
@@ -158,7 +154,6 @@ feature -- Access
if ctx.has_form_data then if ctx.has_form_data then
l_form_data := ctx.form_parameters 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 if l_upload_data = Void and l_upload_filename = Void then
-- Send as form-urlencoded -- Send as form-urlencoded
headers.extend ("application/x-www-form-urlencoded", "Content-Type") headers.extend ("application/x-www-form-urlencoded", "Content-Type")
@@ -166,57 +161,12 @@ feature -- Access
headers.force (l_upload_data.count.out, "Content-Length") headers.force (l_upload_data.count.out, "Content-Length")
else else
-- create form -- create form using multipart/form-data encoding
l_boundary := new_mime_boundary l_boundary := new_mime_boundary
headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type") headers.extend ("multipart/form-data; boundary=" + l_boundary, "Content-Type")
if l_form_data /= Void then if l_form_data /= Void then
headers.extend ("*/*", "Accept") headers.extend ("*/*", "Accept")
from l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary)
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") headers.extend (l_upload_data.count.out, "Content-Length")
end end
end end
@@ -236,7 +186,6 @@ feature -- Access
end end
check l_upload_file /= Void end check l_upload_file /= Void end
end end
end end
end end
@@ -358,16 +307,13 @@ feature -- Access
-- follow redirect -- follow redirect
if l_location /= Void then if l_location /= Void then
if current_redirects < max_redirects then if Result.redirections_count < max_redirects then
current_redirects := current_redirects + 1
initialize (l_location, ctx) initialize (l_location, ctx)
redirection_response := response redirection_response := response
redirection_response.add_redirection (Result.status_line, Result.raw_header, Result.body) redirection_response.add_redirection (Result.status_line, Result.raw_header, Result.body)
Result := redirection_response Result := redirection_response
end end
end end
current_redirects := current_redirects - 1
else else
Result.set_error_message ("Read Timeout") Result.set_error_message ("Read Timeout")
end end
@@ -388,6 +334,78 @@ feature -- Access
feature {NONE} -- Helpers 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_file_content_to_socket (a_file: FILE; a_len: INTEGER; a_output: NETWORK_STREAM_SOCKET)
-- Append `a_file' content to `a_output'. -- Append `a_file' content to `a_output'.
-- If `a_len' >= 0 then read only `a_len' characters. -- If `a_len' >= 0 then read only `a_len' characters.
@@ -488,8 +506,7 @@ feature {NONE} -- Helpers
local local
s: STRING_8 s: STRING_8
r: INTEGER -- remaining count r: INTEGER -- remaining count
n,pos, l_chunk_size, l_count: INTEGER n,l_chunk_size, l_count: INTEGER
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
do do
if a_socket.readable then if a_socket.readable then
if a_len >= 0 then if a_len >= 0 then
@@ -519,77 +536,7 @@ feature {NONE} -- Helpers
end end
check full_content_read: not a_response.error_occurred implies 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 elseif attached a_response.header ("Transfer-Encoding") as l_enc and then l_enc.is_case_insensitive_equal ("chunked") then
debug ("socket_content") append_socket_chunked_content_to (a_response, a_socket, a_output)
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
else else
-- No Content-Length and no chunked transfer encoding! -- No Content-Length and no chunked transfer encoding!
-- maybe HTTP/1.0 ? -- maybe HTTP/1.0 ?
@@ -615,6 +562,91 @@ feature {NONE} -- Helpers
end end
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: STRING
-- New MIME boundary. -- New MIME boundary.
do do

View File

@@ -36,6 +36,7 @@ feature -- Status report
feature -- Custom feature -- Custom
custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
local local
req: HTTP_CLIENT_REQUEST req: HTTP_CLIENT_REQUEST
do do
@@ -44,11 +45,13 @@ feature -- Custom
end 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 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 do
Result := impl_custom (a_method, a_path, a_ctx, data, Void) Result := impl_custom (a_method, a_path, a_ctx, data, Void)
end 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 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 do
Result := impl_custom (a_method, a_path, a_ctx, Void, fn) Result := impl_custom (a_method, a_path, a_ctx, Void, fn)
end end
@@ -56,46 +59,55 @@ feature -- Custom
feature -- Helper feature -- Helper
get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := custom ("GET", a_path, ctx) Result := custom ("GET", a_path, ctx)
end end
head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := custom ("HEAD", a_path, ctx) Result := custom ("HEAD", a_path, ctx)
end end
post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("POST", a_path, a_ctx, data, Void) Result := impl_custom ("POST", a_path, a_ctx, data, Void)
end end
post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE post_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("POST", a_path, a_ctx, Void, fn) Result := impl_custom ("POST", a_path, a_ctx, Void, fn)
end end
patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE patch (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("PATCH", a_path, a_ctx, data, Void) Result := impl_custom ("PATCH", a_path, a_ctx, data, Void)
end end
patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE patch_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn) Result := impl_custom ("PATCH", a_path, a_ctx, Void, fn)
end end
put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE put (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("PUT", a_path, a_ctx, data, Void) Result := impl_custom ("PUT", a_path, a_ctx, data, Void)
end end
put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := impl_custom ("PUT", a_path, a_ctx, Void, fn) Result := impl_custom ("PUT", a_path, a_ctx, Void, fn)
end end
delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE
-- <Precursor>
do do
Result := custom ("DELETE", a_path, ctx) Result := custom ("DELETE", a_path, ctx)
end end

View File

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

View File

@@ -1,11 +1,7 @@
note note
description : "[ description : "[
HTTP_CLIENT_SESSION represents a session NULL version of HTTP_CLIENT_SESSION.
and is used to call get, post, .... request It is used if no implementation is available (libcurl or net)
with predefined settings such as
base_url
specific common headers
timeout and so on ...
]" ]"
date: "$Date$" date: "$Date$"
revision: "$Revision$" revision: "$Revision$"

View File

@@ -11,6 +11,7 @@
<assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/> <assertions precondition="true" postcondition="true" check="true" invariant="true" loop="true" supplier_precondition="true"/>
</option> </option>
<variable name="netssl_http_client_enabled" value="false"/> <variable name="netssl_http_client_enabled" value="false"/>
<variable name="net_http_client_disabled" value="false"/>
<variable name="libcurl_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="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="http_client" location="..\http_client-safe.ecf" readonly="false" use_application_options="true"> <library name="http_client" location="..\http_client-safe.ecf" readonly="false" use_application_options="true">

View File

@@ -7,12 +7,11 @@ feature -- Init
make make
local local
null: NULL_HTTP_CLIENT null: NULL_HTTP_CLIENT_SESSION
do do
create null create null.make ("http://example.com/")
if attached null.new_session ("http://example.com/") as l_sess then check not null.is_available end
check not l_sess.is_available end
end
test_get_with_authentication test_get_with_authentication
test_http_client test_http_client
end end