httpimagestore 1.8.1 → 1.9.0
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.
- checksums.yaml +15 -0
- data/Gemfile +7 -7
- data/Gemfile.lock +20 -20
- data/README.md +165 -37
- data/Rakefile +7 -2
- data/VERSION +1 -1
- data/bin/httpimagestore +74 -41
- data/lib/httpimagestore/configuration/file.rb +20 -11
- data/lib/httpimagestore/configuration/handler.rb +96 -257
- data/lib/httpimagestore/configuration/handler/source_store_base.rb +37 -0
- data/lib/httpimagestore/configuration/handler/statement.rb +114 -0
- data/lib/httpimagestore/configuration/identify.rb +17 -9
- data/lib/httpimagestore/configuration/output.rb +33 -61
- data/lib/httpimagestore/configuration/path.rb +2 -2
- data/lib/httpimagestore/configuration/request_state.rb +131 -0
- data/lib/httpimagestore/configuration/s3.rb +41 -29
- data/lib/httpimagestore/configuration/thumbnailer.rb +189 -96
- data/lib/httpimagestore/configuration/validate_hmac.rb +170 -0
- data/lib/httpimagestore/error_reporter.rb +6 -1
- data/lib/httpimagestore/ruby_string_template.rb +10 -19
- metadata +40 -102
- data/.rspec +0 -1
- data/features/cache-control.feature +0 -41
- data/features/compatibility.feature +0 -165
- data/features/data-uri.feature +0 -55
- data/features/encoding.feature +0 -103
- data/features/error-reporting.feature +0 -281
- data/features/flexi.feature +0 -259
- data/features/health-check.feature +0 -29
- data/features/request-matching.feature +0 -211
- data/features/rewrite.feature +0 -122
- data/features/s3-store-and-thumbnail.feature +0 -82
- data/features/source-failover.feature +0 -71
- data/features/step_definitions/httpimagestore_steps.rb +0 -203
- data/features/storage.feature +0 -198
- data/features/support/env.rb +0 -116
- data/features/support/test-large.jpg +0 -0
- data/features/support/test.empty +0 -0
- data/features/support/test.jpg +0 -0
- data/features/support/test.png +0 -0
- data/features/support/test.txt +0 -1
- data/features/support/tiny.png +0 -0
- data/features/xid-forwarding.feature +0 -49
- data/httpimagestore.gemspec +0 -145
- data/load_test/load_test.1k.23a022f6e.m1.small-comp.csv +0 -3
- data/load_test/load_test.1k.ec9bde794.m1.small.csv +0 -4
- data/load_test/load_test.jmx +0 -317
- data/load_test/thumbnail_specs.csv +0 -11
- data/load_test/thumbnail_specs_v2.csv +0 -10
- data/spec/configuration_file_spec.rb +0 -333
- data/spec/configuration_handler_spec.rb +0 -255
- data/spec/configuration_identify_spec.rb +0 -67
- data/spec/configuration_output_spec.rb +0 -821
- data/spec/configuration_path_spec.rb +0 -138
- data/spec/configuration_s3_spec.rb +0 -911
- data/spec/configuration_source_failover_spec.rb +0 -101
- data/spec/configuration_spec.rb +0 -90
- data/spec/configuration_thumbnailer_spec.rb +0 -483
- data/spec/ruby_string_template_spec.rb +0 -61
- data/spec/spec_helper.rb +0 -89
- data/spec/support/compute.jpg +0 -0
- data/spec/support/cuba_response_env.rb +0 -40
- data/spec/support/full.cfg +0 -183
- data/spec/support/utf_string.txt +0 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'httpimagestore/configuration/handler/statement'
|
|
2
|
+
|
|
3
|
+
module Configuration
|
|
4
|
+
class SourceStoreBase < HandlerStatement
|
|
5
|
+
include ImageName
|
|
6
|
+
include ConditionalInclusion
|
|
7
|
+
include LocalConfiguration
|
|
8
|
+
include GlobalConfiguration
|
|
9
|
+
include PathSpec
|
|
10
|
+
|
|
11
|
+
def initialize(global, image_name, path_spec)
|
|
12
|
+
with_global_configuration(global)
|
|
13
|
+
with_image_name(image_name)
|
|
14
|
+
with_path_spec(path_spec)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def put_sourced_named_image(request_state)
|
|
20
|
+
rendered_path = path_template.render(request_state.with_locals(local_configuration))
|
|
21
|
+
|
|
22
|
+
image = yield @image_name, rendered_path
|
|
23
|
+
|
|
24
|
+
image.source_path = rendered_path
|
|
25
|
+
request_state.images[@image_name] = image
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get_named_image_for_storage(request_state)
|
|
29
|
+
image = request_state.images[@image_name]
|
|
30
|
+
rendered_path = path_template.render(request_state.with_locals(local_configuration))
|
|
31
|
+
image.store_path = rendered_path
|
|
32
|
+
|
|
33
|
+
yield @image_name, image, rendered_path
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require 'httpimagestore/configuration'
|
|
2
|
+
require 'httpimagestore/configuration/handler'
|
|
3
|
+
|
|
4
|
+
module Configuration
|
|
5
|
+
class HandlerStatement < Scope
|
|
6
|
+
# Base class for all statements that are within get/post/put/delete handler
|
|
7
|
+
#
|
|
8
|
+
module LocalConfiguration
|
|
9
|
+
attr_reader :local_configuration
|
|
10
|
+
def config_local(name, value)
|
|
11
|
+
@local_configuration ||= {}
|
|
12
|
+
@local_configuration[name] = value
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ImageName
|
|
17
|
+
attr_reader :image_name
|
|
18
|
+
|
|
19
|
+
def with_image_name(image_name)
|
|
20
|
+
@image_name = image_name
|
|
21
|
+
config_local :imagename, @image_name # deprecated
|
|
22
|
+
config_local :image_name, @image_name
|
|
23
|
+
self
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module GlobalConfiguration
|
|
28
|
+
attr_reader :global
|
|
29
|
+
def with_global_configuration(global)
|
|
30
|
+
@global = global
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def path_template(path_spec)
|
|
34
|
+
@global.paths[path_spec]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module PathSpec
|
|
39
|
+
attr_reader :path_spec
|
|
40
|
+
|
|
41
|
+
def with_path_spec(path_spec)
|
|
42
|
+
@path_spec = path_spec
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# this is more specific than GlobalConfiguration
|
|
47
|
+
def path_template
|
|
48
|
+
@global.paths[@path_spec]
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
module ConditionalInclusion
|
|
53
|
+
class ImageNameOn
|
|
54
|
+
def initialize(template)
|
|
55
|
+
@template = template.to_template
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def included?(request_state)
|
|
59
|
+
image_name = request_state[:image_name]
|
|
60
|
+
@template.render(request_state).split(',').include? image_name
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class VariableMatches
|
|
65
|
+
def initialize(value)
|
|
66
|
+
param_name, template = value.split(':', 2)
|
|
67
|
+
@param_name = param_name.to_sym if param_name
|
|
68
|
+
@template = template.to_template if template
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def included?(request_state)
|
|
72
|
+
return false if not @param_name
|
|
73
|
+
return !request_state[@param_name].empty? if not @template
|
|
74
|
+
@template.render(request_state) == request_state[@param_name]
|
|
75
|
+
rescue Configuration::VariableNotDefinedError
|
|
76
|
+
false
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.grab_conditions_with_remaining(attributes)
|
|
81
|
+
conditions = []
|
|
82
|
+
attributes = attributes.dup
|
|
83
|
+
|
|
84
|
+
if_image_name_on = attributes.delete('if-image-name-on')
|
|
85
|
+
conditions << ConditionalInclusion::ImageNameOn.new(if_image_name_on) if if_image_name_on
|
|
86
|
+
|
|
87
|
+
if_image_name_on = attributes.delete('if-variable-matches')
|
|
88
|
+
conditions << ConditionalInclusion::VariableMatches.new(if_image_name_on) if if_image_name_on
|
|
89
|
+
|
|
90
|
+
[conditions, attributes]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def with_conditions(conditions)
|
|
94
|
+
@conditions ||= []
|
|
95
|
+
@conditions.push(*conditions)
|
|
96
|
+
self
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def included?(request_state)
|
|
100
|
+
return true if not @conditions or @conditions.empty?
|
|
101
|
+
# some conditions may use local_configuration vars
|
|
102
|
+
request_state = request_state.with_locals(local_configuration) if @local_configuration
|
|
103
|
+
@conditions.all? do |matcher|
|
|
104
|
+
matcher.included?(request_state)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def excluded?(request_state)
|
|
109
|
+
not included? request_state
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
@@ -5,6 +5,11 @@ require 'httpimagestore/configuration/handler'
|
|
|
5
5
|
module Configuration
|
|
6
6
|
class Identify < HandlerStatement
|
|
7
7
|
include ClassLogging
|
|
8
|
+
include ImageName
|
|
9
|
+
include LocalConfiguration
|
|
10
|
+
include GlobalConfiguration
|
|
11
|
+
include ConditionalInclusion
|
|
12
|
+
include PerfStats
|
|
8
13
|
|
|
9
14
|
extend Stats
|
|
10
15
|
def_stats(
|
|
@@ -18,18 +23,19 @@ module Configuration
|
|
|
18
23
|
|
|
19
24
|
def self.parse(configuration, node)
|
|
20
25
|
image_name = node.grab_values('image name').first
|
|
21
|
-
if_image_name_on = node.grab_attributes('if-image-name-on').first
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
conditions, remaining = *ConditionalInclusion.grab_conditions_with_remaining(node.attributes)
|
|
28
|
+
remaining.empty? or raise UnexpectedAttributesError.new(node, remaining)
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
iden = self.new(configuration.global, image_name)
|
|
31
|
+
iden.with_conditions(conditions)
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
configuration.processors << iden
|
|
34
|
+
end
|
|
30
35
|
|
|
31
|
-
def initialize(global, image_name
|
|
32
|
-
|
|
36
|
+
def initialize(global, image_name)
|
|
37
|
+
with_global_configuration(global)
|
|
38
|
+
with_image_name(image_name)
|
|
33
39
|
end
|
|
34
40
|
|
|
35
41
|
def realize(request_state)
|
|
@@ -41,7 +47,9 @@ module Configuration
|
|
|
41
47
|
Identify.stats.incr_total_identify_requests
|
|
42
48
|
Identify.stats.incr_total_identify_requests_bytes image.data.bytesize
|
|
43
49
|
|
|
44
|
-
id =
|
|
50
|
+
id = measure "identifying", @image_name do
|
|
51
|
+
client.with_headers(request_state.forward_headers).identify(image.data)
|
|
52
|
+
end
|
|
45
53
|
|
|
46
54
|
image.mime_type = id.mime_type if id.mime_type
|
|
47
55
|
image.width = id.width if id.width
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require 'httpimagestore/configuration/handler'
|
|
1
|
+
require 'httpimagestore/configuration/handler/statement'
|
|
2
2
|
require 'httpimagestore/ruby_string_template'
|
|
3
3
|
require 'addressable/uri'
|
|
4
4
|
require 'base64'
|
|
@@ -16,61 +16,18 @@ module Configuration
|
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
class OutputText
|
|
20
|
-
def self.match(node)
|
|
21
|
-
node.name == 'output_text'
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def self.parse(configuration, node)
|
|
25
|
-
configuration.output and raise StatementCollisionError.new(node, 'output')
|
|
26
|
-
text = node.grab_values('text').first
|
|
27
|
-
status, cache_control = *node.grab_attributes('status', 'cache-control')
|
|
28
|
-
configuration.output = OutputText.new(text, status || 200, cache_control)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def initialize(text, status, cache_control)
|
|
32
|
-
@text = RubyStringTemplate.new(text || fail("no text?!"))
|
|
33
|
-
@status = status || 200
|
|
34
|
-
@cache_control = cache_control
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def realize(request_state)
|
|
38
|
-
# make sure variables are available in request context
|
|
39
|
-
status = @status
|
|
40
|
-
text = @text.render(request_state)
|
|
41
|
-
cache_control = @cache_control
|
|
42
|
-
request_state.output do
|
|
43
|
-
res['Cache-Control'] = cache_control if cache_control
|
|
44
|
-
write_plain status.to_i, text.to_s
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
class OutputOK < OutputText
|
|
50
|
-
def self.match(node)
|
|
51
|
-
node.name == 'output_ok'
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def self.parse(configuration, node)
|
|
55
|
-
configuration.output and raise StatementCollisionError.new(node, 'output')
|
|
56
|
-
cache_control = node.grab_attributes('cache-control').first
|
|
57
|
-
configuration.output = OutputOK.new(cache_control)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def initialize(cache_control = nil)
|
|
61
|
-
super 'OK', 200, cache_control
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
Handler::register_node_parser OutputText
|
|
65
|
-
|
|
66
19
|
class OutputMultiBase
|
|
67
20
|
class OutputSpec < HandlerStatement
|
|
21
|
+
include GlobalConfiguration
|
|
68
22
|
include ImageName
|
|
69
23
|
include PathSpec
|
|
70
24
|
include ConditionalInclusion
|
|
25
|
+
include LocalConfiguration
|
|
71
26
|
|
|
72
|
-
def initialize(global, image_name, scheme, host, port, path_spec
|
|
73
|
-
|
|
27
|
+
def initialize(global, image_name, scheme, host, port, path_spec)
|
|
28
|
+
with_global_configuration(global)
|
|
29
|
+
with_image_name(image_name)
|
|
30
|
+
with_path_spec(path_spec)
|
|
74
31
|
@scheme = scheme && scheme.to_template
|
|
75
32
|
@host = host && host.to_template
|
|
76
33
|
@port = port && port.to_template
|
|
@@ -80,7 +37,7 @@ module Configuration
|
|
|
80
37
|
store_path = request_state.images[@image_name].store_path or raise StorePathNotSetForImage.new(@image_name)
|
|
81
38
|
return store_path unless @path_spec
|
|
82
39
|
|
|
83
|
-
path_template.render(request_state.with_locals(
|
|
40
|
+
path_template.render(request_state.with_locals(local_configuration, path: store_path))
|
|
84
41
|
end
|
|
85
42
|
|
|
86
43
|
def store_url(request_state)
|
|
@@ -96,7 +53,7 @@ module Configuration
|
|
|
96
53
|
request_locals[:host] = url.host if url.host
|
|
97
54
|
request_locals[:port] = url.port if url.port
|
|
98
55
|
|
|
99
|
-
request_state = request_state.with_locals(
|
|
56
|
+
request_state = request_state.with_locals(local_configuration, request_locals)
|
|
100
57
|
|
|
101
58
|
# optional rewrites
|
|
102
59
|
url.scheme = @scheme.render(request_state) if @scheme
|
|
@@ -112,9 +69,13 @@ module Configuration
|
|
|
112
69
|
nodes = node.values.empty? ? node.children : [node]
|
|
113
70
|
output_specs = nodes.map do |node|
|
|
114
71
|
image_name = node.grab_values('image name').first
|
|
115
|
-
scheme, host, port, path_spec,
|
|
116
|
-
|
|
117
|
-
|
|
72
|
+
scheme, host, port, path_spec, remaining = *node.grab_attributes_with_remaining('scheme', 'host', 'port', 'path')
|
|
73
|
+
conditions, remaining = *HandlerStatement::ConditionalInclusion.grab_conditions_with_remaining(remaining)
|
|
74
|
+
remaining.empty? or raise UnexpectedAttributesError.new(node, remaining)
|
|
75
|
+
|
|
76
|
+
out = OutputSpec.new(configuration.global, image_name, scheme, host, port, path_spec)
|
|
77
|
+
out.with_conditions(conditions)
|
|
78
|
+
out
|
|
118
79
|
end
|
|
119
80
|
|
|
120
81
|
configuration.output and raise StatementCollisionError.new(node, 'output')
|
|
@@ -125,10 +86,12 @@ module Configuration
|
|
|
125
86
|
@output_specs = output_specs
|
|
126
87
|
end
|
|
127
88
|
end
|
|
128
|
-
Handler::register_node_parser OutputOK
|
|
129
89
|
|
|
130
|
-
class OutputImage
|
|
90
|
+
class OutputImage < HandlerStatement
|
|
131
91
|
include ClassLogging
|
|
92
|
+
include ImageName
|
|
93
|
+
include LocalConfiguration
|
|
94
|
+
include PerfStats
|
|
132
95
|
|
|
133
96
|
def self.match(node)
|
|
134
97
|
node.name == 'output_image'
|
|
@@ -144,6 +107,7 @@ module Configuration
|
|
|
144
107
|
def initialize(name, cache_control)
|
|
145
108
|
@name = name
|
|
146
109
|
@cache_control = cache_control
|
|
110
|
+
with_image_name(name)
|
|
147
111
|
end
|
|
148
112
|
|
|
149
113
|
def realize(request_state)
|
|
@@ -157,15 +121,20 @@ module Configuration
|
|
|
157
121
|
end
|
|
158
122
|
|
|
159
123
|
cache_control = @cache_control
|
|
124
|
+
_context = self
|
|
160
125
|
request_state.output do
|
|
161
|
-
|
|
162
|
-
|
|
126
|
+
_context.measure "sending response image data", "mime type: #{mime_type} (image #{image.data.bytesize} bytes)" do
|
|
127
|
+
res['Cache-Control'] = cache_control if cache_control
|
|
128
|
+
write 200, mime_type, image.data
|
|
129
|
+
end
|
|
163
130
|
end
|
|
164
131
|
end
|
|
165
132
|
end
|
|
166
133
|
Handler::register_node_parser OutputImage
|
|
167
134
|
|
|
168
135
|
class OutputDataURIImage < OutputImage
|
|
136
|
+
include PerfStats
|
|
137
|
+
|
|
169
138
|
def self.match(node)
|
|
170
139
|
node.name == 'output_data_uri_image'
|
|
171
140
|
end
|
|
@@ -175,9 +144,12 @@ module Configuration
|
|
|
175
144
|
fail "image '#{@name}' needs to be identified first to be used in data URI output" unless image.mime_type
|
|
176
145
|
|
|
177
146
|
cache_control = @cache_control
|
|
147
|
+
_context = self
|
|
178
148
|
request_state.output do
|
|
179
|
-
|
|
180
|
-
|
|
149
|
+
_context.measure "sending response image data with data URI encoding", "mime type: #{image.mime_type} (image #{image.data.bytesize} bytes)" do
|
|
150
|
+
res['Cache-Control'] = cache_control if cache_control
|
|
151
|
+
write 200, 'text/uri-list', "data:#{image.mime_type};base64,#{Base64.strict_encode64(image.data)}"
|
|
152
|
+
end
|
|
181
153
|
end
|
|
182
154
|
end
|
|
183
155
|
end
|
|
@@ -15,7 +15,7 @@ module Configuration
|
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
class
|
|
18
|
+
class NoValueForPathTemplatePlaceholderError < PathRenderingError
|
|
19
19
|
def initialize(path_name, template, placeholder)
|
|
20
20
|
super path_name, template, "no value for '\#{#{placeholder}}'"
|
|
21
21
|
end
|
|
@@ -56,7 +56,7 @@ module Configuration
|
|
|
56
56
|
locals[name]
|
|
57
57
|
rescue ConfigurationError => error
|
|
58
58
|
raise PathRenderingError.new(path_name, template, error.message)
|
|
59
|
-
end or raise
|
|
59
|
+
end or raise NoValueForPathTemplatePlaceholderError.new(path_name, template, name)
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
require 'mime/types'
|
|
2
|
+
require 'digest/sha2'
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Configuration
|
|
6
|
+
class RequestState < Hash
|
|
7
|
+
include ClassLogging
|
|
8
|
+
|
|
9
|
+
class Images < Hash
|
|
10
|
+
def initialize(memory_limit)
|
|
11
|
+
@memory_limit = memory_limit
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def []=(name, image)
|
|
16
|
+
if member?(name)
|
|
17
|
+
@memory_limit.return fetch(name).data.bytesize
|
|
18
|
+
end
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def [](name)
|
|
23
|
+
fetch(name){|image_name| raise ImageNotLoadedError.new(image_name)}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(body, matches, path, query_string, request_uri, request_headers, memory_limit, forward_headers)
|
|
28
|
+
super() do |request_state, name|
|
|
29
|
+
# note that request_state may be different object when useing with_locals that creates duplicate
|
|
30
|
+
request_state[name] = request_state.generate_meta_variable(name) or raise VariableNotDefinedError.new(name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# it is OK to overwrite path with a match
|
|
34
|
+
self[:path] = path
|
|
35
|
+
|
|
36
|
+
merge! matches
|
|
37
|
+
|
|
38
|
+
log.debug "processing request with body length: #{body.bytesize} bytes and variables: #{map{|k,v| "#{k}: '#{v}'"}.join(', ')}"
|
|
39
|
+
|
|
40
|
+
@body = body
|
|
41
|
+
@images = Images.new(memory_limit)
|
|
42
|
+
@query_string = query_string
|
|
43
|
+
@request_uri = request_uri
|
|
44
|
+
@request_headers = request_headers
|
|
45
|
+
@memory_limit = memory_limit
|
|
46
|
+
@output_callback = nil
|
|
47
|
+
|
|
48
|
+
@forward_headers = forward_headers
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
attr_reader :body
|
|
52
|
+
attr_reader :images
|
|
53
|
+
attr_reader :memory_limit
|
|
54
|
+
attr_reader :query_string
|
|
55
|
+
attr_reader :request_uri
|
|
56
|
+
attr_reader :request_headers
|
|
57
|
+
attr_reader :forward_headers
|
|
58
|
+
|
|
59
|
+
def with_locals(*locals)
|
|
60
|
+
locals = locals.reduce{|a, b| a.merge(b)}
|
|
61
|
+
log.debug "using additional local variables: #{locals}"
|
|
62
|
+
self.dup.merge!(locals)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def output(&callback)
|
|
66
|
+
@output_callback = callback
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def output_callback
|
|
70
|
+
@output_callback or fail 'no output callback'
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def fetch_base_variable(name, base_name)
|
|
74
|
+
fetch(base_name, nil) or generate_meta_variable(base_name) or raise NoVariableToGenerateMetaVariableError.new(base_name, name)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def generate_meta_variable(name)
|
|
78
|
+
val = case name
|
|
79
|
+
when :basename
|
|
80
|
+
path = Pathname.new(fetch_base_variable(name, :path))
|
|
81
|
+
path.basename(path.extname).to_s
|
|
82
|
+
when :dirname
|
|
83
|
+
Pathname.new(fetch_base_variable(name, :path)).dirname.to_s
|
|
84
|
+
when :extension
|
|
85
|
+
Pathname.new(fetch_base_variable(name, :path)).extname.delete('.')
|
|
86
|
+
when :digest # deprecated
|
|
87
|
+
@body.empty? and raise NoRequestBodyToGenerateMetaVariableError.new(name)
|
|
88
|
+
Digest::SHA2.new.update(@body).to_s[0,16]
|
|
89
|
+
when :input_digest
|
|
90
|
+
@body.empty? and raise NoRequestBodyToGenerateMetaVariableError.new(name)
|
|
91
|
+
Digest::SHA2.new.update(@body).to_s[0,16]
|
|
92
|
+
when :input_sha256
|
|
93
|
+
@body.empty? and raise NoRequestBodyToGenerateMetaVariableError.new(name)
|
|
94
|
+
Digest::SHA2.new.update(@body).to_s
|
|
95
|
+
when :input_image_width
|
|
96
|
+
@images['input'].width or raise NoImageDataForVariableError.new('input', name)
|
|
97
|
+
when :input_image_height
|
|
98
|
+
@images['input'].height or raise NoImageDataForVariableError.new('input', name)
|
|
99
|
+
when :input_image_mime_extension
|
|
100
|
+
@images['input'].mime_extension or raise NoImageDataForVariableError.new('input', name)
|
|
101
|
+
when :image_digest
|
|
102
|
+
Digest::SHA2.new.update(@images[fetch_base_variable(name, :image_name)].data).to_s[0,16]
|
|
103
|
+
when :image_sha256
|
|
104
|
+
Digest::SHA2.new.update(@images[fetch_base_variable(name, :image_name)].data).to_s
|
|
105
|
+
when :mimeextension # deprecated
|
|
106
|
+
image_name = fetch_base_variable(name, :image_name)
|
|
107
|
+
@images[image_name].mime_extension or raise NoImageDataForVariableError.new(image_name, name)
|
|
108
|
+
when :image_mime_extension
|
|
109
|
+
image_name = fetch_base_variable(name, :image_name)
|
|
110
|
+
@images[image_name].mime_extension or raise NoImageDataForVariableError.new(image_name, name)
|
|
111
|
+
when :image_width
|
|
112
|
+
image_name = fetch_base_variable(name, :image_name)
|
|
113
|
+
@images[image_name].width or raise NoImageDataForVariableError.new(image_name, name)
|
|
114
|
+
when :image_height
|
|
115
|
+
image_name = fetch_base_variable(name, :image_name)
|
|
116
|
+
@images[image_name].height or raise NoImageDataForVariableError.new(image_name, name)
|
|
117
|
+
when :uuid
|
|
118
|
+
SecureRandom.uuid
|
|
119
|
+
when :query_string_options
|
|
120
|
+
query_string.sort.map{|kv| kv.join(':')}.join(',')
|
|
121
|
+
end
|
|
122
|
+
if val
|
|
123
|
+
log.debug "generated meta variable '#{name}': #{val}"
|
|
124
|
+
else
|
|
125
|
+
log.debug "could not generated meta variable '#{name}'"
|
|
126
|
+
end
|
|
127
|
+
val
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|