puffing-billy 0.1.2 → 0.1.3
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.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:
|