http 2.2.2 → 3.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +46 -13
- data/.travis.yml +17 -12
- data/CHANGES.md +25 -1
- data/Gemfile +11 -4
- data/Guardfile +2 -0
- data/README.md +4 -5
- data/Rakefile +14 -13
- data/http.gemspec +3 -1
- data/lib/http.rb +1 -0
- data/lib/http/chainable.rb +15 -14
- data/lib/http/client.rb +27 -24
- data/lib/http/connection.rb +6 -4
- data/lib/http/content_type.rb +1 -0
- data/lib/http/errors.rb +3 -2
- data/lib/http/feature.rb +2 -1
- data/lib/http/features/auto_deflate.rb +77 -20
- data/lib/http/features/auto_inflate.rb +2 -1
- data/lib/http/headers.rb +3 -2
- data/lib/http/headers/known.rb +23 -22
- data/lib/http/headers/mixin.rb +1 -0
- data/lib/http/mime_type.rb +1 -0
- data/lib/http/mime_type/adapter.rb +2 -1
- data/lib/http/mime_type/json.rb +2 -1
- data/lib/http/options.rb +15 -12
- data/lib/http/redirector.rb +4 -3
- data/lib/http/request.rb +25 -10
- data/lib/http/request/body.rb +67 -0
- data/lib/http/request/writer.rb +32 -37
- data/lib/http/response.rb +17 -2
- data/lib/http/response/body.rb +16 -12
- data/lib/http/response/parser.rb +1 -0
- data/lib/http/response/status.rb +1 -0
- data/lib/http/response/status/reasons.rb +1 -0
- data/lib/http/timeout/global.rb +1 -0
- data/lib/http/timeout/null.rb +2 -1
- data/lib/http/timeout/per_operation.rb +19 -6
- data/lib/http/uri.rb +8 -2
- data/lib/http/version.rb +1 -1
- data/spec/lib/http/client_spec.rb +104 -4
- data/spec/lib/http/content_type_spec.rb +1 -0
- data/spec/lib/http/features/auto_deflate_spec.rb +32 -64
- data/spec/lib/http/features/auto_inflate_spec.rb +1 -0
- data/spec/lib/http/headers/mixin_spec.rb +1 -0
- data/spec/lib/http/headers_spec.rb +36 -35
- data/spec/lib/http/options/body_spec.rb +1 -0
- data/spec/lib/http/options/features_spec.rb +1 -0
- data/spec/lib/http/options/form_spec.rb +1 -0
- data/spec/lib/http/options/headers_spec.rb +2 -1
- data/spec/lib/http/options/json_spec.rb +1 -0
- data/spec/lib/http/options/new_spec.rb +2 -1
- data/spec/lib/http/options/proxy_spec.rb +1 -0
- data/spec/lib/http/options_spec.rb +1 -0
- data/spec/lib/http/redirector_spec.rb +1 -0
- data/spec/lib/http/request/body_spec.rb +138 -0
- data/spec/lib/http/request/writer_spec.rb +44 -74
- data/spec/lib/http/request_spec.rb +14 -0
- data/spec/lib/http/response/body_spec.rb +20 -4
- data/spec/lib/http/response/status_spec.rb +27 -26
- data/spec/lib/http/response_spec.rb +10 -0
- data/spec/lib/http/uri_spec.rb +11 -0
- data/spec/lib/http_spec.rb +18 -6
- data/spec/regression_specs.rb +1 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/black_hole.rb +9 -2
- data/spec/support/capture_warning.rb +1 -0
- data/spec/support/dummy_server.rb +2 -1
- data/spec/support/dummy_server/servlet.rb +1 -1
- data/spec/support/fakeio.rb +21 -0
- data/spec/support/http_handling_shared.rb +1 -0
- data/spec/support/proxy_server.rb +1 -0
- data/spec/support/servers/config.rb +1 -0
- data/spec/support/servers/runner.rb +1 -0
- data/spec/support/ssl_helper.rb +3 -2
- metadata +20 -9
data/lib/http/options.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/ClassLength, Style/RedundantSelf
|
4
|
+
|
2
5
|
require "http/headers"
|
3
6
|
require "openssl"
|
4
7
|
require "socket"
|
@@ -8,7 +11,6 @@ require "http/features/auto_inflate"
|
|
8
11
|
require "http/features/auto_deflate"
|
9
12
|
|
10
13
|
module HTTP
|
11
|
-
# rubocop:disable Metrics/ClassLength
|
12
14
|
class Options
|
13
15
|
@default_socket_class = TCPSocket
|
14
16
|
@default_ssl_socket_class = OpenSSL::SSL::SSLSocket
|
@@ -22,7 +24,7 @@ module HTTP
|
|
22
24
|
attr_accessor :default_socket_class, :default_ssl_socket_class, :default_timeout_class
|
23
25
|
attr_reader :available_features
|
24
26
|
|
25
|
-
def new(options = {})
|
27
|
+
def new(options = {}) # rubocop:disable Style/OptionHash
|
26
28
|
return options if options.is_a?(self)
|
27
29
|
super
|
28
30
|
end
|
@@ -46,7 +48,7 @@ module HTTP
|
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
def initialize(options = {})
|
51
|
+
def initialize(options = {}) # rubocop:disable Style/OptionHash
|
50
52
|
defaults = {
|
51
53
|
:response => :auto,
|
52
54
|
:proxy => {},
|
@@ -115,21 +117,22 @@ module HTTP
|
|
115
117
|
end
|
116
118
|
end
|
117
119
|
|
118
|
-
%w
|
120
|
+
%w[
|
119
121
|
proxy params form json body follow response
|
120
122
|
socket_class nodelay ssl_socket_class ssl_context ssl
|
121
123
|
persistent keep_alive_timeout timeout_class timeout_options
|
122
|
-
|
124
|
+
].each do |method_name|
|
123
125
|
def_option method_name
|
124
126
|
end
|
125
127
|
|
126
128
|
def follow=(value)
|
127
|
-
@follow =
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
129
|
+
@follow =
|
130
|
+
case
|
131
|
+
when !value then nil
|
132
|
+
when true == value then {}
|
133
|
+
when value.respond_to?(:fetch) then value
|
134
|
+
else argument_error! "Unsupported follow options: #{value}"
|
135
|
+
end
|
133
136
|
end
|
134
137
|
|
135
138
|
def persistent=(value)
|
@@ -182,7 +185,7 @@ module HTTP
|
|
182
185
|
private
|
183
186
|
|
184
187
|
def argument_error!(message)
|
185
|
-
raise(Error, message, caller
|
188
|
+
raise(Error, message, caller(1..-1))
|
186
189
|
end
|
187
190
|
end
|
188
191
|
end
|
data/lib/http/redirector.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "set"
|
3
4
|
|
4
5
|
require "http/headers"
|
@@ -20,10 +21,10 @@ module HTTP
|
|
20
21
|
|
21
22
|
# Insecure http verbs, which should trigger StateError in strict mode
|
22
23
|
# upon {STRICT_SENSITIVE_CODES}
|
23
|
-
UNSAFE_VERBS = [
|
24
|
+
UNSAFE_VERBS = %i[put delete post].to_set.freeze
|
24
25
|
|
25
26
|
# Verbs which will remain unchanged upon See Other response.
|
26
|
-
SEE_OTHER_ALLOWED_VERBS = [
|
27
|
+
SEE_OTHER_ALLOWED_VERBS = %i[get head].to_set.freeze
|
27
28
|
|
28
29
|
# @!attribute [r] strict
|
29
30
|
# Returns redirector policy.
|
@@ -38,7 +39,7 @@ module HTTP
|
|
38
39
|
# @param [Hash] opts
|
39
40
|
# @option opts [Boolean] :strict (true) redirector hops policy
|
40
41
|
# @option opts [#to_i] :max_hops (5) maximum allowed amount of hops
|
41
|
-
def initialize(opts = {})
|
42
|
+
def initialize(opts = {}) # rubocop:disable Style/OptionHash
|
42
43
|
@strict = opts.fetch(:strict, true)
|
43
44
|
@max_hops = opts.fetch(:max_hops, 5).to_i
|
44
45
|
end
|
data/lib/http/request.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "forwardable"
|
3
4
|
require "base64"
|
4
5
|
require "time"
|
5
6
|
|
6
7
|
require "http/errors"
|
7
8
|
require "http/headers"
|
9
|
+
require "http/request/body"
|
8
10
|
require "http/request/writer"
|
9
11
|
require "http/version"
|
10
12
|
require "http/uri"
|
@@ -22,7 +24,7 @@ module HTTP
|
|
22
24
|
class UnsupportedSchemeError < RequestError; end
|
23
25
|
|
24
26
|
# Default User-Agent header value
|
25
|
-
USER_AGENT = "http.rb/#{HTTP::VERSION}"
|
27
|
+
USER_AGENT = "http.rb/#{HTTP::VERSION}"
|
26
28
|
|
27
29
|
METHODS = [
|
28
30
|
# RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1
|
@@ -48,7 +50,7 @@ module HTTP
|
|
48
50
|
].freeze
|
49
51
|
|
50
52
|
# Allowed schemes
|
51
|
-
SCHEMES = [
|
53
|
+
SCHEMES = %i[http https ws wss].freeze
|
52
54
|
|
53
55
|
# Default ports of supported schemes
|
54
56
|
PORTS = {
|
@@ -74,7 +76,7 @@ module HTTP
|
|
74
76
|
# @option opts [HTTP::URI, #to_s] :uri
|
75
77
|
# @option opts [Hash] :headers
|
76
78
|
# @option opts [Hash] :proxy
|
77
|
-
# @option opts [String] :body
|
79
|
+
# @option opts [String, Enumerable, IO, nil] :body
|
78
80
|
def initialize(opts)
|
79
81
|
@verb = opts.fetch(:verb).to_s.downcase.to_sym
|
80
82
|
@uri = normalize_uri(opts.fetch(:uri))
|
@@ -84,7 +86,7 @@ module HTTP
|
|
84
86
|
raise(UnsupportedSchemeError, "unknown scheme: #{scheme}") unless SCHEMES.include?(@scheme)
|
85
87
|
|
86
88
|
@proxy = opts[:proxy] || {}
|
87
|
-
@body = opts[:body]
|
89
|
+
@body = request_body(opts[:body], opts)
|
88
90
|
@version = opts[:version] || "1.1"
|
89
91
|
@headers = HTTP::Headers.coerce(opts[:headers] || {})
|
90
92
|
|
@@ -94,7 +96,10 @@ module HTTP
|
|
94
96
|
|
95
97
|
# Returns new Request with updated uri
|
96
98
|
def redirect(uri, verb = @verb)
|
97
|
-
|
99
|
+
headers = self.headers.dup
|
100
|
+
headers.delete(Headers::HOST)
|
101
|
+
|
102
|
+
self.class.new(
|
98
103
|
:verb => verb,
|
99
104
|
:uri => @uri.join(uri),
|
100
105
|
:headers => headers,
|
@@ -102,9 +107,6 @@ module HTTP
|
|
102
107
|
:body => body,
|
103
108
|
:version => version
|
104
109
|
)
|
105
|
-
|
106
|
-
req[Headers::HOST] = req.uri.host
|
107
|
-
req
|
108
110
|
end
|
109
111
|
|
110
112
|
# Stream the request to a socket
|
@@ -145,8 +147,14 @@ module HTTP
|
|
145
147
|
|
146
148
|
# Compute HTTP request header for direct or proxy request
|
147
149
|
def headline
|
148
|
-
request_uri =
|
149
|
-
|
150
|
+
request_uri =
|
151
|
+
if using_proxy? && !uri.https?
|
152
|
+
uri.omit(:fragment)
|
153
|
+
else
|
154
|
+
uri.omit(:scheme, :authority, :fragment)
|
155
|
+
end
|
156
|
+
|
157
|
+
"#{verb.to_s.upcase} #{request_uri} HTTP/#{version}"
|
150
158
|
end
|
151
159
|
|
152
160
|
# Compute HTTP request header SSL proxy connection
|
@@ -178,6 +186,13 @@ module HTTP
|
|
178
186
|
|
179
187
|
private
|
180
188
|
|
189
|
+
# Transforms body to an object suitable for streaming.
|
190
|
+
def request_body(body, opts)
|
191
|
+
body = Request::Body.new(body) unless body.is_a?(Request::Body)
|
192
|
+
body = opts[:auto_deflate].deflated_body(body) if opts[:auto_deflate]
|
193
|
+
body
|
194
|
+
end
|
195
|
+
|
181
196
|
# @!attribute [r] host
|
182
197
|
# @return [String]
|
183
198
|
def_delegator :@uri, :host
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTP
|
4
|
+
class Request
|
5
|
+
class Body
|
6
|
+
def initialize(body)
|
7
|
+
@body = body
|
8
|
+
|
9
|
+
validate_body_type!
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns size which should be used for the "Content-Length" header.
|
13
|
+
#
|
14
|
+
# @return [Integer]
|
15
|
+
def size
|
16
|
+
if @body.is_a?(String)
|
17
|
+
@body.bytesize
|
18
|
+
elsif @body.respond_to?(:read)
|
19
|
+
raise RequestError, "IO object must respond to #size" unless @body.respond_to?(:size)
|
20
|
+
@body.size
|
21
|
+
elsif @body.nil?
|
22
|
+
0
|
23
|
+
else
|
24
|
+
raise RequestError, "cannot determine size of body: #{@body.inspect}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Yields chunks of content to be streamed to the request body.
|
29
|
+
#
|
30
|
+
# @yieldparam [String]
|
31
|
+
def each(&block)
|
32
|
+
if @body.is_a?(String)
|
33
|
+
yield @body
|
34
|
+
elsif @body.respond_to?(:read)
|
35
|
+
IO.copy_stream(@body, ProcIO.new(block))
|
36
|
+
elsif @body.is_a?(Enumerable)
|
37
|
+
@body.each(&block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def validate_body_type!
|
44
|
+
return if @body.is_a?(String)
|
45
|
+
return if @body.respond_to?(:read)
|
46
|
+
return if @body.is_a?(Enumerable)
|
47
|
+
return if @body.nil?
|
48
|
+
|
49
|
+
raise RequestError, "body of wrong type: #{@body.class}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# This class provides a "writable IO" wrapper around a proc object, with
|
53
|
+
# #write simply calling the proc, which we can pass in as the
|
54
|
+
# "destination IO" in IO.copy_stream.
|
55
|
+
class ProcIO
|
56
|
+
def initialize(block)
|
57
|
+
@block = block
|
58
|
+
end
|
59
|
+
|
60
|
+
def write(data)
|
61
|
+
@block.call(data)
|
62
|
+
data.bytesize
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/http/request/writer.rb
CHANGED
@@ -1,31 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "http/headers"
|
3
4
|
|
4
5
|
module HTTP
|
5
6
|
class Request
|
6
7
|
class Writer
|
7
8
|
# CRLF is the universal HTTP delimiter
|
8
|
-
CRLF = "\r\n"
|
9
|
+
CRLF = "\r\n"
|
9
10
|
|
10
11
|
# Chunked data termintaor.
|
11
|
-
ZERO = "0"
|
12
|
+
ZERO = "0"
|
12
13
|
|
13
14
|
# Chunked transfer encoding
|
14
|
-
CHUNKED = "chunked"
|
15
|
+
CHUNKED = "chunked"
|
15
16
|
|
16
17
|
# End of a chunked transfer
|
17
|
-
CHUNKED_END = "#{ZERO}#{CRLF}#{CRLF}"
|
18
|
-
|
19
|
-
# Types valid to be used as body source
|
20
|
-
VALID_BODY_TYPES = [String, NilClass, Enumerable].freeze
|
18
|
+
CHUNKED_END = "#{ZERO}#{CRLF}#{CRLF}"
|
21
19
|
|
22
20
|
def initialize(socket, body, headers, headline)
|
23
21
|
@body = body
|
24
22
|
@socket = socket
|
25
23
|
@headers = headers
|
26
24
|
@request_header = [headline]
|
27
|
-
|
28
|
-
validate_body_type!
|
29
25
|
end
|
30
26
|
|
31
27
|
# Adds headers to the request header from the headers array
|
@@ -51,13 +47,9 @@ module HTTP
|
|
51
47
|
# Adds the headers to the header array for the given request body we are working
|
52
48
|
# with
|
53
49
|
def add_body_type_headers
|
54
|
-
if @
|
55
|
-
|
56
|
-
|
57
|
-
@request_header << "#{Headers::CONTENT_LENGTH}: 0"
|
58
|
-
elsif @body.is_a?(Enumerable) && CHUNKED != @headers[Headers::TRANSFER_ENCODING]
|
59
|
-
raise(RequestError, "invalid transfer encoding")
|
60
|
-
end
|
50
|
+
return if @headers[Headers::CONTENT_LENGTH] || chunked?
|
51
|
+
|
52
|
+
@request_header << "#{Headers::CONTENT_LENGTH}: #{@body.size}"
|
61
53
|
end
|
62
54
|
|
63
55
|
# Joins the headers specified in the request into a correctly formatted
|
@@ -69,29 +61,37 @@ module HTTP
|
|
69
61
|
end
|
70
62
|
|
71
63
|
def send_request
|
72
|
-
headers = join_headers
|
73
|
-
|
74
64
|
# It's important to send the request in a single write call when
|
75
65
|
# possible in order to play nicely with Nagle's algorithm. Making
|
76
66
|
# two writes in a row triggers a pathological case where Nagle is
|
77
67
|
# expecting a third write that never happens.
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
write(
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
68
|
+
data = join_headers
|
69
|
+
|
70
|
+
@body.each do |chunk|
|
71
|
+
data << encode_chunk(chunk)
|
72
|
+
write(data)
|
73
|
+
data.clear
|
74
|
+
end
|
75
|
+
|
76
|
+
write(data) unless data.empty?
|
77
|
+
|
78
|
+
write(CHUNKED_END) if chunked?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the chunk encoded for to the specified "Transfer-Encoding" header.
|
82
|
+
def encode_chunk(chunk)
|
83
|
+
if chunked?
|
84
|
+
chunk.bytesize.to_s(16) << CRLF << chunk << CRLF
|
85
|
+
else
|
86
|
+
chunk
|
92
87
|
end
|
93
88
|
end
|
94
89
|
|
90
|
+
# Returns true if the request should be sent in chunked encoding.
|
91
|
+
def chunked?
|
92
|
+
@headers[Headers::TRANSFER_ENCODING] == CHUNKED
|
93
|
+
end
|
94
|
+
|
95
95
|
private
|
96
96
|
|
97
97
|
def write(data)
|
@@ -101,11 +101,6 @@ module HTTP
|
|
101
101
|
data = data.byteslice(length..-1)
|
102
102
|
end
|
103
103
|
end
|
104
|
-
|
105
|
-
def validate_body_type!
|
106
|
-
return if VALID_BODY_TYPES.any? { |type| @body.is_a? type }
|
107
|
-
raise RequestError, "body of wrong type: #{@body.class}"
|
108
|
-
end
|
109
104
|
end
|
110
105
|
end
|
111
106
|
end
|
data/lib/http/response.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "forwardable"
|
3
4
|
|
4
5
|
require "http/headers"
|
@@ -50,7 +51,7 @@ module HTTP
|
|
50
51
|
encoding = opts[:encoding] || charset || Encoding::BINARY
|
51
52
|
stream = body_stream_for(connection, opts)
|
52
53
|
|
53
|
-
@body = Response::Body.new(stream, encoding)
|
54
|
+
@body = Response::Body.new(stream, :encoding => encoding)
|
54
55
|
else
|
55
56
|
@body = opts.fetch(:body)
|
56
57
|
end
|
@@ -98,8 +99,13 @@ module HTTP
|
|
98
99
|
# (not an integer, e.g. empty string or string with non-digits).
|
99
100
|
# @return [Integer] otherwise
|
100
101
|
def content_length
|
102
|
+
# http://greenbytes.de/tech/webdav/rfc7230.html#rfc.section.3.3.3
|
103
|
+
# Clause 3: "If a message is received with both a Transfer-Encoding
|
104
|
+
# and a Content-Length header field, the Transfer-Encoding overrides the Content-Length.
|
105
|
+
return nil if @headers.include?(Headers::TRANSFER_ENCODING)
|
106
|
+
|
101
107
|
value = @headers[Headers::CONTENT_LENGTH]
|
102
|
-
return unless value
|
108
|
+
return nil unless value
|
103
109
|
|
104
110
|
begin
|
105
111
|
Integer(value)
|
@@ -131,6 +137,15 @@ module HTTP
|
|
131
137
|
end
|
132
138
|
end
|
133
139
|
|
140
|
+
def chunked?
|
141
|
+
return false unless @headers.include?(Headers::TRANSFER_ENCODING)
|
142
|
+
|
143
|
+
encoding = @headers.get(Headers::TRANSFER_ENCODING)
|
144
|
+
|
145
|
+
# TODO: "chunked" is frozen in the request writer. How about making it accessible?
|
146
|
+
encoding.last == "chunked"
|
147
|
+
end
|
148
|
+
|
134
149
|
# Parse response body with corresponding MIME type adapter.
|
135
150
|
#
|
136
151
|
# @param [#to_s] as Parse as given MIME type
|
data/lib/http/response/body.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "forwardable"
|
3
4
|
require "http/client"
|
4
5
|
|
@@ -15,18 +16,19 @@ module HTTP
|
|
15
16
|
# @return [HTTP::Connection]
|
16
17
|
attr_reader :connection
|
17
18
|
|
18
|
-
def initialize(stream, encoding
|
19
|
+
def initialize(stream, encoding: Encoding::BINARY)
|
19
20
|
@stream = stream
|
20
21
|
@connection = stream.is_a?(Inflater) ? stream.connection : stream
|
21
22
|
@streaming = nil
|
22
23
|
@contents = nil
|
23
|
-
@encoding = encoding
|
24
|
+
@encoding = find_encoding(encoding)
|
24
25
|
end
|
25
26
|
|
26
27
|
# (see HTTP::Client#readpartial)
|
27
28
|
def readpartial(*args)
|
28
29
|
stream!
|
29
|
-
@stream.readpartial(*args)
|
30
|
+
chunk = @stream.readpartial(*args)
|
31
|
+
chunk.force_encoding(@encoding) if chunk
|
30
32
|
end
|
31
33
|
|
32
34
|
# Iterate over the body, allowing it to be enumerable
|
@@ -42,19 +44,12 @@ module HTTP
|
|
42
44
|
|
43
45
|
raise StateError, "body is being streamed" unless @streaming.nil?
|
44
46
|
|
45
|
-
# see issue 312
|
46
|
-
begin
|
47
|
-
encoding = Encoding.find @encoding
|
48
|
-
rescue ArgumentError
|
49
|
-
encoding = Encoding::BINARY
|
50
|
-
end
|
51
|
-
|
52
47
|
begin
|
53
48
|
@streaming = false
|
54
|
-
@contents = String.new("").force_encoding(encoding)
|
49
|
+
@contents = String.new("").force_encoding(@encoding)
|
55
50
|
|
56
51
|
while (chunk = @stream.readpartial)
|
57
|
-
@contents << chunk.force_encoding(encoding)
|
52
|
+
@contents << chunk.force_encoding(@encoding)
|
58
53
|
end
|
59
54
|
rescue
|
60
55
|
@contents = nil
|
@@ -75,6 +70,15 @@ module HTTP
|
|
75
70
|
def inspect
|
76
71
|
"#<#{self.class}:#{object_id.to_s(16)} @streaming=#{!!@streaming}>"
|
77
72
|
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Retrieve encoding by name. If encoding cannot be found, default to binary.
|
77
|
+
def find_encoding(encoding)
|
78
|
+
Encoding.find encoding
|
79
|
+
rescue ArgumentError
|
80
|
+
Encoding::BINARY
|
81
|
+
end
|
78
82
|
end
|
79
83
|
end
|
80
84
|
end
|