puffing-billy 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/lib/billy.rb +1 -0
- data/lib/billy/cache.rb +39 -0
- data/lib/billy/config.rb +6 -2
- data/lib/billy/proxy.rb +6 -0
- data/lib/billy/proxy_connection.rb +25 -4
- data/lib/billy/version.rb +1 -1
- data/spec/requests/proxy_spec.rb +49 -1
- data/spec/spec_helper.rb +4 -0
- data/spec/support/test_server.rb +8 -1
- metadata +3 -3
data/Gemfile.lock
CHANGED
data/lib/billy.rb
CHANGED
data/lib/billy/cache.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Billy
|
5
|
+
class Cache
|
6
|
+
def initialize
|
7
|
+
reset
|
8
|
+
end
|
9
|
+
|
10
|
+
def cacheable?(url, headers)
|
11
|
+
if Billy.config.cache
|
12
|
+
host = URI(url).host
|
13
|
+
Billy.log(:info, Billy.config.whitelist)
|
14
|
+
!Billy.config.whitelist.include?(host)
|
15
|
+
# TODO test headers for cacheability
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def cached?(url)
|
20
|
+
!@cache[url].nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch(url)
|
24
|
+
@cache[url]
|
25
|
+
end
|
26
|
+
|
27
|
+
def store(url, status, headers, content)
|
28
|
+
@cache[url] = {
|
29
|
+
:status => status,
|
30
|
+
:headers => headers,
|
31
|
+
:content => content
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset
|
36
|
+
@cache = {}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/billy/config.rb
CHANGED
@@ -2,10 +2,14 @@ require 'logger'
|
|
2
2
|
|
3
3
|
module Billy
|
4
4
|
class Config
|
5
|
-
|
5
|
+
DEFAULT_WHITELIST = ['127.0.0.1', 'localhost']
|
6
|
+
|
7
|
+
attr_accessor :logger, :cache, :whitelist
|
6
8
|
|
7
9
|
def initialize
|
8
|
-
@logger
|
10
|
+
@logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
|
11
|
+
@cache = true
|
12
|
+
@whitelist = DEFAULT_WHITELIST
|
9
13
|
end
|
10
14
|
end
|
11
15
|
|
data/lib/billy/proxy.rb
CHANGED
@@ -6,6 +6,7 @@ module Billy
|
|
6
6
|
class Proxy
|
7
7
|
def initialize
|
8
8
|
reset
|
9
|
+
@cache = Billy::Cache.new
|
9
10
|
end
|
10
11
|
|
11
12
|
def start(threaded = true)
|
@@ -48,6 +49,10 @@ module Billy
|
|
48
49
|
@stubs = []
|
49
50
|
end
|
50
51
|
|
52
|
+
def reset_cache
|
53
|
+
@cache.reset
|
54
|
+
end
|
55
|
+
|
51
56
|
protected
|
52
57
|
|
53
58
|
def find_stub(method, url)
|
@@ -66,6 +71,7 @@ module Billy
|
|
66
71
|
|
67
72
|
@signature = EM.start_server('127.0.0.1', 0, ProxyConnection) do |p|
|
68
73
|
p.handler = self
|
74
|
+
p.cache = @cache
|
69
75
|
end
|
70
76
|
|
71
77
|
Billy.log(:info, "Proxy listening on #{url}")
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'uri'
|
1
2
|
require 'eventmachine'
|
2
3
|
require 'http/parser'
|
3
4
|
require 'em-http'
|
@@ -6,6 +7,7 @@ require 'evma_httpserver'
|
|
6
7
|
module Billy
|
7
8
|
class ProxyConnection < EventMachine::Connection
|
8
9
|
attr_accessor :handler
|
10
|
+
attr_accessor :cache
|
9
11
|
|
10
12
|
def post_init
|
11
13
|
@parser = Http::Parser.new(self)
|
@@ -77,6 +79,9 @@ module Billy
|
|
77
79
|
response.headers = result[1].merge('Connection' => 'close')
|
78
80
|
response.content = result[2]
|
79
81
|
response.send_response
|
82
|
+
elsif @parser.http_method == 'GET' && cache.cached?(@url)
|
83
|
+
Billy.log(:info, "CACHE #{@parser.http_method} #{@url}")
|
84
|
+
respond_from_cache
|
80
85
|
else
|
81
86
|
Billy.log(:info, "PROXY #{@parser.http_method} #{@url}")
|
82
87
|
proxy_request
|
@@ -99,18 +104,34 @@ module Billy
|
|
99
104
|
req = req.send(@parser.http_method.downcase, req_opts)
|
100
105
|
|
101
106
|
req.errback do
|
102
|
-
|
107
|
+
Billy.log(:error, "Request failed: #{@url}")
|
103
108
|
close_connection
|
104
109
|
end
|
105
110
|
|
106
111
|
req.callback do
|
112
|
+
res_status = req.response_header.status
|
113
|
+
res_headers = req.response_header.raw
|
114
|
+
res_headers = res_headers.merge('Connection' => 'close')
|
115
|
+
res_headers.delete('Transfer-Encoding')
|
116
|
+
res_content = req.response.force_encoding('BINARY')
|
117
|
+
if @parser.http_method == 'GET' && cache.cacheable?(@url, res_headers)
|
118
|
+
cache.store(@url, res_status, res_headers, res_content)
|
119
|
+
end
|
107
120
|
res = EM::DelegatedHttpResponse.new(self)
|
108
|
-
res.status =
|
109
|
-
res.headers =
|
110
|
-
res.content =
|
121
|
+
res.status = res_status
|
122
|
+
res.headers = res_headers
|
123
|
+
res.content = res_content
|
111
124
|
res.send_response
|
112
125
|
end
|
113
126
|
end
|
114
127
|
|
128
|
+
def respond_from_cache
|
129
|
+
cached_res = cache.fetch(@url)
|
130
|
+
res = EM::DelegatedHttpResponse.new(self)
|
131
|
+
res.status = cached_res[:status]
|
132
|
+
res.headers = cached_res[:headers]
|
133
|
+
res.content = cached_res[:content]
|
134
|
+
res.send_response
|
135
|
+
end
|
115
136
|
end
|
116
137
|
end
|
data/lib/billy/version.rb
CHANGED
data/spec/requests/proxy_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'billy'
|
3
|
+
require 'resolv'
|
3
4
|
|
4
5
|
shared_examples_for 'a proxy server' do
|
5
6
|
it 'should proxy GET requests' do
|
@@ -15,7 +16,7 @@ shared_examples_for 'a proxy server' do
|
|
15
16
|
end
|
16
17
|
|
17
18
|
it 'should proxy HEAD requests' do
|
18
|
-
http.head('/echo').headers['
|
19
|
+
http.head('/echo').headers['HTTP-X-EchoServer'].should == 'HEAD /echo'
|
19
20
|
end
|
20
21
|
|
21
22
|
it 'should proxy DELETE requests' do
|
@@ -55,6 +56,39 @@ shared_examples_for 'a request stub' do
|
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
59
|
+
shared_examples_for 'a cache' do
|
60
|
+
|
61
|
+
context 'whitelisted GET requests' do
|
62
|
+
it 'should not be cached' do
|
63
|
+
r = http.get('/foo')
|
64
|
+
r.body.should == 'GET /foo'
|
65
|
+
expect {
|
66
|
+
expect {
|
67
|
+
r = http.get('/foo')
|
68
|
+
}.to change { r.headers['HTTP-X-EchoCount'].to_i }.by(1)
|
69
|
+
}.to_not change { r.body }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'other GET requests' do
|
74
|
+
around do |example|
|
75
|
+
Billy.configure { |c| c.whitelist = [] }
|
76
|
+
example.run
|
77
|
+
Billy.configure { |c| c.whitelist = Billy::Config::DEFAULT_WHITELIST }
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should be cached' do
|
81
|
+
r = http.get('/foo')
|
82
|
+
r.body.should == 'GET /foo'
|
83
|
+
expect {
|
84
|
+
expect {
|
85
|
+
r = http.get('/foo')
|
86
|
+
}.to_not change { r.headers['HTTP-X-EchoCount'] }
|
87
|
+
}.to_not change { r.body }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
58
92
|
describe Billy::Proxy do
|
59
93
|
|
60
94
|
before do
|
@@ -99,4 +133,18 @@ describe Billy::Proxy do
|
|
99
133
|
|
100
134
|
end
|
101
135
|
|
136
|
+
context 'caching' do
|
137
|
+
|
138
|
+
context 'HTTP' do
|
139
|
+
let!(:http) { @http }
|
140
|
+
it_should_behave_like 'a cache'
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'HTTPS' do
|
144
|
+
let!(:http) { @https }
|
145
|
+
it_should_behave_like 'a cache'
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
102
150
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/test_server.rb
CHANGED
@@ -17,12 +17,19 @@ module Billy
|
|
17
17
|
q = Queue.new
|
18
18
|
Thread.new do
|
19
19
|
EM.run do
|
20
|
+
counter = 0
|
20
21
|
echo = Proc.new do |env|
|
21
22
|
req_body = env['rack.input'].read
|
22
23
|
request_info = "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
|
23
24
|
res_body = request_info
|
24
25
|
res_body += "\n#{req_body}" unless req_body.empty?
|
25
|
-
|
26
|
+
counter += 1
|
27
|
+
[
|
28
|
+
200,
|
29
|
+
{ 'HTTP-X-EchoServer' => request_info,
|
30
|
+
'HTTP-X-EchoCount' => "#{counter}" },
|
31
|
+
[res_body]
|
32
|
+
]
|
26
33
|
end
|
27
34
|
|
28
35
|
Thin::Logging.silent = true
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puffing-billy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -256,6 +256,7 @@ files:
|
|
256
256
|
- examples/facebook_api.html
|
257
257
|
- examples/tumblr_api.html
|
258
258
|
- lib/billy.rb
|
259
|
+
- lib/billy/cache.rb
|
259
260
|
- lib/billy/config.rb
|
260
261
|
- lib/billy/mitm.crt
|
261
262
|
- lib/billy/mitm.key
|
@@ -307,4 +308,3 @@ test_files:
|
|
307
308
|
- spec/requests/proxy_spec.rb
|
308
309
|
- spec/spec_helper.rb
|
309
310
|
- spec/support/test_server.rb
|
310
|
-
has_rdoc:
|