fizx-em-proxy 0.1.1

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.
@@ -0,0 +1,23 @@
1
+ = EM-Proxy
2
+
3
+ EventMachine Proxy DSL:
4
+ - Slides from RailsConf 2009: http://bit.ly/D7oWB
5
+
6
+ == Simple port forwarding proxy
7
+
8
+ Proxy.start(:host => "0.0.0.0", :port => 80) do |conn|
9
+ conn.server :srv, :host => "127.0.0.1", :port => 81
10
+
11
+ # modify / process request stream
12
+ conn.on_data do |data|
13
+ p [:on_data, data]
14
+ data
15
+ end
16
+
17
+ # modify / process response stream
18
+ conn.on_response do |backend, resp|
19
+ p [:on_response, backend, resp]
20
+ resp
21
+ end
22
+ end
23
+
@@ -0,0 +1,12 @@
1
+ require "rubygems"
2
+ require "rack"
3
+
4
+ app = lambda {
5
+ p [:serving, ARGV[0]]
6
+ r = rand(2)
7
+
8
+ sleep(r)
9
+ [200, {"Content-Type" => "text/plain"}, ["hello world: #{r}"]]
10
+ }
11
+
12
+ Rack::Handler::Mongrel.run(app, {:Host => "0.0.0.0", :Port => ARGV[0]})
@@ -0,0 +1,43 @@
1
+ require 'lib/em-proxy'
2
+
3
+ Proxy.start(:host => "0.0.0.0", :port => 11300) do |conn|
4
+ conn.server :srv, :host => "127.0.0.1", :port => 11301
5
+
6
+ # put <pri> <delay> <ttr> <bytes>\r\n
7
+ PUT_CMD = /put (\d+) (\d+) (\d+) (\d+)\r\n/
8
+
9
+ conn.on_data do |data|
10
+ if put = data.match(PUT_CMD)
11
+
12
+ # archive any job > 10 minutes away
13
+ if put[2].to_i > 600
14
+ p [:put, :archive]
15
+ # INSERT INTO ....
16
+
17
+ conn.send_data "INSERTED 9999\r\n"
18
+ data = nil
19
+ end
20
+ end
21
+
22
+ data
23
+ end
24
+
25
+ conn.on_response do |backend, resp|
26
+ p [:resp, resp]
27
+ resp
28
+ end
29
+ end
30
+
31
+ #
32
+ # beanstalkd -p 11301 -d
33
+ # ruby examples/beanstalkd_interceptor.rb
34
+ #
35
+ # irb
36
+ # >> require 'beanstalk-client'
37
+ # >> beanstalk = Beanstalk::Pool.new(['127.0.0.1'])
38
+ # >> beanstalk.put("job1")
39
+ # => 1
40
+ # >> beanstalk.put("job2")
41
+ # => 2
42
+ # >> beanstalk.put("job3", 0, 1000)
43
+ # => 9999
@@ -0,0 +1,36 @@
1
+ require 'lib/em-proxy'
2
+
3
+ Proxy.start(:host => "0.0.0.0", :port => 80) do |conn|
4
+ @start = Time.now
5
+ @data = Hash.new("")
6
+
7
+ conn.server :prod, :host => "127.0.0.1", :port => 81 # production, will render resposne
8
+ conn.server :test, :host => "127.0.0.1", :port => 82 # testing, internal only
9
+
10
+ conn.on_data do |data|
11
+ # rewrite User-Agent
12
+ data.gsub(/User-Agent: .*?\r\n/, 'User-Agent: em-proxy/0.1\r\n')
13
+ end
14
+
15
+ conn.on_response do |server, resp|
16
+ # only render response from production
17
+ @data[server] += resp
18
+ resp if server == :prod
19
+ end
20
+
21
+ conn.on_finish do |name|
22
+ p [:on_finish, name, Time.now - @start]
23
+ p @data
24
+ end
25
+ end
26
+
27
+ #
28
+ # ruby examples/appserver.rb 81
29
+ # ruby examples/appserver.rb 82
30
+ # ruby examples/line_interceptor.rb
31
+ # curl localhost
32
+ #
33
+ # > [:on_finish, 1.008561]
34
+ # > {:prod=>"HTTP/1.1 200 OK\r\nConnection: close\r\nDate: Fri, 01 May 2009 04:20:00 GMT\r\nContent-Type: text/plain\r\n\r\nhello world: 0",
35
+ # :test=>"HTTP/1.1 200 OK\r\nConnection: close\r\nDate: Fri, 01 May 2009 04:20:00 GMT\r\nContent-Type: text/plain\r\n\r\nhello world: 1"}
36
+ #
@@ -0,0 +1,22 @@
1
+ require 'lib/em-proxy'
2
+
3
+ Proxy.start(:host => "0.0.0.0", :port => 80) do |conn|
4
+ conn.server :srv, :host => "127.0.0.1", :port => 81
5
+
6
+ conn.on_data do |data|
7
+ data
8
+ end
9
+
10
+ conn.on_response do |backend, resp|
11
+ # substitute all mentions of hello to 'good bye', aka intercepting proxy
12
+ resp.gsub(/hello/, 'good bye')
13
+ end
14
+ end
15
+
16
+ #
17
+ # ruby examples/appserver.rb 81
18
+ # ruby examples/line_interceptor.rb
19
+ # curl localhost
20
+ #
21
+ # > good bye world: 0
22
+ #
@@ -0,0 +1,18 @@
1
+ require 'lib/em-proxy'
2
+
3
+ Proxy.start(:host => "0.0.0.0", :port => 80) do |conn|
4
+ conn.server :srv, :host => "127.0.0.1", :port => 81
5
+
6
+ # modify / process request stream
7
+ conn.on_data do |data|
8
+ p [:on_data, data]
9
+ data
10
+ end
11
+
12
+ # modify / process response stream
13
+ conn.on_response do |backend, resp|
14
+ p [:on_response, backend, resp]
15
+ # resp = "HTTP/1.1 200 OK\r\nConnection: close\r\nDate: Thu, 30 Apr 2009 03:53:28 GMT\r\nContent-Type: text/plain\r\n\r\nHar!"
16
+ resp
17
+ end
18
+ end
@@ -0,0 +1,107 @@
1
+ require 'lib/em-proxy'
2
+ require 'em-http'
3
+ require 'yaml'
4
+ require 'net/http'
5
+
6
+ Proxy.start(:host => "0.0.0.0", :port => 2524) do |conn|
7
+ conn.server :srv, :host => "127.0.0.1", :port => 2525
8
+
9
+ RCPT_CMD = /RCPT TO:<(.*)?>\r\n/ # RCPT TO:<name@address.com>\r\n
10
+ FROM_CMD = /MAIL FROM:<(.*)?>\r\n/ # MAIL FROM:<ilya@aiderss.com>\r\n
11
+ MSG_CMD = /354 Start your message/ # 354 Start your message
12
+ MSGEND_CMD = /^.\r\n/
13
+
14
+ conn.on_data do |data|
15
+ @from = data.match(FROM_CMD)[1] if data.match(FROM_CMD)
16
+ @rcpt = data.match(RCPT_CMD)[1] if data.match(RCPT_CMD)
17
+ @done = true if data.match(MSGEND_CMD)
18
+
19
+ if @buffer
20
+ @msg += data
21
+ data = nil
22
+ end
23
+
24
+ if @done
25
+ @buffer = false
26
+ res = Net::HTTP.post_form(URI.parse('http://api.defensio.com/app/1.2/audit-comment/77ca297d7546705ee2b5136fad0dcaf8.yaml'), {
27
+ "owner-url" => "http://www.github.com/igrigorik/em-http-request",
28
+ "user-ip" => "216.16.254.254",
29
+ "article-date" => "2009/04/01",
30
+ "comment-author" => @from,
31
+ "comment-type" => "comment",
32
+ "comment-content" => @msg})
33
+
34
+ defensio = YAML.load(res.body)['defensio-result']
35
+ p [:defensio, "SPAM: #{defensio['spam']}, Spaminess: #{defensio['spaminess']}"]
36
+
37
+ if defensio['spam']
38
+ conn.send_data "550 No such user here\n"
39
+ else
40
+ data = @msg
41
+ end
42
+ end
43
+
44
+ data
45
+ end
46
+
47
+ conn.on_response do |server, resp|
48
+ p [:resp, resp]
49
+
50
+ if resp.match(MSG_CMD)
51
+ @buffer = true
52
+ @msg = ""
53
+ end
54
+
55
+ resp
56
+ end
57
+ end
58
+
59
+ # mailtrap run -p 2525 -f /tmp/mailtrap.log
60
+ # ruby examples/smtp_spam_filter.rb
61
+ #
62
+ # >> require 'net/smtp'
63
+ # >> smtp = Net::SMTP.start("localhost", 2524)
64
+ # >> smtp.send_message "Hello World!", "ilya@aiderss.com", "ilya@igvita.com"
65
+
66
+
67
+ # Protocol trace
68
+ #
69
+ # [:srv, :conn_complete]
70
+ # [:srv, "220 localhost MailTrap ready ESTMP\n"]
71
+ # [:relay_from_backend, :srv, "220 localhost MailTrap ready ESTMP\n"]
72
+ # [:resp, "220 localhost MailTrap ready ESTMP\n"]
73
+ # [:connection, "EHLO localhost.localdomain\r\n"]
74
+ # [:srv, "250-localhost offers just ONE extension my pretty"]
75
+ # [:relay_from_backend, :srv, "250-localhost offers just ONE extension my pretty"]
76
+ # [:resp, "250-localhost offers just ONE extension my pretty"]
77
+ # [:srv, "\n250 HELP\n"]
78
+ # [:relay_from_backend, :srv, "\n250 HELP\n"]
79
+ # [:resp, "\n250 HELP\n"]
80
+ # [:connection, "MAIL FROM:<ilya@aiderss.com>\r\n"]
81
+ # [:srv, "250 OK\n"]
82
+ # [:relay_from_backend, :srv, "250 OK\n"]
83
+ # [:resp, "250 OK\n"]
84
+ # [:connection, "RCPT TO:<ilya@igvita.com>\r\n"]
85
+ # [:srv, "250 OK"]
86
+ # [:relay_from_backend, :srv, "250 OK"]
87
+ # [:resp, "250 OK"]
88
+ # [:srv, "\n"]
89
+ # [:relay_from_backend, :srv, "\n"]
90
+ # [:resp, "\n"]
91
+ # [:connection, "DATA\r\n"]
92
+ # [:srv, "354 Start your message"]
93
+ # [:relay_from_backend, :srv, "354 Start your message"]
94
+ # [:resp, "354 Start your message"]
95
+ # [:srv, "\n"]
96
+ # [:relay_from_backend, :srv, "\n"]
97
+ # [:resp, "\n"]
98
+ # [:connection, "Hello World\r\n"]
99
+ # [:connection, ".\r\n"]
100
+ #
101
+ # [:defensio, "SPAM: false, Spaminess: 0.4"]
102
+ #
103
+ # [:srv, "250 OK\n"]
104
+ # [:relay_from_backend, :srv, "250 OK\n"]
105
+ # [:resp, "250 OK\n"]
106
+ #
107
+
@@ -0,0 +1,39 @@
1
+ require 'lib/em-proxy'
2
+
3
+ Proxy.start(:host => "0.0.0.0", :port => 2524) do |conn|
4
+ conn.server :srv, :host => "127.0.0.1", :port => 2525
5
+
6
+ # RCPT TO:<name@address.com>\r\n
7
+ RCPT_CMD = /RCPT TO:<(.*)?>\r\n/
8
+
9
+ conn.on_data do |data|
10
+
11
+ if rcpt = data.match(RCPT_CMD)
12
+ if rcpt[1] != "ilya@igvita.com"
13
+ conn.send_data "550 No such user here\n"
14
+ data = nil
15
+ end
16
+ end
17
+
18
+ data
19
+ end
20
+
21
+ conn.on_response do |backend, resp|
22
+ resp
23
+ end
24
+ end
25
+
26
+
27
+ # mailtrap run -p 2525 -f /tmp/mailtrap.log
28
+ # ruby examples/smtp_whitelist.rb
29
+ #
30
+ # >> require 'net/smtp'
31
+ # >> smtp = Net::SMTP.start("localhost", 2524)
32
+ # >> smtp.send_message "Hello World!", "ilya@aiderss.com", "ilya@igvita.com"
33
+ # => #<Net::SMTP::Response:0xb7dcff5c @status="250", @string="250 OK\n">
34
+ # >> smtp.finish
35
+ # => #<Net::SMTP::Response:0xb7dcc8d4 @status="221", @string="221 Seeya\n">
36
+ #
37
+ # >> smtp.send_message "Hello World!", "ilya@aiderss.com", "missing_user@igvita.com"
38
+ # => Net::SMTPFatalError: 550 No such user here
39
+ #
@@ -0,0 +1,8 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require "rubygems"
4
+ require "eventmachine"
5
+
6
+ %w[ backend proxy connection ].each do |file|
7
+ require "em-proxy/#{file}"
8
+ end
@@ -0,0 +1,36 @@
1
+ module EventMachine
2
+ module ProxyServer
3
+ class Backend < EventMachine::Connection
4
+ attr_accessor :plexer, :data, :name
5
+
6
+ def initialize
7
+ @connected = EM::DefaultDeferrable.new
8
+ @data = []
9
+ end
10
+
11
+ def connection_completed
12
+ p [@name, :conn_complete]
13
+ @connected.succeed
14
+ end
15
+
16
+ def receive_data(data)
17
+ p [@name, data]
18
+ @data.push data
19
+ @plexer.relay_from_backend(@name, data)
20
+ end
21
+
22
+ # Buffer data until the connection to the backend server
23
+ # is established and is ready for use
24
+ def send(data)
25
+ @connected.callback { send_data data }
26
+ end
27
+
28
+ # Notify upstream plexer that the backend server is done
29
+ # processing the request
30
+ def unbind
31
+ p [@name, :unbind]
32
+ @plexer.unbind_backend(@name)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,75 @@
1
+ module EventMachine
2
+ module ProxyServer
3
+ class Connection < EventMachine::Connection
4
+
5
+ ##### Proxy Methods
6
+ def on_data(&blk); @on_data = blk; end
7
+ def on_response(&blk); @on_response = blk; end
8
+ def on_finish(&blk); @on_finish = blk; end
9
+
10
+ ##### EventMachine
11
+ def initialize
12
+ @servers = {}
13
+ end
14
+
15
+ def receive_data(data)
16
+ p [:connection, data]
17
+ processed = @on_data.call(data)
18
+
19
+ if processed.is_a? Array
20
+ data, servers = *processed
21
+
22
+ # guard for "unbound" servers
23
+ servers = servers.collect {|s| @servers[s]}.compact
24
+ else
25
+ data = processed
26
+ servers ||= @servers.values.compact
27
+ end
28
+
29
+ servers.each do |s|
30
+ s.send_data data unless data.nil?
31
+ end
32
+ end
33
+
34
+ #
35
+ # initialize connections to backend servers
36
+ #
37
+ def server(name, opts)
38
+ srv = EventMachine::connect(opts[:host], opts[:port], EventMachine::ProxyServer::Backend) do |c|
39
+ c.name = name
40
+ c.plexer = self
41
+ end
42
+
43
+ @servers[name] = srv
44
+ end
45
+
46
+ #
47
+ # relay data from backend server to client
48
+ #
49
+ def relay_from_backend(name, data)
50
+ p [:relay_from_backend, name, data]
51
+
52
+ data = @on_response.call(name, data)
53
+ send_data data unless data.nil?
54
+ end
55
+
56
+ def unbind
57
+ # terminate any unfinished connections
58
+ @servers.values.compact.each do |s|
59
+ s.close_connection_after_writing
60
+ end
61
+
62
+ close_connection_after_writing
63
+ @on_finish.call(:done) if @servers.values.compact.size.zero? if @on_finish
64
+ end
65
+
66
+ def unbind_backend(name)
67
+ p [:unbind_backend, name]
68
+ @servers[name] = nil
69
+ @on_finish.call(name) if @on_finish
70
+ close_connection_after_writing if @servers.values.compact.size.zero?
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,20 @@
1
+ class Proxy
2
+
3
+ def self.start(options, &blk)
4
+ EM.epoll
5
+ EM.run do
6
+
7
+ trap("TERM") { stop }
8
+ trap("INT") { stop }
9
+
10
+ EventMachine::start_server(options[:host], options[:port], EventMachine::ProxyServer::Connection) do |c|
11
+ c.instance_eval(&blk)
12
+ end
13
+ end
14
+ end
15
+
16
+ def self.stop
17
+ puts "Terminating ProxyServer"
18
+ EventMachine.stop
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fizx-em-proxy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Ilya Grigorik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-07 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: eventmachine
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.12.2
24
+ version:
25
+ description: EventMachine Proxy DSL
26
+ email: ilya@igvita.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - README.rdoc
35
+ - examples/appserver.rb
36
+ - examples/beanstalkd_interceptor.rb
37
+ - examples/duplex.rb
38
+ - examples/line_interceptor.rb
39
+ - examples/port_forward.rb
40
+ - examples/smtp_spam_filter.rb
41
+ - examples/smtp_whitelist.rb
42
+ - lib/em-proxy.rb
43
+ - lib/em-proxy/backend.rb
44
+ - lib/em-proxy/connection.rb
45
+ - lib/em-proxy/proxy.rb
46
+ has_rdoc: true
47
+ homepage: http://github.com/igrigorik/em-proxy
48
+ licenses:
49
+ post_install_message:
50
+ rdoc_options: []
51
+
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project: em-proxy
69
+ rubygems_version: 1.3.5
70
+ signing_key:
71
+ specification_version: 2
72
+ summary: EventMachine Proxy DSL
73
+ test_files: []
74
+