manticore 0.7.0-java → 0.9.0-java

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: 508b2d2c3ce9fc25ca2ac915954807783cc8266aac23936a6896d103121a2f0c
4
- data.tar.gz: a8ac97bd7fb22df23ac164d5f06f3a35c926d9eac57ae7bfb8e84c0c8d9e7907
3
+ metadata.gz: 281400646207bd8e4b34bed53610787e5b5d222c72fe5e82dd1d42d8f071f1be
4
+ data.tar.gz: 421a4a8f1c4f8e06a7d12377812da2613877877904c73d3d8375348fbb9e2103
5
5
  SHA512:
6
- metadata.gz: 56a60c747c187ddd1255e5da7cbc2ad78570c2ebf5390841298f45366133b48c857df8ac7b7b8bdbbb5194546e3b3112a8c08b5109c66a1299f6fe787ae890fc
7
- data.tar.gz: 78e29e767486b820f1d1a280ae4db04b1ad33eccb7da7538bae9e48116305b44d069dc13ee322484146f9cce28b6edf71e868d73be8e075957404c50a99416eb
6
+ metadata.gz: c72e625dc8aa526a1336c239ced7d46cebf9b16817f9c309b1caaa80028a14e959cbe342e745db69dfef8fd284b1413085a518322406ecab3e7a28725815a34f
7
+ data.tar.gz: 37774c46167abf4c8cb637580dc2cf91afc4311b8a284c17d48441ba00824ba0af41e47121780837caffef60632aa3c26bb075b323011a2fcb56fb055495e83c
data/.travis.yml CHANGED
@@ -1,20 +1,22 @@
1
- dist: xenial
1
+ dist: trusty # due Oracle JDK
2
2
  language: ruby
3
3
  cache:
4
4
  - bundler
5
5
  - directories:
6
6
  - $HOME/.m2
7
- rvm:
8
- - jruby-9.1.17.0 # Ruby 2.3
9
- - jruby-9.2.13.0 # Ruby 2.5
10
- jdk:
11
- - openjdk9
12
- - openjdk11
13
7
  before_install:
14
8
  - gem install bundler -v 1.17.3
15
9
  matrix:
16
10
  include:
17
11
  - rvm: jruby-head
18
- jdk: openjdk10
12
+ jdk: openjdk11
13
+ - rvm: jruby-9.3.4.0 # Ruby 2.6
14
+ jdk: openjdk11
15
+ - rvm: jruby-9.2.20.0 # Ruby 2.5
16
+ jdk: oraclejdk8
17
+ - rvm: jruby-9.2.20.0
18
+ jdk: openjdk11
19
+ - rvm: jruby-9.1.17.0 # Ruby 2.3
20
+ jdk: openjdk8
19
21
  allow_failures:
20
22
  - rvm: jruby-head
data/CHANGELOG.md CHANGED
@@ -1,10 +1,32 @@
1
- ## v0.6
1
+ ### v0.9.0
2
+
3
+ * [feat] revamped client.close to release resources (#108)
4
+ * [fix] null password handling when loading keystore
5
+ * [fix] ambiguous Java method selection
6
+ * [refactor] revisit hostname verification (#103)
7
+ * [feat] ssl[:trust_strategy]: wrapper for `org.apache.http.conn.ssl.TrustStrategy` (#106)
8
+
9
+ ### v0.8.0
10
+
11
+ * [feat] restore compat with (legacy) verify: false (#102)
12
+ * Accept untrusted certs when SSL verify is disabled (#100)
13
+ * [deps] update http-client to 4.5.13 (#99)
14
+
15
+ ## v0.7
16
+
17
+ ### v0.7.1
18
+
19
+ * Don't override certificates with same Subject (#93)
20
+ * Set Java cause for ManticoreException types (#96)
21
+ * Fix SSL handshake hang indefinitely (#98)
2
22
 
3
23
  ### v0.7.0
4
24
 
5
25
  * Drop support for JRuby 1.7. It probably still works, but we don't test against it anymore
6
26
  * Fix a thread safety issue with regards to adding requests to the parallel execution queue while the client is already processing a queue (#80)
7
27
 
28
+ ## v0.6
29
+
8
30
  ### v0.6.4
9
31
 
10
32
  * client_cert and client_key now take the literal keys as strings, OpenSSL::X509::Certificate/OpenSSL::PKey::Pkey instances, or key file paths. (#77)
data/Gemfile CHANGED
@@ -4,13 +4,15 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :development, :test do
7
- gem "net-http-server", "~> 0.2"
7
+ gem "rake-compiler", require: false
8
+ gem "simplecov"
9
+
8
10
  gem "rspec", "~> 3.0"
9
11
  gem "rspec-its"
10
- gem "httpclient", "~> 2.3"
11
- gem "rack", ">= 2.1.4"
12
- gem "rake-compiler"
13
- gem "gserver"
14
- gem "simplecov"
15
- gem "json"
12
+
13
+ gem "rack", ">= 2.1.4", require: false
14
+ gem "json", require: false
15
+ gem "webrick", require: false
16
+ gem "net-http-server", require: false
17
+ gem "gserver", require: false
16
18
  end
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Note**: While I'll continue to maintain the library here, I've moved the canonical copy to Gitlab at https://gitlab.com/cheald/manticore - it is preferred that you submit issues and PRs there.
4
4
 
5
- [![Build Status](https://travis-ci.org/cheald/manticore.svg?branch=master)](https://travis-ci.org/cheald/manticore)
5
+ [![Build Status](https://app.travis-ci.com/cheald/manticore.svg?branch=master)](https://app.travis-ci.com/cheald/manticore)
6
6
 
7
7
  Manticore is a fast, robust HTTP client built on the Apache HTTPClient libraries. It is only compatible with JRuby.
8
8
 
@@ -90,7 +90,11 @@ For detailed documentation, see the [full Manticore::Client documentation](http:
90
90
  Rather than using the Facade, you can create your own standalone Client instances. When you create a `Client`, you will pass various parameters that it will use to set up the pool.
91
91
 
92
92
  ```ruby
93
- client = Manticore::Client.new(request_timeout: 5, connect_timeout: 5, socket_timeout: 5, pool_max: 10, pool_max_per_route: 2)
93
+ client = Manticore::Client.new(request_timeout: 5,
94
+ connect_timeout: 5,
95
+ socket_timeout: 5,
96
+ pool_max: 10,
97
+ pool_max_per_route: 2)
94
98
  ```
95
99
 
96
100
  Then, you can make requests from the client. Pooling and route maximum constraints are automatically managed:
@@ -122,11 +126,20 @@ per-route concurrency limits, and other neat things. In general, you should crea
122
126
  To set this up, you might create 2 pools, each configured for the task:
123
127
 
124
128
  ```ruby
125
- general_http_client = Manticore::Client.new connect_timeout: 10, socket_timeout: 10, request_timeout: 10, follow_redirects: true, max_per_route: 2
129
+ general_http_client = Manticore::Client.new(connect_timeout: 10,
130
+ socket_timeout: 10,
131
+ request_timeout: 10,
132
+ follow_redirects: true,
133
+ max_per_route: 2)
126
134
  # With an OpenSSL CA store
127
- proxied_backend_client = Manticore::Client.new proxy: "https://backend.internal:4242", ssl: {ca_file: "my_certs.pem"}
135
+ proxied_backend_client = Manticore::Client.new(proxy: "https://backend.internal:4242",
136
+ ssl: { ca_file: "my_certs.pem" })
128
137
  # Or with a .jks truststore
129
- # proxied_backend_client = Manticore::Client.new proxy: "https://backend.internal:4242", ssl: {truststore: "./truststore.jks", truststore_password: "s3cr3t"}
138
+ proxied_backend_client = Manticore::Client.new(proxy: "https://backend.internal:4242",
139
+ ssl: {
140
+ truststore: "./truststore.jks",
141
+ truststore_password: "s3cr3t"
142
+ })
130
143
  ```
131
144
 
132
145
  This would create 2 separate request pools; the first would be configured with generous timeouts and redirect following, and would use the system
data/Rakefile CHANGED
@@ -36,6 +36,7 @@ task :generate_certs do
36
36
  # Create the CA
37
37
  "#{openssl} genrsa 4096 | #{openssl} pkcs8 -topk8 -nocrypt -out #{root}/root-ca.key",
38
38
  "#{openssl} req -sha256 -x509 -newkey rsa:4096 -nodes -key #{root}/root-ca.key -sha256 -days 365 -out #{root}/root-ca.crt -subj \"/C=US/ST=The Internet/L=The Internet/O=Manticore CA/OU=Manticore/CN=localhost\"",
39
+ "#{openssl} req -sha256 -x509 -newkey rsa:4096 -nodes -key #{root}/root-ca.key -sha256 -days 365 -out #{root}/root-untrusted-ca.crt -subj \"/C=US/ST=The Darknet/L=The Darknet/O=Manticore CA/OU=Manticore/CN=localhost\"",
39
40
 
40
41
  # Create the client CSR, key, and signed cert
41
42
  "#{openssl} genrsa 4096 | #{openssl} pkcs8 -topk8 -nocrypt -out #{root}/client.key",
@@ -48,6 +49,7 @@ task :generate_certs do
48
49
  "#{openssl} req -sha256 -key #{root}/host.key -newkey rsa:4096 -out #{root}/host.csr -subj \"/C=US/ST=The Internet/L=The Internet/O=Manticore Host/OU=Manticore/CN=localhost\"",
49
50
  "#{openssl} x509 -req -in #{root}/host.csr -CA #{root}/root-ca.crt -CAkey #{root}/root-ca.key -CAcreateserial -out #{root}/host.crt -sha256 -days 1",
50
51
  "#{openssl} x509 -req -in #{root}/host.csr -CA #{root}/root-ca.crt -CAkey #{root}/root-ca.key -CAcreateserial -out #{root}/host-expired.crt -sha256 -days -7",
52
+ "#{openssl} x509 -req -in #{root}/host.csr -CA #{root}/root-untrusted-ca.crt -CAkey #{root}/root-ca.key -CAcreateserial -out #{root}/host-untrusted.crt -sha256 -days 1",
51
53
 
52
54
  "#{keytool} -import -file #{root}/root-ca.crt -alias rootCA -keystore #{root}/truststore.jks -noprompt -storepass test123",
53
55
  "#{openssl} pkcs12 -export -clcerts -out #{root}/client.p12 -inkey #{root}/client.key -in #{root}/client.crt -certfile #{root}/root-ca.crt -password pass:test123",
@@ -0,0 +1,119 @@
1
+ module Manticore
2
+ class Client
3
+ ##
4
+ # TrustStrategies is a utility module that provides helpers for
5
+ # working with org.apache.http.conn.ssl.TrustStrategy
6
+ module TrustStrategies
7
+ # Coerces to org.apache.http.conn.ssl.TrustStrategy, allowing nil pass-through
8
+ #
9
+ # @overload coerce(coercible)
10
+ # @param coercible [nil|TrustStrategy]
11
+ # @return [nil,TrustStrategy]
12
+ # @overload coerce(coercible)
13
+ # @param coercible [Proc<(Array<OpenSSL::X509::Certificate>,String)>:Boolean]
14
+ # A proc that accepts two arguments and returns a boolean value, and is effectively a
15
+ # Ruby-native implementation of `org.apache.http.conn.ssl.TrustStrategy#isTrusted`.
16
+ # @param cert_chain [Enumerable<OpenSSL::X509::Certificate>]: the peer's certificate chain
17
+ # @param auth_type [String]: the authentication type based on the client certificate
18
+ # @raise [OpenSSL::X509::CertificateError]: thrown if the certificate is not trusted or invalid
19
+ # @return [Boolean]: true if the certificate can be trusted without verification by the trust manager,
20
+ # false otherwise.
21
+ # @example: CA Trusted Fingerprint
22
+ # ca_trusted_fingerprint = lambda do |cert_chain, type|
23
+ # cert_chain.lazy
24
+ # .map(&:to_der)
25
+ # .map(&::Digest::SHA256.method(:hexdigest))
26
+ # .include?("324a87eebb19265ffb675dc345eb0f3b5d9de3f015159227a00fe552291d4cc4")
27
+ # end
28
+ # TrustStrategies.coerce(ca_trusted_fingerprint)
29
+ def self.coerce(coercible)
30
+ case coercible
31
+ when org.apache.http.conn.ssl.TrustStrategy, nil then coercible
32
+ when ::Proc then CustomTrustStrategy.new(coercible)
33
+ else fail(ArgumentError, "No implicit conversion of #{coercible} to #{self}")
34
+ end
35
+ end
36
+
37
+ # Combines two possibly-nil TrustStrategies-coercible objects into a
38
+ # single org.apache.http.conn.ssl.TrustStrategy, or to nil if both are nil.
39
+ #
40
+ # @param lhs [nil|TrustStrategie#coerce]
41
+ # @param rhs [nil|TrustStrategies#coerce]
42
+ # @return [nil,org.apache.http.conn.ssl.TrustStrategy]
43
+ def self.combine(lhs, rhs)
44
+ return coerce(rhs) if lhs.nil?
45
+ return coerce(lhs) if rhs.nil?
46
+
47
+ CombinedTrustStrategy.new(coerce(lhs), coerce(rhs))
48
+ end
49
+ end
50
+
51
+ ##
52
+ # @api private
53
+ # A CombinedTrustStrategy can be used to bypass the Trust Manager if
54
+ # *EITHER* TrustStrategy trusts the provided certificate chain.
55
+ # @see TrustStrategies::combine
56
+ class CombinedTrustStrategy
57
+ include org.apache.http.conn.ssl.TrustStrategy
58
+
59
+ ##
60
+ # @api private
61
+ # @see TrustStrategies::combine
62
+ def initialize(lhs, rhs)
63
+ @lhs = lhs
64
+ @rhs = rhs
65
+ super()
66
+ end
67
+
68
+ ##
69
+ # @override (see org.apache.http.conn.ssl.TrustStrategy#isTrusted)
70
+ def trusted?(chain, type)
71
+ @lhs.trusted?(chain, type) || @rhs.trusted?(chain, type)
72
+ end
73
+ end
74
+
75
+
76
+ ##
77
+ # @api private
78
+ # A CustomTrustStrategy is an org.apache.http.conn.ssl.TrustStrategy
79
+ # defined with a proc that uses Ruby OpenSSL::X509::Certificates
80
+ # @see TrustStrategies::coerce(Proc)
81
+ class CustomTrustStrategy
82
+ include org.apache.http.conn.ssl.TrustStrategy
83
+
84
+ ##
85
+ # @see TrustStrategies.coerce(Proc)
86
+ def initialize(proc)
87
+ fail(ArgumentError, "2-arity proc required") unless proc.arity == 2
88
+ @trust_strategy = proc
89
+ end
90
+
91
+ CONVERT_JAVA_CERTIFICATE_TO_RUBY = -> (java_cert) { ::OpenSSL::X509::Certificate.new(java_cert.encoded) }
92
+ private_constant :CONVERT_JAVA_CERTIFICATE_TO_RUBY
93
+
94
+ ##
95
+ # @override (see org.apache.http.conn.ssl.TrustStrategy#isTrusted)
96
+ def trusted?(java_chain, type)
97
+ @trust_strategy.call(java_chain.lazy.map(&CONVERT_JAVA_CERTIFICATE_TO_RUBY), String.new(type))
98
+ rescue OpenSSL::X509::CertificateError => e
99
+ raise java_certificate_exception(e)
100
+ end
101
+
102
+ private
103
+
104
+ begin
105
+ # Ruby exceptions can be converted to Throwable since JRuby 9.2
106
+ Exception.new("sentinel").to_java(java.lang.Throwable)
107
+ def java_certificate_exception(ruby_certificate_error)
108
+ throwable = ruby_certificate_error.to_java(java.lang.Throwable)
109
+ java.security.cert.CertificateException.new(throwable)
110
+ end
111
+ rescue TypeError
112
+ def java_certificate_exception(ruby_certificate_error)
113
+ message = ruby_certificate_error.message
114
+ java.security.cert.CertificateException.new(message)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -69,10 +69,8 @@ module Manticore
69
69
  include_package "org.apache.http.client.config"
70
70
  include_package "org.apache.http.config"
71
71
  include_package "org.apache.http.conn.socket"
72
- include_package "org.apache.http.impl"
73
72
  include_package "org.apache.http.impl.client"
74
73
  include_package "org.apache.http.impl.conn"
75
- include_package "org.apache.http.impl.auth"
76
74
  include_package "org.apache.http.entity"
77
75
  include_package "org.apache.http.message"
78
76
  include_package "org.apache.http.params"
@@ -80,28 +78,46 @@ module Manticore
80
78
  include_package "org.apache.http.auth"
81
79
  include_package "java.util.concurrent"
82
80
  include_package "org.apache.http.client.protocol"
83
- include_package "org.apache.http.conn.ssl"
84
81
  include_package "java.security.cert"
85
82
  include_package "java.security.spec"
86
83
  include_package "java.security"
87
- include_package "org.apache.http.client.utils"
88
84
  java_import "org.apache.http.HttpHost"
89
85
  java_import "javax.net.ssl.SSLContext"
90
86
  java_import "org.manticore.HttpGetWithEntity"
91
87
  java_import "org.manticore.HttpDeleteWithEntity"
92
88
  java_import "org.apache.http.auth.UsernamePasswordCredentials"
89
+ java_import "org.apache.http.conn.ssl.DefaultHostnameVerifier"
90
+ java_import "org.apache.http.conn.ssl.NoopHostnameVerifier"
91
+ java_import "org.apache.http.conn.ssl.SSLConnectionSocketFactory"
92
+ java_import "org.apache.http.conn.ssl.TrustAllStrategy"
93
+ java_import "org.apache.http.conn.ssl.TrustSelfSignedStrategy"
94
+ java_import "org.apache.http.client.utils.URIBuilder"
95
+ java_import "org.apache.http.impl.DefaultConnectionReuseStrategy"
96
+ java_import "org.apache.http.impl.auth.BasicScheme"
97
+ java_import "org.apache.http.ssl.SSLContextBuilder"
98
+ java_import "org.apache.http.ssl.TrustStrategy"
93
99
 
94
100
  # This is a class rather than a proc because the proc holds a closure around
95
101
  # the instance of the Client that creates it.
96
102
  class ExecutorThreadFactory
97
- include ::Java::JavaUtilConcurrent::ThreadFactory
103
+ include java.util.concurrent.ThreadFactory
104
+
105
+ java_import 'java.lang.Thread'
106
+
107
+ @@factory_no = java.util.concurrent.atomic.AtomicInteger.new
108
+
109
+ def initialize(client)
110
+ @thread_no = java.util.concurrent.atomic.AtomicInteger.new
111
+ @name_prefix = "manticore##{client.object_id}-#{@@factory_no.increment_and_get}-"
112
+ end
98
113
 
99
114
  def newThread(runnable)
100
- thread = Executors.defaultThreadFactory.newThread(runnable)
101
- thread.daemon = true
102
- return thread
115
+ thread = Thread.new(runnable, @name_prefix + @thread_no.increment_and_get.to_s)
116
+ thread.setDaemon(true)
117
+ thread
103
118
  end
104
119
  end
120
+ private_constant :ExecutorThreadFactory
105
121
 
106
122
  include ProxiesInterface
107
123
 
@@ -159,10 +175,12 @@ module Manticore
159
175
  # @option options [Hash] ssl Hash of options for configuring SSL
160
176
  # @option options [Array<String>] ssl[:protocols] (nil) A list of protocols that Manticore should accept
161
177
  # @option options [Array<String>] ssl[:cipher_suites] (nil) A list of cipher suites that Manticore should accept
162
- # @option options [Symbol] ssl[:verify] (:strict) Hostname verification setting. Set to `:disable` to turn off hostname verification. Setting to `:browser` will
178
+ # @option options [Symbol] ssl[:verify] (:default) Hostname verification setting. Set to `:none` to turn off hostname verification. Setting to `:browser` will
163
179
  # cause Manticore to accept a certificate for *.foo.com for all subdomains and sub-subdomains (eg a.b.foo.com).
164
- # The default `:strict` is like `:browser` except it'll only accept a single level of subdomains for wildcards,
180
+ # The default `:default` is like `:browser` but more strict - only accepts a single level of subdomains for wildcards,
165
181
  # eg `b.foo.com` will be accepted for a `*.foo.com` certificate, but `a.b.foo.com` will not be.
182
+ # @option options [Client::TrustStrategiesInterface] ssl[:trust_strategy] (nil) A trust strategy to use in addition to any built by `ssl[:verify]`.
183
+ # @see Client::TrustStrategiesInterface#coerce
166
184
  # @option options [String] ssl[:truststore] (nil) Path to a custom trust store to use the verifying SSL connections
167
185
  # @option options [String] ssl[:truststore_password] (nil) Password used for decrypting the server trust store
168
186
  # @option options [String] ssl[:truststore_type] (nil) Format of the trust store, ie "JKS" or "PKCS12". If left nil, the type will be inferred from the truststore filename.
@@ -175,8 +193,8 @@ module Manticore
175
193
  # @option options [boolean] ssl[:track_state] (false) Turn on or off connection state tracking. This helps prevent SSL information from leaking across threads, but means that connections
176
194
  # can't be shared across those threads. This should generally be left off unless you know what you're doing.
177
195
  def initialize(options = {})
178
- @finalizers = []
179
- self.class.shutdown_on_finalize self, @finalizers
196
+ @finalizer = Finalizer.new
197
+ self.class.shutdown_on_finalize self, @finalizer
180
198
 
181
199
  builder = client_builder
182
200
  builder.set_user_agent options.fetch(:user_agent, "Manticore #{VERSION}")
@@ -201,11 +219,7 @@ module Manticore
201
219
  builder.set_connection_reuse_strategy DefaultConnectionReuseStrategy.new
202
220
  end
203
221
 
204
- socket_config_builder = SocketConfig.custom
205
- socket_config_builder.set_so_timeout(options.fetch(:socket_timeout, DEFAULT_SOCKET_TIMEOUT) * 1000)
206
- socket_config_builder.set_tcp_no_delay(options.fetch(:tcp_no_delay, true))
207
- builder.set_default_socket_config socket_config_builder.build
208
-
222
+ builder.set_default_socket_config socket_config_from_options(options)
209
223
  builder.set_connection_manager pool(options)
210
224
 
211
225
  request_config = RequestConfig.custom
@@ -346,21 +360,58 @@ module Manticore
346
360
  end
347
361
  end
348
362
 
349
- # Free resources associated with the CloseableHttpClient
350
- def close
351
- @client.close if @client
363
+ # Release resources held by this client, namely:
364
+ # - close the internal http client
365
+ # - shutdown the connection pool
366
+ # - stops accepting async requests in the executor
367
+ #
368
+ # After this call the client is no longer usable.
369
+ # @note In versions before 0.9 this method only closed the underlying `CloseableHttpClient`
370
+ def close(await: nil)
371
+ ObjectSpace.undefine_finalizer(self)
372
+ @finalizer.call # which does ~ :
373
+ # @executor&.shutdown rescue nil
374
+ # @client&.close
375
+ # @pool&.shutdown rescue nil
376
+ @async_requests.close
377
+ case await
378
+ when false, nil
379
+ @executor&.shutdown_now rescue nil
380
+ when Numeric
381
+ # NOTE: the concept of awaiting gracefully might/should also be used with the pool/client closing
382
+ millis = java.util.concurrent.TimeUnit::MILLISECONDS
383
+ @executor&.await_termination(await * 1000, millis) rescue nil
384
+ else
385
+ nil
386
+ end
387
+ end
388
+
389
+ # @private
390
+ class Finalizer
391
+
392
+ def initialize
393
+ @_objs = []
394
+ end
395
+
396
+ def push(obj, args)
397
+ @_objs.unshift [java.lang.ref.WeakReference.new(obj), Array(args)]
398
+ end
399
+
400
+ def call(id = nil) # when called on finalization an object id arg is passed
401
+ @_objs.each { |obj, args| obj.get&.send(*args) rescue nil }
402
+ end
403
+
352
404
  end
405
+ private_constant :Finalizer
353
406
 
354
407
  # Get at the underlying ExecutorService used to invoke asynchronous calls.
355
408
  def executor
356
- create_executor_if_needed
357
- @executor
409
+ @executor ||= create_executor
358
410
  end
359
411
 
360
- def self.shutdown_on_finalize(client, objs)
361
- ObjectSpace.define_finalizer client, -> {
362
- objs.each { |obj, args| obj.send(*args) rescue nil }
363
- }
412
+ # @private
413
+ def self.shutdown_on_finalize(client, finalizer)
414
+ ObjectSpace.define_finalizer(client, finalizer)
364
415
  end
365
416
 
366
417
  protected
@@ -368,8 +419,9 @@ module Manticore
368
419
  # Takes an object and a message to pass to the object to destroy it. This is done rather than
369
420
  # a proc to avoid creating a closure that would maintain a reference to this client, which
370
421
  # would prevent the client from being cleaned up.
422
+ # @private
371
423
  def finalize(object, args)
372
- @finalizers << [WeakRef.new(object), Array(args)]
424
+ @finalizer.push(object, args)
373
425
  end
374
426
 
375
427
  def url_as_regex(url)
@@ -389,7 +441,7 @@ module Manticore
389
441
 
390
442
  # :nocov:
391
443
  if options[:ignore_ssl_validation]
392
- $stderr.puts "The options[:ignore_ssl_validation] setting is deprecated in favor of options[:ssl][:verify]"
444
+ warn "The options[:ignore_ssl_validation] setting is deprecated in favor of options[:ssl][:verify]"
393
445
  options[:ssl] ||= {}
394
446
  options[:ssl] = {:verify => !options.delete(:ignore_ssl_validation)}.merge(options[:ssl])
395
447
  end
@@ -407,22 +459,30 @@ module Manticore
407
459
  cm.set_validate_after_inactivity options.fetch(:check_connection_timeout, 2_000)
408
460
  cm.set_default_max_per_route options.fetch(:pool_max_per_route, @max_pool_size)
409
461
  cm.set_max_total @max_pool_size
462
+ cm.set_default_socket_config socket_config_from_options(options)
463
+
410
464
  finalize cm, :shutdown
411
465
  end
412
466
  end
413
467
  end
468
+
469
+ def socket_config_from_options(options)
470
+ socket_config_builder = SocketConfig.custom
471
+ socket_config_builder.set_so_timeout(options.fetch(:socket_timeout, DEFAULT_SOCKET_TIMEOUT) * 1000)
472
+ socket_config_builder.set_tcp_no_delay(options.fetch(:tcp_no_delay, true))
473
+ socket_config_builder.build
474
+ end
414
475
 
415
- def create_executor_if_needed
416
- return @executor if @executor
417
- @executor = Executors.new_cached_thread_pool(ExecutorThreadFactory.new)
418
- finalize @executor, :shutdown
476
+ def create_executor
477
+ executor = Executors.new_cached_thread_pool(ExecutorThreadFactory.new(self))
478
+ finalize executor, :shutdown
479
+ executor
419
480
  end
420
481
 
421
482
  def request(klass, url, options, &block)
422
483
  req, context = request_from_options(klass, url, options)
423
484
  async = options.delete(:async)
424
485
  background = options.delete(:async_background)
425
- create_executor_if_needed if (background || async)
426
486
  response = response_object_for(req, context, &block)
427
487
 
428
488
  if async
@@ -510,14 +570,13 @@ module Manticore
510
570
 
511
571
  if @use_cookies == :per_request
512
572
  store = BasicCookieStore.new
513
- context.setAttribute(ClientContext.COOKIE_STORE, store)
573
+ context.setAttribute(ClientContext::COOKIE_STORE, store)
514
574
  end
515
575
 
516
576
  return req, context
517
577
  end
518
578
 
519
579
  def get_proxy_host(opt)
520
- host = nil
521
580
  if opt.is_a? String
522
581
  uri = URI.parse(opt)
523
582
  if uri.host
@@ -602,23 +661,30 @@ module Manticore
602
661
 
603
662
  # Configure the SSL Context
604
663
  def ssl_socket_factory_from_options(ssl_options)
605
- trust_store = trust_strategy = nil
606
-
607
- verifier = SSLConnectionSocketFactory::STRICT_HOSTNAME_VERIFIER
608
- case ssl_options.fetch(:verify, :strict)
609
- when false, :disable, :none
610
- trust_store = nil
611
- trust_strategy = TrustSelfSignedStrategy.new
664
+ trust_strategy = nil
665
+
666
+ case ssl_options.fetch(:verify, :default)
667
+ when :none, :disable
668
+ trust_strategy = TrustAllStrategy::INSTANCE
669
+ verifier = NoopHostnameVerifier::INSTANCE
670
+ when false # compatibility
671
+ trust_strategy = TrustSelfSignedStrategy::INSTANCE
612
672
  verifier = SSLConnectionSocketFactory::ALLOW_ALL_HOSTNAME_VERIFIER
613
673
  when :browser
614
674
  verifier = SSLConnectionSocketFactory::BROWSER_COMPATIBLE_HOSTNAME_VERIFIER
615
- when true, :strict, :default
675
+ when :default, true
676
+ verifier = DefaultHostnameVerifier.new
677
+ when :strict # compatibility
616
678
  verifier = SSLConnectionSocketFactory::STRICT_HOSTNAME_VERIFIER
617
679
  else
618
- raise "Invalid value for :verify. Valid values are (:all, :browser, :default)"
680
+ raise "Invalid value for :verify. Valid values are (:default, :browser, :none)"
681
+ end
682
+
683
+ if ssl_options.include?(:trust_strategy)
684
+ trust_strategy = TrustStrategies.combine(trust_strategy, ssl_options.fetch(:trust_strategy))
619
685
  end
620
686
 
621
- context = SSLContexts.custom
687
+ context = SSLContextBuilder.new
622
688
  setup_trust_store ssl_options, context, trust_strategy
623
689
  setup_key_store ssl_options, context
624
690
 
@@ -633,13 +699,13 @@ module Manticore
633
699
  trust_store ||= blank_keystore
634
700
  open(ssl_options[:ca_file]) do |fp|
635
701
  cert_collection = CertificateFactory.get_instance("X509").generate_certificates(fp.to_inputstream).to_a
636
- cert_collection.each do |cert|
637
- trust_store.set_certificate_entry(cert.getSubjectX500Principal.name, cert)
702
+ cert_collection.each_with_index do |cert, i|
703
+ trust_store.set_certificate_entry("#{i}#" + cert.getSubjectX500Principal.name, cert)
638
704
  end
639
705
  end
640
706
  end
641
707
 
642
- context.load_trust_material(trust_store, trust_strategy)
708
+ context.java_send :loadTrustMaterial, [KeyStore, TrustStrategy], trust_store, trust_strategy
643
709
  end
644
710
 
645
711
  KEY_EXTRACTION_REGEXP = /(?:^-----BEGIN(.* )PRIVATE KEY-----\n)(.*?)(?:-----END\1PRIVATE KEY.*$)/m
@@ -651,7 +717,6 @@ module Manticore
651
717
  # Support OpenSSL-style bare X.509 certs with an RSA key
652
718
  if ssl_options[:client_cert] && ssl_options[:client_key]
653
719
  key_store ||= blank_keystore
654
- certs, key = nil, nil
655
720
 
656
721
  cert_str = if ssl_options[:client_cert].is_a?(OpenSSL::X509::Certificate)
657
722
  ssl_options[:client_cert].to_s
@@ -690,7 +755,7 @@ module Manticore
690
755
  def get_store(prefix, options)
691
756
  KeyStore.get_instance(options[:"#{prefix}_type"] || guess_store_type(options[prefix])).tap do |store|
692
757
  instream = open(options[prefix], "rb").to_inputstream
693
- store.load(instream, options.fetch(:"#{prefix}_password", nil).to_java.toCharArray)
758
+ store.load(instream, options.fetch(:"#{prefix}_password", nil).to_java&.toCharArray)
694
759
  end
695
760
  end
696
761
 
@@ -11,14 +11,13 @@ module Manticore
11
11
  # @!attribute [r] callback_result
12
12
  # @return Value returned from any given on_success/response block
13
13
  class Response
14
- include_package "org.apache.http.client"
15
- include_package "org.apache.http.util"
16
- include_package "org.apache.http.protocol"
14
+
15
+ java_import "org.apache.http.client.ResponseHandler"
17
16
  java_import "org.apache.http.client.protocol.HttpClientContext"
18
- java_import "java.util.concurrent.Callable"
17
+ java_import "org.apache.http.protocol.ExecutionContext"
19
18
 
20
- include ResponseHandler
21
- include Callable
19
+ include org.apache.http.client.ResponseHandler
20
+ include java.util.concurrent.Callable
22
21
 
23
22
  attr_accessor :background
24
23
  attr_reader :context, :request, :callback_result, :called, :future
@@ -54,15 +53,16 @@ module Manticore
54
53
  ex = Manticore::ConnectTimeout
55
54
  rescue Java::JavaNet::SocketException => e
56
55
  ex = Manticore::SocketException
57
- rescue Java::OrgApacheHttpClient::ClientProtocolException, Java::JavaxNetSsl::SSLHandshakeException, Java::OrgApacheHttpConn::HttpHostConnectException,
58
- Java::OrgApacheHttp::NoHttpResponseException, Java::OrgApacheHttp::ConnectionClosedException => e
56
+ rescue Java::OrgApacheHttpClient::ClientProtocolException, Java::JavaxNetSsl::SSLHandshakeException,
57
+ Java::OrgApacheHttpConn::HttpHostConnectException, Java::OrgApacheHttp::NoHttpResponseException,
58
+ Java::OrgApacheHttp::ConnectionClosedException => e
59
59
  ex = Manticore::ClientProtocolException
60
60
  rescue Java::JavaNet::UnknownHostException => e
61
61
  ex = Manticore::ResolutionFailure
62
62
  rescue Java::JavaLang::IllegalArgumentException => e
63
63
  ex = Manticore::InvalidArgumentException
64
64
  rescue Java::JavaLang::IllegalStateException => e
65
- if e.message.match(/Connection pool shut down/)
65
+ if (e.message || '').index('Connection pool shut down')
66
66
  ex = Manticore::ClientStoppedException
67
67
  else
68
68
  @exception = e
@@ -75,7 +75,7 @@ module Manticore
75
75
 
76
76
  # TODO: If calling async, execute_complete may fail and then silently swallow exceptions. How do we fix that?
77
77
  if ex || @exception
78
- @exception ||= ex.new(e.cause || e.message)
78
+ @exception ||= ex.new(e)
79
79
  @handlers[:failure].call @exception
80
80
  execute_complete
81
81
  nil
@@ -90,8 +90,8 @@ module Manticore
90
90
  # @return [String]
91
91
  def final_url
92
92
  call_once
93
- last_request = context.get_attribute ExecutionContext.HTTP_REQUEST
94
- last_host = context.get_attribute ExecutionContext.HTTP_TARGET_HOST
93
+ last_request = context.get_attribute ExecutionContext::HTTP_REQUEST
94
+ last_host = context.get_attribute ExecutionContext::HTTP_TARGET_HOST
95
95
  host = last_host.to_uri
96
96
  url = last_request.get_uri
97
97
  URI.join(host, url.to_s)