blobsterix 0.0.14 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.txt +9 -0
  3. data/README.md +5 -1
  4. data/blobsterix.gemspec +1 -0
  5. data/lib/blobsterix/blob/blob_api.rb +1 -0
  6. data/lib/blobsterix/blob/blob_url_helper.rb +2 -39
  7. data/lib/blobsterix/helper/blob_access.rb +9 -10
  8. data/lib/blobsterix/helper/config_loader.rb +10 -18
  9. data/lib/blobsterix/helper/directory_list.rb +131 -0
  10. data/lib/blobsterix/helper/http.rb +13 -14
  11. data/lib/blobsterix/helper/jsonizer.rb +45 -0
  12. data/lib/blobsterix/helper/logable.rb +25 -0
  13. data/lib/blobsterix/helper/murmur.rb +14 -0
  14. data/lib/blobsterix/helper/simple_proxy.rb +11 -0
  15. data/lib/blobsterix/helper/template_renderer.rb +4 -0
  16. data/lib/blobsterix/helper/url_helper.rb +41 -0
  17. data/lib/blobsterix/router/app_router.rb +15 -65
  18. data/lib/blobsterix/s3/s3_api.rb +20 -6
  19. data/lib/blobsterix/s3/s3_auth.rb +32 -0
  20. data/lib/blobsterix/s3/s3_auth_key_store.rb +14 -0
  21. data/lib/blobsterix/s3/s3_auth_v2.rb +62 -0
  22. data/lib/blobsterix/s3/s3_auth_v2_helper.rb +42 -0
  23. data/lib/blobsterix/s3/s3_auth_v2_query.rb +37 -0
  24. data/lib/blobsterix/s3/s3_auth_v4.rb +33 -0
  25. data/lib/blobsterix/s3/s3_url_helper.rb +1 -38
  26. data/lib/blobsterix/service.rb +3 -9
  27. data/lib/blobsterix/storage/bucket.rb +6 -3
  28. data/lib/blobsterix/storage/bucket_entry.rb +11 -1
  29. data/lib/blobsterix/storage/cache.rb +13 -21
  30. data/lib/blobsterix/storage/file_system.rb +37 -40
  31. data/lib/blobsterix/storage/storage.rb +2 -2
  32. data/lib/blobsterix/transformation/image_transformation.rb +134 -386
  33. data/lib/blobsterix/version.rb +1 -1
  34. data/lib/blobsterix.rb +22 -1
  35. data/spec/lib/helper/directory_list_spec.rb +51 -0
  36. data/spec/lib/s3/s3_api_spec.rb +27 -0
  37. data/spec/lib/s3/s3_auth_spec.rb +181 -0
  38. data/spec/lib/storage/file_system_spec.rb +14 -0
  39. data/spec/spec_helper.rb +1 -0
  40. data/templates/storage_template.rb +2 -2
  41. metadata +30 -2
@@ -1,6 +1,7 @@
1
1
  module Blobsterix
2
2
  class S3Api < AppRouterBase
3
3
  include S3UrlHelper
4
+ include UrlHelper
4
5
 
5
6
  get "/", :list_buckets
6
7
 
@@ -26,12 +27,20 @@ module Blobsterix
26
27
  post "*any", :next_api
27
28
 
28
29
  private
30
+ def check_auth
31
+ return true unless Blobsterix.secret_key_store
32
+ Blobsterix::S3Auth.authenticate(env).check(Blobsterix.secret_key_store)
33
+ end
34
+
29
35
  def list_buckets
30
- Blobsterix.event("s3_api.list_bucket",:bucket => bucket)
31
- Http.OK storage.list(bucket).to_xml, "xml"
36
+ return Http.NotAuthorized unless check_auth
37
+ Blobsterix.event("s3_api.list_bucket",:bucket => bucket)
38
+ start_path = env["params"]["marker"] if env["params"]
39
+ Http.OK storage.list(bucket, :start_path => start_path).to_xml, "xml"
32
40
  end
33
41
 
34
42
  def get_file(send_with_data=true)
43
+ return Http.NotAuthorized unless check_auth
35
44
  return Http.NotFound if favicon
36
45
 
37
46
  if bucket?
@@ -41,25 +50,28 @@ module Blobsterix
41
50
  Http.NotFound
42
51
  end
43
52
  else
44
- Http.OK storage.list(bucket).to_xml, "xml"
53
+ list_buckets
45
54
  end
46
55
  end
47
56
 
48
57
  def get_file_head
49
- #TODO: add event?
58
+ return Http.NotAuthorized unless check_auth
59
+ #TODO: add event?
50
60
  get_file(false)
51
61
  end
52
62
 
53
63
  def create_bucket
54
- Blobsterix.event("s3_api.upload",:bucket => bucket)
64
+ return Http.NotAuthorized unless check_auth
65
+ Blobsterix.event("s3_api.upload",:bucket => bucket)
55
66
  Http.OK storage.create(bucket), "xml"
56
67
  end
57
68
 
58
69
  def upload_data
70
+ return Http.NotAuthorized unless check_auth
59
71
  source = cached_upload
60
72
  accept = AcceptType.new("*/*")#source.accept_type()
61
73
 
62
- trafo_current = trafo(trafo_string)
74
+ trafo_current = trafo(transformation_string)
63
75
  file_current = file
64
76
  bucket_current = bucket
65
77
  Blobsterix.event("s3_api.upload", :bucket => bucket_current,
@@ -71,6 +83,7 @@ module Blobsterix
71
83
  end
72
84
 
73
85
  def delete_bucket
86
+ return Http.NotAuthorized unless check_auth
74
87
  Blobsterix.event("s3_api.delete_bucket", :bucket => bucket)
75
88
 
76
89
  if bucket?
@@ -81,6 +94,7 @@ module Blobsterix
81
94
  end
82
95
 
83
96
  def delete_file
97
+ return Http.NotAuthorized unless check_auth
84
98
  Blobsterix.event("s3_api.delete_file", :bucket => bucket,:file => file)
85
99
  if bucket?
86
100
  Http.OK_no_data storage.delete_key(bucket, file), "xml"
@@ -0,0 +1,32 @@
1
+ module Blobsterix
2
+ module S3Auth
3
+
4
+ VERSIONS = [
5
+ V2,
6
+ V2Query,
7
+ V4
8
+ ]
9
+
10
+ def self.authenticate(env)
11
+ VERSIONS.each do |version|
12
+ v = version.create(env)
13
+ return v if v
14
+ end
15
+ NoAuth.new
16
+ end
17
+
18
+ def self.current_time
19
+ (@current_time||=lambda{Time.now}).call
20
+ end
21
+
22
+ def self.current_time=(obj)
23
+ @current_time=obj
24
+ end
25
+
26
+ class NoAuth
27
+ def check(secret)
28
+ false
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ module Blobsterix
2
+ module S3Auth
3
+ class KeyStore
4
+ def initialize(keys={})
5
+ @keys = keys
6
+ @keys.default=""
7
+ end
8
+ # if no key is found it should return an empty string
9
+ def get_key(id)
10
+ @keys[id]
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,62 @@
1
+ module Blobsterix
2
+ module S3Auth
3
+ class V2
4
+ include ::Blobsterix::S3UrlHelper
5
+ include ::Blobsterix::UrlHelper
6
+ include ::Blobsterix::Logable
7
+ include V2Helper
8
+
9
+ # these should actually be used when calculating
10
+ # the signature when suplied as query parameters,
11
+ # but they are not needed in this usecase
12
+ SUBRESOURCES = [
13
+ "acl",
14
+ "lifecycle",
15
+ "location",
16
+ "logging",
17
+ "notification",
18
+ "partNumber",
19
+ "policy",
20
+ "requestPayment",
21
+ "torrent",
22
+ "uploadId",
23
+ "uploads",
24
+ "versionId",
25
+ "versioning",
26
+ "versions",
27
+ "website"
28
+ ]
29
+
30
+ V2_REGEX = /AWS (\w+):(.+)/
31
+
32
+ def self.create(env)
33
+ auth_string = env["HTTP_AUTHORIZATION"]
34
+ matcher = V2_REGEX.match(auth_string)
35
+ matcher ? V2.new(env, matcher[1], matcher[2]) : nil
36
+ end
37
+
38
+ attr_reader :env, :access_key, :signature
39
+ def initialize(env, access_key, signature)
40
+ @env = env
41
+ @access_key = access_key
42
+ @signature = signature
43
+ end
44
+
45
+ def time_to_check
46
+ env["HTTP_X_AMZ_DATE"]||env["HTTP_DATE"]
47
+ end
48
+
49
+ def is_expired?
50
+ ::Blobsterix::S3Auth.current_time-Time.parse(time_to_check) > 15*60
51
+ end
52
+
53
+ def time_of_request
54
+ env["HTTP_DATE"] unless env["HTTP_X_AMZ_DATE"]
55
+ end
56
+
57
+ def server_signature(secret_key, str)
58
+ gen_signature(secret_key, str)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,42 @@
1
+ module Blobsterix
2
+ module S3Auth
3
+ module V2Helper
4
+
5
+ def canonicalized_amz_headers
6
+ amz_headers = env.select{|key,value|
7
+ /HTTP_X_AMZ/i.match(key) if key.is_a?(String)
8
+ }.sort.map{|key,value|
9
+ "#{key.gsub("HTTP_","").downcase.gsub("_","-")}:#{value}"
10
+ }
11
+ return amz_headers.join("\n")+"\n" if amz_headers.length > 0
12
+ ""
13
+ end
14
+
15
+ def canonicalized_resource(escape=false)
16
+ return "/" if bucket == "root"
17
+ escape ? "/#{bucket}/#{CGI.escape(file)}" : "/#{bucket}/#{file}"
18
+ end
19
+
20
+ def string_to_sign(escape=false)
21
+ "#{env["REQUEST_METHOD"]}\n"+
22
+ "#{env["HTTP_CONTENT_MD5"]||""}\n"+
23
+ "#{env["CONTENT_TYPE"]||""}\n"+
24
+ "#{time_of_request}\n"+
25
+ "#{canonicalized_amz_headers}#{canonicalized_resource(escape)}"
26
+ end
27
+
28
+ def check(secret_key_store)
29
+ return false if is_expired?
30
+ # logger.info string_to_sign
31
+ own_key_0 = server_signature(secret_key_store.get_key(access_key), string_to_sign)
32
+ own_key_1 = server_signature(secret_key_store.get_key(access_key), string_to_sign(true))
33
+ # logger.info "[#{@signature}] == [#{own_key_0}] OR [#{@signature}] == [#{own_key_1}]"
34
+ @signature == own_key_0 || @signature == own_key_1
35
+ end
36
+
37
+ def gen_signature(secret_key, str)
38
+ Base64.encode64( OpenSSL::HMAC.digest("sha1", secret_key, str)).chop
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ module Blobsterix
2
+ module S3Auth
3
+ class V2Query
4
+ include ::Blobsterix::S3UrlHelper
5
+ include ::Blobsterix::UrlHelper
6
+ include ::Blobsterix::Logable
7
+ include V2Helper
8
+
9
+ def self.create(env)
10
+ return nil unless env["params"] && env["params"]["AWSAccessKeyId"] && env["params"]["Signature"]
11
+ V2Query.new(env, env["params"]["AWSAccessKeyId"], env["params"]["Signature"], env["params"]["Expires"])
12
+ end
13
+
14
+ attr_reader :env, :access_key, :signature, :expires
15
+ def initialize(env, access_key, signature, expires)
16
+ @env = env
17
+ @access_key = access_key
18
+ @signature = signature
19
+ @expires = expires
20
+ end
21
+
22
+ def time_of_request
23
+ expires
24
+ end
25
+
26
+ def is_expired?
27
+ return false unless expires
28
+ ::Blobsterix::S3Auth.current_time>Time.at(expires.to_i)
29
+ end
30
+
31
+ def server_signature(secret_key, str)
32
+ # URI::encode(gen_signature(secret_key, str))
33
+ gen_signature(secret_key, str)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,33 @@
1
+ module Blobsterix
2
+ module S3Auth
3
+ class V4
4
+ include ::Blobsterix::S3UrlHelper
5
+ include ::Blobsterix::UrlHelper
6
+
7
+
8
+ V4_REGEX = /AWS4-HMAC-SHA256 Credential=(\w+\/\d+\/.+\/\w+\/aws4_request),.*SignedHeaders=(.+),.*Signature=(\w+)/
9
+
10
+ def self.create(env)
11
+ auth_string = env["HTTP_AUTHORIZATION"]
12
+ matcher = V4_REGEX.match(auth_string)
13
+ matcher ? V4.new(env, matcher[1], matcher[2], matcher[3]) : nil
14
+ end
15
+
16
+ attr_reader :env, :credential, :signed_headers, :signature
17
+ def initialize(env, credential, signed_headers, signature)
18
+ @env = env
19
+ @credential = credential
20
+ @signed_headers = signed_headers
21
+ @signature = signature
22
+ end
23
+ def getSignatureKey(key, dateStamp, regionName, serviceName)
24
+ kDate = OpenSSL::HMAC.digest('sha256', "AWS4" + key, dateStamp)
25
+ kRegion = OpenSSL::HMAC.digest('sha256', kDate, regionName)
26
+ kService = OpenSSL::HMAC.digest('sha256', kRegion, serviceName)
27
+ kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
28
+
29
+ kSigning
30
+ end
31
+ end
32
+ end
33
+ end
@@ -9,10 +9,6 @@ module Blobsterix
9
9
  end
10
10
  end
11
11
 
12
- def favicon
13
- @favicon ||= file.match /favicon/
14
- end
15
-
16
12
  def cache_upload
17
13
  cache.put_stream(cache_upload_key, env['rack.input'])
18
14
  end
@@ -30,20 +26,9 @@ module Blobsterix
30
26
  @cache_upload_key ||= Blobsterix::BlobAccess.new(:bucket => bucket, :id => "upload_#{file.gsub("/", "_")}")
31
27
  end
32
28
 
33
- def trafo_string
29
+ def transformation_string
34
30
  @trafo ||= env["HTTP_X_AMZ_META_TRAFO"] || ""
35
31
  end
36
-
37
- #TransformationCommand
38
- def trafo(trafo_s='')
39
- trafo_a = []
40
- trafo_s.split(",").each{|command|
41
- parts = command.split("_")
42
- key = parts.delete_at(0)
43
- trafo_a << [key, parts.join("_")]
44
- }
45
- trafo_a
46
- end
47
32
 
48
33
  def bucket
49
34
  host = bucket_matcher(env['HTTP_HOST'])
@@ -66,27 +51,5 @@ module Blobsterix
66
51
  host = bucket_matcher(env['HTTP_HOST'])
67
52
  host || env[nil][:bucket] || included_bucket
68
53
  end
69
-
70
- def format
71
- @format ||= env[nil][:format]
72
- end
73
-
74
- def included_bucket
75
- if env[nil][:bucket_or_file] && env[nil][:bucket_or_file].include?("/")
76
- env[nil][:bucket] = env[nil][:bucket_or_file].split("/")[0]
77
- env[nil][:bucket_or_file] = env[nil][:bucket_or_file].gsub("#{env[nil][:bucket]}/", "")
78
- true
79
- else
80
- false
81
- end
82
- end
83
-
84
- def file
85
- if format
86
- [env[nil][:file] || env[nil][:bucket_or_file] || "", format].join(".")
87
- else
88
- env[nil][:file] || env[nil][:bucket_or_file] || ""
89
- end
90
- end
91
54
  end
92
55
  end
@@ -1,6 +1,7 @@
1
1
  module Blobsterix
2
2
  class Service < Goliath::API
3
3
  use Goliath::Rack::Params
4
+ # use SudiMiddleWare
4
5
  include Logable
5
6
  =begin
6
7
  def on_headers(env, headers)
@@ -17,16 +18,9 @@ module Blobsterix
17
18
  env.logger.info 'closing connection'
18
19
  end
19
20
  =end
20
- def get_request_id
21
- @request_id||=0
22
- @request_id+=1
23
- end
24
21
  def response(env)
25
- env["BLOBSTERIX_REQUEST_ID"] = get_request_id
26
- logger.info "RAM USAGE Before[#{Process.pid}]: " + `pmap #{Process.pid} | tail -1`[10,40].strip
27
- a = call_stack(env, BlobApi, StatusApi, S3Api)
28
- logger.info "RAM USAGE After[#{Process.pid}]: " + `pmap #{Process.pid} | tail -1`[10,40].strip
29
- a
22
+ # env["params"] = params
23
+ call_stack(env, BlobApi, StatusApi, S3Api)
30
24
  end
31
25
 
32
26
  def call_stack(env, *apis)
@@ -1,11 +1,12 @@
1
1
  module Blobsterix
2
2
  module Storage
3
3
  class Bucket
4
- attr_accessor :name, :creation_date, :contents
4
+ attr_accessor :name, :creation_date, :contents, :truncated, :next_marker, :marker
5
5
  def initialize(name, date)
6
6
  @name = name
7
7
  @creation_date = date
8
8
  @contents = []
9
+ @truncated = false
9
10
  yield self if block_given?
10
11
  end
11
12
  def to_xml()
@@ -14,9 +15,11 @@ module Blobsterix
14
15
  xml.ListBucketResult(:xmlns => "http://doc.s3.amazonaws.com/#{date.year}-#{date.month}-#{date.day}") {
15
16
  xml.Name name
16
17
  xml.Prefix
17
- xml.Marker
18
+ xml.Marker marker
19
+ xml.NextMarker next_marker
18
20
  xml.MaxKeys 1000
19
- xml.IsTruncated false
21
+ xml.KeyCount contents.length
22
+ xml.IsTruncated truncated
20
23
  contents.each{|entry|
21
24
  entry.insert_xml(xml)
22
25
  }
@@ -2,6 +2,16 @@ module Blobsterix
2
2
  module Storage
3
3
  class BucketEntry
4
4
  attr_accessor :key, :last_modified, :etag, :size, :storage_class, :mimetype, :fullpath
5
+
6
+ def self.create(file, meta)
7
+ BucketEntry.new(file.to_s.gsub("\\", "/")) do |entry|
8
+ entry.last_modified = meta.last_modified.strftime("%Y-%m-%dT%H:%M:%S.000Z")
9
+ entry.etag = meta.etag
10
+ entry.size = meta.size
11
+ entry.mimetype = meta.mimetype
12
+ end
13
+ end
14
+
5
15
  def initialize(key)
6
16
  @key = key
7
17
  @last_modified = "2009-10-12T17:50:30.000Z"
@@ -21,7 +31,7 @@ module Blobsterix
21
31
  xml.Size size
22
32
  xml.StorageClass storage_class
23
33
  xml.MimeType mimetype
24
- xml.FullPath fullpath
34
+ # xml.FullPath fullpath
25
35
  }
26
36
  end
27
37
  end
@@ -4,15 +4,16 @@ module Blobsterix
4
4
  include Blobsterix::Logable
5
5
 
6
6
  def invalidation
7
- Blobsterix.wait_for(Proc.new {
8
- each_meta_file do |meta_file|
9
- blob_access=meta_to_blob_access(meta_file)
10
- if Blobsterix.cache_checker.call(blob_access,meta_file.last_accessed,meta_file.last_modified)
11
- invalidate(blob_access, true)
12
- end
7
+ each_meta_file do |meta_file|
8
+ blob_access = Blobsterix::SimpleProxy.new(Proc.new {
9
+ meta_to_blob_access(FileSystemMetaData.new(meta_file))
10
+ })
11
+ if Blobsterix.cache_checker.call(blob_access,File.atime(meta_file),File.ctime(meta_file))
12
+ invalidate(blob_access, true)
13
13
  end
14
- })
14
+ end
15
15
  end
16
+
16
17
  def initialize(path)
17
18
  @path = Pathname.new(path)
18
19
  FileUtils.mkdir_p(@path) if !Dir.exist?(@path)
@@ -68,10 +69,10 @@ module Blobsterix
68
69
  private
69
70
 
70
71
  def each_meta_file
71
- Dir.glob(@path.join("**/*")).each {|file|
72
- cache_file = Pathname.new file
73
- if block_given? && !cache_file.to_s.match(/\.meta$/) && !cache_file.directory?
74
- yield FileSystemMetaData.new(cache_file)
72
+ Blobsterix::DirectoryList.each(@path) {|file_path, file|
73
+ cache_file = file_path.join file
74
+ if block_given? && !cache_file.to_s.match(/\.meta$/)
75
+ yield cache_file
75
76
  cache_file
76
77
  end
77
78
  }
@@ -98,16 +99,7 @@ module Blobsterix
98
99
  end
99
100
 
100
101
  def hash_filename(filename)
101
- hash = Murmur.Hash64B(filename)
102
- bits = hash.to_s(2)
103
- parts = []
104
- 6.times { |index|
105
- len = 11
106
- len = bits.length if len >= bits.length
107
- value = bits.slice!(0, len).to_i(2).to_s(16).rjust(3,"0")
108
- parts.push(value)
109
- }
110
- parts.join("/")
102
+ Murmur.map_filename(filename)
111
103
  end
112
104
  end
113
105
  end
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'benchmark'
2
3
 
3
4
  module Blobsterix
4
5
  module Storage
@@ -15,29 +16,15 @@ module Blobsterix
15
16
  Dir.entries(contents).include?(bucket) and File.directory?(File.join(contents,bucket))
16
17
  end
17
18
 
18
- def list(bucket="root")
19
+ def list(bucket="root", opts={})
19
20
  if bucket =~ /root/
20
- BucketList.new do |l|
21
-
22
- Dir.entries("#{contents}").each{|dir|
23
- l.buckets << Bucket.new(dir, time_string_of(dir)) if File.directory? File.join("#{contents}",dir) and !(dir =='.' || dir == '..')
24
- }
25
- end
21
+ list_buckets
26
22
  else
27
23
  if bucket_exist(bucket)
28
24
  b = Bucket.new(bucket, time_string_of(bucket))
29
- files = bucket_files(bucket)
25
+ b.marker = opts[:start_path] if opts[:start_path]
30
26
  Blobsterix.wait_for(Proc.new {
31
- files.each do |file|
32
- b.contents << BucketEntry.new(file) do |entry|
33
- meta = metaData(bucket, file)
34
- entry.last_modified = meta.last_modified.strftime("%Y-%m-%dT%H:%M:%S.000Z")
35
- entry.etag = meta.etag
36
- entry.size = meta.size
37
- entry.mimetype = meta.mimetype
38
- entry.fullpath = contents(bucket, file).gsub("#{contents}/", "")
39
- end
40
- end
27
+ collect_bucket_entries(bucket, b, opts)
41
28
  })
42
29
  b
43
30
  else
@@ -80,7 +67,7 @@ module Blobsterix
80
67
 
81
68
  def delete(bucket)
82
69
  logger.info "Storage: delete bucket #{contents(bucket)}"
83
- FileUtils.rm_rf(contents(bucket)) if bucket_exist(bucket) && bucket_files(bucket).empty?
70
+ FileUtils.rm_rf(contents(bucket)) if bucket_exist(bucket) && bucket_empty?(bucket)
84
71
  #Dir.rmdir(contents(bucket)) if bucket_exist(bucket) && bucket_files(bucket).empty?
85
72
  end
86
73
 
@@ -92,6 +79,29 @@ module Blobsterix
92
79
  end
93
80
 
94
81
  private
82
+ def list_buckets
83
+ BucketList.new do |l|
84
+ Dir.entries("#{contents}").each{|dir|
85
+ l.buckets << Bucket.new(dir, time_string_of(dir)) if File.directory? File.join("#{contents}",dir) and !(dir =='.' || dir == '..')
86
+ }
87
+ end
88
+ end
89
+ def collect_bucket_entries(bucket, b, opts)
90
+ start_path = map_filename(opts[:start_path].to_s.gsub("/", "\\")) if opts[:start_path]
91
+ current_obj = Blobsterix::DirectoryList.each_limit(contents(bucket), :limit => 20, :start_path => start_path) do |path, file|
92
+ if file.to_s.end_with?(".meta")
93
+ false
94
+ else
95
+ b.contents << BucketEntry.create(file, metaData(bucket, file.to_s))
96
+ true
97
+ end
98
+ end
99
+ next_marker = current_obj.current_file.to_s.gsub("\\", "/")
100
+ if current_obj.next
101
+ b.next_marker=next_marker
102
+ b.truncated=true
103
+ end
104
+ end
95
105
  def contents(bucket=nil, key=nil)
96
106
  if bucket
97
107
  key ? File.join(@contents, bucket, map_filename(key.gsub("/", "\\"))) : File.join(@contents, bucket)
@@ -101,29 +111,16 @@ module Blobsterix
101
111
  end
102
112
 
103
113
  def map_filename(filename)
104
- hash = Murmur.Hash64B(filename)
105
- bits = hash.to_s(2)
106
- parts = []
107
- 6.times { |index|
108
- len = 11
109
- len = bits.length if len >= bits.length
110
- value = bits.slice!(0, len).to_i(2).to_s(16).rjust(3,"0")
111
- parts.push(value)
112
- }
113
- parts.push(filename)
114
- parts.join("/")
114
+ Murmur.map_filename(filename, filename)
115
115
  end
116
116
 
117
- def bucket_files(bucket)
118
- Blobsterix.wait_for(Proc.new {
119
- if (bucket_exist(bucket))
120
- Dir.glob("#{contents}/#{bucket}/**/*").select{|e| !File.directory?(e) and not e.end_with?(".meta")}.map{ |e|
121
- e.gsub("#{contents}/#{bucket}/","").gsub(/\w+\/\w+\/\w+\/\w+\/\w+\/\w+\//, "").gsub("\\", "/")
122
- }
123
- else
124
- []
125
- end
126
- })
117
+ def bucket_empty?(bucket)
118
+ empty = true
119
+ Blobsterix::DirectoryList.each(contents(bucket)) do
120
+ empty = false
121
+ break
122
+ end
123
+ empty
127
124
  end
128
125
 
129
126
  def metaData(bucket, key)
@@ -1,7 +1,7 @@
1
1
  module Blobsterix
2
2
  module Storage
3
3
  class Storage
4
- def list(bucket="root")
4
+ def list(bucket="root", opts={})
5
5
  Nokogiri::XML::Builder.new do |xml|
6
6
  xml.Error "no such bucket"
7
7
  end
@@ -12,7 +12,7 @@ module Blobsterix
12
12
  def get(bucket, key)
13
13
  Blobsterix::Storage::BlobMetaData.new
14
14
  end
15
- def put(bucket, key, value)
15
+ def put(bucket, key, value, opts={})
16
16
  Blobsterix::Storage::BlobMetaData.new
17
17
  end
18
18
  def create(bucket)