diff --git a/.gitmodules b/.gitmodules
index ad9fdb20..5f591df5 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,3 +10,9 @@
[submodule "ext/ise_library/curl"]
path = ext/ise_library/curl
url = http://github.com/EiffelSoftware/mirror-Eiffel-cURL.git
+[submodule "ext/crypto/eel"]
+ path = ext/crypto/eel
+ url = http://github.com/Eiffel-World/eel.git
+[submodule "ext/crypto/eapml"]
+ path = ext/crypto/eapml
+ url = http://github.com/Eiffel-World/eapml.git
diff --git a/examples/restbucks/restbucks-safe.ecf b/examples/restbucks/restbucks-safe.ecf
index 4651633a..6ed4fa57 100644
--- a/examples/restbucks/restbucks-safe.ecf
+++ b/examples/restbucks/restbucks-safe.ecf
@@ -16,8 +16,9 @@
-
+
+
diff --git a/examples/restbucks/src/domain/order.e b/examples/restbucks/src/domain/order.e
index d7092a5f..490139ce 100644
--- a/examples/restbucks/src/domain/order.e
+++ b/examples/restbucks/src/domain/order.e
@@ -86,6 +86,9 @@ feature -- Etag
Result := hash_code.out + revision.out
end
+
+feature -- Output
+
feature -- Report
hash_code: INTEGER_32
diff --git a/examples/restbucks/src/resource/order_handler.e b/examples/restbucks/src/resource/order_handler.e
index aa9dd712..b2abf8bc 100644
--- a/examples/restbucks/src/resource/order_handler.e
+++ b/examples/restbucks/src/resource/order_handler.e
@@ -40,25 +40,48 @@ feature -- HTTP Methods
-- 200 OK, and a representation of the order
-- If the GET request is not SUCCESS, we response with
-- 404 Resource not found
+ -- If is a Condition GET and the resource does not change we send a
+ -- 304, Resource not modifed
local
id : STRING
do
if attached req.orig_path_info as orig_path then
id := get_order_id_from_path (orig_path)
if attached retrieve_order (id) as l_order then
- compute_response_get (ctx, req, res, l_order)
+ if is_conditional_get (req, l_order) then
+ handle_resource_not_modified_response ("The resource" + orig_path + "does not change", ctx, req, res)
+ else
+ compute_response_get (ctx, req, res, l_order)
+ end
else
handle_resource_not_found_response ("The following resource" + orig_path + " is not found ", ctx, req, res)
end
end
end
+ is_conditional_get (req : WGI_REQUEST; l_order : ORDER) : BOOLEAN
+ -- Check if If-None-Match is present and then if there is a representation that has that etag
+ -- if the representation hasn't changed, we return TRUE
+ -- then the response is a 304 with no entity body returned.
+ local
+ etag_util : ETAG_UTILS
+ do
+ if attached req.meta_variable ("HTTP_IF_NONE_MATCH") as if_none_match then
+ create etag_util
+ if if_none_match.as_string.same_string (etag_util.md5_digest (l_order.out).as_string_32) then
+ Result := True
+ end
+ end
+ end
+
compute_response_get (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER; l_order : ORDER)
local
h: EWF_HEADER
l_msg : STRING
+ etag_utils : ETAG_UTILS
do
create h.make
+ create etag_utils
h.put_status ({HTTP_STATUS_CODE}.ok)
h.put_content_type_application_json
if attached {JSON_VALUE} json.value (l_order) as jv then
@@ -67,6 +90,7 @@ feature -- HTTP Methods
if attached req.request_time as time then
h.add_header ("Date:" + time.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
end
+ h.add_header ("etag:" + etag_utils.md5_digest (l_order.out))
res.set_status_code ({HTTP_STATUS_CODE}.ok)
res.write_headers_string (h.string)
res.write_string (l_msg)
@@ -81,6 +105,8 @@ feature -- HTTP Methods
-- 404 if the order is not found
-- 400 in case of a bad request
-- 500 internal server error
+ -- If the request is a Conditional PUT, and it does not mat we response
+ -- 415, precondition failed.
local
l_post: STRING
l_order : detachable ORDER
@@ -90,8 +116,12 @@ feature -- HTTP Methods
l_order := extract_order_request(l_post)
if l_order /= Void and then db_access.orders.has_key (l_order.id) then
if is_valid_to_update(l_order) then
- update_order( l_order)
- compute_response_put (ctx, req, res, l_order)
+ if is_conditional_put (req, l_order) then
+ update_order( l_order)
+ compute_response_put (ctx, req, res, l_order)
+ else
+ handle_precondition_fail_response ("", ctx, req, res)
+ end
else
--| FIXME: Here we need to define the Allow methods
handle_resource_conflict_response (l_post +"%N There is conflict while trying to update the order, the order could not be update in the current state", ctx, req, res)
@@ -101,13 +131,34 @@ feature -- HTTP Methods
end
end
+ is_conditional_put (req : WGI_REQUEST; order : ORDER) : BOOLEAN
+ -- Check if If-Match is present and then if there is a representation that has that etag
+ -- if the representation hasn't changed, we return TRUE
+ local
+ etag_util : ETAG_UTILS
+ do
+ if attached retrieve_order (order.id) as l_order then
+ if attached req.meta_variable ("HTTP_IF_MATCH") as if_match then
+ create etag_util
+ if if_match.as_string.same_string (etag_util.md5_digest (l_order.out).as_string_32) then
+ Result := True
+ end
+ else
+ Result := True
+ end
+ end
+ end
+
+
compute_response_put (ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER; l_order : ORDER)
local
h: EWF_HEADER
joc : JSON_ORDER_CONVERTER
+ etag_utils : ETAG_UTILS
do
create h.make
create joc.make
+ create etag_utils
json.add_converter(joc)
create h.make
@@ -116,6 +167,7 @@ feature -- HTTP Methods
if attached req.request_time as time then
h.add_header ("Date:" +time.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
end
+ h.add_header ("etag:" + etag_utils.md5_digest (l_order.out))
if attached {JSON_VALUE} json.value (l_order) as jv then
h.put_content_length (jv.representation.count)
res.set_status_code ({HTTP_STATUS_CODE}.ok)
@@ -133,7 +185,6 @@ feature -- HTTP Methods
-- 500 if we have an internal server error
local
id: STRING
- h : EWF_HEADER
do
if attached req.orig_path_info as orig_path then
id := get_order_id_from_path (orig_path)
diff --git a/library/server/request/router/src/misc/request_resource_handler_helper.e b/library/server/request/router/src/misc/request_resource_handler_helper.e
index d74e7eea..31bff168 100644
--- a/library/server/request/router/src/misc/request_resource_handler_helper.e
+++ b/library/server/request/router/src/misc/request_resource_handler_helper.e
@@ -182,6 +182,25 @@ feature -- Handle responses
res.write_string (a_description)
end
+
+ handle_precondition_fail_response (a_description:STRING; ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER )
+ local
+ h : EWF_HEADER
+ do
+ create h.make
+ h.put_status ({HTTP_STATUS_CODE}.precondition_failed)
+ if attached ctx.request_content_type (supported_content_types) as l_content_type then
+ h.put_content_type (l_content_type)
+ else
+ h.put_content_type ("*/*")
+ end
+ h.put_content_length (a_description.count)
+ h.put_current_date
+ res.set_status_code ({HTTP_STATUS_CODE}.precondition_failed)
+ res.write_headers_string (h.string)
+ res.write_string (a_description)
+ end
+
handle_internal_server_error (a_description:STRING; ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER )
local
h : EWF_HEADER
@@ -239,6 +258,26 @@ feature -- Handle responses
end
+ handle_resource_not_modified_response (a_description:STRING; ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
+ local
+ h : EWF_HEADER
+ do
+ res.flush
+ create h.make
+ h.put_status ({HTTP_STATUS_CODE}.not_modified)
+ if attached ctx.request_content_type (supported_content_types) as l_content_type then
+ h.put_content_type (l_content_type)
+ else
+ h.put_content_type ("*/*")
+ end
+ h.put_content_length (a_description.count)
+ h.put_current_date
+ res.set_status_code ({HTTP_STATUS_CODE}.not_modified)
+ res.write_headers_string (h.string)
+ res.write_string (a_description)
+ end
+
+
handle_resource_conflict_response (a_description:STRING; ctx: C; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER)
local
h : EWF_HEADER