Class: S3::Application

Inherits:
Sinatra::Base
  • Object
show all
Defined in:
lib/sinatra-s3/base.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.callback(args = {}, &block) ⇒ Object



487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/sinatra-s3/base.rb', line 487

def self.callback(args = {}, &block)
  @@callbacks ||= {}
  if args[:mime_type]
    @@callbacks[:mime_type] ||= {}
    @@callbacks[:mime_type][args[:mime_type]] = block
  elsif args[:error]
    @@callbacks[:error] ||= {}
    @@callbacks[:error][args[:error]] = block
  elsif args[:when]
	@@callbacks[:when] ||= {}
	@@callbacks[:when][args[:when]] = block
  end
end

Instance Method Details

#call(env) ⇒ Object



51
52
53
54
55
56
57
58
# File 'lib/sinatra-s3/base.rb', line 51

def call(env)
  begin
    return if env['PATH_INFO'] =~ /^\/control/
    super(env)
  ensure
	ActiveRecord::Base.connection_pool.release_connection
  end
end

#create_slotObject

Raises:

  • (MissingContentLength)


296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/sinatra-s3/base.rb', line 296

def create_slot
  bucket = Bucket.find_root(params[:captures].first)
  begin
    slot = bucket.find_slot(params[:captures].last)
    only_can_write slot unless slot.nil?
  rescue NoSuchKey
    only_can_write bucket
  end

  raise MissingContentLength unless env['CONTENT_LENGTH']

  if params.has_key?('acl')
	slot = bucket.find_slot(oid)
	slot.grant(requested_acl(slot))
	headers 'ETag' => slot.etag, 'Content-Length' => 0.to_s
	body ""
  elsif env['HTTP_X_AMZ_COPY_SOURCE'].to_s =~ /\/(.+?)\/(.+)/
	source_bucket_name = $1
	source_oid = $2

	source_slot = Bucket.find_root(source_bucket_name).find_slot(source_oid)
	@meta = source_slot.meta unless !env['HTTP_X_AMZ_METADATA_DIRECTIVE'].nil? && env['HTTP_X_AMZ_METADATA_DIRECTIVE'].upcase == "REPLACE"
	only_can_read source_slot

	unless env['HTTP_X_AMZ_COPY_SOURCE_IF_MATCH'].blank?
	  raise PreconditionFailed if source_slot.obj.etag != env['HTTP_X_AMZ_COPY_SOURCE_IF_MATCH']
	end
	unless env['HTTP_X_AMZ_COPY_SOURCE_IF_NONE_MATCH'].blank?
	  raise PreconditionFailed if source_slot.obj.etag == env['HTTP_X_AMZ_COPY_SOURCE_IF_NONE_MATCH']
	end
	unless env['HTTP_X_AMZ_COPY_SOURCE_IF_UNMODIFIED_SINCE'].blank?
	  raise PreconditionFailed if Time.httpdate(env['HTTP_X_AMZ_COPY_SOURCE_IF_UNMODIFIED_SINCE']) > source_slot.updated_at
	end
	unless env['HTTP_X_AMZ_COPY_SOURCE_IF_MODIFIED_SINCE'].blank?
	  raise PreconditionFailed if Time.httpdate(env['HTTP_X_AMZ_COPY_SOURCE_IF_MODIFIED_SINCE']) < source_slot.updated_at
	end

	temp_path = File.join(STORAGE_PATH, source_slot.obj.path)
	fileinfo = source_slot.obj
	fileinfo.path = File.join(params[:captures].first, rand(10000).to_s(36) + '_' + File.basename(temp_path))
	fileinfo.path.succ! while File.exists?(File.join(STORAGE_PATH, fileinfo.path))
	file_path = File.join(STORAGE_PATH,fileinfo.path)
  else
	temp_path = env['rack.input'][:path] rescue nil
	readlen = 0
	md5 = MD5.new

	Tempfile.open(File.basename(params[:captures].last)) do |tmpf|
	  temp_path ||= tmpf.path
	  tmpf.binmode
	  while part = env['rack.input'].read(BUFSIZE)
 readlen += part.size
 md5 << part
 tmpf << part unless env['rack.input'].is_a?(Tempfile)
	  end
	end

	fileinfo = FileInfo.new
	fileinfo.mime_type = env['CONTENT_TYPE'] || "binary/octet-stream"
	fileinfo.disposition = env['CONTENT_DISPOSITION']
	fileinfo.size = readlen
	fileinfo.md5 = Base64.encode64(md5.digest).strip
	fileinfo.etag = '"' + md5.hexdigest + '"'

	raise IncompleteBody if env['CONTENT_LENGTH'].to_i != readlen
	if env['HTTP_CONTENT_MD5']
	  b64cs = /[0-9a-zA-Z+\/]/
 re = /
 ^
	  (?:#{b64cs}{4})*       # any four legal chars
 (?:#{b64cs}{2}        # right-padded by up to two =s
  (?:#{b64cs}|=){2})?
  $
	  /ox

	  raise InvalidDigest unless env['HTTP_CONTENT_MD5'] =~ re
	  raise BadDigest unless fileinfo.md5 == env['HTTP_CONTENT_MD5']
	end
  end

  mdata = {}

  slot = nil
  meta = @meta.nil? || @meta.empty? ? {} : {}.merge(@meta)
  owner_id = @user ? @user.id : bucket.owner_id

  begin
	slot = bucket.find_slot(params[:captures].last)
	if slot.versioning_enabled?
	  nslot = slot.clone()
	  slot.update_attributes(:deleted => true)
	  slot = nslot
	end
	if source_slot.nil?
	  fileinfo.path = slot.obj.path
	  file_path = File.join(STORAGE_PATH,fileinfo.path)
	  FileUtils.mv(temp_path, file_path,{ :force => true })
	else
	  FileUtils.cp(temp_path, file_path)
	end
	slot.update_attributes(:owner_id => owner_id, :meta => meta, :obj => fileinfo, :size => fileinfo.size)
  rescue NoSuchKey
	if source_slot.nil?
	  fileinfo.path = File.join(params[:captures].first, rand(10000).to_s(36) + '_' + File.basename(temp_path))
	  fileinfo.path.succ! while File.exists?(File.join(STORAGE_PATH, fileinfo.path))
	  file_path = File.join(STORAGE_PATH,fileinfo.path)
	  FileUtils.mkdir_p(File.dirname(file_path))
	  FileUtils.mv(temp_path, file_path)
	else
	  FileUtils.cp(temp_path, file_path)
	end
	slot = Slot.create(:name => params[:captures].last, :owner_id => owner_id, :meta => meta, :obj => fileinfo, :size => fileinfo.size)
	bucket.add_child(slot)
  end
  slot.grant(requested_acl(slot))

  h = { 'Content-Length' => 0.to_s, 'ETag' => slot.etag }
  if slot.versioning_enabled?
	begin
	  slot.git_repository.add(File.basename(fileinfo.path))
	  tmp = slot.git_repository.commit("Added #{slot.name} to the Git repository.")
	  slot.git_update
	  slot.update_attributes(:version => slot.git_object.objectish)
	  h.merge!({ 'x-amz-version-id' => slot.git_object.objectish })
	rescue Git::GitExecuteError => error_message
	  puts "[#{Time.now}] GIT: #{error_message}"
	end
  end

  if env['HTTP_X_AMZ_COPY_SOURCE'].blank?
	redirect_url = (params[:success_action_redirect] || params[:redirect])
	redirect redirect_url unless redirect_url.blank?
	status params[:success_action_status].to_i if params[:success_action_status]
	headers h
    body ""
  else
	h['Content-Length'] = nil
	headers h
	xml do |x|
	  x.CopyObjectResult do
 x.LastModified slot.updated_at.httpdate
 x.Etag slot.etag
	  end
	end
  end
end

#slot_headObject

Raises:

  • (NotModified)


192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/sinatra-s3/base.rb', line 192

def slot_head
  bucket = Bucket.find_root(params[:captures].first)

  h = {}
  if params.has_key?('version-id')
	@revision = bucket.git_repository.gcommit(params['version-id'])
	h.merge!({ 'x-amz-version-id' => @revision.sha })
	@slot = Slot.find_by_version(@revision.sha)
	@revision_file = @revision.gtree.blobs[File.basename(@slot.fullpath)].contents { |f| f.read }
  else
	@slot = bucket.find_slot(params[:captures].last)
	git_object = @slot.git_object
	h.merge!({ 'x-amz-version-id' => git_object.objectish }) if git_object
  end

  if params.has_key? 'acl'
	only_can_read_acp @slot
  else
	only_can_read @slot
  end

  etag = @slot.etag
  since = Time.httpdate(env['HTTP_IF_MODIFIED_SINCE']) rescue nil
  raise NotModified if since and @slot.updated_at <= since
  since = Time.httpdate(env['HTTP_IF_UNMODIFIED_SINCE']) rescue nil
  raise PreconditionFailed if since and @slot.updated_at > since
  raise PreconditionFailed if env['HTTP_IF_MATCH'] and etag != env['HTTP_IF_MATCH']
  raise NotModified if env['HTTP_IF_NONE_MATCH'] and etag == env['HTTP_IF_NONE_MATCH']

  @slot.meta.each { |k, v|
	h.merge!({ "x-amz-meta-#{k}" => v })
  }

  if @slot.obj.is_a? FileInfo
	h.merge!({ 'Content-Disposition' => (@slot.obj.disposition.nil? ? "inline" : @slot.obj.disposition), 'Content-Length' => (@revision_file.nil? ? 
	  @slot.obj.size : @revision_file.length).to_s, 'Content-Type' => @slot.obj.mime_type })
  end
  h['Content-Type'] ||= 'binary/octet-stream'
  h.merge!('ETag' => etag, 'Last-Modified' => @slot.updated_at.httpdate) if @revision_file.nil?
  headers h
end