diff --git a/examples/filter/filter-safe.ecf b/examples/filter/filter-safe.ecf index a1b5c3fd..8edae596 100644 --- a/examples/filter/filter-safe.ecf +++ b/examples/filter/filter-safe.ecf @@ -13,6 +13,7 @@ + diff --git a/examples/filter/src/filter_server.e b/examples/filter/src/filter_server.e index 1c7a257c..7368581d 100644 --- a/examples/filter/src/filter_server.e +++ b/examples/filter/src/filter_server.e @@ -24,38 +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_authentication_filter_hdl: AUTHENTICATION_FILTER - l_user_filter: USER_HANDLER - l_routing_filter: WSF_ROUTING_FILTER + l_cors_filter: WSF_CORS_FILTER do - create router.make (1) - create l_authentication_filter_hdl - create l_user_filter - l_authentication_filter_hdl.set_next (l_user_filter) - - router.handle_with_request_methods ("/user/{userid}", l_authentication_filter_hdl, router.methods_get) - create l_routing_filter.make (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 @@ -82,6 +104,9 @@ feature -- Basic operations feature {NONE} -- Implementation + port: INTEGER = 9090 + -- Port number + router: WSF_ROUTER; -- Router diff --git a/library/network/protocol/http/src/http_header.e b/library/network/protocol/http/src/http_header.e index a9cee805..82281ccd 100644 --- a/library/network/protocol/http/src/http_header.e +++ b/library/network/protocol/http/src/http_header.e @@ -504,6 +504,32 @@ 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_values ({HTTP_HEADER_NAMES}.header_access_control_allow_methods, a_methods, Void) + 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]) 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/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/router/filter/wsf_cors_filter.e b/library/server/wsf/router/filter/wsf_cors_filter.e new file mode 100644 index 00000000..75db6afe --- /dev/null +++ b/library/server/wsf/router/filter/wsf_cors_filter.e @@ -0,0 +1,37 @@ +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. + local + l_header: HTTP_HEADER + do + create l_header.make + l_header.put_access_control_allow_all_origin + res.put_header_text (l_header.string) + 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..0c928e2a --- /dev/null +++ b/library/server/wsf/src/response/wsf_cors_options_response.e @@ -0,0 +1,68 @@ +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}.Ok) + header.put_content_type ({HTTP_MIME_TYPES}.text_plain) + header.put_current_date + 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) + 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_request.e b/library/server/wsf/src/wsf_request.e index 129c9937..bc755715 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -1041,6 +1041,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