Skip to content

Commit 7c154c1

Browse files
authored
Merge pull request #742 from jjiang-stripe/jjiang/ssl-proxy-headers
Support additional headers for TLS proxies
2 parents e29b313 + b415556 commit 7c154c1

4 files changed

Lines changed: 41 additions & 26 deletions

File tree

lib/excon/connection.rb

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def request_call(datum)
115115
# we already have data from a middleware, so bail
116116
return datum
117117
else
118-
socket.data = datum
118+
socket(datum)
119119
# start with "METHOD /path"
120120
request = datum[:method].to_s.upcase + ' '
121121
if datum[:proxy] && datum[:scheme] != HTTPS
@@ -144,35 +144,25 @@ def request_call(datum)
144144
end
145145

146146
# add headers to request
147-
datum[:headers].each do |key, values|
148-
if key.to_s.match(/[\r\n]/)
149-
raise Excon::Errors::InvalidHeaderKey.new(key.to_s.inspect + ' contains forbidden "\r" or "\n"')
150-
end
151-
[values].flatten.each do |value|
152-
if value.to_s.match(/[\r\n]/)
153-
raise Excon::Errors::InvalidHeaderValue.new(value.to_s.inspect + ' contains forbidden "\r" or "\n"')
154-
end
155-
request << key.to_s << ': ' << value.to_s << CR_NL
156-
end
157-
end
147+
request << Utils.headers_hash_to_s(datum[:headers])
158148

159149
# add additional "\r\n" to indicate end of headers
160150
request << CR_NL
161151

162152
if datum.has_key?(:request_block)
163-
socket.write(request) # write out request + headers
153+
socket(datum).write(request) # write out request + headers
164154
while true # write out body with chunked encoding
165155
chunk = datum[:request_block].call
166156
chunk = binary_encode(chunk)
167157
if chunk.length > 0
168-
socket.write(chunk.length.to_s(16) << CR_NL << chunk << CR_NL)
158+
socket(datum).write(chunk.length.to_s(16) << CR_NL << chunk << CR_NL)
169159
else
170-
socket.write(String.new("0#{CR_NL}#{CR_NL}"))
160+
socket(datum).write(String.new("0#{CR_NL}#{CR_NL}"))
171161
break
172162
end
173163
end
174164
elsif body.nil?
175-
socket.write(request) # write out request + headers
165+
socket(datum).write(request) # write out request + headers
176166
else # write out body
177167
if body.respond_to?(:binmode) && !body.is_a?(StringIO)
178168
body.binmode
@@ -186,13 +176,13 @@ def request_call(datum)
186176
chunk = body.read([datum[:chunk_size] - request.length, 0].max)
187177
if chunk
188178
chunk = binary_encode(chunk)
189-
socket.write(request << chunk)
179+
socket(datum).write(request << chunk)
190180
else
191-
socket.write(request) # write out request + headers
181+
socket(datum).write(request) # write out request + headers
192182
end
193183

194184
while (chunk = body.read(datum[:chunk_size]))
195-
socket.write(chunk)
185+
socket(datum).write(chunk)
196186
end
197187
end
198188
end
@@ -463,14 +453,14 @@ def response(datum={})
463453
end
464454
end
465455

466-
def socket
467-
unix_proxy = @data[:proxy] ? @data[:proxy][:scheme] == UNIX : false
468-
sockets[@socket_key] ||= if @data[:scheme] == UNIX || unix_proxy
469-
Excon::UnixSocket.new(@data)
470-
elsif @data[:ssl_uri_schemes].include?(@data[:scheme])
471-
Excon::SSLSocket.new(@data)
456+
def socket(datum = @data)
457+
unix_proxy = datum[:proxy] ? datum[:proxy][:scheme] == UNIX : false
458+
sockets[@socket_key] ||= if datum[:scheme] == UNIX || unix_proxy
459+
Excon::UnixSocket.new(datum)
460+
elsif datum[:ssl_uri_schemes].include?(datum[:scheme])
461+
Excon::SSLSocket.new(datum)
472462
else
473-
Excon::Socket.new(@data)
463+
Excon::Socket.new(datum)
474464
end
475465
end
476466

@@ -573,6 +563,9 @@ def setup_proxy
573563
if uri.user
574564
@data[:proxy][:user] = uri.user
575565
end
566+
if @data[:ssl_proxy_headers] && !@data[:ssl_uri_schemes].include?(@data[:scheme])
567+
raise ArgumentError, "The `:ssl_proxy_headers` parameter should only be used with HTTPS requests."
568+
end
576569
if @data[:proxy][:scheme] == UNIX
577570
if @data[:proxy][:host]
578571
raise ArgumentError, "The `:host` parameter should not be set for `unix://` proxies.\n" +

lib/excon/constants.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ module Excon
9999
:ssl_version,
100100
:ssl_min_version,
101101
:ssl_max_version,
102+
:ssl_proxy_headers,
102103
:ssl_uri_schemes,
103104
:tcp_nodelay,
104105
:thread_safe_sockets,

lib/excon/ssl_socket.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ def initialize(data = {})
104104

105105
request += "Proxy-Connection: Keep-Alive#{Excon::CR_NL}"
106106

107+
if @data[:ssl_proxy_headers]
108+
request << Utils.headers_hash_to_s(@data[:ssl_proxy_headers])
109+
end
110+
107111
request += Excon::CR_NL
108112

109113
# write out the proxy setup request

lib/excon/utils.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,22 @@ def unescape_form(str)
121121
str.gsub!(/\+/, ' ')
122122
str.gsub(ESCAPED) { $1.hex.chr }
123123
end
124+
125+
# Performs validation on the passed header hash and returns a string representation of the headers
126+
def headers_hash_to_s(headers)
127+
headers_str = String.new
128+
headers.each do |key, values|
129+
if key.to_s.match(/[\r\n]/)
130+
raise Excon::Errors::InvalidHeaderKey.new(key.to_s.inspect + ' contains forbidden "\r" or "\n"')
131+
end
132+
[values].flatten.each do |value|
133+
if value.to_s.match(/[\r\n]/)
134+
raise Excon::Errors::InvalidHeaderValue.new(value.to_s.inspect + ' contains forbidden "\r" or "\n"')
135+
end
136+
headers_str << key.to_s << ': ' << value.to_s << CR_NL
137+
end
138+
end
139+
headers_str
140+
end
124141
end
125142
end

0 commit comments

Comments
 (0)