Added to WSF_REQUEST

- raw_header_data: like meta_string_variable
  - read_input_data_into (buf: STRING)
  - is_content_type_accepted (a_content_type: READABLE_STRING_GENERAL): BOOLEAN
Changed raw_input_data to return IMMUTABLE_STRING_8
Added WSF_METHOD_NOT_ALLOWED_RESPONSE
Added WSF_TRACE_RESPONSE to respond TRACE request
Now Not_found response return html content if the client accepts, other text/plain
Implemented TRACE response, and Method not allowed as implementation of WSF_ROUTED_SERVICE.execute_default
This commit is contained in:
Jocelyn Fiat
2012-12-13 17:29:46 +01:00
parent ff7d963d55
commit b7505e67b8
6 changed files with 417 additions and 54 deletions

View File

@@ -48,10 +48,23 @@ feature -- Execution
execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) execute_default (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Default procedure -- Default procedure
local local
msg: WSF_RESPONSE_MESSAGE
not_found: WSF_NOT_FOUND_RESPONSE not_found: WSF_NOT_FOUND_RESPONSE
not_allowed: WSF_METHOD_NOT_ALLOWED_RESPONSE
trace: WSF_TRACE_RESPONSE
do do
create not_found.make (req) if req.is_request_method ({HTTP_REQUEST_METHODS}.method_trace) then
res.send (not_found) create trace.make (req)
msg := trace
elseif attached router.allowed_methods_for_request (req) as mtds and then not mtds.is_empty then
create not_allowed.make (req)
not_allowed.set_suggested_methods (mtds)
msg := not_allowed
else
create not_found.make (req)
msg := not_found
end
res.send (msg)
end end
feature -- Access feature -- Access

View File

@@ -207,6 +207,31 @@ feature -- Status report
end end
end end
allowed_methods_for_request (req: WSF_REQUEST): WSF_ROUTER_METHODS
-- Allowed methods for `req'
local
m: WSF_ROUTER_MAPPING
l_rqsmethods: detachable WSF_ROUTER_METHODS
do
create Result
across
mappings as c
loop
m := c.item.mapping
if attached {WSF_ROUTING_HANDLER} m.handler as l_routing then
l_rqsmethods := l_routing.router.allowed_methods_for_request (req)
elseif m.is_mapping (req, Current) then
l_rqsmethods := c.item.request_methods
else
l_rqsmethods := Void
end
if l_rqsmethods /= Void then
Result := Result + l_rqsmethods
end
end
end
feature -- Hook feature -- Hook
execute_before (a_mapping: WSF_ROUTER_MAPPING) execute_before (a_mapping: WSF_ROUTER_MAPPING)

View File

@@ -0,0 +1,139 @@
note
description: "[
This class is used to report a 405 Method not allowed response
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_METHOD_NOT_ALLOWED_RESPONSE
inherit
WSF_RESPONSE_MESSAGE
SHARED_HTML_ENCODER
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST)
do
request := req
create header.make
create suggested_methods
end
feature -- Header
header: HTTP_HEADER
-- Response' header
request: WSF_REQUEST
-- Associated request.
suggested_methods: WSF_ROUTER_METHODS
-- Optional suggestions
-- First is the default.
feature -- Element change
set_suggested_methods (m: like suggested_methods)
-- Set `suggested_methods' to `m'
do
suggested_methods := m
end
feature {WSF_RESPONSE} -- Output
send_to (res: WSF_RESPONSE)
local
s: STRING
l_title: detachable READABLE_STRING_GENERAL
h: like header
do
h := header
res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed)
s := "Not allowed"
if request.is_content_type_accepted ({HTTP_MIME_TYPES}.text_html) then
s := "<html><head>"
s.append ("<title>")
s.append (html_encoder.encoded_string (request.request_uri))
s.append ("Error 405 (Method Not Allowed)!!")
s.append ("</title>%N")
s.append (
"[
<style type="text/css">
div#header {color: #fff; background-color: #000; padding: 20px; width: 100%; text-align: center; font-size: 2em; font-weight: bold;}
div#message { margin: 40px; width: 100%; text-align: center; font-size: 1.5em; }
div#suggestions { margin: auto; width: 60%;}
div#suggestions ul { }
div#footer {color: #999; background-color: #eee; padding: 10px; width: 100%; text-align: center; }
div#logo { float: right; margin: 20px; width: 60px height: auto; font-size: 0.8em; text-align: center; }
div#logo div.outter { padding: 6px; width: 60px; border: solid 3px #500; background-color: #b00;}
div#logo div.outter div.inner1 { display: block; margin: 10px 15px; width: 30px; height: 50px; color: #fff; background-color: #fff; border: solid 2px #900; }
div#logo div.outter div.inner2 { margin: 10px 15px; width: 30px; height: 15px; color: #fff; background-color: #fff; border: solid 2px #900; }
</style>
</head>
<body>
<div id="header">Error 405 (Method Not Allowed)!!</div>
]")
s.append ("<div id=%"logo%">")
s.append ("<div class=%"outter%"> ")
s.append ("<div class=%"inner1%"></div>")
s.append ("<div class=%"inner2%"></div>")
s.append ("</div>")
s.append ("Error 405 (Method Not Allowed)</div>")
s.append ("<div id=%"message%">Error 405 (Method Not Allowed): the request method <code>")
s.append (request.request_method)
s.append ("</code> is inappropriate for the URL for <code>" + html_encoder.encoded_string (request.request_uri) + "</code>.</div>")
if attached suggested_methods as lst and then not lst.is_empty then
s.append ("<div id=%"suggestions%"><strong>Allowed methods:</strong>")
across
lst as c
loop
s.append (" ")
s.append (c.item)
end
s.append ("%N")
end
s.append ("<div id=%"footer%"></div>")
s.append ("</body>%N")
s.append ("</html>%N")
h.put_content_type_text_html
else
s := "Error 405 (Method Not Allowed): the request method "
s.append (request.request_method)
s.append (" is inappropriate for the URL for '" + html_encoder.encoded_string (request.request_uri) + "'.%N")
if attached suggested_methods as lst and then not lst.is_empty then
s.append ("Allowed methods:")
across
lst as c
loop
s.append (" ")
s.append (c.item)
end
s.append ("%N")
end
h.put_content_type_text_plain
end
h.put_content_length (s.count)
res.put_header_text (h.string)
res.put_string (s)
res.flush
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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

View File

@@ -8,7 +8,6 @@ note
class class
WSF_NOT_FOUND_RESPONSE WSF_NOT_FOUND_RESPONSE
inherit inherit
WSF_RESPONSE_MESSAGE WSF_RESPONSE_MESSAGE
@@ -57,59 +56,85 @@ feature {WSF_RESPONSE} -- Output
h := header h := header
res.set_status_code ({HTTP_STATUS_CODE}.not_found) res.set_status_code ({HTTP_STATUS_CODE}.not_found)
s := "<html><head>" if request.is_content_type_accepted ({HTTP_MIME_TYPES}.text_html) then
s.append ("<title>") s := "<html lang=%"en%"><head>"
s.append (html_encoder.encoded_string (request.request_uri)) s.append ("<title>")
s.append (" - 404 Not Found") s.append (html_encoder.encoded_string (request.request_uri))
s.append ("</title>%N") s.append ("Error 404 (Not Found)")
s.append ("[ s.append ("</title>%N")
<style type="text/css"> s.append ("[
div#header {color: #fff; background-color: #000; padding: 20px; width: 100%; text-align: center; font-size: 2em; font-weight: bold;} <style type="text/css">
div#message { margin: 40px; width: 100%; text-align: center; font-size: 1.5em; } div#header {color: #fff; background-color: #000; padding: 20px; width: 100%; text-align: center; font-size: 2em; font-weight: bold;}
div#suggestions { margin: auto; width: 60%;} div#message { margin: 40px; width: 100%; text-align: center; font-size: 1.5em; }
div#suggestions ul { } div#suggestions { margin: auto; width: 60%;}
div#footer {color: #999; background-color: #eee; padding: 10px; width: 100%; text-align: center; } div#suggestions ul { }
div#logo { float: right; margin: 20px; width: 60px height: auto; font-size: 0.8em; text-align: center; } div#footer {color: #999; background-color: #eee; padding: 10px; width: 100%; text-align: center; }
div#logo div.outter { padding: 6px; width: 60px; border: solid 3px #500; background-color: #b00;} div#logo { float: right; margin: 20px; width: 60px height: auto; font-size: 0.8em; text-align: center; }
div#logo div.outter div.inner1 { display: block; margin: 10px 15px; width: 30px; height: 50px; color: #fff; background-color: #fff; border: solid 2px #900; } div#logo div.outter { padding: 6px; width: 60px; border: solid 3px #500; background-color: #b00;}
div#logo div.outter div.inner2 { margin: 10px 15px; width: 30px; height: 15px; color: #fff; background-color: #fff; border: solid 2px #900; } div#logo div.outter div.inner1 { display: block; margin: 10px 15px; width: 30px; height: 50px; color: #fff; background-color: #fff; border: solid 2px #900; }
</style> div#logo div.outter div.inner2 { margin: 10px 15px; width: 30px; height: 15px; color: #fff; background-color: #fff; border: solid 2px #900; }
</head> </style>
<body> </head>
<div id="header">404 Not Found</div> <body>
]") <div id="header">Error 404 (Not Found)</div>
s.append ("<div id=%"logo%">") ]")
s.append ("<div class=%"outter%"> ") s.append ("<div id=%"logo%">")
s.append ("<div class=%"inner1%"></div>") s.append ("<div class=%"outter%"> ")
s.append ("<div class=%"inner2%"></div>") s.append ("<div class=%"inner1%"></div>")
s.append ("</div>") s.append ("<div class=%"inner2%"></div>")
s.append ("404 Not Found</div>") s.append ("</div>")
s.append ("<div id=%"message%">404 Not Found: <code>" + html_encoder.encoded_string (request.request_uri) + "</code></div>") s.append ("Error 404 (Not Found)</div>")
if attached suggested_locations as lst and then not lst.is_empty then s.append ("<div id=%"message%">Error 404 (Not Found): <code>" + html_encoder.encoded_string (request.request_uri) + "</code></div>")
s.append ("<div id=%"suggestions%"><strong>Perhaps your are looking for:</strong><ul>") if attached suggested_locations as lst and then not lst.is_empty then
from s.append ("<div id=%"suggestions%"><strong>Perhaps your are looking for:</strong><ul>")
lst.start from
until lst.start
lst.after until
loop lst.after
s.append ("<li>") loop
l_title := lst.item.title s.append ("<li>")
if l_title = Void then l_title := lst.item.title
l_title := lst.item.location if l_title = Void then
l_title := lst.item.location
end
s.append ("<a href=%"" + lst.item.location + "%">" + html_encoder.encoded_string (l_title.to_string_32) + "</a>")
s.append ("</li>%N")
lst.forth
end end
s.append ("<a href=%"" + lst.item.location + "%">" + html_encoder.encoded_string (l_title.to_string_32) + "</a>") s.append ("</ul></div>%N")
s.append ("</li>%N")
lst.forth
end end
s.append ("</ul></div>%N") s.append ("<div id=%"footer%"></div>")
end s.append ("</body>%N")
s.append ("<div id=%"footer%"></div>") s.append ("</html>%N")
s.append ("</body>%N")
s.append ("</html>%N")
h.put_content_type_text_html
else
s := "Error 404 (Not Found): "
s.append (request.request_uri)
s.append_character ('%N')
if attached suggested_locations as lst and then not lst.is_empty then
s.append ("%NPerhaps your are looking for:%N")
from
lst.start
until
lst.after
loop
s.append (" - ")
l_title := lst.item.title
if l_title = Void then
l_title := lst.item.location
end
s.append (lst.item.location)
s.append ("%N")
lst.forth
end
end
h.put_content_type_text_plain
end
h.put_content_length (s.count) h.put_content_length (s.count)
h.put_content_type_text_html
res.put_header_text (h.string) res.put_header_text (h.string)
res.put_string (s) res.put_string (s)
res.flush res.flush

View File

@@ -0,0 +1,97 @@
note
description: "[
This class is used to respond a TRACE request
]"
date: "$Date$"
revision: "$Revision$"
class
WSF_TRACE_RESPONSE
inherit
WSF_RESPONSE_MESSAGE
create
make
feature {NONE} -- Initialization
make (req: WSF_REQUEST)
do
request := req
create header.make
end
feature -- Header
header: HTTP_HEADER
-- Response' header
request: WSF_REQUEST
-- Associated request.
feature {WSF_RESPONSE} -- Output
send_to (res: WSF_RESPONSE)
local
s: STRING
l_title: detachable READABLE_STRING_GENERAL
h: like header
req: like request
n, nb: INTEGER
do
h := header
res.set_status_code ({HTTP_STATUS_CODE}.ok)
req := request
if attached req.raw_header_data as l_header then
create s.make (l_header.count)
s.append (l_header.to_string_8)
s.append_character ('%N')
else
s := ""
end
if req.is_chunked_input then
h.put_transfer_encoding_chunked
res.put_header_text (h.string)
res.put_chunk (s, Void)
if attached req.input as l_input then
from
n := 1_024
until
n = 0
loop
s.wipe_out
nb := l_input.read_to_string (s, 1, n)
if nb = 0 then
n := 0
else
if nb < n then
n := 0
end
res.put_chunk (s, Void)
end
end
end
res.put_chunk_end
res.flush
else
req.read_input_data_into (s)
h.put_content_length (s.count)
res.put_header_text (h.string)
res.put_string (s)
res.flush
end
end
note
copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, 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

View File

@@ -144,12 +144,26 @@ feature -- Setting
feature -- Raw input data feature -- Raw input data
raw_input_data: detachable READABLE_STRING_8 raw_input_data: detachable IMMUTABLE_STRING_8
-- Raw input data is `raw_input_data_recorded' is True -- Raw input data is `raw_input_data_recorded' is True
set_raw_input_data (d: READABLE_STRING_8) set_raw_input_data (d: READABLE_STRING_8)
do do
raw_input_data := d if attached {IMMUTABLE_STRING_8} d as imm then
raw_input_data := d
else
create raw_input_data.make_from_string (d)
end
end
feature -- Raw header data
raw_header_data: like meta_string_variable
-- Raw header data if available.
do
Result := meta_string_variable ("RAW_HEADER_DATA")
ensure
is_valid_as_string_8: Result /= Void implies Result.is_valid_as_string_8
end end
feature -- Error handling feature -- Error handling
@@ -178,6 +192,48 @@ feature -- Access: Input
Result := wgi_request.is_chunked_input Result := wgi_request.is_chunked_input
end end
read_input_data_into (buf: STRING)
-- retrieve the content from the `input' stream into `s'
-- warning: if the input data has already been retrieved
-- you might not get anything
local
l_input: WGI_INPUT_STREAM
n: INTEGER
s: STRING
do
if raw_input_data_recorded and then attached raw_input_data as d then
buf.copy (d)
else
l_input := input
if is_chunked_input then
from
n := 1_024
until
n = 0
loop
l_input.read_string (n)
s := l_input.last_string
if s.count = 0 then
n := 0
else
if s.count < n then
n := 0
end
buf.append (s)
end
end
else
n := content_length_value.as_integer_32
buf.resize (buf.count + n)
n := l_input.read_to_string (buf, buf.count + 1, n)
check n = content_length_value.as_integer_32 end
end
if raw_input_data_recorded then
set_raw_input_data (buf)
end
end
end
feature -- Helper feature -- Helper
is_request_method (m: READABLE_STRING_GENERAL): BOOLEAN is_request_method (m: READABLE_STRING_GENERAL): BOOLEAN
@@ -198,6 +254,14 @@ feature -- Helper
Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get) Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get)
end end
is_content_type_accepted (a_content_type: READABLE_STRING_GENERAL): BOOLEAN
-- Does client accepts content_type for the response?
do
if attached http_accept as l_accept then
Result := l_accept.has_substring (a_content_type)
end
end
feature -- Eiffel WGI access feature -- Eiffel WGI access
wgi_version: READABLE_STRING_8 wgi_version: READABLE_STRING_8