diff --git a/examples/restbucksCRUD/restbucks-safe.ecf b/examples/restbucksCRUD/restbucks-safe.ecf
index ca7c3f18..08715841 100644
--- a/examples/restbucksCRUD/restbucks-safe.ecf
+++ b/examples/restbucksCRUD/restbucks-safe.ecf
@@ -18,7 +18,7 @@
-
+
diff --git a/examples/restbucksCRUD/src/resource/order_handler.e b/examples/restbucksCRUD/src/resource/order_handler.e
index f1db9d1a..499e5695 100644
--- a/examples/restbucksCRUD/src/resource/order_handler.e
+++ b/examples/restbucksCRUD/src/resource/order_handler.e
@@ -9,7 +9,7 @@ class ORDER_HANDLER
inherit
WSF_SKELETON_HANDLER
-
+
SHARED_DATABASE_API
SHARED_EJSON
@@ -28,9 +28,14 @@ create
make_with_router
-feature -- API DOC
+feature -- Documentation
- api_doc : STRING = "URI:/order METHOD: POST%N URI:/order/{orderid} METHOD: GET, HEAD, PUT, DELETE%N"
+ description: READABLE_STRING_GENERAL
+ -- General description for self-generated documentation;
+ -- The specific URI templates supported will be described automatically
+ do
+ Result := "Create, Read, Update or Delete an ORDER."
+ end
feature -- Access
@@ -93,7 +98,7 @@ feature -- Access
-- Maximum age in seconds before response to `req` is considered stale;
-- This is used to generate a Cache-Control: max-age header.
-- Return 0 to indicate already expired.
- -- Return (365 * 1440 = 1 year) to indicate never expires.
+ -- Return Never_expires to indicate never expires.
do
-- All our responses are considered stale.
end
@@ -135,7 +140,7 @@ feature -- Access
-- If `a_strong' then the strong comparison function must be used.
local
l_id: STRING
- l_etag_util: ETAG_UTILS
+ l_etag_util: ETAG_UTILS
do
l_id := order_id_from_request (req)
if db_access.orders.has_key (l_id) then
@@ -155,8 +160,8 @@ feature -- Access
create l_etag_utils
if attached {ORDER} req.execution_variable ("ORDER") as l_order then
Result := l_etag_utils.md5_digest (l_order.out)
- end
- end
+ end
+ end
modified_since (req: WSF_REQUEST; a_date_time: DATE_TIME): BOOLEAN
-- Has resource requested in `req' been modified since `a_date_time' (UTC)?
@@ -164,7 +169,7 @@ feature -- Access
-- We don't track this information. It is safe to always say yes.
Result := True
end
-
+
feature -- Measurement
content_length (req: WSF_REQUEST): NATURAL
@@ -220,7 +225,7 @@ feature -- Execution
order_saved_only_for_get_head: req.is_get_head_request_method =
attached {ORDER} req.execution_variable ("ORDER")
end
-
+
feature -- GET/HEAD content
ensure_content_available (req: WSF_REQUEST;
@@ -254,7 +259,7 @@ feature -- GET/HEAD content
Result := l_response
end
end
-
+
next_chunk (req: WSF_REQUEST; a_media_type, a_language_type, a_character_type, a_compression_type: READABLE_STRING_8): TUPLE [a_check: READABLE_STRING_8; a_extension: detachable READABLE_STRING_8]
-- Next chunk of entity body in response to `req';
-- The second field of the result is an optional chunk extension.
@@ -270,7 +275,7 @@ feature -- GET/HEAD content
do
-- precondition `is_chunking' is never met
end
-
+
feature -- DELETE
delete (req: WSF_REQUEST)
@@ -315,7 +320,7 @@ feature -- PUT/POST
-- No. We don't care for this example.
end
- check_content_headers (req: WSF_REQUEST)
+ check_content_headers (req: WSF_REQUEST)
-- Check we can support all content headers on request entity.
-- Set `req.execution_variable ("CONTENT_CHECK_CODE")' to {NATURAL} zero if OK, or 415 or 501 if not.
do
@@ -334,14 +339,14 @@ feature -- PUT/POST
-- Create new resource in response to a POST request.
-- Implementor must set error code of 200 OK or 204 No Content or 303 See Other or 500 Server Error.
do
- if attached {ORDER} req.execution_variable ("EXTRACTED_ORDER") as l_order then
+ if attached {ORDER} req.execution_variable ("EXTRACTED_ORDER") as l_order then
save_order (l_order)
compute_response_post (req, res, l_order)
else
handle_bad_request_response ("Not a valid order", req, res)
end
end
-
+
check_conflict (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Check we can support all content headers on request entity.
-- Set `req.execution_variable ("CONFLICT_CHECK_CODE")' to {NATURAL} zero if OK, or 409 if not.
@@ -350,17 +355,15 @@ feature -- PUT/POST
if attached {ORDER} req.execution_variable ("EXTRACTED_ORDER") as l_order then
if not is_valid_to_update (l_order) then
req.set_execution_variable ("CONFLICT_CHECK_CODE", {NATURAL} 409)
- --| FIXME: Here we need to define the Allow methods
handle_resource_conflict_response (l_order.out +"%N There is conflict while trying to update the order, the order could not be update in the current state", req, res)
end
else
req.set_execution_variable ("CONFLICT_CHECK_CODE", {NATURAL} 409)
- --| FIXME: Here we need to define the Allow methods
- --| This ought to be a 500, as if attached should probably be check attached. But as yet I lack a proof. TODO.
+ --| This ought to be a 500, as if attached should probably be check attached. But as yet I lack a proof.
handle_resource_conflict_response ("There is conflict while trying to update the order, the order could not be update in the current state", req, res)
end
end
-
+
check_request (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Check that the request entity is a valid request.
-- The entity is available as `req.execution_variable ("REQUEST_ENTITY")'.
@@ -384,7 +387,7 @@ feature -- PUT/POST
end
else
req.set_execution_variable ("REQUEST_CHECK_CODE", {NATURAL} 0)
- req.set_execution_variable ("EXTRACTED_ORDER", l_order)
+ req.set_execution_variable ("EXTRACTED_ORDER", l_order)
end
else
req.set_execution_variable ("REQUEST_CHECK_CODE", {NATURAL} 400)
@@ -396,14 +399,14 @@ feature -- PUT/POST
-- Perform the update requested in `req'.
-- Write a response to `res' with a code of 204 or 500.
do
- if attached {ORDER} req.execution_variable ("EXTRACTED_ORDER") as l_order then
+ if attached {ORDER} req.execution_variable ("EXTRACTED_ORDER") as l_order then
update_order (l_order)
compute_response_put (req, res, l_order)
else
handle_internal_server_error (res)
end
end
-
+
feature -- HTTP Methods
compute_response_put (req: WSF_REQUEST; res: WSF_RESPONSE; l_order : ORDER)
@@ -553,6 +556,6 @@ feature {NONE} -- Implementation Repository Layer
end
note
- copyright: "2011-2012, Javier Velilla and others"
+ copyright: "2011-2013, Javier Velilla and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end
diff --git a/library/network/protocol/http/src/http_header.e b/library/network/protocol/http/src/http_header.e
index 72e6fd37..d70ac062 100644
--- a/library/network/protocol/http/src/http_header.e
+++ b/library/network/protocol/http/src/http_header.e
@@ -350,7 +350,9 @@ feature -- Header change: general
end
add_header_key_value (k,v: READABLE_STRING_8)
- -- Add header `k:v', or replace existing header of same header name/key
+ -- Add header `k:v'.
+ -- If it already exists, there will be multiple header with same name
+ -- which can also be valid
local
s: STRING_8
do
@@ -509,10 +511,10 @@ feature -- Content related header
put_header_key_value ({HTTP_HEADER_NAMES}.header_content_transfer_encoding, a_mechanism)
end
- put_content_language (a_enc: READABLE_STRING_8)
- -- Put "Content-Language" header of value `a_enc'.
+ put_content_language (a_lang: READABLE_STRING_8)
+ -- Put "Content-Language" header of value `a_lang'.
do
- put_header_key_value ({HTTP_HEADER_NAMES}.header_content_language, a_enc)
+ put_header_key_value ({HTTP_HEADER_NAMES}.header_content_language, a_lang)
end
put_content_encoding (a_enc: READABLE_STRING_8)
diff --git a/library/server/wsf/router/wsf_caching_policy.e b/library/server/wsf/router/wsf_caching_policy.e
index 4c780b19..bc9a4687 100644
--- a/library/server/wsf/router/wsf_caching_policy.e
+++ b/library/server/wsf/router/wsf_caching_policy.e
@@ -10,16 +10,20 @@ deferred class WSF_CACHING_POLICY
feature -- Access
+ Never_expires: NATURAL = 525600
+ -- 525600 = 365 * 24 * 60 * 60 = (almost) 1 year;
+ -- See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21 for an explanation of why this means never expire
+
age (req: WSF_REQUEST): NATURAL
-- Maximum age in seconds before response to `req` is considered stale;
-- This is used to generate a Cache-Control: max-age header.
-- Return 0 to indicate already expired.
- -- Return (365 * 1440 = 1 year) to indicate never expires.
+ -- Return `Never_expires' to indicate never expires.
require
req_attached: req /= Void
deferred
ensure
- not_more_than_1_year: Result <= (365 * 1440).as_natural_32
+ not_more_than_1_year: Result <= Never_expires
end
shared_age (req: WSF_REQUEST): NATURAL
@@ -27,13 +31,13 @@ feature -- Access
-- This is used to generate a Cache-Control: s-maxage header.
-- If you wish to have different expiry ages for shared and provate caches, redefine this routine.
-- Return 0 to indicate already expired.
- -- Return (365 * 1440 = 1 year) to indicate never expires.
+ -- Return `Never_expires' to indicate never expires.
require
req_attached: req /= Void
do
Result := age (req)
ensure
- not_more_than_1_year: Result <= (365 * 1440).as_natural_32
+ not_more_than_1_year: Result <= Never_expires
end
http_1_0_age (req: WSF_REQUEST): NATURAL
@@ -41,7 +45,7 @@ feature -- Access
-- This is used to generate an Expires header, which HTTP/1.0 caches understand.
-- If you wish to generate a different age for HTTP/1.0 caches, then redefine this routine.
-- Return 0 to indicate already expired.
- -- Return (365 * 1440 = 1 year) to indicate never expires. Note this will
+ -- Return `Never_expires' to indicate never expires. Note this will
-- make a result cachecable that would not normally be cacheable (such as as response
-- to a POST), unless overriden by cache-control headers, so be sure to check `req.request_method'.
require
@@ -49,7 +53,7 @@ feature -- Access
do
Result := age (req)
ensure
- not_more_than_1_year: Result <= (365 * 1440).as_natural_32
+ not_more_than_1_year: Result <= Never_expires
end
is_freely_cacheable (req: WSF_REQUEST): BOOLEAN
@@ -81,7 +85,6 @@ feature -- Access
-- Redefine to force revalidation.
end
-
must_proxy_revalidate (req: WSF_REQUEST): BOOLEAN
-- If a shared cache is configured to ignore server's expiration time,
-- should we force revalidation anyway?
@@ -92,7 +95,6 @@ feature -- Access
-- Redefine to force revalidation.
end
-
private_headers (req: WSF_REQUEST): detachable LIST [READABLE_STRING_8]
-- Header names intended for a single user.
-- If non-Void, then a Cache-Control: private header will be generated.
@@ -122,4 +124,14 @@ feature -- Access
deferred
end
+note
+ copyright: "2011-2013, 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
diff --git a/library/server/wsf/router/wsf_delete_helper.e b/library/server/wsf/router/wsf_delete_helper.e
index 6bc0e646..a8633d4b 100644
--- a/library/server/wsf/router/wsf_delete_helper.e
+++ b/library/server/wsf/router/wsf_delete_helper.e
@@ -11,15 +11,10 @@ class WSF_DELETE_HELPER
inherit
WSF_METHOD_HELPER
- rename
- send_get_response as handle_delete
- redefine
- handle_delete
- end
feature {NONE} -- Implementation
- handle_delete (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
+ send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
a_media_type, a_language_type, a_character_type, a_compression_type: detachable READABLE_STRING_8; a_new_resource: BOOLEAN)
-- Write response to deletion of resource named by `req' into `res' in accordance with `a_media_type' etc.
local
diff --git a/library/server/wsf/router/wsf_get_helper.e b/library/server/wsf/router/wsf_get_helper.e
index e20dd50b..6085b979 100644
--- a/library/server/wsf/router/wsf_get_helper.e
+++ b/library/server/wsf/router/wsf_get_helper.e
@@ -12,4 +12,45 @@ inherit
WSF_METHOD_HELPER
+feature {NONE} -- Implementation
+
+ send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
+ a_media_type, a_language_type, a_character_type, a_compression_type: detachable READABLE_STRING_8; a_new_resource: BOOLEAN)
+ -- Write response to `req' into `res' in accordance with `a_media_type' etc.
+ local
+ l_chunked, l_ok: BOOLEAN
+ l_dt: STRING
+ do
+ a_handler.ensure_content_available (req, a_media_type, a_language_type, a_character_type, a_compression_type)
+ l_chunked := a_handler.is_chunking (req)
+ if l_chunked then
+ a_header.put_transfer_encoding_chunked
+ else
+ a_header.put_content_length (a_handler.content_length (req).as_integer_32)
+ end
+ if attached req.request_time as l_time then
+ l_dt := (create {HTTP_DATE}.make_from_date_time (l_time)).rfc1123_string
+ a_header.put_header_key_value ({HTTP_HEADER_NAMES}.header_date, l_dt)
+ generate_cache_headers (req, a_handler, a_header, l_time)
+ end
+ l_ok := a_handler.response_ok (req)
+ if l_ok then
+ res.set_status_code ({HTTP_STATUS_CODE}.ok)
+ else
+ -- TODO - req.error_handler.has_error = True
+ --handle_internal_server_error (a_handler.last_error (req), req, res)
+ end
+ if attached a_handler.etag (req, a_media_type, a_language_type, a_character_type, a_compression_type) as l_etag then
+ a_header.put_header_key_value ({HTTP_HEADER_NAMES}.header_etag, l_etag)
+ end
+ res.put_header_text (a_header.string)
+ if l_ok then
+ if l_chunked then
+ send_chunked_response (req, res, a_handler, a_header, a_media_type, a_language_type, a_character_type, a_compression_type)
+ else
+ res.put_string (a_handler.content (req, a_media_type, a_language_type, a_character_type, a_compression_type))
+ end
+ end
+ end
+
end
diff --git a/library/server/wsf/router/wsf_method_helper.e b/library/server/wsf/router/wsf_method_helper.e
index 6b23ebec..a37e9c3b 100644
--- a/library/server/wsf/router/wsf_method_helper.e
+++ b/library/server/wsf/router/wsf_method_helper.e
@@ -2,12 +2,19 @@ note
description: "[
Policy-driven helpers to implement a method.
- This implementation is suitable for GET and HEAD.
+
]"
date: "$Date$"
revision: "$Revision$"
-class WSF_METHOD_HELPER
+deferred class WSF_METHOD_HELPER
+
+inherit
+
+ HTTP_STATUS_CODE_MESSAGES
+
+ SHARED_HTML_ENCODER
+ export {NONE} all end
feature -- Access
@@ -42,17 +49,17 @@ feature -- Basic operations
if a_handler.resource_previously_existed (req) then
if a_handler.resource_moved_permanently (req) then
l_locs := a_handler.previous_location (req)
- -- TODO 301 Moved permanently response
+ handle_redirection_error (req, res, l_locs, {HTTP_STATUS_CODE}.moved_permanently)
elseif a_handler.resource_moved_temporarily (req) then
l_locs := a_handler.previous_location (req)
- -- TODO := 302 Found response
+ handle_redirection_error (req, res, l_locs, {HTTP_STATUS_CODE}.found)
else
create h.make
h.put_content_type_text_plain
h.put_current_date
h.put_content_length (0)
res.set_status_code ({HTTP_STATUS_CODE}.gone)
- res.put_header_text (h.string)
+ res.put_header_lines (h)
end
else
create h.make
@@ -60,7 +67,7 @@ feature -- Basic operations
h.put_current_date
h.put_content_length (0)
res.set_status_code ({HTTP_STATUS_CODE}.not_found)
- res.put_header_text (h.string)
+ res.put_header_lines (h)
end
end
@@ -78,7 +85,7 @@ feature -- Basic operations
l_date: HTTP_DATE
do
if attached req.http_if_match as l_if_match then
- -- TODO - also if-range when we add support for range requests
+ -- also if-range when we add support for range requests
if not l_if_match.same_string ("*") then
l_etags := l_if_match.split (',')
l_failed := not across l_etags as i_etags some a_handler.matching_etag (req, i_etags.item, True) end
@@ -105,14 +112,14 @@ feature -- Basic operations
across l_etags as i_etags some a_handler.matching_etag (req, i_etags.item, False) end
end
if l_failed then
- handle_if_none_match_failed (req, res)
+ handle_if_none_match_failed (req, res, a_handler)
else
if attached req.http_if_modified_since as l_if_modified_since then
if l_if_modified_since.is_string_8 then
create l_date.make_from_string (l_if_modified_since.as_string_8)
if not l_date.has_error then
if not a_handler.modified_since (req, l_date.date_time) then
- handle_not_modified (req, res)
+ handle_not_modified (req, res, a_handler)
l_failed := True
end
end
@@ -121,13 +128,13 @@ feature -- Basic operations
end
if not l_failed then
handle_content_negotiation (req, res, a_handler, False)
- end
+ end
end
end
end
feature {NONE} -- Implementation
-
+
handle_content_negotiation (req: WSF_REQUEST; res: WSF_RESPONSE;
a_handler: WSF_SKELETON_HANDLER; a_new_resource: BOOLEAN)
-- Negotiate acceptable content for, then write, response requested by `req' into `res'.
@@ -163,7 +170,7 @@ feature {NONE} -- Implementation
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_media_variant)
end
if not l_media.is_acceptable then
- handle_not_accepted ("None of the requested ContentTypes were acceptable", req, res)
+ handle_not_acceptable ("None of the requested ContentTypes were acceptable", l_mime_types, req, res)
else
l_langs := a_handler.languages_supported (req)
l_lang := l_conneg.language_preference (l_langs, req.http_accept_language)
@@ -171,7 +178,7 @@ feature {NONE} -- Implementation
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_lang_variant)
end
if not l_lang.is_acceptable then
- handle_not_accepted ("None of the requested languages were acceptable", req, res)
+ handle_not_acceptable ("None of the requested languages were acceptable", l_langs, req, res)
else
if attached l_lang.language_type as l_language_type then
h.put_content_language (l_language_type)
@@ -182,7 +189,7 @@ feature {NONE} -- Implementation
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_charset_variant)
end
if not l_charset.is_acceptable then
- handle_not_accepted ("None of the requested character encodings were acceptable", req, res)
+ handle_not_acceptable ("None of the requested character encodings were acceptable", l_charsets, req, res)
else
if attached l_media.media_type as l_media_type and attached l_charset.character_type as l_character_type then
h.put_content_type (l_media_type + "; charset=" + l_character_type)
@@ -193,13 +200,13 @@ feature {NONE} -- Implementation
h.add_header_key_value ({HTTP_HEADER_NAMES}.header_vary, l_encoding_variant)
end
if not l_encoding.is_acceptable then
- handle_not_accepted ("None of the requested transfer encodings were acceptable", req, res)
+ handle_not_acceptable ("None of the requested transfer encodings were acceptable", l_encodings, req, res)
else
if attached l_encoding.compression_type as l_compression_type then
h.put_content_encoding (l_compression_type)
end
-- We do not support multiple choices, so
- send_get_response (req, res, a_handler, h,
+ send_response (req, res, a_handler, h,
l_media.media_type, l_lang.language_type, l_charset.character_type, l_encoding.compression_type, a_new_resource)
end
end
@@ -207,52 +214,19 @@ feature {NONE} -- Implementation
end
end
- send_get_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
+ send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
a_media_type, a_language_type, a_character_type, a_compression_type: detachable READABLE_STRING_8; a_new_resource: BOOLEAN)
-- Write response to `req' into `res' in accordance with `a_media_type' etc.
require
req_attached: req /= Void
res_attached: res /= Void
a_handler_attached: a_handler /= Void
- a_header_attached: a_header /= Void
+ a_header_attached: a_header /= Void
a_media_type_attached: a_media_type /= Void
a_language_type_attached: a_language_type /= Void
a_character_type_attached: a_character_type /= Void
a_compression_type_attached: a_compression_type /= Void
- local
- l_chunked, l_ok: BOOLEAN
- l_dt: STRING
- do
- a_handler.ensure_content_available (req, a_media_type, a_language_type, a_character_type, a_compression_type)
- l_chunked := a_handler.is_chunking (req)
- if l_chunked then
- a_header.put_transfer_encoding_chunked
- else
- a_header.put_content_length (a_handler.content_length (req).as_integer_32)
- end
- if attached req.request_time as l_time then
- l_dt := (create {HTTP_DATE}.make_from_date_time (l_time)).rfc1123_string
- a_header.put_header_key_value ({HTTP_HEADER_NAMES}.header_date, l_dt)
- generate_cache_headers (req, a_handler, a_header, l_time)
- end
- l_ok := a_handler.response_ok (req)
- if l_ok then
- res.set_status_code ({HTTP_STATUS_CODE}.ok)
- else
- -- TODO - req.error_handler.has_error = True
- --handle_internal_server_error (a_handler.last_error (req), req, res)
- end
- if attached a_handler.etag (req, a_media_type, a_language_type, a_character_type, a_compression_type) as l_etag then
- a_header.put_header_key_value ({HTTP_HEADER_NAMES}.header_etag, l_etag)
- end
- res.put_header_text (a_header.string)
- if l_ok then
- if l_chunked then
- send_chunked_response (req, res, a_handler, a_header, a_media_type, a_language_type, a_character_type, a_compression_type)
- else
- res.put_string (a_handler.content (req, a_media_type, a_language_type, a_character_type, a_compression_type))
- end
- end
+ deferred
end
send_chunked_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
@@ -265,7 +239,7 @@ feature {NONE} -- Implementation
a_media_type_attached: a_media_type /= Void
a_language_type_attached: a_language_type /= Void
a_character_type_attached: a_character_type /= Void
- a_compression_type_attached: a_compression_type /= Void
+ a_compression_type_attached: a_compression_type /= Void
local
l_chunk: TUPLE [a_chunk: READABLE_STRING_8; a_extension: detachable READABLE_STRING_8]
do
@@ -290,11 +264,11 @@ feature {NONE} -- Implementation
end
end
if a_handler.finished (req) then
- -- TODO - support for trailers
+ -- In future, add support for trailers
res.put_chunk_end
end
end
-
+
generate_cache_headers (req: WSF_REQUEST; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER; a_request_time: DATE_TIME)
-- Write headers affecting caching for `req' into `a_header'.
require
@@ -343,7 +317,7 @@ feature {NONE} -- Implementation
end
if a_handler.must_proxy_revalidate (req) then
a_header.add_header_key_value ({HTTP_HEADER_NAMES}.header_cache_control, "proxy-revalidate")
- end
+ end
end
append_field_name (a_field_names: STRING; a_fields: LIST [READABLE_STRING_8])
@@ -362,36 +336,139 @@ feature {NONE} -- Implementation
feature -- Basic operations
- handle_not_accepted (a_message: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
- -- Write a Not Accepted response to `res'.
+ handle_redirection_error (req: WSF_REQUEST; res: WSF_RESPONSE; a_locations: LIST [URI]; a_status_code: INTEGER)
+ -- Write `a_status_code' error to `res'.
+ -- Include all of `a_locations' in the headers, and hyperlink to the first one in the body.
+ require
+ res_attached: res /= Void
+ req_attached: req /= Void
+ a_locations_attached: a_locations /= Void
+ a_location_not_empty: not a_locations.is_empty
+ a_status_code_code: is_valid_http_status_code (a_status_code)
+ local
+ h: HTTP_HEADER
+ s: STRING
+ do
+ if attached http_status_code_message (a_status_code) as l_msg then
+ create h.make
+ across a_locations as i_location loop
+ h.add_header_key_value ({HTTP_HEADER_NAMES}.header_location, i_location.item.string)
+ end
+ if req.is_content_type_accepted ({HTTP_MIME_TYPES}.text_html) then
+ s := "
"
+ s.append ("")
+ s.append (html_encoder.encoded_string (req.request_uri))
+ s.append ("Error " + a_status_code.out + " (" + l_msg + ")")
+ s.append ("%N")
+ s.append ("[
+
+
+
+ ]")
+ s.append ("")
+ s.append ("")
+ s.append ("
")
+ s.append ("
")
+ s.append ("
")
+ s.append ("
")
+ s.append ("The current location for this resource is
here")
+ s.append ("Error " + a_status_code.out + " (" + l_msg + ")
")
+ s.append ("Error " + a_status_code.out + " (" + l_msg + "): " + html_encoder.encoded_string (req.request_uri) + "
")
+ s.append ("")
+ s.append ("%N")
+ s.append ("%N")
+
+ h.put_content_type_text_html
+ else
+ s := "Error " + a_status_code.out + " (" + l_msg + "): "
+ s.append (req.request_uri)
+ s.append_character ('%N')
+ s.append ("The current location for this resource is " + a_locations.first.string)
+ h.put_content_type_text_plain
+ end
+ h.put_content_length (s.count)
+ res.put_header_lines (h)
+ res.put_string (s)
+ res.flush
+ end
+ end
+
+ handle_not_acceptable (a_message: READABLE_STRING_8; a_supported: LIST [STRING]; req: WSF_REQUEST; res: WSF_RESPONSE)
+ -- Write a Not Acceptable response to `res'.
require
req_attached: req /= Void
res_attached: res /= Void
a_message_attached: a_message /= Void
+ a_supported_attached: a_supported /= Void
+ local
+ h: HTTP_HEADER
+ s: STRING
do
- -- TODO: flag this if it gets to code review.
+ create h.make
+ h.put_content_type_text_plain
+ h.put_current_date
+ s := a_message
+ s.append_character ('%N')
+ s.append_character ('%N')
+ s.append_string ("We accept the following:%N%N")
+ across a_supported as i_supported loop
+ s.append_string (i_supported.item)
+ s.append_character ('%N')
+ end
+ h.put_content_length (s.count)
+ res.set_status_code ({HTTP_STATUS_CODE}.not_acceptable)
+ res.put_header_lines (h)
+ res.put_string (s)
+ res.flush
end
- handle_if_none_match_failed (req: WSF_REQUEST; res: WSF_RESPONSE)
- -- Write a Precondition Failed response to `res'.
+ handle_if_none_match_failed (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
+ -- Write a Not Modified response to `res'.
require
req_attached: req /= Void
res_attached: res /= Void
+ a_handler_attached: a_handler /= Void
do
- -- TODO: flag this if it gets to code review. Why not just handle_precondition_failed?
+ handle_not_modified (req, res, a_handler)
end
- handle_not_modified (req: WSF_REQUEST; res: WSF_RESPONSE)
- -- Write a Precondition Failed response to `res'.
+ handle_not_modified (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
+ -- Write a Not Modified response to `res'.
require
req_attached: req /= Void
res_attached: res /= Void
+ a_handler_attached: a_handler /= Void
+ local
+ h: HTTP_HEADER
do
- -- TODO: flag this if it gets to code review. Why not just handle_precondition_failed?
+ create h.make
+ h.put_content_type_text_plain
+ h.put_content_length (0)
+ if attached a_handler.etag (req, "", "", "", "") as l_etag then
+ -- FIXME: We aren't strictly conformant here, as we have not conducted content negotiation yet,
+ -- so we might not get an identical etag as for a successful (200 OK) request.
+ -- So we should conduct content negotiation at this point (and if not acceptable, we don't include an etag).
+ -- Add add any Vary header that might result.
+ -- Also, when we add support for the Content-Location header in responses, we need to send that here too.
+ h.put_header_key_value ({HTTP_HEADER_NAMES}.header_etag, l_etag)
+ end
+ generate_cache_headers (req, a_handler, h, create {DATE_TIME}.make_now_utc)
+ res.set_status_code ({HTTP_STATUS_CODE}.not_modified)
+ res.put_header_lines (h)
end
handle_precondition_failed (req: WSF_REQUEST; res: WSF_RESPONSE)
- -- Write a precondition failed response for `req' to `res'.
+ -- Write a Precondition Failed response for `req' to `res'.
require
req_attached: req /= Void
res_attached: res /= Void
@@ -403,7 +480,59 @@ feature -- Basic operations
h.put_current_date
h.put_content_length (0)
res.set_status_code ({HTTP_STATUS_CODE}.precondition_failed)
- res.put_header_text (h.string)
+ res.put_header_lines (h)
+ end
+
+ handle_unsupported_media_type (req: WSF_REQUEST; res: WSF_RESPONSE)
+ -- Write a Unsupported Media Type response for `req' to `res'.
+ require
+ req_attached: req /= Void
+ res_attached: res /= Void
+ local
+ h: HTTP_HEADER
+ do
+ create h.make
+ h.put_content_type_text_plain
+ h.put_current_date
+ h.put_content_length (0)
+ res.set_status_code ({HTTP_STATUS_CODE}.unsupported_media_type)
+ res.put_header_lines (h)
+ end
+
+ handle_not_implemented (req: WSF_REQUEST; res: WSF_RESPONSE)
+ -- Write a Not Implemented response for `req' to `res'.
+ require
+ req_attached: req /= Void
+ res_attached: res /= Void
+ local
+ h: HTTP_HEADER
+ do
+ create h.make
+ h.put_content_type_text_plain
+ h.put_current_date
+ h.put_content_length (0)
+ res.set_status_code ({HTTP_STATUS_CODE}.not_implemented)
+ res.put_header_lines (h)
+ end
+
+ handle_request_entity_too_large (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
+ -- Write a Request Entity Too Large response for `req' to `res'.
+ require
+ req_attached: req /= Void
+ res_attached: res /= Void
+ a_handler_attached: a_handler /= Void
+ local
+ h: HTTP_HEADER
+ do
+ create h.make
+ h.put_content_type_text_plain
+ h.put_current_date
+ h.put_content_length (0)
+ res.set_status_code ({HTTP_STATUS_CODE}.request_entity_too_large)
+ res.put_header_lines (h)
+ -- FIXME: Need to check if condition is temporary. This needs a new query
+ -- on the handler. For now we can claim compliance by saying the condition
+ -- is always permenent :-) - author's might not like this though.
end
note
diff --git a/library/server/wsf/router/wsf_method_helper_factory.e b/library/server/wsf/router/wsf_method_helper_factory.e
index c41a6e3d..48f773c4 100644
--- a/library/server/wsf/router/wsf_method_helper_factory.e
+++ b/library/server/wsf/router/wsf_method_helper_factory.e
@@ -18,7 +18,7 @@ feature -- Factory
do
if a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get) or
a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_head) then
- create Result
+ create {WSF_GET_HELPER} Result
elseif a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_put) then
create {WSF_PUT_HELPER} Result
elseif a_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_post) then
@@ -27,5 +27,15 @@ feature -- Factory
create {WSF_DELETE_HELPER} Result
end
end
-
+
+note
+ copyright: "2011-2013, 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
diff --git a/library/server/wsf/router/wsf_post_helper.e b/library/server/wsf/router/wsf_post_helper.e
index b05a40a7..41c54a64 100644
--- a/library/server/wsf/router/wsf_post_helper.e
+++ b/library/server/wsf/router/wsf_post_helper.e
@@ -11,11 +11,8 @@ class WSF_POST_HELPER
inherit
WSF_METHOD_HELPER
- rename
- send_get_response as do_post
redefine
- execute_new_resource,
- do_post
+ execute_new_resource
end
feature -- Basic operations
@@ -23,48 +20,54 @@ feature -- Basic operations
execute_new_resource (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
-- Write response to non-existing resource requested by `req.' into `res'.
-- Policy routines are available in `a_handler'.
- local
- l_etags: LIST [READABLE_STRING_32]
- l_failed: BOOLEAN
do
if a_handler.allow_post_to_missing_resource (req) then
handle_content_negotiation (req, res, a_handler, True)
else
- -- TODO 404 Not Found
+ res.send (create {WSF_NOT_FOUND_RESPONSE}.make(req))
end
end
-
+
feature {NONE} -- Implementation
- do_post (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
+ send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
a_media_type, a_language_type, a_character_type, a_compression_type: detachable READABLE_STRING_8; a_new_resource: BOOLEAN)
-- Write response to `req' into `res' in accordance with `a_media_type' etc. as a new URI.
+ local
+ l_code: NATURAL
do
a_handler.read_entity (req)
if a_handler.is_entity_too_large (req) then
handle_request_entity_too_large (req, res, a_handler)
else
a_handler.check_content_headers (req)
- if a_handler.content_check_code (req) /= 0 then
- -- TODO - 415 or 501
+ l_code := a_handler.content_check_code (req)
+ if l_code /= 0 then
+ if l_code = 415 then
+ handle_unsupported_media_type (req, res)
+ else
+ handle_not_implemented (req, res)
+ end
else
a_handler.check_request (req, res)
if a_handler.request_check_code (req) = 0 then
a_handler.append_resource (req, res)
-- 200 or 204 or 303 or 500 (add support for this?)
- -- TODO: more support, such as includes_response_entity
+ -- FIXME: more support, such as includes_response_entity
end
end
end
end
- handle_request_entity_too_large (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
- -- TODO.
- require
- -- TODO
- do
- -- Need to check if condition is temporary.
- end
-
+note
+ copyright: "2011-2013, 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
diff --git a/library/server/wsf/router/wsf_put_helper.e b/library/server/wsf/router/wsf_put_helper.e
index 67f123b8..6adedbbf 100644
--- a/library/server/wsf/router/wsf_put_helper.e
+++ b/library/server/wsf/router/wsf_put_helper.e
@@ -11,11 +11,8 @@ class WSF_PUT_HELPER
inherit
WSF_METHOD_HELPER
- rename
- send_get_response as do_put
redefine
- execute_new_resource,
- do_put
+ execute_new_resource
end
feature -- Basic operations
@@ -25,7 +22,7 @@ feature -- Basic operations
-- Policy routines are available in `a_handler'.
do
if a_handler.treat_as_moved_permanently (req) then
- -- TODO 301 Moved permanently response (single location)
+ handle_redirection_error (req, res, a_handler.previous_location (req), {HTTP_STATUS_CODE}.moved_permanently)
else
handle_content_negotiation (req, res, a_handler, True)
end
@@ -34,17 +31,24 @@ feature -- Basic operations
feature {NONE} -- Implementation
- do_put (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
+ send_response (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER; a_header: HTTP_HEADER;
a_media_type, a_language_type, a_character_type, a_compression_type: detachable READABLE_STRING_8; a_new_resource: BOOLEAN)
-- Write response to `req' into `res' in accordance with `a_media_type' etc. as a new URI.
+ local
+ l_code: NATURAL
do
a_handler.read_entity (req)
if a_handler.is_entity_too_large (req) then
handle_request_entity_too_large (req, res, a_handler)
else
a_handler.check_content_headers (req)
- if a_handler.content_check_code (req) /= 0 then
- -- TODO - 415 or 501
+ l_code := a_handler.content_check_code (req)
+ if l_code /= 0 then
+ if l_code = 415 then
+ handle_unsupported_media_type (req, res)
+ else
+ handle_not_implemented (req, res)
+ end
else
a_handler.check_request (req, res)
if a_handler.request_check_code (req) = 0 then
@@ -56,20 +60,12 @@ feature {NONE} -- Implementation
if a_handler.conflict_check_code (req) = 0 then
a_handler.update_resource (req, res)
-- 204 or 500 (add support for this?)
- -- TODO: more support, such as includes_response_entity
+ -- FIXME: more support, such as includes_response_entity
end
end
end
end
end
end
-
- handle_request_entity_too_large (req: WSF_REQUEST; res: WSF_RESPONSE; a_handler: WSF_SKELETON_HANDLER)
- -- TODO.
- require
- -- TODO
- do
- -- Need to check if condition is temporary.
- end
end
diff --git a/library/server/wsf/router/wsf_skeleton_handler.e b/library/server/wsf/router/wsf_skeleton_handler.e
index aa8d01c3..cd7bc6d0 100644
--- a/library/server/wsf/router/wsf_skeleton_handler.e
+++ b/library/server/wsf/router/wsf_skeleton_handler.e
@@ -24,6 +24,8 @@ inherit
WSF_METHOD_HELPER_FACTORY
+ WSF_SELF_DOCUMENTED_HANDLER
+
feature -- Access
is_chunking (req: WSF_REQUEST): BOOLEAN
@@ -42,7 +44,7 @@ feature -- Access
end
conneg (req: WSF_REQUEST): CONNEG_SERVER_SIDE
- -- Content negotiatior for `req';
+ -- Content negotiation for `req';
-- This would normally be a once object, ignoring `req'.
require
req_attached: req /= Void
@@ -161,6 +163,25 @@ feature -- Status report
deferred
end
+
+feature -- Documentation
+
+ mapping_documentation (m: WSF_ROUTER_MAPPING; a_request_methods: detachable WSF_REQUEST_METHODS): WSF_ROUTER_MAPPING_DOCUMENTATION
+ -- Documentation associated with Current handler, in the context of the mapping `m' and methods `a_request_methods'
+ --| `m' and `a_request_methods' are useful to produce specific documentation when the handler is used for multiple mapping.
+ do
+ create Result.make (m)
+ Result.add_description (description)
+ end
+
+ description: READABLE_STRING_GENERAL
+ -- General description for self-generated documentation;
+ -- The specific URI templates supported will be described automatically
+ deferred
+ ensure
+ description_attached: Result /= Void
+ end
+
feature -- DELETE
delete (req: WSF_REQUEST)
diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e
index 572d5a28..61a098e9 100644
--- a/library/server/wsf/src/wsf_request.e
+++ b/library/server/wsf/src/wsf_request.e
@@ -376,6 +376,8 @@ feature -- Helper
end
end
Result := l_accept.has_substring (a_content_type)
+ else
+ Result := True
end
end