httpimagestore 0.5.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/Gemfile +10 -12
  2. data/Gemfile.lock +57 -55
  3. data/README.md +829 -0
  4. data/VERSION +1 -1
  5. data/bin/httpimagestore +114 -180
  6. data/features/cache-control.feature +26 -90
  7. data/features/compatibility.feature +129 -0
  8. data/features/error-reporting.feature +207 -0
  9. data/features/health-check.feature +30 -0
  10. data/features/s3-store-and-thumbnail.feature +65 -0
  11. data/features/step_definitions/httpimagestore_steps.rb +66 -26
  12. data/features/support/env.rb +32 -5
  13. data/features/support/test.empty +0 -0
  14. data/httpimagestore.gemspec +60 -47
  15. data/lib/httpimagestore/aws_sdk_regions_hack.rb +23 -0
  16. data/lib/httpimagestore/configuration/file.rb +120 -0
  17. data/lib/httpimagestore/configuration/handler.rb +239 -0
  18. data/lib/httpimagestore/configuration/output.rb +119 -0
  19. data/lib/httpimagestore/configuration/path.rb +77 -0
  20. data/lib/httpimagestore/configuration/s3.rb +194 -0
  21. data/lib/httpimagestore/configuration/thumbnailer.rb +244 -0
  22. data/lib/httpimagestore/configuration.rb +126 -29
  23. data/lib/httpimagestore/error_reporter.rb +36 -0
  24. data/lib/httpimagestore/ruby_string_template.rb +26 -0
  25. data/load_test/load_test.1k.23a022f6e.m1.small-comp.csv +3 -0
  26. data/load_test/load_test.1k.ec9bde794.m1.small.csv +4 -0
  27. data/load_test/load_test.jmx +344 -0
  28. data/load_test/thumbnail_specs.csv +11 -0
  29. data/spec/configuration_file_spec.rb +309 -0
  30. data/spec/configuration_handler_spec.rb +124 -0
  31. data/spec/configuration_output_spec.rb +338 -0
  32. data/spec/configuration_path_spec.rb +92 -0
  33. data/spec/configuration_s3_spec.rb +571 -0
  34. data/spec/configuration_spec.rb +80 -105
  35. data/spec/configuration_thumbnailer_spec.rb +417 -0
  36. data/spec/ruby_string_template_spec.rb +43 -0
  37. data/spec/spec_helper.rb +61 -0
  38. data/spec/support/compute.jpg +0 -0
  39. data/spec/support/cuba_response_env.rb +40 -0
  40. data/spec/support/full.cfg +49 -0
  41. metadata +138 -84
  42. data/README.rdoc +0 -23
  43. data/features/httpimagestore.feature +0 -167
  44. data/lib/httpimagestore/image_path.rb +0 -54
  45. data/lib/httpimagestore/s3_service.rb +0 -37
  46. data/lib/httpimagestore/thumbnail_class.rb +0 -13
  47. data/spec/image_path_spec.rb +0 -72
  48. data/spec/test.cfg +0 -8
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 1.0.0
data/bin/httpimagestore CHANGED
@@ -1,200 +1,134 @@
1
1
  #!/usr/bin/env ruby
2
- require 'ip'
3
- require 'pathname'
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
- def digest(data)
106
- Digest::SHA2.new.update(data).to_s[0,16]
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
- sinatra.before do
111
- begin
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
- image = request.body.read
134
- image_hash = digest(image)
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
- unless image_path.empty?
137
- image_path = ImagePath::Custom.new(image_hash, image_path)
138
- else
139
- image_path = ImagePath::Auto.new(image_hash)
140
- end
101
+ class Configurator
102
+ def initialize(app, configuration)
103
+ @app = app
104
+ @configuration = configuration
105
+ end
141
106
 
142
- thumbnails = $thumbnailer.thumbnail(image) do
143
- thumbnail_classes.each do |thumbnail_class|
144
- thumbnail thumbnail_class.method, thumbnail_class.width, thumbnail_class.height, thumbnail_class.format, thumbnail_class.options
107
+ def call(env)
108
+ env['app.configuration'] = @configuration
109
+ @app.call(env)
110
+ end
145
111
  end
146
- end
147
112
 
148
- # check for errors
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
- urls = []
115
+ # connect Scope tree with Controler logger
116
+ Configuration::Scope.logger = Controler.logger_for(Configuration::Scope)
154
117
 
155
- # store all images
156
- urls << $s3.put_image(image_path.original_image(thumbnails.input_mime_type), thumbnails.input_mime_type, image)
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
- thumbnails.zip(thumbnail_classes).each do |thumbnail, thumbnail_class|
159
- urls << $s3.put_image(image_path.thumbnail_image(thumbnail.mime_type, thumbnail_class.name), thumbnail.mime_type, thumbnail.data)
126
+ HTTPImageStore.use Configurator, Configuration.from_file(settings.config)
127
+ HTTPImageStore
160
128
  end
161
129
 
162
- status 200
163
- headers "Content-Type" => "text/uri-list"
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: Stored objects should have proper Cache-Control header set
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 issthumbtest S3 bucket with key AKIAJMUYVYOSACNXLPTQ and secret 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
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
- s3_key 'AKIAJMUYVYOSACNXLPTQ', 'MAeGhvW+clN7kzK3NboASf3/kZ6a81PRtvwMZj4Y'
15
- s3_bucket 'issthumbtest'
8
+ s3 key="@AWS_ACCESS_KEY_ID@" secret="@AWS_SECRET_ACCESS_KEY@" ssl=false
16
9
 
17
- thumbnail_class 'small', 'crop', 128, 128
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
- @cache-control
34
- Scenario: Image files get Cache-Control header defined with argument
35
- Given httpimagestore argument --cache-control 'private, max-age=300, s-maxage=300'
36
- Given httpimagestore server is running at http://localhost:3000/ with the following configuration
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
- thumbnail_class 'small', 'crop', 128, 128
42
- """
43
- Given there is no test/image/4006450256177f4a/test.jpg file in S3 bucket
44
- And there is no test/image/4006450256177f4a/test-small.jpg file in S3 bucket
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
- And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg Cache-Control header will be private, max-age=300, s-maxage=300
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 undefined when set to ''
85
- Given httpimagestore argument --cache-control ''
86
- Given httpimagestore server is running at http://localhost:3000/ with the following configuration
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/small/test/image/test
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/uri-list
34
+ And response content type will be text/plain
99
35
  And response body will be CRLF ended lines
100
36
  """
101
- http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg
102
- http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg
37
+ OK
103
38
  """
104
- And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test.jpg Cache-Control header will not be set
105
- And http://issthumbtest.s3.amazonaws.com/test/image/4006450256177f4a/test-small.jpg Cache-Control header will not be set
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
+