net-http 0.1.0 → 0.2.1.pre1

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: 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