http 4.4.1 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) 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 +8 -110
  8. data/.rubocop_todo.yml +192 -0
  9. data/.yardopts +1 -1
  10. data/CHANGES.md +87 -3
  11. data/Gemfile +18 -10
  12. data/README.md +17 -20
  13. data/Rakefile +2 -10
  14. data/http.gemspec +3 -3
  15. data/lib/http/chainable.rb +23 -17
  16. data/lib/http/client.rb +36 -30
  17. data/lib/http/connection.rb +11 -7
  18. data/lib/http/content_type.rb +12 -7
  19. data/lib/http/feature.rb +3 -1
  20. data/lib/http/features/auto_deflate.rb +6 -6
  21. data/lib/http/features/auto_inflate.rb +6 -5
  22. data/lib/http/features/instrumentation.rb +1 -1
  23. data/lib/http/features/logging.rb +19 -21
  24. data/lib/http/headers.rb +50 -13
  25. data/lib/http/mime_type/adapter.rb +3 -1
  26. data/lib/http/mime_type/json.rb +1 -0
  27. data/lib/http/options.rb +5 -8
  28. data/lib/http/redirector.rb +2 -1
  29. data/lib/http/request/body.rb +1 -0
  30. data/lib/http/request/writer.rb +3 -2
  31. data/lib/http/request.rb +13 -10
  32. data/lib/http/response/body.rb +2 -2
  33. data/lib/http/response/inflater.rb +1 -1
  34. data/lib/http/response/parser.rb +74 -62
  35. data/lib/http/response/status.rb +4 -3
  36. data/lib/http/response.rb +17 -15
  37. data/lib/http/timeout/global.rb +17 -31
  38. data/lib/http/timeout/null.rb +2 -1
  39. data/lib/http/timeout/per_operation.rb +31 -54
  40. data/lib/http/uri.rb +5 -5
  41. data/lib/http/version.rb +1 -1
  42. data/spec/lib/http/client_spec.rb +119 -30
  43. data/spec/lib/http/connection_spec.rb +8 -5
  44. data/spec/lib/http/features/auto_inflate_spec.rb +4 -2
  45. data/spec/lib/http/features/instrumentation_spec.rb +28 -21
  46. data/spec/lib/http/features/logging_spec.rb +8 -9
  47. data/spec/lib/http/headers_spec.rb +53 -18
  48. data/spec/lib/http/options/headers_spec.rb +1 -1
  49. data/spec/lib/http/options/merge_spec.rb +16 -16
  50. data/spec/lib/http/redirector_spec.rb +2 -1
  51. data/spec/lib/http/request/writer_spec.rb +13 -1
  52. data/spec/lib/http/request_spec.rb +5 -5
  53. data/spec/lib/http/response/parser_spec.rb +33 -4
  54. data/spec/lib/http/response/status_spec.rb +3 -3
  55. data/spec/lib/http/response_spec.rb +11 -22
  56. data/spec/lib/http_spec.rb +30 -3
  57. data/spec/spec_helper.rb +21 -21
  58. data/spec/support/black_hole.rb +1 -1
  59. data/spec/support/dummy_server/servlet.rb +17 -6
  60. data/spec/support/dummy_server.rb +7 -7
  61. data/spec/support/fuubar.rb +21 -0
  62. data/spec/support/http_handling_shared.rb +4 -4
  63. data/spec/support/simplecov.rb +19 -0
  64. data/spec/support/ssl_helper.rb +4 -4
  65. metadata +18 -12
  66. data/.coveralls.yml +0 -1
  67. data/.travis.yml +0 -39
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.map { |k| [k, self[k]] }.to_h
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 => {},
@@ -39,7 +39,7 @@ module HTTP
39
39
  # @param [Hash] opts
40
40
  # @option opts [Boolean] :strict (true) redirector hops policy
41
41
  # @option opts [#to_i] :max_hops (5) maximum allowed amount of hops
42
- def initialize(opts = {}) # rubocop:disable Style/OptionHash
42
+ def initialize(opts = {})
43
43
  @strict = opts.fetch(:strict, true)
44
44
  @max_hops = opts.fetch(:max_hops, 5).to_i
45
45
  end
@@ -90,6 +90,7 @@ module HTTP
90
90
 
91
91
  if UNSAFE_VERBS.include?(verb) && STRICT_SENSITIVE_CODES.include?(code)
92
92
  raise StateError, "can't follow #{@response.status} redirect" if @strict
93
+
93
94
  verb = :get
94
95
  end
95
96
 
@@ -19,6 +19,7 @@ module HTTP
19
19
  @source.bytesize
20
20
  elsif @source.respond_to?(:read)
21
21
  raise RequestError, "IO object must respond to #size" unless @source.respond_to?(:size)
22
+
22
23
  @source.size
23
24
  elsif @source.nil?
24
25
  0
@@ -108,12 +108,13 @@ module HTTP
108
108
  until data.empty?
109
109
  length = @socket.write(data)
110
110
  break unless data.bytesize > length
111
+
111
112
  data = data.byteslice(length..-1)
112
113
  end
113
114
  rescue Errno::EPIPE
114
115
  raise
115
- rescue IOError, SocketError, SystemCallError => ex
116
- raise ConnectionError, "error writing to socket: #{ex}", ex.backtrace
116
+ rescue IOError, SocketError, SystemCallError => e
117
+ raise ConnectionError, "error writing to socket: #{e}", e.backtrace
117
118
  end
118
119
  end
119
120
  end
data/lib/http/request.rb CHANGED
@@ -46,7 +46,10 @@ module HTTP
46
46
  :patch,
47
47
 
48
48
  # draft-reschke-webdav-search: WebDAV Search
49
- :search
49
+ :search,
50
+
51
+ # RFC 4791: Calendaring Extensions to WebDAV -- CalDAV
52
+ :mkcalendar
50
53
  ].freeze
51
54
 
52
55
  # Allowed schemes
@@ -54,10 +57,10 @@ module HTTP
54
57
 
55
58
  # Default ports of supported schemes
56
59
  PORTS = {
57
- :http => 80,
58
- :https => 443,
59
- :ws => 80,
60
- :wss => 443
60
+ :http => 80,
61
+ :https => 443,
62
+ :ws => 80,
63
+ :wss => 443
61
64
  }.freeze
62
65
 
63
66
  # Method is given as a lowercase symbol e.g. :get, :post
@@ -168,8 +171,8 @@ module HTTP
168
171
  # Headers to send with proxy connect request
169
172
  def proxy_connect_headers
170
173
  connect_headers = HTTP::Headers.coerce(
171
- Headers::HOST => headers[Headers::HOST],
172
- Headers::USER_AGENT => headers[Headers::USER_AGENT]
174
+ Headers::HOST => headers[Headers::HOST],
175
+ Headers::USER_AGENT => headers[Headers::USER_AGENT]
173
176
  )
174
177
 
175
178
  connect_headers[Headers::PROXY_AUTHORIZATION] = proxy_authorization_header if using_authenticated_proxy?
@@ -213,7 +216,7 @@ module HTTP
213
216
 
214
217
  # @return [String] Default host (with port if needed) header value.
215
218
  def default_host_header_value
216
- PORTS[@scheme] != port ? "#{host}:#{port}" : host
219
+ PORTS[@scheme] == port ? host : "#{host}:#{port}"
217
220
  end
218
221
 
219
222
  def prepare_body(body)
@@ -223,8 +226,8 @@ module HTTP
223
226
  def prepare_headers(headers)
224
227
  headers = HTTP::Headers.coerce(headers || {})
225
228
 
226
- headers[Headers::HOST] ||= default_host_header_value
227
- headers[Headers::USER_AGENT] ||= USER_AGENT
229
+ headers[Headers::HOST] ||= default_host_header_value
230
+ headers[Headers::USER_AGENT] ||= USER_AGENT
228
231
 
229
232
  headers
230
233
  end
@@ -27,8 +27,7 @@ module HTTP
27
27
  # (see HTTP::Client#readpartial)
28
28
  def readpartial(*args)
29
29
  stream!
30
- chunk = @stream.readpartial(*args)
31
- chunk.force_encoding(@encoding) if chunk
30
+ @stream.readpartial(*args)&.force_encoding(@encoding)
32
31
  end
33
32
 
34
33
  # Iterate over the body, allowing it to be enumerable
@@ -64,6 +63,7 @@ module HTTP
64
63
  # Assert that the body is actively being streamed
65
64
  def stream!
66
65
  raise StateError, "body has already been consumed" if @streaming == false
66
+
67
67
  @streaming = true
68
68
  end
69
69
 
@@ -16,7 +16,7 @@ module HTTP
16
16
  if chunk
17
17
  chunk = zstream.inflate(chunk)
18
18
  elsif !zstream.closed?
19
- zstream.finish
19
+ zstream.finish if zstream.total_in.positive?
20
20
  zstream.close
21
21
  end
22
22
  chunk
@@ -1,69 +1,63 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "http-parser"
3
+ require "llhttp"
4
4
 
5
5
  module HTTP
6
6
  class Response
7
7
  # @api private
8
- #
9
- # NOTE(ixti): This class is a subject of future refactoring, thus don't
10
- # expect this class API to be stable until this message disappears and
11
- # class is not marked as private anymore.
12
8
  class Parser
13
- attr_reader :headers
9
+ attr_reader :parser, :headers, :status_code, :http_version
14
10
 
15
11
  def initialize
16
- @state = HttpParser::Parser.new_instance { |i| i.type = :response }
17
- @parser = HttpParser::Parser.new(self)
18
-
12
+ @handler = Handler.new(self)
13
+ @parser = LLHttp::Parser.new(@handler, :type => :response)
19
14
  reset
20
15
  end
21
16
 
22
- # @return [self]
23
- def add(data)
24
- # XXX(ixti): API doc of HttpParser::Parser is misleading, it says that
25
- # it returns boolean true if data was parsed successfully, but instead
26
- # it's response tells if there was an error; So when it's `true` that
27
- # means parse failed, and `false` means parse was successful.
28
- # case of success.
29
- return self unless @parser.parse(@state, data)
30
-
31
- raise IOError, "Could not parse data"
17
+ def reset
18
+ @parser.finish
19
+ @handler.reset
20
+ @header_finished = false
21
+ @message_finished = false
22
+ @headers = Headers.new
23
+ @chunk = nil
24
+ @status_code = nil
25
+ @http_version = nil
32
26
  end
33
- alias << add
34
27
 
35
- def headers?
36
- @finished[:headers]
37
- end
28
+ def add(data)
29
+ parser << data
38
30
 
39
- def http_version
40
- @state.http_version
31
+ self
32
+ rescue LLHttp::Error => e
33
+ raise IOError, e.message
41
34
  end
42
35
 
43
- def status_code
44
- @state.http_status
36
+ alias << add
37
+
38
+ def mark_header_finished
39
+ @header_finished = true
40
+ @status_code = @parser.status_code
41
+ @http_version = "#{@parser.http_major}.#{@parser.http_minor}"
45
42
  end
46
43
 
47
- #
48
- # HTTP::Parser callbacks
49
- #
44
+ def headers?
45
+ @header_finished
46
+ end
50
47
 
51
- def on_header_field(_response, field)
52
- append_header if @reading_header_value
53
- @field << field
48
+ def add_header(name, value)
49
+ @headers.add(name, value)
54
50
  end
55
51
 
56
- def on_header_value(_response, value)
57
- @reading_header_value = true
58
- @field_value << value
52
+ def mark_message_finished
53
+ @message_finished = true
59
54
  end
60
55
 
61
- def on_headers_complete(_reposse)
62
- append_header if @reading_header_value
63
- @finished[:headers] = true
56
+ def finished?
57
+ @message_finished
64
58
  end
65
59
 
66
- def on_body(_response, chunk)
60
+ def add_body(chunk)
67
61
  if @chunk
68
62
  @chunk << chunk
69
63
  else
@@ -85,32 +79,50 @@ module HTTP
85
79
  chunk
86
80
  end
87
81
 
88
- def on_message_complete(_response)
89
- @finished[:message] = true
90
- end
82
+ class Handler < LLHttp::Delegate
83
+ def initialize(target)
84
+ @target = target
85
+ super()
86
+ reset
87
+ end
91
88
 
92
- def reset
93
- @state.reset!
94
-
95
- @finished = Hash.new(false)
96
- @headers = HTTP::Headers.new
97
- @reading_header_value = false
98
- @field = +""
99
- @field_value = +""
100
- @chunk = nil
101
- end
89
+ def reset
90
+ @reading_header_value = false
91
+ @field_value = +""
92
+ @field = +""
93
+ end
102
94
 
103
- def finished?
104
- @finished[:message]
105
- end
95
+ def on_header_field(field)
96
+ append_header if @reading_header_value
97
+ @field << field
98
+ end
99
+
100
+ def on_header_value(value)
101
+ @reading_header_value = true
102
+ @field_value << value
103
+ end
106
104
 
107
- private
105
+ def on_headers_complete
106
+ append_header if @reading_header_value
107
+ @target.mark_header_finished
108
+ end
109
+
110
+ def on_body(body)
111
+ @target.add_body(body)
112
+ end
108
113
 
109
- def append_header
110
- @headers.add(@field, @field_value)
111
- @reading_header_value = false
112
- @field_value = +""
113
- @field = +""
114
+ def on_message_complete
115
+ @target.mark_message_finished
116
+ end
117
+
118
+ private
119
+
120
+ def append_header
121
+ @target.add_header(@field, @field_value)
122
+ @reading_header_value = false
123
+ @field_value = +""
124
+ @field = +""
125
+ end
114
126
  end
115
127
  end
116
128
  end
@@ -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
@@ -132,7 +132,7 @@ module HTTP
132
132
  end
133
133
 
134
134
  SYMBOLS.each do |code, symbol|
135
- class_eval <<-RUBY, __FILE__, __LINE__
135
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
136
136
  def #{symbol}? # def bad_request?
137
137
  #{code} == code # 400 == code
138
138
  end # end
@@ -141,6 +141,7 @@ module HTTP
141
141
 
142
142
  def __setobj__(obj)
143
143
  raise TypeError, "Expected #{obj.inspect} to respond to #to_i" unless obj.respond_to? :to_i
144
+
144
145
  @code = obj.to_i
145
146
  end
146
147
 
data/lib/http/response.rb CHANGED
@@ -7,7 +7,6 @@ require "http/content_type"
7
7
  require "http/mime_type"
8
8
  require "http/response/status"
9
9
  require "http/response/inflater"
10
- require "http/uri"
11
10
  require "http/cookie_jar"
12
11
  require "time"
13
12
 
@@ -26,8 +25,8 @@ module HTTP
26
25
  # @return [Body]
27
26
  attr_reader :body
28
27
 
29
- # @return [URI, nil]
30
- attr_reader :uri
28
+ # @return [Request]
29
+ attr_reader :request
31
30
 
32
31
  # @return [Hash]
33
32
  attr_reader :proxy_headers
@@ -41,10 +40,10 @@ module HTTP
41
40
  # @option opts [HTTP::Connection] :connection
42
41
  # @option opts [String] :encoding Encoding to use when reading body
43
42
  # @option opts [String] :body
44
- # @option opts [String] :uri
43
+ # @option opts [HTTP::Request] request
45
44
  def initialize(opts)
46
45
  @version = opts.fetch(:version)
47
- @uri = HTTP::URI.parse(opts.fetch(:uri)) if opts.include? :uri
46
+ @request = opts.fetch(:request)
48
47
  @status = HTTP::Response::Status.new(opts.fetch(:status))
49
48
  @headers = HTTP::Headers.coerce(opts[:headers] || {})
50
49
  @proxy_headers = HTTP::Headers.coerce(opts[:proxy_headers] || {})
@@ -61,24 +60,28 @@ module HTTP
61
60
 
62
61
  # @!method reason
63
62
  # @return (see HTTP::Response::Status#reason)
64
- def_delegator :status, :reason
63
+ def_delegator :@status, :reason
65
64
 
66
65
  # @!method code
67
66
  # @return (see HTTP::Response::Status#code)
68
- def_delegator :status, :code
67
+ def_delegator :@status, :code
69
68
 
70
69
  # @!method to_s
71
70
  # (see HTTP::Response::Body#to_s)
72
- def_delegator :body, :to_s
71
+ def_delegator :@body, :to_s
73
72
  alias to_str to_s
74
73
 
75
74
  # @!method readpartial
76
75
  # (see HTTP::Response::Body#readpartial)
77
- def_delegator :body, :readpartial
76
+ def_delegator :@body, :readpartial
78
77
 
79
78
  # @!method connection
80
79
  # (see HTTP::Response::Body#connection)
81
- def_delegator :body, :connection
80
+ def_delegator :@body, :connection
81
+
82
+ # @!method uri
83
+ # @return (see HTTP::Request#uri)
84
+ def_delegator :@request, :uri
82
85
 
83
86
  # Returns an Array ala Rack: `[status, headers, body]`
84
87
  #
@@ -150,12 +153,11 @@ module HTTP
150
153
 
151
154
  # Parse response body with corresponding MIME type adapter.
152
155
  #
153
- # @param [#to_s] as Parse as given MIME type
154
- # instead of the one determined from headers
155
- # @raise [HTTP::Error] if adapter not found
156
+ # @param type [#to_s] Parse as given MIME type.
157
+ # @raise (see MimeType.[])
156
158
  # @return [Object]
157
- def parse(as = nil)
158
- MimeType[as || mime_type].decode to_s
159
+ def parse(type)
160
+ MimeType[type].decode to_s
159
161
  end
160
162
 
161
163
  # Inspect a response