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
data/lib/blobsterix.rb ADDED
@@ -0,0 +1,213 @@
1
+ require "blobsterix/version"
2
+
3
+ #libs
4
+ require 'tempfile'
5
+ require 'scanf'
6
+ require 'nokogiri'
7
+ require 'journey'
8
+ #require 'vips'
9
+ #require 'pry'
10
+ require 'eventmachine'
11
+ require 'em-synchrony'
12
+ require 'mini_magick'
13
+ #require 'mimemagic'
14
+ #require 'ruby-webp'
15
+ #require 'grape'
16
+ #require 'evma_httpserver'
17
+ require 'json'
18
+ require 'logger'
19
+ require 'erb'
20
+ require 'goliath/api'
21
+
22
+ #utility
23
+ require 'blobsterix/mimemagic/tables'
24
+ require 'blobsterix/mimemagic/version'
25
+ require 'blobsterix/mimemagic/magic'
26
+
27
+ #helper
28
+ require 'blobsterix/helper/http'
29
+ require 'blobsterix/helper/accept_type'
30
+ require 'blobsterix/helper/data_response'
31
+ require 'blobsterix/helper/murmur'
32
+ require 'blobsterix/helper/logable'
33
+ require 'blobsterix/helper/blob_access'
34
+ require 'blobsterix/helper/status_info'
35
+ require 'blobsterix/helper/template_renderer'
36
+ require 'blobsterix/helper/config_loader'
37
+
38
+ #router base
39
+ require 'blobsterix/router/app_router'
40
+
41
+ #helper
42
+ require 'blobsterix/s3/s3_url_helper'
43
+ require 'blobsterix/blob/blob_url_helper'
44
+ require 'blobsterix/status/status_url_helper'
45
+
46
+ #apis
47
+ require 'blobsterix/s3/s3_api'
48
+ require 'blobsterix/blob/blob_api'
49
+ require 'blobsterix/status/status_api'
50
+
51
+ #interfaces
52
+ require 'blobsterix/storage/blob_meta_data'
53
+ require 'blobsterix/storage/storage'
54
+ require 'blobsterix/storage/cache'
55
+ require 'blobsterix/storage/bucket_entry'
56
+ require 'blobsterix/storage/bucket'
57
+ require 'blobsterix/storage/bucket_list'
58
+ require 'blobsterix/storage/storage'
59
+ require 'blobsterix/transformation/transformation_manager'
60
+ require 'blobsterix/transformation/transformation_chain'
61
+ require 'blobsterix/transformation/transformation'
62
+
63
+ #implementation
64
+ require 'blobsterix/storage/file_system_meta_data'
65
+ require 'blobsterix/storage/file_system'
66
+
67
+ require 'blobsterix/transformation/image_transformation'
68
+
69
+ #service base
70
+ require 'blobsterix/service'
71
+
72
+ BLOBSTERIX_ROOT=Dir.pwd
73
+ BLOBSTERIX_GEM_DIR = File.join(File.dirname(__FILE__), "../")
74
+
75
+ module Blobsterix
76
+ def self.root
77
+ @root ||= Pathname.new(BLOBSTERIX_ROOT)
78
+ end
79
+
80
+ def self.root_gem
81
+ @root_gem ||= Pathname.new(BLOBSTERIX_GEM_DIR)
82
+ end
83
+
84
+ def self.logger=(obj)
85
+ @logger=obj
86
+ end
87
+
88
+ def self.logger
89
+ @logger ||= Logger.new(STDOUT)
90
+ end
91
+
92
+ def self.storage_dir
93
+ @storage_dir||=root.join("contents")
94
+ end
95
+
96
+ def self.storage_dir=(obj)
97
+ @storage_dir=obj
98
+ end
99
+
100
+ def self.storage
101
+ @storage ||= Storage::FileSystem.new(storage_dir)
102
+ end
103
+
104
+ def self.storage=(obj)
105
+ @storage = obj
106
+ end
107
+
108
+ def self.cache_dir
109
+ @cache_dir||=root.join("cache")
110
+ end
111
+
112
+ def self.cache_dir=(obj)
113
+ @cache_dir=obj
114
+ end
115
+
116
+ def self.cache_original?
117
+ @cache_original||=false
118
+ end
119
+
120
+ def self.cache_original=(obj)
121
+ @cache_original=obj
122
+ end
123
+
124
+ def self.cache
125
+ @cache ||= Storage::Cache.new(cache_dir)
126
+ end
127
+
128
+ def self.cache=(obj)
129
+ @cache = obj
130
+ end
131
+
132
+ def self.decrypt_trafo=(obj)
133
+ @decrypt_trafo=obj
134
+ end
135
+
136
+ def self.decrypt_trafo(blob_access,trafo_string,logger)
137
+ @decrypt_trafo||=lambda{|b,t,l|t}
138
+ if !trafo_string
139
+ return @decrypt_trafo
140
+ end
141
+ @decrypt_trafo.call(blob_access, trafo_string, logger)
142
+ end
143
+
144
+ def self.transformation
145
+ @transformation ||= Blobsterix::Transformations::TransformationManager.new
146
+ end
147
+
148
+ def self.transformation=(obj)
149
+ @transformation=obj
150
+ end
151
+
152
+ def self.cache_checker=(obj)
153
+ @@cache_checker=obj
154
+ end
155
+
156
+ def self.cache_checker
157
+ @@cache_checker||=lambda{|blob_access, last_accessed_at, created_at|
158
+ false
159
+ }
160
+ end
161
+
162
+ def self.storage_event_listener=(obj)
163
+ @storage_event_listener=obj
164
+ end
165
+
166
+ def self.storage_event_listener
167
+ @storage_event_listener||=lambda{|target, hash|
168
+ logger.info("#{target}: #{hash.inspect}")
169
+ }
170
+ end
171
+
172
+ def self.event(name,hash)
173
+ storage_event_listener.call(name,hash)
174
+ end
175
+
176
+ def self.encryption_error(blob_access)
177
+ event("encryption.error",:blob_access => blob_access)
178
+ end
179
+
180
+ def self.cache_miss(blob_access)
181
+ StatusInfo.cache_miss+=1
182
+ StatusInfo.cache_access+=1
183
+ event("cache.miss", :blob_access => blob_access)
184
+ end
185
+
186
+ def self.cache_fatal_error(blob_access)
187
+ StatusInfo.cache_error+=1
188
+ StatusInfo.cache_access+=1
189
+ event("cache.fatal_error", :blob_access => blob_access)
190
+ end
191
+
192
+ def self.cache_hit(blob_access)
193
+ StatusInfo.cache_hit+=1
194
+ StatusInfo.cache_access+=1
195
+ event("cache.hit",:blob_access => blob_access)
196
+ end
197
+
198
+ def self.storage_read(blob_access)
199
+ event("storage.read",:blob_access => blob_access)
200
+ end
201
+
202
+ def self.storage_read_fail(blob_access)
203
+ event("storage.read_fail",:blob_access => blob_access)
204
+ end
205
+
206
+ def self.storage_write(blob_access)
207
+ event("storage.write",:blob_access => blob_access)
208
+ end
209
+
210
+ def self.storage_delete(blob_access)
211
+ event("storage.delete",:blob_access => blob_access)
212
+ end
213
+ end
@@ -0,0 +1,55 @@
1
+ module Blobsterix
2
+ class BlobApi < AppRouterBase
3
+ include BlobUrlHelper
4
+
5
+ get "/blob/v1", :not_allowed
6
+ get "/blob", :not_allowed
7
+ put "/blob/v1", :not_allowed
8
+ put "/blob", :not_allowed
9
+
10
+ get "/blob/v1/(:trafo.)*bucket_or_file.:format", :get_file
11
+ get "/blob/v1/(:trafo.)*bucket_or_file", :get_file
12
+
13
+ head "/blob/v1/(:trafo.)*bucket_or_file.:format", :get_file_head
14
+ head "/blob/v1/(:trafo.)*bucket_or_file", :get_file_head
15
+
16
+ get "*any", :next_api
17
+ put "*any", :next_api
18
+ delete "*any", :next_api
19
+ head "*any", :next_api
20
+ post "*any", :next_api
21
+
22
+ private
23
+ def not_allowed
24
+ Http.NotAllowed "listing blob server not allowed"
25
+ end
26
+
27
+ def get_file(send_with_data=true)
28
+ accept = AcceptType.get(env, format)[0]
29
+
30
+ # check trafo encryption
31
+ trafo_string = Blobsterix.decrypt_trafo(BlobAccess.new(:bucket => bucket, :id => file), transformation_string, logger)
32
+ if !trafo_string
33
+ Blobsterix.encryption_error(BlobAccess.new(:bucket => bucket, :id => file))
34
+ return Http.NotAuthorized
35
+ end
36
+
37
+
38
+ blob_access=BlobAccess.new(:bucket => bucket, :id => file, :accept_type => accept, :trafo => trafo(trafo_string))
39
+ Blobsterix.storage_event_listener.call("blob_api.get", :trafo_string => trafo_string, :blob_access => blob_access)
40
+
41
+ begin
42
+ data = transformation.run(blob_access)
43
+ send_with_data ? data.response(true, env["HTTP_IF_NONE_MATCH"], env, env["HTTP_X_FILE"] === "yes") : data.response(false)
44
+ rescue Errno::ENOENT => e
45
+ logger.error "Cache deleted: #{blob_access}"
46
+ Blobsterix.cache_fatal_error(blob_access)
47
+ Http.ServerError
48
+ end
49
+ end
50
+
51
+ def get_file_head
52
+ get_file(false)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,55 @@
1
+ module Blobsterix
2
+ module BlobUrlHelper
3
+
4
+ def favicon
5
+ @favicon ||= file.match /favicon/
6
+ end
7
+
8
+ def bucket
9
+ if (env[nil] && env[nil][:bucket])
10
+ env[nil][:bucket]
11
+ elsif included_bucket
12
+ env[nil][:bucket]
13
+ else
14
+ "root"
15
+ end
16
+ end
17
+
18
+ def format
19
+ @format ||= env[nil][:format]
20
+ end
21
+
22
+ def included_bucket
23
+ if env[nil][:bucket_or_file] && env[nil][:bucket_or_file].include?("/")
24
+ env[nil][:bucket] = env[nil][:bucket_or_file].split("/")[0]
25
+ env[nil][:bucket_or_file] = env[nil][:bucket_or_file].gsub("#{env[nil][:bucket]}/", "")
26
+ true
27
+ else
28
+ false
29
+ end
30
+ end
31
+
32
+ def file
33
+ if format
34
+ [env[nil][:file] || env[nil][:bucket_or_file] || "", format].join(".")
35
+ else
36
+ env[nil][:file] || env[nil][:bucket_or_file] || ""
37
+ end
38
+ end
39
+
40
+ #TransformationCommand
41
+ def trafo(trafo_s='')
42
+ trafo_a = []
43
+ trafo_s.split(",").each{|command|
44
+ parts = command.split("_")
45
+ key = parts.delete_at(0)
46
+ trafo_a << [key, parts.join("_")]
47
+ }
48
+ trafo_a
49
+ end
50
+
51
+ def transformation_string()
52
+ (env["params"] && env["params"]["trafo"]) || env[nil][:trafo] || ""
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,62 @@
1
+ module Blobsterix
2
+ class AcceptType
3
+
4
+ def self.parse(header, format)
5
+ fields = (header||"").split(",")
6
+ fields << (MimeMagic.by_extension(format) || MimeMagic.new("*/*")).type if format
7
+ fields.map{|entry| AcceptType.new(entry.split(";"))}.sort {|a,b| b.score <=> a.score}
8
+ end
9
+
10
+ def self.get(env, format)
11
+ parse(env["HTTP_ACCEPT"], format)
12
+ end
13
+
14
+ def initialize(*data)
15
+ data = ["*/*"] if data.empty?
16
+ @mimetype = data.flatten[0]
17
+ set_q_factor_string(data.flatten[1] || "q=0.0")
18
+ mediatype
19
+ subtype
20
+ score
21
+ end
22
+
23
+ def to_s()
24
+ @mimetype.to_s
25
+ end
26
+
27
+ def type()
28
+ @mimetype
29
+ end
30
+
31
+ def mediatype()
32
+ @mediatype ||= @mimetype.split("/")[0]
33
+ end
34
+
35
+ def subtype()
36
+ @subtype ||= @mimetype.split("/")[1]
37
+ end
38
+
39
+ def score
40
+ @score ||= factor+(mediatype != "*" ? 1.0: 0.0)+(subtype != "*" ? 1.0: 0.0)
41
+ end
42
+
43
+ def factor
44
+ @q_factor
45
+ end
46
+
47
+ def is? other_type
48
+ mediatype === other_type.mediatype || mediatype === "*" || other_type.mediatype === "*"
49
+ end
50
+
51
+ def equal? other_type
52
+ mediatype === other_type.mediatype and subtype === other_type.subtype# and factor == other_type.factor
53
+ end
54
+
55
+ private
56
+ def set_q_factor_string(str)
57
+ str.scanf("%c=%f"){|char, num|
58
+ @q_factor = num if char === 'q'
59
+ }
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,73 @@
1
+ module Blobsterix
2
+ class BlobAccess
3
+ attr_reader :bucket, :id,:trafo,:accept_type
4
+ attr_reader :source,:target
5
+
6
+ def initialize(atts={})
7
+ @trafo = []
8
+ atts.each do |key,value| send("#{key}=",value) end
9
+ identifier
10
+ end
11
+
12
+ def identifier
13
+ @identifier||= "#{bucket}_#{id.gsub("/","_")}_#{trafo.map {|trafo_pair|"#{trafo_pair[0]}_#{trafo_pair[1]}"}.join(",")}.#{subtype}"
14
+ end
15
+
16
+ def to_s
17
+ "BlobAccess: bucket(#{bucket}), id(#{id}), trafo(#{trafo}), accept_type(#{accept_type})"
18
+ end
19
+
20
+ def get
21
+ @meta||=find_blob
22
+ end
23
+
24
+ def equals?(blob_access)
25
+ identifier == blob_access.identifier
26
+ end
27
+
28
+ def copy
29
+ BlobAccess.new(:bucket => bucket, :id => id, :trafo => trafo, :accept_type => accept_type, :source => source, :target => target)
30
+ end
31
+
32
+ def reset!
33
+ @meta = nil
34
+ self
35
+ end
36
+
37
+ private
38
+
39
+ def find_blob
40
+ unless Blobsterix.cache.exists?(self)
41
+ if trafo.empty? || raw_trafo?
42
+ metaData = Blobsterix.storage.get(self.bucket, self.id)
43
+ if raw_trafo? || raw_accept_type?(metaData.accept_type)
44
+ # puts "Load from storage"
45
+ return metaData unless Blobsterix.cache_original?
46
+ Blobsterix.cache.put(BlobAccess.new(:bucket => bucket, :id => id), metaData.data) if metaData.valid?
47
+ return Blobsterix.cache.get(BlobAccess.new(:bucket => bucket, :id => id)) if metaData.valid?
48
+ else
49
+ # puts "accept type doesn't work"
50
+ end
51
+ else
52
+ # puts "trafo is not raw"
53
+ end
54
+ end
55
+ Blobsterix.cache.get(self)
56
+ end
57
+
58
+ def raw_trafo?
59
+ @raw_trafo||=(trafo.length == 1 && trafo[0][0]=="raw")
60
+ end
61
+
62
+ def raw_accept_type?(other)
63
+ @raw_accept_type||= (!accept_type || accept_type.equal?(other))
64
+ end
65
+
66
+
67
+ def subtype
68
+ accept_type ? accept_type.subtype : ""
69
+ end
70
+ attr_writer :bucket, :id,:trafo,:accept_type
71
+ attr_writer :source, :target
72
+ end
73
+ end