net-http 0.1.0 → 0.2.1.pre1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14bdacaf426ba55dbcb6185d8f79591c7197574e40429fce13a9d7d3a8471245
4
- data.tar.gz: f1c49cd5d4ddc6f465a58b6b01e4a1f9ac871386e018b16e34b747a001d84fb3
3
+ metadata.gz: 31dbc20c10c0e3ca2a3fc77be6612257bb34ad8cbe8cfded64622fccdea6cb08
4
+ data.tar.gz: 2f043ee80140523a0d41f690dba72807f95cf8687cdac29de38bdc3bc9e6ccdb
5
5
  SHA512:
6
- metadata.gz: 7ecc73e91fd63c65b108ff6085028f7490c597b346fa03887b34e8693600618d513bd243eccbf0d9a5cfb808418f1bb548c71ff546358ded71e4641c596927a0
7
- data.tar.gz: a89f8b7a499c914d94745d3b74c09206144fe7d8336b64aa1f3bfde9ed7890206c6b957834210960ff2272018881c500e65d05de73347f30534a55adaa9a447e
6
+ metadata.gz: '00890bbc5eb9a6c3930cfc69554e5d76de6395a31fdcb2c865ad77c6fac0c55cbef5206b6adf0c5b97ea1bb1d8ccc79d2302e377f77d95132c87b5327aeef229'
7
+ data.tar.gz: a1856470ffbfe586204e9a3e97bfd01f86261bf51e9c2760f1b086be45ccafd7edcddb04854f4251b8a033246e7bc485b80a1374eb2a30c507a42d8d61526608
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: 'github-actions'
4
+ directory: '/'
5
+ schedule:
6
+ interval: 'weekly'
@@ -7,18 +7,16 @@ jobs:
7
7
  name: build (${{ matrix.ruby }} / ${{ matrix.os }})
8
8
  strategy:
9
9
  matrix:
10
- ruby: [ 2.7, 2.6, head ]
10
+ ruby: [ 3.1, '3.0', 2.7, 2.6, head ]
11
11
  os: [ ubuntu-latest, macos-latest ]
12
12
  runs-on: ${{ matrix.os }}
13
13
  steps:
14
- - uses: actions/checkout@master
14
+ - uses: actions/checkout@v3
15
15
  - name: Set up Ruby
16
16
  uses: ruby/setup-ruby@v1
17
17
  with:
18
18
  ruby-version: ${{ matrix.ruby }}
19
19
  - name: Install dependencies
20
- run: |
21
- gem install bundler --no-document
22
- bundle install
20
+ run: bundle install
23
21
  - name: Run test
24
22
  run: rake test
data/Gemfile CHANGED
@@ -4,3 +4,4 @@ gemspec
4
4
 
5
5
  gem "rake"
6
6
  gem "test-unit"
7
+ gem "webrick"
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Net::Http
1
+ # Net::HTTP
2
2
 
3
3
  Net::HTTP provides a rich library which can be used to build HTTP
4
4
  user-agents. For more details about HTTP see
data/Rakefile CHANGED
@@ -7,4 +7,11 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.test_files = FileList["test/**/test_*.rb"]
8
8
  end
9
9
 
10
+ task :sync_tool do
11
+ require 'fileutils'
12
+ FileUtils.cp "../ruby/tool/lib/core_assertions.rb", "./test/lib"
13
+ FileUtils.cp "../ruby/tool/lib/envutil.rb", "./test/lib"
14
+ FileUtils.cp "../ruby/tool/lib/find_executable.rb", "./test/lib"
15
+ end
16
+
10
17
  task :default => :test
@@ -5,22 +5,36 @@
5
5
 
6
6
  class Net::HTTP
7
7
  ProxyMod = ProxyDelta
8
- end
9
-
10
- module Net
11
- HTTPSession = Net::HTTP
8
+ deprecate_constant :ProxyMod
12
9
  end
13
10
 
14
11
  module Net::NetPrivate
15
12
  HTTPRequest = ::Net::HTTPRequest
13
+ deprecate_constant :HTTPRequest
16
14
  end
17
15
 
18
- Net::HTTPInformationCode = Net::HTTPInformation
19
- Net::HTTPSuccessCode = Net::HTTPSuccess
20
- Net::HTTPRedirectionCode = Net::HTTPRedirection
21
- Net::HTTPRetriableCode = Net::HTTPRedirection
22
- Net::HTTPClientErrorCode = Net::HTTPClientError
23
- Net::HTTPFatalErrorCode = Net::HTTPClientError
24
- Net::HTTPServerErrorCode = Net::HTTPServerError
25
- Net::HTTPResponceReceiver = Net::HTTPResponse
16
+ module Net
17
+ HTTPSession = HTTP
26
18
 
19
+ HTTPInformationCode = HTTPInformation
20
+ HTTPSuccessCode = HTTPSuccess
21
+ HTTPRedirectionCode = HTTPRedirection
22
+ HTTPRetriableCode = HTTPRedirection
23
+ HTTPClientErrorCode = HTTPClientError
24
+ HTTPFatalErrorCode = HTTPClientError
25
+ HTTPServerErrorCode = HTTPServerError
26
+ HTTPResponseReceiver = HTTPResponse
27
+
28
+ HTTPResponceReceiver = HTTPResponse # Typo since 2001
29
+
30
+ deprecate_constant :HTTPSession,
31
+ :HTTPInformationCode,
32
+ :HTTPSuccessCode,
33
+ :HTTPRedirectionCode,
34
+ :HTTPRetriableCode,
35
+ :HTTPClientErrorCode,
36
+ :HTTPFatalErrorCode,
37
+ :HTTPServerErrorCode,
38
+ :HTTPResponseReceiver,
39
+ :HTTPResponceReceiver
40
+ end
@@ -31,12 +31,12 @@ class Net::HTTPGenericRequest
31
31
 
32
32
  @decode_content = false
33
33
 
34
- if @response_has_body and Net::HTTP::HAVE_ZLIB then
34
+ if Net::HTTP::HAVE_ZLIB then
35
35
  if !initheader ||
36
36
  !initheader.keys.any? { |k|
37
37
  %w[accept-encoding range].include? k.downcase
38
38
  } then
39
- @decode_content = true
39
+ @decode_content = true if @response_has_body
40
40
  initheader = initheader ? initheader.dup : {}
41
41
  initheader["accept-encoding"] =
42
42
  "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
@@ -143,7 +143,7 @@ class Net::HTTPGenericRequest
143
143
  end
144
144
 
145
145
  if host = self['host']
146
- host.sub!(/:.*/s, ''.freeze)
146
+ host.sub!(/:.*/m, ''.freeze)
147
147
  elsif host = @uri.host
148
148
  else
149
149
  host = addr
@@ -202,9 +202,7 @@ class Net::HTTPGenericRequest
202
202
  IO.copy_stream(f, chunker)
203
203
  chunker.finish
204
204
  else
205
- # copy_stream can sendfile() to sock.io unless we use SSL.
206
- # If sock.io is an SSLSocket, copy_stream will hit SSL_write()
207
- IO.copy_stream(f, sock.io)
205
+ IO.copy_stream(f, sock)
208
206
  end
209
207
  end
210
208
 
@@ -423,30 +423,50 @@ module Net::HTTPHeader
423
423
  alias form_data= set_form_data
424
424
 
425
425
  # Set an HTML form data set.
426
- # +params+ is the form data set; it is an Array of Arrays or a Hash
427
- # +enctype is the type to encode the form data set.
428
- # It is application/x-www-form-urlencoded or multipart/form-data.
429
- # +formopt+ is an optional hash to specify the detail.
426
+ # +params+ :: The form data to set, which should be an enumerable.
427
+ # See below for more details.
428
+ # +enctype+ :: The content type to use to encode the form submission,
429
+ # which should be application/x-www-form-urlencoded or
430
+ # multipart/form-data.
431
+ # +formopt+ :: An options hash, supporting the following options:
432
+ # :boundary :: The boundary of the multipart message. If
433
+ # not given, a random boundary will be used.
434
+ # :charset :: The charset of the form submission. All
435
+ # field names and values of non-file fields
436
+ # should be encoded with this charset.
430
437
  #
431
- # boundary:: the boundary of the multipart message
432
- # charset:: the charset of the message. All names and the values of
433
- # non-file fields are encoded as the charset.
434
- #
435
- # Each item of params is an array and contains following items:
436
- # +name+:: the name of the field
437
- # +value+:: the value of the field, it should be a String or a File
438
- # +opt+:: an optional hash to specify additional information
438
+ # Each item of params should respond to +each+ and yield 2-3 arguments,
439
+ # or an array of 2-3 elements. The arguments yielded should be:
440
+ # * The name of the field.
441
+ # * The value of the field, it should be a String or a File or IO-like.
442
+ # * An options hash, supporting the following options, only
443
+ # used for file uploads:
444
+ # :filename :: The name of the file to use.
445
+ # :content_type :: The content type of the uploaded file.
439
446
  #
440
447
  # Each item is a file field or a normal field.
441
- # If +value+ is a File object or the +opt+ have a filename key,
448
+ # If +value+ is a File object or the +opt+ hash has a :filename key,
442
449
  # the item is treated as a file field.
443
450
  #
444
- # If Transfer-Encoding is set as chunked, this send the request in
451
+ # If Transfer-Encoding is set as chunked, this sends the request using
445
452
  # chunked encoding. Because chunked encoding is HTTP/1.1 feature,
446
- # you must confirm the server to support HTTP/1.1 before sending it.
453
+ # you should confirm that the server supports HTTP/1.1 before using
454
+ # chunked encoding.
447
455
  #
448
456
  # Example:
449
- # http.set_form([["q", "ruby"], ["lang", "en"]])
457
+ # req.set_form([["q", "ruby"], ["lang", "en"]])
458
+ #
459
+ # req.set_form({"f"=>File.open('/path/to/filename')},
460
+ # "multipart/form-data",
461
+ # charset: "UTF-8",
462
+ # )
463
+ #
464
+ # req.set_form([["f",
465
+ # File.open('/path/to/filename.bar'),
466
+ # {filename: "other-filename.foo"}
467
+ # ]],
468
+ # "multipart/form-data",
469
+ # )
450
470
  #
451
471
  # See also RFC 2388, RFC 2616, HTML 4.01, and HTML5
452
472
  #
@@ -84,6 +84,8 @@ class Net::HTTPResponse
84
84
  @read = false
85
85
  @uri = nil
86
86
  @decode_content = false
87
+ @body_encoding = false
88
+ @ignore_eof = true
87
89
  end
88
90
 
89
91
  # The HTTP version supported by the server.
@@ -106,6 +108,22 @@ class Net::HTTPResponse
106
108
  # Accept-Encoding header from the user.
107
109
  attr_accessor :decode_content
108
110
 
111
+ # The encoding to use for the response body. If Encoding, use that encoding.
112
+ # If other true value, attempt to detect the appropriate encoding, and use
113
+ # that.
114
+ attr_reader :body_encoding
115
+
116
+ # Set the encoding to use for the response body. If given a String, find
117
+ # the related Encoding.
118
+ def body_encoding=(value)
119
+ value = Encoding.find(value) if value.is_a?(String)
120
+ @body_encoding = value
121
+ end
122
+
123
+ # Whether to ignore EOF when reading bodies with a specified Content-Length
124
+ # header.
125
+ attr_accessor :ignore_eof
126
+
109
127
  def inspect
110
128
  "#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
111
129
  end
@@ -214,6 +232,17 @@ class Net::HTTPResponse
214
232
  end
215
233
  @read = true
216
234
 
235
+ case enc = @body_encoding
236
+ when Encoding, false, nil
237
+ # Encoding: force given encoding
238
+ # false/nil: do not force encoding
239
+ else
240
+ # other value: detect encoding from body
241
+ enc = detect_encoding(@body)
242
+ end
243
+
244
+ @body.force_encoding(enc) if enc
245
+
217
246
  @body
218
247
  end
219
248
 
@@ -245,6 +274,141 @@ class Net::HTTPResponse
245
274
 
246
275
  private
247
276
 
277
+ # :nodoc:
278
+ def detect_encoding(str, encoding=nil)
279
+ if encoding
280
+ elsif encoding = type_params['charset']
281
+ elsif encoding = check_bom(str)
282
+ else
283
+ encoding = case content_type&.downcase
284
+ when %r{text/x(?:ht)?ml|application/(?:[^+]+\+)?xml}
285
+ /\A<xml[ \t\r\n]+
286
+ version[ \t\r\n]*=[ \t\r\n]*(?:"[0-9.]+"|'[0-9.]*')[ \t\r\n]+
287
+ encoding[ \t\r\n]*=[ \t\r\n]*
288
+ (?:"([A-Za-z][\-A-Za-z0-9._]*)"|'([A-Za-z][\-A-Za-z0-9._]*)')/x =~ str
289
+ encoding = $1 || $2 || Encoding::UTF_8
290
+ when %r{text/html.*}
291
+ sniff_encoding(str)
292
+ end
293
+ end
294
+ return encoding
295
+ end
296
+
297
+ # :nodoc:
298
+ def sniff_encoding(str, encoding=nil)
299
+ # the encoding sniffing algorithm
300
+ # http://www.w3.org/TR/html5/parsing.html#determining-the-character-encoding
301
+ if enc = scanning_meta(str)
302
+ enc
303
+ # 6. last visited page or something
304
+ # 7. frequency
305
+ elsif str.ascii_only?
306
+ Encoding::US_ASCII
307
+ elsif str.dup.force_encoding(Encoding::UTF_8).valid_encoding?
308
+ Encoding::UTF_8
309
+ end
310
+ # 8. implementation-defined or user-specified
311
+ end
312
+
313
+ # :nodoc:
314
+ def check_bom(str)
315
+ case str.byteslice(0, 2)
316
+ when "\xFE\xFF"
317
+ return Encoding::UTF_16BE
318
+ when "\xFF\xFE"
319
+ return Encoding::UTF_16LE
320
+ end
321
+ if "\xEF\xBB\xBF" == str.byteslice(0, 3)
322
+ return Encoding::UTF_8
323
+ end
324
+ nil
325
+ end
326
+
327
+ # :nodoc:
328
+ def scanning_meta(str)
329
+ require 'strscan'
330
+ ss = StringScanner.new(str)
331
+ if ss.scan_until(/<meta[\t\n\f\r ]*/)
332
+ attrs = {} # attribute_list
333
+ got_pragma = false
334
+ need_pragma = nil
335
+ charset = nil
336
+
337
+ # step: Attributes
338
+ while attr = get_attribute(ss)
339
+ name, value = *attr
340
+ next if attrs[name]
341
+ attrs[name] = true
342
+ case name
343
+ when 'http-equiv'
344
+ got_pragma = true if value == 'content-type'
345
+ when 'content'
346
+ encoding = extracting_encodings_from_meta_elements(value)
347
+ unless charset
348
+ charset = encoding
349
+ end
350
+ need_pragma = true
351
+ when 'charset'
352
+ need_pragma = false
353
+ charset = value
354
+ end
355
+ end
356
+
357
+ # step: Processing
358
+ return if need_pragma.nil?
359
+ return if need_pragma && !got_pragma
360
+
361
+ charset = Encoding.find(charset) rescue nil
362
+ return unless charset
363
+ charset = Encoding::UTF_8 if charset == Encoding::UTF_16
364
+ return charset # tentative
365
+ end
366
+ nil
367
+ end
368
+
369
+ def get_attribute(ss)
370
+ ss.scan(/[\t\n\f\r \/]*/)
371
+ if ss.peek(1) == '>'
372
+ ss.getch
373
+ return nil
374
+ end
375
+ name = ss.scan(/[^=\t\n\f\r \/>]*/)
376
+ name.downcase!
377
+ raise if name.empty?
378
+ ss.skip(/[\t\n\f\r ]*/)
379
+ if ss.getch != '='
380
+ value = ''
381
+ return [name, value]
382
+ end
383
+ ss.skip(/[\t\n\f\r ]*/)
384
+ case ss.peek(1)
385
+ when '"'
386
+ ss.getch
387
+ value = ss.scan(/[^"]+/)
388
+ value.downcase!
389
+ ss.getch
390
+ when "'"
391
+ ss.getch
392
+ value = ss.scan(/[^']+/)
393
+ value.downcase!
394
+ ss.getch
395
+ when '>'
396
+ value = ''
397
+ else
398
+ value = ss.scan(/[^\t\n\f\r >]+/)
399
+ value.downcase!
400
+ end
401
+ [name, value]
402
+ end
403
+
404
+ def extracting_encodings_from_meta_elements(value)
405
+ # http://dev.w3.org/html5/spec/fetching-resources.html#algorithm-for-extracting-an-encoding-from-a-meta-element
406
+ if /charset[\t\n\f\r ]*=(?:"([^"]*)"|'([^']*)'|["']|\z|([^\t\n\f\r ;]+))/i =~ value
407
+ return $1 || $2 || $3
408
+ end
409
+ return nil
410
+ end
411
+
248
412
  ##
249
413
  # Checks for a supported Content-Encoding header and yields an Inflate
250
414
  # wrapper for this response's socket when zlib is present. If the
@@ -268,12 +432,16 @@ class Net::HTTPResponse
268
432
 
269
433
  begin
270
434
  yield inflate_body_io
435
+ success = true
271
436
  ensure
272
- orig_err = $!
273
437
  begin
274
438
  inflate_body_io.finish
439
+ if self['content-length']
440
+ self['content-length'] = inflate_body_io.bytes_inflated.to_s
441
+ end
275
442
  rescue => err
276
- raise orig_err || err
443
+ # Ignore #finish's error if there is an exception from yield
444
+ raise err if success
277
445
  end
278
446
  end
279
447
  when 'none', 'identity' then
@@ -296,7 +464,7 @@ class Net::HTTPResponse
296
464
 
297
465
  clen = content_length()
298
466
  if clen
299
- @socket.read clen, dest, true # ignore EOF
467
+ @socket.read clen, dest, @ignore_eof
300
468
  return
301
469
  end
302
470
  clen = range_length()
@@ -372,6 +540,14 @@ class Net::HTTPResponse
372
540
  @inflate.finish
373
541
  end
374
542
 
543
+ ##
544
+ # The number of bytes inflated, used to update the Content-Length of
545
+ # the response.
546
+
547
+ def bytes_inflated
548
+ @inflate.total_out
549
+ end
550
+
375
551
  ##
376
552
  # Returns a Net::ReadAdapter that inflates each read chunk into +dest+.
377
553
  #
data/lib/net/http.rb CHANGED
@@ -22,6 +22,7 @@
22
22
 
23
23
  require 'net/protocol'
24
24
  require 'uri'
25
+ require 'resolv'
25
26
  autoload :OpenSSL, 'openssl'
26
27
 
27
28
  module Net #:nodoc:
@@ -327,6 +328,8 @@ module Net #:nodoc:
327
328
  # HTTPInformation:: 1xx
328
329
  # HTTPContinue:: 100
329
330
  # HTTPSwitchProtocol:: 101
331
+ # HTTPProcessing:: 102
332
+ # HTTPEarlyHints:: 103
330
333
  # HTTPSuccess:: 2xx
331
334
  # HTTPOK:: 200
332
335
  # HTTPCreated:: 201
@@ -336,6 +339,7 @@ module Net #:nodoc:
336
339
  # HTTPResetContent:: 205
337
340
  # HTTPPartialContent:: 206
338
341
  # HTTPMultiStatus:: 207
342
+ # HTTPAlreadyReported:: 208
339
343
  # HTTPIMUsed:: 226
340
344
  # HTTPRedirection:: 3xx
341
345
  # HTTPMultipleChoices:: 300
@@ -345,6 +349,7 @@ module Net #:nodoc:
345
349
  # HTTPNotModified:: 304
346
350
  # HTTPUseProxy:: 305
347
351
  # HTTPTemporaryRedirect:: 307
352
+ # HTTPPermanentRedirect:: 308
348
353
  # HTTPClientError:: 4xx
349
354
  # HTTPBadRequest:: 400
350
355
  # HTTPUnauthorized:: 401
@@ -364,6 +369,7 @@ module Net #:nodoc:
364
369
  # HTTPUnsupportedMediaType:: 415
365
370
  # HTTPRequestedRangeNotSatisfiable:: 416
366
371
  # HTTPExpectationFailed:: 417
372
+ # HTTPMisdirectedRequest:: 421
367
373
  # HTTPUnprocessableEntity:: 422
368
374
  # HTTPLocked:: 423
369
375
  # HTTPFailedDependency:: 424
@@ -379,7 +385,10 @@ module Net #:nodoc:
379
385
  # HTTPServiceUnavailable:: 503
380
386
  # HTTPGatewayTimeOut:: 504
381
387
  # HTTPVersionNotSupported:: 505
388
+ # HTTPVariantAlsoNegotiates:: 506
382
389
  # HTTPInsufficientStorage:: 507
390
+ # HTTPLoopDetected:: 508
391
+ # HTTPNotExtended:: 510
383
392
  # HTTPNetworkAuthenticationRequired:: 511
384
393
  #
385
394
  # There is also the Net::HTTPBadResponse exception which is raised when
@@ -388,11 +397,11 @@ module Net #:nodoc:
388
397
  class HTTP < Protocol
389
398
 
390
399
  # :stopdoc:
400
+ VERSION = "0.2.1.pre1"
391
401
  Revision = %q$Revision$.split[1]
392
402
  HTTPVersion = '1.1'
393
403
  begin
394
404
  require 'zlib'
395
- require 'stringio' #for our purposes (unpacking gzip) lump these together
396
405
  HAVE_ZLIB=true
397
406
  rescue LoadError
398
407
  HAVE_ZLIB=false
@@ -427,7 +436,7 @@ module Net #:nodoc:
427
436
  #
428
437
  # Gets the body text from the target and outputs it to $stdout. The
429
438
  # target can either be specified as
430
- # (+uri+), or as (+host+, +path+, +port+ = 80); so:
439
+ # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
431
440
  #
432
441
  # Net::HTTP.get_print URI('http://www.example.com/index.html')
433
442
  #
@@ -435,8 +444,12 @@ module Net #:nodoc:
435
444
  #
436
445
  # Net::HTTP.get_print 'www.example.com', '/index.html'
437
446
  #
438
- def HTTP.get_print(uri_or_host, path = nil, port = nil)
439
- get_response(uri_or_host, path, port) {|res|
447
+ # you can also specify request headers:
448
+ #
449
+ # Net::HTTP.get_print URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' }
450
+ #
451
+ def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil)
452
+ get_response(uri_or_host, path_or_headers, port) {|res|
440
453
  res.read_body do |chunk|
441
454
  $stdout.print chunk
442
455
  end
@@ -446,7 +459,7 @@ module Net #:nodoc:
446
459
 
447
460
  # Sends a GET request to the target and returns the HTTP response
448
461
  # as a string. The target can either be specified as
449
- # (+uri+), or as (+host+, +path+, +port+ = 80); so:
462
+ # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
450
463
  #
451
464
  # print Net::HTTP.get(URI('http://www.example.com/index.html'))
452
465
  #
@@ -454,13 +467,17 @@ module Net #:nodoc:
454
467
  #
455
468
  # print Net::HTTP.get('www.example.com', '/index.html')
456
469
  #
457
- def HTTP.get(uri_or_host, path = nil, port = nil)
458
- get_response(uri_or_host, path, port).body
470
+ # you can also specify request headers:
471
+ #
472
+ # Net::HTTP.get(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' })
473
+ #
474
+ def HTTP.get(uri_or_host, path_or_headers = nil, port = nil)
475
+ get_response(uri_or_host, path_or_headers, port).body
459
476
  end
460
477
 
461
478
  # Sends a GET request to the target and returns the HTTP response
462
479
  # as a Net::HTTPResponse object. The target can either be specified as
463
- # (+uri+), or as (+host+, +path+, +port+ = 80); so:
480
+ # (+uri+, +headers+), or as (+host+, +path+, +port+ = 80); so:
464
481
  #
465
482
  # res = Net::HTTP.get_response(URI('http://www.example.com/index.html'))
466
483
  # print res.body
@@ -470,17 +487,23 @@ module Net #:nodoc:
470
487
  # res = Net::HTTP.get_response('www.example.com', '/index.html')
471
488
  # print res.body
472
489
  #
473
- def HTTP.get_response(uri_or_host, path = nil, port = nil, &block)
474
- if path
490
+ # you can also specify request headers:
491
+ #
492
+ # Net::HTTP.get_response(URI('http://www.example.com/index.html'), { 'Accept' => 'text/html' })
493
+ #
494
+ def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block)
495
+ if path_or_headers && !path_or_headers.is_a?(Hash)
475
496
  host = uri_or_host
497
+ path = path_or_headers
476
498
  new(host, port || HTTP.default_port).start {|http|
477
499
  return http.request_get(path, &block)
478
500
  }
479
501
  else
480
502
  uri = uri_or_host
503
+ headers = path_or_headers
481
504
  start(uri.hostname, uri.port,
482
505
  :use_ssl => uri.scheme == 'https') {|http|
483
- return http.request_get(uri, &block)
506
+ return http.request_get(uri, headers, &block)
484
507
  }
485
508
  end
486
509
  end
@@ -509,14 +532,13 @@ module Net #:nodoc:
509
532
  #
510
533
  # { "cmd" => "search", "q" => "ruby", "max" => "50" }
511
534
  #
512
- # This method also does Basic Authentication iff +url+.user exists.
535
+ # This method also does Basic Authentication if and only if +url+.user exists.
513
536
  # But userinfo for authentication is deprecated (RFC3986).
514
537
  # So this feature will be removed.
515
538
  #
516
539
  # Example:
517
540
  #
518
541
  # require 'net/http'
519
- # require 'uri'
520
542
  #
521
543
  # Net::HTTP.post_form URI('http://www.example.com/search.cgi'),
522
544
  # { "q" => "ruby", "max" => "50" }
@@ -571,7 +593,7 @@ module Net #:nodoc:
571
593
  # _opt_ :: optional hash
572
594
  #
573
595
  # _opt_ sets following values by its accessor.
574
- # The keys are ipaddr, ca_file, ca_path, cert, cert_store, ciphers,
596
+ # The keys are ipaddr, ca_file, ca_path, cert, cert_store, ciphers, keep_alive_timeout,
575
597
  # close_on_empty_response, key, open_timeout, read_timeout, write_timeout, ssl_timeout,
576
598
  # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
577
599
  # If you set :use_ssl as true, you can use https and default value of
@@ -676,6 +698,8 @@ module Net #:nodoc:
676
698
  @continue_timeout = nil
677
699
  @max_retries = 1
678
700
  @debug_output = nil
701
+ @response_body_encoding = false
702
+ @ignore_eof = true
679
703
 
680
704
  @proxy_from_env = false
681
705
  @proxy_uri = nil
@@ -723,6 +747,18 @@ module Net #:nodoc:
723
747
  # The local port used to establish the connection.
724
748
  attr_accessor :local_port
725
749
 
750
+ # The encoding to use for the response body. If Encoding, uses the
751
+ # specified encoding. If other true value, tries to detect the response
752
+ # body encoding.
753
+ attr_reader :response_body_encoding
754
+
755
+ # Set the encoding to use for the response body. If given a String, find
756
+ # the related Encoding.
757
+ def response_body_encoding=(value)
758
+ value = Encoding.find(value) if value.is_a?(String)
759
+ @response_body_encoding = value
760
+ end
761
+
726
762
  attr_writer :proxy_from_env
727
763
  attr_writer :proxy_address
728
764
  attr_writer :proxy_port
@@ -804,6 +840,10 @@ module Net #:nodoc:
804
840
  # The default value is 2 seconds.
805
841
  attr_accessor :keep_alive_timeout
806
842
 
843
+ # Whether to ignore EOF when reading response bodies with defined
844
+ # Content-Length headers. For backwards compatibility, the default is true.
845
+ attr_accessor :ignore_eof
846
+
807
847
  # Returns true if the HTTP session has been started.
808
848
  def started?
809
849
  @started
@@ -836,6 +876,7 @@ module Net #:nodoc:
836
876
  :@cert,
837
877
  :@cert_store,
838
878
  :@ciphers,
879
+ :@extra_chain_cert,
839
880
  :@key,
840
881
  :@ssl_timeout,
841
882
  :@ssl_version,
@@ -852,6 +893,7 @@ module Net #:nodoc:
852
893
  :cert,
853
894
  :cert_store,
854
895
  :ciphers,
896
+ :extra_chain_cert,
855
897
  :key,
856
898
  :ssl_timeout,
857
899
  :ssl_version,
@@ -882,6 +924,10 @@ module Net #:nodoc:
882
924
  # Sets the available ciphers. See OpenSSL::SSL::SSLContext#ciphers=
883
925
  attr_accessor :ciphers
884
926
 
927
+ # Sets the extra X509 certificates to be added to the certificate chain.
928
+ # See OpenSSL::SSL::SSLContext#extra_chain_cert=
929
+ attr_accessor :extra_chain_cert
930
+
885
931
  # Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
886
932
  # (This method is appeared in Michal Rokos's OpenSSL extension.)
887
933
  attr_accessor :key
@@ -952,6 +998,12 @@ module Net #:nodoc:
952
998
  private :do_start
953
999
 
954
1000
  def connect
1001
+ if use_ssl?
1002
+ # reference early to load OpenSSL before connecting,
1003
+ # as OpenSSL may take time to load.
1004
+ @ssl_context = OpenSSL::SSL::SSLContext.new
1005
+ end
1006
+
955
1007
  if proxy? then
956
1008
  conn_addr = proxy_address
957
1009
  conn_port = proxy_port
@@ -960,17 +1012,16 @@ module Net #:nodoc:
960
1012
  conn_port = port
961
1013
  end
962
1014
 
963
- D "opening connection to #{conn_addr}:#{conn_port}..."
964
- s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
965
- begin
966
- TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
967
- rescue => e
968
- raise e, "Failed to open TCP connection to " +
969
- "#{conn_addr}:#{conn_port} (#{e.message})"
970
- end
971
- }
1015
+ debug "opening connection to #{conn_addr}:#{conn_port}..."
1016
+ begin
1017
+ s = Socket.tcp conn_addr, conn_port, @local_host, @local_port, connect_timeout: @open_timeout
1018
+ rescue => e
1019
+ e = Net::OpenTimeout.new(e) if e.is_a?(Errno::ETIMEDOUT) #for compatibility with previous versions
1020
+ raise e, "Failed to open TCP connection to " +
1021
+ "#{conn_addr}:#{conn_port} (#{e.message})"
1022
+ end
972
1023
  s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
973
- D "opened"
1024
+ debug "opened"
974
1025
  if use_ssl?
975
1026
  if proxy?
976
1027
  plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
@@ -999,35 +1050,56 @@ module Net #:nodoc:
999
1050
  end
1000
1051
  end
1001
1052
  end
1002
- @ssl_context = OpenSSL::SSL::SSLContext.new
1003
1053
  @ssl_context.set_params(ssl_parameters)
1004
- @ssl_context.session_cache_mode =
1005
- OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
1006
- OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
1007
- @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
1008
- D "starting SSL for #{conn_addr}:#{conn_port}..."
1054
+ unless @ssl_context.session_cache_mode.nil? # a dummy method on JRuby
1055
+ @ssl_context.session_cache_mode =
1056
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
1057
+ OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
1058
+ end
1059
+ if @ssl_context.respond_to?(:session_new_cb) # not implemented under JRuby
1060
+ @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
1061
+ end
1062
+
1063
+ # Still do the post_connection_check below even if connecting
1064
+ # to IP address
1065
+ verify_hostname = @ssl_context.verify_hostname
1066
+
1067
+ # Server Name Indication (SNI) RFC 3546/6066
1068
+ case @address
1069
+ when Resolv::IPv4::Regex, Resolv::IPv6::Regex
1070
+ # don't set SNI, as IP addresses in SNI is not valid
1071
+ # per RFC 6066, section 3.
1072
+
1073
+ # Avoid openssl warning
1074
+ @ssl_context.verify_hostname = false
1075
+ else
1076
+ ssl_host_address = @address
1077
+ end
1078
+
1079
+ debug "starting SSL for #{conn_addr}:#{conn_port}..."
1009
1080
  s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
1010
1081
  s.sync_close = true
1011
- # Server Name Indication (SNI) RFC 3546
1012
- s.hostname = @address if s.respond_to? :hostname=
1082
+ s.hostname = ssl_host_address if s.respond_to?(:hostname=) && ssl_host_address
1083
+
1013
1084
  if @ssl_session and
1014
1085
  Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
1015
1086
  s.session = @ssl_session
1016
1087
  end
1017
1088
  ssl_socket_connect(s, @open_timeout)
1018
- if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && @ssl_context.verify_hostname
1089
+ if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && verify_hostname
1019
1090
  s.post_connection_check(@address)
1020
1091
  end
1021
- D "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}"
1092
+ debug "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}"
1022
1093
  end
1023
1094
  @socket = BufferedIO.new(s, read_timeout: @read_timeout,
1024
1095
  write_timeout: @write_timeout,
1025
1096
  continue_timeout: @continue_timeout,
1026
1097
  debug_output: @debug_output)
1098
+ @last_communicated = nil
1027
1099
  on_connect
1028
1100
  rescue => exception
1029
1101
  if s
1030
- D "Conn close because of connect error #{exception}"
1102
+ debug "Conn close because of connect error #{exception}"
1031
1103
  s.close
1032
1104
  end
1033
1105
  raise
@@ -1159,7 +1231,8 @@ module Net #:nodoc:
1159
1231
  # The username of the proxy server, if one is configured.
1160
1232
  def proxy_user
1161
1233
  if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
1162
- proxy_uri&.user
1234
+ user = proxy_uri&.user
1235
+ unescape(user) if user
1163
1236
  else
1164
1237
  @proxy_user
1165
1238
  end
@@ -1168,7 +1241,8 @@ module Net #:nodoc:
1168
1241
  # The password of the proxy server, if one is configured.
1169
1242
  def proxy_pass
1170
1243
  if ENVIRONMENT_VARIABLE_IS_MULTIUSER_SAFE && @proxy_from_env
1171
- proxy_uri&.password
1244
+ pass = proxy_uri&.password
1245
+ unescape(pass) if pass
1172
1246
  else
1173
1247
  @proxy_pass
1174
1248
  end
@@ -1179,6 +1253,11 @@ module Net #:nodoc:
1179
1253
 
1180
1254
  private
1181
1255
 
1256
+ def unescape(value)
1257
+ require 'cgi/util'
1258
+ CGI.unescape(value)
1259
+ end
1260
+
1182
1261
  # without proxy, obsolete
1183
1262
 
1184
1263
  def conn_address # :nodoc:
@@ -1535,6 +1614,8 @@ module Net #:nodoc:
1535
1614
  begin
1536
1615
  res = HTTPResponse.read_new(@socket)
1537
1616
  res.decode_content = req.decode_content
1617
+ res.body_encoding = @response_body_encoding
1618
+ res.ignore_eof = @ignore_eof
1538
1619
  end while res.kind_of?(HTTPInformation)
1539
1620
 
1540
1621
  res.uri = req.uri
@@ -1554,10 +1635,10 @@ module Net #:nodoc:
1554
1635
  if count < max_retries && IDEMPOTENT_METHODS_.include?(req.method)
1555
1636
  count += 1
1556
1637
  @socket.close if @socket
1557
- D "Conn close because of error #{exception}, and retry"
1638
+ debug "Conn close because of error #{exception}, and retry"
1558
1639
  retry
1559
1640
  end
1560
- D "Conn close because of error #{exception}"
1641
+ debug "Conn close because of error #{exception}"
1561
1642
  @socket.close if @socket
1562
1643
  raise
1563
1644
  end
@@ -1565,7 +1646,7 @@ module Net #:nodoc:
1565
1646
  end_transport req, res
1566
1647
  res
1567
1648
  rescue => exception
1568
- D "Conn close because of error #{exception}"
1649
+ debug "Conn close because of error #{exception}"
1569
1650
  @socket.close if @socket
1570
1651
  raise exception
1571
1652
  end
@@ -1575,11 +1656,11 @@ module Net #:nodoc:
1575
1656
  connect
1576
1657
  elsif @last_communicated
1577
1658
  if @last_communicated + @keep_alive_timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC)
1578
- D 'Conn close because of keep_alive_timeout'
1659
+ debug 'Conn close because of keep_alive_timeout'
1579
1660
  @socket.close
1580
1661
  connect
1581
1662
  elsif @socket.io.to_io.wait_readable(0) && @socket.eof?
1582
- D "Conn close because of EOF"
1663
+ debug "Conn close because of EOF"
1583
1664
  @socket.close
1584
1665
  connect
1585
1666
  end
@@ -1597,15 +1678,15 @@ module Net #:nodoc:
1597
1678
  @curr_http_version = res.http_version
1598
1679
  @last_communicated = nil
1599
1680
  if @socket.closed?
1600
- D 'Conn socket closed'
1681
+ debug 'Conn socket closed'
1601
1682
  elsif not res.body and @close_on_empty_response
1602
- D 'Conn close'
1683
+ debug 'Conn close'
1603
1684
  @socket.close
1604
1685
  elsif keep_alive?(req, res)
1605
- D 'Conn keep-alive'
1686
+ debug 'Conn keep-alive'
1606
1687
  @last_communicated = Process.clock_gettime(Process::CLOCK_MONOTONIC)
1607
1688
  else
1608
- D 'Conn close'
1689
+ debug 'Conn close'
1609
1690
  @socket.close
1610
1691
  end
1611
1692
  end
@@ -1660,11 +1741,14 @@ module Net #:nodoc:
1660
1741
  default_port == port ? addr : "#{addr}:#{port}"
1661
1742
  end
1662
1743
 
1663
- def D(msg)
1744
+ # Adds a message to debugging output
1745
+ def debug(msg)
1664
1746
  return unless @debug_output
1665
1747
  @debug_output << msg
1666
1748
  @debug_output << "\n"
1667
1749
  end
1750
+
1751
+ alias_method :D, :debug
1668
1752
  end
1669
1753
 
1670
1754
  end
data/net-http.gemspec CHANGED
@@ -1,12 +1,15 @@
1
- begin
2
- require_relative "lib/net/http/version"
3
- rescue LoadError # Fallback to load version file in ruby core repository
4
- require_relative "version"
1
+ # frozen_string_literal: true
2
+
3
+ name = File.basename(__FILE__, ".gemspec")
4
+ version = ["lib", Array.new(name.count("-")+1, "..").join("/")].find do |dir|
5
+ break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
6
+ /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
7
+ end rescue nil
5
8
  end
6
9
 
7
10
  Gem::Specification.new do |spec|
8
- spec.name = "net-http"
9
- spec.version = Net::Http::VERSION
11
+ spec.name = name
12
+ spec.version = version
10
13
  spec.authors = ["NARUSE, Yui"]
11
14
  spec.email = ["naruse@airemix.jp"]
12
15
 
@@ -14,6 +17,7 @@ Gem::Specification.new do |spec|
14
17
  spec.description = %q{HTTP client api for Ruby.}
15
18
  spec.homepage = "https://github.com/ruby/net-http"
16
19
  spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
20
+ spec.licenses = ["Ruby", "BSD-2-Clause"]
17
21
 
18
22
  spec.metadata["homepage_uri"] = spec.homepage
19
23
  spec.metadata["source_code_uri"] = spec.homepage
@@ -21,9 +25,10 @@ Gem::Specification.new do |spec|
21
25
  # Specify which files should be added to the gem when it is released.
22
26
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
27
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ `git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
29
  end
26
30
  spec.bindir = "exe"
27
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
31
  spec.require_paths = ["lib"]
32
+
33
+ spec.add_dependency "uri"
29
34
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1.pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - NARUSE, Yui
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-01 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-04-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ name: uri
20
+ prerelease: false
21
+ type: :runtime
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: HTTP client api for Ruby.
14
28
  email:
15
29
  - naruse@airemix.jp
@@ -17,10 +31,11 @@ executables: []
17
31
  extensions: []
18
32
  extra_rdoc_files: []
19
33
  files:
34
+ - ".github/dependabot.yml"
20
35
  - ".github/workflows/test.yml"
21
36
  - ".gitignore"
22
37
  - Gemfile
23
- - Gemfile.lock
38
+ - LICENSE.txt
24
39
  - README.md
25
40
  - Rakefile
26
41
  - bin/console
@@ -36,15 +51,16 @@ files:
36
51
  - lib/net/http/response.rb
37
52
  - lib/net/http/responses.rb
38
53
  - lib/net/http/status.rb
39
- - lib/net/http/version.rb
40
54
  - lib/net/https.rb
41
55
  - net-http.gemspec
42
56
  homepage: https://github.com/ruby/net-http
43
- licenses: []
57
+ licenses:
58
+ - Ruby
59
+ - BSD-2-Clause
44
60
  metadata:
45
61
  homepage_uri: https://github.com/ruby/net-http
46
62
  source_code_uri: https://github.com/ruby/net-http
47
- post_install_message:
63
+ post_install_message:
48
64
  rdoc_options: []
49
65
  require_paths:
50
66
  - lib
@@ -55,12 +71,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
55
71
  version: 2.6.0
56
72
  required_rubygems_version: !ruby/object:Gem::Requirement
57
73
  requirements:
58
- - - ">="
74
+ - - ">"
59
75
  - !ruby/object:Gem::Version
60
- version: '0'
76
+ version: 1.3.1
61
77
  requirements: []
62
- rubygems_version: 3.2.0.pre1
63
- signing_key:
78
+ rubygems_version: 3.3.3
79
+ signing_key:
64
80
  specification_version: 4
65
81
  summary: HTTP client api for Ruby.
66
82
  test_files: []
data/Gemfile.lock DELETED
@@ -1,23 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- net-http (0.1.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- power_assert (1.1.5)
10
- rake (13.0.1)
11
- test-unit (3.3.5)
12
- power_assert
13
-
14
- PLATFORMS
15
- ruby
16
-
17
- DEPENDENCIES
18
- net-http!
19
- rake
20
- test-unit
21
-
22
- BUNDLED WITH
23
- 2.1.4
@@ -1,5 +0,0 @@
1
- module Net
2
- module Http
3
- VERSION = "0.1.0"
4
- end
5
- end