whack-a-node 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -1
- data/Gemfile.lock +8 -3
- data/README.rdoc +43 -5
- data/Rakefile +0 -1
- data/VERSION +1 -1
- data/lib/whack-a-node.rb +1 -105
- data/lib/whack_a_node.rb +4 -0
- data/lib/whack_a_node/proxy.rb +44 -0
- data/lib/whack_a_node/redirect.rb +19 -0
- data/lib/whack_a_node/rpc.rb +104 -0
- data/lib/whack_a_node/whacky.rb +13 -0
- data/spec/rpc_spec.rb +24 -0
- data/spec/spec_helper.rb +0 -3
- data/spec/whack-a-node_spec.rb +10 -4
- metadata +67 -27
- data/.DS_Store +0 -0
- data/lib/net_http_hacked.rb +0 -91
- data/lib/rack/http_streaming_response.rb +0 -63
- data/lib/rack/reverse_proxy.rb +0 -142
- data/lib/rack/streaming_proxy.rb +0 -70
- data/spec/rack/reverse_proxy_spec.rb +0 -180
- data/spec/rack/streaming_proxy_spec.rb +0 -16
data/Gemfile
CHANGED
@@ -2,8 +2,11 @@ source "http://rubygems.org"
|
|
2
2
|
# Add dependencies required to use your gem here.
|
3
3
|
# Example:
|
4
4
|
# gem "activesupport", ">= 2.3.5"
|
5
|
-
gem 'rack-proxy'
|
6
5
|
gem 'rack'
|
6
|
+
gem 'dnode'
|
7
|
+
gem 'eventmachine'
|
8
|
+
gem 'events'
|
9
|
+
gem 'json'
|
7
10
|
|
8
11
|
# Add dependencies to develop your gem here.
|
9
12
|
# Include everything needed to run rake, tests, features, etc.
|
data/Gemfile.lock
CHANGED
@@ -4,14 +4,16 @@ GEM
|
|
4
4
|
addressable (2.2.5)
|
5
5
|
crack (0.1.8)
|
6
6
|
diff-lcs (1.1.2)
|
7
|
+
dnode (0.0.1)
|
8
|
+
eventmachine (0.12.10)
|
9
|
+
events (0.9.5)
|
7
10
|
git (1.2.5)
|
8
11
|
jeweler (1.5.2)
|
9
12
|
bundler (~> 1.0.0)
|
10
13
|
git (>= 1.2.5)
|
11
14
|
rake
|
15
|
+
json (1.5.1)
|
12
16
|
rack (1.2.2)
|
13
|
-
rack-proxy (0.3.4)
|
14
|
-
rack
|
15
17
|
rack-test (0.5.7)
|
16
18
|
rack (>= 1.0)
|
17
19
|
rake (0.8.7)
|
@@ -33,9 +35,12 @@ PLATFORMS
|
|
33
35
|
|
34
36
|
DEPENDENCIES
|
35
37
|
bundler (~> 1.0.0)
|
38
|
+
dnode
|
39
|
+
eventmachine
|
40
|
+
events
|
36
41
|
jeweler (~> 1.5.2)
|
42
|
+
json
|
37
43
|
rack
|
38
|
-
rack-proxy
|
39
44
|
rack-test
|
40
45
|
rcov
|
41
46
|
rspec (~> 2.3.0)
|
data/README.rdoc
CHANGED
@@ -4,20 +4,58 @@ Node apps running in any rack server as a endpoint, and potentially a middleware
|
|
4
4
|
|
5
5
|
Leverage authentication, authorization and more in front of your node apps, letting node be used for high concurrency portions of your app.
|
6
6
|
|
7
|
+
Reach a node app as an RPC directly from your Rails models (coming soon)
|
8
|
+
|
9
|
+
== Prerequisites
|
10
|
+
|
11
|
+
* Ruby 1.8.7 or 1.9.2 work fine
|
12
|
+
* Node.js
|
13
|
+
brew install node (or equivalent for your environment)
|
14
|
+
* Rack[http://rack.rubyforge.org/] (or Rails 3)
|
15
|
+
|
16
|
+
== Installation
|
17
|
+
|
18
|
+
sudo gem install whack-a-node
|
19
|
+
|
7
20
|
== Usage
|
8
21
|
|
9
|
-
|
22
|
+
In Rails 3's routes.rb:
|
10
23
|
|
11
|
-
|
24
|
+
match "/proxy" => WhackANode::Proxy.new("/")
|
12
25
|
|
13
|
-
|
26
|
+
This will attempt to reach a node server on localhost at port 8810. The response will be proxied by Rack.
|
14
27
|
|
15
|
-
|
28
|
+
match "/redirect" => WhackANode::Redirect.new("/","www.example.com", 2306)
|
16
29
|
|
17
|
-
|
30
|
+
This will redirect to a node server at www.example.com on port 2306. In some cases, this can result in faster performance, as you don't have to have a Rack instance sitting around for the whole request.
|
18
31
|
|
19
32
|
Generally speaking, using the proxy over a Rails controller ends up being about 8-10 times faster. While still much slower than Node directly, this can still yield benefit.
|
20
33
|
|
34
|
+
== Examples
|
35
|
+
|
36
|
+
=== Node server code
|
37
|
+
|
38
|
+
server.js:
|
39
|
+
|
40
|
+
var http = require('http');
|
41
|
+
http.createServer(function (req, res) {
|
42
|
+
res.writeHead(200, {'Content-Type': 'text/plain'});
|
43
|
+
res.end('Hello World\n');
|
44
|
+
}).listen(8810, "127.0.0.1");
|
45
|
+
console.log('Server running at http://127.0.0.1:8810/');
|
46
|
+
|
47
|
+
Start the node server by running node server.js
|
48
|
+
|
49
|
+
=== Rails server
|
50
|
+
Create a new Rails app. Add the following to routes.rb
|
51
|
+
|
52
|
+
match "/proxy" => WhackANode::Proxy.new("/") -- WhackANode defaults to localhost port 8810, so this will work with above server
|
53
|
+
|
54
|
+
Add Whack-A-Node to your Gemfile:
|
55
|
+
|
56
|
+
gem 'whack-a-node'
|
57
|
+
|
58
|
+
Start your Rails app. Hit http://localhost:3000/proxy. You should see 'Hello World' generated by Node.js
|
21
59
|
|
22
60
|
== Contributing to whack-a-node
|
23
61
|
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/lib/whack-a-node.rb
CHANGED
@@ -1,105 +1 @@
|
|
1
|
-
require
|
2
|
-
class WhackANode
|
3
|
-
|
4
|
-
def initialize(path,host="localhost", port="8810",redirect=false)
|
5
|
-
@path = path
|
6
|
-
@host = host
|
7
|
-
@port = port
|
8
|
-
@redirect = redirect
|
9
|
-
@paths = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
def uri
|
13
|
-
URI("http://#{@host}:#{@port}#{@path}")
|
14
|
-
end
|
15
|
-
|
16
|
-
def proxy_request
|
17
|
-
uri = self.uri
|
18
|
-
session = Net::HTTP.new(uri.host, uri.port)
|
19
|
-
session.start {|http|
|
20
|
-
req = Net::HTTP::Get.new(uri.request_uri)
|
21
|
-
body = ''
|
22
|
-
res = http.request(req) do |res|
|
23
|
-
res.read_body do |segment|
|
24
|
-
body << segment
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
[res.code, create_response_headers(res), [body]]
|
29
|
-
}
|
30
|
-
end
|
31
|
-
|
32
|
-
def forward_request
|
33
|
-
[ 302, {'Location'=> uri.to_s }, [] ]
|
34
|
-
end
|
35
|
-
|
36
|
-
def call(env)
|
37
|
-
return @redirect ? forward_request : proxy_request
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
|
43
|
-
def get_matcher_and_url path
|
44
|
-
matches = @paths.select do |matcher, url|
|
45
|
-
match_path(path, matcher)
|
46
|
-
end
|
47
|
-
|
48
|
-
if matches.length < 1
|
49
|
-
nil
|
50
|
-
elsif matches.length > 1
|
51
|
-
raise AmbiguousProxyMatch.new(path, matches)
|
52
|
-
else
|
53
|
-
matches.first.map{|a| a.dup}
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def create_response_headers http_response
|
58
|
-
response_headers = Rack::Utils::HeaderHash.new(http_response.to_hash)
|
59
|
-
# handled by Rack
|
60
|
-
response_headers.delete('status')
|
61
|
-
# TODO: figure out how to handle chunked responses
|
62
|
-
response_headers.delete('transfer-encoding')
|
63
|
-
# TODO: Verify Content Length, and required Rack headers
|
64
|
-
response_headers
|
65
|
-
end
|
66
|
-
|
67
|
-
def match_path(path, matcher)
|
68
|
-
if matcher.is_a?(Regexp)
|
69
|
-
path.match(matcher)
|
70
|
-
else
|
71
|
-
path.match(/^#{matcher.to_s}/)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def get_uri(url, matcher, path)
|
76
|
-
if url =~/\$\d/
|
77
|
-
match_path(path, matcher).to_a.each_with_index { |m, i| url.gsub!("$#{i.to_s}", m) }
|
78
|
-
URI(url)
|
79
|
-
else
|
80
|
-
URI.join(url, path)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def reverse_proxy matcher, url, opts={}
|
85
|
-
raise GenericProxyURI.new(url) if matcher.is_a?(String) && URI(url).class == URI::Generic
|
86
|
-
@paths.merge!(matcher => url)
|
87
|
-
@opts.merge!(opts)
|
88
|
-
end
|
89
|
-
|
90
|
-
|
91
|
-
def rewrite_env(env)
|
92
|
-
env["PORT"] = "8000"
|
93
|
-
|
94
|
-
env
|
95
|
-
end
|
96
|
-
|
97
|
-
def rewrite_response(triplet)
|
98
|
-
status, headers, body = triplet
|
99
|
-
|
100
|
-
headers["X-Foo"] = "Bar"
|
101
|
-
|
102
|
-
triplet
|
103
|
-
end
|
104
|
-
|
105
|
-
end
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/whack_a_node")
|
data/lib/whack_a_node.rb
ADDED
@@ -0,0 +1,4 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/whack_a_node/proxy')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/whack_a_node/redirect')
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/whack_a_node/whacky')
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/whack_a_node/rpc')
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
module WhackANode
|
3
|
+
class Proxy
|
4
|
+
|
5
|
+
def initialize(path="/",host="localhost", port="8810")
|
6
|
+
@path = path
|
7
|
+
@host = host
|
8
|
+
@port = port
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
uri = self.uri
|
13
|
+
session = Net::HTTP.new(uri.host, uri.port)
|
14
|
+
session.start {|http|
|
15
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
16
|
+
body = ''
|
17
|
+
res = http.request(req) do |res|
|
18
|
+
res.read_body do |segment|
|
19
|
+
body << segment
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
[res.code, create_response_headers(res), [body]]
|
24
|
+
}
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def uri
|
29
|
+
URI("http://#{@host}:#{@port}#{@path}")
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def create_response_headers http_response
|
35
|
+
response_headers = Rack::Utils::HeaderHash.new(http_response.to_hash)
|
36
|
+
# handled by Rack
|
37
|
+
response_headers.delete('status')
|
38
|
+
# TODO: figure out how to handle chunked responses
|
39
|
+
response_headers.delete('transfer-encoding')
|
40
|
+
# TODO: Verify Content Length, and required Rack headers
|
41
|
+
response_headers
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module WhackANode
|
2
|
+
class Redirect
|
3
|
+
|
4
|
+
def initialize(path="/",host="localhost", port="8810")
|
5
|
+
@path = path
|
6
|
+
@host = host
|
7
|
+
@port = port
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
[ 302, {'Location'=> uri.to_s }, [] ]
|
12
|
+
end
|
13
|
+
|
14
|
+
def uri
|
15
|
+
URI("http://#{@host}:#{@port}#{@path}")
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'dnode'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'events'
|
4
|
+
module WhackANode
|
5
|
+
class Rpc
|
6
|
+
def initialize(path="/",host="localhost", port="8820", redirect=false)
|
7
|
+
@path = path
|
8
|
+
@host = host
|
9
|
+
@port = port
|
10
|
+
@redirect = redirect
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def proxy_request
|
15
|
+
uri = self.uri
|
16
|
+
session = Net::HTTP.new(uri.host, uri.port)
|
17
|
+
session.start {|http|
|
18
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
19
|
+
body = ''
|
20
|
+
res = http.request(req) do |res|
|
21
|
+
res.read_body do |segment|
|
22
|
+
body << segment
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
[res.code, create_response_headers(res), [body]]
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def forward_request
|
31
|
+
[ 302, {'Location'=> uri.to_s }, [] ]
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(env)
|
35
|
+
return @redirect ? forward_request : proxy_request
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
|
41
|
+
def get_matcher_and_url path
|
42
|
+
matches = @paths.select do |matcher, url|
|
43
|
+
match_path(path, matcher)
|
44
|
+
end
|
45
|
+
|
46
|
+
if matches.length < 1
|
47
|
+
nil
|
48
|
+
elsif matches.length > 1
|
49
|
+
raise AmbiguousProxyMatch.new(path, matches)
|
50
|
+
else
|
51
|
+
matches.first.map{|a| a.dup}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_response_headers http_response
|
56
|
+
response_headers = Rack::Utils::HeaderHash.new(http_response.to_hash)
|
57
|
+
# handled by Rack
|
58
|
+
response_headers.delete('status')
|
59
|
+
# TODO: figure out how to handle chunked responses
|
60
|
+
response_headers.delete('transfer-encoding')
|
61
|
+
# TODO: Verify Content Length, and required Rack headers
|
62
|
+
response_headers
|
63
|
+
end
|
64
|
+
|
65
|
+
def match_path(path, matcher)
|
66
|
+
if matcher.is_a?(Regexp)
|
67
|
+
path.match(matcher)
|
68
|
+
else
|
69
|
+
path.match(/^#{matcher.to_s}/)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_uri(url, matcher, path)
|
74
|
+
if url =~/\$\d/
|
75
|
+
match_path(path, matcher).to_a.each_with_index { |m, i| url.gsub!("$#{i.to_s}", m) }
|
76
|
+
URI(url)
|
77
|
+
else
|
78
|
+
URI.join(url, path)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def reverse_proxy matcher, url, opts={}
|
83
|
+
raise GenericProxyURI.new(url) if matcher.is_a?(String) && URI(url).class == URI::Generic
|
84
|
+
@paths.merge!(matcher => url)
|
85
|
+
@opts.merge!(opts)
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def rewrite_env(env)
|
90
|
+
env["PORT"] = "8000"
|
91
|
+
|
92
|
+
env
|
93
|
+
end
|
94
|
+
|
95
|
+
def rewrite_response(triplet)
|
96
|
+
status, headers, body = triplet
|
97
|
+
|
98
|
+
headers["X-Foo"] = "Bar"
|
99
|
+
|
100
|
+
triplet
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
data/spec/rpc_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'rack/test'
|
3
|
+
|
4
|
+
describe "Rpc" do
|
5
|
+
include Rack::Test::Methods
|
6
|
+
include WebMock::API
|
7
|
+
class DwhackyTest < WhackANode::Rpc
|
8
|
+
def rewrite_env(env)
|
9
|
+
env['PORT'] = 90220
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def app
|
14
|
+
WhackANode::Rpc.new
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have a port of 90210" do
|
18
|
+
stub_request(:get, "http://localhost:8810/").
|
19
|
+
with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
|
20
|
+
to_return(:status => 200, :body => "", :headers => {})
|
21
|
+
#@app.should_not be_nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -3,10 +3,7 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
3
3
|
require 'rspec'
|
4
4
|
require 'rack/test'
|
5
5
|
require 'whack-a-node'
|
6
|
-
require 'rack/reverse_proxy'
|
7
|
-
require 'rack/streaming_proxy'
|
8
6
|
require 'webmock/rspec'
|
9
|
-
|
10
7
|
# Requires supporting files with custom matchers and macros, etc,
|
11
8
|
# in ./support/ and its subdirectories.
|
12
9
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
data/spec/whack-a-node_spec.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
require 'rack/test'
|
3
|
-
include Rack::Test::Methods
|
4
3
|
|
5
4
|
describe "WhackANode" do
|
6
|
-
|
5
|
+
include Rack::Test::Methods
|
6
|
+
include WebMock::API
|
7
|
+
class WhackyTest < WhackANode::Proxy
|
7
8
|
def rewrite_env(env)
|
8
9
|
env['PORT'] = 90210
|
9
10
|
end
|
@@ -17,8 +18,13 @@ describe "WhackANode" do
|
|
17
18
|
#@app = WhackyTest.new
|
18
19
|
#end
|
19
20
|
it "should have a port of 90210" do
|
20
|
-
|
21
|
-
|
21
|
+
stub_request(:get, "http://localhost:8810/").
|
22
|
+
with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
|
23
|
+
to_return(:status => 200, :body => "", :headers => {})
|
24
|
+
|
25
|
+
#stub_request(:get, 'http://example.com/').to_return({:body => "Proxied App"})
|
26
|
+
#get '/'
|
27
|
+
#last_response.should_not be_nil
|
22
28
|
#@app.should_not be_nil
|
23
29
|
end
|
24
30
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: whack-a-node
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Niels Meersschaert
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2011-05-
|
19
|
+
date: 2011-05-19 00:00:00 -04:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -31,7 +31,7 @@ dependencies:
|
|
31
31
|
segments:
|
32
32
|
- 0
|
33
33
|
version: "0"
|
34
|
-
name: rack
|
34
|
+
name: rack
|
35
35
|
version_requirements: *id001
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
prerelease: false
|
@@ -45,12 +45,54 @@ dependencies:
|
|
45
45
|
segments:
|
46
46
|
- 0
|
47
47
|
version: "0"
|
48
|
-
name:
|
48
|
+
name: dnode
|
49
49
|
version_requirements: *id002
|
50
50
|
- !ruby/object:Gem::Dependency
|
51
51
|
prerelease: false
|
52
|
-
type: :
|
52
|
+
type: :runtime
|
53
53
|
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
name: eventmachine
|
63
|
+
version_requirements: *id003
|
64
|
+
- !ruby/object:Gem::Dependency
|
65
|
+
prerelease: false
|
66
|
+
type: :runtime
|
67
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
name: events
|
77
|
+
version_requirements: *id004
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
prerelease: false
|
80
|
+
type: :runtime
|
81
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
name: json
|
91
|
+
version_requirements: *id005
|
92
|
+
- !ruby/object:Gem::Dependency
|
93
|
+
prerelease: false
|
94
|
+
type: :development
|
95
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
54
96
|
none: false
|
55
97
|
requirements:
|
56
98
|
- - ~>
|
@@ -62,11 +104,11 @@ dependencies:
|
|
62
104
|
- 0
|
63
105
|
version: 2.3.0
|
64
106
|
name: rspec
|
65
|
-
version_requirements: *
|
107
|
+
version_requirements: *id006
|
66
108
|
- !ruby/object:Gem::Dependency
|
67
109
|
prerelease: false
|
68
110
|
type: :development
|
69
|
-
requirement: &
|
111
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
70
112
|
none: false
|
71
113
|
requirements:
|
72
114
|
- - ~>
|
@@ -78,11 +120,11 @@ dependencies:
|
|
78
120
|
- 0
|
79
121
|
version: 1.0.0
|
80
122
|
name: bundler
|
81
|
-
version_requirements: *
|
123
|
+
version_requirements: *id007
|
82
124
|
- !ruby/object:Gem::Dependency
|
83
125
|
prerelease: false
|
84
126
|
type: :development
|
85
|
-
requirement: &
|
127
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
86
128
|
none: false
|
87
129
|
requirements:
|
88
130
|
- - ~>
|
@@ -94,11 +136,11 @@ dependencies:
|
|
94
136
|
- 2
|
95
137
|
version: 1.5.2
|
96
138
|
name: jeweler
|
97
|
-
version_requirements: *
|
139
|
+
version_requirements: *id008
|
98
140
|
- !ruby/object:Gem::Dependency
|
99
141
|
prerelease: false
|
100
142
|
type: :development
|
101
|
-
requirement: &
|
143
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
102
144
|
none: false
|
103
145
|
requirements:
|
104
146
|
- - ">="
|
@@ -108,11 +150,11 @@ dependencies:
|
|
108
150
|
- 0
|
109
151
|
version: "0"
|
110
152
|
name: rack-test
|
111
|
-
version_requirements: *
|
153
|
+
version_requirements: *id009
|
112
154
|
- !ruby/object:Gem::Dependency
|
113
155
|
prerelease: false
|
114
156
|
type: :development
|
115
|
-
requirement: &
|
157
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
116
158
|
none: false
|
117
159
|
requirements:
|
118
160
|
- - ">="
|
@@ -122,11 +164,11 @@ dependencies:
|
|
122
164
|
- 0
|
123
165
|
version: "0"
|
124
166
|
name: webmock
|
125
|
-
version_requirements: *
|
167
|
+
version_requirements: *id010
|
126
168
|
- !ruby/object:Gem::Dependency
|
127
169
|
prerelease: false
|
128
170
|
type: :development
|
129
|
-
requirement: &
|
171
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
130
172
|
none: false
|
131
173
|
requirements:
|
132
174
|
- - ">="
|
@@ -136,7 +178,7 @@ dependencies:
|
|
136
178
|
- 0
|
137
179
|
version: "0"
|
138
180
|
name: rcov
|
139
|
-
version_requirements: *
|
181
|
+
version_requirements: *id011
|
140
182
|
description: Extend your middleware to include Node.js
|
141
183
|
email: nmeersschaert@mac.com
|
142
184
|
executables: []
|
@@ -147,20 +189,19 @@ extra_rdoc_files:
|
|
147
189
|
- LICENSE.txt
|
148
190
|
- README.rdoc
|
149
191
|
files:
|
150
|
-
- .DS_Store
|
151
192
|
- Gemfile
|
152
193
|
- Gemfile.lock
|
153
194
|
- LICENSE.txt
|
154
195
|
- README.rdoc
|
155
196
|
- Rakefile
|
156
197
|
- VERSION
|
157
|
-
- lib/net_http_hacked.rb
|
158
|
-
- lib/rack/http_streaming_response.rb
|
159
|
-
- lib/rack/reverse_proxy.rb
|
160
|
-
- lib/rack/streaming_proxy.rb
|
161
198
|
- lib/whack-a-node.rb
|
162
|
-
-
|
163
|
-
-
|
199
|
+
- lib/whack_a_node.rb
|
200
|
+
- lib/whack_a_node/proxy.rb
|
201
|
+
- lib/whack_a_node/redirect.rb
|
202
|
+
- lib/whack_a_node/rpc.rb
|
203
|
+
- lib/whack_a_node/whacky.rb
|
204
|
+
- spec/rpc_spec.rb
|
164
205
|
- spec/spec_helper.rb
|
165
206
|
- spec/whack-a-node_spec.rb
|
166
207
|
has_rdoc: true
|
@@ -198,7 +239,6 @@ signing_key:
|
|
198
239
|
specification_version: 3
|
199
240
|
summary: Run node apps as Rack apps
|
200
241
|
test_files:
|
201
|
-
- spec/
|
202
|
-
- spec/rack/streaming_proxy_spec.rb
|
242
|
+
- spec/rpc_spec.rb
|
203
243
|
- spec/spec_helper.rb
|
204
244
|
- spec/whack-a-node_spec.rb
|
data/.DS_Store
DELETED
Binary file
|
data/lib/net_http_hacked.rb
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
# We are hacking net/http to change semantics of streaming handling
|
2
|
-
# from "block" semantics to regular "return" semnatics.
|
3
|
-
# We need it to construct a streamable rack triplet:
|
4
|
-
#
|
5
|
-
# [status, headers, streamable_body]
|
6
|
-
#
|
7
|
-
# See http://github.com/aniero/rack-streaming-proxy
|
8
|
-
# for alternative that uses additional process.
|
9
|
-
#
|
10
|
-
# BTW I don't like monkey patching either
|
11
|
-
# but this is not real monkey patching.
|
12
|
-
# I just added some methods and named them very uniquely
|
13
|
-
# to avoid eventual conflicts. You're safe. Trust me.
|
14
|
-
#
|
15
|
-
# Also, in Ruby 1.9.2 you could use Fibers to avoid hacking net/http.
|
16
|
-
|
17
|
-
require 'net/http'
|
18
|
-
|
19
|
-
class Net::HTTP
|
20
|
-
# Original #request with block semantics.
|
21
|
-
#
|
22
|
-
# def request(req, body = nil, &block)
|
23
|
-
# unless started?
|
24
|
-
# start {
|
25
|
-
# req['connection'] ||= 'close'
|
26
|
-
# return request(req, body, &block)
|
27
|
-
# }
|
28
|
-
# end
|
29
|
-
# if proxy_user()
|
30
|
-
# unless use_ssl?
|
31
|
-
# req.proxy_basic_auth proxy_user(), proxy_pass()
|
32
|
-
# end
|
33
|
-
# end
|
34
|
-
#
|
35
|
-
# req.set_body_internal body
|
36
|
-
# begin_transport req
|
37
|
-
# req.exec @socket, @curr_http_version, edit_path(req.path)
|
38
|
-
# begin
|
39
|
-
# res = HTTPResponse.read_new(@socket)
|
40
|
-
# end while res.kind_of?(HTTPContinue)
|
41
|
-
# res.reading_body(@socket, req.response_body_permitted?) {
|
42
|
-
# yield res if block_given?
|
43
|
-
# }
|
44
|
-
# end_transport req, res
|
45
|
-
#
|
46
|
-
# res
|
47
|
-
# end
|
48
|
-
|
49
|
-
def begin_request_hacked(req)
|
50
|
-
begin_transport req
|
51
|
-
req.exec @socket, @curr_http_version, edit_path(req.path)
|
52
|
-
begin
|
53
|
-
res = Net::HTTPResponse.read_new(@socket)
|
54
|
-
end while res.kind_of?(Net::HTTPContinue)
|
55
|
-
res.begin_reading_body_hacked(@socket, req.response_body_permitted?)
|
56
|
-
@req_hacked, @res_hacked = req, res
|
57
|
-
@res_hacked
|
58
|
-
end
|
59
|
-
|
60
|
-
def end_request_hacked
|
61
|
-
@res_hacked.end_reading_body_hacked
|
62
|
-
end_transport @req_hacked, @res_hacked
|
63
|
-
@res_hacked
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
class Net::HTTPResponse
|
68
|
-
# Original #reading_body with block semantics
|
69
|
-
#
|
70
|
-
# def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only
|
71
|
-
# @socket = sock
|
72
|
-
# @body_exist = reqmethodallowbody && self.class.body_permitted?
|
73
|
-
# begin
|
74
|
-
# yield
|
75
|
-
# self.body # ensure to read body
|
76
|
-
# ensure
|
77
|
-
# @socket = nil
|
78
|
-
# end
|
79
|
-
# end
|
80
|
-
|
81
|
-
def begin_reading_body_hacked(sock, reqmethodallowbody)
|
82
|
-
@socket = sock
|
83
|
-
@body_exist = reqmethodallowbody && self.class.body_permitted?
|
84
|
-
end
|
85
|
-
|
86
|
-
def end_reading_body_hacked
|
87
|
-
self.body
|
88
|
-
@socket = nil
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
@@ -1,63 +0,0 @@
|
|
1
|
-
require "net_http_hacked"
|
2
|
-
|
3
|
-
module Rack
|
4
|
-
|
5
|
-
# Wraps the hacked net/http in a Rack way.
|
6
|
-
class HttpStreamingResponse
|
7
|
-
def initialize(request, host, port = nil)
|
8
|
-
@request, @host, @port = request, host, port
|
9
|
-
end
|
10
|
-
|
11
|
-
def status
|
12
|
-
response.code.to_i
|
13
|
-
end
|
14
|
-
|
15
|
-
def headers
|
16
|
-
h = Utils::HeaderHash.new
|
17
|
-
|
18
|
-
response.each_header do |k, v|
|
19
|
-
h[k] = v
|
20
|
-
end
|
21
|
-
|
22
|
-
h
|
23
|
-
end
|
24
|
-
|
25
|
-
def body
|
26
|
-
self
|
27
|
-
end
|
28
|
-
|
29
|
-
# Can be called only once!
|
30
|
-
def each(&block)
|
31
|
-
response.read_body(&block)
|
32
|
-
ensure
|
33
|
-
session.end_request_hacked
|
34
|
-
end
|
35
|
-
|
36
|
-
def to_s
|
37
|
-
@body ||= begin
|
38
|
-
lines = []
|
39
|
-
|
40
|
-
each do |line|
|
41
|
-
lines << line
|
42
|
-
end
|
43
|
-
|
44
|
-
lines.join
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
protected
|
49
|
-
|
50
|
-
# Net::HTTPResponse
|
51
|
-
def response
|
52
|
-
@response ||= session.begin_request_hacked(@request)
|
53
|
-
end
|
54
|
-
|
55
|
-
# Net::HTTP
|
56
|
-
def session
|
57
|
-
@session ||= Net::HTTP.start(@host, @port)
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
|
data/lib/rack/reverse_proxy.rb
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
require 'net/https'
|
3
|
-
|
4
|
-
module Rack
|
5
|
-
class ReverseProxy
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(app = nil, &b)
|
9
|
-
@app = app || lambda { [404, [], []] }
|
10
|
-
@paths = {}
|
11
|
-
@opts = {:preserve_host => false}
|
12
|
-
instance_eval &b if block_given?
|
13
|
-
end
|
14
|
-
|
15
|
-
def call(env)
|
16
|
-
rackreq = Rack::Request.new(env)
|
17
|
-
matcher, url = get_matcher_and_url rackreq.fullpath
|
18
|
-
return @app.call(env) if matcher.nil?
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
uri = get_uri(url, matcher, rackreq.fullpath)
|
23
|
-
headers = Rack::Utils::HeaderHash.new
|
24
|
-
env.each { |key, value|
|
25
|
-
if key =~ /HTTP_(.*)/
|
26
|
-
headers[$1] = value
|
27
|
-
end
|
28
|
-
}
|
29
|
-
headers['HOST'] = uri.host if @opts[:preserve_host]
|
30
|
-
|
31
|
-
session = Net::HTTP.new(uri.host, uri.port)
|
32
|
-
session.use_ssl = (uri.scheme == 'https')
|
33
|
-
session.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
34
|
-
session.start { |http|
|
35
|
-
m = rackreq.request_method
|
36
|
-
case m
|
37
|
-
when "GET", "HEAD", "DELETE", "OPTIONS", "TRACE"
|
38
|
-
req = Net::HTTP.const_get(m.capitalize).new(uri.request_uri, headers)
|
39
|
-
req.basic_auth @opts[:username], @opts[:password] if @opts[:username] and @opts[:password]
|
40
|
-
when "PUT", "POST"
|
41
|
-
req = Net::HTTP.const_get(m.capitalize).new(uri.request_uri, headers)
|
42
|
-
req.basic_auth @opts[:username], @opts[:password] if @opts[:username] and @opts[:password]
|
43
|
-
req.content_length = rackreq.body.length
|
44
|
-
req.body_stream = rackreq.body
|
45
|
-
else
|
46
|
-
raise "method not supported: #{m}"
|
47
|
-
end
|
48
|
-
|
49
|
-
body = ''
|
50
|
-
res = http.request(req) do |res|
|
51
|
-
res.read_body do |segment|
|
52
|
-
body << segment
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
[res.code, create_response_headers(res), [body]]
|
57
|
-
}
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
|
63
|
-
def get_matcher_and_url path
|
64
|
-
matches = @paths.select do |matcher, url|
|
65
|
-
match_path(path, matcher)
|
66
|
-
end
|
67
|
-
|
68
|
-
if matches.length < 1
|
69
|
-
nil
|
70
|
-
elsif matches.length > 1
|
71
|
-
raise AmbiguousProxyMatch.new(path, matches)
|
72
|
-
else
|
73
|
-
matches.first.map{|a| a.dup}
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def create_response_headers http_response
|
78
|
-
response_headers = Rack::Utils::HeaderHash.new(http_response.to_hash)
|
79
|
-
# handled by Rack
|
80
|
-
response_headers.delete('status')
|
81
|
-
# TODO: figure out how to handle chunked responses
|
82
|
-
response_headers.delete('transfer-encoding')
|
83
|
-
# TODO: Verify Content Length, and required Rack headers
|
84
|
-
response_headers
|
85
|
-
end
|
86
|
-
|
87
|
-
def match_path(path, matcher)
|
88
|
-
if matcher.is_a?(Regexp)
|
89
|
-
path.match(matcher)
|
90
|
-
else
|
91
|
-
path.match(/^#{matcher.to_s}/)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def get_uri(url, matcher, path)
|
96
|
-
if url =~/\$\d/
|
97
|
-
match_path(path, matcher).to_a.each_with_index { |m, i| url.gsub!("$#{i.to_s}", m) }
|
98
|
-
URI(url)
|
99
|
-
else
|
100
|
-
URI.join(url, path)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def reverse_proxy matcher, url, opts={}
|
105
|
-
raise GenericProxyURI.new(url) if matcher.is_a?(String) && URI(url).class == URI::Generic
|
106
|
-
@paths.merge!(matcher => url)
|
107
|
-
@opts.merge!(opts)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
class GenericProxyURI < Exception
|
112
|
-
attr_reader :url
|
113
|
-
|
114
|
-
def intialize(url)
|
115
|
-
@url = url
|
116
|
-
end
|
117
|
-
|
118
|
-
def to_s
|
119
|
-
%Q(Your URL "#{@url}" is too generic. Did you mean "http://#{@url}"?)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
class AmbiguousProxyMatch < Exception
|
124
|
-
attr_reader :path, :matches
|
125
|
-
def initialize(path, matches)
|
126
|
-
@path = path
|
127
|
-
@matches = matches
|
128
|
-
end
|
129
|
-
|
130
|
-
def to_s
|
131
|
-
%Q(Path "#{path}" matched multiple endpoints: #{formatted_matches})
|
132
|
-
end
|
133
|
-
|
134
|
-
private
|
135
|
-
|
136
|
-
def formatted_matches
|
137
|
-
matches.map {|m| %Q("#{m[0].to_s}" => "#{m[1]}")}.join(', ')
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
end
|
142
|
-
|
data/lib/rack/streaming_proxy.rb
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
require "rack/http_streaming_response"
|
2
|
-
|
3
|
-
module Rack
|
4
|
-
|
5
|
-
# Subclass and bring your own #rewrite_request and #rewrite_response
|
6
|
-
class StreamingProxy
|
7
|
-
VERSION = "0.3.4"
|
8
|
-
|
9
|
-
def call(env)
|
10
|
-
rewrite_response(perform_request(rewrite_env(env)))
|
11
|
-
end
|
12
|
-
|
13
|
-
# Return modified env
|
14
|
-
def rewrite_env(env)
|
15
|
-
env
|
16
|
-
end
|
17
|
-
|
18
|
-
# Return a rack triplet [status, headers, body]
|
19
|
-
def rewrite_response(triplet)
|
20
|
-
triplet
|
21
|
-
end
|
22
|
-
|
23
|
-
protected
|
24
|
-
|
25
|
-
def perform_request(env)
|
26
|
-
source_request = Rack::Request.new(env)
|
27
|
-
|
28
|
-
# Initialize request
|
29
|
-
target_request = Net::HTTP.const_get(source_request.request_method.capitalize).new(source_request.fullpath)
|
30
|
-
|
31
|
-
# Setup headers
|
32
|
-
target_request.initialize_http_header(extract_http_request_headers(source_request.env))
|
33
|
-
|
34
|
-
# Setup body
|
35
|
-
if target_request.request_body_permitted? && source_request.body
|
36
|
-
target_request.body_stream = source_request.body
|
37
|
-
target_request.content_length = source_request.content_length
|
38
|
-
target_request.content_type = source_request.content_type if source_request.content_type
|
39
|
-
end
|
40
|
-
|
41
|
-
# Create a streaming response (the actual network communication is deferred, a.k.a. streamed)
|
42
|
-
target_response = HttpStreamingResponse.new(target_request, source_request.host, source_request.port)
|
43
|
-
|
44
|
-
[target_response.status, target_response.headers, target_response.body]
|
45
|
-
end
|
46
|
-
|
47
|
-
def extract_http_request_headers(env)
|
48
|
-
headers = env.reject do |k, v|
|
49
|
-
!(/^HTTP_[A-Z_]+$/ === k)
|
50
|
-
end.map do |k, v|
|
51
|
-
[reconstruct_header_name(k), v]
|
52
|
-
end.inject(Utils::HeaderHash.new) do |hash, k_v|
|
53
|
-
k, v = k_v
|
54
|
-
hash[k] = v
|
55
|
-
hash
|
56
|
-
end
|
57
|
-
|
58
|
-
x_forwarded_for = (headers["X-Forwarded-For"].to_s.split(/, +/) << env["REMOTE_ADDR"]).join(", ")
|
59
|
-
|
60
|
-
headers.merge!("X-Forwarded-For" => x_forwarded_for)
|
61
|
-
end
|
62
|
-
|
63
|
-
def reconstruct_header_name(name)
|
64
|
-
name.sub(/^HTTP_/, "").gsub("_", "-")
|
65
|
-
end
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
end
|
70
|
-
|
@@ -1,180 +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 { [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
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
it "should forward requests to the calling app when the path is not matched" do
|
23
|
-
stub_request(:get, 'http://example.com/').to_return(:headers => {'Status' => '200 OK'})
|
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 "the response header should never contain Status" do
|
36
|
-
stub_request(:any, 'example.com/test/stuff').to_return(:headers => {'Status' => '200 OK'})
|
37
|
-
get '/test/stuff'
|
38
|
-
last_response.headers['Status'].should == nil
|
39
|
-
end
|
40
|
-
|
41
|
-
it "the response header should never transfer-encoding" do
|
42
|
-
stub_request(:any, 'example.com/test/stuff').to_return(:headers => {'transfer-encoding' => 'Chunked'})
|
43
|
-
get '/test/stuff'
|
44
|
-
last_response.headers['transfer-encoding'].should == nil
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should set the Host header" do
|
48
|
-
stub_request(:any, 'example.com/test/stuff')
|
49
|
-
get '/test/stuff'
|
50
|
-
a_request(:get, 'http://example.com/test/stuff').with(:headers => {"Host" => "example.com"}).should have_been_made
|
51
|
-
end
|
52
|
-
|
53
|
-
describe "with preserve host turned off" do
|
54
|
-
def app
|
55
|
-
Rack::ReverseProxy.new(dummy_app) do
|
56
|
-
reverse_proxy '/test', 'http://example.com/'
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should not set the Host header" do
|
61
|
-
stub_request(:any, 'example.com/test/stuff')
|
62
|
-
get '/test/stuff'
|
63
|
-
a_request(:get, 'http://example.com/test/stuff').should have_been_made
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe "with basic auth turned on" do
|
68
|
-
def app
|
69
|
-
Rack::ReverseProxy.new(dummy_app) do
|
70
|
-
reverse_proxy '/test', 'http://example.com/', {:username => "joe", :password => "shmoe"}
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
it "should make request with basic auth" do
|
75
|
-
stub_request(:get, "http://joe:shmoe@example.com/test/stuff").to_return(:body => "secured content")
|
76
|
-
get '/test/stuff'
|
77
|
-
last_response.body.should == "secured content"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
describe "with ambiguous routes" do
|
82
|
-
def app
|
83
|
-
Rack::ReverseProxy.new(dummy_app) do
|
84
|
-
reverse_proxy '/test', 'http://example.com/'
|
85
|
-
reverse_proxy /^\/test/, 'http://example.com/'
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
it "should throw an exception" do
|
90
|
-
lambda { get '/test' }.should raise_error(Rack::AmbiguousProxyMatch)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
describe "with a route as a regular expression" do
|
95
|
-
def app
|
96
|
-
Rack::ReverseProxy.new(dummy_app) do
|
97
|
-
reverse_proxy %r|^/test(/.*)$|, 'http://example.com$1'
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
it "should support subcaptures" do
|
102
|
-
stub_request(:get, 'http://example.com/path').to_return({:body => "Proxied App"})
|
103
|
-
get '/test/path'
|
104
|
-
last_response.body.should == "Proxied App"
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
describe "with a https route" do
|
109
|
-
def app
|
110
|
-
Rack::ReverseProxy.new(dummy_app) do
|
111
|
-
reverse_proxy '/test', 'https://example.com'
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
it "should make a secure request" do
|
116
|
-
stub_request(:get, 'https://example.com/test/stuff').to_return({:body => "Proxied Secure App"})
|
117
|
-
get '/test/stuff'
|
118
|
-
last_response.body.should == "Proxied Secure App"
|
119
|
-
end
|
120
|
-
|
121
|
-
end
|
122
|
-
|
123
|
-
describe "with a route as a string" do
|
124
|
-
def app
|
125
|
-
Rack::ReverseProxy.new(dummy_app) do
|
126
|
-
reverse_proxy '/test', 'http://example.com'
|
127
|
-
reverse_proxy '/path', 'http://example.com/foo$0'
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
it "should append the full path to the uri" do
|
132
|
-
stub_request(:get, 'http://example.com/test/stuff').to_return({:body => "Proxied App"})
|
133
|
-
get '/test/stuff'
|
134
|
-
last_response.body.should == "Proxied App"
|
135
|
-
end
|
136
|
-
|
137
|
-
end
|
138
|
-
|
139
|
-
describe "with a generic url" do
|
140
|
-
def app
|
141
|
-
Rack::ReverseProxy.new(dummy_app) do
|
142
|
-
reverse_proxy '/test', 'example.com'
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
it "should throw an exception" do
|
147
|
-
lambda{ app }.should raise_error(Rack::GenericProxyURI)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
describe "with a matching route" do
|
152
|
-
def app
|
153
|
-
Rack::ReverseProxy.new(dummy_app) do
|
154
|
-
reverse_proxy '/test', 'http://example.com/'
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
%w|get head delete put post|.each do |method|
|
159
|
-
describe "and using method #{method}" do
|
160
|
-
it "should forward the correct request" do
|
161
|
-
stub_request(method.to_sym, 'http://example.com/test').to_return({:body => "Proxied App for #{method}"})
|
162
|
-
eval "#{method} '/test'"
|
163
|
-
last_response.body.should == "Proxied App for #{method}"
|
164
|
-
end
|
165
|
-
|
166
|
-
if %w|put post|.include?(method)
|
167
|
-
it "should forward the request payload" do
|
168
|
-
stub_request(method.to_sym, 'http://example.com/test').to_return { |req| {:body => req.body} }
|
169
|
-
eval "#{method} '/test', {:test => 'test'}"
|
170
|
-
last_response.body.should == "test=test"
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
|
179
|
-
end
|
180
|
-
|
@@ -1,16 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
-
|
3
|
-
describe Rack::StreamingProxy do
|
4
|
-
include Rack::Test::Methods
|
5
|
-
include WebMock::API
|
6
|
-
|
7
|
-
def app
|
8
|
-
Rack::StreamingProxy.new
|
9
|
-
end
|
10
|
-
|
11
|
-
it "should have a port of 90210" do
|
12
|
-
get "/"
|
13
|
-
last_response.should_not be_nil
|
14
|
-
#@app.should_not be_nil
|
15
|
-
end
|
16
|
-
end
|