net-http 0.9.0 → 0.10.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b248ee1b72f192c00aeaa7667d98f0e1b7c8589ca0715e074c7134ecaf79b20
4
- data.tar.gz: e275a50ec3e743c3c32a89321a31589833c943a243a24196d4a9494e299badca
3
+ metadata.gz: 7ef5db5bbe7d235bad14b04a257948207798e176d4f1d03029801615aa26823c
4
+ data.tar.gz: 1677b07e53a693f5d784a39e4d76a447868b5d435f3269561ccc772dd0345d10
5
5
  SHA512:
6
- metadata.gz: d845907907e467c35bedf95eb9854fa15344f717a9a499495cdd403efdb71be40f33eaaeca01c3f949b491bd57abd03beaf1d8a75a4dd241509927596dd9d59b
7
- data.tar.gz: b28a46915ac4e105b83d7de85a13707b9ed938771d41dccf7bb16019a3718b141f15974d4d413f7664fdf64c0b033566ba6666755843efe43bee1a6dfa9a581a
6
+ metadata.gz: fd9e8e4ec27ff431f0a36acde5d6caecdebf34c5a625a6023b02bade546e7133bb6b12b3a0594d6cb3c5824577e093ef8677a209b9b35a2ee727b5b1906bf77e
7
+ data.tar.gz: b04a6747ae31dedd05716b221f9c9a6e668587cd025094f91f4b7c487feb85d769d4b8ff0d22ee55a79b96755324e447094579bf2306fc3acb363bb547e38b3d
data/README.md CHANGED
@@ -81,6 +81,8 @@ res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50')
81
81
  puts res.body
82
82
  ```
83
83
 
84
+ See the [documentation](https://docs.ruby-lang.org/en/master/Net/HTTP.html) for more details.
85
+
84
86
  ## Development
85
87
 
86
88
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -336,6 +336,9 @@ class Net::HTTPGenericRequest
336
336
  boundary = opt[:boundary]
337
337
  require 'securerandom' unless defined?(SecureRandom)
338
338
  boundary ||= SecureRandom.urlsafe_base64(40)
339
+ if /[\r\n]/.match?(boundary.to_s)
340
+ raise ArgumentError, "multipart boundary cannot include CR/LF"
341
+ end
339
342
  chunked_p = chunked?
340
343
 
341
344
  buf = +''
@@ -349,7 +352,10 @@ class Net::HTTPGenericRequest
349
352
  buf << "--#{boundary}\r\n"
350
353
  if filename
351
354
  filename = quote_string(filename, charset)
352
- type = h[:content_type] || 'application/octet-stream'
355
+ type = (h[:content_type] || 'application/octet-stream').to_s
356
+ if /[\r\n]/.match?(type)
357
+ raise ArgumentError, "field content type cannot include CR/LF"
358
+ end
353
359
  buf << "Content-Disposition: form-data; " \
354
360
  "name=\"#{key}\"; filename=\"#{filename}\"\r\n" \
355
361
  "Content-Type: #{type}\r\n\r\n"
@@ -384,6 +390,9 @@ class Net::HTTPGenericRequest
384
390
 
385
391
  def quote_string(str, charset)
386
392
  str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset
393
+ if /[\r\n]/.match?(str)
394
+ raise ArgumentError, "multipart field name or filename cannot include CR/LF"
395
+ end
387
396
  str.gsub(/[\\"]/, '\\\\\&')
388
397
  end
389
398
 
@@ -193,11 +193,9 @@ module Net::HTTPHeader
193
193
  warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE
194
194
  else
195
195
  value = value.strip # raise error for invalid byte sequences
196
- if key.to_s.bytesize > MAX_KEY_LENGTH
197
- raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..."
198
- end
196
+ validate_field_name(key)
199
197
  if value.to_s.bytesize > MAX_FIELD_LENGTH
200
- raise ArgumentError, "header #{key} has too long field value: #{value.bytesize}"
198
+ raise ArgumentError, "header #{key} has too long field value: #{value.bytesize} bytes (limit #{MAX_FIELD_LENGTH})"
201
199
  end
202
200
  if value.count("\r\n") > 0
203
201
  raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF"
@@ -263,21 +261,34 @@ module Net::HTTPHeader
263
261
  def add_field(key, val)
264
262
  stringified_downcased_key = key.downcase.to_s
265
263
  if @header.key?(stringified_downcased_key)
266
- append_field_value(@header[stringified_downcased_key], val)
264
+ append_field_value(@header[stringified_downcased_key], val, key)
267
265
  else
268
266
  set_field(key, val)
269
267
  end
270
268
  end
271
269
 
272
270
  # :stopdoc:
271
+ private def validate_field_name(key)
272
+ if key.to_s.bytesize > MAX_KEY_LENGTH
273
+ raise ArgumentError, "too long (#{key.to_s.bytesize} bytes) header: #{key.to_s[0, 30].inspect}..."
274
+ end
275
+ if /[\x00-\x1f\x7f:]/n.match?(key.to_s.b)
276
+ raise ArgumentError, "header field name cannot include control characters or colon: #{key.to_s[0, 30].inspect}"
277
+ end
278
+ end
279
+
273
280
  private def set_field(key, val)
281
+ validate_field_name(key)
274
282
  case val
275
283
  when Enumerable
276
284
  ary = []
277
- append_field_value(ary, val)
285
+ append_field_value(ary, val, key)
278
286
  @header[key.downcase.to_s] = ary
279
287
  else
280
288
  val = val.to_s # for compatibility use to_s instead of to_str
289
+ if val.bytesize > MAX_FIELD_LENGTH
290
+ raise ArgumentError, "header #{key} has too long field value: #{val.bytesize} bytes (limit #{MAX_FIELD_LENGTH})"
291
+ end
281
292
  if val.b.count("\r\n") > 0
282
293
  raise ArgumentError, 'header field value cannot include CR/LF'
283
294
  end
@@ -285,12 +296,15 @@ module Net::HTTPHeader
285
296
  end
286
297
  end
287
298
 
288
- private def append_field_value(ary, val)
299
+ private def append_field_value(ary, val, key)
289
300
  case val
290
301
  when Enumerable
291
- val.each{|x| append_field_value(ary, x)}
302
+ val.each{|x| append_field_value(ary, x, key)}
292
303
  else
293
304
  val = val.to_s
305
+ if val.bytesize > MAX_FIELD_LENGTH
306
+ raise ArgumentError, "header #{key} has too long field value: #{val.bytesize} bytes (limit #{MAX_FIELD_LENGTH})"
307
+ end
294
308
  if /[\r\n]/n.match?(val.b)
295
309
  raise ArgumentError, 'header field value cannot include CR/LF'
296
310
  end
@@ -617,11 +631,35 @@ module Net::HTTPHeader
617
631
  # res = Net::HTTP.get_response(hostname, '/todos/1')
618
632
  # res.content_length # => nil
619
633
  #
634
+ # The value must consist of digits only.
635
+ # Multiple <tt>'Content-Length'</tt> values are accepted
636
+ # only when they are all identical;
637
+ # otherwise Net::HTTPHeaderSyntaxError is raised.
638
+ # See {RFC 9110 Section 8.6}[https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6].
620
639
  def content_length
621
- return nil unless key?('Content-Length')
622
- len = self['Content-Length'].slice(/\d+/) or
623
- raise Net::HTTPHeaderSyntaxError, 'wrong Content-Length format'
624
- len.to_i
640
+ values = @header['content-length']
641
+ return nil if values.nil?
642
+
643
+ lengths = []
644
+ values.each do |value|
645
+ value = value.strip
646
+ if value.empty?
647
+ raise Net::HTTPHeaderSyntaxError,
648
+ "empty Content-Length value"
649
+ end
650
+ lengths.concat(value.split(/\s*,\s*/, -1))
651
+ end
652
+ if lengths.uniq.size != 1
653
+ raise Net::HTTPHeaderSyntaxError,
654
+ "Content-Length has multiple different values: " +
655
+ values.join(", ")
656
+ end
657
+ length = lengths.first
658
+ unless /\A\d+\z/.match?(length)
659
+ raise Net::HTTPHeaderSyntaxError,
660
+ "wrong Content-Length format: #{length}"
661
+ end
662
+ length.to_i
625
663
  end
626
664
 
627
665
  # Sets the value of field <tt>'Content-Length'</tt> to the given numeric;
@@ -647,7 +685,7 @@ module Net::HTTPHeader
647
685
  end
648
686
 
649
687
  # Returns +true+ if field <tt>'Transfer-Encoding'</tt>
650
- # exists and has value <tt>'chunked'</tt>,
688
+ # exists and has <tt>'chunked'</tt> as its final transfer coding,
651
689
  # +false+ otherwise;
652
690
  # see {Transfer-Encoding response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#transfer-encoding-response-header]:
653
691
  #
@@ -655,10 +693,13 @@ module Net::HTTPHeader
655
693
  # res['Transfer-Encoding'] # => "chunked"
656
694
  # res.chunked? # => true
657
695
  #
696
+ # <tt>'gzip, chunked'</tt> is chunked but <tt>'chunked, gzip'</tt> is not,
697
+ # because only the final transfer coding frames the message.
698
+ # See {RFC 9112 Section 6.3}[https://www.rfc-editor.org/rfc/rfc9112.html#section-6.3].
658
699
  def chunked?
659
700
  return false unless @header['transfer-encoding']
660
701
  field = self['Transfer-Encoding']
661
- (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
702
+ (/(?:\A|[^\-\w])chunked(?![\-\w])\s*(?:,\s*)*\z/i =~ field) ? true : false
662
703
  end
663
704
 
664
705
  # Returns a Range object representing the value of field
@@ -774,7 +815,7 @@ module Net::HTTPHeader
774
815
  #
775
816
  # Net::HTTPHeader#content_type= is an alias for Net::HTTPHeader#set_content_type.
776
817
  def set_content_type(type, params = {})
777
- @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
818
+ set_field('Content-Type', type + params.map{|k,v|"; #{k}=#{v}"}.join(''))
778
819
  end
779
820
 
780
821
  alias content_type= set_content_type
@@ -61,6 +61,7 @@
61
61
  # - Net::HTTP::Options
62
62
  # - Net::HTTP::Trace
63
63
  # - Net::HTTP::Patch
64
+ # - Net::HTTP::Query
64
65
  #
65
66
  # Subclasses for WebDAV requests:
66
67
  #
@@ -19,9 +19,9 @@
19
19
  #
20
20
  # - Request body: optional.
21
21
  # - Response body: yes.
22
- # - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
23
- # - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
24
- # - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
22
+ # - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes.
23
+ # - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes.
24
+ # - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: yes.
25
25
  #
26
26
  # Related:
27
27
  #
@@ -52,9 +52,9 @@ end
52
52
  #
53
53
  # - Request body: optional.
54
54
  # - Response body: no.
55
- # - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
56
- # - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
57
- # - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
55
+ # - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes.
56
+ # - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes.
57
+ # - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: yes.
58
58
  #
59
59
  # Related:
60
60
  #
@@ -87,9 +87,9 @@ end
87
87
  #
88
88
  # - Request body: yes.
89
89
  # - Response body: yes.
90
- # - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
91
- # - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
92
- # - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
90
+ # - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: no.
91
+ # - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: no.
92
+ # - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: yes.
93
93
  #
94
94
  # Related:
95
95
  #
@@ -123,9 +123,9 @@ end
123
123
  #
124
124
  # - Request body: yes.
125
125
  # - Response body: yes.
126
- # - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
127
- # - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
128
- # - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
126
+ # - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: no.
127
+ # - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes.
128
+ # - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no.
129
129
  #
130
130
  # Related:
131
131
  #
@@ -157,9 +157,9 @@ end
157
157
  #
158
158
  # - Request body: optional.
159
159
  # - Response body: yes.
160
- # - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
161
- # - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
162
- # - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
160
+ # - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: no.
161
+ # - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes.
162
+ # - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no.
163
163
  #
164
164
  # Related:
165
165
  #
@@ -189,9 +189,9 @@ end
189
189
  #
190
190
  # - Request body: optional.
191
191
  # - Response body: yes.
192
- # - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
193
- # - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
194
- # - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
192
+ # - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes.
193
+ # - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes.
194
+ # - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no.
195
195
  #
196
196
  # Related:
197
197
  #
@@ -221,9 +221,9 @@ end
221
221
  #
222
222
  # - Request body: no.
223
223
  # - Response body: yes.
224
- # - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
225
- # - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
226
- # - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
224
+ # - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes.
225
+ # - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes.
226
+ # - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no.
227
227
  #
228
228
  # Related:
229
229
  #
@@ -256,9 +256,9 @@ end
256
256
  #
257
257
  # - Request body: yes.
258
258
  # - Response body: yes.
259
- # - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
260
- # - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
261
- # - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
259
+ # - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: no.
260
+ # - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: no.
261
+ # - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: no.
262
262
  #
263
263
  # Related:
264
264
  #
@@ -271,6 +271,41 @@ class Net::HTTP::Patch < Net::HTTPRequest
271
271
  RESPONSE_HAS_BODY = true
272
272
  end
273
273
 
274
+ # \Class for representing
275
+ # {HTTP method QUERY}[https://www.rfc-editor.org/rfc/rfc10008.html]:
276
+ #
277
+ # require 'net/http'
278
+ # uri = URI('http://example.com')
279
+ # hostname = uri.hostname # => "example.com"
280
+ # uri.path = '/posts'
281
+ # req = Net::HTTP::Query.new(uri) # => #<Net::HTTP::Query QUERY>
282
+ # req.body = '{"title": "foo","body": "bar","userId": 1}'
283
+ # req.content_type = 'application/json'
284
+ # res = Net::HTTP.start(hostname) do |http|
285
+ # http.request(req)
286
+ # end
287
+ #
288
+ # See {Request Headers}[rdoc-ref:Net::HTTPRequest@Request+Headers].
289
+ #
290
+ # Properties:
291
+ #
292
+ # - Request body: yes.
293
+ # - Response body: yes.
294
+ # - {Safe}[https://en.wikipedia.org/wiki/HTTP#Safe_method]: yes.
295
+ # - {Idempotent}[https://en.wikipedia.org/wiki/HTTP#Idempotent_method]: yes.
296
+ # - {Cacheable}[https://en.wikipedia.org/wiki/HTTP#Cacheable_method]: yes.
297
+ #
298
+ # Related:
299
+ #
300
+ # - Net::HTTP#query: sends +QUERY+ request, returns response object.
301
+ #
302
+ class Net::HTTP::Query < Net::HTTPRequest
303
+ # :stopdoc:
304
+ METHOD = 'QUERY'
305
+ REQUEST_HAS_BODY = true
306
+ RESPONSE_HAS_BODY = true
307
+ end
308
+
274
309
  #
275
310
  # WebDAV methods --- RFC2518
276
311
  #
@@ -133,6 +133,13 @@
133
133
  # there is a protocol error.
134
134
  #
135
135
  class Net::HTTPResponse
136
+ # The maximum total size in bytes of the response header.
137
+ MAX_RESPONSE_HEADER_LENGTH = 1024 * 1024 # 1 MiB
138
+
139
+ # The maximum number of informational (1xx) responses accepted before the
140
+ # final response.
141
+ MAX_INFORMATIONAL_RESPONSES = 100
142
+
136
143
  class << self
137
144
  # true if the response has a body.
138
145
  def body_permitted?
@@ -152,11 +159,17 @@ class Net::HTTPResponse
152
159
  res
153
160
  end
154
161
 
162
+ def read_line(sock, limit, ignore_eof = false) #:nodoc: internal use only
163
+ sock.readuntil("\n", ignore_eof, limit: limit)
164
+ rescue Net::ReadLimitExceeded
165
+ raise Net::HTTPBadResponse, 'response line too long'
166
+ end
167
+
155
168
  private
156
169
  # :stopdoc:
157
170
 
158
171
  def read_status_line(sock)
159
- str = sock.readline
172
+ str = read_line(sock, MAX_RESPONSE_HEADER_LENGTH).chop
160
173
  m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?\z/in.match(str) or
161
174
  raise Net::HTTPBadResponse, "wrong status line: #{str.dump}"
162
175
  m.captures
@@ -170,12 +183,19 @@ class Net::HTTPResponse
170
183
 
171
184
  def each_response_header(sock)
172
185
  key = value = nil
186
+ remaining = MAX_RESPONSE_HEADER_LENGTH
173
187
  while true
174
- line = sock.readuntil("\n", true).sub(/\s+\z/, '')
188
+ line = read_line(sock, MAX_RESPONSE_HEADER_LENGTH, true)
189
+ remaining -= line.bytesize
190
+ raise Net::HTTPBadResponse, 'response header too large' if remaining < 0
191
+ line = line.chomp
175
192
  break if line.empty?
176
193
  if line[0] == ?\s or line[0] == ?\t and value
177
- value << ' ' unless value.empty?
178
- value << line.strip
194
+ folded = line.strip
195
+ unless folded.empty?
196
+ value << ' ' unless value.empty?
197
+ value << folded
198
+ end
179
199
  else
180
200
  yield key, value if key
181
201
  key, value = line.strip.split(/\s*:\s*/, 2)
@@ -599,15 +619,20 @@ class Net::HTTPResponse
599
619
 
600
620
  @socket = inflate_body_io
601
621
 
602
- clen = content_length()
603
- if clen
604
- @socket.read clen, dest, @ignore_eof
605
- return
606
- end
607
- clen = range_length()
608
- if clen
609
- @socket.read clen, dest
610
- return
622
+ # Transfer-Encoding overrides Content-Length, so a body that is not
623
+ # chunk-framed runs until the server closes the connection.
624
+ # See RFC 9112 Section 6.3.
625
+ unless @header['transfer-encoding']
626
+ clen = content_length()
627
+ if clen
628
+ @socket.read clen, dest, @ignore_eof
629
+ return
630
+ end
631
+ clen = range_length()
632
+ if clen
633
+ @socket.read clen, dest
634
+ return
635
+ end
611
636
  end
612
637
  @socket.read_all dest
613
638
  end
@@ -623,7 +648,7 @@ class Net::HTTPResponse
623
648
  def read_chunked(dest, chunk_data_io) # :nodoc:
624
649
  total = 0
625
650
  while true
626
- line = @socket.readline
651
+ line = self.class.read_line(@socket, MAX_RESPONSE_HEADER_LENGTH).chop
627
652
  hexlen = line.slice(/[0-9a-fA-F]+/) or
628
653
  raise Net::HTTPBadResponse, "wrong chunk size line: #{line}"
629
654
  len = hexlen.hex
@@ -635,7 +660,7 @@ class Net::HTTPResponse
635
660
  @socket.read 2 # \r\n
636
661
  end
637
662
  end
638
- until @socket.readline.empty?
663
+ until self.class.read_line(@socket, MAX_RESPONSE_HEADER_LENGTH).chop.empty?
639
664
  # none
640
665
  end
641
666
  end