http 5.0.0.pre2 → 5.0.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.
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