From c553bd1e1e5866f1830a3a94658b7d1c58b019f8 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Fri, 27 May 2011 13:07:06 +0200 Subject: [PATCH] abstracted the HTTP_HANDLER to let the user integrate at the level of its choice (either very early so handle itself the header handling, or later to reuse existing code) --- application.e | 4 +- application_handler.e | 57 +++++++ exception_trace.log | 47 ------ io/http_input_stream.e | 20 +++ request/get_request_handler.e | 2 +- server/http_connection_handler.e | 248 +++++-------------------------- server/http_handler.e | 123 +++++++++++++++ server/http_protocol_handler.e | 4 +- server/http_server.e | 6 +- server/shared_document_root.e | 15 -- 10 files changed, 249 insertions(+), 277 deletions(-) create mode 100644 application_handler.e delete mode 100644 exception_trace.log create mode 100644 server/http_handler.e delete mode 100644 server/shared_document_root.e diff --git a/application.e b/application.e index 6c206526..39c86209 100644 --- a/application.e +++ b/application.e @@ -19,14 +19,14 @@ feature {NONE} -- Initialization local l_server : HTTP_SERVER l_cfg: HTTP_SERVER_CONFIGURATION - l_http_handler : HTTP_CONNECTION_HANDLER + l_http_handler : HTTP_HANDLER do create l_cfg.make l_cfg.http_server_port := 9_000 l_cfg.document_root := default_document_root create l_server.make (l_cfg) - create l_http_handler.make (l_server, "HTTP_HANDLER") + create {APPLICATION_CONNECTION_HANDLER} l_http_handler.make (l_server, "HTTP_HANDLER") l_server.setup (l_http_handler) end diff --git a/application_handler.e b/application_handler.e new file mode 100644 index 00000000..9a63a82a --- /dev/null +++ b/application_handler.e @@ -0,0 +1,57 @@ +note + description: "Summary description for {HTTP_CONNECTION_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + APPLICATION_CONNECTION_HANDLER + +inherit + HTTP_CONNECTION_HANDLER + +create + make + +feature -- Request processing + + process_request (a_uri: STRING; a_method: STRING; a_headers_map: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM) + -- Process request ... + do + if a_method.is_equal (Get) then + execute_get_request (a_uri, a_headers_map, a_headers_text, a_input, a_output) + elseif a_method.is_equal (Post) then + execute_post_request (a_uri, a_headers_map, a_headers_text, a_input, a_output) + elseif a_method.is_equal (Put) then + elseif a_method.is_equal (Options) then + elseif a_method.is_equal (Head) then + elseif a_method.is_equal (Delete) then + elseif a_method.is_equal (Trace) then + elseif a_method.is_equal (Connect) then + else + debug + print ("Method [" + a_method + "] not supported") + end + end + end + + execute_get_request (a_uri: STRING; a_headers_map: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM) + local + l_http_request : HTTP_REQUEST_HANDLER + do + create {GET_REQUEST_HANDLER} l_http_request.make (a_input, a_output) + l_http_request.set_uri (a_uri) + l_http_request.process + end + + execute_post_request (a_uri: STRING; a_headers_map: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM) + local + l_http_request : HTTP_REQUEST_HANDLER + do + check not_yet_implemented: False end + create {POST_REQUEST_HANDLER} l_http_request.make (a_input, a_output) + l_http_request.set_uri (a_uri) + l_http_request.process + end + +end diff --git a/exception_trace.log b/exception_trace.log deleted file mode 100644 index e44d7f4e..00000000 --- a/exception_trace.log +++ /dev/null @@ -1,47 +0,0 @@ - -nino: system execution failed. -Following is the set of recorded exceptions: - -******************************** Thread exception ***************************** -In thread Child thread 0x88 (thread id) -******************************************************************************* -------------------------------------------------------------------------------- -Class / Object Routine Nature of exception Effect -------------------------------------------------------------------------------- -TCP_STREAM_SOCKET send @1 socket_exists: -<00000000024B1A48> (From SOCKET) Precondition violated. Fail -------------------------------------------------------------------------------- -GET_REQUEST_HANDLER send_message @4 -<00000000024B4428> Routine failure. Fail -------------------------------------------------------------------------------- -GET_REQUEST_HANDLER execute @2 -<00000000024B4428> Routine failure. Fail -------------------------------------------------------------------------------- -GET_REQUEST_HANDLER thr_main @4 -<00000000024B4428> (From THREAD) Routine failure. Rescue -------------------------------------------------------------------------------- -APPLICATION root's creation -<00000000024B05A8> Routine failure. Exit -------------------------------------------------------------------------------- - -nino: system execution failed. -Following is the set of recorded exceptions: - -******************************** Thread exception ***************************** -In thread Child thread 0x88 (thread id) -******************************************************************************* -------------------------------------------------------------------------------- -Class / Object Routine Nature of exception Effect -------------------------------------------------------------------------------- -TCP_STREAM_SOCKET put_string @1 extendible: -<0000000002621A48> (From SOCKET) Precondition violated. Fail -------------------------------------------------------------------------------- -GET_REQUEST_HANDLER execute @2 -<0000000002624408> Routine failure. Fail -------------------------------------------------------------------------------- -GET_REQUEST_HANDLER thr_main @4 -<0000000002624408> (From THREAD) Routine failure. Rescue -------------------------------------------------------------------------------- -APPLICATION root's creation -<00000000026205A8> Routine failure. Exit -------------------------------------------------------------------------------- diff --git a/io/http_input_stream.e b/io/http_input_stream.e index bd85fafd..9dc67f73 100644 --- a/io/http_input_stream.e +++ b/io/http_input_stream.e @@ -20,14 +20,34 @@ feature {NONE} -- Initialization source: TCP_STREAM_SOCKET +feature -- Status Report + + is_readable: BOOLEAN + -- Is readable? + do + Result := source.is_open_read + end + feature -- Basic operation + read_line + require + is_readable: is_readable + do + last_string.wipe_out + if source.socket_ok then + source.read_line_thread_aware + last_string.append_string (source.last_string) + end + end + read_stream (nb_char: INTEGER) -- Read a string of at most `nb_char' bound characters -- or until end of file. -- Make result available in `last_string'. require nb_char_positive: nb_char > 0 + is_readable: is_readable do last_string.wipe_out if source.socket_ok then diff --git a/request/get_request_handler.e b/request/get_request_handler.e index 2aa269d9..e37d77b2 100644 --- a/request/get_request_handler.e +++ b/request/get_request_handler.e @@ -4,7 +4,7 @@ inherit HTTP_REQUEST_HANDLER - SHARED_DOCUMENT_ROOT + HTTP_SERVER_SHARED_CONFIGURATION undefine default_create end diff --git a/server/http_connection_handler.e b/server/http_connection_handler.e index 7dfb2359..3ed23376 100644 --- a/server/http_connection_handler.e +++ b/server/http_connection_handler.e @@ -4,16 +4,14 @@ note date: "$Date$" revision: "$Revision$" -class +deferred class HTTP_CONNECTION_HANDLER inherit - THREAD - - HTTP_CONSTANTS - -create - make + HTTP_HANDLER + redefine + make + end feature {NONE} -- Initialization @@ -22,70 +20,26 @@ feature {NONE} -- Initialization -- -- `a_main_server': The main server object -- `a_name': The name of this module - require - a_main_server_attached: a_main_server /= Void - a_name_attached: a_name /= Void do - main_server := a_main_server - create current_request_message.make_empty + Precursor (a_main_server, a_name) create method.make_empty create uri.make_empty + create request_header.make_empty create request_header_map.make (10) - is_stop_requested := False - ensure - main_server_set: a_main_server ~ main_server - current_request_message_attached: current_request_message /= Void end -feature -- Inherited Features +feature -- Execution - execute - -- - -- Creates a socket and connects to the http server. + receive_message_and_send_reply (client_socket: TCP_STREAM_SOCKET) local - l_http_socket: detachable TCP_STREAM_SOCKET - l_http_port: INTEGER + l_input: HTTP_INPUT_STREAM + l_output: HTTP_OUTPUT_STREAM do - is_stop_requested := False - l_http_port := main_server_configuration.http_server_port - create l_http_socket.make_server_by_port (l_http_port) - if not l_http_socket.is_bound then - print ("Socket could not be bound on port " + l_http_port.out ) - else - from - l_http_socket.listen (main_server_configuration.max_tcp_clients) - print ("%NHTTP Connection Server ready on port " + l_http_port.out +"%N") - until - is_stop_requested - loop - l_http_socket.accept - if not is_stop_requested then - if attached l_http_socket.accepted as l_thread_http_socket then - receive_message_and_send_replay (l_thread_http_socket) - l_thread_http_socket.cleanup - check - socket_closed: l_thread_http_socket.is_closed - end - end - end - end - l_http_socket.cleanup - check - socket_is_closed: l_http_socket.is_closed - end - end - print ("HTTP Connection Server ends.") - rescue - print ("HTTP Connection Server shutdown due to exception. Please relaunch manually.") + create l_input.make (client_socket) + create l_output.make (client_socket) - if attached l_http_socket as ll_http_socket then - ll_http_socket.cleanup - check - socket_is_closed: ll_http_socket.is_closed - end - end - is_stop_requested := True - retry + analyze_request_message (l_input) + process_request (uri, method, request_header_map, request_header, l_input, l_output) end feature -- Request processing @@ -98,70 +52,17 @@ feature -- Request processing a_headers_text_attached: a_headers_text /= Void a_input_attached: a_input /= Void a_output_attached: a_output /= Void - do - if a_method.is_equal (Get) then - execute_get_request (a_uri, a_headers_map, a_headers_text, a_input, a_output) - elseif a_method.is_equal (Post) then - execute_post_request (a_uri, a_headers_map, a_headers_text, a_input, a_output) - elseif a_method.is_equal (Put) then - elseif a_method.is_equal (Options) then - elseif a_method.is_equal (Head) then - elseif a_method.is_equal (Delete) then - elseif a_method.is_equal (Trace) then - elseif a_method.is_equal (Connect) then - else - debug - print ("Method [" + a_method + "] not supported") - end - end + deferred end - execute_get_request (a_uri: STRING; a_headers_map: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM) - local - l_http_request : HTTP_REQUEST_HANDLER - do - create {GET_REQUEST_HANDLER} l_http_request.make (a_input, a_output) - l_http_request.set_uri (a_uri) - l_http_request.process --- client_socket.put_string (l_http_request.answer.reply_header + l_http_request.answer.reply_text) - end - - execute_post_request (a_uri: STRING; a_headers_map: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM) - local - l_http_request : HTTP_REQUEST_HANDLER - do - check not_yet_implemented: False end - create {POST_REQUEST_HANDLER} l_http_request.make (a_input, a_output) - l_http_request.set_uri (a_uri) - l_http_request.process --- client_socket.put_string (l_http_request.answer.reply_header + l_http_request.answer.reply_text) - end - -feature -- Access - - is_stop_requested: BOOLEAN - -- Set true to stop accept loop - feature {NONE} -- Access + request_header: STRING + -- Header' source + request_header_map : HASH_TABLE [STRING,STRING] -- Contains key:value of the header - main_server: HTTP_SERVER - -- The main server object - - main_server_configuration: HTTP_SERVER_CONFIGURATION - -- The main server's configuration - do - Result := main_server.configuration - end - - current_request_message: STRING - -- Stores the current request message received from http server - - Max_fragments: INTEGER = 1000 - -- Defines the maximum number of fragments that can be received - method: STRING -- http verb @@ -172,54 +73,7 @@ feature {NONE} -- Access -- http_version --| unused for now -feature -- Status setting - - shutdown - -- Stops the thread - do - is_stop_requested := True - end - -feature {NONE} -- Implementation - - read_string_from_socket (a_socket: TCP_STREAM_SOCKET; a_n: NATURAL): STRING - -- Reads characters from the socket and concatenates them to a string - -- - -- `a_socket': The socket to read from - -- `a_n': The number of characters to read - -- `Result': The created string - require - socket_is_open: not a_socket.is_closed - local - l_read_size: INTEGER - l_buf: detachable STRING - do - create Result.make (a_n.as_integer_32) - from - l_read_size := 0 - Result := "" - l_buf := "" - until - l_buf.is_equal ("%R") - loop - a_socket.read_line_thread_aware - l_buf := a_socket.last_string - if l_buf /= Void then - Result.append (l_buf) - end - if l_buf.is_equal ("%R") then - a_socket.set_nodelay - a_socket.put_string ("HTTP/1.1 100 Continue%/13/%/10/%/13/%/10/") - a_socket.close_socket - end - - l_read_size := Result.count - end - ensure - Result_attached: Result /= Void - end - -feature -- New implementation +feature -- Parsing parse_http_request_line (line: STRING) require @@ -237,63 +91,44 @@ feature -- New implementation not_void_method: method /= Void end -feature -- New Implementation - receive_message_and_send_replay (client_socket: TCP_STREAM_SOCKET) - require - socket_attached: client_socket /= Void --- socket_valid: client_socket.is_open_read and then client_socket.is_open_write - a_http_socket:client_socket /= Void and then not client_socket.is_closed - local - l_headers_text: detachable STRING - l_input: HTTP_INPUT_STREAM - l_output: HTTP_OUTPUT_STREAM - do - parse_request_line (client_socket) - l_headers_text := receive_message_internal (client_socket) - - create l_input.make (client_socket) - create l_output.make (client_socket) - process_request (uri, method, request_header_map, l_headers_text, l_input, l_output) - end - - parse_request_line (socket: NETWORK_STREAM_SOCKET) + analyze_request_message (a_input: HTTP_INPUT_STREAM) require - socket: socket /= Void and then not socket.is_closed - do - socket.read_line - parse_request_line_internal (socket.last_string) - end - - receive_message_internal (socket: TCP_STREAM_SOCKET) : STRING - require - socket: socket /= Void and then not socket.is_closed + input_redable: a_input /= Void and then not a_input.is_readable local end_of_stream : BOOLEAN pos : INTEGER line : STRING + txt: STRING do + create txt.make (64) + a_input.read_line + line := a_input.last_string + analyze_request_line (line) + txt.append (line) + txt.append_character ('%N') + + request_header := txt from - socket.read_line_thread_aware - Result := "" + a_input.read_line until end_of_stream loop - line := socket.last_string + line := a_input.last_string print ("%N" +line+ "%N") pos := line.index_of (':',1) request_header_map.put (line.substring (pos + 1, line.count), line.substring (1,pos-1)) - Result.append (line) - Result.append_character ('%N') - if not line.is_equal("%R") and socket.socket_ok then - socket.read_line_thread_aware - else + txt.append (line) + txt.append_character ('%N') + if line.is_empty or else line[1] = '%R' then end_of_stream := True - end + else + a_input.read_line + end end end - parse_request_line_internal (line: STRING) + analyze_request_line (line: STRING) require line /= Void local @@ -310,7 +145,6 @@ feature -- New Implementation end invariant - main_server_attached: main_server /= Void - current_request_message_attached: current_request_message /= Void + request_header_attached: request_header /= Void end diff --git a/server/http_handler.e b/server/http_handler.e new file mode 100644 index 00000000..80369127 --- /dev/null +++ b/server/http_handler.e @@ -0,0 +1,123 @@ +note + description: "Summary description for {HTTP_CONNECTION_HANDLER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTP_HANDLER + +inherit + THREAD + + HTTP_CONSTANTS + +feature {NONE} -- Initialization + + make (a_main_server: like main_server; a_name: STRING) + -- Creates a {HTTP_HANDLER}, assigns the main_server and initialize various values + -- + -- `a_main_server': The main server object + -- `a_name': The name of this module + require + a_main_server_attached: a_main_server /= Void + a_name_attached: a_name /= Void + do + main_server := a_main_server + is_stop_requested := False + ensure + main_server_set: a_main_server ~ main_server + end + +feature -- Inherited Features + + execute + -- + -- Creates a socket and connects to the http server. + local + l_http_socket: detachable TCP_STREAM_SOCKET + l_http_port: INTEGER + do + is_stop_requested := False + l_http_port := main_server_configuration.http_server_port + create l_http_socket.make_server_by_port (l_http_port) + if not l_http_socket.is_bound then + print ("Socket could not be bound on port " + l_http_port.out ) + else + from + l_http_socket.listen (main_server_configuration.max_tcp_clients) + print ("%NHTTP Connection Server ready on port " + l_http_port.out +"%N") + until + is_stop_requested + loop + l_http_socket.accept + if not is_stop_requested then + if attached l_http_socket.accepted as l_thread_http_socket then + receive_message_and_send_reply (l_thread_http_socket) + l_thread_http_socket.cleanup + check + socket_closed: l_thread_http_socket.is_closed + end + end + end + end + l_http_socket.cleanup + check + socket_is_closed: l_http_socket.is_closed + end + end + print ("HTTP Connection Server ends.") + rescue + print ("HTTP Connection Server shutdown due to exception. Please relaunch manually.") + + if attached l_http_socket as ll_http_socket then + ll_http_socket.cleanup + check + socket_is_closed: ll_http_socket.is_closed + end + end + is_stop_requested := True + retry + end + +feature -- Access + + is_stop_requested: BOOLEAN + -- Set true to stop accept loop + +feature {NONE} -- Access + + main_server: HTTP_SERVER + -- The main server object + + main_server_configuration: HTTP_SERVER_CONFIGURATION + -- The main server's configuration + do + Result := main_server.configuration + end + + Max_fragments: INTEGER = 1000 + -- Defines the maximum number of fragments that can be received + +feature -- Status setting + + shutdown + -- Stops the thread + do + is_stop_requested := True + end + +feature -- Execution + + receive_message_and_send_reply (client_socket: TCP_STREAM_SOCKET) + require + socket_attached: client_socket /= Void +-- socket_valid: client_socket.is_open_read and then client_socket.is_open_write + a_http_socket:client_socket /= Void and then not client_socket.is_closed + deferred + end + +invariant + main_server_attached: main_server /= Void + +end diff --git a/server/http_protocol_handler.e b/server/http_protocol_handler.e index bb1995dd..6be59b67 100644 --- a/server/http_protocol_handler.e +++ b/server/http_protocol_handler.e @@ -70,7 +70,7 @@ feature -- Implementation done loop if socket.socket_ok then - done := receive_message_and_send_replay (socket) + done := receive_message_and_send_reply (socket) request_header_map.wipe_out else done := True @@ -80,7 +80,7 @@ feature -- Implementation io.put_new_line end - receive_message_and_send_replay (client_socket: NETWORK_STREAM_SOCKET): BOOLEAN + receive_message_and_send_reply (client_socket: NETWORK_STREAM_SOCKET): BOOLEAN require socket_attached: client_socket /= Void socket_valid: client_socket.is_open_read and then client_socket.is_open_write diff --git a/server/http_server.e b/server/http_server.e index a9e22a3b..3ef61c09 100644 --- a/server/http_server.e +++ b/server/http_server.e @@ -8,7 +8,7 @@ class HTTP_SERVER inherit - SHARED_DOCUMENT_ROOT + HTTP_SERVER_SHARED_CONFIGURATION create make @@ -20,7 +20,7 @@ feature -- Initialization configuration := cfg end - setup (a_http_handler : HTTP_CONNECTION_HANDLER) + setup (a_http_handler : HTTP_HANDLER) require a_http_handler_valid: a_http_handler /= Void do @@ -29,7 +29,7 @@ feature -- Initialization stop_requested := False set_server_configuration (configuration) a_http_handler.launch - run + a_http_handler.join end shutdown_server diff --git a/server/shared_document_root.e b/server/shared_document_root.e deleted file mode 100644 index cc516ee7..00000000 --- a/server/shared_document_root.e +++ /dev/null @@ -1,15 +0,0 @@ -note - description: "Summary description for {SHARED_DOCUMENT_ROOT}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - SHARED_DOCUMENT_ROOT - -obsolete "Use HTTP_SERVER_SHARED_CONFIGURATION" - -inherit - HTTP_SERVER_SHARED_CONFIGURATION - -end