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.
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
+