indexing description: "Fetches seed addresses from http uri" license: "MIT license (see ../license.txt)" author: "Beat Strasser " date: "$Date$" revision: "$Revision$" class P2P_SEED_RESOLVER inherit P2P_SOCKET_EXTENSIONS export {NONE} all end P2P_EXCEPTION_LOG feature -- Access url: HTTP_URL local_host: NETWORK_SOCKET_ADDRESS local_host_address: STRING found_seeds: DS_LIST [P2P_ENDPOINT_ADDRESS] has_failed: BOOLEAN Max_wait: INTEGER is 20 -- 20s feature -- Basic operations resolve (a_uri: STRING) is -- Open a http connection with the specified `a_uri', parse endpoint addresses -- and store them in `found_seeds'. Also fetches the local ip address. do if not has_failed then create url.make (a_uri.twin) -- we don't want our uri to be changed from this constructor! -- open connection create {NETWORK_STREAM_SOCKET} socket.make_client_by_port (url.port, url.host) make_non_blocking (socket, max_wait) -- TODO change to non blocking as soon as eiffel net supports non blocking calls socket.set_blocking socket.connect socket.set_non_blocking if connect_successful then -- fetch ip fetch_local_ip -- send http header send_http_request -- fetch result and parse fetch_http_result -- cleanup connection socket.cleanup else has_failed := True end end if found_seeds = Void then -- create empty result list, if error create {DS_ARRAYED_LIST [P2P_ENDPOINT_ADDRESS]} found_seeds.make (0) end ensure Seeds_set: found_seeds /= Void Ip_set: not has_failed implies (local_host /= Void and local_host_address /= Void) rescue has_failed := True log_exceptions retry end feature {NONE} -- Implementation Wait_for_retrieve: INTEGER is 500000000 -- 500ms Default_buffer_size: INTEGER is 16384 Http_get: STRING is "GET " Http_version: STRING is " HTTP/1.0" Http_new_line: STRING is "%R%N" Http_host: STRING is "Host: " send_http_request is -- Send http header do -- send request uri write_chars (http_get + "/" + url.path + http_version + http_new_line) write_chars (http_host + url.host) if url.port /= url.default_port then write_chars (":" + url.port.out) end write_chars (http_new_line + http_new_line) send_buffer end fetch_http_result is -- Wait for result and parse data local body_pos: INTEGER list: LIST [STRING] ea: P2P_ENDPOINT_ADDRESS do -- try to wait for full data arrival -- (sometimes we got only half of the stream, although the data was sent in one tcp packet) sleep (wait_for_retrieve) if ready_for_reading_or_failure then -- now read http result read_chars_once (default_buffer_size) if last_string /= Void then body_pos := last_string.substring_index (http_new_line + http_new_line, 1) + 2 * http_new_line.count if body_pos /= 0 then list := last_string.substring (body_pos, last_string.count).split ('%N') create {DS_ARRAYED_LIST [P2P_ENDPOINT_ADDRESS]} found_seeds.make (list.count) from list.start until list.after loop list.item.prune_all ('%R') if list.item.count > 0 then create ea.make_from_uri (list.item) if ea.is_valid then found_seeds.put_last (ea) end end list.forth end end end end end fetch_local_ip is -- Store local ip in `local_ip' require Socket_valid: socket /= Void and socket.exists and socket.is_open_read do create local_host.make -- TODO change to non blocking as soon as eiffel net supports non blocking calls socket.set_blocking c_sock_name (socket.descriptor, local_host.socket_address.item, local_host.socket_address.count) socket.set_non_blocking local_host_address := local_host.host_address.host_address end c_sock_name (soc: INTEGER_32; addr: POINTER; length: INTEGER_32) -- External c routine that returns the socket name. external "C" end end