From c25ea52bf8e8d63804146ca422f4df2f340a3d15 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 1 Apr 2015 22:41:43 +0200 Subject: [PATCH] Better support for HTTP/1.0 and also related to persistent connection. --- .../src/httpd/httpd_request_handler_i.e | 26 +++--- .../wgi_httpd_request_handler.e | 7 ++ .../src/wgi_standalone_output_stream.e | 7 +- .../src/wgi_standalone_response_stream.e | 92 +++++++++++++++---- .../specification/stream/wgi_output_stream.e | 13 +++ 5 files changed, 116 insertions(+), 29 deletions(-) diff --git a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_i.e b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_i.e index 1f987381..8e701145 100644 --- a/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_i.e +++ b/library/server/ewsgi/connectors/standalone/src/httpd/httpd_request_handler_i.e @@ -39,7 +39,7 @@ feature {NONE} -- Initialization create request_header.make_empty create request_header_map.make (10) - keep_alive_enabled := False + keep_alive_requested := False end feature -- Status report @@ -85,8 +85,10 @@ feature -- Access remote_info: detachable TUPLE [addr: STRING; hostname: STRING; port: INTEGER] -- Information related to remote client - keep_alive_enabled: BOOLEAN - -- Inside a persistent connection? + keep_alive_requested: BOOLEAN + -- Persistent connection requested? + -- either has "Connection: Keep-Alive" header, + -- or is HTTP/1.1 and no header "Connection: close". is_http_version_1_0: BOOLEAN do @@ -117,7 +119,6 @@ feature -- Change set_is_verbose (b: BOOLEAN) do is_verbose := b - print ("set_is_verbose " + b.out + "%N") end feature -- Execution @@ -164,7 +165,9 @@ feature -- Execution n := n + 1 -- FIXME: it seems to be called one more time, mostly to see this is done. execute_request - l_exit := has_error or l_socket.is_closed or not l_socket.is_open_read or not keep_alive_enabled + l_exit := not {HTTPD_SERVER}.is_persistent_connection_supported + or has_error or l_socket.is_closed or not l_socket.is_open_read + or not keep_alive_requested reset_request end end @@ -297,20 +300,19 @@ feature -- Parsing line := next_line (a_socket) end end - if not {HTTPD_SERVER}.is_persistent_connection_supported then - keep_alive_enabled := False - elseif is_http_version_1_0 then - keep_alive_enabled := attached request_header_map.item ("Connection") as l_connection and then + -- Except for HTTP/1.0, persistent connection is the default. + keep_alive_requested := True + if is_http_version_1_0 then + keep_alive_requested := attached request_header_map.item ("Connection") as l_connection and then l_connection.is_case_insensitive_equal_general ("keep-alive") else -- By default HTTP:1/1 support persistent connection. if attached request_header_map.item ("Connection") as l_connection then - print ("Connection -> " + l_connection + "%N") if l_connection.is_case_insensitive_equal_general ("close") then - keep_alive_enabled := False + keep_alive_requested := False end else - keep_alive_enabled := True + keep_alive_requested := True end end end diff --git a/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler.e b/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler.e index e00743a3..0f4ad723 100644 --- a/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler.e +++ b/library/server/ewsgi/connectors/standalone/src/implementation/wgi_httpd_request_handler.e @@ -76,6 +76,13 @@ feature -- Request processing create req.make (httpd_environment (a_socket), l_input, connector) create res.make (l_output, l_error) + if is_http_version_1_0 then + l_output.set_http_version ({HTTP_CONSTANTS}.http_version_1_0) + res.set_http_version_1_0 + else + l_output.set_http_version (version) + end + res.set_is_persistent_connection_requested (keep_alive_requested) req.set_meta_string_variable ("RAW_HEADER_DATA", request_header) diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_output_stream.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_output_stream.e index 8fbeb459..29c38300 100644 --- a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_output_stream.e +++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_output_stream.e @@ -50,7 +50,12 @@ feature -- Status writing m: detachable READABLE_STRING_8 do create s.make (16) - s.append ({HTTP_CONSTANTS}.http_version_1_1) + if attached http_version as v then + s.append (v) + else + -- Default to 1.1 + s.append ({HTTP_CONSTANTS}.http_version_1_1) + end s.append_character (' ') s.append_integer (a_code) m := a_reason_phrase diff --git a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_response_stream.e b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_response_stream.e index 1823bcaa..b9ccf59a 100644 --- a/library/server/ewsgi/connectors/standalone/src/wgi_standalone_response_stream.e +++ b/library/server/ewsgi/connectors/standalone/src/wgi_standalone_response_stream.e @@ -18,6 +18,28 @@ inherit create make +feature -- Settings + + is_http_version_1_0: BOOLEAN + -- Is associated request using HTTP/1.0 ? + + is_persistent_connection_requested: BOOLEAN + -- Is persistent connection requested? + +feature -- Settings change + + set_http_version_1_0 + -- Set associated request is using HTTP/1.0. + do + is_http_version_1_0 := True + end + + set_is_persistent_connection_requested (b: BOOLEAN) + -- Set `is_persistent_connection_requested' to `b'. + do + is_persistent_connection_requested := b + end + feature -- Header output operation put_header_text (a_text: READABLE_STRING_8) @@ -30,26 +52,64 @@ feature -- Header output operation o := output create s.make_from_string (a_text) - -- FIXME: check if HTTP versions 1.0 or else. - i := s.substring_index ("%NConnection:", 1) - if i > 0 then - j := s.index_of ('%R', i + 12) - end if {HTTPD_SERVER}.is_persistent_connection_supported then - if i = 0 then - s.append ("Connection: Keep-Alive") - s.append (o.crlf) - end - else - -- standalone does not support persistent connection for now - if j > 0 then - l_connection := s.substring (i + 12, j - 1) - l_connection.adjust - if not l_connection.is_case_insensitive_equal_general ("close") then - s.replace_substring ("Connection: close", i + 1, j - 1) + -- Current standalone support persistent connection. + -- If HTTP/1.1: + -- by default all connection are persistent + -- then no need to return "Connection:" header + -- unless header has "Connection: close" + -- then return "Connection: close" + -- If HTTP/1.0: + -- by default, connection is not persistent + -- unless header has "Connection: Keep-Alive" + -- then return "Connection: Keep-Alive" + -- if header has "Connection: Close" + -- then return "Connection: close" + if is_persistent_connection_requested then + if is_http_version_1_0 then + if i = 0 then + -- Existing response header does not has "Connection: " header. + s.append ("Connection: Keep-Alive") + s.append (o.crlf) + else + -- Do not override the application decision. + end end else + -- If HTTP/1.1 and persistent connection is not requested, + -- then return "close" + if i = 0 and not is_http_version_1_0 then + -- Existing response header does not has "Connection: " header. + s.append ("Connection: close") + s.append (o.crlf) + else + -- Do not override the application decision. + end + end + else + -- persistent connection support is disabled. + -- Return "Connection: close" in any case. + -- Except for HTTP/1.0 since not required. + if i > 0 then + j := s.index_of ('%R', i + 12) + end + if j > 0 then + -- Replace existing "Connection:" header with "Connection: close" + l_connection := s.substring (i + 12, j - 1) + l_connection.adjust + if + not is_http_version_1_0 and + not l_connection.is_case_insensitive_equal_general ("close") + then + s.replace_substring ("Connection: close", i + 1, j - 1) + end + elseif not is_http_version_1_0 then + -- HTTP/1.1: always return "close" since persistent connection is not supported. + s.append ("Connection: close") + s.append (o.crlf) + elseif is_persistent_connection_requested then + -- For HTTP/1.0, return "Connection: close", only if client sent a "Connection: Keep-Alive" s.append ("Connection: close") s.append (o.crlf) end diff --git a/library/server/ewsgi/specification/stream/wgi_output_stream.e b/library/server/ewsgi/specification/stream/wgi_output_stream.e index aa731ece..8d02bdea 100644 --- a/library/server/ewsgi/specification/stream/wgi_output_stream.e +++ b/library/server/ewsgi/specification/stream/wgi_output_stream.e @@ -74,6 +74,9 @@ feature -- Status writing feature -- Status report + http_version: detachable READABLE_STRING_8 + -- Optional HTTP version. + is_available: BOOLEAN -- Is output available? --| i.e: no issue with associated output stream, like closed socket, or related? @@ -85,6 +88,16 @@ feature -- Status report deferred end +feature -- Element change + + set_http_version (v: like http_version) + -- Set `http_version' to `v'. + require + valid_version: v /= Void implies v.starts_with ("HTTP/") + do + http_version := v + end + feature -- Basic operations flush