Improve socket management for EiffelWeb standalone connector.

This commit is contained in:
2016-10-04 18:49:48 +02:00
parent 0217c6d3f4
commit 77085364ee
5 changed files with 143 additions and 45 deletions

View File

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

View File

@@ -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,30 +295,31 @@ 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 if is_verbose then
if is_verbose then log (request_header, information_level)
log (request_header, information_level) 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")
dbglog ("execute_request socket=" + l_socket.descriptor.out + "} close persistent connection.")
end
end
debug ("dbglog") debug ("dbglog")
dbglog (generator + ".execute_request {" + l_socket.descriptor.out + "} LEAVE") dbglog ("execute_request {" + l_socket.descriptor.out + "} LEAVE")
end end
end end
end end
@@ -329,22 +352,27 @@ feature -- Request processing
h: STRING h: STRING
s: STRING s: STRING
do do
s := "{ -- NOTE: this is experiment code, and not ready yet.
if a_socket.is_connected and then a_socket.ready_for_writing then
s := "{
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head> <html><head>
<title>400 Bad Request</title> <title>400 Bad Request</title>
</head><body> </head><body>
<h1>Bad Request</h1> <h1>Bad Request</h1>
</body></html> </body></html>
}" }"
create h.make (1_024) create h.make (1_024)
h.append ("HTTP/1.1 400 Bad Request%R%N") h.append ("HTTP/1.1 400 Bad Request%R%N")
h.append ("Content-Length: " + s.count.out + "%R%N") h.append ("Content-Length: " + s.count.out + "%R%N")
h.append ("Connection: close%R%N") h.append ("Connection: close%R%N")
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)
a_socket.put_string (s) if a_socket.is_connected and then a_socket.ready_for_writing then
a_socket.put_string (s)
end
end
end end
feature -- Parsing feature -- Parsing
@@ -365,15 +393,23 @@ 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
attached next_line (a_socket) as l_request_line and then
not l_request_line.is_empty
then then
txt.append (l_request_line) if not a_socket.has_incoming_data then
txt.append_character ('%N') dbglog ("analyze_request_message: NO INCOMING DATA !!!")
analyze_request_line (l_request_line) end
if
attached next_line (a_socket) as l_request_line and then
not l_request_line.is_empty
then
txt.append (l_request_line)
txt.append_character ('%N')
analyze_request_line (l_request_line)
else
report_error ("Bad header line (empty)")
end
else else
has_error := True 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

View File

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

View File

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

View File

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