manticore 0.7.0-java → 0.9.0-java

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