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.
-
#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.
-
#transaction_id ⇒ String
Generate a transaction ID.
Methods included from Extensions
Methods included from Logging
Constructor Details
#initialize(homeserver, **params) ⇒ Api
Returns a new instance of Api.
42 43 44 45 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 |
# File 'lib/matrix_sdk/api.rb', line 42 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, 60) @read_timeout = params.fetch(:read_timeout, 240) @well_known = params.fetch(:well_known, {}) @global_headers = DEFAULT_HEADERS.dup @global_headers.merge!(params.fetch(:global_headers)) if params.key? :global_headers @http = nil ([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 |
#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
90 91 92 93 94 95 96 97 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 |
# File 'lib/matrix_sdk/api.rb', line 90 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 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 resolver.getresource("_matrix._tcp.#{domain}") rescue StandardError nil end if target_uri.nil? # Attempt .well-known discovery for server-to-server well_known = begin data = Net::HTTP.get("https://#{domain}/.well-known/matrix/server") JSON.parse(data) rescue StandardError 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 data = Net::HTTP.get("https://#{domain}/.well-known/matrix/client") JSON.parse(data) rescue StandardError 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 # 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
177 178 179 |
# File 'lib/matrix_sdk/api.rb', line 177 def protocol?(protocol) protocols.include? protocol end |
#protocols ⇒ Symbol[]
Get a list of enabled protocols on the API client
161 162 163 164 165 166 167 |
# File 'lib/matrix_sdk/api.rb', line 161 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
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 |
# File 'lib/matrix_sdk/api.rb', line 248 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 request = Net::HTTP.const_get(method.to_s.capitalize.to_sym).new url.request_uri request.body = [:body] if .key? :body request.body = request.body.to_json if .key?(:body) && !request.body.is_a?(String) request.body_stream = [:body_stream] if .key? :body_stream global_headers.each { |h, v| request[h] = v } if request.body || request.body_stream request.content_type = 'application/json' request.content_length = (request.body || request.body_stream).size end request['authorization'] = "Bearer #{access_token}" if access_token && !.fetch(:skip_auth, false) if .key? :headers [:headers].each do |h, v| request[h.to_s.downcase] = v end 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 print_http(request, id: req_id) begin dur_start = Time.now response = http.request request dur_end = Time.now duration = dur_end - dur_start rescue EOFError logger.error 'Socket closed unexpectedly' raise end print_http(response, duration: duration, id: req_id) data = JSON.parse(response.body, symbolize_names: true) rescue nil 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 return MatrixSdk::Response.new self, data if response.is_a? Net::HTTPSuccess raise MatrixRequestError.new_by_code(data, response.code) if data raise MatrixConnectionError.class_by_code(response.code), response end end |
#transaction_id ⇒ String
Generate a transaction ID
311 312 313 314 315 |
# File 'lib/matrix_sdk/api.rb', line 311 def transaction_id ret = @transaction_id ||= 0 @transaction_id = @transaction_id.succ ret end |