r_proxy 0.1.1 → 0.2.0

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.
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