class Canistor::Storage::Upload

Constants

META_HEADERS

Attributes

bucket[RW]
headers[R]
id[RW]
key[RW]
parts[RW]
region[RW]

Public Class Methods

new(**attributes) click to toggle source
# File lib/canistor/storage/upload.rb, line 14
def initialize(**attributes)
  @id = SecureRandom.hex(64)
  @parts = {}
  @headers = {}
  attributes.each do |name, value|
    send("#{name}=", value)
  end
end

Public Instance Methods

get(context) click to toggle source
# File lib/canistor/storage/upload.rb, line 27
def get(context)
  list_parts(context)
end
post(context, subject) click to toggle source
# File lib/canistor/storage/upload.rb, line 50
def post(context, subject)
  catch(:rendered_error) do
    object = Canistor::Storage::Object.new(
      region: region,
      bucket: bucket,
      key: key
    )
    object.write(headers, String.new)
    each_part_in_body(context) do |part|
      object.data << part.data
    end
    show_complete_upload(context, object)
    object
  end
end
put(context, subject) click to toggle source
# File lib/canistor/storage/upload.rb, line 31
def put(context, subject)
  params = CGI::parse(context.http_request.endpoint.query.to_s)
  if params.has_key?('uploads')
    write(context.http_request.headers)
    show_initiate_upload(context)
  elsif params.has_key?('partNumber')
    part_number = Integer(params['partNumber'][0])
    part = find_or_build_part(subject, context, part_number)
    parts[part_number] = part
    part.put(context, subject)
  else
    raise(
      RuntimeError,
      "Implementation should never attempt to PUT an upload when there " \
      "is no uploads or partNumber parameter present in the request."
    )
  end
end
write(headers) click to toggle source
# File lib/canistor/storage/upload.rb, line 23
def write(headers)
  self.headers = headers
end

Private Instance Methods

each_part() { |parts| ... } click to toggle source
# File lib/canistor/storage/upload.rb, line 145
def each_part(&block)
  parts.keys.sort.each do |number|
    yield parts[number]
  end
end
each_part_in_body(context) { |found| ... } click to toggle source
# File lib/canistor/storage/upload.rb, line 112
def each_part_in_body(context)
  document = Nokogiri::XML.parse(context.http_request.body.read)
  number = 0
  document.css('Part').each do |element|
    part_number = Integer(element.css('PartNumber').text)
    if part_number > number
      part_etag = element.css('ETag').text
      found = find_part(part_number, part_etag)
      if found
        yield found
      else
        Canistor::ErrorHandler.serve_invalid_part(
          context, id, part_number, part_etag
        )
        throw :rendered_error
      end
      number = part_number
    else
      Canistor::ErrorHandler.serve_invalid_part_order(context, id)
      throw :rendered_error
    end
  end
end
find_or_build_part(subject, context, part_number) click to toggle source
# File lib/canistor/storage/upload.rb, line 82
def find_or_build_part(subject, context, part_number)
  parts[part_number] || Canistor::Storage::Part.new(
    region: subject.region,
    bucket: subject.bucket,
    key: subject.key,
    upload_id: id,
    number: part_number
  )
end
find_part(part_number, part_etag) click to toggle source
# File lib/canistor/storage/upload.rb, line 136
def find_part(part_number, part_etag)
  if part = parts[part_number]
    if part.etag == part_etag
      return part
    end
  end
  nil
end
headers=(headers) click to toggle source
# File lib/canistor/storage/upload.rb, line 73
def headers=(headers)
  return if headers.nil?
  headers.each do |name, value|
    if META_HEADERS.include?(name)
      @headers[name] = value
    end
  end
end
list_parts(context) click to toggle source
# File lib/canistor/storage/upload.rb, line 151
def list_parts(context)
  context.http_response.signal_headers(
    200,
    'date' => Time.now.httpdate,
    'x-amz-request-id' => SecureRandom.hex(8).upcase
  )
  context.http_response.signal_data(to_upload_parts_xml(context))
end
show_complete_upload(context, etag) click to toggle source
# File lib/canistor/storage/upload.rb, line 103
def show_complete_upload(context, etag)
  context.http_response.signal_headers(
    200,
    'date' => Time.now.httpdate,
    'x-amz-request-id' => SecureRandom.hex(8).upcase
  )
  context.http_response.signal_data(to_complete_xml(context, etag))
end
show_initiate_upload(context) click to toggle source
# File lib/canistor/storage/upload.rb, line 92
def show_initiate_upload(context)
  context.http_response.signal_headers(
    200,
    'date' => Time.now.httpdate,
    'x-amz-request-id' => SecureRandom.hex(8).upcase
  )
  unless context.http_request.http_method == 'HEAD'
    context.http_response.signal_data(to_initiate_xml(context))
  end
end
to_complete_xml(context, object) click to toggle source
# File lib/canistor/storage/upload.rb, line 172
def to_complete_xml(context, object)
  Nokogiri::XML::Builder.new do |xml|
    xml.CompleteMultipartUploadResult(
      xmlns: 'http://s3.amazonaws.com/doc/2006-03-01/'
    ) do
      xml.Location object.location
      xml.Bucket object.bucket
      xml.Key object.key
      xml.ETag object.etag
    end
  end.to_xml
end
to_initiate_xml(context) click to toggle source
# File lib/canistor/storage/upload.rb, line 160
def to_initiate_xml(context)
  Nokogiri::XML::Builder.new do |xml|
    xml.InitiateMultipartUploadResult(
      xmlns: 'http://s3.amazonaws.com/doc/2006-03-01/'
    ) do
      xml.Bucket bucket
      xml.Key key
      xml.UploadId id
    end
  end.to_xml
end
to_upload_parts_xml(context) click to toggle source
# File lib/canistor/storage/upload.rb, line 185
def to_upload_parts_xml(context)
  Nokogiri::XML::Builder.new do |xml|
    xml.ListPartsResult(
      xmlns: 'http://s3.amazonaws.com/doc/2006-03-01/'
    ) do
      xml.Bucket bucket
      xml.Key key
      xml.UploadId id
      each_part do |part|
        xml.Part do
          xml.PartNumber part.number
          xml.ETag part.etag
          xml.Size part.size
        end
      end
    end
  end.to_xml
end