blobsterix 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +27 -0
  2. data/CHANGELOG.txt +13 -0
  3. data/Gemfile +16 -0
  4. data/LICENSE +22 -0
  5. data/README.md +122 -0
  6. data/Rakefile +13 -0
  7. data/bin/blobsterix +152 -0
  8. data/bin/test +26 -0
  9. data/blobsterix.gemspec +39 -0
  10. data/config/lighttpd.conf +50 -0
  11. data/lib/blobsterix.rb +213 -0
  12. data/lib/blobsterix/blob/blob_api.rb +55 -0
  13. data/lib/blobsterix/blob/blob_url_helper.rb +55 -0
  14. data/lib/blobsterix/helper/accept_type.rb +62 -0
  15. data/lib/blobsterix/helper/blob_access.rb +73 -0
  16. data/lib/blobsterix/helper/config_loader.rb +33 -0
  17. data/lib/blobsterix/helper/data_response.rb +54 -0
  18. data/lib/blobsterix/helper/http.rb +47 -0
  19. data/lib/blobsterix/helper/logable.rb +11 -0
  20. data/lib/blobsterix/helper/murmur.rb +137 -0
  21. data/lib/blobsterix/helper/status_info.rb +42 -0
  22. data/lib/blobsterix/helper/template_renderer.rb +39 -0
  23. data/lib/blobsterix/mimemagic/magic.rb +138 -0
  24. data/lib/blobsterix/mimemagic/tables.rb +1770 -0
  25. data/lib/blobsterix/mimemagic/version.rb +5 -0
  26. data/lib/blobsterix/router/app_router.rb +134 -0
  27. data/lib/blobsterix/s3/s3_api.rb +92 -0
  28. data/lib/blobsterix/s3/s3_url_helper.rb +93 -0
  29. data/lib/blobsterix/service.rb +34 -0
  30. data/lib/blobsterix/status/status_api.rb +62 -0
  31. data/lib/blobsterix/status/status_url_helper.rb +11 -0
  32. data/lib/blobsterix/storage/blob_meta_data.rb +60 -0
  33. data/lib/blobsterix/storage/bucket.rb +36 -0
  34. data/lib/blobsterix/storage/bucket_entry.rb +29 -0
  35. data/lib/blobsterix/storage/bucket_list.rb +26 -0
  36. data/lib/blobsterix/storage/cache.rb +90 -0
  37. data/lib/blobsterix/storage/file_system.rb +132 -0
  38. data/lib/blobsterix/storage/file_system_meta_data.rb +136 -0
  39. data/lib/blobsterix/storage/storage.rb +30 -0
  40. data/lib/blobsterix/transformation/image_transformation.rb +439 -0
  41. data/lib/blobsterix/transformation/transformation.rb +30 -0
  42. data/lib/blobsterix/transformation/transformation_chain.rb +78 -0
  43. data/lib/blobsterix/transformation/transformation_manager.rb +115 -0
  44. data/lib/blobsterix/version.rb +3 -0
  45. data/scripts/download.rb +30 -0
  46. data/scripts/test +6 -0
  47. data/spec/lib/blob/blob_api_spec.rb +81 -0
  48. data/spec/lib/helper/blob_access_spec.rb +72 -0
  49. data/spec/lib/s3/s3_api_spec.rb +183 -0
  50. data/spec/lib/service_spec.rb +12 -0
  51. data/spec/lib/status/status_api_spec.rb +42 -0
  52. data/spec/lib/storage/cache_spec.rb +135 -0
  53. data/spec/lib/storage/file_system_spec.rb +84 -0
  54. data/spec/spec_helper.rb +139 -0
  55. data/templates/app/Gemfile +12 -0
  56. data/templates/app/Rakefile +7 -0
  57. data/templates/app/config.rb +61 -0
  58. data/templates/app/config/environments/development.rb +40 -0
  59. data/templates/app/config/environments/production.rb +40 -0
  60. data/templates/app/storages/.keep +0 -0
  61. data/templates/app/transformators/.keep +0 -0
  62. data/templates/app/views/.keep +0 -0
  63. data/templates/storage_template.rb +30 -0
  64. data/templates/transformation_template.rb +41 -0
  65. data/templates/views/error_page.erb +18 -0
  66. data/templates/views/status_page.erb +31 -0
  67. metadata +325 -0
@@ -0,0 +1,5 @@
1
+ class MimeMagic
2
+ # MimeMagic version string
3
+ # @api public
4
+ VERSION = '0.2.1'
5
+ end
@@ -0,0 +1,134 @@
1
+ module Blobsterix
2
+ module Jsonizer
3
+ def json_var(*var_names)
4
+ @json_vars = (@json_vars||[])+var_names.flatten
5
+ end
6
+
7
+ def json_vars
8
+ @json_vars||= []
9
+ end
10
+ end
11
+
12
+ class AppRouterBase
13
+
14
+ extend Jsonizer
15
+ include Logable
16
+
17
+ #attr_reader :logger
18
+ attr_accessor :env
19
+
20
+ def initialize(env)
21
+ @env = env
22
+ #@logger = env["rack.logger"]
23
+ end
24
+
25
+ def storage
26
+ @storage ||= Blobsterix.storage
27
+ end
28
+
29
+ def cache
30
+ @cache ||= Blobsterix.cache
31
+ end
32
+
33
+ def transformation
34
+ @transformation ||= Blobsterix.transformation
35
+ end
36
+
37
+ def next_api
38
+ Http.NextApi
39
+ end
40
+
41
+ def renderer
42
+ @@renderer||=(Blobsterix.respond_to?(:env) && Blobsterix.env == :production) ? TemplateRenderer.new(binding) : ReloadTemplateRenderer.new(binding)
43
+ end
44
+
45
+ def render(template_name, status_code=200, bind=nil)
46
+ begin
47
+ Http.Response(status_code, renderer.render(template_name, bind||binding), "html")
48
+ rescue Errno::ENOENT => e
49
+ Http.NotFound
50
+ end
51
+ end
52
+
53
+ def render_json(obj=nil)
54
+ Http.OK (obj||self).to_json, "json"
55
+ end
56
+
57
+ def render_xml(obj=nil)
58
+ obj = Nokogiri::XML::Builder.new do |xml|
59
+ yield xml
60
+ end if block_given?
61
+ Http.OK (obj||self).to_xml, "xml"
62
+ end
63
+
64
+ def to_json
65
+ stuff = Hash.new
66
+ self.class.json_vars.each{|var_name|
67
+ stuff[var_name.to_sym]=send(var_name) if respond_to?(var_name)
68
+ }
69
+ stuff.to_json
70
+ end
71
+ def to_xml()
72
+ xml = Nokogiri::XML::Builder.new do |xml|
73
+ xml.BlobsterixStatus() {
74
+ self.class.json_vars.each{|var_name|
75
+ var = send(var_name)
76
+ var = var.to_xml if var.respond_to?(:to_xml)
77
+ xml.send(var_name, var) if respond_to?(var_name)
78
+ }
79
+ }
80
+ end
81
+ xml.to_xml
82
+ end
83
+
84
+ def self.options(opt)
85
+ opt = {:function => opt.to_sym} if opt.class != Hash
86
+ {:controller => self.name, :function => :call}.merge(opt)
87
+ end
88
+
89
+ def self.get(path, opt = {})
90
+ path = Journey::Path::Pattern.new path
91
+ router.routes.add_route(lambda{|env| call_controller(options(opt), env)}, path, {:request_method => "GET"}, {})
92
+ end
93
+
94
+ def self.post(path, opt = {})
95
+ path = Journey::Path::Pattern.new path
96
+ router.routes.add_route(lambda{|env| call_controller(options(opt), env)}, path, {:request_method => "POST"}, {})
97
+ end
98
+
99
+ def self.put(path, opt = {})
100
+ path = Journey::Path::Pattern.new path
101
+ router.routes.add_route(lambda{|env| call_controller(options(opt), env)}, path, {:request_method => "PUT"}, {})
102
+ end
103
+
104
+ def self.delete(path, opt = {})
105
+ path = Journey::Path::Pattern.new path
106
+ router.routes.add_route(lambda{|env| call_controller(options(opt), env)}, path, {:request_method => "DELETE"}, {})
107
+ end
108
+
109
+ def self.head(path, opt = {})
110
+ path = Journey::Path::Pattern.new path
111
+ router.routes.add_route(lambda{|env| call_controller(options(opt), env)}, path, {:request_method => "HEAD"}, {})
112
+ end
113
+
114
+ def self.call(env)
115
+ Blobsterix::StatusInfo.connections+=1
116
+ result=router.call(env)
117
+ Blobsterix::StatusInfo.connections-=1
118
+ result
119
+ end
120
+
121
+ def self.call_controller(options, env)
122
+ options[:controller].respond_to?(options[:function]) ? options[:controller].send(options[:function], env) : Blobsterix.const_get(options[:controller]).new(env).send(options[:function])
123
+ end
124
+
125
+ private
126
+ def self.routes()
127
+ (@@routes ||= {})[self.name] ||= Journey::Routes.new
128
+ end
129
+
130
+ def self.router()
131
+ (@@router ||= {})[self.name] ||= Journey::Router.new routes, {}
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,92 @@
1
+ module Blobsterix
2
+ class S3Api < AppRouterBase
3
+ include S3UrlHelper
4
+
5
+ get "/", :list_buckets
6
+
7
+ get "/*bucket_or_file.:format", :get_file
8
+ get "/*bucket_or_file", :get_file
9
+
10
+ head "/*bucket_or_file.:format", :get_file_head
11
+ head "/*bucket_or_file", :get_file_head
12
+
13
+ put "/", :create_bucket
14
+
15
+ put "/*file.:format", :upload_data
16
+ put "/*file", :upload_data
17
+
18
+ delete "/", :delete_bucket
19
+ delete "/*file.:format", :delete_file
20
+ delete "/*file", :delete_file
21
+
22
+ get "*any", :next_api
23
+ put "*any", :next_api
24
+ delete "*any", :next_api
25
+ head "*any", :next_api
26
+ post "*any", :next_api
27
+
28
+ private
29
+ def list_buckets
30
+ Blobsterix.event("s3_api.list_bucket",:bucket => bucket)
31
+ Http.OK storage.list(bucket).to_xml, "xml"
32
+ end
33
+
34
+ def get_file(send_with_data=true)
35
+ return Http.NotFound if favicon
36
+
37
+ if bucket?
38
+ if meta = storage.get(bucket, file)
39
+ send_with_data ? meta.response(true, env["HTTP_IF_NONE_MATCH"], env, env["HTTP_X_FILE"] === "yes", false) : meta.response(false)
40
+ else
41
+ Http.NotFound
42
+ end
43
+ else
44
+ Http.OK storage.list(bucket).to_xml, "xml"
45
+ end
46
+ end
47
+
48
+ def get_file_head
49
+ #TODO: add event?
50
+ get_file(false)
51
+ end
52
+
53
+ def create_bucket
54
+ Blobsterix.event("s3_api.upload",:bucket => bucket)
55
+ Http.OK storage.create(bucket), "xml"
56
+ end
57
+
58
+ def upload_data
59
+ source = cached_upload
60
+ accept = AcceptType.new("*/*")#source.accept_type()
61
+
62
+ trafo_current = trafo(trafo_string)
63
+ file_current = file
64
+ bucket_current = bucket
65
+ Blobsterix.event("s3_api.upload", :bucket => bucket_current,
66
+ :file => file_current, :accept_type => accept.type, :trafo => trafo_current)
67
+ blob_access=BlobAccess.new(:source => source, :bucket => bucket_current, :id => file_current, :accept_type => accept, :trafo => trafo_current)
68
+ data = transformation.run(blob_access)
69
+ cached_upload_clear
70
+ storage.put(bucket_current, file_current, data).response(false)
71
+ end
72
+
73
+ def delete_bucket
74
+ Blobsterix.event("s3_api.delete_bucket", :bucket => bucket)
75
+
76
+ if bucket?
77
+ Http.OK_no_data storage.delete(bucket), "xml"
78
+ else
79
+ Http.NotFound "no such bucket"
80
+ end
81
+ end
82
+
83
+ def delete_file
84
+ Blobsterix.event("s3_api.delete_file", :bucket => bucket,:file => file)
85
+ if bucket?
86
+ Http.OK_no_data storage.delete_key(bucket, file), "xml"
87
+ else
88
+ Http.NotFound "no such bucket"
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,93 @@
1
+ module Blobsterix
2
+ module S3UrlHelper
3
+ HOST_PATH = /(\w+)(\.s3)?\.\w+\.\w+/
4
+ def bucket_matcher(str)
5
+ if str.include?("s3")
6
+ str.match(/(\w+)\.s3\.\w+\.\w+/)
7
+ else
8
+ str.match(/(\w+)\.\w+\.\w+/)
9
+ end
10
+ end
11
+
12
+ def favicon
13
+ @favicon ||= file.match /favicon/
14
+ end
15
+
16
+ def cache_upload
17
+ cache.put(cache_upload_key, env['rack.input'].read)
18
+ end
19
+
20
+ def cached_upload
21
+ cache_upload if not cache.exists?(cache_upload_key)
22
+ cache.get(cache_upload_key)
23
+ end
24
+
25
+ def cached_upload_clear
26
+ cache.delete(cache_upload_key)
27
+ end
28
+
29
+ def cache_upload_key
30
+ #@cache_upload_key ||= "upload/"+bucket.gsub("/", "_")+"_"+file.gsub("/", "_")
31
+ @cache_upload_key ||= Blobsterix::BlobAccess.new(:bucket => bucket, :id => "upload_#{file.gsub("/", "_")}")
32
+ end
33
+
34
+ def trafo_string
35
+ @trafo ||= env["HTTP_X_AMZ_META_TRAFO"] || ""
36
+ end
37
+
38
+ #TransformationCommand
39
+ def trafo(trafo_s='')
40
+ trafo_a = []
41
+ trafo_s.split(",").each{|command|
42
+ parts = command.split("_")
43
+ key = parts.delete_at(0)
44
+ trafo_a << [key, parts.join("_")]
45
+ }
46
+ trafo_a
47
+ end
48
+
49
+ def bucket
50
+ host = bucket_matcher(env['HTTP_HOST'])
51
+ if host
52
+ host[1]
53
+ elsif (env[nil] && env[nil][:bucket])
54
+ env[nil][:bucket]
55
+ elsif (env[nil] && env[nil][:bucket_or_file])
56
+ if env[nil][:bucket_or_file].include?("/")
57
+ env[nil][:bucket_or_file].split("/")[0]
58
+ else
59
+ env[nil][:bucket_or_file]
60
+ end
61
+ else
62
+ "root"
63
+ end
64
+ end
65
+
66
+ def bucket?
67
+ host = bucket_matcher(env['HTTP_HOST'])
68
+ host || env[nil][:bucket] || included_bucket
69
+ end
70
+
71
+ def format
72
+ @format ||= env[nil][:format]
73
+ end
74
+
75
+ def included_bucket
76
+ if env[nil][:bucket_or_file] && env[nil][:bucket_or_file].include?("/")
77
+ env[nil][:bucket] = env[nil][:bucket_or_file].split("/")[0]
78
+ env[nil][:bucket_or_file] = env[nil][:bucket_or_file].gsub("#{env[nil][:bucket]}/", "")
79
+ true
80
+ else
81
+ false
82
+ end
83
+ end
84
+
85
+ def file
86
+ if format
87
+ [env[nil][:file] || env[nil][:bucket_or_file] || "", format].join(".")
88
+ else
89
+ env[nil][:file] || env[nil][:bucket_or_file] || ""
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,34 @@
1
+ module Blobsterix
2
+ class Service < Goliath::API
3
+ use Goliath::Rack::Params
4
+ =begin
5
+ def on_headers(env, headers)
6
+ env.logger.info 'received headers: ' + headers.inspect
7
+ env['async-headers'] = headers
8
+ end
9
+
10
+ def on_body(env, data)
11
+ env.logger.info 'received data: ' + data
12
+ (env['async-body'] ||= '') << data
13
+ end
14
+
15
+ def on_close(env)
16
+ env.logger.info 'closing connection'
17
+ end
18
+ =end
19
+ def response(env)
20
+ call_stack(env, BlobApi, StatusApi, S3Api)
21
+ end
22
+
23
+ def call_stack(env, *apis)
24
+ last_answer = [404,{}, ""]
25
+ apis.each do |api|
26
+ last_answer = api.call(env)
27
+ if last_answer[0] != 600
28
+ return last_answer
29
+ end
30
+ end
31
+ last_answer[0] != 600 ? last_answer : [404,{}, ""]
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,62 @@
1
+ module Blobsterix
2
+ class StatusApi < AppRouterBase
3
+ include StatusUrlHelper
4
+
5
+ get "/status(.:format)", :status
6
+
7
+ get "*any", :next_api
8
+ put "*any", :next_api
9
+ delete "*any", :next_api
10
+ head "*any", :next_api
11
+ post "*any", :next_api
12
+
13
+ json_var :cache_hits, :cache_misses, :cache_errors, :cache_accesses, :connections, :cache_hit_rate, :ram_usage, :uptime
14
+
15
+ def status
16
+ case format
17
+ when :json
18
+ render_json
19
+ when :xml
20
+ render_xml
21
+ else
22
+ render "status_page"
23
+ end
24
+ end
25
+
26
+ def ram_usage
27
+ `pmap #{Process.pid} | tail -1`[10,40].strip
28
+ end
29
+
30
+ def uptime
31
+ @uptime||=StatusInfo.uptime
32
+ end
33
+
34
+ def cache_hits
35
+ @cache_hits||=StatusInfo.cache_hit
36
+ end
37
+
38
+ def cache_hit_rate
39
+ if cache_hits > 0 && cache_accesses > 0
40
+ cache_hits.to_f/cache_accesses.to_f
41
+ else
42
+ 1.to_f
43
+ end
44
+ end
45
+
46
+ def cache_misses
47
+ @cache_misses||=StatusInfo.cache_miss
48
+ end
49
+
50
+ def cache_errors
51
+ @cache_errors||=StatusInfo.cache_error
52
+ end
53
+
54
+ def cache_accesses
55
+ @cache_accesses||=StatusInfo.cache_access
56
+ end
57
+
58
+ def connections
59
+ @connections||=StatusInfo.connections
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,11 @@
1
+ module Blobsterix
2
+ module StatusUrlHelper
3
+ def format
4
+ if env[nil] && env[nil][:format]
5
+ env[nil][:format].to_sym
6
+ else
7
+ :html
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,60 @@
1
+ module Blobsterix
2
+ module Storage
3
+ class BlobMetaData
4
+ def check(key)
5
+ false
6
+ end
7
+ def etag
8
+ ""
9
+ end
10
+ def read
11
+ data()
12
+ end
13
+ def data
14
+ ""
15
+ end
16
+ def path
17
+ ""
18
+ end
19
+ def last_modified
20
+ end
21
+ def last_accessed
22
+ end
23
+ def payload
24
+ {}
25
+ end
26
+ def header
27
+ {}
28
+ end
29
+ def mimetype
30
+ "*/*"
31
+ end
32
+ def mediatype
33
+ "*"
34
+ end
35
+ def size
36
+ 0
37
+ end
38
+ def accept_type
39
+ AcceptType.new
40
+ end
41
+ def valid
42
+ false
43
+ end
44
+ def valid?
45
+ valid
46
+ end
47
+ def write
48
+ if block_given?
49
+ #should yield file
50
+ end
51
+ self
52
+ end
53
+ def delete
54
+ end
55
+ def response(with_data=true, _etag=nil, env = nil, xfile = false, allow_chunks=true)
56
+ Blobsterix::Http::DataResponse.new(self, with_data, _etag, env).call(xfile, allow_chunks)
57
+ end
58
+ end
59
+ end
60
+ end