diff --git a/ext/server/nino b/ext/server/nino index c772fc51..de778025 160000 --- a/ext/server/nino +++ b/ext/server/nino @@ -1 +1 @@ -Subproject commit c772fc5194d69b91d26666a99ffd051451015fdb +Subproject commit de77802546317cb7561c7172e777ff9bcce50393 diff --git a/library/error/error-safe.ecf b/library/error/error-safe.ecf new file mode 100644 index 00000000..b5eb8493 --- /dev/null +++ b/library/error/error-safe.ecf @@ -0,0 +1,16 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + diff --git a/library/error/error.ecf b/library/error/error.ecf new file mode 100644 index 00000000..ea7b6080 --- /dev/null +++ b/library/error/error.ecf @@ -0,0 +1,16 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + diff --git a/library/error/license.lic b/library/error/license.lic new file mode 100644 index 00000000..c929225f --- /dev/null +++ b/library/error/license.lic @@ -0,0 +1 @@ +reference:forum2 diff --git a/library/error/src/error.e b/library/error/src/error.e new file mode 100644 index 00000000..5687ecac --- /dev/null +++ b/library/error/src/error.e @@ -0,0 +1,80 @@ +note + description : "Objects that represent an error" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + ERROR + +feature -- Access + + code: INTEGER + deferred + ensure + result_not_zero: Result /= 0 + end + + name: STRING + deferred + ensure + result_attached: Result /= Void + end + + message: detachable STRING_32 + -- Potential error message + deferred + end + + parent: detachable ERROR + -- Eventual error prior to Current + +feature -- String representation + + string_representation: STRING_32 + -- String representation for Current + do + create Result.make_from_string (name.as_string_32) + Result.append_character (' ') + Result.append_character ('(') + Result.append_integer (code) + Result.append_character (')') + if attached message as m then + Result.append_character (':') + Result.append_character (' ') + Result.append_string (m) + end + end + +feature -- Change + + set_parent (a_parent: like parent) + -- Set `parent' to `a_parent' + do + parent := a_parent + end + +feature -- Visitor + + process (a_visitor: ERROR_VISITOR) + -- Process Current using `a_visitor'. + require + a_visitor_not_void: a_visitor /= Void + deferred + end + +invariant + name_attached: name /= Void + +note + copyright: "Copyright (c) 1984-2011, 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/error/src/error_custom.e b/library/error/src/error_custom.e new file mode 100644 index 00000000..7b139fab --- /dev/null +++ b/library/error/src/error_custom.e @@ -0,0 +1,57 @@ +note + description : "Objects that represent a custom error" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + ERROR_CUSTOM + +inherit + ERROR + +create + make + +feature {NONE} -- Initialization + + make (a_code: INTEGER; a_name: STRING; a_message: detachable like message) + -- Initialize `Current'. + do + code := a_code + name := a_name + if attached a_message then + message := a_message + else + message := "Error: " + a_name + " (code=" + a_code.out + ")" + end + end + +feature -- Access + + code: INTEGER + + name: STRING + + message: STRING_32 + +feature -- Visitor + + process (a_visitor: ERROR_VISITOR) + -- Process Current using `a_visitor'. + do + a_visitor.process_custom (Current) + end + +note + copyright: "Copyright (c) 1984-2011, 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/error/src/error_group.e b/library/error/src/error_group.e new file mode 100644 index 00000000..df101759 --- /dev/null +++ b/library/error/src/error_group.e @@ -0,0 +1,75 @@ +note + description : "Objects that represent a group of errors" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + ERROR_GROUP + +inherit + ERROR + +create + make + +feature {NONE} -- Initialization + + make (a_errors: LIST [ERROR]) + -- Initialize `Current'. + do + name := a_errors.count.out + " errors" + create {ARRAYED_LIST [ERROR]} sub_errors.make (a_errors.count) + sub_errors.fill (a_errors) + end + +feature -- Access + + code: INTEGER = -1 + + name: STRING + + message: detachable STRING_32 + do + create Result.make_from_string (name) + from + sub_errors.start + until + sub_errors.after + loop + if + attached sub_errors.item as e and then + attached e.message as m + then + + Result.append_character ('%N') + Result.append_string (m) + end + sub_errors.forth + end + end + + sub_errors: LIST [ERROR] + -- Error contained by Current + +feature -- Visitor + + process (a_visitor: ERROR_VISITOR) + -- Process Current using `a_visitor'. + do + a_visitor.process_group (Current) + end + + +note + copyright: "Copyright (c) 1984-2011, 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/error/src/error_handler.e b/library/error/src/error_handler.e new file mode 100644 index 00000000..1a124b49 --- /dev/null +++ b/library/error/src/error_handler.e @@ -0,0 +1,92 @@ +note + description : "Objects that handle error..." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + ERROR_HANDLER + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + do + create {ARRAYED_LIST [ERROR]} errors.make (3) + end + +feature -- Status + + has_error: BOOLEAN + -- Has error? + do + Result := count > 0 + end + + count: INTEGER + do + Result := errors.count + end + + errors: LIST [ERROR] + -- Errors container + +feature -- Basic operation + + add_error (a_error: ERROR) + -- Add `a_error' to the stack of error + do + errors.force (a_error) + end + + add_error_details, add_custom_error (a_code: INTEGER; a_name: STRING; a_message: detachable STRING_32) + -- Add custom error to the stack of error + local + e: ERROR_CUSTOM + do + create e.make (a_code, a_name, a_message) + add_error (e) + end + +feature -- Access + + as_single_error: detachable ERROR + do + if count > 1 then + create {ERROR_GROUP} Result.make (errors) + elseif count > 0 then + Result := errors.first + end + end + +feature -- Element changes + + concatenate + -- Concatenate into a single error if any + do + if count > 1 and then attached as_single_error as e then + wipe_out + add_error (e) + end + end + + reset, wipe_out + do + errors.wipe_out + end + +note + copyright: "Copyright (c) 1984-2011, 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/error/src/visitor/error_iterator.e b/library/error/src/visitor/error_iterator.e new file mode 100644 index 00000000..0102544a --- /dev/null +++ b/library/error/src/visitor/error_iterator.e @@ -0,0 +1,49 @@ +note + description : "Error list iterator" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + ERROR_ITERATOR + +inherit + ERROR_VISITOR + +feature -- Access + + process_error (e: ERROR) + do + end + + process_custom (e: ERROR_CUSTOM) + do + process_error (e) + end + + process_group (g: ERROR_GROUP) + do + if attached g.sub_errors as err then + from + err.start + until + err.after + loop + process_error (err.item) + err.forth + end + end + end + +note + copyright: "Copyright (c) 1984-2011, 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/error/src/visitor/error_null_visitor.e b/library/error/src/visitor/error_null_visitor.e new file mode 100644 index 00000000..24af641c --- /dev/null +++ b/library/error/src/visitor/error_null_visitor.e @@ -0,0 +1,38 @@ +note + description : "Null error visitor" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + ERROR_NULL_VISITOR + +inherit + ERROR_VISITOR + +feature -- Access + + process_error (e: ERROR) + do + end + + process_custom (e: ERROR_CUSTOM) + do + end + + process_group (g: ERROR_GROUP) + do + end + +note + copyright: "Copyright (c) 1984-2011, 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/error/src/visitor/error_visitor.e b/library/error/src/visitor/error_visitor.e new file mode 100644 index 00000000..40d205e6 --- /dev/null +++ b/library/error/src/visitor/error_visitor.e @@ -0,0 +1,35 @@ +note + description : "Objects to visit an ERROR" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + ERROR_VISITOR + +feature -- Access + + process_error (e: ERROR) + deferred + end + + process_custom (e: ERROR_CUSTOM) + deferred + end + + process_group (g: ERROR_GROUP) + deferred + end + +note + copyright: "Copyright (c) 1984-2011, 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/error/src/visitor/file_output_error_visitor.e b/library/error/src/visitor/file_output_error_visitor.e new file mode 100644 index 00000000..588c4250 --- /dev/null +++ b/library/error/src/visitor/file_output_error_visitor.e @@ -0,0 +1,53 @@ +note + description: "File error output visitor" + date: "$Date$" + revision: "$Revision$" + +class + FILE_OUTPUT_ERROR_VISITOR + +inherit + OUTPUT_ERROR_VISITOR + redefine + output_integer, + output_new_line + end + +create + make + +feature -- Initialization + + make (f: like file) + require + f_open_write: f /= Void and then f.is_open_write + do + file := f + end + +feature -- Access + + file: FILE + +feature -- Output + + output_string (a_str: detachable STRING_GENERAL) + -- Output Unicode string + do + if a_str /= Void then + to_implement ("Convert into UTF-8 or console encoding before output") + file.put_string (a_str.as_string_8) + end + end + + output_integer (i: INTEGER) + do + file.put_integer (i) + end + + output_new_line + do + file.put_new_line + end + +end diff --git a/library/error/src/visitor/output_error_visitor.e b/library/error/src/visitor/output_error_visitor.e new file mode 100644 index 00000000..22d1128d --- /dev/null +++ b/library/error/src/visitor/output_error_visitor.e @@ -0,0 +1,93 @@ +note + description: "General error output visitor" + date: "$Date$" + revision: "$Revision$" + +deferred class + OUTPUT_ERROR_VISITOR + +inherit + ERROR_VISITOR + + REFACTORING_HELPER + +feature -- Output + + output_string (a_str: detachable STRING_GENERAL) + -- Output Unicode string + deferred + end + + output_any (obj: detachable ANY) + -- Output Unicode string + do + if attached {STRING_GENERAL} obj as l_str then + to_implement ("Convert into UTF-8 or console encoding before output") + output_string (l_str) + elseif obj /= Void then + output_string (obj.out) + end + end + + output_integer (i: INTEGER) + do + output_string (i.out) + end + + output_new_line + do + output_string ("%N") + end + +feature -- Process + + process_error (e: ERROR) + do + output_string ({STRING_32}"Error Name: ") + output_string (e.name) + output_string ({STRING_32}"Code: ") + output_integer (e.code) + output_new_line + output_string ({STRING_32}"%TMessage: ") + output_string (e.message) + output_new_line + end + + process_custom (e: ERROR_CUSTOM) + do + output_string ({STRING_32}"Error Name: ") + output_string (e.name) + output_string ({STRING_32}"Code: ") + output_integer (e.code) + output_new_line + output_string ({STRING_32}"%TMessage: ") + output_string (e.message) + output_new_line + end + + process_group (g: ERROR_GROUP) + local + l_errors: LIST [ERROR] + do + from + l_errors := g.sub_errors + l_errors.start + until + l_errors.after + loop + l_errors.item.process (Current) + l_errors.forth + end + end + +note + copyright: "Copyright (c) 1984-2011, 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/error/src/visitor/text_output_error_visitor.e b/library/error/src/visitor/text_output_error_visitor.e new file mode 100644 index 00000000..bff7f86f --- /dev/null +++ b/library/error/src/visitor/text_output_error_visitor.e @@ -0,0 +1,53 @@ +note + description: "Text error output visitor" + date: "$Date$" + revision: "$Revision$" + +class + TEXT_OUTPUT_ERROR_VISITOR + +inherit + OUTPUT_ERROR_VISITOR + redefine + output_integer, + output_new_line + end + +create + make + +feature -- Initialization + + make (buf: like buffer) + require + buf_attached: buf /= Void + do + buffer := buf + end + +feature -- Access + + buffer: STRING + +feature -- Output + + output_string (a_str: detachable STRING_GENERAL) + -- Output Unicode string + do + if a_str /= Void then + to_implement ("Convert into UTF-8 or console encoding before output") + buffer.append_string_general (a_str) + end + end + + output_integer (i: INTEGER) + do + buffer.append_integer (i) + end + + output_new_line + do + buffer.append_character ('%N') + end + +end diff --git a/library/protocol/http/http-safe.ecf b/library/protocol/http/http-safe.ecf new file mode 100644 index 00000000..cee2c04b --- /dev/null +++ b/library/protocol/http/http-safe.ecf @@ -0,0 +1,17 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + diff --git a/library/protocol/http/http.ecf b/library/protocol/http/http.ecf new file mode 100644 index 00000000..f8e77884 --- /dev/null +++ b/library/protocol/http/http.ecf @@ -0,0 +1,18 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + + diff --git a/library/protocol/http/src/http_constants.e b/library/protocol/http/src/http_constants.e new file mode 100644 index 00000000..9569b03f --- /dev/null +++ b/library/protocol/http/src/http_constants.e @@ -0,0 +1,97 @@ +note + description: "Summary description for {HTTP_CONSTANTS}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + HTTP_CONSTANTS + +feature -- Ports + + default_http_port: INTEGER = 80 + default_https_port: INTEGER = 443 + +feature -- Method + + method_get: STRING = "GET" + method_post: STRING = "POST" + method_put: STRING = "PUT" + method_delete: STRING = "DELETE" + method_head: STRING = "HEAD" + method_download: STRING = "DOWNLOAD" + +feature -- Content type + + octet_stream: STRING = "application/octet-stream" + -- Octet stream content-type header + multipart_form: STRING = "multipart/form-data" + -- Starting chars of multipart form data content-type header + form_encoded: STRING = "application/x-www-form-urlencoded" + -- Starting chars of form url-encoded data content-type header + xml_text: STRING = "text/xml" + -- XML text content-type header + html_text: STRING = "text/html" + -- HTML text content-type header + json_text: STRING = "text/json" + -- JSON text content-type header + json_app: STRING = "application/json" + -- JSON application content-type header + js_text: STRING = "text/javascript" + -- Javascript text content-type header + js_app: STRING = "application/javascript" + -- JavaScript application content-type header + plain_text: STRING = "text/plain" + -- Plain text content-type header + +feature -- Server + + http_version_1_0: STRING = "HTTP/1.0" + http_host_header: STRING = "Host" + http_authorization_header: STRING = "Authorization: " + http_end_of_header_line: STRING = "%R%N" + http_end_of_command: STRING = "%R%N%R%N" + http_content_length: STRING = "Content-Length: " + http_content_type: STRING = "Content-Type: " + http_content_location: STRING = "Content-Location: " + http_content_disposition: STRING = "Content-Disposition: " + http_path_translated: STRING = "Path-Translated: " + http_agent: STRING = "User-agent: " + http_from: STRING = "From: " + +feature -- Server: header + + header_host: STRING = "Host" + header_authorization: STRING = "Authorization" + header_content_length: STRING = "Content-Length" + header_content_type: STRING = "Content-Type" + header_content_location: STRING = "Content-Location" + header_content_disposition: STRING = "Content-Disposition" + header_cache_control: STRING = "Cache-Control" + header_path_translated: STRING = "Path-Translated" + header_agent: STRING = "User-Agent" + header_referer: STRING = "Referer" -- Officially mispelled in std + header_location: STRING = "Location" + header_from: STRING = "From" + header_status: STRING = "Status" + header_multipart_tag_value_separator: CHARACTER = ';' + +feature -- Misc + + http_status_ok: STRING = "200 OK" + + default_bufsize: INTEGER = 16384 --| 16K + + +note + copyright: "Copyright (c) 1984-2011, 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/protocol/http/src/http_date_time_utilities.e b/library/protocol/http/src/http_date_time_utilities.e new file mode 100644 index 00000000..d24c6b09 --- /dev/null +++ b/library/protocol/http/src/http_date_time_utilities.e @@ -0,0 +1,71 @@ +note + description: "Summary description for {HTTP_DATE_TIME_UTILITIES}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + HTTP_DATE_TIME_UTILITIES + +feature -- Access + + now_utc: DATE_TIME + do + create Result.make_now_utc + end + + epoch: DATE_TIME + once ("THREAD") + create Result.make_from_epoch (0) + end + +feature -- Unix time stamp + + unix_time_stamp (dt: detachable DATE_TIME): INTEGER_64 + -- Unix time stamp from `dt' if attached or from epoch is detached + local + l_date_time: DATE_TIME + do + if dt /= Void then + l_date_time := dt + else + l_date_time := now_utc + end + Result := l_date_time.definite_duration (epoch).seconds_count + end + + fine_unix_time_stamp (dt: detachable DATE_TIME): DOUBLE + -- Fine unix time stamp from `dt' if attached or from epoch is detached + local + l_date_time: DATE_TIME + do + if dt /= Void then + l_date_time := dt + else + l_date_time := now_utc + end + Result := l_date_time.definite_duration (epoch).fine_seconds_count + end + +feature -- Unix time stamp conversion + + unix_time_stamp_to_date_time (i64: INTEGER_64): DATE_TIME + -- Date time related to `i64' + do + create Result.make_from_epoch (i64.as_integer_32) + ensure + same_unix_time_stamp: unix_time_stamp (Result) = i64 + end + +;note + copyright: "Copyright (c) 1984-2011, 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/protocol/http/src/http_status_code.e b/library/protocol/http/src/http_status_code.e new file mode 100644 index 00000000..43375c1f --- /dev/null +++ b/library/protocol/http/src/http_status_code.e @@ -0,0 +1,106 @@ +note + description: "[ + Status code constants pertaining to the HTTP protocol + See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes + ]" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + HTTP_STATUS_CODE + +feature -- 1xx : Informational + + continue: INTEGER = 100 + switching_protocols: INTEGER = 101 + processing: INTEGER = 102 -- WebDAV RFC 2518 + ie7_request_uri_too_long: INTEGER = 122 -- non standard, IE7 only + +feature -- 2xx : Success + + ok: INTEGER = 200 + created: INTEGER = 201 + accepted: INTEGER = 202 + nonauthoritative_info: INTEGER = 203 + no_content: INTEGER = 204 + reset_content: INTEGER = 205 + partial_content: INTEGER = 206 + multistatus: INTEGER = 207 -- WebDAV RFC 4918 + im_used: INTEGER = 226 -- RFC 4918 + +feature -- 3xx : Redirection + + multiple_choices: INTEGER = 300 + moved_permanently: INTEGER = 301 + found: INTEGER = 302 + see_other: INTEGER = 303 + not_modified: INTEGER = 304 + use_proxy: INTEGER = 305 + switch_proxy: INTEGER = 306 + temp_redirect: INTEGER = 307 + +feature -- 4xx : Client Error + + bad_request: INTEGER = 400 + unauthorized: INTEGER = 401 + payment_required: INTEGER = 402 + forbidden: INTEGER = 403 + not_found: INTEGER = 404 + method_not_allowed: INTEGER = 405 + not_acceptable: INTEGER = 406 + proxy_auth_required: INTEGER = 407 + request_timeout: INTEGER = 408 + conflict: INTEGER = 409 + gone: INTEGER = 410 + length_required: INTEGER = 411 + precondition_failed: INTEGER = 412 + request_entity_too_large: INTEGER = 413 + request_uri_too_long: INTEGER = 414 + unsupported_media_type: INTEGER = 415 + request_range_not_satisfiable: INTEGER = 416 + expectation_failed: INTEGER = 417 + teapot: INTEGER = 418 + +feature -- 4xx : Client Error : WebDAV errors + + too_many_connections: INTEGER = 421 + unprocessable_entity: INTEGER = 422 + locked: INTEGER = 423 + failed_dependency: INTEGER = 424 + unordered_collection: INTEGER = 425 + + upgrade_required: INTEGER = 426 + no_response: INTEGER = 444 + retry_with: INTEGER = 449 + blocked_parental: INTEGER = 450 + client_closed_request: INTEGER = 499 + +feature -- 5xx : Server Error + + internal_server_error: INTEGER = 500 + not_implemented: INTEGER = 501 + bad_gateway: INTEGER = 502 + service_unavailable: INTEGER = 503 + gateway_timeout: INTEGER = 504 + http_version_not_supported: INTEGER = 505 + variant_also_negotiates: INTEGER = 506 + insufficient_storage: INTEGER = 507 -- WebDAV RFC 4918 + + bandwidth_limit_exceeded: INTEGER = 509 + not_extended: INTEGER = 510 + + user_access_denied: INTEGER = 530 + +note + copyright: "Copyright (c) 1984-2011, 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/protocol/http/src/http_status_code_messages.e b/library/protocol/http/src/http_status_code_messages.e new file mode 100644 index 00000000..27b4274b --- /dev/null +++ b/library/protocol/http/src/http_status_code_messages.e @@ -0,0 +1,160 @@ +note + description: "[ + Status code constants pertaining to the HTTP protocol + See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes + ]" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + HTTP_STATUS_CODE_MESSAGES + +inherit + HTTP_STATUS_CODE + +feature -- Status report + + is_valid_http_status_code (v: INTEGER): BOOLEAN + -- Is the given value a valid http status code? + do + Result := v >= continue and v <= user_access_denied + end + +feature -- Status messages + + http_status_code_message (a_code: INTEGER): detachable STRING + -- Header message related to HTTP status code `a_code' + do + inspect a_code + when continue then + Result := "Continue" + when switching_protocols then + Result := "Switching Protocols" + when processing then + Result := "Processing" + when ok then + Result := "OK" + when created then + Result := "Created" + when accepted then + Result := "Accepted" + when nonauthoritative_info then + Result := "Non-Authoritative Information" + when no_content then + Result := "No Content" + when reset_content then + Result := "Reset Content" + when partial_content then + Result := "Partial Content" + when multistatus then + Result := "Multi-Status" + when multiple_choices then + Result := "Multiple Choices" + when moved_permanently then + Result := "Moved Permanently" + when found then + Result := "Found" + when see_other then + Result := "See Other" + when not_modified then + Result := "Not Modified" + when use_proxy then + Result := "Use Proxy" + when switch_proxy then + Result := "Switch Proxy" + when temp_redirect then + Result := "Temporary Redirect" + when bad_request then + Result := "Bad Request" + when unauthorized then + Result := "Unauthorized" + when payment_required then + Result := "Payment Required" + when forbidden then + Result := "Forbidden" + when not_found then + Result := "Not Found" + when method_not_allowed then + Result := "Method Not Allowed" + when not_acceptable then + Result := "Not Acceptable" + when proxy_auth_required then + Result := "Proxy Authentication Required" + when request_timeout then + Result := "Request Timeout" + when conflict then + Result := "Conflict" + when gone then + Result := "Gone" + when length_required then + Result := "Length Required" + when precondition_failed then + Result := "Precondition Failed" + when request_entity_too_large then + Result := "Request Entity Too Large" + when request_uri_too_long then + Result := "Request-URI Too Long" + when unsupported_media_type then + Result := "Unsupported Media Type" + when request_range_not_satisfiable then + Result := "Requested Range Not Satisfiable" + when expectation_failed then + Result := "Expectation Failed" + when teapot then + Result := "I'm a teapot" + when too_many_connections then + Result := "There are too many connections from your internet address" + when unprocessable_entity then + Result := "Unprocessable Entity" + when locked then + Result := "Locked" + when failed_dependency then + Result := "Failed Dependency" + when unordered_collection then + Result := "Unordered Collection" + when upgrade_required then + Result := "Upgrade Required" + when retry_with then + Result := "Retry With" + when blocked_parental then + Result := "Blocked by Windows Parental Controls" + when internal_server_error then + Result := "Internal Server Error" + when not_implemented then + Result := "Not Implemented" + when bad_gateway then + Result := "Bad Gateway" + when service_unavailable then + Result := "Service Unavailable" + when gateway_timeout then + Result := "Gateway Timeout" + when http_version_not_supported then + Result := "HTTP Version Not Supported" + when variant_also_negotiates then + Result := "Variant Also Negotiates" + when insufficient_storage then + Result := "Insufficient Storage" + when bandwidth_limit_exceeded then + Result := "Bandwidth Limit Exceeded" + when not_extended then + Result := "Not Extended" + when user_access_denied then + Result := "User access denied" + else + Result := Void + end + end + +note + copyright: "Copyright (c) 1984-2011, 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/ewsgi/COPYRIGHT b/library/server/ewsgi/COPYRIGHT similarity index 100% rename from library/ewsgi/COPYRIGHT rename to library/server/ewsgi/COPYRIGHT diff --git a/library/server/ewsgi/connectors/cgi/cgi-safe.ecf b/library/server/ewsgi/connectors/cgi/cgi-safe.ecf new file mode 100644 index 00000000..a1900388 --- /dev/null +++ b/library/server/ewsgi/connectors/cgi/cgi-safe.ecf @@ -0,0 +1,16 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + diff --git a/library/server/ewsgi/connectors/cgi/cgi.ecf b/library/server/ewsgi/connectors/cgi/cgi.ecf new file mode 100644 index 00000000..412abe6b --- /dev/null +++ b/library/server/ewsgi/connectors/cgi/cgi.ecf @@ -0,0 +1,16 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + diff --git a/library/server/ewsgi/connectors/cgi/license.lic b/library/server/ewsgi/connectors/cgi/license.lic new file mode 100644 index 00000000..c929225f --- /dev/null +++ b/library/server/ewsgi/connectors/cgi/license.lic @@ -0,0 +1 @@ +reference:forum2 diff --git a/library/server/ewsgi/connectors/cgi/src/gw_cgi_connector.e b/library/server/ewsgi/connectors/cgi/src/gw_cgi_connector.e new file mode 100644 index 00000000..d229eee7 --- /dev/null +++ b/library/server/ewsgi/connectors/cgi/src/gw_cgi_connector.e @@ -0,0 +1,36 @@ +note + description: "Summary description for {GW_CGI_CONNECTOR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + GW_CGI_CONNECTOR + +inherit + GW_CONNECTOR + +create + make + +feature -- Execution + + launch + local + env: GW_ENVIRONMENT_VARIABLES + do + create env.make_with_variables ((create {EXECUTION_ENVIRONMENT}).starting_environment_variables) + application.process (env, create {GW_CGI_INPUT_STREAM}.make, create {GW_CGI_OUTPUT_STREAM}.make) + end + +note + copyright: "Copyright (c) 1984-2011, 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/ewsgi/connectors/cgi/src/gw_cgi_input_stream.e b/library/server/ewsgi/connectors/cgi/src/gw_cgi_input_stream.e new file mode 100644 index 00000000..0a49425d --- /dev/null +++ b/library/server/ewsgi/connectors/cgi/src/gw_cgi_input_stream.e @@ -0,0 +1,39 @@ +note + description: "Summary description for GW_CGI_INPUT_STREAM." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_CGI_INPUT_STREAM + +inherit + GW_INPUT_STREAM + + CONSOLE + rename + make as console_make + end + +create + make + +feature {NONE} -- Initialization + + make + do + make_open_stdin ("stdin") + end + +note + copyright: "2011-2011, 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/ewsgi/connectors/cgi/src/gw_cgi_output_stream.e b/library/server/ewsgi/connectors/cgi/src/gw_cgi_output_stream.e new file mode 100644 index 00000000..7a05d8ca --- /dev/null +++ b/library/server/ewsgi/connectors/cgi/src/gw_cgi_output_stream.e @@ -0,0 +1,39 @@ +note + description: "Summary description for GW_CGI_OUTPUT_STREAM." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_CGI_OUTPUT_STREAM + +inherit + GW_OUTPUT_STREAM + + CONSOLE + rename + make as console_make + end + +create + make + +feature {NONE} -- Initialization + + make + do + make_open_stdout ("stdout") + end + +note + copyright: "2011-2011, 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/ewsgi/connectors/libfcgi/libfcgi-safe.ecf b/library/server/ewsgi/connectors/libfcgi/libfcgi-safe.ecf new file mode 100644 index 00000000..d02b81dd --- /dev/null +++ b/library/server/ewsgi/connectors/libfcgi/libfcgi-safe.ecf @@ -0,0 +1,17 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + diff --git a/library/server/ewsgi/connectors/libfcgi/libfcgi.ecf b/library/server/ewsgi/connectors/libfcgi/libfcgi.ecf new file mode 100644 index 00000000..3eb715f7 --- /dev/null +++ b/library/server/ewsgi/connectors/libfcgi/libfcgi.ecf @@ -0,0 +1,17 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + diff --git a/library/server/ewsgi/connectors/libfcgi/license.lic b/library/server/ewsgi/connectors/libfcgi/license.lic new file mode 100644 index 00000000..c929225f --- /dev/null +++ b/library/server/ewsgi/connectors/libfcgi/license.lic @@ -0,0 +1 @@ +reference:forum2 diff --git a/library/server/ewsgi/connectors/libfcgi/src/gw_libfcgi_connector.e b/library/server/ewsgi/connectors/libfcgi/src/gw_libfcgi_connector.e new file mode 100644 index 00000000..91def76e --- /dev/null +++ b/library/server/ewsgi/connectors/libfcgi/src/gw_libfcgi_connector.e @@ -0,0 +1,81 @@ +note + description: "Summary description for {GW_LIBFCGI_CONNECTOR}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_LIBFCGI_CONNECTOR + +inherit + GW_CONNECTOR + redefine + initialize + end + +create + make + +feature {NONE} -- Initialization + + initialize + do + create fcgi.make + create {GW_LIBFCGI_INPUT_STREAM} input.make (fcgi) + create {GW_LIBFCGI_OUTPUT_STREAM} output.make (fcgi) + end + +feature -- Server + + launch + local + res: INTEGER + do + from + res := fcgi.fcgi_listen + until + res < 0 + loop + process_fcgi_request (fcgi.updated_environ_variables, input, output) + res := fcgi.fcgi_listen + end + end + +feature -- Execution + + process_fcgi_request (vars: HASH_TABLE [STRING, STRING]; a_input: like input; a_output: like output) + local + gw_env: GW_ENVIRONMENT_VARIABLES + do + create gw_env.make_with_variables (vars) + application.process (gw_env, a_input, a_output) + end + +feature -- Input/Output + + input: GW_INPUT_STREAM + -- Input from client (from httpd server via FCGI) + + output: GW_OUTPUT_STREAM + -- Output to client (via httpd server/fcgi) + +feature {NONE} -- Implementation + + fcgi: FCGI + +invariant + fcgi_attached: fcgi /= Void + +note + copyright: "Copyright (c) 1984-2011, 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/ewsgi/connectors/libfcgi/src/gw_libfcgi_input_stream.e b/library/server/ewsgi/connectors/libfcgi/src/gw_libfcgi_input_stream.e new file mode 100644 index 00000000..75db59d7 --- /dev/null +++ b/library/server/ewsgi/connectors/libfcgi/src/gw_libfcgi_input_stream.e @@ -0,0 +1,66 @@ +note + description: "Summary description for GW_LIBFCGI_INPUT_STREAM." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_LIBFCGI_INPUT_STREAM + +inherit + GW_INPUT_STREAM + + STRING_HANDLER + +create + make + +feature {NONE} -- Initialization + + make (a_fcgi: like fcgi) + require + valid_fcgi: a_fcgi /= Void + do + fcgi := a_fcgi + initialize + end + + initialize + -- Initialize Current + do + create last_string.make_empty + end + +feature -- Basic operation + + read_stream (nb_char: INTEGER) + -- Read a string of at most `nb_char' bound characters + -- or until end of file. + -- Make result available in `last_string'. + do + fcgi.fill_string_from_stdin (last_string, nb_char) + end + +feature -- Access + + last_string: STRING + -- Last string read + +feature {NONE} -- Implementation + + fcgi: FCGI; + -- Bridge to FCGI world + +note + copyright: "Copyright (c) 1984-2011, 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/ewsgi/connectors/libfcgi/src/gw_libfcgi_output_stream.e b/library/server/ewsgi/connectors/libfcgi/src/gw_libfcgi_output_stream.e new file mode 100644 index 00000000..331bb763 --- /dev/null +++ b/library/server/ewsgi/connectors/libfcgi/src/gw_libfcgi_output_stream.e @@ -0,0 +1,57 @@ +note + description: "Summary description for {GW_LIBFCGI_OUTPUT_STREAM}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_LIBFCGI_OUTPUT_STREAM + +inherit + GW_OUTPUT_STREAM + +create + make + +feature {NONE} -- Initialization + + make (a_fcgi: like fcgi) + require + valid_fcgi: a_fcgi /= Void + do + fcgi := a_fcgi + end + +feature -- Basic operation + + put_string (s: STRING) + -- Send `s' to http client + do + fcgi.put_string (s) + end + + flush + do + end + +feature {NONE} -- Implementation + + fcgi: FCGI + -- Bridge to FCGI world + +invariant + fcgi_attached: fcgi /= Void + +note + copyright: "Copyright (c) 1984-2011, 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/ewsgi/connectors/nino/license.lic b/library/server/ewsgi/connectors/nino/license.lic new file mode 100644 index 00000000..c929225f --- /dev/null +++ b/library/server/ewsgi/connectors/nino/license.lic @@ -0,0 +1 @@ +reference:forum2 diff --git a/library/server/ewsgi/connectors/nino/nino-safe.ecf b/library/server/ewsgi/connectors/nino/nino-safe.ecf new file mode 100644 index 00000000..1b3cbefb --- /dev/null +++ b/library/server/ewsgi/connectors/nino/nino-safe.ecf @@ -0,0 +1,17 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + diff --git a/library/server/ewsgi/connectors/nino/nino.ecf b/library/server/ewsgi/connectors/nino/nino.ecf new file mode 100644 index 00000000..97a1099a --- /dev/null +++ b/library/server/ewsgi/connectors/nino/nino.ecf @@ -0,0 +1,17 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + diff --git a/library/server/ewsgi/connectors/nino/src/gw_nino_connector.e b/library/server/ewsgi/connectors/nino/src/gw_nino_connector.e new file mode 100644 index 00000000..c14bcc8a --- /dev/null +++ b/library/server/ewsgi/connectors/nino/src/gw_nino_connector.e @@ -0,0 +1,93 @@ +note + description: "Summary description for {GW_NINO_CONNECTOR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + GW_NINO_CONNECTOR + +inherit + GW_CONNECTOR + redefine + initialize + end + +create + make, + make_with_base + +feature {NONE} -- Initialization + + make_with_base (a_app: like application; a_base: like base) + do + make (a_app) + base := a_base + end + +feature {NONE} -- Initialization + + initialize + local + cfg: HTTP_SERVER_CONFIGURATION + do + create cfg.make + create server.make (cfg) + end + +feature -- Access + + server: HTTP_SERVER + + configuration: HTTP_SERVER_CONFIGURATION + do + Result := server.configuration + end + +feature -- Access + + base: detachable STRING + -- Root url base + +feature -- Element change + + set_base (b: like base) + do + base := b + end + +feature -- Server + + launch + local + l_http_handler : HTTP_HANDLER + do + create {GW_NINO_HANDLER} l_http_handler.make_with_callback (server, "GW_NINO_HANDLER", Current) + debug ("nino") + if attached base as l_base then + print ("Base=" + l_base + "%N") + end + end + server.setup (l_http_handler) + end + + process_request (env: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM) + local + gw_env: GW_ENVIRONMENT_VARIABLES + do + create gw_env.make_with_variables (env) + gw_env.set_variable ("RAW_HEADER_DATA", a_headers_text) + application.process (gw_env, create {GW_NINO_INPUT_STREAM}.make (a_input), create {GW_NINO_OUTPUT_STREAM}.make (a_output)) + end + +note + copyright: "Copyright (c) 1984-2011, 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/ewsgi/connectors/nino/src/gw_nino_handler.e b/library/server/ewsgi/connectors/nino/src/gw_nino_handler.e new file mode 100644 index 00000000..264ba393 --- /dev/null +++ b/library/server/ewsgi/connectors/nino/src/gw_nino_handler.e @@ -0,0 +1,156 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + GW_NINO_HANDLER + +inherit + HTTP_CONNECTION_HANDLER + +create + make_with_callback + +feature {NONE} -- Initialization + + make_with_callback (a_main_server: like main_server; a_name: STRING; a_callback: like callback) + -- Initialize `Current'. + do + make (a_main_server, a_name) + callback := a_callback + end + + callback: GW_NINO_CONNECTOR + +feature -- Access + + base: detachable STRING + -- Root url base + +feature -- Element change + + set_base (a_uri: like base) + -- Set `base' to `a_uri' + do + base := a_uri + end + +feature -- Request processing + + process_request (a_handler: HTTP_CONNECTION_HANDLER; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM) + -- Process request ... + local + env, vars: HASH_TABLE [STRING, STRING] + p: INTEGER + l_request_uri, l_script_name, l_query_string, l_path_info: STRING + l_server_name, l_server_port: detachable STRING + a_headers_map: HASH_TABLE [STRING, STRING] + vn: STRING + + e: EXECUTION_ENVIRONMENT + do + l_request_uri := a_handler.uri + a_headers_map := a_handler.request_header_map + create e + vars := e.starting_environment_variables + env := vars.twin + + --| for Any Abc-Def-Ghi add (or replace) the HTTP_ABC_DEF_GHI variable to `env' + from + a_headers_map.start + until + a_headers_map.after + loop + vn := a_headers_map.key_for_iteration.as_upper + vn.replace_substring_all ("-", "_") + add_environment_variable (a_headers_map.item_for_iteration, vn, env) + a_headers_map.forth + end + + --| Specific cases + + p := l_request_uri.index_of ('?', 1) + if p > 0 then + l_script_name := l_request_uri.substring (1, p - 1) + l_query_string := l_request_uri.substring (p + 1, l_request_uri.count) + else + l_script_name := l_request_uri.string + l_query_string := "" + end + if attached a_headers_map.item ("Host") as l_host then + add_environment_variable (l_host, "HTTP_HOST", env) + p := l_host.index_of (':', 1) + if p > 0 then + l_server_name := l_host.substring (1, p - 1) + l_server_port := l_host.substring (p+1, l_host.count) + else + l_server_name := l_host + l_server_port := "80" -- Default + end + end + + if attached a_headers_map.item ("Authorization") as l_authorization then + add_environment_variable (l_authorization, "HTTP_AUTHORIZATION", env) + p := l_authorization.index_of (' ', 1) + if p > 0 then + add_environment_variable (l_authorization.substring (1, p - 1), "AUTH_TYPE", env) + end + end + + add_environment_variable ("CGI/1.1", "GATEWAY_INTERFACE", env) + add_environment_variable (l_query_string, "QUERY_STRING", env) + + if attached a_handler.remote_info as l_remote_info then + add_environment_variable (l_remote_info.addr, "REMOTE_ADDR", env) + add_environment_variable (l_remote_info.hostname, "REMOTE_HOST", env) + add_environment_variable (l_remote_info.port.out, "REMOTE_PORT", env) +-- add_environment_variable (Void, "REMOTE_IDENT", env) +-- add_environment_variable (Void, "REMOTE_USER", env) + end + + add_environment_variable (l_request_uri, "REQUEST_URI", env) + add_environment_variable (a_handler.method, "REQUEST_METHOD", env) + + add_environment_variable (l_script_name, "SCRIPT_NAME", env) + add_environment_variable (l_server_name, "SERVER_NAME", env) + add_environment_variable (l_server_port, "SERVER_PORT", env) + add_environment_variable (a_handler.version, "SERVER_PROTOCOL", env) + add_environment_variable ({HTTP_SERVER_CONFIGURATION}.Server_details, "SERVER_SOFTWARE", env) + + --| Apply `base' value + if attached base as l_base and then l_request_uri /= Void then + if l_request_uri.starts_with (l_base) then + l_path_info := l_request_uri.substring (l_base.count + 1, l_request_uri.count) + p := l_path_info.index_of ('?', 1) + if p > 0 then + l_path_info.keep_head (p - 1) + end + env.force (l_path_info, "PATH_INFO") + env.force (l_base, "SCRIPT_NAME") + end + end + + callback.process_request (env, a_handler.request_header, a_input, a_output) + end + + add_environment_variable (a_value: detachable STRING; a_var_name: STRING; env: HASH_TABLE [STRING, STRING]) + -- Add variable `a_var_name => a_value' to `env' + do + if a_value /= Void then + env.force (a_value, a_var_name) + end + end + +note + copyright: "2011-2011, 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/ewsgi/connectors/nino/src/gw_nino_input_stream.e b/library/server/ewsgi/connectors/nino/src/gw_nino_input_stream.e new file mode 100644 index 00000000..a5f97038 --- /dev/null +++ b/library/server/ewsgi/connectors/nino/src/gw_nino_input_stream.e @@ -0,0 +1,61 @@ +note + description: "Summary description for {GW_NINO_INPUT_STREAM}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_NINO_INPUT_STREAM + +inherit + GW_INPUT_STREAM + +create + make + +feature {NONE} -- Initialization + + make (a_nino_input: like nino_input) + do + create last_string.make_empty + set_nino_input (a_nino_input) + end + +feature {GW_NINO_APPLICATION} -- Nino + + set_nino_input (i: like nino_input) + do + nino_input := i + end + + nino_input: HTTP_INPUT_STREAM + +feature -- Basic operation + + read_stream (nb_char: INTEGER) + -- Read a string of at most `nb_char' bound characters + -- or until end of file. + -- Make result available in `last_string'. + do + nino_input.read_stream (nb_char) + last_string := nino_input.last_string + end + +feature -- Access + + last_string: STRING + -- Last string read + +;note + copyright: "Copyright (c) 1984-2011, 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/ewsgi/connectors/nino/src/gw_nino_output_stream.e b/library/server/ewsgi/connectors/nino/src/gw_nino_output_stream.e new file mode 100644 index 00000000..ff4b3e75 --- /dev/null +++ b/library/server/ewsgi/connectors/nino/src/gw_nino_output_stream.e @@ -0,0 +1,60 @@ +note + description: "Summary description for {GW_NINO_OUTPUT_STREAM}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_NINO_OUTPUT_STREAM + +inherit + GW_OUTPUT_STREAM + +create + make + +feature {NONE} -- Initialization + + make (a_nino_output: like nino_output) + do + set_nino_output (a_nino_output) + end + +feature {GW_NINO_APPLICATION} -- Nino + + set_nino_output (o: like nino_output) + do + nino_output := o + end + + nino_output: HTTP_OUTPUT_STREAM + +feature -- Basic operation + + put_string (s: STRING_8) + -- Send `s' to http client + do + debug ("nino") + print (s) + end + nino_output.put_string (s) + end + + flush + -- Flush the output stream + do + end + +note + copyright: "2011-2011, 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/ewsgi/doc/cgi_spec_1.0.txt b/library/server/ewsgi/doc/cgi_spec_1.0.txt similarity index 100% rename from library/ewsgi/doc/cgi_spec_1.0.txt rename to library/server/ewsgi/doc/cgi_spec_1.0.txt diff --git a/library/ewsgi/doc/cgi_spec_1.2.txt b/library/server/ewsgi/doc/cgi_spec_1.2.txt similarity index 100% rename from library/ewsgi/doc/cgi_spec_1.2.txt rename to library/server/ewsgi/doc/cgi_spec_1.2.txt diff --git a/library/ewsgi/ewsgi.ecf b/library/server/ewsgi/ewsgi-safe.ecf similarity index 81% rename from library/ewsgi/ewsgi.ecf rename to library/server/ewsgi/ewsgi-safe.ecf index fe78d298..578a1c68 100644 --- a/library/ewsgi/ewsgi.ecf +++ b/library/server/ewsgi/ewsgi-safe.ecf @@ -11,10 +11,8 @@ - - - GW_REQUEST_CONTEXT_IMP - - + + + diff --git a/library/server/ewsgi/ewsgi.ecf b/library/server/ewsgi/ewsgi.ecf new file mode 100644 index 00000000..ca19856a --- /dev/null +++ b/library/server/ewsgi/ewsgi.ecf @@ -0,0 +1,18 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + + diff --git a/library/server/ewsgi/examples/hello_world/hello-safe.ecf b/library/server/ewsgi/examples/hello_world/hello-safe.ecf new file mode 100644 index 00000000..bc9bc974 --- /dev/null +++ b/library/server/ewsgi/examples/hello_world/hello-safe.ecf @@ -0,0 +1,19 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + + + diff --git a/library/server/ewsgi/examples/hello_world/src/hello_world.e b/library/server/ewsgi/examples/hello_world/src/hello_world.e new file mode 100644 index 00000000..59649b48 --- /dev/null +++ b/library/server/ewsgi/examples/hello_world/src/hello_world.e @@ -0,0 +1,58 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + HELLO_WORLD + +inherit + GW_APPLICATION_IMP + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + local + conn: detachable GW_CONNECTOR + nino_conn: GW_NINO_CONNECTOR + do + if is_nino then + create nino_conn.make_with_base (Current, "/hello_world") + if attached nino_conn.server.server_configuration as cfg then + cfg.http_server_port := 8080 + cfg.force_single_threaded := True + end + conn := nino_conn + elseif is_cgi then + create {GW_CGI_CONNECTOR} conn.make (Current) + elseif is_libfcgi then + create {GW_LIBFCGI_CONNECTOR} conn.make (Current) + else + io.error.put_string ("Unsupported connector") + end + if conn /= Void then + conn.launch + end + end + + is_nino: BOOLEAN = True + is_cgi: BOOLEAN = False + is_libfcgi: BOOLEAN = False + +feature -- Execution + + execute (ctx: GW_REQUEST_CONTEXT) + -- Execute the request + do + ctx.output.put_string ("Hello World!%N") + if attached ctx.execution_variable ("REQUEST_COUNT") as rq_count then + ctx.output.put_string ("Request #" + rq_count + "%N") + end + end + +end diff --git a/library/ewsgi/license.lic b/library/server/ewsgi/license.lic similarity index 100% rename from library/ewsgi/license.lic rename to library/server/ewsgi/license.lic diff --git a/library/ewsgi/src/gw_environment.e b/library/server/ewsgi/src/context/gw_environment.e similarity index 95% rename from library/ewsgi/src/gw_environment.e rename to library/server/ewsgi/src/context/gw_environment.e index 2a974d52..1c7f4146 100644 --- a/library/ewsgi/src/gw_environment.e +++ b/library/server/ewsgi/src/context/gw_environment.e @@ -143,19 +143,19 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 auth_type: detachable STRING -- This variable is specific to requests made via the "http" -- scheme. - -- + -- -- If the Script-URI required access authentication for external -- access, then the server MUST set the value of this variable -- from the 'auth-scheme' token in the request's "Authorization" -- header field. Otherwise it is set to NULL. - -- + -- -- AUTH_TYPE = "" | auth-scheme -- auth-scheme = "Basic" | "Digest" | token - -- + -- -- HTTP access authentication schemes are described in section 11 -- of the HTTP/1.1 specification [8]. The auth-scheme is not -- case-sensitive. - -- + -- -- Servers MUST provide this metavariable to scripts if the -- request header included an "Authorization" field that was -- authenticated. @@ -169,9 +169,9 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- either NULL or not defined. The syntax is the same as for the -- HTTP "Content-Length" header field (section 14.14, HTTP/1.1 -- specification [8]). - -- + -- -- CONTENT_LENGTH = "" | 1*digit - -- + -- -- Servers MUST provide this metavariable to scripts if the -- request was accompanied by a message-body entity. deferred @@ -189,7 +189,7 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- or if the server can determine it in the absence of a supplied -- "Content-type" field. The syntax is the same as for the HTTP -- "Content-Type" header field. - -- + -- -- CONTENT_TYPE = "" | media-type -- media-type = type "/" subtype *( ";" parameter) -- type = token @@ -197,16 +197,16 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- parameter = attribute "=" value -- attribute = token -- value = token | quoted-string - -- + -- -- The type, subtype, and parameter attribute names are not -- case-sensitive. Parameter values MAY be case sensitive. Media -- types and their use in HTTP are described in section 3.7 of -- the HTTP/1.1 specification [8]. - -- + -- -- Example: - -- + -- -- application/x-www-form-urlencoded - -- + -- -- There is no default value for this variable. If and only if it -- is unset, then the script MAY attempt to determine the media -- type from the data received. If the type remains unknown, then @@ -214,7 +214,7 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- application/octet-stream or reject the request with a 415 -- ("Unsupported Media Type") error. See section 7.2.1.3 for more -- information about returning error status values. - -- + -- -- Servers MUST provide this metavariable to scripts if a -- "Content-Type" field was present in the original request -- header. If the server receives a request with an attached @@ -228,23 +228,23 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 gateway_interface: STRING -- This metavariable is set to the dialect of CGI being used by -- the server to communicate with the script. Syntax: - -- + -- -- GATEWAY_INTERFACE = "CGI" "/" major "." minor -- major = 1*digit -- minor = 1*digit - -- + -- -- Note that the major and minor numbers are treated as separate -- integers and hence each may be more than a single digit. Thus -- CGI/2.4 is a lower version than CGI/2.13 which in turn is -- lower than CGI/12.3. Leading zeros in either the major or the -- minor number MUST be ignored by scripts and SHOULD NOT be -- generated by servers. - -- + -- -- This document defines the 1.1 version of the CGI interface -- ("CGI/1.1"). - -- + -- -- Servers MUST provide this metavariable to scripts. - -- + -- -- The version of the CGI specification to which this server -- complies. Syntax: -- @@ -258,7 +258,7 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 deferred end - path_info: STRING + path_info: STRING assign update_path_info -- The PATH_INFO metavariable specifies a path to be interpreted -- by the CGI script. It identifies the resource or sub-resource -- to be returned by the CGI script, and it is derived from the @@ -267,21 +267,21 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- to a decoded HTTP URL 'path' token (defined in RFC 2396 [4]), -- with the exception that a PATH_INFO of "/" represents a single -- void path segment. - -- + -- -- PATH_INFO = "" | ( "/" path ) -- path = segment *( "/" segment ) -- segment = *pchar -- pchar = - -- + -- -- The PATH_INFO string is the trailing part of the -- component of the Script-URI (see section 3.2) that follows the -- SCRIPT_NAME portion of the path. - -- + -- -- Servers MAY impose their own restrictions and limitations on -- what values they will accept for PATH_INFO, and MAY reject or -- edit any values they consider objectionable before passing -- them to the script. - -- + -- -- Servers MUST make this URI component available to CGI scripts. -- The PATH_INFO value is case-sensitive, and the server MUST -- preserve the case of the PATH_INFO element of the URI when @@ -297,27 +297,27 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- map it onto the server's document repository structure. If the -- request URI includes no path-info component, the -- PATH_TRANSLATED metavariable SHOULD NOT be defined. - -- - -- + -- + -- -- PATH_TRANSLATED = *CHAR - -- + -- -- For a request such as the following: - -- + -- -- http://somehost.com/cgi-bin/somescript/this%2eis%2epath%2einfo -- -- the PATH_INFO component would be decoded, and the result -- parsed as though it were a request for the following: - -- + -- -- http://somehost.com/this.is.the.path.info - -- + -- -- This would then be translated to a location in the server's -- document repository, perhaps a filesystem path something like -- this: - -- + -- -- /usr/local/www/htdocs/this.is.the.path.info - -- + -- -- The result of the translation is the value of PATH_TRANSLATED. - -- + -- -- The value of PATH_TRANSLATED may or may not map to a valid -- repository location. Servers MUST preserve the case of the -- path-info segment if and only if the underlying repository @@ -325,11 +325,11 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- case-aware, case-preserving, or case-blind with regard to -- document names, servers are not required to preserve the case -- of the original segment through the translation. - -- + -- -- The translation algorithm the server uses to derive -- PATH_TRANSLATED is implementation defined; CGI scripts which -- use this variable may suffer limited portability. - -- + -- -- Servers SHOULD provide this metavariable to scripts if and -- only if the request URI includes a path-info component. deferred @@ -338,13 +338,13 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 query_string: STRING -- A URL-encoded string; the part of the Script-URI. (See -- section 3.2.) - -- + -- -- QUERY_STRING = query-string -- query-string = *uric -- The URL syntax for a query string is described in section 3 of -- RFC 2396 [4]. - -- + -- -- Servers MUST supply this value to scripts. The QUERY_STRING -- value is case-sensitive. If the Script-URI does not include a -- query component, the QUERY_STRING metavariable MUST be defined @@ -356,13 +356,13 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- The IP address of the client sending the request to the -- server. This is not necessarily that of the user agent (such -- as if the request came through a proxy). - -- + -- -- REMOTE_ADDR = hostnumber -- hostnumber = ipv4-address | ipv6-address -- The definitions of ipv4-address and ipv6-address are provided -- in Appendix B of RFC 2373 [13]. - -- + -- -- Servers MUST supply this value to scripts. deferred end @@ -373,7 +373,7 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- section 6.1.9.) Fully qualified domain names take the form as -- described in section 3.5 of RFC 1034 [10] and section 2.1 of -- RFC 1123 [5]. Domain names are not case sensitive. - -- + -- -- Servers SHOULD provide this information to scripts. deferred end @@ -383,12 +383,12 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- RFC 1413 [11] request to the remote agent, if available. -- Servers MAY choose not to support this feature, or not to -- request the data for efficiency reasons. - -- + -- -- REMOTE_IDENT = *CHAR - -- + -- -- The data returned may be used for authentication purposes, but -- the level of trust reposed in them should be minimal. - -- + -- -- Servers MAY supply this information to scripts if the RFC1413 -- [11] lookup is performed. deferred @@ -400,12 +400,12 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- "Basic"), then the value of the REMOTE_USER metavariable is -- set to the user-ID supplied. In all other cases the value of -- this metavariable is undefined. - -- + -- -- REMOTE_USER = *OCTET - -- + -- -- This variable is specific to requests made via the HTTP -- protocol. - -- + -- -- Servers SHOULD provide this metavariable to scripts. deferred end @@ -415,18 +415,18 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- which the request was made, as described in section 5.1.1 of -- the HTTP/1.0 specification [3] and section 5.1.1 of the -- HTTP/1.1 specification [8]. - -- + -- -- REQUEST_METHOD = http-method -- http-method = "GET" | "HEAD" | "POST" | "PUT" | "DELETE" -- | "OPTIONS" | "TRACE" | extension-method -- extension-method = token - -- + -- -- The method is case sensitive. CGI/1.1 servers MAY choose to -- process some methods directly rather than passing them to -- scripts. - -- + -- -- This variable is specific to requests made with HTTP. - -- + -- -- Servers MUST provide this metavariable to scripts. deferred end @@ -436,15 +436,15 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- identify the CGI script (rather than the script's output). The -- syntax and semantics are identical to a decoded HTTP URL -- 'path' token (see RFC 2396 [4]). - -- + -- -- SCRIPT_NAME = "" | ( "/" [ path ] ) - -- + -- -- The SCRIPT_NAME string is some leading part of the -- component of the Script-URI derived in some implementation -- defined manner. No PATH_INFO or QUERY_STRING segments (see -- sections 6.1.6 and 6.1.8) are included in the SCRIPT_NAME -- value. - -- + -- -- Servers MUST provide this metavariable to scripts. deferred end @@ -453,9 +453,9 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- The SERVER_NAME metavariable is set to the name of the server, -- as derived from the part of the Script-URI (see section -- 3.2). - -- + -- -- SERVER_NAME = hostname | hostnumber - -- + -- -- Servers MUST provide this metavariable to scripts. deferred end @@ -464,13 +464,13 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- The SERVER_PORT metavariable is set to the port on which the -- request was received, as used in the part of the -- Script-URI. - -- + -- -- SERVER_PORT = 1*digit - -- + -- -- If the portion of the script-URI is blank, the actual -- port number upon which the request was received MUST be -- supplied. - -- + -- -- Servers MUST provide this metavariable to scripts. deferred end @@ -480,25 +480,25 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- revision of the information protocol with which the request -- arrived. This is not necessarily the same as the protocol -- version used by the server in its response to the client. - -- + -- -- SERVER_PROTOCOL = HTTP-Version | extension-version -- | extension-token -- HTTP-Version = "HTTP" "/" 1*digit "." 1*digit -- extension-version = protocol "/" 1*digit "." 1*digit -- protocol = 1*( alpha | digit | "+" | "-" | "." ) -- extension-token = token - -- + -- -- 'protocol' is a version of the part of the -- Script-URI, but is not identical to it. For example, the -- scheme of a request may be "https" while the protocol remains -- "http". The protocol is not case sensitive, but by convention, -- 'protocol' is in upper case. - -- + -- -- A well-known extension token value is "INCLUDED", which -- signals that the current document is being included as part of -- a composite document, rather than being the direct target of -- the client request. - -- + -- -- Servers MUST provide this metavariable to scripts. deferred end @@ -507,7 +507,7 @@ feature -- Common Gateway Interface - 1.1 8 January 1996 -- The SERVER_SOFTWARE metavariable is set to the name and -- version of the information server software answering the -- request (and running the gateway). - -- + -- -- SERVER_SOFTWARE = 1*product -- product = token [ "/" product-version ] -- product-version = token @@ -581,6 +581,31 @@ feature -- Extra deferred end +feature {GW_REQUEST_CONTEXT} -- Element change + + set_orig_path_info (s: STRING) + -- Set ORIG_PATH_INFO to `s' + require + s_attached: s /= Void + deferred + ensure + same_orig_path_info: orig_path_info ~ variable ({GW_ENVIRONMENT_NAMES}.orig_path_info) + end + + unset_orig_path_info + -- Unset ORIG_PATH_INFO + deferred + ensure + unset: not has_variable ({GW_ENVIRONMENT_NAMES}.orig_path_info) + end + + update_path_info (a_path_info: like path_info) + -- Updated PATH_INFO + deferred + ensure + same_path_info: path_info ~ variable ({GW_ENVIRONMENT_NAMES}.path_info) + end + invariant server_name_not_empty: not server_name.is_empty server_port_set: server_port /= 0 @@ -589,6 +614,8 @@ invariant query_string_attached: query_string /= Void remote_addr_attached: remote_addr /= Void + path_info_identical: path_info ~ variable ({GW_ENVIRONMENT_NAMES}.path_info) + ;note copyright: "2011-2011, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/library/server/ewsgi/src/context/gw_environment_variables.e b/library/server/ewsgi/src/context/gw_environment_variables.e new file mode 100644 index 00000000..392dbd3d --- /dev/null +++ b/library/server/ewsgi/src/context/gw_environment_variables.e @@ -0,0 +1,292 @@ +note + description: "Summary description for {GW_ENVIRONMENT_VARIABLES}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_ENVIRONMENT_VARIABLES + +inherit + GW_ENVIRONMENT + redefine + update_path_info + end + +create + make_with_variables + +feature {NONE} -- Initialization + + make_with_variables (a_vars: HASH_TABLE [STRING, STRING]) + -- Fill with variable from `a_vars' + local + s: detachable STRING + do + create empty_string.make_empty + + create table.make (a_vars.count) + from + a_vars.start + until + a_vars.after + loop + table.force (a_vars.item_for_iteration, a_vars.key_for_iteration) + a_vars.forth + end + + --| QUERY_STRING + query_string := variable_or_default ({GW_ENVIRONMENT_NAMES}.query_string, empty_string, False) + + --| REQUEST_METHOD + request_method := variable_or_default ({GW_ENVIRONMENT_NAMES}.request_method, empty_string, False) + + --| CONTENT_TYPE + s := variable ({GW_ENVIRONMENT_NAMES}.content_type) + if s /= Void and then not s.is_empty then + content_type := s + else + content_type := Void + end + + --| CONTENT_LENGTH + s := variable ({GW_ENVIRONMENT_NAMES}.content_length) + content_length := s + if s /= Void and then s.is_integer then + content_length_value := s.to_integer + else + --| content_length := 0 + end + + --| PATH_INFO + path_info := variable_or_default ({GW_ENVIRONMENT_NAMES}.path_info, empty_string, False) + + --| SERVER_NAME + server_name := variable_or_default ({GW_ENVIRONMENT_NAMES}.server_name, empty_string, False) + + --| SERVER_PORT + s := variable ({GW_ENVIRONMENT_NAMES}.server_port) + if s /= Void and then s.is_integer then + server_port := s.to_integer + else + server_port := 80 + end + + --| SCRIPT_NAME + script_name := variable_or_default ({GW_ENVIRONMENT_NAMES}.script_name, empty_string, False) + + --| REMOTE_ADDR + remote_addr := variable_or_default ({GW_ENVIRONMENT_NAMES}.remote_addr, empty_string, False) + + --| REMOTE_HOST + remote_host := variable_or_default ({GW_ENVIRONMENT_NAMES}.remote_host, empty_string, False) + + --| REQUEST_URI + request_uri := variable_or_default ({GW_ENVIRONMENT_NAMES}.request_uri, empty_string, False) + end + +feature -- Access + + table: HASH_TABLE [STRING, STRING] + +feature -- Access + + variable (a_name: STRING): detachable STRING + do + Result := table.item (a_name) + end + + has_variable (a_name: STRING): BOOLEAN + do + Result := table.has_key (a_name) + end + +feature {GW_REQUEST_CONTEXT, GW_APPLICATION, GW_CONNECTOR} -- Element change + + set_variable (a_name: STRING; a_value: STRING) + do + table.force (a_value, a_name) + end + + unset_variable (a_name: STRING) + do + table.remove (a_name) + end + +feature -- Common Gateway Interface - 1.1 8 January 1996 + + auth_type: detachable STRING + + content_length: detachable STRING + + content_length_value: INTEGER + + content_type: detachable STRING + + gateway_interface: STRING + do + Result := variable_or_default ({GW_ENVIRONMENT_NAMES}.gateway_interface, "", False) + end + + path_info: STRING + -- + -- + --| For instance, if the current script was accessed via the URL + --| http://www.example.com/eiffel/path_info.exe/some/stuff?foo=bar, then $_SERVER['PATH_INFO'] would contain /some/stuff. + --| + --| Note that is the PATH_INFO variable does not exists, the `path_info' value will be empty + + path_translated: detachable STRING + do + Result := variable ({GW_ENVIRONMENT_NAMES}.path_translated) + end + + query_string: STRING + + remote_addr: STRING + + remote_host: STRING + + remote_ident: detachable STRING + do + Result := variable ({GW_ENVIRONMENT_NAMES}.remote_ident) + end + + remote_user: detachable STRING + do + Result := variable ({GW_ENVIRONMENT_NAMES}.remote_user) + end + + request_method: STRING + + script_name: STRING + + server_name: STRING + + server_port: INTEGER + + server_protocol: STRING + do + Result := variable_or_default ({GW_ENVIRONMENT_NAMES}.server_protocol, "HTTP/1.0", True) + end + + server_software: STRING + do + Result := variable_or_default ({GW_ENVIRONMENT_NAMES}.server_software, "Unknown Server", True) + end + +feature -- HTTP_* + + http_accept: detachable STRING + -- Contents of the Accept: header from the current request, if there is one. + do + Result := table.item ({GW_ENVIRONMENT_NAMES}.http_accept) + end + + http_accept_charset: detachable STRING + -- Contents of the Accept-Charset: header from the current request, if there is one. + -- Example: 'iso-8859-1,*,utf-8'. + do + Result := table.item ({GW_ENVIRONMENT_NAMES}.http_accept_charset) + end + + http_accept_encoding: detachable STRING + -- Contents of the Accept-Encoding: header from the current request, if there is one. + -- Example: 'gzip'. + do + Result := table.item ({GW_ENVIRONMENT_NAMES}.http_accept_encoding) + end + + http_accept_language: detachable STRING + -- Contents of the Accept-Language: header from the current request, if there is one. + -- Example: 'en'. + do + Result := table.item ({GW_ENVIRONMENT_NAMES}.http_accept_language) + end + + http_connection: detachable STRING + -- Contents of the Connection: header from the current request, if there is one. + -- Example: 'Keep-Alive'. + do + Result := table.item ({GW_ENVIRONMENT_NAMES}.http_connection) + end + + http_host: detachable STRING + -- Contents of the Host: header from the current request, if there is one. + do + Result := table.item ({GW_ENVIRONMENT_NAMES}.http_host) + end + + http_referer: detachable STRING + -- The address of the page (if any) which referred the user agent to the current page. + -- This is set by the user agent. + -- Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature. + -- In short, it cannot really be trusted. + do + Result := table.item ({GW_ENVIRONMENT_NAMES}.http_referer) + end + + http_user_agent: detachable STRING + -- Contents of the User-Agent: header from the current request, if there is one. + -- This is a string denoting the user agent being which is accessing the page. + -- A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586). + -- Among other things, you can use this value to tailor your page's + -- output to the capabilities of the user agent. + do + Result := table.item ({GW_ENVIRONMENT_NAMES}.http_user_agent) + end + + http_authorization: detachable STRING + -- Contents of the Authorization: header from the current request, if there is one. + do + Result := table.item ({GW_ENVIRONMENT_NAMES}.http_authorization) + end + +feature -- Extra + + request_uri: STRING + -- The URI which was given in order to access this page; for instance, '/index.html'. + + orig_path_info: detachable STRING + -- Original version of `path_info' before processed by Current environment + +feature {GW_REQUEST_CONTEXT} -- Update + + set_orig_path_info (s: STRING) + do + orig_path_info := s + set_variable ({GW_ENVIRONMENT_NAMES}.orig_path_info, s) + end + + unset_orig_path_info + do + orig_path_info := Void + unset_variable ({GW_ENVIRONMENT_NAMES}.orig_path_info) + end + + update_path_info (a_path_info: like path_info) + do + path_info := a_path_info + set_variable ({GW_ENVIRONMENT_NAMES}.path_info, a_path_info) + end + +feature {NONE} -- Implementation + + empty_string: STRING + -- Reusable empty string + +invariant + empty_string_unchanged: empty_string.is_empty + +;note + copyright: "2011-2011, 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/ewsgi/src/gw_execution_variables.e b/library/server/ewsgi/src/context/gw_execution_variables.e similarity index 70% rename from library/ewsgi/src/gw_execution_variables.e rename to library/server/ewsgi/src/context/gw_execution_variables.e index 8d0f02fa..7408955c 100644 --- a/library/ewsgi/src/gw_execution_variables.e +++ b/library/server/ewsgi/src/context/gw_execution_variables.e @@ -31,29 +31,16 @@ feature -- Status report Result := has (a_name) end -feature -- Element change +feature {GW_REQUEST_CONTEXT, GW_APPLICATION, GW_CONNECTOR} -- Element change - replace_variable (v: STRING; k: STRING) - -- Replace variable `k' + set_variable (a_name: STRING; a_value: STRING_32) do - force (v, k) + force (a_value, a_name) end - add_variable (v: STRING; k: STRING) - -- Add variable `k' with value `v' - require - k_attached: k /= Void - v_attached: k /= Void + unset_variable (a_name: STRING) do - force (v, k) - end - - delete_variable (k: STRING) - -- Remove variable `k' - require - k_attached: k /= Void - do - remove (k) + remove (a_name) end note diff --git a/library/ewsgi/src/gw_request_context.e b/library/server/ewsgi/src/context/gw_request_context.e similarity index 85% rename from library/ewsgi/src/gw_request_context.e rename to library/server/ewsgi/src/context/gw_request_context.e index d4bb4966..e6d62060 100644 --- a/library/ewsgi/src/gw_request_context.e +++ b/library/server/ewsgi/src/context/gw_request_context.e @@ -17,13 +17,13 @@ deferred class feature -- Access: Input/Output - output: GW_OUTPUT_STREAM - -- Server output channel + input: GW_INPUT_STREAM + -- Server input channel deferred end - input: GW_INPUT_STREAM - -- Server input channel + output: GW_OUTPUT_STREAM + -- Server output channel deferred end @@ -31,8 +31,8 @@ feature -- Access: global variable variables: HASH_TABLE [STRING_32, STRING_32] -- Table containing all the various variables - -- Warning: this is computed each time, if you change this content of other container - -- this won't update this Result's content. + -- Warning: this is computed each time, if you change the content of other containers + -- this won't update this Result's content, unless you query it again local vars: HASH_TABLE [STRING_GENERAL, STRING_GENERAL] do @@ -100,11 +100,11 @@ feature -- Access: global variable if s = Void then s := environment_variable (n8) if s = Void then - s := parameters.variable (n8) + s := parameter (n8) if s = Void then - s := form_fields.variable (n8) + s := form_field (n8) if s = Void then - s := cookies_variables.item (n8) + s := cookies_variable (n8) end end end @@ -114,6 +114,13 @@ feature -- Access: global variable end end +feature -- Access: environment extra values + + request_time: detachable DATE_TIME + -- Request time (UTC) + deferred + end + feature -- Access: environment variables environment: GW_ENVIRONMENT @@ -144,13 +151,27 @@ feature -- Access: execution variables Result := execution_variables.variable (a_name) end -feature -- Queries +feature -- URL Parameters + + parameter (n: STRING): detachable STRING_32 + -- Parameter for name `n'. + do + Result := parameters.variable (n) + end parameters: GW_REQUEST_VARIABLES -- Variables extracted from QUERY_STRING deferred end +feature -- Form fields and related + + form_field (n: STRING): detachable STRING_32 + -- Field for name `n'. + do + Result := form_fields.variable (n) + end + form_fields: GW_REQUEST_VARIABLES -- Variables sent by POST request deferred @@ -164,6 +185,16 @@ feature -- Queries --| tmp_base_name: basename of `tmp_name' --| error: if /= 0 , there was an error : TODO ... --| size: size of the file given by the http request + deferred + end + +feature -- Cookies + + cookies_variable (n: STRING): detachable STRING + -- Field for name `n'. + do + Result := cookies_variables.item (n) + end cookies_variables: HASH_TABLE [STRING, STRING] -- Expanded cookies variable diff --git a/library/ewsgi/src/gw_request_variables.e b/library/server/ewsgi/src/context/gw_request_variables.e similarity index 82% rename from library/ewsgi/src/gw_request_variables.e rename to library/server/ewsgi/src/context/gw_request_variables.e index a3f6850c..00fc5069 100644 --- a/library/ewsgi/src/gw_request_variables.e +++ b/library/server/ewsgi/src/context/gw_request_variables.e @@ -16,8 +16,8 @@ inherit ITERABLE [STRING_32] create - make--, --- make_from_urlencoded + make, + make_from_urlencoded feature -- Initialization @@ -27,11 +27,11 @@ feature -- Initialization table.compare_objects end --- make_from_urlencoded (a_content: STRING; decoding: BOOLEAN) --- do --- make (a_content.occurrences ('&') + 1) --- import_urlencoded (a_content, decoding) --- end + make_from_urlencoded (a_content: STRING; decoding: BOOLEAN) + do + make (a_content.occurrences ('&') + 1) + import_urlencoded (a_content, decoding) + end feature -- Status report @@ -51,15 +51,28 @@ feature -- Status report Result := table.has (a_name) end +feature {GW_REQUEST_CONTEXT, GW_APPLICATION, GW_CONNECTOR} -- Element change + + set_variable (a_name: STRING; a_value: STRING_32) + do + table.force (a_value, a_name) + end + + unset_variable (a_name: STRING) + do + table.remove (a_name) + end + feature -- Import urlencoded --- import_urlencoded (a_content: STRING; decoding: BOOLEAN) --- -- Import `a_content' --- local + import_urlencoded (a_content: STRING; decoding: BOOLEAN) + -- Import `a_content' + local -- n, p, i, j: INTEGER -- s: STRING -- l_name,l_value: STRING_32 --- do + do +-- FIXME -- n := a_content.count -- if n > 0 then -- from @@ -89,7 +102,7 @@ feature -- Import urlencoded -- end -- end -- end --- end + end feature -- Access: table diff --git a/library/ewsgi/src/gw_variables.e b/library/server/ewsgi/src/context/gw_variables.e similarity index 75% rename from library/ewsgi/src/gw_variables.e rename to library/server/ewsgi/src/context/gw_variables.e index 9aac05a8..818eda07 100644 --- a/library/ewsgi/src/gw_variables.e +++ b/library/server/ewsgi/src/context/gw_variables.e @@ -46,6 +46,24 @@ feature -- Access end end +feature {GW_REQUEST_CONTEXT, GW_APPLICATION, GW_CONNECTOR} -- Element change + + set_variable (a_name: STRING; a_value: G) + require + a_name_not_empty: a_name /= Void and then not a_name.is_empty + deferred + ensure + variable_set: has_variable (a_name) and then variable (a_name) ~ a_value + end + + unset_variable (a_name: STRING) + require + a_name_not_empty: a_name /= Void and then not a_name.is_empty + deferred + ensure + variable_unset: not has_variable (a_name) + end + note copyright: "2011-2011, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/library/ewsgi/src/gw_application.e b/library/server/ewsgi/src/gw_application.e similarity index 91% rename from library/ewsgi/src/gw_application.e rename to library/server/ewsgi/src/gw_application.e index 334d3456..28cdf494 100644 --- a/library/ewsgi/src/gw_application.e +++ b/library/server/ewsgi/src/gw_application.e @@ -11,6 +11,7 @@ deferred class feature -- Execution process (env: GW_ENVIRONMENT; a_input: GW_INPUT_STREAM; a_output: GW_OUTPUT_STREAM) + -- Process request with environment `env', and i/o streams `a_input' and `a_output' do execute (new_request_context (env, a_input, a_output)) end diff --git a/library/server/ewsgi/src/gw_connector.e b/library/server/ewsgi/src/gw_connector.e new file mode 100644 index 00000000..881a7e4c --- /dev/null +++ b/library/server/ewsgi/src/gw_connector.e @@ -0,0 +1,44 @@ +note + description: "Summary description for {GW_CONNECTOR}." + author: "" + date: "$Date$" + revision: "$Revision$" + +deferred class + GW_CONNECTOR + +feature {NONE} -- Initialization + + make (a_app: like application) + do + application := a_app + initialize + end + + initialize + -- Initialize connector + do + end + +feature {NONE} -- Access + + application: GW_APPLICATION + -- Gateway Application + +feature -- Server + + launch + deferred + end + +note + copyright: "2011-2011, 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/ewsgi/src/gw_cookie.e b/library/server/ewsgi/src/gw_cookie.e similarity index 100% rename from library/ewsgi/src/gw_cookie.e rename to library/server/ewsgi/src/gw_cookie.e diff --git a/library/ewsgi/src/gw_environment_names.e b/library/server/ewsgi/src/gw_environment_names.e similarity index 100% rename from library/ewsgi/src/gw_environment_names.e rename to library/server/ewsgi/src/gw_environment_names.e diff --git a/library/server/ewsgi/src/gw_error.e b/library/server/ewsgi/src/gw_error.e new file mode 100644 index 00000000..3cc1caf8 --- /dev/null +++ b/library/server/ewsgi/src/gw_error.e @@ -0,0 +1,66 @@ +note + description: "Summary description for {GW_ERROR}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_ERROR + +inherit + ERROR + + HTTP_STATUS_CODE_MESSAGES + +create + make + +feature {NONE} -- Initialization + + make (a_code: INTEGER) + do + code := a_code + name := "HTTP Error" + if attached http_status_code_message (a_code) as m then + name := m + end + end + +feature -- Access + + code: INTEGER + + name: STRING + + message: detachable STRING_32 + +feature -- Element change + + set_message (m: like message) + -- Set `message' to `m' + require + m_attached: m /= Void + do + message := m + end + +feature -- Visitor + + process (a_visitor: ERROR_VISITOR) + -- Process Current using `a_visitor'. + do + a_visitor.process_error (Current) + end + +note + copyright: "Copyright (c) 1984-2011, 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/ewsgi/src/implementation/gw_application_imp.e b/library/server/ewsgi/src/implementation/gw_application_imp.e new file mode 100644 index 00000000..1f1961cc --- /dev/null +++ b/library/server/ewsgi/src/implementation/gw_application_imp.e @@ -0,0 +1,50 @@ +note + description: "Summary description for {GW_APPLICATION_IMP} " + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + GW_APPLICATION_IMP + +inherit + GW_APPLICATION + redefine + process + end + +feature -- Access + + request_count: INTEGER + -- Request count + +feature -- Execution + + process (env: GW_ENVIRONMENT; a_input: GW_INPUT_STREAM; a_output: GW_OUTPUT_STREAM) + -- Process request with environment `env', and i/o streams `a_input' and `a_output' + do + request_count := request_count + 1 + Precursor (env, a_input, a_output) + end + +feature -- Factory + + new_request_context (env: GW_ENVIRONMENT; a_input: GW_INPUT_STREAM; a_output: GW_OUTPUT_STREAM): GW_REQUEST_CONTEXT + do + create {GW_REQUEST_CONTEXT_IMP} Result.make (env, a_input, a_output) + Result.execution_variables.set_variable (request_count.out, "REQUEST_COUNT") + end + +;note + copyright: "2011-2011, 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/ewsgi/src/implementation/gw_request_context_imp.e b/library/server/ewsgi/src/implementation/gw_request_context_imp.e new file mode 100644 index 00000000..cdd83630 --- /dev/null +++ b/library/server/ewsgi/src/implementation/gw_request_context_imp.e @@ -0,0 +1,818 @@ +note + description: "[ + Server request context of the httpd request + + You can create your own descendant of this class to + add/remove specific value or processing + + This object is created by {GW_APPLICATION}.new_request_context + ]" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + GW_REQUEST_CONTEXT_IMP + +inherit + GW_REQUEST_CONTEXT + +create + make + +feature {NONE} -- Initialization + + make (env: GW_ENVIRONMENT; a_input: like input; a_output: like output) + require + env_attached: env /= Void + do + create error_handler.make + input := a_input + output := a_output + environment := env + content_length := env.content_length_value + create execution_variables.make (10) + create uploaded_files.make (0) + + raw_post_data_recorded := True + + initialize + analyze + end + + initialize + -- Specific initialization + local + p: INTEGER + dt: DATE_TIME + env: like environment + do + env := environment + --| Here one can set its own environment entries if needed + + --| do not use `force', to avoid overwriting existing variable + if attached env.request_uri as rq_uri then + p := rq_uri.index_of ('?', 1) + if p > 0 then + env.set_variable (rq_uri.substring (1, p-1), {GW_ENVIRONMENT_NAMES}.self) + else + env.set_variable (rq_uri, {GW_ENVIRONMENT_NAMES}.self) + end + end + if env.variable ({GW_ENVIRONMENT_NAMES}.request_time) = Void then + env.set_variable (date_time_utilities.unix_time_stamp (Void).out, {GW_ENVIRONMENT_NAMES}.request_time) + end + end + + analyze + -- Analyze context, set various attributes and validate values + do + extract_variables + end + +feature -- Access: Input/Output + + output: GW_OUTPUT_STREAM + -- Server output channel + + input: GW_INPUT_STREAM + -- Server input channel + +feature -- Status + + raw_post_data_recorded: BOOLEAN assign set_raw_post_data_recorded + -- Record RAW POST DATA in environment variables + -- otherwise just forget about it + -- Default: true + --| warning: you might keep in memory big amount of memory ... + +feature -- Error handling + + has_error: BOOLEAN + do + Result := error_handler.has_error + end + + error_handler: ERROR_HANDLER + -- Error handler + -- By default initialized to new handler + +feature -- Access: environment variables + + environment: GW_ENVIRONMENT + -- Environment variables + + content_length: INTEGER + -- Extracted Content-Length value + +feature -- Access: execution variables + + execution_variables: GW_EXECUTION_VARIABLES + -- Execution variables set by the application + +feature -- URL parameters + + parameters: GW_REQUEST_VARIABLES + local + vars: like internal_parameters + p,e: INTEGER + rq_uri: like environment.request_uri + s: detachable STRING + do + vars := internal_parameters + if vars = Void then + s := environment.query_string + if s = Void then + rq_uri := environment.request_uri + p := rq_uri.index_of ('?', 1) + if p > 0 then + e := rq_uri.index_of ('#', p + 1) + if e = 0 then + e := rq_uri.count + else + e := e - 1 + end + s := rq_uri.substring (p+1, e) + end + end + if s /= Void and then not s.is_empty then + create vars.make_from_urlencoded (s, True) + else + create vars.make (0) + end + internal_parameters := vars + end + Result := vars + end + +feature -- Form fields and related + + form_fields: GW_REQUEST_VARIABLES + local + vars: like internal_form_fields + s: STRING + n: INTEGER + l_type: detachable STRING + do + vars := internal_form_fields + if vars = Void then + n := content_length + if n > 0 then + l_type := environment.content_type + if + l_type /= Void and then + l_type.starts_with ({HTTP_CONSTANTS}.multipart_form) + then + create vars.make (5) + --| FIXME: optimization ... fetch the input data progressively, otherwise we might run out of memory ... + s := form_input_data (n) + analyze_multipart_form (l_type, s, vars) + else + s := form_input_data (n) + create vars.make_from_urlencoded (s, True) + end + if raw_post_data_recorded then + vars.add_variable (s, "RAW_POST_DATA") + end + else + create vars.make (0) + end + internal_form_fields := vars + end + Result := vars + end + + uploaded_files: HASH_TABLE [TUPLE [name: STRING; type: STRING; tmp_name: STRING; tmp_basename: STRING; error: INTEGER; size: INTEGER], STRING] + -- Table of uploaded files information + --| name: original path from the user + --| type: content type + --| tmp_name: path to temp file that resides on server + --| tmp_base_name: basename of `tmp_name' + --| error: if /= 0 , there was an error : TODO ... + --| size: size of the file given by the http request + +feature -- Cookies + + cookies_variables: HASH_TABLE [STRING, STRING] + -- Expanded cookies variable + local + l_cookies: like cookies + do + l_cookies := cookies + create Result.make (l_cookies.count) + from + l_cookies.start + until + l_cookies.after + loop + if attached l_cookies.item_for_iteration.variables as vars then + from + vars.start + until + vars.after + loop + Result.force (vars.item_for_iteration, vars.key_for_iteration) + vars.forth + end + else + check same_name: l_cookies.key_for_iteration.same_string (l_cookies.item_for_iteration.name) end + Result.force (l_cookies.item_for_iteration.value, l_cookies.key_for_iteration) + end + l_cookies.forth + end + end + + cookies: HASH_TABLE [GW_COOKIE, STRING] + -- Cookies Information + local + i,j,p,n: INTEGER + l_cookies: like internal_cookies + k,v: STRING + do + l_cookies := internal_cookies + if l_cookies = Void then + if attached environment_variable ({GW_ENVIRONMENT_NAMES}.http_cookie) as s then + create l_cookies.make (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) + + p := 0 -- force termination + else + k := s.substring (p, i - 1) + v := s.substring (i + 1, j - 1) + p := j + 1 + end + l_cookies.put (create {GW_COOKIE}.make (k,v), k) + end + end + else + create l_cookies.make (0) + end + internal_cookies := l_cookies + end + Result := l_cookies + end + +feature -- Query + +-- script_absolute_url (a_path: STRING): STRING +-- -- Absolute Url for the script if any, extended by `a_path' +-- do +-- Result := script_url (a_path) +-- if attached http_host as h then +-- Result.prepend (h) +-- else +-- --| Issue ?? +-- end +-- end + +-- script_url (a_path: STRING): STRING +-- -- Url relative to script name if any, extended by `a_path' +-- require +-- a_path_attached: a_path /= Void +-- local +-- l_base_url: like script_url_base +-- i,m,n: INTEGER +-- l_rq_uri: like request_uri +-- do +-- l_base_url := script_url_base +-- if l_base_url = Void then +-- if attached environment.script_name as l_script_name then +-- l_rq_uri := request_uri +-- if l_rq_uri.starts_with (l_script_name) then +-- l_base_url := l_script_name +-- else +-- --| Handle Rewrite url engine, to have clean path +-- from +-- i := 1 +-- m := l_rq_uri.count +-- n := l_script_name.count +-- until +-- i > m or i > n or l_rq_uri[i] /= l_script_name[i] +-- loop +-- i := i + 1 +-- end +-- if i > 1 then +-- if l_rq_uri[i-1] = '/' then +-- i := i -1 +-- end +-- l_base_url := l_rq_uri.substring (1, i - 1) +-- end +-- end +-- end +-- if l_base_url = Void then +-- create l_base_url.make_empty +-- end +-- script_url_base := l_base_url +-- end +-- Result := l_base_url + a_path +-- end + +-- script_url_base: detachable STRING +-- -- URL base of potential script + +feature -- Access environment information + + request_time: detachable DATE_TIME + -- Request time (UTC) + do + if + attached environment.variable ({GW_ENVIRONMENT_NAMES}.request_time) as t and then + t.is_integer_64 + then + Result := date_time_utilities.unix_time_stamp_to_date_time (t.to_integer_64) + end + end + +feature -- Element change + + set_raw_post_data_recorded (b: BOOLEAN) + -- Set `raw_post_data_recorded' to `b' + do + raw_post_data_recorded := b + end + + set_error_handler (ehdl: like error_handler) + -- Set `error_handler' to `ehdl' + do + error_handler := ehdl + end + + update_path_info (env: GW_ENVIRONMENT) + -- Fix and update PATH_INFO value if needed + local + l_path_info: STRING + do + l_path_info := env.path_info + --| Warning + --| on IIS: we might have PATH_INFO = /sample.exe/foo/bar + --| on apache: PATH_INFO = /foo/bar + --| So, we might need to check with SCRIPT_NAME and remove it on IIS + --| store original PATH_INFO in ORIG_PATH_INFO + if l_path_info.is_empty then + env.unset_orig_path_info + else + env.set_orig_path_info (l_path_info) + if attached env.script_name as l_script_name then + if l_path_info.starts_with (l_script_name) then + env.path_info := l_path_info.substring (l_script_name.count + 1 , l_path_info.count) + end + end + end + end + +feature -- Uploaded File Handling + + move_uploaded_file (a_filename: STRING; a_destination: STRING): BOOLEAN + -- Move uploaded file `a_filename' to `a_destination' + --| if this is not an uploaded file, do not move it. + local + f: RAW_FILE + do + if is_uploaded_file (a_filename) then + create f.make (a_filename) + if f.exists then + f.change_name (a_destination) + Result := True + end + end + end + + is_uploaded_file (a_filename: STRING): BOOLEAN + -- Is `a_filename' a file uploaded via HTTP Form + local + l_files: like uploaded_files + do + l_files := uploaded_files + if not l_files.is_empty then + from + l_files.start + until + l_files.after or Result + loop + if l_files.item_for_iteration.tmp_name.same_string (a_filename) then + Result := True + end + l_files.forth + end + end + end + +feature {NONE} -- Temporary File handling + + delete_uploaded_file (a_filename: STRING) + -- Delete file `a_filename' + local + f: RAW_FILE + do + if is_uploaded_file (a_filename) then + create f.make (a_filename) + if f.exists and then f.is_writable then + f.delete + else + error_handler.add_custom_error (0, "Can not delete file", "Can not delete file %""+ a_filename +"%"") + end + else + error_handler.add_custom_error (0, "Not uploaded file", "This file %""+ a_filename +"%" is not an uploaded file.") + end + end + + save_uploaded_file (a_content: STRING; a_filename: STRING): detachable TUPLE [name: STRING; basename: STRING] + -- Save uploaded file content to `a_filename' + local + bn: STRING + l_safe_name: STRING + f: RAW_FILE + dn: STRING + fn: FILE_NAME + d: DIRECTORY + n: INTEGER + rescued: BOOLEAN + do + if not rescued then + dn := (create {EXECUTION_ENVIRONMENT}).current_working_directory + create d.make (dn) + if d.exists and then d.is_writable then + l_safe_name := safe_filename (a_filename) + from + create fn.make_from_string (dn) + bn := "tmp-" + l_safe_name + fn.set_file_name (bn) + create f.make (fn.string) + n := 0 + until + not f.exists + or else n > 1_000 + loop + n := n + 1 + fn.make_from_string (dn) + bn := "tmp-" + n.out + "-" + l_safe_name + fn.set_file_name (bn) + f.make (fn.string) + end + + if not f.exists or else f.is_writable then + f.open_write + f.put_string (a_content) + f.close + Result := [f.name, bn] + else + Result := Void + end + else + error_handler.add_custom_error (0, "Directory not writable", "Can not create file in directory %""+ dn +"%"") + end + else + Result := Void + end + rescue + rescued := True + retry + end + + safe_filename (fn: STRING): STRING + local + c: CHARACTER + i, n, p: INTEGER + l_accentued, l_non_accentued: STRING + do + l_accentued := "ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçèéêëìíîïðòóôõöùúûüýÿ" + l_non_accentued := "AAAAAACEEEEIIIIOOOOOUUUUYaaaaaaceeeeiiiioooooouuuuyy" + + --| Compute safe filename, to avoid creating impossible filename, or dangerous one + from + i := 1 + n := fn.count + create Result.make (n) + until + i > n + loop + c := fn[i] + inspect c + when '.', '-', '_' then + Result.extend (c) + when 'A' .. 'Z', 'a' .. 'z', '0' .. '9' then + Result.extend (c) + else + p := l_accentued.index_of (c, 1) + if p > 0 then + Result.extend (l_non_accentued[p]) + else + Result.extend ('-') + end + end + i := i + 1 + end + end + +feature {NONE} -- Implementation: Form analyzer + + analyze_multipart_form (t: STRING; s: STRING; vars: like form_fields) + -- Analyze multipart form content + --| FIXME[2011-06-21]: integrate eMIME parser library + require + t_attached: t /= Void + s_attached: s /= Void + vars_attached: vars /= Void + local + p,i,next_b: INTEGER + l_boundary_prefix: STRING + l_boundary: STRING + l_boundary_len: INTEGER + m: STRING + is_crlf: BOOLEAN + do + p := t.substring_index ("boundary=", 1) + if p > 0 then + l_boundary := t.substring (p + 9, t.count) + p := s.substring_index (l_boundary, 1) + if p > 1 then + l_boundary_prefix := s.substring (1, p - 1) + l_boundary := l_boundary_prefix + l_boundary + else + create l_boundary_prefix.make_empty + end + l_boundary_len := l_boundary.count + --| Let's support either %R%N and %N ... + --| Since both cases might occurs (for instance, our implementation of CGI does not have %R%N) + --| then let's be as flexible as possible on this. + is_crlf := s[l_boundary_len + 1] = '%R' + from + i := 1 + l_boundary_len + 1 + if is_crlf then + i := i + 1 --| +1 = CR = %R + end + next_b := i + until + i = 0 + loop + next_b := s.substring_index (l_boundary, i) + if next_b > 0 then + if is_crlf then + m := s.substring (i, next_b - 1 - 2) --| 2 = CR LF = %R %N + else + m := s.substring (i, next_b - 1 - 1) --| 1 = LF = %N + end + analyze_multipart_form_input (m, vars) + i := next_b + l_boundary_len + 1 + if is_crlf then + i := i + 1 --| +1 = CR = %R + end + else + if is_crlf then + i := i + 1 + end + m := s.substring (i - 1, s.count) + m.right_adjust + if not l_boundary_prefix.same_string (m) then + error_handler.add_custom_error (0, "Invalid form data", "Invalid ending for form data from input") + end + i := next_b + end + end + end + end + + analyze_multipart_form_input (s: STRING; vars_post: like form_fields) + -- Analyze multipart entry + require + s_not_empty: s /= Void and then not s.is_empty + local + n, i,p, b,e: INTEGER + l_name, l_filename, l_content_type: detachable STRING + l_header: detachable STRING + l_content: detachable STRING + l_line: detachable STRING + do + from + p := 1 + n := s.count + until + p > n or l_header /= Void + loop + inspect s[p] + when '%R' then -- CR + if + n >= p + 3 and then + s[p+1] = '%N' and then -- LF + s[p+2] = '%R' and then -- CR + s[p+3] = '%N' -- LF + then + l_header := s.substring (1, p + 1) + l_content := s.substring (p + 4, n) + end + when '%N' then + if + n >= p + 1 and then + s[p+1] = '%N' + then + l_header := s.substring (1, p) + l_content := s.substring (p + 2, n) + end + else + end + p := p + 1 + end + if l_header /= Void and l_content /= Void then + from + i := 1 + n := l_header.count + until + i = 0 or i > n + loop + l_line := Void + b := i + p := l_header.index_of ('%N', b) + if p > 0 then + if l_header[p - 1] = '%R' then + p := p - 1 + i := p + 2 + else + i := p + 1 + end + end + if p > 0 then + l_line := l_header.substring (b, p - 1) + if l_line.starts_with ("Content-Disposition: form-data") then + p := l_line.substring_index ("name=", 1) + if p > 0 then + p := p + 4 --| 4 = ("name=").count - 1 + if l_line.valid_index (p+1) and then l_line[p+1] = '%"' then + p := p + 1 + e := l_line.index_of ('"', p + 1) + else + e := l_line.index_of (';', p + 1) + if e = 0 then + e := l_line.count + end + end + l_name := l_header.substring (p + 1, e - 1) + end + + p := l_line.substring_index ("filename=", 1) + if p > 0 then + p := p + 8 --| 8 = ("filename=").count - 1 + if l_line.valid_index (p+1) and then l_line[p+1] = '%"' then + p := p + 1 + e := l_line.index_of ('"', p + 1) + else + e := l_line.index_of (';', p + 1) + if e = 0 then + e := l_line.count + end + end + l_filename := l_header.substring (p + 1, e - 1) + end + elseif l_line.starts_with ("Content-Type: ") then + l_content_type := l_line.substring (15, l_line.count) + end + else + i := 0 + end + end + if l_name /= Void then + if l_filename /= Void then + if l_content_type = Void then + l_content_type := default_content_type + end + if attached save_uploaded_file (l_content, l_filename) as l_saved_fn_info then + uploaded_files.force ([l_filename, l_content_type, l_saved_fn_info.name, l_saved_fn_info.basename, 0, l_content.count], l_name) + else + uploaded_files.force ([l_filename, l_content_type, "", "", -1, l_content.count], l_name) + end + else + vars_post.add_variable (l_content, l_name) + end + else + error_handler.add_custom_error (0, "unamed multipart entry", Void) + end + else + error_handler.add_custom_error (0, "missformed multipart entry", Void) + end + end + +feature {NONE} -- Internal value + + default_content_type: STRING = "text/plain" + -- Default content type + + form_input_data (nb: INTEGER): STRING + -- data from input form + local + n: INTEGER + t: STRING + do + from + n := nb + create Result.make (n) + if n > 1_024 then + n := 1_024 + end + until + n <= 0 + loop + read_input (n) + t := last_input_string + Result.append_string (t) + if t.count < n then + n := 0 + end + n := nb - t.count + end + end + + internal_parameters: detachable like parameters + -- cached value for `parameters' + + internal_form_fields: detachable like form_fields + -- cached value for `form_fields' + + internal_cookies: detachable like cookies + -- cached value for `cookies' + +feature {NONE} -- I/O: implementation + + read_input (nb: INTEGER) + -- Read `nb' bytes from `input' + do + input.read_stream (nb) + end + + last_input_string: STRING + -- Last string read from `input' + do + Result := input.last_string + end + +feature {NONE} -- Implementation + + report_bad_request_error (a_message: detachable STRING) + -- Report error + local + e: GW_ERROR + do + create e.make ({HTTP_STATUS_CODE}.bad_request) + if a_message /= Void then + e.set_message (a_message) + end + error_handler.add_error (e) + end + + extract_variables + -- Extract relevant environment variables + local + s: detachable STRING + do + s := environment.request_uri + if s.is_empty then + report_bad_request_error ("Missing URI") + end + if not has_error then + s := environment.request_method + if s.is_empty then + report_bad_request_error ("Missing request method") + end + end + if not has_error then + s := environment.http_host + if s = Void or else s.is_empty then + report_bad_request_error ("Missing host header") + end + end + if not has_error then + update_path_info (environment) + end + end + +feature {NONE} -- Implementation: utilities + + date_time_utilities: HTTP_DATE_TIME_UTILITIES + -- Utilities classes related to date and time. + once + create Result + end + +note + copyright: "2011-2011, 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/ewsgi/src/gw_input_stream.e b/library/server/ewsgi/src/stream/gw_input_stream.e similarity index 100% rename from library/ewsgi/src/gw_input_stream.e rename to library/server/ewsgi/src/stream/gw_input_stream.e diff --git a/library/ewsgi/src/gw_output_stream.e b/library/server/ewsgi/src/stream/gw_output_stream.e similarity index 100% rename from library/ewsgi/src/gw_output_stream.e rename to library/server/ewsgi/src/stream/gw_output_stream.e diff --git a/library/server/libfcgi/README.txt b/library/server/libfcgi/README.txt new file mode 100644 index 00000000..046d354f --- /dev/null +++ b/library/server/libfcgi/README.txt @@ -0,0 +1,7 @@ +To compile your own binaries .lib and .dll on Windows +please get the source from + +https://github.com/EiffelSoftware/libfcgi + +git repository: + git clone https://github.com/EiffelSoftware/libfcgi.git \ No newline at end of file diff --git a/library/server/libfcgi/doc/Configuration_examples.txt b/library/server/libfcgi/doc/Configuration_examples.txt new file mode 100644 index 00000000..fcb3f1e4 --- /dev/null +++ b/library/server/libfcgi/doc/Configuration_examples.txt @@ -0,0 +1,50 @@ += How to configure FCGI = +== Apache2/Windows == +* in apache2/conf/httpd.conf add + LoadModule fcgid_module modules/mod_fcgid.so + +* and for instance, you can put the following code to test the tests/eiffelweb example + + + FcgidIdleTimeout 60 + FcgidBusyScanInterval 120 + FcgidProcessLifeTime 1600 + #7200 + FcgidMaxProcesses 5 + FcgidMaxProcessesPerClass 100 + FcgidMinProcessesPerClass 100 + FcgidConnectTimeout 8 + FcgidIOTimeout 3000 + FcgidBusyTimeout 3200 + FcgidPassHeader Authorization + + + alias /eiffelweb/ "c:/_dev/EiffelWebReloaded/library/fcgi/tests/EIFGENs/eiffelweb/W_code/" + + AddHandler fcgid-script .exe + Options Indexes FollowSymLinks ExecCGI + + Order allow,deny + Allow from all + + + + +== IIS/Windows == +* When you install IIS on Windows 7, you just need to enable to CGI module which also include the FastCGI module +* otherwise, have a look at http://www.iis.net/download/FastCGI and also http://learn.iis.net/page.aspx/375/set-up-fastcgi-for-php/ +* Then open the IIS Manager + Modules: to check you really have FastCGIModule + Handler Mappings and then "Add Module Mapping" + Request path: *.exe + Module: FastCgiModule + Executable (optional): c:\_dev\EiffelWebReloaded\library\fcgi\tests\EIFGENs\eiffelweb\W_code\eiffelweb.exe + Name: Eiffel Web Solution using FastCGI on IIS + + += Notes = +* The notes in this readme are very basic, mainly to test your executable. +* You will need to configure more precisely your httpd server to fulfill your needs + +== for execution == +* On Windows, be sure to have the libfcgi.dll in your path (or in same folder as executable) diff --git a/library/server/libfcgi/fcgi.ecf b/library/server/libfcgi/fcgi.ecf new file mode 100644 index 00000000..14784c26 --- /dev/null +++ b/library/server/libfcgi/fcgi.ecf @@ -0,0 +1,52 @@ + + + + + + /\.svn$ + /\.git$ + /EIFGENs$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + /linux$ + /fake$ + + + + + + /windows$ + /fake$ + + + + + + + diff --git a/library/server/libfcgi/implementation/fake/fcgi_imp.e b/library/server/libfcgi/implementation/fake/fcgi_imp.e new file mode 100644 index 00000000..fd4e3e56 --- /dev/null +++ b/library/server/libfcgi/implementation/fake/fcgi_imp.e @@ -0,0 +1,104 @@ +note + description: "Implementation for the FCGI_I interface" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class FCGI_IMP + +inherit + FCGI_I + +feature -- Access + + environ: POINTER + -- Get the (char**) environ variable from the DLL. + external + "C inline use %"fcgi_stdio.h%"" + alias + "return (char**) environ;" + end + +feature -- FCGI connection + + fcgi_listen: INTEGER + -- Listen to the FCGI input stream + -- Return 0 for successful calls, -1 otherwise. + do + -- Set state on first call to prevent looping indefinitely + if fcgi_has_run then + Result := -1 + else --if not is_interactive then + fcgi_has_run := True + end + end + + fcgi_has_run: BOOLEAN + -- For emulation only; Has fcgi_listen been called? + + fcgi_finish + -- Finish current request from HTTP server started from + -- the most recent call to `fcgi_accept'. + do + end + + set_fcgi_exit_status (v: INTEGER) + do + end + +feature -- FCGI output + + put_string (a_str: STRING) + -- Put `a_str' on the FastCGI stdout. + do + io.put_string (a_str) + end + +-- fcgi_printf (fmt: STRING; args: FINITE[ANY]) +-- -- Put args, formatted per 'fmt' on the FastCGI stdout. +-- do +-- apf.printf (fmt, args) +-- end + +feature -- FCGI Input + + read_from_stdin (n: INTEGER) + -- Read up to n bytes from stdin and store in input buffer + do + end + + copy_from_stdin (n: INTEGER; tf: FILE) + -- Read up to n bytes from stdin and write to given file + do + end + +feature -- Status + + buffer_contents: STRING + do + if attached private_input_buffer as buf then + Result := buf + else + Result := "" + end + end + + buffer_capacity: INTEGER + do + if attached private_input_buffer as bug then + Result := buf.capacity + end + end + +note + copyright: "Copyright (c) 1984-2011, 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/libfcgi/implementation/linux/fcgi_c_api.e b/library/server/libfcgi/implementation/linux/fcgi_c_api.e new file mode 100644 index 00000000..909bc652 --- /dev/null +++ b/library/server/libfcgi/implementation/linux/fcgi_c_api.e @@ -0,0 +1,97 @@ +note + description: "Wrappers around FastCGI C API." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + FCGI_C_API + +feature -- Connection + + accept: INTEGER + -- Accept a Fast CGI connection. + -- Return 0 for successful calls, -1 otherwise. + external + "C inline use %"fcgi_stdio.h%"" + alias + "return FCGI_Accept();" + end + + environ: POINTER + -- Get the (char**) environ variable from the DLL. + external + "C inline use %"fcgi_stdio.h%"" + alias + "return (char**) environ;" + end + + finish + -- Finished current request from HTTP server started from + -- the most recent call to `fcgi_accept'. + external + "C inline use %"fcgi_stdio.h%"" + alias + "FCGI_Finish();" + end + + set_exit_status (v: INTEGER) + -- Set the exit status for the most recent request + external + "C inline use %"fcgi_stdio.h%"" + alias + "FCGI_SetExitStatus($v);" + end + +feature -- Input + + read_content_into (a_buffer: POINTER; a_length: INTEGER): INTEGER + -- Read content stream into `a_buffer' but no more than `a_length' character. + external + "C inline use %"fcgi_stdio.h%"" + alias + "[ + { + size_t n; + if (! FCGI_feof(FCGI_stdin)) { + n = FCGI_fread($a_buffer, 1, $a_length, FCGI_stdin); + } else { + n = 0; + } + return n; + } + ]" + end + + gets (s: POINTER): POINTER + -- gets() reads a line from stdin into the buffer pointed to + -- by s until either a terminating newline or EOF, which it + -- replaces with '\0' + -- No check for buffer overrun is performed + external + "C inline use %"fcgi_stdio.h%"" + alias + "return FCGI_gets($s);" + end + +feature -- Output + + put_string (v: POINTER; n: INTEGER) + external + "C inline use %"fcgi_stdio.h%"" + alias + "FCGI_fwrite($v, 1, $n, FCGI_stdout);" + end + +note + copyright: "Copyright (c) 1984-2011, 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/libfcgi/implementation/linux/fcgi_imp.e b/library/server/libfcgi/implementation/linux/fcgi_imp.e new file mode 100644 index 00000000..f7dcb620 --- /dev/null +++ b/library/server/libfcgi/implementation/linux/fcgi_imp.e @@ -0,0 +1,183 @@ +note + description: "Implementation for the FCGI_I interface" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class FCGI_IMP + +inherit + FCGI_I + STRING_HANDLER + +feature {NONE} -- Initialization + + make + -- Initialize FCGI interface + do + create fcgi + end + + fcgi: FCGI_C_API + -- FastCGI C API primitives + + +feature -- Access + + fcgi_environ: POINTER + do + Result := fcgi.environ + end + +feature -- FCGI Connection + + fcgi_listen: INTEGER + -- Listen to the FCGI input stream + -- Return 0 for successful calls, -1 otherwise. + do + Result := {FCGI_C_API}.accept + end + + update_eif_environ + external + "C inline use " + alias + "[ + eif_environ = (char**) environ; + ]" + end + + fcgi_finish + -- Finish current request from HTTP server started from + -- the most recent call to `fcgi_accept'. + do + {FCGI_C_API}.finish + end + + set_fcgi_exit_status (v: INTEGER) + do + {FCGI_C_API}.set_exit_status (-2) + end + +feature -- FCGI output + + put_string (a_str: STRING) + -- Put `a_str' on the FastCGI stdout. + local + l_c_str: C_STRING + do + l_c_str := c_buffer + l_c_str.set_string (a_str) + {FCGI_C_API}.put_string (l_c_str.item, l_c_str.count) + end + +-- fcgi_printf (fmt: STRING; args: FINITE[ANY]) +-- -- Put args, formatted per 'fmt' on the FastCGI stdout. +-- local +-- l_c_str: C_STRING +-- do +-- create l_c_str.make (apf.aprintf (fmt, args)) +-- {FCGI_C_API}.put_string (l_c_str.item, l_c_str.count) +-- end + +feature -- FCGI Input + + copy_from_stdin (n: INTEGER; tf: FILE) + -- Read up to n bytes from stdin and write to given file + local + l_c_str: C_STRING + num, readsize, writecount: INTEGER + done: BOOLEAN + do + --put_trace ("copy_from_stdin, n=" +n.out) + readsize := n.min (K_input_bufsize) + --put_trace ("copy_from_stdin, readsize=" +readsize.out) + l_c_str := c_buffer + from + until done or writecount >= n + loop + num := {FCGI_C_API}.read_content_into (l_c_str.item, readsize) + --put_trace ("copy_from_stdin, num=" +num.out) + if num = 0 then + -- EOF + done := True + else + tf.put_managed_pointer (c_buffer.managed_data, 0, num) + writecount := writecount + num + end + end + end + +feature {NONE} -- Implementation: FCGI Input + + fill_pointer_from_stdin (p: POINTER; n: INTEGER): INTEGER + -- Read up to `n' bytes from stdin and store in pointer `p' + -- and return number of bytes read. + do + Result := {FCGI_C_API}.read_content_into (p, n) + end + +feature -- I/O Routines + +--RFO read_stdin_into (a_str: STRING) +--RFO -- Read a string from the `stdin' into `a_str'. +--RFO require +--RFO a_str_not_void: a_str /= Void +--RFO local +--RFO l_c_str: C_STRING +--RFO n: INTEGER +--RFO do +--RFO l_c_str := c_buffer +--RFO n := {FCGI_C_API}.read_content_into (l_c_str.item, l_c_str.capacity) +--RFO a_str.set_count (n) +--RFO l_c_str.read_substring_into (a_str, 1, n) +--RFO end + +--RFO read_string_into (a_str: STRING) +--RFO require +--RFO exists: a_str /= Void +--RFO local +--RFO l_c_str: C_STRING +--RFO p: POINTER +--RFO do +--RFO create l_c_str.make_empty (1024) +--RFO p := {FCGI_C_API}.gets (l_c_str.item) +--RFO-- if p /= default_pointer and p = l_c_str.item then +--RFO a_str.resize (l_c_str.count) +--RFO l_c_str.read_string_into (a_str) +--RFO-- else +--RFO-- put_error_line ("Bad pointer from gets") +--RFO-- end +--RFO end + +--RFO read_line +--RFO -- Read up to the next end of line, or end of input +--RFO -- Leave result in last_string +--RFO -- Newline character is absent from result +--RFO do +--RFO if last_string = Void then +--RFO create Result.make (K_input_bufsize) +--RFO else +--RFO last_string.wipe_out +--RFO end +--RFO-- if input_filename /= Void then +--RFO-- io.read_line +--RFO-- last_string.append (io.last_string) +--RFO-- else +--RFO read_string_into (last_string) +--RFO-- end +--RFO end + +note + copyright: "Copyright (c) 1984-2011, 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/libfcgi/implementation/windows/fcgi_c_api.e b/library/server/libfcgi/implementation/windows/fcgi_c_api.e new file mode 100644 index 00000000..efae2d8f --- /dev/null +++ b/library/server/libfcgi/implementation/windows/fcgi_c_api.e @@ -0,0 +1,136 @@ +note + description: "Wrappers around FastCGI C API." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + FCGI_C_API + +feature -- Connections + + accept: INTEGER + -- Accept a Fast CGI connection. + -- Return 0 for successful calls, -1 otherwise. + external + "dll libfcgi.dll signature (): EIF_INTEGER use fcgi_stdio.h " + alias + "FCGI_Accept" + end + + environ: POINTER + -- Get the (char**) environ variable from the DLL. + external + "dll libfcgi.dll signature (): EIF_POINTER use fcgi_stdio.h " + alias + "FCGI_Environ" + end + + finish + -- Finished current request from HTTP server started from + -- the most recent call to `accept'. + external + "dll libfcgi.dll signature () use fcgi_stdio.h " + alias + "FCGI_Finish" + end + + set_exit_status (v: INTEGER) + -- Set the exit status for the most recent request + external + "dll libfcgi.dll signature (EIF_INTEGER) use fcgi_stdio.h " + alias + "FCGI_SetExitStatus" + end + +feature -- Input + + fread (v: POINTER; a_size: INTEGER; n: INTEGER; fp: POINTER): INTEGER + -- FCGI_fread() read from input `fp' and put into `v' + external + "dll libfcgi.dll signature (EIF_POINTER, EIF_INTEGER, EIF_INTEGER, EIF_POINTER): EIF_INTEGER use fcgi_stdio.h " + alias + "FCGI_fread" + end + + feof (v: POINTER): INTEGER + -- FCGI_feof() + external + "dll libfcgi.dll signature (EIF_POINTER): EIF_INTEGER use fcgi_stdio.h " + alias + "FCGI_feof" + end + + read_content_into (a_buffer: POINTER; a_length: INTEGER): INTEGER + -- Read content stream into `a_buffer' but no more than `a_length' character. + local + i: INTEGER + l_stdin: POINTER + do + l_stdin := stdin + i := feof (l_stdin) + if i /= 0 then + Result := 0 + else + Result := fread(a_buffer, 1, a_length, l_stdin) + end + end + + gets (s: POINTER): POINTER + -- gets() reads a line from stdin into the buffer pointed to + -- by `s' until either a terminating newline or EOF, which it + -- replaces with '\0' + -- No check for buffer overrun is performed + external + "dll libfcgi.dll signature (EIF_POINTER): EIF_POINTER use fcgi_stdio.h " + alias + "FCGI_gets" + end + +feature -- Output + + put_string (v: POINTER; n: INTEGER) + local + i: INTEGER + do + i := fwrite (v, 1, n, stdout) + end + + fwrite (v: POINTER; a_size: INTEGER; n: INTEGER; fp: POINTER): INTEGER + -- FCGI_fwrite() ouput `v' to `fp' + external + "dll libfcgi.dll signature (EIF_POINTER, EIF_INTEGER, EIF_INTEGER, EIF_POINTER): EIF_INTEGER use fcgi_stdio.h " + alias + "FCGI_fwrite" + end + +feature -- Access + + stdout: POINTER + -- FCGI_stdout() return pointer on output FCGI_FILE + external + "C inline use %"fcgi_stdio.h%"" + alias + "FCGI_stdout" + end + + stdin: POINTER + -- FCGI_stdin() return pointer on input FCGI_FILE + external + "C inline use %"fcgi_stdio.h%"" + alias + "FCGI_stdin" + end + +note + copyright: "Copyright (c) 1984-2011, 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/libfcgi/implementation/windows/fcgi_imp.e b/library/server/libfcgi/implementation/windows/fcgi_imp.e new file mode 100644 index 00000000..8c35d3f5 --- /dev/null +++ b/library/server/libfcgi/implementation/windows/fcgi_imp.e @@ -0,0 +1,267 @@ +note + description: "Implementation for the FCGI_I interface" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class FCGI_IMP + +inherit + FCGI_I + +feature {NONE} -- Initialization + + make + -- Initialize FCGI interface + do + create fcgi + end + + fcgi: FCGI_C_API + -- FastCGI C API primitives + +feature -- Access + + fcgi_environ: POINTER + do + Result := fcgi.environ + end + +-- updated_environ_variables: HASH_TABLE [STRING, STRING] +-- local +---- n, l_size, +-- i: INTEGER +-- p, v, null: POINTER +-- do +---- update_eif_environ +---- Result := starting_environment_variables +-- +---- p := environ_strings_pointer ($n) +---- from +---- i := 1 +---- l_size := 0 +---- create Result.make (n) +---- until +---- i > n +---- loop +---- create s.make_from_c (p.plus (l_size)) +---- l_size := l_size + s.count + 1 +---- if attached separated_variables (s) as t then +---- Result.force (t.value, t.key) +---- end +---- i := i + 1 +---- end +-- +-- p := fcgi.environ +-- create Result.make (50) +-- if p /= null then +-- from +-- i := 0 +-- v := fcgi_i_th_environ (i,p) +-- until +-- v = null +-- loop +-- if attached separated_variables (create {STRING}.make_from_c (v)) as t then +-- Result.force (t.value, t.key) +-- end +-- i := i + 1 +-- v := fcgi_i_th_environ (i,p) +-- end +-- end +-- end +-- +-- fcgi_i_th_environ (i: INTEGER; p: POINTER): POINTER +-- -- Environment variable at `i'-th position of `p'. +-- require +-- i_valid: i >=0 +-- external +-- "C inline use " +-- alias +-- "[ +-- return ((char **)$p)[$i]; +-- ]" +-- end + +feature -- FCGI connection + + fcgi_listen: INTEGER + -- Listen to the FCGI input stream + -- Return 0 for successful calls, -1 otherwise. + do + Result := fcgi.accept + end + +-- update_eif_environ +-- external +-- "C inline use " +-- alias +-- "[ +-- #ifdef EIF_WINDOWS +-- #ifndef GetEnvironmentStringsA +-- extern LPVOID WINAPI GetEnvironmentStringsA(void); +-- #endif +-- +-- eif_environ = (char**) GetEnvironmentStringsA(); +-- #endif +-- ]" +-- end + + fcgi_finish + -- Finish current request from HTTP server started from + -- the most recent call to `fcgi_accept'. + do + fcgi.finish + end + + set_fcgi_exit_status (v: INTEGER) + do + fcgi.set_exit_status (-2) + end + +feature -- FCGI output + + put_string (a_str: STRING) + -- Put `a_str' on the FastCGI stdout. + local + l_c_str: C_STRING + do + l_c_str := c_buffer + l_c_str.set_string (a_str) + fcgi.put_string (l_c_str.item, l_c_str.count) + end + +feature -- FCGI input + + copy_from_stdin (n: INTEGER; f: FILE) + -- Read up to n bytes from stdin and write to given file + local + l_c_str: C_STRING + num, readsize, writecount: INTEGER + done: BOOLEAN + l_fcgi: like fcgi + do + --put_trace ("copy_from_stdin, n=" +n.out) + readsize := n.min (K_input_bufsize) + --put_trace ("copy_from_stdin, readsize=" +readsize.out) + l_c_str := c_buffer + from + l_fcgi := fcgi + until done or writecount >= n + loop + num := l_fcgi.read_content_into (l_c_str.item, readsize) + --put_trace ("copy_from_stdin, num=" +num.out) + if num = 0 then + -- EOF + done := True + else + f.put_managed_pointer (c_buffer.managed_data, 0, num) + writecount := writecount + num + end + end + end + +feature {NONE} -- Implementation: FCGI Input + + fill_pointer_from_stdin (p: POINTER; n: INTEGER): INTEGER + -- Read up to `n' bytes from stdin and store in pointer `p' + -- and return number of bytes read. + do + Result := fcgi.read_content_into (p, n) + end + +feature -- I/O Routines + +--RFO read_stdin_into (a_str: STRING) +--RFO -- Read a string from the `stdin' into `a_str'. +--RFO require +--RFO a_str_not_void: a_str /= Void +--RFO local +--RFO l_c_str: C_STRING +--RFO n: INTEGER +--RFO do +--RFO l_c_str := c_buffer +--RFO n := fcgi.read_content_into (l_c_str.item, l_c_str.capacity) +--RFO a_str.set_count (n) +--RFO l_c_str.read_substring_into (a_str, 1, n) +--RFO end + +--RFO read_string_into (a_str: STRING) +--RFO require +--RFO exists: a_str /= Void +--RFO local +--RFO l_c_str: C_STRING +--RFO p: POINTER +--RFO do +--RFO create l_c_str.make_empty (1024) +--RFO p := fcgi.gets (l_c_str.item) +--RFO-- if p /= default_pointer and p = l_c_str.item then +--RFO a_str.resize (l_c_str.count) +--RFO l_c_str.read_string_into (a_str) +--RFO-- else +--RFO-- put_error_line ("Bad pointer from gets") +--RFO-- end +--RFO end + + +--RFO read_line +--RFO -- Read up to the next end of line, or end of input +--RFO -- Leave result in last_string +--RFO -- Newline character is absent from result +--RFO do +--RFO if last_string = Void then +--RFO create Result.make (K_input_bufsize) +--RFO else +--RFO last_string.wipe_out +--RFO end +--RFO-- if input_filename /= Void then +--RFO-- io.read_line +--RFO-- last_string.append (io.last_string) +--RFO-- else +--RFO read_string_into (last_string) +--RFO-- end +--RFO end + + + +feature {NONE} -- Implementation: environment + +-- environ_strings_pointer (p_nb: TYPED_POINTER [INTEGER]): POINTER +-- -- Environment variable strings returned by `GetEnvironmentStringsA' +-- -- `p_nb' return the count of environment variables. +-- external +-- "C inline use " +-- alias +-- "[ +-- #ifdef EIF_WINDOWS +-- #ifndef GetEnvironmentStringsA +-- extern LPVOID WINAPI GetEnvironmentStringsA(void); +-- #endif +-- +-- int cnt = 0; +-- LPSTR vars = GetEnvironmentStringsA(); +-- char** p = (char**) vars; +-- +-- for (; *vars; vars++) { +-- while (*vars) { vars++; } +-- cnt++; +-- } +-- +-- *$p_nb = cnt; +-- return (EIF_POINTER) p; +-- #endif +-- ]" +-- end + + +note + copyright: "Copyright (c) 1984-2011, 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/libfcgi/interface/fcgi.e b/library/server/libfcgi/interface/fcgi.e new file mode 100644 index 00000000..4f56f56a --- /dev/null +++ b/library/server/libfcgi/interface/fcgi.e @@ -0,0 +1,26 @@ +note + description: "Interface to FCGI C library" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class FCGI + +inherit + FCGI_IMP + +create + make + +note + copyright: "Copyright (c) 1984-2011, 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/libfcgi/interface/fcgi_i.e b/library/server/libfcgi/interface/fcgi_i.e new file mode 100644 index 00000000..7010e48b --- /dev/null +++ b/library/server/libfcgi/interface/fcgi_i.e @@ -0,0 +1,236 @@ +note + description: "Abstract interface to FCGI C library" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class FCGI_I + +inherit + STRING_HANDLER + +feature {NONE} -- Initialization + + make + -- Initialize FCGI interface + deferred + end + +feature -- Access + + updated_environ_variables: HASH_TABLE [STRING, STRING] + local + i: INTEGER + p, v, null: POINTER + do + p := fcgi_environ + create Result.make (50) + if p /= null then + from + i := 0 + v := fcgi_i_th_environ (i,p) + until + v = null + loop + if attached separated_variables (create {STRING}.make_from_c (v)) as t then + Result.force (t.value, t.key) + end + i := i + 1 + v := fcgi_i_th_environ (i,p) + end + end + end + +feature -- FCGI interface + + fcgi_listen: INTEGER + -- Listen to the FCGI input stream + -- Return 0 for successful calls, -1 otherwise. + deferred + end + + fcgi_environ: POINTER + -- Get the (char**) environ variable from the DLL. + deferred + end + + fcgi_finish + -- Finish current request from HTTP server started from + -- the most recent call to `fcgi_accept'. + deferred + end + + set_fcgi_exit_status (v: INTEGER) + deferred + end + +feature -- Status + + is_interactive: BOOLEAN + -- Is execution interactive? (for debugging) + do + end + +feature -- Input + + fill_string_from_stdin (s: STRING; n: INTEGER) + -- Read up to `n' bytes from stdin and store in string `s' + local + new_count: INTEGER + str_area: ANY + do + s.grow (n) + str_area := s.area + new_count := fill_pointer_from_stdin ($str_area, n) + s.set_count (new_count) + end + + read_from_stdin (n: INTEGER) + -- Read up to n bytes from stdin and store in input buffer + require + small_enough: n <= buffer_capacity + local + l_c_str: C_STRING + l_count: INTEGER + do + last_read_is_empty_ref.set_item (False) + l_c_str := c_buffer + l_count := fill_pointer_from_stdin (l_c_str.item, n) + last_read_count_ref.set_item (l_count) + if l_count <= 0 then + last_read_is_empty_ref.set_item (True) + end + end + + fill_pointer_from_stdin (p: POINTER; n: INTEGER): INTEGER + -- Read up to `n' bytes from stdin and store in pointer `p' + -- and return number of bytes read. + deferred + end + + copy_from_stdin (n: INTEGER; f: FILE) + -- Read up to n bytes from stdin and write to given file + require +-- small_enough: n <= buffer_capacity + file_exists: f /= Void + file_open: f.is_open_write or f.is_open_append + deferred + end + +feature -- Output + + put_string (a_str: STRING) + -- Put `a_str' on the FastCGI stdout. + require + a_str_not_void: a_str /= Void + deferred + end + +feature -- Implementation + + buffer_contents: STRING + local + n: like last_read_count + do + n := last_read_count + create Result.make (n) + Result.set_count (n) + c_buffer.read_substring_into (Result, 1, n) + end + + buffer_capacity: INTEGER + do + Result := c_buffer.capacity + end + +--RFO last_string: STRING +--RFO once +--RFO create Result.make (K_input_bufsize) +--RFO end + + last_read_count: INTEGER + do + Result := last_read_count_ref.item + end + + last_read_is_empty: BOOLEAN + do + Result := last_read_is_empty_ref.item + end + +feature {NONE} -- Shared buffer + + c_buffer: C_STRING + -- Buffer for Eiffel to C and C to Eiffel string conversions. + once + create Result.make_empty (K_input_bufsize) + ensure + c_buffer_not_void: Result /= Void + end + + +feature {NONE} -- Constants + + last_read_count_ref: INTEGER_REF + once + create Result + end + + last_read_is_empty_ref: BOOLEAN_REF + once + create Result + end + + K_input_bufsize: INTEGER = 1024_000 + +feature {NONE} -- Implementation: Environment + + fcgi_i_th_environ (i: INTEGER; p: POINTER): POINTER + -- Environment variable at `i'-th position of `p'. + require + i_valid: i >=0 + external + "C inline use " + alias + "return ((char **)$p)[$i];" + end + + separated_variables (a_var: STRING): detachable TUPLE [value: STRING; key: STRING] + -- Given an environment variable `a_var' in form of "key=value", + -- return separated key and value. + -- Return Void if `a_var' is in incorrect format. + require + a_var_attached: a_var /= Void + local + i, j: INTEGER + done: BOOLEAN + do + j := a_var.count + from + i := 1 + until + i > j or done + loop + if a_var.item (i) = '=' then + done := True + else + i := i + 1 + end + end + if i > 1 and then i < j then + Result := [a_var.substring (i + 1, j), a_var.substring (1, i - 1)] + end + end + +note + copyright: "Copyright (c) 1984-2011, 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/libfcgi/libfcgi-safe.ecf b/library/server/libfcgi/libfcgi-safe.ecf new file mode 100644 index 00000000..d5a9e54f --- /dev/null +++ b/library/server/libfcgi/libfcgi-safe.ecf @@ -0,0 +1,52 @@ + + + + + + /\.svn$ + /\.git$ + /EIFGENs$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + /linux$ + /fake$ + + + + + + /windows$ + /fake$ + + + + + + + diff --git a/library/server/libfcgi/libfcgi.ecf b/library/server/libfcgi/libfcgi.ecf new file mode 100644 index 00000000..d69396be --- /dev/null +++ b/library/server/libfcgi/libfcgi.ecf @@ -0,0 +1,52 @@ + + + + + + /\.svn$ + /\.git$ + /EIFGENs$ + + + + + + + + + + + + + + + + + + + + + + + + + + + + /linux$ + /fake$ + + + + + + /windows$ + /fake$ + + + + + + + diff --git a/library/server/libfcgi/license.lic b/library/server/libfcgi/license.lic new file mode 100644 index 00000000..c929225f --- /dev/null +++ b/library/server/libfcgi/license.lic @@ -0,0 +1 @@ +reference:forum2 diff --git a/library/server/libfcgi/spec/include/libfcgi/fastcgi.h b/library/server/libfcgi/spec/include/libfcgi/fastcgi.h new file mode 100644 index 00000000..d5b54686 --- /dev/null +++ b/library/server/libfcgi/spec/include/libfcgi/fastcgi.h @@ -0,0 +1,136 @@ +/* + * fastcgi.h -- + * + * Defines for the FastCGI protocol. + * + * + * Copyright (c) 1995-1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: fastcgi.h,v 1.1.1.1 1997/09/16 15:36:32 stanleyg Exp $ + */ + +#ifndef _FASTCGI_H +#define _FASTCGI_H + +/* + * Listening socket file number + */ +#define FCGI_LISTENSOCK_FILENO 0 + +typedef struct { + unsigned char version; + unsigned char type; + unsigned char requestIdB1; + unsigned char requestIdB0; + unsigned char contentLengthB1; + unsigned char contentLengthB0; + unsigned char paddingLength; + unsigned char reserved; +} FCGI_Header; + +#define FCGI_MAX_LENGTH 0xffff + +/* + * Number of bytes in a FCGI_Header. Future versions of the protocol + * will not reduce this number. + */ +#define FCGI_HEADER_LEN 8 + +/* + * Value for version component of FCGI_Header + */ +#define FCGI_VERSION_1 1 + +/* + * Values for type component of FCGI_Header + */ +#define FCGI_BEGIN_REQUEST 1 +#define FCGI_ABORT_REQUEST 2 +#define FCGI_END_REQUEST 3 +#define FCGI_PARAMS 4 +#define FCGI_STDIN 5 +#define FCGI_STDOUT 6 +#define FCGI_STDERR 7 +#define FCGI_DATA 8 +#define FCGI_GET_VALUES 9 +#define FCGI_GET_VALUES_RESULT 10 +#define FCGI_UNKNOWN_TYPE 11 +#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) + +/* + * Value for requestId component of FCGI_Header + */ +#define FCGI_NULL_REQUEST_ID 0 + + +typedef struct { + unsigned char roleB1; + unsigned char roleB0; + unsigned char flags; + unsigned char reserved[5]; +} FCGI_BeginRequestBody; + +typedef struct { + FCGI_Header header; + FCGI_BeginRequestBody body; +} FCGI_BeginRequestRecord; + +/* + * Mask for flags component of FCGI_BeginRequestBody + */ +#define FCGI_KEEP_CONN 1 + +/* + * Values for role component of FCGI_BeginRequestBody + */ +#define FCGI_RESPONDER 1 +#define FCGI_AUTHORIZER 2 +#define FCGI_FILTER 3 + + +typedef struct { + unsigned char appStatusB3; + unsigned char appStatusB2; + unsigned char appStatusB1; + unsigned char appStatusB0; + unsigned char protocolStatus; + unsigned char reserved[3]; +} FCGI_EndRequestBody; + +typedef struct { + FCGI_Header header; + FCGI_EndRequestBody body; +} FCGI_EndRequestRecord; + +/* + * Values for protocolStatus component of FCGI_EndRequestBody + */ +#define FCGI_REQUEST_COMPLETE 0 +#define FCGI_CANT_MPX_CONN 1 +#define FCGI_OVERLOADED 2 +#define FCGI_UNKNOWN_ROLE 3 + + +/* + * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records + */ +#define FCGI_MAX_CONNS "FCGI_MAX_CONNS" +#define FCGI_MAX_REQS "FCGI_MAX_REQS" +#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" + + +typedef struct { + unsigned char type; + unsigned char reserved[7]; +} FCGI_UnknownTypeBody; + +typedef struct { + FCGI_Header header; + FCGI_UnknownTypeBody body; +} FCGI_UnknownTypeRecord; + +#endif /* _FASTCGI_H */ + diff --git a/library/server/libfcgi/spec/include/libfcgi/fcgi_config.h b/library/server/libfcgi/spec/include/libfcgi/fcgi_config.h new file mode 100644 index 00000000..c59cda80 --- /dev/null +++ b/library/server/libfcgi/spec/include/libfcgi/fcgi_config.h @@ -0,0 +1,39 @@ +/* + * Copied to fcgi_config.h when building on WinNT without cygwin, + * i.e. configure is not run. See fcgi_config.h.in for details. + */ + +#define HAVE_FPOS 1 +#define HAVE_LIMITS_H 1 +#define HAVE_STREAMBUF_CHAR_TYPE 1 +#define HAVE_STRERROR 1 +#undef HAVE_ARPA_INET_H +#undef HAVE_DLFCN_H +#undef HAVE_FILENO_PROTO +#undef HAVE_INTTYPES_H +#undef HAVE_IOSTREAM_WITHASSIGN_STREAMBUF +#undef HAVE_LIBNSL +#undef HAVE_LIBSOCKET +#undef HAVE_MEMORY_H +#undef HAVE_NETDB_H +#undef HAVE_NETINET_IN_H +#undef HAVE_PTHREAD +#undef HAVE_SOCKADDR_UN_SUN_LEN +#undef HAVE_SOCKLEN +#undef HAVE_STDINT_H +#undef HAVE_STDLIB_H +#undef HAVE_STRING_H +#undef HAVE_STRINGS_H +#undef HAVE_SYS_PARAM_H +#undef HAVE_SYS_SOCKET_H +#undef HAVE_SYS_STAT_H +#undef HAVE_SYS_TIME_H +#undef HAVE_SYS_TYPES_H +#undef HAVE_UNISTD_H +#undef HAVE_VA_ARG_LONG_DOUBLE_BUG +#undef PTHREAD_CREATE_JOINABLE +#undef STDC_HEADERS +#undef USE_LOCKING +#undef const +#undef inline +#undef ssize_t diff --git a/library/server/libfcgi/spec/include/libfcgi/fcgi_config_x86.h b/library/server/libfcgi/spec/include/libfcgi/fcgi_config_x86.h new file mode 100644 index 00000000..c59cda80 --- /dev/null +++ b/library/server/libfcgi/spec/include/libfcgi/fcgi_config_x86.h @@ -0,0 +1,39 @@ +/* + * Copied to fcgi_config.h when building on WinNT without cygwin, + * i.e. configure is not run. See fcgi_config.h.in for details. + */ + +#define HAVE_FPOS 1 +#define HAVE_LIMITS_H 1 +#define HAVE_STREAMBUF_CHAR_TYPE 1 +#define HAVE_STRERROR 1 +#undef HAVE_ARPA_INET_H +#undef HAVE_DLFCN_H +#undef HAVE_FILENO_PROTO +#undef HAVE_INTTYPES_H +#undef HAVE_IOSTREAM_WITHASSIGN_STREAMBUF +#undef HAVE_LIBNSL +#undef HAVE_LIBSOCKET +#undef HAVE_MEMORY_H +#undef HAVE_NETDB_H +#undef HAVE_NETINET_IN_H +#undef HAVE_PTHREAD +#undef HAVE_SOCKADDR_UN_SUN_LEN +#undef HAVE_SOCKLEN +#undef HAVE_STDINT_H +#undef HAVE_STDLIB_H +#undef HAVE_STRING_H +#undef HAVE_STRINGS_H +#undef HAVE_SYS_PARAM_H +#undef HAVE_SYS_SOCKET_H +#undef HAVE_SYS_STAT_H +#undef HAVE_SYS_TIME_H +#undef HAVE_SYS_TYPES_H +#undef HAVE_UNISTD_H +#undef HAVE_VA_ARG_LONG_DOUBLE_BUG +#undef PTHREAD_CREATE_JOINABLE +#undef STDC_HEADERS +#undef USE_LOCKING +#undef const +#undef inline +#undef ssize_t diff --git a/library/server/libfcgi/spec/include/libfcgi/fcgi_stdio.h b/library/server/libfcgi/spec/include/libfcgi/fcgi_stdio.h new file mode 100644 index 00000000..7deae531 --- /dev/null +++ b/library/server/libfcgi/spec/include/libfcgi/fcgi_stdio.h @@ -0,0 +1,246 @@ +/* + * fcgi_stdio.h -- + * + * FastCGI-stdio compatibility package + * + * + * Copyright (c) 1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: fcgi_stdio.h,v 1.5 2001/06/22 13:21:15 robs Exp $ + */ + +#ifndef _FCGI_STDIO +#define _FCGI_STDIO 1 + +#include +#include +#include "fcgiapp.h" + +#if defined (c_plusplus) || defined (__cplusplus) +extern "C" { +#endif + +#ifndef DLLAPI +#ifdef _WIN32 +#define DLLAPI __declspec(dllimport) +#else +#define DLLAPI +#endif +#endif + +/* + * Wrapper type for FILE + */ + +typedef struct { + FILE *stdio_stream; + FCGX_Stream *fcgx_stream; +} FCGI_FILE; + +/* + * The four new functions and two new macros + */ + +DLLAPI int FCGI_Accept(void); +DLLAPI char** FCGI_Environ(void); +DLLAPI void FCGI_Finish(void); +DLLAPI int FCGI_StartFilterData(void); +DLLAPI void FCGI_SetExitStatus(int status); + +#define FCGI_ToFILE(fcgi_file) (fcgi_file->stdio_stream) +#define FCGI_ToFcgiStream(fcgi_file) (fcgi_file->fcgx_stream) + +/* + * Wrapper stdin, stdout, and stderr variables, set up by FCGI_Accept() + */ + +DLLAPI extern FCGI_FILE _fcgi_sF[]; +#define FCGI_stdin (&_fcgi_sF[0]) +#define FCGI_stdout (&_fcgi_sF[1]) +#define FCGI_stderr (&_fcgi_sF[2]) + +/* + * Wrapper function prototypes, grouped according to sections + * of Harbison & Steele, "C: A Reference Manual," fourth edition, + * Prentice-Hall, 1995. + */ + +DLLAPI void FCGI_perror(const char *str); + +DLLAPI FCGI_FILE *FCGI_fopen(const char *path, const char *mode); +DLLAPI int FCGI_fclose(FCGI_FILE *fp); +DLLAPI int FCGI_fflush(FCGI_FILE *fp); +DLLAPI FCGI_FILE *FCGI_freopen(const char *path, const char *mode, FCGI_FILE *fp); + +DLLAPI int FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size); +DLLAPI void FCGI_setbuf(FCGI_FILE *fp, char *buf); + +DLLAPI int FCGI_fseek(FCGI_FILE *fp, long offset, int whence); +DLLAPI int FCGI_ftell(FCGI_FILE *fp); +DLLAPI void FCGI_rewind(FCGI_FILE *fp); +#ifdef HAVE_FPOS +DLLAPI int FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos); +DLLAPI int FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos); +#endif +DLLAPI int FCGI_fgetc(FCGI_FILE *fp); +DLLAPI int FCGI_getchar(void); +DLLAPI int FCGI_ungetc(int c, FCGI_FILE *fp); + +DLLAPI char *FCGI_fgets(char *str, int size, FCGI_FILE *fp); +DLLAPI char *FCGI_gets(char *str); + +/* + * Not yet implemented + * + * int FCGI_fscanf(FCGI_FILE *fp, const char *format, ...); + * int FCGI_scanf(const char *format, ...); + * + */ + +DLLAPI int FCGI_fputc(int c, FCGI_FILE *fp); +DLLAPI int FCGI_putchar(int c); + +DLLAPI int FCGI_fputs(const char *str, FCGI_FILE *fp); +DLLAPI int FCGI_puts(const char *str); + +DLLAPI int FCGI_fprintf(FCGI_FILE *fp, const char *format, ...); +DLLAPI int FCGI_printf(const char *format, ...); + +DLLAPI int FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap); +DLLAPI int FCGI_vprintf(const char *format, va_list ap); + +DLLAPI size_t FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp); +DLLAPI size_t FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp); + +DLLAPI int FCGI_feof(FCGI_FILE *fp); +DLLAPI int FCGI_ferror(FCGI_FILE *fp); +DLLAPI void FCGI_clearerr(FCGI_FILE *fp); + +DLLAPI FCGI_FILE *FCGI_tmpfile(void); + +DLLAPI int FCGI_fileno(FCGI_FILE *fp); +DLLAPI FCGI_FILE *FCGI_fdopen(int fd, const char *mode); +DLLAPI FCGI_FILE *FCGI_popen(const char *cmd, const char *type); +DLLAPI int FCGI_pclose(FCGI_FILE *); + +/* + * The remaining definitions are for application programs, + * not for fcgi_stdio.c + */ + +#ifndef NO_FCGI_DEFINES + +/* + * Replace standard types, variables, and functions with FastCGI wrappers. + * Use undef in case a macro is already defined. + */ + +#undef FILE +#define FILE FCGI_FILE + +#undef stdin +#define stdin FCGI_stdin +#undef stdout +#define stdout FCGI_stdout +#undef stderr +#define stderr FCGI_stderr + +#undef perror +#define perror FCGI_perror + +#undef fopen +#define fopen FCGI_fopen +#undef fclose +#define fclose FCGI_fclose +#undef fflush +#define fflush FCGI_fflush +#undef freopen +#define freopen FCGI_freopen + +#undef setvbuf +#define setvbuf FCGI_setvbuf +#undef setbuf +#define setbuf FCGI_setbuf + +#undef fseek +#define fseek FCGI_fseek +#undef ftell +#define ftell FCGI_ftell +#undef rewind +#define rewind FCGI_rewind +#undef fgetpos +#define fgetpos FCGI_fgetpos +#undef fsetpos +#define fsetpos FCGI_fsetpos + +#undef fgetc +#define fgetc FCGI_fgetc +#undef getc +#define getc FCGI_fgetc +#undef getchar +#define getchar FCGI_getchar +#undef ungetc +#define ungetc FCGI_ungetc + +#undef fgets +#define fgets FCGI_fgets +#undef gets +#define gets FCGI_gets + +#undef fputc +#define fputc FCGI_fputc +#undef putc +#define putc FCGI_fputc +#undef putchar +#define putchar FCGI_putchar + +#undef fputs +#define fputs FCGI_fputs +#undef puts +#define puts FCGI_puts + +#undef fprintf +#define fprintf FCGI_fprintf +#undef printf +#define printf FCGI_printf + +#undef vfprintf +#define vfprintf FCGI_vfprintf +#undef vprintf +#define vprintf FCGI_vprintf + +#undef fread +#define fread FCGI_fread +#undef fwrite +#define fwrite FCGI_fwrite + +#undef feof +#define feof FCGI_feof +#undef ferror +#define ferror FCGI_ferror +#undef clearerr +#define clearerr FCGI_clearerr + +#undef tmpfile +#define tmpfile FCGI_tmpfile + +#undef fileno +#define fileno FCGI_fileno +#undef fdopen +#define fdopen FCGI_fdopen +#undef popen +#define popen FCGI_popen +#undef pclose +#define pclose FCGI_pclose + +#endif /* NO_FCGI_DEFINES */ + +#if defined (__cplusplus) || defined (c_plusplus) +} /* terminate extern "C" { */ +#endif + +#endif /* _FCGI_STDIO */ + diff --git a/library/server/libfcgi/spec/include/libfcgi/fcgiapp.h b/library/server/libfcgi/spec/include/libfcgi/fcgiapp.h new file mode 100644 index 00000000..d7236f6f --- /dev/null +++ b/library/server/libfcgi/spec/include/libfcgi/fcgiapp.h @@ -0,0 +1,622 @@ +/* + * fcgiapp.h -- + * + * Definitions for FastCGI application server programs + * + * + * Copyright (c) 1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: fcgiapp.h,v 1.12 2001/11/21 21:10:11 robs Exp $ + */ + +#ifndef _FCGIAPP_H +#define _FCGIAPP_H + +/* Hack to see if we are building TCL - TCL needs varargs not stdarg */ +#ifndef TCL_LIBRARY +#include +#else +#include +#endif + +#ifndef DLLAPI +#ifdef _WIN32 +#define DLLAPI __declspec(dllimport) +#else +#define DLLAPI +#endif +#endif + +#if defined (c_plusplus) || defined (__cplusplus) +extern "C" { +#endif + +/* + * Error codes. Assigned to avoid conflict with EOF and errno(2). + */ +#define FCGX_UNSUPPORTED_VERSION -2 +#define FCGX_PROTOCOL_ERROR -3 +#define FCGX_PARAMS_ERROR -4 +#define FCGX_CALL_SEQ_ERROR -5 + +/* + * This structure defines the state of a FastCGI stream. + * Streams are modeled after the FILE type defined in stdio.h. + * (We wouldn't need our own if platform vendors provided a + * standard way to subclass theirs.) + * The state of a stream is private and should only be accessed + * by the procedures defined below. + */ +typedef struct FCGX_Stream { + unsigned char *rdNext; /* reader: first valid byte + * writer: equals stop */ + unsigned char *wrNext; /* writer: first free byte + * reader: equals stop */ + unsigned char *stop; /* reader: last valid byte + 1 + * writer: last free byte + 1 */ + unsigned char *stopUnget; /* reader: first byte of current buffer + * fragment, for ungetc + * writer: undefined */ + int isReader; + int isClosed; + int wasFCloseCalled; + int FCGI_errno; /* error status */ + void (*fillBuffProc) (struct FCGX_Stream *stream); + void (*emptyBuffProc) (struct FCGX_Stream *stream, int doClose); + void *data; +} FCGX_Stream; + +/* + * An environment (as defined by environ(7)): A NULL-terminated array + * of strings, each string having the form name=value. + */ +typedef char **FCGX_ParamArray; + +/* + * FCGX_Request Flags + * + * Setting FCGI_FAIL_ACCEPT_ON_INTR prevents FCGX_Accept() from + * restarting upon being interrupted. + */ +#define FCGI_FAIL_ACCEPT_ON_INTR 1 + +/* + * FCGX_Request -- State associated with a request. + * + * Its exposed for API simplicity, I expect parts of it to change! + */ +typedef struct FCGX_Request { + int requestId; /* valid if isBeginProcessed */ + int role; + FCGX_Stream *in; + FCGX_Stream *out; + FCGX_Stream *err; + char **envp; + + /* Don't use anything below here */ + + struct Params *paramsPtr; + int ipcFd; /* < 0 means no connection */ + int isBeginProcessed; /* FCGI_BEGIN_REQUEST seen */ + int keepConnection; /* don't close ipcFd at end of request */ + int appStatus; + int nWriters; /* number of open writers (0..2) */ + int flags; + int listen_sock; +} FCGX_Request; + + +/* + *====================================================================== + * Control + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_IsCGI -- + * + * Returns TRUE iff this process appears to be a CGI process + * rather than a FastCGI process. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_IsCGI(void); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Init -- + * + * Initialize the FCGX library. Call in multi-threaded apps + * before calling FCGX_Accept_r(). + * + * Returns 0 upon success. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_Init(void); + +/* + *---------------------------------------------------------------------- + * + * FCGX_OpenSocket -- + * + * Create a FastCGI listen socket. + * + * path is the Unix domain socket (named pipe for WinNT), or a colon + * followed by a port number. e.g. "/tmp/fastcgi/mysocket", ":5000" + * + * backlog is the listen queue depth used in the listen() call. + * + * Returns the socket's file descriptor or -1 on error. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_OpenSocket(const char *path, int backlog); + +/* + *---------------------------------------------------------------------- + * + * FCGX_InitRequest -- + * + * Initialize a FCGX_Request for use with FCGX_Accept_r(). + * + * sock is a file descriptor returned by FCGX_OpenSocket() or 0 (default). + * The only supported flag at this time is FCGI_FAIL_ON_INTR. + * + * Returns 0 upon success. + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_InitRequest(FCGX_Request *request, int sock, int flags); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Accept_r -- + * + * Accept a new request (multi-thread safe). Be sure to call + * FCGX_Init() first. + * + * Results: + * 0 for successful call, -1 for error. + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * Creates input, output, and error streams and + * assigns them to *in, *out, and *err respectively. + * Creates a parameters data structure to be accessed + * via getenv(3) (if assigned to environ) or by FCGX_GetParam + * and assigns it to *envp. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + * DON'T use the FCGX_Request, its structure WILL change. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_Accept_r(FCGX_Request *request); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Finish_r -- + * + * Finish the request (multi-thread safe). + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_Finish_r(FCGX_Request *request); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Free -- + * + * Free the memory and, if close is true, + * IPC FD associated with the request (multi-thread safe). + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_Free(FCGX_Request * request, int close); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Accept -- + * + * Accept a new request (NOT multi-thread safe). + * + * Results: + * 0 for successful call, -1 for error. + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * Creates input, output, and error streams and + * assigns them to *in, *out, and *err respectively. + * Creates a parameters data structure to be accessed + * via getenv(3) (if assigned to environ) or by FCGX_GetParam + * and assigns it to *envp. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_Accept( + FCGX_Stream **in, + FCGX_Stream **out, + FCGX_Stream **err, + FCGX_ParamArray *envp); + +/* + *---------------------------------------------------------------------- + * + * FCGX_Finish -- + * + * Finish the current request (NOT multi-thread safe). + * + * Side effects: + * + * Finishes the request accepted by (and frees any + * storage allocated by) the previous call to FCGX_Accept. + * + * DO NOT retain pointers to the envp array or any strings + * contained in it (e.g. to the result of calling FCGX_GetParam), + * since these will be freed by the next call to FCGX_Finish + * or FCGX_Accept. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_Finish(void); + +/* + *---------------------------------------------------------------------- + * + * FCGX_StartFilterData -- + * + * stream is an input stream for a FCGI_FILTER request. + * stream is positioned at EOF on FCGI_STDIN. + * Repositions stream to the start of FCGI_DATA. + * If the preconditions are not met (e.g. FCGI_STDIN has not + * been read to EOF) sets the stream error code to + * FCGX_CALL_SEQ_ERROR. + * + * Results: + * 0 for a normal return, < 0 for error + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_StartFilterData(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_SetExitStatus -- + * + * Sets the exit status for stream's request. The exit status + * is the status code the request would have exited with, had + * the request been run as a CGI program. You can call + * SetExitStatus several times during a request; the last call + * before the request ends determines the value. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_SetExitStatus(int status, FCGX_Stream *stream); + +/* + *====================================================================== + * Parameters + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetParam -- obtain value of FCGI parameter in environment + * + * + * Results: + * Value bound to name, NULL if name not present in the + * environment envp. Caller must not mutate the result + * or retain it past the end of this request. + * + *---------------------------------------------------------------------- + */ +DLLAPI char *FCGX_GetParam(const char *name, FCGX_ParamArray envp); + +/* + *====================================================================== + * Readers + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetChar -- + * + * Reads a byte from the input stream and returns it. + * + * Results: + * The byte, or EOF (-1) if the end of input has been reached. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_GetChar(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_UnGetChar -- + * + * Pushes back the character c onto the input stream. One + * character of pushback is guaranteed once a character + * has been read. No pushback is possible for EOF. + * + * Results: + * Returns c if the pushback succeeded, EOF if not. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_UnGetChar(int c, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetStr -- + * + * Reads up to n consecutive bytes from the input stream + * into the character array str. Performs no interpretation + * of the input bytes. + * + * Results: + * Number of bytes read. If result is smaller than n, + * the end of input has been reached. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_GetStr(char *str, int n, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetLine -- + * + * Reads up to n-1 consecutive bytes from the input stream + * into the character array str. Stops before n-1 bytes + * have been read if '\n' or EOF is read. The terminating '\n' + * is copied to str. After copying the last byte into str, + * stores a '\0' terminator. + * + * Results: + * NULL if EOF is the first thing read from the input stream, + * str otherwise. + * + *---------------------------------------------------------------------- + */ +DLLAPI char *FCGX_GetLine(char *str, int n, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_HasSeenEOF -- + * + * Returns EOF if end-of-file has been detected while reading + * from stream; otherwise returns 0. + * + * Note that FCGX_HasSeenEOF(s) may return 0, yet an immediately + * following FCGX_GetChar(s) may return EOF. This function, like + * the standard C stdio function feof, does not provide the + * ability to peek ahead. + * + * Results: + * EOF if end-of-file has been detected, 0 if not. + * + *---------------------------------------------------------------------- + */ + +DLLAPI int FCGX_HasSeenEOF(FCGX_Stream *stream); + +/* + *====================================================================== + * Writers + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_PutChar -- + * + * Writes a byte to the output stream. + * + * Results: + * The byte, or EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_PutChar(int c, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_PutStr -- + * + * Writes n consecutive bytes from the character array str + * into the output stream. Performs no interpretation + * of the output bytes. + * + * Results: + * Number of bytes written (n) for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_PutS -- + * + * Writes a null-terminated character string to the output stream. + * + * Results: + * number of bytes written for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_PutS(const char *str, FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_FPrintF, FCGX_VFPrintF -- + * + * Performs printf-style output formatting and writes the results + * to the output stream. + * + * Results: + * number of bytes written for normal return, + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_FPrintF(FCGX_Stream *stream, const char *format, ...); + +DLLAPI int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg); + +/* + *---------------------------------------------------------------------- + * + * FCGX_FFlush -- + * + * Flushes any buffered output. + * + * Server-push is a legitimate application of FCGX_FFlush. + * Otherwise, FCGX_FFlush is not very useful, since FCGX_Accept + * does it implicitly. Calling FCGX_FFlush in non-push applications + * results in extra writes and therefore reduces performance. + * + * Results: + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_FFlush(FCGX_Stream *stream); + +/* + *====================================================================== + * Both Readers and Writers + *====================================================================== + */ + +/* + *---------------------------------------------------------------------- + * + * FCGX_FClose -- + * + * Closes the stream. For writers, flushes any buffered + * output. + * + * Close is not a very useful operation since FCGX_Accept + * does it implicitly. Closing the out stream before the + * err stream results in an extra write if there's nothing + * in the err stream, and therefore reduces performance. + * + * Results: + * EOF (-1) if an error occurred. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_FClose(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_GetError -- + * + * Return the stream error code. 0 means no error, > 0 + * is an errno(2) error, < 0 is an FastCGI error. + * + *---------------------------------------------------------------------- + */ +DLLAPI int FCGX_GetError(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_ClearError -- + * + * Clear the stream error code and end-of-file indication. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_ClearError(FCGX_Stream *stream); + +/* + *---------------------------------------------------------------------- + * + * FCGX_CreateWriter -- + * + * Create a FCGX_Stream (used by cgi-fcgi). This shouldn't + * be needed by a FastCGI applictaion. + * + *---------------------------------------------------------------------- + */ +DLLAPI FCGX_Stream *FCGX_CreateWriter( + int socket, + int requestId, + int bufflen, + int streamType); + +/* + *---------------------------------------------------------------------- + * + * FCGX_FreeStream -- + * + * Free a FCGX_Stream (used by cgi-fcgi). This shouldn't + * be needed by a FastCGI applictaion. + * + *---------------------------------------------------------------------- + */ +DLLAPI void FCGX_FreeStream(FCGX_Stream **stream); + +/* ---------------------------------------------------------------------- + * + * Prevent the lib from accepting any new requests. Signal handler safe. + * + * ---------------------------------------------------------------------- + */ +DLLAPI void FCGX_ShutdownPending(void); + +#if defined (__cplusplus) || defined (c_plusplus) +} /* terminate extern "C" { */ +#endif + +#endif /* _FCGIAPP_H */ diff --git a/library/server/libfcgi/spec/include/libfcgi/fcgimisc.h b/library/server/libfcgi/spec/include/libfcgi/fcgimisc.h new file mode 100644 index 00000000..0464455f --- /dev/null +++ b/library/server/libfcgi/spec/include/libfcgi/fcgimisc.h @@ -0,0 +1,38 @@ +/* + * fcgimisc.h -- + * + * Miscellaneous definitions + * + * + * Copyright (c) 1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: fcgimisc.h,v 1.3 2001/06/18 14:25:47 robs Exp $ + */ + +#ifndef _FCGIMISC_H +#define _FCGIMISC_H + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (1) +#endif + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef ASSERT +#define ASSERT(assertion) assert(assertion) +#endif + +#endif /* _FCGIMISC_H */ diff --git a/library/server/libfcgi/spec/include/libfcgi/fcgio.h b/library/server/libfcgi/spec/include/libfcgi/fcgio.h new file mode 100644 index 00000000..20d222ab --- /dev/null +++ b/library/server/libfcgi/spec/include/libfcgi/fcgio.h @@ -0,0 +1,151 @@ +// +// Provides support for FastCGI via C++ iostreams. +// +// $Id: fcgio.h,v 1.15 2002/02/25 13:16:11 robs Exp $ +// +// This work is based on routines written by George Feinberg. They +// have been mostly re-written and extensively changed by +// Michael Richards. +// +// Rewritten again with bug fixes and numerous enhancements by +// Michael Shell. +// +// And rewritten again by Rob Saccoccio. +// +// Special Thanks to Dietmar Kuehl for his help and the numerous custom +// streambuf examples on his web site. +// +// Copyright (c) 2000 Tux the Linux Penguin +// Copyright (c) 2001 Rob Saccoccio and Chelsea Networks +// +// You are free to use this software without charge or royalty +// as long as this notice is not removed or altered, and recognition +// is given to the author(s) +// +// This code is offered as-is without any warranty either expressed or +// implied; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. If it breaks, you get to keep +// both halves. + +#ifndef FCGIO_H +#define FCGIO_H + +#include + +#include "fcgiapp.h" + +#ifndef DLLAPI +#ifdef _WIN32 +#define DLLAPI __declspec(dllimport) +#else +#define DLLAPI +#endif +#endif + +#if ! HAVE_STREAMBUF_CHAR_TYPE +typedef char char_type; +#endif + +/* + * fcgi_streambuf + */ +class DLLAPI fcgi_streambuf : public std::streambuf +{ +public: + + // Note that if no buf is assigned (the default), iostream methods + // such as peek(), unget() and putback() will fail. If a buf is + // assigned, I/O is a bit less effecient and output streams will + // have to be flushed (or the streambuf destroyed) before the next + // call to "accept". + fcgi_streambuf(FCGX_Stream * fcgx, char * buf, int len); + + fcgi_streambuf(char_type * buf, std::streamsize len); + + fcgi_streambuf(FCGX_Stream * fcgx = 0); + + ~fcgi_streambuf(void); + + int attach(FCGX_Stream * fcgx); + +protected: + + // Consume the put area (if buffered) and c (if c is not EOF). + virtual int overflow(int); + + // Flush the put area (if buffered) and the FCGX buffer to the client. + virtual int sync(); + + // Remove and return the current character. + virtual int uflow(); + + // Fill the get area (if buffered) and return the current character. + virtual int underflow(); + + // Use a buffer. The only reasons that a buffer would be useful is + // to support the use of the unget()/putback() or seek() methods. Using + // a buffer will result in less efficient I/O. Note: the underlying + // FastCGI library (FCGX) maintains its own input and output buffers. + virtual std::streambuf * setbuf(char_type * buf, std::streamsize len); + + virtual std::streamsize xsgetn(char_type * s, std::streamsize n); + virtual std::streamsize xsputn(const char_type * s, std::streamsize n); + +private: + + FCGX_Stream * fcgx; + + // buf is just handy to have around + char_type * buf; + + // this isn't kept by the base class + std::streamsize bufsize; + + void init(FCGX_Stream * fcgx, char_type * buf, std::streamsize bufsize); + + void reset(void); +}; + +/* + * fcgi_istream - deprecated + */ +class DLLAPI fcgi_istream : public std::istream +{ +public: + + // deprecated + fcgi_istream(FCGX_Stream * fcgx = 0); + + // deprecated + ~fcgi_istream(void) {} + + // deprecated + virtual void attach(FCGX_Stream * fcgx); + +private: + + fcgi_streambuf fcgi_strmbuf; +}; + +/* + * fcgi_ostream - deprecated + */ +class DLLAPI fcgi_ostream : public std::ostream +{ +public: + + // deprecated + fcgi_ostream(FCGX_Stream * fcgx = 0); + + // deprecated + ~fcgi_ostream(void) {} + + // deprecated + virtual void attach(FCGX_Stream *fcgx); + +private: + + fcgi_streambuf fcgi_strmbuf; +}; + +#endif /* FCGIO_H */ diff --git a/library/server/libfcgi/spec/include/libfcgi/fcgios.h b/library/server/libfcgi/spec/include/libfcgi/fcgios.h new file mode 100644 index 00000000..de7c3c7c --- /dev/null +++ b/library/server/libfcgi/spec/include/libfcgi/fcgios.h @@ -0,0 +1,130 @@ +/* + * fcgios.h -- + * + * Description of file. + * + * + * Copyright (c) 1996 Open Market, Inc. + * All rights reserved. + * + * This file contains proprietary and confidential information and + * remains the unpublished property of Open Market, Inc. Use, + * disclosure, or reproduction is prohibited except as permitted by + * express written license agreement with Open Market, Inc. + * + * Bill Snapper + * snapper@openmarket.com + */ +#ifndef _FCGIOS_H +#define _FCGIOS_H + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#endif + +#include "fcgi_config.h" + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#if defined (c_plusplus) || defined (__cplusplus) +extern "C" { +#endif + +#ifdef _WIN32 +#define OS_Errno GetLastError() +#define OS_SetErrno(err) SetLastError(err) +#ifndef O_NONBLOCK +#define O_NONBLOCK 0x0004 /* no delay */ +#endif +#else /* !_WIN32 */ +#define OS_Errno errno +#define OS_SetErrno(err) errno = (err) +#endif /* !_WIN32 */ + +#ifndef DLLAPI +#ifdef _WIN32 +#define DLLAPI __declspec(dllimport) +#else +#define DLLAPI +#endif +#endif + + +/* This is the initializer for a "struct timeval" used in a select() call + * right after a new request is accept()ed to determine readablity. Its + * a drop-dead timer. Its only used for AF_UNIX sockets (not TCP sockets). + * Its a workaround for a kernel bug in Linux 2.0.x and SCO Unixware. + * Making this as small as possible, yet remain reliable would be best. + * 2 seconds is very conservative. 0,0 is not reliable. The shorter the + * timeout, the faster request processing will recover. The longer the + * timeout, the more likely this application being "busy" will cause other + * requests to abort and cause more dead sockets that need this timeout. */ +#define READABLE_UNIX_FD_DROP_DEAD_TIMEVAL 2,0 + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +#ifndef MAXPATHLEN +#define MAXPATHLEN 1024 +#endif + +#ifndef X_OK +#define X_OK 0x01 +#endif + +#ifndef _CLIENTDATA +# if defined(__STDC__) || defined(__cplusplus) + typedef void *ClientData; +# else + typedef int *ClientData; +# endif /* __STDC__ */ +#define _CLIENTDATA +#endif + +typedef void (*OS_AsyncProc) (ClientData clientData, int len); + +DLLAPI int OS_LibInit(int stdioFds[3]); +DLLAPI void OS_LibShutdown(void); +DLLAPI int OS_CreateLocalIpcFd(const char *bindPath, int backlog); +DLLAPI int OS_FcgiConnect(char *bindPath); +DLLAPI int OS_Read(int fd, char * buf, size_t len); +DLLAPI int OS_Write(int fd, char * buf, size_t len); +DLLAPI int OS_SpawnChild(char *execPath, int listenFd); +DLLAPI int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, + ClientData clientData); +DLLAPI int OS_AsyncRead(int fd, int offset, void *buf, int len, + OS_AsyncProc procPtr, ClientData clientData); +DLLAPI int OS_AsyncWrite(int fd, int offset, void *buf, int len, + OS_AsyncProc procPtr, ClientData clientData); +DLLAPI int OS_Close(int fd); +DLLAPI int OS_CloseRead(int fd); +DLLAPI int OS_DoIo(struct timeval *tmo); +DLLAPI int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs); +DLLAPI int OS_IpcClose(int ipcFd); +DLLAPI int OS_IsFcgi(int sock); +DLLAPI void OS_SetFlags(int fd, int flags); + +DLLAPI void OS_ShutdownPending(void); + +#if defined (__cplusplus) || defined (c_plusplus) +} /* terminate extern "C" { */ +#endif + +#endif /* _FCGIOS_H */ diff --git a/library/server/libfcgi/spec/lib/win64/msc/libfcgi.dll b/library/server/libfcgi/spec/lib/win64/msc/libfcgi.dll new file mode 100644 index 00000000..6b18c88f Binary files /dev/null and b/library/server/libfcgi/spec/lib/win64/msc/libfcgi.dll differ diff --git a/library/server/libfcgi/spec/lib/win64/msc/libfcgi.lib b/library/server/libfcgi/spec/lib/win64/msc/libfcgi.lib new file mode 100644 index 00000000..839cda3f Binary files /dev/null and b/library/server/libfcgi/spec/lib/win64/msc/libfcgi.lib differ diff --git a/library/server/libfcgi/spec/lib/windows/msc/libfcgi.dll b/library/server/libfcgi/spec/lib/windows/msc/libfcgi.dll new file mode 100644 index 00000000..83d69a06 Binary files /dev/null and b/library/server/libfcgi/spec/lib/windows/msc/libfcgi.dll differ diff --git a/library/server/libfcgi/spec/lib/windows/msc/libfcgi.lib b/library/server/libfcgi/spec/lib/windows/msc/libfcgi.lib new file mode 100644 index 00000000..01af4f19 Binary files /dev/null and b/library/server/libfcgi/spec/lib/windows/msc/libfcgi.lib differ diff --git a/library/server/libfcgi/tests/apache2.conf b/library/server/libfcgi/tests/apache2.conf new file mode 100644 index 00000000..5d9f3700 --- /dev/null +++ b/library/server/libfcgi/tests/apache2.conf @@ -0,0 +1,64 @@ + + ServerAdmin webmaster@localhost + + DocumentRoot /var/www + + Options FollowSymLinks + AllowOverride None + + + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + allow from all + + + ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ + + AllowOverride None + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + Order allow,deny + Allow from all + + + ErrorLog ${APACHE_LOG_DIR}/error.log + + # Possible values include: debug, info, notice, warn, error, crit, + # alert, emerg. + LogLevel warn + + CustomLog ${APACHE_LOG_DIR}/access.log combined + + Alias /doc/ "/usr/share/doc/" + + Options Indexes MultiViews FollowSymLinks + AllowOverride None + Order deny,allow + Deny from all + Allow from 127.0.0.0/255.0.0.0 ::1/128 + + + +# FcgidIdleTimeout 600 +# FcgidBusyScanInterval 120 +# FcgidProcessLifeTime 0 +# #7200 +# FcgidMaxProcesses 1000 +# FcgidMaxProcessesPerClass 100 +# FcgidMinProcessesPerClass 100 +# FcgidConnectTimeout 8 +# FcgidIOTimeout 60 +# FcgidBusyTimeout 1200 + +alias /eiffelweb/ "/home/jfiat/_dev/EiffelWebReloaded/library/fcgi/tests/EIFGENs/eiffelweb/W_code/" + +AddHandler fcgid-script .ews +Options Indexes FollowSymLinks ExecCGI +# Customize the next two directives for your requirements. +Order allow,deny +Allow from all + + + + + diff --git a/library/server/libfcgi/tests/application_root.e b/library/server/libfcgi/tests/application_root.e new file mode 100644 index 00000000..336f899c --- /dev/null +++ b/library/server/libfcgi/tests/application_root.e @@ -0,0 +1,80 @@ +note + description : "Objects that ..." + author : "$Author$" + date : "$Date$" + revision : "$Revision$" + +class + APPLICATION_ROOT + +create + make + +feature {NONE} -- Initialization + + make + -- Initialize `Current'. + local + res: INTEGER + nb: INTEGER + do + initialize + from + res := fcgi.fcgi_listen + until + res < 0 + loop + nb := nb + 1 + fcgi.put_string (header ("FCGI Eiffel Application")) + + fcgi.put_string ("

Hello FCGI Eiffel Application

%N") + fcgi.put_string ("Request number " + nb.out + "
%N") + + fcgi.put_string ("
    Environment variables%N") + print_environment_variables (fcgi.updated_environ_variables) + fcgi.put_string ("
") + fcgi.put_string (footer) + + res := fcgi.fcgi_listen + end + end + +feature -- Access + + header (a_title: STRING): STRING + do + Result := "Content-type: text/html%R%N" + Result.append ("%R%N") + Result.append ("%N") + Result.append ("" + a_title + "") + Result.append ("%N") + end + + footer: STRING + do + Result := "%N%N" + end + + print_environment_variables (vars: HASH_TABLE [STRING, STRING]) + local + do + from + vars.start + until + vars.after + loop + fcgi.put_string ("
  • " + vars.key_for_iteration + " = " + vars.item_for_iteration + "
  • %N") + vars.forth + end + end + +feature {NONE} -- Implementation + + initialize + do + create fcgi.make + end + + fcgi: FCGI + +end diff --git a/library/server/libfcgi/tests/eiffelweb.ecf b/library/server/libfcgi/tests/eiffelweb.ecf new file mode 100644 index 00000000..7ce530ca --- /dev/null +++ b/library/server/libfcgi/tests/eiffelweb.ecf @@ -0,0 +1,16 @@ + + + + + + /EIFGENs$ + /\.git$ + /\.svn$ + + + + + + + + diff --git a/library/text/encoder/encoder-safe.ecf b/library/text/encoder/encoder-safe.ecf new file mode 100644 index 00000000..24ceb5c2 --- /dev/null +++ b/library/text/encoder/encoder-safe.ecf @@ -0,0 +1,35 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + /tests$ + + + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + diff --git a/library/text/encoder/encoder.ecf b/library/text/encoder/encoder.ecf new file mode 100644 index 00000000..5ba35276 --- /dev/null +++ b/library/text/encoder/encoder.ecf @@ -0,0 +1,35 @@ + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + /tests$ + + + + + + + + /.git$ + /EIFGENs$ + /.svn$ + + + + + + + diff --git a/library/text/encoder/license.lic b/library/text/encoder/license.lic new file mode 100644 index 00000000..c929225f --- /dev/null +++ b/library/text/encoder/license.lic @@ -0,0 +1 @@ +reference:forum2 diff --git a/library/text/encoder/src/base64.e b/library/text/encoder/src/base64.e new file mode 100644 index 00000000..821139de --- /dev/null +++ b/library/text/encoder/src/base64.e @@ -0,0 +1,218 @@ +note + description: "Summary description for {BASE64}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + BASE64 + +inherit + ENCODER [STRING_8, STRING_8] + redefine + valid_encoded_string + end + +feature -- Access + + name: STRING = "base64" + +feature -- Status report + + has_error: BOOLEAN + + valid_encoded_string (v: STRING): BOOLEAN + do + Result := Precursor (v) and then + (v.is_empty or v.count >= 4) + end + +feature -- base64 encoder + + encoded_string (s: STRING): STRING_8 + -- base64 encoded value of `s'. + local + i,n: INTEGER + c: INTEGER + f: SPECIAL [BOOLEAN] + base64chars: STRING_8 + do + has_error := False + base64chars := character_map + from + n := s.count + i := (8 * n) \\ 6 + if i > 0 then + create f.make_filled (False, 8 * n + (6 - i)) + else + create f.make_filled (False, 8 * n) + end + i := 0 + until + i > n - 1 + loop + c := s.item (i + 1).code + f[8 * i + 0] := c.bit_test(7) + f[8 * i + 1] := c.bit_test(6) + f[8 * i + 2] := c.bit_test(5) + f[8 * i + 3] := c.bit_test(4) + f[8 * i + 4] := c.bit_test(3) + f[8 * i + 5] := c.bit_test(2) + f[8 * i + 6] := c.bit_test(1) + f[8 * i + 7] := c.bit_test(0) + i := i + 1 + end + from + i := 0 + n := f.count + create Result.make (n // 6) + until + i > n - 1 + loop + c := 0 + if f[i + 0] then c := c + 0x20 end + if f[i + 1] then c := c + 0x10 end + if f[i + 2] then c := c + 0x8 end + if f[i + 3] then c := c + 0x4 end + if f[i + 4] then c := c + 0x2 end + if f[i + 5] then c := c + 0x1 end + Result.extend (base64chars.item (c + 1)) + i := i + 6 + end + + i := s.count \\ 3 + if i > 0 then + from until i > 2 loop + Result.extend ('=') + i := i + 1 + end + end + end + +feature -- Decoder + + decoded_string (v: STRING): STRING + -- base64 decoded value of `s'. + local + byte_count: INTEGER + pos, n: INTEGER + byte1, byte2, byte3, byte4, tmp1, tmp2: INTEGER + done: BOOLEAN + base64chars: STRING_8 + c: CHARACTER + do + has_error := False + base64chars := character_map + n := v.count + create Result.make (n) + + from + pos := 0 + invariant + n = v.count + until + (pos >= n) or done + loop + byte_count := 0 + + pos := next_encoded_character_position (v, pos) + 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 + byte2 := base64chars.index_of (v[pos], 1) - 1 + byte_count := byte_count + 1 + + pos := next_encoded_character_position (v, pos) + if pos <= n then + c := v[pos] + if c /= '=' then + byte3 := base64chars.index_of (c, 1) - 1 + byte_count := byte_count + 1 + end + + pos := next_encoded_character_position (v, pos) + if pos <= n then + c := v[pos] + if c /= '=' then + byte4 := base64chars.index_of (c, 1) - 1 + byte_count := byte_count + 1 + end + end + end + end + end +-- pos := pos + byte_count + + done := byte_count < 4 + + if byte_count > 1 then + tmp1 := byte1.bit_shift_left (2) & 0xff + tmp2 := byte2.bit_shift_right (4) & 0x03 + Result.extend ((tmp1 | tmp2).to_character_8) + if byte_count > 2 then + tmp1 := byte2.bit_shift_left (4) & 0xff + tmp2 := byte3.bit_shift_right (2) & 0x0f + Result.extend ((tmp1 | tmp2).to_character_8) + if byte_count > 3 then + Result.extend( + ((byte4 | byte3.bit_shift_left(6))& 0xff).to_character_8) + end + end + end + end + end + + next_encoded_character_position (v: STRING; from_pos: INTEGER): INTEGER + -- Next encoded character position from `v' starting after `from_pos' index. + -- Result over `v.count' denodes no remaining decodable position + --| Mainly to handle base64 encoded text on multiple line + --| thus we just skip %N, %R and eventually all blanks + require + v_attached: v /= Void + valid_from_pos: v.valid_index (from_pos + 1) + local + n: INTEGER + l_map: like character_map + do + l_map := character_map + from + Result := from_pos + 1 + n := v.count + until + in_character_map (v[Result]) or Result > n + loop + Result := Result + 1 + end + ensure + result_after_from_pos: Result > from_pos + end + + in_character_map (c: CHARACTER): BOOLEAN + -- Is a character map element? + do + inspect c + when 'A' .. 'Z', 'a' .. 'z', '0'..'9', '+', '/', '=' then + Result := True + else + end + end + +feature {NONE} -- Constants + + character_map: STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" + +note + copyright: "Copyright (c) 1984-2011, 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/text/encoder/src/encoder.e b/library/text/encoder/src/encoder.e new file mode 100644 index 00000000..52cfa047 --- /dev/null +++ b/library/text/encoder/src/encoder.e @@ -0,0 +1,73 @@ +note + description: "Summary description for {ENCODER}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +deferred class + ENCODER [U -> STRING_GENERAL, E -> STRING_GENERAL] --| U:unencoded type, E:encoded type + +feature -- Access + + name: STRING + -- Encoding name. + deferred + end + +feature -- Status report + + has_error: BOOLEAN + -- Error occurred + deferred + end + +feature -- Assertion helpers + + valid_unencoded_string (s: U): BOOLEAN + -- Is `s' a valid unencoded string ? + do + Result := s /= Void + end + + valid_encoded_string (v: E): BOOLEAN + -- Is `v' a valid encoded string ? + do + Result := v /= Void + end + +feature -- Encoder + + encoded_string (s: U): E + -- Encoded value of `s'. + require + valid_unencoded_string: valid_unencoded_string (s) + deferred + ensure + unchanged: s ~ (old s) + valid_encoded_string: valid_encoded_string (Result) + end + +feature -- Decoder + + decoded_string (v: E): U + -- Decoded value of `v'. + require + valid_encoded_string: valid_encoded_string (v) + deferred + ensure + unchanged: v ~ (old v) + valid_unencoded_string: valid_unencoded_string (Result) + end + +note + copyright: "Copyright (c) 1984-2011, 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/text/encoder/src/html_encoder.e b/library/text/encoder/src/html_encoder.e new file mode 100644 index 00000000..d3a409aa --- /dev/null +++ b/library/text/encoder/src/html_encoder.e @@ -0,0 +1,267 @@ +note + description: "[ + Summary description for {HTML_ENCODER}. + + see: http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references + ]" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + HTML_ENCODER + +inherit + ENCODER [STRING_32, STRING_8] + + PLATFORM + export + {NONE} all + end + +feature -- Access + + name: STRING = "HTML-encoded" + +feature -- Status report + + has_error: BOOLEAN + +feature -- Encoder + + encoded_string (s: STRING_32): STRING_8 + -- HTML-encoded value of `s'. + local + i, n: INTEGER + uc: CHARACTER_32 + c: CHARACTER_8 + do + has_error := False + create Result.make (s.count + s.count // 10) + n := s.count + from i := 1 until i > n loop + uc := s.item (i) + if uc.is_character_8 then + c := uc.to_character_8 + inspect c + when '%"' then Result.append_string (""") + when '&' then Result.append_string ("&") + when '%'' then Result.append_string ("'") + when '<' then Result.append_string ("<") + when '>' then Result.append_string (">") + else + Result.extend (c) + end + else + Result.append ("&#") + Result.append (uc.code.out) + Result.extend (';') + end + i := i + 1 + end + end + +feature -- Decoder + + decoded_string (v: STRING_8): STRING_32 + -- The HTML-encoded equivalent of the given string + local + i, n: INTEGER + c: CHARACTER + cl_i: CELL [INTEGER] + do + n := v.count + create Result.make (n) + create cl_i.put (0) + from i := 1 until i > n loop + c := v.item (i) + if c = '&' then + cl_i.replace (i) + Result.append_string (next_entity (v, cl_i)) + i := cl_i.item + else + Result.append_character (c.to_character_32) + i := i + 1 + end + end + end + +feature {NONE} -- Implementation: decoder + + next_entity (v: STRING_8; cl_i: CELL [INTEGER]): STRING_32 + -- Return next entity value + -- move index + local + i: INTEGER + c: CHARACTER + is_char: BOOLEAN + is_hexa: BOOLEAN + s: STRING_32 + do + i := cl_i.item + create s.make_empty + i := i + 1 + c := v[i] + if c = '#' then + is_char := True + i := i + 1 + c := v[i] + if c = 'x' then + is_hexa := True + i := i + 1 + c := v[i] + end + end + if is_char then + if is_hexa then + from + until + not c.is_hexa_digit or c = ';' + loop + s.append_character (c) + i := i + 1 + c := v[i] + end + else + from + until + not c.is_digit or c = ';' + loop + s.append_character (c) + i := i + 1 + c := v[i] + end + end + else + from + until + not valid_entity_character (c) or c = ';' + loop + s.append_character (c) + i := i + 1 + c := v[i] + end + end + if c = ';' then + if is_char then + if is_hexa then + -- not yet implemented + s.prepend_character ('x') + s.prepend_character ('#') + s.prepend_character ('&') + s.append_character (';') + Result := s + elseif s.is_integer then + create Result.make (1) + Result.append_code (s.to_natural_32) + else + s.prepend_character ('&') + s.append_character (';') + Result := s + end + else + resolve_entity (s) + Result := s + end + cl_i.replace (i + 1) + else + cl_i.replace (cl_i.item + 1) + s.prepend_character ('&') + has_error := True -- ("Invalid entity syntax " + s) + Result := s + end + end + + resolve_entity (s: STRING_32) + -- Resolve `s' as an entity + --| Hardcoding the xml entities " & ' < and > + require + s_attached: s /= Void + local + l_resolved: BOOLEAN + do + inspect s[1].as_lower + when 'a' then + if + s.count = 3 and then + s[2].as_lower = 'm' and then + s[3].as_lower = 'p' + then -- & + l_resolved := True + s.wipe_out + s.append_character ('&') + elseif -- ' + s.count = 4 and then + s[2].as_lower = 'p' and then + s[3].as_lower = 'o' and then + s[4].as_lower = 's' + then + l_resolved := True + s.wipe_out + s.append_character ('%'') + end + when 'q' then + if + s.count = 4 and then + s[2].as_lower = 'u' and then + s[3].as_lower = 'o' and then + s[4].as_lower = 't' + then -- " + l_resolved := True + s.wipe_out + s.append_character ('%"') + end + when 'l' then + if + s.count = 2 and then + s[2].as_lower = 't' + then -- " + l_resolved := True + s.wipe_out + s.append_character ('<') + end + when 'g' then + if + s.count = 2 and then + s[2].as_lower = 't' + then -- " + l_resolved := True + s.wipe_out + s.append_character ('>') + end + else + end + if not l_resolved then + s.prepend_character ('&') + s.append_character (';') + end + end + + valid_entity_character (c: CHARACTER): BOOLEAN + -- Is `c' a valid character in html entity value? + --| such as & + do + inspect + c + when 'a'..'z' then + Result := True + when 'A'..'Z' then + Result := True + when '0'..'9' then + Result := True + else + end + end + +note + copyright: "Copyright (c) 1984-2011, 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/text/encoder/src/url_encoder.e b/library/text/encoder/src/url_encoder.e new file mode 100644 index 00000000..63b1af79 --- /dev/null +++ b/library/text/encoder/src/url_encoder.e @@ -0,0 +1,321 @@ +note + description: "Summary description for {URL_ENCODER}." + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + URL_ENCODER + +inherit + ENCODER [STRING_32, STRING_8] + + PLATFORM + export + {NONE} all + end + +feature -- Access + + name: STRING = "URL-encoded" + +feature -- Status report + + has_error: BOOLEAN + +feature -- Encoder + + encoded_string (s: STRING_32): STRING_8 + -- URL-encoded value of `s'. + local + i, n: INTEGER + uc: CHARACTER_32 + c: CHARACTER_8 + do + has_error := False + create Result.make (s.count + s.count // 10) + n := s.count + from i := 1 until i > n loop + uc := s.item (i) + if uc.is_character_8 then + c := uc.to_character_8 + inspect c + when + 'A' .. 'Z', + 'a' .. 'z', '0' .. '9', + '.', '-', '~', '_' + then + Result.extend (c) + when ' ' then + Result.extend ('+') + else + Result.append (url_encoded_char (uc)) + end + else + Result.append (url_encoded_char (uc)) + end + i := i + 1 + end + end + +feature {NONE} -- encoder character + + url_encoded_char (uc: CHARACTER_32): STRING_8 + do + create Result.make (3) + if uc.is_character_8 then + Result.extend ('%%') + Result.append (uc.code.to_hex_string) + from + until + Result.count < 2 or else Result[2] /= '0' + loop + Result.remove (2) + end + else + has_error := True --| Non-ascii escape not currently supported + end + ensure + exists: Result /= Void + end + +feature -- Decoder + + decoded_string (v: STRING_8): STRING_32 + -- The URL-encoded equivalent of the given string + local + i, n: INTEGER + c: CHARACTER + pr: CELL [INTEGER] + do + n := v.count + create Result.make (n) + from i := 1 + until i > n + loop + c := v.item (i) + inspect c + when '+' then + Result.append_character ({CHARACTER_32}' ') + when '%%' then + -- An escaped character ? + if i = n then + Result.append_character (c.to_character_32) + else + 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 + end + +note + copyright: "Copyright (c) 1984-2011, 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/text/encoder/src/xml_encoder.e b/library/text/encoder/src/xml_encoder.e new file mode 100644 index 00000000..23273037 --- /dev/null +++ b/library/text/encoder/src/xml_encoder.e @@ -0,0 +1,267 @@ +note + description: "[ + Summary description for {XML_ENCODER}. + + see: http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references + ]" + legal: "See notice at end of class." + status: "See notice at end of class." + date: "$Date$" + revision: "$Revision$" + +class + XML_ENCODER + +inherit + ENCODER [STRING_32, STRING_8] + + PLATFORM + export + {NONE} all + end + +feature -- Access + + name: STRING = "XML-encoded" + +feature -- Status report + + has_error: BOOLEAN + +feature -- Encoder + + encoded_string (s: STRING_32): STRING_8 + -- XML-encoded value of `s'. + local + i, n: INTEGER + uc: CHARACTER_32 + c: CHARACTER_8 + do + has_error := False + create Result.make (s.count + s.count // 10) + n := s.count + from i := 1 until i > n loop + uc := s.item (i) + if uc.is_character_8 then + c := uc.to_character_8 + inspect c + when '%"' then Result.append_string (""") + when '&' then Result.append_string ("&") + when '%'' then Result.append_string ("'") + when '<' then Result.append_string ("<") + when '>' then Result.append_string (">") + else + Result.extend (c) + end + else + Result.append ("&#") + Result.append (uc.code.out) + Result.extend (';') + end + i := i + 1 + end + end + +feature -- Decoder + + decoded_string (v: STRING_8): STRING_32 + -- The XML-encoded equivalent of the given string + local + i, n: INTEGER + c: CHARACTER + cl_i: CELL [INTEGER] + do + n := v.count + create Result.make (n) + create cl_i.put (0) + from i := 1 until i > n loop + c := v.item (i) + if c = '&' then + cl_i.replace (i) + Result.append_string (next_entity (v, cl_i)) + i := cl_i.item + else + Result.append_character (c.to_character_32) + i := i + 1 + end + end + end + +feature {NONE} -- Implementation: decoder + + next_entity (v: STRING_8; cl_i: CELL [INTEGER]): STRING_32 + -- Return next entity value + -- move index + local + i: INTEGER + c: CHARACTER + is_char: BOOLEAN + is_hexa: BOOLEAN + s: STRING_32 + do + i := cl_i.item + create s.make_empty + i := i + 1 + c := v[i] + if c = '#' then + is_char := True + i := i + 1 + c := v[i] + if c = 'x' then + is_hexa := True + i := i + 1 + c := v[i] + end + end + if is_char then + if is_hexa then + from + until + not c.is_hexa_digit or c = ';' + loop + s.append_character (c) + i := i + 1 + c := v[i] + end + else + from + until + not c.is_digit or c = ';' + loop + s.append_character (c) + i := i + 1 + c := v[i] + end + end + else + from + until + not valid_entity_character (c) or c = ';' + loop + s.append_character (c) + i := i + 1 + c := v[i] + end + end + if c = ';' then + if is_char then + if is_hexa then + -- not yet implemented + s.prepend_character ('x') + s.prepend_character ('#') + s.prepend_character ('&') + s.append_character (';') + Result := s + elseif s.is_integer then + create Result.make (1) + Result.append_code (s.to_natural_32) + else + s.prepend_character ('&') + s.append_character (';') + Result := s + end + else + resolve_entity (s) + Result := s + end + cl_i.replace (i + 1) + else + cl_i.replace (cl_i.item + 1) + s.prepend_character ('&') + has_error := True -- ("Invalid entity syntax " + s) + Result := s + end + end + + resolve_entity (s: STRING_32) + -- Resolve `s' as an entity + --| Hardcoding the xml entities " & ' < and > + require + s_attached: s /= Void + local + l_resolved: BOOLEAN + do + inspect s[1].as_lower + when 'a' then + if + s.count = 3 and then + s[2].as_lower = 'm' and then + s[3].as_lower = 'p' + then -- & + l_resolved := True + s.wipe_out + s.append_character ('&') + elseif -- ' + s.count = 4 and then + s[2].as_lower = 'p' and then + s[3].as_lower = 'o' and then + s[4].as_lower = 's' + then + l_resolved := True + s.wipe_out + s.append_character ('%'') + end + when 'q' then + if + s.count = 4 and then + s[2].as_lower = 'u' and then + s[3].as_lower = 'o' and then + s[4].as_lower = 't' + then -- " + l_resolved := True + s.wipe_out + s.append_character ('%"') + end + when 'l' then + if + s.count = 2 and then + s[2].as_lower = 't' + then -- " + l_resolved := True + s.wipe_out + s.append_character ('<') + end + when 'g' then + if + s.count = 2 and then + s[2].as_lower = 't' + then -- " + l_resolved := True + s.wipe_out + s.append_character ('>') + end + else + end + if not l_resolved then + s.prepend_character ('&') + s.append_character (';') + end + end + + valid_entity_character (c: CHARACTER): BOOLEAN + -- Is `c' a valid character in html entity value? + --| such as & + do + inspect + c + when 'a'..'z' then + Result := True + when 'A'..'Z' then + Result := True + when '0'..'9' then + Result := True + else + end + end + +note + copyright: "Copyright (c) 1984-2011, 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/text/encoder/tests/test_base64.e b/library/text/encoder/tests/test_base64.e new file mode 100644 index 00000000..e0b0d5ea --- /dev/null +++ b/library/text/encoder/tests/test_base64.e @@ -0,0 +1,49 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + TEST_BASE64 + +inherit + EQA_TEST_SET + +feature -- Test routines + + test_base64_encoder + note + testing: "base64" + do + test_base64_encoding ("Il était une fois !") + end + + test_base64_encoding (s: STRING_8) + local + u: STRING_8 + e: STRING_8 + b: BASE64 + do + create b + e := b.encoded_string (s) + u := b.decoded_string (e) + assert ("decoded encoded string is same", u ~ s) + end + +note + copyright: "Copyright (c) 1984-2011, 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/text/encoder/tests/test_url_encoder.e b/library/text/encoder/tests/test_url_encoder.e new file mode 100644 index 00000000..40a7e16d --- /dev/null +++ b/library/text/encoder/tests/test_url_encoder.e @@ -0,0 +1,49 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + TEST_URL_ENCODER + +inherit + EQA_TEST_SET + +feature -- Test routines + + test_url_encoded_encoder + note + testing: "url-encoded" + do + test_url_encoded_encoding ("http://domain.tld/foo/bar/script.php?test='toto'&foo=bar&title=il était une fois") + end + + test_url_encoded_encoding (s: STRING_32) + local + u: STRING_32 + e: STRING_8 + b: URL_ENCODER + do + create b + e := b.encoded_string (s) + u := b.decoded_string (e) + assert ("decoded encoded string is same", u ~ s) + end + +note + copyright: "Copyright (c) 1984-2011, 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/text/encoder/tests/text_xml_encoder.e b/library/text/encoder/tests/text_xml_encoder.e new file mode 100644 index 00000000..824714bd --- /dev/null +++ b/library/text/encoder/tests/text_xml_encoder.e @@ -0,0 +1,47 @@ +note + description: "[ + Eiffel tests that can be executed by testing tool. + ]" + author: "EiffelStudio test wizard" + date: "$Date$" + revision: "$Revision$" + testing: "type/manual" + +class + TEST_XML_ENCODER + +inherit + EQA_TEST_SET + +feature -- Test routines + + test_xml_encoded_encoder + note + testing: "xml-encoded" + do + test_xml_encoded_encoding ({STRING_32}"il était une fois Ni & Hao (你好)") + end + + test_xml_encoded_encoding (s: STRING_32) + local + u: STRING_32 + e: STRING_8 + b: XML_ENCODER + do + create b + e := b.encoded_string (s) + u := b.decoded_string (e) + assert ("decoded encoded string is same", u ~ s) + end + +note + copyright: "Copyright (c) 1984-2011, 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