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