Class: Everywhere::Platform::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/everywhere/platform/client.rb

Defined Under Namespace

Classes: Response

Constant Summary collapse

NETWORK_ERRORS =

Everything a flaky link, a hung host or a TLS-intercepting proxy can throw at us. OpenSSL is an optional stdlib (net/http requires it best-effort), so its error class is only listed when it loaded.

[
  SocketError, EOFError,
  Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT,
  Errno::EHOSTUNREACH, Errno::ENETUNREACH, Errno::EPIPE,
  Net::OpenTimeout, Net::ReadTimeout, Net::WriteTimeout,
  *(defined?(OpenSSL::SSL::SSLError) ? [OpenSSL::SSL::SSLError] : [])
].freeze
OPEN_TIMEOUT =
15

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base_url, token: nil) ⇒ Client

Returns a new instance of Client.



55
56
57
58
59
# File 'lib/everywhere/platform/client.rb', line 55

def initialize(base_url, token: nil)
  @base_url = base_url
  @base = URI.parse("#{base_url}/")
  @token = token
end

Instance Attribute Details

#base_urlObject (readonly)

Returns the value of attribute base_url.



53
54
55
# File 'lib/everywhere/platform/client.rb', line 53

def base_url
  @base_url
end

Class Method Details

.base_url(explicit = nil) ⇒ Object



49
50
51
# File 'lib/everywhere/platform/client.rb', line 49

def self.base_url(explicit = nil)
  (explicit || ENV["EVERYWHERE_PLATFORM_URL"] || DEFAULT_URL).chomp("/")
end

Instance Method Details

#delete(path) ⇒ Object



64
# File 'lib/everywhere/platform/client.rb', line 64

def delete(path)           = request(Net::HTTP::Delete, path)

#download(url, dest, redirects: 5) ⇒ Object

GET a (possibly redirecting, signed) URL and stream it to dest. Sends the bearer token only to the platform host (the first hop when the URL is a /runner or /cli endpoint); a redirect to a signed blob/S3 URL is followed without it.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/everywhere/platform/client.rb', line 86

def download(url, dest, redirects: 5)
  uri = URI.parse(url)
  req = Net::HTTP::Get.new(uri)
  req["Authorization"] = "Bearer #{@token}" if @token && uri.host == @base.host && uri.port == @base.port
  http(uri) do |conn|
    conn.request(req) do |res|
      case res
      when Net::HTTPRedirection
        raise Error, "too many redirects downloading #{url}" if redirects <= 0

        return download(redirect_target(uri, res["location"]), dest, redirects: redirects - 1)
      when Net::HTTPSuccess
        File.open(dest, "wb") { |f| res.read_body { |chunk| f.write(chunk) } }
      else
        raise Error, "download failed (HTTP #{res.code})"
      end
    end
  end
  dest
rescue *NETWORK_ERRORS => e
  raise Error, "download from #{uri.host} failed (#{e.class}: #{e.message})"
end

#get(path) ⇒ Object



63
# File 'lib/everywhere/platform/client.rb', line 63

def get(path)              = request(Net::HTTP::Get, path)

#patch(path, body = {}) ⇒ Object



62
# File 'lib/everywhere/platform/client.rb', line 62

def patch(path, body = {}) = request(Net::HTTP::Patch, path, body)

#post(path, body = {}) ⇒ Object



61
# File 'lib/everywhere/platform/client.rb', line 61

def post(path, body = {})  = request(Net::HTTP::Post, path, body)

#put_file(url, path, headers: {}) ⇒ Object

Stream a file with a raw PUT (Active Storage direct upload). url is absolute (from the reservation); headers come from the reservation too. Returns the Net::HTTP response.



69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/everywhere/platform/client.rb', line 69

def put_file(url, path, headers: {})
  uri = URI.parse(url)
  req = Net::HTTP::Put.new(uri)
  headers.each { |k, v| req[k] = v }
  req["Content-Length"] = File.size(path).to_s
  File.open(path, "rb") do |io|
    req.body_stream = io
    http(uri) { |conn| conn.request(req) }
  end
rescue *NETWORK_ERRORS => e
  raise Error, "upload to #{uri.host} failed (#{e.class}: #{e.message})"
end