class Canistor::Storage::Object

Constants

META_HEADERS

Attributes

bucket[RW]
data[R]
key[RW]
last_modified[R]
region[RW]

Public Class Methods

new(**attributes) click to toggle source
# File lib/canistor/storage/object.rb, line 43
def initialize(**attributes)
  @versioned = false
  @headers = {}
  @data = ''
  attributes.each do |name, value|
    send("#{name}=", value)
  end
  @digest = nil
end

Public Instance Methods

copy() click to toggle source
# File lib/canistor/storage/object.rb, line 136
def copy
  object = self.class.new(region: region, bucket: bucket, key: key)
  object.write(headers, data)
  object
end
copy_headers(context) click to toggle source
# File lib/canistor/storage/object.rb, line 146
def copy_headers(context)
  directive = context.http_request.headers['x-amz-metadata-directive']
  case directive
  when 'COPY'
    identity_headers
  when 'REPLACE', nil
    context.http_request.headers
  else
    raise ArgumentError, "Unsupported metadata directive: `#{directive}'"
  end
end
delete(context, subject) click to toggle source
# File lib/canistor/storage/object.rb, line 142
def delete(context, subject)
  context.http_response.signal_headers(200, {})
end
digest() click to toggle source
# File lib/canistor/storage/object.rb, line 80
def digest
  @digest ||= Digest::MD5.hexdigest(data)
end
endpoint() click to toggle source
# File lib/canistor/storage/object.rb, line 88
def endpoint
  Aws::Partitions::EndpointProvider.resolve(region, 's3')
end
etag() click to toggle source
# File lib/canistor/storage/object.rb, line 84
def etag
  '"' + digest + '"'
end
get(context, subject) click to toggle source
# File lib/canistor/storage/object.rb, line 123
def get(context, subject)
  context.http_response.signal_headers(200, headers)
  context.http_response.signal_data(data)
end
head(context, subject) click to toggle source
# File lib/canistor/storage/object.rb, line 119
def head(context, subject)
  context.http_response.signal_headers(200, headers)
end
headers() click to toggle source
# File lib/canistor/storage/object.rb, line 100
def headers
  @headers.merge(identity_headers).merge(
    'date' => Time.now.httpdate,
    'content-length' => size.to_s,
    'last-modified' => last_modified.httpdate,
    'server' => 'Canistor'
  )
end
identity_headers() click to toggle source
# File lib/canistor/storage/object.rb, line 109
def identity_headers
  {
    'etag' => etag,
    'x-amz-id' => digest[0, 16],
    'x-amz-id-2' => Base64.strict_encode64(digest),
    'x-amz-request-id' => Base64.strict_encode64(SecureRandom.hex(16)),
    'x-amz-version-id' => version_id
  }.compact
end
label() click to toggle source
# File lib/canistor/storage/object.rb, line 71
def label
  [region, bucket, key].map(&:to_s).join(':') + ' ' + headers.inspect
end
location() click to toggle source
# File lib/canistor/storage/object.rb, line 92
def location
  [
    endpoint,
    bucket,
    key,
  ].join('/')
end
put(context, subject) click to toggle source
# File lib/canistor/storage/object.rb, line 128
def put(context, subject)
  if source_object = find_source(context, subject)
    apply_source_object(context, subject, source_object)
  else
    apply_request(context, subject)
  end
end
size() click to toggle source
# File lib/canistor/storage/object.rb, line 67
def size
  data&.size
end
version_id() click to toggle source
# File lib/canistor/storage/object.rb, line 61
def version_id
  if versioned?
    @version_id ||= generate_version_id
  end
end
versioned=(versioned) click to toggle source
# File lib/canistor/storage/object.rb, line 53
def versioned=(versioned)
  @versioned = versioned
end
versioned?() click to toggle source
# File lib/canistor/storage/object.rb, line 57
def versioned?
  !!@versioned
end
write(headers, data) click to toggle source
# File lib/canistor/storage/object.rb, line 75
def write(headers, data)
  self.headers = headers
  self.data = data
end

Private Instance Methods

apply_request(context, subject) click to toggle source
# File lib/canistor/storage/object.rb, line 177
def apply_request(context, subject)
  write(context.http_request.headers, context.http_request.body)
  context.http_response.signal_headers(200, identity_headers)
end
apply_source_object(context, subject, source_object) click to toggle source
# File lib/canistor/storage/object.rb, line 182
def apply_source_object(context, subject, source_object)
  write(source_object.copy_headers(context), source_object.data)
  CopyObjectResult.serve(context, subject, source_object, self)
end
data=(data) click to toggle source
# File lib/canistor/storage/object.rb, line 205
def data=(data)
  @digest = nil
  @last_modified = Time.now
  if data.respond_to?(:read)
    @data = data.read
  else
    @data = data
  end
end
find_source(context, subject) click to toggle source
# File lib/canistor/storage/object.rb, line 160
def find_source(context, subject)
  if source = context.http_request.headers['x-amz-copy-source']
    bucket_name, key = source.split('/', 2)
    if bucket = Canistor.find_bucket(region, bucket_name)
      if object = bucket.dig(key)
        object
      else
        Canistor::ErrorHandler.serve_no_such_key(context, subject)
        throw :rendered_error
      end
    else
      Canistor::ErrorHandler.serve_no_such_bucket(context, subject)
      throw :rendered_error
    end
  end
end
generate_version_id() click to toggle source
# File lib/canistor/storage/object.rb, line 187
def generate_version_id
  Base64.strict_encode64(SecureRandom.hex(16)).gsub('=', '')
end
headers=(headers) click to toggle source
# File lib/canistor/storage/object.rb, line 196
def headers=(headers)
  return if headers.nil?
  headers.each do |name, value|
    if META_HEADERS.include?(name) || name.start_with?('x-amz-meta')
      @headers[name] = value
    end
  end
end