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