whack-a-node 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|