http 4.4.1 → 5.1.1

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +85 -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 +8 -110
  8. data/.rubocop_todo.yml +206 -0
  9. data/.yardopts +1 -1
  10. data/CHANGES.md +200 -3
  11. data/Gemfile +18 -10
  12. data/LICENSE.txt +1 -1
  13. data/README.md +48 -86
  14. data/Rakefile +2 -10
  15. data/SECURITY.md +5 -0
  16. data/http.gemspec +9 -8
  17. data/lib/http/chainable.rb +23 -17
  18. data/lib/http/client.rb +44 -34
  19. data/lib/http/connection.rb +11 -7
  20. data/lib/http/content_type.rb +12 -7
  21. data/lib/http/errors.rb +3 -0
  22. data/lib/http/feature.rb +3 -1
  23. data/lib/http/features/auto_deflate.rb +6 -6
  24. data/lib/http/features/auto_inflate.rb +6 -7
  25. data/lib/http/features/instrumentation.rb +1 -1
  26. data/lib/http/features/logging.rb +19 -21
  27. data/lib/http/headers.rb +50 -13
  28. data/lib/http/mime_type/adapter.rb +3 -1
  29. data/lib/http/mime_type/json.rb +1 -0
  30. data/lib/http/options.rb +5 -8
  31. data/lib/http/redirector.rb +55 -4
  32. data/lib/http/request/body.rb +1 -0
  33. data/lib/http/request/writer.rb +9 -4
  34. data/lib/http/request.rb +28 -11
  35. data/lib/http/response/body.rb +6 -4
  36. data/lib/http/response/inflater.rb +1 -1
  37. data/lib/http/response/parser.rb +74 -62
  38. data/lib/http/response/status.rb +4 -3
  39. data/lib/http/response.rb +44 -18
  40. data/lib/http/timeout/global.rb +20 -36
  41. data/lib/http/timeout/null.rb +2 -1
  42. data/lib/http/timeout/per_operation.rb +32 -55
  43. data/lib/http/uri.rb +51 -6
  44. data/lib/http/version.rb +1 -1
  45. data/spec/lib/http/client_spec.rb +155 -30
  46. data/spec/lib/http/connection_spec.rb +8 -5
  47. data/spec/lib/http/features/auto_inflate_spec.rb +3 -2
  48. data/spec/lib/http/features/instrumentation_spec.rb +27 -21
  49. data/spec/lib/http/features/logging_spec.rb +8 -10
  50. data/spec/lib/http/headers_spec.rb +53 -18
  51. data/spec/lib/http/options/headers_spec.rb +1 -1
  52. data/spec/lib/http/options/merge_spec.rb +16 -16
  53. data/spec/lib/http/redirector_spec.rb +133 -3
  54. data/spec/lib/http/request/body_spec.rb +3 -3
  55. data/spec/lib/http/request/writer_spec.rb +25 -2
  56. data/spec/lib/http/request_spec.rb +5 -5
  57. data/spec/lib/http/response/body_spec.rb +5 -5
  58. data/spec/lib/http/response/parser_spec.rb +33 -4
  59. data/spec/lib/http/response/status_spec.rb +3 -3
  60. data/spec/lib/http/response_spec.rb +80 -3
  61. data/spec/lib/http/uri_spec.rb +39 -0
  62. data/spec/lib/http_spec.rb +30 -3
  63. data/spec/spec_helper.rb +21 -21
  64. data/spec/support/black_hole.rb +1 -1
  65. data/spec/support/dummy_server/servlet.rb +19 -6
  66. data/spec/support/dummy_server.rb +7 -7
  67. data/spec/support/fuubar.rb +21 -0
  68. data/spec/support/http_handling_shared.rb +5 -5
  69. data/spec/support/simplecov.rb +19 -0
  70. data/spec/support/ssl_helper.rb +4 -4
  71. metadata +22 -14
  72. data/.coveralls.yml +0 -1
  73. data/.travis.yml +0 -39
data/lib/http/client.rb CHANGED
@@ -16,7 +16,7 @@ module HTTP
16
16
  extend Forwardable
17
17
  include Chainable
18
18
 
19
- HTTP_OR_HTTPS_RE = %r{^https?://}i
19
+ HTTP_OR_HTTPS_RE = %r{^https?://}i.freeze
20
20
 
21
21
  def initialize(default_options = {})
22
22
  @default_options = HTTP::Options.new(default_options)
@@ -25,19 +25,19 @@ module HTTP
25
25
  end
26
26
 
27
27
  # Make an HTTP request
28
- def request(verb, uri, opts = {}) # rubocop:disable Style/OptionHash
28
+ def request(verb, uri, opts = {})
29
29
  opts = @default_options.merge(opts)
30
30
  req = build_request(verb, uri, opts)
31
31
  res = perform(req, opts)
32
32
  return res unless opts.follow
33
33
 
34
34
  Redirector.new(opts.follow).perform(req, res) do |request|
35
- perform(request, opts)
35
+ perform(wrap_request(request, opts), opts)
36
36
  end
37
37
  end
38
38
 
39
39
  # Prepare an HTTP request
40
- def build_request(verb, uri, opts = {}) # rubocop:disable Style/OptionHash
40
+ def build_request(verb, uri, opts = {})
41
41
  opts = @default_options.merge(opts)
42
42
  uri = make_request_uri(uri, opts)
43
43
  headers = make_request_headers(opts)
@@ -52,9 +52,7 @@ module HTTP
52
52
  :body => body
53
53
  )
54
54
 
55
- opts.features.inject(req) do |request, (_name, feature)|
56
- feature.wrap_request(request)
57
- end
55
+ wrap_request(req, opts)
58
56
  end
59
57
 
60
58
  # @!method persistent?
@@ -68,22 +66,20 @@ module HTTP
68
66
 
69
67
  @state = :dirty
70
68
 
71
- @connection ||= HTTP::Connection.new(req, options)
72
-
73
- unless @connection.failed_proxy_connect?
74
- @connection.send_request(req)
75
- @connection.read_headers!
69
+ begin
70
+ @connection ||= HTTP::Connection.new(req, options)
71
+
72
+ unless @connection.failed_proxy_connect?
73
+ @connection.send_request(req)
74
+ @connection.read_headers!
75
+ end
76
+ rescue Error => e
77
+ options.features.each_value do |feature|
78
+ feature.on_error(req, e)
79
+ end
80
+ raise
76
81
  end
77
-
78
- res = Response.new(
79
- :status => @connection.status_code,
80
- :version => @connection.http_version,
81
- :headers => @connection.headers,
82
- :proxy_headers => @connection.proxy_response_headers,
83
- :connection => @connection,
84
- :encoding => options.encoding,
85
- :uri => req.uri
86
- )
82
+ res = build_response(req, options)
87
83
 
88
84
  res = options.features.inject(res) do |response, (_name, feature)|
89
85
  feature.wrap_response(response)
@@ -99,26 +95,44 @@ module HTTP
99
95
  end
100
96
 
101
97
  def close
102
- @connection.close if @connection
98
+ @connection&.close
103
99
  @connection = nil
104
100
  @state = :clean
105
101
  end
106
102
 
107
103
  private
108
104
 
105
+ def wrap_request(req, opts)
106
+ opts.features.inject(req) do |request, (_name, feature)|
107
+ feature.wrap_request(request)
108
+ end
109
+ end
110
+
111
+ def build_response(req, options)
112
+ Response.new(
113
+ :status => @connection.status_code,
114
+ :version => @connection.http_version,
115
+ :headers => @connection.headers,
116
+ :proxy_headers => @connection.proxy_response_headers,
117
+ :connection => @connection,
118
+ :encoding => options.encoding,
119
+ :request => req
120
+ )
121
+ end
122
+
109
123
  # Verify our request isn't going to be made against another URI
110
124
  def verify_connection!(uri)
111
125
  if default_options.persistent? && uri.origin != default_options.persistent
112
126
  raise StateError, "Persistence is enabled for #{default_options.persistent}, but we got #{uri.origin}"
127
+ end
128
+
113
129
  # We re-create the connection object because we want to let prior requests
114
130
  # lazily load the body as long as possible, and this mimics prior functionality.
115
- elsif @connection && (!@connection.keep_alive? || @connection.expired?)
116
- close
131
+ return close if @connection && (!@connection.keep_alive? || @connection.expired?)
132
+
117
133
  # If we get into a bad state (eg, Timeout.timeout ensure being killed)
118
134
  # close the connection to prevent potential for mixed responses.
119
- elsif @state == :dirty
120
- close
121
- end
135
+ return close if @state == :dirty
122
136
  end
123
137
 
124
138
  # Merges query params if needed
@@ -128,15 +142,11 @@ module HTTP
128
142
  def make_request_uri(uri, opts)
129
143
  uri = uri.to_s
130
144
 
131
- if default_options.persistent? && uri !~ HTTP_OR_HTTPS_RE
132
- uri = "#{default_options.persistent}#{uri}"
133
- end
145
+ uri = "#{default_options.persistent}#{uri}" if default_options.persistent? && uri !~ HTTP_OR_HTTPS_RE
134
146
 
135
147
  uri = HTTP::URI.parse uri
136
148
 
137
- if opts.params && !opts.params.empty?
138
- uri.query_values = uri.query_values(Array).to_a.concat(opts.params.to_a)
139
- end
149
+ uri.query_values = uri.query_values(Array).to_a.concat(opts.params.to_a) if opts.params && !opts.params.empty?
140
150
 
141
151
  # Some proxies (seen on WEBRick) fail if URL has
142
152
  # empty path (e.g. `http://example.com`) while it's RFC-complaint:
@@ -3,7 +3,6 @@
3
3
  require "forwardable"
4
4
 
5
5
  require "http/headers"
6
- require "http/response/parser"
7
6
 
8
7
  module HTTP
9
8
  # A connection to the HTTP server
@@ -45,8 +44,8 @@ module HTTP
45
44
  send_proxy_connect_request(req)
46
45
  start_tls(req, options)
47
46
  reset_timer
48
- rescue IOError, SocketError, SystemCallError => ex
49
- raise ConnectionError, "failed to connect: #{ex}", ex.backtrace
47
+ rescue IOError, SocketError, SystemCallError => e
48
+ raise ConnectionError, "failed to connect: #{e}", e.backtrace
50
49
  end
51
50
 
52
51
  # @see (HTTP::Response::Parser#status_code)
@@ -68,8 +67,13 @@ module HTTP
68
67
  # @param [Request] req Request to send to the server
69
68
  # @return [nil]
70
69
  def send_request(req)
71
- raise StateError, "Tried to send a request while one is pending already. Make sure you read off the body." if @pending_response
72
- raise StateError, "Tried to send a request while a response is pending. Make sure you read off the body." if @pending_request
70
+ if @pending_response
71
+ raise StateError, "Tried to send a request while one is pending already. Make sure you read off the body."
72
+ end
73
+
74
+ if @pending_request
75
+ raise StateError, "Tried to send a request while a response is pending. Make sure you read off the body."
76
+ end
73
77
 
74
78
  @pending_request = true
75
79
 
@@ -216,8 +220,8 @@ module HTTP
216
220
  elsif value
217
221
  @parser << value
218
222
  end
219
- rescue IOError, SocketError, SystemCallError => ex
220
- raise ConnectionError, "error reading from socket: #{ex}", ex.backtrace
223
+ rescue IOError, SocketError, SystemCallError => e
224
+ raise ConnectionError, "error reading from socket: #{e}", e.backtrace
221
225
  end
222
226
  end
223
227
  end
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP
4
- ContentType = Struct.new(:mime_type, :charset) do
5
- MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}
6
- CHARSET_RE = /;\s*charset=([^;]+)/i
4
+ class ContentType
5
+ MIME_TYPE_RE = %r{^([^/]+/[^;]+)(?:$|;)}.freeze
6
+ CHARSET_RE = /;\s*charset=([^;]+)/i.freeze
7
+
8
+ attr_accessor :mime_type, :charset
7
9
 
8
10
  class << self
9
11
  # Parse string and return ContentType struct
@@ -15,15 +17,18 @@ module HTTP
15
17
 
16
18
  # :nodoc:
17
19
  def mime_type(str)
18
- m = str.to_s[MIME_TYPE_RE, 1]
19
- m && m.strip.downcase
20
+ str.to_s[MIME_TYPE_RE, 1]&.strip&.downcase
20
21
  end
21
22
 
22
23
  # :nodoc:
23
24
  def charset(str)
24
- m = str.to_s[CHARSET_RE, 1]
25
- m && m.strip.delete('"')
25
+ str.to_s[CHARSET_RE, 1]&.strip&.delete('"')
26
26
  end
27
27
  end
28
+
29
+ def initialize(mime_type = nil, charset = nil)
30
+ @mime_type = mime_type
31
+ @charset = charset
32
+ end
28
33
  end
29
34
  end
data/lib/http/errors.rb CHANGED
@@ -19,6 +19,9 @@ module HTTP
19
19
  # Generic Timeout error
20
20
  class TimeoutError < Error; end
21
21
 
22
+ # Timeout when first establishing the conncetion
23
+ class ConnectTimeoutError < TimeoutError; end
24
+
22
25
  # Header value is of unexpected format (similar to Net::HTTPHeaderSyntaxError)
23
26
  class HeaderError < Error; end
24
27
  end
data/lib/http/feature.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module HTTP
4
4
  class Feature
5
- def initialize(opts = {}) # rubocop:disable Style/OptionHash
5
+ def initialize(opts = {})
6
6
  @opts = opts
7
7
  end
8
8
 
@@ -13,6 +13,8 @@ module HTTP
13
13
  def wrap_response(response)
14
14
  response
15
15
  end
16
+
17
+ def on_error(request, error); end
16
18
  end
17
19
  end
18
20
 
@@ -27,12 +27,12 @@ module HTTP
27
27
  request.headers[Headers::CONTENT_ENCODING] = method
28
28
 
29
29
  Request.new(
30
- :version => request.version,
31
- :verb => request.verb,
32
- :uri => request.uri,
33
- :headers => request.headers,
34
- :proxy => request.proxy,
35
- :body => deflated_body(request.body),
30
+ :version => request.version,
31
+ :verb => request.verb,
32
+ :uri => request.uri,
33
+ :headers => request.headers,
34
+ :proxy => request.proxy,
35
+ :body => deflated_body(request.body),
36
36
  :uri_normalizer => request.uri_normalizer
37
37
  )
38
38
  end
@@ -12,16 +12,15 @@ module HTTP
12
12
  return response unless supported_encoding?(response)
13
13
 
14
14
  options = {
15
- :status => response.status,
16
- :version => response.version,
17
- :headers => response.headers,
15
+ :status => response.status,
16
+ :version => response.version,
17
+ :headers => response.headers,
18
18
  :proxy_headers => response.proxy_headers,
19
- :connection => response.connection,
20
- :body => stream_for(response.connection)
19
+ :connection => response.connection,
20
+ :body => stream_for(response.connection),
21
+ :request => response.request
21
22
  }
22
23
 
23
- options[:uri] = response.uri if response.uri
24
-
25
24
  Response.new(options)
26
25
  end
27
26
 
@@ -29,7 +29,7 @@ module HTTP
29
29
  def wrap_request(request)
30
30
  # Emit a separate "start" event, so a logger can print the request
31
31
  # being run without waiting for a response
32
- instrumenter.instrument("start_#{name}", :request => request) {}
32
+ instrumenter.instrument("start_#{name}", :request => request)
33
33
  instrumenter.start(name, :request => request)
34
34
  request
35
35
  end
@@ -9,6 +9,20 @@ module HTTP
9
9
  # HTTP.use(logging: {logger: Logger.new(STDOUT)}).get("https://example.com/")
10
10
  #
11
11
  class Logging < Feature
12
+ HTTP::Options.register_feature(:logging, self)
13
+
14
+ class NullLogger
15
+ %w[fatal error warn info debug].each do |level|
16
+ define_method(level.to_sym) do |*_args|
17
+ nil
18
+ end
19
+
20
+ define_method(:"#{level}?") do
21
+ true
22
+ end
23
+ end
24
+ end
25
+
12
26
  attr_reader :logger
13
27
 
14
28
  def initialize(logger: NullLogger.new)
@@ -17,39 +31,23 @@ module HTTP
17
31
 
18
32
  def wrap_request(request)
19
33
  logger.info { "> #{request.verb.to_s.upcase} #{request.uri}" }
20
- logger.debug do
21
- headers = request.headers.map { |name, value| "#{name}: #{value}" }.join("\n")
22
- body = request.body.source
34
+ logger.debug { "#{stringify_headers(request.headers)}\n\n#{request.body.source}" }
23
35
 
24
- headers + "\n\n" + body.to_s
25
- end
26
36
  request
27
37
  end
28
38
 
29
39
  def wrap_response(response)
30
40
  logger.info { "< #{response.status}" }
31
- logger.debug do
32
- headers = response.headers.map { |name, value| "#{name}: #{value}" }.join("\n")
33
- body = response.body.to_s
41
+ logger.debug { "#{stringify_headers(response.headers)}\n\n#{response.body}" }
34
42
 
35
- headers + "\n\n" + body
36
- end
37
43
  response
38
44
  end
39
45
 
40
- class NullLogger
41
- %w[fatal error warn info debug].each do |level|
42
- define_method(level.to_sym) do |*_args|
43
- nil
44
- end
46
+ private
45
47
 
46
- define_method(:"#{level}?") do
47
- true
48
- end
49
- end
48
+ def stringify_headers(headers)
49
+ headers.map { |name, value| "#{name}: #{value}" }.join("\n")
50
50
  end
51
-
52
- HTTP::Options.register_feature(:logging, self)
53
51
  end
54
52
  end
55
53
  end
data/lib/http/headers.rb CHANGED
@@ -13,14 +13,18 @@ module HTTP
13
13
  include Enumerable
14
14
 
15
15
  # Matches HTTP header names when in "Canonical-Http-Format"
16
- CANONICAL_NAME_RE = /^[A-Z][a-z]*(?:-[A-Z][a-z]*)*$/
16
+ CANONICAL_NAME_RE = /\A[A-Z][a-z]*(?:-[A-Z][a-z]*)*\z/.freeze
17
17
 
18
18
  # Matches valid header field name according to RFC.
19
19
  # @see http://tools.ietf.org/html/rfc7230#section-3.2
20
- COMPLIANT_NAME_RE = /^[A-Za-z0-9!#\$%&'*+\-.^_`|~]+$/
20
+ COMPLIANT_NAME_RE = /\A[A-Za-z0-9!#$%&'*+\-.^_`|~]+\z/.freeze
21
21
 
22
22
  # Class constructor.
23
23
  def initialize
24
+ # The @pile stores each header value using a three element array:
25
+ # 0 - the normalized header key, used for lookup
26
+ # 1 - the header key as it will be sent with a request
27
+ # 2 - the value
24
28
  @pile = []
25
29
  end
26
30
 
@@ -45,12 +49,31 @@ module HTTP
45
49
 
46
50
  # Appends header.
47
51
  #
48
- # @param [#to_s] name header name
52
+ # @param [String, Symbol] name header name. When specified as a string, the
53
+ # name is sent as-is. When specified as a symbol, the name is converted
54
+ # to a string of capitalized words separated by a dash. Word boundaries
55
+ # are determined by an underscore (`_`) or a dash (`-`).
56
+ # Ex: `:content_type` is sent as `"Content-Type"`, and `"auth_key"` (string)
57
+ # is sent as `"auth_key"`.
49
58
  # @param [Array<#to_s>, #to_s] value header value(s) to be appended
50
59
  # @return [void]
51
60
  def add(name, value)
52
- name = normalize_header name.to_s
53
- Array(value).each { |v| @pile << [name, v.to_s] }
61
+ lookup_name = normalize_header(name.to_s)
62
+ wire_name = case name
63
+ when String
64
+ name
65
+ when Symbol
66
+ lookup_name
67
+ else
68
+ raise HTTP::HeaderError, "HTTP header must be a String or Symbol: #{name.inspect}"
69
+ end
70
+ Array(value).each do |v|
71
+ @pile << [
72
+ lookup_name,
73
+ wire_name,
74
+ validate_value(v)
75
+ ]
76
+ end
54
77
  end
55
78
 
56
79
  # Returns list of header values if any.
@@ -58,7 +81,7 @@ module HTTP
58
81
  # @return [Array<String>]
59
82
  def get(name)
60
83
  name = normalize_header name.to_s
61
- @pile.select { |k, _| k == name }.map { |_, v| v }
84
+ @pile.select { |k, _| k == name }.map { |_, _, v| v }
62
85
  end
63
86
 
64
87
  # Smart version of {#get}.
@@ -88,7 +111,7 @@ module HTTP
88
111
  #
89
112
  # @return [Hash]
90
113
  def to_h
91
- Hash[keys.map { |k| [k, self[k]] }]
114
+ keys.to_h { |k| [k, self[k]] }
92
115
  end
93
116
  alias to_hash to_h
94
117
 
@@ -96,7 +119,7 @@ module HTTP
96
119
  #
97
120
  # @return [Array<[String, String]>]
98
121
  def to_a
99
- @pile.map { |pair| pair.map(&:dup) }
122
+ @pile.map { |item| item[1..2] }
100
123
  end
101
124
 
102
125
  # Returns human-readable representation of `self` instance.
@@ -110,7 +133,7 @@ module HTTP
110
133
  #
111
134
  # @return [Array<String>]
112
135
  def keys
113
- @pile.map { |k, _| k }.uniq
136
+ @pile.map { |_, k, _| k }.uniq
114
137
  end
115
138
 
116
139
  # Compares headers to another Headers or Array of key/value pairs
@@ -118,7 +141,8 @@ module HTTP
118
141
  # @return [Boolean]
119
142
  def ==(other)
120
143
  return false unless other.respond_to? :to_a
121
- @pile == other.to_a
144
+
145
+ to_a == other.to_a
122
146
  end
123
147
 
124
148
  # Calls the given block once for each key/value pair in headers container.
@@ -127,7 +151,8 @@ module HTTP
127
151
  # @return [Headers] self-reference
128
152
  def each
129
153
  return to_enum(__method__) unless block_given?
130
- @pile.each { |arr| yield(arr) }
154
+
155
+ @pile.each { |item| yield(item[1..2]) }
131
156
  self
132
157
  end
133
158
 
@@ -139,7 +164,7 @@ module HTTP
139
164
 
140
165
  # @!method hash
141
166
  # Compute a hash-code for this headers container.
142
- # Two conatiners with the same content will have the same hash code.
167
+ # Two containers with the same content will have the same hash code.
143
168
  #
144
169
  # @see http://www.ruby-doc.org/core/Object.html#method-i-hash
145
170
  # @return [Fixnum]
@@ -150,7 +175,7 @@ module HTTP
150
175
  # @api private
151
176
  def initialize_copy(orig)
152
177
  super
153
- @pile = to_a
178
+ @pile = @pile.map(&:dup)
154
179
  end
155
180
 
156
181
  # Merges `other` headers into `self`.
@@ -209,5 +234,17 @@ module HTTP
209
234
 
210
235
  raise HeaderError, "Invalid HTTP header field name: #{name.inspect}"
211
236
  end
237
+
238
+ # Ensures there is no new line character in the header value
239
+ #
240
+ # @param [String] value
241
+ # @raise [HeaderError] if value includes new line character
242
+ # @return [String] stringified header value
243
+ def validate_value(value)
244
+ v = value.to_s
245
+ return v unless v.include?("\n")
246
+
247
+ raise HeaderError, "Invalid HTTP header field value: #{v.inspect}"
248
+ end
212
249
  end
213
250
  end
@@ -14,13 +14,15 @@ module HTTP
14
14
  def_delegators :instance, :encode, :decode
15
15
  end
16
16
 
17
+ # rubocop:disable Style/DocumentDynamicEvalDefinition
17
18
  %w[encode decode].each do |operation|
18
- class_eval <<-RUBY, __FILE__, __LINE__
19
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
19
20
  def #{operation}(*)
20
21
  fail Error, "\#{self.class} does not supports ##{operation}"
21
22
  end
22
23
  RUBY
23
24
  end
25
+ # rubocop:enable Style/DocumentDynamicEvalDefinition
24
26
  end
25
27
  end
26
28
  end
@@ -10,6 +10,7 @@ module HTTP
10
10
  # Encodes object to JSON
11
11
  def encode(obj)
12
12
  return obj.to_json if obj.respond_to?(:to_json)
13
+
13
14
  ::JSON.dump obj
14
15
  end
15
16
 
data/lib/http/options.rb CHANGED
@@ -1,14 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Metrics/ClassLength
4
-
5
3
  require "http/headers"
6
4
  require "openssl"
7
5
  require "socket"
8
6
  require "http/uri"
9
7
 
10
8
  module HTTP
11
- class Options
9
+ class Options # rubocop:disable Metrics/ClassLength
12
10
  @default_socket_class = TCPSocket
13
11
  @default_ssl_socket_class = OpenSSL::SSL::SSLSocket
14
12
  @default_timeout_class = HTTP::Timeout::Null
@@ -18,9 +16,8 @@ module HTTP
18
16
  attr_accessor :default_socket_class, :default_ssl_socket_class, :default_timeout_class
19
17
  attr_reader :available_features
20
18
 
21
- def new(options = {}) # rubocop:disable Style/OptionHash
22
- return options if options.is_a?(self)
23
- super
19
+ def new(options = {})
20
+ options.is_a?(self) ? options : super
24
21
  end
25
22
 
26
23
  def defined_options
@@ -35,7 +32,7 @@ module HTTP
35
32
 
36
33
  def def_option(name, reader_only: false, &interpreter)
37
34
  defined_options << name.to_sym
38
- interpreter ||= lambda { |v| v }
35
+ interpreter ||= ->(v) { v }
39
36
 
40
37
  if reader_only
41
38
  attr_reader name
@@ -50,7 +47,7 @@ module HTTP
50
47
  end
51
48
  end
52
49
 
53
- def initialize(options = {}) # rubocop:disable Style/OptionHash
50
+ def initialize(options = {})
54
51
  defaults = {
55
52
  :response => :auto,
56
53
  :proxy => {},