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
This commit is contained in:
Olivier Ligot
2013-01-09 17:34:50 +01:00
parent 65d7545320
commit ff57d0ecd4
10 changed files with 324 additions and 82 deletions

View File

@@ -12,22 +12,23 @@
<assertions precondition="true" postcondition="true" invariant="true" supplier_precondition="true"/>
</option>
<setting name="concurrency" value="thread"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="connector_nino" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false">
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf" readonly="true"/>
<library name="net" location="$ISE_LIBRARY\library\net\net-safe.ecf" readonly="true"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf" readonly="true"/>
<library name="connector_nino" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="true">
<option debug="true">
<debug name="nino" enabled="true"/>
</option>
</library>
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="false"/>
<library name="eel" location="..\..\contrib\ise_library\text\encryption\eel\eel-safe.ecf" readonly="false"/>
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
<library name="http" location="../../library/network/protocol/http/http-safe.ecf" readonly="false"/>
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="false"/>
<library name="uri_template" location="../../library/text/parser/uri_template/uri_template-safe.ecf" readonly="false"/>
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="false"/>
<library name="wsf_extension" location="..\..\library\server\wsf\wsf_extension-safe.ecf" readonly="false"/>
<library name="http_authorization" location="..\..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="false"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="default_nino" location="..\..\library\server\wsf\default\nino-safe.ecf" readonly="true"/>
<library name="eel" location="..\..\contrib\ise_library\text\encryption\eel\eel-safe.ecf" readonly="true"/>
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="true"/>
<library name="http" location="../../library/network/protocol/http/http-safe.ecf" readonly="true"/>
<library name="json" location="..\..\contrib\library\text\parser\json\library\json-safe.ecf" readonly="true"/>
<library name="uri_template" location="../../library/text/parser/uri_template/uri_template-safe.ecf" readonly="true"/>
<library name="wsf" location="..\..\library\server\wsf\wsf-safe.ecf" readonly="true"/>
<library name="wsf_extension" location="..\..\library\server\wsf\wsf_extension-safe.ecf" readonly="true"/>
<library name="http_authorization" location="..\..\library\server\authentication\http_authorization\http_authorization-safe.ecf" readonly="true"/>
<cluster name="src" location="src\" recursive="true"/>
</target>
</system>

View File

@@ -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

View File

@@ -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

View File

@@ -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