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