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.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- puffing-billy (0.1.1)
4
+ puffing-billy (0.1.3)
5
5
  capybara
6
6
  em-http-request
7
7
  eventmachine
@@ -1,5 +1,6 @@
1
1
  require "billy/version"
2
2
  require "billy/config"
3
3
  require "billy/proxy_request_stub"
4
+ require "billy/cache"
4
5
  require "billy/proxy"
5
6
  require "billy/proxy_connection"
@@ -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
@@ -2,10 +2,14 @@ require 'logger'
2
2
 
3
3
  module Billy
4
4
  class Config
5
- attr_accessor :logger
5
+ DEFAULT_WHITELIST = ['127.0.0.1', 'localhost']
6
+
7
+ attr_accessor :logger, :cache, :whitelist
6
8
 
7
9
  def initialize
8
- @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
10
+ @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
11
+ @cache = true
12
+ @whitelist = DEFAULT_WHITELIST
9
13
  end
10
14
  end
11
15
 
@@ -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
- puts "Request failed: #{@url}"
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 = req.response_header.status
109
- res.headers = req.response_header.merge('Connection' => 'close')
110
- res.content = req.response.force_encoding('BINARY')
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
@@ -1,3 +1,3 @@
1
1
  module Billy
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
@@ -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['http_x_echoserver'].should == 'HEAD /echo'
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
@@ -21,4 +21,8 @@ RSpec.configure do |config|
21
21
  config.before :all do
22
22
  start_test_servers
23
23
  end
24
+
25
+ config.before :each do
26
+ proxy.reset_cache
27
+ end
24
28
  end
@@ -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
- [200, {'HTTP-X-EchoServer'=>request_info}, [res_body]]
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.2
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-10-15 00:00:00.000000000 Z
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: