imageproxy 0.1.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.
@@ -0,0 +1,87 @@
1
+ require 'base64'
2
+ require "#{File.dirname(__FILE__)}/../imageproxy"
3
+ require "#{File.dirname(__FILE__)}/../lib/options"
4
+
5
+ describe Options do
6
+ describe "parsing path" do
7
+ context "a simple URL" do
8
+ subject { Options.new "/process/color/blue/size/medium", {} }
9
+ its(:command) { should == "process" }
10
+ its(:color) { should == "blue" }
11
+ its(:size) { should == "medium" }
12
+ end
13
+
14
+ context "source" do
15
+ context "when double-escaped" do
16
+ subject { Options.new "/process/source/http%253A%252F%252Fexample.com%252Fdog.jpg", {} }
17
+ it("should unescape") { subject.source.should == "http://example.com/dog.jpg" }
18
+ end
19
+
20
+ context "when escaped" do
21
+ subject { Options.new "/process/source/http%3A%2F%2Fexample.com%2Fdog.jpg", {} }
22
+ it("should unescape") { subject.source.should == "http://example.com/dog.jpg" }
23
+ end
24
+
25
+ context "when not escaped" do
26
+ subject { Options.new "/process/source/foo", {} }
27
+ it("should not unescape") { subject.source.should == "foo" }
28
+ end
29
+
30
+ context "when parameter is named 'src'" do
31
+ subject { Options.new "/process/src/foo", {} }
32
+ it("should rename to 'source'") { subject.source.should == "foo" }
33
+ end
34
+ end
35
+
36
+ context "signature" do
37
+ context "when escaped with + signs" do
38
+ subject { Options.new "/process/source/foo/signature/foo+bar", {} }
39
+ it("should keep the + sign") { subject.signature.should == "foo+bar" }
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "adding query params" do
45
+ subject { Options.new "/convert/source/foo", { "resize" => "20x20" } }
46
+ it("should add query params") { subject.resize.should == "20x20" }
47
+ it("should keep params from path") { subject.source.should == "foo" }
48
+ end
49
+
50
+ describe "content type" do
51
+ context "when guessing based on source filename" do
52
+ it("should understand .png") { Options.new("/convert", "source" => "foo.png").content_type.should == "image/png" }
53
+ it("should understand .jpg") { Options.new("/convert", "source" => "foo.jpg").content_type.should == "image/jpeg" }
54
+ it("should understand .JPEG") { Options.new("/convert", "source" => "foo.JPEG").content_type.should == "image/jpeg" }
55
+ end
56
+ end
57
+
58
+ describe "obfuscation" do
59
+ it "should allow the query string to be encoded in base64" do
60
+ encoded = CGI.escape(Base64.encode64("resize=20x20&source=http://example.com/dog.jpg"))
61
+ options = Options.new "/convert", "_" => encoded
62
+ options.resize.should == "20x20"
63
+ options.source.should == "http://example.com/dog.jpg"
64
+ end
65
+
66
+ it "should allow the path to be encoded in base64" do
67
+ encoded = CGI.escape(Base64.encode64("resize/20x20/source/http%3A%2F%2Fexample.com%2Fdog.jpg"))
68
+ options = Options.new "/convert/-/#{encoded}", {}
69
+ options.resize.should == "20x20"
70
+ options.source.should == "http://example.com/dog.jpg"
71
+ end
72
+ end
73
+
74
+ describe "quality" do
75
+ it "should be set to 0 if it's less than 0" do
76
+ Options.new("/convert", "quality" => "-39").quality.should == "0"
77
+ end
78
+
79
+ it "should be set to 100 if it's > 100" do
80
+ Options.new("/convert", "quality" => "293").quality.should == "100"
81
+ end
82
+
83
+ it "should not change if it's >= 0 <= 100" do
84
+ Options.new("/convert", "quality" => "59").quality.should == "59"
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Server" do
4
+ include Rack::Test::Methods
5
+
6
+ RSpec::Matchers.define :succeed do
7
+ match do |actual|
8
+ actual.status == 200
9
+ end
10
+ end
11
+
12
+ RSpec::Matchers.define :fail do
13
+ match do |actual|
14
+ actual.status == 500
15
+ end
16
+ end
17
+
18
+ def app
19
+ @app ||= Server.new
20
+ end
21
+
22
+ context "when converting" do
23
+ it "should send back the right result" do
24
+ app.stub!(:config) { |sym| nil }
25
+ get("/convert/resize/10x20/source/#{escaped_test_image_url}").should succeed
26
+ Compare.new(response_body_as_file, test_image_path("10x20")).execute.should == "0"
27
+ end
28
+ end
29
+
30
+ context "when identifying" do
31
+ it "should send back information about the image" do
32
+ app.stub!(:config) { |sym| nil }
33
+ get "/identify/source/#{escaped_test_image_url}"
34
+ last_response.body.should =~ /Format: PNG.*Geometry: 200x116\+0\+0/m
35
+ end
36
+ end
37
+
38
+ context "when signature is required" do
39
+ before do
40
+ @secret = "SEEKRET"
41
+ app.stub!(:config) { |sym| {:signature_required => "true", :signature_secret => @secret}[sym] }
42
+ end
43
+
44
+ it "should fail if the signature is missing" do
45
+ get("/convert/resize/10x20/source/#{escaped_test_image_url}").should fail
46
+ end
47
+
48
+ it "should fail if the signature is incorrect" do
49
+ url = "/convert/resize/10x20/source/#{escaped_test_image_url}"
50
+ signature = "BAD"
51
+ get("#{url}?signature=#{signature}").should fail
52
+ end
53
+
54
+ it "should work if the signature is correct" do
55
+ url = "/convert/resize/10x20/source/#{escaped_test_image_url}"
56
+ signature = Signature.create(url, @secret)
57
+ get("#{url}?signature=#{CGI.escape(signature)}").should succeed
58
+ end
59
+
60
+ it "should work if the signature is part of the path" do
61
+ url = "/convert/resize/10x20/source/#{escaped_test_image_url}"
62
+ signature = Signature.create(url, @secret)
63
+ get("#{url}/signature/#{URI.escape(signature)}").should succeed
64
+ end
65
+ end
66
+
67
+ context "when limiting to certain domains" do
68
+ before do
69
+ app.stub!(:config) { |sym| {:allowed_domains => " example.com ,example.org"}[sym] }
70
+ end
71
+
72
+ it "should parse the allowed domains" do
73
+ app.send(:allowed_domains).should =~ ["example.com", "example.org"]
74
+ end
75
+
76
+ it "should only examine the second-level domain" do
77
+ app.send(:url_to_domain, "http://foo.bar.example.com/something").should == "example.com"
78
+ end
79
+
80
+ it "should fail if the source domain is not in the allowed domains" do
81
+ get("/convert/resize/10x20/source/#{CGI.escape('http://example.net/dog.jpg')}").should fail
82
+ end
83
+
84
+ it "should pass if the source domain is in the allowed domains" do
85
+ get("/convert/resize/10x20/source/#{CGI.escape('http://example.org/dog.jpg')}").should succeed
86
+ end
87
+ end
88
+
89
+ context "when limiting to a maximum size" do
90
+ before do
91
+ app.stub!(:config) { |sym| { :max_size => "50" }[sym] }
92
+ end
93
+
94
+ it "should parse out the larger dimension" do
95
+ app.send(:requested_size, "10x50").should == 50
96
+ app.send(:requested_size, "50x50").should == 50
97
+ app.send(:requested_size, "50").should == 50
98
+ end
99
+
100
+ it "should pass when converting to a smaller size" do
101
+ get("/convert/resize/20x20/source/#{escaped_test_image_url}").should succeed
102
+ end
103
+
104
+ it "should pass when converting to the max size" do
105
+ get("/convert/resize/50x50/source/#{escaped_test_image_url}").should succeed
106
+ end
107
+
108
+ it "should fail when converting to a larger size" do
109
+ get("/convert/resize/50x51/source/#{escaped_test_image_url}").should fail
110
+ end
111
+
112
+ it "should pass when thumbnailing to a smaller size" do
113
+ get("/convert/thumbnail/20x20/source/#{escaped_test_image_url}").should succeed
114
+ end
115
+
116
+ it "should fail when thumbnailing to a larger size" do
117
+ get("/convert/thumbnail/50x51/source/#{escaped_test_image_url}").should fail
118
+ end
119
+
120
+ end
121
+ end
122
+
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe Signature do
4
+ describe "#create" do
5
+ it "should create a signature from a query string" do
6
+ Signature.create("/convert?src=http://www.example.com/dog.jpg&resize=400x400&signature=AAA&key=BBB", "SECRET").should_not be_nil
7
+ end
8
+
9
+ it "should ignore the signature param" do
10
+ Signature.create("/convert?src=SRC&resize=10x10&signature=SIG&key=KEY", "SECRET").should == Signature.create("/convert?src=SRC&resize=10x10&key=KEY", "SECRET")
11
+ end
12
+ end
13
+
14
+ describe "#correct?" do
15
+ it "should validate a signature" do
16
+ Signature.correct?(Signature.create("/convert?src=SRC&resize=10x10&key=KEY", "SECRET"), "/convert?src=SRC&resize=10x10&key=KEY", "SECRET").should be_true
17
+ end
18
+
19
+ it "should return false if signature is nil" do
20
+ Signature.correct?(nil, "/convert?src=SRC&resize=10x10&key=KEY", "SECRET").should be_false
21
+ end
22
+
23
+ it "should handle URL-safe signatures" do
24
+ Signature.correct?("_v70E0zfdcRR4cJehS2mhvqJ-8s=", "YLANEBHFSJGCAWKDNCKWEKJRXKPMYU", "SECRET").should be_true
25
+ end
26
+
27
+ it "should handle non-URL-safe signatures" do
28
+ Signature.correct?("k7DMQ/G8YAsbSovX+mDFjlHHMjo=", "YPMMYCRRECCCIPSXPDDFIJFSINOIRC", "SECRET").should be_true
29
+ end
30
+ end
31
+
32
+ describe "#remove_signature_from" do
33
+ it "should remove the signature when it's the only query param" do
34
+ Signature.remove_signature_from("/convert/a/apple/b/banana?signature=SIG").should ==
35
+ "/convert/a/apple/b/banana"
36
+ end
37
+
38
+ it "should remove the signature from the beginning of the query string" do
39
+ Signature.remove_signature_from("/convert/a/apple/b/banana?signature=SIG&c=cherry&d=donut").should ==
40
+ "/convert/a/apple/b/banana?c=cherry&d=donut"
41
+ end
42
+
43
+ it "should remove the signature from the middle of the query string" do
44
+ Signature.remove_signature_from("/convert/a/apple/b/banana?c=cherry&signature=SIG&d=donut").should ==
45
+ "/convert/a/apple/b/banana?c=cherry&d=donut"
46
+ end
47
+
48
+ it "should remove the signature from the end of the query string" do
49
+ Signature.remove_signature_from("/convert/a/apple/b/banana?c=cherry&d=donut&signature=SIG").should ==
50
+ "/convert/a/apple/b/banana?c=cherry&d=donut"
51
+ end
52
+
53
+ it "should remove the signature from the beginning of the path" do
54
+ Signature.remove_signature_from("/convert/signature/SIG/a/apple/b/banana?c=cherry&d=donut").should ==
55
+ "/convert/a/apple/b/banana?c=cherry&d=donut"
56
+ end
57
+
58
+ it "should remove the signature from the middle of the path" do
59
+ Signature.remove_signature_from("/convert/a/apple/signature/SIG/b/banana?c=cherry&d=donut").should ==
60
+ "/convert/a/apple/b/banana?c=cherry&d=donut"
61
+ end
62
+
63
+ it "should remove the signature from the end of the path" do
64
+ Signature.remove_signature_from("/convert/a/apple/b/banana/signature/SIG?c=cherry&d=donut").should ==
65
+ "/convert/a/apple/b/banana?c=cherry&d=donut"
66
+ end
67
+
68
+ it "should remove the signature from the end of the path when there's no query string" do
69
+ Signature.remove_signature_from("/convert/a/apple/b/banana/signature/SIG").should ==
70
+ "/convert/a/apple/b/banana"
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,23 @@
1
+ Bundler.require :test
2
+
3
+ Dir.glob(File.join(File.dirname(__FILE__), "..", "lib", "**", "*.rb")).each {|f| require f }
4
+
5
+ def response_body_as_file
6
+ result_file = Tempfile.new("request_spec")
7
+ result_file.write(last_response.body)
8
+ result_file.close
9
+ result_file
10
+ end
11
+
12
+ def test_image_path(size=nil)
13
+ size_suffix = size.nil? ? "" : "_#{size}"
14
+ File.expand_path(File.dirname(__FILE__) + "/../public/sample#{size_suffix}.png")
15
+ end
16
+
17
+ def test_image_url
18
+ "file://#{test_image_path}"
19
+ end
20
+
21
+ def escaped_test_image_url
22
+ CGI.escape test_image_url
23
+ end
metadata ADDED
@@ -0,0 +1,184 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: imageproxy
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Erik Hanson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-06-08 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rack
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: mime-types
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: heroku
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: shotgun
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: rack-test
84
+ requirement: &id007 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: "0"
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: awesome_print
95
+ requirement: &id008 !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ type: :development
102
+ prerelease: false
103
+ version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: jeweler
106
+ requirement: &id009 !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: "0"
112
+ type: :development
113
+ prerelease: false
114
+ version_requirements: *id009
115
+ description: A image processing proxy server, written in Ruby as a Rack application. Requires ImageMagick.
116
+ email: erik@eahanson.com
117
+ executables: []
118
+
119
+ extensions: []
120
+
121
+ extra_rdoc_files:
122
+ - LICENSE.txt
123
+ - README.mdown
124
+ files:
125
+ - .document
126
+ - Gemfile
127
+ - Gemfile.lock
128
+ - LICENSE.txt
129
+ - README.mdown
130
+ - Rakefile
131
+ - VERSION
132
+ - config.ru
133
+ - imageproxy.gemspec
134
+ - imageproxy.rb
135
+ - lib/command.rb
136
+ - lib/compare.rb
137
+ - lib/convert.rb
138
+ - lib/identify.rb
139
+ - lib/options.rb
140
+ - lib/selftest.rb
141
+ - lib/server.rb
142
+ - lib/signature.rb
143
+ - public/background.png
144
+ - public/sample.png
145
+ - public/sample_10x20.png
146
+ - spec/command_spec.rb
147
+ - spec/convert_spec.rb
148
+ - spec/options_spec.rb
149
+ - spec/server_spec.rb
150
+ - spec/signature_spec.rb
151
+ - spec/spec_helper.rb
152
+ has_rdoc: true
153
+ homepage: http://github.com/eahanson/imageproxy
154
+ licenses:
155
+ - MIT
156
+ post_install_message:
157
+ rdoc_options: []
158
+
159
+ require_paths:
160
+ - lib
161
+ required_ruby_version: !ruby/object:Gem::Requirement
162
+ none: false
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ hash: -3683020924727670917
167
+ segments:
168
+ - 0
169
+ version: "0"
170
+ required_rubygems_version: !ruby/object:Gem::Requirement
171
+ none: false
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: "0"
176
+ requirements: []
177
+
178
+ rubyforge_project:
179
+ rubygems_version: 1.6.2
180
+ signing_key:
181
+ specification_version: 3
182
+ summary: A image processing proxy server, written in Ruby as a Rack application.
183
+ test_files: []
184
+