httpthumbnailer 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
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
|
data/httpthumbnailer.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "httpthumbnailer"
|
8
|
-
s.version = "0.0.
|
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-
|
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:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
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-
|
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
|