Class: MatrixSdk::Api
Constant Summary collapse
- USER_AGENT =
"Ruby Matrix SDK v#{MatrixSdk::VERSION}"
- DEFAULT_HEADERS =
{ 'accept' => 'application/json', 'user-agent' => USER_AGENT }.freeze
Instance Attribute Summary collapse
-
#access_token ⇒ Object
Returns the value of attribute access_token.
-
#autoretry ⇒ Object
Returns the value of attribute autoretry.
-
#connection_address ⇒ Object
Returns the value of attribute connection_address.
-
#connection_port ⇒ Object
Returns the value of attribute connection_port.
-
#device_id ⇒ Object
Returns the value of attribute device_id.
-
#global_headers ⇒ Object
Returns the value of attribute global_headers.
-
#homeserver ⇒ Object
Returns the value of attribute homeserver.
-
#open_timeout ⇒ Object
Returns the value of attribute open_timeout.
-
#proxy_uri ⇒ Object
Returns the value of attribute proxy_uri.
-
#read_timeout ⇒ Object
Returns the value of attribute read_timeout.
-
#threadsafe ⇒ Object
Returns the value of attribute threadsafe.
-
#validate_certificate ⇒ Object
Returns the value of attribute validate_certificate.
-
#well_known ⇒ Object
readonly
Returns the value of attribute well_known.
Class Method Summary collapse
-
.new_for_domain(domain, target: :client, keep_wellknown: false, ssl: true, **params) ⇒ API
Create an API connection to a domain entry.
Instance Method Summary collapse
-
#initialize(homeserver, **params) ⇒ Api
constructor
A new instance of Api.
-
#protocol?(protocol) ⇒ Boolean
Check if a protocol is enabled on the API connection.
-
#protocols ⇒ Symbol[]
Get a list of enabled protocols on the API client.
-
#request(method, api, path, **options) ⇒ Object
Perform a raw Matrix API request.
- #stop_inflight ⇒ Object
-
#transaction_id ⇒ String
Generate a transaction ID.
Methods included from Extensions
Methods included from Logging
Constructor Details
#initialize(homeserver, **params) ⇒ Api
Using threadsafe :multithread
currently doesn’t support connection re-use
Returns a new instance of Api.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/matrix_sdk/api.rb', line 46 def initialize(homeserver, **params) @homeserver = homeserver raise ArgumentError, 'Homeserver URL must be String or URI' unless @homeserver.is_a?(String) || @homeserver.is_a?(URI) @homeserver = URI.parse("#{'https://' unless @homeserver.start_with? 'http'}#{@homeserver}") unless @homeserver.is_a? URI @homeserver.path.gsub!(/\/?_matrix\/?/, '') if @homeserver.path =~ /_matrix\/?$/ raise ArgumentError, 'Please use the base URL for your HS (without /_matrix/)' if @homeserver.path.include? '/_matrix/' @proxy_uri = params.fetch(:proxy_uri, nil) @connection_address = params.fetch(:address, nil) @connection_port = params.fetch(:port, nil) @access_token = params.fetch(:access_token, nil) @device_id = params.fetch(:device_id, nil) @autoretry = params.fetch(:autoretry, true) @validate_certificate = params.fetch(:validate_certificate, false) @transaction_id = params.fetch(:transaction_id, 0) @backoff_time = params.fetch(:backoff_time, 5000) @open_timeout = params.fetch(:open_timeout, nil) @read_timeout = params.fetch(:read_timeout, nil) @well_known = params.fetch(:well_known, {}) @global_headers = DEFAULT_HEADERS.dup @global_headers.merge!(params.fetch(:global_headers)) if params.key? :global_headers @synapse = params.fetch(:synapse, true) @http = nil @inflight = [] self.threadsafe = params.fetch(:threadsafe, :multithread) ([params.fetch(:protocols, [:CS])].flatten - protocols).each do |proto| self.class.include MatrixSdk::Protocols.const_get(proto) end login(user: @homeserver.user, password: @homeserver.password) if @homeserver.user && @homeserver.password && !@access_token && !params[:skip_login] && protocol?(:CS) @homeserver.userinfo = '' unless params[:skip_login] end |
Instance Attribute Details
#access_token ⇒ Object
Returns the value of attribute access_token.
21 22 23 |
# File 'lib/matrix_sdk/api.rb', line 21 def access_token @access_token end |
#autoretry ⇒ Object
Returns the value of attribute autoretry.
21 22 23 |
# File 'lib/matrix_sdk/api.rb', line 21 def autoretry @autoretry end |
#connection_address ⇒ Object
Returns the value of attribute connection_address.
21 22 23 |
# File 'lib/matrix_sdk/api.rb', line 21 def connection_address @connection_address end |
#connection_port ⇒ Object
Returns the value of attribute connection_port.
21 22 23 |
# File 'lib/matrix_sdk/api.rb', line 21 def connection_port @connection_port end |
#device_id ⇒ Object
Returns the value of attribute device_id.
21 22 23 |
# File 'lib/matrix_sdk/api.rb', line 21 def device_id @device_id end |
#global_headers ⇒ Object
Returns the value of attribute global_headers.
21 22 23 |
# File 'lib/matrix_sdk/api.rb', line 21 def global_headers @global_headers end |
#homeserver ⇒ Object
Returns the value of attribute homeserver.
22 23 24 |
# File 'lib/matrix_sdk/api.rb', line 22 def homeserver @homeserver end |
#open_timeout ⇒ Object
Returns the value of attribute open_timeout.
22 23 24 |
# File 'lib/matrix_sdk/api.rb', line 22 def open_timeout @open_timeout end |
#proxy_uri ⇒ Object
Returns the value of attribute proxy_uri.
22 23 24 |
# File 'lib/matrix_sdk/api.rb', line 22 def proxy_uri @proxy_uri end |
#read_timeout ⇒ Object
Returns the value of attribute read_timeout.
22 23 24 |
# File 'lib/matrix_sdk/api.rb', line 22 def read_timeout @read_timeout end |
#threadsafe ⇒ Object
Returns the value of attribute threadsafe.
22 23 24 |
# File 'lib/matrix_sdk/api.rb', line 22 def threadsafe @threadsafe end |
#validate_certificate ⇒ Object
Returns the value of attribute validate_certificate.
22 23 24 |
# File 'lib/matrix_sdk/api.rb', line 22 def validate_certificate @validate_certificate end |
#well_known ⇒ Object (readonly)
Returns the value of attribute well_known.
22 23 24 |
# File 'lib/matrix_sdk/api.rb', line 22 def well_known @well_known end |
Class Method Details
.new_for_domain(domain, target: :client, keep_wellknown: false, ssl: true, **params) ⇒ API
Create an API connection to a domain entry
This will follow the server discovery spec for client-server and federation
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/matrix_sdk/api.rb', line 98 def self.new_for_domain(domain, target: :client, keep_wellknown: false, ssl: true, **params) domain, port = domain.split(':') uri = URI("http#{ssl ? 's' : ''}://#{domain}") well_known = nil target_uri = nil logger = ::Logging.logger[self] logger.debug "Resolving #{domain}" if !port.nil? && !port.empty? # If the domain is fully qualified according to Matrix (FQDN and port) then skip discovery target_uri = URI("https://#{domain}:#{port}") elsif target == :server # Attempt SRV record discovery target_uri = begin require 'resolv' resolver = Resolv::DNS.new srv = "_matrix._tcp.#{domain}" logger.debug "Trying DNS #{srv}..." d = resolver.getresource(srv, Resolv::DNS::Resource::IN::SRV) d rescue StandardError => e logger.debug "DNS lookup failed with #{e.class}: #{e.}" nil end if target_uri.nil? # Attempt .well-known discovery for server-to-server well_known = begin wk_uri = URI("https://#{domain}/.well-known/matrix/server") logger.debug "Trying #{wk_uri}..." data = Net::HTTP.start(wk_uri.host, wk_uri.port, use_ssl: true, open_timeout: 5, read_timeout: 5, write_timeout: 5) do |http| http.get(wk_uri.path).body end JSON.parse(data) rescue StandardError => e logger.debug "Well-known failed with #{e.class}: #{e.}" nil end target_uri = well_known['m.server'] if well_known&.key?('m.server') else target_uri = URI("https://#{target_uri.target}:#{target_uri.port}") end elsif %i[client identity].include? target # Attempt .well-known discovery well_known = begin wk_uri = URI("https://#{domain}/.well-known/matrix/client") logger.debug "Trying #{wk_uri}..." data = Net::HTTP.start(wk_uri.host, wk_uri.port, use_ssl: true, open_timeout: 5, read_timeout: 5, write_timeout: 5) do |http| http.get(wk_uri.path).body end JSON.parse(data) rescue StandardError => e logger.debug "Well-known failed with #{e.class}: #{e.}" nil end if well_known key = 'm.homeserver' key = 'm.identity_server' if target == :identity if well_known.key?(key) && well_known[key].key?('base_url') uri = URI(well_known[key]['base_url']) target_uri = uri end end end logger.debug "Using #{target_uri.inspect}" # Fall back to direct domain connection target_uri ||= URI("https://#{domain}:8448") params[:well_known] = well_known if keep_wellknown new( uri, **params.merge( address: target_uri.host, port: target_uri.port ) ) end |
Instance Method Details
#protocol?(protocol) ⇒ Boolean
Check if a protocol is enabled on the API connection
204 205 206 |
# File 'lib/matrix_sdk/api.rb', line 204 def protocol?(protocol) protocols.include? protocol end |
#protocols ⇒ Symbol[]
Get a list of enabled protocols on the API client
188 189 190 191 192 193 194 |
# File 'lib/matrix_sdk/api.rb', line 188 def protocols self .class.included_modules .reject { |m| m&.name.nil? } .select { |m| m.name.start_with? 'MatrixSdk::Protocols::' } .map { |m| m.name.split('::').last.to_sym } end |
#request(method, api, path, **options) ⇒ Object
Perform a raw Matrix API request
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/matrix_sdk/api.rb', line 286 def request(method, api, path, **) url = homeserver.dup.tap do |u| u.path = api_to_path(api) + path u.query = [u.query, URI.encode_www_form(.fetch(:query))].flatten.compact.join('&') if [:query] u.query = nil if u.query.nil? || u.query.empty? end failures = 0 loop do raise MatrixConnectionError, "Server still too busy to handle request after #{failures} attempts, try again later" if failures >= 10 req_id = ('A'..'Z').to_a.sample(4).join req_obj = construct_request(url: url, method: method, **) print_http(req_obj, id: req_id) response = duration = nil loc_http = http perform_request = proc do @inflight << loc_http dur_start = Time.now response = loc_http.request req_obj dur_end = Time.now duration = dur_end - dur_start rescue EOFError logger.error 'Socket closed unexpectedly' raise ensure @inflight.delete loc_http end if @threadsafe == true http_lock.synchronize { perform_request.call } else perform_request.call loc_http.finish if @threadsafe == :multithread end print_http(response, duration: duration, id: req_id) begin data = JSON.parse(response.body, symbolize_names: true) rescue JSON::JSONError => e logger.debug "#{e.class} error when parsing response. #{e}" data = nil end if response.is_a? Net::HTTPTooManyRequests raise MatrixRequestError.new_by_code(data, response.code) unless autoretry failures += 1 waittime = data[:retry_after_ms] || data[:error][:retry_after_ms] || @backoff_time sleep(waittime.to_f / 1000.0) next end if response.is_a? Net::HTTPSuccess unless data logger.error "Received non-parsable data in 200 response; #{response.body.inspect}" raise MatrixConnectionError, response end return MatrixSdk::Response.new self, data end raise MatrixRequestError.new_by_code(data, response.code) if data raise MatrixConnectionError.class_by_code(response.code), response end end |
#stop_inflight ⇒ Object
363 364 365 |
# File 'lib/matrix_sdk/api.rb', line 363 def stop_inflight @inflight.each(&:finish) end |
#transaction_id ⇒ String
Generate a transaction ID
357 358 359 360 361 |
# File 'lib/matrix_sdk/api.rb', line 357 def transaction_id ret = @transaction_id ||= 0 @transaction_id = @transaction_id.succ ret end |