r_proxy 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8a74c2bf49d46d1c83f61c84c5d93e489180f73fe6d6bb1a691700db3a55e66
4
- data.tar.gz: 03d4d23c639f5b410f3d50260d0d7db241bb0393cbc0c0f1b6a3c2c584a22c4d
3
+ metadata.gz: eb665c8aedadf57b45585d927e08de7e572dc1e5bfdca207a0f889b41791d4c9
4
+ data.tar.gz: 8fea768841cce3fc8741efdefdbee247b0a48abf680bf906d35f97e6fcbf2e43
5
5
  SHA512:
6
- metadata.gz: a8cde243c051d33e13a64160f31582a5fed08db3077a470b6823db087c5fcd36092dbaec32d80b51f620b07fe3dd317747ef800bb964ee0e0c2e00ef0cf523b1
7
- data.tar.gz: '0903bb936989764a934a10f0c8c3070324f97ec1c3416be22082a97eaaa28283da8ff749faa11d4e3d98ee0d23e25de279f7edaef5e70382cd92b128f5c2cf84'
6
+ metadata.gz: e747ed1002a3a05330312691c9dbe2648c96859ff8af184dcd3c1e5f2738f58f37642343806f5ade14735c7a931e84f58a20279adf8bf3c088d5d71f66b5539e
7
+ data.tar.gz: b850bad10bf4b3d361bfcb8fa9d1fbe395dc6816c1e14afe3799d9be876fab53f1db3c8b25419116af121fb91e9f9141c7f0ca4cee0418ca15da93691c351ce8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- r_proxy (0.1.1)
4
+ r_proxy (0.2.0)
5
5
  eventmachine (~> 1.2, >= 1.2.7)
6
6
  redis (~> 4.1, >= 4.1.4)
7
7
 
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  a powerful ruby http proxy server, base on eventmachine
4
4
 
5
- able to run in multi-process like nginx works
5
+ able to run in multi-process like nginx workers
6
6
  ## Installation
7
7
 
8
8
  Add this line to your application's Gemfile:
@@ -79,5 +79,5 @@ server.run!
79
79
 
80
80
  ## Contributing
81
81
 
82
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/r_proxy.
82
+ Bug reports and pull requests are welcome on GitHub at https://github.com/nickoan/r_proxy.
83
83
 
data/example.rb CHANGED
@@ -7,11 +7,16 @@ server.set(:port, 8080)
7
7
 
8
8
  server.set(:instances, 3)
9
9
 
10
- server.set(:disable_auth, true)
11
- # server.set(:disable_unbind_cb, true)
12
- server.set(:enable_ssl, false)
10
+ #server.set(:disable_auth, true)
11
+ server.set(:disable_unbind_cb, false)
12
+ server.set(:enable_ssl, true)
13
13
 
14
- server.set(:callback_url,'http://127.0.0.1:1234')
14
+ server.set(:callback_url,'http://localhost:3000/api/proxy_callback')
15
+
16
+ server.set(:no_cache_below, 1 * 1024 * 1024 * 1024)
17
+ server.set(:cache_clear_threshold, 1)
18
+ server.set(:enable_force_quit, true)
19
+ server.set(:enable_cache, true)
15
20
 
16
21
  server.set(:redis_url, "redis://@localhost:6379/1")
17
22
 
@@ -0,0 +1,37 @@
1
+ module RProxy
2
+ class CachePool
3
+
4
+ def initialize
5
+ @pool = {}
6
+ @able_write = true
7
+ end
8
+
9
+ def []=(key, value)
10
+ return value if !@able_write
11
+ @pool[key] = value
12
+ end
13
+
14
+
15
+ def [](key)
16
+ @pool[key]
17
+ end
18
+
19
+ def writable?
20
+ @able_write
21
+ end
22
+
23
+ def disable_write!
24
+ @able_write = false
25
+ end
26
+
27
+ def enable_write!
28
+ @able_write = true
29
+ end
30
+
31
+ def flush
32
+ tmp = @pool
33
+ @pool = {}
34
+ tmp
35
+ end
36
+ end
37
+ end
@@ -1,6 +1,7 @@
1
1
  module RProxy
2
2
  class CallbackService
3
3
  def self.call(url, user, pass, value)
4
+ return if url.nil? || url.empty?
4
5
  uri = URI(url)
5
6
  tls = uri.scheme == 'https'
6
7
 
@@ -20,9 +20,13 @@ module RProxy
20
20
  tmp = snapshot_value.to_i - result.to_i
21
21
 
22
22
  if tmp >= @usage_threshold
23
- connection = RProxy::CallbackService.call(@cb_url, user, pass, tmp)
24
- connection.assign_logger(@logger)
25
- @redis.setex(s_key, @snapshot_expire_in, result)
23
+ begin
24
+ connection = RProxy::CallbackService.call(@cb_url, user, pass, tmp)
25
+ connection.assign_logger(@logger)
26
+ @redis.setex(s_key, @snapshot_expire_in, result)
27
+ rescue => e
28
+ @logger.error("callback service: @id:#{s_key}, #{e.message}") if @logger
29
+ end
26
30
  end
27
31
  end
28
32
  end
@@ -40,6 +40,12 @@ module RProxy
40
40
  add_config(:usage_threshold, 1 * 1024 * 1024 * 1024)
41
41
  add_config(:proxy_buffer, 1024 * 1024 * 10) # default is 10M
42
42
 
43
+ add_config(:enable_cache, true)
44
+ add_config(:cache_clear_threshold, 200)
45
+ add_config(:no_cache_below, 500 * 1024 * 1024)
46
+
47
+ add_config(:enable_force_quit, true)
48
+
43
49
  add_config(:disable_auth, false)
44
50
  add_config(:disable_unbind_cb, false)
45
51
 
@@ -1,10 +1,9 @@
1
1
  module RProxy
2
2
  class ConnectionHandler < EM::Connection
3
- def initialize(config)
3
+ def initialize(config, cache_pool)
4
4
  @config = config
5
5
  @logger = @config.logger
6
6
  @redis = RProxy::RedisService.instance(@config.redis_url)
7
- @http_parser = HttpProxyParser.new(@redis)
8
7
  @disable_auth = @config.disable_auth
9
8
  @disable_unbind_cb = @config.disable_unbind_cb
10
9
  @buffer_size = @config.proxy_buffer
@@ -12,9 +11,11 @@ module RProxy
12
11
  @username = nil
13
12
  @password = nil
14
13
  @target_connection = nil
15
-
16
- @unbind_service = UnbindService.new(config, @redis)
14
+ @cache_pool = cache_pool
15
+ @usage_manager = RProxy::UsageManager.new(config, @cache_pool, @redis)
16
+ @http_parser = HttpProxyParser.new(@usage_manager)
17
17
  @snapshot_service = RProxy::CheckSnapshotService.new(@redis, @config)
18
+ @enable_force_quit = config.enable_force_quit
18
19
  end
19
20
 
20
21
  def post_init
@@ -27,9 +28,11 @@ module RProxy
27
28
  end
28
29
  @port, @ip = Socket.unpack_sockaddr_in(get_peername)
29
30
 
30
- @timer = EventMachine.add_timer(20) do
31
- self.close_connection(false)
32
- @timer = nil
31
+ if @enable_force_quit
32
+ @timer = EventMachine.add_timer(20) do
33
+ self.close_connection(false)
34
+ @timer = nil
35
+ end
33
36
  end
34
37
  rescue => e
35
38
  if @logger
@@ -50,7 +53,7 @@ module RProxy
50
53
  self,
51
54
  @disable_unbind_cb,
52
55
  @buffer_size,
53
- @unbind_service)
56
+ @usage_manager)
54
57
  @target_connection.assign_logger(@logger)
55
58
  if !@disable_auth
56
59
  @username = @http_parser.username
@@ -79,8 +82,11 @@ module RProxy
79
82
  end
80
83
 
81
84
  def unbind
85
+ if @timer
86
+ EventMachine.cancel_timer(@timer)
87
+ end
82
88
  return if @disable_unbind_cb
83
- @unbind_service.call(@username, @password, get_proxied_bytes)
89
+ @usage_manager.report_usage(@username, @password, get_proxied_bytes)
84
90
  end
85
91
  end
86
92
  end
@@ -36,7 +36,7 @@ module RProxy
36
36
 
37
37
  def init_headers
38
38
  {
39
- 'User-Agent' => "RSocks/#{RProxy::VERSION}",
39
+ 'User-Agent' => "RProxy/#{RProxy::VERSION}",
40
40
  'Content-Type' => 'application/json',
41
41
  }
42
42
  end
@@ -5,8 +5,8 @@ module RProxy
5
5
 
6
6
  attr_reader :username, :password
7
7
 
8
- def initialize(redis)
9
- @redis = redis
8
+ def initialize(usage_manager)
9
+ @usage_manager = usage_manager
10
10
  @max_connection_size = 4 * 1024
11
11
  end
12
12
 
@@ -31,11 +31,10 @@ module RProxy
31
31
  rescue
32
32
  raise RProxy::HTTPNotSupport, "token parse failed #{token}"
33
33
  end
34
- key = "proxy:#{@username}-#{@password}"
35
- value = @redis.get(key)
36
34
 
37
- raise RProxy::HTTPAuthFailed if value.nil?
38
- value
35
+ auth_result = @usage_manager.auth_user(@username, @password)
36
+ raise RProxy::HTTPAuthFailed if auth_result.nil?
37
+ auth_result
39
38
  end
40
39
 
41
40
  def parse_connect_request(data)
@@ -42,11 +42,11 @@ module RProxy
42
42
  instance_amount = @config.instances
43
43
  server = TCPServer.new(@config.host, @config.port)
44
44
  instance_amount.times do
45
- timestamp = Time.now.to_i
46
45
  pid = Process.fork do
46
+ timestamp = (Time.now.to_f * 1000).round
47
47
  begin
48
48
  @logger.info("r_proxy @#{timestamp} process start....") if @logger
49
- RProxy::ProxyServer.new(server, @config).run!
49
+ RProxy::ProxyServer.new(server, @config, timestamp).run!
50
50
  rescue Interrupt
51
51
  @logger.info("r_proxy TPC server instance @#{timestamp} closed now....") if @logger
52
52
  rescue => e
@@ -57,6 +57,7 @@ module RProxy
57
57
 
58
58
  Process.detach(pid)
59
59
  @pids << pid
60
+ sleep(0.1)
60
61
  end
61
62
 
62
63
  EventMachine.kqueue=(true)
@@ -1,14 +1,50 @@
1
1
  module RProxy
2
2
  class ProxyServer
3
- def initialize(sock, config)
3
+ def initialize(sock, config, instance_id)
4
4
  @sock = sock
5
5
  @config = config
6
+ @cache_pool = RProxy::CachePool.new
7
+ @logger = @config.logger
8
+ @instance_id = instance_id
6
9
  end
7
10
 
8
11
  def run!
9
12
  Signal.trap("TERM") { exit! }
10
13
  EventMachine.run do
11
- EventMachine.attach_server(@sock, RProxy::ConnectionHandler, @config)
14
+
15
+ if @config.enable_cache
16
+ @period_timer = EventMachine.add_periodic_timer(30) do
17
+ @cache_pool.disable_write!
18
+
19
+ tmp = @cache_pool.flush
20
+
21
+ report_and_clean_cache(tmp)
22
+
23
+ @cache_pool.enable_write!
24
+ end
25
+ end
26
+
27
+ EventMachine.attach_server(@sock, RProxy::ConnectionHandler, @config, @cache_pool)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def report_and_clean_cache(cache)
34
+ return if cache.empty?
35
+
36
+ redis = RProxy::RedisService.instance(@config.redis_url)
37
+ time_start = (Time.now.to_f * 1000).floor
38
+ cache.each do |k, value|
39
+ used = value[:used]
40
+ next if used.nil? || used.zero?
41
+ redis.decrby(k, used)
42
+ end
43
+ time_end = (Time.now.to_f * 1000).floor
44
+ spend = time_end - time_start
45
+
46
+ if spend > @config.cache_clear_threshold
47
+ @logger.info("@#{@instance_id} clear cache: #{spend} ms, remove: #{cache.size} items") if @logger
12
48
  end
13
49
  end
14
50
  end
@@ -1,11 +1,11 @@
1
1
  module RProxy
2
2
  class TargetConnection < EM::Connection
3
3
 
4
- def initialize(client, disable_cb, buffer_size, unbind)
4
+ def initialize(client, disable_cb, buffer_size, usage_manager)
5
5
  @disable_unbind_callback = disable_cb
6
6
  @client_connection = client
7
7
  @buffer_size = buffer_size
8
- @unbind_service = unbind
8
+ @usage_manager = usage_manager
9
9
  end
10
10
 
11
11
  def assign_logger(logger)
@@ -27,7 +27,7 @@ module RProxy
27
27
 
28
28
  def unbind
29
29
  return if @disable_unbind_callback
30
- @unbind_service.call(@username, @password, get_proxied_bytes)
30
+ @usage_manager.report_usage(@username, @password, get_proxied_bytes)
31
31
  end
32
32
 
33
33
  private
@@ -0,0 +1,59 @@
1
+ module RProxy
2
+ class UsageManager
3
+ def initialize(config, cache_pool, redis)
4
+ @enable_cache = config.enable_cache
5
+ @cache_pool = cache_pool
6
+ @redis = redis
7
+ @no_cache_below = config.no_cache_below
8
+ end
9
+
10
+ def auth_user(user, pass)
11
+ key = proxy_key(user, pass)
12
+
13
+ value = fetch_usage(key)
14
+
15
+ return value if !value.nil? && value.to_i > 0
16
+ nil
17
+ end
18
+
19
+ def report_usage(user, pass, value)
20
+ return if user.nil? || pass.nil? || value.nil?
21
+
22
+ key = proxy_key(user, pass)
23
+ cache = @cache_pool[key]
24
+
25
+ if cache.nil? || !@cache_pool.writable?
26
+ @redis.decrby(key, value)
27
+ else
28
+ tmp = cache[:used]
29
+ @cache_pool[key][:used] = tmp + value
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def fetch_usage(key)
36
+ return @redis.get(key) if !@enable_cache || !@cache_pool.writable?
37
+
38
+ cache = @cache_pool[key]
39
+
40
+ if cache.nil?
41
+ value = @redis.get(key)
42
+
43
+ return value if !value.nil? && value.to_i <= @no_cache_below
44
+
45
+ @cache_pool[key] = {
46
+ usage: value,
47
+ used: 0
48
+ }
49
+ return value
50
+ end
51
+
52
+ cache[:usage]
53
+ end
54
+
55
+ def proxy_key(user, pass)
56
+ "proxy:#{user}-#{pass}"
57
+ end
58
+ end
59
+ end
@@ -1,3 +1,3 @@
1
1
  module RProxy
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/r_proxy.rb CHANGED
@@ -3,6 +3,8 @@ require 'r_proxy/version'
3
3
  require 'eventmachine'
4
4
  require 'redis'
5
5
  require 'r_proxy/config'
6
+ require 'r_proxy/cache_pool'
7
+ require 'r_proxy/usage_manager'
6
8
  require 'r_proxy/constants'
7
9
  require 'r_proxy/check_snapshot_service'
8
10
  require 'r_proxy/http_proxy_parser'
@@ -13,7 +15,6 @@ require 'r_proxy/process_handler'
13
15
 
14
16
  require 'r_proxy/target_connection'
15
17
  require 'r_proxy/connection_handler'
16
- require 'r_proxy/unbind_service'
17
18
 
18
19
  require 'r_proxy/callback_connection'
19
20
  require 'r_proxy/http_post_template'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: r_proxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick An
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-08 00:00:00.000000000 Z
11
+ date: 2020-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eventmachine
@@ -68,6 +68,7 @@ files:
68
68
  - bin/setup
69
69
  - example.rb
70
70
  - lib/r_proxy.rb
71
+ - lib/r_proxy/cache_pool.rb
71
72
  - lib/r_proxy/callback_connection.rb
72
73
  - lib/r_proxy/callback_service.rb
73
74
  - lib/r_proxy/check_snapshot_service.rb
@@ -81,7 +82,7 @@ files:
81
82
  - lib/r_proxy/proxy_server.rb
82
83
  - lib/r_proxy/redis_service.rb
83
84
  - lib/r_proxy/target_connection.rb
84
- - lib/r_proxy/unbind_service.rb
85
+ - lib/r_proxy/usage_manager.rb
85
86
  - lib/r_proxy/version.rb
86
87
  - r_proxy.gemspec
87
88
  homepage: https://github.com/nickoan/r_proxy
@@ -1,24 +0,0 @@
1
- module RProxy
2
- class UnbindService
3
-
4
- def initialize(config, redis)
5
- @config = config
6
- @cb_url = config.callback_url
7
- @redis = redis
8
- @usage_threshold = @config.usage_threshold
9
- @snapshot_expire_in = 15 * 60 # 15 min
10
- end
11
-
12
- def call(user, pass, usage)
13
-
14
- return if user.nil? || pass.nil? || usage.nil?
15
-
16
- key = proxy_key(user, pass)
17
- @redis.decrby(key, usage)
18
- end
19
-
20
- def proxy_key(user, pass)
21
- "proxy:#{user}-#{pass}"
22
- end
23
- end
24
- end