From 9ec2baf7d39f6388df7f57796b364b8fd72acd1f Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 27 Feb 2014 06:39:28 -0800 Subject: [PATCH 01/41] used instead of form (jekill has trouble with it) --- Documentation.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation.md b/Documentation.md index ad1d2494..14145ffe 100644 --- a/Documentation.md +++ b/Documentation.md @@ -14,7 +14,7 @@ The framework also provides a router component to help dispatching the incoming A service can be a web api, a web interface, … what ever run on top of HTTP. - + # Service > see interface: **WSF_SERVICE** @@ -28,7 +28,7 @@ For convenience, the framework provides richer service interface that handles th > [Learn more about service](Documentation__Service) - + # Request and Response > see interface: **WSF_REQUEST** and **WSF_RESPONSE** @@ -50,7 +50,7 @@ The **WSF_RESPONSE** represents the communication toward the client, a service n > [Learn more about request](Documentation__Request) and [about response](Documentation__Response) - + # Connectors: > see **WGI_CONNECTOR** @@ -65,7 +65,7 @@ It is fairly easy to add new connector, it just has to follow the EWSGI interfac > [Learn more about connector](Documentation__Connector) - + # Router or Request Dispatcher: > Routes HTTP requests to the proper execution code @@ -165,10 +165,10 @@ examples ## EWF application generators - + # EWSGI Specification - + # Libraries External libraries are included, such as Cypress OAuth (Security), HTML parsing library, Template Engine Smarty. From fe913b0072ba6242c3e675f218f2bbc7c9fec504 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 27 Feb 2014 06:48:24 -0800 Subject: [PATCH 02/41] added anchor link for wiki and jekyl engine --- Documentation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation.md b/Documentation.md index 14145ffe..89c4264c 100644 --- a/Documentation.md +++ b/Documentation.md @@ -15,6 +15,7 @@ The framework also provides a router component to help dispatching the incoming A service can be a web api, a web interface, … what ever run on top of HTTP. + # Service > see interface: **WSF_SERVICE** @@ -29,6 +30,7 @@ For convenience, the framework provides richer service interface that handles th > [Learn more about service](Documentation__Service) + # Request and Response > see interface: **WSF_REQUEST** and **WSF_RESPONSE** @@ -51,6 +53,7 @@ The **WSF_RESPONSE** represents the communication toward the client, a service n > [Learn more about request](Documentation__Request) and [about response](Documentation__Response) + # Connectors: > see **WGI_CONNECTOR** @@ -66,6 +69,7 @@ It is fairly easy to add new connector, it just has to follow the EWSGI interfac > [Learn more about connector](Documentation__Connector) + # Router or Request Dispatcher: > Routes HTTP requests to the proper execution code @@ -166,9 +170,11 @@ examples + # EWSGI Specification + # Libraries External libraries are included, such as Cypress OAuth (Security), HTML parsing library, Template Engine Smarty. From a16a7358d1025110455772bc6bc19e7e81bda339 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 3 Mar 2014 17:59:26 +0100 Subject: [PATCH 03/41] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a1cdee27..029f4f1e 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ For more information please have a look at the related wiki: For download, check * https://github.com/EiffelWebFramework/EWF/downloads +Tasks and issues are managed with github issue system +* See https://github.com/EiffelWebFramework/EWF/issues +* And visual dashboard: https://waffle.io/eiffelwebframework/ewf + ## Requirements * Compiling from EiffelStudio 7.2 to 13.11 and more recent version of the compiler. * Developped using EiffelStudio 13.11 (on Windows, Linux) From a2cbcbbbc6aec01d3f638e5b9186eaaa47af6a2d Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 13 Mar 2014 13:02:04 +0100 Subject: [PATCH 04/41] Updated demo_basic example to be easier to read, and demonstrate various scenario. --- .../http_authorization/example/demo_basic.e | 154 ++++++++++++++---- 1 file changed, 121 insertions(+), 33 deletions(-) diff --git a/library/server/authentication/http_authorization/example/demo_basic.e b/library/server/authentication/http_authorization/example/demo_basic.e index 0883c8f6..fddbc3ee 100644 --- a/library/server/authentication/http_authorization/example/demo_basic.e +++ b/library/server/authentication/http_authorization/example/demo_basic.e @@ -73,75 +73,163 @@ feature -- Basic operations -- local auth: HTTP_AUTHORIZATION + l_authenticated_username: detachable READABLE_STRING_32 + l_invalid_credential: BOOLEAN do if attached req.http_authorization as l_http_auth then create auth.make (l_http_auth) if attached auth.login as l_login and then is_valid_credential (l_login, auth.password) then - handle_authorized (l_login, req, res) + l_authenticated_username := auth.login else - handle_unauthorized ("ERROR: Invalid credential", req, res) + l_invalid_credential := True end + end + if l_invalid_credential then + handle_unauthorized ("ERROR: Invalid credential", req, res) else - handle_unauthorized ("ERROR: Authentication information is missing ...", req, res) + if l_authenticated_username /= Void then + handle_authenticated (l_authenticated_username, req, res) + elseif req.path_info.same_string_general ("/login") then + handle_unauthorized ("Please provide credential ...", req, res) + elseif req.path_info.starts_with_general ("/protected/") then + -- any "/protected/*" url + handle_unauthorized ("Protected area, please sign in before", req, res) + else + handle_anonymous (req, res) + end end end - handle_authorized (a_username: READABLE_STRING_32; req: WSF_REQUEST; res: WSF_RESPONSE) + handle_authenticated (a_username: READABLE_STRING_32; req: WSF_REQUEST; res: WSF_RESPONSE) -- User `a_username' is authenticated, execute request `req' with response `res'. require valid_username: not a_username.is_empty known_username: is_known_login (a_username) local s: STRING - l_logout_url: STRING + page: WSF_HTML_PAGE_RESPONSE do create s.make_empty - s.append ("Welcome %"") + + append_html_header (req, s) + + s.append ("

The authenticated user is ") s.append (html_encoder.general_encoded_string (a_username)) - s.append ("%" ...
") + s.append ("
...

") - l_logout_url := req.absolute_script_url ("/") - l_logout_url.replace_substring_all ("://", "://_@") -- Hack to clear http authorization, i.e connect with bad username. - s.append ("logout") + append_html_menu (a_username, req, s) + append_html_logout (a_username, req, s) + append_html_footer (req, s) - -- Append the raw header data for information - if attached req.raw_header_data as l_header then - s.append ("
")
-				s.append (l_header)
-				s.append ("
") - end + create page.make + page.set_body (s) + res.send (page) + end - res.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", s.count.out]>>) - res.put_string (s) + handle_anonymous (req: WSF_REQUEST; res: WSF_RESPONSE) + -- No user is authenticated, execute request `req' with response `res'. + local + s: STRING + page: WSF_HTML_PAGE_RESPONSE + do + create s.make_empty + append_html_header (req, s) + + s.append ("Anonymous visitor ...
") + + append_html_login (req, s) + append_html_menu (Void, req, s) + append_html_footer (req, s) + + create page.make + page.set_body (s) + res.send (page) end handle_unauthorized (a_description: STRING; req: WSF_REQUEST; res: WSF_RESPONSE) - -- Handle forbidden. + -- Restricted page, authenticated user is required. + -- Send `a_description' as part of the response. local h: HTTP_HEADER s: STRING + page: WSF_HTML_PAGE_RESPONSE do create s.make_from_string (a_description) - -- Append the raw header data for information - if attached req.raw_header_data as l_header then - s.append ("
")
-				s.append (l_header)
-				s.append ("
") - end + append_html_login (req, s) + append_html_menu (Void, req, s) + append_html_footer (req, s) - create h.make - h.put_content_type_text_html - h.put_content_length (s.count) - h.put_current_date - h.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, + create page.make + page.set_status_code ({HTTP_STATUS_CODE}.unauthorized) + page.header.put_header_key_value ({HTTP_HEADER_NAMES}.header_www_authenticate, "Basic realm=%"Please enter a valid username and password (demo [" + html_encoder.encoded_string (demo_credential) + "])%"" --| warning: for this example: a valid credential is provided in the message, of course that for real application. ) - res.set_status_code ({HTTP_STATUS_CODE}.unauthorized) - res.put_header_text (h.string) - res.put_string (s) + page.set_body (s) + res.send (page) end +feature -- Helper + + append_html_header (req: WSF_REQUEST; s: STRING) + -- Append header paragraph to `s'. + do + s.append ("

The current page is " + html_encoder.encoded_string (req.path_info) + "

") + end + + append_html_menu (a_username: detachable READABLE_STRING_32; req: WSF_REQUEST; s: STRING) + -- Append menu to `s'. + -- when an user is authenticated, `a_username' is attached. + do + if a_username /= Void then + s.append ("
  • Your account (displayed only is user is authenticated!)
  • ") + end + s.append ("
  • home
  • ") + s.append ("
  • public area
  • ") + s.append ("
  • protected area
  • ") + end + + append_html_login (req: WSF_REQUEST; s: STRING) + -- Append login link to `s'. + do + s.append ("
  • sign in
  • ") + end + + append_html_logout (a_username: detachable READABLE_STRING_32; req: WSF_REQUEST; s: STRING) + -- Append logout link to `s'. + local + l_logout_url: STRING + do + l_logout_url := req.absolute_script_url ("/login") + l_logout_url.replace_substring_all ("://", "://_@") -- Hack to clear http authorization, i.e connect with bad username "_". + s.append ("
  • logout
  • ") + end + + append_html_footer (req: WSF_REQUEST; s: STRING) + -- Append html footer to `s'. + local + hauth: HTTP_AUTHORIZATION + do + s.append ("
    ") + if attached req.http_authorization as l_http_authorization then + s.append ("Has Authorization: header: ") + create hauth.make (req.http_authorization) + if attached hauth.login as l_login then + s.append (" login=" + html_encoder.encoded_string (l_login)+ "") + end + if attached hauth.password as l_password then + s.append (" password=" + html_encoder.encoded_string (l_password)+ "") + end + s.append ("
    ") + end + if attached req.raw_header_data as l_header then + -- Append the raw header data for information + s.append ("Raw header data:") + s.append ("
    ")
    +				s.append (l_header)
    +				s.append ("
    ") + end + end end From 718cebc7002f073c307195467ffb90b248155c66 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 17 Mar 2014 18:10:55 +0100 Subject: [PATCH 05/41] Extracting HTTP_HEADER_BUILDER from HTTP_HEADER to provide useful interface on WSF_RESPONSE, and make WSF_SESSION easier to use. --- .../network/protocol/http/src/http_header.e | 549 ++-------------- .../protocol/http/src/http_header_builder.e | 587 ++++++++++++++++++ .../server/wsf/session/wsf_cookie_session.e | 8 +- library/server/wsf/session/wsf_session.e | 13 +- library/server/wsf/src/wsf_response.e | 38 +- library/server/wsf/src/wsf_response_header.e | 63 ++ 6 files changed, 729 insertions(+), 529 deletions(-) create mode 100644 library/network/protocol/http/src/http_header_builder.e create mode 100644 library/server/wsf/src/wsf_response_header.e diff --git a/library/network/protocol/http/src/http_header.e b/library/network/protocol/http/src/http_header.e index 61f6a0e5..ab7f882e 100644 --- a/library/network/protocol/http/src/http_header.e +++ b/library/network/protocol/http/src/http_header.e @@ -1,8 +1,9 @@ note description: "[ - The class provides an easy way to build HTTP header. + The class represents a HTTP header, and it provides simple routine + to build it. - You will also find some helper feature to help coding most common usage + You will also find some helper features to help coding most common usages Please, have a look at constants classes such as HTTP_MIME_TYPES @@ -24,6 +25,13 @@ class inherit ITERABLE [READABLE_STRING_8] + HTTP_HEADER_BUILDER + redefine + add_header_key_value, + put_header_key_value, + put_header_key_values + end + create make, make_with_count, @@ -116,6 +124,8 @@ feature -- Access result_has_single_ending_cr_lf: Result.count >= 4 implies not Result.substring (Result.count - 3, Result.count).same_string ("%R%N%R%N") end +feature -- Conversion + to_name_value_iterable: ITERABLE [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]] -- Iterable representation of the header entries. local @@ -132,8 +142,6 @@ feature -- Access Result := res end -feature -- Conversion - append_string_to (a_result: STRING_8) -- Append current as string representation to `a_result' local @@ -250,60 +258,6 @@ feature -- Header: merging end end -feature -- Status report - - has, has_header_named (a_name: READABLE_STRING_8): BOOLEAN - -- Has header item for `n'? - do - Result := across headers as c some has_same_header_name (c.item, a_name) end - end - - has_content_length: BOOLEAN - -- Has header "Content-Length" - do - Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length) - end - - has_content_type: BOOLEAN - -- Has header "Content-Type" - do - Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_type) - end - - has_transfer_encoding_chunked: BOOLEAN - -- Has "Transfer-Encoding: chunked" header - do - if has_header_named ({HTTP_HEADER_NAMES}.header_transfer_encoding) then - Result := attached header_named_value ({HTTP_HEADER_NAMES}.header_transfer_encoding) as v and then v.same_string (str_chunked) - end - end - -feature -- Access - - header_named_value (a_name: READABLE_STRING_8): detachable STRING_8 - -- First header item found for `a_name' if any - require - has_header: has_header_named (a_name) - local - n: INTEGER - l_line: READABLE_STRING_8 - do - n := a_name.count - - across - headers as ic - until - Result /= Void - loop - l_line := ic.item - if has_same_header_name (l_line, a_name) then - Result := l_line.substring (n + 2, l_line.count) - Result.left_adjust - Result.right_adjust - end - end - end - feature -- Removal remove_header_named (a_name: READABLE_STRING_8) @@ -334,16 +288,12 @@ feature -- Header change: general -- Add header `h' -- if it already exists, there will be multiple header with same name -- which can also be valid - require - h_not_empty: not h.is_empty do headers.force (h) end put_header (h: READABLE_STRING_8) -- Add header `h' or replace existing header of same header name - require - h_not_empty: not h.is_empty do force_header_by_name (header_name_colon (h), h) end @@ -352,357 +302,29 @@ feature -- Header change: general -- Add header `k:v'. -- If it already exists, there will be multiple header with same name -- which can also be valid - local - s: STRING_8 do - create s.make (k.count + 2 + v.count) - s.append (k) - s.append (colon_space) - s.append (v) - add_header (s) - ensure + Precursor (k, v) + ensure then added: has_header_named (k) end put_header_key_value (k,v: READABLE_STRING_8) -- Add header `k:v', or replace existing header of same header name/key - local - s: STRING_8 do - create s.make (k.count + 2 + v.count) - s.append (k) - s.append (colon_space) - s.append (v) - put_header (s) - ensure + Precursor (k, v) + ensure then added: has_header_named (k) end put_header_key_values (k: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8) -- Add header `k: a_values', or replace existing header of same header values/key. -- Use `comma_space' as default separator if `a_separator' is Void or empty. - local - s: STRING_8 - l_separator: READABLE_STRING_8 do - if a_separator /= Void and then not a_separator.is_empty then - l_separator := a_separator - else - l_separator := comma_space - end - create s.make_empty - across - a_values as c - loop - if not s.is_empty then - s.append_string (l_separator) - end - s.append (c.item) - end - if not s.is_empty then - put_header_key_value (k, s) - end - ensure + Precursor (k, a_values, a_separator) + ensure then added: has_header_named (k) end -feature -- Content related header - - put_content_type (t: READABLE_STRING_8) - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) - end - - add_content_type (t: READABLE_STRING_8) - -- same as `put_content_type', but allow multiple definition of "Content-Type" - do - add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) - end - - put_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) - local - s: STRING_8 - do - if a_params /= Void and then not a_params.is_empty then - create s.make_from_string (t) - across - a_params as p - loop - if attached p.item as nv then - s.append_character (';') - s.append_character (' ') - s.append (nv.name) - s.append_character ('=') - s.append_character ('%"') - s.append (nv.value) - s.append_character ('%"') - end - end - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) - else - put_content_type (t) - end - end - - add_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) - local - s: STRING_8 - do - if a_params /= Void and then not a_params.is_empty then - create s.make_from_string (t) - across - a_params as p - loop - if attached p.item as nv then - s.append_character (';') - s.append_character (' ') - s.append (nv.name) - s.append_character ('=') - s.append_character ('%"') - s.append (nv.value) - s.append_character ('%"') - end - end - add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) - else - add_content_type (t) - end - end - - put_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8) - do - put_content_type_with_parameters (t, <<["charset", c]>>) - end - - add_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8) - -- same as `put_content_type_with_charset', but allow multiple definition of "Content-Type" - do - add_content_type_with_parameters (t, <<["charset", c]>>) - end - - put_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) - do - put_content_type_with_parameters (t, <<["name", n]>>) - end - - add_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) - -- same as `put_content_type_with_name', but allow multiple definition of "Content-Type" - do - add_content_type_with_parameters (t, <<["name", n]>>) - end - - put_content_length (n: INTEGER) - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, n.out) - end - - put_content_transfer_encoding (a_mechanism: READABLE_STRING_8) - -- Put "Content-Transfer-Encoding" header with for instance "binary" - --| encoding := "Content-Transfer-Encoding" ":" mechanism - --| - --| mechanism := "7bit" ; case-insensitive - --| / "quoted-printable" - --| / "base64" - --| / "8bit" - --| / "binary" - --| / x-token - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_transfer_encoding, a_mechanism) - end - - put_content_language (a_lang: READABLE_STRING_8) - -- Put "Content-Language" header of value `a_lang'. - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_language, a_lang) - end - - put_content_encoding (a_enc: READABLE_STRING_8) - -- Put "Content-Encoding" header of value `a_enc'. - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_encoding, a_enc) - end - - put_transfer_encoding (a_enc: READABLE_STRING_8) - -- Put "Transfer-Encoding" header with for instance "chunked" - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_enc) - end - - put_transfer_encoding_binary - -- Put "Transfer-Encoding: binary" header - do - put_transfer_encoding (str_binary) - end - - put_transfer_encoding_chunked - -- Put "Transfer-Encoding: chunked" header - do - put_transfer_encoding (str_chunked) - end - - put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8) - -- Put "Content-Disposition" header - --| See RFC2183 - --| disposition := "Content-Disposition" ":" - --| disposition-type - --| *(";" disposition-parm) - --| disposition-type := "inline" - --| / "attachment" - --| / extension-token - --| ; values are not case-sensitive - --| disposition-parm := filename-parm - --| / creation-date-parm - --| / modification-date-parm - --| / read-date-parm - --| / size-parm - --| / parameter - --| filename-parm := "filename" "=" value - --| creation-date-parm := "creation-date" "=" quoted-date-time - --| modification-date-parm := "modification-date" "=" quoted-date-time - --| read-date-parm := "read-date" "=" quoted-date-time - --| size-parm := "size" "=" 1*DIGIT - --| quoted-date-time := quoted-string - --| ; contents MUST be an RFC 822 `date-time' - --| ; numeric timezones (+HHMM or -HHMM) MUST be used - do - if a_params /= Void then - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type + semi_colon_space + a_params) - else - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type) - end - end - -feature -- Content-type helpers - - put_content_type_text_css do put_content_type ({HTTP_MIME_TYPES}.text_css) end - put_content_type_text_csv do put_content_type ({HTTP_MIME_TYPES}.text_csv) end - put_content_type_text_html do put_content_type ({HTTP_MIME_TYPES}.text_html) end - put_content_type_text_javascript do put_content_type ({HTTP_MIME_TYPES}.text_javascript) end - put_content_type_text_json do put_content_type ({HTTP_MIME_TYPES}.text_json) end - put_content_type_text_plain do put_content_type ({HTTP_MIME_TYPES}.text_plain) end - put_content_type_text_xml do put_content_type ({HTTP_MIME_TYPES}.text_xml) end - - put_content_type_application_json do put_content_type ({HTTP_MIME_TYPES}.application_json) end - put_content_type_application_javascript do put_content_type ({HTTP_MIME_TYPES}.application_javascript) end - put_content_type_application_zip do put_content_type ({HTTP_MIME_TYPES}.application_zip) end - put_content_type_application_pdf do put_content_type ({HTTP_MIME_TYPES}.application_pdf) end - - put_content_type_image_gif do put_content_type ({HTTP_MIME_TYPES}.image_gif) end - put_content_type_image_png do put_content_type ({HTTP_MIME_TYPES}.image_png) end - put_content_type_image_jpg do put_content_type ({HTTP_MIME_TYPES}.image_jpg) end - put_content_type_image_svg_xml do put_content_type ({HTTP_MIME_TYPES}.image_svg_xml) end - - put_content_type_message_http do put_content_type ({HTTP_MIME_TYPES}.message_http) end - - put_content_type_multipart_mixed do put_content_type ({HTTP_MIME_TYPES}.multipart_mixed) end - put_content_type_multipart_alternative do put_content_type ({HTTP_MIME_TYPES}.multipart_alternative) end - put_content_type_multipart_related do put_content_type ({HTTP_MIME_TYPES}.multipart_related) end - put_content_type_multipart_form_data do put_content_type ({HTTP_MIME_TYPES}.multipart_form_data) end - put_content_type_multipart_signed do put_content_type ({HTTP_MIME_TYPES}.multipart_signed) end - 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]) - -- If `a_methods' is not empty, put `Allow' header with list `a_methods' of methods - do - put_header_key_values ({HTTP_HEADER_NAMES}.header_allow, a_methods, Void) - end - -feature -- Date - - put_date (s: READABLE_STRING_8) - -- Put "Date: " header - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_date, s) - end - - put_current_date - -- Put current date time with "Date" header - do - put_utc_date (create {DATE_TIME}.make_now_utc) - end - - put_utc_date (a_utc_date: DATE_TIME) - -- Put UTC date time `dt' with "Date" header - do - put_date (date_to_rfc1123_http_date_format (a_utc_date)) - end - - put_last_modified (a_utc_date: DATE_TIME) - -- Put UTC date time `dt' with "Date" header - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (a_utc_date)) - end - -feature -- Authorization - - put_authorization (s: READABLE_STRING_8) - -- Put authorization `s' with "Authorization" header - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_authorization, s) - end - -feature -- Others - - put_expires (sec: INTEGER) - do - put_expires_string (sec.out) - end - - put_expires_string (s: STRING) - do - put_header_key_value ("Expires", s) - end - - put_expires_date (a_utc_date: DATE_TIME) - do - put_header_key_value ("Expires", date_to_rfc1123_http_date_format (a_utc_date)) - end - - put_cache_control (s: READABLE_STRING_8) - -- `s' could be for instance "no-cache, must-revalidate" - do - put_header_key_value ("Cache-Control", s) - end - - put_pragma (s: READABLE_STRING_8) - do - put_header_key_value ("Pragma", s) - end - - put_pragma_no_cache - do - put_pragma ("no-cache") - end - feature -- Redirection remove_location @@ -711,79 +333,7 @@ feature -- Redirection remove_header_named ({HTTP_HEADER_NAMES}.header_location) end - put_location (a_location: READABLE_STRING_8) - -- Tell the client the new location `a_location' - require - a_location_valid: not a_location.is_empty - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_location) - end - - put_refresh (a_location: READABLE_STRING_8; a_timeout_in_seconds: INTEGER) - -- Tell the client to refresh page with `a_location' after `a_timeout_in_seconds' in seconds - require - a_location_valid: not a_location.is_empty - do - put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_location) - end - -feature -- Cookie - - put_cookie (key, value: READABLE_STRING_8; expiration, path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN) - -- Set a cookie on the client's machine - -- with key 'key' and value 'value'. - -- Note: you should avoid using "localhost" as `domain' for local cookies - -- since they are not always handled by browser (for instance Chrome) - require - make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty) - domain_without_port_info: domain /= Void implies domain.index_of (':', 1) = 0 - local - s: STRING - do - s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value - if - domain /= Void and then not domain.same_string ("localhost") - then - s.append ("; Domain=") - s.append (domain) - end - if path /= Void then - s.append ("; Path=") - s.append (path) - end - if expiration /= Void then - s.append ("; Expires=") - s.append (expiration) - end - if secure then - s.append ("; Secure") - end - if http_only then - s.append ("; HttpOnly") - end - add_header (s) - end - - put_cookie_with_expiration_date (key, value: READABLE_STRING_8; expiration: DATE_TIME; path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN) - -- Set a cookie on the client's machine - -- with key 'key' and value 'value'. - require - make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty) - do - put_cookie (key, value, date_to_rfc1123_http_date_format (expiration), path, domain, secure, http_only) - end - -feature {NONE} -- Implementation: Header - - has_same_header_name (h: READABLE_STRING_8; a_name: READABLE_STRING_8): BOOLEAN - -- Header line `h' has same name as `a_name' ? - do - if h.starts_with (a_name) then - if h.valid_index (a_name.count + 1) then - Result := h[a_name.count + 1] = ':' - end - end - end +feature {NONE} -- Implementation: Header change force_header_by_name (n: detachable READABLE_STRING_8; h: READABLE_STRING_8) -- Add header `h' or replace existing header of same header name `n' @@ -811,6 +361,22 @@ feature {NONE} -- Implementation: Header end end +feature {NONE} -- Implementation: Header conversion + + append_line_to (s: READABLE_STRING_8; h: STRING_8) + do + h.append_string (s) + append_end_of_line_to (h) + end + + append_end_of_line_to (h: STRING_8) + do + h.append_character ('%R') + h.append_character ('%N') + end + +feature {NONE} -- Implementation: Header queries + header_name_colon (h: READABLE_STRING_8): detachable STRING_8 -- If any, header's name with colon --| ex: for "Foo-bar: something", this will return "Foo-bar:" @@ -870,51 +436,6 @@ feature {NONE} -- Implementation: Header end end -feature {NONE} -- Implementation - - append_line_to (s: READABLE_STRING_8; h: STRING_8) - do - h.append_string (s) - append_end_of_line_to (h) - end - - append_end_of_line_to (h: STRING_8) - do - h.append_character ('%R') - h.append_character ('%N') - end - -feature -- Access - - date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8 - -- String representation of `dt' using the RFC 1123 - local - d: HTTP_DATE - do - create d.make_from_date_time (dt) - Result := d.string - end - -feature {NONE} -- Constants - - str_binary: STRING = "binary" - str_chunked: STRING = "chunked" - - colon_space: IMMUTABLE_STRING_8 - once - create Result.make_from_string (": ") - end - - semi_colon_space: IMMUTABLE_STRING_8 - once - create Result.make_from_string ("; ") - end - - comma_space: IMMUTABLE_STRING_8 - once - create Result.make_from_string (", ") - end - note copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/library/network/protocol/http/src/http_header_builder.e b/library/network/protocol/http/src/http_header_builder.e new file mode 100644 index 00000000..eb25525c --- /dev/null +++ b/library/network/protocol/http/src/http_header_builder.e @@ -0,0 +1,587 @@ +note + description: "[ + The class provides an easy way to build HTTP header text + thanks to add_header (..) and put_header (..) + + You will also find some helper features to help coding most common usages + + Please, have a look at constants classes such as + HTTP_MIME_TYPES + HTTP_HEADER_NAMES + HTTP_STATUS_CODE + HTTP_REQUEST_METHODS + (or HTTP_CONSTANTS which groups them for convenience) + + Note the return status code is not part of the HTTP header + ]" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + HTTP_HEADER_BUILDER + +inherit + ITERABLE [READABLE_STRING_8] + +feature -- Access: deferred + + new_cursor: INDEXABLE_ITERATION_CURSOR [READABLE_STRING_8] + -- Fresh cursor associated with current structure. + deferred + end + +feature -- Header change: deferred + + add_header (h: READABLE_STRING_8) + -- Add header `h' + -- if it already exists, there will be multiple header with same name + -- which can also be valid + require + h_not_empty: not h.is_empty + deferred + end + + put_header (h: READABLE_STRING_8) + -- Add header `h' or replace existing header of same header name + require + h_not_empty: not h.is_empty + deferred + end + +feature -- Status report + + has, has_header_named (a_name: READABLE_STRING_8): BOOLEAN + -- Has header item for `n'? + local + ic: like new_cursor + do + from + ic := new_cursor + until + ic.after or Result + loop + Result := has_same_header_name (ic.item, a_name) + ic.forth + end + end + + has_content_length: BOOLEAN + -- Has header "Content-Length" + do + Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length) + end + + has_content_type: BOOLEAN + -- Has header "Content-Type" + do + Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_type) + end + + has_transfer_encoding_chunked: BOOLEAN + -- Has "Transfer-Encoding: chunked" header + do + if has_header_named ({HTTP_HEADER_NAMES}.header_transfer_encoding) then + Result := attached header_named_value ({HTTP_HEADER_NAMES}.header_transfer_encoding) as v and then v.same_string (str_chunked) + end + end + +feature -- Access + + header_named_value (a_name: READABLE_STRING_8): detachable STRING_8 + -- First header item found for `a_name' if any + local + n: INTEGER + l_line: READABLE_STRING_8 + ic: like new_cursor + do + n := a_name.count + + from + ic := new_cursor + until + ic.after or Result /= Void + loop + l_line := ic.item + if has_same_header_name (l_line, a_name) then + Result := l_line.substring (n + 2, l_line.count) + Result.left_adjust + Result.right_adjust + end + ic.forth + end + end + +feature -- Header change: general + + add_header_key_value (k,v: READABLE_STRING_8) + -- Add header `k:v'. + -- If it already exists, there will be multiple header with same name + -- which can also be valid + local + s: STRING_8 + do + create s.make (k.count + 2 + v.count) + s.append (k) + s.append (colon_space) + s.append (v) + add_header (s) + end + + put_header_key_value (k,v: READABLE_STRING_8) + -- Add header `k:v', or replace existing header of same header name/key + local + s: STRING_8 + do + create s.make (k.count + 2 + v.count) + s.append (k) + s.append (colon_space) + s.append (v) + put_header (s) + end + + put_header_key_values (k: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8) + -- Add header `k: a_values', or replace existing header of same header values/key. + -- Use `comma_space' as default separator if `a_separator' is Void or empty. + local + s: STRING_8 + l_separator: READABLE_STRING_8 + do + if a_separator /= Void and then not a_separator.is_empty then + l_separator := a_separator + else + l_separator := comma_space + end + create s.make_empty + across + a_values as c + loop + if not s.is_empty then + s.append_string (l_separator) + 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) + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) + end + + add_content_type (t: READABLE_STRING_8) + -- same as `put_content_type', but allow multiple definition of "Content-Type" + do + add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) + end + + put_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) + local + s: STRING_8 + do + if a_params /= Void and then not a_params.is_empty then + create s.make_from_string (t) + across + a_params as p + loop + if attached p.item as nv then + s.append_character (';') + s.append_character (' ') + s.append (nv.name) + s.append_character ('=') + s.append_character ('%"') + s.append (nv.value) + s.append_character ('%"') + end + end + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) + else + put_content_type (t) + end + end + + add_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) + local + s: STRING_8 + do + if a_params /= Void and then not a_params.is_empty then + create s.make_from_string (t) + across + a_params as p + loop + if attached p.item as nv then + s.append_character (';') + s.append_character (' ') + s.append (nv.name) + s.append_character ('=') + s.append_character ('%"') + s.append (nv.value) + s.append_character ('%"') + end + end + add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) + else + add_content_type (t) + end + end + + put_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8) + do + put_content_type_with_parameters (t, <<["charset", c]>>) + end + + add_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8) + -- same as `put_content_type_with_charset', but allow multiple definition of "Content-Type" + do + add_content_type_with_parameters (t, <<["charset", c]>>) + end + + put_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) + do + put_content_type_with_parameters (t, <<["name", n]>>) + end + + add_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) + -- same as `put_content_type_with_name', but allow multiple definition of "Content-Type" + do + add_content_type_with_parameters (t, <<["name", n]>>) + end + + put_content_length (n: INTEGER) + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, n.out) + end + + put_content_transfer_encoding (a_mechanism: READABLE_STRING_8) + -- Put "Content-Transfer-Encoding" header with for instance "binary" + --| encoding := "Content-Transfer-Encoding" ":" mechanism + --| + --| mechanism := "7bit" ; case-insensitive + --| / "quoted-printable" + --| / "base64" + --| / "8bit" + --| / "binary" + --| / x-token + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_transfer_encoding, a_mechanism) + end + + put_content_language (a_lang: READABLE_STRING_8) + -- Put "Content-Language" header of value `a_lang'. + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_language, a_lang) + end + + put_content_encoding (a_enc: READABLE_STRING_8) + -- Put "Content-Encoding" header of value `a_enc'. + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_encoding, a_enc) + end + + put_transfer_encoding (a_enc: READABLE_STRING_8) + -- Put "Transfer-Encoding" header with for instance "chunked" + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_enc) + end + + put_transfer_encoding_binary + -- Put "Transfer-Encoding: binary" header + do + put_transfer_encoding (str_binary) + end + + put_transfer_encoding_chunked + -- Put "Transfer-Encoding: chunked" header + do + put_transfer_encoding (str_chunked) + end + + put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8) + -- Put "Content-Disposition" header + --| See RFC2183 + --| disposition := "Content-Disposition" ":" + --| disposition-type + --| *(";" disposition-parm) + --| disposition-type := "inline" + --| / "attachment" + --| / extension-token + --| ; values are not case-sensitive + --| disposition-parm := filename-parm + --| / creation-date-parm + --| / modification-date-parm + --| / read-date-parm + --| / size-parm + --| / parameter + --| filename-parm := "filename" "=" value + --| creation-date-parm := "creation-date" "=" quoted-date-time + --| modification-date-parm := "modification-date" "=" quoted-date-time + --| read-date-parm := "read-date" "=" quoted-date-time + --| size-parm := "size" "=" 1*DIGIT + --| quoted-date-time := quoted-string + --| ; contents MUST be an RFC 822 `date-time' + --| ; numeric timezones (+HHMM or -HHMM) MUST be used + do + if a_params /= Void then + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type + semi_colon_space + a_params) + else + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type) + end + end + +feature -- Content-type helpers + + put_content_type_text_css do put_content_type ({HTTP_MIME_TYPES}.text_css) end + put_content_type_text_csv do put_content_type ({HTTP_MIME_TYPES}.text_csv) end + put_content_type_text_html do put_content_type ({HTTP_MIME_TYPES}.text_html) end + put_content_type_text_javascript do put_content_type ({HTTP_MIME_TYPES}.text_javascript) end + put_content_type_text_json do put_content_type ({HTTP_MIME_TYPES}.text_json) end + put_content_type_text_plain do put_content_type ({HTTP_MIME_TYPES}.text_plain) end + put_content_type_text_xml do put_content_type ({HTTP_MIME_TYPES}.text_xml) end + + put_content_type_application_json do put_content_type ({HTTP_MIME_TYPES}.application_json) end + put_content_type_application_javascript do put_content_type ({HTTP_MIME_TYPES}.application_javascript) end + put_content_type_application_zip do put_content_type ({HTTP_MIME_TYPES}.application_zip) end + put_content_type_application_pdf do put_content_type ({HTTP_MIME_TYPES}.application_pdf) end + + put_content_type_image_gif do put_content_type ({HTTP_MIME_TYPES}.image_gif) end + put_content_type_image_png do put_content_type ({HTTP_MIME_TYPES}.image_png) end + put_content_type_image_jpg do put_content_type ({HTTP_MIME_TYPES}.image_jpg) end + put_content_type_image_svg_xml do put_content_type ({HTTP_MIME_TYPES}.image_svg_xml) end + + put_content_type_message_http do put_content_type ({HTTP_MIME_TYPES}.message_http) end + + put_content_type_multipart_mixed do put_content_type ({HTTP_MIME_TYPES}.multipart_mixed) end + put_content_type_multipart_alternative do put_content_type ({HTTP_MIME_TYPES}.multipart_alternative) end + put_content_type_multipart_related do put_content_type ({HTTP_MIME_TYPES}.multipart_related) end + put_content_type_multipart_form_data do put_content_type ({HTTP_MIME_TYPES}.multipart_form_data) end + put_content_type_multipart_signed do put_content_type ({HTTP_MIME_TYPES}.multipart_signed) end + 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 + + put_content_type_utf_8_text_plain do put_content_type_with_charset ({HTTP_MIME_TYPES}.text_plain, "utf-8") 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]) + -- If `a_methods' is not empty, put `Allow' header with list `a_methods' of methods + do + put_header_key_values ({HTTP_HEADER_NAMES}.header_allow, a_methods, Void) + end + +feature -- Date + + put_date (s: READABLE_STRING_8) + -- Put "Date: " header + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_date, s) + end + + put_current_date + -- Put current date time with "Date" header + do + put_utc_date (create {DATE_TIME}.make_now_utc) + end + + put_utc_date (a_utc_date: DATE_TIME) + -- Put UTC date time `dt' with "Date" header + do + put_date (date_to_rfc1123_http_date_format (a_utc_date)) + end + + put_last_modified (a_utc_date: DATE_TIME) + -- Put UTC date time `dt' with "Date" header + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (a_utc_date)) + end + +feature -- Authorization + + put_authorization (s: READABLE_STRING_8) + -- Put authorization `s' with "Authorization" header + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_authorization, s) + end + +feature -- Others + + put_expires (sec: INTEGER) + do + put_expires_string (sec.out) + end + + put_expires_string (s: STRING) + do + put_header_key_value ("Expires", s) + end + + put_expires_date (a_utc_date: DATE_TIME) + do + put_header_key_value ("Expires", date_to_rfc1123_http_date_format (a_utc_date)) + end + + put_cache_control (s: READABLE_STRING_8) + -- `s' could be for instance "no-cache, must-revalidate" + do + put_header_key_value ("Cache-Control", s) + end + + put_pragma (s: READABLE_STRING_8) + do + put_header_key_value ("Pragma", s) + end + + put_pragma_no_cache + do + put_pragma ("no-cache") + end + +feature -- Redirection + + put_location (a_location: READABLE_STRING_8) + -- Tell the client the new location `a_location' + require + a_location_valid: not a_location.is_empty + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_location) + end + + put_refresh (a_location: READABLE_STRING_8; a_timeout_in_seconds: INTEGER) + -- Tell the client to refresh page with `a_location' after `a_timeout_in_seconds' in seconds + require + a_location_valid: not a_location.is_empty + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_location) + end + +feature -- Cookie + + put_cookie (key, value: READABLE_STRING_8; expiration, path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN) + -- Set a cookie on the client's machine + -- with key 'key' and value 'value'. + -- Note: you should avoid using "localhost" as `domain' for local cookies + -- since they are not always handled by browser (for instance Chrome) + require + make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty) + domain_without_port_info: domain /= Void implies domain.index_of (':', 1) = 0 + local + s: STRING + do + s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value + if + domain /= Void and then not domain.same_string ("localhost") + then + s.append ("; Domain=") + s.append (domain) + end + if path /= Void then + s.append ("; Path=") + s.append (path) + end + if expiration /= Void then + s.append ("; Expires=") + s.append (expiration) + end + if secure then + s.append ("; Secure") + end + if http_only then + s.append ("; HttpOnly") + end + add_header (s) + end + + put_cookie_with_expiration_date (key, value: READABLE_STRING_8; expiration: DATE_TIME; path, domain: detachable READABLE_STRING_8; secure, http_only: BOOLEAN) + -- Set a cookie on the client's machine + -- with key 'key' and value 'value'. + require + make_sense: (key /= Void and value /= Void) and then (not key.is_empty and not value.is_empty) + do + put_cookie (key, value, date_to_rfc1123_http_date_format (expiration), path, domain, secure, http_only) + end + + +feature -- Access + + date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8 + -- String representation of `dt' using the RFC 1123 + local + d: HTTP_DATE + do + create d.make_from_date_time (dt) + Result := d.string + end + +feature {NONE} -- Implementation + + has_same_header_name (h: READABLE_STRING_8; a_name: READABLE_STRING_8): BOOLEAN + -- Header line `h' has same name as `a_name' ? + do + if h.starts_with (a_name) then + if h.valid_index (a_name.count + 1) then + Result := h[a_name.count + 1] = ':' + end + end + end + +feature {NONE} -- Constants + + str_binary: STRING = "binary" + str_chunked: STRING = "chunked" + + colon_space: IMMUTABLE_STRING_8 + once + create Result.make_from_string (": ") + end + + semi_colon_space: IMMUTABLE_STRING_8 + once + create Result.make_from_string ("; ") + end + + comma_space: IMMUTABLE_STRING_8 + once + create Result.make_from_string (", ") + end + +note + copyright: "2011-2014, Jocelyn Fiat, 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/session/wsf_cookie_session.e b/library/server/wsf/session/wsf_cookie_session.e index 0e499f6d..4c9472f5 100644 --- a/library/server/wsf/session/wsf_cookie_session.e +++ b/library/server/wsf/session/wsf_cookie_session.e @@ -60,15 +60,15 @@ feature {NONE} -- Initialization feature -- Cookie - apply_to (h: HTTP_HEADER; a_request: WSF_REQUEST; a_path: detachable READABLE_STRING_8) + apply_to (h: HTTP_HEADER_BUILDER; a_request: WSF_REQUEST; a_path: detachable READABLE_STRING_8) local dt: detachable DATE_TIME l_domain: detachable READABLE_STRING_8 do l_domain := a_request.server_name if l_domain.same_string ("localhost") then - -- Due to limitation of specific handling of local cookies - -- it is recommended to use Void or IP instead of "localhost" + -- Due to limitation of specific handling of local cookies + -- it is recommended to use Void or IP instead of "localhost" l_domain := Void end if is_destroyed then @@ -177,7 +177,7 @@ feature {NONE} -- Implementation end note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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/session/wsf_session.e b/library/server/wsf/session/wsf_session.e index 52a7f7aa..b387ed68 100644 --- a/library/server/wsf/session/wsf_session.e +++ b/library/server/wsf/session/wsf_session.e @@ -78,12 +78,21 @@ feature -- Control deferred end - apply_to (h: HTTP_HEADER; req: WSF_REQUEST; a_path: detachable READABLE_STRING_8) + apply_to (h: HTTP_HEADER_BUILDER; req: WSF_REQUEST; a_path: detachable READABLE_STRING_8) + -- Apply current session to header `h' for request `req' and optional path `a_path'. + -- note: either use `apply_to' or `apply', not both. deferred end + apply (req: WSF_REQUEST; res: WSF_RESPONSE; a_path: detachable READABLE_STRING_8) + -- Apply current session to response `res' for request `req' and optional path `a_path'. + -- note: either use `apply' or `apply_to', not both. + do + apply_to (res.header, req, a_path) + end + note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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/src/wsf_response.e b/library/server/wsf/src/wsf_response.e index dbb36855..2a81f7e3 100644 --- a/library/server/wsf/src/wsf_response.e +++ b/library/server/wsf/src/wsf_response.e @@ -35,7 +35,7 @@ feature {NONE} -- Initialization wres: detachable WSF_WGI_DELAYED_HEADER_RESPONSE do transfered_content_length := 0 - create header.make + create internal_header.make wgi_response := r if attached {WSF_WGI_DELAYED_HEADER_RESPONSE} r as r_delayed then r_delayed.update_wsf_response (Current) @@ -53,7 +53,7 @@ feature {NONE} -- Initialization do transfered_content_length := 0 wgi_response := res.wgi_response - header := res.header + internal_header := res.internal_header set_status_code ({HTTP_STATUS_CODE}.ok) -- Default value end @@ -62,7 +62,7 @@ feature {WSF_RESPONSE, WSF_RESPONSE_EXPORTER} -- Properties wgi_response: WGI_RESPONSE -- Associated WGI_RESPONSE. - header: WSF_HEADER + internal_header: WSF_HEADER -- Associated response header. feature {WSF_RESPONSE_EXPORTER} -- Change @@ -158,7 +158,7 @@ feature {WSF_RESPONSE_EXPORTER} -- Header output operation -- commit status code and reason phrase wgi_response.set_status_code (status_code, status_reason_phrase) -- commit header text - wgi_response.put_header_text (header.string) + wgi_response.put_header_text (internal_header.string) end ensure status_committed: status_committed @@ -170,6 +170,26 @@ feature {WSF_RESPONSE_EXPORTER} -- Header output operation put_error ("Content already sent, new header text ignored!") end +feature -- Header access + + header: HTTP_HEADER_BUILDER + -- Associated header builder interface. + local + res: like internal_response_header + do + res := internal_response_header + if res = Void then + create {WSF_RESPONSE_HEADER} res.make_with_response (Current) + internal_response_header := res + end + Result := res + end + +feature {NONE} -- Header access + + internal_response_header: detachable like header + -- Cached version of `header'. + feature -- Header output operation put_header_line (h: READABLE_STRING_8) @@ -181,7 +201,7 @@ feature -- Header output operation if header_committed then report_content_already_sent_and_header_ignored else - header.put_header (h) + internal_header.put_header (h) end end @@ -194,7 +214,7 @@ feature -- Header output operation if header_committed then report_content_already_sent_and_header_ignored else - header.add_header (h) + internal_header.add_header (h) end end @@ -209,7 +229,7 @@ feature -- Header output operation if header_committed then report_content_already_sent_and_header_ignored else - header.put_raw_header_data (a_text) + internal_header.put_raw_header_data (a_text) end ensure message_writable: message_writable @@ -227,7 +247,7 @@ feature -- Header output operation if header_committed then report_content_already_sent_and_header_ignored else - header.append_raw_header_data (a_text) + internal_header.append_raw_header_data (a_text) end ensure status_set: status_is_set @@ -496,7 +516,7 @@ feature -- Error reporting end note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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/src/wsf_response_header.e b/library/server/wsf/src/wsf_response_header.e new file mode 100644 index 00000000..e2bf5480 --- /dev/null +++ b/library/server/wsf/src/wsf_response_header.e @@ -0,0 +1,63 @@ +note + description: "Summary description for {WSF_RESPONSE_HEADER}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + WSF_RESPONSE_HEADER + +inherit + HTTP_HEADER_BUILDER + + WSF_RESPONSE_EXPORTER -- to access WSF_RESPONSE.internal_header + +create + make_with_response + +feature {NONE} -- Initialization + + make_with_response (res: WSF_RESPONSE) + do + response := res + end + +feature -- Access + + response: WSF_RESPONSE + +feature -- Access + + new_cursor: INDEXABLE_ITERATION_CURSOR [READABLE_STRING_8] + -- Fresh cursor associated with current structure. + do + Result := response.internal_header.new_cursor + end + +feature -- Header change: core + + add_header (h: READABLE_STRING_8) + -- Add header `h' + -- if it already exists, there will be multiple header with same name + -- which can also be valid + do + response.add_header_line (h) + end + + put_header (h: READABLE_STRING_8) + -- Add header `h' or replace existing header of same header name + do + response.put_header_line (h) + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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 From ae0ba6641644707f2522a03d4b14b3c9c4f58e35 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Mon, 17 Mar 2014 21:30:34 +0100 Subject: [PATCH 06/41] Added comments, used better parameter names. --- .../network/protocol/http/src/http_header.e | 43 +---- .../protocol/http/src/http_header_builder.e | 179 ++++++++++++------ .../protocol/http/src/http_header_names.e | 17 +- 3 files changed, 141 insertions(+), 98 deletions(-) diff --git a/library/network/protocol/http/src/http_header.e b/library/network/protocol/http/src/http_header.e index ab7f882e..a162f9d4 100644 --- a/library/network/protocol/http/src/http_header.e +++ b/library/network/protocol/http/src/http_header.e @@ -26,11 +26,6 @@ inherit ITERABLE [READABLE_STRING_8] HTTP_HEADER_BUILDER - redefine - add_header_key_value, - put_header_key_value, - put_header_key_values - end create make, @@ -142,6 +137,8 @@ feature -- Conversion Result := res end +feature -- + append_string_to (a_result: STRING_8) -- Append current as string representation to `a_result' local @@ -298,33 +295,6 @@ feature -- Header change: general force_header_by_name (header_name_colon (h), h) end - add_header_key_value (k,v: READABLE_STRING_8) - -- Add header `k:v'. - -- If it already exists, there will be multiple header with same name - -- which can also be valid - do - Precursor (k, v) - ensure then - added: has_header_named (k) - end - - put_header_key_value (k,v: READABLE_STRING_8) - -- Add header `k:v', or replace existing header of same header name/key - do - Precursor (k, v) - ensure then - added: has_header_named (k) - end - - put_header_key_values (k: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8) - -- Add header `k: a_values', or replace existing header of same header values/key. - -- Use `comma_space' as default separator if `a_separator' is Void or empty. - do - Precursor (k, a_values, a_separator) - ensure then - added: has_header_named (k) - end - feature -- Redirection remove_location @@ -363,13 +333,18 @@ feature {NONE} -- Implementation: Header change feature {NONE} -- Implementation: Header conversion - append_line_to (s: READABLE_STRING_8; h: STRING_8) + append_line_to (a_line: READABLE_STRING_8; h: STRING_8) + -- Append header line `a_line' to string `h'. + --| this is used to build the header text + require + not_ending_with_new_line: not a_line.ends_with_general ("%N") do - h.append_string (s) + h.append_string (a_line) append_end_of_line_to (h) end append_end_of_line_to (h: STRING_8) + -- Append the CRLN end of header line to string `h'. do h.append_character ('%R') h.append_character ('%N') diff --git a/library/network/protocol/http/src/http_header_builder.e b/library/network/protocol/http/src/http_header_builder.e index eb25525c..12002f67 100644 --- a/library/network/protocol/http/src/http_header_builder.e +++ b/library/network/protocol/http/src/http_header_builder.e @@ -39,14 +39,14 @@ feature -- Header change: deferred -- if it already exists, there will be multiple header with same name -- which can also be valid require - h_not_empty: not h.is_empty + h_not_empty: h /= Void and then not h.is_empty deferred end put_header (h: READABLE_STRING_8) -- Add header `h' or replace existing header of same header name require - h_not_empty: not h.is_empty + h_not_empty: h /= Void and then not h.is_empty deferred end @@ -127,6 +127,8 @@ feature -- Header change: general s.append (colon_space) s.append (v) add_header (s) + ensure + added: has_header_named (k) end put_header_key_value (k,v: READABLE_STRING_8) @@ -139,6 +141,8 @@ feature -- Header change: general s.append (colon_space) s.append (v) put_header (s) + ensure + added: has_header_named (k) end put_header_key_values (k: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8) @@ -165,27 +169,32 @@ feature -- Header change: general if not s.is_empty then put_header_key_value (k, s) end + ensure + added: has_header_named (k) end feature -- Content related header - put_content_type (t: READABLE_STRING_8) + put_content_type (a_content_type: READABLE_STRING_8) + -- Put header line "Content-Type:" + type `a_content_type' do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, a_content_type) end - add_content_type (t: READABLE_STRING_8) + add_content_type (a_content_type: READABLE_STRING_8) -- same as `put_content_type', but allow multiple definition of "Content-Type" do - add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) + add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, a_content_type) end - put_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) + put_content_type_with_parameters (a_content_type: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) + -- Put header line "Content-Type:" + type `a_content_type' and extra paramaters `a_params' + --| note: see `put_content_type_with_charset' for examples. local s: STRING_8 do if a_params /= Void and then not a_params.is_empty then - create s.make_from_string (t) + create s.make_from_string (a_content_type) across a_params as p loop @@ -201,16 +210,18 @@ feature -- Content related header end put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) else - put_content_type (t) + put_content_type (a_content_type) end end - add_content_type_with_parameters (t: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) + add_content_type_with_parameters (a_content_type: READABLE_STRING_8; a_params: detachable ARRAY [TUPLE [name: READABLE_STRING_8; value: READABLE_STRING_8]]) + -- Add header line "Content-Type:" + type `a_content_type' and extra paramaters `a_params'. + --| note: see `put_content_type_with_charset' for examples. local s: STRING_8 do if a_params /= Void and then not a_params.is_empty then - create s.make_from_string (t) + create s.make_from_string (a_content_type) across a_params as p loop @@ -226,39 +237,42 @@ feature -- Content related header end add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, s) else - add_content_type (t) + add_content_type (a_content_type) end end - put_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8) + put_content_type_with_charset (a_content_type: READABLE_STRING_8; a_charset: READABLE_STRING_8) + -- Put content type `a_content_type' with `a_charset' as "charset" parameter. do - put_content_type_with_parameters (t, <<["charset", c]>>) + put_content_type_with_parameters (a_content_type, <<["charset", a_charset]>>) end - add_content_type_with_charset (t: READABLE_STRING_8; c: READABLE_STRING_8) - -- same as `put_content_type_with_charset', but allow multiple definition of "Content-Type" + add_content_type_with_charset (a_content_type: READABLE_STRING_8; a_charset: READABLE_STRING_8) + -- Same as `put_content_type_with_charset', but allow multiple definition of "Content-Type". do - add_content_type_with_parameters (t, <<["charset", c]>>) + add_content_type_with_parameters (a_content_type, <<["charset", a_charset]>>) end - put_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) + put_content_type_with_name (a_content_type: READABLE_STRING_8; a_name: READABLE_STRING_8) + -- Put content type `a_content_type' with `a_name' as "name" parameter. do - put_content_type_with_parameters (t, <<["name", n]>>) + put_content_type_with_parameters (a_content_type, <<["name", a_name]>>) end - add_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) + add_content_type_with_name (a_content_type: READABLE_STRING_8; a_name: READABLE_STRING_8) -- same as `put_content_type_with_name', but allow multiple definition of "Content-Type" do - add_content_type_with_parameters (t, <<["name", n]>>) + add_content_type_with_parameters (a_content_type, <<["name", a_name]>>) end - put_content_length (n: INTEGER) + put_content_length (a_length: INTEGER) + -- Put "Content-Length:" + length `a_length'. do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, n.out) + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, a_length.out) end put_content_transfer_encoding (a_mechanism: READABLE_STRING_8) - -- Put "Content-Transfer-Encoding" header with for instance "binary" + -- Put "Content-Transfer-Encoding" header with `a_mechanism' --| encoding := "Content-Transfer-Encoding" ":" mechanism --| --| mechanism := "7bit" ; case-insensitive @@ -277,16 +291,17 @@ feature -- Content related header put_header_key_value ({HTTP_HEADER_NAMES}.header_content_language, a_lang) end - put_content_encoding (a_enc: READABLE_STRING_8) - -- Put "Content-Encoding" header of value `a_enc'. + put_content_encoding (a_encoding: READABLE_STRING_8) + -- Put "Content-Encoding" header of value `a_encoding'. do - put_header_key_value ({HTTP_HEADER_NAMES}.header_content_encoding, a_enc) + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_encoding, a_encoding) end - put_transfer_encoding (a_enc: READABLE_STRING_8) - -- Put "Transfer-Encoding" header with for instance "chunked" + put_transfer_encoding (a_encoding: READABLE_STRING_8) + -- Put "Transfer-Encoding" header with `a_encoding' value. + --| for instance "chunked" do - put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_enc) + put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_encoding) end put_transfer_encoding_binary @@ -367,10 +382,12 @@ feature -- Content-type helpers feature -- Cross-Origin Resource Sharing - put_access_control_allow_origin (s: READABLE_STRING_8) - -- Put "Access-Control-Allow-Origin" header. + put_access_control_allow_origin (a_origin: READABLE_STRING_8) + -- Put "Access-Control-Allow-Origin: " + `a_origin' header. + -- `a_origin' specifies a URI that may access the resource + --| for instance "http://example.com" do - put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_origin, s) + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_origin, a_origin) end put_access_control_allow_all_origin @@ -379,16 +396,43 @@ feature -- Cross-Origin Resource Sharing put_access_control_allow_origin ("*") end + put_access_control_allow_credentials (b: BOOLEAN) + -- Indicates whether or not the response to the request can be exposed when the credentials flag is true. + -- When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. + -- Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials, + -- if this header is not returned with the resource, the response is ignored by the browser and not returned to web content. + -- ex: Access-Control-Allow-Credentials: true | false + do + if b then + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_Credentials, "true") + else + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_Credentials, "false") + end + 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 + -- `a_methods' specifies the method or methods allowed when accessing the resource. + -- This is used in response to a preflight request. + -- ex: Access-Control-Allow-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. + put_access_control_allow_headers (a_headers: READABLE_STRING_8) + -- Put "Access-Control-Allow-Headers" header. with value `a_headers' + -- Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request. + -- ex: Access-Control-Allow-Headers: [, ]* do - put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, s) + put_header_key_value ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, a_headers) + end + + put_access_control_allow_iterable_headers (a_fields: ITERABLE [READABLE_STRING_8]) + -- Put "Access-Control-Allow-Headers" header. with value `a_headers' + -- Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request. + -- ex: Access-Control-Allow-Headers: [, ]* + do + put_header_key_values ({HTTP_HEADER_NAMES}.header_access_control_allow_headers, a_fields, Void) end feature -- Method related @@ -401,10 +445,10 @@ feature -- Method related feature -- Date - put_date (s: READABLE_STRING_8) + put_date (a_date: READABLE_STRING_8) -- Put "Date: " header do - put_header_key_value ({HTTP_HEADER_NAMES}.header_date, s) + put_header_key_value ({HTTP_HEADER_NAMES}.header_date, a_date) end put_current_date @@ -414,74 +458,89 @@ feature -- Date end put_utc_date (a_utc_date: DATE_TIME) - -- Put UTC date time `dt' with "Date" header + -- Put UTC date time `a_utc_date' with "Date" header + -- using RFC1123 date formating. do put_date (date_to_rfc1123_http_date_format (a_utc_date)) end put_last_modified (a_utc_date: DATE_TIME) - -- Put UTC date time `dt' with "Date" header + -- Put UTC date time `dt' with "Last-Modified" header do put_header_key_value ({HTTP_HEADER_NAMES}.header_last_modified, date_to_rfc1123_http_date_format (a_utc_date)) end feature -- Authorization - put_authorization (s: READABLE_STRING_8) - -- Put authorization `s' with "Authorization" header + put_authorization (a_authorization: READABLE_STRING_8) + -- Put `a_authorization' with "Authorization" header + -- The Authorization header is constructed as follows: + -- 1. Username and password are combined into a string "username:password". + -- 2. The resulting string literal is then encoded using Base64. + -- 3. The authorization method and a space, i.e. "Basic " is then put before the encoded string. + -- ex: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== do - put_header_key_value ({HTTP_HEADER_NAMES}.header_authorization, s) + put_header_key_value ({HTTP_HEADER_NAMES}.header_authorization, a_authorization) end feature -- Others - put_expires (sec: INTEGER) + put_expires (a_seconds: INTEGER) + -- Put "Expires" header to `a_seconds' seconds do - put_expires_string (sec.out) + put_expires_string (a_seconds.out) end - put_expires_string (s: STRING) + put_expires_string (a_expires: STRING) + -- Put "Expires" header with `a_expires' string value do - put_header_key_value ("Expires", s) + put_header_key_value ("Expires", a_expires) end put_expires_date (a_utc_date: DATE_TIME) + -- Put "Expires" header with UTC date time value + -- formatted following RFC1123 specification. do put_header_key_value ("Expires", date_to_rfc1123_http_date_format (a_utc_date)) end - put_cache_control (s: READABLE_STRING_8) - -- `s' could be for instance "no-cache, must-revalidate" + put_cache_control (a_cache_control: READABLE_STRING_8) + -- Put "Cache-Control" header with value `a_cache_control' + --| note: ex "Cache-Control: no-cache, must-revalidate" do - put_header_key_value ("Cache-Control", s) + put_header_key_value ("Cache-Control", a_cache_control) end - put_pragma (s: READABLE_STRING_8) + put_pragma (a_pragma: READABLE_STRING_8) + -- Put "Pragma" header with value `a_pragma' do - put_header_key_value ("Pragma", s) + put_header_key_value ("Pragma", a_pragma) end put_pragma_no_cache + -- Put "Pragma" header with "no-cache" a_pragma do put_pragma ("no-cache") end feature -- Redirection - put_location (a_location: READABLE_STRING_8) - -- Tell the client the new location `a_location' + put_location (a_uri: READABLE_STRING_8) + -- Tell the client the new location `a_uri' + -- using "Location" header. require - a_location_valid: not a_location.is_empty + a_uri_valid: not a_uri.is_empty do - put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_location) + put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_uri) end - put_refresh (a_location: READABLE_STRING_8; a_timeout_in_seconds: INTEGER) - -- Tell the client to refresh page with `a_location' after `a_timeout_in_seconds' in seconds + put_refresh (a_uri: READABLE_STRING_8; a_timeout_in_seconds: INTEGER) + -- Tell the client to refresh page with `a_uri' after `a_timeout_in_seconds' in seconds + -- using "Refresh" header. require - a_location_valid: not a_location.is_empty + a_uri_valid: not a_uri.is_empty do - put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_location) + put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_uri) end feature -- Cookie diff --git a/library/network/protocol/http/src/http_header_names.e b/library/network/protocol/http/src/http_header_names.e index 4bab76e8..f64bdc26 100644 --- a/library/network/protocol/http/src/http_header_names.e +++ b/library/network/protocol/http/src/http_header_names.e @@ -199,17 +199,26 @@ 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 + --| Example: Access-Control-Allow-Origin: http://example.org + + header_access_control_allow_credentials: STRING = "Access-Control-Allow-Credentials" + -- Indicates whether or not the response to the request can be exposed when the credentials flag is true. + -- When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. + -- Note that simple GET requests are not preflighted, and so if a request is made for a resource with credentials, + -- if this header is not returned with the resource, the response is ignored by the browser and not returned to web content. + --| Access-Control-Allow-Credentials: true | false 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 + --| Access-Control-Allow-Methods: [, ]* + --| 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 + --| Access-Control-Allow-Headers: [, ]* + --| Example: Access-Control-Allow-Headers: Authorization feature -- Request or Response header name @@ -265,7 +274,7 @@ feature -- MIME related header_content_transfer_encoding: STRING = "Content-Transfer-Encoding" note - copyright: "2011-2013, Jocelyn Fiat, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software From da92d2d365e865630b5b83eca564efbb9af3fe91 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 18 Mar 2014 14:13:16 +0100 Subject: [PATCH 07/41] Added alias "[]" to `item', to get header value for a header name. Added assigner for `item' to make it easier to add header item without confusing key and value. Better parameter names (more explicit) --- .../protocol/http/src/http_header_builder.e | 67 ++++++++++++------- library/server/wsf/src/wsf_response_header.e | 5 +- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/library/network/protocol/http/src/http_header_builder.e b/library/network/protocol/http/src/http_header_builder.e index 12002f67..68970220 100644 --- a/library/network/protocol/http/src/http_header_builder.e +++ b/library/network/protocol/http/src/http_header_builder.e @@ -83,20 +83,21 @@ feature -- Status report -- Has "Transfer-Encoding: chunked" header do if has_header_named ({HTTP_HEADER_NAMES}.header_transfer_encoding) then - Result := attached header_named_value ({HTTP_HEADER_NAMES}.header_transfer_encoding) as v and then v.same_string (str_chunked) + Result := attached item ({HTTP_HEADER_NAMES}.header_transfer_encoding) as v and then v.same_string (str_chunked) end end feature -- Access - header_named_value (a_name: READABLE_STRING_8): detachable STRING_8 + item alias "[]" (a_header_name: READABLE_STRING_8): detachable READABLE_STRING_8 assign force -- First header item found for `a_name' if any local + res: STRING_8 n: INTEGER l_line: READABLE_STRING_8 ic: like new_cursor do - n := a_name.count + n := a_header_name.count from ic := new_cursor @@ -104,49 +105,69 @@ feature -- Access ic.after or Result /= Void loop l_line := ic.item - if has_same_header_name (l_line, a_name) then - Result := l_line.substring (n + 2, l_line.count) - Result.left_adjust - Result.right_adjust + if has_same_header_name (l_line, a_header_name) then + res := l_line.substring (n + 2, l_line.count) + res.left_adjust + res.right_adjust + Result := res end ic.forth end end + header_named_value (a_name: READABLE_STRING_8): like item + -- First header item found for `a_name' if any + obsolete + "Use `item' [2014-03]" + do + Result := item (a_name) + end + feature -- Header change: general - add_header_key_value (k,v: READABLE_STRING_8) - -- Add header `k:v'. + force (a_value: detachable READABLE_STRING_8; a_header_name: READABLE_STRING_8) + -- Put header `a_header_name:a_value' or replace existing header of name `a_header_name'. + --| this is used as assigner for `item' + do + if a_value = Void then + put_header_key_value (a_header_name, "") + else + put_header_key_value (a_header_name, a_value) + end + end + + add_header_key_value (a_header_name, a_value: READABLE_STRING_8) + -- Add header `a_header_name:a_value'. -- If it already exists, there will be multiple header with same name -- which can also be valid local s: STRING_8 do - create s.make (k.count + 2 + v.count) - s.append (k) + create s.make (a_header_name.count + 2 + a_value.count) + s.append (a_header_name) s.append (colon_space) - s.append (v) + s.append (a_value) add_header (s) ensure - added: has_header_named (k) + added: has_header_named (a_header_name) end - put_header_key_value (k,v: READABLE_STRING_8) - -- Add header `k:v', or replace existing header of same header name/key + put_header_key_value (a_header_name, a_value: READABLE_STRING_8) + -- Add header `a_header_name:a_value', or replace existing header of same header name/key local s: STRING_8 do - create s.make (k.count + 2 + v.count) - s.append (k) + create s.make (a_header_name.count + 2 + a_value.count) + s.append (a_header_name) s.append (colon_space) - s.append (v) + s.append (a_value) put_header (s) ensure - added: has_header_named (k) + added: has_header_named (a_header_name) end - put_header_key_values (k: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8) - -- Add header `k: a_values', or replace existing header of same header values/key. + put_header_key_values (a_header_name: READABLE_STRING_8; a_values: ITERABLE [READABLE_STRING_8]; a_separator: detachable READABLE_STRING_8) + -- Add header `a_header_name: a_values', or replace existing header of same header values/key. -- Use `comma_space' as default separator if `a_separator' is Void or empty. local s: STRING_8 @@ -167,10 +188,10 @@ feature -- Header change: general s.append (c.item) end if not s.is_empty then - put_header_key_value (k, s) + put_header_key_value (a_header_name, s) end ensure - added: has_header_named (k) + added: has_header_named (a_header_name) end feature -- Content related header diff --git a/library/server/wsf/src/wsf_response_header.e b/library/server/wsf/src/wsf_response_header.e index e2bf5480..07e0a1f4 100644 --- a/library/server/wsf/src/wsf_response_header.e +++ b/library/server/wsf/src/wsf_response_header.e @@ -1,6 +1,7 @@ note - description: "Summary description for {WSF_RESPONSE_HEADER}." - author: "" + description: "[ + Interface to build the http header associated with WSF_RESPONSE. + ]" date: "$Date$" revision: "$Revision$" From 195f3b4f683952d8475d50df8abc210f89dadfd0 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 2 Apr 2014 01:51:04 -0700 Subject: [PATCH 08/41] Updated Documentation__Request (markdown) --- Documentation__Request.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation__Request.md b/Documentation__Request.md index 09b73ab0..e1286fed 100644 --- a/Documentation__Request.md +++ b/Documentation__Request.md @@ -1 +1,10 @@ -See WSF_REQUEST \ No newline at end of file +See WSF_REQUEST + +Note that by default there is a smart computation for the query/post/... parameters: +for instance +- `?q=a&q=b` : will create a WSF_MULTIPLE_STRING parameter with name **q** and value `[a,b]` +- `?tab[a]=ewf&tab[b]=demo` : will create a WSF_TABLE parameter with name **tab** and value `{ "a": "ewf", "b": "demo"}` +- `?tab[]=ewf&tab[]=demo` : will create a WSF_TABLE parameter with name **tab** and value `{ "1": "ewf", "2": "demo"}` +- `?tab[foo]=foo&tab[foo]=bar` : will create a WSF_TABLE parameter with name **tab** and value `{ "foo": "bar"}` **WARNING: only the last `tab[foo]` is kept**. + +Those rules are applied to query, post, path, .... parameters. \ No newline at end of file From e687affd17b53b5492875ce29d0a74c3a4de3e2a Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 2 Apr 2014 01:52:00 -0700 Subject: [PATCH 09/41] Updated Documentation__Request (markdown) --- Documentation__Request.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation__Request.md b/Documentation__Request.md index e1286fed..b7d4cdda 100644 --- a/Documentation__Request.md +++ b/Documentation__Request.md @@ -2,9 +2,9 @@ See WSF_REQUEST Note that by default there is a smart computation for the query/post/... parameters: for instance -- `?q=a&q=b` : will create a WSF_MULTIPLE_STRING parameter with name **q** and value `[a,b]` -- `?tab[a]=ewf&tab[b]=demo` : will create a WSF_TABLE parameter with name **tab** and value `{ "a": "ewf", "b": "demo"}` -- `?tab[]=ewf&tab[]=demo` : will create a WSF_TABLE parameter with name **tab** and value `{ "1": "ewf", "2": "demo"}` -- `?tab[foo]=foo&tab[foo]=bar` : will create a WSF_TABLE parameter with name **tab** and value `{ "foo": "bar"}` **WARNING: only the last `tab[foo]` is kept**. +- `?q=a&q=b` : will create a **WSF_MULTIPLE_STRING** parameter with name **q** and value `[a,b]` +- `?tab[a]=ewf&tab[b]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "a": "ewf", "b": "demo"}` +- `?tab[]=ewf&tab[]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "1": "ewf", "2": "demo"}` +- `?tab[foo]=foo&tab[foo]=bar` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "foo": "bar"}` **WARNING: only the last `tab[foo]` is kept**. Those rules are applied to query, post, path, .... parameters. \ No newline at end of file From 2100b856b0ab45db50c3f6476f20b59547445398 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 2 Apr 2014 01:52:31 -0700 Subject: [PATCH 10/41] Updated Documentation__Request (markdown) --- Documentation__Request.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation__Request.md b/Documentation__Request.md index b7d4cdda..65bf60b5 100644 --- a/Documentation__Request.md +++ b/Documentation__Request.md @@ -2,9 +2,9 @@ See WSF_REQUEST Note that by default there is a smart computation for the query/post/... parameters: for instance -- `?q=a&q=b` : will create a **WSF_MULTIPLE_STRING** parameter with name **q** and value `[a,b]` -- `?tab[a]=ewf&tab[b]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "a": "ewf", "b": "demo"}` -- `?tab[]=ewf&tab[]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "1": "ewf", "2": "demo"}` -- `?tab[foo]=foo&tab[foo]=bar` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "foo": "bar"}` **WARNING: only the last `tab[foo]` is kept**. +- `q=a&q=b` : will create a **WSF_MULTIPLE_STRING** parameter with name **q** and value `[a,b]` +- `tab[a]=ewf&tab[b]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "a": "ewf", "b": "demo"}` +- `tab[]=ewf&tab[]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "1": "ewf", "2": "demo"}` +- `tab[foo]=foo&tab[foo]=bar` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "foo": "bar"}` **WARNING: only the last `tab[foo]` is kept**. Those rules are applied to query, post, path, .... parameters. \ No newline at end of file From 1d0a2363d8055aedc3c9260e5cc07c9bf68b0975 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 8 Apr 2014 21:47:39 +0200 Subject: [PATCH 11/41] Fixed issue with URL_ENCODER encoding (and small optimization) --- library/text/encoder/src/url_encoder.e | 51 ++++++++++++------- library/text/encoder/tests/test_url_encoder.e | 1 + 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/library/text/encoder/src/url_encoder.e b/library/text/encoder/src/url_encoder.e index 41d5c8e6..b15e9385 100644 --- a/library/text/encoder/src/url_encoder.e +++ b/library/text/encoder/src/url_encoder.e @@ -61,10 +61,10 @@ feature -- Encoder then Result.extend (c) else - Result.append (url_encoded_char (l_code)) + append_url_encoded_char (l_code, Result) end else - Result.append (url_encoded_char (l_code)) + append_url_encoded_char (l_code, Result) end i := i + 1 end @@ -97,14 +97,14 @@ feature -- Encoder if a_ignore.has (c) then s8.extend (c) else - s8.append (url_encoded_char (l_code)) + append_url_encoded_char (l_code, s8) end end else if a_ignore.has (c) then s8.extend (c) else - s8.append (url_encoded_char (l_code)) + append_url_encoded_char (l_code, s8) end end i := i + 1 @@ -113,25 +113,42 @@ feature -- Encoder feature {NONE} -- encoder character - url_encoded_char (a_code: NATURAL_32): STRING_8 + append_url_encoded_char (a_code: NATURAL_32; a_output: STRING_GENERAL) + local + c: INTEGER do - create Result.make (3) if a_code.is_valid_character_8_code then - Result.extend ('%%') - Result.append (a_code.to_hex_string) - from - until - Result.count < 2 or else Result[2] /= '0' - loop - Result.remove (2) - end + c := a_code.to_integer_32 + a_output.append_code (37) -- 37 '%%' + a_output.append_code (hex_digit [c |>> 4]) + a_output.append_code (hex_digit [c & 0xF]) else has_error := True --| Non-ascii escape not currently supported end - ensure - exists: Result /= Void end + hex_digit: SPECIAL [NATURAL_32] + -- Hexadecimal digits. + once + create Result.make_filled (0, 16) + Result [0] := {NATURAL_32} 48 -- 48 '0' + Result [1] := {NATURAL_32} 49 -- 49 '1' + Result [2] := {NATURAL_32} 50 -- 50 '2' + Result [3] := {NATURAL_32} 51 -- 51 '3' + Result [4] := {NATURAL_32} 52 -- 52 '4' + Result [5] := {NATURAL_32} 53 -- 53 '5' + Result [6] := {NATURAL_32} 54 -- 54 '6' + Result [7] := {NATURAL_32} 55 -- 55 '7' + Result [8] := {NATURAL_32} 56 -- 56 '8' + Result [9] := {NATURAL_32} 57 -- 57 '9' + Result [10] := {NATURAL_32} 65 -- 65 'A' + Result [11] := {NATURAL_32} 66 -- 66 'B' + Result [12] := {NATURAL_32} 67 -- 67 'C' + Result [13] := {NATURAL_32} 68 -- 68 'D' + Result [14] := {NATURAL_32} 69 -- 69 'E' + Result [15] := {NATURAL_32} 70 -- 70 'F' + end + feature -- Decoder decoded_string (v: READABLE_STRING_8): STRING_32 @@ -365,7 +382,7 @@ feature {NONE} -- Hexadecimal and strings end note - copyright: "2011-2012, Eiffel Software and others" + copyright: "2011-2014, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/text/encoder/tests/test_url_encoder.e b/library/text/encoder/tests/test_url_encoder.e index 25cff7b7..fc11974e 100644 --- a/library/text/encoder/tests/test_url_encoder.e +++ b/library/text/encoder/tests/test_url_encoder.e @@ -22,6 +22,7 @@ feature -- Test routines test_url_encoded_encoding ({STRING_32}"http://domain.tld/foo/bar/script.php?test='toto'&foo=bar&title=il tait une fois") test_url_encoded_encoding ({STRING_32}"t") test_url_encoded_decoding ({STRING_8}"%%E9t%%E9", {STRING_32}"t") + test_url_encoded_decoding ({STRING_8}"Test%0A", {STRING_32}"Test%N") test_utf8_url_encoded_decoding ({STRING_8}"%%C3%%A9t%%C3%%A9", {STRING_32}"t") end From f94820c824ea17afab9f3237411e28dde303d6ca Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 9 Apr 2014 08:34:43 +0200 Subject: [PATCH 12/41] Fixing JSON encoding code to include among other TAB (%T <-> \t) --- library/kernel/json_string.e | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/library/kernel/json_string.e b/library/kernel/json_string.e index a692219d..4cc190ee 100644 --- a/library/kernel/json_string.e +++ b/library/kernel/json_string.e @@ -82,12 +82,21 @@ feature -- Access when '%"' then Result.append_character ('%"') i := i + 2 + when 'b' then + Result.append_character ('%B') + i := i + 2 + when 'f' then + Result.append_character ('%F') + i := i + 2 when 'n' then Result.append_character ('%N') i := i + 2 when 'r' then Result.append_character ('%R') i := i + 2 + when 't' then + Result.append_character ('%T') + i := i + 2 when 'u' then --| Leave Unicode \uXXXX unescaped Result.append_character ('\') @@ -129,12 +138,21 @@ feature -- Access when '%"' then Result.append_character ('%"') i := i + 2 + when 'b' then + Result.append_character ('%B') + i := i + 2 + when 'f' then + Result.append_character ('%F') + i := i + 2 when 'n' then Result.append_character ('%N') i := i + 2 when 'r' then Result.append_character ('%R') i := i + 2 + when 'T' then + Result.append_character ('%T') + i := i + 2 when 'u' then hex := s.substring (i+2, i+2+4 - 1) if hex.count = 4 then @@ -264,8 +282,11 @@ feature {NONE} -- Implementation inspect c when '%"' then Result.append_string ("\%"") when '\' then Result.append_string ("\\") - when '%R' then Result.append_string ("\r") + when '%B' then Result.append_string ("\b") + when '%F' then Result.append_string ("\f") when '%N' then Result.append_string ("\n") + when '%R' then Result.append_string ("\r") + when '%T' then Result.append_string ("\t") else Result.extend (c) end @@ -292,8 +313,11 @@ feature {NONE} -- Implementation inspect c when '%"' then Result.append_string ("\%"") when '\' then Result.append_string ("\\") - when '%R' then Result.append_string ("\r") + when '%B' then Result.append_string ("\b") + when '%F' then Result.append_string ("\f") when '%N' then Result.append_string ("\n") + when '%R' then Result.append_string ("\r") + when '%T' then Result.append_string ("\t") else Result.extend (c) end From 6481d2ec7d0ac0b2cfa0b022224af400e4311d6a Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 9 Apr 2014 12:22:25 +0200 Subject: [PATCH 13/41] Fixed all-stable-safe.ecf fusion --- tests/all-stable-safe.ecf | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/all-stable-safe.ecf b/tests/all-stable-safe.ecf index 32b6fd1c..9903a6eb 100644 --- a/tests/all-stable-safe.ecf +++ b/tests/all-stable-safe.ecf @@ -10,45 +10,45 @@ - - - - - - - - - - - - - - - - - - - - - + - + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + Compiling as Windows , on other platforms than Windows From d50b3cb28cc22734226e12f3b7d3f7003cea6ef8 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 9 Apr 2014 12:22:36 +0200 Subject: [PATCH 14/41] For maintenance filter, response with http status code {HTTP_STATUS_CODE}.service_unavailable --- library/server/wsf/router/filter/wsf_maintenance_filter.e | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/server/wsf/router/filter/wsf_maintenance_filter.e b/library/server/wsf/router/filter/wsf_maintenance_filter.e index 00c63937..3e9cc147 100644 --- a/library/server/wsf/router/filter/wsf_maintenance_filter.e +++ b/library/server/wsf/router/filter/wsf_maintenance_filter.e @@ -61,6 +61,7 @@ feature -- Basic operations create h.make_with_count (1) h.put_content_length (s.count) h.put_content_type_text_plain + res.set_status_code ({HTTP_STATUS_CODE}.service_unavailable) res.put_header_lines (h) res.put_string (s) else @@ -69,7 +70,7 @@ feature -- Basic operations end note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software From 46920fb99108fcb03b93bcfde4e9cced97231486 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 9 Apr 2014 12:27:31 +0200 Subject: [PATCH 15/41] Do not try to read more bytes from input than provided Content-Length value. --- library/server/wsf/src/wsf_request.e | 39 +++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index 93980c60..06aec84f 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -294,25 +294,28 @@ feature -- Access: Input until l_step = 0 or l_input.end_of_input loop - l_input.append_to_string (s, l_step) - nb := l_input.last_appended_count - l_size := l_size + nb.to_natural_64 - len := len - nb.to_natural_64 - - debug ("wsf") - io.error.put_string (" append (s, " + l_step.out + ") -> " + nb.out + " (" + l_size.out + " / "+ content_length_value.out + ")%N") - end - - a_file.put_string (s) - if l_raw_data /= Void then - l_raw_data.append (s) - end - s.wipe_out - if nb < l_step then - l_step := 0 - elseif len < l_step.to_natural_64 then + if len < l_step.to_natural_64 then l_step := len.to_integer_32 end + if l_step > 0 then + l_input.append_to_string (s, l_step) + nb := l_input.last_appended_count + l_size := l_size + nb.to_natural_64 + len := len - nb.to_natural_64 + + debug ("wsf") + io.error.put_string (" append (s, " + l_step.out + ") -> " + nb.out + " (" + l_size.out + " / "+ content_length_value.out + ")%N") + end + + a_file.put_string (s) + if l_raw_data /= Void then + l_raw_data.append (s) + end + s.wipe_out + if nb < l_step then + l_step := 0 + end + end end a_file.flush debug ("wsf") @@ -2065,7 +2068,7 @@ invariant wgi_request.content_type /= Void implies content_type /= Void note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software From a4c126319027ae740b0d35ad24c61cd2c44d1a1d Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 9 Apr 2014 16:22:42 +0200 Subject: [PATCH 16/41] fixed code for test_url_encoder --- library/text/encoder/tests/test_url_encoder.e | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/text/encoder/tests/test_url_encoder.e b/library/text/encoder/tests/test_url_encoder.e index fc11974e..c8df97d7 100644 --- a/library/text/encoder/tests/test_url_encoder.e +++ b/library/text/encoder/tests/test_url_encoder.e @@ -22,7 +22,7 @@ feature -- Test routines test_url_encoded_encoding ({STRING_32}"http://domain.tld/foo/bar/script.php?test='toto'&foo=bar&title=il tait une fois") test_url_encoded_encoding ({STRING_32}"t") test_url_encoded_decoding ({STRING_8}"%%E9t%%E9", {STRING_32}"t") - test_url_encoded_decoding ({STRING_8}"Test%0A", {STRING_32}"Test%N") + test_url_encoded_decoding ({STRING_8}"Test%%0A", {STRING_32}"Test%N") test_utf8_url_encoded_decoding ({STRING_8}"%%C3%%A9t%%C3%%A9", {STRING_32}"t") end From 6e27f663062dd6f174e95abf80e8910254d92db8 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 9 Apr 2014 16:56:40 +0200 Subject: [PATCH 17/41] Improved BASE64 to update has_error when decoding. Added manual tests. --- library/text/encoder/src/base64.e | 14 ++++++++--- library/text/encoder/tests/test_base64.e | 32 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/library/text/encoder/src/base64.e b/library/text/encoder/src/base64.e index 10e63952..2da2146a 100644 --- a/library/text/encoder/src/base64.e +++ b/library/text/encoder/src/base64.e @@ -125,17 +125,17 @@ feature -- Decoder byte_count := 0 pos := next_encoded_character_position (v, pos) - if pos <= n then + if pos < n then byte1 := base64chars.index_of (v[pos], 1) - 1 byte_count := byte_count + 1 pos := next_encoded_character_position (v, pos) - if pos <= n then + if pos < n then byte2 := base64chars.index_of (v[pos], 1) - 1 byte_count := byte_count + 1 pos := next_encoded_character_position (v, pos) - if pos <= n then + if pos < n then c := v[pos] if c /= '=' then byte3 := base64chars.index_of (c, 1) - 1 @@ -150,8 +150,14 @@ feature -- Decoder byte_count := byte_count + 1 end end + else + has_error := True end + else + has_error := True end + else + has_error := True end -- pos := pos + byte_count @@ -293,7 +299,7 @@ feature {NONE} -- Constants character_map: STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" note - copyright: "Copyright (c) 1984-2011, Eiffel Software and others" + copyright: "Copyright (c) 2011-2014, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/text/encoder/tests/test_base64.e b/library/text/encoder/tests/test_base64.e index e0b0d5ea..14530dc9 100644 --- a/library/text/encoder/tests/test_base64.e +++ b/library/text/encoder/tests/test_base64.e @@ -34,6 +34,38 @@ feature -- Test routines assert ("decoded encoded string is same", u ~ s) end +feature -- Tests + + test_valid_64_encoding + do + assert ("Expected encoded True:", is_valid_base64_encoding ((create {BASE64}).encoded_string ("content"))) + end + + test_not_valid64_encoding + do + assert ("Expected encoded False:", not is_valid_base64_encoding ("content")) + assert ("Expected encoded False:", not is_valid_base64_encoding ("!@#$%%^")) + end + +feature {NONE} -- Implementation + + is_valid_base64_encoding (a_string: STRING): BOOLEAN + -- is `a_string' base64 encoded? + local + l_encoder: BASE64 + l_string: STRING + l_retry: BOOLEAN + do + if not l_retry then + create l_encoder + l_string := l_encoder.decoded_string (a_string) + Result := not l_encoder.has_error + end + rescue + l_retry := True + retry + end + note copyright: "Copyright (c) 1984-2011, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" From 9999b5e4005ff337aa9db19a106605b38fa2c840 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 9 Apr 2014 17:19:09 +0200 Subject: [PATCH 18/41] Moved implementation of WSF_PERCENT_ENCODER into "encoder" library, and added the *partial* variant. --- .../src/implementation/wsf_percent_encoder.e | 504 +-------------- library/text/encoder/license.lic | 2 +- library/text/encoder/src/percent_encoder.e | 593 ++++++++++++++++++ 3 files changed, 597 insertions(+), 502 deletions(-) create mode 100644 library/text/encoder/src/percent_encoder.e diff --git a/library/server/wsf/src/implementation/wsf_percent_encoder.e b/library/server/wsf/src/implementation/wsf_percent_encoder.e index 8d60b89a..93797fa2 100644 --- a/library/server/wsf/src/implementation/wsf_percent_encoder.e +++ b/library/server/wsf/src/implementation/wsf_percent_encoder.e @@ -9,509 +9,11 @@ note class WSF_PERCENT_ENCODER -feature -- Percent encoding - - percent_encoded_string (v: READABLE_STRING_GENERAL): STRING_8 - -- Return `a_string' percent-encoded - do - create Result.make (v.count) - append_percent_encoded_string_to (v, Result) - end - - append_percent_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL) - -- Append `a_string' as percent-encoded value to `a_result' - local - c: NATURAL_32 - i,n: INTEGER - do - from - i := 1 - n := s.count - until - i > n - loop - c := s.code (i) - if - --| unreserved ALPHA / DIGIT - (48 <= c and c <= 57) -- DIGIT: 0 .. 9 - or (65 <= c and c <= 90) -- ALPHA: A .. Z - or (97 <= c and c <= 122) -- ALPHA: a .. z - then - a_result.append_code (c) - else - inspect c - when - 45, 46, 95, 126 -- unreserved characters: -._~ - then - a_result.append_code (c) - when - 58, 64, -- reserved =+ gen-delims: :@ - 33, 36, 38, 39, 40, 41, 42, -- reserved =+ sub-delims: !$&'()* - 43, 44, 59, 61, -- reserved = sub-delims: +,;= - 37 -- percent encoding: % - then - append_percent_encoded_character_code_to (c, a_result) - else - append_percent_encoded_character_code_to (c, a_result) - end - end - i := i + 1 - end - end - -feature -- Percent encoding: character - - append_percent_encoded_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL) - -- Append character code `a_code' as percent-encoded content into `a_result' - do - if a_code > 0xFF then - -- Unicode - append_percent_encoded_unicode_character_code_to (a_code, a_result) - elseif a_code > 0x7F then - -- Extended ASCII - -- This requires percent-encoding on UTF-8 converted character. - append_percent_encoded_unicode_character_code_to (a_code, a_result) - else - -- ASCII - append_percent_encoded_ascii_character_code_to (a_code, a_result) - end - ensure - appended: a_result.count > old a_result.count - end - -feature {NONE} -- Implementation: character encoding - - append_percent_encoded_ascii_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL) - -- Append extended ascii character code `a_code' as percent-encoded content into `a_result' - -- Note: it does not UTF-8 convert this extended ASCII. - require - is_extended_ascii: a_code <= 0xFF - local - c: INTEGER - do - if a_code > 0xFF then - -- Unicode - append_percent_encoded_unicode_character_code_to (a_code, a_result) - else - -- Extended ASCII - c := a_code.to_integer_32 - a_result.append_code (37) -- 37 '%%' - a_result.append_code (hex_digit [c |>> 4]) - a_result.append_code (hex_digit [c & 0xF]) - end - ensure - appended: a_result.count > old a_result.count - end - - append_percent_encoded_unicode_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL) - -- Append Unicode character code `a_code' as UTF-8 and percent-encoded content into `a_result' - -- Note: it does include UTF-8 conversion of extended ASCII and Unicode. - do - if a_code <= 0x7F then - -- 0xxxxxxx - append_percent_encoded_ascii_character_code_to (a_code, a_result) - elseif a_code <= 0x7FF then - -- 110xxxxx 10xxxxxx - append_percent_encoded_ascii_character_code_to ((a_code |>> 6) | 0xC0, a_result) - append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result) - elseif a_code <= 0xFFFF then - -- 1110xxxx 10xxxxxx 10xxxxxx - append_percent_encoded_ascii_character_code_to ((a_code |>> 12) | 0xE0, a_result) - append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result) - append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result) - else - -- c <= 1FFFFF - there are no higher code points - -- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - append_percent_encoded_ascii_character_code_to ((a_code |>> 18) | 0xF0, a_result) - append_percent_encoded_ascii_character_code_to (((a_code |>> 12) & 0x3F) | 0x80, a_result) - append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result) - append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result) - end - ensure - appended: a_result.count > old a_result.count - end - -feature -- Percent decoding - - percent_decoded_string (v: READABLE_STRING_GENERAL): STRING_32 - -- Return the percent decoded string equivalent to the percent-encoded string `v' - --| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8 - do - create Result.make (v.count) - append_percent_decoded_string_to (v, Result) - end - - append_percent_decoded_string_to (v: READABLE_STRING_GENERAL; a_result: STRING_GENERAL) - -- Append to `a_result' a string equivalent to the percent-encoded string `v' - --| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8 - local - i,n: INTEGER - c: NATURAL_32 - pr: CELL [INTEGER] - a_result_is_string_32: BOOLEAN - do - a_result_is_string_32 := attached {STRING_32} a_result - from - i := 1 - create pr.put (i) - n := v.count - until - i > n - loop - c := v.code (i) - inspect c - when 43 then -- 43 '+' - -- Some implementation are replacing spaces with "+" instead of "%20" - a_result.append_code (32) -- 32 ' ' - when 37 then -- 37 '%%' - -- An escaped character ? - if i = n then -- Error? - a_result.append_code (c) - else - if a_result_is_string_32 then - -- Convert UTF-8 to UTF-32 - pr.replace (i) - c := next_percent_decoded_unicode_character_code (v, pr) - a_result.append_code (c) - i := pr.item - else - -- Keep UTF-8 - pr.replace (i) - c := next_percent_decoded_character_code (v, pr) - a_result.append_code (c) - i := pr.item - end - end - else - if c <= 0x7F then - a_result.append_code (c) - else - if a_result_is_string_32 then - a_result.append_code (c) - else - append_percent_encoded_character_code_to (c, a_result) - end - end - end - i := i + 1 - end - end - -feature {NONE} -- Implementation: decoding - - next_percent_decoded_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32 - -- Character decoded from string `v' starting from index `a_position.item' - -- note: it also updates `a_position.item' to indicate the new index position. - require - valid_start: a_position.item <= v.count - is_percent_char: v.code (a_position.item) = 37 -- 37 '%%' - local - c: NATURAL_32 - i, n: INTEGER - not_a_digit: BOOLEAN - ascii_pos: NATURAL_32 - ival: NATURAL_32 - pos: INTEGER - c_is_digit: BOOLEAN - do - --| pos is index in stream of escape character ('%') - pos := a_position.item - c := v.code (pos + 1) - if c = 85 or c = 117 then -- 117 'u' 85 'U' - -- NOTE: this is not a standard, but it can occur, so use this for decoding only - -- An escaped Unicode (ucs2) value, from ECMA scripts - -- has the form: %u where is the UCS value - -- of the character (two byte integer, one to 4 chars - -- after escape sequence). - -- See: http://en.wikipedia.org/wiki/Percent-encoding#Non-standard_implementations - -- UTF-8 result can be 1 to 4 characters. - from - i := pos + 2 - n := v.count - until - (i > n) or not_a_digit - loop - c := v.code (i) - c_is_digit := (48 <= c and c <= 57) -- DIGIT: 0 .. 9 - if - c_is_digit - or (97 <= c and c <= 102) -- ALPHA: a..f - or (65 <= c and c <= 70) -- ALPHA: A..F - then - ival := ival * 16 - if c_is_digit then - ival := ival + (c - 48) -- 48 '0' - else - if c > 70 then -- a..f - ival := ival + (c - 97) + 10 -- 97 'a' - else -- A..F - ival := ival + (c - 65) + 10 -- 65 'A' - end - end - i := i + 1 - else - not_a_digit := True - i := i - 1 - end - end - a_position.replace (i) - Result := ival - else - -- ASCII char? - ascii_pos := hexadecimal_string_to_natural_32 (v.substring (pos + 1, pos + 2)) - Result := ascii_pos - a_position.replace (pos + 2) - end - end - - next_percent_decoded_unicode_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32 - -- Next decoded character from `v' at position `a_position.item' - -- note: it also updates `a_position' to indicate the new index position. - require - valid_start: a_position.item <= v.count - is_percent_char: v.code (a_position.item) = 37 -- 37 '%%' - local - n, j: INTEGER - c: NATURAL_32 - c1, c2, c3, c4: NATURAL_32 - pr: CELL [INTEGER] - do - create pr.put (a_position.item) - c1 := next_percent_decoded_character_code (v, pr) - - j := pr.item - n := v.count - - Result := c1 - a_position.replace (j) - - if c1 <= 0x7F then - -- 0xxxxxxx - Result := c1 - elseif c1 <= 0xDF then - -- 110xxxxx 10xxxxxx - if j + 2 <= n then - c := v.code (j + 1) - if c = 37 then -- 37 '%%' - pr.replace (j + 1) - c2 := next_percent_decoded_character_code (v, pr) - j := pr.item - Result := ( - ((c1 & 0x1F) |<< 6) | - ( c2 & 0x3F ) - ) - a_position.replace (j) - else - -- Do not try to decode - end - end - elseif c1 <= 0xEF then - -- 1110xxxx 10xxxxxx 10xxxxxx - if j + 2 <= n then - c := v.code (j + 1) - if c = 37 then -- 37 '%%' - pr.replace (j + 1) - c2 := next_percent_decoded_character_code (v, pr) - j := pr.item - if j + 2 <= n then - c := v.code (j + 1) - if c = 37 then -- 37 '%%' - pr.replace (j + 1) - c3 := next_percent_decoded_character_code (v, pr) - j := pr.item - - Result := ( - ((c1 & 0xF) |<< 12) | - ((c2 & 0x3F) |<< 6) | - ( c3 & 0x3F ) - ) - a_position.replace (j) - else - -- Do not try to decode - end - end - else - -- Do not try to decode - end - end - elseif c1 <= 0xF7 then - -- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - - if j + 2 <= n then - c := v.code (j + 1) - if c = 37 then -- 37 '%%' - pr.replace (j + 1) - c2 := next_percent_decoded_character_code (v, pr) - j := pr.item - if j + 2 <= n then - c := v.code (j + 1) - if c = 37 then -- 37 '%%' - pr.replace (j + 1) - c3 := next_percent_decoded_character_code (v, pr) - j := pr.item - if j + 2 <= n then - c := v.code (j + 1) - if c = 37 then -- 37 '%%' - pr.replace (j + 1) - c4 := next_percent_decoded_character_code (v, pr) - j := pr.item - - a_position.replace (j) - - Result := ( - ((c1 & 0x7) |<< 18 ) | - ((c2 & 0x3F) |<< 12) | - ((c3 & 0x3F) |<< 6) | - ( c4 & 0x3F ) - ) - else - -- Do not try to decode - end - end - else - -- Do not try to decode - end - end - else - -- Do not try to decode - end - end - else - Result := c1 - end - end - -feature -- RFC and characters - - is_hexa_decimal_character (c: CHARACTER_32): BOOLEAN - -- Is hexadecimal character ? - do - Result := ('a' <= c and c <= 'f') or ('A' <= c and c <= 'F') -- HEXA - or ('0' <= c and c <= '9') -- DIGIT - end - - is_alpha_or_digit_character (c: CHARACTER_32): BOOLEAN - -- Is ALPHA or DIGIT character ? - do - Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') -- ALPHA - or ('0' <= c and c <= '9') -- DIGIT - end - - is_alpha_character (c: CHARACTER_32): BOOLEAN - -- Is ALPHA character ? - do - Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') - end - - is_digit_character (c: CHARACTER_32): BOOLEAN - -- Is DIGIT character ? - do - Result := ('0' <= c and c <= '9') - end - - is_unreserved_character (c: CHARACTER_32): BOOLEAN - -- unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - do - if - ('a' <= c and c <= 'z') -- ALPHA - or ('A' <= c and c <= 'Z') -- ALPHA - or ('0' <= c and c <= '9') -- DIGIT - then - Result := True - else - inspect c - when '-', '_', '.', '~' then -- unreserved - Result := True - else - end - end - end - - is_reserved_character (c: CHARACTER_32): BOOLEAN - -- reserved = gen-delims / sub-delims - do - Result := is_gen_delims_character (c) or is_sub_delims_character (c) - end - - is_gen_delims_character (c: CHARACTER_32): BOOLEAN - -- gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" - do - inspect c - when ':' , '/', '?' , '#' , '[' , ']' , '@' then - Result := True - else - end - end - - is_sub_delims_character (c: CHARACTER_32): BOOLEAN - -- sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - -- / "*" / "+" / "," / ";" / "=" - do - inspect c - when '!' , '$' , '&' , '%'' , '(' , ')' , '*' , '+' , ',' , ';' , '=' then -- sub-delims - Result := True - else - end - end - -feature {NONE} -- Implementation - - hex_digit: SPECIAL [NATURAL_32] - -- Hexadecimal digits. - once - create Result.make_filled (0, 16) - Result [0] := {NATURAL_32} 48 -- 48 '0' - Result [1] := {NATURAL_32} 49 -- 49 '1' - Result [2] := {NATURAL_32} 50 -- 50 '2' - Result [3] := {NATURAL_32} 51 -- 51 '3' - Result [4] := {NATURAL_32} 52 -- 52 '4' - Result [5] := {NATURAL_32} 53 -- 53 '5' - Result [6] := {NATURAL_32} 54 -- 54 '6' - Result [7] := {NATURAL_32} 55 -- 55 '7' - Result [8] := {NATURAL_32} 56 -- 56 '8' - Result [9] := {NATURAL_32} 57 -- 57 '9' - Result [10] := {NATURAL_32} 65 -- 65 'A' - Result [11] := {NATURAL_32} 66 -- 66 'B' - Result [12] := {NATURAL_32} 67 -- 67 'C' - Result [13] := {NATURAL_32} 68 -- 68 'D' - Result [14] := {NATURAL_32} 69 -- 69 'E' - Result [15] := {NATURAL_32} 70 -- 70 'F' - end - - is_hexa_decimal (a_string: READABLE_STRING_GENERAL): BOOLEAN - -- Is `a_string' a valid hexadecimal sequence? - local - l_convertor: like ctoi_convertor - do - l_convertor := ctoi_convertor - l_convertor.parse_string_with_type (a_string, {NUMERIC_INFORMATION}.type_natural_32) - Result := l_convertor.is_integral_integer - end - - hexadecimal_string_to_natural_32 (a_hex_string: READABLE_STRING_GENERAL): NATURAL_32 - -- Convert hexadecimal value `a_hex_string' to its corresponding NATURAL_32 value. - require - is_hexa: is_hexa_decimal (a_hex_string) - local - l_convertor: like ctoi_convertor - do - l_convertor := ctoi_convertor - l_convertor.parse_string_with_type (a_hex_string, {NUMERIC_INFORMATION}.type_no_limitation) - Result := l_convertor.parsed_natural_32 - end - - ctoi_convertor: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER - -- Converter used to convert string to integer or natural. - once - create Result.make - Result.set_leading_separators_acceptable (False) - Result.set_trailing_separators_acceptable (False) - ensure - ctoi_convertor_not_void: Result /= Void - end +inherit + PERCENT_ENCODER note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/text/encoder/license.lic b/library/text/encoder/license.lic index cf2d1ed9..545cc9d7 100644 --- a/library/text/encoder/license.lic +++ b/library/text/encoder/license.lic @@ -1,5 +1,5 @@ ${NOTE_KEYWORD} - copyright: "2011-${YEAR}, Eiffel Software and others" + copyright: "Copyright (c) 2011-${YEAR}, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/text/encoder/src/percent_encoder.e b/library/text/encoder/src/percent_encoder.e new file mode 100644 index 00000000..512094da --- /dev/null +++ b/library/text/encoder/src/percent_encoder.e @@ -0,0 +1,593 @@ +note + description: "[ + Component to handle percent encoding + ]" + date: "$Date: 2014-04-09 16:37:28 +0200 (mer., 09 avr. 2014) $" + revision: "$Revision: 94801 $" + EIS: "name=Percent-encoding", "protocol=URI", "src=http://en.wikipedia.org/wiki/Percent-encoding" + +class + PERCENT_ENCODER + +feature -- Status report + + has_error: BOOLEAN + -- Error occurred + --| For now this is not fully implemented, and thus not reliable. + +feature -- Percent encoding + + percent_encoded_string (s: READABLE_STRING_GENERAL): STRING_8 + -- Return `s' percent-encoded + do + create Result.make (s.count) + append_percent_encoded_string_to (s, Result) + end + + append_percent_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL) + -- Append `s' as percent-encoded value to `a_result' + local + c: NATURAL_32 + i,n: INTEGER + do + has_error := False + from + i := 1 + n := s.count + until + i > n + loop + c := s.code (i) + if + --| unreserved ALPHA / DIGIT + (48 <= c and c <= 57) -- DIGIT: 0 .. 9 + or (65 <= c and c <= 90) -- ALPHA: A .. Z + or (97 <= c and c <= 122) -- ALPHA: a .. z + then + a_result.append_code (c) + else + inspect c + when + 45, 46, 95, 126 -- unreserved characters: -._~ + then + a_result.append_code (c) + when + 58, 64, -- reserved =+ gen-delims: :@ + 33, 36, 38, 39, 40, 41, 42, -- reserved =+ sub-delims: !$&'()* + 43, 44, 59, 61, -- reserved = sub-delims: +,;= + 37 -- percent encoding: % + then + append_percent_encoded_character_code_to (c, a_result) + else + append_percent_encoded_character_code_to (c, a_result) + end + end + i := i + 1 + end + end + + partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ITERABLE [CHARACTER_32]): STRING_8 + -- Return `s' as percent-encoded value, + -- but does not escape character listed in `a_ignore'. + do + create Result.make (s.count) + append_partial_percent_encoded_string_to (s, Result, a_ignore) + end + + append_partial_percent_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL; a_ignore: ITERABLE [CHARACTER_32]) + -- Append `s' as percent-encoded value to `a_result', + -- but does not escape character listed in `a_ignore'. + local + c: NATURAL_32 + ch: CHARACTER_32 + i,n: INTEGER + do + has_error := False + from + i := 1 + n := s.count + until + i > n + loop + c := s.code (i) + if + --| unreserved ALPHA / DIGIT + (48 <= c and c <= 57) -- DIGIT: 0 .. 9 + or (65 <= c and c <= 90) -- ALPHA: A .. Z + or (97 <= c and c <= 122) -- ALPHA: a .. z + then + a_result.append_code (c) + else + inspect c + when + 45, 46, 95, 126 -- unreserved characters: -._~ + then + a_result.append_code (c) + when + 58, 64, -- reserved =+ gen-delims: :@ + 33, 36, 38, 39, 40, 41, 42, -- reserved =+ sub-delims: !$&'()* + 43, 44, 59, 61, -- reserved = sub-delims: +,;= + 37 -- percent encoding: % + then + ch := c.to_character_32 + if across a_ignore as ic some ic.item = ch end then + a_result.append_code (c) + else + append_percent_encoded_character_code_to (c, a_result) + end + else + if across a_ignore as ic some ic.item = ch end then + a_result.append_code (c) + else + append_percent_encoded_character_code_to (c, a_result) + end + end + end + i := i + 1 + end + end + +feature -- Percent encoding: character + + append_percent_encoded_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL) + -- Append character code `a_code' as percent-encoded content into `a_result' + do + if a_code > 0xFF then + -- Unicode + append_percent_encoded_unicode_character_code_to (a_code, a_result) + elseif a_code > 0x7F then + -- Extended ASCII + -- This requires percent-encoding on UTF-8 converted character. + append_percent_encoded_unicode_character_code_to (a_code, a_result) + else + -- ASCII + append_percent_encoded_ascii_character_code_to (a_code, a_result) + end + ensure + appended: a_result.count > old a_result.count + end + +feature {NONE} -- Implementation: character encoding + + append_percent_encoded_ascii_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL) + -- Append extended ascii character code `a_code' as percent-encoded content into `a_result' + -- Note: it does not UTF-8 convert this extended ASCII. + require + is_extended_ascii: a_code <= 0xFF + local + c: INTEGER + do + if a_code > 0xFF then + -- Unicode + append_percent_encoded_unicode_character_code_to (a_code, a_result) + else + -- Extended ASCII + c := a_code.to_integer_32 + a_result.append_code (37) -- 37 '%%' + a_result.append_code (hex_digit [c |>> 4]) + a_result.append_code (hex_digit [c & 0xF]) + end + ensure + appended: a_result.count > old a_result.count + end + + append_percent_encoded_unicode_character_code_to (a_code: NATURAL_32; a_result: STRING_GENERAL) + -- Append Unicode character code `a_code' as UTF-8 and percent-encoded content into `a_result' + -- Note: it does include UTF-8 conversion of extended ASCII and Unicode. + do + if a_code <= 0x7F then + -- 0xxxxxxx + append_percent_encoded_ascii_character_code_to (a_code, a_result) + elseif a_code <= 0x7FF then + -- 110xxxxx 10xxxxxx + append_percent_encoded_ascii_character_code_to ((a_code |>> 6) | 0xC0, a_result) + append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result) + elseif a_code <= 0xFFFF then + -- 1110xxxx 10xxxxxx 10xxxxxx + append_percent_encoded_ascii_character_code_to ((a_code |>> 12) | 0xE0, a_result) + append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result) + append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result) + else + -- c <= 1FFFFF - there are no higher code points + -- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + append_percent_encoded_ascii_character_code_to ((a_code |>> 18) | 0xF0, a_result) + append_percent_encoded_ascii_character_code_to (((a_code |>> 12) & 0x3F) | 0x80, a_result) + append_percent_encoded_ascii_character_code_to (((a_code |>> 6) & 0x3F) | 0x80, a_result) + append_percent_encoded_ascii_character_code_to ((a_code & 0x3F) | 0x80, a_result) + end + ensure + appended: a_result.count > old a_result.count + end + +feature -- Percent decoding + + percent_decoded_string (v: READABLE_STRING_GENERAL): STRING_32 + -- Return the percent decoded string equivalent to the percent-encoded string `v' + --| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8 + do + create Result.make (v.count) + append_percent_decoded_string_to (v, Result) + end + + append_percent_decoded_string_to (v: READABLE_STRING_GENERAL; a_result: STRING_GENERAL) + -- Append to `a_result' a string equivalent to the percent-encoded string `v' + --| Note that is `a_result' is a STRING_8, any Unicode character will be kept as UTF-8 + local + i,n: INTEGER + c: NATURAL_32 + pr: CELL [INTEGER] + a_result_is_string_32: BOOLEAN + do + has_error := False + a_result_is_string_32 := attached {STRING_32} a_result + from + i := 1 + create pr.put (i) + n := v.count + until + i > n + loop + c := v.code (i) + inspect c + when 43 then -- 43 '+' + -- Some implementation are replacing spaces with "+" instead of "%20" + a_result.append_code (32) -- 32 ' ' + when 37 then -- 37 '%%' + -- An escaped character ? + if i = n then -- Error? + has_error := True + a_result.append_code (c) + else + if a_result_is_string_32 then + -- Convert UTF-8 to UTF-32 + pr.replace (i) + c := next_percent_decoded_unicode_character_code (v, pr) + a_result.append_code (c) + i := pr.item + else + -- Keep UTF-8 + pr.replace (i) + c := next_percent_decoded_character_code (v, pr) + a_result.append_code (c) + i := pr.item + end + end + else + if c <= 0x7F then + a_result.append_code (c) + else + if a_result_is_string_32 then + a_result.append_code (c) + else + append_percent_encoded_character_code_to (c, a_result) + end + end + end + i := i + 1 + end + end + +feature {NONE} -- Implementation: decoding + + next_percent_decoded_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32 + -- Character decoded from string `v' starting from index `a_position.item' + -- note: it also updates `a_position.item' to indicate the new index position. + require + valid_start: a_position.item <= v.count + is_percent_char: v.code (a_position.item) = 37 -- 37 '%%' + local + c: NATURAL_32 + i, n: INTEGER + not_a_digit: BOOLEAN + ascii_pos: NATURAL_32 + ival: NATURAL_32 + pos: INTEGER + c_is_digit: BOOLEAN + do + --| pos is index in stream of escape character ('%') + pos := a_position.item + c := v.code (pos + 1) + if c = 85 or c = 117 then -- 117 'u' 85 'U' + -- NOTE: this is not a standard, but it can occur, so use this for decoding only + -- An escaped Unicode (ucs2) value, from ECMA scripts + -- has the form: %u where is the UCS value + -- of the character (two byte integer, one to 4 chars + -- after escape sequence). + -- See: http://en.wikipedia.org/wiki/Percent-encoding#Non-standard_implementations + -- UTF-8 result can be 1 to 4 characters. + from + i := pos + 2 + n := v.count + until + (i > n) or not_a_digit + loop + c := v.code (i) + c_is_digit := (48 <= c and c <= 57) -- DIGIT: 0 .. 9 + if + c_is_digit + or (97 <= c and c <= 102) -- ALPHA: a..f + or (65 <= c and c <= 70) -- ALPHA: A..F + then + ival := ival * 16 + if c_is_digit then + ival := ival + (c - 48) -- 48 '0' + else + if c > 70 then -- a..f + ival := ival + (c - 97) + 10 -- 97 'a' + else -- A..F + ival := ival + (c - 65) + 10 -- 65 'A' + end + end + i := i + 1 + else + not_a_digit := True + i := i - 1 + end + end + a_position.replace (i) + Result := ival + else + -- ASCII char? + ascii_pos := hexadecimal_string_to_natural_32 (v.substring (pos + 1, pos + 2)) + Result := ascii_pos + a_position.replace (pos + 2) + end + end + + next_percent_decoded_unicode_character_code (v: READABLE_STRING_GENERAL; a_position: CELL [INTEGER]): NATURAL_32 + -- Next decoded character from `v' at position `a_position.item' + -- note: it also updates `a_position' to indicate the new index position. + require + valid_start: a_position.item <= v.count + is_percent_char: v.code (a_position.item) = 37 -- 37 '%%' + local + n, j: INTEGER + c: NATURAL_32 + c1, c2, c3, c4: NATURAL_32 + pr: CELL [INTEGER] + do + create pr.put (a_position.item) + c1 := next_percent_decoded_character_code (v, pr) + + j := pr.item + n := v.count + + Result := c1 + a_position.replace (j) + + if c1 <= 0x7F then + -- 0xxxxxxx + Result := c1 + elseif c1 <= 0xDF then + -- 110xxxxx 10xxxxxx + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c2 := next_percent_decoded_character_code (v, pr) + j := pr.item + Result := ( + ((c1 & 0x1F) |<< 6) | + ( c2 & 0x3F ) + ) + a_position.replace (j) + else + -- Do not try to decode + end + end + elseif c1 <= 0xEF then + -- 1110xxxx 10xxxxxx 10xxxxxx + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c2 := next_percent_decoded_character_code (v, pr) + j := pr.item + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c3 := next_percent_decoded_character_code (v, pr) + j := pr.item + + Result := ( + ((c1 & 0xF) |<< 12) | + ((c2 & 0x3F) |<< 6) | + ( c3 & 0x3F ) + ) + a_position.replace (j) + else + -- Do not try to decode + end + end + else + -- Do not try to decode + end + end + elseif c1 <= 0xF7 then + -- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c2 := next_percent_decoded_character_code (v, pr) + j := pr.item + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c3 := next_percent_decoded_character_code (v, pr) + j := pr.item + if j + 2 <= n then + c := v.code (j + 1) + if c = 37 then -- 37 '%%' + pr.replace (j + 1) + c4 := next_percent_decoded_character_code (v, pr) + j := pr.item + + a_position.replace (j) + + Result := ( + ((c1 & 0x7) |<< 18 ) | + ((c2 & 0x3F) |<< 12) | + ((c3 & 0x3F) |<< 6) | + ( c4 & 0x3F ) + ) + else + -- Do not try to decode + end + end + else + -- Do not try to decode + end + end + else + -- Do not try to decode + end + end + else + Result := c1 + end + end + +feature -- RFC and characters + + is_hexa_decimal_character (c: CHARACTER_32): BOOLEAN + -- Is hexadecimal character ? + do + Result := ('a' <= c and c <= 'f') or ('A' <= c and c <= 'F') -- HEXA + or ('0' <= c and c <= '9') -- DIGIT + end + + is_alpha_or_digit_character (c: CHARACTER_32): BOOLEAN + -- Is ALPHA or DIGIT character ? + do + Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') -- ALPHA + or ('0' <= c and c <= '9') -- DIGIT + end + + is_alpha_character (c: CHARACTER_32): BOOLEAN + -- Is ALPHA character ? + do + Result := ('a' <= c and c <= 'z') or ('A' <= c and c <= 'Z') + end + + is_digit_character (c: CHARACTER_32): BOOLEAN + -- Is DIGIT character ? + do + Result := ('0' <= c and c <= '9') + end + + is_unreserved_character (c: CHARACTER_32): BOOLEAN + -- unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + do + if + ('a' <= c and c <= 'z') -- ALPHA + or ('A' <= c and c <= 'Z') -- ALPHA + or ('0' <= c and c <= '9') -- DIGIT + then + Result := True + else + inspect c + when '-', '_', '.', '~' then -- unreserved + Result := True + else + end + end + end + + is_reserved_character (c: CHARACTER_32): BOOLEAN + -- reserved = gen-delims / sub-delims + do + Result := is_gen_delims_character (c) or is_sub_delims_character (c) + end + + is_gen_delims_character (c: CHARACTER_32): BOOLEAN + -- gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" + do + inspect c + when ':' , '/', '?' , '#' , '[' , ']' , '@' then + Result := True + else + end + end + + is_sub_delims_character (c: CHARACTER_32): BOOLEAN + -- sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + -- / "*" / "+" / "," / ";" / "=" + do + inspect c + when '!' , '$' , '&' , '%'' , '(' , ')' , '*' , '+' , ',' , ';' , '=' then -- sub-delims + Result := True + else + end + end + +feature {NONE} -- Implementation + + hex_digit: SPECIAL [NATURAL_32] + -- Hexadecimal digits. + once + create Result.make_filled (0, 16) + Result [0] := {NATURAL_32} 48 -- 48 '0' + Result [1] := {NATURAL_32} 49 -- 49 '1' + Result [2] := {NATURAL_32} 50 -- 50 '2' + Result [3] := {NATURAL_32} 51 -- 51 '3' + Result [4] := {NATURAL_32} 52 -- 52 '4' + Result [5] := {NATURAL_32} 53 -- 53 '5' + Result [6] := {NATURAL_32} 54 -- 54 '6' + Result [7] := {NATURAL_32} 55 -- 55 '7' + Result [8] := {NATURAL_32} 56 -- 56 '8' + Result [9] := {NATURAL_32} 57 -- 57 '9' + Result [10] := {NATURAL_32} 65 -- 65 'A' + Result [11] := {NATURAL_32} 66 -- 66 'B' + Result [12] := {NATURAL_32} 67 -- 67 'C' + Result [13] := {NATURAL_32} 68 -- 68 'D' + Result [14] := {NATURAL_32} 69 -- 69 'E' + Result [15] := {NATURAL_32} 70 -- 70 'F' + end + + is_hexa_decimal (a_string: READABLE_STRING_GENERAL): BOOLEAN + -- Is `a_string' a valid hexadecimal sequence? + local + l_convertor: like ctoi_convertor + do + l_convertor := ctoi_convertor + l_convertor.parse_string_with_type (a_string, {NUMERIC_INFORMATION}.type_natural_32) + Result := l_convertor.is_integral_integer + end + + hexadecimal_string_to_natural_32 (a_hex_string: READABLE_STRING_GENERAL): NATURAL_32 + -- Convert hexadecimal value `a_hex_string' to its corresponding NATURAL_32 value. + require + is_hexa: is_hexa_decimal (a_hex_string) + local + l_convertor: like ctoi_convertor + do + l_convertor := ctoi_convertor + l_convertor.parse_string_with_type (a_hex_string, {NUMERIC_INFORMATION}.type_no_limitation) + Result := l_convertor.parsed_natural_32 + end + + ctoi_convertor: HEXADECIMAL_STRING_TO_INTEGER_CONVERTER + -- Converter used to convert string to integer or natural. + once + create Result.make + Result.set_leading_separators_acceptable (False) + Result.set_trailing_separators_acceptable (False) + ensure + ctoi_convertor_not_void: Result /= Void + end + +note + copyright: "Copyright (c) 2011-2014, 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 From d40131f863af8abd11c7aa32ef2591fa00e21729 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 9 Apr 2014 18:10:51 +0200 Subject: [PATCH 19/41] Updated encoder library, especially URL encoders to reuse implementation of percent_encoder.e Fixed JSON_ENCODER for %T and related. Updated related autotest cases. --- library/text/encoder/src/json_encoder.e | 16 +- library/text/encoder/src/percent_encoder.e | 18 +- library/text/encoder/src/url_encoder.e | 348 +----------------- library/text/encoder/src/utf8_url_encoder.e | 54 +-- .../text/encoder/tests/test_json_encoder.e | 1 + .../text/encoder/tests/test_utf8_encoder.e | 32 +- 6 files changed, 62 insertions(+), 407 deletions(-) diff --git a/library/text/encoder/src/json_encoder.e b/library/text/encoder/src/json_encoder.e index 333c7e34..84909edd 100644 --- a/library/text/encoder/src/json_encoder.e +++ b/library/text/encoder/src/json_encoder.e @@ -50,8 +50,11 @@ feature -- Encoder inspect c when '%"' then Result.append_string ("\%"") when '\' then Result.append_string ("\\") - when '%R' then Result.append_string ("\r") + when '%B' then Result.append_string ("\b") + when '%F' then Result.append_string ("\f") when '%N' then Result.append_string ("\n") + when '%R' then Result.append_string ("\r") + when '%T' then Result.append_string ("\t") else Result.extend (c) end @@ -103,12 +106,21 @@ feature -- Decoder when '%"' then Result.append_character ('%"') i := i + 2 + when 'b' then + Result.append_character ('%B') + i := i + 2 + when 'f' then + Result.append_character ('%F') + i := i + 2 when 'n' then Result.append_character ('%N') i := i + 2 when 'r' then Result.append_character ('%R') i := i + 2 + when 't' then + Result.append_character ('%T') + i := i + 2 when 'u' then hex := v.substring (i+2, i+2+4 - 1) if hex.count = 4 then @@ -170,7 +182,7 @@ feature {NONE} -- Implementation end note - copyright: "2011-2012, Eiffel Software and others" + copyright: "Copyright (c) 2011-2014, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/text/encoder/src/percent_encoder.e b/library/text/encoder/src/percent_encoder.e index 512094da..75b9961e 100644 --- a/library/text/encoder/src/percent_encoder.e +++ b/library/text/encoder/src/percent_encoder.e @@ -66,7 +66,7 @@ feature -- Percent encoding end end - partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ITERABLE [CHARACTER_32]): STRING_8 + partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ITERABLE [CHARACTER]): STRING_8 -- Return `s' as percent-encoded value, -- but does not escape character listed in `a_ignore'. do @@ -74,12 +74,12 @@ feature -- Percent encoding append_partial_percent_encoded_string_to (s, Result, a_ignore) end - append_partial_percent_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL; a_ignore: ITERABLE [CHARACTER_32]) + append_partial_percent_encoded_string_to (s: READABLE_STRING_GENERAL; a_result: STRING_GENERAL; a_ignore: ITERABLE [CHARACTER]) -- Append `s' as percent-encoded value to `a_result', -- but does not escape character listed in `a_ignore'. local c: NATURAL_32 - ch: CHARACTER_32 + ch: CHARACTER_8 i,n: INTEGER do has_error := False @@ -109,15 +109,21 @@ feature -- Percent encoding 43, 44, 59, 61, -- reserved = sub-delims: +,;= 37 -- percent encoding: % then - ch := c.to_character_32 + check c.is_valid_character_8_code end + ch := c.to_character_8 if across a_ignore as ic some ic.item = ch end then a_result.append_code (c) else append_percent_encoded_character_code_to (c, a_result) end else - if across a_ignore as ic some ic.item = ch end then - a_result.append_code (c) + if c.is_valid_character_8_code then + ch := c.to_character_8 + if across a_ignore as ic some ic.item = ch end then + a_result.append_code (c) + else + append_percent_encoded_character_code_to (c, a_result) + end else append_percent_encoded_character_code_to (c, a_result) end diff --git a/library/text/encoder/src/url_encoder.e b/library/text/encoder/src/url_encoder.e index b15e9385..8975e8cb 100644 --- a/library/text/encoder/src/url_encoder.e +++ b/library/text/encoder/src/url_encoder.e @@ -20,6 +20,12 @@ inherit {NONE} all end + PERCENT_ENCODER + rename + percent_encoded_string as general_encoded_string, + percent_decoded_string as general_decoded_string + end + feature -- Access name: READABLE_STRING_8 @@ -27,10 +33,6 @@ feature -- Access create {IMMUTABLE_STRING_8} Result.make_from_string ("URL-encoded") end -feature -- Status report - - has_error: BOOLEAN - feature -- Encoder encoded_string (s: READABLE_STRING_32): STRING_8 @@ -39,350 +41,16 @@ feature -- Encoder Result := general_encoded_string (s) end - general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8 - -- URL-encoded value of `s'. - local - i, n: INTEGER - c: CHARACTER_8 - l_code: NATURAL_32 - do - has_error := False - create Result.make (s.count + s.count // 10) - n := s.count - from i := 1 until i > n loop - l_code := s.code (i) - if l_code.is_valid_character_8_code then - c := l_code.to_character_8 - inspect c - when - 'A' .. 'Z', - 'a' .. 'z', '0' .. '9', - '.', '-', '~', '_' - then - Result.extend (c) - else - append_url_encoded_char (l_code, Result) - end - else - append_url_encoded_char (l_code, Result) - end - i := i + 1 - end - end - - partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ARRAY [CHARACTER]): STRING_8 - -- URL-encoded value of `s'. - local - i, n: INTEGER - l_code: NATURAL_32 - c: CHARACTER_8 - s8: STRING_8 - do - has_error := False - create s8.make (s.count + s.count // 10) - Result := s8 - n := s.count - from i := 1 until i > n loop - l_code := s.code (i) - if l_code.is_valid_character_8_code then - c := l_code.to_character_8 - inspect c - when - 'A' .. 'Z', - 'a' .. 'z', '0' .. '9', - '.', '-', '~', '_' - then - s8.extend (c) - else - if a_ignore.has (c) then - s8.extend (c) - else - append_url_encoded_char (l_code, s8) - end - end - else - if a_ignore.has (c) then - s8.extend (c) - else - append_url_encoded_char (l_code, s8) - end - end - i := i + 1 - end - end - -feature {NONE} -- encoder character - - append_url_encoded_char (a_code: NATURAL_32; a_output: STRING_GENERAL) - local - c: INTEGER - do - if a_code.is_valid_character_8_code then - c := a_code.to_integer_32 - a_output.append_code (37) -- 37 '%%' - a_output.append_code (hex_digit [c |>> 4]) - a_output.append_code (hex_digit [c & 0xF]) - else - has_error := True --| Non-ascii escape not currently supported - end - end - - hex_digit: SPECIAL [NATURAL_32] - -- Hexadecimal digits. - once - create Result.make_filled (0, 16) - Result [0] := {NATURAL_32} 48 -- 48 '0' - Result [1] := {NATURAL_32} 49 -- 49 '1' - Result [2] := {NATURAL_32} 50 -- 50 '2' - Result [3] := {NATURAL_32} 51 -- 51 '3' - Result [4] := {NATURAL_32} 52 -- 52 '4' - Result [5] := {NATURAL_32} 53 -- 53 '5' - Result [6] := {NATURAL_32} 54 -- 54 '6' - Result [7] := {NATURAL_32} 55 -- 55 '7' - Result [8] := {NATURAL_32} 56 -- 56 '8' - Result [9] := {NATURAL_32} 57 -- 57 '9' - Result [10] := {NATURAL_32} 65 -- 65 'A' - Result [11] := {NATURAL_32} 66 -- 66 'B' - Result [12] := {NATURAL_32} 67 -- 67 'C' - Result [13] := {NATURAL_32} 68 -- 68 'D' - Result [14] := {NATURAL_32} 69 -- 69 'E' - Result [15] := {NATURAL_32} 70 -- 70 'F' - end - feature -- Decoder decoded_string (v: READABLE_STRING_8): STRING_32 -- The URL-encoded equivalent of the given string - local - i, n: INTEGER - c: CHARACTER - pr: CELL [INTEGER] - changed: BOOLEAN do - has_error := False - n := v.count - create Result.make (n) - from i := 1 - until i > n - loop - c := v.item (i) - inspect c - when '+' then - changed := True - Result.append_character ({CHARACTER_32}' ') - when '%%' then - -- An escaped character ? - if i = n then - Result.append_character (c.to_character_32) - else - changed := True - create pr.put (i) - Result.append (url_decoded_char (v, pr)) - i := pr.item - end - else - Result.append_character (c.to_character_32) - end - i := i + 1 - end - end - -feature {NONE} -- decoded character - - url_decoded_char (buf: STRING_8; posr: CELL [INTEGER]): STRING_32 - -- Character(s) resulting from decoding the URL-encoded string - require - stream_exists: buf /= Void - posr_exists: posr /= Void - valid_start: posr.item <= buf.count - local - c: CHARACTER - i, n, nb: INTEGER - not_a_digit: BOOLEAN - ascii_pos, ival: INTEGER - pos: INTEGER - do - --| pos is index in stream of escape character ('%') - pos := posr.item - create Result.make (4) - if buf.item (pos + 1) = 'u' then - -- An escaped Unicode (ucs2) value, from ECMA scripts - -- Has the form: %u where is the UCS value - -- of the character (two byte integer, one to 4 chars - -- after escape sequence). - -- UTF-8 result can be 1 to 4 characters - n := buf.count - from i := pos + 2 - until (i > n) or not_a_digit - loop - c := buf.item (i) - if c.is_hexa_digit then - ival := ival * 16 - if c.is_digit then - ival := ival + (c |-| '0') - else - ival := ival + (c.upper |-| 'A') + 10 - end - i := i + 1 - else - not_a_digit := True - end - end - posr.replace (i) - -- ival is now UCS2 value; needs conversion to UTF8 - Result.append_code (ival.as_natural_32) - nb := utf8_bytes_in_sequence (buf, pos) - else - -- ASCII char? - ascii_pos := hex_to_integer_32 (buf.substring (pos+1, pos+2)) - if ascii_pos >= 0x80 and ascii_pos <= 0xff then - -- Might be improperly escaped - Result.append_code (ascii_pos.as_natural_32) - posr.replace (pos + 2) - else - Result.append_code (ascii_pos.as_natural_32) - posr.replace (pos + 2) - end - end - ensure - exists: Result /= Void - end - -feature {NONE} -- UTF8 - - utf8_bytes_in_sequence (s: STRING_8; spos: INTEGER): INTEGER - -- If the given character is a legal first byte element in a - -- utf8 byte sequence (aka character), then return the number - -- of bytes in that sequence - -- Result of zero means it's not a utf8 first byte - require - exists: s /= Void - long_enough: s.count >= spos - do - Result := bytes_in_utf8_char (s.item (spos)) - end - - bytes_in_utf8_char (v: CHARACTER_8): INTEGER - -- If the given byte a legal first byte element in a utf8 sequence, - -- then the number of bytes in that character - -- Zero denotes an error, i.e. not a legal UTF8 char - -- - -- The first byte of a UTF8 encodes the length - local - c: NATURAL_8 - do - c := v.code.to_natural_8 - Result := 1 -- 7 bit ASCII - if (c & 0x80) /= 0 then - -- Hi bit means not ASCII - Result := 0 - if (c & 0xe0) = 0xc0 then - -- If we see a first byte as b110xxxxx - -- then we expect a two-byte character - Result := 2 - elseif (c & 0xf0) = 0xe0 then - -- If we see a first byte as b1110xxxx - -- then we expect a three-byte character - Result := 3 - elseif (c & 0xf8) = 0xf0 then - -- If we see a first byte as b11110xxx - -- then we expect a four-byte character - Result := 4 - elseif (c & 0xfc) = 0xf8 then - -- If we see a first byte as b111110xx - -- then we expect a five-byte character - Result := 5 - elseif (c & 0xfe) = 0xfc then - -- If we see a first byte as b1111110x - -- then we expect a six-byte character - Result := 6 - end - end - end - -feature {NONE} -- Hexadecimal and strings - - hex_to_integer_32 (s: STRING): INTEGER_32 - -- Hexadecimal string `s' converted to INTEGER_32 value - require - s_not_void: s /= Void - local - i, nb: INTEGER; - char: CHARACTER - do - nb := s.count - - if nb >= 2 and then s.item (2) = 'x' then - i := 3 - else - i := 1 - end - - from - until - i > nb - loop - Result := Result * 16 - char := s.item (i) - if char >= '0' and then char <= '9' then - Result := Result + (char |-| '0') - else - Result := Result + (char.lower |-| 'a' + 10) - end - i := i + 1 - end - end - - hex_to_integer_64 (s: STRING): INTEGER_64 - -- Hexadecimal string `s' converted to INTEGER_64 value - require - s_not_void: s /= Void - local - i, nb: INTEGER; - char: CHARACTER - do - nb := s.count - - if nb >= 2 and then s.item (2) = 'x' then - i := 3 - else - i := 1 - end - - from - until - i > nb - loop - Result := Result * 16 - char := s.item (i) - if char >= '0' and then char <= '9' then - Result := Result + (char |-| '0') - else - Result := Result + (char.lower |-| 'a' + 10) - end - i := i + 1 - end - end - - hex_to_pointer (s: STRING): POINTER - -- Hexadecimal string `s' converted to POINTER value - require - s_not_void: s /= Void - local - val_32: INTEGER_32 - val_64: INTEGER_64 - do - if Pointer_bytes = Integer_64_bytes then - val_64 := hex_to_integer_64 (s) - ($Result).memory_copy ($val_64, Pointer_bytes) - else - val_32 := hex_to_integer_32 (s) - ($Result).memory_copy ($val_32, Pointer_bytes) - end + Result := general_decoded_string (v) end note - copyright: "2011-2014, Eiffel Software and others" + copyright: "Copyright (c) 2011-2014, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/text/encoder/src/utf8_url_encoder.e b/library/text/encoder/src/utf8_url_encoder.e index 1edfef50..33c97b2e 100644 --- a/library/text/encoder/src/utf8_url_encoder.e +++ b/library/text/encoder/src/utf8_url_encoder.e @@ -14,23 +14,6 @@ class inherit URL_ENCODER - redefine - name, - general_encoded_string, - encoded_string, partial_encoded_string, - decoded_string - select - encoded_string, - decoded_string, - has_error - end - - UTF8_ENCODER - rename - general_encoded_string as utf8_general_encoded_string, - encoded_string as utf8_encoded_string, - decoded_string as utf8_decoded_string, - has_error as utf8_has_error redefine name end @@ -42,43 +25,8 @@ feature -- Access create {IMMUTABLE_STRING_8} Result.make_from_string ("UTF8-URL-encoded") end -feature -- Encoder - - encoded_string (s: READABLE_STRING_32): STRING_8 - -- URL-encoded value of `s'. - do - Result := general_encoded_string (s) - end - - general_encoded_string (s: READABLE_STRING_GENERAL): STRING_8 - do - Result := utf8_general_encoded_string (s) - Result := Precursor {URL_ENCODER} (Result) - has_error := has_error or utf8_has_error - end - - partial_encoded_string (s: READABLE_STRING_GENERAL; a_ignore: ARRAY [CHARACTER]): STRING_8 - -- URL-encoded value of `s'. - do - Result := utf8_general_encoded_string (s) - Result := Precursor {URL_ENCODER} (Result, a_ignore) - has_error := has_error or utf8_has_error - end - -feature -- Decoder - - decoded_string (v: READABLE_STRING_8): STRING_32 - -- The URL-encoded equivalent of the given string - do - Result := Precursor {URL_ENCODER} (v) - if not has_error then - Result := utf8_decoded_string (Result) - has_error := utf8_has_error - end - end - note - copyright: "2011-2013, Eiffel Software and others" + copyright: "Copyright (c) 2011-2014, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software diff --git a/library/text/encoder/tests/test_json_encoder.e b/library/text/encoder/tests/test_json_encoder.e index 6c54c974..3d370213 100644 --- a/library/text/encoder/tests/test_json_encoder.e +++ b/library/text/encoder/tests/test_json_encoder.e @@ -21,6 +21,7 @@ feature -- Test routines do test_json_encoded_encoding ({STRING_32}"il était une fois %"Ni & Hao%" (你好) \a\b\c") test_json_encoded_encoding ({STRING_32}" it's `abc’ ") + test_json_encoded_encoding ({STRING_32}"tab%Tnew line%N %"double quote %"") end test_json_encoded_encoding (s: STRING_32) diff --git a/library/text/encoder/tests/test_utf8_encoder.e b/library/text/encoder/tests/test_utf8_encoder.e index e1f29c61..b2c1525a 100644 --- a/library/text/encoder/tests/test_utf8_encoder.e +++ b/library/text/encoder/tests/test_utf8_encoder.e @@ -1,4 +1,4 @@ -note +note description: "[ Eiffel tests that can be executed by testing tool. ]" @@ -18,23 +18,43 @@ feature -- Test routines test_url_encoded_encoder note testing: "url-encoded" + local + utf8: STRING_8 do - test_utf8_decoding ("%%C3%%A9t%%C3%%A9", {STRING_32}"t") + create utf8.make_empty + utf8.append_code (195) --+ + utf8.append_code (169) -- é + utf8.append_code (116) -- t + utf8.append_code (195) --+ + utf8.append_code (169) -- é + test_utf8_decoding (utf8, {STRING_32}"été") + + create utf8.make_empty + utf8.append_code (228) --+ + utf8.append_code (189) --+ + utf8.append_code (160) -- 你 + + utf8.append_code (229) --+ + utf8.append_code (165) --+ + utf8.append_code (189) -- 好 + + utf8.append_code (229) --+ + utf8.append_code (144) --+ + utf8.append_code (151) -- 吗 + + test_utf8_decoding (utf8, {STRING_32}"你好吗") end test_utf8_decoding (s: STRING_8; e: STRING_32) local - url: URL_ENCODER u: STRING_32 b: UTF8_ENCODER do create b - create url - u := b.decoded_string (url.decoded_string (s)) + u := b.decoded_string (s) assert ("decoded encoded string is same for %"" + s + "%"", u ~ e) end - note copyright: "2011-2011, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" From 4b497060a0da0c36b1c6de462724fe566eefc344 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 10 Apr 2014 15:28:19 +0200 Subject: [PATCH 20/41] Added an example to embed EWF nino service into a Vision2 desktop application. This is locally consumed via the embedded web browser component. --- examples/desktop_app/README.md | 2 + examples/desktop_app/desktop_app.ecf | 28 +++ examples/desktop_app/files/index.html | 1 + examples/desktop_app/home.html | 32 +++ .../src/app_embedded_web_service.e | 230 ++++++++++++++++++ examples/desktop_app/src/desktop_app.e | 71 ++++++ examples/desktop_app/src/main_window.e | 202 +++++++++++++++ .../src/service/embedded_web_service.e | 114 +++++++++ .../shared_embeded_web_service_information.e | 27 ++ 9 files changed, 707 insertions(+) create mode 100644 examples/desktop_app/README.md create mode 100644 examples/desktop_app/desktop_app.ecf create mode 100644 examples/desktop_app/files/index.html create mode 100644 examples/desktop_app/home.html create mode 100644 examples/desktop_app/src/app_embedded_web_service.e create mode 100644 examples/desktop_app/src/desktop_app.e create mode 100644 examples/desktop_app/src/main_window.e create mode 100644 examples/desktop_app/src/service/embedded_web_service.e create mode 100644 examples/desktop_app/src/service/shared_embeded_web_service_information.e diff --git a/examples/desktop_app/README.md b/examples/desktop_app/README.md new file mode 100644 index 00000000..f9054fb4 --- /dev/null +++ b/examples/desktop_app/README.md @@ -0,0 +1,2 @@ +This example demonstrates the use of embedded Vision2 web browser component, and embedded EWF server (using nino). + diff --git a/examples/desktop_app/desktop_app.ecf b/examples/desktop_app/desktop_app.ecf new file mode 100644 index 00000000..78fd6e17 --- /dev/null +++ b/examples/desktop_app/desktop_app.ecf @@ -0,0 +1,28 @@ + + + Vision2+web browser widget+embedded web service + + This example demonstrates how to build a vision2 desktop application that embed a web browser accessing the service of an embedded web service. + + + + + + + + + + + + + + + /EIFGENs$ + /CVS$ + /.svn$ + + + + diff --git a/examples/desktop_app/files/index.html b/examples/desktop_app/files/index.html new file mode 100644 index 00000000..345e6aef --- /dev/null +++ b/examples/desktop_app/files/index.html @@ -0,0 +1 @@ +Test diff --git a/examples/desktop_app/home.html b/examples/desktop_app/home.html new file mode 100644 index 00000000..26e7c6e2 --- /dev/null +++ b/examples/desktop_app/home.html @@ -0,0 +1,32 @@ + + + + + + +

    This is a local file test with js

  • back to home
  • Let AJAX change this text

    + +
    + diff --git a/examples/desktop_app/src/app_embedded_web_service.e b/examples/desktop_app/src/app_embedded_web_service.e new file mode 100644 index 00000000..e9ebed88 --- /dev/null +++ b/examples/desktop_app/src/app_embedded_web_service.e @@ -0,0 +1,230 @@ +note + description: "Summary description for {APP_EMBEDDED_WEB_SERVICE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + APP_EMBEDDED_WEB_SERVICE + +inherit + EMBEDDED_WEB_SERVICE + redefine + make + end + +create + make + +feature {NONE} -- Initialization + + make + do + Precursor + create request_exit_operation_actions + local_connection_restriction_enabled := True + end + +feature -- Execution + + request_exit_operation_actions: ACTION_SEQUENCE [TUPLE] + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the request + -- See `req.input' for input stream + -- `req.meta_variables' for the CGI meta variable + -- and `res' for output buffer + local + router: WSF_ROUTER + sess: detachable WSF_ROUTER_SESSION + m: WSF_HTML_PAGE_RESPONSE + b: STRING + fs: WSF_FILE_SYSTEM_HANDLER + do + create router.make (3) + router.handle ("/test/{var}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_test)) + router.handle ("/env", create {WSF_URI_AGENT_HANDLER}.make (agent handle_env)) + router.handle ("/exit", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent handle_exit)) + create fs.make_with_path ((create {EXECUTION_ENVIRONMENT}).current_working_path.extended ("files")) + router.handle ("/files", fs) + create sess + router.dispatch (req, res, sess) + if not sess.dispatched then + create m.make + create b.make_from_string ("

    Hello Eiffel desktop user

    ") + b.append ("
  • test
  • ") + b.append ("
  • env
  • ") + b.append ("
  • files
  • ") + b.append ("
  • exit
  • ") + m.set_body (b) + res.send (m) + end + end + + handle_test (req: WSF_REQUEST; res: WSF_RESPONSE) + local + m: WSF_HTML_PAGE_RESPONSE + b: STRING + l_name: READABLE_STRING_32 + do + if attached {WSF_STRING} req.item ("var") as p_name then + l_name := p_name.value + else + l_name := {STRING_32} "Embedded web service and web_browser in vision2 application" + end + create m.make + create b.make_from_string ("

    This is a test about "+ m.html_encoded_string (l_name) +"

    ") + b.append ("
  • back to home
  • ") + if l_name.is_case_insensitive_equal_general ("start") then + b.append ("
  • test javascript+ajax
  • ") + elseif l_name.is_case_insensitive_equal_general ("js") then + b.append ("[ +

    Let AJAX change this text

    + +
    + ]") + m.add_javascript_content ("[ + function loadXMLDoc() + { + var xmlhttp; + if (window.XMLHttpRequest) + {// code for IE7+, Firefox, Chrome, Opera, Safari + xmlhttp=new XMLHttpRequest(); + } + else + {// code for IE6, IE5 + xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); + } + xmlhttp.onreadystatechange=function() + { + if (xmlhttp.readyState==4 && xmlhttp.status==200) + { + document.getElementById("myDiv").innerHTML=xmlhttp.responseText; + } + } + xmlhttp.open("GET","/test/ajax.txt",true); + xmlhttp.send(); + } + ]") + elseif l_name.is_case_insensitive_equal_general ("ajax.txt") then + b := "This is AJAX response ... from " + req.absolute_script_url ("") + end + m.set_body (b) + res.send (m) + end + + handle_env (req: WSF_REQUEST; res: WSF_RESPONSE) + local + s: STRING_8 + p: WSF_PAGE_RESPONSE + v: STRING_8 + do + create s.make (2048) + s.append ("**DEBUG**%N") + req.set_raw_input_data_recorded (True) + + append_iterable_to ("Meta variables:", req.meta_variables, s) + s.append_character ('%N') + + append_iterable_to ("Path parameters", req.path_parameters, s) + s.append_character ('%N') + + append_iterable_to ("Query parameters", req.query_parameters, s) + s.append_character ('%N') + + append_iterable_to ("Form parameters", req.form_parameters, s) + s.append_character ('%N') + + if attached req.content_type as l_type then + s.append ("Content: type=" + l_type.debug_output) + s.append (" length=") + s.append_natural_64 (req.content_length_value) + s.append_character ('%N') + create v.make (req.content_length_value.to_integer_32) + req.read_input_data_into (v) + across + v.split ('%N') as v_cursor + loop + s.append (" |") + s.append (v_cursor.item) + s.append_character ('%N') + end + end + + create p.make_with_body (s) + p.header.put_content_type_text_plain + res.send (p) + end + + handle_exit (req: WSF_REQUEST; res: WSF_RESPONSE) + local + m: WSF_HTML_PAGE_RESPONSE + b: STRING + do + create m.make + create b.make_from_string ("

    Embedded server is about to shutdown

    ") + b.append ("
  • back to home
  • ") + m.set_body (b) + res.send (m) + if attached {WGI_NINO_CONNECTOR} req.wgi_connector as nino then + nino.server.shutdown_server + end + request_exit_operation_actions.call (Void) + end + +feature {NONE} -- Implementation + + append_iterable_to (a_title: READABLE_STRING_8; it: detachable ITERABLE [WSF_VALUE]; s: STRING_8) + local + n: INTEGER + t: READABLE_STRING_8 + v: READABLE_STRING_8 + do + s.append (a_title) + s.append_character (':') + if it /= Void then + across it as c loop + n := n + 1 + end + if n = 0 then + s.append (" empty") + s.append_character ('%N') + else + s.append_character ('%N') + across + it as c + loop + s.append (" - ") + s.append (c.item.url_encoded_name) + t := c.item.generating_type + if t.same_string ("WSF_STRING") then + else + s.append_character (' ') + s.append_character ('{') + s.append (t) + s.append_character ('}') + end + s.append_character ('=') + v := c.item.string_representation.as_string_8 + if v.has ('%N') then + s.append_character ('%N') + across + v.split ('%N') as v_cursor + loop + s.append (" |") + s.append (v_cursor.item) + s.append_character ('%N') + end + else + s.append (v) + s.append_character ('%N') + end + end + end + else + s.append (" none") + s.append_character ('%N') + end + end + +end diff --git a/examples/desktop_app/src/desktop_app.e b/examples/desktop_app/src/desktop_app.e new file mode 100644 index 00000000..85aaf27c --- /dev/null +++ b/examples/desktop_app/src/desktop_app.e @@ -0,0 +1,71 @@ +note + description: "Objects that represent the Vision2 application.% + %The original version of this class has been generated by EiffelBuild." + generator: "EiffelBuild" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date: 2012-09-29 01:29:13 +0200 (sam., 29 sept. 2012) $" + revision: "$Revision: 89488 $" + + +class + DESKTOP_APP + +inherit + EV_APPLICATION + +create + make_and_launch + +feature {NONE} -- Initialization + + make_and_launch + -- Create `Current', build and display `main_window', + -- then launch the application. + local + l_win: like main_window + l_embeded_services: APP_EMBEDDED_WEB_SERVICE + do + default_create + create l_win.make + main_window := l_win + l_win.show + create l_embeded_services.make + l_embeded_services.set_port_number (0) -- Use first available port number + + l_embeded_services.on_launched_actions.force (agent on_web_service_launched (l_win)) + l_embeded_services.request_exit_operation_actions.force (agent on_quit) + l_embeded_services.launch + launch + end + + on_quit + do + if attached main_window as win then + win.destroy_and_exit_if_last + end + end + + on_web_service_launched (a_win: attached like main_window) + do + add_idle_action_kamikaze (agent a_win.open_link) + end + +feature {NONE} -- Implementation + + main_window: detachable MAIN_WINDOW + -- Main window of `Current' + +;note + copyright: "Copyright (c) 1984-2009, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 356 Storke Road, 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/examples/desktop_app/src/main_window.e b/examples/desktop_app/src/main_window.e new file mode 100644 index 00000000..b259db99 --- /dev/null +++ b/examples/desktop_app/src/main_window.e @@ -0,0 +1,202 @@ +note + description: "Objects that represent an EV_TITLED_WINDOW.% + %The original version of this class was generated by EiffelBuild." + generator: "EiffelBuild" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date: 2010-08-17 10:49:12 +0200 (mar., 17 août 2010) $" + revision: "$Revision: 84189 $" + +class + MAIN_WINDOW + +inherit + EV_TITLED_WINDOW + redefine + create_interface_objects, initialize, is_in_default_state + end + + SHARED_EMBEDED_WEB_SERVICE_INFORMATION + undefine + default_create, copy + end + +create + make + +feature {NONE} -- Initialization + + make + -- Creation method + do + default_create + end + + initialize + -- Initialize `Current'. + do + Precursor {EV_TITLED_WINDOW} + + set_title ("Desktop Application (demo embedded EWF+browser)") + + -- Connect events. + -- Close the application when an interface close + -- request is received on `Current'. i.e. the cross is clicked. + close_request_actions.extend (agent destroy_and_exit_if_last) + + -- Call `user_initialization'. + user_initialization + end + + create_interface_objects + -- Create objects + do + create home_button.make_with_text ("Home") + create back_button.make_with_text ("Back") + create forth_button.make_with_text ("Forth") + create refresh_button.make_with_text ("Refresh") + create stop_button.make_with_text ("Stop") + create url_text_field.make_with_text ("http://localhost:" + port_number.out) + create go_button.make_with_text ("Go") + + create web_browser + end + + user_initialization + -- Called by `initialize'. + -- Any custom user initialization that + -- could not be performed in `initialize', + -- (due to regeneration of implementation class) + -- can be added here. + local + l_browser_box: EV_VERTICAL_BOX + l_server_box: EV_VERTICAL_BOX + l_hor_box: EV_HORIZONTAL_BOX + vb: EV_VERTICAL_BOX + do + set_size (800, 600) + + create vb + extend (vb) + vb.set_border_width (3) + vb.set_padding_width (3) + + -- browser part + create l_browser_box + + create l_hor_box + l_browser_box.extend (l_hor_box) + l_browser_box.disable_item_expand (l_hor_box) + + home_button.select_actions.force_extend (agent on_home_button_action) + l_hor_box.extend (home_button) + l_hor_box.disable_item_expand (home_button) + + back_button.select_actions.force_extend (agent on_back_button_action) + l_hor_box.extend (back_button) + l_hor_box.disable_item_expand (back_button) + + forth_button.select_actions.force_extend (agent on_forth_button_action) + l_hor_box.extend (forth_button) + l_hor_box.disable_item_expand (forth_button) + + refresh_button.select_actions.force_extend (agent on_refresh_button_action) + l_hor_box.extend (refresh_button) + l_hor_box.disable_item_expand (refresh_button) + + stop_button.select_actions.force_extend (agent on_stop_button_action) + l_hor_box.extend (stop_button) + l_hor_box.disable_item_expand (stop_button) + + l_hor_box.extend (url_text_field) + + go_button.select_actions.force_extend (agent on_go_button_action) + l_hor_box.extend (go_button) + l_hor_box.disable_item_expand (go_button) + + l_browser_box.extend (web_browser) + + -------------------- + vb.extend (l_browser_box) + end + + is_in_default_state: BOOLEAN + do + Result := True + end + +feature -- Basic operation + + open_link + do + url_text_field.set_text ("http://localhost:" + port_number.out) + on_go_button_action + end + +feature {NONE} -- Implementation + + home_button, go_button, back_button, forth_button, stop_button, refresh_button: EV_BUTTON + -- Buttons + + url_text_field: EV_TEXT_FIELD + -- URL text field + + on_go_button_action + -- Action for `go_button' + local + l_uri: STRING_32 + do + l_uri := url_text_field.text + if l_uri /= Void and then not l_uri.is_empty then + web_browser.load_uri (l_uri) + else + on_home_button_action + end + end + + on_home_button_action + -- Action for `home_button' + do + web_browser.load_uri ("http://localhost:" + port_number.out) + end + + on_back_button_action + -- Action for `back_button' + do + web_browser.back + end + + on_forth_button_action + -- Action for `forth_button' + do + web_browser.forth + end + + on_refresh_button_action + -- Action for `refresh_button' + do + web_browser.refresh + end + + on_stop_button_action + -- Action for `stop_button' + do + web_browser.stop + end + + web_browser: EV_WEB_BROWSER + -- Web browser widget + +;note + copyright: "Copyright (c) 1984-2009, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 356 Storke Road, 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/examples/desktop_app/src/service/embedded_web_service.e b/examples/desktop_app/src/service/embedded_web_service.e new file mode 100644 index 00000000..995f2393 --- /dev/null +++ b/examples/desktop_app/src/service/embedded_web_service.e @@ -0,0 +1,114 @@ +note + description: "Summary description for {EMBEDDED_WEB_SERVICE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + EMBEDDED_WEB_SERVICE + +inherit + THREAD + rename + make as make_thread, + execute as execute_thread + end + + WSF_SERVICE + rename + execute as execute_embedded + end + + SHARED_EMBEDED_WEB_SERVICE_INFORMATION + +feature -- Initialization + + make + do + make_thread + create on_launched_actions + end + +feature {NONE} -- Execution + + execute_embedded (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the request + -- See `req.input' for input stream + -- `req.meta_variables' for the CGI meta variable + -- and `res' for output buffer + local + filter: WSF_AGENT_FILTER + m: WSF_PAGE_RESPONSE + do + if local_connection_restriction_enabled then + if + attached req.remote_addr as l_remote_addr and then + l_remote_addr.is_case_insensitive_equal_general ("127.0.0.1") + then + execute (req, res) + else + create m.make_with_body ("Only local connection is allowed") + m.set_status_code (403) -- Forbidden + res.send (m) + end + else + execute (req, res) + end + end + + execute_thread + local + nino: WSF_NINO_SERVICE_LAUNCHER + opts: WSF_SERVICE_LAUNCHER_OPTIONS + do + create opts.default_create + opts.set_verbose (True) + opts.set_option ("port", port_number) + create nino.make (Current, opts) + nino.on_launched_actions.force (agent on_launched) + nino.launch + end + + execute (req: WSF_REQUEST; res: WSF_RESPONSE) + -- Execute the request + -- See `req.input' for input stream + -- `req.meta_variables' for the CGI meta variable + -- and `res' for output buffer + deferred + end + + on_launched (conn: WGI_CONNECTOR) + do + if attached {WGI_NINO_CONNECTOR} conn as nino then + set_port_number (nino.port) + end + on_launched_actions.call (Void) + end + +feature -- Control + + wait + -- Wait for server to be terminated. + do + join + end + +feature -- Access + + on_launched_actions: ACTION_SEQUENCE [TUPLE] + +feature -- Status report + + local_connection_restriction_enabled: BOOLEAN + -- Accept only local connection? + --| based on 127.0.0.1 IP + --| TO IMPROVE + +feature -- Change + + set_local_connection_restriction_enabled (b: BOOLEAN) + do + local_connection_restriction_enabled := b + end + +end diff --git a/examples/desktop_app/src/service/shared_embeded_web_service_information.e b/examples/desktop_app/src/service/shared_embeded_web_service_information.e new file mode 100644 index 00000000..ba04e855 --- /dev/null +++ b/examples/desktop_app/src/service/shared_embeded_web_service_information.e @@ -0,0 +1,27 @@ +note + description: "Summary description for {SHARED_EMBEDED_WEB_SERVICE_INFORMATION}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + SHARED_EMBEDED_WEB_SERVICE_INFORMATION + +feature -- Access + + port_number: INTEGER + do + Result := port_number_cell.item + end + + set_port_number (a_port: like port_number) + do + port_number_cell.replace (a_port) + end + + port_number_cell: CELL [INTEGER] + once ("process") + create Result.put (0) + end + +end From 7f3ece2da9ef5cebfb048a753e7e8def8c3e6e44 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 17 Apr 2014 07:23:33 -0700 Subject: [PATCH 21/41] Updated Documentation__Request (markdown) --- Documentation__Request.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation__Request.md b/Documentation__Request.md index 65bf60b5..90c093bd 100644 --- a/Documentation__Request.md +++ b/Documentation__Request.md @@ -1,5 +1,6 @@ See WSF_REQUEST +== About parameters == Note that by default there is a smart computation for the query/post/... parameters: for instance - `q=a&q=b` : will create a **WSF_MULTIPLE_STRING** parameter with name **q** and value `[a,b]` @@ -7,4 +8,10 @@ for instance - `tab[]=ewf&tab[]=demo` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "1": "ewf", "2": "demo"}` - `tab[foo]=foo&tab[foo]=bar` : will create a **WSF_TABLE** parameter with name **tab** and value `{ "foo": "bar"}` **WARNING: only the last `tab[foo]` is kept**. -Those rules are applied to query, post, path, .... parameters. \ No newline at end of file +Those rules are applied to query, post, path, .... parameters. + +== How to get the input data (i.e entity-body) ?== +See {WSF_REQUEST}.read_input_data_into (buf: STRING) + +== How to get the raw header data (i.e the http header text) ?== +See {WSF_REQUEST}.raw_header_data: detachable READABLE_STRING_32 From d089921ef585f2ec60c66b583eed5ba860b1a017 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 17 Apr 2014 07:23:54 -0700 Subject: [PATCH 22/41] Updated Documentation__Request (markdown) --- Documentation__Request.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation__Request.md b/Documentation__Request.md index 90c093bd..e7719509 100644 --- a/Documentation__Request.md +++ b/Documentation__Request.md @@ -1,6 +1,6 @@ See WSF_REQUEST -== About parameters == +## About parameters Note that by default there is a smart computation for the query/post/... parameters: for instance - `q=a&q=b` : will create a **WSF_MULTIPLE_STRING** parameter with name **q** and value `[a,b]` @@ -10,8 +10,8 @@ for instance Those rules are applied to query, post, path, .... parameters. -== How to get the input data (i.e entity-body) ?== +## How to get the input data (i.e entity-body) ? See {WSF_REQUEST}.read_input_data_into (buf: STRING) -== How to get the raw header data (i.e the http header text) ?== +## How to get the raw header data (i.e the http header text) ? See {WSF_REQUEST}.raw_header_data: detachable READABLE_STRING_32 From 9db9a4957acb25698a4f71e55c9735b8e9f09b74 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 17 Apr 2014 07:24:13 -0700 Subject: [PATCH 23/41] Updated Documentation__Request (markdown) --- Documentation__Request.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation__Request.md b/Documentation__Request.md index e7719509..379df595 100644 --- a/Documentation__Request.md +++ b/Documentation__Request.md @@ -11,7 +11,7 @@ for instance Those rules are applied to query, post, path, .... parameters. ## How to get the input data (i.e entity-body) ? -See {WSF_REQUEST}.read_input_data_into (buf: STRING) +See `{WSF_REQUEST}.read_input_data_into (buf: STRING)` ## How to get the raw header data (i.e the http header text) ? -See {WSF_REQUEST}.raw_header_data: detachable READABLE_STRING_32 +See `{WSF_REQUEST}.raw_header_data: detachable READABLE_STRING_32` From d45cd032a72ef65f55e64af0bee71113fc9b3edb Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 22 Apr 2014 09:59:18 +0200 Subject: [PATCH 24/41] Corrected support of https request in `server_url' (and callers). Added query `is_https' to indicate if the request is done via a https connection or not. --- library/server/wsf/src/wsf_request.e | 37 +++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index 06aec84f..96747207 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -16,6 +16,9 @@ note And also has execution_variable (a_name: READABLE_STRING_GENERAL): detachable ANY --| to keep value attached to the request + + About https support: `is_https' precises if the request is made through an https connection or not. + ]" date: "$Date$" revision: "$Revision$" @@ -120,6 +123,21 @@ feature {NONE} -- Initialization if meta_variable ({WSF_META_NAMES}.request_time) = Void then set_meta_string_variable ({WSF_META_NAMES}.request_time, date_time_utilities.unix_time_stamp (Void).out) end + + --| HTTPS support + if attached meta_string_variable ("REQUEST_SCHEME") as l_scheme and then not l_scheme.is_empty then + is_https := l_scheme.is_case_insensitive_equal_general ("https") + elseif attached execution_environment.item ("HTTPS") as l_https and then not l_https.is_empty then + is_https := l_https.is_case_insensitive_equal_general ("on") + or else l_https.is_case_insensitive_equal_general ("yes") + or else l_https.is_case_insensitive_equal_general ("true") + or else l_https.is_case_insensitive_equal_general ("1") + --| Usually, if not empty, this means this is https + --| but it occurs that server (like IIS) sets "off" when this is NOT https + --| so, let's be flexible, and accepts other variants of "on" + else + check is_not_https: is_https = False end + end end wgi_request: WGI_REQUEST @@ -160,6 +178,10 @@ feature -- Destroy feature -- Status report + is_https: BOOLEAN + -- Is https scheme or protocol? + --| based on REQUEST_SCHEME, or environment variable HTTPS=on + debug_output: STRING_8 do create Result.make_from_string (request_method + " " + request_uri) @@ -1738,10 +1760,7 @@ feature -- URL Utility do s := internal_server_url if s = Void then - if - server_protocol.count >= 5 and then - server_protocol.substring (1, 5).is_case_insensitive_equal ("https") - then + if is_https then create s.make_from_string ("https://") else create s.make_from_string ("http://") @@ -1749,8 +1768,14 @@ feature -- URL Utility s.append (server_name) p := server_port if p > 0 then - s.append_character (':') - s.append_integer (p) + if is_https and p = 443 then + -- :443 is default for https, so no need to put it + elseif not is_https and p = 80 then + -- :80 is default for http, so no need to put it + else + s.append_character (':') + s.append_integer (p) + end end end Result := s From 161607cf8a741cdc162ed444186c5f5f1a7fcc0a Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 22 Apr 2014 10:01:27 +0200 Subject: [PATCH 25/41] better comments. --- library/server/wsf/src/wsf_request.e | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index 96747207..c6646853 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -17,7 +17,7 @@ note execution_variable (a_name: READABLE_STRING_GENERAL): detachable ANY --| to keep value attached to the request - About https support: `is_https' precises if the request is made through an https connection or not. + About https support: `is_https' indicates if the request is made through an https connection or not. ]" date: "$Date$" From 7168941495cc769c344eb1f381fa0389be13e7d6 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 22 Apr 2014 10:09:03 +0200 Subject: [PATCH 26/41] is_https should not rely on REQUEST_SCHEME which may still be "http" for SSL connection. --- library/server/wsf/src/wsf_request.e | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index c6646853..0d603d3e 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -125,9 +125,7 @@ feature {NONE} -- Initialization end --| HTTPS support - if attached meta_string_variable ("REQUEST_SCHEME") as l_scheme and then not l_scheme.is_empty then - is_https := l_scheme.is_case_insensitive_equal_general ("https") - elseif attached execution_environment.item ("HTTPS") as l_https and then not l_https.is_empty then + if attached meta_string_variable ("HTTPS") as l_https and then not l_https.is_empty then is_https := l_https.is_case_insensitive_equal_general ("on") or else l_https.is_case_insensitive_equal_general ("yes") or else l_https.is_case_insensitive_equal_general ("true") @@ -179,8 +177,8 @@ feature -- Destroy feature -- Status report is_https: BOOLEAN - -- Is https scheme or protocol? - --| based on REQUEST_SCHEME, or environment variable HTTPS=on + -- Is https connection? + --| based on meta variable HTTPS=on . debug_output: STRING_8 do From 322fd80f40ba09a63c45ca0cf76984b266199f51 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 22 Apr 2014 10:18:16 +0200 Subject: [PATCH 27/41] Be sure to reset `is_https' to False, in case the wsf_request object is reused by the implementation. --- library/server/wsf/src/wsf_request.e | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index 0d603d3e..35b4286e 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -125,6 +125,7 @@ feature {NONE} -- Initialization end --| HTTPS support + is_https := False if attached meta_string_variable ("HTTPS") as l_https and then not l_https.is_empty then is_https := l_https.is_case_insensitive_equal_general ("on") or else l_https.is_case_insensitive_equal_general ("yes") @@ -172,6 +173,7 @@ feature -- Destroy raw_input_data_recorded := False request_method := empty_string_8 set_uploaded_file_path (Void) + is_https := False end feature -- Status report From a74cda2f33bc46b23749b3d5c16fa040f4074e52 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 22 Apr 2014 15:45:31 +0200 Subject: [PATCH 28/41] Added support for UTF-8 during decoding. The JSON specification does not require it, but some json encoders are using utf-8 encoding for json encoding. Added related autotest case. --- library/kernel/json_string.e | 272 +++++++++++++-------- test/autotest/test_suite/test_json_suite.e | 23 +- 2 files changed, 194 insertions(+), 101 deletions(-) diff --git a/library/kernel/json_string.e b/library/kernel/json_string.e index 4cc190ee..89ec4648 100644 --- a/library/kernel/json_string.e +++ b/library/kernel/json_string.e @@ -61,117 +61,29 @@ feature -- Access item: STRING -- Contents with escaped entities if any +feature -- Conversion + unescaped_string_8: STRING_8 - -- Unescaped string from `item' + -- Unescaped string from `item'. + --| note: valid only if `item' does not encode any unicode character. local s: like item - i, n: INTEGER - c: CHARACTER do s := item - n := s.count - create Result.make (n) - from i := 1 until i > n loop - c := s[i] - if c = '\' then - if i < n then - inspect s[i+1] - when '\' then - Result.append_character ('\') - i := i + 2 - when '%"' then - Result.append_character ('%"') - i := i + 2 - when 'b' then - Result.append_character ('%B') - i := i + 2 - when 'f' then - Result.append_character ('%F') - i := i + 2 - when 'n' then - Result.append_character ('%N') - i := i + 2 - when 'r' then - Result.append_character ('%R') - i := i + 2 - when 't' then - Result.append_character ('%T') - i := i + 2 - when 'u' then - --| Leave Unicode \uXXXX unescaped - Result.append_character ('\') - i := i + 1 - else - Result.append_character ('\') - i := i + 1 - end - else - Result.append_character ('\') - i := i + 1 - end - else - Result.append_character (c) - i := i + 1 - end - end + create Result.make (s.count) + unescape_to_string_8 (Result) end unescaped_string_32: STRING_32 -- Unescaped string 32 from `item' + --| some encoders uses UTF-8 , and not the recommended pure json encoding + --| thus, let's support the UTF-8 encoding during decoding. local - s: like item - i, n: INTEGER - c: CHARACTER - hex: STRING + s: READABLE_STRING_8 do s := item - n := s.count - create Result.make (n) - from i := 1 until i > n loop - c := s[i] - if c = '\' then - if i < n then - inspect s[i+1] - when '\' then - Result.append_character ('\') - i := i + 2 - when '%"' then - Result.append_character ('%"') - i := i + 2 - when 'b' then - Result.append_character ('%B') - i := i + 2 - when 'f' then - Result.append_character ('%F') - i := i + 2 - when 'n' then - Result.append_character ('%N') - i := i + 2 - when 'r' then - Result.append_character ('%R') - i := i + 2 - when 'T' then - Result.append_character ('%T') - i := i + 2 - when 'u' then - hex := s.substring (i+2, i+2+4 - 1) - if hex.count = 4 then - Result.append_code (hexadecimal_to_natural_32 (hex)) - end - i := i + 2 + 4 - else - Result.append_character ('\') - i := i + 1 - end - else - Result.append_character ('\') - i := i + 1 - end - else - Result.append_character (c.to_character_32) - i := i + 1 - end - end + create Result.make (s.count) + unescape_to_string_32 (Result) end representation: STRING @@ -183,6 +95,156 @@ feature -- Access Result.append_character ('%"') end + unescape_to_string_8 (a_output: STRING_8) + -- Unescape string `item' into `a_output'. + --| note: valid only if `item' does not encode any unicode character. + local + s: like item + i, n: INTEGER + c: CHARACTER + do + s := item + n := s.count + from i := 1 until i > n loop + c := s[i] + if c = '\' then + if i < n then + inspect s[i+1] + when '\' then + a_output.append_character ('\') + i := i + 2 + when '%"' then + a_output.append_character ('%"') + i := i + 2 + when 'b' then + a_output.append_character ('%B') + i := i + 2 + when 'f' then + a_output.append_character ('%F') + i := i + 2 + when 'n' then + a_output.append_character ('%N') + i := i + 2 + when 'r' then + a_output.append_character ('%R') + i := i + 2 + when 't' then + a_output.append_character ('%T') + i := i + 2 + when 'u' then + --| Leave Unicode \uXXXX unescaped + a_output.append_character ('\') + i := i + 1 + else + a_output.append_character ('\') + i := i + 1 + end + else + a_output.append_character ('\') + i := i + 1 + end + else + a_output.append_character (c) + i := i + 1 + end + end + end + + unescape_to_string_32 (a_output: STRING_32) + -- Unescape string `item' into `a_output' string 32. + --| some encoders uses UTF-8 , and not the recommended pure json encoding + --| thus, let's support the UTF-8 encoding during decoding. + local + s: READABLE_STRING_8 + i, n: INTEGER + c: NATURAL_32 + ch: CHARACTER_8 + hex: READABLE_STRING_8 + do + s := item + n := s.count + from i := 1 until i > n loop + ch := s.item (i) + if ch = '\' then + if i < n then + inspect s[i+1] + when '\' then + a_output.append_character ('\') + i := i + 2 + when '%"' then + a_output.append_character ('%"') + i := i + 2 + when 'b' then + a_output.append_character ('%B') + i := i + 2 + when 'f' then + a_output.append_character ('%F') + i := i + 2 + when 'n' then + a_output.append_character ('%N') + i := i + 2 + when 'r' then + a_output.append_character ('%R') + i := i + 2 + when 't' then + a_output.append_character ('%T') + i := i + 2 + when 'u' then + hex := s.substring (i + 2, i + 5) -- i+2 , i+2+4-1 + if hex.count = 4 then + a_output.append_code (hexadecimal_to_natural_32 (hex)) + end + i := i + 6 -- i +2 +4 + else + a_output.append_character ('\') + i := i + 1 + end + else + a_output.append_character ('\') + i := i + 1 + end + else + c := ch.natural_32_code + if c <= 0x7F then + -- 0xxxxxxx + check ch = c.to_character_32 end + a_output.append_character (ch) + elseif c <= 0xDF then + -- 110xxxxx 10xxxxxx + i := i + 1 + if i <= n then + a_output.append_code ( + ((c & 0x1F) |<< 6) | + (s.code (i) & 0x3F) + ) + end + elseif c <= 0xEF then + -- 1110xxxx 10xxxxxx 10xxxxxx + i := i + 2 + if i <= n then + a_output.append_code ( + ((c & 0xF) |<< 12) | + ((s.code (i - 1) & 0x3F) |<< 6) | + (s.code (i) & 0x3F) + ) + end + elseif c <= 0xF7 then + -- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + i := i + 3 + if i <= n then + a_output.append_code ( + ((c & 0x7) |<< 18) | + ((s.code (i - 2) & 0x3F) |<< 12) | + ((s.code (i - 1) & 0x3F) |<< 6) | + (s.code (i) & 0x3F) + ) + end + end + i := i + 1 + end + end + end + feature -- Visitor pattern accept (a_visitor: JSON_VISITOR) @@ -231,8 +293,18 @@ feature {NONE} -- Implementation is_hexadecimal (s: READABLE_STRING_8): BOOLEAN -- Is `s' an hexadecimal value? + local + i: INTEGER do - Result := across s as scur all scur.item.is_hexa_digit end + from + Result := True + i := 1 + until + i > s.count or not Result + loop + Result := s[i].is_hexa_digit + i := i + 1 + end end hexadecimal_to_natural_32 (s: READABLE_STRING_8): NATURAL_32 diff --git a/test/autotest/test_suite/test_json_suite.e b/test/autotest/test_suite/test_json_suite.e index eb5df766..c2a66b71 100644 --- a/test/autotest/test_suite/test_json_suite.e +++ b/test/autotest/test_suite/test_json_suite.e @@ -1,4 +1,4 @@ -note +note description: "[ Eiffel tests that can be executed by testing tool. ]" @@ -62,6 +62,27 @@ feature -- Tests Pass end end + test_json_utf_8_pass1 + local + parse_json: like new_json_parser + utf: UTF_CONVERTER + s: READABLE_STRING_32 + do + s := {STRING_32} "{ %"nihaoma%": %"你好吗\t?%" }" + + parse_json := new_json_parser (utf.string_32_to_utf_8_string_8 (s)) + json_value := parse_json.parse_json + assert ("utf8.pass1.json", parse_json.is_parsed = True) + if + attached {JSON_OBJECT} json_value as jo and then + attached {JSON_STRING} jo.item ("nihaoma") as js + then + assert ("utf8.nihaoma", js.unescaped_string_32.same_string ({STRING_32} "你好吗%T?")) + else + assert ("utf8.nihaoma", False) + end + end + feature -- Tests Failures test_json_fail1 -- From d6b77e938bfeb2fe6538457a2918e2beac303641 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 22 Apr 2014 19:25:07 +0200 Subject: [PATCH 29/41] Added more tests for uri-template matching, especially with url that contains %2F i.e the percent encoded slash '/' --- .../tests/test_uri_template_matcher.e | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/library/text/parser/uri_template/tests/test_uri_template_matcher.e b/library/text/parser/uri_template/tests/test_uri_template_matcher.e index e0caf791..87604661 100644 --- a/library/text/parser/uri_template/tests/test_uri_template_matcher.e +++ b/library/text/parser/uri_template/tests/test_uri_template_matcher.e @@ -15,7 +15,7 @@ inherit feature -- Matcher - test_uri_template_matcher + test_uri_template_matcher_01 note testing: "uri-template" local @@ -80,6 +80,19 @@ feature -- Matcher end + test_uri_template_matcher_02 + note + testing: "uri-template" + local + tpl: URI_TEMPLATE + do + create tpl.make ("/test/{vars}") + uri_template_match (tpl, "/test/foo%%2Fbar", <<["vars", "foo%%2Fbar"]>>, <<>>) + + create tpl.make ("/test{/vars}") + uri_template_match (tpl, "/test/foo%%2Fbar/abc%%2Fdef", <<["vars", "/foo%%2Fbar/abc%%2Fdef"], ["vars[1]", "foo%%2Fbar"], ["vars[2]", "abc%%2Fdef"]>>, <<>>) + end + feature {NONE} -- Implementations uri_template_mismatch (a_uri_template: URI_TEMPLATE; a_uri: STRING) From fd66d79ecbd3434748401090fdcee0be3f76a519 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 22 Apr 2014 21:47:29 +0200 Subject: [PATCH 30/41] Updated ecf files toward complete void-safety Added iron package files. Added libfcgi files to compile .lib and .dll on Windows --- .../library/network/server/nino/nino-safe.ecf | 4 ++-- examples/restbucksCRUD/restbucks-safe.ecf | 6 ++++-- library/network/http_client/package.iron | 15 ++++++++++++++ .../protocol/content_negotiation/package.iron | 15 ++++++++++++++ library/network/protocol/http/package.iron | 15 ++++++++++++++ .../process/notification_email/package.iron | 15 ++++++++++++++ library/security/openid/package.iron | 16 +++++++++++++++ .../http_authorization/package.iron | 15 ++++++++++++++ .../ewsgi/connectors/nino/nino-safe.ecf | 10 +++++----- library/server/ewsgi/ewsgi-safe.ecf | 2 +- library/server/libfcgi/Clib/Makefile-win.SH | 3 +++ library/server/libfcgi/Clib/build_win32.bat | 1 + library/server/libfcgi/Clib/build_win64.bat | 1 + library/server/libfcgi/Clib/build_windows.bat | 20 +++++++++++++++++++ library/server/wsf/connector/nino-safe.ecf | 2 +- library/server/wsf/default/nino-safe.ecf | 2 +- library/server/wsf/wsf-safe.ecf | 4 ++-- library/server/wsf/wsf_policy_driven-safe.ecf | 10 +++++----- library/text/parser/uri_template/package.iron | 15 ++++++++++++++ library/utility/general/error/package.iron | 15 ++++++++++++++ 20 files changed, 167 insertions(+), 19 deletions(-) create mode 100644 library/network/http_client/package.iron create mode 100644 library/network/protocol/content_negotiation/package.iron create mode 100644 library/network/protocol/http/package.iron create mode 100644 library/runtime/process/notification_email/package.iron create mode 100644 library/security/openid/package.iron create mode 100644 library/server/authentication/http_authorization/package.iron create mode 100644 library/server/libfcgi/Clib/Makefile-win.SH create mode 100644 library/server/libfcgi/Clib/build_windows.bat create mode 100644 library/text/parser/uri_template/package.iron create mode 100644 library/utility/general/error/package.iron diff --git a/contrib/library/network/server/nino/nino-safe.ecf b/contrib/library/network/server/nino/nino-safe.ecf index b76c24df..9a2d69e9 100644 --- a/contrib/library/network/server/nino/nino-safe.ecf +++ b/contrib/library/network/server/nino/nino-safe.ecf @@ -1,5 +1,5 @@ - + @@ -8,7 +8,7 @@ /CVS$ /.svn$ - diff --git a/examples/restbucksCRUD/restbucks-safe.ecf b/examples/restbucksCRUD/restbucks-safe.ecf index 735a2e52..fc40603b 100644 --- a/examples/restbucksCRUD/restbucks-safe.ecf +++ b/examples/restbucksCRUD/restbucks-safe.ecf @@ -1,11 +1,13 @@ - + /EIFGENs$ /\.git$ /\.svn$ + @@ -14,8 +16,8 @@ - + diff --git a/library/network/http_client/package.iron b/library/network/http_client/package.iron new file mode 100644 index 00000000..72eed711 --- /dev/null +++ b/library/network/http_client/package.iron @@ -0,0 +1,15 @@ +package http_client + +project + http_client = "http_client-safe.ecf" + http_client = "http_client.ecf" + +note +-- title: +-- description: +-- tags: +-- license: +-- copyright: +-- link[doc]: "Documentation" http:// + +end diff --git a/library/network/protocol/content_negotiation/package.iron b/library/network/protocol/content_negotiation/package.iron new file mode 100644 index 00000000..b01d71f6 --- /dev/null +++ b/library/network/protocol/content_negotiation/package.iron @@ -0,0 +1,15 @@ +package content_negotiation + +project + conneg = "conneg-safe.ecf" + conneg = "conneg.ecf" + +note +-- title: +-- description: +-- tags: +-- license: +-- copyright: +-- link[doc]: "Documentation" http:// + +end diff --git a/library/network/protocol/http/package.iron b/library/network/protocol/http/package.iron new file mode 100644 index 00000000..b1045d62 --- /dev/null +++ b/library/network/protocol/http/package.iron @@ -0,0 +1,15 @@ +package http + +project + http = "http-safe.ecf" + http = "http.ecf" + +note +-- title: +-- description: +-- tags: +-- license: +-- copyright: +-- link[doc]: "Documentation" http:// + +end diff --git a/library/runtime/process/notification_email/package.iron b/library/runtime/process/notification_email/package.iron new file mode 100644 index 00000000..23279943 --- /dev/null +++ b/library/runtime/process/notification_email/package.iron @@ -0,0 +1,15 @@ +package notification_email + +project + notification_email = "notification_email-safe.ecf" + notification_email = "notification_email.ecf" + +note +-- title: +-- description: +-- tags: +-- license: +-- copyright: +-- link[doc]: "Documentation" http:// + +end diff --git a/library/security/openid/package.iron b/library/security/openid/package.iron new file mode 100644 index 00000000..9e5b27f9 --- /dev/null +++ b/library/security/openid/package.iron @@ -0,0 +1,16 @@ +package openid + +project + openid = "consumer/openid.ecf" + openid = "consumer/openid-safe.ecf" + demo = "consumer/demo/demo-safe.ecf" + +note + title: Eiffel OpenID + description: OpenID library (for now only consumer) + tags: openid,security + license: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt) +-- copyright: +-- link[doc]: "Documentation" http:// + +end diff --git a/library/server/authentication/http_authorization/package.iron b/library/server/authentication/http_authorization/package.iron new file mode 100644 index 00000000..314ae856 --- /dev/null +++ b/library/server/authentication/http_authorization/package.iron @@ -0,0 +1,15 @@ +package http_authorization + +project + http_authorization = "http_authorization-safe.ecf" + http_authorization = "http_authorization.ecf" + +note +-- title: +-- description: +-- tags: +-- license: +-- copyright: +-- link[doc]: "Documentation" http:// + +end diff --git a/library/server/ewsgi/connectors/nino/nino-safe.ecf b/library/server/ewsgi/connectors/nino/nino-safe.ecf index b27dd0b2..effbfebe 100644 --- a/library/server/ewsgi/connectors/nino/nino-safe.ecf +++ b/library/server/ewsgi/connectors/nino/nino-safe.ecf @@ -1,5 +1,5 @@ - + @@ -7,13 +7,13 @@ /\.git$ /\.svn$ - - + - - + + diff --git a/library/server/ewsgi/ewsgi-safe.ecf b/library/server/ewsgi/ewsgi-safe.ecf index e2979ff7..adeb8d5e 100644 --- a/library/server/ewsgi/ewsgi-safe.ecf +++ b/library/server/ewsgi/ewsgi-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/libfcgi/Clib/Makefile-win.SH b/library/server/libfcgi/Clib/Makefile-win.SH new file mode 100644 index 00000000..9eef0e8b --- /dev/null +++ b/library/server/libfcgi/Clib/Makefile-win.SH @@ -0,0 +1,3 @@ +all:: + build_win64.bat + diff --git a/library/server/libfcgi/Clib/build_win32.bat b/library/server/libfcgi/Clib/build_win32.bat index 2eae0679..2ef8e1e0 100644 --- a/library/server/libfcgi/Clib/build_win32.bat +++ b/library/server/libfcgi/Clib/build_win32.bat @@ -16,3 +16,4 @@ link.exe %LINK_FLAGS% /DLL %E_libFCGI_OUTDIR%\fcgi_stdio.obj %E_libFCGI_OUTDIR% copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\windows\msc endlocal +exit 0 diff --git a/library/server/libfcgi/Clib/build_win64.bat b/library/server/libfcgi/Clib/build_win64.bat index 303632cf..301db24a 100644 --- a/library/server/libfcgi/Clib/build_win64.bat +++ b/library/server/libfcgi/Clib/build_win64.bat @@ -16,3 +16,4 @@ link.exe %LINK_FLAGS% /DLL %E_libFCGI_OUTDIR%\fcgi_stdio.obj %E_libFCGI_OUTDIR% copy %E_libFCGI_OUTDIR%\libfcgi.* %~dp0..\spec\lib\win64\msc endlocal +exit 0 diff --git a/library/server/libfcgi/Clib/build_windows.bat b/library/server/libfcgi/Clib/build_windows.bat new file mode 100644 index 00000000..2c6f5424 --- /dev/null +++ b/library/server/libfcgi/Clib/build_windows.bat @@ -0,0 +1,20 @@ +setlocal +echo off + +if "%ISE_PLATFORM%" == "win64" goto build_win64 +goto build_win32 +goto end + +:build_win64 +echo Building libfcgi for win64 +%~dp0\build_win64.bat +goto end + +:build_win32 +echo Building libfcgi for win32 +%~dp0\build_win32.bat +goto end + +:end +endlocal +exit 0 diff --git a/library/server/wsf/connector/nino-safe.ecf b/library/server/wsf/connector/nino-safe.ecf index c7a77a3a..75bd218a 100644 --- a/library/server/wsf/connector/nino-safe.ecf +++ b/library/server/wsf/connector/nino-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/wsf/default/nino-safe.ecf b/library/server/wsf/default/nino-safe.ecf index 1aafe224..3d22d656 100644 --- a/library/server/wsf/default/nino-safe.ecf +++ b/library/server/wsf/default/nino-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/wsf/wsf-safe.ecf b/library/server/wsf/wsf-safe.ecf index 7cf1b0bf..7867df01 100644 --- a/library/server/wsf/wsf-safe.ecf +++ b/library/server/wsf/wsf-safe.ecf @@ -1,5 +1,5 @@ - + @@ -11,11 +11,11 @@ + - diff --git a/library/server/wsf/wsf_policy_driven-safe.ecf b/library/server/wsf/wsf_policy_driven-safe.ecf index 189fae62..46b47a4c 100644 --- a/library/server/wsf/wsf_policy_driven-safe.ecf +++ b/library/server/wsf/wsf_policy_driven-safe.ecf @@ -1,5 +1,5 @@ - + @@ -10,12 +10,12 @@ + + + + - - - - diff --git a/library/text/parser/uri_template/package.iron b/library/text/parser/uri_template/package.iron new file mode 100644 index 00000000..f9510205 --- /dev/null +++ b/library/text/parser/uri_template/package.iron @@ -0,0 +1,15 @@ +package uri_template + +project + uri_template = "uri_template-safe.ecf" + uri_template = "uri_template.ecf" + +note +-- title: +-- description: +-- tags: +-- license: +-- copyright: +-- link[doc]: "Documentation" http:// + +end diff --git a/library/utility/general/error/package.iron b/library/utility/general/error/package.iron new file mode 100644 index 00000000..3db2e673 --- /dev/null +++ b/library/utility/general/error/package.iron @@ -0,0 +1,15 @@ +package error + +project + error = "error-safe.ecf" + error = "error.ecf" + +note +-- title: +-- description: +-- tags: +-- license: +-- copyright: +-- link[doc]: "Documentation" http:// + +end From cb3de17be94e3a11dd0ae0191d46673245c2be3e Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 09:35:55 +0200 Subject: [PATCH 31/41] renamed HTTP_HEADER_BUILDER as HTTP_HEADER_MODIFIER --- library/network/protocol/http/src/http_header.e | 2 +- .../src/{http_header_builder.e => http_header_modifier.e} | 4 ++-- library/server/wsf/src/wsf_response.e | 2 +- library/server/wsf/src/wsf_response_header.e | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename library/network/protocol/http/src/{http_header_builder.e => http_header_modifier.e} (99%) diff --git a/library/network/protocol/http/src/http_header.e b/library/network/protocol/http/src/http_header.e index a162f9d4..db797f9a 100644 --- a/library/network/protocol/http/src/http_header.e +++ b/library/network/protocol/http/src/http_header.e @@ -25,7 +25,7 @@ class inherit ITERABLE [READABLE_STRING_8] - HTTP_HEADER_BUILDER + HTTP_HEADER_MODIFIER create make, diff --git a/library/network/protocol/http/src/http_header_builder.e b/library/network/protocol/http/src/http_header_modifier.e similarity index 99% rename from library/network/protocol/http/src/http_header_builder.e rename to library/network/protocol/http/src/http_header_modifier.e index 68970220..3af4e45b 100644 --- a/library/network/protocol/http/src/http_header_builder.e +++ b/library/network/protocol/http/src/http_header_modifier.e @@ -1,6 +1,6 @@ note description: "[ - The class provides an easy way to build HTTP header text + The class provides an easy way to build and modify HTTP header text thanks to add_header (..) and put_header (..) You will also find some helper features to help coding most common usages @@ -20,7 +20,7 @@ note revision: "$Revision$" deferred class - HTTP_HEADER_BUILDER + HTTP_HEADER_MODIFIER inherit ITERABLE [READABLE_STRING_8] diff --git a/library/server/wsf/src/wsf_response.e b/library/server/wsf/src/wsf_response.e index 2a81f7e3..977acd4a 100644 --- a/library/server/wsf/src/wsf_response.e +++ b/library/server/wsf/src/wsf_response.e @@ -172,7 +172,7 @@ feature {WSF_RESPONSE_EXPORTER} -- Header output operation feature -- Header access - header: HTTP_HEADER_BUILDER + header: HTTP_HEADER_MODIFIER -- Associated header builder interface. local res: like internal_response_header diff --git a/library/server/wsf/src/wsf_response_header.e b/library/server/wsf/src/wsf_response_header.e index 07e0a1f4..bb87b3f0 100644 --- a/library/server/wsf/src/wsf_response_header.e +++ b/library/server/wsf/src/wsf_response_header.e @@ -9,7 +9,7 @@ class WSF_RESPONSE_HEADER inherit - HTTP_HEADER_BUILDER + HTTP_HEADER_MODIFIER WSF_RESPONSE_EXPORTER -- to access WSF_RESPONSE.internal_header From 02f5a0968929d4afb5c93a411896e11701d555d8 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 09:36:22 +0200 Subject: [PATCH 32/41] Added comment to explain why conversion to string 8 is safe --- library/server/wsf/src/response/wsf_trace_response.e | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/server/wsf/src/response/wsf_trace_response.e b/library/server/wsf/src/response/wsf_trace_response.e index d4c2709e..4b08c80d 100644 --- a/library/server/wsf/src/response/wsf_trace_response.e +++ b/library/server/wsf/src/response/wsf_trace_response.e @@ -45,7 +45,7 @@ feature {WSF_RESPONSE} -- Output req := request if attached req.raw_header_data as l_header then create s.make (l_header.count) - s.append (l_header.to_string_8) + s.append (l_header.to_string_8) -- Is valid as string 8, as ensured by req.raw_header_data s.append_character ('%N') else create s.make_empty @@ -99,7 +99,7 @@ feature {WSF_RESPONSE} -- Output end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software From d158579bdcb9f42c0bd23d70ab0f698e26e1bf48 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 09:45:15 +0200 Subject: [PATCH 33/41] Replaced notion of session uuid by session id which is more generic (could be a uuid, or something else). Use STRING_TABLE for the implementation of session data container. Added a few missing comments. --- .../server/wsf/session/wsf_cookie_session.e | 16 ++++++--- .../wsf/session/wsf_fs_session_manager.e | 4 +-- library/server/wsf/session/wsf_session.e | 34 ++++++++++++++++--- library/server/wsf/session/wsf_session_data.e | 26 +++++++++++++- 4 files changed, 67 insertions(+), 13 deletions(-) diff --git a/library/server/wsf/session/wsf_cookie_session.e b/library/server/wsf/session/wsf_cookie_session.e index 4c9472f5..aced8b95 100644 --- a/library/server/wsf/session/wsf_cookie_session.e +++ b/library/server/wsf/session/wsf_cookie_session.e @@ -60,7 +60,8 @@ feature {NONE} -- Initialization feature -- Cookie - apply_to (h: HTTP_HEADER_BUILDER; a_request: WSF_REQUEST; a_path: detachable READABLE_STRING_8) + apply_to (h: HTTP_HEADER_MODIFIER; a_request: WSF_REQUEST; a_path: detachable READABLE_STRING_8) + -- local dt: detachable DATE_TIME l_domain: detachable READABLE_STRING_8 @@ -79,13 +80,18 @@ feature -- Cookie create dt.make_now_utc dt.day_add (40) end - h.put_cookie_with_expiration_date (cookie_name, uuid, dt, a_path, l_domain, False, True) + h.put_cookie_with_expiration_date (cookie_name, id, dt, a_path, l_domain, False, True) end end cookie_name: READABLE_STRING_8 -feature -- Access +feature -- Access + + id: READABLE_STRING_8 + do + Result := uuid + end uuid: READABLE_STRING_8 @@ -135,8 +141,8 @@ feature {NONE} -- Storage load do - if manager.session_exists (uuid) then - if attached manager.session_data (uuid) as d then + if manager.session_exists (id) then + if attached manager.session_data (id) as d then data := d set_expiration (data.expiration) else diff --git a/library/server/wsf/session/wsf_fs_session_manager.e b/library/server/wsf/session/wsf_fs_session_manager.e index e478ca58..88eb66e3 100644 --- a/library/server/wsf/session/wsf_fs_session_manager.e +++ b/library/server/wsf/session/wsf_fs_session_manager.e @@ -68,7 +68,7 @@ feature -- Persistence delete_session (a_session) else ensure_session_folder_exists - create f.make_with_path (file_name (a_session.uuid)) + create f.make_with_path (file_name (a_session.id)) if not f.exists or else f.is_writable then f.create_read_write a_session.data.set_expiration (a_session.expiration) @@ -91,7 +91,7 @@ feature -- Persistence rescued: BOOLEAN do if not rescued then - create f.make_with_path (file_name (a_session.uuid)) + create f.make_with_path (file_name (a_session.id)) if f.exists then f.delete end diff --git a/library/server/wsf/session/wsf_session.e b/library/server/wsf/session/wsf_session.e index b387ed68..ab2f51d8 100644 --- a/library/server/wsf/session/wsf_session.e +++ b/library/server/wsf/session/wsf_session.e @@ -7,26 +7,43 @@ note deferred class WSF_SESSION -feature -- Access +feature -- Access + + id: READABLE_STRING_8 + -- Session identifier. + deferred + end uuid: READABLE_STRING_8 + obsolete + "Use `id' which is more general [2014-03]" deferred end data: WSF_SESSION_DATA + -- Data associated with current session. deferred end expiration: detachable DATE_TIME + -- Expiration date for current session, if any. deferred end expired: BOOLEAN + -- Is current session expired now? + do + Result := expired_at (create {DATE_TIME}.make_now_utc) + end + + expired_at (dt: DATE_TIME): BOOLEAN + -- Is current session expired at date and time `dt'? do if attached expiration as e then - Result := e < (create {DATE_TIME}.make_now_utc) + Result := e < (dt) end end + feature -- status is_pending: BOOLEAN @@ -36,27 +53,32 @@ feature -- status end is_destroyed: BOOLEAN + -- Is current session in destroyed state? deferred end feature -- Entries - table: TABLE_ITERABLE [detachable ANY, READABLE_STRING_32] + table: TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL] + -- Table of session data indexed by key do Result := data end - item (k: READABLE_STRING_GENERAL): detachable ANY + item alias "[]" (k: READABLE_STRING_GENERAL): detachable ANY assign remember + -- Session value associated with key `k'. do Result := data.item (table_key (k)) end remember (v: detachable ANY; k: READABLE_STRING_GENERAL) + -- Remember value `v' in association with key `k'. do data.force (v, table_key (k)) end forget (k: READABLE_STRING_GENERAL) + -- Forget about value associated with key `k'. do data.remove (table_key (k)) end @@ -71,14 +93,16 @@ feature {NONE} -- Implementation feature -- Control destroy + -- Destroy current session. deferred end commit + -- Commit current session, including data associated. deferred end - apply_to (h: HTTP_HEADER_BUILDER; req: WSF_REQUEST; a_path: detachable READABLE_STRING_8) + apply_to (h: HTTP_HEADER_MODIFIER; req: WSF_REQUEST; a_path: detachable READABLE_STRING_8) -- Apply current session to header `h' for request `req' and optional path `a_path'. -- note: either use `apply_to' or `apply', not both. deferred diff --git a/library/server/wsf/session/wsf_session_data.e b/library/server/wsf/session/wsf_session_data.e index a7675ec0..8eed0ef0 100644 --- a/library/server/wsf/session/wsf_session_data.e +++ b/library/server/wsf/session/wsf_session_data.e @@ -8,7 +8,13 @@ class WSF_SESSION_DATA inherit - HASH_TABLE [detachable ANY, READABLE_STRING_32] + STRING_TABLE [detachable ANY] + rename + make as old_make, + make_caseless as make + redefine + empty_duplicate + end create make @@ -24,4 +30,22 @@ feature -- Element change expiration := dt end +feature {NONE} -- Duplication + + empty_duplicate (n: INTEGER): like Current + -- Create an empty copy of Current that can accommodate `n' items + do + create Result.make (n) + end + +note + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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 From bb11c24681c9cf9213e0a8b424fc6b2b578a8ba2 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 09:53:04 +0200 Subject: [PATCH 34/41] check that cookies data is valid string 8 to follow assertions. --- library/server/wsf/src/wsf_request.e | 56 ++++++++++++++-------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index 93980c60..48a9e89a 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -1262,41 +1262,43 @@ feature {NONE} -- Cookies local i,j,p,n: INTEGER l_cookies: like internal_cookies_table + s32: READABLE_STRING_32 k,v,s: STRING do l_cookies := internal_cookies_table if l_cookies = Void then + create l_cookies.make_equal (0) if attached {WSF_STRING} meta_variable ({WSF_META_NAMES}.http_cookie) as val then - s := val.value - create l_cookies.make_equal (5) - from - n := s.count - p := 1 - i := 1 - until - p < 1 - loop - i := s.index_of ('=', p) - if i > 0 then - j := s.index_of (';', i) - if j = 0 then - j := n + 1 - k := s.substring (p, i - 1) - v := s.substring (i + 1, n) + s32 := val.value + if s32.is_valid_as_string_8 then + s := s32.to_string_8 + from + n := s.count + p := 1 + i := 1 + until + p < 1 + loop + i := s.index_of ('=', p) + if i > 0 then + j := s.index_of (';', i) + if j = 0 then + j := n + 1 + k := s.substring (p, i - 1) + v := s.substring (i + 1, n) - p := 0 -- force termination - else - k := s.substring (p, i - 1) - v := s.substring (i + 1, j - 1) - p := j + 1 + p := 0 -- force termination + else + k := s.substring (p, i - 1) + v := s.substring (i + 1, j - 1) + p := j + 1 + end + k.left_adjust + k.right_adjust + add_value_to_table (k, v, l_cookies) end - k.left_adjust - k.right_adjust - add_value_to_table (k, v, l_cookies) end end - else - create l_cookies.make_equal (0) end internal_cookies_table := l_cookies end @@ -2065,7 +2067,7 @@ invariant wgi_request.content_type /= Void implies content_type /= Void note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software From ca6ccc7291fbd1d50e4904319e7c15e6bd0df16f Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 09:59:29 +0200 Subject: [PATCH 35/41] debug_output can return a string 32, so avoid truncated unicode value by returning a string 32 value for `debug_output' . --- library/server/wsf/router/wsf_router_item.e | 21 +++++++++++-------- .../server/wsf/router/wsf_router_mapping.e | 6 +++--- .../wsf_starts_with_context_mapping.e | 4 ++-- .../wsf_router_context_mapping.e | 4 ++-- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/library/server/wsf/router/wsf_router_item.e b/library/server/wsf/router/wsf_router_item.e index 1800e7b9..00467390 100644 --- a/library/server/wsf/router/wsf_router_item.e +++ b/library/server/wsf/router/wsf_router_item.e @@ -3,8 +3,8 @@ note Entry of WSF_ROUTER It contains - mapping - - request methods - + - request methods + ]" date: "$Date$" revision: "$Revision$" @@ -40,20 +40,23 @@ feature -- Access feature -- Status report - debug_output: STRING + debug_output: READABLE_STRING_GENERAL -- String that should be displayed in debugger to represent `Current'. + local + s: STRING_32 do - create Result.make_from_string (mapping.debug_output) + create s.make_from_string_general (mapping.debug_output) if attached request_methods as mtds then - Result.append_string (" [ ") + s.append_string (" [ ") across mtds as c loop - Result.append_string (c.item) - Result.append_string (" ") + s.append_string (c.item) + s.append_string (" ") end - Result.append_string ("]") + s.append_string ("]") end + Result := s end feature -- Change @@ -68,7 +71,7 @@ invariant mapping_attached: mapping /= Void note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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/wsf_router_mapping.e b/library/server/wsf/router/wsf_router_mapping.e index 873a25cb..9a2a3135 100644 --- a/library/server/wsf/router/wsf_router_mapping.e +++ b/library/server/wsf/router/wsf_router_mapping.e @@ -48,10 +48,10 @@ feature -- Documentation feature -- Status report - debug_output: STRING + debug_output: READABLE_STRING_GENERAL -- String that should be displayed in debugger to represent `Current'. do - Result := description.as_string_8 + " : " + associated_resource + Result := description + {STRING_32} " : " + associated_resource.to_string_32 end feature -- Status @@ -88,7 +88,7 @@ feature -- Helper end note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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_context/support/starts_with/wsf_starts_with_context_mapping.e b/library/server/wsf/router_context/support/starts_with/wsf_starts_with_context_mapping.e index a43e19bb..95f54564 100644 --- a/library/server/wsf/router_context/support/starts_with/wsf_starts_with_context_mapping.e +++ b/library/server/wsf/router_context/support/starts_with/wsf_starts_with_context_mapping.e @@ -45,14 +45,14 @@ feature {NONE} -- Execution feature -- Status report - debug_output: STRING + debug_output: READABLE_STRING_GENERAL -- String that should be displayed in debugger to represent `Current'. do Result := Precursor + " {" + ({C}).name + "}" end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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_context/wsf_router_context_mapping.e b/library/server/wsf/router_context/wsf_router_context_mapping.e index bf7f8895..87ea3a67 100644 --- a/library/server/wsf/router_context/wsf_router_context_mapping.e +++ b/library/server/wsf/router_context/wsf_router_context_mapping.e @@ -22,14 +22,14 @@ feature -- Access feature -- Status report - debug_output: STRING + debug_output: READABLE_STRING_GENERAL -- String that should be displayed in debugger to represent `Current'. do Result := Precursor + " {" + ({C}).name + "}" end note - copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software From 1163b99f391da55c5eede3799736fa49e5c4e45e Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 10:02:38 +0200 Subject: [PATCH 36/41] Support for unicode error message for the ERROR_HANDLER.as_string_representation: STRING_32 and as well for debug_output, this avoid unecessary unicode string truncation. --- library/utility/general/error/src/error.e | 6 +++--- library/utility/general/error/src/error_handler.e | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/utility/general/error/src/error.e b/library/utility/general/error/src/error.e index e4c266b0..f5231c09 100644 --- a/library/utility/general/error/src/error.e +++ b/library/utility/general/error/src/error.e @@ -54,9 +54,9 @@ feature -- String representation feature -- Status report - debug_output: STRING + debug_output: STRING_32 do - Result := string_representation.as_string_8 + Result := string_representation end feature -- Change @@ -80,7 +80,7 @@ invariant name_attached: name /= Void note - copyright: "2011-2012, Eiffel Software and others" + copyright: "2011-2014, 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/utility/general/error/src/error_handler.e b/library/utility/general/error/src/error_handler.e index 45085d56..09941972 100644 --- a/library/utility/general/error/src/error_handler.e +++ b/library/utility/general/error/src/error_handler.e @@ -260,7 +260,7 @@ feature -- Access has_error_implies_result_attached: has_error implies Result /= Void end - as_string_representation: STRING + as_string_representation: STRING_32 -- String representation of all error(s). require has_error @@ -269,7 +269,7 @@ feature -- Access Result := e.string_representation else check has_error: False end - Result := "Error occured" + Result := {STRING_32} "Error occured" end end From f099a70b87a322b203b6454b28512a23b336736a Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 10:03:15 +0200 Subject: [PATCH 37/41] Apply recent change on error_handler interface to support unicode error message in response. --- library/server/wsf/policy_driven/wsf_method_helper.e | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/library/server/wsf/policy_driven/wsf_method_helper.e b/library/server/wsf/policy_driven/wsf_method_helper.e index 39b2f8d8..d94e3c17 100644 --- a/library/server/wsf/policy_driven/wsf_method_helper.e +++ b/library/server/wsf/policy_driven/wsf_method_helper.e @@ -376,10 +376,11 @@ feature -- Error reporting local h: HTTP_HEADER m: READABLE_STRING_8 + utf: UTF_CONVERTER do - m := req.error_handler.as_string_representation + m := utf.string_32_to_utf_8_string_8 (req.error_handler.as_string_representation) create h.make - h.put_content_type_text_plain + h.put_content_type_utf_8_text_plain h.put_content_length (m.count) res.set_status_code (req.error_handler.primary_error_code) res.put_header_lines (h) From 5a179f514cc297f0407dd8f9be9430fb1fa69e8c Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 10:04:17 +0200 Subject: [PATCH 38/41] Minor change to avoid unecessary conversion from eventual immutable string 8 to string 8. --- .../documentation/wsf_router_self_documentation_message.e | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/server/wsf/router/documentation/wsf_router_self_documentation_message.e b/library/server/wsf/router/documentation/wsf_router_self_documentation_message.e index acd21662..799f3cb6 100644 --- a/library/server/wsf/router/documentation/wsf_router_self_documentation_message.e +++ b/library/server/wsf/router/documentation/wsf_router_self_documentation_message.e @@ -98,8 +98,8 @@ feature {WSF_RESPONSE} -- Output local h: HTTP_HEADER l_description: STRING_8 - l_base_url: STRING_8 - l_api_resource: detachable STRING_8 + l_base_url: READABLE_STRING_8 + l_api_resource: detachable READABLE_STRING_8 do create h.make h.put_content_type_text_html @@ -132,7 +132,7 @@ feature {WSF_RESPONSE} -- Output if attached router.base_url as u then l_base_url := u else - create l_base_url.make_empty + create {STRING_8} l_base_url.make_empty end debug @@ -324,7 +324,7 @@ feature {NONE} -- Implementation end note - copyright: "2011-2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + copyright: "2011-2014, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software From 31fcd61401bf313cbc68cffda9a22a4bd11ebccf Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 10:14:51 +0200 Subject: [PATCH 39/41] Make sure to be able to compile in complete void-safe for 14.05 and still compile with 13.11 --- .../library/network/server/nino/nino-safe.ecf | 2 +- .../text/parser/json/library/json-safe.ecf | 2 +- examples/restbucksCRUD/client/client-safe.ecf | 4 +-- examples/restbucksCRUD/restbucks-safe.ecf | 6 ++--- examples/upload_image/upload_image-safe.ecf | 9 +++---- .../network/http_client/http_client-safe.ecf | 2 +- .../content_negotiation/conneg-safe.ecf | 5 ++-- library/network/protocol/http/http-safe.ecf | 2 +- .../notification_email-safe.ecf | 4 +-- .../openid/consumer/demo/demo-safe.ecf | 14 +++++----- .../security/openid/consumer/openid-safe.ecf | 2 +- .../http_authorization-safe.ecf | 2 +- .../server/ewsgi/connectors/cgi/cgi-safe.ecf | 2 +- .../ewsgi/connectors/nino/nino-safe.ecf | 2 +- library/server/ewsgi/ewsgi-safe.ecf | 2 +- library/server/ewsgi/ewsgi_spec-safe.ecf | 2 +- .../ewsgi/examples/hello_world/hello-safe.ecf | 2 +- library/server/libfcgi/libfcgi-safe.ecf | 6 ++--- library/server/wsf/connector/all-safe.ecf | 27 +++++++++---------- library/server/wsf/connector/nino-safe.ecf | 2 +- .../server/wsf/connector/openshift-safe.ecf | 8 +++--- library/server/wsf/default/cgi-safe.ecf | 2 +- library/server/wsf/default/nino-safe.ecf | 2 +- library/server/wsf/default/openshift-safe.ecf | 11 ++++---- library/server/wsf/wsf-safe.ecf | 2 +- library/text/encoder/encoder-safe.ecf | 2 +- tests/all-stable-safe.ecf | 2 +- tools/ise_wizard/ewf_ise_wizard-safe.ecf | 2 +- 28 files changed, 64 insertions(+), 66 deletions(-) diff --git a/contrib/library/network/server/nino/nino-safe.ecf b/contrib/library/network/server/nino/nino-safe.ecf index 9a2d69e9..11e63dfd 100644 --- a/contrib/library/network/server/nino/nino-safe.ecf +++ b/contrib/library/network/server/nino/nino-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/contrib/library/text/parser/json/library/json-safe.ecf b/contrib/library/text/parser/json/library/json-safe.ecf index 0eb6cc52..d1936431 100644 --- a/contrib/library/text/parser/json/library/json-safe.ecf +++ b/contrib/library/text/parser/json/library/json-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/examples/restbucksCRUD/client/client-safe.ecf b/examples/restbucksCRUD/client/client-safe.ecf index 5c53670e..69acd23e 100644 --- a/examples/restbucksCRUD/client/client-safe.ecf +++ b/examples/restbucksCRUD/client/client-safe.ecf @@ -1,5 +1,5 @@ - + @@ -11,7 +11,7 @@ - + diff --git a/examples/restbucksCRUD/restbucks-safe.ecf b/examples/restbucksCRUD/restbucks-safe.ecf index fc40603b..626f0d5e 100644 --- a/examples/restbucksCRUD/restbucks-safe.ecf +++ b/examples/restbucksCRUD/restbucks-safe.ecf @@ -1,5 +1,5 @@ - + /EIFGENs$ @@ -29,7 +29,7 @@ - @@ -41,7 +41,7 @@ - diff --git a/examples/upload_image/upload_image-safe.ecf b/examples/upload_image/upload_image-safe.ecf index ba2e34de..66fb0fcc 100644 --- a/examples/upload_image/upload_image-safe.ecf +++ b/examples/upload_image/upload_image-safe.ecf @@ -1,5 +1,5 @@ - + @@ -20,11 +20,10 @@ - + - + - - + diff --git a/library/network/http_client/http_client-safe.ecf b/library/network/http_client/http_client-safe.ecf index 1c7091d1..041fabd1 100644 --- a/library/network/http_client/http_client-safe.ecf +++ b/library/network/http_client/http_client-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/network/protocol/content_negotiation/conneg-safe.ecf b/library/network/protocol/content_negotiation/conneg-safe.ecf index 2bb7d7b2..af7edc59 100644 --- a/library/network/protocol/content_negotiation/conneg-safe.ecf +++ b/library/network/protocol/content_negotiation/conneg-safe.ecf @@ -1,5 +1,5 @@ - + @@ -18,7 +18,6 @@ /implementation - + diff --git a/library/network/protocol/http/http-safe.ecf b/library/network/protocol/http/http-safe.ecf index 024bdb8c..4cffe316 100644 --- a/library/network/protocol/http/http-safe.ecf +++ b/library/network/protocol/http/http-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/runtime/process/notification_email/notification_email-safe.ecf b/library/runtime/process/notification_email/notification_email-safe.ecf index 8bf4ed88..01d95e0a 100644 --- a/library/runtime/process/notification_email/notification_email-safe.ecf +++ b/library/runtime/process/notification_email/notification_email-safe.ecf @@ -1,5 +1,5 @@ - + @@ -13,6 +13,6 @@ - + diff --git a/library/security/openid/consumer/demo/demo-safe.ecf b/library/security/openid/consumer/demo/demo-safe.ecf index 10bd0633..ac320d01 100644 --- a/library/security/openid/consumer/demo/demo-safe.ecf +++ b/library/security/openid/consumer/demo/demo-safe.ecf @@ -1,5 +1,5 @@ - + @@ -7,17 +7,17 @@ /CVS$ /.svn$ - - - - + + + - - + + diff --git a/library/security/openid/consumer/openid-safe.ecf b/library/security/openid/consumer/openid-safe.ecf index 059358e4..67c5f936 100644 --- a/library/security/openid/consumer/openid-safe.ecf +++ b/library/security/openid/consumer/openid-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/authentication/http_authorization/http_authorization-safe.ecf b/library/server/authentication/http_authorization/http_authorization-safe.ecf index e5282ae4..4f5f95a9 100644 --- a/library/server/authentication/http_authorization/http_authorization-safe.ecf +++ b/library/server/authentication/http_authorization/http_authorization-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/ewsgi/connectors/cgi/cgi-safe.ecf b/library/server/ewsgi/connectors/cgi/cgi-safe.ecf index ef1e2700..8d0eaded 100644 --- a/library/server/ewsgi/connectors/cgi/cgi-safe.ecf +++ b/library/server/ewsgi/connectors/cgi/cgi-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/ewsgi/connectors/nino/nino-safe.ecf b/library/server/ewsgi/connectors/nino/nino-safe.ecf index effbfebe..433154aa 100644 --- a/library/server/ewsgi/connectors/nino/nino-safe.ecf +++ b/library/server/ewsgi/connectors/nino/nino-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/ewsgi/ewsgi-safe.ecf b/library/server/ewsgi/ewsgi-safe.ecf index adeb8d5e..62bce7d7 100644 --- a/library/server/ewsgi/ewsgi-safe.ecf +++ b/library/server/ewsgi/ewsgi-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/ewsgi/ewsgi_spec-safe.ecf b/library/server/ewsgi/ewsgi_spec-safe.ecf index 32ac76f1..eaae9ddf 100644 --- a/library/server/ewsgi/ewsgi_spec-safe.ecf +++ b/library/server/ewsgi/ewsgi_spec-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/ewsgi/examples/hello_world/hello-safe.ecf b/library/server/ewsgi/examples/hello_world/hello-safe.ecf index a381ea19..8d992f5c 100644 --- a/library/server/ewsgi/examples/hello_world/hello-safe.ecf +++ b/library/server/ewsgi/examples/hello_world/hello-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/libfcgi/libfcgi-safe.ecf b/library/server/libfcgi/libfcgi-safe.ecf index 525ce69e..0ae6aede 100644 --- a/library/server/libfcgi/libfcgi-safe.ecf +++ b/library/server/libfcgi/libfcgi-safe.ecf @@ -1,5 +1,5 @@ - + @@ -46,16 +46,16 @@ /fake$ - /windows$ /mac$ + /windows$ + /linux$ /fake$ /windows$ - /linux$ diff --git a/library/server/wsf/connector/all-safe.ecf b/library/server/wsf/connector/all-safe.ecf index 11f19f8f..ad23f576 100644 --- a/library/server/wsf/connector/all-safe.ecf +++ b/library/server/wsf/connector/all-safe.ecf @@ -1,5 +1,5 @@ - + @@ -10,21 +10,20 @@ - - - - - - + + + + + + + - - - - - - - + + + + + diff --git a/library/server/wsf/connector/nino-safe.ecf b/library/server/wsf/connector/nino-safe.ecf index 75bd218a..a6bcb9e1 100644 --- a/library/server/wsf/connector/nino-safe.ecf +++ b/library/server/wsf/connector/nino-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/wsf/connector/openshift-safe.ecf b/library/server/wsf/connector/openshift-safe.ecf index 50e1a697..7bbb62cf 100644 --- a/library/server/wsf/connector/openshift-safe.ecf +++ b/library/server/wsf/connector/openshift-safe.ecf @@ -1,5 +1,5 @@ - + @@ -10,9 +10,9 @@ - - + + - + diff --git a/library/server/wsf/default/cgi-safe.ecf b/library/server/wsf/default/cgi-safe.ecf index 6034e626..8a6faae1 100644 --- a/library/server/wsf/default/cgi-safe.ecf +++ b/library/server/wsf/default/cgi-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/wsf/default/nino-safe.ecf b/library/server/wsf/default/nino-safe.ecf index 3d22d656..f9f0aa19 100644 --- a/library/server/wsf/default/nino-safe.ecf +++ b/library/server/wsf/default/nino-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/server/wsf/default/openshift-safe.ecf b/library/server/wsf/default/openshift-safe.ecf index 1f5a6c95..904ad908 100644 --- a/library/server/wsf/default/openshift-safe.ecf +++ b/library/server/wsf/default/openshift-safe.ecf @@ -1,5 +1,5 @@ - + @@ -7,10 +7,11 @@ /\.git$ /\.svn$ - - - - + + + diff --git a/library/server/wsf/wsf-safe.ecf b/library/server/wsf/wsf-safe.ecf index 7867df01..9fd98569 100644 --- a/library/server/wsf/wsf-safe.ecf +++ b/library/server/wsf/wsf-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/library/text/encoder/encoder-safe.ecf b/library/text/encoder/encoder-safe.ecf index b7307359..e6f73687 100644 --- a/library/text/encoder/encoder-safe.ecf +++ b/library/text/encoder/encoder-safe.ecf @@ -1,5 +1,5 @@ - + diff --git a/tests/all-stable-safe.ecf b/tests/all-stable-safe.ecf index 9903a6eb..1f0b84f5 100644 --- a/tests/all-stable-safe.ecf +++ b/tests/all-stable-safe.ecf @@ -1,5 +1,5 @@ - + Integration project including many lib diff --git a/tools/ise_wizard/ewf_ise_wizard-safe.ecf b/tools/ise_wizard/ewf_ise_wizard-safe.ecf index 36ca6b24..8b68184e 100644 --- a/tools/ise_wizard/ewf_ise_wizard-safe.ecf +++ b/tools/ise_wizard/ewf_ise_wizard-safe.ecf @@ -1,5 +1,5 @@ - + From fcf8b6366670ef8a4c12efbdb4fa9acd5d667757 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 16:16:32 +0200 Subject: [PATCH 40/41] Make sure to be able to compile in complete void-safe for 14.05 and still compile with 13.11 --- tests/all-safe.ecf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/all-safe.ecf b/tests/all-safe.ecf index 869f2432..9319a159 100644 --- a/tests/all-safe.ecf +++ b/tests/all-safe.ecf @@ -1,5 +1,5 @@ - + Integration project including many lib @@ -47,7 +47,6 @@ - @@ -55,6 +54,7 @@ + From b59966595ed74dd9ba591821563189bf1a3c0a92 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 14 May 2014 16:18:10 +0200 Subject: [PATCH 41/41] Make sure to be able to compile in complete void-safe for 14.05 and still compile with 13.11 --- tests/all-stable-safe.ecf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/all-stable-safe.ecf b/tests/all-stable-safe.ecf index 1f0b84f5..fccfd95c 100644 --- a/tests/all-stable-safe.ecf +++ b/tests/all-stable-safe.ecf @@ -1,5 +1,5 @@ - + Integration project including many lib