From 8a58d62a7ee1118bd6e508d4b664a362c07223c7 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 11 Jun 2012 14:58:13 +0200 Subject: [PATCH] Adopted convention name and value or values for WSF_VALUE and descendant (WSF_STRING ...) kept `key' as redirection, and also string as obsolete redirection. Router: provide a way to pass the request methods without using manifest string, thanks to WSF_ROUTER_METHODS so instead of using manifest array or manifest strings, just create an instance of WSF_ROUTER_METHODS for convenience, WSF_ROUTER provides a few `methods_...' returning prebuilt WSF_ROUTER_METHODS objects Improved code related to unicode handling in URL, and parameters (before the framework was doing too much) --- examples/tutorial/step_3/hello/hello.ecf | 2 +- .../step_4/hello/src/hello_application.e | 36 +- .../step_4/hello/src/user_message_handler.e | 31 +- .../protocol/http/src/http_request_methods.e | 31 +- .../wsf/router/uri/default/wsf_uri_router.e | 6 +- .../server/wsf/router/uri/wsf_uri_router_i.e | 30 +- .../default/wsf_uri_template_router.e | 4 +- .../uri_template/wsf_uri_template_router_i.e | 16 +- .../wsf/router/wsf_file_system_handler.e | 2 +- library/server/wsf/router/wsf_handler.e | 2 +- library/server/wsf/router/wsf_router.e | 141 ++++-- .../server/wsf/router/wsf_router_methods.e | 402 ++++++++++++++++++ .../server/wsf/router/wsf_routing_handler.e | 5 +- .../server/wsf/src/request/value/wsf_any.e | 15 +- .../src/request/value/wsf_multiple_string.e | 45 +- .../server/wsf/src/request/value/wsf_string.e | 100 +++-- .../server/wsf/src/request/value/wsf_table.e | 11 +- .../wsf/src/request/value/wsf_uploaded_file.e | 7 +- library/server/wsf/src/request/wsf_value.e | 7 +- .../wsf/src/response/wsf_download_response.e | 2 +- .../wsf/src/response/wsf_file_response.e | 2 +- .../wsf/src/response/wsf_html_page_response.e | 14 +- .../response/wsf_html_redirection_response.e | 4 +- .../wsf/src/response/wsf_page_response.e | 2 +- .../src/response/wsf_redirection_response.e | 2 +- library/server/wsf/src/wsf_request.e | 23 +- library/server/wsf/src/wsf_response_message.e | 15 +- library/text/encoder/src/html_encoder.e | 23 +- library/text/encoder/src/utf8_url_encoder.e | 8 +- 29 files changed, 790 insertions(+), 198 deletions(-) create mode 100644 library/server/wsf/router/wsf_router_methods.e diff --git a/examples/tutorial/step_3/hello/hello.ecf b/examples/tutorial/step_3/hello/hello.ecf index 1cfd0b49..b58272ca 100644 --- a/examples/tutorial/step_3/hello/hello.ecf +++ b/examples/tutorial/step_3/hello/hello.ecf @@ -12,7 +12,7 @@ - + diff --git a/examples/tutorial/step_4/hello/src/hello_application.e b/examples/tutorial/step_4/hello/src/hello_application.e index fab7157c..68dd1cec 100644 --- a/examples/tutorial/step_4/hello/src/hello_application.e +++ b/examples/tutorial/step_4/hello/src/hello_application.e @@ -30,11 +30,10 @@ feature {NONE} -- Initialization do router.map_agent ("/hello", agent execute_hello) + router.map_with_request_methods ("/users/{user}/message/{mesgid}", create {USER_MESSAGE_HANDLER}, router.methods_GET_POST) + router.map_with_request_methods ("/users/{user}/message/", create {USER_MESSAGE_HANDLER}, router.methods_GET_POST) - router.map_with_request_methods ("/users/{user}/message/{mesgid}", create {USER_MESSAGE_HANDLER}, <<"GET", "POST">>) - router.map_with_request_methods ("/users/{user}/message/", create {USER_MESSAGE_HANDLER}, <<"GET", "POST">>) - - router.map_agent_response_with_request_methods ("/users/{user}/{?op}", agent response_user, <<"GET">>) + router.map_agent_response_with_request_methods ("/users/{user}/{?op}", agent response_user, router.methods_GET) end feature -- Execution @@ -50,6 +49,7 @@ feature -- Execution local mesg: WSF_HTML_PAGE_RESPONSE s: STRING_8 + l_user_name: READABLE_STRING_32 do --| It is now returning a WSF_HTML_PAGE_RESPONSE --| Since it is easier for building html page @@ -59,9 +59,12 @@ feature -- Execution --| this could be a query, or a form parameter if attached {WSF_STRING} req.item ("user") as u then --| If yes, say hello world #name - s := "

Hello " + u.html_encoded_string + "!

" - s.append ("Display a message

") - s.append ("

Click here to quit.

") + + l_user_name := (create {HTML_ENCODER}).decoded_string (u.value) + + s := "

Hello " + mesg.html_encoded_string (l_user_name) + "!

" + s.append ("Display a message

") + s.append ("

Click here to quit.

") mesg.set_body (s) --| We should html encode this name --| but to keep the example simple, we don't do that for now. @@ -69,7 +72,7 @@ feature -- Execution --| Otherwise, ask for name s := (create {HTML_ENCODER}).encoded_string ({STRING_32} "Hello / ahoj / नमस्ते / Ciào / مرحبا / Hola / 你好 / Hallo / Selam / Bonjour ") s.append ("[ -
+ What is your name?

@@ -96,30 +99,33 @@ feature -- Execution html: WSF_HTML_PAGE_RESPONSE redir: WSF_HTML_DELAYED_REDIRECTION_RESPONSE s: STRING_8 + l_username: STRING_32 do if attached {WSF_STRING} ctx.path_parameter ("user") as u then + l_username := (create {HTML_ENCODER}).general_decoded_string (u.value) if attached {WSF_STRING} req.query_parameter ("op") as l_op then if l_op.is_case_insensitive_equal ("quit") then create redir.make (req.script_url ("/hello"), 5) - redir.set_title ("Bye " + u.url_encoded_string) - redir.set_body ("Bye " + u.url_encoded_string + ",
see you soon.

You will be redirected to " + + redir.set_title ("Bye " + u.url_encoded_value) + redir.set_body ("Bye " + u.url_encoded_value + ",
see you soon.

You will be redirected to " + redir.url_location + " in " + redir.delay.out + " second(s) ...

" ) Result := redir else create html.make html.set_title ("Bad request") - html.set_body ("Bad request: unknown operation '" + l_op.url_encoded_string + "'.") + html.set_body ("Bad request: unknown operation '" + l_op.url_encoded_value + "'.") Result := html end else - s := "

User '" + u.url_encoded_string + "'!

" - s.append ("Display a message

") - s.append ("

Click here to quit.

") create html.make - html.set_title ("User '" + u.url_encoded_string + "'") + + s := "

User '" + html.html_encoded_string (l_username) + "'!

" + s.append ("Display a message

") + s.append ("

Click here to quit.

") + html.set_title ("User '" + u.url_encoded_value + "'") html.set_body (s) Result := html end diff --git a/examples/tutorial/step_4/hello/src/user_message_handler.e b/examples/tutorial/step_4/hello/src/user_message_handler.e index 3a7d3ffd..616cd337 100644 --- a/examples/tutorial/step_4/hello/src/user_message_handler.e +++ b/examples/tutorial/step_4/hello/src/user_message_handler.e @@ -14,12 +14,15 @@ inherit feature -- Access response (ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST): WSF_RESPONSE_MESSAGE + local + l_username: READABLE_STRING_32 do if attached {WSF_STRING} ctx.path_parameter ("user") as u then + l_username := html_decoded_string (u.value) if req.is_request_method ("GET") then - Result := user_message_get (u, ctx, req) + Result := user_message_get (l_username, ctx, req) elseif req.is_request_method ("POST") then - Result := user_message_response_post (u, ctx, req) + Result := user_message_response_post (l_username, ctx, req) else Result := unsupported_method_response (req) end @@ -43,12 +46,12 @@ feature -- Access end - user_message_get (u: WSF_STRING; ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST): WSF_HTML_PAGE_RESPONSE + user_message_get (u: READABLE_STRING_32; ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST): WSF_HTML_PAGE_RESPONSE local s: STRING_8 do create Result.make - s := "

No message from user '" + u.html_encoded_string + "'.

" + s := "

No message from user '" + Result.html_encoded_string (u) + "'.

" s.append ("") s.append ("") s.append ("") @@ -56,18 +59,32 @@ feature -- Access Result.set_body (s) end - user_message_response_post (u: WSF_STRING; ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST): WSF_HTML_PAGE_RESPONSE + user_message_response_post (u: READABLE_STRING_32; ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST): WSF_HTML_PAGE_RESPONSE local s: STRING_8 do create Result.make - s := "

Message from user '" + u.html_encoded_string + "'.

" + s := "

Message from user '" + Result.html_encoded_string (u) + "'.

" if attached {WSF_STRING} req.form_parameter ("message") as m and then not m.is_empty then - s.append ("") + s.append ("") else s.append ("No or empty message!") end Result.set_body (s) end + url_encoded_string (s: READABLE_STRING_32): STRING_8 + do + Result := (create {UTF8_URL_ENCODER}).encoded_string (s) + end + + html_decoded_string (v: READABLE_STRING_32): READABLE_STRING_32 + do + if v.is_valid_as_string_8 then + Result := (create {HTML_ENCODER}).decoded_string (v) + else + Result := v + end + end + end diff --git a/library/protocol/http/src/http_request_methods.e b/library/protocol/http/src/http_request_methods.e index c4f0a0ab..1a23f8c2 100644 --- a/library/protocol/http/src/http_request_methods.e +++ b/library/protocol/http/src/http_request_methods.e @@ -13,6 +13,35 @@ note class HTTP_REQUEST_METHODS +feature -- Query + + method (m: READABLE_STRING_8): READABLE_STRING_8 + -- Return the associated constant object if any + -- otherwise the uppercased version of `m' + do + if m.is_case_insensitive_equal (method_get) then + Result := method_get + elseif m.is_case_insensitive_equal (method_post) then + Result := method_post + elseif m.is_case_insensitive_equal (method_head) then + Result := method_head + elseif m.is_case_insensitive_equal (method_trace) then + Result := method_trace + elseif m.is_case_insensitive_equal (method_options) then + Result := method_options + elseif m.is_case_insensitive_equal (method_put) then + Result := method_put + elseif m.is_case_insensitive_equal (method_delete) then + Result := method_delete + elseif m.is_case_insensitive_equal (method_connect) then + Result := method_connect + elseif m.is_case_insensitive_equal (method_patch) then + Result := method_patch + else + Result := m.as_upper + end + end + feature -- Safe Methods method_head: STRING = "HEAD" @@ -63,7 +92,7 @@ feature -- Other Methods -- Is used to apply partial modifications to a resource note - copyright: "2011-2011, Eiffel Software and others" + copyright: "2011-2012, 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/uri/default/wsf_uri_router.e b/library/server/wsf/router/uri/default/wsf_uri_router.e index 5b7c78ca..b4305757 100644 --- a/library/server/wsf/router/uri/default/wsf_uri_router.e +++ b/library/server/wsf/router/uri/default/wsf_uri_router.e @@ -13,14 +13,14 @@ inherit redefine map_agent_with_request_methods, map_agent_response_with_request_methods end - + create make feature -- Mapping agent map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: WSF_URI_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE]]; - rqst_methods: detachable ARRAY [READABLE_STRING_8]) + rqst_methods: detachable WSF_ROUTER_METHODS) local h: WSF_AGENT_HANDLER [WSF_URI_HANDLER_CONTEXT] do @@ -29,7 +29,7 @@ feature -- Mapping agent end map_agent_response_with_request_methods (a_id: READABLE_STRING_8; a_action: FUNCTION [ANY, TUPLE [ctx: WSF_URI_HANDLER_CONTEXT; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE]; - rqst_methods: detachable ARRAY [READABLE_STRING_8]) + rqst_methods: detachable WSF_ROUTER_METHODS) local h: WSF_AGENT_RESPONSE_HANDLER [WSF_URI_HANDLER_CONTEXT] do diff --git a/library/server/wsf/router/uri/wsf_uri_router_i.e b/library/server/wsf/router/uri/wsf_uri_router_i.e index 25bf6165..3058a73d 100644 --- a/library/server/wsf/router/uri/wsf_uri_router_i.e +++ b/library/server/wsf/router/uri/wsf_uri_router_i.e @@ -47,7 +47,7 @@ feature -- Initialization feature {WSF_ROUTED_SERVICE_I} -- Status report - handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable ARRAY [READABLE_STRING_8]): detachable LIST [H] + handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS): detachable LIST [H] local l_res: READABLE_STRING_8 do @@ -79,7 +79,7 @@ feature {WSF_ROUTED_SERVICE_I} -- Default: implementation feature -- Registration - map_with_request_methods (p: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8]) + map_with_request_methods (p: READABLE_STRING_8; h: H; rqst_methods: detachable WSF_ROUTER_METHODS) local l_uri: READABLE_STRING_8 do @@ -88,7 +88,7 @@ feature -- Registration else l_uri := p end - handlers.force ([h, l_uri, formatted_request_methods (rqst_methods)]) + handlers.force ([h, l_uri, rqst_methods]) h.on_handler_mapped (l_uri, rqst_methods) end @@ -109,10 +109,13 @@ feature {WSF_ROUTED_SERVICE_I} -- Handler local h: detachable H ctx: detachable C + rq_method: READABLE_STRING_8 do - h := handler_by_path (source_uri (req), req.request_method) + rq_method := request_method (req) + + h := handler_by_path (source_uri (req), rq_method) if h = Void then - if attached smart_handler_by_path (source_uri (req), req.request_method) as info then + if attached smart_handler_by_path (source_uri (req), rq_method) as info then h := info.handler ctx := handler_context (info.path, req) end @@ -131,16 +134,7 @@ feature {WSF_ROUTED_SERVICE_I} -- Handler feature {NONE} -- Access: Implementation - smart_handler (req: WSF_REQUEST): detachable TUPLE [path: READABLE_STRING_8; handler: H] - require - req_valid: req /= Void and then source_uri (req) /= Void - do - Result := smart_handler_by_path (source_uri (req), req.request_method) - ensure - req_path_info_unchanged: source_uri (req).same_string (old source_uri (req)) - end - - handler_by_path (a_path: READABLE_STRING_GENERAL; rqst_method: READABLE_STRING_GENERAL): detachable H + handler_by_path (a_path: READABLE_STRING_GENERAL; rqst_method: READABLE_STRING_8): detachable H require a_path_valid: a_path /= Void local @@ -163,7 +157,7 @@ feature {NONE} -- Access: Implementation a_path_unchanged: a_path.same_string (old a_path) end - smart_handler_by_path (a_path: READABLE_STRING_8; rqst_method: READABLE_STRING_GENERAL): detachable TUPLE [path: READABLE_STRING_8; handler: H] + smart_handler_by_path (a_path: READABLE_STRING_8; rqst_method: READABLE_STRING_8): detachable TUPLE [path: READABLE_STRING_8; handler: H] require a_path_valid: a_path /= Void local @@ -207,7 +201,7 @@ feature {NONE} -- Context factory feature -- Access - new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]] + new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]] -- Fresh cursor associated with current structure do Result := handlers.new_cursor @@ -215,7 +209,7 @@ feature -- Access feature {NONE} -- Implementation - handlers: ARRAYED_LIST [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]] + handlers: ARRAYED_LIST [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]] -- Handlers indexed by the template expression -- see `templates' diff --git a/library/server/wsf/router/uri_template/default/wsf_uri_template_router.e b/library/server/wsf/router/uri_template/default/wsf_uri_template_router.e index a29c3492..e537d0bd 100644 --- a/library/server/wsf/router/uri_template/default/wsf_uri_template_router.e +++ b/library/server/wsf/router/uri_template/default/wsf_uri_template_router.e @@ -22,7 +22,7 @@ create feature -- Mapping agent map_agent_with_request_methods (a_id: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST; res: WSF_RESPONSE]]; - rqst_methods: detachable ARRAY [READABLE_STRING_8]) + rqst_methods: detachable WSF_ROUTER_METHODS) local h: WSF_AGENT_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT] do @@ -31,7 +31,7 @@ feature -- Mapping agent end map_agent_response_with_request_methods (a_id: READABLE_STRING_8; a_action: FUNCTION [ANY, TUPLE [ctx: WSF_URI_TEMPLATE_HANDLER_CONTEXT; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE]; - rqst_methods: detachable ARRAY [READABLE_STRING_8]) + rqst_methods: detachable WSF_ROUTER_METHODS) local h: WSF_AGENT_RESPONSE_HANDLER [WSF_URI_TEMPLATE_HANDLER_CONTEXT] do diff --git a/library/server/wsf/router/uri_template/wsf_uri_template_router_i.e b/library/server/wsf/router/uri_template/wsf_uri_template_router_i.e index fd4b8b06..24ac6ea3 100644 --- a/library/server/wsf/router/uri_template/wsf_uri_template_router_i.e +++ b/library/server/wsf/router/uri_template/wsf_uri_template_router_i.e @@ -44,7 +44,7 @@ feature -- Initialization feature -- Status report - handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable ARRAY [READABLE_STRING_8]): detachable LIST [H] + handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS): detachable LIST [H] local l_res: READABLE_STRING_8 do @@ -75,7 +75,7 @@ feature -- Registration map_with_uri_template_and_request_methods (uri, h, Void) end - map_with_uri_template_and_request_methods (uri: URI_TEMPLATE; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8]) + map_with_uri_template_and_request_methods (uri: URI_TEMPLATE; h: H; rqst_methods: detachable WSF_ROUTER_METHODS) require uri_is_valid: uri.is_valid has_not_such_map: not has_map (uri.template, rqst_methods, h) @@ -86,12 +86,12 @@ feature -- Registration l_uri := based_uri (uri) l_tpl := l_uri.template - handlers.force ([h, l_tpl, formatted_request_methods (rqst_methods)]) + handlers.force ([h, l_tpl, rqst_methods]) templates.force (l_uri, l_tpl) h.on_handler_mapped (l_tpl, rqst_methods) end - map_with_request_methods (tpl: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8]) + map_with_request_methods (tpl: READABLE_STRING_8; h: H; rqst_methods: detachable WSF_ROUTER_METHODS) do map_with_uri_template_and_request_methods (create {URI_TEMPLATE}.make (tpl), h, rqst_methods) end @@ -124,12 +124,12 @@ feature {WSF_ROUTED_SERVICE_I} -- Handler l_handlers: like handlers t: READABLE_STRING_8 p: READABLE_STRING_8 - l_req_method: READABLE_STRING_GENERAL + l_req_method: READABLE_STRING_8 l_res: URI_TEMPLATE_MATCH_RESULT do p := source_uri (req) from - l_req_method := req.request_method + l_req_method := request_method (req) l_handlers := handlers l_handlers.start until @@ -178,7 +178,7 @@ feature {NONE} -- Context factory feature -- Access: ITERABLE - new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]] + new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]] -- Fresh cursor associated with current structure do Result := handlers.new_cursor @@ -186,7 +186,7 @@ feature -- Access: ITERABLE feature {NONE} -- Implementation - handlers: ARRAYED_LIST [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]] + handlers: ARRAYED_LIST [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]] -- Handlers indexed by the template expression -- see `templates' diff --git a/library/server/wsf/router/wsf_file_system_handler.e b/library/server/wsf/router/wsf_file_system_handler.e index 0eb12e71..fc18bedb 100644 --- a/library/server/wsf/router/wsf_file_system_handler.e +++ b/library/server/wsf/router/wsf_file_system_handler.e @@ -67,7 +67,7 @@ feature -- Execution -- i.e: path of the file system resource if any do if attached {WSF_STRING} ctx.item ("path") as v_path then - Result := v_path.string.as_string_8 + Result := v_path.value.as_string_8 end end diff --git a/library/server/wsf/router/wsf_handler.e b/library/server/wsf/router/wsf_handler.e index 33c85c04..0bdf431d 100644 --- a/library/server/wsf/router/wsf_handler.e +++ b/library/server/wsf/router/wsf_handler.e @@ -76,7 +76,7 @@ feature -- Execution: report feature {WSF_ROUTER} -- Routes change - on_handler_mapped (a_resource: READABLE_STRING_8; a_rqst_methods: detachable ARRAY [READABLE_STRING_8]) + on_handler_mapped (a_resource: READABLE_STRING_8; a_rqst_methods: detachable WSF_ROUTER_METHODS) -- Callback called when a router map a route to Current handler do end diff --git a/library/server/wsf/router/wsf_router.e b/library/server/wsf/router/wsf_router.e index c75186bc..79ad7360 100644 --- a/library/server/wsf/router/wsf_router.e +++ b/library/server/wsf/router/wsf_router.e @@ -10,7 +10,7 @@ deferred class WSF_ROUTER [H -> WSF_HANDLER [C], C -> WSF_HANDLER_CONTEXT] inherit - ITERABLE [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]] + ITERABLE [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]] feature {NONE} -- Initialization @@ -22,7 +22,7 @@ feature {NONE} -- Initialization feature -- Status report - has_map (a_resource: READABLE_STRING_8; rqst_methods: detachable ARRAY [READABLE_STRING_8]; a_handler: detachable H): BOOLEAN + has_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS; a_handler: detachable H): BOOLEAN -- Has a map corresponding to `a_resource' and `rqst_methods' other than `a_handler'? do if attached handlers_matching_map (a_resource, rqst_methods) as lst then @@ -30,7 +30,7 @@ feature -- Status report end end - handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable ARRAY [READABLE_STRING_8]): detachable LIST [H] + handlers_matching_map (a_resource: READABLE_STRING_8; rqst_methods: detachable WSF_ROUTER_METHODS): detachable LIST [H] -- Existing handlers matching map with `a_resource' and `rqst_methods' deferred end @@ -53,7 +53,7 @@ feature -- Mapping map (a_resource, h) end - map_with_request_methods (a_resource: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8]) + map_with_request_methods (a_resource: READABLE_STRING_8; h: H; rqst_methods: detachable WSF_ROUTER_METHODS) -- Map handler `h' with `a_resource' and `rqst_methods' require has_not_such_map: not has_map (a_resource, rqst_methods, h) @@ -69,7 +69,7 @@ feature -- Mapping agent end map_agent_with_request_methods (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]]; - rqst_methods: detachable ARRAY [READABLE_STRING_8]) + rqst_methods: detachable WSF_ROUTER_METHODS) -- Map `a_action' as an handler with `a_resource' and `rqst_methods' local rah: WSF_AGENT_HANDLER [C] @@ -91,7 +91,7 @@ feature -- Mapping response agent end map_agent_response_with_request_methods (a_resource: READABLE_STRING_8; a_function: FUNCTION [ANY, TUPLE [ctx: C; req: WSF_REQUEST], WSF_RESPONSE_MESSAGE]; - rqst_methods: detachable ARRAY [READABLE_STRING_8]) + rqst_methods: detachable WSF_ROUTER_METHODS) -- Map response as Result `a_function' as an handler with `a_resource' and `rqst_methods' local rah: WSF_AGENT_RESPONSE_HANDLER [C] @@ -203,7 +203,7 @@ feature -- status report feature -- Traversing - new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable ARRAY [READABLE_STRING_8]]] + new_cursor: ITERATION_CURSOR [TUPLE [handler: H; resource: READABLE_STRING_8; request_methods: detachable WSF_ROUTER_METHODS]] -- Fresh cursor associated with current structure deferred end @@ -225,51 +225,108 @@ feature {WSF_ROUTED_SERVICE_I} -- Handler source_uri_unchanged: source_uri (req).same_string (old source_uri (req)) end +feature -- Request methods helper + + methods_head: WSF_ROUTER_METHODS + once ("THREAD") + create Result + Result.enable_head + Result.lock + end + + methods_options: WSF_ROUTER_METHODS + once ("THREAD") + create Result + Result.enable_options + Result.lock + end + + methods_get: WSF_ROUTER_METHODS + once ("THREAD") + create Result + Result.enable_get + Result.lock + end + + methods_post: WSF_ROUTER_METHODS + once ("THREAD") + create Result + Result.enable_post + Result.lock + end + + methods_put: WSF_ROUTER_METHODS + once ("THREAD") + create Result + Result.enable_put + Result.lock + end + + methods_delete: WSF_ROUTER_METHODS + once ("THREAD") + create Result + Result.enable_delete + Result.lock + end + + methods_get_post: WSF_ROUTER_METHODS + once ("THREAD") + create Result.make (2) + Result.enable_get + Result.enable_post + Result.lock + end + + methods_put_post: WSF_ROUTER_METHODS + once ("THREAD") + create Result.make (2) + Result.enable_put + Result.enable_post + Result.lock + end + feature {NONE} -- Access: Implementation - is_matching_request_methods (a_request_method: READABLE_STRING_GENERAL; a_rqst_methods: like formatted_request_methods): BOOLEAN - -- `a_request_method' is matching `a_rqst_methods' contents + request_method (req: WSF_REQUEST): READABLE_STRING_8 + -- Request method from `req' to be used in the router implementation. local - i,n: INTEGER - m: READABLE_STRING_GENERAL + m: READABLE_STRING_8 + do + m := req.request_method + if m.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get) then + Result := {HTTP_REQUEST_METHODS}.method_get + elseif m.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_post) then + Result := {HTTP_REQUEST_METHODS}.method_post + elseif m.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_head) then + Result := {HTTP_REQUEST_METHODS}.method_head + elseif m.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_trace) then + Result := {HTTP_REQUEST_METHODS}.method_trace + elseif m.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_options) then + Result := {HTTP_REQUEST_METHODS}.method_options + elseif m.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_put) then + Result := {HTTP_REQUEST_METHODS}.method_put + elseif m.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_delete) then + Result := {HTTP_REQUEST_METHODS}.method_delete + elseif m.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_connect) then + Result := {HTTP_REQUEST_METHODS}.method_connect + elseif m.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_patch) then + Result := {HTTP_REQUEST_METHODS}.method_patch + else + Result := m.as_upper + end + end + + is_matching_request_methods (a_request_method: READABLE_STRING_8; a_rqst_methods: detachable WSF_ROUTER_METHODS): BOOLEAN + -- `a_request_method' is matching `a_rqst_methods' contents do if a_rqst_methods /= Void and then not a_rqst_methods.is_empty then - m := a_request_method - from - i := a_rqst_methods.lower - n := a_rqst_methods.upper - until - i > n or Result - loop - Result := m.same_string (a_rqst_methods [i]) - i := i + 1 - end + Result := a_rqst_methods.has (a_request_method) else Result := True end end - formatted_request_methods (rqst_methods: like formatted_request_methods): detachable ARRAY [READABLE_STRING_8] - -- Formatted request methods values - local - i,l,u: INTEGER - do - if rqst_methods /= Void and then not rqst_methods.is_empty then - l := rqst_methods.lower - u := rqst_methods.upper - create Result.make_filled (rqst_methods[l], l, u) - from - i := l + 1 - until - i > u - loop - Result[i] := rqst_methods[i].as_string_8.as_upper - i := i + 1 - end - end - end - -;note +note copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ diff --git a/library/server/wsf/router/wsf_router_methods.e b/library/server/wsf/router/wsf_router_methods.e new file mode 100644 index 00000000..96f7917e --- /dev/null +++ b/library/server/wsf/router/wsf_router_methods.e @@ -0,0 +1,402 @@ +note + description: "[ + Object that is use in relation with WSF_ROUTER, to precise which request methods is accepted. + For convenience, `make_from_iterable' is available, so that you can use <<"GET", "POST">> for instance + but remember manifest string are evil ... + Since in HTTP you can use your own custom request method, this is not possible to catch any typo + ( for instance if you write "POST" instead of "P0ST" this is hard to find the error, + but in one case it uses upper "o" and in the other case this is zero "0" + ) + + The recommanded way to use is for instance + create {WSF_ROUTER_METHODS}.make_get_post + create methods; methods.enable_get; methods.enable_post + + This sounds heavy, but this is much safer. + + ( note: in addition internally this first checks using reference comparison + and then compare string value, so it brings optimization for accepted request methods. + ) + ]" + date: "$Date$" + revision: "$Revision$" + +class + WSF_ROUTER_METHODS + +inherit + ITERABLE [READABLE_STRING_8] + redefine + default_create + end + + HTTP_REQUEST_METHODS + redefine + default_create + end + +create + default_create, + make, + make_from_iterable, + make_from_string + +convert + to_array: {ARRAY [READABLE_STRING_8]}, + make_from_iterable ({ITERABLE [READABLE_STRING_8], ITERABLE [STRING_8], ARRAY [READABLE_STRING_8], ARRAY [STRING_8]}), + make_from_string ({READABLE_STRING_8, STRING_8}) + +feature {NONE} -- Initialization + + default_create + do + Precursor + make (1) + end + + make (n: INTEGER) + do + create methods.make (n) + end + + make_from_iterable (v: ITERABLE [READABLE_STRING_8]) + do + make (1) + across + v as vc + loop + smart_add_method (vc.item) + end + end + + make_from_string (v: READABLE_STRING_8) + do + make_from_iterable (v.split (',')) + end + +feature -- Status report + + is_locked: BOOLEAN + -- Is Current locked? And then can not be modified? + + is_empty: BOOLEAN + do + Result := methods.count = 0 + end + + has (a_method: READABLE_STRING_8): BOOLEAN + -- Has `a_method' enabled? + require + a_method_is_uppercase: a_method.same_string (a_method.as_upper) + do + -- First look for string object itself, + -- in case `a_method' comes from one of the HTTP_REQUEST_METHODS constants + Result := across methods as c some c.item = a_method end + if not Result then + -- If not found, look for the same string value + Result := across methods as c some c.item.same_string_general (a_method) end + end + end + +feature -- Access + + new_cursor: ITERATION_CURSOR [READABLE_STRING_8] + -- Fresh cursor associated with current structure + do + Result := methods.new_cursor + end + +feature -- Status change + + lock + -- Lock current and prevent any change in methods + -- Once it is locked, it is impossible to unlock. + do + is_locked := True + end + +feature -- Element change + + enable_get + require + is_not_locked: not is_locked + get_disabled: not has (method_get) + do + methods.extend (method_get) + ensure + get_enabled: has (method_get) + end + + disable_get + require + is_not_locked: not is_locked + get_enabled: has (method_get) + do + prune_method (method_get) + ensure + get_disabled: not has (method_get) + end + + enable_post + require + is_not_locked: not is_locked + post_disabled: not has (method_post) + do + methods.extend (method_post) + ensure + post_enabled: has (method_post) + end + + disable_post + require + is_not_locked: not is_locked + post_enabled: has (method_post) + do + prune_method (method_post) + ensure + post_disabled: not has (method_post) + end + + enable_put + require + is_not_locked: not is_locked + put_disabled: not has (method_put) + do + methods.extend (method_put) + ensure + put_enabled: has (method_put) + end + + disable_put + require + is_not_locked: not is_locked + put_enabled: has (method_put) + do + prune_method (method_put) + ensure + put_disabled: not has (method_put) + end + + enable_delete + require + is_not_locked: not is_locked + delete_disabled: not has (method_delete) + do + methods.extend (method_delete) + ensure + delete_enabled: has (method_delete) + end + + disable_delete + require + is_not_locked: not is_locked + delete_enabled: has (method_delete) + do + prune_method (method_delete) + ensure + delete_disabled: not has (method_delete) + end + + enable_options + require + is_not_locked: not is_locked + options_disabled: not has (method_options) + do + methods.extend (method_options) + ensure + options_enabled: has (method_options) + end + + disable_options + require + is_not_locked: not is_locked + options_enabled: has (method_options) + do + prune_method (method_options) + ensure + options_disabled: not has (method_options) + end + + enable_head + require + is_not_locked: not is_locked + head_disabled: not has (method_head) + do + methods.extend (method_head) + ensure + head_enabled: has (method_head) + end + + disable_head + require + is_not_locked: not is_locked + head_enabled: has (method_head) + do + prune_method (method_head) + ensure + head_disabled: not has (method_head) + end + + enable_trace + require + is_not_locked: not is_locked + trace_disabled: not has (method_trace) + do + methods.extend (method_trace) + ensure + trace_enabled: has (method_trace) + end + + disable_trace + require + is_not_locked: not is_locked + trace_enabled: has (method_trace) + do + prune_method (method_trace) + ensure + trace_disabled: not has (method_trace) + end + + enable_connect + require + is_not_locked: not is_locked + connect_disabled: not has (method_connect) + do + methods.extend (method_connect) + ensure + connect_enabled: has (method_connect) + end + + disable_connect + require + is_not_locked: not is_locked + connect_enabled: has (method_connect) + do + prune_method (method_connect) + ensure + connect_disabled: not has (method_connect) + end + + enable_patch + require + is_not_locked: not is_locked + patch_disabled: not has (method_patch) + do + methods.extend (method_patch) + ensure + patch_enabled: has (method_patch) + end + + disable_patch + require + is_not_locked: not is_locked + patch_enabled: has (method_patch) + do + prune_method (method_patch) + ensure + patch_disabled: not has (method_patch) + end + + enable_custom (m: READABLE_STRING_8) + require + is_not_locked: not is_locked + not_blank: not across m as mc some mc.item.is_space end + custom_disabled: not has (m.as_upper) + do + methods.extend (m.as_upper) + ensure + custom_enabled: has (m.as_upper) + end + + disable_custom (m: READABLE_STRING_8) + require + is_not_locked: not is_locked + not_blank: not across m as mc some mc.item.is_space end + custom_enabled: has (m.as_upper) + do + prune_method (m.as_upper) + ensure + custom_disabled: not has (m.as_upper) + end + +feature -- Access + + methods: ARRAYED_LIST [READABLE_STRING_8] + + to_array: ARRAY [READABLE_STRING_8] + do + Result := methods.to_array + end + +feature {NONE} -- Implementation + + smart_add_method (v: READABLE_STRING_8) + do + if v.is_case_insensitive_equal (method_get) then + enable_get + elseif v.is_case_insensitive_equal (method_post) then + enable_post + elseif v.is_case_insensitive_equal (method_put) then + enable_put + elseif v.is_case_insensitive_equal (method_delete) then + enable_delete + elseif v.is_case_insensitive_equal (method_head) then + enable_delete + elseif v.is_case_insensitive_equal (method_options) then + enable_options + elseif v.is_case_insensitive_equal (method_trace) then + enable_trace + elseif v.is_case_insensitive_equal (method_connect) then + enable_connect + elseif v.is_case_insensitive_equal (method_patch) then + enable_patch + else + enable_custom (v) + end + end + + add_method (v: READABLE_STRING_8) + require + is_upper_case: v.same_string (v.as_upper) + do + if not is_locked then + methods.extend (v) + end + end + + prune_method (v: READABLE_STRING_8) + require + is_upper_case: v.same_string (v.as_upper) + local + m: READABLE_STRING_8 + do + if not is_locked then + from + methods.start + until + methods.after + loop + m := methods.item + if m = v or else m.same_string (v) then + methods.remove + else + methods.forth + end + end + end + end + +invariant + methods_are_uppercase: across methods as c all c.item.same_string (c.item.as_upper) end + +;note + copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, 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/wsf_routing_handler.e b/library/server/wsf/router/wsf_routing_handler.e index 3d37fa20..384dfa4f 100644 --- a/library/server/wsf/router/wsf_routing_handler.e +++ b/library/server/wsf/router/wsf_routing_handler.e @@ -82,7 +82,8 @@ feature -- Mapping router.map (a_resource, h) end - map_with_request_methods (a_resource: READABLE_STRING_8; h: H; rqst_methods: detachable ARRAY [READABLE_STRING_8]) + map_with_request_methods (a_resource: READABLE_STRING_8; h: H; + rqst_methods: detachable WSF_ROUTER_METHODS) -- Map handler `h' with `a_resource' and `rqst_methods' do router.map_with_request_methods (a_resource, h, rqst_methods) @@ -94,7 +95,7 @@ feature -- Mapping end map_agent_with_request_methods (a_resource: READABLE_STRING_8; a_action: PROCEDURE [ANY, TUPLE [ctx: C; req: WSF_REQUEST; res: WSF_RESPONSE]]; - rqst_methods: detachable ARRAY [READABLE_STRING_8]) + rqst_methods: detachable WSF_ROUTER_METHODS) do router.map_agent_with_request_methods (a_resource, a_action, rqst_methods) end diff --git a/library/server/wsf/src/request/value/wsf_any.e b/library/server/wsf/src/request/value/wsf_any.e index a5f22d8c..0d671051 100644 --- a/library/server/wsf/src/request/value/wsf_any.e +++ b/library/server/wsf/src/request/value/wsf_any.e @@ -1,6 +1,7 @@ note - description: "Summary description for {WSF_ANY}." - author: "" + description: "[ + {WSF_ANY} represents a parameter holding any object. + ]" date: "$Date$" revision: "$Revision$" @@ -15,11 +16,11 @@ create feature {NONE} -- Initialization - make (a_name: READABLE_STRING_8; a_value: like item) + make (a_name: READABLE_STRING_8; a_value: like value) do name := url_decoded_string (a_name) url_encoded_name := a_name - item := a_value + value := a_value end feature -- Access @@ -28,7 +29,7 @@ feature -- Access url_encoded_name: READABLE_STRING_8 - item: detachable ANY + value: detachable ANY feature -- Element change @@ -50,8 +51,8 @@ feature -- Query -- String representation of Current -- if possible do - if attached item as i then - Result := i.generating_type + if attached value as v then + Result := v.generating_type else Result := "Void" end diff --git a/library/server/wsf/src/request/value/wsf_multiple_string.e b/library/server/wsf/src/request/value/wsf_multiple_string.e index 8f46af03..6636c649 100644 --- a/library/server/wsf/src/request/value/wsf_multiple_string.e +++ b/library/server/wsf/src/request/value/wsf_multiple_string.e @@ -1,6 +1,7 @@ note - description: "Summary description for {WSF_MULTIPLE_STRING}." - author: "" + description: "[ + {WSF_MULTIPLE_STRING} represents a sequence of name=value parameters + ]" date: "$Date$" revision: "$Revision$" @@ -25,7 +26,7 @@ feature {NONE} -- Initialization make_with_value (a_value: WSF_VALUE) do name := a_value.name - create {LINKED_LIST [WSF_STRING]} string_values.make + create {LINKED_LIST [WSF_STRING]} values.make add_value (a_value) end @@ -58,11 +59,25 @@ feature -- Access name: READABLE_STRING_32 - string_values: LIST [WSF_STRING] + values: LIST [WSF_STRING] - first_string_value: WSF_STRING + frozen string_values: like values + obsolete + "Use `values' [2012-May-31]" do - Result := string_values.first + Result := values + end + + first_value: WSF_STRING + do + Result := values.first + end + + frozen first_string_value: WSF_STRING + obsolete + "Use `first_value' [2012-May-31]" + do + Result := first_value end feature -- Element change @@ -79,15 +94,15 @@ feature -- Status report is_string: BOOLEAN -- Is Current as a WSF_STRING representation? do - Result := string_values.count = 1 + Result := values.count = 1 end feature -- Conversion as_string: WSF_STRING do - if string_values.count = 1 then - Result := first_string_value + if values.count = 1 then + Result := first_value else Result := Precursor end @@ -97,19 +112,19 @@ feature -- Traversing new_cursor: ITERATION_CURSOR [WSF_STRING] do - Result := string_values.new_cursor + Result := values.new_cursor end feature -- Helper string_representation: STRING_32 do - if string_values.count = 1 then - create Result.make_from_string (first_string_value) + if values.count = 1 then + create Result.make_from_string (first_value) else create Result.make_from_string ("[") across - string_values as c + values as c loop if Result.count > 1 then Result.append_character (',') @@ -139,7 +154,7 @@ feature -- Element change add_string_value (s: WSF_STRING) do - string_values.extend (s) + values.extend (s) end feature -- Visitor @@ -150,7 +165,7 @@ feature -- Visitor end invariant - string_values_not_empty: string_values.count >= 1 + string_values_not_empty: values.count >= 1 note copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Eiffel Software and others" diff --git a/library/server/wsf/src/request/value/wsf_string.e b/library/server/wsf/src/request/value/wsf_string.e index 812e205a..a4be2f27 100644 --- a/library/server/wsf/src/request/value/wsf_string.e +++ b/library/server/wsf/src/request/value/wsf_string.e @@ -1,6 +1,7 @@ note - description: "Summary description for {WSF_STRING}." - author: "" + description: "[ + {WSF_STRING} represents a String parameter + ]" date: "$Date$" revision: "$Revision$" @@ -25,21 +26,70 @@ feature {NONE} -- Initialization make (a_name: READABLE_STRING_8; a_string: READABLE_STRING_8) do name := url_decoded_string (a_name) - string := url_decoded_string (a_string) + value := url_decoded_string (a_string) url_encoded_name := a_name - url_encoded_string := a_string + url_encoded_value := a_string end feature -- Access name: READABLE_STRING_32 + -- + --| Note that the value might be html encoded as well + --| this is the application responsibility to html decode it - string: READABLE_STRING_32 + value: READABLE_STRING_32 + -- + --| Note that the value might be html encoded as well + --| this is the application responsibility to html decode it url_encoded_name: READABLE_STRING_8 + -- URL encoded string of `name'. - url_encoded_string: READABLE_STRING_8 + url_encoded_value: READABLE_STRING_8 + -- URL encoded string of `value'. + + frozen string: like value + obsolete + "Use value [2012-May-31]" + do + Result := value + end + + frozen url_encoded_string: like url_encoded_value + obsolete + "Use url_encoded_value [2012-May-31]" + do + Result := url_encoded_value + end + +feature -- Conversion + + integer_value: INTEGER + -- Integer value of `value'. + require + value_is_integer: is_integer + do + Result := value.to_integer + end + +feature -- Status report + + is_string: BOOLEAN = True + -- Is Current as a WSF_STRING representation? + + is_empty: BOOLEAN + -- Is empty? + do + Result := value.is_empty + end + + is_integer: BOOLEAN + -- Is `value' an integer? + do + Result := value.is_integer + end feature -- Element change @@ -51,31 +101,20 @@ feature -- Element change a_name.same_string (url_encoded_name) end -feature -- Status report - - is_string: BOOLEAN = True - -- Is Current as a WSF_STRING representation? - - is_empty: BOOLEAN - -- Is empty? - do - Result := string.is_empty - end - feature -- Helper same_string (a_other: READABLE_STRING_GENERAL): BOOLEAN -- Does `a_other' represent the same string as `Current'? do - Result := string.same_string_general (a_other) + Result := value.same_string_general (a_other) end is_case_insensitive_equal (a_other: READABLE_STRING_8): BOOLEAN -- Does `a_other' represent the same case insensitive string as `Current'? local - v: like string + v: like value do - v := string + v := value if v = a_other then Result := True elseif v.is_valid_as_string_8 then @@ -87,26 +126,7 @@ feature -- Conversion string_representation: STRING_32 do - create Result.make_from_string (string) - end - - html_encoded_name: READABLE_STRING_8 - -- HTML encoded string `name' - do - Result := (create {HTML_ENCODER}).encoded_string (name) - end - - html_encoded_string: READABLE_STRING_8 - -- HTML encoded string `string' - do - Result := (create {HTML_ENCODER}).encoded_string (string) - end - -feature {NONE} -- Conversion - - html_decoded_string (s: READABLE_STRING_GENERAL): READABLE_STRING_32 - do - Result := (create {HTML_ENCODER}).general_decoded_string (s) + create Result.make_from_string (value) end feature -- Visitor diff --git a/library/server/wsf/src/request/value/wsf_table.e b/library/server/wsf/src/request/value/wsf_table.e index ee531144..ecbbc052 100644 --- a/library/server/wsf/src/request/value/wsf_table.e +++ b/library/server/wsf/src/request/value/wsf_table.e @@ -47,7 +47,7 @@ feature -- Access end end - first_key: detachable READABLE_STRING_32 + first_name: detachable READABLE_STRING_32 do across values as c @@ -70,6 +70,13 @@ feature -- Access Result := values.count end + frozen first_key: like first_name + obsolete + "Use first_name [2012-May-31]" + do + Result := first_name + end + feature -- Element change change_name (a_name: like name) @@ -122,7 +129,7 @@ feature -- Helper string_representation: STRING_32 do create Result.make_from_string ("{") - if values.count = 1 and then attached first_key as fk then + if values.count = 1 and then attached first_name as fk then Result.append (fk + ": ") if attached value (fk) as fv then Result.append (fv.string_representation) diff --git a/library/server/wsf/src/request/value/wsf_uploaded_file.e b/library/server/wsf/src/request/value/wsf_uploaded_file.e index 84ac6fc6..11a3c1d3 100644 --- a/library/server/wsf/src/request/value/wsf_uploaded_file.e +++ b/library/server/wsf/src/request/value/wsf_uploaded_file.e @@ -1,6 +1,7 @@ note - description: "Summary description for {WSF_UPLOADED_FILE}." - author: "" + description: "[ + {WSF_UPLOADED_FILE} represents an uploaded file from form parameters. + ]" date: "$Date$" revision: "$Revision$" @@ -40,13 +41,11 @@ feature -- Element change a_name.same_string (url_encoded_name) end - feature -- Status report is_string: BOOLEAN = False -- Is Current as a WSF_STRING representation? - feature -- Conversion string_representation: STRING_32 diff --git a/library/server/wsf/src/request/wsf_value.e b/library/server/wsf/src/request/wsf_value.e index e25cf544..c9ec4b75 100644 --- a/library/server/wsf/src/request/wsf_value.e +++ b/library/server/wsf/src/request/wsf_value.e @@ -16,6 +16,11 @@ feature -- Access deferred end + frozen key: like name + do + Result := name + end + feature -- Element change change_name (a_name: like name) @@ -78,7 +83,7 @@ feature -- Status report debug_output: STRING -- String that should be displayed in debugger to represent `Current'. do - create Result.make_from_string (name.as_string_8 + "=" + string_representation.as_string_8) + create Result.make_from_string (url_encoder.encoded_string (name) + "=" + url_encoder.encoded_string (string_representation)) end feature {NONE} -- Implementation diff --git a/library/server/wsf/src/response/wsf_download_response.e b/library/server/wsf/src/response/wsf_download_response.e index 6e35e24d..c4f6808e 100644 --- a/library/server/wsf/src/response/wsf_download_response.e +++ b/library/server/wsf/src/response/wsf_download_response.e @@ -104,7 +104,7 @@ feature -- Element change answer_head_request_method := b end -feature {WSF_SERVICE, WSF_RESPONSE} -- Basic operations +feature {WSF_RESPONSE} -- Output send_to (res: WSF_RESPONSE) do diff --git a/library/server/wsf/src/response/wsf_file_response.e b/library/server/wsf/src/response/wsf_file_response.e index 7c3d87d1..2797b647 100644 --- a/library/server/wsf/src/response/wsf_file_response.e +++ b/library/server/wsf/src/response/wsf_file_response.e @@ -150,7 +150,7 @@ feature -- Element change update_content_length end -feature {WSF_SERVICE, WSF_RESPONSE} -- Output +feature {WSF_RESPONSE} -- Output send_to (res: WSF_RESPONSE) local diff --git a/library/server/wsf/src/response/wsf_html_page_response.e b/library/server/wsf/src/response/wsf_html_page_response.e index 41916a59..25377163 100644 --- a/library/server/wsf/src/response/wsf_html_page_response.e +++ b/library/server/wsf/src/response/wsf_html_page_response.e @@ -110,7 +110,7 @@ feature -- Element change body := b end -feature {WSF_SERVICE, WSF_RESPONSE} -- Output +feature {WSF_RESPONSE} -- Output send_to (res: WSF_RESPONSE) local @@ -147,8 +147,20 @@ feature {WSF_SERVICE, WSF_RESPONSE} -- Output res.put_string (s) end +feature -- HTML facilities + + html_encoded_string (s: READABLE_STRING_32): READABLE_STRING_8 + do + Result := html_encoder.encoded_string (s) + end + feature {NONE} -- HTML Generation + html_encoder: HTML_ENCODER + once ("thread") + create Result + end + append_html_head_code (s: STRING_8) local t: like title diff --git a/library/server/wsf/src/response/wsf_html_redirection_response.e b/library/server/wsf/src/response/wsf_html_redirection_response.e index 9fe8e0e3..eca25b6a 100644 --- a/library/server/wsf/src/response/wsf_html_redirection_response.e +++ b/library/server/wsf/src/response/wsf_html_redirection_response.e @@ -1,6 +1,6 @@ note description: "[ - Immediate redirection with HTML content + Immediate redirection with HTML content ]" date: "$Date$" revision: "$Revision$" @@ -41,7 +41,7 @@ feature -- Element change url_location := a_url_location end -feature {WSF_SERVICE, WSF_RESPONSE} -- Output +feature {WSF_RESPONSE} -- Output send_to (res: WSF_RESPONSE) local diff --git a/library/server/wsf/src/response/wsf_page_response.e b/library/server/wsf/src/response/wsf_page_response.e index 56f5999b..0d29d844 100644 --- a/library/server/wsf/src/response/wsf_page_response.e +++ b/library/server/wsf/src/response/wsf_page_response.e @@ -79,7 +79,7 @@ feature -- Element change l_body.append (a_string) end -feature {WSF_SERVICE, WSF_RESPONSE} -- Output +feature {WSF_RESPONSE} -- Output send_to (res: WSF_RESPONSE) local diff --git a/library/server/wsf/src/response/wsf_redirection_response.e b/library/server/wsf/src/response/wsf_redirection_response.e index 10ee5aee..ed76739c 100644 --- a/library/server/wsf/src/response/wsf_redirection_response.e +++ b/library/server/wsf/src/response/wsf_redirection_response.e @@ -70,7 +70,7 @@ feature -- Element change content_type := Void end -feature {WSF_SERVICE, WSF_RESPONSE} -- Output +feature {WSF_RESPONSE} -- Output send_to (res: WSF_RESPONSE) local diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index 632ea624..91990c2d 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -87,12 +87,12 @@ feature {NONE} -- Initialization end --| PATH_INFO - path_info := url_encoder.decoded_string (wgi_request.path_info) + path_info := raw_url_encoder.decoded_string (wgi_request.path_info) --| PATH_TRANSLATED s8 := wgi_request.path_translated if s8 /= Void then - path_translated := url_encoder.decoded_string (s8) + path_translated := raw_url_encoder.decoded_string (s8) end --| Here one can set its own environment entries if needed @@ -253,7 +253,7 @@ feature -- Access: global variable string_item (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 do if attached {WSF_STRING} item (a_name) as val then - Result := val.string + Result := val.value else check is_string_value: False end end @@ -303,7 +303,7 @@ feature -- Access: CGI Meta variables a_name_valid: a_name /= Void and then not a_name.is_empty do if attached meta_variable (a_name) as val then - Result := val.string + Result := val.value end end @@ -317,7 +317,7 @@ feature -- Access: CGI Meta variables a_name_not_empty: a_name /= Void and then not a_name.is_empty do if attached meta_variable (a_name) as val then - Result := val.string + Result := val.value if use_default_when_empty and then Result.is_empty then Result := a_default end @@ -332,7 +332,7 @@ feature -- Access: CGI Meta variables do meta_variables_table.force (new_string_value (a_name, a_value), a_name) ensure - param_set: attached {WSF_STRING} meta_variable (a_name) as val and then val.url_encoded_string.same_string (a_value) + param_set: attached {WSF_STRING} meta_variable (a_name) as val and then val.url_encoded_value.same_string (a_value) end unset_meta_variable (a_name: READABLE_STRING_GENERAL) @@ -825,9 +825,9 @@ feature -- Extra CGI environment variables do if attached {WSF_STRING} meta_variable ({WSF_META_NAMES}.request_time) as t and then - t.string.is_integer_64 + t.value.is_integer_64 then - Result := date_time_utilities.unix_time_stamp_to_date_time (t.string.to_integer_64) + Result := date_time_utilities.unix_time_stamp_to_date_time (t.value.to_integer_64) end end @@ -857,7 +857,7 @@ feature {NONE} -- Cookies l_cookies := internal_cookies_table if l_cookies = Void then if attached {WSF_STRING} meta_variable ({WSF_META_NAMES}.http_cookie) as val then - s := val.string + s := val.value create l_cookies.make_with_key_tester (5, string_equality_tester) l_cookies.compare_objects from @@ -1582,6 +1582,11 @@ feature {NONE} -- Implementation: utilities empty_string: READABLE_STRING_32 -- Reusable empty string + raw_url_encoder: URL_ENCODER + once + create {URL_ENCODER} Result + end + url_encoder: URL_ENCODER once create {UTF8_URL_ENCODER} Result diff --git a/library/server/wsf/src/wsf_response_message.e b/library/server/wsf/src/wsf_response_message.e index 3e5c0300..50f00f7a 100644 --- a/library/server/wsf/src/wsf_response_message.e +++ b/library/server/wsf/src/wsf_response_message.e @@ -1,17 +1,24 @@ note - description: "Summary description for {WSF_RESPONSE_MESSAGE}." - author: "" + description: "[ + Object to represent a full message to be send to the client + via {WSF_RESPONSE}.send (obj) + + The only requirement is to implement correctly the `send_to (WSF_RESPONSE)' + method. + ]" date: "$Date$" revision: "$Revision$" deferred class WSF_RESPONSE_MESSAGE -feature {WSF_SERVICE, WSF_RESPONSE} -- Output +feature {WSF_RESPONSE} -- Output send_to (res: WSF_RESPONSE) -- Send Current message to `res' - --| This should not be called by user's code directly + -- + -- This feature should be called via `{WSF_RESPONSE}.send (obj)' + -- where `obj' is the current object require header_not_committed: not res.header_committed status_not_committed: not res.status_committed diff --git a/library/text/encoder/src/html_encoder.e b/library/text/encoder/src/html_encoder.e index d92a856e..796fe95b 100644 --- a/library/text/encoder/src/html_encoder.e +++ b/library/text/encoder/src/html_encoder.e @@ -3,6 +3,7 @@ note Summary description for {HTML_ENCODER}. see: http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references + see: http://en.wikipedia.org/wiki/Character_encodings_in_HTML ]" legal: "See notice at end of class." status: "See notice at end of class." @@ -38,6 +39,7 @@ feature -- Encoder local i, n: INTEGER uc: CHARACTER_32 + l_code: INTEGER c: CHARACTER_8 do has_error := False @@ -47,14 +49,31 @@ feature -- Encoder uc := s.item (i) if uc.is_character_8 then c := uc.to_character_8 + inspect c + when '%T', '%N', '%R' then + Result.extend (c) when '%"' then Result.append_string (""") when '&' then Result.append_string ("&") when '%'' then Result.append_string ("'") when '<' then Result.append_string ("<") when '>' then Result.append_string (">") else - Result.extend (c) + l_code := c.code + if + l_code <= 31 or -- Hexa 1F + l_code = 127 -- Hexa 7F + then + -- Ignore (forbidden in HTML, even by reference + + elseif l_code >= 128 then + --| Tolerated + Result.append ("&#") + Result.append (l_code.out) + Result.extend (';') + else + Result.extend (c) + end end else Result.append ("&#") @@ -134,7 +153,7 @@ feature {NONE} -- Implementation: decoder do sharp_code := ('#').natural_32_code x_code := ('x').natural_32_code - x_code := (';').natural_32_code + semi_colon_code := (';').natural_32_code i := cl_i.item create s.make_empty diff --git a/library/text/encoder/src/utf8_url_encoder.e b/library/text/encoder/src/utf8_url_encoder.e index 8fca01d2..fd333d74 100644 --- a/library/text/encoder/src/utf8_url_encoder.e +++ b/library/text/encoder/src/utf8_url_encoder.e @@ -45,11 +45,8 @@ feature -- Encoder encoded_string (s: READABLE_STRING_32): STRING_8 -- URL-encoded value of `s'. do - Result := Precursor (s) - if not has_error then - Result := utf32_to_utf8 (Result) - has_error := not last_conversion_successful - end + Result := utf32_to_utf8 (s) + Result := Precursor (Result) end partial_encoded_string (s: READABLE_STRING_32; a_ignore: ARRAY [CHARACTER]): READABLE_STRING_8 @@ -58,7 +55,6 @@ feature -- Encoder Result := Precursor (s, a_ignore) if not has_error then Result := utf32_to_utf8 (Result) - has_error := not last_conversion_successful end end