rack-proxy 0.0.1
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/.gitignore +1 -0
- data/Rakefile +28 -0
- data/Readme +1 -0
- data/VERSION +1 -0
- data/lib/net_http_hacked.rb +88 -0
- data/lib/rack/http_streaming_response.rb +49 -0
- data/lib/rack/proxy.rb +54 -0
- data/rack-proxy.gemspec +57 -0
- data/test/http_streaming_response_test.rb +35 -0
- data/test/net_http_hacked_test.rb +36 -0
- data/test/rack_proxy_test.rb +22 -0
- data/test/test_helper.rb +9 -0
- metadata +88 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "rake/testtask"
|
2
|
+
|
3
|
+
task :test do
|
4
|
+
Rake::TestTask.new do |t|
|
5
|
+
t.libs << "test"
|
6
|
+
t.test_files = FileList['test/*_test.rb']
|
7
|
+
t.verbose = true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :test
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
gem.name = "rack-proxy"
|
17
|
+
gem.summary = "A request/response rewriting HTTP proxy. A Rack app."
|
18
|
+
gem.description = "A Rack app that provides request/response rewriting proxy capabilities with streaming."
|
19
|
+
gem.email = "jacek.becela@gmail.com"
|
20
|
+
gem.homepage = "http://github.com/ncr/rack-proxy"
|
21
|
+
gem.authors = ["Jacek Becela"]
|
22
|
+
gem.add_dependency "rack"
|
23
|
+
gem.add_development_dependency "rack-test"
|
24
|
+
end
|
25
|
+
Jeweler::GemcutterTasks.new
|
26
|
+
rescue LoadError
|
27
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
28
|
+
end
|
data/Readme
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
A request/response rewriting HTTP proxy. A Rack app.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,88 @@
|
|
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
|
+
require 'net/http'
|
16
|
+
|
17
|
+
class Net::HTTP
|
18
|
+
# Original #request with block semantics.
|
19
|
+
#
|
20
|
+
# def request(req, body = nil, &block)
|
21
|
+
# unless started?
|
22
|
+
# start {
|
23
|
+
# req['connection'] ||= 'close'
|
24
|
+
# return request(req, body, &block)
|
25
|
+
# }
|
26
|
+
# end
|
27
|
+
# if proxy_user()
|
28
|
+
# unless use_ssl?
|
29
|
+
# req.proxy_basic_auth proxy_user(), proxy_pass()
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# req.set_body_internal body
|
34
|
+
# begin_transport req
|
35
|
+
# req.exec @socket, @curr_http_version, edit_path(req.path)
|
36
|
+
# begin
|
37
|
+
# res = HTTPResponse.read_new(@socket)
|
38
|
+
# end while res.kind_of?(HTTPContinue)
|
39
|
+
# res.reading_body(@socket, req.response_body_permitted?) {
|
40
|
+
# yield res if block_given?
|
41
|
+
# }
|
42
|
+
# end_transport req, res
|
43
|
+
#
|
44
|
+
# res
|
45
|
+
# end
|
46
|
+
|
47
|
+
def begin_request_hacked(req)
|
48
|
+
begin_transport req
|
49
|
+
req.exec @socket, @curr_http_version, edit_path(req.path)
|
50
|
+
begin
|
51
|
+
res = Net::HTTPResponse.read_new(@socket)
|
52
|
+
end while res.kind_of?(Net::HTTPContinue)
|
53
|
+
res.begin_reading_body_hacked(@socket, req.response_body_permitted?)
|
54
|
+
@req_hacked, @res_hacked = req, res
|
55
|
+
@res_hacked
|
56
|
+
end
|
57
|
+
|
58
|
+
def end_request_hacked
|
59
|
+
@res_hacked.end_reading_body_hacked
|
60
|
+
end_transport @req_hacked, @res_hacked
|
61
|
+
@res_hacked
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Net::HTTPResponse
|
66
|
+
# Original #reading_body with block semantics
|
67
|
+
#
|
68
|
+
# def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only
|
69
|
+
# @socket = sock
|
70
|
+
# @body_exist = reqmethodallowbody && self.class.body_permitted?
|
71
|
+
# begin
|
72
|
+
# yield
|
73
|
+
# self.body # ensure to read body
|
74
|
+
# ensure
|
75
|
+
# @socket = nil
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
|
79
|
+
def begin_reading_body_hacked(sock, reqmethodallowbody)
|
80
|
+
@socket = sock
|
81
|
+
@body_exist = reqmethodallowbody && self.class.body_permitted?
|
82
|
+
end
|
83
|
+
|
84
|
+
def end_reading_body_hacked
|
85
|
+
self.body
|
86
|
+
@socket = nil
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,49 @@
|
|
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
|
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
|
+
def each(&block)
|
30
|
+
response.read_body(&block)
|
31
|
+
ensure
|
32
|
+
session.end_request_hacked
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
# Net::HTTPResponse
|
38
|
+
def response
|
39
|
+
@response ||= session.begin_request_hacked(@request)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Net::HTTP
|
43
|
+
def session
|
44
|
+
@session ||= Net::HTTP.start(@host, @port)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/lib/rack/proxy.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require "net_http_hacked"
|
2
|
+
require "rack/http_streaming_response"
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
|
6
|
+
# Subclass and bring your own #rewrite_request and #rewrite_response
|
7
|
+
class Proxy
|
8
|
+
def call(env)
|
9
|
+
rewrite_response(perform_request(rewrite_request(Rack::Request.new(env))))
|
10
|
+
end
|
11
|
+
|
12
|
+
def rewrite_request(req)
|
13
|
+
req
|
14
|
+
end
|
15
|
+
|
16
|
+
def rewrite_response(res)
|
17
|
+
res
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def perform_request(req)
|
23
|
+
# Initialize request
|
24
|
+
http_request = Net::HTTP.const_get(req.request_method.capitalize).new(req.fullpath)
|
25
|
+
|
26
|
+
# Setup headers
|
27
|
+
http_request.initialize_http_header(http_request_headers(req))
|
28
|
+
|
29
|
+
# Setup body
|
30
|
+
if http_request.request_body_permitted? && req.body
|
31
|
+
http_request.body_stream = req.body
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create a streaming response (the actual network communication is deferred, a.k.a. streamed)
|
35
|
+
http_response = HttpStreamingResponse.new(http_request, req.host, req.port)
|
36
|
+
|
37
|
+
[http_response.status, http_response.headers, http_response.body]
|
38
|
+
end
|
39
|
+
|
40
|
+
def http_request_headers(req)
|
41
|
+
req.env.reject do |k, v|
|
42
|
+
!(/^HTTP_[A-Z_]+$/ === k)
|
43
|
+
end.map do |k, v|
|
44
|
+
[k.sub(/^HTTP_/, ""), v]
|
45
|
+
end.inject({}) do |hash, k_v|
|
46
|
+
k, v = k_v
|
47
|
+
hash[k] = v
|
48
|
+
hash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/rack-proxy.gemspec
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{rack-proxy}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jacek Becela"]
|
12
|
+
s.date = %q{2010-01-08}
|
13
|
+
s.description = %q{A Rack app that provides request/response rewriting proxy capabilities with streaming.}
|
14
|
+
s.email = %q{jacek.becela@gmail.com}
|
15
|
+
s.files = [
|
16
|
+
".gitignore",
|
17
|
+
"Rakefile",
|
18
|
+
"Readme",
|
19
|
+
"VERSION",
|
20
|
+
"lib/net_http_hacked.rb",
|
21
|
+
"lib/rack/http_streaming_response.rb",
|
22
|
+
"lib/rack/proxy.rb",
|
23
|
+
"rack-proxy.gemspec",
|
24
|
+
"test/http_streaming_response_test.rb",
|
25
|
+
"test/net_http_hacked_test.rb",
|
26
|
+
"test/rack_proxy_test.rb",
|
27
|
+
"test/test_helper.rb"
|
28
|
+
]
|
29
|
+
s.homepage = %q{http://github.com/ncr/rack-proxy}
|
30
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
31
|
+
s.require_paths = ["lib"]
|
32
|
+
s.rubygems_version = %q{1.3.5}
|
33
|
+
s.summary = %q{A request/response rewriting HTTP proxy. A Rack app.}
|
34
|
+
s.test_files = [
|
35
|
+
"test/http_streaming_response_test.rb",
|
36
|
+
"test/net_http_hacked_test.rb",
|
37
|
+
"test/rack_proxy_test.rb",
|
38
|
+
"test/test_helper.rb"
|
39
|
+
]
|
40
|
+
|
41
|
+
if s.respond_to? :specification_version then
|
42
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
43
|
+
s.specification_version = 3
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
46
|
+
s.add_runtime_dependency(%q<rack>, [">= 0"])
|
47
|
+
s.add_development_dependency(%q<rack-test>, [">= 0"])
|
48
|
+
else
|
49
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
50
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
51
|
+
end
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<rack>, [">= 0"])
|
54
|
+
s.add_dependency(%q<rack-test>, [">= 0"])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "rack/http_streaming_response"
|
3
|
+
|
4
|
+
class HttpStreamingResponseTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_streaming
|
7
|
+
host, req = "trix.pl", Net::HTTP::Get.new("/")
|
8
|
+
|
9
|
+
response = Rack::HttpStreamingResponse.new(req, host)
|
10
|
+
|
11
|
+
# Response status
|
12
|
+
assert response.status == "200"
|
13
|
+
|
14
|
+
# Headers
|
15
|
+
headers = response.headers
|
16
|
+
|
17
|
+
assert headers.size > 0
|
18
|
+
assert headers["content-type"] == "text/html"
|
19
|
+
assert headers["CoNtEnT-TyPe"] == "text/html"
|
20
|
+
assert headers["content-length"].to_i > 0
|
21
|
+
|
22
|
+
# Body
|
23
|
+
chunks = []
|
24
|
+
response.body.each do |chunk|
|
25
|
+
chunks << chunk
|
26
|
+
end
|
27
|
+
|
28
|
+
assert chunks.size > 0
|
29
|
+
chunks.each do |chunk|
|
30
|
+
assert chunk.is_a?(String)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "net_http_hacked"
|
3
|
+
|
4
|
+
class NetHttpHackedTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_net_http_hacked
|
7
|
+
req = Net::HTTP::Get.new("/")
|
8
|
+
http = Net::HTTP.start("trix.pl", "80")
|
9
|
+
|
10
|
+
# Response code
|
11
|
+
res = http.begin_request_hacked(req)
|
12
|
+
assert res.code == "200"
|
13
|
+
|
14
|
+
# Headers
|
15
|
+
headers = {}
|
16
|
+
res.each_header { |k, v| headers[k] = v }
|
17
|
+
|
18
|
+
assert headers.size > 0
|
19
|
+
assert headers["content-type"] == "text/html"
|
20
|
+
assert headers["content-length"].to_i > 0
|
21
|
+
|
22
|
+
# Body
|
23
|
+
chunks = []
|
24
|
+
res.read_body do |chunk|
|
25
|
+
chunks << chunk
|
26
|
+
end
|
27
|
+
|
28
|
+
assert chunks.size > 0
|
29
|
+
chunks.each do |chunk|
|
30
|
+
assert chunk.is_a?(String)
|
31
|
+
end
|
32
|
+
|
33
|
+
http.end_request_hacked
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "rack/proxy"
|
3
|
+
|
4
|
+
class RackProxyTest < Test::Unit::TestCase
|
5
|
+
class TrixProxy < Rack::Proxy
|
6
|
+
def rewrite_request(req)
|
7
|
+
req.env["HTTP_HOST"] = "trix.pl"
|
8
|
+
|
9
|
+
req
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def app
|
14
|
+
TrixProxy.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_trix
|
18
|
+
get "/"
|
19
|
+
assert last_response.ok?
|
20
|
+
assert /Jacek Becela/ === last_response.body
|
21
|
+
end
|
22
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rack-proxy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jacek Becela
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-08 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rack
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rack-test
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description: A Rack app that provides request/response rewriting proxy capabilities with streaming.
|
36
|
+
email: jacek.becela@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- Rakefile
|
46
|
+
- Readme
|
47
|
+
- VERSION
|
48
|
+
- lib/net_http_hacked.rb
|
49
|
+
- lib/rack/http_streaming_response.rb
|
50
|
+
- lib/rack/proxy.rb
|
51
|
+
- rack-proxy.gemspec
|
52
|
+
- test/http_streaming_response_test.rb
|
53
|
+
- test/net_http_hacked_test.rb
|
54
|
+
- test/rack_proxy_test.rb
|
55
|
+
- test/test_helper.rb
|
56
|
+
has_rdoc: true
|
57
|
+
homepage: http://github.com/ncr/rack-proxy
|
58
|
+
licenses: []
|
59
|
+
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options:
|
62
|
+
- --charset=UTF-8
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.3.5
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: A request/response rewriting HTTP proxy. A Rack app.
|
84
|
+
test_files:
|
85
|
+
- test/http_streaming_response_test.rb
|
86
|
+
- test/net_http_hacked_test.rb
|
87
|
+
- test/rack_proxy_test.rb
|
88
|
+
- test/test_helper.rb
|