indexing description: "Represents a TCP message header block" license: "MIT license (see ../../../license.txt)" author: "Beat Strasser " date: "$Date$" revision: "$Revision$" class P2P_TCP_HEADER inherit P2P_SOCKET_EXTENSIONS rename make as esocket_make, make_non_blocking as esocket_make_non_blocking, send_buffer as esocket_send_buffer export {NONE} all {ANY} buffer, buffer_length end P2P_EXCEPTION_LOG create make, make_from_wire feature {NONE} -- Initialization make is -- Create empty message header local string_equality_tester: KL_EQUALITY_TESTER [STRING] do create headers.make (initial_headers_capacity) create string_equality_tester headers.set_key_equality_tester (string_equality_tester) end make_from_wire (medium: NETWORK_SOCKET; read_timeout: INTEGER; a_checker: like checker) is -- Read message header from wire -- `a_checker' is a query function returning true whenever reading process should stop. require Medium_valid: medium /= Void and medium.exists local name: STRING do make if not has_failed then esocket_make_non_blocking (medium, read_timeout) if a_checker /= Void then set_checking_restraint (a_checker) end -- read headers from -- first header name length read_byte_1 until last_integer_8 = 0 loop -- header name string read_chars (last_integer_8) name := last_string -- header value length read_byte_2 -- header value string read_chars (last_integer_16) replace_header (name, last_string) -- header name length read_byte_1 end end rescue has_failed := True log_exceptions retry end feature -- Access Content_length_header_name: STRING is "content-length" Content_type_header_name: STRING is "content-type" header (name: STRING): STRING is -- Value of given header name require Name_set: name /= Void do if headers.has (name) then Result := headers.item (name) end end content_length: INTEGER_64 is -- `content-length' header local buffered: STRING p: MANAGED_POINTER pos: INTEGER do if headers.has (content_length_header_name) then buffered := header (content_length_header_name) create p.make (8) from pos := 0 until pos = 8 loop if pos >= 8 - buffered.count then p.put_character (buffered.item (1 + pos - (8 - buffered.count)), pos) else p.put_integer_8 (0, pos) end pos := pos + 1 end Result := p.read_natural_64_be (0).as_integer_64 end end content_type: STRING is -- `content-type' header do Result := header (content_type_header_name) ensure Result_set: Result = header (content_type_header_name) end size: INTEGER is -- Number of bytes of binary output require Header_valid: is_valid do Result := 1 -- empty header from headers.start until headers.after loop Result := Result + 3 + headers.key_for_iteration.count + headers.item_for_iteration.count headers.forth end end feature -- Status is_valid: BOOLEAN is -- Is message header block a valid header (containing the required headers)? do Result := headers.has (content_length_header_name) and headers.has (content_type_header_name) end has_failed: BOOLEAN feature -- Element change replace_header (name, value: STRING) is -- Add/Replace header require Header_valid: name /= Void and value /= Void do headers.force (value, name) ensure Header_saved: header (name) = value end set_content_length (length: INTEGER_64) is -- Add a `content-length' header require Length_valid: length >= 0 local int_to_string: P2P_SOCKET_EXTENSIONS do create int_to_string.empty_buffer int_to_string.write_byte_8 (length) replace_header (content_length_header_name, int_to_string.buffer) ensure Header_saved: content_length = length end set_content_type (type: STRING) is -- Add a `content-type' header require type_valid: type /= Void do replace_header (content_type_header_name, type) ensure Header_saved: header (content_type_header_name) = type end feature -- Removal remove_header (name: STRING) is -- Remove header require Name_valid: name /= Void do headers.remove (name) ensure Header_removed: header (name) = Void end feature -- Basic operations send_to_buffer is -- Write header block to wire buffer require Header_valid: is_valid do empty_buffer -- send each header from headers.start until headers.after loop -- header name length write_byte_1 (headers.key_for_iteration.count.as_integer_8) -- header name string write_chars (headers.key_for_iteration) -- header value length write_byte_2 (headers.item_for_iteration.count.as_integer_16) -- header value string write_chars (headers.item_for_iteration) headers.forth end -- empty header write_byte_1 (0) end send_buffer (medium: NETWORK_SOCKET) is -- Send buffer to wire require Medium_valid: medium /= Void and medium.is_writable do if not has_failed then socket := medium esocket_send_buffer end rescue has_failed := True log_exceptions retry end feature {NONE} -- Implementation headers: DS_HASH_TABLE [STRING, STRING] Initial_headers_capacity: INTEGER is 5 end