Class: Async::Matrix::MediaClient

Inherits:
Object
  • Object
show all
Defined in:
lib/async/matrix/media_client.rb

Overview

Async HTTP client for binary Matrix media operations.

Handles raw byte uploads and downloads against the Matrix content repository. Unlike the JSON-only Client, this never wraps request bodies in JSON.generate and can return raw binary response bodies.

Not intended to be used directly — the Chain dispatches here automatically when the request path matches a BINARY_ROUTES entry.

media_client = Async::Matrix::MediaClient.new(config)
media_client.upload("POST", path, bytes, "image/png")
bytes = media_client.download(path)

Constant Summary collapse

UPLOAD_RESPONSE_SIZE_LIMIT =

Upload JSON responses are small; error bodies are even smaller.

1 * 1024 * 1024
ERROR_RESPONSE_SIZE_LIMIT =

1 MiB

512 * 1024

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ MediaClient

512 KiB



32
33
34
35
36
37
38
39
# File 'lib/async/matrix/media_client.rb', line 32

def initialize(config)
  @config = config
  @base   = config.homeserver.address
  @auth_headers = [
    ["authorization", "Bearer #{config.appservice.as_token}"],
    ["user-agent",    "AsyncMatrix/#{Async::Matrix::VERSION}"]
  ]
end

Instance Method Details

#closeObject



111
112
113
114
# File 'lib/async/matrix/media_client.rb', line 111

def close
  @internet&.close
  @internet = nil
end

#download(path) ⇒ Protocol::HTTP::Response

Download from the content repository.

Returns the raw HTTP response object so the caller has access to both the body and response metadata:

response = media_client.download(path)
response.read                          # raw bytes (String, Encoding::BINARY)
response.headers["content-type"]       # "image/png"
response.headers["content-disposition"] # "inline; filename=\"photo.jpg\""
response.body.each { |chunk| ... }     # streaming

Parameters:

  • path (String)

    URL path (already URL-encoded, may include query string)

Returns:

  • (Protocol::HTTP::Response)

    the HTTP response



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/async/matrix/media_client.rb', line 87

def download(path)
  url = "#{@base}#{path}"

  Console.debug(self) { "DOWNLOAD #{path}" }

  response = internet.call("GET", url, @auth_headers, nil)
  status   = response.status

  unless (200..299).cover?(status)
    payload = response.read
    parsed = ApplicationService::ErrorResponse.new(
      begin; JSON.parse(payload); rescue; {} end
    )
    Console.error(self) { "Matrix media download #{status}: #{parsed.errcode}#{parsed.error}" }
    raise HomeserverError.new(
      parsed.errcode || "UNKNOWN",
      parsed.error || payload.to_s[0..200],
      status: status
    )
  end

  response
end

#upload(method, path, body, content_type = "application/octet-stream") ⇒ Hash

Upload raw bytes to the content repository.

Parameters:

  • method (String)

    HTTP method (“POST” or “PUT”)

  • path (String)

    URL path (already URL-encoded)

  • body (String)

    raw bytes to upload

  • content_type (String) (defaults to: "application/octet-stream")

    MIME type of the body

Returns:

  • (Hash)

    parsed JSON response (e.g. => “mxc://…”)



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/async/matrix/media_client.rb', line 48

def upload(method, path, body, content_type = "application/octet-stream")
  url = "#{@base}#{path}"
  headers = @auth_headers + [["content-type", content_type]]

  Console.debug(self) { "UPLOAD #{method} #{path} (#{content_type}, #{body.bytesize} bytes)" }

  response = internet.call(method, url, headers, body)
  status   = response.status

  unless (200..299).cover?(status)
    payload = read_limited(response, ERROR_RESPONSE_SIZE_LIMIT)
    parsed = ApplicationService::ErrorResponse.new(
      begin; JSON.parse(payload); rescue; {} end
    )
    Console.error(self) { "Matrix media upload #{status}: #{parsed.errcode}#{parsed.error}" }
    raise HomeserverError.new(
      parsed.errcode || "UNKNOWN",
      parsed.error || payload.to_s[0..200],
      status: status
    )
  end

  payload = read_limited(response, UPLOAD_RESPONSE_SIZE_LIMIT)
  payload && !payload.empty? ? JSON.parse(payload) : {}
end