proxymachine 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,12 @@
1
+ = 1.1.0 / 2009-11-05
2
+ * New Features
3
+ * Add { :remote, :data, :reply } command [github.com/coderrr]
4
+ * Minor Changes
5
+ * Namespace connection classes under ProxyMachine instead of EM [github.com/cmelbye]
6
+ * Require socket [github.com/cmelbye]
7
+ * Up EM dep to 0.12.10
8
+ * Add SOCKS4 Proxy example [github.com/coderrr]
9
+
1
10
  = 1.0.0 / 2009-10-19
2
11
  * No changes. Production ready!
3
12
 
data/README.md CHANGED
@@ -26,7 +26,9 @@ backend connections are hooked up to form a transparent proxy. This
26
26
  bidirectional proxy continues to exist until either the client or backend
27
27
  close the connection.
28
28
 
29
- ProxyMachine was developed for GitHub's federated architecture and is successfully used in production to proxy millions of requests every day. The performance and memory profile have both proven to be excellent.
29
+ ProxyMachine was developed for GitHub's federated architecture and is
30
+ successfully used in production to proxy millions of requests every day. The
31
+ performance and memory profile have both proven to be excellent.
30
32
 
31
33
 
32
34
  Installation
@@ -86,11 +88,26 @@ Example routing config file
86
88
  end
87
89
 
88
90
 
91
+ Example SOCKS4 Proxy in 7 Lines
92
+ -------------------------------
93
+
94
+ proxy do |data|
95
+ return if data.size < 9
96
+ v, c, port, o1, o2, o3, o4, user = data.unpack("CCnC4a*")
97
+ return { :close => "\0\x5b\0\0\0\0\0\0" } if v != 4 or c != 1
98
+ return if ! idx = user.index("\0")
99
+ { :remote => "#{[o1,o2,o3,o4]*'.'}:#{port}",
100
+ :reply => "\0\x5a\0\0\0\0\0\0",
101
+ :data => data[idx+9..-1] }
102
+ end
103
+
104
+
89
105
  Valid return values
90
106
  -------------------
91
107
 
92
108
  `{ :remote => String }` - String is the host:port of the backend server that will be proxied.
93
109
  `{ :remote => String, :data => String }` - Same as above, but send the given data instead.
110
+ `{ :remote => String, :data => String, :reply => String}` - Same as above, but reply with given data back to the client
94
111
  `{ :noop => true }` - Do nothing.
95
112
  `{ :close => true }` - Close the connection.
96
113
  `{ :close => String }` - Close the connection after sending the String.
@@ -120,4 +137,4 @@ your changes merged back into core is as follows:
120
137
  Copyright
121
138
  ---------
122
139
 
123
- Copyright (c) 2009 Tom Preston-Werner. See LICENSE for details.
140
+ Copyright (c) 2009 Tom Preston-Werner. See LICENSE for details.
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ begin
9
9
  gem.email = "tom@mojombo.com"
10
10
  gem.homepage = "http://github.com/mojombo/proxymachine"
11
11
  gem.authors = ["Tom Preston-Werner"]
12
- gem.add_dependency('eventmachine', '>= 0.12.9')
12
+ gem.add_dependency('eventmachine', '>= 0.12.10')
13
13
 
14
14
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
15
  end
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :minor: 0
3
- :patch: 0
4
2
  :major: 1
3
+ :minor: 1
4
+ :patch: 0
data/lib/proxymachine.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'eventmachine'
3
3
  require 'logger'
4
+ require 'socket'
4
5
 
5
6
  require 'proxymachine/client_connection'
6
7
  require 'proxymachine/server_connection'
@@ -79,7 +80,7 @@ class ProxyMachine
79
80
  EM.epoll
80
81
 
81
82
  EM.run do
82
- EventMachine::Protocols::ClientConnection.start(host, port)
83
+ ProxyMachine::ClientConnection.start(host, port)
83
84
  trap('QUIT') do
84
85
  self.graceful_shutdown('QUIT')
85
86
  end
@@ -1,104 +1,105 @@
1
- module EventMachine
2
- module Protocols
3
- class ClientConnection < Connection
4
- def self.start(host, port)
5
- $server = EM.start_server(host, port, self)
6
- LOGGER.info "Listening on #{host}:#{port}"
7
- LOGGER.info "Send QUIT to quit after waiting for all connections to finish."
8
- LOGGER.info "Send TERM or INT to quit after waiting for up to 10 seconds for connections to finish."
9
- end
1
+ class ProxyMachine
2
+ class ClientConnection < EventMachine::Connection
3
+ def self.start(host, port)
4
+ $server = EM.start_server(host, port, self)
5
+ LOGGER.info "Listening on #{host}:#{port}"
6
+ LOGGER.info "Send QUIT to quit after waiting for all connections to finish."
7
+ LOGGER.info "Send TERM or INT to quit after waiting for up to 10 seconds for connections to finish."
8
+ end
10
9
 
11
- def post_init
12
- LOGGER.info "Accepted #{peer}"
13
- @buffer = []
14
- @tries = 0
15
- ProxyMachine.incr
16
- end
10
+ def post_init
11
+ LOGGER.info "Accepted #{peer}"
12
+ @buffer = []
13
+ @tries = 0
14
+ ProxyMachine.incr
15
+ end
17
16
 
18
- def peer
19
- @peer ||=
20
- begin
21
- port, ip = Socket.unpack_sockaddr_in(get_peername)
22
- "#{ip}:#{port}"
23
- end
17
+ def peer
18
+ @peer ||=
19
+ begin
20
+ port, ip = Socket.unpack_sockaddr_in(get_peername)
21
+ "#{ip}:#{port}"
24
22
  end
23
+ end
25
24
 
26
- def receive_data(data)
27
- if !@server_side
28
- @buffer << data
29
- ensure_server_side_connection
30
- end
31
- rescue => e
32
- close_connection
33
- LOGGER.info "#{e.class} - #{e.message}"
25
+ def receive_data(data)
26
+ if !@server_side
27
+ @buffer << data
28
+ ensure_server_side_connection
34
29
  end
30
+ rescue => e
31
+ close_connection
32
+ LOGGER.info "#{e.class} - #{e.message}"
33
+ end
35
34
 
36
- def ensure_server_side_connection
37
- @timer.cancel if @timer
38
- unless @server_side
39
- commands = ProxyMachine.router.call(@buffer.join)
40
- LOGGER.info "#{peer} #{commands.inspect}"
41
- close_connection unless commands.instance_of?(Hash)
42
- if remote = commands[:remote]
43
- m, host, port = *remote.match(/^(.+):(.+)$/)
44
- if try_server_connect(host, port.to_i)
45
- if data = commands[:data]
46
- @buffer = [data]
47
- end
48
- send_and_clear_buffer
35
+ def ensure_server_side_connection
36
+ @timer.cancel if @timer
37
+ unless @server_side
38
+ commands = ProxyMachine.router.call(@buffer.join)
39
+ LOGGER.info "#{peer} #{commands.inspect}"
40
+ close_connection unless commands.instance_of?(Hash)
41
+ if remote = commands[:remote]
42
+ m, host, port = *remote.match(/^(.+):(.+)$/)
43
+ if try_server_connect(host, port.to_i)
44
+ if data = commands[:data]
45
+ @buffer = [data]
49
46
  end
50
- elsif close = commands[:close]
51
- if close == true
52
- close_connection
53
- else
54
- send_data(close)
55
- close_connection_after_writing
47
+ if reply = commands[:reply]
48
+ send_data(reply)
56
49
  end
57
- elsif commands[:noop]
58
- # do nothing
59
- else
50
+ send_and_clear_buffer
51
+ end
52
+ elsif close = commands[:close]
53
+ if close == true
60
54
  close_connection
55
+ else
56
+ send_data(close)
57
+ close_connection_after_writing
61
58
  end
59
+ elsif commands[:noop]
60
+ # do nothing
61
+ else
62
+ close_connection
62
63
  end
63
64
  end
65
+ end
64
66
 
65
- def try_server_connect(host, port)
66
- @server_side = ServerConnection.request(host, port, self)
67
- proxy_incoming_to(@server_side, 10240)
68
- LOGGER.info "Successful connection to #{host}:#{port}."
69
- true
70
- rescue => e
71
- if @tries < 10
72
- @tries += 1
73
- LOGGER.info "Failed on server connect attempt #{@tries}. Trying again..."
74
- @timer.cancel if @timer
75
- @timer = EventMachine::Timer.new(0.1) do
76
- self.ensure_server_side_connection
77
- end
78
- else
79
- LOGGER.info "Failed after ten connection attempts."
67
+ def try_server_connect(host, port)
68
+ @server_side = ServerConnection.request(host, port, self)
69
+ proxy_incoming_to(@server_side, 10240)
70
+ LOGGER.info "Successful connection to #{host}:#{port}."
71
+ true
72
+ rescue => e
73
+ if @tries < 10
74
+ @tries += 1
75
+ LOGGER.info "Failed on server connect attempt #{@tries}. Trying again..."
76
+ @timer.cancel if @timer
77
+ @timer = EventMachine::Timer.new(0.1) do
78
+ self.ensure_server_side_connection
80
79
  end
81
- false
80
+ else
81
+ LOGGER.info "Failed after ten connection attempts."
82
82
  end
83
+ false
84
+ end
83
85
 
84
- def send_and_clear_buffer
85
- if !@buffer.empty?
86
- @buffer.each do |x|
87
- @server_side.send_data(x)
88
- end
89
- @buffer = []
86
+ def send_and_clear_buffer
87
+ if !@buffer.empty?
88
+ @buffer.each do |x|
89
+ @server_side.send_data(x)
90
90
  end
91
+ @buffer = []
91
92
  end
93
+ end
92
94
 
93
- def unbind
94
- @server_side.close_connection_after_writing if @server_side
95
- ProxyMachine.decr
96
- end
95
+ def unbind
96
+ @server_side.close_connection_after_writing if @server_side
97
+ ProxyMachine.decr
98
+ end
97
99
 
98
- # Proxy connection has been lost
99
- def proxy_target_unbound
100
- @server_side = nil
101
- end
100
+ # Proxy connection has been lost
101
+ def proxy_target_unbound
102
+ @server_side = nil
102
103
  end
103
104
  end
104
105
  end
@@ -1,21 +1,19 @@
1
- module EventMachine
2
- module Protocols
3
- class ServerConnection < Connection
4
- def self.request(host, port, client_side)
5
- EventMachine.connect(host, port, self, client_side)
6
- end
1
+ class ProxyMachine
2
+ class ServerConnection < EventMachine::Connection
3
+ def self.request(host, port, client_side)
4
+ EventMachine.connect(host, port, self, client_side)
5
+ end
7
6
 
8
- def initialize(conn)
9
- @client_side = conn
10
- end
7
+ def initialize(conn)
8
+ @client_side = conn
9
+ end
11
10
 
12
- def post_init
13
- proxy_incoming_to(@client_side, 10240)
14
- end
11
+ def post_init
12
+ proxy_incoming_to(@client_side, 10240)
13
+ end
15
14
 
16
- def unbind
17
- @client_side.close_connection_after_writing
18
- end
15
+ def unbind
16
+ @client_side.close_connection_after_writing
19
17
  end
20
18
  end
21
19
  end
data/proxymachine.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{proxymachine}
8
- s.version = "1.0.0"
8
+ s.version = "1.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tom Preston-Werner"]
12
- s.date = %q{2009-10-19}
12
+ s.date = %q{2009-11-05}
13
13
  s.default_executable = %q{proxymachine}
14
14
  s.email = %q{tom@mojombo.com}
15
15
  s.executables = ["proxymachine"]
@@ -56,11 +56,11 @@ Gem::Specification.new do |s|
56
56
  s.specification_version = 3
57
57
 
58
58
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
59
- s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.9"])
59
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.10"])
60
60
  else
61
- s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
61
+ s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
62
62
  end
63
63
  else
64
- s.add_dependency(%q<eventmachine>, [">= 0.12.9"])
64
+ s.add_dependency(%q<eventmachine>, [">= 0.12.10"])
65
65
  end
66
66
  end
@@ -13,6 +13,8 @@ proxy do |data|
13
13
  { :noop => true }
14
14
  elsif data == 'e' * 2048 + 'f'
15
15
  { :remote => "localhost:9980" }
16
+ elsif data == 'g'
17
+ { :remote => "localhost:9980", :data => 'g2', :reply => 'g3-' }
16
18
  else
17
19
  { :close => true }
18
20
  end
@@ -28,6 +28,10 @@ class ProxymachineTest < Test::Unit::TestCase
28
28
  assert_proxy('localhost', 9990, 'd', 'ddd')
29
29
  end
30
30
 
31
+ should "handle data plus reply" do
32
+ assert_proxy('localhost', 9990, 'g', 'g3-9980:g2')
33
+ end
34
+
31
35
  should "handle noop" do
32
36
  sock = TCPSocket.new('localhost', 9990)
33
37
  sock.write('e' * 2048)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proxymachine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Preston-Werner
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-19 00:00:00 -07:00
12
+ date: 2009-11-05 00:00:00 -08:00
13
13
  default_executable: proxymachine
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.12.9
23
+ version: 0.12.10
24
24
  version:
25
25
  description:
26
26
  email: tom@mojombo.com