forward-proxy 0.4.0 → 0.6.2

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: 1d2b9dbd7abcfa81bcdbd29b6dd2045e6d2210e92b303f7cfcdc4ab35ab0b868
4
- data.tar.gz: 729d4b7d964c8a5a3bec860f6bceb669a12225900a01b4737de8fdf6ba02a049
3
+ metadata.gz: a1fe517be17887550c0afe7d8ad757227d79667083ad5530aa714a8e0b2398be
4
+ data.tar.gz: 574b65e5fd8bf6e324f0657f8bfc87342481c5f177c87f1e9f2cffe49953db17
5
5
  SHA512:
6
- metadata.gz: 70c0b51f0313e4ffedd1146ae93035db45bc8fe3a7cee647ea11cb0397d6862e99047befbc39aee6a08b17fd1e3d16394eb59ad9ad092140ea9354da10feadf6
7
- data.tar.gz: b0f3587f3c08b255be222f8f59530e19b5fcc6e589f93eafb1eaa21fac832a0f01847dfcf309ed443e51fdafd96a516241143c1a75795b3d09f0ce7088eedac0
6
+ metadata.gz: 34d36e4bfc21abaf59a75ecec4fa86b5d9157beb5e7f7437201f15689f584fb92076f6db5095929d52062b6c543a53d65bc81393bc5013204206d8f2c041c4d2
7
+ data.tar.gz: 4e7c99b2485929fe9fb93da86256bd642ff56f8f93207b3cdd0eccc6606d4c6f53d64514adbf550831062185390ae5b1327619bfa26dcd2631ca817acab6c36d
@@ -8,7 +8,7 @@ jobs:
8
8
 
9
9
  strategy:
10
10
  matrix:
11
- ruby: [ '2.3', '2.7' ]
11
+ ruby: [ '2.5', '2.7' ]
12
12
 
13
13
  steps:
14
14
  - uses: actions/checkout@v2
@@ -8,7 +8,7 @@ jobs:
8
8
 
9
9
  strategy:
10
10
  matrix:
11
- ruby: [ '2.3', '2.7' ]
11
+ ruby: [ '2.5', '2.7' ]
12
12
 
13
13
  steps:
14
14
  - uses: actions/checkout@v2
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.6.0
4
+
5
+ - add connection timeout to stop tracking connection from saturating client threads.
6
+ - add cli flats for connection timeout `-t` and `--timeout`.
7
+ - change cli short flag `-t` to `-c` for `--threads`.
8
+
9
+ ## 0.5.0
10
+
11
+ - increase default threads from `32` to `128`.
12
+
3
13
  ## 0.2.0
4
14
 
5
15
  - Extract errors into module.
data/README.md CHANGED
@@ -6,8 +6,8 @@ Minimal forward proxy using 150LOC and only standard libraries. Useful for devel
6
6
 
7
7
  ```
8
8
  $ forward-proxy --binding 0.0.0.0 --port 3182 --threads 2
9
- [2021-01-14 19:37:47 +1100] INFO Listening 0.0.0.0:3182
10
- [2021-01-14 19:38:24 +1100] INFO CONNECT raw.githubusercontent.com:443 HTTP/1.1
9
+ I, [2021-07-04T10:33:32.947653 #1790] INFO -- : Listening 0.0.0.0:3182
10
+ I, [2021-07-04T10:33:32.998298 #1790] INFO -- : CONNECT raw.githubusercontent.com:443 HTTP/1.1
11
11
  ```
12
12
 
13
13
  ## Installation
@@ -38,7 +38,8 @@ forward-proxy
38
38
  Usage: forward-proxy [options]
39
39
  -p, --port=PORT Bind to specified port. Default: 9292
40
40
  -b, --binding=BINDING Bind to the specified ip. Default: 127.0.0.1
41
- -t, --threads=THREADS Specify the number of client threads. Default: 32
41
+ -t, --timeout=TIMEOUT Specify the connection timeout in seconds. Default: 300
42
+ -c, --threads=THREADS Specify the number of client threads. Default: 128
42
43
  -h, --help Prints this help.
43
44
  ```
44
45
 
data/exe/forward-proxy CHANGED
@@ -15,7 +15,13 @@ OptionParser.new do |parser|
15
15
  options[:bind_address] = bind_address
16
16
  end
17
17
 
18
- parser.on("-tTHREADS", "--threads=THREADS", Integer, "Specify the number of client threads. Default: 32") do |threads|
18
+ parser.on("-tTIMEOUT", "--timeout=TIMEOUT", Integer,
19
+ "Specify the connection timeout in seconds. Default: 300") do |threads|
20
+ options[:timeout] = threads
21
+ end
22
+
23
+ parser.on("-cTHREADS", "--threads=THREADS", Integer,
24
+ "Specify the number of client threads. Default: 128") do |threads|
19
25
  options[:threads] = threads
20
26
  end
21
27
 
@@ -0,0 +1,5 @@
1
+ module ForwardProxy
2
+ module Errors
3
+ class ConnectionTimeoutError < StandardError; end
4
+ end
5
+ end
@@ -1,20 +1,23 @@
1
1
  require 'logger'
2
2
  require 'socket'
3
- require 'webrick'
3
+ require 'timeout'
4
4
  require 'net/http'
5
+ require 'webrick'
6
+ require 'forward_proxy/errors/connection_timeout_error'
5
7
  require 'forward_proxy/errors/http_method_not_implemented'
6
8
  require 'forward_proxy/errors/http_parse_error'
7
9
  require 'forward_proxy/thread_pool'
8
10
 
9
11
  module ForwardProxy
10
12
  class Server
11
- attr_reader :bind_address, :bind_port, :logger
13
+ attr_reader :bind_address, :bind_port, :logger, :timeout
12
14
 
13
- def initialize(bind_address: "127.0.0.1", bind_port: 9292, threads: 32, logger: Logger.new(STDOUT, level: :info))
14
- @logger = logger
15
- @thread_pool = ThreadPool.new(threads)
15
+ def initialize(bind_address: "127.0.0.1", bind_port: 9292, threads: 4, timeout: 300, logger: default_logger)
16
16
  @bind_address = bind_address
17
17
  @bind_port = bind_port
18
+ @logger = logger
19
+ @thread_pool = ThreadPool.new(threads)
20
+ @timeout = timeout
18
21
  end
19
22
 
20
23
  def start
@@ -27,18 +30,20 @@ module ForwardProxy
27
30
  loop do
28
31
  thread_pool.schedule(socket.accept) do |client_conn|
29
32
  begin
30
- req = parse_req(client_conn)
33
+ Timeout::timeout(timeout, Errors::ConnectionTimeoutError, "connection exceeded #{timeout} seconds") do
34
+ req = parse_req(client_conn)
31
35
 
32
- logger.info(req.request_line)
36
+ logger.info(req.request_line.strip)
33
37
 
34
- case req.request_method
35
- when METHOD_CONNECT then handle_tunnel(client_conn, req)
36
- when METHOD_GET, METHOD_POST then handle(client_conn, req)
37
- else
38
- raise Errors::HTTPMethodNotImplemented
38
+ case req.request_method
39
+ when METHOD_CONNECT then handle_tunnel(client_conn, req)
40
+ when METHOD_GET, METHOD_POST then handle(client_conn, req)
41
+ else
42
+ raise Errors::HTTPMethodNotImplemented
43
+ end
39
44
  end
40
- rescue => err
41
- handle_error(err, client_conn)
45
+ rescue => e
46
+ handle_error(client_conn, e)
42
47
  ensure
43
48
  client_conn.close
44
49
  end
@@ -130,20 +135,20 @@ module ForwardProxy
130
135
  # "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content"
131
136
  # https://tools.ietf.org/html/rfc7231#section-4.3.6
132
137
 
133
- # An intermediary MAY combine an ordered subsequence of Via header
134
- # field entries into a single such entry if the entries have identical
135
- # received-protocol values. For example,
138
+ # An intermediary MAY combine an ordered subsequence of Via header
139
+ # field entries into a single such entry if the entries have identical
140
+ # received-protocol values. For example,
136
141
  #
137
- # Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy
142
+ # Via: 1.0 ricky, 1.1 ethel, 1.1 fred, 1.0 lucy
138
143
  #
139
- # could be collapsed to
144
+ # could be collapsed to
140
145
  #
141
- # Via: 1.0 ricky, 1.1 mertz, 1.0 lucy
146
+ # Via: 1.0 ricky, 1.1 mertz, 1.0 lucy
142
147
  #
143
- # A sender SHOULD NOT combine multiple entries unless they are all
144
- # under the same organizational control and the hosts have already been
145
- # replaced by pseudonyms. A sender MUST NOT combine entries that have
146
- # different received-protocol values.
148
+ # A sender SHOULD NOT combine multiple entries unless they are all
149
+ # under the same organizational control and the hosts have already been
150
+ # replaced by pseudonyms. A sender MUST NOT combine entries that have
151
+ # different received-protocol values.
147
152
  headers = resp.to_hash.merge(Via: [HEADER_VIA, resp['Via']].compact.join(', '))
148
153
 
149
154
  client_conn.puts <<~eos.chomp
@@ -165,18 +170,27 @@ module ForwardProxy
165
170
  end
166
171
  end
167
172
 
168
- def handle_error(err, client_conn)
173
+ def handle_error(client_conn, err)
174
+ status_code = case err
175
+ when Errors::ConnectionTimeoutError then 504
176
+ else
177
+ 502
178
+ end
179
+
169
180
  client_conn.puts <<~eos.chomp
170
- HTTP/1.1 502
181
+ HTTP/1.1 #{status_code}
171
182
  Via: #{HEADER_VIA}
172
183
  #{HEADER_EOP}
173
184
  eos
174
185
 
175
186
  logger.error(err.message)
176
-
177
187
  logger.debug(err.backtrace.join("\n"))
178
188
  end
179
189
 
190
+ def default_logger
191
+ Logger.new(STDOUT, level: :info)
192
+ end
193
+
180
194
  def map_webrick_to_net_http_req(req)
181
195
  req_headers = Hash[req.header.map { |k, v| [k, v.first] }]
182
196
 
@@ -1,21 +1,18 @@
1
1
  module ForwardProxy
2
2
  class ThreadPool
3
- attr_reader :queue, :threads, :size
3
+ attr_reader :queue, :size
4
4
 
5
5
  def initialize(size)
6
- @size = size
7
- @queue = Queue.new
8
- @threads = []
6
+ @size = size
7
+ @queue = Queue.new
9
8
  end
10
9
 
11
10
  def start
12
11
  size.times do
13
- threads << Thread.new do
14
- catch(:exit) do
15
- loop do
16
- job, args = queue.pop
17
- job.call(*args)
18
- end
12
+ Thread.new do
13
+ loop do
14
+ job, args = queue.pop
15
+ job.call(*args)
19
16
  end
20
17
  end
21
18
  end
@@ -24,13 +21,5 @@ module ForwardProxy
24
21
  def schedule(*args, &block)
25
22
  queue.push([block, args])
26
23
  end
27
-
28
- def shutdown
29
- threads.each do
30
- schedule { throw :exit }
31
- end
32
-
33
- threads.each(&:join)
34
- end
35
24
  end
36
25
  end
@@ -1,3 +1,3 @@
1
1
  module ForwardProxy
2
- VERSION = "0.4.0"
2
+ VERSION = "0.6.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forward-proxy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Moriarty
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-04 00:00:00.000000000 Z
11
+ date: 2021-07-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Forward proxy using just Ruby standard libraries.
14
14
  email:
@@ -32,6 +32,7 @@ files:
32
32
  - exe/forward-proxy
33
33
  - forward-proxy.gemspec
34
34
  - lib/forward_proxy.rb
35
+ - lib/forward_proxy/errors/connection_timeout_error.rb
35
36
  - lib/forward_proxy/errors/http_method_not_implemented.rb
36
37
  - lib/forward_proxy/errors/http_parse_error.rb
37
38
  - lib/forward_proxy/server.rb