httpimagestore 0.5.0 → 1.0.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.
- data/Gemfile +10 -12
- data/Gemfile.lock +57 -55
- data/README.md +829 -0
- data/VERSION +1 -1
- data/bin/httpimagestore +114 -180
- data/features/cache-control.feature +26 -90
- data/features/compatibility.feature +129 -0
- data/features/error-reporting.feature +207 -0
- data/features/health-check.feature +30 -0
- data/features/s3-store-and-thumbnail.feature +65 -0
- data/features/step_definitions/httpimagestore_steps.rb +66 -26
- data/features/support/env.rb +32 -5
- data/features/support/test.empty +0 -0
- data/httpimagestore.gemspec +60 -47
- data/lib/httpimagestore/aws_sdk_regions_hack.rb +23 -0
- data/lib/httpimagestore/configuration/file.rb +120 -0
- data/lib/httpimagestore/configuration/handler.rb +239 -0
- data/lib/httpimagestore/configuration/output.rb +119 -0
- data/lib/httpimagestore/configuration/path.rb +77 -0
- data/lib/httpimagestore/configuration/s3.rb +194 -0
- data/lib/httpimagestore/configuration/thumbnailer.rb +244 -0
- data/lib/httpimagestore/configuration.rb +126 -29
- data/lib/httpimagestore/error_reporter.rb +36 -0
- data/lib/httpimagestore/ruby_string_template.rb +26 -0
- data/load_test/load_test.1k.23a022f6e.m1.small-comp.csv +3 -0
- data/load_test/load_test.1k.ec9bde794.m1.small.csv +4 -0
- data/load_test/load_test.jmx +344 -0
- data/load_test/thumbnail_specs.csv +11 -0
- data/spec/configuration_file_spec.rb +309 -0
- data/spec/configuration_handler_spec.rb +124 -0
- data/spec/configuration_output_spec.rb +338 -0
- data/spec/configuration_path_spec.rb +92 -0
- data/spec/configuration_s3_spec.rb +571 -0
- data/spec/configuration_spec.rb +80 -105
- data/spec/configuration_thumbnailer_spec.rb +417 -0
- data/spec/ruby_string_template_spec.rb +43 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/support/compute.jpg +0 -0
- data/spec/support/cuba_response_env.rb +40 -0
- data/spec/support/full.cfg +49 -0
- metadata +138 -84
- data/README.rdoc +0 -23
- data/features/httpimagestore.feature +0 -167
- data/lib/httpimagestore/image_path.rb +0 -54
- data/lib/httpimagestore/s3_service.rb +0 -37
- data/lib/httpimagestore/thumbnail_class.rb +0 -13
- data/spec/image_path_spec.rb +0 -72
- data/spec/test.cfg +0 -8
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/bin/httpimagestore
CHANGED
@@ -1,200 +1,134 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require 'cli'
|
5
|
-
|
6
|
-
cli_settings = CLI.new do
|
7
|
-
description 'HTTP based image storage and thumbnailer'
|
8
|
-
|
9
|
-
switch :no_bind,
|
10
|
-
description: "Do not bind to TCP socket - useful with -s fastcgi option"
|
11
|
-
switch :no_logging,
|
12
|
-
description: "Disable logging"
|
13
|
-
switch :debug,
|
14
|
-
description: "Enable debugging"
|
15
|
-
switch :no_uri_encode,
|
16
|
-
description: "Disable output URI encoding"
|
17
|
-
|
18
|
-
option :bind,
|
19
|
-
short: :b,
|
20
|
-
default: IP.new('127.0.0.1'),
|
21
|
-
cast: IP,
|
22
|
-
description: "HTTP server bind address - use 0.0.0.0 to bind to all interfaces"
|
23
|
-
option :port,
|
24
|
-
short: :p,
|
25
|
-
default: 3000,
|
26
|
-
cast: Integer,
|
27
|
-
description: "HTTP server TCP port"
|
28
|
-
option :server,
|
29
|
-
short: :s,
|
30
|
-
default: 'mongrel',
|
31
|
-
description: "Rack server handler like thin, mongrel, webrick, fastcgi etc."
|
32
|
-
option :upload_retry_times,
|
33
|
-
default: 3,
|
34
|
-
cast: Integer,
|
35
|
-
description: "Number of times file upload will be retried in case of an error"
|
36
|
-
option :upload_retry_delay,
|
37
|
-
default: 0.0,
|
38
|
-
cast: Float,
|
39
|
-
description: "Time to sleep between retries"
|
40
|
-
options :cache_control,
|
41
|
-
default: "public, max-age=#{(86400 * 365.25).to_i}",
|
42
|
-
description: "Stored object Cache-Control header value - can be used multiple times to specify flag by flag"
|
43
|
-
|
44
|
-
argument :config,
|
45
|
-
cast: Pathname,
|
46
|
-
description: "Configuration file path"
|
47
|
-
end.parse!
|
48
|
-
|
49
|
-
require 'sinatra/base'
|
50
|
-
require 'httpthumbnailer-client'
|
51
|
-
require 'digest/sha2'
|
2
|
+
require 'unicorn-cuba-base'
|
3
|
+
require 'base64'
|
52
4
|
|
53
5
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
54
|
-
require 'httpimagestore/configuration'
|
55
|
-
require 'httpimagestore/image_path'
|
56
|
-
require 'httpimagestore/s3_service'
|
57
|
-
|
58
|
-
sinatra = Sinatra.new
|
59
|
-
|
60
|
-
unless cli_settings.no_bind
|
61
|
-
sinatra.set :port, cli_settings.port
|
62
|
-
sinatra.set :bind, cli_settings.bind.to_s
|
63
|
-
else
|
64
|
-
sinatra.set :port, nil
|
65
|
-
sinatra.set :bind, nil
|
66
|
-
end
|
67
|
-
sinatra.set :environment, 'production'
|
68
|
-
sinatra.set :server, cli_settings.server
|
69
|
-
sinatra.set :lock, true
|
70
|
-
sinatra.set :logging, (not cli_settings.no_logging)
|
71
|
-
sinatra.set :debug, cli_settings.debug
|
72
|
-
sinatra.set :uri_encode, (not cli_settings.no_uri_encode)
|
73
|
-
sinatra.set :upload_retry_times, cli_settings.upload_retry_times
|
74
|
-
sinatra.set :upload_retry_delay, cli_settings.upload_retry_delay
|
75
|
-
sinatra.set :cache_control, cli_settings.cache_control
|
76
|
-
|
77
|
-
Configuration.from_file(cli_settings.config).put(sinatra)
|
78
|
-
|
79
|
-
class ThumbnailingError < RuntimeError
|
80
|
-
def initialize(thumbnail_class, remote_error)
|
81
|
-
@remote_error = remote_error
|
82
|
-
super "Thumbnailing for class '#{thumbnail_class.name}' failed: #{remote_error.message}"
|
83
|
-
end
|
84
|
-
|
85
|
-
attr_reader :remote_error
|
86
|
-
end
|
87
|
-
|
88
|
-
sinatra.helpers do
|
89
|
-
def hatl_response(status, msg)
|
90
|
-
headers "Content-Type" => "text/plain"
|
91
|
-
body msg.gsub("\n", "\r\n") + "\r\n"
|
92
|
-
halt status
|
93
|
-
end
|
94
|
-
|
95
|
-
def halt_exception(status, exception)
|
96
|
-
msg = "Error: #{exception.class.name}: #{exception}"
|
97
|
-
if status >= 500
|
98
|
-
logger.error msg + (exception.respond_to?(:response) ? ': ' + exception.response.inspect : '')
|
99
|
-
else
|
100
|
-
logger.warn msg
|
101
|
-
end
|
102
|
-
hatl_response(status, msg)
|
103
|
-
end
|
104
6
|
|
105
|
-
|
106
|
-
|
7
|
+
Application.new('httpimagestore', port: 3000, processor_count_factor: 2) do
|
8
|
+
cli do
|
9
|
+
description 'HTTP based image storage and thumbnailer'
|
10
|
+
argument :config,
|
11
|
+
cast: Pathname,
|
12
|
+
description: 'configuration file path'
|
13
|
+
version (Pathname.new(__FILE__).dirname + '..' + 'VERSION').read
|
107
14
|
end
|
108
|
-
end
|
109
15
|
|
110
|
-
|
111
|
-
|
112
|
-
logger.level = Logger::DEBUG if settings.logging and settings.debug
|
113
|
-
# singletons
|
114
|
-
$s3 ||= S3Service.new(settings.s3_key_id, settings.s3_key_secret, settings.s3_bucket,
|
115
|
-
cache_control: settings.cache_control,
|
116
|
-
logger: logger,
|
117
|
-
upload_retry_times: settings.upload_retry_times,
|
118
|
-
upload_retry_delay: settings.upload_retry_delay
|
119
|
-
)
|
120
|
-
$thumbnailer ||= HTTPThumbnailerClient.new(settings.thumbnailer_url)
|
121
|
-
rescue => exception
|
122
|
-
halt_exception(500, exception)
|
16
|
+
settings do |settings|
|
17
|
+
Controler.settings[:config_file] = settings.config
|
123
18
|
end
|
124
|
-
end
|
125
|
-
|
126
|
-
sinatra.get '/' do
|
127
|
-
"hello"
|
128
|
-
end
|
129
|
-
|
130
|
-
sinatra.put %r{/thumbnail/([^/]*)/?(.*)} do |thumbnail_classes, image_path|
|
131
|
-
thumbnail_classes = thumbnail_classes.split(',').map{|thumbnail_class| settings.thumbnail_classes[thumbnail_class]}
|
132
19
|
|
133
|
-
|
134
|
-
|
20
|
+
main do |settings|
|
21
|
+
require 'httpimagestore/error_reporter'
|
22
|
+
|
23
|
+
class HTTPImageStore < Controler
|
24
|
+
extend Stats
|
25
|
+
def_stats(
|
26
|
+
:workers,
|
27
|
+
:total_requests,
|
28
|
+
:total_errors
|
29
|
+
)
|
30
|
+
|
31
|
+
raindrops_stats = Raindrops::Middleware::Stats.new
|
32
|
+
self.use Raindrops::Middleware, stats: raindrops_stats
|
33
|
+
|
34
|
+
StatsReporter << HTTPImageStore.stats
|
35
|
+
StatsReporter << raindrops_stats
|
36
|
+
StatsReporter << Plugin::ResponseHelpers.stats
|
37
|
+
|
38
|
+
self.define do
|
39
|
+
HTTPImageStore.stats.incr_total_requests
|
40
|
+
on error? do
|
41
|
+
HTTPImageStore.stats.incr_total_errors
|
42
|
+
run ErrorReporter
|
43
|
+
end
|
44
|
+
|
45
|
+
on 'stats' do
|
46
|
+
run StatsReporter
|
47
|
+
end
|
48
|
+
|
49
|
+
on 'health_check' do
|
50
|
+
log.debug "health_check"
|
51
|
+
if client = env['app.configuration'].thumbnailer
|
52
|
+
# 8x8 PNG
|
53
|
+
data = Base64.decode64('iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAI0lEQVQI1z3KMQoAMAyAwEv//2c7pFQQHISqssXaQWby+NsFYkkV7w+CVgAAAAAASUVORK5CYII=')
|
54
|
+
|
55
|
+
begin
|
56
|
+
thumbnail = client.thumbnail(data, 'fit', 4, 4, 'jpeg')
|
57
|
+
unless thumbnail.data.length > 300 and thumbnail.data.include? 'JFIF'
|
58
|
+
write_plain 502, 'bad image data returned from thumbnailer'
|
59
|
+
halt res.finish
|
60
|
+
end
|
61
|
+
rescue Errno::ECONNREFUSED => error
|
62
|
+
write_error 502, error
|
63
|
+
halt res.finish
|
64
|
+
end
|
65
|
+
end
|
66
|
+
write_plain 200, 'HTTP Image Store OK'
|
67
|
+
end
|
68
|
+
|
69
|
+
env['app.configuration'].handlers.each do |handler|
|
70
|
+
on eval(handler.http_method), *handler.uri_matchers.map{|m| instance_eval(&m.matcher)} do |*args|
|
71
|
+
locals = {}
|
72
|
+
|
73
|
+
{
|
74
|
+
path: env["PATH_INFO"][1..-1] || ''
|
75
|
+
}.merge(
|
76
|
+
Hash[handler.uri_matchers.select{|m| m.name}.map{|m| m.name}.zip(args)]
|
77
|
+
).each do |name, value|
|
78
|
+
locals[name] = URI.decode(value).force_encoding('UTF-8')
|
79
|
+
end
|
80
|
+
|
81
|
+
state = Configuration::RequestState.new(req.body.read, locals, memory_limit)
|
82
|
+
|
83
|
+
handler.image_sources.each do |image_source|
|
84
|
+
image_source.realize(state) unless image_source.respond_to? :excluded? and image_source.excluded?(state)
|
85
|
+
end
|
86
|
+
handler.stores.each do |store|
|
87
|
+
store.realize(state) unless store.respond_to? :excluded? and store.excluded?(state)
|
88
|
+
end
|
89
|
+
handler.output.realize(state)
|
90
|
+
|
91
|
+
instance_eval &state.output_callback
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
on root do
|
96
|
+
write_plain 200, 'HTTP Image Store'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
135
100
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
101
|
+
class Configurator
|
102
|
+
def initialize(app, configuration)
|
103
|
+
@app = app
|
104
|
+
@configuration = configuration
|
105
|
+
end
|
141
106
|
|
142
|
-
|
143
|
-
|
144
|
-
|
107
|
+
def call(env)
|
108
|
+
env['app.configuration'] = @configuration
|
109
|
+
@app.call(env)
|
110
|
+
end
|
145
111
|
end
|
146
|
-
end
|
147
112
|
|
148
|
-
|
149
|
-
thumbnails.zip(thumbnail_classes).each do |thumbnail, thumbnail_class|
|
150
|
-
raise ThumbnailingError.new(thumbnail_class, thumbnail) if thumbnail.kind_of? HTTPThumbnailerClient::ThumbnailingError
|
151
|
-
end
|
113
|
+
require 'httpimagestore/configuration'
|
152
114
|
|
153
|
-
|
115
|
+
# connect Scope tree with Controler logger
|
116
|
+
Configuration::Scope.logger = Controler.logger_for(Configuration::Scope)
|
154
117
|
|
155
|
-
|
156
|
-
|
118
|
+
# load builin supported set
|
119
|
+
require 'httpimagestore/configuration/path'
|
120
|
+
require 'httpimagestore/configuration/handler'
|
121
|
+
require 'httpimagestore/configuration/thumbnailer'
|
122
|
+
require 'httpimagestore/configuration/file'
|
123
|
+
require 'httpimagestore/configuration/output'
|
124
|
+
require 'httpimagestore/configuration/s3'
|
157
125
|
|
158
|
-
|
159
|
-
|
126
|
+
HTTPImageStore.use Configurator, Configuration.from_file(settings.config)
|
127
|
+
HTTPImageStore
|
160
128
|
end
|
161
129
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
body urls.map{|u| settings.uri_encode ? URI.encode(u) : u}.map{|u| u + "\r\n"}.join
|
166
|
-
end
|
167
|
-
|
168
|
-
sinatra.error HTTPThumbnailerClient::UnsupportedMediaTypeError do
|
169
|
-
halt_exception(415, env['sinatra.error'])
|
170
|
-
end
|
171
|
-
|
172
|
-
sinatra.error HTTPThumbnailerClient::ImageTooLargeError do
|
173
|
-
halt_exception(413, env['sinatra.error'])
|
174
|
-
end
|
175
|
-
|
176
|
-
# problem with singe thumbnail
|
177
|
-
sinatra.error ThumbnailingError do
|
178
|
-
exception = env['sinatra.error']
|
179
|
-
case exception.remote_error.type
|
180
|
-
when "Thumbnailer::ImageTooLargeError"
|
181
|
-
halt_exception(413, exception)
|
182
|
-
else
|
183
|
-
halt_exception(500, exception)
|
130
|
+
after_fork do |server, worker|
|
131
|
+
HTTPImageStore.stats.incr_workers
|
184
132
|
end
|
185
133
|
end
|
186
134
|
|
187
|
-
sinatra.error Configuration::ThumbnailClassDoesNotExistError do
|
188
|
-
halt_exception(404, env['sinatra.error'])
|
189
|
-
end
|
190
|
-
|
191
|
-
sinatra.error Sinatra::NotFound do
|
192
|
-
hatl_response(404, "Resource '#{request.path_info}' not found")
|
193
|
-
end
|
194
|
-
|
195
|
-
sinatra.error do
|
196
|
-
halt_exception(500, env['sinatra.error'])
|
197
|
-
end
|
198
|
-
|
199
|
-
sinatra.run!
|
200
|
-
|
@@ -1,106 +1,42 @@
|
|
1
|
-
Feature:
|
1
|
+
Feature: S3 object Cache-Control header settings
|
2
|
+
S3 objects can have Cache-Control header set during storage.
|
2
3
|
|
3
4
|
Background:
|
4
|
-
Given
|
5
|
-
Given httpimagestore log is empty
|
6
|
-
Given httpthumbnailer log is empty
|
7
|
-
Given httpthumbnailer server is running at http://localhost:3100/
|
8
|
-
Given Content-Type header set to image/autodetect
|
9
|
-
|
10
|
-
@cache-control
|
11
|
-
Scenario: Image files get Cache-Control header with max-age set by default
|
5
|
+
Given S3 settings in AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_S3_TEST_BUCKET environment variables
|
12
6
|
Given httpimagestore server is running at http://localhost:3000/ with the following configuration
|
13
7
|
"""
|
14
|
-
|
15
|
-
s3_bucket 'issthumbtest'
|
8
|
+
s3 key="@AWS_ACCESS_KEY_ID@" secret="@AWS_SECRET_ACCESS_KEY@" ssl=false
|
16
9
|
|
17
|
-
|
18
|
-
"""
|
19
|
-
Given there is no test/image/4006450256177f4a/test.jpg file in S3 bucket
|
20
|
-
And there is no test/image/4006450256177f4a/test-small.jpg file in S3 bucket
|
21
|
-
Given test.jpg file content as request body
|
22
|
-
When I do PUT request http://localhost:3000/thumbnail/small/test/image/test
|
23
|
-
Then response status will be 200
|
24
|
-
And response content type will be text/uri-list
|
25
|
-
And response body will be CRLF ended lines
|
26
|
-
"""
|
27
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg
|
28
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg
|
29
|
-
"""
|
30
|
-
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg Cache-Control header will be public, max-age=31557600
|
31
|
-
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg Cache-Control header will be public, max-age=31557600
|
10
|
+
path "hash" "#{digest}.#{mimeextension}"
|
11
|
+
path "hash-name" "#{digest}-#{imagename}.#{mimeextension}"
|
32
12
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
s3_key 'AKIAJMUYVYOSACNXLPTQ', 'MAeGhvW+clN7kzK3NboASf3/kZ6a81PRtvwMZj4Y'
|
39
|
-
s3_bucket 'issthumbtest'
|
13
|
+
put "thumbnail" {
|
14
|
+
thumbnail "input" {
|
15
|
+
"no-cache" operation="crop" width=128 height=128 format="jpeg"
|
16
|
+
"cache" operation="crop" width=256 height=256 format="jpeg"
|
17
|
+
}
|
40
18
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
Given test.jpg file content as request body
|
46
|
-
When I do PUT request http://localhost:3000/thumbnail/small/test/image/test
|
47
|
-
Then response status will be 200
|
48
|
-
And response content type will be text/uri-list
|
49
|
-
And response body will be CRLF ended lines
|
50
|
-
"""
|
51
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg
|
52
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg
|
19
|
+
store_s3 "input" bucket="@AWS_S3_TEST_BUCKET@" public=true path="hash"
|
20
|
+
store_s3 "no-cache" bucket="@AWS_S3_TEST_BUCKET@" public=true path="hash-name"
|
21
|
+
store_s3 "cache" bucket="@AWS_S3_TEST_BUCKET@" public=true path="hash-name" cache-control="public, max-age=31557600, s-maxage=0"
|
22
|
+
}
|
53
23
|
"""
|
54
|
-
|
55
|
-
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg Cache-Control header will be private, max-age=300, s-maxage=300
|
56
|
-
|
57
|
-
@cache-control
|
58
|
-
Scenario: Image files get Cache-Control header defined with argument
|
59
|
-
Given httpimagestore argument --cache-control 'private'
|
60
|
-
Given httpimagestore argument --cache-control 'max-age=300'
|
61
|
-
Given httpimagestore argument --cache-control 's-maxage=300'
|
62
|
-
Given httpimagestore server is running at http://localhost:3000/ with the following configuration
|
63
|
-
"""
|
64
|
-
s3_key 'AKIAJMUYVYOSACNXLPTQ', 'MAeGhvW+clN7kzK3NboASf3/kZ6a81PRtvwMZj4Y'
|
65
|
-
s3_bucket 'issthumbtest'
|
66
|
-
|
67
|
-
thumbnail_class 'small', 'crop', 128, 128
|
68
|
-
"""
|
69
|
-
Given there is no test/image/4006450256177f4a/test.jpg file in S3 bucket
|
70
|
-
And there is no test/image/4006450256177f4a/test-small.jpg file in S3 bucket
|
71
|
-
Given test.jpg file content as request body
|
72
|
-
When I do PUT request http://localhost:3000/thumbnail/small/test/image/test
|
73
|
-
Then response status will be 200
|
74
|
-
And response content type will be text/uri-list
|
75
|
-
And response body will be CRLF ended lines
|
76
|
-
"""
|
77
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg
|
78
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg
|
79
|
-
"""
|
80
|
-
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg Cache-Control header will be private, max-age=300, s-maxage=300
|
81
|
-
And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg Cache-Control header will be private, max-age=300, s-maxage=300
|
24
|
+
Given httpthumbnailer server is running at http://localhost:3100/
|
82
25
|
|
83
26
|
@cache-control
|
84
|
-
Scenario: Image files get Cache-Control header
|
85
|
-
Given
|
86
|
-
|
87
|
-
|
88
|
-
s3_key 'AKIAJMUYVYOSACNXLPTQ', 'MAeGhvW+clN7kzK3NboASf3/kZ6a81PRtvwMZj4Y'
|
89
|
-
s3_bucket 'issthumbtest'
|
90
|
-
|
91
|
-
thumbnail_class 'small', 'crop', 128, 128
|
92
|
-
"""
|
93
|
-
Given there is no test/image/4006450256177f4a/test.jpg file in S3 bucket
|
94
|
-
And there is no test/image/4006450256177f4a/test-small.jpg file in S3 bucket
|
27
|
+
Scenario: Image files get don't get Cache-Control header by default
|
28
|
+
Given there is no 4006450256177f4a.jpg file in S3 bucket
|
29
|
+
And there is no 4006450256177f4a-no-cache.jpg file in S3 bucket
|
30
|
+
And there is no 4006450256177f4a-cache.jpg file in S3 bucket
|
95
31
|
Given test.jpg file content as request body
|
96
|
-
When I do PUT request http://localhost:3000/thumbnail
|
32
|
+
When I do PUT request http://localhost:3000/thumbnail
|
97
33
|
Then response status will be 200
|
98
|
-
And response content type will be text/
|
34
|
+
And response content type will be text/plain
|
99
35
|
And response body will be CRLF ended lines
|
100
36
|
"""
|
101
|
-
|
102
|
-
http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg
|
37
|
+
OK
|
103
38
|
"""
|
104
|
-
And http
|
105
|
-
And http
|
39
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a.jpg Cache-Control header will not be set
|
40
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a-no-cache.jpg Cache-Control header will not be set
|
41
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a-cache.jpg Cache-Control header will be public, max-age=31557600, s-maxage=0
|
106
42
|
|
@@ -0,0 +1,129 @@
|
|
1
|
+
Feature: Image list based thumbnailing and S3 storage
|
2
|
+
Storage based on URL specified image names to be generated and stored using two different path formats.
|
3
|
+
This configuration should be mostly compatible with pre v1.0 release.
|
4
|
+
|
5
|
+
Background:
|
6
|
+
Given S3 settings in AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_S3_TEST_BUCKET environment variables
|
7
|
+
Given httpimagestore server is running at http://localhost:3000/ with the following configuration
|
8
|
+
"""
|
9
|
+
s3 key="@AWS_ACCESS_KEY_ID@" secret="@AWS_SECRET_ACCESS_KEY@" ssl=false
|
10
|
+
|
11
|
+
path "hash" "#{digest}.#{mimeextension}"
|
12
|
+
path "hash-name" "#{digest}/#{imagename}.#{mimeextension}"
|
13
|
+
path "structured" "#{dirname}/#{digest}/#{basename}.#{mimeextension}"
|
14
|
+
path "structured-name" "#{dirname}/#{digest}/#{basename}-#{imagename}.#{mimeextension}"
|
15
|
+
|
16
|
+
put "thumbnail" ":name_list" ":path/.+/" {
|
17
|
+
thumbnail "input" {
|
18
|
+
"small" operation="crop" width=128 height=128 format="jpeg" if-image-name-on="#{name_list}"
|
19
|
+
"tiny_png" operation="crop" width=32 height=32 format="png" if-image-name-on="#{name_list}"
|
20
|
+
"bad" operation="crop" width=0 height=0 if-image-name-on="#{name_list}"
|
21
|
+
}
|
22
|
+
|
23
|
+
store_s3 "input" bucket="@AWS_S3_TEST_BUCKET@" path="structured" public=true
|
24
|
+
store_s3 "small" bucket="@AWS_S3_TEST_BUCKET@" path="structured-name" public=true if-image-name-on="#{name_list}"
|
25
|
+
store_s3 "tiny_png" bucket="@AWS_S3_TEST_BUCKET@" path="structured-name" public=true if-image-name-on="#{name_list}"
|
26
|
+
|
27
|
+
output_store_url {
|
28
|
+
"input"
|
29
|
+
"small" if-image-name-on="#{name_list}"
|
30
|
+
"tiny_png" if-image-name-on="#{name_list}"
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
put "thumbnail" ":name_list" {
|
35
|
+
thumbnail "input" {
|
36
|
+
"small" operation="crop" width=128 height=128 format="jpeg" if-image-name-on="#{name_list}"
|
37
|
+
"tiny_png" operation="crop" width=32 height=32 format="png" if-image-name-on="#{name_list}"
|
38
|
+
}
|
39
|
+
|
40
|
+
store_s3 "input" bucket="@AWS_S3_TEST_BUCKET@" path="hash" public=true
|
41
|
+
store_s3 "small" bucket="@AWS_S3_TEST_BUCKET@" path="hash-name" public=true if-image-name-on="#{name_list}"
|
42
|
+
store_s3 "tiny_png" bucket="@AWS_S3_TEST_BUCKET@" path="hash-name" public=true if-image-name-on="#{name_list}"
|
43
|
+
|
44
|
+
output_store_url {
|
45
|
+
"input"
|
46
|
+
"small" if-image-name-on="#{name_list}"
|
47
|
+
"tiny_png" if-image-name-on="#{name_list}"
|
48
|
+
"bad" if-image-name-on="#{name_list}"
|
49
|
+
}
|
50
|
+
}
|
51
|
+
"""
|
52
|
+
Given httpthumbnailer server is running at http://localhost:3100/
|
53
|
+
|
54
|
+
@compatibility @test
|
55
|
+
Scenario: Putting original and its thumbnails to S3 bucket
|
56
|
+
Given there is no 4006450256177f4a.jpg file in S3 bucket
|
57
|
+
And there is no 4006450256177f4a/small.jpg file in S3 bucket
|
58
|
+
And there is no 4006450256177f4a/tiny_png.png file in S3 bucket
|
59
|
+
Given test.jpg file content as request body
|
60
|
+
When I do PUT request http://localhost:3000/thumbnail/small,tiny_png
|
61
|
+
Then response status will be 200
|
62
|
+
And response content type will be text/uri-list
|
63
|
+
And response body will be CRLF ended lines
|
64
|
+
"""
|
65
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a.jpg
|
66
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a/small.jpg
|
67
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a/tiny_png.png
|
68
|
+
"""
|
69
|
+
Then http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a.jpg will contain JPEG image of size 509x719
|
70
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a.jpg content type will be image/jpeg
|
71
|
+
Then http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a/small.jpg will contain JPEG image of size 128x128
|
72
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a/small.jpg content type will be image/jpeg
|
73
|
+
Then http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a/tiny_png.png will contain PNG image of size 32x32
|
74
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/4006450256177f4a/tiny_png.png content type will be image/png
|
75
|
+
|
76
|
+
@compatibility
|
77
|
+
Scenario: Putting original and its thumbnails to S3 bucket under custom path
|
78
|
+
Given there is no test/image/4006450256177f4a/test.jpg file in S3 bucket
|
79
|
+
And there is no test/image/4006450256177f4a/test-small.jpg file in S3 bucket
|
80
|
+
And there is no test/image/4006450256177f4a/test-tiny_png.png file in S3 bucket
|
81
|
+
Given test.jpg file content as request body
|
82
|
+
When I do PUT request http://localhost:3000/thumbnail/small,tiny_png/test/image/test
|
83
|
+
Then response status will be 200
|
84
|
+
And response content type will be text/uri-list
|
85
|
+
And response body will be CRLF ended lines
|
86
|
+
"""
|
87
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg
|
88
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg
|
89
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png
|
90
|
+
"""
|
91
|
+
Then http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg will contain JPEG image of size 509x719
|
92
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg content type will be image/jpeg
|
93
|
+
Then http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg will contain JPEG image of size 128x128
|
94
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg content type will be image/jpeg
|
95
|
+
Then http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png will contain PNG image of size 32x32
|
96
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png content type will be image/png
|
97
|
+
|
98
|
+
@compatibility
|
99
|
+
Scenario: Input file extension should be based on content detected mime type and not on provided path
|
100
|
+
Given there is no test/image/4006450256177f4a/test.jpg file in S3 bucket
|
101
|
+
And there is no test/image/4006450256177f4a/test-tiny_png.jpg file in S3 bucket
|
102
|
+
Given test.jpg file content as request body
|
103
|
+
When I do PUT request http://localhost:3000/thumbnail/tiny_png/test/image/test.gif
|
104
|
+
Then response status will be 200
|
105
|
+
And response content type will be text/uri-list
|
106
|
+
And response body will be CRLF ended lines
|
107
|
+
"""
|
108
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg
|
109
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png
|
110
|
+
"""
|
111
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg content type will be image/jpeg
|
112
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/image/4006450256177f4a/test-tiny_png.png content type will be image/png
|
113
|
+
|
114
|
+
@compatibility
|
115
|
+
Scenario: Custom path name encoding when UTF-8 characters can be used
|
116
|
+
Given there is no test/图像/4006450256177f4a/测试.jpg file in S3 bucket
|
117
|
+
And there is no test/图像/4006450256177f4a/测试-small.jpg file in S3 bucket
|
118
|
+
Given test.jpg file content as request body
|
119
|
+
When I do PUT request http://localhost:3000/thumbnail/small/test/图像/测试
|
120
|
+
Then response status will be 200
|
121
|
+
And response content type will be text/uri-list
|
122
|
+
And response body will be CRLF ended lines
|
123
|
+
"""
|
124
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/%E5%9B%BE%E5%83%8F/4006450256177f4a/%E6%B5%8B%E8%AF%95.jpg
|
125
|
+
http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/%E5%9B%BE%E5%83%8F/4006450256177f4a/%E6%B5%8B%E8%AF%95-small.jpg
|
126
|
+
"""
|
127
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/图像/4006450256177f4a/测试.jpg will contain JPEG image of size 509x719
|
128
|
+
And http://@AWS_S3_TEST_BUCKET@.s3.amazonaws.com/test/图像/4006450256177f4a/测试-small.jpg will contain JPEG image of size 128x128
|
129
|
+
|