Class: ConnectionTester

Inherits:
Object
  • Object
show all
Includes:
Diaspora::Logging
Defined in:
lib/connection_tester.rb

Defined Under Namespace

Classes: AddressFailure, DNSFailure, Failure, HTTPFailure, NetFailure, NodeInfoFailure, Result, SSLFailure

Constant Summary collapse

NODEINFO_FRAGMENT =
"/.well-known/nodeinfo"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url, result = Result.new) ⇒ ConnectionTester

Returns a new instance of ConnectionTester.

Raises:

  • if the specified url is not http(s)



68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/connection_tester.rb', line 68

def initialize(url, result=Result.new)
  @url ||= url
  @result ||= result
  @uri ||= URI.parse(@url)
  raise AddressFailure,
        "invalid protocol: '#{@uri.scheme.upcase}'" unless http_uri?(@uri)
rescue AddressFailure => e
  raise e
rescue URI::InvalidURIError => e
  raise AddressFailure, e.message
rescue StandardError => e
  unexpected_error(e)
end

Class Method Details

.check(url) ⇒ Result

Test the reachability of a server by the given HTTP/S URL. In the first step, a DNS query is performed to check whether the given name even resolves correctly. The second step is to send a HTTP request and look at the returned status code or any returned errors. This function isn’t intended to check for the availability of a specific page, instead a GET request is sent to the root directory of the server. In the third step an attempt is made to determine the software version used on the server, via the nodeinfo page.

Parameters:

  • URL

Returns:

  • result object containing information about the server and to what point the connection was successful

API:

  • This is the entry point you’re supposed to use for testing connections to other diaspora-compatible servers.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/connection_tester.rb', line 25

def check(url)
  result = Result.new

  begin
    ct = ConnectionTester.new(url, result)

    # test DNS resolving
    ct.resolve

    # test HTTP request
    ct.request

    # test for the diaspora* version
    ct.nodeinfo

  rescue Failure => e
    result_from_failure(result, e)
  end

  result.freeze
end

Instance Method Details

#nodeinfoObject

Try to find out the version of the other servers software. Assuming the server speaks nodeinfo

Raises:

  • if the document can’t be fetched

  • if the document can’t be parsed or is invalid



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/connection_tester.rb', line 123

def nodeinfo
  with_http_connection do |http|
    ni_resp = http.get(NODEINFO_FRAGMENT)
    ni_urls = find_nodeinfo_urls(ni_resp.body)
    raise NodeInfoFailure, "No supported NodeInfo version found" if ni_urls.empty?

    version, url = ni_urls.max
    find_software_version(version, http.get(url).body)
  end
rescue Faraday::ClientError => e
  raise HTTPFailure, "#{e.class}: #{e.message}"
rescue NodeInfoFailure => e
  raise e
rescue JSON::Schema::ValidationError, JSON::Schema::SchemaError, Faraday::TimeoutError => e
  raise NodeInfoFailure, "#{e.class}: #{e.message}"
rescue JSON::JSONError => e
  raise NodeInfoFailure, e.message[0..255].encode(Encoding.default_external, undef: :replace)
rescue StandardError => e
  unexpected_error(e)
end

#requestInteger

Perform a HTTP GET request to determine the following information

  • is the host reachable

  • is port 80/443 open

  • is the SSL certificate valid (only on HTTPS)

  • does the server return a successful HTTP status code

  • is there a reasonable amount of redirects (3 by default)

(can’t do a HEAD request, since that’s not a defined route in the app)

Returns:

  • HTTP status code

Raises:

  • if any of the checks fail



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/connection_tester.rb', line 102

def request
  with_http_connection do |http|
    capture_response_time { handle_http_response(http.get("/")) }
  end
rescue HTTPFailure => e
  raise e
rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
  raise NetFailure, e.message
rescue Faraday::SSLError => e
  raise SSLFailure, e.message
rescue ArgumentError, Faraday::ClientError, Faraday::ServerError => e
  raise HTTPFailure, "#{e.class}: #{e.message}"
rescue StandardError => e
  unexpected_error(e)
end

#resolveObject

Perform the DNS query, the IP address will be stored in the result

Raises:

  • caused by a failure to resolve or a timeout



84
85
86
87
88
89
90
# File 'lib/connection_tester.rb', line 84

def resolve
  @result.ip = IPSocket.getaddress(@uri.host)
rescue SocketError => e
  raise DNSFailure, "'#{@uri.host}' - #{e.message}"
rescue StandardError => e
  unexpected_error(e)
end