From b7505e67b83987167f6d122abbf8d81049c460cd Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Thu, 13 Dec 2012 17:29:46 +0100 Subject: [PATCH] Added to WSF_REQUEST - raw_header_data: like meta_string_variable - read_input_data_into (buf: STRING) - is_content_type_accepted (a_content_type: READABLE_STRING_GENERAL): BOOLEAN Changed raw_input_data to return IMMUTABLE_STRING_8 Added WSF_METHOD_NOT_ALLOWED_RESPONSE Added WSF_TRACE_RESPONSE to respond TRACE request Now Not_found response return html content if the client accepts, other text/plain Implemented TRACE response, and Method not allowed as implementation of WSF_ROUTED_SERVICE.execute_default --- .../server/wsf/router/wsf_routed_service.e | 17 ++- library/server/wsf/router/wsf_router.e | 25 ++++ .../wsf_method_not_allowed_response.e | 139 ++++++++++++++++++ .../wsf/src/response/wsf_not_found_response.e | 125 +++++++++------- .../wsf/src/response/wsf_trace_response.e | 97 ++++++++++++ library/server/wsf/src/wsf_request.e | 68 ++++++++- 6 files changed, 417 insertions(+), 54 deletions(-) create mode 100644 library/server/wsf/src/response/wsf_method_not_allowed_response.e create mode 100644 library/server/wsf/src/response/wsf_trace_response.e diff --git a/library/server/wsf/router/wsf_routed_service.e b/library/server/wsf/router/wsf_routed_service.e index 00dd52ab..9f201eed 100644 --- a/library/server/wsf/router/wsf_routed_service.e +++ b/library/server/wsf/router/wsf_routed_service.e @@ -48,10 +48,23 @@ feature -- Execution execute_default (req: WSF_REQUEST; res: WSF_RESPONSE) -- Default procedure local + msg: WSF_RESPONSE_MESSAGE not_found: WSF_NOT_FOUND_RESPONSE + not_allowed: WSF_METHOD_NOT_ALLOWED_RESPONSE + trace: WSF_TRACE_RESPONSE do - create not_found.make (req) - res.send (not_found) + if req.is_request_method ({HTTP_REQUEST_METHODS}.method_trace) then + create trace.make (req) + msg := trace + elseif attached router.allowed_methods_for_request (req) as mtds and then not mtds.is_empty then + create not_allowed.make (req) + not_allowed.set_suggested_methods (mtds) + msg := not_allowed + else + create not_found.make (req) + msg := not_found + end + res.send (msg) end feature -- Access diff --git a/library/server/wsf/router/wsf_router.e b/library/server/wsf/router/wsf_router.e index c47f03f3..c3db762e 100644 --- a/library/server/wsf/router/wsf_router.e +++ b/library/server/wsf/router/wsf_router.e @@ -207,6 +207,31 @@ feature -- Status report end end + allowed_methods_for_request (req: WSF_REQUEST): WSF_ROUTER_METHODS + -- Allowed methods for `req' + local + m: WSF_ROUTER_MAPPING + l_rqsmethods: detachable WSF_ROUTER_METHODS + do + create Result + + across + mappings as c + loop + m := c.item.mapping + if attached {WSF_ROUTING_HANDLER} m.handler as l_routing then + l_rqsmethods := l_routing.router.allowed_methods_for_request (req) + elseif m.is_mapping (req, Current) then + l_rqsmethods := c.item.request_methods + else + l_rqsmethods := Void + end + if l_rqsmethods /= Void then + Result := Result + l_rqsmethods + end + end + end + feature -- Hook execute_before (a_mapping: WSF_ROUTER_MAPPING) diff --git a/library/server/wsf/src/response/wsf_method_not_allowed_response.e b/library/server/wsf/src/response/wsf_method_not_allowed_response.e new file mode 100644 index 00000000..6b99b59b --- /dev/null +++ b/library/server/wsf/src/response/wsf_method_not_allowed_response.e @@ -0,0 +1,139 @@ +note + description: "[ + This class is used to report a 405 Method not allowed response + ]" + date: "$Date$" + revision: "$Revision$" + +class + WSF_METHOD_NOT_ALLOWED_RESPONSE + +inherit + WSF_RESPONSE_MESSAGE + + SHARED_HTML_ENCODER + +create + make + +feature {NONE} -- Initialization + + make (req: WSF_REQUEST) + do + request := req + create header.make + create suggested_methods + end + +feature -- Header + + header: HTTP_HEADER + -- Response' header + + request: WSF_REQUEST + -- Associated request. + + suggested_methods: WSF_ROUTER_METHODS + -- Optional suggestions + -- First is the default. + +feature -- Element change + + set_suggested_methods (m: like suggested_methods) + -- Set `suggested_methods' to `m' + do + suggested_methods := m + end + +feature {WSF_RESPONSE} -- Output + + send_to (res: WSF_RESPONSE) + local + s: STRING + l_title: detachable READABLE_STRING_GENERAL + h: like header + do + h := header + res.set_status_code ({HTTP_STATUS_CODE}.method_not_allowed) + s := "Not allowed" + + 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 405 (Method Not Allowed)!!") + s.append ("%N") + s.append ( + "[ + + + + + ]") + s.append ("
") + s.append ("
") + s.append ("
") + s.append ("
") + s.append ("
") + s.append ("Error 405 (Method Not Allowed)
") + s.append ("
Error 405 (Method Not Allowed): the request method ") + s.append (request.request_method) + s.append (" is inappropriate for the URL for " + html_encoder.encoded_string (request.request_uri) + ".
") + if attached suggested_methods as lst and then not lst.is_empty then + s.append ("
Allowed methods:") + across + lst as c + loop + s.append (" ") + s.append (c.item) + end + s.append ("%N") + end + s.append ("
") + s.append ("%N") + s.append ("%N") + + h.put_content_type_text_html + else + s := "Error 405 (Method Not Allowed): the request method " + s.append (request.request_method) + s.append (" is inappropriate for the URL for '" + html_encoder.encoded_string (request.request_uri) + "'.%N") + if attached suggested_methods as lst and then not lst.is_empty then + s.append ("Allowed methods:") + across + lst as c + loop + s.append (" ") + s.append (c.item) + end + 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: "2011-2012, 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/response/wsf_not_found_response.e b/library/server/wsf/src/response/wsf_not_found_response.e index 3a17cf0f..0f6ecc5c 100644 --- a/library/server/wsf/src/response/wsf_not_found_response.e +++ b/library/server/wsf/src/response/wsf_not_found_response.e @@ -8,7 +8,6 @@ note class WSF_NOT_FOUND_RESPONSE - inherit WSF_RESPONSE_MESSAGE @@ -57,59 +56,85 @@ feature {WSF_RESPONSE} -- Output h := header res.set_status_code ({HTTP_STATUS_CODE}.not_found) - s := "" - s.append ("") - s.append (html_encoder.encoded_string (request.request_uri)) - s.append (" - 404 Not Found") - s.append ("%N") - s.append ("[ - - - - - ]") - s.append ("
") - s.append ("
") - s.append ("
") - s.append ("
") - s.append ("
") - s.append ("404 Not Found
") - s.append ("
404 Not Found: " + html_encoder.encoded_string (request.request_uri) + "
") - if attached suggested_locations as lst and then not lst.is_empty then - s.append ("
Perhaps your are looking for:
    ") - from - lst.start - until - lst.after - loop - s.append ("
  • ") - l_title := lst.item.title - if l_title = Void then - l_title := lst.item.location + 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 404 (Not Found)") + s.append ("%N") + s.append ("[ + + + + + ]") + s.append ("
    ") + s.append ("
    ") + s.append ("
    ") + s.append ("
    ") + s.append ("
    ") + s.append ("Error 404 (Not Found)
    ") + s.append ("
    Error 404 (Not Found): " + html_encoder.encoded_string (request.request_uri) + "
    ") + if attached suggested_locations as lst and then not lst.is_empty then + s.append ("
    Perhaps your are looking for:
    %N") end - s.append ("
%N") - end - s.append ("
") - s.append ("%N") - s.append ("%N") + s.append ("
") + s.append ("%N") + s.append ("%N") + h.put_content_type_text_html + else + s := "Error 404 (Not Found): " + s.append (request.request_uri) + s.append_character ('%N') + if attached suggested_locations as lst and then not lst.is_empty then + s.append ("%NPerhaps your are looking for:%N") + from + lst.start + until + lst.after + loop + s.append (" - ") + l_title := lst.item.title + if l_title = Void then + l_title := lst.item.location + end + s.append (lst.item.location) + s.append ("%N") + + lst.forth + end + end + + h.put_content_type_text_plain + end h.put_content_length (s.count) - h.put_content_type_text_html res.put_header_text (h.string) res.put_string (s) res.flush diff --git a/library/server/wsf/src/response/wsf_trace_response.e b/library/server/wsf/src/response/wsf_trace_response.e new file mode 100644 index 00000000..d3446956 --- /dev/null +++ b/library/server/wsf/src/response/wsf_trace_response.e @@ -0,0 +1,97 @@ +note + description: "[ + This class is used to respond a TRACE request + ]" + date: "$Date$" + revision: "$Revision$" + +class + WSF_TRACE_RESPONSE + +inherit + WSF_RESPONSE_MESSAGE + +create + make + +feature {NONE} -- Initialization + + make (req: WSF_REQUEST) + do + request := req + create header.make + end + +feature -- Header + + header: HTTP_HEADER + -- Response' header + + request: WSF_REQUEST + -- Associated request. + +feature {WSF_RESPONSE} -- Output + + send_to (res: WSF_RESPONSE) + local + s: STRING + l_title: detachable READABLE_STRING_GENERAL + h: like header + req: like request + n, nb: INTEGER + do + h := header + res.set_status_code ({HTTP_STATUS_CODE}.ok) + req := request + if attached req.raw_header_data as l_header then + create s.make (l_header.count) + s.append (l_header.to_string_8) + s.append_character ('%N') + else + s := "" + end + if req.is_chunked_input then + h.put_transfer_encoding_chunked + res.put_header_text (h.string) + res.put_chunk (s, Void) + if attached req.input as l_input then + + from + n := 1_024 + until + n = 0 + loop + s.wipe_out + nb := l_input.read_to_string (s, 1, n) + if nb = 0 then + n := 0 + else + if nb < n then + n := 0 + end + res.put_chunk (s, Void) + end + end + end + res.put_chunk_end + res.flush + else + req.read_input_data_into (s) + h.put_content_length (s.count) + res.put_header_text (h.string) + res.put_string (s) + res.flush + end + end + +note + copyright: "2011-2012, 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 ad31a9dc..99e5d22a 100644 --- a/library/server/wsf/src/wsf_request.e +++ b/library/server/wsf/src/wsf_request.e @@ -144,12 +144,26 @@ feature -- Setting feature -- Raw input data - raw_input_data: detachable READABLE_STRING_8 + raw_input_data: detachable IMMUTABLE_STRING_8 -- Raw input data is `raw_input_data_recorded' is True set_raw_input_data (d: READABLE_STRING_8) do - raw_input_data := d + if attached {IMMUTABLE_STRING_8} d as imm then + raw_input_data := d + else + create raw_input_data.make_from_string (d) + end + end + +feature -- Raw header data + + raw_header_data: like meta_string_variable + -- Raw header data if available. + do + Result := meta_string_variable ("RAW_HEADER_DATA") + ensure + is_valid_as_string_8: Result /= Void implies Result.is_valid_as_string_8 end feature -- Error handling @@ -178,6 +192,48 @@ feature -- Access: Input Result := wgi_request.is_chunked_input end + read_input_data_into (buf: STRING) + -- retrieve the content from the `input' stream into `s' + -- warning: if the input data has already been retrieved + -- you might not get anything + local + l_input: WGI_INPUT_STREAM + n: INTEGER + s: STRING + do + if raw_input_data_recorded and then attached raw_input_data as d then + buf.copy (d) + else + l_input := input + if is_chunked_input then + from + n := 1_024 + until + n = 0 + loop + l_input.read_string (n) + s := l_input.last_string + if s.count = 0 then + n := 0 + else + if s.count < n then + n := 0 + end + buf.append (s) + end + end + else + n := content_length_value.as_integer_32 + buf.resize (buf.count + n) + n := l_input.read_to_string (buf, buf.count + 1, n) + check n = content_length_value.as_integer_32 end + end + if raw_input_data_recorded then + set_raw_input_data (buf) + end + end + end + feature -- Helper is_request_method (m: READABLE_STRING_GENERAL): BOOLEAN @@ -198,6 +254,14 @@ feature -- Helper Result := request_method.is_case_insensitive_equal ({HTTP_REQUEST_METHODS}.method_get) end + is_content_type_accepted (a_content_type: READABLE_STRING_GENERAL): BOOLEAN + -- Does client accepts content_type for the response? + do + if attached http_accept as l_accept then + Result := l_accept.has_substring (a_content_type) + end + end + feature -- Eiffel WGI access wgi_version: READABLE_STRING_8