blobsterix 0.0.14 → 0.0.19

Sign up to get free protection for your applications and to get access to all the features.
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)