proxy-server 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .idea
2
+ .bundle
3
+ coverage
4
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,54 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ proxy-server (0.0.1)
5
+ httpclient
6
+ json (>= 1.4.6)
7
+ sinatra (>= 1.2.6)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ addressable (2.2.6)
13
+ crack (0.3.1)
14
+ diff-lcs (1.1.3)
15
+ httpclient (2.2.1)
16
+ json (1.6.1)
17
+ multi_json (1.0.3)
18
+ rack (1.3.4)
19
+ rack-protection (1.1.4)
20
+ rack
21
+ rack-test (0.6.1)
22
+ rack (>= 1.0)
23
+ rake (0.9.2)
24
+ rspec (2.6.0)
25
+ rspec-core (~> 2.6.0)
26
+ rspec-expectations (~> 2.6.0)
27
+ rspec-mocks (~> 2.6.0)
28
+ rspec-core (2.6.4)
29
+ rspec-expectations (2.6.0)
30
+ diff-lcs (~> 1.1.2)
31
+ rspec-mocks (2.6.0)
32
+ simplecov (0.5.4)
33
+ multi_json (~> 1.0.3)
34
+ simplecov-html (~> 0.5.3)
35
+ simplecov-html (0.5.3)
36
+ sinatra (1.3.1)
37
+ rack (>= 1.3.4, ~> 1.3)
38
+ rack-protection (>= 1.1.2, ~> 1.1)
39
+ tilt (>= 1.3.3, ~> 1.3)
40
+ tilt (1.3.3)
41
+ webmock (1.7.6)
42
+ addressable (~> 2.2, > 2.2.5)
43
+ crack (>= 0.1.7)
44
+
45
+ PLATFORMS
46
+ ruby
47
+
48
+ DEPENDENCIES
49
+ proxy-server!
50
+ rack-test (>= 0.5.7)
51
+ rake (>= 0.9.2)
52
+ rspec (>= 2.6.0)
53
+ simplecov (>= 0.4.2)
54
+ webmock
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require 'rspec/core/rake_task'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ task :coverage do
7
+ require 'simplecov'
8
+ require 'rspec/core'
9
+
10
+ SimpleCov.start do
11
+ add_group "lib", "lib"
12
+ end
13
+ SimpleCov.start
14
+ RSpec::Core::Runner.run %w[spec]
15
+ end
16
+
17
+
18
+ require 'rake/clean'
19
+ CLEAN.include %w(**/*.{log,pyc,rbc,tgz} doc)
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'proxy-server'
3
+ require 'rest_client' # you will need to install this via gem install rest-client / bundler etc
4
+ require 'json'
5
+
6
+ ENV.clear
7
+
8
+ Thread.new do
9
+ ProxyManager.run! :port => 4983
10
+ end
11
+
12
+ until_proxy_is_running = 2
13
+ sleep until_proxy_is_running # need to implement the ability to wait for the proxy to be running
14
+
15
+ proxy = JSON.parse(RestClient.post 'http://localhost:4983/proxies', {:proxy => 'http://www-cache.reith.bbc.co.uk:80'})
16
+
17
+ sleep until_proxy_is_running
18
+
19
+ RestClient.post "http://localhost:4983/proxies/#{proxy['port']}/requests", {:track => 'google.com'}
20
+
21
+ client = HTTPClient.new :proxy => "http://localhost:#{proxy['port']}"
22
+ client.get 'http://www.google.com'
23
+ client.get 'http://github.com'
24
+
25
+ p JSON.parse(HTTPClient.get("http://localhost:4983/proxies/#{proxy['port']}/requests").body)
26
+
27
+
28
+
@@ -0,0 +1,70 @@
1
+ require_relative './proxy_server'
2
+ require 'sinatra/base'
3
+ require 'json'
4
+
5
+ class ProxyManager < Sinatra::Base
6
+ START_PORT = 5000
7
+
8
+ attr_reader :running_proxy_servers, :assigned_proxy_ports
9
+ disable :show_exceptions
10
+
11
+ def initialize()
12
+ @running_proxy_servers = {}
13
+ @assigned_proxy_ports = []
14
+ super
15
+ end
16
+
17
+ get '/proxies' do
18
+ assigned_proxy_ports.to_json
19
+ end
20
+
21
+ post '/proxies' do
22
+ new_proxy_port = assigned_proxy_ports.max
23
+ new_proxy_port += 1 unless new_proxy_port.nil?
24
+ new_proxy_port ||= START_PORT
25
+
26
+ assigned_proxy_ports << new_proxy_port
27
+
28
+ options = {
29
+ :port => new_proxy_port
30
+ }
31
+
32
+ options[:proxy] = params[:proxy] if params[:proxy]
33
+ start_proxy(options)
34
+
35
+ {:port => new_proxy_port}.to_json
36
+ end
37
+
38
+ delete '/proxies' do
39
+ assigned_proxy_ports.clear
40
+ end
41
+
42
+ post '/proxies/:port/requests' do |port|
43
+ proxy_server = get_proxy(port.to_i)
44
+ proxy_server.track_request(params[:track])
45
+ end
46
+
47
+ get '/proxies/:port/requests' do |port|
48
+ proxy_server = get_proxy(port.to_i)
49
+ proxy_server.requests.to_json
50
+ end
51
+
52
+ post '/proxies/:port/requests/substitute' do |port|
53
+ proxy_server = get_proxy(port.to_i)
54
+ options = {}
55
+ options[:body] = params[:body] if params[:body]
56
+ options[:url] = params[:url] if params[:url]
57
+ proxy_server.substitute_request(params[:pattern], options)
58
+ end
59
+
60
+ def get_proxy(port)
61
+ running_proxy_servers[port]
62
+ end
63
+
64
+ def start_proxy(options)
65
+ proxy_server = ProxyServer.new(options)
66
+ proxy_server.run
67
+ running_proxy_servers[options[:port]] = proxy_server
68
+ end
69
+ end
70
+
@@ -0,0 +1,104 @@
1
+ require 'rack'
2
+ require 'httpclient'
3
+
4
+ class ProxyServer
5
+
6
+ attr_reader :upstream_proxy, :port
7
+ attr_reader :requests
8
+
9
+ DEFAULT_PORT = 8080
10
+
11
+ def initialize(options = {})
12
+ @upstream_proxy = options[:proxy]
13
+ @client = create_http_client
14
+
15
+ @port = options.fetch(:port, DEFAULT_PORT)
16
+
17
+ @substitute_requests = {}
18
+ @requests = []
19
+ @track_requests = []
20
+ end
21
+
22
+ def create_http_client
23
+ HTTPClient.new :proxy => @upstream_proxy
24
+ end
25
+
26
+ def run
27
+ Thread.new do
28
+ @handler = Rack::Handler::WEBrick
29
+
30
+ @handler.run self, :Port => self.port
31
+ end
32
+ end
33
+
34
+ def call(env)
35
+ log_request env
36
+
37
+ response = get_substitution(env)
38
+ return response unless response.nil?
39
+
40
+ method = env['REQUEST_METHOD']
41
+ uri = "http://#{env['HTTP_HOST']}#{env['PATH_INFO']}"
42
+ params = get_params(env['QUERY_STRING'])
43
+ body = get_request_body(env)
44
+ headers = get_request_headers(env)
45
+ response = @client.request(method, uri, params, body, headers)
46
+
47
+ [ response.status, response.headers, [response.body] ]
48
+ end
49
+
50
+ def get_substitution(env)
51
+ uri = env['REQUEST_URI'] || "http://#{env['SERVER_NAME']}#{env['PATH_INFO']}#{env['QUERY_STRING'] ? '?'+env['QUERY_STRING'] : ''}"
52
+ @substitute_requests.each do |pattern, options|
53
+ if Regexp.new(pattern) =~ uri
54
+ return get_substituted_response(options)
55
+ end
56
+ end
57
+ nil
58
+ end
59
+
60
+ def get_substituted_response(options)
61
+ if options[:body]
62
+ [ 200, {}, [options[:body]] ]
63
+ elsif options[:url]
64
+ response = @client.get(options[:url])
65
+ [ response.status, response.headers, [response.body] ]
66
+ end
67
+ end
68
+
69
+ def substitute_request(pattern, options)
70
+ @substitute_requests[pattern] = options
71
+ end
72
+
73
+ def track_request(pattern)
74
+ @track_requests << pattern
75
+ end
76
+
77
+ def get_params(query_string)
78
+ query_string.split('&').inject({}) do |hsh, i|
79
+ kv = i.split('='); hsh[kv[0]] = kv[1]; hsh
80
+ end unless query_string.nil? or query_string.length == 0
81
+ end
82
+
83
+ def log_request(env)
84
+
85
+ url = env['REQUEST_URI'] || "http://#{env['SERVER_NAME']}#{env['PATH_INFO']}#{env['QUERY_STRING'].length>0 ? '?'+env['QUERY_STRING'] : ''}"
86
+ @track_requests.each do |pattern|
87
+ @requests << url if Regexp.new(pattern) =~ url
88
+ end
89
+ end
90
+
91
+ private
92
+ def get_request_body(env)
93
+ body = ''
94
+ env['rack.input'].each_line {|string| body << string }
95
+ body
96
+ end
97
+
98
+ def get_request_headers(env)
99
+ headers = {}
100
+ env.each {|k,v| if k =~ /HTTP_(\w+)/ then headers[$1] = v end }
101
+ headers
102
+ end
103
+ end
104
+
@@ -0,0 +1,2 @@
1
+ require_relative './proxy/proxy_server'
2
+ require_relative './proxy/proxy_manager'
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'proxy-server'
5
+ s.version = '0.0.1'
6
+ s.authors = ["Derek Ekins"]
7
+ s.description = 'Proxy server'
8
+ s.summary = "proxy-server-#{s.version}"
9
+ s.email = 'derek@spathi.com'
10
+ s.homepage = "http://github.com/dereke/proxy-server"
11
+
12
+ s.platform = Gem::Platform::RUBY
13
+
14
+ s.add_dependency 'json', '>= 1.4.6'
15
+ s.add_dependency 'sinatra', '>= 1.2.6'
16
+ s.add_dependency 'httpclient'
17
+
18
+ s.add_development_dependency 'rake', '>= 0.9.2'
19
+ s.add_development_dependency 'rspec', '>= 2.6.0'
20
+ s.add_development_dependency 'simplecov', '>= 0.4.2'
21
+ s.add_development_dependency 'webmock'
22
+
23
+
24
+ s.add_development_dependency 'rack-test', '>= 0.5.7'
25
+
26
+ s.rubygems_version = ">= 1.6.1"
27
+ s.files = `git ls-files`.split("\n")
28
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
29
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
30
+ s.rdoc_options = ["--charset=UTF-8"]
31
+ s.require_path = "lib"
32
+ end
@@ -0,0 +1,77 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../../lib/proxy/proxy_manager'
3
+ require 'rack/test'
4
+ require 'webmock/rspec'
5
+
6
+ describe ProxyManager do
7
+ include Rack::Test::Methods
8
+
9
+ before do
10
+ @proxy_server = Object.new
11
+ ProxyManager.any_instance.stub(:start_proxy).with(anything()).and_return(@proxy_server)
12
+ ProxyManager.any_instance.stub(:get_proxy).with(anything()).and_return(@proxy_server)
13
+ end
14
+
15
+ let(:app) { ProxyManager.new }
16
+
17
+ it "should create a new proxy when I ask for one" do
18
+ response = post '/proxies'
19
+ proxy = JSON.parse(response.body)
20
+ proxy.should == {'port' => ProxyManager::START_PORT}
21
+ end
22
+
23
+ it "should create a new proxy with an upstream proxy when asked to" do
24
+ ProxyManager.any_instance.should_receive(:start_proxy).with(hash_including(:proxy => 'http://my_proxy:80'))
25
+ response = post '/proxies', {:proxy => 'http://my_proxy:80'}
26
+ end
27
+
28
+ it "should tell me about all the proxies that have been created" do
29
+ post '/proxies'
30
+ response = get '/proxies'
31
+ JSON.parse(response.body).should == [ProxyManager::START_PORT]
32
+ end
33
+
34
+ it "should remove all proxies when asked" do
35
+ post '/proxies'
36
+ delete '/proxies'
37
+
38
+ response = get '/proxies'
39
+ JSON.parse(response.body).should == []
40
+ end
41
+
42
+ it "should assign new proxy ports when more than one is asked for" do
43
+ first_expected_proxy = ProxyManager::START_PORT
44
+ second_expected_proxy = ProxyManager::START_PORT + 1
45
+
46
+ first_response = post '/proxies'
47
+ second_response = post '/proxies'
48
+
49
+ JSON.parse(first_response.body)['port'].should == first_expected_proxy
50
+ JSON.parse(second_response.body)['port'].should == second_expected_proxy
51
+ end
52
+
53
+ it "ads a given url to a list of them to track" do
54
+ track_url = 'public/.*.js'
55
+
56
+ @proxy_server.should_receive(:track_request).with(track_url)
57
+ post "/proxies/1111/requests", {:track => track_url}
58
+ end
59
+
60
+ it "ads a given url to a list of them to track" do
61
+ @proxy_server.stub!(:requests).and_return(['request 1', 'request 2'])
62
+ response = get "/proxies/1111/requests"
63
+ requests = JSON.parse(response.body)
64
+ requests.should include('request 1')
65
+ requests.should include('request 2')
66
+ end
67
+
68
+ it "can substitute a request with another body" do
69
+ @proxy_server.should_receive(:substitute_request).with('*.js', :body => 'alert(1);')
70
+ post "/proxies/1111/requests/substitute", {:pattern => '*.js', :body => 'alert(1);'}
71
+ end
72
+
73
+ it "can substitute a request with another url" do
74
+ @proxy_server.should_receive(:substitute_request).with('*.js', :url => 'http://example.com/test.js')
75
+ post "/proxies/1111/requests/substitute", {:pattern => '*.js', :url => 'http://example.com/test.js'}
76
+ end
77
+ end
@@ -0,0 +1,81 @@
1
+ require_relative '../spec_helper'
2
+ require_relative '../../lib/proxy/proxy_server'
3
+ require 'rack/test'
4
+ require 'webmock/rspec'
5
+
6
+ describe ProxyServer do
7
+ include Rack::Test::Methods
8
+ before do
9
+ ENV.clear
10
+ end
11
+
12
+ let(:app) { ProxyServer.new }
13
+
14
+ it "can be configured with an upstream proxy" do
15
+ proxy_uri = 'http://test-proxy:80'
16
+ proxy = ProxyServer.new :proxy => proxy_uri
17
+ proxy.upstream_proxy.should == proxy_uri
18
+ end
19
+
20
+ it "can be configured to run on a specific port" do
21
+ proxy_port = 8080
22
+ proxy = ProxyServer.new :port => proxy_port
23
+ proxy.port.should == proxy_port
24
+ end
25
+
26
+ it "default the port when none is specified" do
27
+ proxy = ProxyServer.new
28
+ proxy.port.should == ProxyServer::DEFAULT_PORT
29
+ end
30
+
31
+ it "should allow requests" do
32
+ stub_request(:get, "http://www.example.com")
33
+
34
+ response = get 'http://www.example.com'
35
+ response.status.should == 200
36
+ end
37
+
38
+ it "should allow requests with query strings" do
39
+ stub_request(:get, "http://www.example.com?para=value")
40
+
41
+ response = get "http://www.example.com?para=value"
42
+ response.status.should == 200
43
+ end
44
+
45
+ it "can substitute the response of a request with a body of text" do
46
+ proxy = ProxyServer.new
47
+ expected_response = "this is not what you are looking for"
48
+ proxy.substitute_request '.*com', :body => expected_response
49
+
50
+ browser = Rack::Test::Session.new(Rack::MockSession.new(proxy))
51
+ response = browser.get 'http://www.google.com'
52
+ response.body.should == expected_response
53
+ end
54
+
55
+ it "can substitute the response of a request with another url to call" do
56
+ proxy = ProxyServer.new
57
+ substitute_url = "http://example.com"
58
+ expected_response_body = "substitute body"
59
+ stub_request(:get, substitute_url).to_return(:body => expected_response_body)
60
+ proxy.substitute_request '.*com', :url => substitute_url
61
+
62
+ browser = Rack::Test::Session.new(Rack::MockSession.new(proxy))
63
+ response = browser.get 'http://www.google.com'
64
+ response.body.should == expected_response_body
65
+ end
66
+
67
+ it "tracks a url that matches the pattern" do
68
+ proxy = ProxyServer.new
69
+ expected_response = "this is not what you are looking for"
70
+ proxy.track_request '.*com'
71
+
72
+ stub_request(:get, 'http://www.google.com/')
73
+ stub_request(:get, 'http://www.google.co.uk/')
74
+
75
+ browser = Rack::Test::Session.new(Rack::MockSession.new(proxy))
76
+ browser.get 'http://www.google.com/'
77
+ browser.get 'http://www.google.co.uk/'
78
+
79
+ proxy.requests.should include('http://www.google.com/')
80
+ end
81
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+ require 'rspec'
5
+ ENV['RACK_ENV'] = 'test'
6
+
7
+ class String
8
+ alias each each_line
9
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: proxy-server
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Derek Ekins
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-13 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: &12655120 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.4.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *12655120
25
+ - !ruby/object:Gem::Dependency
26
+ name: sinatra
27
+ requirement: &12654340 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.2.6
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *12654340
36
+ - !ruby/object:Gem::Dependency
37
+ name: httpclient
38
+ requirement: &12653840 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *12653840
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &12653160 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.2
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *12653160
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: &12652520 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: 2.6.0
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *12652520
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: &12651940 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: 0.4.2
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *12651940
80
+ - !ruby/object:Gem::Dependency
81
+ name: webmock
82
+ requirement: &12651440 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *12651440
91
+ - !ruby/object:Gem::Dependency
92
+ name: rack-test
93
+ requirement: &12650780 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: 0.5.7
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *12650780
102
+ description: Proxy server
103
+ email: derek@spathi.com
104
+ executables: []
105
+ extensions: []
106
+ extra_rdoc_files: []
107
+ files:
108
+ - .gitignore
109
+ - Gemfile
110
+ - Gemfile.lock
111
+ - Rakefile
112
+ - examples/proxy_in_process.rb
113
+ - lib/proxy-server.rb
114
+ - lib/proxy/proxy_manager.rb
115
+ - lib/proxy/proxy_server.rb
116
+ - proxy-server.gemspec
117
+ - spec/proxy/proxy_manager_spec.rb
118
+ - spec/proxy/proxy_server_spec.rb
119
+ - spec/spec_helper.rb
120
+ homepage: http://github.com/dereke/proxy-server
121
+ licenses: []
122
+ post_install_message:
123
+ rdoc_options:
124
+ - --charset=UTF-8
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 1.8.10
142
+ signing_key:
143
+ specification_version: 3
144
+ summary: proxy-server-0.0.1
145
+ test_files: []