Method: MatrixSdk::Api#request

Defined in:
lib/matrix_sdk/api.rb

#request(method, api, path, **options) ⇒ Object

Perform a raw Matrix API request

Examples:

Simple API query

api.request(:get, :client_r0, '/account/whoami')
# => { :user_id => "@alice:matrix.org" }

Advanced API request

api.request(:post,
            :media_r0,
            '/upload',
            body_stream: open('./file'),
            headers: { 'content-type' => 'image/png' })
# => { :content_uri => "mxc://example.com/AQwafuaFswefuhsfAFAgsw" }

Parameters:

  • method (Symbol)

    The method to use, can be any of the ones under Net::HTTP

  • api (Symbol)

    The API symbol to use, :client_r0 is the current CS one

  • path (String)

    The API path to call, this is the part that comes after the API definition in the spec

  • options (Hash)

    Additional options to pass along to the request

Options Hash (**options):

  • :query (Hash)

    Query parameters to set on the URL

  • :body (Hash, String)

    The body to attach to the request, will be JSON-encoded if sent as a hash

  • :body_stream (IO)

    A body stream to attach to the request

  • :headers (Hash)

    Additional headers to set on the request

  • :skip_auth (Boolean) — default: false

    Skip authentication



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, **options)
  url = homeserver.dup.tap do |u|
    u.path = api_to_path(api) + path
    u.query = [u.query, URI.encode_www_form(options.fetch(:query))].flatten.compact.join('&') if options[: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, **options)
    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