Class: Cloudflare::DurableObjectStub

Inherits:
Object
  • Object
show all
Defined in:
lib/homura/runtime/durable_object.rb

Overview

Wraps a stub (the object returned by ‘namespace.get(id)`). Its only callable method is `fetch(request)`, which submits an HTTP request to the DO instance and returns a `Response`. We return the same `Cloudflare::HTTPResponse` shape as Phase 6 so routes don’t need to learn a second API.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(js) ⇒ DurableObjectStub

Returns a new instance of DurableObjectStub.



131
132
133
# File 'lib/homura/runtime/durable_object.rb', line 131

def initialize(js)
  @js = js
end

Instance Attribute Details

#jsObject (readonly)

Returns the value of attribute js.



129
130
131
# File 'lib/homura/runtime/durable_object.rb', line 129

def js
  @js
end

Instance Method Details

#delete(path, headers: nil) ⇒ Object



216
217
218
# File 'lib/homura/runtime/durable_object.rb', line 216

def delete(path, headers: nil)
  request(path, method: "DELETE", headers: headers)
end

#fetch(url_or_request, method: "GET", headers: nil, body: nil) ⇒ Object

Call the DO with a plain URL (String) and optional init-Hash. The DO’s fetch handler sees the URL as-is (no routing layer strips the prefix), so user code can use any pathname it wants as its internal DO command channel. Returns a JS Promise the caller ‘__await__`s to get a `Cloudflare::HTTPResponse`.



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/homura/runtime/durable_object.rb', line 157

def fetch(url_or_request, method: "GET", headers: nil, body: nil)
  js_stub = @js
  hdrs = headers || {}
  method_str = method.to_s.upcase
  js_headers = Cloudflare::HTTP.ruby_headers_to_js(hdrs)
  js_body = body.nil? ? nil : body.to_s
  url_str = url_or_request.to_s
  err_klass = Cloudflare::DurableObjectError
  response_klass = Cloudflare::HTTPResponse
  do_class_label = "DurableObjectStub"

  # Single-line IIFE — see `lib/homura/runtime/cache.rb#put`
  # for why Opal can silently drop a multi-line x-string Promise.
  js_promise =
    `(async function(stub, url_str, method_str, js_headers, js_body, Kernel, err_klass, do_class_label) { var init = { method: method_str, headers: js_headers }; if (js_body !== null && js_body !== undefined && js_body !== Opal.nil) { init.body = js_body; } var resp; try { resp = await stub.fetch(url_str, init); } catch (e) { Kernel.$raise(err_klass.$new(e && e.message ? e.message : String(e), Opal.hash({ operation: 'stub.fetch', do_class: do_class_label }))); } var text = ''; try { text = await resp.text(); } catch (_) { text = ''; } var hk = []; var hv = []; if (resp.headers && typeof resp.headers.forEach === 'function') { resp.headers.forEach(function(v, k) { hk.push(String(k).toLowerCase()); hv.push(String(v)); }); } return { status: resp.status|0, text: text, hkeys: hk, hvals: hv }; })(#{js_stub}, #{url_str}, #{method_str}, #{js_headers}, #{js_body}, #{Kernel}, #{err_klass}, #{do_class_label})`

  js_result = js_promise.__await__
  hkeys = `#{js_result}.hkeys`
  hvals = `#{js_result}.hvals`
  h = {}
  i = 0
  len = `#{hkeys}.length`
  while i < len
    h[`#{hkeys}[#{i}]`] = `#{hvals}[#{i}]`
    i += 1
  end

  response_klass.new(
    status: `#{js_result}.status`,
    headers: h,
    body: `#{js_result}.text`,
    url: url_str
  )
end

#fetch_raw(url_or_request, method: "GET", headers: nil, body: nil) ⇒ Object

Same as ‘fetch` but returns the raw JS Response (a JS Promise resolving to it) instead of wrapping it in a `Cloudflare::HTTPResponse`. Needed for WebSocket-upgrade responses — the 101 Response carries its WebSocket in a `.webSocket` property that disappears if we reconstruct the Response via the HTTPResponse wrapper.



141
142
143
144
145
146
147
148
149
150
# File 'lib/homura/runtime/durable_object.rb', line 141

def fetch_raw(url_or_request, method: "GET", headers: nil, body: nil)
  hdrs = headers || {}
  method_str = method.to_s.upcase
  js_headers = Cloudflare::HTTP.ruby_headers_to_js(hdrs)
  js_body = body.nil? ? nil : body.to_s
  url_str = url_or_request.to_s
  js_stub = @js
  err_klass = Cloudflare::DurableObjectError
  `(async function(stub, url_str, method_str, js_headers, js_body, Kernel, err_klass) { var init = { method: method_str, headers: js_headers }; if (js_body !== null && js_body !== undefined && js_body !== Opal.nil) { init.body = js_body; } try { return await stub.fetch(url_str, init); } catch (e) { Kernel.$raise(err_klass.$new(e && e.message ? e.message : String(e), Opal.hash({ operation: 'stub.fetch_raw' }))); } })(#{js_stub}, #{url_str}, #{method_str}, #{js_headers}, #{js_body}, #{Kernel}, #{err_klass})`
end

#get(path, headers: nil) ⇒ Object



204
205
206
# File 'lib/homura/runtime/durable_object.rb', line 204

def get(path, headers: nil)
  request(path, method: "GET", headers: headers)
end

#post(path, body = nil, headers: nil) ⇒ Object



208
209
210
# File 'lib/homura/runtime/durable_object.rb', line 208

def post(path, body = nil, headers: nil)
  request(path, method: "POST", headers: headers, body: body)
end

#put(path, body = nil, headers: nil) ⇒ Object



212
213
214
# File 'lib/homura/runtime/durable_object.rb', line 212

def put(path, body = nil, headers: nil)
  request(path, method: "PUT", headers: headers, body: body)
end

#request(path, method: "GET", headers: nil, body: nil) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
# File 'lib/homura/runtime/durable_object.rb', line 192

def request(path, method: "GET", headers: nil, body: nil)
  hdrs = headers ? headers.dup : {}
  request_body = body
  if body.is_a?(Hash) || body.is_a?(Array)
    request_body = body.to_json
    unless hdrs.key?("content-type") || hdrs.key?("Content-Type")
      hdrs["content-type"] = "application/json"
    end
  end
  fetch(path, method: method, headers: hdrs, body: request_body)
end