framed_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +11 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG +1 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +1 -0
  6. data/README.md +107 -0
  7. data/framed_rails.gemspec +37 -0
  8. data/lib/framed/client.rb +34 -0
  9. data/lib/framed/emitters.rb +113 -0
  10. data/lib/framed/example.rb +17 -0
  11. data/lib/framed/exceptions.rb +13 -0
  12. data/lib/framed/okjson.rb +602 -0
  13. data/lib/framed/rails.rb +43 -0
  14. data/lib/framed/railtie.rb +9 -0
  15. data/lib/framed/utils.rb +54 -0
  16. data/lib/framed/version.rb +4 -0
  17. data/lib/framed_rails.rb +71 -0
  18. data/vendor/gems/excon-0.45.3/data/cacert.pem +3860 -0
  19. data/vendor/gems/excon-0.45.3/lib/excon/connection.rb +469 -0
  20. data/vendor/gems/excon-0.45.3/lib/excon/constants.rb +142 -0
  21. data/vendor/gems/excon-0.45.3/lib/excon/errors.rb +155 -0
  22. data/vendor/gems/excon-0.45.3/lib/excon/extensions/uri.rb +33 -0
  23. data/vendor/gems/excon-0.45.3/lib/excon/headers.rb +83 -0
  24. data/vendor/gems/excon-0.45.3/lib/excon/middlewares/base.rb +24 -0
  25. data/vendor/gems/excon-0.45.3/lib/excon/middlewares/decompress.rb +35 -0
  26. data/vendor/gems/excon-0.45.3/lib/excon/middlewares/escape_path.rb +11 -0
  27. data/vendor/gems/excon-0.45.3/lib/excon/middlewares/expects.rb +18 -0
  28. data/vendor/gems/excon-0.45.3/lib/excon/middlewares/idempotent.rb +33 -0
  29. data/vendor/gems/excon-0.45.3/lib/excon/middlewares/instrumentor.rb +34 -0
  30. data/vendor/gems/excon-0.45.3/lib/excon/middlewares/mock.rb +51 -0
  31. data/vendor/gems/excon-0.45.3/lib/excon/middlewares/redirect_follower.rb +56 -0
  32. data/vendor/gems/excon-0.45.3/lib/excon/middlewares/response_parser.rb +12 -0
  33. data/vendor/gems/excon-0.45.3/lib/excon/pretty_printer.rb +45 -0
  34. data/vendor/gems/excon-0.45.3/lib/excon/response.rb +212 -0
  35. data/vendor/gems/excon-0.45.3/lib/excon/socket.rb +310 -0
  36. data/vendor/gems/excon-0.45.3/lib/excon/ssl_socket.rb +151 -0
  37. data/vendor/gems/excon-0.45.3/lib/excon/standard_instrumentor.rb +27 -0
  38. data/vendor/gems/excon-0.45.3/lib/excon/unix_socket.rb +40 -0
  39. data/vendor/gems/excon-0.45.3/lib/excon/utils.rb +87 -0
  40. data/vendor/gems/excon-0.45.3/lib/excon.rb +234 -0
  41. metadata +91 -0
@@ -0,0 +1,212 @@
1
+ module Excon
2
+ class Response
3
+
4
+ attr_accessor :data
5
+
6
+ # backwards compatability reader/writers
7
+ def body=(new_body)
8
+ @data[:body] = new_body
9
+ end
10
+ def body
11
+ @data[:body]
12
+ end
13
+ def headers=(new_headers)
14
+ @data[:headers] = new_headers
15
+ end
16
+ def headers
17
+ @data[:headers]
18
+ end
19
+ def status=(new_status)
20
+ @data[:status] = new_status
21
+ end
22
+ def status
23
+ @data[:status]
24
+ end
25
+ def status_line
26
+ @data[:status_line]
27
+ end
28
+ def status_line=(new_status_line)
29
+ @data[:status_line] = new_status_line
30
+ end
31
+ def reason_phrase=(new_reason_phrase)
32
+ @data[:reason_phrase] = new_reason_phrase
33
+ end
34
+ def reason_phrase
35
+ @data[:reason_phrase]
36
+ end
37
+ def remote_ip=(new_remote_ip)
38
+ @data[:remote_ip] = new_remote_ip
39
+ end
40
+ def remote_ip
41
+ @data[:remote_ip]
42
+ end
43
+ def local_port
44
+ @data[:local_port]
45
+ end
46
+ def local_address
47
+ @data[:local_address]
48
+ end
49
+
50
+ def self.parse(socket, datum)
51
+ # this will discard any trailing lines from the previous response if any.
52
+ begin
53
+ line = socket.readline
54
+ end until status = line[9, 3].to_i
55
+
56
+ reason_phrase = line[13..-3] # -3 strips the trailing "\r\n"
57
+
58
+ datum[:response] = {
59
+ :body => '',
60
+ :headers => Excon::Headers.new,
61
+ :status => status,
62
+ :status_line => line,
63
+ :reason_phrase => reason_phrase
64
+ }
65
+
66
+ unix_proxy = datum[:proxy] ? datum[:proxy][:scheme] == UNIX : false
67
+ unless datum[:scheme] == UNIX || unix_proxy
68
+ datum[:response].merge!(
69
+ :remote_ip => socket.remote_ip,
70
+ :local_port => socket.local_port,
71
+ :local_address => socket.local_address
72
+ )
73
+ end
74
+
75
+ parse_headers(socket, datum)
76
+
77
+ unless (['HEAD', 'CONNECT'].include?(datum[:method].to_s.upcase)) || NO_ENTITY.include?(datum[:response][:status])
78
+
79
+ if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Transfer-Encoding') == 0 }
80
+ encodings = Utils.split_header_value(datum[:response][:headers][key])
81
+ if (encoding = encodings.last) && encoding.casecmp('chunked') == 0
82
+ transfer_encoding_chunked = true
83
+ if encodings.length == 1
84
+ datum[:response][:headers].delete(key)
85
+ else
86
+ datum[:response][:headers][key] = encodings[0...-1].join(', ')
87
+ end
88
+ end
89
+ end
90
+
91
+ # use :response_block unless :expects would fail
92
+ if response_block = datum[:response_block]
93
+ if datum[:middlewares].include?(Excon::Middleware::Expects) && datum[:expects] &&
94
+ !Array(datum[:expects]).include?(datum[:response][:status])
95
+ response_block = nil
96
+ end
97
+ end
98
+
99
+ if transfer_encoding_chunked
100
+ if response_block
101
+ while (chunk_size = socket.readline.chomp!.to_i(16)) > 0
102
+ while chunk_size > 0
103
+ chunk = socket.read(chunk_size)
104
+ chunk_size -= chunk.bytesize
105
+ response_block.call(chunk, nil, nil)
106
+ end
107
+ new_line_size = 2 # 2 == "\r\n".length
108
+ while new_line_size > 0
109
+ new_line_size -= socket.read(new_line_size).length
110
+ end
111
+ end
112
+ else
113
+ while (chunk_size = socket.readline.chomp!.to_i(16)) > 0
114
+ while chunk_size > 0
115
+ chunk = socket.read(chunk_size)
116
+ chunk_size -= chunk.bytesize
117
+ datum[:response][:body] << chunk
118
+ end
119
+ new_line_size = 2 # 2 == "\r\n".length
120
+ while new_line_size > 0
121
+ new_line_size -= socket.read(new_line_size).length
122
+ end
123
+ end
124
+ end
125
+ parse_headers(socket, datum) # merge trailers into headers
126
+ else
127
+ if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Length') == 0 }
128
+ content_length = datum[:response][:headers][key].to_i
129
+ end
130
+
131
+ if remaining = content_length
132
+ if response_block
133
+ while remaining > 0
134
+ chunk = socket.read([datum[:chunk_size], remaining].min)
135
+ response_block.call(chunk, [remaining - chunk.bytesize, 0].max, content_length)
136
+ remaining -= chunk.bytesize
137
+ end
138
+ else
139
+ while remaining > 0
140
+ chunk = socket.read([datum[:chunk_size], remaining].min)
141
+ datum[:response][:body] << chunk
142
+ remaining -= chunk.bytesize
143
+ end
144
+ end
145
+ else
146
+ if response_block
147
+ while chunk = socket.read(datum[:chunk_size])
148
+ response_block.call(chunk, nil, nil)
149
+ end
150
+ else
151
+ while chunk = socket.read(datum[:chunk_size])
152
+ datum[:response][:body] << chunk
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ datum
159
+ end
160
+
161
+ def self.parse_headers(socket, datum)
162
+ last_key = nil
163
+ until (data = socket.readline.chomp!).empty?
164
+ if !data.lstrip!.nil?
165
+ raise Excon::Errors::ResponseParseError, 'malformed header' unless last_key
166
+ # append to last_key's last value
167
+ datum[:response][:headers][last_key] << ' ' << data.rstrip
168
+ else
169
+ key, value = data.split(':', 2)
170
+ raise Excon::Errors::ResponseParseError, 'malformed header' unless value
171
+ # add key/value or append value to existing values
172
+ datum[:response][:headers][key] = ([datum[:response][:headers][key]] << value.strip).compact.join(', ')
173
+ last_key = key
174
+ end
175
+ end
176
+ end
177
+
178
+ def initialize(params={})
179
+ @data = {
180
+ :body => ''
181
+ }.merge(params)
182
+ @data[:headers] = Excon::Headers.new.merge!(params[:headers] || {})
183
+
184
+ @body = @data[:body]
185
+ @headers = @data[:headers]
186
+ @status = @data[:status]
187
+ @remote_ip = @data[:remote_ip]
188
+ @local_port = @data[:local_port]
189
+ @local_address = @data[:local_address]
190
+ end
191
+
192
+ def [](key)
193
+ @data[key]
194
+ end
195
+
196
+ def params
197
+ Excon.display_warning('Excon::Response#params is deprecated use Excon::Response#data instead.')
198
+ data
199
+ end
200
+
201
+ def pp
202
+ Excon::PrettyPrinter.pp($stdout, @data)
203
+ end
204
+
205
+ # Retrieve a specific header value. Header names are treated case-insensitively.
206
+ # @param [String] name Header name
207
+ def get_header(name)
208
+ headers[name]
209
+ end
210
+
211
+ end # class Response
212
+ end # module Excon
@@ -0,0 +1,310 @@
1
+ module Excon
2
+ class Socket
3
+ include Utils
4
+
5
+ extend Forwardable
6
+
7
+ attr_accessor :data
8
+
9
+ def params
10
+ Excon.display_warning('Excon::Socket#params is deprecated use Excon::Socket#data instead.')
11
+ @data
12
+ end
13
+
14
+ def params=(new_params)
15
+ Excon.display_warning('Excon::Socket#params= is deprecated use Excon::Socket#data= instead.')
16
+ @data = new_params
17
+ end
18
+
19
+ attr_reader :remote_ip
20
+
21
+ def_delegators(:@socket, :close)
22
+
23
+ def initialize(data = {})
24
+ @data = data
25
+ @nonblock = data[:nonblock]
26
+ @read_buffer = ''
27
+ @eof = false
28
+ connect
29
+ end
30
+
31
+ def read(max_length = nil)
32
+ if @eof
33
+ return max_length ? nil : ''
34
+ elsif @nonblock
35
+ read_nonblock(max_length)
36
+ else
37
+ read_block(max_length)
38
+ end
39
+ end
40
+
41
+ def readline
42
+ return legacy_readline if RUBY_VERSION.to_f <= 1.8_7
43
+ buffer = ''
44
+ begin
45
+ buffer << @socket.read_nonblock(1) while buffer[-1] != "\n"
46
+ buffer
47
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
48
+ if timeout_reached('read')
49
+ raise_timeout_error('read')
50
+ else
51
+ retry
52
+ end
53
+ rescue OpenSSL::SSL::SSLError => e
54
+ if e.message == 'read would block'
55
+ if timeout_reached('read')
56
+ raise_timeout_error('read')
57
+ else
58
+ retry
59
+ end
60
+ else
61
+ raise(error)
62
+ end
63
+ end
64
+ end
65
+
66
+ def legacy_readline
67
+ begin
68
+ Timeout.timeout(@data[:read_timeout]) do
69
+ @socket.readline
70
+ end
71
+ rescue Timeout::Error
72
+ raise Excon::Errors::Timeout.new('read timeout reached')
73
+ end
74
+ end
75
+
76
+ def write(data)
77
+ if @nonblock
78
+ write_nonblock(data)
79
+ else
80
+ write_block(data)
81
+ end
82
+ end
83
+
84
+ def local_address
85
+ unpacked_sockaddr[1]
86
+ end
87
+
88
+ def local_port
89
+ unpacked_sockaddr[0]
90
+ end
91
+
92
+ private
93
+
94
+ def connect
95
+ @socket = nil
96
+ exception = nil
97
+
98
+ if @data[:proxy]
99
+ family = @data[:proxy][:family] || ::Socket::Constants::AF_UNSPEC
100
+ args = [@data[:proxy][:hostname], @data[:proxy][:port], family, ::Socket::Constants::SOCK_STREAM]
101
+ else
102
+ family = @data[:family] || ::Socket::Constants::AF_UNSPEC
103
+ args = [@data[:hostname], @data[:port], family, ::Socket::Constants::SOCK_STREAM]
104
+ end
105
+ if RUBY_VERSION >= '1.9.2' && defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby'
106
+ args << nil << nil << false # no reverse lookup
107
+ end
108
+ addrinfo = ::Socket.getaddrinfo(*args)
109
+
110
+ addrinfo.each do |_, port, _, ip, a_family, s_type|
111
+ @remote_ip = ip
112
+
113
+ # already succeeded on previous addrinfo
114
+ if @socket
115
+ break
116
+ end
117
+
118
+ # nonblocking connect
119
+ begin
120
+ sockaddr = ::Socket.sockaddr_in(port, ip)
121
+
122
+ socket = ::Socket.new(a_family, s_type, 0)
123
+
124
+ if @data[:reuseaddr]
125
+ socket.setsockopt(::Socket::Constants::SOL_SOCKET, ::Socket::Constants::SO_REUSEADDR, true)
126
+ if defined?(::Socket::Constants::SO_REUSEPORT)
127
+ socket.setsockopt(::Socket::Constants::SOL_SOCKET, ::Socket::Constants::SO_REUSEPORT, true)
128
+ end
129
+ end
130
+
131
+ if @nonblock
132
+ socket.connect_nonblock(sockaddr)
133
+ else
134
+ socket.connect(sockaddr)
135
+ end
136
+ @socket = socket
137
+ rescue Errno::EINPROGRESS
138
+ unless IO.select(nil, [socket], nil, @data[:connect_timeout])
139
+ raise(Excon::Errors::Timeout.new('connect timeout reached'))
140
+ end
141
+ begin
142
+ socket.connect_nonblock(sockaddr)
143
+ @socket = socket
144
+ rescue Errno::EISCONN
145
+ @socket = socket
146
+ rescue SystemCallError => exception
147
+ socket.close rescue nil
148
+ end
149
+ rescue SystemCallError => exception
150
+ socket.close rescue nil if socket
151
+ end
152
+ end
153
+
154
+ # this will be our last encountered exception
155
+ fail exception unless @socket
156
+
157
+ if @data[:tcp_nodelay]
158
+ @socket.setsockopt(::Socket::IPPROTO_TCP,
159
+ ::Socket::TCP_NODELAY,
160
+ true)
161
+ end
162
+ end
163
+
164
+ def read_nonblock(max_length)
165
+ begin
166
+ if max_length
167
+ until @read_buffer.length >= max_length
168
+ @read_buffer << @socket.read_nonblock(max_length - @read_buffer.length)
169
+ end
170
+ else
171
+ loop do
172
+ @read_buffer << @socket.read_nonblock(@data[:chunk_size])
173
+ end
174
+ end
175
+ rescue OpenSSL::SSL::SSLError => error
176
+ if error.message == 'read would block'
177
+ if timeout_reached('read')
178
+ raise_timeout_error('read')
179
+ else
180
+ retry
181
+ end
182
+ else
183
+ raise(error)
184
+ end
185
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
186
+ if @read_buffer.empty?
187
+ # if we didn't read anything, try again...
188
+ if timeout_reached('read')
189
+ raise_timeout_error('read')
190
+ else
191
+ retry
192
+ end
193
+ end
194
+ rescue EOFError
195
+ @eof = true
196
+ end
197
+
198
+ if max_length
199
+ if @read_buffer.empty?
200
+ nil # EOF met at beginning
201
+ else
202
+ @read_buffer.slice!(0, max_length)
203
+ end
204
+ else
205
+ # read until EOFError, so return everything
206
+ @read_buffer.slice!(0, @read_buffer.length)
207
+ end
208
+ end
209
+
210
+ def read_block(max_length)
211
+ @socket.read(max_length)
212
+ rescue OpenSSL::SSL::SSLError => error
213
+ if error.message == 'read would block'
214
+ if timeout_reached('read')
215
+ raise_timeout_error('read')
216
+ else
217
+ retry
218
+ end
219
+ else
220
+ raise(error)
221
+ end
222
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
223
+ if @read_buffer.empty?
224
+ if timeout_reached('read')
225
+ raise_timeout_error('read')
226
+ else
227
+ retry
228
+ end
229
+ end
230
+ rescue EOFError
231
+ @eof = true
232
+ end
233
+
234
+ def write_nonblock(data)
235
+ if FORCE_ENC
236
+ data.force_encoding('BINARY')
237
+ end
238
+ loop do
239
+ written = nil
240
+ begin
241
+ # I wish that this API accepted a start position, then we wouldn't
242
+ # have to slice data when there is a short write.
243
+ written = @socket.write_nonblock(data)
244
+ rescue Errno::EFAULT
245
+ if OpenSSL.const_defined?(:OPENSSL_LIBRARY_VERSION) && OpenSSL::OPENSSL_LIBRARY_VERSION.split(' ')[1] == '1.0.2'
246
+ msg = "The version of OpenSSL this ruby is built against (1.0.2) has a vulnerability
247
+ which causes a fault. For more, see https://github.com/excon/excon/issues/467"
248
+ raise SecurityError.new(msg)
249
+ else
250
+ raise error
251
+ end
252
+ rescue OpenSSL::SSL::SSLError, Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable => error
253
+ if error.is_a?(OpenSSL::SSL::SSLError) && error.message != 'write would block'
254
+ raise error
255
+ else
256
+ if timeout_reached('write')
257
+ raise_timeout_error('write')
258
+ else
259
+ retry
260
+ end
261
+ end
262
+ end
263
+
264
+ # Fast, common case.
265
+ break if written == data.size
266
+
267
+ # This takes advantage of the fact that most ruby implementations
268
+ # have Copy-On-Write strings. Thusly why requesting a subrange
269
+ # of data, we actually don't copy data because the new string
270
+ # simply references a subrange of the original.
271
+ data = data[written, data.size]
272
+ end
273
+ end
274
+
275
+ def write_block(data)
276
+ @socket.write(data)
277
+ rescue OpenSSL::SSL::SSLError, Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitWritable => error
278
+ if error.is_a?(OpenSSL::SSL::SSLError) && error.message != 'write would block'
279
+ raise error
280
+ else
281
+ if timeout_reached('write')
282
+ raise_timeout_error('write')
283
+ else
284
+ retry
285
+ end
286
+ end
287
+ end
288
+
289
+ def timeout_reached(type)
290
+ if type == 'read'
291
+ args = [[@socket], nil, nil, @data[:read_timeout]]
292
+ else
293
+ args = [nil, [@socket], nil, @data[:write_timeout]]
294
+ end
295
+ IO.select(*args) ? nil : true
296
+ end
297
+
298
+ def raise_timeout_error(type)
299
+ fail Excon::Errors::Timeout.new("#{type} timeout reached")
300
+ end
301
+
302
+ def unpacked_sockaddr
303
+ @unpacked_sockaddr ||= ::Socket.unpack_sockaddr_in(@socket.to_io.getsockname)
304
+ rescue ArgumentError => e
305
+ unless e.message == 'not an AF_INET/AF_INET6 sockaddr'
306
+ raise
307
+ end
308
+ end
309
+ end
310
+ end
@@ -0,0 +1,151 @@
1
+ module Excon
2
+ class SSLSocket < Socket
3
+ HAVE_NONBLOCK = [:connect_nonblock, :read_nonblock, :write_nonblock].all? do |m|
4
+ OpenSSL::SSL::SSLSocket.public_method_defined?(m)
5
+ end
6
+
7
+ def initialize(data = {})
8
+ super
9
+
10
+ # create ssl context
11
+ ssl_context = OpenSSL::SSL::SSLContext.new
12
+
13
+ # disable less secure options, when supported
14
+ ssl_context_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
15
+ if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
16
+ ssl_context_options &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS
17
+ end
18
+
19
+ if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
20
+ ssl_context_options |= OpenSSL::SSL::OP_NO_COMPRESSION
21
+ end
22
+ ssl_context.options = ssl_context_options
23
+
24
+ ssl_context.ciphers = @data[:ciphers]
25
+ if @data[:ssl_version]
26
+ ssl_context.ssl_version = @data[:ssl_version]
27
+ end
28
+
29
+ if @data[:ssl_verify_peer]
30
+ # turn verification on
31
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
32
+
33
+ if ca_file = @data[:ssl_ca_file] || ENV['SSL_CERT_FILE']
34
+ ssl_context.ca_file = ca_file
35
+ end
36
+ if ca_path = @data[:ssl_ca_path] || ENV['SSL_CERT_DIR']
37
+ ssl_context.ca_path = ca_path
38
+ end
39
+ if cert_store = @data[:ssl_cert_store]
40
+ ssl_context.cert_store = cert_store
41
+ end
42
+
43
+ # no defaults, fallback to bundled
44
+ unless ca_file || ca_path || cert_store
45
+ ssl_context.cert_store = OpenSSL::X509::Store.new
46
+ ssl_context.cert_store.set_default_paths
47
+
48
+ # workaround issue #257 (JRUBY-6970)
49
+ ca_file = DEFAULT_CA_FILE
50
+ ca_file.gsub!(/^jar:/, '') if ca_file =~ /^jar:file:\//
51
+
52
+ begin
53
+ ssl_context.cert_store.add_file(ca_file)
54
+ rescue
55
+ Excon.display_warning("Excon unable to add file to cert store, ignoring: #{ca_file}\n[#{$!.class}] #{$!.message}")
56
+ end
57
+ end
58
+
59
+ if verify_callback = @data[:ssl_verify_callback]
60
+ ssl_context.verify_callback = verify_callback
61
+ end
62
+ else
63
+ # turn verification off
64
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
65
+ end
66
+
67
+ # maintain existing API
68
+ certificate_path = @data[:client_cert] || @data[:certificate_path]
69
+ private_key_path = @data[:client_key] || @data[:private_key_path]
70
+ private_key_pass = @data[:client_key_pass] || @data[:private_key_pass]
71
+
72
+ if certificate_path && private_key_path
73
+ ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(certificate_path))
74
+ if OpenSSL::PKey.respond_to? :read
75
+ ssl_context.key = OpenSSL::PKey.read(File.read(private_key_path), private_key_pass)
76
+ else
77
+ ssl_context.key = OpenSSL::PKey::RSA.new(File.read(private_key_path), private_key_pass)
78
+ end
79
+ elsif @data.key?(:certificate) && @data.key?(:private_key)
80
+ ssl_context.cert = OpenSSL::X509::Certificate.new(@data[:certificate])
81
+ if OpenSSL::PKey.respond_to? :read
82
+ ssl_context.key = OpenSSL::PKey.read(@data[:private_key], private_key_pass)
83
+ else
84
+ ssl_context.key = OpenSSL::PKey::RSA.new(@data[:private_key], private_key_pass)
85
+ end
86
+ end
87
+
88
+ if @data[:proxy]
89
+ request = 'CONNECT ' << @data[:host] << port_string(@data.merge(:omit_default_port => false)) << Excon::HTTP_1_1
90
+ request << 'Host: ' << @data[:host] << port_string(@data) << Excon::CR_NL
91
+
92
+ if @data[:proxy][:password] || @data[:proxy][:user]
93
+ auth = ['' << @data[:proxy][:user].to_s << ':' << @data[:proxy][:password].to_s].pack('m').delete(Excon::CR_NL)
94
+ request << 'Proxy-Authorization: Basic ' << auth << Excon::CR_NL
95
+ end
96
+
97
+ request << 'Proxy-Connection: Keep-Alive' << Excon::CR_NL
98
+
99
+ request << Excon::CR_NL
100
+
101
+ # write out the proxy setup request
102
+ @socket.write(request)
103
+
104
+ # eat the proxy's connection response
105
+ Excon::Response.parse(self, :expects => 200, :method => 'CONNECT')
106
+ end
107
+
108
+ # convert Socket to OpenSSL::SSL::SSLSocket
109
+ @socket = OpenSSL::SSL::SSLSocket.new(@socket, ssl_context)
110
+ @socket.sync_close = true
111
+
112
+ # Server Name Indication (SNI) RFC 3546
113
+ if @socket.respond_to?(:hostname=)
114
+ @socket.hostname = @data[:host]
115
+ end
116
+
117
+ begin
118
+ if @nonblock
119
+ begin
120
+ @socket.connect_nonblock
121
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
122
+ IO.select([@socket])
123
+ retry
124
+ rescue IO::WaitWritable
125
+ IO.select(nil, [@socket])
126
+ retry
127
+ end
128
+ else
129
+ @socket.connect
130
+ end
131
+ rescue Errno::ETIMEDOUT, Timeout::Error
132
+ raise Excon::Errors::Timeout.new('connect timeout reached')
133
+ end
134
+
135
+ # verify connection
136
+ if @data[:ssl_verify_peer]
137
+ @socket.post_connection_check(@data[:ssl_verify_peer_host] || @data[:host])
138
+ end
139
+
140
+ @socket
141
+ end
142
+
143
+ private
144
+
145
+ def connect
146
+ # backwards compatability for things lacking nonblock
147
+ @nonblock = HAVE_NONBLOCK && @nonblock
148
+ super
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,27 @@
1
+ module Excon
2
+ class StandardInstrumentor
3
+ def self.instrument(name, params = {}, &block)
4
+ params = params.dup
5
+
6
+ # reduce duplication/noise of output
7
+ params.delete(:connection)
8
+ params.delete(:stack)
9
+
10
+ if params.has_key?(:headers) && params[:headers].has_key?('Authorization')
11
+ params[:headers] = params[:headers].dup
12
+ params[:headers]['Authorization'] = REDACTED
13
+ end
14
+
15
+ if params.has_key?(:password)
16
+ params[:password] = REDACTED
17
+ end
18
+
19
+ $stderr.puts(name)
20
+ Excon::PrettyPrinter.pp($stderr, params)
21
+
22
+ if block_given?
23
+ yield
24
+ end
25
+ end
26
+ end
27
+ end