Removed any (put|write)_file_content from the WSF_ or WGI_ OUTPUT classes

Now DEFAULT_SERVICE has to be created instead of inherited.
   - This seems to be better for new user, and this avoid potential conflict and difference when inheriting between the various DEFAULT_SERVICE implementation.
   - remember that DEFAULT_SERVICE, is mainly to help the user to build its very first service.
Use READABLE_STRING_8 as argument whenever it is possible.
Added WSF_RESPONSE_MESSAGE, and WSF_RESPONSE.put_response (a_response_message)
Now WSF_RESPONSE inherit from WGI_RESPONSE
This commit is contained in:
Jocelyn Fiat
2011-12-12 11:16:15 +01:00
parent 019fb539ae
commit 89572b4f33
19 changed files with 426 additions and 128 deletions

View File

@@ -193,6 +193,12 @@ feature -- Content related header
put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_enc)
end
put_transfer_encoding_binary
-- Put "Transfer-Encoding: binary" header
do
put_transfer_encoding ("binary")
end
put_transfer_encoding_chunked
-- Put "Transfer-Encoding: chunked" header
do

View File

@@ -6,7 +6,7 @@ note
deferred class
WGI_RESPONSE
feature {WGI_SERVICE} -- Commit
feature {WGI_RESPONSE, WGI_SERVICE} -- Commit
commit
-- Commit the current response
@@ -110,13 +110,6 @@ feature -- Output operation
deferred
end
write_file_content (fn: READABLE_STRING_8)
-- Send the content of file `fn'
require
message_writable: message_writable
deferred
end
flush
-- Flush if it makes sense
deferred

View File

@@ -43,25 +43,6 @@ feature -- Basic operation
put_string (s.substring (start_index, end_index))
end
put_file_content (fn: STRING)
-- Send the content of file `fn'
local
f: RAW_FILE
do
create f.make (fn)
if f.exists and then f.is_readable then
f.open_read
from
until
f.exhausted
loop
f.read_stream (4096)
put_string (f.last_string)
end
f.close
end
end
put_header_line (s: STRING)
-- Send `s' to http client as header line
do

View File

@@ -115,12 +115,6 @@ feature -- Output operation
output.put_substring (s, start_index, end_index)
end
write_file_content (fn: READABLE_STRING_8)
-- Send the content of file `fn'
do
output.put_file_content (fn)
end
flush
do
output.flush

View File

@@ -46,28 +46,6 @@ feature -- Output
put_string (c.out)
end
put_file_content (fn: READABLE_STRING_8)
-- Send the content of file `fn'
require
string_not_empty: not fn.is_empty
is_readable: (create {RAW_FILE}.make (fn)).is_readable
local
f: RAW_FILE
do
create f.make (fn)
check f.exists and then f.is_readable end
f.open_read
from
until
f.exhausted
loop
f.read_stream (4096)
put_string (f.last_string)
end
f.close
end
feature -- Specific output
put_header_line (s: READABLE_STRING_8)

View File

@@ -120,7 +120,7 @@ feature -- Input
feature -- Output
put_string (a_str: STRING)
put_string (a_str: READABLE_STRING_8)
-- Put `a_str' on the FastCGI stdout.
require
a_str_not_void: a_str /= Void

View File

@@ -147,32 +147,21 @@ feature -- Execution
respond_file (f: FILE; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)
local
fn: READABLE_STRING_8
h: HTTP_HEADER
ext: READABLE_STRING_8
ct: detachable READABLE_STRING_8
fres: WSF_FILE_RESPONSE
do
fn := f.name
ext := extension (fn)
ext := extension (f.name)
ct := extension_mime_mapping.mime_type (ext)
create h.make
if ct = Void then
ct := {HTTP_MIME_TYPES}.application_force_download
end
create fres.make_with_content_type (ct, f.name)
fres.set_status_code ({HTTP_STATUS_CODE}.ok)
fres.set_answer_head_request_method (req.request_method.same_string ({HTTP_REQUEST_METHODS}.method_head))
if ct /= Void then
h.put_content_type (ct)
h.put_content_length (f.count)
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.write_header_text (h.string)
else
create h.make
h.put_content_type ({HTTP_MIME_TYPES}.application_force_download)
h.put_content_length (f.count)
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.write_header_text (h.string)
end
if not req.request_method.same_string ({HTTP_REQUEST_METHODS}.method_head) then
res.write_file_content (fn)
end
res.flush
res.put_response (fres)
end
respond_not_found (uri: READABLE_STRING_8; ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE)

View File

@@ -3,22 +3,37 @@ note
date: "$Date$"
revision: "$Revision$"
deferred class
class
DEFAULT_SERVICE
inherit
WSF_SERVICE
create
make_and_launch
feature {NONE} -- Initialization
make_and_launch
make_and_launch (a_action: like action)
local
cgi: WGI_CGI_CONNECTOR
do
action := a_action
create cgi.make (Current)
cgi.launch
end
feature -- Execution
action: PROCEDURE [ANY, TUPLE [WSF_REQUEST, WSF_RESPONSE]]
-- Action to be executed on request incoming
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
do
action.call ([req, res])
end
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -3,22 +3,37 @@ note
date: "$Date$"
revision: "$Revision$"
deferred class
class
DEFAULT_SERVICE
inherit
WSF_SERVICE
create
make_and_launch
feature {NONE} -- Initialization
make_and_launch
make_and_launch (a_action: like action)
local
conn: WGI_LIBFCGI_CONNECTOR
do
action := a_action
create conn.make (Current)
conn.launch
end
feature -- Execution
action: PROCEDURE [ANY, TUPLE [WSF_REQUEST, WSF_RESPONSE]]
-- Action to be executed on request incoming
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
do
action.call ([req, res])
end
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -3,34 +3,48 @@ note
date: "$Date$"
revision: "$Revision$"
deferred class
class
DEFAULT_SERVICE
inherit
WSF_SERVICE
create
make_and_launch
feature {NONE} -- Initialization
make_and_launch
make_and_launch (a_action: like action)
local
app: NINO_SERVICE
port_number: INTEGER
base_url: STRING
do
port_number := 8080
action := a_action
port_number := 80 --| Default, but quite often, this port is already used ...
base_url := ""
debug ("nino")
print ("Example: start a Nino web server on port " + port_number.out +
", %Nand reply Hello World for any request such as http://localhost:" + port_number.out + "/" + base_url + "%N")
end
create app.make_custom (agent wgi_execute, base_url)
debug ("nino")
app.set_is_verbose (True)
end
app.listen (port_number)
end
port_number: INTEGER
feature -- Execution
base_url: STRING
action: PROCEDURE [ANY, TUPLE [WSF_REQUEST, WSF_RESPONSE]]
-- Action to be executed on request incoming
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- <Precursor>
do
action.call ([req, res])
end
invariant
port_number_valid: port_number > 0
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,26 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
WSF_DOWNLOAD_RESPONSE
inherit
WSF_FILE_RESPONSE
redefine
get_content_type
end
create
make
feature {NONE} -- Implementation
get_content_type
do
content_type := {HTTP_MIME_TYPES}.application_force_download
end
end

View File

@@ -0,0 +1,210 @@
note
description : "Objects that ..."
author : "$Author$"
date : "$Date$"
revision : "$Revision$"
class
WSF_FILE_RESPONSE
inherit
WSF_RESPONSE_MESSAGE
create
make,
make_with_content_type,
make_html
feature {NONE} -- Initialization
make (a_file_name: READABLE_STRING_8)
do
file_name := a_file_name
base_name := basename (a_file_name)
get_content_type
initialize
end
make_with_content_type (a_content_type: READABLE_STRING_8; a_filename: READABLE_STRING_8)
-- Initialize `Current'.
do
file_name := a_filename
base_name := basename (a_filename)
content_type := a_content_type
initialize
end
make_html (a_filename: READABLE_STRING_8)
-- Initialize `Current'.
do
make_with_content_type ({HTTP_MIME_TYPES}.text_html, a_filename)
end
initialize
local
h: like header
do
create h.make
header := h
h.put_content_type (content_type)
h.put_transfer_encoding_binary
h.put_content_length (filesize (file_name))
h.put_content_disposition ("attachment", "filename=%""+ base_name +"%"")
end
feature -- Element change
set_expires (t: INTEGER)
do
header.put_expires (t)
end
set_no_cache
local
h: like header
do
h := header
h.put_expires (0)
h.put_cache_control ("no-cache, must-revalidate")
h.put_pragma_no_cache
end
feature -- Access
header: HTTP_HEADER
status_code: INTEGER assign set_status_code
file_name: READABLE_STRING_8
base_name: READABLE_STRING_8
content_type: READABLE_STRING_8
feature -- Settings
answer_head_request_method: BOOLEAN assign set_answer_head_request_method
-- For HEAD request method, only http header should be sent
feature -- Element change
set_status_code (c: like status_code)
-- Set `status_code' to `c'.
require
valid_status_code: status_code > 0
do
status_code := c
ensure
status_code_set: status_code = c
end
set_answer_head_request_method (b: BOOLEAN)
-- Set answer_head_request_method' to `b'.
do
answer_head_request_method := b
end
feature -- Basic operations
send_to (res: WSF_RESPONSE)
do
res.set_status_code (status_code)
res.write_header_text (header.string)
if not answer_head_request_method then
send_file_content_to (file_name, res)
end
end
feature {NONE} -- Implementation: file system helper
filesize (fn: STRING): INTEGER
-- Size of the file `fn'.
local
f: RAW_FILE
do
create f.make (fn)
if f.exists then
Result := f.count
end
end
file_extension (fn: STRING): STRING
-- Extension of file `fn'.
local
p: INTEGER
do
p := fn.last_index_of ('.', fn.count)
if p > 0 then
Result := fn.substring (p + 1, fn.count)
else
create Result.make_empty
end
end
basename (fn: STRING): STRING
-- Basename of `fn'.
local
p: INTEGER
do
p := fn.last_index_of ((create {OPERATING_ENVIRONMENT}).Directory_separator, fn.count)
if p > 0 then
Result := fn.substring (p + 1, fn.count)
else
Result := fn
end
end
dirname (fn: STRING): STRING
-- Dirname of `fn'.
local
p: INTEGER
do
p := fn.last_index_of ((create {OPERATING_ENVIRONMENT}).Directory_separator, fn.count)
if p > 0 then
Result := fn.substring (1, p - 1)
else
create Result.make_empty
end
end
feature -- Content-type related
get_content_type
-- Content type associated with `file_name'
local
m_map: HTTP_FILE_EXTENSION_MIME_MAPPING
m: detachable READABLE_STRING_8
do
create m_map.make_default
m := m_map.mime_type (file_extension (file_name).as_lower)
if m = Void then
m := {HTTP_MIME_TYPES}.application_force_download
end
content_type := m
end
feature {NONE} -- Implementation: output
send_file_content_to (fn: READABLE_STRING_8; res: WSF_RESPONSE)
-- Send the content of file `fn'
require
string_not_empty: not fn.is_empty
is_readable: (create {RAW_FILE}.make (fn)).is_readable
local
f: RAW_FILE
do
create f.make (fn)
check f.exists and then f.is_readable end
f.open_read
from
until
f.exhausted
loop
f.read_stream (4_096)
res.write_string (f.last_string)
end
f.close
end
end

View File

@@ -0,0 +1,45 @@
note
description: "Summary description for {WSF_PAGE_RESPONSE}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
WSF_PAGE_RESPONSE
inherit
WSF_RESPONSE_MESSAGE
create
make
feature {NONE} -- Initialization
make
do
status_code := {HTTP_STATUS_CODE}.ok
create header.make
end
feature -- Status
status_code: INTEGER
feature -- Header
header: HTTP_HEADER
body: detachable STRING_8
feature -- Output
send_to (res: WSF_RESPONSE)
do
res.set_status_code (status_code)
res.write_header_text (header.string)
if attached body as b then
res.write_string (b)
end
end
end

View File

@@ -8,6 +8,9 @@ note
class
WSF_RESPONSE
inherit
WGI_RESPONSE
create {WSF_SERVICE}
make_from_wgi
@@ -55,14 +58,8 @@ feature -- Status setting
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 status_is_set
header_not_committed: not header_committed
do
wgi_response.set_status_code (a_code)
ensure
status_code_set: status_code = a_code
status_set: status_is_set
end
status_code: INTEGER
@@ -71,18 +68,20 @@ feature -- Status setting
Result := wgi_response.status_code
end
feature {WGI_RESPONSE} -- Core output operation
write (s: READABLE_STRING_8)
-- Send the string `s'
-- this can be used for header and body
do
wgi_response.write (s)
end
feature -- Header output operation
write_header_text (a_headers: READABLE_STRING_8)
require
status_set: status_is_set
header_not_committed: not header_committed
do
wgi_response.write_header_text (a_headers)
ensure
status_set: status_is_set
header_committed: header_committed
message_writable: message_writable
end
write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]])
@@ -114,20 +113,21 @@ feature -- Header output operation
message_writable: message_writable
end
write_header_lines (a_lines: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]])
do
wgi_response.write_header_lines (a_lines)
end
feature -- Output operation
write_string (s: READABLE_STRING_8)
-- Send the string `s'
require
message_writable: message_writable
do
wgi_response.write_string (s)
end
write_substring (s: READABLE_STRING_8; a_begin_index, a_end_index: INTEGER)
-- Send the substring `s[a_begin_index:a_end_index]'
require
message_writable: message_writable
do
wgi_response.write_substring (s, a_begin_index, a_end_index)
end
@@ -163,14 +163,6 @@ feature -- Output operation
flush
end
write_file_content (fn: READABLE_STRING_8)
-- Send the content of file `fn'
require
message_writable: message_writable
do
wgi_response.write_file_content (fn)
end
flush
-- Flush if it makes sense
do
@@ -179,6 +171,17 @@ feature -- Output operation
feature -- Helper
put_response (obj: WSF_RESPONSE_MESSAGE)
require
not header_committed
not status_is_set
not message_committed
do
obj.send_to (Current)
end
feature -- Redirect
redirect_now_with_custom_status_code (a_url: READABLE_STRING_8; a_status_code: INTEGER)
-- Redirect to the given url `a_url' and precise custom `a_status_code'
-- Please see http://www.faqs.org/rfcs/rfc2616 to use proper status code.
@@ -205,6 +208,14 @@ feature -- Helper
redirect_now_with_custom_status_code (a_url, {HTTP_STATUS_CODE}.moved_permanently)
end
feature {WGI_RESPONSE, WGI_SERVICE} -- Commit
commit
-- Commit the current response
do
wgi_response.commit
end
note
copyright: "2011-2011, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"

View File

@@ -0,0 +1,16 @@
note
description: "Summary description for {WSF_RESPONSE_MESSAGE}."
author: ""
date: "$Date$"
revision: "$Revision$"
deferred class
WSF_RESPONSE_MESSAGE
feature -- Output
send_to (res: WSF_RESPONSE)
deferred
end
end