rack-reverse-proxy 0.4.4 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d51045cd47dd0cee3f5afb010a2915f9dd6367ba
4
+ data.tar.gz: c675c804802eb806d6fca45b471630ed0d980268
5
+ SHA512:
6
+ metadata.gz: dada6c70259a95d63705ffad9ad59f54d492b721b98d2496322ba33cec38d1005a778830091dfe44c2f5a2b23a9d4b91ff002d761f90a93a37dc32f4de38c7ad
7
+ data.tar.gz: 7aa447d71a827b37b3a53818723b426c8c0135b90b7c7dcdc2b850dcd1717531e5dddd99afc82ffdcee57f6ebc4a761bb4fba320529f793ad239e21b08a99c3b
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # A Reverse Proxy for Rack
2
+ [![TravisCI](https://secure.travis-ci.org/pex/rack-reverse-proxy.png "Build Status")](http://travis-ci.org/pex/rack-reverse-proxy "Build Status")
3
+
4
+ This is a simple reverse proxy for Rack that pretty heavily rips off Rack Forwarder. It is not meant for production systems (although it may work), as the webserver fronting your app is generally much better at this sort of thing.
5
+
6
+ ## Installation
7
+ The gem is available on gemcutter. Assuming you have a recent version of Rubygems you should just be able to install it via:
8
+
9
+ ```
10
+ gem install rack-reverse-proxy
11
+ ```
12
+
13
+ For your Gemfile use:
14
+
15
+ ```ruby
16
+ gem "rack-reverse-proxy", require: "rack/reverse_proxy"
17
+ ```
18
+
19
+ ## Usage
20
+ Matchers can be a regex or a string. If a regex is used, you can use the subcaptures in your forwarding url by denoting them with a `$`.
21
+
22
+ Right now if more than one matcher matches any given route, it throws an exception for an ambiguous match. This will probably change later. If no match is found, the call is forwarded to your application.
23
+
24
+ Below is an example for configuring the middleware:
25
+
26
+ ```ruby
27
+ require 'rack/reverse_proxy'
28
+
29
+ use Rack::ReverseProxy do
30
+ # Set :preserve_host to true globally (default is true already)
31
+ reverse_proxy_options preserve_host: true
32
+
33
+ # Forward the path /test* to http://example.com/test*
34
+ reverse_proxy '/test', 'http://example.com/'
35
+
36
+ # Forward the path /foo/* to http://example.com/bar/*
37
+ reverse_proxy /^\/foo(\/.*)$/, 'http://example.com/bar$1', username: 'name', password: 'basic_auth_secret'
38
+ end
39
+
40
+ app = proc do |env|
41
+ [ 200, {'Content-Type' => 'text/plain'}, "b" ]
42
+ end
43
+ run app
44
+ ```
45
+
46
+ reverse_proxy_options sets global options for all reverse proxies. Available options are:
47
+ * `:preserve_host` Set to false to omit Host headers
48
+ * `:username` username for basic auth
49
+ * `:password` password for basic auth
50
+ * `:matching` is a global only option, if set to :first the first matched url will be requested (no ambigous error). Default: :all.
51
+ * `:timeout` seconds to timout the requests
52
+
53
+ ## Note on Patches/Pull Requests
54
+ * Fork the project.
55
+ * Make your feature addition or bug fix.
56
+ * Add tests for it. This is important so I don't break it in a
57
+ future version unintentionally.
58
+ * Commit, do not mess with rakefile, version, or history.
59
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
60
+ * Send me a pull request. Bonus points for topic branches.
61
+
62
+ ## Copyright
63
+
64
+ Copyright (c) 2010 Jon Swope. See LICENSE for details.
@@ -0,0 +1,31 @@
1
+ module Rack
2
+ class GenericProxyURI < Exception
3
+ attr_reader :url
4
+
5
+ def intialize(url)
6
+ @url = url
7
+ end
8
+
9
+ def to_s
10
+ %Q(Your URL "#{@url}" is too generic. Did you mean "http://#{@url}"?)
11
+ end
12
+ end
13
+
14
+ class AmbiguousProxyMatch < Exception
15
+ attr_reader :path, :matches
16
+ def initialize(path, matches)
17
+ @path = path
18
+ @matches = matches
19
+ end
20
+
21
+ def to_s
22
+ %Q(Path "#{path}" matched multiple endpoints: #{formatted_matches})
23
+ end
24
+
25
+ private
26
+
27
+ def formatted_matches
28
+ matches.map {|matcher| matcher.to_s}.join(', ')
29
+ end
30
+ end
31
+ end
@@ -1,173 +1,133 @@
1
1
  require 'net/http'
2
2
  require 'net/https'
3
+ require "rack-proxy"
4
+ require "rack/reverse_proxy_matcher"
5
+ require "rack/exception"
3
6
 
4
7
  module Rack
5
8
  class ReverseProxy
9
+ include NewRelic::Agent::Instrumentation::ControllerInstrumentation if defined? NewRelic
10
+
6
11
  def initialize(app = nil, &b)
7
12
  @app = app || lambda {|env| [404, [], []] }
8
13
  @matchers = []
9
- @global_options = {:preserve_host => true, :matching => :all, :verify_ssl => true}
14
+ @global_options = {:preserve_host => true, :x_forwarded_host => true, :matching => :all, :replace_response_host => false}
10
15
  instance_eval &b if block_given?
11
16
  end
12
17
 
13
18
  def call(env)
14
19
  rackreq = Rack::Request.new(env)
15
- matcher = get_matcher rackreq.fullpath
20
+ matcher = get_matcher(rackreq.fullpath, extract_http_request_headers(rackreq.env), rackreq)
16
21
  return @app.call(env) if matcher.nil?
17
22
 
18
- uri = matcher.get_uri(rackreq.fullpath,env)
19
- all_opts = @global_options.dup.merge(matcher.options)
20
- headers = Rack::Utils::HeaderHash.new
21
- env.each { |key, value|
22
- if key =~ /HTTP_(.*)/
23
- headers[$1] = value
23
+ if @global_options[:newrelic_instrumentation]
24
+ action_name = "#{rackreq.path.gsub(/\/\d+/,'/:id').gsub(/^\//,'')}/#{rackreq.request_method}" # Rack::ReverseProxy/foo/bar#GET
25
+ perform_action_with_newrelic_trace(:name => action_name, :request => rackreq) do
26
+ proxy(env, rackreq, matcher)
24
27
  end
25
- }
26
- headers['HOST'] = uri.host if all_opts[:preserve_host]
27
-
28
- session = Net::HTTP.new(uri.host, uri.port)
29
- session.read_timeout=all_opts[:timeout] if all_opts[:timeout]
30
-
31
- session.use_ssl = (uri.scheme == 'https')
32
- if uri.scheme == 'https' && all_opts[:verify_ssl]
33
- session.verify_mode = OpenSSL::SSL::VERIFY_PEER
34
28
  else
35
- # DO NOT DO THIS IN PRODUCTION !!!
36
- session.verify_mode = OpenSSL::SSL::VERIFY_NONE
29
+ proxy(env, rackreq, matcher)
37
30
  end
38
- session.start { |http|
39
- m = rackreq.request_method
40
- case m
41
- when "GET", "HEAD", "DELETE", "OPTIONS", "TRACE"
42
- req = Net::HTTP.const_get(m.capitalize).new(uri.request_uri, headers)
43
- req.basic_auth all_opts[:username], all_opts[:password] if all_opts[:username] and all_opts[:password]
44
- when "PUT", "POST"
45
- req = Net::HTTP.const_get(m.capitalize).new(uri.request_uri, headers)
46
- req.basic_auth all_opts[:username], all_opts[:password] if all_opts[:username] and all_opts[:password]
47
-
48
- if rackreq.body.respond_to?(:read) && rackreq.body.respond_to?(:rewind)
49
- body = rackreq.body.read
50
- req.content_length = body.size
51
- rackreq.body.rewind
52
- else
53
- req.content_length = rackreq.body.size
54
- end
55
-
56
- req.content_type = rackreq.content_type unless rackreq.content_type.nil?
57
- req.body_stream = rackreq.body
58
- else
59
- raise "method not supported: #{m}"
60
- end
61
-
62
- body = ''
63
- res = http.request(req) do |res|
64
- res.read_body do |segment|
65
- body << segment
66
- end
67
- end
68
-
69
- [res.code, create_response_headers(res), [body]]
70
- }
71
31
  end
72
32
 
73
33
  private
74
34
 
75
- def get_matcher path
76
- matches = @matchers.select do |matcher|
77
- matcher.match?(path)
35
+ def proxy(env, source_request, matcher)
36
+ uri = matcher.get_uri(source_request.fullpath,env)
37
+ if uri.nil?
38
+ return @app.call(env)
78
39
  end
40
+ options = @global_options.dup.merge(matcher.options)
79
41
 
80
- if matches.length < 1
81
- nil
82
- elsif matches.length > 1 && @global_options[:matching] != :first
83
- raise AmbiguousProxyMatch.new(path, matches)
84
- else
85
- matches.first
42
+ # Initialize request
43
+ target_request = Net::HTTP.const_get(source_request.request_method.capitalize).new(uri.request_uri)
44
+
45
+ # Setup headers
46
+ target_request_headers = extract_http_request_headers(source_request.env)
47
+
48
+ if options[:preserve_host]
49
+ target_request_headers['HOST'] = "#{uri.host}:#{uri.port}"
86
50
  end
87
- end
88
51
 
89
- def create_response_headers http_response
90
- response_headers = Rack::Utils::HeaderHash.new(http_response.to_hash)
91
- # handled by Rack
92
- response_headers.delete('status')
93
- # TODO: figure out how to handle chunked responses
94
- response_headers.delete('transfer-encoding')
95
- # TODO: Verify Content Length, and required Rack headers
96
- response_headers
97
- end
52
+ if options[:x_forwarded_host]
53
+ target_request_headers['X-Forwarded-Host'] = source_request.host
54
+ target_request_headers['X-Forwarded-Port'] = "#{source_request.port}"
55
+ end
98
56
 
57
+ target_request.initialize_http_header(target_request_headers)
99
58
 
100
- def reverse_proxy_options(options)
101
- @global_options=options
102
- end
59
+ # Basic auth
60
+ target_request.basic_auth options[:username], options[:password] if options[:username] and options[:password]
103
61
 
104
- def reverse_proxy matcher, url, opts={}
105
- raise GenericProxyURI.new(url) if matcher.is_a?(String) && url.is_a?(String) && URI(url).class == URI::Generic
106
- @matchers << ReverseProxyMatcher.new(matcher,url,opts)
107
- end
108
- end
62
+ # Setup body
63
+ if target_request.request_body_permitted? && source_request.body
64
+ source_request.body.rewind
65
+ target_request.body_stream = source_request.body
66
+ end
109
67
 
110
- class GenericProxyURI < Exception
111
- attr_reader :url
68
+ target_request.content_length = source_request.content_length || 0
69
+ target_request.content_type = source_request.content_type if source_request.content_type
112
70
 
113
- def intialize(url)
114
- @url = url
115
- end
71
+ # Create a streaming response (the actual network communication is deferred, a.k.a. streamed)
72
+ target_response = HttpStreamingResponse.new(target_request, uri.host, uri.port)
116
73
 
117
- def to_s
118
- %Q(Your URL "#{@url}" is too generic. Did you mean "http://#{@url}"?)
119
- end
120
- end
74
+ target_response.use_ssl = "https" == uri.scheme
121
75
 
122
- class AmbiguousProxyMatch < Exception
123
- attr_reader :path, :matches
124
- def initialize(path, matches)
125
- @path = path
126
- @matches = matches
127
- end
76
+ # Let rack set the transfer-encoding header
77
+ response_headers = target_response.headers
78
+ response_headers.delete('transfer-encoding')
128
79
 
129
- def to_s
130
- %Q(Path "#{path}" matched multiple endpoints: #{formatted_matches})
80
+ # Replace the location header with the proxy domain
81
+ if response_headers['location'] && options[:replace_response_host]
82
+ response_location = URI(response_headers['location'][0])
83
+ response_location.host = source_request.host
84
+ response_headers['location'] = response_location.to_s
85
+ end
86
+
87
+ [target_response.status, response_headers, target_response.body]
131
88
  end
132
89
 
133
- private
90
+ def extract_http_request_headers(env)
91
+ headers = env.reject do |k, v|
92
+ !(/^HTTP_[A-Z_]+$/ === k) || v.nil?
93
+ end.map do |k, v|
94
+ [reconstruct_header_name(k), v]
95
+ end.inject(Utils::HeaderHash.new) do |hash, k_v|
96
+ k, v = k_v
97
+ hash[k] = v
98
+ hash
99
+ end
134
100
 
135
- def formatted_matches
136
- matches.map {|matcher| matcher.to_s}.join(', ')
137
- end
138
- end
101
+ x_forwarded_for = (headers["X-Forwarded-For"].to_s.split(/, +/) << env["REMOTE_ADDR"]).join(", ")
139
102
 
140
- class ReverseProxyMatcher
141
- def initialize(matching,url,options)
142
- @matching=matching
143
- @url=url
144
- @options=options
145
- @matching_regexp= matching.kind_of?(Regexp) ? matching : /^#{matching.to_s}/
103
+ headers.merge!("X-Forwarded-For" => x_forwarded_for)
146
104
  end
147
105
 
148
- attr_reader :matching,:matching_regexp,:url,:options
149
-
150
- def match?(path)
151
- match_path(path) ? true : false
106
+ def reconstruct_header_name(name)
107
+ name.sub(/^HTTP_/, "").gsub("_", "-")
152
108
  end
153
109
 
154
- def get_uri(path,env)
155
- _url=(url.respond_to?(:call) ? url.call(env) : url.clone)
156
- if _url =~/\$\d/
157
- match_path(path).to_a.each_with_index { |m, i| _url.gsub!("$#{i.to_s}", m) }
158
- URI(_url)
110
+ def get_matcher(path, headers, rackreq)
111
+ matches = @matchers.select do |matcher|
112
+ matcher.match?(path, headers, rackreq)
113
+ end
114
+
115
+ if matches.length < 1
116
+ nil
117
+ elsif matches.length > 1 && @global_options[:matching] != :first
118
+ raise AmbiguousProxyMatch.new(path, matches)
159
119
  else
160
- URI.join(_url, path)
120
+ matches.first
161
121
  end
162
122
  end
163
- def to_s
164
- %Q("#{matching.to_s}" => "#{url}")
165
- end
166
- private
167
- def match_path(path)
168
- path.match(matching_regexp)
169
- end
170
123
 
124
+ def reverse_proxy_options(options)
125
+ @global_options=options
126
+ end
171
127
 
128
+ def reverse_proxy(matcher, url=nil, opts={})
129
+ raise GenericProxyURI.new(url) if matcher.is_a?(String) && url.is_a?(String) && URI(url).class == URI::Generic
130
+ @matchers << ReverseProxyMatcher.new(matcher,url,opts)
131
+ end
172
132
  end
173
133
  end
@@ -0,0 +1,53 @@
1
+ module Rack
2
+ class ReverseProxyMatcher
3
+ def initialize(matcher,url=nil,options)
4
+ @default_url=url
5
+ @url=url
6
+ @options=options
7
+
8
+ if matcher.kind_of?(String)
9
+ @matcher = /^#{matcher.to_s}/
10
+ elsif matcher.respond_to?(:match)
11
+ @matcher = matcher
12
+ else
13
+ raise "Invalid Matcher for reverse_proxy"
14
+ end
15
+ end
16
+
17
+ attr_reader :matcher,:url, :default_url,:options
18
+
19
+ def match?(path, *args)
20
+ match_path(path, *args) ? true : false
21
+ end
22
+
23
+ def get_uri(path,env)
24
+ return nil if url.nil?
25
+ _url=(url.respond_to?(:call) ? url.call(env) : url.clone)
26
+ if _url =~/\$\d/
27
+ match_path(path).to_a.each_with_index { |m, i| _url.gsub!("$#{i.to_s}", m) }
28
+ URI(_url)
29
+ else
30
+ default_url.nil? ? URI.parse(_url) : URI.join(_url, path)
31
+ end
32
+ end
33
+
34
+ def to_s
35
+ %Q("#{matcher.to_s}" => "#{url}")
36
+ end
37
+
38
+ private
39
+ def match_path(path, *args)
40
+ headers = args[0]
41
+ rackreq = args[1]
42
+ arity = matcher.method(:match).arity
43
+ if arity == -1
44
+ match = matcher.match(path)
45
+ else
46
+ params = [path, (@options[:accept_headers] ? headers : nil), rackreq]
47
+ match = matcher.match(*params[0..(arity - 1)])
48
+ end
49
+ @url = match.url(path) if match && default_url.nil?
50
+ match
51
+ end
52
+ end
53
+ end
metadata CHANGED
@@ -1,125 +1,167 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-reverse-proxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
5
- prerelease:
4
+ version: 0.8.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jon Swope
8
+ - Ian Ehlert
9
+ - Roman Ernst
10
+ - Oleksii Fedorov
9
11
  autorequire:
10
12
  bindir: bin
11
13
  cert_chain: []
12
- date: 2012-01-26 00:00:00.000000000Z
14
+ date: 2015-05-24 00:00:00.000000000 Z
13
15
  dependencies:
14
16
  - !ruby/object:Gem::Dependency
15
17
  name: rspec
16
- requirement: &21582760 !ruby/object:Gem::Requirement
17
- none: false
18
+ requirement: !ruby/object:Gem::Requirement
18
19
  requirements:
19
- - - ~>
20
+ - - "~>"
20
21
  - !ruby/object:Gem::Version
21
- version: 1.3.2
22
+ version: '3.1'
22
23
  type: :development
23
24
  prerelease: false
24
- version_requirements: *21582760
25
- - !ruby/object:Gem::Dependency
26
- name: bundler
27
- requirement: &21581540 !ruby/object:Gem::Requirement
28
- none: false
25
+ version_requirements: !ruby/object:Gem::Requirement
29
26
  requirements:
30
- - - ~>
27
+ - - "~>"
31
28
  - !ruby/object:Gem::Version
32
- version: 1.0.15
33
- type: :development
34
- prerelease: false
35
- version_requirements: *21581540
29
+ version: '3.1'
36
30
  - !ruby/object:Gem::Dependency
37
31
  name: rake
38
- requirement: &21580900 !ruby/object:Gem::Requirement
39
- none: false
32
+ requirement: !ruby/object:Gem::Requirement
40
33
  requirements:
41
- - - ~>
34
+ - - "~>"
42
35
  - !ruby/object:Gem::Version
43
- version: 0.8.7
36
+ version: '10.3'
44
37
  type: :development
45
38
  prerelease: false
46
- version_requirements: *21580900
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '10.3'
47
44
  - !ruby/object:Gem::Dependency
48
45
  name: rack-test
49
- requirement: &21580220 !ruby/object:Gem::Requirement
50
- none: false
46
+ requirement: !ruby/object:Gem::Requirement
51
47
  requirements:
52
- - - ~>
48
+ - - "~>"
53
49
  - !ruby/object:Gem::Version
54
- version: 0.5.7
50
+ version: '0.6'
55
51
  type: :development
56
52
  prerelease: false
57
- version_requirements: *21580220
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '0.6'
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: webmock
60
- requirement: &21578760 !ruby/object:Gem::Requirement
61
- none: false
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - "~>"
63
+ - !ruby/object:Gem::Version
64
+ version: '1.18'
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '1.18'
72
+ - !ruby/object:Gem::Dependency
73
+ name: guard-rspec
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
62
82
  requirements:
63
- - - ~>
83
+ - - ">="
64
84
  - !ruby/object:Gem::Version
65
- version: 1.5.0
85
+ version: '0'
86
+ - !ruby/object:Gem::Dependency
87
+ name: guard-bundler
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
66
93
  type: :development
67
94
  prerelease: false
68
- version_requirements: *21578760
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
69
100
  - !ruby/object:Gem::Dependency
70
101
  name: rack
71
- requirement: &21577540 !ruby/object:Gem::Requirement
72
- none: false
102
+ requirement: !ruby/object:Gem::Requirement
73
103
  requirements:
74
- - - ! '>='
104
+ - - ">="
75
105
  - !ruby/object:Gem::Version
76
106
  version: 1.0.0
77
107
  type: :runtime
78
108
  prerelease: false
79
- version_requirements: *21577540
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: 1.0.0
114
+ - !ruby/object:Gem::Dependency
115
+ name: rack-proxy
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: '0.5'
121
+ type: :runtime
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: '0.5'
80
128
  description: A Rack based reverse proxy for basic needs. Useful for testing or in
81
129
  cases where webserver configuration is unavailable.
82
- email: jaswope@gmail.com
130
+ email:
131
+ - jaswope@gmail.com
132
+ - ehlertij@gmail.com
133
+ - rernst@farbenmeer.net
134
+ - waterlink000@gmail.com
83
135
  executables: []
84
136
  extensions: []
85
- extra_rdoc_files:
86
- - LICENSE
87
- - README.rdoc
137
+ extra_rdoc_files: []
88
138
  files:
89
- - .document
90
139
  - LICENSE
91
- - README.rdoc
92
- - Rakefile
93
- - VERSION
140
+ - README.md
141
+ - lib/rack/exception.rb
94
142
  - lib/rack/reverse_proxy.rb
95
- - rack-reverse-proxy.gemspec
96
- - spec/rack/reverse_proxy_spec.rb
97
- - spec/spec.opts
98
- - spec/spec_helper.rb
99
- homepage: http://github.com/jaswope/rack-reverse-proxy
143
+ - lib/rack/reverse_proxy_matcher.rb
144
+ homepage: http://github.com/waterlink/rack-reverse-proxy
100
145
  licenses: []
146
+ metadata: {}
101
147
  post_install_message:
102
148
  rdoc_options: []
103
149
  require_paths:
104
150
  - lib
105
151
  required_ruby_version: !ruby/object:Gem::Requirement
106
- none: false
107
152
  requirements:
108
- - - ! '>='
153
+ - - ">="
109
154
  - !ruby/object:Gem::Version
110
155
  version: '0'
111
156
  required_rubygems_version: !ruby/object:Gem::Requirement
112
- none: false
113
157
  requirements:
114
- - - ! '>='
158
+ - - ">="
115
159
  - !ruby/object:Gem::Version
116
160
  version: '0'
117
161
  requirements: []
118
162
  rubyforge_project:
119
- rubygems_version: 1.8.10
163
+ rubygems_version: 2.2.2
120
164
  signing_key:
121
- specification_version: 3
165
+ specification_version: 4
122
166
  summary: A Simple Reverse Proxy for Rack
123
- test_files:
124
- - spec/rack/reverse_proxy_spec.rb
125
- - spec/spec_helper.rb
167
+ test_files: []
data/.document DELETED
@@ -1,5 +0,0 @@
1
- README.rdoc
2
- lib/**/*.rb
3
- bin/*
4
- features/**/*.feature
5
- LICENSE
data/README.rdoc DELETED
@@ -1,56 +0,0 @@
1
- = A Reverse Proxy for Rack
2
-
3
- This is a simple reverse proxy for Rack that pretty heavily rips off Rack Forwarder. It is not meant for production systems (although it may work), as the webserver fronting your app is generally much better at this sort of thing.
4
-
5
- == Installation
6
- The gem is available on gemcutter. Assuming you have a recent version of Rubygems you should just be able to install it via:
7
- gem install rack-reverse-proxy
8
-
9
- For your Gemfile use:
10
- gem "rack-reverse-proxy", :require => "rack/reverse_proxy"
11
-
12
- == Usage
13
- Matchers can be a regex or a string. If a regex is used, you can use the subcaptures in your forwarding url by denoting them with a $.
14
-
15
- Right now if more than one matcher matches any given route, it throws an exception for an ambiguous match. This will probably change later. If no match is found, the call is forwarded to your application.
16
-
17
- Below is an example for configuring the middleware:
18
-
19
- require 'rack/reverse_proxy'
20
-
21
- use Rack::ReverseProxy do
22
- # Set :preserve_host to true globally (default is true already)
23
- reverse_proxy_options :preserve_host => true
24
-
25
- # Forward the path /test* to http://example.com/test*
26
- reverse_proxy '/test', 'http://example.com/'
27
-
28
- # Forward the path /foo/* to http://example.com/bar/*
29
- reverse_proxy /^\/foo(\/.*)$/, 'http://example.com/bar$1', :username => 'name', :password => 'basic_auth_secret'
30
- end
31
-
32
- app = proc do |env|
33
- [ 200, {'Content-Type' => 'text/plain'}, "b" ]
34
- end
35
- run app
36
-
37
- reverse_proxy_options sets global options for all reverse proxies. Available options are:
38
- * :preserve_host Set to false to omit Host headers
39
- * :username username for basic auth
40
- * :password password for basic auth
41
- * :matching is a global only option, if set to :first the first matched url will be requested (no ambigous error). Default: :all.
42
- * :timeout seconds to timout the requests
43
-
44
- == Note on Patches/Pull Requests
45
-
46
- * Fork the project.
47
- * Make your feature addition or bug fix.
48
- * Add tests for it. This is important so I don't break it in a
49
- future version unintentionally.
50
- * Commit, do not mess with rakefile, version, or history.
51
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
52
- * Send me a pull request. Bonus points for topic branches.
53
-
54
- == Copyright
55
-
56
- Copyright (c) 2010 Jon Swope. See LICENSE for details.
data/Rakefile DELETED
@@ -1,27 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'bundler/gem_tasks'
4
-
5
- require 'spec/rake/spectask'
6
- Spec::Rake::SpecTask.new(:spec) do |spec|
7
- spec.libs << 'lib' << 'spec'
8
- spec.spec_files = FileList['spec/**/*_spec.rb']
9
- end
10
-
11
- Spec::Rake::SpecTask.new(:rcov) do |spec|
12
- spec.libs << 'lib' << 'spec'
13
- spec.pattern = 'spec/**/*_spec.rb'
14
- spec.rcov = true
15
- end
16
-
17
- task :default => :spec
18
-
19
- require 'rake/rdoctask'
20
- Rake::RDocTask.new do |rdoc|
21
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
22
-
23
- rdoc.rdoc_dir = 'rdoc'
24
- rdoc.title = "rack-reverse-proxy #{version}"
25
- rdoc.rdoc_files.include('README*')
26
- rdoc.rdoc_files.include('lib/**/*.rb')
27
- end
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.4.4
@@ -1,42 +0,0 @@
1
- Gem::Specification.new do |s|
2
- s.name = %q{rack-reverse-proxy}
3
- s.version = "0.4.4"
4
-
5
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
- s.authors = ["Jon Swope"]
7
- s.date = %q{2012-01-26}
8
- s.description = %q{A Rack based reverse proxy for basic needs. Useful for testing or in cases where webserver configuration is unavailable.}
9
- s.email = %q{jaswope@gmail.com}
10
- s.extra_rdoc_files = [
11
- "LICENSE",
12
- "README.rdoc"
13
- ]
14
- s.files = [
15
- ".document",
16
- "LICENSE",
17
- "README.rdoc",
18
- "Rakefile",
19
- "VERSION",
20
- "lib/rack/reverse_proxy.rb",
21
- "rack-reverse-proxy.gemspec",
22
- "spec/rack/reverse_proxy_spec.rb",
23
- "spec/spec.opts",
24
- "spec/spec_helper.rb"
25
- ]
26
- s.homepage = %q{http://github.com/jaswope/rack-reverse-proxy}
27
- s.require_paths = ["lib"]
28
- s.rubygems_version = %q{1.3.7}
29
- s.summary = %q{A Simple Reverse Proxy for Rack}
30
- s.test_files = [
31
- "spec/rack/reverse_proxy_spec.rb",
32
- "spec/spec_helper.rb"
33
- ]
34
-
35
- s.add_development_dependency "rspec", "~> 1.3.2"
36
- s.add_development_dependency "bundler", "~> 1.0.15"
37
- s.add_development_dependency "rake", "~> 0.8.7"
38
- s.add_development_dependency "rack-test", "~> 0.5.7"
39
- s.add_development_dependency "webmock", "~> 1.5.0"
40
- s.add_dependency "rack", ">= 1.0.0"
41
- end
42
-
@@ -1,209 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe Rack::ReverseProxy do
4
- include Rack::Test::Methods
5
- include WebMock::API
6
-
7
- def app
8
- Rack::ReverseProxy.new
9
- end
10
-
11
- def dummy_app
12
- lambda { |env| [200, {}, ['Dummy App']] }
13
- end
14
-
15
- describe "as middleware" do
16
- def app
17
- Rack::ReverseProxy.new(dummy_app) do
18
- reverse_proxy '/test', 'http://example.com/', {:preserve_host => true}
19
- reverse_proxy '/2test', lambda{ |env| 'http://example.com/'}
20
- end
21
- end
22
-
23
- it "should forward requests to the calling app when the path is not matched" do
24
- get '/'
25
- last_response.body.should == "Dummy App"
26
- last_response.should be_ok
27
- end
28
-
29
- it "should proxy requests when a pattern is matched" do
30
- stub_request(:get, 'http://example.com/test').to_return({:body => "Proxied App"})
31
- get '/test'
32
- last_response.body.should == "Proxied App"
33
- end
34
-
35
- it "should proxy requests to a lambda url when a pattern is matched" do
36
- stub_request(:get, 'http://example.com/2test').to_return({:body => "Proxied App2"})
37
- get '/2test'
38
- last_response.body.should == "Proxied App2"
39
- end
40
-
41
- it "the response header should never contain Status" do
42
- stub_request(:any, 'example.com/test/stuff').to_return(:headers => {'Status' => '200 OK'})
43
- get '/test/stuff'
44
- last_response.headers['Status'].should == nil
45
- end
46
-
47
- it "the response header should never transfer-encoding" do
48
- stub_request(:any, 'example.com/test/stuff').to_return(:headers => {'transfer-encoding' => 'Chunked'})
49
- get '/test/stuff'
50
- last_response.headers['transfer-encoding'].should == nil
51
- end
52
-
53
- it "should set the Host header" do
54
- stub_request(:any, 'example.com/test/stuff')
55
- get '/test/stuff'
56
- a_request(:get, 'http://example.com/test/stuff').with(:headers => {"Host" => "example.com"}).should have_been_made
57
- end
58
-
59
- describe "with preserve host turned off" do
60
- def app
61
- Rack::ReverseProxy.new(dummy_app) do
62
- reverse_proxy '/test', 'http://example.com/', {:preserve_host => false}
63
- end
64
- end
65
-
66
- it "should not set the Host header" do
67
- stub_request(:any, 'example.com/test/stuff')
68
- get '/test/stuff'
69
- a_request(:get, 'http://example.com/test/stuff').with(:headers => {"Host" => "example.com"}).should_not have_been_made
70
- a_request(:get, 'http://example.com/test/stuff').should have_been_made
71
- end
72
- end
73
-
74
- describe "with basic auth turned on" do
75
- def app
76
- Rack::ReverseProxy.new(dummy_app) do
77
- reverse_proxy '/test', 'http://example.com/', {:username => "joe", :password => "shmoe"}
78
- end
79
- end
80
-
81
- it "should make request with basic auth" do
82
- stub_request(:get, "http://joe:shmoe@example.com/test/stuff").to_return(:body => "secured content")
83
- get '/test/stuff'
84
- last_response.body.should == "secured content"
85
- end
86
- end
87
-
88
- describe "with ambiguous routes and all matching" do
89
- def app
90
- Rack::ReverseProxy.new(dummy_app) do
91
- reverse_proxy_options :matching => :all
92
- reverse_proxy '/test', 'http://example.com/'
93
- reverse_proxy /^\/test/, 'http://example.com/'
94
- end
95
- end
96
-
97
- it "should throw an exception" do
98
- lambda { get '/test' }.should raise_error(Rack::AmbiguousProxyMatch)
99
- end
100
- end
101
-
102
- describe "with ambiguous routes and first matching" do
103
- def app
104
- Rack::ReverseProxy.new(dummy_app) do
105
- reverse_proxy_options :matching => :first
106
- reverse_proxy '/test', 'http://example1.com/'
107
- reverse_proxy /^\/test/, 'http://example2.com/'
108
- end
109
- end
110
-
111
- it "should throw an exception" do
112
- stub_request(:get, 'http://example1.com/test').to_return({:body => "Proxied App"})
113
- get '/test'
114
- last_response.body.should == "Proxied App"
115
- end
116
- end
117
-
118
- describe "with a route as a regular expression" do
119
- def app
120
- Rack::ReverseProxy.new(dummy_app) do
121
- reverse_proxy %r|^/test(/.*)$|, 'http://example.com$1'
122
- end
123
- end
124
-
125
- it "should support subcaptures" do
126
- stub_request(:get, 'http://example.com/path').to_return({:body => "Proxied App"})
127
- get '/test/path'
128
- last_response.body.should == "Proxied App"
129
- end
130
- end
131
-
132
- describe "with a https route" do
133
- def app
134
- Rack::ReverseProxy.new(dummy_app) do
135
- reverse_proxy '/test', 'https://example.com'
136
- end
137
- end
138
-
139
- it "should make a secure request" do
140
- stub_request(:get, 'https://example.com/test/stuff').to_return({:body => "Proxied Secure App"})
141
- get '/test/stuff'
142
- last_response.body.should == "Proxied Secure App"
143
- end
144
-
145
- end
146
-
147
- describe "with a route as a string" do
148
- def app
149
- Rack::ReverseProxy.new(dummy_app) do
150
- reverse_proxy '/test', 'http://example.com'
151
- reverse_proxy '/path', 'http://example.com/foo$0'
152
- end
153
- end
154
-
155
- it "should append the full path to the uri" do
156
- stub_request(:get, 'http://example.com/test/stuff').to_return({:body => "Proxied App"})
157
- get '/test/stuff'
158
- last_response.body.should == "Proxied App"
159
- end
160
-
161
- end
162
-
163
- describe "with a generic url" do
164
- def app
165
- Rack::ReverseProxy.new(dummy_app) do
166
- reverse_proxy '/test', 'example.com'
167
- end
168
- end
169
-
170
- it "should throw an exception" do
171
- lambda{ app }.should raise_error(Rack::GenericProxyURI)
172
- end
173
- end
174
-
175
- describe "with a matching route" do
176
- def app
177
- Rack::ReverseProxy.new(dummy_app) do
178
- reverse_proxy '/test', 'http://example.com/'
179
- end
180
- end
181
-
182
- %w|get head delete put post|.each do |method|
183
- describe "and using method #{method}" do
184
- it "should forward the correct request" do
185
- stub_request(method.to_sym, 'http://example.com/test').to_return({:body => "Proxied App for #{method}"})
186
- eval "#{method} '/test'"
187
- last_response.body.should == "Proxied App for #{method}"
188
- end
189
-
190
- if %w|put post|.include?(method)
191
- it "should forward the request payload" do
192
- stub_request(method.to_sym, 'http://example.com/test').to_return { |req| {:body => req.body} }
193
- eval "#{method} '/test', {:test => 'test'}"
194
- last_response.body.should == "test=test"
195
- end
196
- end
197
- end
198
- end
199
- end
200
- end
201
-
202
- describe "as a rack app" do
203
- it "should respond with 404 when the path is not matched" do
204
- get '/'
205
- last_response.should be_not_found
206
- end
207
- end
208
-
209
- end
data/spec/spec.opts DELETED
@@ -1,2 +0,0 @@
1
- --color
2
- --format specdoc
data/spec/spec_helper.rb DELETED
@@ -1,13 +0,0 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
- require 'rack/reverse_proxy'
4
- require 'spec'
5
- require 'spec/autorun'
6
- require 'rubygems'
7
- require 'rack/test'
8
- require 'webmock'
9
- require 'webmock/rspec'
10
-
11
- Spec::Runner.configure do |config|
12
- WebMock.disable_net_connect!
13
- end