Skip to content

Commit cdea78c

Browse files
authored
Add support for NO_PROXY env variable (#355)
1 parent 82ededc commit cdea78c

File tree

3 files changed

+79
-10
lines changed

3 files changed

+79
-10
lines changed

lib/em-http/http_connection_options.rb

+15-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def initialize(uri, options)
2222
uri.port ||= (@https ? 443 : 80)
2323
@tls[:sni_hostname] = uri.hostname
2424

25-
@proxy = options[:proxy] || proxy_from_env
25+
@proxy = options[:proxy] || proxy_from_env(uri.hostname)
2626

2727
if proxy
2828
@host = proxy[:host]
@@ -45,8 +45,7 @@ def socks_proxy?
4545
@proxy && (@proxy[:type] == :socks5)
4646
end
4747

48-
def proxy_from_env
49-
# TODO: Add support for $http_no_proxy or $no_proxy ?
48+
def proxy_from_env(host)
5049
proxy_str = if @https
5150
ENV['HTTPS_PROXY'] || ENV['https_proxy']
5251
else
@@ -59,6 +58,9 @@ def proxy_from_env
5958
# so, let's short-circuit that:
6059
return if !proxy_str || proxy_str.empty?
6160

61+
no_proxy_hosts = (ENV['NO_PROXY'] || ENV['no_proxy'] || '').split(',')
62+
return if use_env_proxy_for_host?(no_proxy_hosts, host)
63+
6264
proxy_env_uri = Addressable::URI::parse(proxy_str)
6365
{ :host => proxy_env_uri.host, :port => proxy_env_uri.port, :type => :http }
6466

@@ -67,4 +69,14 @@ def proxy_from_env
6769
# We should somehow log / warn about this, perhaps...
6870
return
6971
end
72+
73+
private
74+
75+
def use_env_proxy_for_host?(no_proxy_hosts, host)
76+
return true if no_proxy_hosts.include?(host)
77+
78+
no_proxy_hosts
79+
.select { |h| h.start_with?('.') }
80+
.any? { |w| host.end_with?(w) }
81+
end
7082
end

spec/http_proxy_spec.rb

+63-6
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
end
1818

1919
shared_examples "*_PROXY var (testing var)" do
20-
subject { HttpConnectionOptions.new("#{proxy_test_scheme}://example.com", {}) }
21-
it { expect(subject.proxy_from_env).to eq({ :host => "127.0.0.1", :port => 8083, :type => :http }) }
20+
subject { HttpConnectionOptions.new("#{proxy_test_scheme}://#{request_host}", {}) }
21+
let(:request_host) { 'example.com' }
22+
23+
it { expect(subject.proxy_from_env(request_host)).to eq({ :host => "127.0.0.1", :port => 8083, :type => :http }) }
2224
it { expect(subject.host).to eq "127.0.0.1" }
2325
it { expect(subject.port).to be 8083 }
2426
it do
@@ -201,10 +203,10 @@
201203
before(:all) do
202204
PROXY_ENV_VARS.each {|k| ENV.delete k }
203205
end
204-
205-
subject { HttpConnectionOptions.new("http://example.com", {}) }
206-
it { expect(subject.proxy_from_env).to be_nil }
207-
it { expect(subject.host).to eq "example.com" }
206+
let(:request_host) { 'example.com' }
207+
subject { HttpConnectionOptions.new("http://#{request_host}", {}) }
208+
it { expect(subject.proxy_from_env(request_host)).to be_nil }
209+
it { expect(subject.host).to eq request_host }
208210
it { expect(subject.port).to be 80 }
209211
it { expect(subject.http_proxy?).to be_falsey }
210212
it { expect(subject.connect_proxy?).to be_falsey }
@@ -264,5 +266,60 @@
264266

265267
include_examples "*_PROXY var (testing var)"
266268
end
269+
270+
context 'with $NO_PROXY env' do
271+
let(:request_host) { 'ignore.me' }
272+
let(:no_proxy_hosts) { ['host1', request_host, 'host2'] }
273+
subject { HttpConnectionOptions.new("http://#{request_host}", {}) }
274+
275+
before(:each) do
276+
PROXY_ENV_VARS.each { |k| ENV.delete k }
277+
ENV['ALL_PROXY'] = 'http://127.0.0.1:8083'
278+
ENV[no_proxy_var_name] = no_proxy_hosts&.join(',')
279+
end
280+
281+
describe 'when $NO_PROXY includes host from current request' do
282+
let(:no_proxy_var_name) { 'NO_PROXY' }
283+
284+
it 'should not apply proxy from env' do
285+
expect(subject.proxy_from_env(request_host)).to be_nil
286+
end
287+
end
288+
289+
describe 'when $NO_PROXY does not include host from current request' do
290+
let(:no_proxy_var_name) { 'NO_PROXY' }
291+
let(:no_proxy_hosts) { %w[host1 host2] }
292+
293+
it 'should apply proxy from env' do
294+
expect(subject.proxy_from_env(request_host)).not_to be_nil
295+
end
296+
end
297+
298+
describe 'when $no_proxy includes host from current request' do
299+
let(:no_proxy_var_name) { 'no_proxy' }
300+
301+
it 'should not apply proxy from env' do
302+
expect(subject.proxy_from_env(request_host)).to be_nil
303+
end
304+
end
305+
306+
describe 'when $no_proxy does not include host from current request' do
307+
let(:no_proxy_var_name) { 'no_proxy' }
308+
let(:no_proxy_hosts) { %w[host1 host2] }
309+
310+
it 'should apply proxy from env' do
311+
expect(subject.proxy_from_env(request_host)).not_to be_nil
312+
end
313+
end
314+
315+
describe 'when .domain is set in $no_proxy' do
316+
let(:no_proxy_var_name) { 'no_proxy' }
317+
let(:no_proxy_hosts) { %w[host1 .me host2] }
318+
319+
it 'should not apply proxy from env' do
320+
expect(subject.proxy_from_env(request_host)).to be_nil
321+
end
322+
end
323+
end
267324
end
268325
end

spec/spec_helper.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
PROXY_ENV_VARS = %w[HTTP_PROXY http_proxy HTTPS_PROXY https_proxy ALL_PROXY]
1+
PROXY_ENV_VARS = %w[HTTP_PROXY http_proxy HTTPS_PROXY https_proxy ALL_PROXY NO_PROXY no_proxy].freeze
22

33
RSpec.configure do |config|
44
proxy_envs = {}

0 commit comments

Comments
 (0)