blobsterix 0.0.9

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 (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