diff --git a/examples/hello_routed_world/src/hello_routed_world.e b/examples/hello_routed_world/src/hello_routed_world.e index f668235c..23f70cd2 100644 --- a/examples/hello_routed_world/src/hello_routed_world.e +++ b/examples/hello_routed_world/src/hello_routed_world.e @@ -30,7 +30,6 @@ feature {NONE} -- Initialization create_router do --- (create {EXCEPTIONS}).raise ("ouch") check False end create router.make (5) end @@ -38,33 +37,28 @@ feature {NONE} -- Initialization setup_router local ra: REQUEST_AGENT_HANDLER [REQUEST_URI_TEMPLATE_HANDLER_CONTEXT] - rag: REQUEST_URI_TEMPLATE_ROUTING_HANDLER + hello: REQUEST_URI_TEMPLATE_ROUTING_HANDLER do router.map_agent ("/home", agent execute_home) - create rag.make (3) + --| Map all "/hello*" using a ROUTING_HANDLER + create hello.make (3) + router.map ("/hello", hello) create ra.make (agent handle_hello) - rag.map ("/hello/{name}.{format}", ra) - rag.map ("/hello.{format}/{name}", ra) - rag.map ("/hello/{name}", ra) --- router.map ("/hello/{name}.{format}", ra) --- router.map ("/hello.{format}/{name}", ra) --- router.map ("/hello/{name}", ra) + hello.map ("/hello/{name}.{format}", ra) + hello.map ("/hello.{format}/{name}", ra) + hello.map ("/hello/{name}", ra) create ra.make (agent handle_anonymous_hello) - rag.map ("/hello", ra) - rag.map ("/hello.{format}", ra) --- router.map ("/hello", ra) --- router.map ("/hello.{format}", ra) - - router.map ("/hello", rag) + hello.map ("/hello", ra) + hello.map ("/hello.{format}", ra) + --| Various various route, directly on the "router" router.map_agent_with_request_methods ("/method/any", agent handle_method_any, Void) router.map_agent_with_request_methods ("/method/guess", agent handle_method_get_or_post, <<"GET", "POST">>) router.map_agent_with_request_methods ("/method/custom", agent handle_method_get, <<"GET">>) router.map_agent_with_request_methods ("/method/custom", agent handle_method_post, <<"POST">>) - end feature -- Execution @@ -81,8 +75,9 @@ feature -- Execution n := 3 create h.make h.put_refresh (l_url, 5) - res.set_status_code (200) + res.set_status_code ({HTTP_STATUS_CODE}.moved_permanently) res.write_headers_string (h.string) + from create e until @@ -115,49 +110,53 @@ feature -- Execution end execute_home (ctx: REQUEST_URI_TEMPLATE_HANDLER_CONTEXT; req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) + local + l_body: STRING_8 do - res.write_header (200, <<["Content-Type", "text/html"]>>) - res.write_string ("Hello World ?!%N") - res.write_string ("

Please try the following links

%N") - + create l_body.make (255) + l_body.append ("Hello World ?!%N") + l_body.append ("

Please try the following links

%N") if attached req.item ("REQUEST_COUNT") as rqc then - res.write_string ("request #"+ rqc.as_string + "%N") + l_body.append ("request #"+ rqc.as_string + "%N") end - res.write_string ("%N") + l_body.append ("%N") + + res.write_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html"], ["Content-Length", l_body.count.out]>>) + res.write_string (l_body) end execute_hello (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER; a_name: detachable READABLE_STRING_32; ctx: REQUEST_HANDLER_CONTEXT) local l_response_content_type: detachable STRING - msg: STRING h: EWF_HEADER content_type_supported: ARRAY [STRING] + l_body: STRING_8 do if a_name /= Void then - msg := "Hello %"" + a_name + "%" !%N" + l_body := "Hello %"" + a_name + "%" !%N" else - msg := "Hello anonymous visitor !%N" + l_body := "Hello anonymous visitor !%N" end - content_type_supported := <<{HTTP_CONSTANTS}.json_app, {HTTP_CONSTANTS}.html_text, {HTTP_CONSTANTS}.xml_text, {HTTP_CONSTANTS}.plain_text>> + content_type_supported := <<{HTTP_MIME_TYPES}.application_json, {HTTP_MIME_TYPES}.text_html, {HTTP_MIME_TYPES}.text_xml, {HTTP_MIME_TYPES}.text_plain>> inspect ctx.request_format_id ("format", content_type_supported) when {HTTP_FORMAT_CONSTANTS}.json then - l_response_content_type := {HTTP_CONSTANTS}.json_app - msg := "{%N%"application%": %"/hello%",%N %"message%": %"" + msg + "%" %N}" + l_response_content_type := {HTTP_MIME_TYPES}.application_json + l_body := "{%N%"application%": %"/hello%",%N %"message%": %"" + l_body + "%" %N}" when {HTTP_FORMAT_CONSTANTS}.html then - l_response_content_type := {HTTP_CONSTANTS}.html_text + l_response_content_type := {HTTP_MIME_TYPES}.text_html when {HTTP_FORMAT_CONSTANTS}.xml then - l_response_content_type := {HTTP_CONSTANTS}.xml_text - msg := "/hello" + msg + "%N" + l_response_content_type := {HTTP_MIME_TYPES}.text_xml + l_body := "/hello" + l_body + "%N" when {HTTP_FORMAT_CONSTANTS}.text then - l_response_content_type := {HTTP_CONSTANTS}.plain_text + l_response_content_type := {HTTP_MIME_TYPES}.text_plain else execute_content_type_not_allowed (req, res, content_type_supported, <<{HTTP_FORMAT_CONSTANTS}.json_name, {HTTP_FORMAT_CONSTANTS}.html_name, {HTTP_FORMAT_CONSTANTS}.xml_name, {HTTP_FORMAT_CONSTANTS}.text_name>> @@ -165,16 +164,11 @@ feature -- Execution end if l_response_content_type /= Void then create h.make - h.put_status (200) h.put_content_type (l_response_content_type) - h.put_content_length (msg.count) - res.set_status_code (200) + h.put_content_length (l_body.count) + res.set_status_code ({HTTP_STATUS_CODE}.ok) res.write_headers_string (h.string) --- res.write_header (200, << --- ["Content-Type", l_response_content_type], --- ["Content-Length", msg.count.out --- >>) - res.write_string (msg) + res.write_string (l_body) end end diff --git a/library/protocol/http/src/http_constants.e b/library/protocol/http/src/http_constants.e index 577def21..b0776f38 100644 --- a/library/protocol/http/src/http_constants.e +++ b/library/protocol/http/src/http_constants.e @@ -1,5 +1,7 @@ note - description: "Summary description for {HTTP_CONSTANTS}." + description: "[ + Constants class providing most common constants used in HTTP communication + ]" legal: "See notice at end of class." status: "See notice at end of class." date: "$Date$" @@ -8,83 +10,29 @@ note class HTTP_CONSTANTS +inherit + HTTP_MIME_TYPES + + HTTP_HEADER_NAMES + + HTTP_STATUS_CODE + + HTTP_REQUEST_METHODS + 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 +feature -- Server, header http_version_1_0: STRING = "HTTP/1.0" http_version_1_1: STRING = "HTTP/1.1" - 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: "2011-2011, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" diff --git a/library/protocol/http/src/http_header_names.e b/library/protocol/http/src/http_header_names.e new file mode 100644 index 00000000..73f037fd --- /dev/null +++ b/library/protocol/http/src/http_header_names.e @@ -0,0 +1,257 @@ +note + description: "[ + See http://en.wikipedia.org/wiki/List_of_HTTP_headers + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_HEADER_NAMES + +feature -- Request header name + + header_accept: STRING = "Accept" + -- Content-Types that are acceptable + --| Example: Accept: text/plain + + header_accept_charset: STRING = "Accept-Charset" + -- Character sets that are acceptable + --| Example: Accept-Charset: utf-8 + + header_accept_encoding: STRING = "Accept-Encoding" + -- Acceptable encodings. See HTTP compression. + --| Example: Accept-Encoding: + + header_accept_language: STRING = "Accept-Language" + -- Acceptable languages for response + --| Example: Accept-Language: en-US + + header_authorization: STRING = "Authorization" + -- Authentication credentials for HTTP authentication + --| Example: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + + header_cookie: STRING = "Cookie" + -- an HTTP cookie previously sent by the server with Set-Cookie (below) + --| Example: Cookie: $Version=1; Skin=new; + + header_expect: STRING = "Expect" + -- Indicates that particular server behaviors are required by the client + --| Example: Expect: 100-continue + + header_from: STRING = "From" + -- The email address of the user making the request + --| Example: From: user@example.com + + header_host: STRING = "Host" + -- The domain name of the server (for virtual hosting), mandatory since HTTP/1.1 + --| Example: Host: en.wikipedia.org + + header_if_match: STRING = "If-Match" + -- Only perform the action if the client supplied entity matches the same entity on the server. This is mainly for methods like PUT to only update a resource if it has not been modified since the user last updated it. + --| Example: If-Match: "737060cd8c284d8af7ad3082f209582d" + + header_if_modified_since: STRING = "If-Modified-Since" + -- Allows a 304 Not Modified to be returned if content is unchanged + --| Example: If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT + + header_if_none_match: STRING = "If-None-Match" + -- Allows a 304 Not Modified to be returned if content is unchanged, see HTTP ETag + --| Example: If-None-Match: "737060cd8c284d8af7ad3082f209582d" + + header_if_range: STRING = "If-Range" + -- If the entity is unchanged, send me the part(s) that I am missing; otherwise, send me the entire new entity + --| Example: If-Range: "737060cd8c284d8af7ad3082f209582d" + + header_if_unmodified_since: STRING = "If-Unmodified-Since" + -- Only send the response if the entity has not been modified since a specific time. + --| Example: If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT + + header_max_forwards: STRING = "Max-Forwards" + -- Limit the number of times the message can be forwarded through proxies or gateways. + --| Example: Max-Forwards: 10 + + header_proxy_authorization: STRING = "Proxy-Authorization" + -- Authorization credentials for connecting to a proxy. + --| Example: Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + + header_range: STRING = "Range" + -- Request only part of an entity. Bytes are numbered from 0. + --| Example: Range: bytes=500-999 + + header_referer: STRING = "Referer[sic]" + -- This is the address of the previous web page from which a link to the currently requested page was followed. (The word “referrer” is misspelled in the RFC as well as in most implementations.) + --| Example: Referer: http://en.wikipedia.org/wiki/Main_Page + + header_te: STRING = "TE" + -- The transfer encodings the user agent is willing to accept: the same values as for the response header Transfer-Encoding can be used, plus the "trailers" value (related to the "chunked" transfer method) to notify the server it accepts to receive additional headers (the trailers) after the last, zero-sized, chunk. + --| Example: TE: trailers, deflate + + header_upgrade: STRING = "Upgrade" + -- Ask the server to upgrade to another protocol. + --| Example: Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 + + header_user_agent: STRING = "User-Agent" + -- The user agent string of the user agent + --| Example: User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0) + +feature -- Response header name + + header_accept_ranges: STRING = "Accept-Ranges" + -- "Accept-Ranges" What partial content range types this server supports + --| Example: Accept-Ranges: bytes + + header_age: STRING = "Age" + -- The age the object has been in a proxy cache in seconds + --| Example: Age: 12 + + header_allow: STRING = "Allow" + -- Valid actions for a specified resource. To be used for a 405 Method not allowed + --| Example: Allow: GET, HEAD + + header_content_encoding: STRING = "Content-Encoding" + -- The type of encoding used on the data. See HTTP compression. + --| Example: Content-Encoding: gzip + + header_content_language: STRING = "Content-Language" + -- The language the content is in + --| Example: Content-Language: da + + header_content_location: STRING = "Content-Location" + -- An alternate location for the returned data + --| Example: Content-Location: /index.htm + + header_content_disposition: STRING = "Content-Disposition" + -- An opportunity to raise a "File Download" dialogue box for a known MIME type + --| Example: Content-Disposition: attachment; filename=fname.ext + + header_content_range: STRING = "Content-Range" + -- Where in a full body message this partial message belongs + --| Example: Content-Range: bytes 21010-47021/47022 + + header_etag: STRING = "ETag" + -- An identifier for a specific version of a resource, often a message digest + --| Example: ETag: "737060cd8c284d8af7ad3082f209582d" + + header_expires: STRING = "Expires" + -- Gives the date/time after which the response is considered stale + --| Example: Expires: Thu, 01 Dec 1994 16:00:00 GMT + + header_last_modified: STRING = "Last-Modified" + -- The last modified date for the requested object, in RFC 2822 format + --| Example: Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT + + header_link: STRING = "Link" + -- Used to express a typed relationship with another resource, where the relation type is defined by RFC 5988 + --| Example: Link: ; rel="alternate" + + header_location: STRING = "Location" + -- Used in redirection, or when a new resource has been created. + --| Example: Location: http://www.w3.org/pub/WWW/People.html + + header_p3p: STRING = "P3P" + -- This header is supposed to set P3P policy, in the form of P3P:CP="your_compact_policy". However, P3P did not take off,[5] most browsers have never fully implemented it, a lot of websites set this header with fake policy text, that was enough to fool browsers the existence of P3P policy and grant permissions for third party cookies. + --| Example: P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info." + + + header_proxy_authenticate: STRING = "Proxy-Authenticate" + -- Request authentication to access the proxy. + --| Example: Proxy-Authenticate: Basic + + header_refresh: STRING = "Refresh" + -- Used in redirection, or when a new resource has been created. This refresh redirects after 5 seconds. This is a proprietary, non-standard header extension introduced by Netscape and supported by most web browsers. + --| Example: Refresh: 5; url=http://www.w3.org/pub/WWW/People.html + + header_retry_after: STRING = "Retry-After" + -- If an entity is temporarily unavailable, this instructs the client to try again after a specified period of time. + --| Example: Retry-After: 120 + + header_server: STRING = "Server" + -- A name for the server + --| Example: Server: Apache/1.3.27 (Unix) (Red-Hat/Linux) + + header_set_cookie: STRING = "Set-Cookie" + -- an HTTP cookie + --| Example: Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1 + + header_strict_transport_security: STRING = "Strict-Transport-Security" + -- A HSTS Policy informing the HTTP client how long to cache the HTTPS only policy and whether this applies to subdomains. + --| Example: Strict-Transport-Security: max-age=16070400; includeSubDomains + + header_trailer: STRING = "Trailer" + -- The Trailer general field value indicates that the given set of header fields is present in the trailer of a message encoded with chunked transfer-coding. + --| Example: Trailer: Max-Forwards + + header_transfer_encoding: STRING = "Transfer-Encoding" + -- The form of encoding used to safely transfer the entity to the user. Currently defined methods are: chunked, compress, deflate, gzip, identity. + --| Example: Transfer-Encoding: chunked + + header_vary: STRING = "Vary" + -- Tells downstream proxies how to match future request headers to decide whether the cached response + -- can be used rather than requesting a fresh one from the origin server. + --| Example: Vary: * + + header_www_authenticate: STRING = "WWW-Authenticate" + -- Indicates the authentication scheme that should be used to access the requested entity. + --| Example: WWW-Authenticate: Basic + +feature -- Request or Response header name + + header_cache_control: STRING = "Cache-Control" + -- Request: Used to specify directives that MUST be obeyed by all caching mechanisms along the request/response chain + -- Response: Tells all caching mechanisms from server to client whether they may cache this object. It is measured in seconds + --| Request example: Cache-Control: no-cache + --| Response example: Cache-Control: max-age=3600 + + header_connection: STRING = "Connection" + -- Request: What type of connection the user-agent would prefer + -- Response: Options that are desired for the connection[4] + --| Example: Connection: close + + header_content_length: STRING = "Content-Length" + -- Request: The length of the request body in octets (8-bit bytes) + -- Response: The length of the response body in octets (8-bit bytes) + --| Example: Content-Length: 348 + + header_content_md5: STRING = "Content-MD5" + -- Request: A Base64-encoded binary MD5 sum of the content of the request body + -- Response: A Base64-encoded binary MD5 sum of the content of the response + --| Example: Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== + + header_content_type: STRING = "Content-Type" + -- Request: The mime type of the body of the request (used with POST and PUT requests) + -- Response : The mime type of this content + --| Request example: Content-Type: application/x-www-form-urlencoded + --| Response example: Content-Type: text/html; charset=utf-8 + + header_date: STRING = "Date" + -- The date and time that the message was sent + --| Example: Date: Tue, 15 Nov 1994 08:12:31 GMT + + header_pragma: STRING = "Pragma" + -- Implementation-specific headers that may have various effects anywhere along the request-response chain. + --| Example: Pragma: no-cache + + header_via: STRING = "Via" + -- Request: Informs the server of proxies through which the request was sent. + -- Response: Informs the client of proxies through which the response was sent. + --| Example: Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) + + header_warning: STRING = "Warning" + -- A general warning about possible problems with the entity body. + --| Example: Warning: 199 Miscellaneous warning + +feature -- MIME related + + header_content_transfer_encoding: STRING = "Content-Transfer-Encoding" + +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/protocol/http/src/http_mime_types.e b/library/protocol/http/src/http_mime_types.e new file mode 100644 index 00000000..4110d81c --- /dev/null +++ b/library/protocol/http/src/http_mime_types.e @@ -0,0 +1,140 @@ +note + description: "[ + Various common MIME types + + See also for longer list and description + http://www.iana.org/assignments/media-types/index.html + http://www.webmaster-toolkit.com/mime-types.shtml + + Please suggest common/popular MIME type which might be missing here + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_MIME_TYPES + +feature -- Content type : application + + application_atom_xml: STRING = "application/atom+xml" + -- atom application content-type header + + application_x_www_form_encoded: STRING = "application/x-www-form-urlencoded" + -- Starting chars of form url-encoded data content-type header + + application_octet_stream: STRING = "application/octet-stream" + -- Octet stream content-type header + + application_javascript: STRING = "application/javascript" + -- JavaScript application content-type header + + application_json: STRING = "application/json" + -- JSON application content-type header + + application_pdf: STRING = "application/pdf" + -- pdf application content-type header + + application_rss_xml: STRING = "application/rss+xml" + -- rss application content-type header + + application_xml: STRING = "application/xml" + -- xml application content-type header + + application_x_compressed: STRING = "application/x-compressed" + -- x-compressed application content-type header + + application_zip: STRING = "application/zip" + -- ZIP application content-type header + +feature -- Content type : audio + +feature -- Content type : image + + image_gif: STRING = "image/gif" + -- GIF image content-type header + + image_jpeg: STRING = "image/jpeg" + -- JPEG image content-type header + + image_jpg: STRING = "image/jpg" + -- JPEG image content-type header + + image_png: STRING = "image/png" + -- PNG image content-type header + + image_svg_xml: STRING = "image/svg+xml" + -- SVG+XML image content-type header + +feature -- Content type : message + + message_http: STRING = "message/http" + -- http message content-type header + + message_s_http: STRING = "message/s-http" + -- s-http message content-type + + message_partial: STRING = "message/partial" + -- partial message content-type + + message_sip: STRING = "message/sip" + -- sip message content-type + +feature -- Content type : model + +feature -- Content type : multipart + + multipart_mixed: STRING = "multipart/mixed" + + multipart_alternative: STRING = "multipart/alternative" + + multipart_related: STRING = "multipart/related" + + multipart_form_data: STRING = "multipart/form-data" + + multipart_signed: STRING = "multipart/signed" + + multipart_encrypted: STRING = "multipart/encrypted" + +feature -- Content type : text + + text_css: STRING = "text/css" + -- CSS text content-type header + + text_csv: STRING = "text/csv" + -- CSV text content-type header + + text_html: STRING = "text/html" + -- HTML text content-type header + + text_javascript: STRING = "text/javascript" + -- Javascript text content-type header + -- OBSOLETE + + text_json: STRING = "text/json" + -- JSON text content-type header + + text_plain: STRING = "text/plain" + -- Plain text content-type header + + text_rtf: STRING = "text/rtf" + -- rtf content-type header + + text_xml: STRING = "text/xml" + -- XML text content-type header + + text_vcard: STRING = "text/vcard" + -- vcard text content-type header + +feature -- Content type : video + +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/protocol/http/src/http_request_method_constants.e b/library/protocol/http/src/http_request_method_constants.e index d8bf51d8..19201481 100644 --- a/library/protocol/http/src/http_request_method_constants.e +++ b/library/protocol/http/src/http_request_method_constants.e @@ -6,31 +6,30 @@ note class HTTP_REQUEST_METHOD_CONSTANTS +inherit + HTTP_REQUEST_METHODS + feature -- Id - method_get: INTEGER = 0x1 + head: INTEGER = 0x1 - method_post: INTEGER = 0x2 + get: INTEGER = 0x2 - method_put: INTEGER = 0x4 + trace: INTEGER = 0x4 - method_delete: INTEGER = 0x8 + options: INTEGER = 0x8 - method_head: INTEGER = 0x10 + post: INTEGER = 0x10 + + put: INTEGER = 0x20 + + delete: INTEGER = 0x40 + + connect: INTEGER = 0x80 feature -- Name - method_get_name: STRING = "GET" - - method_post_name: STRING = "POST" - - method_put_name: STRING = "PUT" - - method_delete_name: STRING = "DELETE" - - method_head_name: STRING = "HEAD" - - method_empty_name: STRING = "" + method_empty: STRING = "" feature -- Query @@ -39,35 +38,45 @@ feature -- Query s: STRING do s := a_id.as_lower - if s.same_string (method_get_name) then - Result := method_get - elseif s.same_string (method_post_name) then - Result := method_post - elseif s.same_string (method_put_name) then - Result := method_put - elseif s.same_string (method_delete_name) then - Result := method_delete - elseif s.same_string (method_head_name) then - Result := method_head + if s.same_string (method_get) then + Result := get + elseif s.same_string (method_post) then + Result := post + elseif s.same_string (method_put) then + Result := put + elseif s.same_string (method_delete) then + Result := delete + elseif s.same_string (method_head) then + Result := head + elseif s.same_string (method_trace) then + Result := trace + elseif s.same_string (method_options) then + Result := options + elseif s.same_string (method_connect) then + Result := connect end end method_name (a_id: INTEGER): STRING do inspect a_id - when method_get then Result := method_get_name - when method_post then Result := method_post_name - when method_put then Result := method_put_name - when method_delete then Result := method_delete_name - when method_head then Result := method_head_name - else Result := method_empty_name + when head then Result := method_head + when get then Result := method_get + when trace then Result := method_trace + when options then Result := method_options + when post then Result := method_post + when put then Result := method_put + when delete then Result := method_delete + when connect then Result := method_connect + else + Result := method_empty end ensure result_is_upper_case: Result /= Void and then Result.as_upper ~ Result end note - copyright: "Copyright (c) 1984-2011, Eiffel Software and others" + copyright: "2011-2011, Eiffel Software and others" license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" source: "[ Eiffel Software @@ -76,4 +85,5 @@ note Website http://www.eiffel.com Customer support http://support.eiffel.com ]" + end diff --git a/library/protocol/http/src/http_request_methods.e b/library/protocol/http/src/http_request_methods.e new file mode 100644 index 00000000..56603683 --- /dev/null +++ b/library/protocol/http/src/http_request_methods.e @@ -0,0 +1,75 @@ +note + description: "[ + + Safe method: HEAD, GET, TRACE, OPTIONS + Intended only for information retrieval, should not change state of server + + + See http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods + ]" + date: "$Date$" + revision: "$Revision$" + +class + HTTP_REQUEST_METHODS + +feature -- Safe Methods + + method_head: STRING = "HEAD" + -- Asks for the response identical to the one that would correspond + -- to a GET request, but without the response body. + -- This is useful for retrieving meta-information written in response headers, + -- without having to transport the entire content. + + method_get: STRING = "GET" + -- Requests a representation of the specified resource. + -- Requests using GET (and a few other HTTP methods) + -- "SHOULD NOT have the significance of taking an action other than retrieval" + -- The W3C has published guidance principles on this distinction, saying, + -- "Web application design should be informed by the above principles, + -- but also by the relevant limitations." + + method_trace: STRING = "TRACE" + -- Echoes back the received request, so that a client can see what + -- (if any) changes or additions have been made by intermediate servers. + + method_options: STRING = "OPTIONS" + -- Returns the HTTP methods that the server supports for specified URL. + -- This can be used to check the functionality of a web server by requesting '*' + -- instead of a specific resource. + +feature -- Methods intented for actions + + method_post: STRING = "POST" + -- Submits data to be processed (e.g., from an HTML form) to the identified resource. + -- The data is included in the body of the request. + -- This may result in the creation of a new resource or the updates of existing + -- resources or both. + + method_put: STRING = "PUT" + -- Uploads a representation of the specified resource. + + method_delete: STRING = "DELETE" + -- Deletes the specified resource. + +feature -- Other Methods + + method_connect: STRING = "CONNECT" + -- Converts the request connection to a transparent TCP/IP tunnel, + -- usually to facilitate SSL-encrypted communication (HTTPS) through + -- an unencrypted HTTP proxy. + + method_patch: STRING = "PATCH" + -- Is used to apply partial modifications to a resource + +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/support/ewf_header.e b/library/server/ewsgi/src/support/ewf_header.e index 8b01783b..618f9af3 100644 --- a/library/server/ewsgi/src/support/ewf_header.e +++ b/library/server/ewsgi/src/support/ewf_header.e @@ -1,8 +1,18 @@ note description: "[ - Summary description for {EWF_HEADER}. - + The class provides an easy way to build HTTP header. + + You will also find some helper feature to help coding most common usage + + Please, have a look at constants classes such as + HTTP_MIME_TYPES + HTTP_HEADER_NAMES + HTTP_STATUS_CODE + HTTP_REQUEST_METHODS + (or HTTP_CONSTANTS which groups them for convenience) + Note the return status code is not part of the HTTP header + However you can set the "Status: " header line if you want ]" legal: "See notice at end of class." status: "See notice at end of class." @@ -15,25 +25,33 @@ class inherit ANY - HTTP_STATUS_CODE_MESSAGES + HTTP_STATUS_CODE_MESSAGES --| useful for `put_status' export {NONE} all end create - make + make, + make_with_count feature {NONE} -- Initialization make -- Initialize current do - create {ARRAYED_LIST [READABLE_STRING_8]} headers.make (3) + make_with_count (3) + end + + make_with_count (n: INTEGER) + -- Make with a capacity of `n' header entries + do + create {ARRAYED_LIST [READABLE_STRING_8]} headers.make (n) end feature -- Recycle recycle + -- Recycle current object do headers.wipe_out end @@ -68,12 +86,19 @@ feature -- Access feature -- Header change: general add_header (h: READABLE_STRING_8) + -- Add header `h' + -- if it already exists, there will be multiple header with same name + -- which can also be valid + require + h_not_empty: not h.is_empty do headers.force (h) end put_header (h: READABLE_STRING_8) -- Add header `h' or replace existing header of same header name + require + h_not_empty: not h.is_empty do force_header_by_name (header_name (h), h) end @@ -90,9 +115,11 @@ feature -- Header change: general put_header (k + colon_space + v) end -feature -- Content related header +feature -- Status related put_status (c: INTEGER) + -- Put "Status: " header + -- Rarely used local s: STRING do @@ -104,58 +131,33 @@ feature -- Content related header put_header_key_value ("Status", s) end +feature -- Content related header + put_content_type (t: READABLE_STRING_8) do - put_header_key_value (name_content_type, t) + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) end add_content_type (t: READABLE_STRING_8) -- same as `put_content_type', but allow multiple definition of "Content-Type" do - add_header_key_value (name_content_type, t) + add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t) end put_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) do - put_header_key_value (name_content_type, t + "; name=%"" + n + "%"") + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t + "; name=%"" + n + "%"") end add_content_type_with_name (t: READABLE_STRING_8; n: READABLE_STRING_8) -- same as `put_content_type_with_name', but allow multiple definition of "Content-Type" do - add_header_key_value (name_content_type, t + "; name=%"" + n + "%"") + add_header_key_value ({HTTP_HEADER_NAMES}.header_content_type, t + "; name=%"" + n + "%"") end - put_content_type_text_css do put_content_type ("text/css") end - put_content_type_text_csv do put_content_type ("text/csv") end - put_content_type_text_html do put_content_type ("text/html") end - put_content_type_text_javascript do put_content_type ("text/javascript") end - put_content_type_text_json do put_content_type ("text/json") end - put_content_type_text_plain do put_content_type ("text/plain") end - put_content_type_text_xml do put_content_type ("text/xml") end - - put_content_type_application_json do put_content_type ("application/json") end - put_content_type_application_javascript do put_content_type ("application/javascript") end - put_content_type_application_zip do put_content_type ("application/zip") end - - put_content_type_image_gif do put_content_type ("image/gif") end - put_content_type_image_png do put_content_type ("image/png") end - put_content_type_image_jpg do put_content_type ("image/jpg") end - put_content_type_image_svg_xml do put_content_type ("image/svg+xml") end - - put_content_type_message_http do put_content_type ("message/http") end - - put_content_type_multipart_mixed do put_content_type ("multipart/mixed") end - put_content_type_multipart_alternative do put_content_type ("multipart/alternative") end - put_content_type_multipart_related do put_content_type ("multipart/related") end - put_content_type_multipart_form_data do put_content_type ("multipart/form-data") end - put_content_type_multipart_signed do put_content_type ("multipart/signed") end - put_content_type_multipart_encrypted do put_content_type ("multipart/encrypted") end - - put_content_length (n: INTEGER) do - put_header_key_value (name_content_length, n.out) + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_length, n.out) end put_content_transfer_encoding (a_mechanism: READABLE_STRING_8) @@ -170,7 +172,19 @@ feature -- Content related header --| / x-token do - put_header_key_value ("Content-Transfer-Encoding", a_mechanism) + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_transfer_encoding, a_mechanism) + end + + put_transfer_encoding (a_enc: READABLE_STRING_8) + -- Put "Transfer-Encoding" header with for instance "chunked" + do + put_header_key_value ({HTTP_HEADER_NAMES}.header_transfer_encoding, a_enc) + end + + put_transfer_encoding_chunked + -- Put "Transfer-Encoding: chunked" header + do + put_transfer_encoding ("chunked") end put_content_disposition (a_type: READABLE_STRING_8; a_params: detachable READABLE_STRING_8) @@ -199,17 +213,46 @@ feature -- Content related header --| ; numeric timezones (+HHMM or -HHMM) MUST be used do if a_params /= Void then - put_header_key_value ("Content-Disposition", a_type + "; " + a_params) + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type + semi_colon_space + a_params) else - put_header_key_value ("Content-Disposition", a_type) + put_header_key_value ({HTTP_HEADER_NAMES}.header_content_disposition, a_type) end end -feature -- Others +feature -- Content-type helpers + + put_content_type_text_css do put_content_type ({HTTP_MIME_TYPES}.text_css) end + put_content_type_text_csv do put_content_type ({HTTP_MIME_TYPES}.text_csv) end + put_content_type_text_html do put_content_type ({HTTP_MIME_TYPES}.text_html) end + put_content_type_text_javascript do put_content_type ({HTTP_MIME_TYPES}.text_javascript) end + put_content_type_text_json do put_content_type ({HTTP_MIME_TYPES}.text_json) end + put_content_type_text_plain do put_content_type ({HTTP_MIME_TYPES}.text_plain) end + put_content_type_text_xml do put_content_type ({HTTP_MIME_TYPES}.text_xml) end + + put_content_type_application_json do put_content_type ({HTTP_MIME_TYPES}.application_json) end + put_content_type_application_javascript do put_content_type ({HTTP_MIME_TYPES}.application_javascript) end + put_content_type_application_zip do put_content_type ({HTTP_MIME_TYPES}.application_zip) end + + put_content_type_image_gif do put_content_type ({HTTP_MIME_TYPES}.image_gif) end + put_content_type_image_png do put_content_type ({HTTP_MIME_TYPES}.image_png) end + put_content_type_image_jpg do put_content_type ({HTTP_MIME_TYPES}.image_jpg) end + put_content_type_image_svg_xml do put_content_type ({HTTP_MIME_TYPES}.image_svg_xml) end + + put_content_type_message_http do put_content_type ({HTTP_MIME_TYPES}.message_http) end + + put_content_type_multipart_mixed do put_content_type ({HTTP_MIME_TYPES}.multipart_mixed) end + put_content_type_multipart_alternative do put_content_type ({HTTP_MIME_TYPES}.multipart_alternative) end + put_content_type_multipart_related do put_content_type ({HTTP_MIME_TYPES}.multipart_related) end + put_content_type_multipart_form_data do put_content_type ({HTTP_MIME_TYPES}.multipart_form_data) end + put_content_type_multipart_signed do put_content_type ({HTTP_MIME_TYPES}.multipart_signed) end + put_content_type_multipart_encrypted do put_content_type ({HTTP_MIME_TYPES}.multipart_encrypted) end + +feature -- Date put_date (s: READABLE_STRING_8) + -- Put "Date: " header do - put_header_key_value ("Date", s) + put_header_key_value ({HTTP_HEADER_NAMES}.header_date, s) end put_current_date @@ -224,6 +267,8 @@ feature -- Others put_date (dt.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT") end +feature -- Others + put_expires (n: INTEGER) do put_header_key_value ("Expires", n.out) @@ -249,14 +294,18 @@ feature -- Redirection put_location (a_location: READABLE_STRING_8) -- Tell the client the new location `a_location' + require + a_location_valid: not a_location.is_empty do - put_header_key_value ("Location", a_location) + put_header_key_value ({HTTP_HEADER_NAMES}.header_location, a_location) end put_refresh (a_location: READABLE_STRING_8; a_timeout_in_seconds: INTEGER) -- Tell the client to refresh page with `a_location' after `a_timeout_in_seconds' in seconds + require + a_location_valid: not a_location.is_empty do - put_header_key_value ("Refresh", a_timeout_in_seconds.out + "; url=" + a_location) + put_header_key_value ({HTTP_HEADER_NAMES}.header_refresh, a_timeout_in_seconds.out + "; url=" + a_location) end feature -- Cookie @@ -269,18 +318,18 @@ feature -- Cookie local s: STRING do - s := "Set-Cookie:" + key + "=" + value + s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + key + "=" + value if expiration /= Void then - s.append (";expires=" + expiration) + s.append ("; expires=" + expiration) end if path /= Void then - s.append (";path=" + path) + s.append ("; path=" + path) end if domain /= Void then - s.append (";domain=" + domain) + s.append ("; domain=" + domain) end if secure /= Void then - s.append (";secure=" + secure) + s.append ("; secure=" + secure) end add_header (s) end @@ -292,6 +341,7 @@ feature -- Status report local c: like headers.new_cursor n: INTEGER + l_line: READABLE_STRING_8 do from n := a_name.count @@ -299,7 +349,12 @@ feature -- Status report until c.after or Result loop - Result := c.item.starts_with (a_name) and then c.item [n + 1] = ':' + l_line := c.item + if l_line.starts_with (a_name) then + if l_line.valid_index (n + 1) then + Result := l_line [n + 1] = ':' + end + end c.forth end end @@ -307,7 +362,7 @@ feature -- Status report has_content_length: BOOLEAN -- Has header "content_length" do - Result := has_header_named (name_content_length) + Result := has_header_named ({HTTP_HEADER_NAMES}.header_content_length) end feature {NONE} -- Implementation: Header @@ -384,8 +439,7 @@ feature {NONE} -- Implementation feature {NONE} -- Constants colon_space: STRING = ": " - name_content_length: STRING = "Content-Length" - name_content_type: STRING = "Content-Type" + semi_colon_space: STRING = "; " note copyright: "2011-2011, Eiffel Software and others" diff --git a/library/server/ewsgi/src/wgi_request_from_table.e b/library/server/ewsgi/src/wgi_request_from_table.e index 8f967bf4..9d181d79 100644 --- a/library/server/ewsgi/src/wgi_request_from_table.e +++ b/library/server/ewsgi/src/wgi_request_from_table.e @@ -513,7 +513,7 @@ feature {NONE} -- Form fields and related l_type := content_type if l_type /= Void and then - l_type.starts_with ({HTTP_CONSTANTS}.multipart_form) + l_type.starts_with ({HTTP_MIME_TYPES}.multipart_form_data) then create vars.make (5) vars.compare_objects diff --git a/library/server/ewsgi/src/wgi_response_stream_buffer.e b/library/server/ewsgi/src/wgi_response_stream_buffer.e index 416b6fdd..fa39f475 100644 --- a/library/server/ewsgi/src/wgi_response_stream_buffer.e +++ b/library/server/ewsgi/src/wgi_response_stream_buffer.e @@ -90,7 +90,6 @@ feature -- Header output operation do set_status_code (a_status_code) create h.make - h.put_status (a_status_code) if a_headers /= Void then from i := a_headers.lower diff --git a/library/server/ewsgi/tests/test_ewsgi_request.e b/library/server/ewsgi/tests/test_ewsgi_request.e index 30ac9873..9f0a7be7 100644 --- a/library/server/ewsgi/tests/test_ewsgi_request.e +++ b/library/server/ewsgi/tests/test_ewsgi_request.e @@ -161,7 +161,7 @@ feature {NONE} -- Events do get_http_session if attached http_session as sess then - if attached sess.post (a_url, ctx) as res and then not res.error_occurred and then attached res.body as l_body then + if attached sess.post (a_url, ctx, Void) as res and then not res.error_occurred and then attached res.body as l_body then assert ("Good answer got=%""+l_body+"%" expected=%""+a_expected_body+"%"", l_body.same_string (a_expected_body)) else assert ("Request %""+a_url+"%" failed", False) diff --git a/library/server/request/router/src/misc/request_resource_handler_helper.e b/library/server/request/router/src/misc/request_resource_handler_helper.e index 80c497d9..f106b16f 100644 --- a/library/server/request/router/src/misc/request_resource_handler_helper.e +++ b/library/server/request/router/src/misc/request_resource_handler_helper.e @@ -15,21 +15,21 @@ feature -- Execute template m: READABLE_STRING_8 do m := req.request_method.as_upper - if m.same_string ("GET") then + if m.same_string ({HTTP_REQUEST_METHODS}.method_get) then execute_get (ctx, req, res) - elseif m.same_string ("PUT") then + elseif m.same_string ({HTTP_REQUEST_METHODS}.method_put) then execute_put (ctx, req, res) - elseif m.same_string ("DELETE") then + elseif m.same_string ({HTTP_REQUEST_METHODS}.method_delete) then execute_delete (ctx, req, res) - elseif m.same_string ("POST") then + elseif m.same_string ({HTTP_REQUEST_METHODS}.method_post) then execute_post (ctx, req, res) - elseif m.same_string ("TRACE") then + elseif m.same_string ({HTTP_REQUEST_METHODS}.method_trace) then execute_trace (ctx, req, res) - elseif m.same_string ("OPTIONS") then + elseif m.same_string ({HTTP_REQUEST_METHODS}.method_options) then execute_options (ctx, req, res) - elseif m.same_string ("HEAD") then + elseif m.same_string ({HTTP_REQUEST_METHODS}.method_head) then execute_head (ctx, req, res) - elseif m.same_string ("CONNECT") then + elseif m.same_string ({HTTP_REQUEST_METHODS}.method_connect) then execute_connect (ctx, req, res) else --| Eventually handle other methods... diff --git a/library/server/request/router/src/misc/routed_application_helper.e b/library/server/request/router/src/misc/routed_application_helper.e index 092511d2..ce36ac54 100644 --- a/library/server/request/router/src/misc/routed_application_helper.e +++ b/library/server/request/router/src/misc/routed_application_helper.e @@ -79,7 +79,7 @@ feature -- Helper end res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed) res.write_header ({HTTP_STATUS_CODE}.method_not_allowed, << - ["Content-Type", {HTTP_CONSTANTS}.plain_text], + ["Content-Type", {HTTP_MIME_TYPES}.text_plain], ["Allow", s] >>) res.write_string ("Unsupported request method, Allow: " + s + "%N") diff --git a/library/server/request/router/src/request_handler_context.e b/library/server/request/router/src/request_handler_context.e index bfb16b39..0e29c643 100644 --- a/library/server/request/router/src/request_handler_context.e +++ b/library/server/request/router/src/request_handler_context.e @@ -1,6 +1,5 @@ note description: "Summary description for {REQUEST_HANDLER_CONTEXT}." - author: "" date: "$Date$" revision: "$Revision$" @@ -56,15 +55,15 @@ feature -- Query -- `a_content_type' converted into a request format name do if a_content_type /= Void then - if a_content_type.same_string ({HTTP_CONSTANTS}.json_text) then + if a_content_type.same_string ({HTTP_MIME_TYPES}.text_json) then Result := {HTTP_FORMAT_CONSTANTS}.json_name - elseif a_content_type.same_string ({HTTP_CONSTANTS}.json_app) then + elseif a_content_type.same_string ({HTTP_MIME_TYPES}.application_json) then Result := {HTTP_FORMAT_CONSTANTS}.json_name - elseif a_content_type.same_string ({HTTP_CONSTANTS}.xml_text) then + elseif a_content_type.same_string ({HTTP_MIME_TYPES}.text_xml) then Result := {HTTP_FORMAT_CONSTANTS}.xml_name - elseif a_content_type.same_string ({HTTP_CONSTANTS}.html_text) then + elseif a_content_type.same_string ({HTTP_MIME_TYPES}.text_html) then Result := {HTTP_FORMAT_CONSTANTS}.html_name - elseif a_content_type.same_string ({HTTP_CONSTANTS}.plain_text) then + elseif a_content_type.same_string ({HTTP_MIME_TYPES}.text_plain) then Result := {HTTP_FORMAT_CONSTANTS}.text_name end end