imageproxy 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.mdown CHANGED
@@ -50,6 +50,12 @@ imageproxy doesn't do any sort of caching. That kind of thing is better left up
50
50
  Also, imageproxy itself isn't nearly as fast as it could be. It's written in an interpreted language, and it shells out to curl and ImageMagick to do its work. Presumably, it would be way faster written in C as an Apache module, but this implementation was quite a bit easier :)
51
51
 
52
52
 
53
+ INSTALLING
54
+ ----------
55
+
56
+ gem install imageproxy
57
+
58
+
53
59
  API
54
60
  ---
55
61
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
data/config.ru CHANGED
@@ -4,4 +4,4 @@ require 'rack/sendfile'
4
4
  require File.join(File.expand_path(File.dirname(__FILE__)), "imageproxy")
5
5
  require File.join(File.expand_path(File.dirname(__FILE__)), "lib", "server")
6
6
 
7
- run Rack::Sendfile.new(Server.new)
7
+ run Rack::Sendfile.new(Imageproxy::Server.new)
data/imageproxy.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{imageproxy}
8
- s.version = "0.1.0"
8
+ s.version = "0.1.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Erik Hanson"]
data/lib/command.rb CHANGED
@@ -1,22 +1,23 @@
1
- class Command
2
- protected
1
+ module Imageproxy
2
+ class Command
3
+ protected
3
4
 
4
- def execute_command(command_line)
5
- stdin, stdout, stderr = Open3.popen3(command_line)
6
- [output_to_string(stdout), output_to_string(stderr)].join("")
7
- end
5
+ def execute_command(command_line)
6
+ stdin, stdout, stderr = Open3.popen3(command_line)
7
+ [output_to_string(stdout), output_to_string(stderr)].join("")
8
+ end
8
9
 
9
- def curl(url, options={})
10
- user_agent = options[:user_agent] || "imageproxy"
11
- %|curl -s -A "#{user_agent}" "#{url}"|
12
- end
10
+ def curl(url, options={})
11
+ user_agent = options[:user_agent] || "imageproxy"
12
+ %|curl -s -A "#{user_agent}" "#{url}"|
13
+ end
13
14
 
14
- def to_path(obj)
15
- obj.respond_to?(:path) ? obj.path : obj.to_s
16
- end
15
+ def to_path(obj)
16
+ obj.respond_to?(:path) ? obj.path : obj.to_s
17
+ end
17
18
 
18
- def output_to_string(output)
19
- output.readlines.join("").chomp
19
+ def output_to_string(output)
20
+ output.readlines.join("").chomp
21
+ end
20
22
  end
21
-
22
23
  end
data/lib/compare.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  require File.join(File.expand_path(File.dirname(__FILE__)), "command")
2
2
 
3
- class Compare < Command
4
- def initialize(a, b)
5
- @path_a = to_path(a)
6
- @path_b = to_path(b)
7
- end
3
+ module Imageproxy
4
+ class Compare < Imageproxy::Command
5
+ def initialize(a, b)
6
+ @path_a = to_path(a)
7
+ @path_b = to_path(b)
8
+ end
8
9
 
9
- def execute
10
- execute_command %'compare -metric AE -fuzz 10% "#{@path_a}" "#{@path_b}" "#{Tempfile.new("compare").path}"'
10
+ def execute
11
+ execute_command %'compare -metric AE -fuzz 10% "#{@path_a}" "#{@path_b}" "#{Tempfile.new("compare").path}"'
12
+ end
11
13
  end
12
14
  end
data/lib/convert.rb CHANGED
@@ -1,72 +1,74 @@
1
1
  require File.join(File.expand_path(File.dirname(__FILE__)), "command")
2
2
 
3
- class Convert < Command
4
- attr_reader :options
5
-
6
- def initialize(options)
7
- @options = options
8
- if (!(options.resize || options.thumbnail || options.rotate || options.flip || options.format || options.quality))
9
- raise "Missing action or illegal parameter value"
10
- end
11
- end
3
+ module Imageproxy
4
+ class Convert < Imageproxy::Command
5
+ attr_reader :options
12
6
 
13
- def execute(user_agent=nil)
14
- execute_command %'#{curl options.source, :user_agent => user_agent} | convert - #{convert_options} #{new_format}#{file.path}'
15
- file
16
- end
7
+ def initialize(options)
8
+ @options = options
9
+ if (!(options.resize || options.thumbnail || options.rotate || options.flip || options.format || options.quality))
10
+ raise "Missing action or illegal parameter value"
11
+ end
12
+ end
17
13
 
18
- def convert_options
19
- convert_options = []
20
- convert_options << "-resize #{resize_thumbnail_options(options.resize)}" if options.resize
21
- convert_options << "-thumbnail #{resize_thumbnail_options(options.thumbnail)}" if options.thumbnail
22
- convert_options << "-flop" if options.flip == "horizontal"
23
- convert_options << "-flip" if options.flip == "vertical"
24
- convert_options << rotate_options if options.rotate
25
- convert_options << "-colors 256" if options.format == "png8"
26
- convert_options << "-quality #{options.quality}" if options.quality
27
- convert_options << interlace_options if options.progressive
28
- convert_options.join " "
29
- end
14
+ def execute(user_agent=nil)
15
+ execute_command %'#{curl options.source, :user_agent => user_agent} | convert - #{convert_options} #{new_format}#{file.path}'
16
+ file
17
+ end
30
18
 
31
- def resize_thumbnail_options(size)
32
- case options.shape
33
- when "cut"
34
- "#{size}^ -gravity center -extent #{size}"
35
- when "preserve"
36
- size
37
- when "pad"
38
- background = options.background ? %|"#{options.background}"| : %|none -matte|
39
- "#{size} -background #{background} -gravity center -extent #{size}"
40
- else
41
- size
19
+ def convert_options
20
+ convert_options = []
21
+ convert_options << "-resize #{resize_thumbnail_options(options.resize)}" if options.resize
22
+ convert_options << "-thumbnail #{resize_thumbnail_options(options.thumbnail)}" if options.thumbnail
23
+ convert_options << "-flop" if options.flip == "horizontal"
24
+ convert_options << "-flip" if options.flip == "vertical"
25
+ convert_options << rotate_options if options.rotate
26
+ convert_options << "-colors 256" if options.format == "png8"
27
+ convert_options << "-quality #{options.quality}" if options.quality
28
+ convert_options << interlace_options if options.progressive
29
+ convert_options.join " "
42
30
  end
43
- end
44
31
 
45
- def rotate_options
46
- if options.rotate.to_f % 90 == 0
47
- "-rotate #{options.rotate}"
48
- else
49
- background = options.background ? %|"#{options.background}"| : %|none|
50
- "-background #{background} -matte -rotate #{options.rotate}"
32
+ def resize_thumbnail_options(size)
33
+ case options.shape
34
+ when "cut"
35
+ "#{size}^ -gravity center -extent #{size}"
36
+ when "preserve"
37
+ size
38
+ when "pad"
39
+ background = options.background ? %|"#{options.background}"| : %|none -matte|
40
+ "#{size} -background #{background} -gravity center -extent #{size}"
41
+ else
42
+ size
43
+ end
51
44
  end
52
- end
53
45
 
54
- def interlace_options
55
- case options.progressive
56
- when "true"
57
- "-interlace JPEG"
58
- when "false"
59
- "-interlace none"
46
+ def rotate_options
47
+ if options.rotate.to_f % 90 == 0
48
+ "-rotate #{options.rotate}"
60
49
  else
61
- ""
50
+ background = options.background ? %|"#{options.background}"| : %|none|
51
+ "-background #{background} -matte -rotate #{options.rotate}"
52
+ end
62
53
  end
63
- end
64
54
 
65
- def new_format
66
- options.format ? "#{options.format}:" : ""
67
- end
55
+ def interlace_options
56
+ case options.progressive
57
+ when "true"
58
+ "-interlace JPEG"
59
+ when "false"
60
+ "-interlace none"
61
+ else
62
+ ""
63
+ end
64
+ end
68
65
 
69
- def file
70
- @tempfile ||= Tempfile.new("imageproxy").tap(&:close)
66
+ def new_format
67
+ options.format ? "#{options.format}:" : ""
68
+ end
69
+
70
+ def file
71
+ @tempfile ||= Tempfile.new("imageproxy").tap(&:close)
72
+ end
71
73
  end
72
74
  end
data/lib/identify.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  require File.join(File.expand_path(File.dirname(__FILE__)), "command")
2
2
 
3
- class Identify < Command
4
- def initialize(options)
5
- @options = options
6
- end
3
+ module Imageproxy
4
+ class Identify < Imageproxy::Command
5
+ def initialize(options)
6
+ @options = options
7
+ end
7
8
 
8
- def execute(user_agent=nil)
9
- execute_command %'#{curl @options.source, :user_agent => user_agent} | identify -verbose -'
9
+ def execute(user_agent=nil)
10
+ execute_command %'#{curl @options.source, :user_agent => user_agent} | identify -verbose -'
11
+ end
10
12
  end
11
13
  end
data/lib/options.rb CHANGED
@@ -2,72 +2,74 @@ require 'uri'
2
2
  require 'cgi'
3
3
  require 'mime/types'
4
4
 
5
- class Options
6
- def initialize(path, query_params)
7
- params_from_path = path.split('/').reject { |s| s.nil? || s.empty? }
8
- command = params_from_path.shift
5
+ module Imageproxy
6
+ class Options
7
+ def initialize(path, query_params)
8
+ params_from_path = path.split('/').reject { |s| s.nil? || s.empty? }
9
+ command = params_from_path.shift
9
10
 
10
- @hash = Hash[*params_from_path]
11
- @hash['command'] = command
12
- @hash.merge! query_params
13
- merge_obfuscated
14
- @hash["source"] = @hash.delete("src") if @hash.has_key?("src")
15
-
16
- unescape_source
17
- unescape_signature
18
- check_parameters
19
- end
11
+ @hash = Hash[*params_from_path]
12
+ @hash['command'] = command
13
+ @hash.merge! query_params
14
+ merge_obfuscated
15
+ @hash["source"] = @hash.delete("src") if @hash.has_key?("src")
20
16
 
21
- def check_parameters
22
- check_param('resize',/^[0-9]{1,5}(x[0-9]{1,5})?$/)
23
- check_param('thumbnail',/^[0-9]{1,5}(x[0-9]{1,5})?$/)
24
- check_param('rotate',/^(-)?[0-9]{1,3}(\.[0-9]+)?$/)
25
- check_param('format',/^[0-9a-zA-Z]{2,6}$/)
26
- check_param('progressive',/^true|false$/i)
27
- check_param('background',/^#[0-9a-f]{3}([0-9a-f]{3})?|rgba\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-1](.[0-9]+)?\)$/)
28
- check_param('shape',/^preserve|pad|cut$/i)
29
- @hash['quality'] = [[@hash['quality'].to_i, 100].min, 0].max.to_s if @hash.has_key?('quality')
30
- end
17
+ unescape_source
18
+ unescape_signature
19
+ check_parameters
20
+ end
21
+
22
+ def check_parameters
23
+ check_param('resize', /^[0-9]{1,5}(x[0-9]{1,5})?$/)
24
+ check_param('thumbnail', /^[0-9]{1,5}(x[0-9]{1,5})?$/)
25
+ check_param('rotate', /^(-)?[0-9]{1,3}(\.[0-9]+)?$/)
26
+ check_param('format', /^[0-9a-zA-Z]{2,6}$/)
27
+ check_param('progressive', /^true|false$/i)
28
+ check_param('background', /^#[0-9a-f]{3}([0-9a-f]{3})?|rgba\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-1](.[0-9]+)?\)$/)
29
+ check_param('shape', /^preserve|pad|cut$/i)
30
+ @hash['quality'] = [[@hash['quality'].to_i, 100].min, 0].max.to_s if @hash.has_key?('quality')
31
+ end
31
32
 
32
- def check_param(param, rega)
33
- if @hash.has_key? param
34
- if (! rega.match(@hash[param]))
35
- @hash.delete(param)
33
+ def check_param(param, rega)
34
+ if @hash.has_key? param
35
+ if (!rega.match(@hash[param]))
36
+ @hash.delete(param)
37
+ end
36
38
  end
37
39
  end
38
- end
39
40
 
40
- def method_missing(symbol)
41
- @hash[symbol.to_s] || @hash[symbol]
42
- end
41
+ def method_missing(symbol)
42
+ @hash[symbol.to_s] || @hash[symbol]
43
+ end
43
44
 
44
- def content_type
45
- MIME::Types.of(@hash['source']).first.content_type
46
- end
45
+ def content_type
46
+ MIME::Types.of(@hash['source']).first.content_type
47
+ end
47
48
 
48
- private
49
-
50
- def unescape_source
51
- @hash['source'] &&= CGI.unescape(CGI.unescape(@hash['source']))
52
- end
49
+ private
53
50
 
54
- def unescape_signature
55
- @hash['signature'] &&= URI.unescape(@hash['signature'])
56
- end
51
+ def unescape_source
52
+ @hash['source'] &&= CGI.unescape(CGI.unescape(@hash['source']))
53
+ end
57
54
 
58
- def merge_obfuscated
59
- if @hash["_"]
60
- decoded = Base64.decode64(CGI.unescape(@hash["_"]))
61
- decoded_hash = CGI.parse(decoded)
62
- @hash.delete "_"
63
- decoded_hash.map { |k, v| @hash[k] = (v.class == Array) ? v.first : v }
55
+ def unescape_signature
56
+ @hash['signature'] &&= URI.unescape(@hash['signature'])
64
57
  end
65
58
 
66
- if @hash["-"]
67
- decoded = Base64.decode64(CGI.unescape(@hash["-"]))
68
- decoded_hash = Hash[*decoded.split('/').reject { |s| s.nil? || s.empty? }]
69
- @hash.delete "-"
70
- decoded_hash.map { |k, v| @hash[k] = (v.class == Array) ? v.first : v }
59
+ def merge_obfuscated
60
+ if @hash["_"]
61
+ decoded = Base64.decode64(CGI.unescape(@hash["_"]))
62
+ decoded_hash = CGI.parse(decoded)
63
+ @hash.delete "_"
64
+ decoded_hash.map { |k, v| @hash[k] = (v.class == Array) ? v.first : v }
65
+ end
66
+
67
+ if @hash["-"]
68
+ decoded = Base64.decode64(CGI.unescape(@hash["-"]))
69
+ decoded_hash = Hash[*decoded.split('/').reject { |s| s.nil? || s.empty? }]
70
+ @hash.delete "-"
71
+ decoded_hash.map { |k, v| @hash[k] = (v.class == Array) ? v.first : v }
72
+ end
71
73
  end
72
74
  end
73
75
  end
data/lib/selftest.rb CHANGED
@@ -1,6 +1,7 @@
1
- class Selftest
2
- def self.html(request, signature_required, signature_secret)
3
- html = <<-HTML
1
+ module Imageproxy
2
+ class Selftest
3
+ def self.html(request, signature_required, signature_secret)
4
+ html = <<-HTML
4
5
  <html>
5
6
  <head>
6
7
  <title>imageproxy selftest</title>
@@ -12,61 +13,62 @@ class Selftest
12
13
  </style>
13
14
  </head>
14
15
  <body>
15
- HTML
16
+ HTML
16
17
 
17
- url_prefix = "#{request.scheme}://#{request.host_with_port}"
18
- raw_source = "http://eahanson.s3.amazonaws.com/imageproxy/sample.png"
19
- source = CGI.escape(URI.escape(URI.escape(raw_source)))
18
+ url_prefix = "#{request.scheme}://#{request.host_with_port}"
19
+ raw_source = "http://eahanson.s3.amazonaws.com/imageproxy/sample.png"
20
+ source = CGI.escape(URI.escape(URI.escape(raw_source)))
20
21
 
21
- html += <<-HTML
22
+ html += <<-HTML
22
23
  <h3>Original Image</h3>
23
24
  <a href="#{raw_source}">#{raw_source}</a>
24
25
  <img src="#{raw_source}">
25
- HTML
26
+ HTML
26
27
 
27
- examples = [
28
- ["Resize (regular query-string URL format)", "/convert?resize=100x100&source=#{source}"],
29
- ["Resize (CloudFront-compatible URL format)", "/convert/resize/100x100/source/#{source}"],
28
+ examples = [
29
+ ["Resize (regular query-string URL format)", "/convert?resize=100x100&source=#{source}"],
30
+ ["Resize (CloudFront-compatible URL format)", "/convert/resize/100x100/source/#{source}"],
30
31
 
31
- ["Resize with padding", "/convert?resize=100x100&shape=pad&source=#{source}"],
32
- ["Resize with padding & background color", "/convert?resize=100x100&shape=pad&background=%23ff00ff&source=#{source}"],
32
+ ["Resize with padding", "/convert?resize=100x100&shape=pad&source=#{source}"],
33
+ ["Resize with padding & background color", "/convert?resize=100x100&shape=pad&background=%23ff00ff&source=#{source}"],
33
34
 
34
- ["Resize with cutting", "/convert?resize=100x100&shape=cut&source=#{source}"],
35
+ ["Resize with cutting", "/convert?resize=100x100&shape=cut&source=#{source}"],
35
36
 
36
- ["Flipping horizontally", "/convert?flip=horizontal&source=#{source}"],
37
- ["Flipping vertically", "/convert?flip=vertical&source=#{source}"],
37
+ ["Flipping horizontally", "/convert?flip=horizontal&source=#{source}"],
38
+ ["Flipping vertically", "/convert?flip=vertical&source=#{source}"],
38
39
 
39
- ["Rotating to a 90-degree increment", "/convert?rotate=90&source=#{source}"],
40
- ["Rotating to a non-90-degree increment", "/convert?rotate=120&source=#{source}"],
41
- ["Rotating to a non-90-degree increment with a background color", "/convert?rotate=120&background=%23ff00ff&source=#{source}"],
40
+ ["Rotating to a 90-degree increment", "/convert?rotate=90&source=#{source}"],
41
+ ["Rotating to a non-90-degree increment", "/convert?rotate=120&source=#{source}"],
42
+ ["Rotating to a non-90-degree increment with a background color", "/convert?rotate=120&background=%23ff00ff&source=#{source}"],
42
43
 
43
- ["Combo", "/convert?resize=100x100&shape=cut&rotate=45&background=%23ff00ff&source=#{source}"]
44
- ]
44
+ ["Combo", "/convert?resize=100x100&shape=cut&rotate=45&background=%23ff00ff&source=#{source}"]
45
+ ]
45
46
 
46
- examples.each do |example|
47
- path = example[1]
48
- if (signature_required)
49
- signature = CGI.escape(Signature.create(path, signature_secret))
50
- if path.include?("&")
51
- path += "&signature=#{signature}"
52
- else
53
- path += "/signature/#{signature}"
47
+ examples.each do |example|
48
+ path = example[1]
49
+ if (signature_required)
50
+ signature = CGI.escape(Signature.create(path, signature_secret))
51
+ if path.include?("&")
52
+ path += "&signature=#{signature}"
53
+ else
54
+ path += "/signature/#{signature}"
55
+ end
54
56
  end
55
- end
56
- example_url = url_prefix + path
57
- html += <<-HTML
57
+ example_url = url_prefix + path
58
+ html += <<-HTML
58
59
  <h3>#{example[0]}</h3>
59
60
  <a href="#{example_url}">#{example_url}</a>
60
61
  <img src="#{example_url}">
61
- HTML
62
- end
62
+ HTML
63
+ end
63
64
 
64
- html += <<-HTML
65
+ html += <<-HTML
65
66
  <div class="footer"><a href="https://github.com/eahanson/imageproxy">imageproxy</a> selftest</div>
66
67
  </body>
67
68
  </html>
68
- HTML
69
+ HTML
69
70
 
70
- html
71
+ html
72
+ end
71
73
  end
72
74
  end
data/lib/server.rb CHANGED
@@ -5,101 +5,103 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "selftest")
5
5
  require File.join(File.expand_path(File.dirname(__FILE__)), "signature")
6
6
  require 'uri'
7
7
 
8
- class Server
9
- def initialize
10
- @file_server = Rack::File.new(File.join(File.expand_path(File.dirname(__FILE__)), "..", "public"))
11
- end
8
+ module Imageproxy
9
+ class Server
10
+ def initialize
11
+ @file_server = Rack::File.new(File.join(File.expand_path(File.dirname(__FILE__)), "..", "public"))
12
+ end
12
13
 
13
- def call(env)
14
- request = Rack::Request.new(env)
15
- options = Options.new(request.path_info, request.params)
16
- user_agent = request.env["HTTP_USER_AGENT"]
17
- cachetime = config(:cache_time) ? config(:cache_time) : 86400
18
-
19
- case options.command
20
- when "convert", "process", nil
21
- check_signature request, options
22
- check_domain options
23
- check_size options
24
-
25
- file = Convert.new(options).execute(user_agent)
26
- class << file
27
- alias to_path path
28
- end
29
-
30
- file.open
31
- [200, {"Content-Type" => options.content_type, "Cache-Control" => "max-age=#{cachetime}, must-revalidate"}, file]
32
- when "identify"
33
- check_signature request, options
34
- check_domain options
35
-
36
- [200, {"Content-Type" => "text/plain"}, [Identify.new(options).execute(user_agent)]]
37
- when "selftest"
38
- [200, {"Content-Type" => "text/html"}, [Selftest.html(request, config?(:signature_required), config(:signature_secret))]]
39
- else
40
- @file_server.call(env)
14
+ def call(env)
15
+ request = Rack::Request.new(env)
16
+ options = Options.new(request.path_info, request.params)
17
+ user_agent = request.env["HTTP_USER_AGENT"]
18
+ cachetime = config(:cache_time) ? config(:cache_time) : 86400
19
+
20
+ case options.command
21
+ when "convert", "process", nil
22
+ check_signature request, options
23
+ check_domain options
24
+ check_size options
25
+
26
+ file = Convert.new(options).execute(user_agent)
27
+ class << file
28
+ alias to_path path
29
+ end
30
+
31
+ file.open
32
+ [200, {"Content-Type" => options.content_type, "Cache-Control" => "max-age=#{cachetime}, must-revalidate"}, file]
33
+ when "identify"
34
+ check_signature request, options
35
+ check_domain options
36
+
37
+ [200, {"Content-Type" => "text/plain"}, [Identify.new(options).execute(user_agent)]]
38
+ when "selftest"
39
+ [200, {"Content-Type" => "text/html"}, [Selftest.html(request, config?(:signature_required), config(:signature_secret))]]
40
+ else
41
+ @file_server.call(env)
42
+ end
43
+ rescue
44
+ STDERR.puts $!
45
+ [500, {"Content-Type" => "text/plain"}, ["Error (#{$!})"]]
41
46
  end
42
- rescue
43
- STDERR.puts $!
44
- [500, {"Content-Type" => "text/plain"}, ["Error (#{$!})"]]
45
- end
46
47
 
47
- private
48
+ private
48
49
 
49
- def config(symbol)
50
- ENV["IMAGEPROXY_#{symbol.to_s.upcase}"]
51
- end
50
+ def config(symbol)
51
+ ENV["IMAGEPROXY_#{symbol.to_s.upcase}"]
52
+ end
52
53
 
53
- def config?(symbol)
54
- config(symbol) && config(symbol).casecmp("TRUE") == 0
55
- end
54
+ def config?(symbol)
55
+ config(symbol) && config(symbol).casecmp("TRUE") == 0
56
+ end
56
57
 
57
- def check_signature(request, options)
58
- if config?(:signature_required)
59
- raise "Missing siganture" if options.signature.nil?
58
+ def check_signature(request, options)
59
+ if config?(:signature_required)
60
+ raise "Missing siganture" if options.signature.nil?
60
61
 
61
- valid_signature = Signature.correct?(options.signature, request.fullpath, config(:signature_secret))
62
- raise "Invalid signature #{options.signature} for #{request.url}" unless valid_signature
62
+ valid_signature = Signature.correct?(options.signature, request.fullpath, config(:signature_secret))
63
+ raise "Invalid signature #{options.signature} for #{request.url}" unless valid_signature
64
+ end
63
65
  end
64
- end
65
66
 
66
- def check_domain(options)
67
- raise "Invalid domain" unless domain_allowed? options.source
68
- end
67
+ def check_domain(options)
68
+ raise "Invalid domain" unless domain_allowed? options.source
69
+ end
69
70
 
70
- def check_size(options)
71
- raise "Image size too large" if exceeds_max_size(options.resize, options.thumbnail)
72
- end
71
+ def check_size(options)
72
+ raise "Image size too large" if exceeds_max_size(options.resize, options.thumbnail)
73
+ end
73
74
 
74
- def domain_allowed?(url)
75
- return true unless allowed_domains
76
- allowed_domains.include?(url_to_domain url)
77
- end
75
+ def domain_allowed?(url)
76
+ return true unless allowed_domains
77
+ allowed_domains.include?(url_to_domain url)
78
+ end
78
79
 
79
- def url_to_domain(url)
80
- URI::parse(url).host.split(".")[-2, 2].join(".")
81
- rescue
82
- ""
83
- end
80
+ def url_to_domain(url)
81
+ URI::parse(url).host.split(".")[-2, 2].join(".")
82
+ rescue
83
+ ""
84
+ end
84
85
 
85
- def allowed_domains
86
- config(:allowed_domains) && config(:allowed_domains).split(",").map(&:strip)
87
- end
86
+ def allowed_domains
87
+ config(:allowed_domains) && config(:allowed_domains).split(",").map(&:strip)
88
+ end
88
89
 
89
- def exceeds_max_size(*sizes)
90
- max_size && sizes.any? { |size| size && requested_size(size) > max_size }
91
- end
90
+ def exceeds_max_size(*sizes)
91
+ max_size && sizes.any? { |size| size && requested_size(size) > max_size }
92
+ end
92
93
 
93
- def max_size
94
- config(:max_size) && config(:max_size).to_i
95
- end
94
+ def max_size
95
+ config(:max_size) && config(:max_size).to_i
96
+ end
96
97
 
97
- def requested_size(req_size)
98
- sizes = req_size.scan(/\d*/)
99
- if sizes[2] && (sizes[2].to_i > sizes[0].to_i)
100
- sizes[2].to_i
101
- else
102
- sizes[0].to_i
98
+ def requested_size(req_size)
99
+ sizes = req_size.scan(/\d*/)
100
+ if sizes[2] && (sizes[2].to_i > sizes[0].to_i)
101
+ sizes[2].to_i
102
+ else
103
+ sizes[0].to_i
104
+ end
103
105
  end
104
106
  end
105
- end
107
+ end
data/lib/signature.rb CHANGED
@@ -1,24 +1,26 @@
1
1
  require 'base64'
2
2
  require 'openssl'
3
3
 
4
- class Signature
5
- def self.create(path, secret)
6
- Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), secret, remove_signature_from(path))).strip.tr('+/', '-_')
7
- end
4
+ module Imageproxy
5
+ class Signature
6
+ def self.create(path, secret)
7
+ Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), secret, remove_signature_from(path))).strip.tr('+/', '-_')
8
+ end
8
9
 
9
- def self.remove_signature_from(path)
10
- #TODO: do this in fewer passes
11
- path.
12
- sub(%r{&signature(=[^&]*)?(?=&|$)}, "").
13
- sub(%r{\?signature(=[^&]*)?&}, "?").
14
- sub(%r{\?signature(=[^&]*)?$}, "").
15
- sub(%r{/signature/[^\?/]+/}, "/").
16
- sub(%r{/signature/[^\?/]+\?}, "?").
17
- sub(%r{/signature/[^\?/]+}, "")
18
- end
10
+ def self.remove_signature_from(path)
11
+ #TODO: do this in fewer passes
12
+ path.
13
+ sub(%r{&signature(=[^&]*)?(?=&|$)}, "").
14
+ sub(%r{\?signature(=[^&]*)?&}, "?").
15
+ sub(%r{\?signature(=[^&]*)?$}, "").
16
+ sub(%r{/signature/[^\?/]+/}, "/").
17
+ sub(%r{/signature/[^\?/]+\?}, "?").
18
+ sub(%r{/signature/[^\?/]+}, "")
19
+ end
19
20
 
20
- def self.correct?(signature, path, secret)
21
- created = create(path, secret)
22
- signature != nil && path != nil && secret != nil && (created == signature || created == signature.tr('+/', '-_'))
21
+ def self.correct?(signature, path, secret)
22
+ created = create(path, secret)
23
+ signature != nil && path != nil && secret != nil && (created == signature || created == signature.tr('+/', '-_'))
24
+ end
23
25
  end
24
26
  end
data/spec/command_spec.rb CHANGED
@@ -1,17 +1,17 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Command do
3
+ describe Imageproxy::Command do
4
4
  describe "#curl" do
5
5
  context "when a user agent is supplied" do
6
6
  it "should send that user agent" do
7
- Command.new.send(:curl, "http://example.com/dog.jpg", :user_agent => "some user agent").should ==
7
+ Imageproxy::Command.new.send(:curl, "http://example.com/dog.jpg", :user_agent => "some user agent").should ==
8
8
  %|curl -s -A "some user agent" "http://example.com/dog.jpg"|
9
9
  end
10
10
  end
11
11
 
12
12
  context "when no user agent is supplied" do
13
13
  it "should send a default user agent" do
14
- Command.new.send(:curl, "http://example.com/dog.jpg").should ==
14
+ Imageproxy::Command.new.send(:curl, "http://example.com/dog.jpg").should ==
15
15
  %|curl -s -A "imageproxy" "http://example.com/dog.jpg"|
16
16
  end
17
17
  end
data/spec/convert_spec.rb CHANGED
@@ -1,13 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Convert do
3
+ describe Imageproxy::Convert do
4
4
  before do
5
5
  @mock_file = mock("file")
6
6
  @mock_file.stub!(:path).and_return("/mock/file/path")
7
7
  end
8
8
 
9
9
  def command(options)
10
- command = Convert.new(Options.new("", {:source => "http%3A%2F%2Fexample.com%2Fdog.jpg"}.merge(options)))
10
+ command = Imageproxy::Convert.new(Imageproxy::Options.new("", {:source => "http%3A%2F%2Fexample.com%2Fdog.jpg"}.merge(options)))
11
11
  command.stub!(:file).and_return(@mock_file)
12
12
  command.stub!(:system)
13
13
  command
@@ -15,7 +15,7 @@ describe Convert do
15
15
 
16
16
  context "general" do
17
17
  before do
18
- @command = Convert.new(Options.new("/convert/format/png/resize/10x20/source/http%3A%2F%2Fexample.com%2Fdog.jpg", {}))
18
+ @command = Imageproxy::Convert.new(Imageproxy::Options.new("/convert/format/png/resize/10x20/source/http%3A%2F%2Fexample.com%2Fdog.jpg", {}))
19
19
  @command.stub!(:file).and_return(@mock_file)
20
20
  @command.stub!(:system)
21
21
  end
data/spec/options_spec.rb CHANGED
@@ -2,10 +2,10 @@ require 'base64'
2
2
  require "#{File.dirname(__FILE__)}/../imageproxy"
3
3
  require "#{File.dirname(__FILE__)}/../lib/options"
4
4
 
5
- describe Options do
5
+ describe Imageproxy::Options do
6
6
  describe "parsing path" do
7
7
  context "a simple URL" do
8
- subject { Options.new "/process/color/blue/size/medium", {} }
8
+ subject { Imageproxy::Options.new "/process/color/blue/size/medium", {} }
9
9
  its(:command) { should == "process" }
10
10
  its(:color) { should == "blue" }
11
11
  its(:size) { should == "medium" }
@@ -13,59 +13,59 @@ describe Options do
13
13
 
14
14
  context "source" do
15
15
  context "when double-escaped" do
16
- subject { Options.new "/process/source/http%253A%252F%252Fexample.com%252Fdog.jpg", {} }
16
+ subject { Imageproxy::Options.new "/process/source/http%253A%252F%252Fexample.com%252Fdog.jpg", {} }
17
17
  it("should unescape") { subject.source.should == "http://example.com/dog.jpg" }
18
18
  end
19
19
 
20
20
  context "when escaped" do
21
- subject { Options.new "/process/source/http%3A%2F%2Fexample.com%2Fdog.jpg", {} }
21
+ subject { Imageproxy::Options.new "/process/source/http%3A%2F%2Fexample.com%2Fdog.jpg", {} }
22
22
  it("should unescape") { subject.source.should == "http://example.com/dog.jpg" }
23
23
  end
24
24
 
25
25
  context "when not escaped" do
26
- subject { Options.new "/process/source/foo", {} }
26
+ subject { Imageproxy::Options.new "/process/source/foo", {} }
27
27
  it("should not unescape") { subject.source.should == "foo" }
28
28
  end
29
29
 
30
30
  context "when parameter is named 'src'" do
31
- subject { Options.new "/process/src/foo", {} }
31
+ subject { Imageproxy::Options.new "/process/src/foo", {} }
32
32
  it("should rename to 'source'") { subject.source.should == "foo" }
33
33
  end
34
34
  end
35
35
 
36
36
  context "signature" do
37
37
  context "when escaped with + signs" do
38
- subject { Options.new "/process/source/foo/signature/foo+bar", {} }
38
+ subject { Imageproxy::Options.new "/process/source/foo/signature/foo+bar", {} }
39
39
  it("should keep the + sign") { subject.signature.should == "foo+bar" }
40
40
  end
41
41
  end
42
42
  end
43
43
 
44
44
  describe "adding query params" do
45
- subject { Options.new "/convert/source/foo", { "resize" => "20x20" } }
45
+ subject { Imageproxy::Options.new "/convert/source/foo", { "resize" => "20x20" } }
46
46
  it("should add query params") { subject.resize.should == "20x20" }
47
47
  it("should keep params from path") { subject.source.should == "foo" }
48
48
  end
49
49
 
50
50
  describe "content type" do
51
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" }
52
+ it("should understand .png") { Imageproxy::Options.new("/convert", "source" => "foo.png").content_type.should == "image/png" }
53
+ it("should understand .jpg") { Imageproxy::Options.new("/convert", "source" => "foo.jpg").content_type.should == "image/jpeg" }
54
+ it("should understand .JPEG") { Imageproxy::Options.new("/convert", "source" => "foo.JPEG").content_type.should == "image/jpeg" }
55
55
  end
56
56
  end
57
57
 
58
58
  describe "obfuscation" do
59
59
  it "should allow the query string to be encoded in base64" do
60
60
  encoded = CGI.escape(Base64.encode64("resize=20x20&source=http://example.com/dog.jpg"))
61
- options = Options.new "/convert", "_" => encoded
61
+ options = Imageproxy::Options.new "/convert", "_" => encoded
62
62
  options.resize.should == "20x20"
63
63
  options.source.should == "http://example.com/dog.jpg"
64
64
  end
65
65
 
66
66
  it "should allow the path to be encoded in base64" do
67
67
  encoded = CGI.escape(Base64.encode64("resize/20x20/source/http%3A%2F%2Fexample.com%2Fdog.jpg"))
68
- options = Options.new "/convert/-/#{encoded}", {}
68
+ options = Imageproxy::Options.new "/convert/-/#{encoded}", {}
69
69
  options.resize.should == "20x20"
70
70
  options.source.should == "http://example.com/dog.jpg"
71
71
  end
@@ -73,15 +73,15 @@ describe Options do
73
73
 
74
74
  describe "quality" do
75
75
  it "should be set to 0 if it's less than 0" do
76
- Options.new("/convert", "quality" => "-39").quality.should == "0"
76
+ Imageproxy::Options.new("/convert", "quality" => "-39").quality.should == "0"
77
77
  end
78
78
 
79
79
  it "should be set to 100 if it's > 100" do
80
- Options.new("/convert", "quality" => "293").quality.should == "100"
80
+ Imageproxy::Options.new("/convert", "quality" => "293").quality.should == "100"
81
81
  end
82
82
 
83
83
  it "should not change if it's >= 0 <= 100" do
84
- Options.new("/convert", "quality" => "59").quality.should == "59"
84
+ Imageproxy::Options.new("/convert", "quality" => "59").quality.should == "59"
85
85
  end
86
86
  end
87
87
  end
data/spec/server_spec.rb CHANGED
@@ -16,14 +16,14 @@ describe "Server" do
16
16
  end
17
17
 
18
18
  def app
19
- @app ||= Server.new
19
+ @app ||= Imageproxy::Server.new
20
20
  end
21
21
 
22
22
  context "when converting" do
23
23
  it "should send back the right result" do
24
24
  app.stub!(:config) { |sym| nil }
25
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"
26
+ Imageproxy::Compare.new(response_body_as_file, test_image_path("10x20")).execute.should == "0"
27
27
  end
28
28
  end
29
29
 
@@ -53,13 +53,13 @@ describe "Server" do
53
53
 
54
54
  it "should work if the signature is correct" do
55
55
  url = "/convert/resize/10x20/source/#{escaped_test_image_url}"
56
- signature = Signature.create(url, @secret)
56
+ signature = Imageproxy::Signature.create(url, @secret)
57
57
  get("#{url}?signature=#{CGI.escape(signature)}").should succeed
58
58
  end
59
59
 
60
60
  it "should work if the signature is part of the path" do
61
61
  url = "/convert/resize/10x20/source/#{escaped_test_image_url}"
62
- signature = Signature.create(url, @secret)
62
+ signature = Imageproxy::Signature.create(url, @secret)
63
63
  get("#{url}/signature/#{URI.escape(signature)}").should succeed
64
64
  end
65
65
  end
@@ -1,72 +1,72 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Signature do
3
+ describe Imageproxy::Signature do
4
4
  describe "#create" do
5
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
6
+ Imageproxy::Signature.create("/convert?src=http://www.example.com/dog.jpg&resize=400x400&signature=AAA&key=BBB", "SECRET").should_not be_nil
7
7
  end
8
8
 
9
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")
10
+ Imageproxy::Signature.create("/convert?src=SRC&resize=10x10&signature=SIG&key=KEY", "SECRET").should == Imageproxy::Signature.create("/convert?src=SRC&resize=10x10&key=KEY", "SECRET")
11
11
  end
12
12
  end
13
13
 
14
14
  describe "#correct?" do
15
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
16
+ Imageproxy::Signature.correct?(Imageproxy::Signature.create("/convert?src=SRC&resize=10x10&key=KEY", "SECRET"), "/convert?src=SRC&resize=10x10&key=KEY", "SECRET").should be_true
17
17
  end
18
18
 
19
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
20
+ Imageproxy::Signature.correct?(nil, "/convert?src=SRC&resize=10x10&key=KEY", "SECRET").should be_false
21
21
  end
22
22
 
23
23
  it "should handle URL-safe signatures" do
24
- Signature.correct?("_v70E0zfdcRR4cJehS2mhvqJ-8s=", "YLANEBHFSJGCAWKDNCKWEKJRXKPMYU", "SECRET").should be_true
24
+ Imageproxy::Signature.correct?("_v70E0zfdcRR4cJehS2mhvqJ-8s=", "YLANEBHFSJGCAWKDNCKWEKJRXKPMYU", "SECRET").should be_true
25
25
  end
26
26
 
27
27
  it "should handle non-URL-safe signatures" do
28
- Signature.correct?("k7DMQ/G8YAsbSovX+mDFjlHHMjo=", "YPMMYCRRECCCIPSXPDDFIJFSINOIRC", "SECRET").should be_true
28
+ Imageproxy::Signature.correct?("k7DMQ/G8YAsbSovX+mDFjlHHMjo=", "YPMMYCRRECCCIPSXPDDFIJFSINOIRC", "SECRET").should be_true
29
29
  end
30
30
  end
31
31
 
32
32
  describe "#remove_signature_from" do
33
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 ==
34
+ Imageproxy::Signature.remove_signature_from("/convert/a/apple/b/banana?signature=SIG").should ==
35
35
  "/convert/a/apple/b/banana"
36
36
  end
37
37
 
38
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 ==
39
+ Imageproxy::Signature.remove_signature_from("/convert/a/apple/b/banana?signature=SIG&c=cherry&d=donut").should ==
40
40
  "/convert/a/apple/b/banana?c=cherry&d=donut"
41
41
  end
42
42
 
43
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 ==
44
+ Imageproxy::Signature.remove_signature_from("/convert/a/apple/b/banana?c=cherry&signature=SIG&d=donut").should ==
45
45
  "/convert/a/apple/b/banana?c=cherry&d=donut"
46
46
  end
47
47
 
48
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 ==
49
+ Imageproxy::Signature.remove_signature_from("/convert/a/apple/b/banana?c=cherry&d=donut&signature=SIG").should ==
50
50
  "/convert/a/apple/b/banana?c=cherry&d=donut"
51
51
  end
52
52
 
53
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 ==
54
+ Imageproxy::Signature.remove_signature_from("/convert/signature/SIG/a/apple/b/banana?c=cherry&d=donut").should ==
55
55
  "/convert/a/apple/b/banana?c=cherry&d=donut"
56
56
  end
57
57
 
58
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 ==
59
+ Imageproxy::Signature.remove_signature_from("/convert/a/apple/signature/SIG/b/banana?c=cherry&d=donut").should ==
60
60
  "/convert/a/apple/b/banana?c=cherry&d=donut"
61
61
  end
62
62
 
63
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 ==
64
+ Imageproxy::Signature.remove_signature_from("/convert/a/apple/b/banana/signature/SIG?c=cherry&d=donut").should ==
65
65
  "/convert/a/apple/b/banana?c=cherry&d=donut"
66
66
  end
67
67
 
68
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 ==
69
+ Imageproxy::Signature.remove_signature_from("/convert/a/apple/b/banana/signature/SIG").should ==
70
70
  "/convert/a/apple/b/banana"
71
71
  end
72
72
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: imageproxy
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
5
+ version: 0.1.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Erik Hanson
@@ -163,7 +163,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
- hash: -3683020924727670917
166
+ hash: -2872923147310214835
167
167
  segments:
168
168
  - 0
169
169
  version: "0"