Improve socket management for EiffelWeb standalone connector.
This commit is contained in:
@@ -24,6 +24,8 @@ feature -- Basic operations
|
|||||||
s := "Hello World!"
|
s := "Hello World!"
|
||||||
create dt.make_now_utc
|
create dt.make_now_utc
|
||||||
s.append (" (UTC time is " + dt.rfc850_string + ").")
|
s.append (" (UTC time is " + dt.rfc850_string + ").")
|
||||||
|
s.append ("%N")
|
||||||
|
s.append ("Your request: " + request.request_uri + " %N")
|
||||||
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>)
|
||||||
response.set_status_code ({HTTP_STATUS_CODE}.ok)
|
response.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||||
response.header.put_content_type_text_html
|
response.header.put_content_type_text_html
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ feature {NONE} -- Initialization
|
|||||||
do
|
do
|
||||||
reset_request
|
reset_request
|
||||||
|
|
||||||
has_error := False
|
reset_error
|
||||||
if attached internal_client_socket as l_sock then
|
if attached internal_client_socket as l_sock then
|
||||||
l_sock.cleanup
|
l_sock.cleanup
|
||||||
end
|
end
|
||||||
@@ -156,6 +156,20 @@ feature -- Status report
|
|||||||
has_error: BOOLEAN
|
has_error: BOOLEAN
|
||||||
-- Error occurred during `analyze_request_message'
|
-- Error occurred during `analyze_request_message'
|
||||||
|
|
||||||
|
feature -- Status change
|
||||||
|
|
||||||
|
report_error (m: detachable READABLE_STRING_GENERAL)
|
||||||
|
-- Report error occurred, with optional message `m'.
|
||||||
|
do
|
||||||
|
has_error := True
|
||||||
|
end
|
||||||
|
|
||||||
|
reset_error
|
||||||
|
-- Reset previous error for current request handler.
|
||||||
|
do
|
||||||
|
has_error := False
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Change
|
feature -- Change
|
||||||
|
|
||||||
set_is_verbose (b: BOOLEAN)
|
set_is_verbose (b: BOOLEAN)
|
||||||
@@ -236,11 +250,18 @@ feature -- Execution
|
|||||||
require
|
require
|
||||||
is_connected: is_connected
|
is_connected: is_connected
|
||||||
reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported
|
reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported
|
||||||
|
no_error: not has_error
|
||||||
local
|
local
|
||||||
l_remote_info: detachable like remote_info
|
l_remote_info: detachable like remote_info
|
||||||
l_socket: like client_socket
|
l_socket: like client_socket
|
||||||
l_is_ready: BOOLEAN
|
l_is_ready: BOOLEAN
|
||||||
do
|
do
|
||||||
|
debug ("dbglog")
|
||||||
|
if a_is_reusing_connection then
|
||||||
|
dbglog ("execute_request: wait on persistent connection.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
reset_error
|
||||||
l_socket := client_socket
|
l_socket := client_socket
|
||||||
check
|
check
|
||||||
socket_attached: l_socket /= Void
|
socket_attached: l_socket /= Void
|
||||||
@@ -248,17 +269,18 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
if l_socket.is_closed then
|
if l_socket.is_closed then
|
||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
dbglog (generator + ".execute_request {socket is Closed!}")
|
dbglog ("execute_request {socket is Closed!}")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
dbglog ("execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
||||||
end
|
end
|
||||||
|
|
||||||
if a_is_reusing_connection then
|
if a_is_reusing_connection then
|
||||||
--| set by default 5 seconds.
|
--| set by default 5 seconds.
|
||||||
l_socket.set_recv_timeout (keep_alive_timeout) -- in seconds!
|
l_socket.set_recv_timeout (keep_alive_timeout) -- in seconds!
|
||||||
l_is_ready := l_socket.ready_for_reading
|
-- FIXME: check if both are really needed.
|
||||||
|
l_is_ready := l_socket.ready_for_reading and then l_socket.has_incoming_data
|
||||||
else
|
else
|
||||||
l_is_ready := True
|
l_is_ready := True
|
||||||
end
|
end
|
||||||
@@ -273,20 +295,12 @@ feature -- Execution
|
|||||||
remote_info := l_remote_info
|
remote_info := l_remote_info
|
||||||
end
|
end
|
||||||
analyze_request_message (l_socket)
|
analyze_request_message (l_socket)
|
||||||
else
|
|
||||||
has_error := True
|
|
||||||
debug ("dbglog")
|
|
||||||
dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + "} timeout!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if has_error then
|
if has_error then
|
||||||
if l_is_ready then
|
|
||||||
-- check catch_bad_incoming_connection: False end
|
-- check catch_bad_incoming_connection: False end
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level)
|
log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
process_bad_request (l_socket)
|
process_bad_request (l_socket)
|
||||||
is_persistent_connection_requested := False
|
is_persistent_connection_requested := False
|
||||||
else
|
else
|
||||||
@@ -295,8 +309,17 @@ feature -- Execution
|
|||||||
end
|
end
|
||||||
process_request (l_socket)
|
process_request (l_socket)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
check is_reusing_connection: a_is_reusing_connection end
|
||||||
|
-- Close persistent connection, since no new connection occurred in the delay `keep_alive_timeout'.
|
||||||
|
is_persistent_connection_requested := False
|
||||||
debug ("dbglog")
|
debug ("dbglog")
|
||||||
dbglog (generator + ".execute_request {" + l_socket.descriptor.out + "} LEAVE")
|
dbglog ("execute_request socket=" + l_socket.descriptor.out + "} close persistent connection.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
debug ("dbglog")
|
||||||
|
dbglog ("execute_request {" + l_socket.descriptor.out + "} LEAVE")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -329,6 +352,8 @@ feature -- Request processing
|
|||||||
h: STRING
|
h: STRING
|
||||||
s: STRING
|
s: STRING
|
||||||
do
|
do
|
||||||
|
-- NOTE: this is experiment code, and not ready yet.
|
||||||
|
if a_socket.is_connected and then a_socket.ready_for_writing then
|
||||||
s := "{
|
s := "{
|
||||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
|
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
|
||||||
<html><head>
|
<html><head>
|
||||||
@@ -344,8 +369,11 @@ feature -- Request processing
|
|||||||
h.append ("Content-Type: text/html; charset=iso-8859-1%R%N")
|
h.append ("Content-Type: text/html; charset=iso-8859-1%R%N")
|
||||||
h.append ("%R%N")
|
h.append ("%R%N")
|
||||||
a_socket.put_string (h)
|
a_socket.put_string (h)
|
||||||
|
if a_socket.is_connected and then a_socket.ready_for_writing then
|
||||||
a_socket.put_string (s)
|
a_socket.put_string (s)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
feature -- Parsing
|
feature -- Parsing
|
||||||
|
|
||||||
@@ -365,7 +393,12 @@ feature -- Parsing
|
|||||||
request_header := txt
|
request_header := txt
|
||||||
if
|
if
|
||||||
not has_error and then
|
not has_error and then
|
||||||
a_socket.is_readable and then
|
a_socket.readable
|
||||||
|
then
|
||||||
|
if not a_socket.has_incoming_data then
|
||||||
|
dbglog ("analyze_request_message: NO INCOMING DATA !!!")
|
||||||
|
end
|
||||||
|
if
|
||||||
attached next_line (a_socket) as l_request_line and then
|
attached next_line (a_socket) as l_request_line and then
|
||||||
not l_request_line.is_empty
|
not l_request_line.is_empty
|
||||||
then
|
then
|
||||||
@@ -373,7 +406,10 @@ feature -- Parsing
|
|||||||
txt.append_character ('%N')
|
txt.append_character ('%N')
|
||||||
analyze_request_line (l_request_line)
|
analyze_request_line (l_request_line)
|
||||||
else
|
else
|
||||||
has_error := True
|
report_error ("Bad header line (empty)")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
report_error ("Socket is not readable")
|
||||||
end
|
end
|
||||||
l_is_verbose := is_verbose
|
l_is_verbose := is_verbose
|
||||||
if not has_error then
|
if not has_error then
|
||||||
@@ -448,7 +484,9 @@ feature -- Parsing
|
|||||||
n := n - 1
|
n := n - 1
|
||||||
end
|
end
|
||||||
version := line.substring (next_pos + 1, n)
|
version := line.substring (next_pos + 1, n)
|
||||||
has_error := method.is_empty
|
if method.is_empty then
|
||||||
|
report_error ("Missing request method data")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
|
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
|
||||||
@@ -460,7 +498,7 @@ feature -- Parsing
|
|||||||
retried: BOOLEAN
|
retried: BOOLEAN
|
||||||
do
|
do
|
||||||
if retried then
|
if retried then
|
||||||
has_error := True
|
report_error ("Rescue in next_line")
|
||||||
Result := Void
|
Result := Void
|
||||||
elseif a_socket.readable then
|
elseif a_socket.readable then
|
||||||
a_socket.read_line_thread_aware
|
a_socket.read_line_thread_aware
|
||||||
@@ -468,14 +506,14 @@ feature -- Parsing
|
|||||||
-- Do no check `socket_ok' before socket operation,
|
-- Do no check `socket_ok' before socket operation,
|
||||||
-- otherwise it may be False, due to error during other socket operation in same thread.
|
-- otherwise it may be False, due to error during other socket operation in same thread.
|
||||||
if not a_socket.socket_ok then
|
if not a_socket.socket_ok then
|
||||||
has_error := True
|
report_error ("Socket error")
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log (request_header +"%N" + Result + "%N## socket_ok=False! ##", debug_level)
|
log (request_header +"%N" + Result + "%N## socket_ok=False! ##", debug_level)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Error with socket...
|
-- Error with socket...
|
||||||
has_error := True
|
report_error ("Socket error: not readable")
|
||||||
if is_verbose then
|
if is_verbose then
|
||||||
log (request_header + "%N## Socket is not readable! ##", debug_level)
|
log (request_header + "%N## Socket is not readable! ##", debug_level)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -141,6 +141,15 @@ feature -- Input
|
|||||||
socket.read_character
|
socket.read_character
|
||||||
end
|
end
|
||||||
|
|
||||||
|
peek_stream (nb_char: INTEGER)
|
||||||
|
require
|
||||||
|
nb_char_positive: nb_char > 0
|
||||||
|
do
|
||||||
|
if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||||||
|
l_socket.peek_stream (nb_char)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
bytes_read: INTEGER
|
bytes_read: INTEGER
|
||||||
do
|
do
|
||||||
Result := socket.bytes_read
|
Result := socket.bytes_read
|
||||||
@@ -308,6 +317,13 @@ feature -- Status Report
|
|||||||
Result := socket.readable
|
Result := socket.readable
|
||||||
end
|
end
|
||||||
|
|
||||||
|
has_incoming_data: BOOLEAN
|
||||||
|
do
|
||||||
|
if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||||||
|
Result := l_socket.has_incoming_data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
ready_for_reading: BOOLEAN
|
ready_for_reading: BOOLEAN
|
||||||
do
|
do
|
||||||
if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||||||
|
|||||||
@@ -55,6 +55,38 @@ feature {NONE} -- Initialization
|
|||||||
|
|
||||||
feature -- Basic operation
|
feature -- Basic operation
|
||||||
|
|
||||||
|
peek_stream (nb_char: INTEGER)
|
||||||
|
-- Read a string of at most `nb_char' characters without removing the data from the queue.
|
||||||
|
-- Make result available in last_string.
|
||||||
|
require
|
||||||
|
readable: readable
|
||||||
|
socket_exists: exists
|
||||||
|
local
|
||||||
|
ext: C_STRING
|
||||||
|
retval: INTEGER
|
||||||
|
l: like last_string
|
||||||
|
do
|
||||||
|
create ext.make_empty (nb_char + 1)
|
||||||
|
retval := c_receive (descriptor, ext.item, nb_char, c_peekmsg)
|
||||||
|
if retval = 0 then
|
||||||
|
last_string.wipe_out
|
||||||
|
socket_error := Void
|
||||||
|
elseif retval > 0 then
|
||||||
|
ext.set_count (retval)
|
||||||
|
l := last_string
|
||||||
|
l.wipe_out
|
||||||
|
l.grow (retval)
|
||||||
|
l.set_count (retval)
|
||||||
|
ext.read_substring_into (l, 1, retval)
|
||||||
|
socket_error := Void
|
||||||
|
else
|
||||||
|
last_string.wipe_out
|
||||||
|
socket_error := "Socket error (MSG_PEEK)"
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
last_string_not_void: last_string /= Void
|
||||||
|
end
|
||||||
|
|
||||||
send_message (a_msg: STRING)
|
send_message (a_msg: STRING)
|
||||||
do
|
do
|
||||||
put_string (a_msg)
|
put_string (a_msg)
|
||||||
@@ -73,6 +105,14 @@ feature -- Output
|
|||||||
|
|
||||||
feature -- Status report
|
feature -- Status report
|
||||||
|
|
||||||
|
has_incoming_data: BOOLEAN
|
||||||
|
require
|
||||||
|
socket_exists: exists
|
||||||
|
do
|
||||||
|
peek_stream (1)
|
||||||
|
Result := last_string.count = 1
|
||||||
|
end
|
||||||
|
|
||||||
try_ready_for_reading: BOOLEAN
|
try_ready_for_reading: BOOLEAN
|
||||||
-- Is data available for reading from the socket right now?
|
-- Is data available for reading from the socket right now?
|
||||||
require
|
require
|
||||||
|
|||||||
@@ -97,7 +97,9 @@ feature -- Request processing
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
has_error := l_output = Void or else not l_output.is_available
|
if l_output = Void or else not l_output.is_available then
|
||||||
|
report_error ("Missing WGI output")
|
||||||
|
end
|
||||||
if not retried then
|
if not retried then
|
||||||
retried := True
|
retried := True
|
||||||
retry
|
retry
|
||||||
|
|||||||
Reference in New Issue
Block a user