httpclient 2.8.2.4 → 2.9.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
- SHA1:
3
- metadata.gz: 241dd999985ce751afe54cf45ca1d2df8a4b05c7
4
- data.tar.gz: 9a4021e0b3f9f1a9a40ecb56f48b139217d51621
2
+ SHA256:
3
+ metadata.gz: e1df0b78f53d502000683d048be1316f156247e5e17995f5fb04a125302bdb4e
4
+ data.tar.gz: d533eeaf2333f2f35820441a74cdf84075e5227d4fb0648f4e2ea4d2099bd511
5
5
  SHA512:
6
- metadata.gz: 2d306ba29a27b6e1e8507141b83a162c8b4c97ac03b4daf61982883be51ef0831cd79ff1d7e90047c5cda4ca1f775d6609d645a01a7ce289cb7e2a99519637d6
7
- data.tar.gz: c3fe5f9e0e6ab7b2e4afd0560ff2ec8f01e736d18dc66e431d6d3f1b89481e20acee6d4d4eadb8c2a6d03c27b6ecc3f0175e84dccaefa74e461615f579f93716
6
+ metadata.gz: 94024e0c5b5bd08800d9b3989151ab919869fabcaed65e4a375490fde0f6ea3b5bfc3ad4a5a9803bb057c5b8d36efc10c6ae5ade8a974c665d443730c63bc7ba
7
+ data.tar.gz: 768ae9ad96a73740675aca861a81075af3164a4b8875f81368a3228ecfdf523fe6c13cd9355bbe8ddd9155031ff1f908d8ec85e1bd112e0cd31d8b26d809dd6a
data/lib/hexdump.rb CHANGED
@@ -11,13 +11,13 @@ module HexDump
11
11
  result = []
12
12
  while raw = str.slice(offset, 16) and raw.length > 0
13
13
  # data field
14
- data = ''
14
+ data = ''.dup
15
15
  for v in raw.unpack('N* a*')
16
- if v.kind_of? Integer
17
- data << sprintf("%08x ", v)
18
- else
19
- v.each_byte {|c| data << sprintf("%02x", c) }
20
- end
16
+ if v.kind_of? Integer
17
+ data << sprintf("%08x ", v)
18
+ else
19
+ v.each_byte {|c| data << sprintf("%02x", c) }
20
+ end
21
21
  end
22
22
  # text field
23
23
  text = raw.tr("\000-\037\177-\377", ".")
@@ -25,12 +25,12 @@ module HexDump
25
25
  offset += 16
26
26
  # omit duplicate line
27
27
  if /^(#{regex_quote_n(raw)})+/n =~ str[offset .. -1]
28
- result << sprintf("%08x ...", offset)
29
- offset += $&.length
30
- # should print at the end
31
- if offset == str.length
32
- result << sprintf("%08x %-36s %s", offset-16, data, text)
33
- end
28
+ result << sprintf("%08x ...", offset)
29
+ offset += $&.length
30
+ # should print at the end
31
+ if offset == str.length
32
+ result << sprintf("%08x %-36s %s", offset-16, data, text)
33
+ end
34
34
  end
35
35
  end
36
36
  result
@@ -238,7 +238,7 @@ module HTTP
238
238
  if defined?(Encoding::ASCII_8BIT)
239
239
  def set_body_encoding
240
240
  if type = self.content_type
241
- OpenURI::Meta.init(o = '')
241
+ OpenURI::Meta.init(o = ''.dup)
242
242
  o.meta_add_field('content-type', type)
243
243
  @body_encoding = o.encoding
244
244
  end
@@ -491,7 +491,7 @@ module HTTP
491
491
  # String.
492
492
  #
493
493
  # assert: @size is not nil
494
- def dump(header = '', dev = '')
494
+ def dump(header = '', dev = ''.dup)
495
495
  if @body.is_a?(Parts)
496
496
  dev << header
497
497
  @body.parts.each do |part|
@@ -521,7 +521,7 @@ module HTTP
521
521
  # reason. (header is dumped to dev, too)
522
522
  # If no dev (the second argument) given, this method returns a dumped
523
523
  # String.
524
- def dump_chunked(header = '', dev = '')
524
+ def dump_chunked(header = '', dev = ''.dup)
525
525
  dev << header
526
526
  if @body.is_a?(Parts)
527
527
  @body.parts.each do |part|
@@ -574,7 +574,7 @@ module HTTP
574
574
  end
575
575
 
576
576
  def dump_file(io, dev, sz)
577
- buf = ''
577
+ buf = ''.dup
578
578
  rest = sz
579
579
  while rest > 0
580
580
  n = io.read([rest, @chunk_size].min, buf)
@@ -585,7 +585,7 @@ module HTTP
585
585
  end
586
586
 
587
587
  def dump_chunks(io, dev)
588
- buf = ''
588
+ buf = ''.dup
589
589
  while !io.read(@chunk_size, buf).nil?
590
590
  dev << dump_chunk(buf)
591
591
  end
@@ -618,8 +618,8 @@ module HTTP
618
618
  if Message.file?(part)
619
619
  @as_stream = true
620
620
  @body << part
621
- if part.respond_to?(:lstat)
622
- sz = part.lstat.size
621
+ if part.respond_to?(:stat)
622
+ sz = part.stat.size
623
623
  add_size(part, sz)
624
624
  elsif part.respond_to?(:size)
625
625
  if sz = part.size
@@ -954,7 +954,7 @@ module HTTP
954
954
 
955
955
  # Dumps message (header and body) to given dev.
956
956
  # dev needs to respond to <<.
957
- def dump(dev = '')
957
+ def dump(dev = ''.dup)
958
958
  str = @http_header.dump + CRLF
959
959
  if @http_header.chunked
960
960
  dev = @http_body.dump_chunked(str, dev)
@@ -15,9 +15,27 @@ class HTTPClient
15
15
  unless defined?(SSLSocket)
16
16
 
17
17
  class JavaSocketWrap
18
+ java_import 'java.net.InetSocketAddress'
18
19
  java_import 'java.io.BufferedInputStream'
20
+
19
21
  BUF_SIZE = 1024 * 16
20
22
 
23
+ def self.normalize_timeout(timeout)
24
+ [Java::JavaLang::Integer::MAX_VALUE, timeout].min
25
+ end
26
+
27
+ def self.connect(socket, site, opts = {})
28
+ socket_addr = InetSocketAddress.new(site.host, site.port)
29
+ if opts[:connect_timeout]
30
+ socket.connect(socket_addr, normalize_timeout(opts[:connect_timeout]))
31
+ else
32
+ socket.connect(socket_addr)
33
+ end
34
+ socket.setSoTimeout(normalize_timeout(opts[:so_timeout])) if opts[:so_timeout]
35
+ socket.setKeepAlive(true) if opts[:tcp_keepalive]
36
+ socket
37
+ end
38
+
21
39
  def initialize(socket, debug_dev = nil)
22
40
  @socket = socket
23
41
  @debug_dev = debug_dev
@@ -39,7 +57,6 @@ unless defined?(SSLSocket)
39
57
  @socket.isClosed
40
58
  end
41
59
 
42
-
43
60
  def gets(rs)
44
61
  while (size = @bufstr.index(rs)).nil?
45
62
  if fill() == -1
@@ -105,11 +122,15 @@ unless defined?(SSLSocket)
105
122
  private
106
123
 
107
124
  def fill
108
- size = @instr.read(@buf)
109
- if size > 0
110
- @bufstr << String.from_java_bytes(@buf, Encoding::BINARY)[0, size]
125
+ begin
126
+ size = @instr.read(@buf)
127
+ if size > 0
128
+ @bufstr << String.from_java_bytes(@buf, Encoding::BINARY)[0, size]
129
+ end
130
+ size
131
+ rescue java.io.IOException => e
132
+ raise OpenSSL::SSL::SSLError.new("#{e.class}: #{e.getMessage}")
111
133
  end
112
- size
113
134
  end
114
135
 
115
136
  def debug(str)
@@ -267,8 +288,8 @@ unless defined?(SSLSocket)
267
288
 
268
289
  module PEMUtils
269
290
  def self.read_certificate(pem)
270
- pem = pem.sub(/-----BEGIN CERTIFICATE-----/, '').sub(/-----END CERTIFICATE-----/, '')
271
- der = pem.unpack('m*').first
291
+ cert = pem.sub(/.*?-----BEGIN CERTIFICATE-----/m, '').sub(/-----END CERTIFICATE-----.*?/m, '')
292
+ der = cert.unpack('m*').first
272
293
  cf = CertificateFactory.getInstance('X.509')
273
294
  cf.generateCertificate(ByteArrayInputStream.new(der.to_java_bytes))
274
295
  end
@@ -440,27 +461,58 @@ unless defined?(SSLSocket)
440
461
  end
441
462
 
442
463
  def self.create_socket(session)
443
- site = session.proxy || session.dest
444
- socket = Socket.new(site.host, site.port)
464
+ opts = {
465
+ :connect_timeout => session.connect_timeout * 1000,
466
+ # send_timeout is ignored in JRuby
467
+ :so_timeout => session.receive_timeout * 1000,
468
+ :tcp_keepalive => session.tcp_keepalive,
469
+ :debug_dev => session.debug_dev
470
+ }
471
+ socket = nil
445
472
  begin
446
473
  if session.proxy
474
+ site = session.proxy || session.dest
475
+ socket = JavaSocketWrap.connect(Socket.new, site, opts)
447
476
  session.connect_ssl_proxy(JavaSocketWrap.new(socket), Util.urify(session.dest.to_s))
448
477
  end
478
+ new(socket, session.dest, session.ssl_config, opts)
449
479
  rescue
450
- socket.close
480
+ socket.close if socket
451
481
  raise
452
482
  end
453
- new(socket, session.dest, session.ssl_config, session.debug_dev)
454
483
  end
455
484
 
456
485
  DEFAULT_SSL_PROTOCOL = (java.lang.System.getProperty('java.specification.version') == '1.7') ? 'TLSv1.2' : 'TLS'
457
- def initialize(socket, dest, config, debug_dev = nil)
486
+ def initialize(socket, dest, config, opts = {})
458
487
  @config = config
488
+ begin
489
+ @ssl_socket = create_ssl_socket(socket, dest, config, opts)
490
+ ssl_version = java_ssl_version(config)
491
+ @ssl_socket.setEnabledProtocols([ssl_version].to_java(java.lang.String)) if ssl_version != DEFAULT_SSL_PROTOCOL
492
+ if config.ciphers != SSLConfig::CIPHERS_DEFAULT
493
+ @ssl_socket.setEnabledCipherSuites(config.ciphers.to_java(java.lang.String))
494
+ end
495
+ ssl_connect(dest.host)
496
+ rescue java.security.GeneralSecurityException => e
497
+ raise OpenSSL::SSL::SSLError.new(e.getMessage)
498
+ rescue java.net.SocketTimeoutException => e
499
+ raise HTTPClient::ConnectTimeoutError.new(e.getMessage)
500
+ rescue java.io.IOException => e
501
+ raise OpenSSL::SSL::SSLError.new("#{e.class}: #{e.getMessage}")
502
+ end
503
+
504
+ super(@ssl_socket, opts[:debug_dev])
505
+ end
506
+
507
+ def java_ssl_version(config)
459
508
  if config.ssl_version == :auto
460
- ssl_version = DEFAULT_SSL_PROTOCOL
509
+ DEFAULT_SSL_PROTOCOL
461
510
  else
462
- ssl_version = config.ssl_version.to_s.tr('_', '.')
511
+ config.ssl_version.to_s.tr('_', '.')
463
512
  end
513
+ end
514
+
515
+ def create_ssl_context(config)
464
516
  unless config.cert_store_crl_items.empty?
465
517
  raise NotImplementedError.new('Manual CRL configuration is not yet supported')
466
518
  end
@@ -489,36 +541,24 @@ unless defined?(SSLSocket)
489
541
  tmf.init(trust_store)
490
542
  tm = tmf.getTrustManagers
491
543
 
492
- ctx = SSLContext.getInstance(ssl_version)
544
+ ctx = SSLContext.getInstance(java_ssl_version(config))
493
545
  ctx.init(km, tm, nil)
494
546
  if config.timeout
495
547
  ctx.getClientSessionContext.setSessionTimeout(config.timeout)
496
548
  end
549
+ ctx
550
+ end
497
551
 
552
+ def create_ssl_socket(socket, dest, config, opts)
553
+ ctx = create_ssl_context(config)
498
554
  factory = ctx.getSocketFactory
499
- begin
500
- ssl_socket = factory.createSocket(socket, dest.host, dest.port, true)
501
- ssl_socket.setEnabledProtocols([ssl_version].to_java(java.lang.String)) if ssl_version != DEFAULT_SSL_PROTOCOL
502
- if config.ciphers != SSLConfig::CIPHERS_DEFAULT
503
- ssl_socket.setEnabledCipherSuites(config.ciphers.to_java(java.lang.String))
504
- end
505
- ssl_socket.startHandshake
506
- ssl_session = ssl_socket.getSession
507
- @peer_cert = JavaCertificate.new(ssl_session.getPeerCertificates.first)
508
- if $DEBUG
509
- warn("Protocol version: #{ssl_session.getProtocol}")
510
- warn("Cipher: #{ssl_socket.getSession.getCipherSuite}")
511
- end
512
- post_connection_check(dest.host, @peer_cert)
513
- rescue java.security.GeneralSecurityException => e
514
- raise OpenSSL::SSL::SSLError.new(e.getMessage)
515
- rescue javax.net.ssl.SSLException => e
516
- raise OpenSSL::SSL::SSLError.new(e.getMessage)
517
- rescue java.net.SocketException => e
518
- raise OpenSSL::SSL::SSLError.new(e.getMessage)
555
+ unless socket
556
+ # Create a plain socket first to set connection timeouts on,
557
+ # then wrap it in a SSL socket so that SNI gets setup on it.
558
+ socket = javax.net.SocketFactory.getDefault.createSocket
559
+ JavaSocketWrap.connect(socket, dest, opts)
519
560
  end
520
-
521
- super(ssl_socket, debug_dev)
561
+ factory.createSocket(socket, dest.host, dest.port, true)
522
562
  end
523
563
 
524
564
  def peer_cert
@@ -527,11 +567,22 @@ unless defined?(SSLSocket)
527
567
 
528
568
  private
529
569
 
530
- def post_connection_check(hostname, wrap_cert)
570
+ def ssl_connect(hostname)
571
+ @ssl_socket.startHandshake
572
+ ssl_session = @ssl_socket.getSession
573
+ @peer_cert = JavaCertificate.new(ssl_session.getPeerCertificates.first)
574
+ if $DEBUG
575
+ warn("Protocol version: #{ssl_session.getProtocol}")
576
+ warn("Cipher: #{@ssl_socket.getSession.getCipherSuite}")
577
+ end
578
+ post_connection_check(hostname)
579
+ end
580
+
581
+ def post_connection_check(hostname)
531
582
  if !@config.verify?
532
583
  return
533
584
  else
534
- BrowserCompatHostnameVerifier.new.verify(hostname, wrap_cert.cert)
585
+ BrowserCompatHostnameVerifier.new.verify(hostname, @peer_cert.cert)
535
586
  end
536
587
  end
537
588
  end
@@ -21,7 +21,7 @@ require 'zlib'
21
21
  require 'httpclient/timeout' # TODO: remove this once we drop 1.8 support
22
22
  require 'httpclient/ssl_config'
23
23
  require 'httpclient/http'
24
- if RUBY_ENGINE == 'jruby'
24
+ if defined? JRUBY_VERSION
25
25
  require 'httpclient/jruby_ssl_socket'
26
26
  else
27
27
  require 'httpclient/ssl_socket'
@@ -76,7 +76,7 @@ class HTTPClient
76
76
  def to_s # :nodoc:
77
77
  addr
78
78
  end
79
-
79
+
80
80
  # Returns true if scheme, host and port of the given URI matches with this.
81
81
  def match(uri)
82
82
  (@scheme == uri.scheme) and (@host == uri.host) and (@port == uri.port.to_i)
@@ -105,6 +105,8 @@ class HTTPClient
105
105
  attr_accessor :debug_dev
106
106
  # Boolean value for Socket#sync
107
107
  attr_accessor :socket_sync
108
+ # Boolean value to send TCP keepalive packets; no timing settings exist at present
109
+ attr_accessor :tcp_keepalive
108
110
 
109
111
  attr_accessor :connect_timeout
110
112
  # Maximum retry count. 0 for infinite.
@@ -137,6 +139,7 @@ class HTTPClient
137
139
  @protocol_version = nil
138
140
  @debug_dev = client.debug_dev
139
141
  @socket_sync = true
142
+ @tcp_keepalive = false
140
143
  @chunk_size = ::HTTP::Message::Body::DEFAULT_CHUNK_SIZE
141
144
 
142
145
  @connect_timeout = 60
@@ -216,6 +219,7 @@ class HTTPClient
216
219
  sess = Session.new(@client, site, @agent_name, @from)
217
220
  sess.proxy = via_proxy ? @proxy : nil
218
221
  sess.socket_sync = @socket_sync
222
+ sess.tcp_keepalive = @tcp_keepalive
219
223
  sess.requested_version = @protocol_version if @protocol_version
220
224
  sess.connect_timeout = @connect_timeout
221
225
  sess.connect_retry = @connect_retry
@@ -437,6 +441,8 @@ class HTTPClient
437
441
  attr_accessor :proxy
438
442
  # Boolean value for Socket#sync
439
443
  attr_accessor :socket_sync
444
+ # Boolean value to send TCP keepalive packets; no timing settings exist at present
445
+ attr_accessor :tcp_keepalive
440
446
  # Requested protocol version
441
447
  attr_accessor :requested_version
442
448
  # Device for dumping log for debugging
@@ -464,6 +470,7 @@ class HTTPClient
464
470
  @dest = dest
465
471
  @proxy = nil
466
472
  @socket_sync = true
473
+ @tcp_keepalive = false
467
474
  @requested_version = nil
468
475
 
469
476
  @debug_dev = nil
@@ -606,17 +613,16 @@ class HTTPClient
606
613
  clean_local = @socket_local.host.delete("[]")
607
614
  socket = TCPSocket.new(clean_host, port, clean_local, @socket_local.port)
608
615
  end
616
+ socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) if @tcp_keepalive
609
617
  if @debug_dev
610
618
  @debug_dev << "! CONNECTION ESTABLISHED\n"
611
619
  socket.extend(DebugSocket)
612
620
  socket.debug_dev = @debug_dev
613
621
  end
614
622
  rescue SystemCallError => e
615
- e.message << " (#{host}:#{port})"
616
- raise
623
+ raise e.class, e.message + " (#{host}:#{port})"
617
624
  rescue SocketError => e
618
- e.message << " (#{host}:#{port})"
619
- raise
625
+ raise e.class, e.message + " (#{host}:#{port})"
620
626
  end
621
627
  socket
622
628
  end
@@ -944,7 +950,7 @@ class HTTPClient
944
950
  end
945
951
 
946
952
  def empty_bin_str
947
- str = ''
953
+ str = ''.dup
948
954
  str.force_encoding('BINARY') if str.respond_to?(:force_encoding)
949
955
  str
950
956
  end