forward-proxy 0.4.0 → 0.6.2

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