From ff57d0ecd429a774f190d81a87707a7b44d0b59c Mon Sep 17 00:00:00 2001 From: Olivier Ligot Date: Wed, 9 Jan 2013 17:34:50 +0100 Subject: [PATCH 1/6] Cross-Origin Resource Sharing initial support Initial support for the Cross-Origin Resource Sharing specification. This allows JavaScript to make requests across domain boundaries. Also reviewed the filter example to get rid of the context and the generic classes (we can actually use {WSF_REQUEST}.execution_variable and {WSF_REQUEST}.set_execution_variable). Links: * How to enable server-side: http://enable-cors.org/server.html * Specification: http://www.w3.org/TR/cors/ * Github: http://developer.github.com/v3/#cross-origin-resource-sharing --- examples/filter/filter-safe.ecf | 25 +++--- .../filter/src/filter/authentication_filter.e | 12 +-- examples/filter/src/filter_server.e | 83 +++++++++++-------- examples/filter/src/resource/user_handler.e | 22 ++--- .../network/protocol/http/src/http_header.e | 63 ++++++++++---- .../protocol/http/src/http_header_names.e | 19 ++++- .../wsf/router/filter/wsf_cors_filter.e | 33 ++++++++ .../router/filter/wsf_cors_options_filter.e | 59 +++++++++++++ .../src/response/wsf_cors_options_response.e | 64 ++++++++++++++ library/server/wsf/src/wsf_response.e | 26 +++++- 10 files changed, 324 insertions(+), 82 deletions(-) create mode 100644 library/server/wsf/router/filter/wsf_cors_filter.e create mode 100644 library/server/wsf/router/filter/wsf_cors_options_filter.e create mode 100644 library/server/wsf/src/response/wsf_cors_options_response.e diff --git a/examples/filter/filter-safe.ecf b/examples/filter/filter-safe.ecf index ade4f80f..6c86cb8e 100644 --- a/examples/filter/filter-safe.ecf +++ b/examples/filter/filter-safe.ecf @@ -12,22 +12,23 @@ - - + + + + - - - - - - - - - - + + + + + + + + + diff --git a/examples/filter/src/filter/authentication_filter.e b/examples/filter/src/filter/authentication_filter.e index 9b407c7b..a18da1e7 100644 --- a/examples/filter/src/filter/authentication_filter.e +++ b/examples/filter/src/filter/authentication_filter.e @@ -8,9 +8,9 @@ class AUTHENTICATION_FILTER inherit - WSF_FILTER_CONTEXT_HANDLER [FILTER_HANDLER_CONTEXT] + WSF_FILTER - WSF_URI_TEMPLATE_CONTEXT_HANDLER [FILTER_HANDLER_CONTEXT] + WSF_URI_TEMPLATE_HANDLER SHARED_DATABASE_API @@ -18,7 +18,7 @@ inherit feature -- Basic operations - execute (ctx: FILTER_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE) + execute (req: WSF_REQUEST; res: WSF_RESPONSE) -- Execute the filter local l_auth: HTTP_AUTHORIZATION @@ -31,8 +31,8 @@ feature -- Basic operations attached l_auth.password as l_auth_password and then l_auth_password.same_string (l_user.password) then - ctx.set_user (l_user) - execute_next (ctx, req, res) + req.set_execution_variable ("user", l_user) + execute_next (req, res) else handle_unauthorized ("Unauthorized", req, res) end @@ -56,6 +56,6 @@ feature {NONE} -- Implementation end note - copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others" + copyright: "2011-2013, Olivier Ligot, Jocelyn Fiat and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" end diff --git a/examples/filter/src/filter_server.e b/examples/filter/src/filter_server.e index 25d16fef..7368581d 100644 --- a/examples/filter/src/filter_server.e +++ b/examples/filter/src/filter_server.e @@ -24,39 +24,60 @@ create feature {NONE} -- Initialization make + local + l_message: STRING + l_factory: INET_ADDRESS_FACTORY do + create router.make (1) initialize_filter initialize_json - set_service_option ("port", 9090) + set_service_option ("port", port) + create l_message.make_empty + l_message.append_string ("Launching filter server at ") + create l_factory + l_message.append_string (l_factory.create_localhost.host_name) + l_message.append_string (" port ") + l_message.append_integer (port) + io.put_string (l_message) + io.put_new_line make_and_launch end create_filter -- Create `filter' local - l_router: WSF_ROUTER - l_authentication_filter_hdl: AUTHENTICATION_FILTER - l_user_filter: USER_HANDLER - l_routing_filter: WSF_ROUTING_FILTER + l_cors_filter: WSF_CORS_FILTER do - create l_router.make (1) - create l_authentication_filter_hdl - create l_user_filter - l_authentication_filter_hdl.set_next (l_user_filter) - - l_router.handle_with_request_methods ("/user/{userid}", l_authentication_filter_hdl, l_router.methods_get) - create l_routing_filter.make (l_router) - l_routing_filter.set_execute_default_action (agent execute_default) - filter := l_routing_filter + create l_cors_filter + filter := l_cors_filter end setup_filter -- Setup `filter' local + l_options_filter: WSF_CORS_OPTIONS_FILTER + l_authentication_filter: AUTHENTICATION_FILTER + l_user_filter: USER_HANDLER + l_methods: WSF_REQUEST_METHODS + l_routing_filter: WSF_ROUTING_FILTER l_logging_filter: WSF_LOGGING_FILTER do + create l_options_filter.make (router) + create l_authentication_filter + l_options_filter.set_next (l_authentication_filter) + create l_user_filter + l_authentication_filter.set_next (l_user_filter) + + create l_methods + l_methods.enable_options + l_methods.enable_get + router.handle_with_request_methods ("/user/{userid}", l_options_filter, l_methods) + create l_routing_filter.make (router) + l_routing_filter.set_execute_default_action (agent execute_default) + filter.set_next (l_routing_filter) + create l_logging_filter - filter.set_next (l_logging_filter) + l_routing_filter.set_next (l_logging_filter) end initialize_json @@ -73,30 +94,24 @@ feature -- Basic operations end execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) - -- I'm using this method to handle the method not allowed response - -- in the case that the given uri does not have a corresponding http method - -- to handle it. local - h : HTTP_HEADER - l_description : STRING - l_api_doc : STRING + l_message: WSF_DEFAULT_ROUTER_RESPONSE do - if req.content_length_value > 0 then - req.input.read_string (req.content_length_value.as_integer_32) - end - create h.make - h.put_content_type_text_plain - l_api_doc := "%NPlease check the API%NURI:/user/{userid} METHOD: GET%N" - l_description := req.request_method + req.request_uri + " is not allowed" + "%N" + l_api_doc - h.put_content_length (l_description.count) - h.put_current_date - res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed) - res.put_header_text (h.string) - res.put_string (l_description) + create l_message.make_with_router (req, router) + l_message.set_documentation_included (True) + res.send (l_message) end +feature {NONE} -- Implementation + + port: INTEGER = 9090 + -- Port number + + router: WSF_ROUTER; + -- Router + note - copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others" + copyright: "2011-2013, Olivier Ligot, Jocelyn Fiat and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/examples/filter/src/resource/user_handler.e b/examples/filter/src/resource/user_handler.e index 223e93d5..722ccd03 100644 --- a/examples/filter/src/resource/user_handler.e +++ b/examples/filter/src/resource/user_handler.e @@ -8,11 +8,11 @@ class USER_HANDLER inherit - WSF_FILTER_CONTEXT_HANDLER [FILTER_HANDLER_CONTEXT] + WSF_FILTER - WSF_URI_TEMPLATE_CONTEXT_HANDLER [FILTER_HANDLER_CONTEXT] + WSF_URI_TEMPLATE_HANDLER - WSF_RESOURCE_CONTEXT_HANDLER_HELPER [FILTER_HANDLER_CONTEXT] + WSF_RESOURCE_HANDLER_HELPER redefine do_get end @@ -23,30 +23,30 @@ inherit feature -- Basic operations - execute (ctx: FILTER_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE) + execute (req: WSF_REQUEST; res: WSF_RESPONSE) -- Execute request handler do - execute_methods (ctx, req, res) - execute_next (ctx, req, res) + execute_methods (req, res) + execute_next (req, res) end - do_get (ctx: FILTER_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE) + do_get (req: WSF_REQUEST; res: WSF_RESPONSE) -- Using GET to retrieve resource information. -- If the GET request is SUCCESS, we response with -- 200 OK, and a representation of the user -- If the GET request is not SUCCESS, we response with -- 404 Resource not found require else - authenticated_user_attached: attached ctx.user + authenticated_user_attached: attached {USER} req.execution_variable ("user") local id : STRING do if attached req.orig_path_info as orig_path then id := get_user_id_from_path (orig_path) if attached retrieve_user (id) as l_user then - if l_user ~ ctx.user then + if l_user ~ req.execution_variable ("user") then compute_response_get (req, res, l_user) - elseif attached ctx.user as l_auth_user then + elseif attached {USER} req.execution_variable ("user") as l_auth_user then -- Trying to access another user that the authenticated one, -- which is forbidden in this example... handle_forbidden ("You try to access the user " + id.out + " while authenticating with the user " + l_auth_user.id.out, req, res) @@ -92,6 +92,6 @@ feature {NONE} -- Implementation end note - copyright: "2011-2012, Olivier Ligot, Jocelyn Fiat and others" + copyright: "2011-2013, Olivier Ligot, Jocelyn Fiat 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 001067a9..4e89707e 100644 --- a/library/network/protocol/http/src/http_header.e +++ b/library/network/protocol/http/src/http_header.e @@ -213,6 +213,25 @@ feature -- Header change: general put_header (k + colon_space + v) end + put_header_key_methods (k: READABLE_STRING_8; a_methods: ITERABLE [READABLE_STRING_8]) + -- Add header `k: a_methods', or replace existing header of same header methods/key + local + s: STRING_8 + do + create s.make_empty + across + a_methods as c + loop + if not s.is_empty then + s.append_string (", ") + end + s.append (c.item) + end + if not s.is_empty then + put_header_key_value (k, s) + end + end + feature -- Content related header put_content_type (t: READABLE_STRING_8) @@ -397,26 +416,38 @@ feature -- Content-type helpers put_content_type_multipart_encrypted do put_content_type ({HTTP_MIME_TYPES}.multipart_encrypted) end put_content_type_application_x_www_form_encoded do put_content_type ({HTTP_MIME_TYPES}.application_x_www_form_encoded) end +feature -- Cross-Origin Resource Sharing + + put_access_control_allow_origin (s: READABLE_STRING_8) + -- Put "Access-Control-Allow-Origin" header. + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_origin, s) + end + + put_access_control_allow_all_origin + -- Put "Access-Control-Allow-Origin: *" header. + do + put_access_control_allow_origin ("*") + end + + put_access_control_allow_methods (a_methods: ITERABLE [READABLE_STRING_8]) + -- If `a_methods' is not empty, put `Access-Control-Allow-Methods' header with list `a_methods' of methods + do + put_header_key_methods ({HTTP_HEADER_NAMES}.header_access_control_allow_methods, a_methods) + end + + put_access_control_allow_headers (s: READABLE_STRING_8) + -- Put "Access-Control-Allow-Headers" header. + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, s) + end + feature -- Method related put_allow (a_methods: ITERABLE [READABLE_STRING_8]) -- If `a_methods' is not empty, put `Allow' header with list `a_methods' of methods - local - s: STRING_8 do - create s.make_empty - across - a_methods as c - loop - if not s.is_empty then - s.append_character (',') - end - s.append_character (' ') - s.append (c.item) - end - if not s.is_empty then - put_header_key_value ({HTTP_HEADER_NAMES}.header_allow, s) - end + put_header_key_methods ({HTTP_HEADER_NAMES}.header_allow, a_methods) end feature -- Date @@ -738,7 +769,7 @@ feature {NONE} -- Constants semi_colon_space: STRING = "; " note - copyright: "2011-2012, Jocelyn Fiat, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/network/protocol/http/src/http_header_names.e b/library/network/protocol/http/src/http_header_names.e index 8d679ea9..4bab76e8 100644 --- a/library/network/protocol/http/src/http_header_names.e +++ b/library/network/protocol/http/src/http_header_names.e @@ -194,6 +194,23 @@ feature -- Response header name -- Indicates the authentication scheme that should be used to access the requested entity. --| Example: WWW-Authenticate: Basic +feature -- Cross-Origin Resource Sharing + + header_access_control_allow_origin: STRING = "Access-Control-Allow-Origin" + -- Indicates whether a resource can be shared based by returning + -- the value of the Origin request header in the response. + -- | Example: Access-Control-Allow-Origin: http://example.org + + header_access_control_allow_methods: STRING = "Access-Control-Allow-Methods" + -- Indicates, as part of the response to a preflight request, + -- which methods can be used during the actual request. + -- | Example: Access-Control-Allow-Methods: PUT, DELETE + + header_access_control_allow_headers: STRING = "Access-Control-Allow-Headers" + -- Indicates, as part of the response to a preflight request, + -- which header field names can be used during the actual request. + -- | Example: Access-Control-Allow-Headers: Authorization + feature -- Request or Response header name header_cache_control: STRING = "Cache-Control" @@ -248,7 +265,7 @@ feature -- MIME related header_content_transfer_encoding: STRING = "Content-Transfer-Encoding" note - copyright: "2011-2012, Jocelyn Fiat, Eiffel Software and others" + copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/server/wsf/router/filter/wsf_cors_filter.e b/library/server/wsf/router/filter/wsf_cors_filter.e new file mode 100644 index 00000000..b306ad60 --- /dev/null +++ b/library/server/wsf/router/filter/wsf_cors_filter.e @@ -0,0 +1,33 @@ +note + description: "Cross-Origin Resource Sharing filter." + author: "Olivier Ligot" + date: "$Date$" + revision: "$Revision$" + EIS: "name=Cross-Origin Resource Sharing", "src=http://www.w3.org/TR/cors/", "tag=W3C" + +class + WSF_CORS_FILTER + +inherit + WSF_FILTER + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + do + res.header.put_access_control_allow_all_origin + execute_next (req, res) + 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/filter/wsf_cors_options_filter.e b/library/server/wsf/router/filter/wsf_cors_options_filter.e new file mode 100644 index 00000000..7ca0bd87 --- /dev/null +++ b/library/server/wsf/router/filter/wsf_cors_options_filter.e @@ -0,0 +1,59 @@ +note + description: "Filter that handles an OPTIONS request, with Cross-Origin Resource Sharing support." + author: "Olvier Ligot" + date: "$Date$" + revision: "$Revision$" + EIS: "name=Cross-Origin Resource Sharing", "src=http://www.w3.org/TR/cors/", "tag=W3C" + +class + WSF_CORS_OPTIONS_FILTER + +inherit + WSF_FILTER + + WSF_URI_TEMPLATE_HANDLER + +create + make + +feature {NONE} -- Initialization + + make (a_router: like router) + -- Initialize Current with `a_router'. + do + router := a_router + ensure + router_set: router = a_router + end + +feature -- Access + + router: WSF_ROUTER + -- Associated router + +feature -- Basic operations + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the filter. + local + msg: WSF_CORS_OPTIONS_RESPONSE + do + if req.is_request_method ({HTTP_REQUEST_METHODS}.method_options) then + create msg.make (req, router) + res.send (msg) + else + execute_next (req, res) + 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/src/response/wsf_cors_options_response.e b/library/server/wsf/src/response/wsf_cors_options_response.e new file mode 100644 index 00000000..a0790a0b --- /dev/null +++ b/library/server/wsf/src/response/wsf_cors_options_response.e @@ -0,0 +1,64 @@ +note + description: "Response to an OPTIONS request, with Cross-Origin Resource Sharing support." + author: "Olivier Ligt" + date: "$Date$" + revision: "$Revision$" + EIS: "name=Cross-Origin Resource Sharing", "src=http://www.w3.org/TR/cors/", "tag=W3C" + +class + WSF_CORS_OPTIONS_RESPONSE + +inherit + WSF_RESPONSE_MESSAGE + +create + make + +feature {NONE} -- Initialization + + make (req: WSF_REQUEST; a_router: like router) + do + request := req + router := a_router + create header.make + end + +feature -- Access + + request: WSF_REQUEST + -- Associated request + + router: WSF_ROUTER + -- Associated router + + header: HTTP_HEADER + -- Response' header + +feature {WSF_RESPONSE} -- Output + + send_to (res: WSF_RESPONSE) + local + l_methods: WSF_REQUEST_METHODS + do + res.set_status_code ({HTTP_STATUS_CODE}.No_content) + header.put_current_date + header.put_access_control_allow_headers ({HTTP_HEADER_NAMES}.header_authorization) + l_methods := router.allowed_methods_for_request (request) + if not l_methods.is_empty then + header.put_allow (l_methods) + header.put_access_control_allow_methods (l_methods) + end + res.put_header_text (header.string) + 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/src/wsf_response.e b/library/server/wsf/src/wsf_response.e index edd69fed..81d1e208 100644 --- a/library/server/wsf/src/wsf_response.e +++ b/library/server/wsf/src/wsf_response.e @@ -31,6 +31,7 @@ feature {NONE} -- Initialization do transfered_content_length := 0 wgi_response := r + create header.make end feature {WSF_RESPONSE_EXPORTER} -- Properties @@ -114,6 +115,13 @@ feature -- Status setting feature -- Header output operation + header: HTTP_HEADER + -- Header + -- This is useful when we want to fill the `header' + -- in two pass (i.e. in two different classes). + -- We first call features of `header', and finally + -- we call `put_header_text' + put_header_text (a_text: READABLE_STRING_8) -- Sent `a_text' and just before send the status code require @@ -121,9 +129,23 @@ feature -- Header output operation header_not_committed: not header_committed a_text_ends_with_single_crlf: a_text.count > 2 implies not a_text.substring (a_text.count - 2, a_text.count).same_string ("%R%N") a_text_does_not_end_with_double_crlf: a_text.count > 4 implies not a_text.substring (a_text.count - 4, a_text.count).same_string ("%R%N%R%N") + local + l_text: READABLE_STRING_8 + l_header: HTTP_HEADER do wgi_response.set_status_code (status_code, status_reason_phrase) - wgi_response.put_header_text (a_text) + if header.is_empty then + l_text := a_text + else + create l_header.make_from_raw_header_data (a_text) + across + l_header as c + loop + header.put_header (c.item.string) + end + l_text := header.string + end + wgi_response.put_header_text (l_text) ensure status_set: status_is_set status_committed: status_committed @@ -376,7 +398,7 @@ feature -- Error reporting end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + 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 From 12404a2d5cf309229735d6f6ced7f86c6d3fcf8e Mon Sep 17 00:00:00 2001 From: Olivier Ligot Date: Fri, 22 Feb 2013 15:58:09 +0100 Subject: [PATCH 2/6] CORS: respect specification regarding Access-Control-Allow-Headers According to the specification, the value of the response header Access-Control-Allow-Headers must contain at least all the values of the request header Access-Control-Request-Headers to be considered a valid request. Before this commit, only the Authorization value was present, which is enough for Firefox but not for Chrome. This should now work as expected. --- .../server/ewsgi/specification/request/wgi_meta_names.e | 2 ++ library/server/ewsgi/specification/request/wgi_request.e | 6 ++++++ .../ewsgi/src/implementation/wgi_request_from_table.e | 7 +++++++ .../server/wsf/src/response/wsf_cors_options_response.e | 8 ++++++-- library/server/wsf/src/wsf_request.e | 7 +++++++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/library/server/ewsgi/specification/request/wgi_meta_names.e b/library/server/ewsgi/specification/request/wgi_meta_names.e index 4b2f10b8..7eeb913e 100644 --- a/library/server/ewsgi/specification/request/wgi_meta_names.e +++ b/library/server/ewsgi/specification/request/wgi_meta_names.e @@ -50,6 +50,8 @@ feature -- Access http_transfer_encoding: STRING = "HTTP_TRANSFER_ENCODING" + http_access_control_request_headers: STRING = "HTTP_ACCESS_CONTROL_REQUEST_HEADERS" + gateway_interface: STRING = "GATEWAY_INTERFACE" auth_type: STRING = "AUTH_TYPE" diff --git a/library/server/ewsgi/specification/request/wgi_request.e b/library/server/ewsgi/specification/request/wgi_request.e index 1dccd709..86e1e489 100644 --- a/library/server/ewsgi/specification/request/wgi_request.e +++ b/library/server/ewsgi/specification/request/wgi_request.e @@ -598,6 +598,12 @@ feature -- HTTP_* deferred end + http_access_control_request_headers: detachable READABLE_STRING_8 + -- Indicates which headers will be used in the actual request + -- as part of the preflight request + deferred + end + feature -- Extra CGI environment variables request_uri: READABLE_STRING_8 diff --git a/library/server/ewsgi/src/implementation/wgi_request_from_table.e b/library/server/ewsgi/src/implementation/wgi_request_from_table.e index 2bfa00d6..79fb3007 100644 --- a/library/server/ewsgi/src/implementation/wgi_request_from_table.e +++ b/library/server/ewsgi/src/implementation/wgi_request_from_table.e @@ -241,6 +241,13 @@ feature -- Access: HTTP_* CGI meta parameters - 1.1 Result := meta_string_variable ({WGI_META_NAMES}.http_transfer_encoding) end + http_access_control_request_headers: detachable READABLE_STRING_8 + -- Indicates which headers will be used in the actual request + -- as part of the preflight request + do + Result := meta_string_variable ({WGI_META_NAMES}.http_access_control_request_headers) + end + feature -- Access: Extension to CGI meta parameters - 1.1 request_uri: READABLE_STRING_8 diff --git a/library/server/wsf/src/response/wsf_cors_options_response.e b/library/server/wsf/src/response/wsf_cors_options_response.e index a0790a0b..0c928e2a 100644 --- a/library/server/wsf/src/response/wsf_cors_options_response.e +++ b/library/server/wsf/src/response/wsf_cors_options_response.e @@ -40,9 +40,13 @@ feature {WSF_RESPONSE} -- Output local l_methods: WSF_REQUEST_METHODS do - res.set_status_code ({HTTP_STATUS_CODE}.No_content) + res.set_status_code ({HTTP_STATUS_CODE}.Ok) + header.put_content_type ({HTTP_MIME_TYPES}.text_plain) header.put_current_date - header.put_access_control_allow_headers ({HTTP_HEADER_NAMES}.header_authorization) + header.put_content_length (0) + if attached request.http_access_control_request_headers as l_headers then + header.put_access_control_allow_headers (l_headers) + end l_methods := router.allowed_methods_for_request (request) if not l_methods.is_empty then header.put_allow (l_methods) diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index d90d89b2..6cc4e65d 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -958,6 +958,13 @@ feature -- HTTP_* Result := wgi_request.http_transfer_encoding end + http_access_control_request_headers: detachable READABLE_STRING_8 + -- Indicates which headers will be used in the actual request + -- as part of the preflight request + do + Result := wgi_request.http_access_control_request_headers + end + feature -- Extra CGI environment variables request_uri: READABLE_STRING_8 From 45f0971594ef2dcbc3ebf7280a4d3e8846fc2fa4 Mon Sep 17 00:00:00 2001 From: Olivier Ligot Date: Fri, 15 Mar 2013 13:38:40 +0100 Subject: [PATCH 3/6] Use new upstrem method put_header_key_values --- .../network/protocol/http/src/http_header.e | 21 +------------------ library/server/wsf/src/wsf_response.e | 3 --- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/library/network/protocol/http/src/http_header.e b/library/network/protocol/http/src/http_header.e index 1460baaa..82281ccd 100644 --- a/library/network/protocol/http/src/http_header.e +++ b/library/network/protocol/http/src/http_header.e @@ -320,25 +320,6 @@ feature -- Header change: general end end - put_header_key_methods (k: READABLE_STRING_8; a_methods: ITERABLE [READABLE_STRING_8]) - -- Add header `k: a_methods', or replace existing header of same header methods/key - local - s: STRING_8 - do - create s.make_empty - across - a_methods as c - loop - if not s.is_empty then - s.append_string (", ") - end - s.append (c.item) - end - if not s.is_empty then - put_header_key_value (k, s) - end - end - feature -- Content related header put_content_type (t: READABLE_STRING_8) @@ -540,7 +521,7 @@ feature -- Cross-Origin Resource Sharing put_access_control_allow_methods (a_methods: ITERABLE [READABLE_STRING_8]) -- If `a_methods' is not empty, put `Access-Control-Allow-Methods' header with list `a_methods' of methods do - put_header_key_methods ({HTTP_HEADER_NAMES}.header_access_control_allow_methods, a_methods) + put_header_key_values ({HTTP_HEADER_NAMES}.header_access_control_allow_methods, a_methods, Void) end put_access_control_allow_headers (s: READABLE_STRING_8) diff --git a/library/server/wsf/src/wsf_response.e b/library/server/wsf/src/wsf_response.e index bacd9dc8..c70c1da3 100644 --- a/library/server/wsf/src/wsf_response.e +++ b/library/server/wsf/src/wsf_response.e @@ -187,9 +187,6 @@ feature -- Header output operation header_not_committed: not header_committed a_text_ends_with_single_crlf: a_text.count > 2 implies not a_text.substring (a_text.count - 2, a_text.count).same_string ("%R%N") a_text_does_not_end_with_double_crlf: a_text.count > 4 implies not a_text.substring (a_text.count - 4, a_text.count).same_string ("%R%N%R%N") - local - l_text: READABLE_STRING_8 - l_header: HTTP_HEADER do if header_committed then report_content_already_sent_and_header_ignored From 69495e69a97c672adb8525b2240a47911b95f18f Mon Sep 17 00:00:00 2001 From: Olivier Ligot Date: Fri, 15 Mar 2013 13:44:24 +0100 Subject: [PATCH 4/6] Filter example: remove unused libraries in ecf file --- examples/filter/filter-safe.ecf | 9 --------- 1 file changed, 9 deletions(-) diff --git a/examples/filter/filter-safe.ecf b/examples/filter/filter-safe.ecf index 6c86cb8e..f77a20c8 100644 --- a/examples/filter/filter-safe.ecf +++ b/examples/filter/filter-safe.ecf @@ -14,18 +14,9 @@ - - - - - - - From dde70512d8ec4db2c671c8b909a0a7dc5e3e1448 Mon Sep 17 00:00:00 2001 From: Olivier Ligot Date: Fri, 15 Mar 2013 14:15:17 +0100 Subject: [PATCH 5/6] Use features from the flexible_response branch --- library/server/wsf/router/filter/wsf_cors_filter.e | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/server/wsf/router/filter/wsf_cors_filter.e b/library/server/wsf/router/filter/wsf_cors_filter.e index b306ad60..75db6afe 100644 --- a/library/server/wsf/router/filter/wsf_cors_filter.e +++ b/library/server/wsf/router/filter/wsf_cors_filter.e @@ -15,8 +15,12 @@ feature -- Basic operations execute (req: WSF_REQUEST; res: WSF_RESPONSE) -- Execute the filter. + local + l_header: HTTP_HEADER do - res.header.put_access_control_allow_all_origin + create l_header.make + l_header.put_access_control_allow_all_origin + res.put_header_text (l_header.string) execute_next (req, res) end From c8845e73545a460b8272934974b8bb110b1510e0 Mon Sep 17 00:00:00 2001 From: Olivier Ligot Date: Fri, 15 Mar 2013 14:18:13 +0100 Subject: [PATCH 6/6] Fix indentation --- examples/filter/filter-safe.ecf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/filter/filter-safe.ecf b/examples/filter/filter-safe.ecf index f77a20c8..8edae596 100644 --- a/examples/filter/filter-safe.ecf +++ b/examples/filter/filter-safe.ecf @@ -19,7 +19,7 @@ - +