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 +4 -4
- data/.travis.yml +10 -8
- data/CHANGELOG.md +23 -1
- data/Gemfile +9 -7
- data/README.md +18 -5
- data/Rakefile +2 -0
- data/lib/commons-codec/commons-codec/1.15/commons-codec-1.15.jar +0 -0
- data/lib/manticore/client/trust_strategies.rb +119 -0
- data/lib/manticore/client.rb +115 -50
- data/lib/manticore/response.rb +12 -12
- data/lib/manticore/version.rb +1 -1
- data/lib/manticore.rb +27 -2
- data/lib/manticore_jars.rb +16 -6
- data/lib/org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar +0 -0
- data/lib/org/apache/httpcomponents/httpcore/4.4.14/httpcore-4.4.14.jar +0 -0
- data/lib/org/apache/httpcomponents/httpmime/4.5.13/httpmime-4.5.13.jar +0 -0
- data/lib/org/manticore/manticore-ext.jar +0 -0
- data/manticore.gemspec +5 -5
- data/spec/manticore/client_spec.rb +73 -16
- data/spec/manticore/client_trust_strategies_spec.rb +168 -0
- data/spec/manticore/response_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- metadata +16 -41
- data/lib/commons-codec/commons-codec/1.10/commons-codec-1.10.jar +0 -0
- data/lib/org/apache/httpcomponents/httpclient/4.5.2/httpclient-4.5.2.jar +0 -0
- data/lib/org/apache/httpcomponents/httpcore/4.4.4/httpcore-4.4.4.jar +0 -0
- data/lib/org/apache/httpcomponents/httpmime/4.5.2/httpmime-4.5.2.jar +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 281400646207bd8e4b34bed53610787e5b5d222c72fe5e82dd1d42d8f071f1be
|
4
|
+
data.tar.gz: 421a4a8f1c4f8e06a7d12377812da2613877877904c73d3d8375348fbb9e2103
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c72e625dc8aa526a1336c239ced7d46cebf9b16817f9c309b1caaa80028a14e959cbe342e745db69dfef8fd284b1413085a518322406ecab3e7a28725815a34f
|
7
|
+
data.tar.gz: 37774c46167abf4c8cb637580dc2cf91afc4311b8a284c17d48441ba00824ba0af41e47121780837caffef60632aa3c26bb075b323011a2fcb56fb055495e83c
|
data/.travis.yml
CHANGED
@@ -1,20 +1,22 @@
|
|
1
|
-
dist:
|
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:
|
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
|
-
|
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 "
|
7
|
+
gem "rake-compiler", require: false
|
8
|
+
gem "simplecov"
|
9
|
+
|
8
10
|
gem "rspec", "~> 3.0"
|
9
11
|
gem "rspec-its"
|
10
|
-
|
11
|
-
gem "rack", ">= 2.1.4"
|
12
|
-
gem "
|
13
|
-
gem "
|
14
|
-
gem "
|
15
|
-
gem "
|
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.
|
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,
|
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
|
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
|
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
|
-
|
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",
|
Binary file
|
@@ -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
|
data/lib/manticore/client.rb
CHANGED
@@ -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
|
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 =
|
101
|
-
thread.
|
102
|
-
|
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] (:
|
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 `:
|
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
|
-
@
|
179
|
-
self.class.shutdown_on_finalize self, @
|
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
|
-
|
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
|
-
#
|
350
|
-
|
351
|
-
|
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
|
-
|
357
|
-
@executor
|
409
|
+
@executor ||= create_executor
|
358
410
|
end
|
359
411
|
|
360
|
-
|
361
|
-
|
362
|
-
|
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
|
-
@
|
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
|
-
|
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
|
416
|
-
|
417
|
-
|
418
|
-
|
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
|
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
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
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
|
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 (:
|
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 =
|
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.
|
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.
|
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
|
758
|
+
store.load(instream, options.fetch(:"#{prefix}_password", nil).to_java&.toCharArray)
|
694
759
|
end
|
695
760
|
end
|
696
761
|
|
data/lib/manticore/response.rb
CHANGED
@@ -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
|
-
|
15
|
-
|
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 "
|
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,
|
58
|
-
Java::
|
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.
|
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
|
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
|
94
|
-
last_host = context.get_attribute ExecutionContext
|
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)
|