httpthumbnailer 0.0.5 → 0.0.6

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.5
1
+ 0.0.6
data/bin/httpthumbnailer CHANGED
@@ -18,11 +18,14 @@ sinatra.set :lock, true
18
18
  sinatra.set :boundary, "thumnail image data"
19
19
  sinatra.set :logging, true
20
20
  sinatra.set :debug, true
21
+ sinatra.set :limit_memory, 128*10**6 # B (keep in mem)
22
+ sinatra.set :limit_map, 256*10**6 # B (if not fit in mem, map to disk)
23
+ sinatra.set :limit_disk, 0 # B (if not fit in map use standard IO)
21
24
 
22
25
  sinatra.before do
23
26
  logger.level = Logger::DEBUG if settings.debug == true
24
27
  if $thumbnailer.nil?
25
- $thumbnailer = Thumbnailer.new(:logger => logger)
28
+ $thumbnailer = Thumbnailer.new(:logger => logger, :limit_memory => settings.limit_memory, :limit_map => settings.limit_map, :limit_disk => settings.limit_disk)
26
29
 
27
30
  $thumbnailer.method('crop') do |image, spec|
28
31
  image.resize_to_fill!(spec.width, spec.height)
@@ -100,6 +103,11 @@ sinatra.error Thumbnailer::UnsupportedMediaTypeError do
100
103
  halt 415
101
104
  end
102
105
 
106
+ sinatra.error Thumbnailer::ImageTooLargeError do
107
+ plain_exception(env['sinatra.error'])
108
+ halt 413
109
+ end
110
+
103
111
  sinatra.error 404 do
104
112
  plain_response("Resource '#{request.path_info}' not found")
105
113
  end
@@ -141,3 +141,30 @@ Feature: Generating set of thumbnails with single PUT request
141
141
  And third part mime type will be image/jpeg
142
142
  And there will be no leaked images
143
143
 
144
+ Scenario: Memory limits exhausted while loading
145
+ Given test-large.jpg file content as request body
146
+ When I do PUT request http://localhost:3100/thumbnail/crop,16,16,PNG
147
+ Then response status will be 413
148
+ And response content type will be text/plain
149
+ And response body will be CRLF endend lines like
150
+ """
151
+ Error: Thumbnailer::ImageTooLargeError: Magick::ImageMagickError: cache resources exhausted
152
+ """
153
+ And there will be no leaked images
154
+
155
+ Scenario: Memory limits exhausted while thumbnailing
156
+ Given test.jpg file content as request body
157
+ When I do PUT request http://localhost:3100/thumbnail/crop,16,16,PNG/crop,16000,16000,JPG/crop,16,32,JPEG
158
+ Then response status will be 200
159
+ And I will get multipart response
160
+ Then first part will contain PNG image of size 16x16
161
+ And first part mime type will be image/png
162
+ And second part content type will be text/plain
163
+ And second part body will be CRLF endend lines like
164
+ """
165
+ Error: Magick::ImageMagickError: cache resources exhausted
166
+ """
167
+ Then third part will contain JPEG image of size 16x32
168
+ And third part mime type will be image/jpeg
169
+ And there will be no leaked images
170
+
@@ -55,6 +55,14 @@ Then /(.*) part body will be CRLF endend lines$/ do |part, body|
55
55
  @response_multipart.part[part_no(part)].body.should == body.gsub("\n", "\r\n") + "\r\n"
56
56
  end
57
57
 
58
+ Then /(.*) part body will be CRLF endend lines like$/ do |part, body|
59
+ pbody = @response_multipart.part[part_no(part)].body
60
+ pbody.should match(body)
61
+ pbody.each do |line|
62
+ line[-2,2].should == "\r\n"
63
+ end
64
+ end
65
+
58
66
  Then /(.*) part will contain (.*) image of size (.*)x(.*)/ do |part, format, width, height|
59
67
  mime = @response_multipart.part[part_no(part)].header['Content-Type']
60
68
  data = @response_multipart.part[part_no(part)].body
@@ -77,7 +85,6 @@ And /that image pixel at (.*)x(.*) will be of color (.*)/ do |x, y, color|
77
85
  @image.pixel_color(x.to_i, y.to_i).to_color.sub(/^#/, '0x').should == color
78
86
  end
79
87
 
80
-
81
88
  And /there will be no leaked images/ do
82
89
  HTTPClient.new.get_content("http://localhost:3100/stats/images").to_i.should == 0
83
90
  end
Binary file
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "httpthumbnailer"
8
- s.version = "0.0.5"
8
+ s.version = "0.0.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jakub Pastuszek"]
12
- s.date = "2011-11-29"
12
+ s.date = "2011-11-30"
13
13
  s.description = "Provides HTTP API for thumbnailing images"
14
14
  s.email = "jpastuszek@gmail.com"
15
15
  s.executables = ["httpthumbnailer"]
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
30
30
  "features/httpthumbnailer.feature",
31
31
  "features/step_definitions/httpthumbnailer_steps.rb",
32
32
  "features/support/env.rb",
33
+ "features/support/test-large.jpg",
33
34
  "features/support/test-transparent.png",
34
35
  "features/support/test.jpg",
35
36
  "features/support/test.txt",
@@ -50,6 +50,12 @@ class Thumbnailer
50
50
  end
51
51
  end
52
52
 
53
+ class ImageTooLargeError < ArgumentError
54
+ def initialize(e)
55
+ super("#{e.class.name}: #{e}")
56
+ end
57
+ end
58
+
53
59
  class ImageHandler
54
60
  class ImageDestroyedError < RuntimeError
55
61
  def initialize
@@ -86,6 +92,7 @@ class Thumbnailer
86
92
  begin
87
93
  @image = Magick::Image.from_blob(io.read).first.strip!
88
94
  rescue Magick::ImageMagickError => e
95
+ raise ImageTooLargeError, e if e.message =~ /cache resources exhausted/
89
96
  raise UnsupportedMediaTypeError, e
90
97
  end
91
98
  @methods = methods
@@ -126,6 +133,11 @@ class Thumbnailer
126
133
 
127
134
  @logger.info "Initializing thumbniler"
128
135
 
136
+ set_limit(:area, options[:limit_area]) if options.member?(:limit_area)
137
+ set_limit(:memory, options[:limit_memory]) if options.member?(:limit_memory)
138
+ set_limit(:map, options[:limit_map]) if options.member?(:limit_map)
139
+ set_limit(:disk, options[:limit_disk]) if options.member?(:limit_disk)
140
+
129
141
  @images = 0
130
142
  Magick.trace_proc = lambda do |which, description, id, method|
131
143
  case which
@@ -159,5 +171,10 @@ class Thumbnailer
159
171
  def method(method, &impl)
160
172
  @methods[method] = impl
161
173
  end
174
+
175
+ def set_limit(limit, value)
176
+ old = Magick.limit_resource(limit, value)
177
+ @logger.info "Changed limit of #{limit} from #{old} to #{value}"
178
+ end
162
179
  end
163
180
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpthumbnailer
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 5
10
- version: 0.0.5
9
+ - 6
10
+ version: 0.0.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jakub Pastuszek
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-11-29 00:00:00 Z
18
+ date: 2011-11-30 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  type: :runtime
@@ -219,6 +219,7 @@ files:
219
219
  - features/httpthumbnailer.feature
220
220
  - features/step_definitions/httpthumbnailer_steps.rb
221
221
  - features/support/env.rb
222
+ - features/support/test-large.jpg
222
223
  - features/support/test-transparent.png
223
224
  - features/support/test.jpg
224
225
  - features/support/test.txt