diff --git a/.gitignore b/.gitignore index c4d48214..332345d9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ tests/temp/ .svn/ *.swp *~ +*# diff --git a/library/network/http_client/src/http_client_request_context.e b/library/network/http_client/src/http_client_request_context.e index a02ba6b3..663c6a57 100644 --- a/library/network/http_client/src/http_client_request_context.e +++ b/library/network/http_client/src/http_client_request_context.e @@ -113,6 +113,27 @@ feature -- Element change headers.force (v, k) end + add_header_line (s: READABLE_STRING_8) + local + i: INTEGER + do + i := s.index_of (':', 1) + if i > 0 then + add_header (s.substring (1, i - 1), s.substring (i + 1, s.count)) + end + end + + add_header_lines (lst: ITERABLE [READABLE_STRING_8]) + local + i: INTEGER + do + across + lst as c + loop + add_header_line (c.item) + end + end + add_query_parameter (k: READABLE_STRING_32; v: READABLE_STRING_32) do query_parameters.force (v, k) diff --git a/library/network/http_client/src/http_client_session.e b/library/network/http_client/src/http_client_session.e index a2721957..a0aeec09 100644 --- a/library/network/http_client/src/http_client_session.e +++ b/library/network/http_client/src/http_client_session.e @@ -76,6 +76,11 @@ feature -- Basic operation end end + custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + -- Response for `a_method' request based on Current, `a_path' and `ctx'. + deferred + end + get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE -- Response for GET request based on Current, `a_path' and `ctx'. deferred @@ -124,7 +129,7 @@ feature -- Status report -- Is interface usable? deferred end - + feature -- Settings timeout: INTEGER diff --git a/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e b/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e index 4f80befb..e54dd360 100644 --- a/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e +++ b/library/network/http_client/src/spec/libcurl/libcurl_http_client_session.e @@ -33,20 +33,22 @@ feature -- Status report feature -- Basic operation - get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + custom (a_method: READABLE_STRING_8; a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE local req: HTTP_CLIENT_REQUEST do - create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "GET", Current, ctx) + create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, a_method, Current, ctx) Result := req.execute end - head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE - local - req: HTTP_CLIENT_REQUEST + get (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE do - create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "HEAD", Current, ctx) - Result := req.execute + Result := custom ("GET", a_path, ctx) + end + + head (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE + do + Result := custom ("HEAD", a_path, ctx) end post (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; data: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE @@ -87,8 +89,7 @@ feature -- Basic operation ctx.set_upload_filename (f.name) end end - create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "PUT", Current, ctx) - Result := req.execute + Result := custom ("PUT", a_path, ctx) if f /= Void then f.delete end @@ -100,7 +101,6 @@ feature -- Basic operation put_file (a_path: READABLE_STRING_8; a_ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT; fn: detachable READABLE_STRING_8): HTTP_CLIENT_RESPONSE local - req: HTTP_CLIENT_REQUEST ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT do ctx := a_ctx @@ -110,16 +110,12 @@ feature -- Basic operation end ctx.set_upload_filename (fn) end - create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "PUT", Current, ctx) - Result := req.execute + Result := custom ("PUT", a_path, ctx) end delete (a_path: READABLE_STRING_8; ctx: detachable HTTP_CLIENT_REQUEST_CONTEXT): HTTP_CLIENT_RESPONSE - local - req: HTTP_CLIENT_REQUEST do - create {LIBCURL_HTTP_CLIENT_REQUEST} req.make (base_url + a_path, "DELETE", Current, ctx) - Result := req.execute + Result := custom ("DELETE", a_path, ctx) end feature {NONE} -- Implementation diff --git a/library/server/ewsgi/specification/request/wgi_meta_names.e b/library/server/ewsgi/specification/request/wgi_meta_names.e index 7eeb913e..a4578a6b 100644 --- a/library/server/ewsgi/specification/request/wgi_meta_names.e +++ b/library/server/ewsgi/specification/request/wgi_meta_names.e @@ -52,6 +52,8 @@ feature -- Access http_access_control_request_headers: STRING = "HTTP_ACCESS_CONTROL_REQUEST_HEADERS" + http_if_match: STRING = "HTTP_IF_MATCH" + gateway_interface: STRING = "GATEWAY_INTERFACE" auth_type: STRING = "AUTH_TYPE" diff --git a/library/server/ewsgi/specification/request/wgi_request.e b/library/server/ewsgi/specification/request/wgi_request.e index 86e1e489..9420a766 100644 --- a/library/server/ewsgi/specification/request/wgi_request.e +++ b/library/server/ewsgi/specification/request/wgi_request.e @@ -604,6 +604,11 @@ feature -- HTTP_* deferred end + http_if_match: detachable READABLE_STRING_8 + -- Existence check on resource + deferred + end + feature -- Extra CGI environment variables request_uri: READABLE_STRING_8 diff --git a/library/server/ewsgi/src/implementation/wgi_request_from_table.e b/library/server/ewsgi/src/implementation/wgi_request_from_table.e index 79fb3007..636f5495 100644 --- a/library/server/ewsgi/src/implementation/wgi_request_from_table.e +++ b/library/server/ewsgi/src/implementation/wgi_request_from_table.e @@ -248,6 +248,12 @@ feature -- Access: HTTP_* CGI meta parameters - 1.1 Result := meta_string_variable ({WGI_META_NAMES}.http_access_control_request_headers) end + http_if_match: detachable READABLE_STRING_8 + -- Existence check on resource + do + Result := meta_string_variable ({WGI_META_NAMES}.http_if_match) + end + feature -- Access: Extension to CGI meta parameters - 1.1 request_uri: READABLE_STRING_8 diff --git a/library/server/wsf/router/wsf_routed_skeleton_service.e b/library/server/wsf/router/wsf_routed_skeleton_service.e index 22c44c90..48284652 100644 --- a/library/server/wsf/router/wsf_routed_skeleton_service.e +++ b/library/server/wsf/router/wsf_routed_skeleton_service.e @@ -256,6 +256,7 @@ feature {NONE} -- Implementation h.put_current_date h.put_location (proxy_server (req).string) h.put_content_length (0) + res.put_header_lines (h) res.set_status_code ({HTTP_STATUS_CODE}.use_proxy) ensure response_status_is_set: res.status_is_set diff --git a/library/server/wsf/src/response/wsf_default_router_response.e b/library/server/wsf/src/response/wsf_default_router_response.e index cc933b03..23b7ef99 100644 --- a/library/server/wsf/src/response/wsf_default_router_response.e +++ b/library/server/wsf/src/response/wsf_default_router_response.e @@ -58,8 +58,20 @@ feature {WSF_RESPONSE} -- Output if req.is_request_method ({HTTP_REQUEST_METHODS}.method_trace) then msg := trace_message (req) elseif attached method_not_allowed_message (req) as not_allowed then + --| We give this precedence over 412 Precondition Failed or 404 Not Found, + --| as we assume the existence of a handler for at least one method + --| indicates existence of the resource. This is obviously not the + --| case if the only method allowed is POST, but the handler ought + --| to handle the 404 Not Found and 412 Precondition Failed cases in that case. + --| Ditto for template URI handlers where not all template variable + --| values map to existing resources. msg := not_allowed + elseif attached req.http_if_match as l_match and then l_match.same_string ("*") then + msg := precondition_failed_message (req) else + --| Other response codes are possible, such as 301 Moved permananently, + --| 302 Found and 410 Gone. But these require handlers to implement, + --| so no other code can be given at this point. msg := not_found_message (req) end res.send (msg) @@ -67,7 +79,17 @@ feature {WSF_RESPONSE} -- Output feature {NONE} -- Implementation + precondition_failed_message (req: WSF_REQUEST): WSF_PRECONDITION_FAILED_MESSAGE + -- Automatically generated response for 412 Precondition Failed response + require + req_attached: req /= Void + do + create Result.make (req) + end + method_not_allowed_message (req: WSF_REQUEST): detachable WSF_METHOD_NOT_ALLOWED_RESPONSE + require + req_attached: req /= Void local vis: WSF_ROUTER_AGENT_ITERATOR do diff --git a/library/server/wsf/src/response/wsf_download_response.e b/library/server/wsf/src/response/wsf_download_response.e index fada53f8..cedc8a2a 100644 --- a/library/server/wsf/src/response/wsf_download_response.e +++ b/library/server/wsf/src/response/wsf_download_response.e @@ -45,6 +45,7 @@ feature {NONE} -- Initialization initialize local h: like header + d: HTTP_DATE do create h.make header := h @@ -52,6 +53,9 @@ feature {NONE} -- Initialization h.put_transfer_encoding_binary h.put_content_length (filesize (file_name)) h.put_content_disposition ("attachment", "filename=%""+ base_name +"%"") + if attached filedate (file_name) as dt then + h.put_last_modified (dt) + end end feature -- Element change @@ -138,6 +142,19 @@ feature {NONE} -- Implementation: file system helper end end + filedate (fn: STRING): detachable DATE_TIME + -- Size of the file `fn'. + local + f: RAW_FILE + d: HTTP_DATE + do + create f.make (fn) + if f.exists then + create d.make_from_timestamp (f.date) + Result := d.date_time + end + end + file_extension (fn: STRING): STRING -- Extension of file `fn'. local diff --git a/library/server/wsf/src/response/wsf_precondition_failed_message.e b/library/server/wsf/src/response/wsf_precondition_failed_message.e new file mode 100644 index 00000000..da0ac71f --- /dev/null +++ b/library/server/wsf/src/response/wsf_precondition_failed_message.e @@ -0,0 +1,130 @@ +note + description: "[ + This class is used to report a 412 Precondition Failed page + ]" + date: "$Date$" + revision: "$Revision$" + +class WSF_PRECONDITION_FAILED_MESSAGE + +inherit + + WSF_RESPONSE_MESSAGE + + SHARED_HTML_ENCODER + +create + + make + +feature {NONE} -- Initialization + + make (req: WSF_REQUEST) + -- Initialize setting `request' from `req'. + do + request := req + create header.make + ensure + req_aliased: request = req + end + +feature -- Header + + header: HTTP_HEADER + -- Response' header + + request: WSF_REQUEST + -- Associated request. + + body: detachable READABLE_STRING_8 + -- Optional body + -- Displayed as extra content + +feature -- Element change + + set_body (b: like body) + -- Set `body' to `b' + do + body := b + end + +feature {WSF_RESPONSE} -- Output + + send_to (res: WSF_RESPONSE) + local + s: STRING + l_text: detachable READABLE_STRING_GENERAL + l_loc: detachable READABLE_STRING_8 + h: like header + do + h := header + res.set_status_code ({HTTP_STATUS_CODE}.precondition_failed) + + if request.is_content_type_accepted ({HTTP_MIME_TYPES}.text_html) then + s := "" + s.append ("") + s.append (html_encoder.encoded_string (request.request_uri)) + s.append ("Error 412 (Precondition Failed)") + s.append ("%N") + s.append ("[ + + + + + ]") + s.append ("
") + s.append ("
") + s.append ("
") + s.append ("
") + s.append ("
") + s.append ("Error 412 (Precondition Failed)
") + s.append ("
Error 412 (Precondition Failed): " + html_encoder.encoded_string (request.request_uri) + "
") + if attached body as b then + s.append ("
") + s.append (b) + s.append ("
%N") + end + + s.append ("
") + s.append ("%N") + s.append ("%N") + + h.put_content_type_text_html + else + s := "Error 412 (Precondition Failed): " + s.append (request.request_uri) + s.append_character ('%N') + if attached body as b then + s.append ("%N") + s.append (b) + s.append ("%N") + end + + h.put_content_type_text_plain + end + h.put_content_length (s.count) + res.put_header_text (h.string) + res.put_string (s) + res.flush + end + +note + + copyright: "2013, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others" + license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)" + source: "[ + Eiffel Software + 5949 Hollister Ave., Goleta, CA 93117 USA + Telephone 805-685-1006, Fax 805-685-6869 + Website http://www.eiffel.com + Customer support http://support.eiffel.com + ]" +end diff --git a/library/server/wsf/src/wsf_request.e b/library/server/wsf/src/wsf_request.e index eeea1a54..f460180a 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -1042,6 +1042,12 @@ feature -- HTTP_* Result := wgi_request.http_access_control_request_headers end + http_if_match: detachable READABLE_STRING_8 + -- Existence check on resource + do + Result := wgi_request.http_if_match + end + feature -- Extra CGI environment variables request_uri: READABLE_STRING_8 diff --git a/library/server/wsf/src/wsf_response.e b/library/server/wsf/src/wsf_response.e index 72be7265..5ec5c0f6 100644 --- a/library/server/wsf/src/wsf_response.e +++ b/library/server/wsf/src/wsf_response.e @@ -207,7 +207,7 @@ feature -- Header output operation if header_committed then report_content_already_sent_and_header_ignored else - header.append_raw_header_data (a_text) + header.put_raw_header_data (a_text) end ensure message_writable: message_writable