Fixed timeout issue due to too many "ready_for_reading".

Fixed Connection behavior.
Fixed Content-Type settings.
Removed condition on POST or PUT, since code also applied to any request methods.
Added verbose output implementation.
This commit is contained in:
2015-09-17 22:48:04 +02:00
parent b69b8aaaf9
commit b64a281d75
6 changed files with 198 additions and 114 deletions

View File

@@ -18,11 +18,7 @@
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/> <library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
<library name="http" location="..\protocol\http\http-safe.ecf"/> <library name="http" location="..\protocol\http\http-safe.ecf"/>
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/> <library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization-safe.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"> <library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf"/>
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
</condition>
</library>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf"> <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl-safe.ecf">
<condition> <condition>
<custom name="net_http_client_disabled" excluded_value="true"/> <custom name="net_http_client_disabled" excluded_value="true"/>

View File

@@ -18,11 +18,7 @@
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/> <library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
<library name="http" location="..\protocol\http\http.ecf"/> <library name="http" location="..\protocol\http\http.ecf"/>
<library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/> <library name="http_auth" location="..\..\server\authentication\http_authorization\http_authorization.ecf"/>
<library name="net" location="$ISE_LIBRARY\library\net\net.ecf"> <library name="net" location="$ISE_LIBRARY\library\net\net.ecf"/>
<condition>
<custom name="net_http_client_disabled" excluded_value="true"/>
</condition>
</library>
<library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf"> <library name="net_ssl" location="$ISE_LIBRARY\unstable\library\network\socket\netssl\net_ssl.ecf">
<condition> <condition>
<custom name="net_http_client_disabled" excluded_value="true"/> <custom name="net_http_client_disabled" excluded_value="true"/>

View File

@@ -85,6 +85,61 @@ feature -- Access
end end
end end
feature {NONE} -- Access: verbose
verbose_mode: INTEGER
-- Internal verbose mode.
verbose_header_sent_mode: INTEGER = 1 --| 0001
verbose_header_received_mode: INTEGER = 2 --| 0010
verbose_debug_mode: INTEGER = 4 --| 0100
feature -- Access: verbose
is_header_sent_verbose: BOOLEAN
do
Result := verbose_mode & verbose_header_sent_mode = verbose_header_sent_mode
end
is_header_received_verbose: BOOLEAN
do
Result := verbose_mode & verbose_header_received_mode = verbose_header_received_mode
end
is_debug_verbose: BOOLEAN
do
Result := verbose_mode & verbose_debug_mode = verbose_debug_mode
end
feature -- Element change: verbose
set_header_sent_verbose (b: BOOLEAN)
do
if b then
verbose_mode := verbose_mode | verbose_header_sent_mode
else
verbose_mode := verbose_mode & verbose_header_sent_mode.bit_not
end
end
set_header_received_verbose (b: BOOLEAN)
do
if b then
verbose_mode := verbose_mode | verbose_header_received_mode
else
verbose_mode := verbose_mode & verbose_header_received_mode.bit_not
end
end
set_debug_verbose (b: BOOLEAN)
do
if b then
verbose_mode := verbose_mode | verbose_debug_mode
else
verbose_mode := verbose_mode & verbose_debug_mode.bit_not
end
end
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

View File

@@ -62,6 +62,7 @@ feature -- Execution
l_upload_filename: detachable READABLE_STRING_GENERAL l_upload_filename: detachable READABLE_STRING_GENERAL
l_headers: like headers l_headers: like headers
l_is_http_1_0: BOOLEAN l_is_http_1_0: BOOLEAN
l_uri: URI
do do
if not retried then if not retried then
curl := session.curl curl := session.curl
@@ -84,6 +85,38 @@ feature -- Execution
append_parameters_to_url (ctx.query_parameters, l_url) append_parameters_to_url (ctx.query_parameters, l_url)
end end
if session.is_header_sent_verbose then
io.error.put_string ("> Sending:%N")
create l_uri.make_from_string (l_url)
io.error.put_string ("> ")
io.error.put_string (request_method + " " + l_uri.path)
if attached l_uri.query as q then
io.error.put_string (q)
end
if l_is_http_1_0 then
io.error.put_string (" HTTP/1.0")
else
io.error.put_string (" HTTP/1.1")
end
io.error.put_new_line
if attached l_uri.host as l_host then
io.error.put_string ("> ")
io.error.put_string ("Host: " + l_host)
io.error.put_new_line
end
across
headers as ic
loop
io.error.put_string ("> ")
io.error.put_string (ic.key)
io.error.put_string (": ")
io.error.put_string (ic.item)
io.error.put_new_line
end
io.error.put_string ("> ... ")
io.error.put_new_line
end
debug ("service") debug ("service")
io.put_string ("SERVICE: " + l_url) io.put_string ("SERVICE: " + l_url)
io.put_new_line io.put_new_line
@@ -248,6 +281,10 @@ feature -- Execution
Result.status := response_status_code (curl_easy, curl_handle) Result.status := response_status_code (curl_easy, curl_handle)
if l_curl_string /= Void then if l_curl_string /= Void then
Result.set_response_message (l_curl_string.string, ctx) Result.set_response_message (l_curl_string.string, ctx)
if session.is_header_received_verbose then
io.error.put_string ("< Receiving:%N")
io.error.put_string (Result.raw_header)
end
end end
else else
Result.set_error_message ("Error: cURL Error[" + l_result.out + "]") Result.set_error_message ("Error: cURL Error[" + l_result.out + "]")

View File

@@ -127,6 +127,13 @@ feature -- Access
l_host := l_url.host l_host := l_url.host
end end
if attached session.proxy as l_proxy_settings then
-- For now, so proxy support.
check
not_supported: False
end
end
-- Connect -- Connect
l_socket := session_socket (l_host, l_port, l_is_https, ctx) l_socket := session_socket (l_host, l_port, l_is_https, ctx)
if l_socket.is_connected then if l_socket.is_connected then
@@ -194,27 +201,23 @@ feature -- Access
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")
l_upload_data := form_date_and_uploaded_files_to_mime_string (l_form_data, l_upload_filename, l_boundary) 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") headers.extend (l_upload_data.count.out, "Content-Length")
end end
end end
elseif elseif l_upload_data /= Void then
request_method.is_case_insensitive_equal ("POST") check ctx.has_upload_data end
or else request_method.is_case_insensitive_equal ("PUT") if not headers.has ("Content-Type") then
then
if l_upload_data /= Void then
check ctx.has_upload_data end
headers.extend ("application/x-www-form-urlencoded", "Content-Type") 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
headers.extend (l_upload_data.count.out, "Content-Length")
elseif l_upload_filename /= Void then
check ctx.has_upload_filename end
create l_upload_file.make_with_name (l_upload_filename)
if l_upload_file.exists and then l_upload_file.readable then
headers.extend (l_upload_file.count.out, "Content-Length")
end
check l_upload_file /= Void end
end end
end end
@@ -242,8 +245,12 @@ feature -- Access
s.append (l_host) s.append (l_host)
end end
s.append (http_end_of_header_line) s.append (http_end_of_header_line)
-- s.append ("Connection: close") if not headers.has ("Connection") then
-- s.append (http_end_of_header_line) if l_is_http_1_0_request then
s.append ("Connection: keep-alive")
s.append (http_end_of_header_line)
end
end
-- Append the given request headers -- Append the given request headers
l_cookie := Void l_cookie := Void
@@ -279,14 +286,14 @@ feature -- Access
s.append (http_end_of_header_line) s.append (http_end_of_header_line)
end end
--| End of client header.
s.append (Http_end_of_header_line)
if l_upload_data /= Void then if l_upload_data /= Void then
s.append (l_upload_data) s.append (l_upload_data)
s.append (http_end_of_header_line) s.append (http_end_of_header_line)
end end
--| End of client header.
s.append (Http_end_of_header_line)
--| Note that any remaining file to upload will be done directly via the socket --| Note that any remaining file to upload will be done directly via the socket
--| to optimize memory usage --| to optimize memory usage
@@ -302,8 +309,9 @@ feature -- Access
--| Send request |-- --| Send request |--
--|-------------------------|-- --|-------------------------|--
debug ("socket_header") if session.is_header_sent_verbose then
io.error.put_string (s) log ("> Sending:%N")
log (s)
end end
l_socket.put_string (s) l_socket.put_string (s)
--| Send remaining payload data, if needed. --| Send remaining payload data, if needed.
@@ -316,9 +324,13 @@ feature -- Access
--| Get response. |-- --| Get response. |--
--| Get header message |-- --| Get header message |--
--|-------------------------|-- --|-------------------------|--
if l_socket.ready_for_reading then if is_ready_for_reading (l_socket) then
create l_message.make_empty create l_message.make_empty
append_socket_header_content_to (Result, l_socket, l_message) append_socket_header_content_to (Result, l_socket, l_message)
if session.is_header_received_verbose then
log ("< Receiving:%N")
log (l_message)
end
l_prev_header := Result.raw_header l_prev_header := Result.raw_header
Result.set_raw_header (l_message.string) Result.set_raw_header (l_message.string)
l_message.append (http_end_of_header_line) l_message.append (http_end_of_header_line)
@@ -372,12 +384,21 @@ feature -- Access
end end
end end
else else
if session.is_debug_verbose then
log ("Debug: Read Timeout!%N")
end
Result.set_error_message ("Read Timeout") Result.set_error_message ("Read Timeout")
end end
else else
if session.is_debug_verbose then
log ("Debug: Write Timeout!%N")
end
Result.set_error_message ("Write Timeout") Result.set_error_message ("Write Timeout")
end end
else else
if session.is_debug_verbose then
log ("Debug: Could not connect!%N")
end
Result.set_error_message ("Could not connect") Result.set_error_message ("Could not connect")
end end
else else
@@ -391,7 +412,21 @@ feature -- Access
feature {NONE} -- Helpers feature {NONE} -- Helpers
log (m: READABLE_STRING_8)
-- Output log messages.
do
io.error.put_string (m)
end
is_ready_for_reading (a_socket: NETWORK_STREAM_SOCKET): BOOLEAN
-- Is `a_socket' ready for reading?
do
Result := a_socket.ready_for_reading
end
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 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
-- Form data and uploaded files converted to mime string.
-- TODO: design a proper MIME... component.
local local
l_path: PATH l_path: PATH
l_mime_type: READABLE_STRING_8 l_mime_type: READABLE_STRING_8
@@ -544,28 +579,18 @@ feature {NONE} -- Helpers
until until
s.same_string ("%R") or not a_socket.readable or a_response.error_occurred s.same_string ("%R") or not a_socket.readable or a_response.error_occurred
loop loop
if a_socket.ready_for_reading then a_socket.read_line_thread_aware
a_socket.read_line_thread_aware s := a_socket.last_string
s := a_socket.last_string if s.is_empty then
debug ("socket_header") if session.is_debug_verbose then
io.error.put_string ("header-line: " + s + "%N") log ("Debug: ERROR: zero byte read when receiving header.%N")
end
if s.is_empty then
debug ("socket_header")
io.error.put_string ("ERROR: zero byte read when receiving header.%N")
end
a_response.set_error_message ("Read zero byte, expecting header line")
elseif s.same_string ("%R") then
-- Reach end of header
else
a_output.append (s)
a_output.append_character ('%N')
end end
a_response.set_error_message ("Read zero byte, expecting header line")
elseif s.same_string ("%R") then
-- Reach end of header
else else
debug ("socket_header") a_output.append (s)
io.error.put_string ("ERROR: timeout when receiving header.%N") a_output.append_character ('%N')
end
a_response.set_error_message ("Could not read header data, timeout")
end end
end end
end end
@@ -581,29 +606,22 @@ feature {NONE} -- Helpers
do do
if a_socket.readable then if a_socket.readable then
if a_len >= 0 then if a_len >= 0 then
debug ("socket_content") if session.is_debug_verbose then
io.error.put_string ("Content-Length="+ a_len.out +"%N") log ("Debug: Content-Length="+ a_len.out +"%N")
end end
from from
r := a_len r := a_len
until until
r = 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 loop
if a_socket.ready_for_reading then a_socket.read_stream_thread_aware (r)
a_socket.read_stream_thread_aware (r) l_count := l_count + a_socket.bytes_read
l_count := l_count + a_socket.bytes_read if session.is_debug_verbose then
debug ("socket_content") log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
io.error.put_string (" - byte read=" + a_socket.bytes_read.out + "%N") log ("Debug: - current count=" + l_count.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
r := r - a_socket.bytes_read
a_output.append (a_socket.last_string)
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
@@ -619,15 +637,11 @@ feature {NONE} -- Helpers
until until
n < l_chunk_size or not a_socket.readable n < l_chunk_size or not a_socket.readable
loop loop
if a_socket.ready_for_reading then a_socket.read_stream_thread_aware (l_chunk_size)
a_socket.read_stream_thread_aware (l_chunk_size) s := a_socket.last_string
s := a_socket.last_string n := a_socket.bytes_read
n := a_socket.bytes_read l_count := l_count + n
l_count := l_count + n a_output.append (s)
a_output.append (s)
else
a_response.set_error_message ("Could not read data, timeout")
end
end end
end end
end end
@@ -645,8 +659,8 @@ feature {NONE} -- Helpers
n,pos, l_count: INTEGER n,pos, l_count: INTEGER
hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER hexa2int: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER
do do
debug ("socket_content") if session.is_debug_verbose then
io.error.put_string ("Chunked encoding%N") log ("Debug: Chunked encoding%N")
end end
from from
create hexa2int.make create hexa2int.make
@@ -657,8 +671,8 @@ feature {NONE} -- Helpers
a_socket.read_line_thread_aware -- Read chunk info a_socket.read_line_thread_aware -- Read chunk info
s := a_socket.last_string s := a_socket.last_string
s.right_adjust s.right_adjust
debug ("socket_content") if session.is_debug_verbose then
io.error.put_string (" - chunk info='" + s + "'%N") log ("Debug: - chunk info='" + s + "'%N")
end end
pos := s.index_of (';', 1) pos := s.index_of (';', 1)
if pos > 0 then if pos > 0 then
@@ -674,8 +688,8 @@ feature {NONE} -- Helpers
n := 0 n := 0
end end
end end
debug ("socket_content") if session.is_debug_verbose then
io.error.put_string (" - chunk size=" + n.out + "%N") log ("Debug: - chunk size=" + n.out + "%N")
end end
if n > 0 then if n > 0 then
from from
@@ -683,36 +697,22 @@ feature {NONE} -- Helpers
until until
r = 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 loop
if a_socket.ready_for_reading then a_socket.read_stream_thread_aware (r)
a_socket.read_stream_thread_aware (r) l_count := l_count + a_socket.bytes_read
l_count := l_count + a_socket.bytes_read if session.is_debug_verbose then
debug ("socket_content") log ("Debug: - byte read=" + a_socket.bytes_read.out + "%N")
io.error.put_string (" - byte read=" + a_socket.bytes_read.out + "%N") log ("Debug: - current count=" + l_count.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
r := r - a_socket.bytes_read
a_output.append (a_socket.last_string)
end end
if a_socket.ready_for_reading then a_socket.read_character
a_socket.read_character check a_socket.last_character = '%R' end
check a_socket.last_character = '%R' end a_socket.read_character
a_socket.read_character check a_socket.last_character = '%N' end
check a_socket.last_character = '%N' end if session.is_debug_verbose then
debug ("socket_content") log ("Debug: - Found CRNL %N")
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
end end

View File

@@ -36,12 +36,12 @@ feature -- Status report
end end
end end
feature -- Access feature -- Access: persistent connection
persistent_connection: detachable NET_HTTP_CLIENT_CONNECTION persistent_connection: detachable NET_HTTP_CLIENT_CONNECTION
-- Socket used for persistent connection purpose. -- Socket used for persistent connection purpose.
feature -- Element change feature -- Element change: persistent connection
set_persistent_connection (a_connection: like persistent_connection) set_persistent_connection (a_connection: like persistent_connection)
-- Set `persistent_connection' to `a_connection'. -- Set `persistent_connection' to `a_connection'.