diff --git a/library/protocol/http/src/http_constants.e b/library/protocol/http/src/http_constants.e index 9569b03f..577def21 100644 --- a/library/protocol/http/src/http_constants.e +++ b/library/protocol/http/src/http_constants.e @@ -48,6 +48,7 @@ feature -- Content type feature -- Server http_version_1_0: STRING = "HTTP/1.0" + http_version_1_1: STRING = "HTTP/1.1" http_host_header: STRING = "Host" http_authorization_header: STRING = "Authorization: " http_end_of_header_line: STRING = "%R%N" @@ -85,7 +86,7 @@ feature -- Misc note - copyright: "Copyright (c) 1984-2011, Eiffel Software and others" + copyright: "2011-2011, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/ewsgi/src/context/gw_request.e b/library/server/ewsgi/src/context/gw_request.e index a4bebea2..1bea727c 100644 --- a/library/server/ewsgi/src/context/gw_request.e +++ b/library/server/ewsgi/src/context/gw_request.e @@ -40,8 +40,7 @@ feature -- Access: environment variables -- Environment variable related to `a_name' require a_name_valid: a_name /= Void and then not a_name.is_empty - do - Result := environment.variable (a_name) + deferred end feature -- Access: execution variables @@ -55,36 +54,37 @@ feature -- Access: execution variables -- Execution variable related to `a_name' require a_name_valid: a_name /= Void and then not a_name.is_empty - do - Result := execution_variables.variable (a_name) + deferred end feature -- URL Parameters - parameter (n: STRING): detachable STRING_32 - -- Parameter for name `n'. - do - Result := parameters.variable (n) - end - parameters: GW_REQUEST_VARIABLES -- Variables extracted from QUERY_STRING deferred end -feature -- Form fields and related - - form_field (n: STRING): detachable STRING_32 - -- Field for name `n'. - do - Result := form_fields.variable (n) + parameter (a_name: STRING): detachable STRING_32 + -- Parameter for name `n'. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred end +feature -- Form fields and related + form_fields: GW_REQUEST_VARIABLES -- Variables sent by POST request deferred end + form_field (a_name: STRING): detachable STRING_32 + -- Field for name `a_name'. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred + end + uploaded_files: HASH_TABLE [GW_UPLOADED_FILE_DATA, STRING] -- Table of uploaded files information --| name: original path from the user @@ -98,17 +98,18 @@ feature -- Form fields and related feature -- Cookies - cookies_variable (n: STRING): detachable STRING - -- Field for name `n'. - do - Result := cookies_variables.item (n) - end - cookies_variables: HASH_TABLE [STRING, STRING] -- Expanded cookies variable deferred end + cookies_variable (a_name: STRING): detachable STRING + -- Field for name `a_name'. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred + end + cookies: HASH_TABLE [GW_COOKIE, STRING] -- Cookies Information deferred @@ -120,85 +121,16 @@ feature -- Access: global variable -- Table containing all the various variables -- Warning: this is computed each time, if you change the content of other containers -- this won't update this Result's content, unless you query it again - local - vars: HASH_TABLE [STRING_GENERAL, STRING_GENERAL] - do - create Result.make (100) - - vars := execution_variables - from - vars.start - until - vars.after - loop - Result.put (vars.item_for_iteration, vars.key_for_iteration) - vars.forth - end - - vars := environment.table - from - vars.start - until - vars.after - loop - Result.put (vars.item_for_iteration, vars.key_for_iteration) - vars.forth - end - - vars := parameters.table - from - vars.start - until - vars.after - loop - Result.put (vars.item_for_iteration, vars.key_for_iteration) - vars.forth - end - - vars := form_fields.table - from - vars.start - until - vars.after - loop - Result.put (vars.item_for_iteration, vars.key_for_iteration) - vars.forth - end - - vars := cookies_variables - from - vars.start - until - vars.after - loop - Result.put (vars.item_for_iteration, vars.key_for_iteration) - vars.forth - end + deferred end - variable (n8: STRING_8): detachable STRING_32 - -- Variable named `n' from any of the variables container + variable (a_name: STRING_8): detachable STRING_32 + -- Variable named `a_name' from any of the variables container -- and following a specific order -- execution, environment, get, post, cookies - local - s: detachable STRING_GENERAL - do - s := execution_variable (n8) - if s = Void then - s := environment_variable (n8) - if s = Void then - s := parameter (n8) - if s = Void then - s := form_field (n8) - if s = Void then - s := cookies_variable (n8) - end - end - end - end - if s /= Void then - Result := s.as_string_32 - end + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred end feature -- Uploaded File Handling @@ -208,15 +140,6 @@ feature -- Uploaded File Handling deferred end -feature {NONE} -- Temporary File handling - - delete_uploaded_file (f: GW_UPLOADED_FILE_DATA) - -- Delete file `f' - require - f_valid: f /= Void - deferred - end - feature -- URL Utility absolute_script_url (a_path: STRING): STRING diff --git a/library/server/ewsgi/src/gw_application.e b/library/server/ewsgi/src/gw_application.e index 1e3728f5..be0b0c01 100644 --- a/library/server/ewsgi/src/gw_application.e +++ b/library/server/ewsgi/src/gw_application.e @@ -61,7 +61,7 @@ feature {NONE} -- Execution and a_exception /= Void and then attached a_exception.exception_trace as l_trace then res.write_header ({HTTP_STATUS_CODE}.internal_server_error, Void) - res.write ("
" + l_trace + "
") + res.write_string ("
" + l_trace + "
") end post_execute (req, res) end diff --git a/library/server/ewsgi/src/implementation/gw_request_imp.e b/library/server/ewsgi/src/implementation/gw_request_imp.e index d336736b..d82e0a85 100644 --- a/library/server/ewsgi/src/implementation/gw_request_imp.e +++ b/library/server/ewsgi/src/implementation/gw_request_imp.e @@ -98,6 +98,12 @@ feature -- Access: environment variables environment: GW_ENVIRONMENT -- Environment variables + environment_variable (a_name: STRING): detachable STRING + -- Environment variable related to `a_name' + do + Result := environment.variable (a_name) + end + content_length: INTEGER -- Extracted Content-Length value @@ -106,6 +112,12 @@ feature -- Access: execution variables execution_variables: GW_EXECUTION_VARIABLES -- Execution variables set by the application + execution_variable (a_name: STRING): detachable STRING_32 + -- Execution variable related to `a_name' + do + Result := execution_variables.variable (a_name) + end + feature -- URL parameters parameters: GW_REQUEST_VARIABLES @@ -141,6 +153,12 @@ feature -- URL parameters Result := vars end + parameter (a_name: STRING): detachable STRING_32 + -- Parameter for name `n'. + do + Result := parameters.variable (a_name) + end + feature -- Form fields and related form_fields: GW_REQUEST_VARIABLES @@ -178,6 +196,12 @@ feature -- Form fields and related Result := vars end + form_field (a_name: STRING): detachable STRING_32 + -- Field for name `a_name'. + do + Result := form_fields.variable (a_name) + end + uploaded_files: HASH_TABLE [GW_UPLOADED_FILE_DATA, STRING] -- Table of uploaded files information --| name: original path from the user @@ -218,6 +242,12 @@ feature -- Cookies end end + cookies_variable (a_name: STRING): detachable STRING + -- Field for name `a_name'. + do + Result := cookies_variables.item (a_name) + end + cookies: HASH_TABLE [GW_COOKIE, STRING] -- Cookies Information local @@ -261,6 +291,94 @@ feature -- Cookies Result := l_cookies end +feature -- Access: global variable + + variables: HASH_TABLE [STRING_32, STRING_32] + -- Table containing all the various variables + -- Warning: this is computed each time, if you change the content of other containers + -- this won't update this Result's content, unless you query it again + local + vars: HASH_TABLE [STRING_GENERAL, STRING_GENERAL] + do + create Result.make (100) + + vars := execution_variables + from + vars.start + until + vars.after + loop + Result.put (vars.item_for_iteration, vars.key_for_iteration) + vars.forth + end + + vars := environment.table + from + vars.start + until + vars.after + loop + Result.put (vars.item_for_iteration, vars.key_for_iteration) + vars.forth + end + + vars := parameters.table + from + vars.start + until + vars.after + loop + Result.put (vars.item_for_iteration, vars.key_for_iteration) + vars.forth + end + + vars := form_fields.table + from + vars.start + until + vars.after + loop + Result.put (vars.item_for_iteration, vars.key_for_iteration) + vars.forth + end + + vars := cookies_variables + from + vars.start + until + vars.after + loop + Result.put (vars.item_for_iteration, vars.key_for_iteration) + vars.forth + end + end + + variable (a_name: STRING_8): detachable STRING_32 + -- Variable named `a_name' from any of the variables container + -- and following a specific order + -- execution, environment, get, post, cookies + local + s: detachable STRING_GENERAL + do + s := execution_variable (a_name) + if s = Void then + s := environment_variable (a_name) + if s = Void then + s := parameter (a_name) + if s = Void then + s := form_field (a_name) + if s = Void then + s := cookies_variable (a_name) + end + end + end + end + if s /= Void then + Result := s.as_string_32 + end + end + + feature -- Access extra information request_time: detachable DATE_TIME @@ -397,6 +515,8 @@ feature {NONE} -- Temporary File handling delete_uploaded_file (uf: GW_UPLOADED_FILE_DATA) -- Delete file `a_filename' + require + uf_valid: uf /= Void local f: RAW_FILE do diff --git a/library/server/ewsgi/src/response/gw_header.e b/library/server/ewsgi/src/response/gw_header.e index 156b6165..a223d0a0 100644 --- a/library/server/ewsgi/src/response/gw_header.e +++ b/library/server/ewsgi/src/response/gw_header.e @@ -1,5 +1,9 @@ note - description: "Summary description for {GW_HEADER}." + description: "[ + Summary description for {GW_HEADER}. + + Note the return status code is not part of the HTTP header + ]" legal: "See notice at end of class." status: "See notice at end of class." date: "$Date$" @@ -8,9 +12,6 @@ note class GW_HEADER -inherit - HTTP_STATUS_CODE_MESSAGES - create make @@ -20,16 +21,12 @@ feature {NONE} -- Initialization -- Initialize current do create {ARRAYED_LIST [STRING]} headers.make (3) - http_version := "HTTP/1.1" end feature -- Recycle recycle do - status_code := 0 - status_message := Void - http_version := "HTTP/1.1" headers.wipe_out end @@ -38,33 +35,22 @@ feature -- Access headers: LIST [STRING] -- Header's lines - send_to (a_output: GW_OUTPUT_STREAM) + send_to (res: GW_RESPONSE) -- Send Current string representation to `a_output' do - a_output.put_string (string) - --| TO OPTIMIZE + res.write_string (string) + --| Could be optimized end string: STRING -- String representation of the headers local l_headers: like headers - h: STRING do create Result.make (32) - create h.make (16) - h.append_string (http_version) - h.append_character (' ') - h.append_integer (status_code) - h.append_character (' ') - if attached status_message as l_status_message then - h.append_string (l_status_message) - end - append_line_to (h, Result) - l_headers := headers if l_headers.is_empty then - put_content_type_text_html + put_content_type_text_html -- See if this make sense to define a default content-type else from l_headers.start @@ -206,23 +192,6 @@ feature -- Content related header end end -feature -- Status, ... - - status_code: INTEGER - -- Status - - status_message: detachable STRING - -- Optional reason - - http_version: STRING - -- HTTP version - - put_status (a_code: INTEGER) - do - status_code := a_code - status_message := http_status_code_message (a_code) - end - feature -- Others put_expires (n: INTEGER) @@ -250,21 +219,11 @@ feature -- Redirection put_redirection (a_location: STRING; a_code: INTEGER) do - if a_code > 0 then - put_status (a_code) - else - put_status (302) -- Found - end put_header_key_value ("Location", a_location) end put_refresh (a_location: STRING; a_timeout: INTEGER; a_code: INTEGER) do - if a_code > 0 then - put_status (a_code) - else - put_status (200) -- Ok - end put_header_key_value ("Refresh", a_timeout.out + "; url=" + a_location) end diff --git a/library/server/ewsgi/src/response/gw_response.e b/library/server/ewsgi/src/response/gw_response.e index 3d281a18..ce5e85d0 100644 --- a/library/server/ewsgi/src/response/gw_response.e +++ b/library/server/ewsgi/src/response/gw_response.e @@ -16,21 +16,59 @@ feature {GW_APPLICATION} -- Commit a_output_stream.flush end -feature -- Output operation +feature {NONE} -- Core output operation write (s: STRING) - -- Send the content of `s' + -- Send the string `s' + -- this can be used for header and body deferred end +feature -- Status setting + + is_status_set: BOOLEAN + do + Result := status_code /= 0 + end + + set_status_code (a_code: INTEGER) + -- Set response status code + -- Should be done before sending any data back to the client + require + status_not_set: not is_status_set + do + status_code := a_code + write_status (a_code) + ensure + status_set: is_status_set + end + + status_code: INTEGER + -- Response status + +feature {NONE} -- Status output + + write_status (a_code: INTEGER) + -- Send status line for `a_code' + deferred + ensure + status_set: is_status_set + end + +feature -- Output operation + write_string (s: STRING) - -- Send the content of `s' + -- Send the string `s' + require + status_set: is_status_set do write (s) end write_file_content (fn: STRING) -- Send the content of file `fn' + require + status_set: is_status_set deferred end @@ -38,17 +76,21 @@ feature -- Header output operation write_header_object (h: GW_HEADER) -- Send `header' to `output'. + require + status_set: is_status_set deferred end - write_header (a_status: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]]) + write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]]) -- Send headers with status `a_status', and headers from `a_headers' + require + status_not_set: not is_status_set local h: GW_HEADER i,n: INTEGER do + set_status_code (a_status_code) create h.make - h.put_status (a_status) if a_headers /= Void then from i := a_headers.lower @@ -61,13 +103,8 @@ feature -- Header output operation end end write_header_object (h) - end - - write_header_line (s: STRING) - -- Send `s' to http client as header line - do - write (s) - write ("%R%N") + ensure + status_set: is_status_set end note diff --git a/library/server/ewsgi/src/response/gw_response_imp.e b/library/server/ewsgi/src/response/gw_response_imp.e index d0112fa5..b6a74dfc 100644 --- a/library/server/ewsgi/src/response/gw_response_imp.e +++ b/library/server/ewsgi/src/response/gw_response_imp.e @@ -20,7 +20,7 @@ feature {NONE} -- Initialization output := a_output end -feature -- Output operation +feature {NONE} -- Core output operation write (s: STRING) -- Send the content of `s' @@ -28,6 +28,16 @@ feature -- Output operation output.put_string (s) end +feature {NONE} -- Status output + + write_status (a_code: INTEGER) + -- Send status line for `a_code' + do + output.put_status_line (a_code) + end + +feature -- Output operation + write_file_content (fn: STRING) -- Send the content of file `fn' do @@ -37,7 +47,7 @@ feature -- Output operation write_header_object (h: GW_HEADER) -- Send `header' to `output'. do - h.send_to (output) + h.send_to (Current) end feature {NONE} -- Implementation: Access diff --git a/library/server/ewsgi/src/stream/gw_output_stream.e b/library/server/ewsgi/src/stream/gw_output_stream.e index 0ed8d0af..9cee6145 100644 --- a/library/server/ewsgi/src/stream/gw_output_stream.e +++ b/library/server/ewsgi/src/stream/gw_output_stream.e @@ -10,6 +10,14 @@ note deferred class GW_OUTPUT_STREAM +inherit + ANY + + HTTP_STATUS_CODE_MESSAGES + export + {NONE} all + end + feature -- Core operation put_string (s: STRING_8) @@ -24,8 +32,36 @@ feature -- Core operation do end +feature -- Status writing + + put_status_line (a_code: INTEGER) + -- Put status code line for `a_code' + --| Note this is a default implemantation, and could be redefined + --| for instance in relation to NPH CGI script + local + s: STRING + do + create s.make (16) + s.append ({HTTP_CONSTANTS}.http_version_1_1) + s.append_character (' ') + s.append_integer (a_code) + if attached http_status_code_message (a_code) as l_status_message then + s.append_character (' ') + s.append_string (l_status_message) + end + put_header_line (s) + end + feature -- Basic operation + put_substring (s: STRING; start_index, end_index: INTEGER) + -- Write `s[start_index:end_index]' into the output stream + require + s_not_empty: s /= Void and then not s.is_empty + do + put_string (s.substring (start_index, end_index)) + end + put_file_content (fn: STRING) -- Send the content of file `fn' local @@ -38,33 +74,13 @@ feature -- Basic operation until f.exhausted loop - f.read_stream (1024) + f.read_stream (4096) put_string (f.last_string) end f.close end end - put_header (a_status: INTEGER; a_headers: ARRAY [TUPLE [key: STRING; value: STRING]]) - -- Send headers with status `a_status', and headers from `a_headers' - local - h: GW_HEADER - i,n: INTEGER - do - create h.make - h.put_status (a_status) - from - i := a_headers.lower - n := a_headers.upper - until - i > n - loop - h.put_header_key_value (a_headers[i].key, a_headers[i].value) - i := i + 1 - end - put_string (h.string) - end - put_header_line (s: STRING) -- Send `s' to http client as header line do