Nino connector:
- fixed issue related to `ready_for_reading' now use the `try_...' variant - for now Nino does not support persistent connection, then we have to respond with "Connection: close" REQUEST_FILE_SYSTEM_HANDLER: - added not_found_handler and access_denied_handler, so that the user can customize related response WSF_REQUEST and WSF_VALUE: - modified how uploaded file are handled, fixed various issues, and added WSF_UPLOADED_FILE (it is a WSF_VALUE) WSF_VALUE: - added change_name (a_name: like name) - added url_encoded_name to other WSF_values WSF_REQUEST: - added `destroy' to perform end of request cleaning (such as deleting temp uploaded files) - renamed `raw_post_data_recorded' as `raw_input_data_recorded', and related feature - do not store the RAW_POST_DATA in meta variable anymore, but in WSF_REQUEST.raw_input_data is asked Added WSF_HTML_PAGE_RESPONSE to help user WSF_REPONSE.redirect_... now use "temp_redirect" as default instead of "moved_permanently" which is specific usage Removed many obsolete features.
This commit is contained in:
423
library/server/ewsgi/src/implementation/wgi_request_from_table.e
Normal file
423
library/server/ewsgi/src/implementation/wgi_request_from_table.e
Normal file
@@ -0,0 +1,423 @@
|
||||
note
|
||||
description: "[
|
||||
Request created from a hash_table of meta variables
|
||||
]"
|
||||
specification: "EWSGI specification https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/EWSGI-specification"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WGI_REQUEST_FROM_TABLE
|
||||
|
||||
inherit
|
||||
WGI_REQUEST
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_vars: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]; a_input: like input; a_wgi_connector: like wgi_connector)
|
||||
require
|
||||
vars_attached: a_vars /= Void
|
||||
do
|
||||
wgi_connector := a_wgi_connector
|
||||
input := a_input
|
||||
set_meta_variables (a_vars)
|
||||
update_path_info
|
||||
if attached http_transfer_encoding as l_transfer_encoding and then l_transfer_encoding.same_string ("chunked") then
|
||||
is_chunked_input := True
|
||||
create chunked_input.make (a_input)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access: Input
|
||||
|
||||
is_chunked_input: BOOLEAN
|
||||
-- Is request using chunked transfer-encoding?
|
||||
|
||||
input: WGI_INPUT_STREAM
|
||||
-- Server input channel
|
||||
|
||||
chunked_input: detachable WGI_CHUNKED_INPUT_STREAM
|
||||
-- Chunked server input channel
|
||||
|
||||
feature -- EWSGI access
|
||||
|
||||
wgi_version: STRING = "0.1"
|
||||
|
||||
wgi_implementation: STRING = "Eiffel Web Framework 0.1"
|
||||
|
||||
wgi_connector: WGI_CONNECTOR
|
||||
|
||||
feature -- Access: CGI meta parameters
|
||||
|
||||
meta_variables: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
||||
-- CGI Environment parameters
|
||||
|
||||
meta_variable (a_name: READABLE_STRING_8): detachable READABLE_STRING_8
|
||||
-- CGI meta variable related to `a_name'
|
||||
do
|
||||
Result := meta_variables.item (a_name)
|
||||
end
|
||||
|
||||
meta_string_variable_or_default (a_name: READABLE_STRING_8; a_default: READABLE_STRING_8; use_default_when_empty: BOOLEAN): READABLE_STRING_8
|
||||
-- Value for meta parameter `a_name'
|
||||
-- If not found, return `a_default'
|
||||
require
|
||||
a_name_not_empty: a_name /= Void and then not a_name.is_empty
|
||||
do
|
||||
if attached meta_variable (a_name) as val then
|
||||
Result := val.string
|
||||
if use_default_when_empty and then Result.is_empty then
|
||||
Result := a_default
|
||||
end
|
||||
else
|
||||
Result := a_default
|
||||
end
|
||||
end
|
||||
|
||||
set_meta_string_variable (a_name: READABLE_STRING_8; a_value: READABLE_STRING_8)
|
||||
do
|
||||
meta_variables.force (a_value, a_name)
|
||||
ensure
|
||||
param_set: attached meta_variable (a_name) as val and then val ~ a_value
|
||||
end
|
||||
|
||||
unset_meta_variable (a_name: READABLE_STRING_8)
|
||||
do
|
||||
meta_variables.remove (a_name)
|
||||
ensure
|
||||
param_unset: meta_variable (a_name) = Void
|
||||
end
|
||||
|
||||
feature -- Access: CGI meta parameters - 1.1
|
||||
|
||||
auth_type: detachable READABLE_STRING_8
|
||||
|
||||
content_length: detachable READABLE_STRING_8
|
||||
|
||||
content_type: detachable READABLE_STRING_8
|
||||
|
||||
gateway_interface: READABLE_STRING_8
|
||||
do
|
||||
Result := meta_string_variable_or_default ({WGI_META_NAMES}.gateway_interface, "", False)
|
||||
end
|
||||
|
||||
path_info: READABLE_STRING_8
|
||||
-- <Precursor/>
|
||||
--
|
||||
--| For instance, if the current script was accessed via the URL
|
||||
--| http://www.example.com/eiffel/path_info.exe/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain /some/stuff.
|
||||
--|
|
||||
--| Note that is the PATH_INFO variable does not exists, the `path_info' value will be empty
|
||||
|
||||
path_translated: detachable READABLE_STRING_8
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.path_translated)
|
||||
end
|
||||
|
||||
query_string: READABLE_STRING_8
|
||||
|
||||
remote_addr: READABLE_STRING_8
|
||||
|
||||
remote_host: READABLE_STRING_8
|
||||
|
||||
remote_ident: detachable READABLE_STRING_8
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.remote_ident)
|
||||
end
|
||||
|
||||
remote_user: detachable READABLE_STRING_8
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.remote_user)
|
||||
end
|
||||
|
||||
request_method: READABLE_STRING_8
|
||||
|
||||
script_name: READABLE_STRING_8
|
||||
|
||||
server_name: READABLE_STRING_8
|
||||
|
||||
server_port: INTEGER
|
||||
|
||||
server_protocol: READABLE_STRING_8
|
||||
do
|
||||
Result := meta_string_variable_or_default ({WGI_META_NAMES}.server_protocol, "HTTP/1.0", True)
|
||||
end
|
||||
|
||||
server_software: READABLE_STRING_8
|
||||
do
|
||||
Result := meta_string_variable_or_default ({WGI_META_NAMES}.server_software, "Unknown Server", True)
|
||||
end
|
||||
|
||||
feature -- Access: HTTP_* CGI meta parameters - 1.1
|
||||
|
||||
http_accept: detachable READABLE_STRING_8
|
||||
-- Contents of the Accept: header from the current request, if there is one.
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_accept)
|
||||
end
|
||||
|
||||
http_accept_charset: detachable READABLE_STRING_8
|
||||
-- Contents of the Accept-Charset: header from the current request, if there is one.
|
||||
-- Example: 'iso-8859-1,*,utf-8'.
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_accept_charset)
|
||||
end
|
||||
|
||||
http_accept_encoding: detachable READABLE_STRING_8
|
||||
-- Contents of the Accept-Encoding: header from the current request, if there is one.
|
||||
-- Example: 'gzip'.
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_accept_encoding)
|
||||
end
|
||||
|
||||
http_accept_language: detachable READABLE_STRING_8
|
||||
-- Contents of the Accept-Language: header from the current request, if there is one.
|
||||
-- Example: 'en'.
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_accept_language)
|
||||
end
|
||||
|
||||
http_connection: detachable READABLE_STRING_8
|
||||
-- Contents of the Connection: header from the current request, if there is one.
|
||||
-- Example: 'Keep-Alive'.
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_connection)
|
||||
end
|
||||
|
||||
http_host: detachable READABLE_STRING_8
|
||||
-- Contents of the Host: header from the current request, if there is one.
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_host)
|
||||
end
|
||||
|
||||
http_referer: detachable READABLE_STRING_8
|
||||
-- The address of the page (if any) which referred the user agent to the current page.
|
||||
-- This is set by the user agent.
|
||||
-- Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature.
|
||||
-- In short, it cannot really be trusted.
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_referer)
|
||||
end
|
||||
|
||||
http_user_agent: detachable READABLE_STRING_8
|
||||
-- Contents of the User-Agent: header from the current request, if there is one.
|
||||
-- This is a string denoting the user agent being which is accessing the page.
|
||||
-- A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586).
|
||||
-- Among other things, you can use this value to tailor your page's
|
||||
-- output to the capabilities of the user agent.
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_user_agent)
|
||||
end
|
||||
|
||||
http_authorization: detachable READABLE_STRING_8
|
||||
-- Contents of the Authorization: header from the current request, if there is one.
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_authorization)
|
||||
end
|
||||
|
||||
http_transfer_encoding: detachable READABLE_STRING_8
|
||||
-- Transfer-Encoding
|
||||
-- for instance chunked
|
||||
do
|
||||
Result := meta_string_variable ({WGI_META_NAMES}.http_transfer_encoding)
|
||||
end
|
||||
|
||||
feature -- Access: Extension to CGI meta parameters - 1.1
|
||||
|
||||
request_uri: READABLE_STRING_8
|
||||
-- The URI which was given in order to access this page; for instance, '/index.html'.
|
||||
|
||||
orig_path_info: detachable READABLE_STRING_8
|
||||
-- Original version of `path_info' before processed by Current environment
|
||||
|
||||
feature {NONE} -- Element change: CGI meta parameter related to PATH_INFO
|
||||
|
||||
set_meta_variables (a_vars: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8])
|
||||
-- Fill with variable from `a_vars'
|
||||
local
|
||||
s: like meta_string_variable
|
||||
table: HASH_TABLE [READABLE_STRING_8, READABLE_STRING_8]
|
||||
l_query_string: like query_string
|
||||
l_request_uri: detachable STRING_32
|
||||
do
|
||||
create {STRING_8} empty_string.make_empty
|
||||
|
||||
create table.make (a_vars.count)
|
||||
table.compare_objects
|
||||
meta_variables := table
|
||||
from
|
||||
a_vars.start
|
||||
until
|
||||
a_vars.after
|
||||
loop
|
||||
table.force (a_vars.item_for_iteration, a_vars.key_for_iteration)
|
||||
a_vars.forth
|
||||
end
|
||||
|
||||
--| QUERY_STRING
|
||||
l_query_string := meta_string_variable_or_default ({WGI_META_NAMES}.query_string, empty_string, False)
|
||||
query_string := l_query_string
|
||||
|
||||
--| REQUEST_METHOD
|
||||
request_method := meta_string_variable_or_default ({WGI_META_NAMES}.request_method, empty_string, False)
|
||||
|
||||
--| CONTENT_TYPE
|
||||
s := meta_string_variable ({WGI_META_NAMES}.content_type)
|
||||
if s /= Void and then not s.is_empty then
|
||||
content_type := s
|
||||
else
|
||||
content_type := Void
|
||||
end
|
||||
|
||||
--| CONTENT_LENGTH
|
||||
content_length := meta_string_variable ({WGI_META_NAMES}.content_length)
|
||||
|
||||
--| PATH_INFO
|
||||
path_info := meta_string_variable_or_default ({WGI_META_NAMES}.path_info, empty_string, False)
|
||||
|
||||
--| SERVER_NAME
|
||||
server_name := meta_string_variable_or_default ({WGI_META_NAMES}.server_name, empty_string, False)
|
||||
|
||||
--| SERVER_PORT
|
||||
s := meta_string_variable ({WGI_META_NAMES}.server_port)
|
||||
if s /= Void and then s.is_integer then
|
||||
server_port := s.to_integer
|
||||
else
|
||||
server_port := 80
|
||||
end
|
||||
|
||||
--| SCRIPT_NAME
|
||||
script_name := meta_string_variable_or_default ({WGI_META_NAMES}.script_name, empty_string, False)
|
||||
|
||||
--| REMOTE_ADDR
|
||||
remote_addr := meta_string_variable_or_default ({WGI_META_NAMES}.remote_addr, empty_string, False)
|
||||
|
||||
--| REMOTE_HOST
|
||||
remote_host := meta_string_variable_or_default ({WGI_META_NAMES}.remote_host, empty_string, False)
|
||||
|
||||
--| REQUEST_URI
|
||||
s := meta_string_variable ({WGI_META_NAMES}.request_uri)
|
||||
if s /= Void then
|
||||
l_request_uri := s
|
||||
else
|
||||
--| It might occur that REQUEST_URI is not available, so let's compute it from SCRIPT_NAME
|
||||
create l_request_uri.make_from_string (script_name)
|
||||
if not l_query_string.is_empty then
|
||||
l_request_uri.append_character ('?')
|
||||
l_request_uri.append (l_query_string)
|
||||
end
|
||||
end
|
||||
request_uri := single_slash_starting_string (l_request_uri)
|
||||
end
|
||||
|
||||
set_orig_path_info (s: READABLE_STRING_8)
|
||||
-- Set ORIG_PATH_INFO to `s'
|
||||
require
|
||||
s_attached: s /= Void
|
||||
do
|
||||
orig_path_info := s
|
||||
set_meta_string_variable ({WGI_META_NAMES}.orig_path_info, s)
|
||||
end
|
||||
|
||||
unset_orig_path_info
|
||||
-- Unset ORIG_PATH_INFO
|
||||
do
|
||||
orig_path_info := Void
|
||||
unset_meta_variable ({WGI_META_NAMES}.orig_path_info)
|
||||
ensure
|
||||
unset: meta_variable ({WGI_META_NAMES}.orig_path_info) = Void
|
||||
end
|
||||
|
||||
update_path_info
|
||||
-- Fix and update PATH_INFO value if needed
|
||||
local
|
||||
l_path_info: STRING
|
||||
do
|
||||
l_path_info := path_info
|
||||
--| Warning
|
||||
--| on IIS: we might have PATH_INFO = /sample.exe/foo/bar
|
||||
--| on apache: PATH_INFO = /foo/bar
|
||||
--| So, we might need to check with SCRIPT_NAME and remove it on IIS
|
||||
--| store original PATH_INFO in ORIG_PATH_INFO
|
||||
if l_path_info.is_empty then
|
||||
unset_orig_path_info
|
||||
else
|
||||
set_orig_path_info (l_path_info)
|
||||
if attached script_name as l_script_name then
|
||||
if l_path_info.starts_with (l_script_name) then
|
||||
path_info := l_path_info.substring (l_script_name.count + 1 , l_path_info.count)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: utilities
|
||||
|
||||
single_slash_starting_string (s: READABLE_STRING_8): STRING_8
|
||||
-- Return the string `s' (or twin) with one and only one starting slash
|
||||
local
|
||||
i, n: INTEGER
|
||||
do
|
||||
n := s.count
|
||||
if n > 1 then
|
||||
if s[1] /= '/' then
|
||||
create Result.make (1 + n)
|
||||
Result.append_character ('/')
|
||||
Result.append (s)
|
||||
elseif s[2] = '/' then
|
||||
--| We need to remove all starting slash, except one
|
||||
from
|
||||
i := 3
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
if s[i] /= '/' then
|
||||
n := 0 --| exit loop
|
||||
else
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
n := s.count
|
||||
check i >= 2 and i <= n end
|
||||
Result := s.substring (i - 1, s.count)
|
||||
else
|
||||
--| starts with one '/' and only one
|
||||
Result := s
|
||||
end
|
||||
elseif n = 1 then
|
||||
if s[1] = '/' then
|
||||
Result := s
|
||||
else
|
||||
create Result.make (2)
|
||||
Result.append_character ('/')
|
||||
Result.append (s)
|
||||
end
|
||||
else --| n = 0
|
||||
create Result.make_filled ('/', 1)
|
||||
end
|
||||
ensure
|
||||
one_starting_slash: Result[1] = '/' and (Result.count = 1 or else Result[2] /= '/')
|
||||
end
|
||||
|
||||
empty_string: READABLE_STRING_8
|
||||
-- Reusable empty string
|
||||
|
||||
invariant
|
||||
empty_string_unchanged: empty_string.is_empty
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
145
library/server/ewsgi/src/implementation/wgi_response_stream.e
Normal file
145
library/server/ewsgi/src/implementation/wgi_response_stream.e
Normal file
@@ -0,0 +1,145 @@
|
||||
note
|
||||
description: "[
|
||||
WGI Response implemented using stream buffer
|
||||
|
||||
]"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
WGI_RESPONSE_STREAM
|
||||
|
||||
inherit
|
||||
WGI_RESPONSE
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_output: like output)
|
||||
do
|
||||
output := a_output
|
||||
end
|
||||
|
||||
feature {WGI_CONNECTOR, WGI_SERVICE} -- Commit
|
||||
|
||||
commit
|
||||
-- Commit the current response
|
||||
do
|
||||
output.flush
|
||||
message_committed := True
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
status_committed: BOOLEAN
|
||||
-- Status code set and committed?
|
||||
|
||||
header_committed: BOOLEAN
|
||||
-- Header committed?
|
||||
|
||||
message_committed: BOOLEAN
|
||||
-- Message committed?
|
||||
|
||||
message_writable: BOOLEAN
|
||||
-- Can message be written?
|
||||
do
|
||||
Result := status_is_set and header_committed
|
||||
end
|
||||
|
||||
feature {NONE} -- Core output operation
|
||||
|
||||
write (s: READABLE_STRING_8)
|
||||
-- Send the content of `s'
|
||||
-- this can be used for header and body
|
||||
do
|
||||
output.put_string (s)
|
||||
end
|
||||
|
||||
feature -- Status setting
|
||||
|
||||
status_is_set: BOOLEAN
|
||||
-- Is status set?
|
||||
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
|
||||
do
|
||||
status_code := a_code
|
||||
output.put_status_line (a_code)
|
||||
status_committed := True
|
||||
end
|
||||
|
||||
status_code: INTEGER
|
||||
-- Response status
|
||||
|
||||
feature -- Header output operation
|
||||
|
||||
put_header_text (a_text: READABLE_STRING_8)
|
||||
do
|
||||
write (a_text)
|
||||
write (crlf)
|
||||
header_committed := True
|
||||
end
|
||||
|
||||
put_header_lines (a_lines: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
|
||||
local
|
||||
h: STRING_8
|
||||
do
|
||||
create h.make (256)
|
||||
across
|
||||
a_lines as c
|
||||
loop
|
||||
h.append (c.item.name)
|
||||
h.append_character (':')
|
||||
h.append_character (' ')
|
||||
h.append (c.item.value)
|
||||
h.append_character ('%R')
|
||||
h.append_character ('%N')
|
||||
end
|
||||
put_header_text (h)
|
||||
end
|
||||
|
||||
feature -- Output operation
|
||||
|
||||
put_string (s: READABLE_STRING_8)
|
||||
-- Send the string `s'
|
||||
do
|
||||
write (s)
|
||||
end
|
||||
|
||||
put_substring (s: READABLE_STRING_8; start_index, end_index: INTEGER)
|
||||
-- Send the substring `start_index:end_index]'
|
||||
--| Could be optimized according to the target output
|
||||
do
|
||||
output.put_substring (s, start_index, end_index)
|
||||
end
|
||||
|
||||
flush
|
||||
do
|
||||
output.flush
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation: Access
|
||||
|
||||
crlf: STRING = "%R%N"
|
||||
-- End of header
|
||||
|
||||
output: WGI_OUTPUT_STREAM
|
||||
-- Server output channel
|
||||
|
||||
;note
|
||||
copyright: "2011-2012, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
Reference in New Issue
Block a user