http 5.0.0.pre2 → 5.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +65 -0
  3. data/.gitignore +6 -10
  4. data/.rspec +0 -4
  5. data/.rubocop/layout.yml +8 -0
  6. data/.rubocop/style.yml +32 -0
  7. data/.rubocop.yml +7 -124
  8. data/.rubocop_todo.yml +192 -0
  9. data/CHANGES.md +114 -1
  10. data/Gemfile +18 -11
  11. data/LICENSE.txt +1 -1
  12. data/README.md +13 -16
  13. data/Rakefile +2 -10
  14. data/http.gemspec +3 -3
  15. data/lib/http/chainable.rb +15 -14
  16. data/lib/http/client.rb +26 -15
  17. data/lib/http/connection.rb +7 -3
  18. data/lib/http/content_type.rb +10 -5
  19. data/lib/http/feature.rb +1 -1
  20. data/lib/http/features/auto_inflate.rb +0 -2
  21. data/lib/http/features/instrumentation.rb +1 -1
  22. data/lib/http/features/logging.rb +19 -21
  23. data/lib/http/headers.rb +3 -3
  24. data/lib/http/mime_type/adapter.rb +2 -0
  25. data/lib/http/options.rb +2 -2
  26. data/lib/http/redirector.rb +1 -1
  27. data/lib/http/request/writer.rb +5 -1
  28. data/lib/http/request.rb +22 -5
  29. data/lib/http/response/body.rb +5 -4
  30. data/lib/http/response/inflater.rb +1 -1
  31. data/lib/http/response/parser.rb +74 -62
  32. data/lib/http/response/status.rb +2 -2
  33. data/lib/http/response.rb +22 -4
  34. data/lib/http/timeout/global.rb +41 -35
  35. data/lib/http/timeout/null.rb +2 -1
  36. data/lib/http/timeout/per_operation.rb +56 -59
  37. data/lib/http/version.rb +1 -1
  38. data/spec/lib/http/client_spec.rb +109 -41
  39. data/spec/lib/http/features/auto_inflate_spec.rb +0 -1
  40. data/spec/lib/http/features/instrumentation_spec.rb +21 -16
  41. data/spec/lib/http/features/logging_spec.rb +2 -5
  42. data/spec/lib/http/headers_spec.rb +3 -3
  43. data/spec/lib/http/redirector_spec.rb +44 -0
  44. data/spec/lib/http/request/writer_spec.rb +12 -1
  45. data/spec/lib/http/response/body_spec.rb +5 -5
  46. data/spec/lib/http/response/parser_spec.rb +30 -1
  47. data/spec/lib/http/response_spec.rb +62 -10
  48. data/spec/lib/http_spec.rb +20 -2
  49. data/spec/spec_helper.rb +21 -21
  50. data/spec/support/black_hole.rb +1 -1
  51. data/spec/support/dummy_server/servlet.rb +14 -2
  52. data/spec/support/dummy_server.rb +1 -1
  53. data/spec/support/fuubar.rb +21 -0
  54. data/spec/support/simplecov.rb +19 -0
  55. metadata +23 -17
  56. data/.coveralls.yml +0 -1
  57. data/.travis.yml +0 -38
@@ -58,7 +58,7 @@ module HTTP
58
58
  # SYMBOLS[418] # => :im_a_teapot
59
59
  #
60
60
  # @return [Hash<Fixnum => Symbol>]
61
- SYMBOLS = Hash[REASONS.map { |k, v| [k, symbolize(v)] }].freeze
61
+ SYMBOLS = REASONS.transform_values { |v| symbolize(v) }.freeze
62
62
 
63
63
  # Reversed {SYMBOLS} map.
64
64
  #
@@ -69,7 +69,7 @@ module HTTP
69
69
  # SYMBOL_CODES[:im_a_teapot] # => 418
70
70
  #
71
71
  # @return [Hash<Symbol => Fixnum>]
72
- SYMBOL_CODES = Hash[SYMBOLS.map { |k, v| [v, k] }].freeze
72
+ SYMBOL_CODES = SYMBOLS.map { |k, v| [v, k] }.to_h.freeze
73
73
 
74
74
  # @return [Fixnum] status code
75
75
  attr_reader :code
data/lib/http/response.rb CHANGED
@@ -40,10 +40,11 @@ module HTTP
40
40
  # @option opts [HTTP::Connection] :connection
41
41
  # @option opts [String] :encoding Encoding to use when reading body
42
42
  # @option opts [String] :body
43
- # @option opts [HTTP::Request] request
43
+ # @option opts [HTTP::Request] request The request this is in response to.
44
+ # @option opts [String] :uri (DEPRECATED) used to populate a missing request
44
45
  def initialize(opts)
45
46
  @version = opts.fetch(:version)
46
- @request = opts.fetch(:request)
47
+ @request = init_request(opts)
47
48
  @status = HTTP::Response::Status.new(opts.fetch(:status))
48
49
  @headers = HTTP::Headers.coerce(opts[:headers] || {})
49
50
  @proxy_headers = HTTP::Headers.coerce(opts[:proxy_headers] || {})
@@ -156,13 +157,30 @@ module HTTP
156
157
  # @param type [#to_s] Parse as given MIME type.
157
158
  # @raise (see MimeType.[])
158
159
  # @return [Object]
159
- def parse(type)
160
- MimeType[type].decode to_s
160
+ def parse(type = nil)
161
+ MimeType[type || mime_type].decode to_s
161
162
  end
162
163
 
163
164
  # Inspect a response
164
165
  def inspect
165
166
  "#<#{self.class}/#{@version} #{code} #{reason} #{headers.to_h.inspect}>"
166
167
  end
168
+
169
+ private
170
+
171
+ # Initialize an HTTP::Request from options.
172
+ #
173
+ # @return [HTTP::Request]
174
+ def init_request(opts)
175
+ raise ArgumentError, ":uri is for backwards compatibilty and conflicts with :request" \
176
+ if opts[:request] && opts[:uri]
177
+
178
+ # For backwards compatibilty
179
+ if opts[:uri]
180
+ HTTP::Request.new(:uri => opts[:uri], :verb => :get)
181
+ else
182
+ opts.fetch(:request)
183
+ end
184
+ end
167
185
  end
168
186
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "timeout"
4
3
  require "io/wait"
4
+ require "resolv"
5
+ require "timeout"
5
6
 
6
7
  require "http/timeout/null"
7
8
 
@@ -12,6 +13,9 @@ module HTTP
12
13
  super
13
14
 
14
15
  @timeout = @time_left = options.fetch(:global_timeout)
16
+ @dns_resolver = options.fetch(:dns_resolver) do
17
+ ::Resolv.method(:getaddresses)
18
+ end
15
19
  end
16
20
 
17
21
  # To future me: Don't remove this again, past you was smarter.
@@ -19,14 +23,28 @@ module HTTP
19
23
  @time_left = @timeout
20
24
  end
21
25
 
22
- def connect(socket_class, host, port, nodelay = false)
26
+ def connect(socket_class, host_name, *args)
27
+ connect_operation = lambda do |host_address|
28
+ ::Timeout.timeout(@time_left, TimeoutError) do
29
+ super(socket_class, host_address, *args)
30
+ end
31
+ end
32
+ host_addresses = @dns_resolver.call(host_name)
33
+ # ensure something to iterates
34
+ trying_targets = host_addresses.empty? ? [host_name] : host_addresses
23
35
  reset_timer
24
- ::Timeout.timeout(@time_left, TimeoutError) do
25
- @socket = socket_class.open(host, port)
26
- @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
36
+ trying_iterator = trying_targets.lazy
37
+ error = nil
38
+ begin
39
+ connect_operation.call(trying_iterator.next).tap do
40
+ log_time
41
+ end
42
+ rescue TimeoutError => e
43
+ error = e
44
+ retry
45
+ rescue ::StopIteration
46
+ raise error
27
47
  end
28
-
29
- log_time
30
48
  end
31
49
 
32
50
  def connect_ssl
@@ -59,22 +77,12 @@ module HTTP
59
77
 
60
78
  private
61
79
 
62
- if RUBY_VERSION < "2.1.0"
63
- def read_nonblock(size, buffer = nil)
64
- @socket.read_nonblock(size, buffer)
65
- end
66
-
67
- def write_nonblock(data)
68
- @socket.write_nonblock(data)
69
- end
70
- else
71
- def read_nonblock(size, buffer = nil)
72
- @socket.read_nonblock(size, buffer, :exception => false)
73
- end
80
+ def read_nonblock(size, buffer = nil)
81
+ @socket.read_nonblock(size, buffer, :exception => false)
82
+ end
74
83
 
75
- def write_nonblock(data)
76
- @socket.write_nonblock(data, :exception => false)
77
- end
84
+ def write_nonblock(data)
85
+ @socket.write_nonblock(data, :exception => false)
78
86
  end
79
87
 
80
88
  # Perform the given I/O operation with the given argument
@@ -82,20 +90,18 @@ module HTTP
82
90
  reset_timer
83
91
 
84
92
  loop do
85
- begin
86
- result = yield
87
-
88
- case result
89
- when :wait_readable then wait_readable_or_timeout
90
- when :wait_writable then wait_writable_or_timeout
91
- when NilClass then return :eof
92
- else return result
93
- end
94
- rescue IO::WaitReadable
95
- wait_readable_or_timeout
96
- rescue IO::WaitWritable
97
- wait_writable_or_timeout
93
+ result = yield
94
+
95
+ case result
96
+ when :wait_readable then wait_readable_or_timeout
97
+ when :wait_writable then wait_writable_or_timeout
98
+ when NilClass then return :eof
99
+ else return result
98
100
  end
101
+ rescue IO::WaitReadable
102
+ wait_readable_or_timeout
103
+ rescue IO::WaitWritable
104
+ wait_writable_or_timeout
99
105
  end
100
106
  rescue EOFError
101
107
  :eof
@@ -12,7 +12,7 @@ module HTTP
12
12
 
13
13
  attr_reader :options, :socket
14
14
 
15
- def initialize(options = {}) # rubocop:disable Style/OptionHash
15
+ def initialize(options = {})
16
16
  @options = options
17
17
  end
18
18
 
@@ -36,6 +36,7 @@ module HTTP
36
36
  connect_ssl
37
37
 
38
38
  return unless ssl_context.verify_mode == OpenSSL::SSL::VERIFY_PEER
39
+ return if ssl_context.respond_to?(:verify_hostname) && !ssl_context.verify_hostname
39
40
 
40
41
  @socket.post_connection_check(host)
41
42
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "resolv"
3
4
  require "timeout"
4
5
 
5
6
  require "http/timeout/null"
@@ -17,14 +18,34 @@ module HTTP
17
18
  @read_timeout = options.fetch(:read_timeout, READ_TIMEOUT)
18
19
  @write_timeout = options.fetch(:write_timeout, WRITE_TIMEOUT)
19
20
  @connect_timeout = options.fetch(:connect_timeout, CONNECT_TIMEOUT)
21
+ @dns_resolver = options.fetch(:dns_resolver) do
22
+ ::Resolv.method(:getaddresses)
23
+ end
20
24
  end
21
25
 
22
- def connect(socket_class, host, port, nodelay = false)
23
- ::Timeout.timeout(@connect_timeout, TimeoutError) do
24
- @socket = socket_class.open(host, port)
25
- @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
26
+ # TODO: refactor
27
+ # rubocop:disable Metrics/MethodLength
28
+ def connect(socket_class, host_name, *args)
29
+ connect_operation = lambda do |host_address|
30
+ ::Timeout.timeout(@connect_timeout, TimeoutError) do
31
+ super(socket_class, host_address, *args)
32
+ end
33
+ end
34
+ host_addresses = @dns_resolver.call(host_name)
35
+ # ensure something to iterates
36
+ trying_targets = host_addresses.empty? ? [host_name] : host_addresses
37
+ trying_iterator = trying_targets.lazy
38
+ error = nil
39
+ begin
40
+ connect_operation.call(trying_iterator.next)
41
+ rescue TimeoutError => e
42
+ error = e
43
+ retry
44
+ rescue ::StopIteration
45
+ raise error
26
46
  end
27
47
  end
48
+ # rubocop:enable Metrics/MethodLength
28
49
 
29
50
  def connect_ssl
30
51
  rescue_readable(@connect_timeout) do
@@ -34,66 +55,42 @@ module HTTP
34
55
  end
35
56
  end
36
57
 
37
- # NIO with exceptions
38
- if RUBY_VERSION < "2.1.0"
39
- # Read data from the socket
40
- def readpartial(size, buffer = nil)
41
- rescue_readable do
42
- @socket.read_nonblock(size, buffer)
43
- end
44
- rescue EOFError
45
- :eof
46
- end
47
-
48
- # Write data to the socket
49
- def write(data)
50
- rescue_writable do
51
- @socket.write_nonblock(data)
52
- end
53
- rescue EOFError
54
- :eof
55
- end
56
-
57
- # NIO without exceptions
58
- else
59
- # Read data from the socket
60
- def readpartial(size, buffer = nil)
61
- timeout = false
62
- loop do
63
- result = @socket.read_nonblock(size, buffer, :exception => false)
64
-
65
- return :eof if result.nil?
66
- return result if result != :wait_readable
67
-
68
- raise TimeoutError, "Read timed out after #{@read_timeout} seconds" if timeout
69
-
70
- # marking the socket for timeout. Why is this not being raised immediately?
71
- # it seems there is some race-condition on the network level between calling
72
- # #read_nonblock and #wait_readable, in which #read_nonblock signalizes waiting
73
- # for reads, and when waiting for x seconds, it returns nil suddenly without completing
74
- # the x seconds. In a normal case this would be a timeout on wait/read, but it can
75
- # also mean that the socket has been closed by the server. Therefore we "mark" the
76
- # socket for timeout and try to read more bytes. If it returns :eof, it's all good, no
77
- # timeout. Else, the first timeout was a proper timeout.
78
- # This hack has to be done because io/wait#wait_readable doesn't provide a value for when
79
- # the socket is closed by the server, and HTTP::Parser doesn't provide the limit for the chunks.
80
- timeout = true unless @socket.to_io.wait_readable(@read_timeout)
81
- end
58
+ # Read data from the socket
59
+ def readpartial(size, buffer = nil)
60
+ timeout = false
61
+ loop do
62
+ result = @socket.read_nonblock(size, buffer, :exception => false)
63
+
64
+ return :eof if result.nil?
65
+ return result if result != :wait_readable
66
+
67
+ raise TimeoutError, "Read timed out after #{@read_timeout} seconds" if timeout
68
+
69
+ # marking the socket for timeout. Why is this not being raised immediately?
70
+ # it seems there is some race-condition on the network level between calling
71
+ # #read_nonblock and #wait_readable, in which #read_nonblock signalizes waiting
72
+ # for reads, and when waiting for x seconds, it returns nil suddenly without completing
73
+ # the x seconds. In a normal case this would be a timeout on wait/read, but it can
74
+ # also mean that the socket has been closed by the server. Therefore we "mark" the
75
+ # socket for timeout and try to read more bytes. If it returns :eof, it's all good, no
76
+ # timeout. Else, the first timeout was a proper timeout.
77
+ # This hack has to be done because io/wait#wait_readable doesn't provide a value for when
78
+ # the socket is closed by the server, and HTTP::Parser doesn't provide the limit for the chunks.
79
+ timeout = true unless @socket.to_io.wait_readable(@read_timeout)
82
80
  end
81
+ end
83
82
 
84
- # Write data to the socket
85
- def write(data)
86
- timeout = false
87
- loop do
88
- result = @socket.write_nonblock(data, :exception => false)
89
- return result unless result == :wait_writable
83
+ # Write data to the socket
84
+ def write(data)
85
+ timeout = false
86
+ loop do
87
+ result = @socket.write_nonblock(data, :exception => false)
88
+ return result unless result == :wait_writable
90
89
 
91
- raise TimeoutError, "Write timed out after #{@write_timeout} seconds" if timeout
90
+ raise TimeoutError, "Write timed out after #{@write_timeout} seconds" if timeout
92
91
 
93
- timeout = true unless @socket.to_io.wait_writable(@write_timeout)
94
- end
92
+ timeout = true unless @socket.to_io.wait_writable(@write_timeout)
95
93
  end
96
-
97
94
  end
98
95
  end
99
96
  end
data/lib/http/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP
4
- VERSION = "5.0.0.pre2"
4
+ VERSION = "5.0.2"
5
5
  end
@@ -4,50 +4,55 @@
4
4
  require "support/http_handling_shared"
5
5
  require "support/dummy_server"
6
6
  require "support/ssl_helper"
7
+ require "logger"
7
8
 
8
9
  RSpec.describe HTTP::Client do
9
10
  run_server(:dummy) { DummyServer.new }
10
11
 
11
- StubbedClient = Class.new(HTTP::Client) do
12
- def perform(request, options)
13
- stubbed = stubs[request.uri]
14
- stubbed ? stubbed.call(request) : super(request, options)
15
- end
16
-
17
- def stubs
18
- @stubs ||= {}
19
- end
12
+ before do
13
+ stubbed_client = Class.new(HTTP::Client) do
14
+ def perform(request, options)
15
+ stubbed = stubs[HTTP::URI::NORMALIZER.call(request.uri).to_s]
16
+ stubbed ? stubbed.call(request) : super(request, options)
17
+ end
20
18
 
21
- def stub(stubs)
22
- @stubs = stubs.each_with_object({}) do |(k, v), o|
23
- o[HTTP::URI.parse k] = v
19
+ def stubs
20
+ @stubs ||= {}
24
21
  end
25
22
 
26
- self
23
+ def stub(stubs)
24
+ @stubs = stubs.transform_keys do |k|
25
+ HTTP::URI::NORMALIZER.call(k).to_s
26
+ end
27
+
28
+ self
29
+ end
27
30
  end
28
- end
29
31
 
30
- def redirect_response(location, status = 302)
31
- lambda do |request|
32
- HTTP::Response.new(
33
- :status => status,
34
- :version => "1.1",
35
- :headers => {"Location" => location},
36
- :body => "",
37
- :request => request
38
- )
32
+ def redirect_response(location, status = 302)
33
+ lambda do |request|
34
+ HTTP::Response.new(
35
+ :status => status,
36
+ :version => "1.1",
37
+ :headers => {"Location" => location},
38
+ :body => "",
39
+ :request => request
40
+ )
41
+ end
39
42
  end
40
- end
41
43
 
42
- def simple_response(body, status = 200)
43
- lambda do |request|
44
- HTTP::Response.new(
45
- :status => status,
46
- :version => "1.1",
47
- :body => body,
48
- :request => request
49
- )
44
+ def simple_response(body, status = 200)
45
+ lambda do |request|
46
+ HTTP::Response.new(
47
+ :status => status,
48
+ :version => "1.1",
49
+ :body => body,
50
+ :request => request
51
+ )
52
+ end
50
53
  end
54
+
55
+ stub_const("StubbedClient", stubbed_client)
51
56
  end
52
57
 
53
58
  describe "following redirects" do
@@ -105,13 +110,45 @@ RSpec.describe HTTP::Client do
105
110
  end
106
111
 
107
112
  it "works like a charm in real world" do
108
- url = "http://git.io/jNeY"
109
- client = HTTP.follow
110
- expect(client.get(url).to_s).to include "support for non-ascii URIs"
113
+ expect(HTTP.follow.get("https://bit.ly/2UaBT4R").parse(:json)).
114
+ to include("url" => "https://httpbin.org/anything/könig")
111
115
  end
112
116
  end
113
117
  end
114
118
 
119
+ describe "following redirects with logging" do
120
+ let(:logger) do
121
+ logger = Logger.new(logdev)
122
+ logger.formatter = ->(severity, _, _, message) { format("** %s **\n%s\n", severity, message) }
123
+ logger.level = Logger::INFO
124
+ logger
125
+ end
126
+
127
+ let(:logdev) { StringIO.new }
128
+
129
+ it "logs all requests" do
130
+ client = StubbedClient.new(:follow => true, :features => { :logging => { :logger => logger } }).stub(
131
+ "http://example.com/" => redirect_response("/1"),
132
+ "http://example.com/1" => redirect_response("/2"),
133
+ "http://example.com/2" => redirect_response("/3"),
134
+ "http://example.com/3" => simple_response("OK")
135
+ )
136
+
137
+ expect { client.get("http://example.com/") }.not_to raise_error
138
+
139
+ expect(logdev.string).to eq <<~OUTPUT
140
+ ** INFO **
141
+ > GET http://example.com/
142
+ ** INFO **
143
+ > GET http://example.com/1
144
+ ** INFO **
145
+ > GET http://example.com/2
146
+ ** INFO **
147
+ > GET http://example.com/3
148
+ OUTPUT
149
+ end
150
+ end
151
+
115
152
  describe "parsing params" do
116
153
  let(:client) { HTTP::Client.new }
117
154
  before { allow(client).to receive :perform }
@@ -197,6 +234,22 @@ RSpec.describe HTTP::Client do
197
234
 
198
235
  client.get("http://example.com/", :form => {:foo => HTTP::FormData::Part.new("content")})
199
236
  end
237
+
238
+ context "when passing an HTTP::FormData object directly" do
239
+ it "creates url encoded form data object" do
240
+ client = HTTP::Client.new
241
+ form_data = HTTP::FormData::Multipart.new({ :foo => "bar" })
242
+
243
+ allow(client).to receive(:perform)
244
+
245
+ expect(HTTP::Request).to receive(:new) do |opts|
246
+ expect(opts[:body]).to be form_data
247
+ expect(opts[:body].to_s).to match(/^Content-Disposition: form-data; name="foo"\r\n\r\nbar\r\n/m)
248
+ end
249
+
250
+ client.get("http://example.com/", :form => form_data)
251
+ end
252
+ end
200
253
  end
201
254
 
202
255
  describe "passing json" do
@@ -220,9 +273,9 @@ RSpec.describe HTTP::Client do
220
273
  end
221
274
 
222
275
  it "works like a charm in real world" do
223
- url = "https://github.com/httprb/http.rb/pull/197/ö無"
224
- client = HTTP.follow
225
- expect(client.get(url).to_s).to include "support for non-ascii URIs"
276
+ url = "https://httpbin.org/anything/ö無"
277
+
278
+ expect(HTTP.follow.get(url).parse(:json)).to include("url" => url)
226
279
  end
227
280
  end
228
281
 
@@ -291,6 +344,7 @@ RSpec.describe HTTP::Client do
291
344
  end
292
345
  end
293
346
  end
347
+
294
348
  it "is given a chance to wrap the Request" do
295
349
  feature_instance = feature_class.new
296
350
 
@@ -299,7 +353,7 @@ RSpec.describe HTTP::Client do
299
353
 
300
354
  expect(response.code).to eq(200)
301
355
  expect(feature_instance.captured_request.verb).to eq(:get)
302
- expect(feature_instance.captured_request.uri.to_s).to eq(dummy.endpoint + "/")
356
+ expect(feature_instance.captured_request.uri.to_s).to eq("#{dummy.endpoint}/")
303
357
  end
304
358
 
305
359
  it "is given a chance to wrap the Response" do
@@ -325,6 +379,19 @@ RSpec.describe HTTP::Client do
325
379
  expect(feature_instance.captured_request.verb).to eq(:post)
326
380
  expect(feature_instance.captured_request.uri.to_s).to eq(sleep_url)
327
381
  end
382
+
383
+ it "is given a chance to handle a connection timeout error" do
384
+ allow(TCPSocket).to receive(:open) { sleep 1 }
385
+ sleep_url = "#{dummy.endpoint}/sleep"
386
+ feature_instance = feature_class.new
387
+
388
+ expect do
389
+ client.use(:test_feature => feature_instance).
390
+ timeout(0.001).
391
+ request(:post, sleep_url)
392
+ end.to raise_error(HTTP::TimeoutError)
393
+ expect(feature_instance.captured_error).to be_a(HTTP::TimeoutError)
394
+ end
328
395
  end
329
396
  end
330
397
 
@@ -335,7 +402,8 @@ RSpec.describe HTTP::Client do
335
402
  let(:client) { described_class.new(options.merge(extra_options)) }
336
403
  end
337
404
 
338
- describe "working with SSL" do
405
+ # TODO: https://github.com/httprb/http/issues/627
406
+ xdescribe "working with SSL" do
339
407
  run_server(:dummy_ssl) { DummyServer.new(:ssl => true) }
340
408
 
341
409
  let(:extra_options) { {} }
@@ -476,7 +544,7 @@ RSpec.describe HTTP::Client do
476
544
  BODY
477
545
  end
478
546
 
479
- it "raises HTTP::ConnectionError" do
547
+ xit "raises HTTP::ConnectionError" do
480
548
  expect { client.get(dummy.endpoint).to_s }.to raise_error(HTTP::ConnectionError)
481
549
  end
482
550
  end