Improve socket management for EiffelWeb standalone connector.
This commit is contained in:
@@ -33,7 +33,7 @@ feature {NONE} -- Initialization
|
||||
do
|
||||
reset_request
|
||||
|
||||
has_error := False
|
||||
reset_error
|
||||
if attached internal_client_socket as l_sock then
|
||||
l_sock.cleanup
|
||||
end
|
||||
@@ -156,6 +156,20 @@ feature -- Status report
|
||||
has_error: BOOLEAN
|
||||
-- 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
|
||||
|
||||
set_is_verbose (b: BOOLEAN)
|
||||
@@ -236,11 +250,18 @@ feature -- Execution
|
||||
require
|
||||
is_connected: is_connected
|
||||
reuse_connection_when_possible: a_is_reusing_connection implies is_persistent_connection_supported
|
||||
no_error: not has_error
|
||||
local
|
||||
l_remote_info: detachable like remote_info
|
||||
l_socket: like client_socket
|
||||
l_is_ready: BOOLEAN
|
||||
do
|
||||
debug ("dbglog")
|
||||
if a_is_reusing_connection then
|
||||
dbglog ("execute_request: wait on persistent connection.")
|
||||
end
|
||||
end
|
||||
reset_error
|
||||
l_socket := client_socket
|
||||
check
|
||||
socket_attached: l_socket /= Void
|
||||
@@ -248,17 +269,18 @@ feature -- Execution
|
||||
end
|
||||
if l_socket.is_closed then
|
||||
debug ("dbglog")
|
||||
dbglog (generator + ".execute_request {socket is Closed!}")
|
||||
dbglog ("execute_request {socket is Closed!}")
|
||||
end
|
||||
else
|
||||
debug ("dbglog")
|
||||
dbglog (generator + ".execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
||||
dbglog ("execute_request socket=" + l_socket.descriptor.out + " ENTER")
|
||||
end
|
||||
|
||||
if a_is_reusing_connection then
|
||||
--| set by default 5 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
|
||||
l_is_ready := True
|
||||
end
|
||||
@@ -273,30 +295,31 @@ feature -- Execution
|
||||
remote_info := l_remote_info
|
||||
end
|
||||
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 l_is_ready then
|
||||
if has_error then
|
||||
-- check catch_bad_incoming_connection: False end
|
||||
if is_verbose then
|
||||
log (request_header + "%NWARNING: invalid HTTP incoming request", warning_level)
|
||||
end
|
||||
end
|
||||
process_bad_request (l_socket)
|
||||
is_persistent_connection_requested := False
|
||||
else
|
||||
if is_verbose then
|
||||
log (request_header, information_level)
|
||||
end
|
||||
process_request (l_socket)
|
||||
end
|
||||
process_bad_request (l_socket)
|
||||
is_persistent_connection_requested := False
|
||||
else
|
||||
if is_verbose then
|
||||
log (request_header, information_level)
|
||||
end
|
||||
process_request (l_socket)
|
||||
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")
|
||||
dbglog (generator + ".execute_request {" + l_socket.descriptor.out + "} LEAVE")
|
||||
dbglog ("execute_request {" + l_socket.descriptor.out + "} LEAVE")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -329,22 +352,27 @@ feature -- Request processing
|
||||
h: STRING
|
||||
s: STRING
|
||||
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">
|
||||
<html><head>
|
||||
<title>400 Bad Request</title>
|
||||
</head><body>
|
||||
<h1>Bad Request</h1>
|
||||
</body></html>
|
||||
}"
|
||||
create h.make (1_024)
|
||||
h.append ("HTTP/1.1 400 Bad Request%R%N")
|
||||
h.append ("Content-Length: " + s.count.out + "%R%N")
|
||||
h.append ("Connection: close%R%N")
|
||||
h.append ("Content-Type: text/html; charset=iso-8859-1%R%N")
|
||||
h.append ("%R%N")
|
||||
a_socket.put_string (h)
|
||||
a_socket.put_string (s)
|
||||
</body></html>
|
||||
}"
|
||||
create h.make (1_024)
|
||||
h.append ("HTTP/1.1 400 Bad Request%R%N")
|
||||
h.append ("Content-Length: " + s.count.out + "%R%N")
|
||||
h.append ("Connection: close%R%N")
|
||||
h.append ("Content-Type: text/html; charset=iso-8859-1%R%N")
|
||||
h.append ("%R%N")
|
||||
a_socket.put_string (h)
|
||||
if a_socket.is_connected and then a_socket.ready_for_writing then
|
||||
a_socket.put_string (s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Parsing
|
||||
@@ -365,15 +393,23 @@ feature -- Parsing
|
||||
request_header := txt
|
||||
if
|
||||
not has_error and then
|
||||
a_socket.is_readable and then
|
||||
attached next_line (a_socket) as l_request_line and then
|
||||
not l_request_line.is_empty
|
||||
a_socket.readable
|
||||
then
|
||||
txt.append (l_request_line)
|
||||
txt.append_character ('%N')
|
||||
analyze_request_line (l_request_line)
|
||||
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
|
||||
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
|
||||
has_error := True
|
||||
report_error ("Socket is not readable")
|
||||
end
|
||||
l_is_verbose := is_verbose
|
||||
if not has_error then
|
||||
@@ -448,7 +484,9 @@ feature -- Parsing
|
||||
n := n - 1
|
||||
end
|
||||
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
|
||||
|
||||
next_line (a_socket: HTTPD_STREAM_SOCKET): detachable STRING
|
||||
@@ -460,7 +498,7 @@ feature -- Parsing
|
||||
retried: BOOLEAN
|
||||
do
|
||||
if retried then
|
||||
has_error := True
|
||||
report_error ("Rescue in next_line")
|
||||
Result := Void
|
||||
elseif a_socket.readable then
|
||||
a_socket.read_line_thread_aware
|
||||
@@ -468,14 +506,14 @@ feature -- Parsing
|
||||
-- Do no check `socket_ok' before socket operation,
|
||||
-- otherwise it may be False, due to error during other socket operation in same thread.
|
||||
if not a_socket.socket_ok then
|
||||
has_error := True
|
||||
report_error ("Socket error")
|
||||
if is_verbose then
|
||||
log (request_header +"%N" + Result + "%N## socket_ok=False! ##", debug_level)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Error with socket...
|
||||
has_error := True
|
||||
report_error ("Socket error: not readable")
|
||||
if is_verbose then
|
||||
log (request_header + "%N## Socket is not readable! ##", debug_level)
|
||||
end
|
||||
|
||||
@@ -141,6 +141,15 @@ feature -- Input
|
||||
socket.read_character
|
||||
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
|
||||
do
|
||||
Result := socket.bytes_read
|
||||
@@ -308,6 +317,13 @@ feature -- Status Report
|
||||
Result := socket.readable
|
||||
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
|
||||
do
|
||||
if attached {TCP_STREAM_SOCKET} socket as l_socket then
|
||||
|
||||
@@ -55,6 +55,38 @@ feature {NONE} -- Initialization
|
||||
|
||||
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)
|
||||
do
|
||||
put_string (a_msg)
|
||||
@@ -73,6 +105,14 @@ feature -- Output
|
||||
|
||||
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
|
||||
-- Is data available for reading from the socket right now?
|
||||
require
|
||||
|
||||
@@ -97,7 +97,9 @@ feature -- Request processing
|
||||
end
|
||||
end
|
||||
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
|
||||
retried := True
|
||||
retry
|
||||
|
||||
Reference in New Issue
Block a user