manticore 0.2.1-java → 0.3.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
  SHA1:
3
- metadata.gz: 79bd078a381a202fd2e4e022b384b454fdb80242
4
- data.tar.gz: 892736cf30a0c490cb92cf690e5e8aa7eb881368
3
+ metadata.gz: 0078d294015d5ca40a8e4afadc48973fb9246de1
4
+ data.tar.gz: bcff310d2a92c1e592cac51f63859a3ee483b63f
5
5
  SHA512:
6
- metadata.gz: 03a61af6c2dce93aaa68088b9e605cdcb06c8813a182d04ef17152893947b9e9b58b4031561eab68376eb044e08afcd2aa29bd0cc6448f1fa10a4b786b664e71
7
- data.tar.gz: 54e578d3b61ac6db04cb60fb13919ddbf157ca0b664e5a066d56eb2ac727ff7798b644ead04e054c74820123a93ec1607c128b1eee9a60dd445d86c8851ae0ac
6
+ metadata.gz: ef8bfdbaf46d56810e0f137fb4cf96425a46bc22ecf157f9451093bcf9acf721ff4cff86161593ec7c95112a64793856e5345c19454569d0b3ca457c3216d99f
7
+ data.tar.gz: cd2bcc65d8d377d8622fae139e0f4c5e91816bcd0cea3926c8d1a5503eda2e68c28ef179cae5f153a389cd8c1474bb5ffbb3f789408e197f8d297b385b741e0a
checksums.yaml.gz.sig ADDED
@@ -0,0 +1 @@
1
+ h>Ĭ��odd(��`���P��{F� ҆<U����$�ѐ�/#�5q�6�>q�>��NJ�YE�'��P�[���򳽠8EP���>��a�W�"{vh��|�0���W`Or���x?a�L:{�=������� ѫ�5c���>.��n�[5n%ph����,�Ҳ{b��L�D��l�G����Q�-hn�K�:�X[v^d�ܨjj�X���ޔO�i!����tmK�U�k��z9���힏3Ͽ~�2�x�<uY
data/CHANGELOG.md ADDED
@@ -0,0 +1,31 @@
1
+ ## v0.3
2
+ ### v0.3.0 (pending)
3
+
4
+ * Major refactor of `Response`/`AsyncResponse` to eliminate redundant code. `AsyncResponse` has been removed and
5
+ its functionality has been rolled into `Response`.
6
+ * Added `StubbedResponse`, a subclass of `Response`, to be used for stubbing requests/responses for testing.
7
+ * Added `Client#stub`, `Client#unstub` and `Client#respond_with`
8
+ * Responses are now lazy-evaluated by default (similar to how `AsyncResponse` used to behave). The following
9
+ rules apply:
10
+ * Synchronous responses which do NOT pass a block are lazy-evaluated the first time one of their results is requested.
11
+ * Synchronous responses which DO pass a block are evaluated immediately, and are passed to the handler block.
12
+ * Async responses are always evaluted when `Client#execute!` is called.
13
+ * You can evaluate a `Response` at any time by invoking `#call` on it. Invoking an async response before `Client#execute`
14
+ is called on it will cause `Client#execute` to throw an exception.
15
+ * Responses (both synchronous and async) may use on_success handlers and the like.
16
+
17
+ ## v0.2
18
+ ### v0.2.1
19
+
20
+ * Added basic auth support
21
+ * Added proxy support
22
+ * Added support for per-request cookies (as opposed to per-session cookies)
23
+ * Added a `Response#cookies` convenience method.
24
+
25
+ ### v0.2.0
26
+
27
+ * Added documentation and licenses
28
+ * Significant performance overhaul
29
+ * Response handler blocks are now only yielded the Response. `#request` is available on
30
+ the response object.
31
+ * Patched httpclient.jar to address https://issues.apache.org/jira/browse/HTTPCLIENT-1461
data/README.md CHANGED
@@ -34,6 +34,7 @@ Or install it yourself as:
34
34
  * Transparent gzip and deflate handling
35
35
  * Transparent cookie handling
36
36
  * Both synchronous and asynchronous execution models
37
+ * Lazy evaluation
37
38
  * Authentication
38
39
  * Proxy support
39
40
  * SSL
@@ -44,77 +45,199 @@ Or install it yourself as:
44
45
 
45
46
  If you don't want to worry about setting up and maintaining client pools, Manticore comes with a facade that you can use to start making requests right away:
46
47
 
47
- Manticore.get "http://www.google.com/"
48
+ ```ruby
49
+ document_body = Manticore.get("http://www.google.com/").body
50
+ ```
48
51
 
49
52
  Additionally, you can mix the `Manticore::Facade` into your own class for similar behavior:
50
53
 
51
- class MyClient
52
- include Manticore::Facade
53
- include_http_client user_agent: "MyClient/1.0"
54
- end
54
+ ```ruby
55
+ class MyClient
56
+ include Manticore::Facade
57
+ include_http_client user_agent: "MyClient/1.0"
58
+ end
55
59
 
56
- MyClient.get "http://www.google.com/"
60
+ response_code = MyClient.get("http://www.google.com/").code
61
+ ```
57
62
 
58
63
  Mixing the client into a class will create a new new pool. If you want to share a single pool between clients, specify the `shared_pool` option:
59
64
 
60
- class MyClient
61
- include Manticore::Facade
62
- include_http_client shared_pool: true
63
- end
65
+ ```ruby
66
+ class MyClient
67
+ include Manticore::Facade
68
+ include_http_client shared_pool: true
69
+ end
64
70
 
65
- class MyOtherClient
66
- include Manticore::Facade
67
- include_http_client shared_pool: true
68
- end
71
+ class MyOtherClient
72
+ include Manticore::Facade
73
+ include_http_client shared_pool: true
74
+ end
75
+ ```
69
76
 
70
77
  ### More Control
71
78
 
72
79
  Manticore is built around a connection pool. When you create a `Client`, you will pass various parameters that it will use to set up the pool.
73
80
 
74
- client = Manticore::Client.new(request_timeout: 5, connect_timeout: 5, socket_timeout: 5, pool_max: 10, pool_max_per_route: 2)
81
+ ```ruby
82
+ client = Manticore::Client.new(request_timeout: 5, connect_timeout: 5, socket_timeout: 5, pool_max: 10, pool_max_per_route: 2)
83
+ ```
75
84
 
76
85
  Then, you can make requests from the client. Pooling and route maximum constraints are automatically managed:
77
86
 
78
- response = client.get("http://www.google.com/")
87
+ ```ruby
88
+ response = client.get("http://www.google.com/")
89
+ body = response.body
90
+ ```
79
91
 
80
92
  It is recommend that you instantiate a client once, then re-use it, rather than instantiating a new client per request.
81
93
 
82
94
  Additionally, if you pass a block to the initializer, the underlying [HttpClientBuilder](http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/HttpClientBuilder.html) and [RequestConfig.Builder](http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/config/RequestConfig.Builder.html) will be yielded so that you can operate on them directly:
83
95
 
84
- client = Manticore::Client.new(socket_timeout: 5) do |http_client_builder, request_builder|
85
- http_client_builder.disable_redirect_handling
86
- end
96
+ ```ruby
97
+ client = Manticore::Client.new(socket_timeout: 5) do |http_client_builder, request_builder|
98
+ http_client_builder.disable_redirect_handling
99
+ end
100
+ ```
87
101
 
88
102
  ### Parallel execution
89
103
 
90
- Manticore can perform multiple concurrent execution of requests.
104
+ Manticore can perform concurrent execution of multiple requests.
91
105
 
92
- client = Manticore::Client.new
106
+ ```ruby
107
+ client = Manticore::Client.new
93
108
 
94
- # These aren't actually executed until #execute! is called.
95
- # You can define response handlers in a block when you queue the request:
96
- client.async_get("http://www.google.com") {|req|
97
- req.on_success do |response|
98
- puts response.body
99
- end
109
+ # These aren't actually executed until #execute! is called.
110
+ # You can define response handlers in a block when you queue the request:
111
+ client.async.get("http://www.google.com") {|req|
112
+ req.on_success do |response|
113
+ puts response.body
114
+ end
100
115
 
101
- req.on_failure do |exception|
102
- puts "Boom! #{exception.message}"
103
- end
104
- }
116
+ req.on_failure do |exception|
117
+ puts "Boom! #{exception.message}"
118
+ end
119
+ }
105
120
 
106
- # ...or by invoking the method on the queued response returned:
107
- response = client.async_get("http://www.yahoo.com")
108
- response.on_success do |response|
109
- puts "The length of the Yahoo! homepage is #{response.body.length}"
110
- end
121
+ # ...or by invoking the method on the queued response returned:
122
+ response = client.async.get("http://www.yahoo.com")
123
+ response.on_success do |response|
124
+ puts "The length of the Yahoo! homepage is #{response.body.length}"
125
+ end
111
126
 
112
- # ...or even by chaining them onto the call
113
- client.async_get("http://bing.com").
114
- on_success {|r| puts r.code }.
115
- on_failure {|e| puts "on noes!"}
127
+ # ...or even by chaining them onto the call
128
+ client.async.get("http://bing.com").
129
+ on_success {|r| puts r.code }.
130
+ on_failure {|e| puts "on noes!"}
116
131
 
117
- client.execute!
132
+ client.execute!
133
+ ```
134
+
135
+ ### Lazy Evaluation
136
+
137
+ Manticore attempts to avoid doing any actual work until right before you need results. As a result,
138
+ responses are lazy-evaluated as late as possible. The following rules apply:
139
+
140
+ 1. Synchronous responses are evaluted when you call an accessor on them, like `#body` or `#headers`.
141
+ 2. Synchronous responses which pass a handler block are evaluated immediately.
142
+ 3. Asynchronous responses are always evaluated when you call `Client#execute!`
143
+ 4. Background responses are always immediately evaluated, but return a `Future`.
144
+
145
+ As a result, with the exception of background requests, this allows you to attach handlers to synchronous
146
+ and asynchronous responses in the same fashion:
147
+
148
+ ```ruby
149
+ # Response doesn't evaluate when you call get, since you don't need any results from it yet
150
+ response = client.get("http://google.com").on_success {|r| "Success handler!" }
151
+ # As soon as you request #body, the response will evaluate to a result.
152
+ body = response.body
153
+
154
+ response = client.async.get("http://google.com").on_success {|r| "Success handler!" }
155
+ client.execute!
156
+ body = response.body
157
+ ```
158
+
159
+ If you want to make a response that is not lazy-evaluated, you can either pass a handler block to it, or you can
160
+ call `#call` on the resulting response:
161
+
162
+ ```ruby
163
+ # This will evaluate immediately
164
+ client.get("http://google.com") {r| r.body }
165
+
166
+ # As will this, via explicit invocation of #call
167
+ client.get("http://google.com").call
168
+ ```
169
+
170
+ ### Stubbing
171
+
172
+ Manticore provides a stubbing interface somewhat similar to Typhoeus'
173
+
174
+ ```ruby
175
+ client.stub("http://google.com", body: "response body", code: 200)
176
+ client.get("http://google.com") do |response|
177
+ response.body.should == "response body"
178
+ end
179
+ client.clear_stubs!
180
+ ```
181
+
182
+ This works for async requests as well:
183
+
184
+ ```ruby
185
+ client.stub("http://google.com", body: "response body", code: 200)
186
+
187
+ # The request to google.com returns a stub as expected
188
+ client.async.get("http://google.com").on_success do |response|
189
+ response.should be_a Manticore::ResponseStub
190
+ end
191
+
192
+ # Since yahoo.com isn't stubbed, a full request will be performed
193
+ client.async.get("http://yahoo.com").on_success do |response|
194
+ response.should be_a Manticore::Response
195
+ end
196
+ client.clear_stubs!
197
+ ```
198
+
199
+ If you don't want to worry about stub teardown, you can just use `#respond_with`, which will stub the next
200
+ response the client makes with a ResponseStub rather than permitting it to execute a remote request.
201
+
202
+ ```ruby
203
+ client.respond_with(body: "body").get("http://google.com") do |response|
204
+ response.body.should == "body"
205
+ end
206
+ ```
207
+
208
+ You can also chain proxies to, say, stub an async request:
209
+
210
+ ```ruby
211
+ response = client.async.respond_with(body: "response body").get("http://google.com")
212
+ client.execute!
213
+
214
+ response.body.should == "response body"
215
+ ```
216
+
217
+ Additionally, you can stub and unstub individual URLs as desired:
218
+ ```ruby
219
+ client.stub("http://google.com", body: "response body", code: 200)
220
+ client.stub("http://yahoo.com", body: "response body", code: 200)
221
+
222
+ # The request to google.com returns a stub as expected
223
+ client.get("http://google.com") do |response|
224
+ response.should be_a Manticore::ResponseStub
225
+ end
226
+
227
+ # After this point, yahoo will remain stubbed, while google will not.
228
+ client.unstub("http://google.com")
229
+ ```
230
+
231
+ ### Background requests
232
+
233
+ You might want to fire-and-forget requests without blocking your calling thread. You can do this with `Client#background`:
234
+
235
+ ```ruby
236
+ future = client.background.get("http://google.com")
237
+ # The request is now running, but the calling thread isn't blocked
238
+ # Do whatever stuff you need to right now. At some point, if you want the result of the request, you can call `Future#get`:
239
+ response = future.get
240
+ ```
118
241
 
119
242
  ## Contributing
120
243
 
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDaDCCAlCgAwIBAgIBATANBgkqhkiG9w0BAQUFADA9MQ8wDQYDVQQDDAZjaGVh
3
+ bGQxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2NvbTAe
4
+ Fw0xNDAzMDEyMTE4MzJaFw0xNTAzMDEyMTE4MzJaMD0xDzANBgNVBAMMBmNoZWFs
5
+ ZDEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29tMIIB
6
+ IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwcnX2yl+rbjUztC4V+iUJgWv
7
+ NPxqU4bQBaL+w00wVABWr04Hjg+cEkqiJ6A0kXxZIz5uXKhhvsaO50NvHfplVcUf
8
+ BxQabIfCS79xdvexXN0or3F5saatGaGa4cj/0uUHjX7w+K5MpEVfbjJp0uAKp62B
9
+ wUU2ilmn7EvZhEUPOMQi01t8z8OsOGc8kF2UtU1kGCxLV7eThWqu8CdXrux5E140
10
+ 7SnFnPlnXNeHqwZdOMZzQ9PiTQAPCKO3AY0aBFQeG3wlFPqkcEjOrtV1h7werUwz
11
+ aNb4t5sAuu0f/9B646BOjiMgj1WeUlhgiAsaF5kVvLFNCxwS/xpcN3X01M2KdQID
12
+ AQABo3MwcTAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUtFVpwfEG
13
+ 78mBd2ulzsS+SlffdOcwGwYDVR0RBBQwEoEQY2hlYWxkQGdtYWlsLmNvbTAbBgNV
14
+ HRIEFDASgRBjaGVhbGRAZ21haWwuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQANcBIJ
15
+ vIk27iNu4Ic6awNRy4Rd2YchzW4DaMhfpTXxtYWgAE502OdqUTFtmihd2PmnVRIP
16
+ xdP5K2SpbCLuLKFCZ825qp8R02JSNMdgi2d4Btl0vHSO8O3lQ1ZAOM1BZPn2+Kbn
17
+ KsiSURg3SnDXItcoedMsS+CQdNyBSdqvpkQ5gn22dN4BSwJg5ApwEYJ9tTuTpRXt
18
+ 1KCQFe5hamoenfoHO6uUXPEs24PUHnXl8QibWCLbt1FIBpTrJYNGiMHHnB7ip+zv
19
+ E7PWS50D9moUJ6xWcemf0qKYC87qBFh0ng73awjG9uf+13lMslqJRMtek8C92cvh
20
+ +R9zgQlbeNjy9O1i
21
+ -----END CERTIFICATE-----
@@ -0,0 +1,57 @@
1
+ module Manticore
2
+ class Client
3
+ module ProxiesInterface
4
+ def respond_with(stubs)
5
+ StubProxy.new(self, stubs)
6
+ end
7
+
8
+ # Causes the next request to be made asynchronously
9
+ def async
10
+ AsyncProxy.new(self)
11
+ end
12
+
13
+ # Causes the next request to be made immediately in the background
14
+ def background
15
+ BackgroundProxy.new(self)
16
+ end
17
+ end
18
+
19
+ class BaseProxy
20
+ include ProxiesInterface
21
+ def initialize(client)
22
+ @client = client
23
+ end
24
+ end
25
+
26
+ class AsyncProxy < BaseProxy
27
+ %w(get put head post options patch).each do |func|
28
+ define_method func do |url, options = {}, &block|
29
+ @client.send(func, url, options.merge(async: true), &block)
30
+ end
31
+ end
32
+ end
33
+
34
+ class StubProxy < BaseProxy
35
+ def initialize(client, stubs)
36
+ super(client)
37
+ @stubs = stubs
38
+ end
39
+
40
+ %w(get put head post options patch).each do |func|
41
+ define_method func do |url, options = {}, &block|
42
+ @client.stub(url, @stubs)
43
+ @client.send(func, url, options, &block).complete { @client.unstub url }
44
+ end
45
+ end
46
+ end
47
+
48
+ class BackgroundProxy < BaseProxy
49
+ %w(get put head post options patch).each do |func|
50
+ define_method func do |url, options = {}, &block|
51
+ request = @client.send(func, url, options.merge(async: true), &block)
52
+ @client.executor.java_method(:submit, [java.util.concurrent.Callable.java_class]).call request
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -37,7 +37,12 @@ module Manticore
37
37
  #
38
38
  # @!macro [new] http_method_shared_sync
39
39
  # @example Simple usage
40
- # client.$0("http://example.com/some/resource", params: {foo: "bar"}, headers: {"X-Custom-Header" => "whee"})
40
+ # body = client.$0("http://example.com/some/resource", params: {foo: "bar"}, headers: {"X-Custom-Header" => "whee"}).body
41
+ # @example Passing a block as the success handler:
42
+ # body = client.$0("http://example.com/some/resource", params: {foo: "bar"}, headers: {"X-Custom-Header" => "whee"}) {|response| response.body }
43
+ # @example Explicit success handler:
44
+ # body = client.$0("http://example.com/some/resource", params: {foo: "bar"}, headers: {"X-Custom-Header" => "whee"}).
45
+ # on_success {|response| response.body }
41
46
  # @macro http_method_shared
42
47
  # @macro http_request_exceptions
43
48
  #
@@ -66,13 +71,11 @@ module Manticore
66
71
  include_package "java.util.concurrent"
67
72
  include_package "org.apache.http.client.protocol"
68
73
  java_import "org.apache.http.HttpHost"
74
+ include ProxiesInterface
69
75
 
70
76
  # The default maximum pool size for requests
71
77
  DEFAULT_MAX_POOL_SIZE = 50
72
78
 
73
- # The default maximum number of threads per route that will be permitted
74
- DEFAULT_MAX_PER_ROUTE = 10
75
-
76
79
  DEFAULT_REQUEST_TIMEOUT = 60
77
80
  DEFAULT_SOCKET_TIMEOUT = 10
78
81
  DEFAULT_CONNECT_TIMEOUT = 10
@@ -99,7 +102,7 @@ module Manticore
99
102
  # @param options [Hash] Client pool options
100
103
  # @option options [String] user_agent The user agent used in requests.
101
104
  # @option options [Integer] pool_max (50) The maximum number of active connections in the pool
102
- # @option options [integer] pool_max_per_route (2) Sets the maximum number of active connections for a given target endpoint
105
+ # @option options [integer] pool_max_per_route (pool_max) Sets the maximum number of active connections for a given target endpoint
103
106
  # @option options [boolean] cookies (true) enable or disable automatic cookie management between requests
104
107
  # @option options [boolean] compression (true) enable or disable transparent gzip/deflate support
105
108
  # @option options [integer] request_timeout (60) Sets the timeout for requests. Raises {Manticore::Timeout} on failure.
@@ -112,6 +115,9 @@ module Manticore
112
115
  # @option options [String] proxy Proxy host in form: http://proxy.org:1234
113
116
  # @option options [Hash] proxy Proxy host in form: {host: 'proxy.org'[, port: 80[, scheme: 'http']]}
114
117
  # @option options [URI] proxy Proxy host as a URI object
118
+ # @option options [Boolean/Integer] keepalive (true) Whether to allow connections to be reused. Defaults to true. If an integer,
119
+ # then connections will be kept alive for this long when Connection: keep-alive
120
+ # is sent, but no Keep-Alive header is sent.
115
121
  def initialize(options = {})
116
122
  builder = client_builder
117
123
  builder.set_user_agent options.fetch(:user_agent, "Manticore #{VERSION}")
@@ -121,10 +127,20 @@ module Manticore
121
127
  builder.set_proxy get_proxy_host(options[:proxy]) if options.key?(:proxy)
122
128
 
123
129
  # This should make it easier to reuse connections
124
- builder.disable_connection_state
125
- builder.set_connection_reuse_strategy DefaultConnectionReuseStrategy.new
130
+ # TODO: Determine what this actually does!
131
+ # builder.disable_connection_state
132
+
133
+ keepalive = options.fetch(:keepalive, true)
134
+ if keepalive == false
135
+ builder.set_connection_reuse_strategy {|response, context| false }
136
+ else
137
+ builder.set_connection_reuse_strategy DefaultConnectionReuseStrategy.new
138
+ end
139
+
140
+ socket_config_builder = SocketConfig.custom
141
+ socket_config_builder.setSoTimeout( options.fetch(:socket_timeout, DEFAULT_SOCKET_TIMEOUT) * 1000 )
142
+ builder.set_default_socket_config socket_config_builder.build
126
143
 
127
- # socket_config = SocketConfig.custom.set_tcp_no_delay(true).build
128
144
  builder.set_connection_manager pool(options)
129
145
 
130
146
  request_config = RequestConfig.custom
@@ -134,7 +150,6 @@ module Manticore
134
150
  request_config.set_max_redirects options.fetch(:max_redirects, DEFAULT_MAX_REDIRECTS)
135
151
  request_config.set_expect_continue_enabled options.fetch(:expect_continue, DEFAULT_EXPECT_CONTINUE)
136
152
  request_config.set_stale_connection_check_enabled options.fetch(:stale_check, DEFAULT_STALE_CHECK)
137
- # request_config.set_authentication_enabled options.fetch(:use_auth, false)
138
153
  request_config.set_circular_redirects_allowed false
139
154
 
140
155
  yield builder, request_config if block_given?
@@ -143,6 +158,18 @@ module Manticore
143
158
  @client = builder.build
144
159
  @options = options
145
160
  @async_requests = []
161
+ @stubs = {}
162
+ end
163
+
164
+ # Return a hash of statistics about this client's HTTP connection pool
165
+ def pool_stats
166
+ stats = @pool.get_total_stats
167
+ {
168
+ max: stats.get_max,
169
+ leased: stats.get_leased,
170
+ pending: stats.get_pending,
171
+ available: stats.get_available
172
+ }
146
173
  end
147
174
 
148
175
  ### Sync methods
@@ -177,58 +204,39 @@ module Manticore
177
204
  request HttpDelete, url, options, &block
178
205
  end
179
206
 
207
+ # Perform a HTTP OPTIONS request
180
208
  # @macro http_method_shared_sync
181
209
  def options(url, options = {}, &block)
182
210
  request HttpOptions, url, options, &block
183
211
  end
184
212
 
213
+ # Perform a HTTP PATCH request
185
214
  # @macro http_method_shared_sync_with_body
186
215
  def patch(url, options = {}, &block)
187
216
  request HttpPatch, url, options, &block
188
217
  end
189
218
 
190
- ### Async methods
191
-
192
- # Queue an asynchronous HTTP GET request
193
- # @macro http_method_shared_async
194
- def async_get(url, options = {}, &block)
195
- get url, options.merge(async: true), &block
196
- end
197
-
198
- # Queue an asynchronous HTTP HEAD request
199
- # @macro http_method_shared_async
200
- def async_head(url, options = {}, &block)
201
- head url, options.merge(async: true), &block
202
- end
203
-
204
- # Queue an asynchronous HTTP PUT request
205
- # @macro http_method_shared_async_with_body
206
- def async_put(url, options = {}, &block)
207
- put url, options.merge(async: true), &block
208
- end
209
-
210
- # Queue an asynchronous HTTP POST request
211
- # @macro http_method_shared_async_with_body
212
- def async_post(url, options = {}, &block)
213
- post url, options.merge(async: true), &block
219
+ %w(get put head post options patch).each do |func|
220
+ define_method "#{func}!" do |url, options, &block|
221
+ send(func, url, options, &block).call
222
+ end
214
223
  end
215
224
 
216
- # Queue an asynchronous HTTP DELETE request
217
- # @macro http_method_shared_async
218
- def async_delete(url, options = {}, &block)
219
- delete url, options.merge(async: true), &block
225
+ # Cause this client to return a stubbed response for this URL
226
+ # @param url [String] URL to stub for
227
+ # @param stubs [Hash] Hash of options to return for the stubbed response
228
+ def stub(url, stubs)
229
+ @stubs[url] = stubs
220
230
  end
221
231
 
222
- # Queue an asynchronous HTTP OPTIONS request
223
- # @macro http_method_shared_async
224
- def async_options(url, options = {}, &block)
225
- options url, options.merge(async: true), &block
232
+ # Cause this client to unstubbed previously-stubbed URL
233
+ def unstub(url)
234
+ @stubs.delete(url)
226
235
  end
227
236
 
228
- # Queue an asynchronous HTTP PATCH request
229
- # @macro http_method_shared_async_with_body
230
- def async_patch(url, options = {}, &block)
231
- patch url, options.merge(async: true), &block
237
+ # Wipe all currently-set stubs.
238
+ def clear_stubs!
239
+ @stubs.clear
232
240
  end
233
241
 
234
242
  # Remove all pending asynchronous requests.
@@ -243,9 +251,16 @@ module Manticore
243
251
  #
244
252
  # @return [Array] An array of the responses from the requests executed.
245
253
  def execute!
246
- result = @executor.invoke_all(@async_requests).map(&:get)
254
+ method = executor.java_method(:submit, [java.util.concurrent.Callable.java_class])
255
+ result = @async_requests.map {|r| method.call r }
247
256
  @async_requests.clear
248
- result
257
+ result.map(&:get)
258
+ end
259
+
260
+ # Get at the underlying ExecutorService used to invoke asynchronous calls.
261
+ def executor
262
+ create_executor_if_needed
263
+ @executor
249
264
  end
250
265
 
251
266
  protected
@@ -262,7 +277,7 @@ module Manticore
262
277
  @pool ||= begin
263
278
  @max_pool_size = options.fetch(:pool_max, DEFAULT_MAX_POOL_SIZE)
264
279
  cm = pool_builder
265
- cm.set_default_max_per_route options.fetch(:pool_max_per_route, DEFAULT_MAX_PER_ROUTE)
280
+ cm.set_default_max_per_route options.fetch(:pool_max_per_route, @max_pool_size)
266
281
  cm.set_max_total @max_pool_size
267
282
  Thread.new {
268
283
  loop {
@@ -283,35 +298,35 @@ module Manticore
283
298
  def request(klass, url, options, &block)
284
299
  req, context = request_from_options(klass, url, options)
285
300
  if options.delete(:async)
286
- async_request req, context, &block
301
+ async_request req, context
287
302
  else
288
303
  sync_request req, context, &block
289
304
  end
290
305
  end
291
306
 
292
- def async_request(request, context, &block)
307
+ def async_request(request, context)
293
308
  create_executor_if_needed
294
- response = AsyncResponse.new(@client, request, context, block)
309
+ response = response_object_for(@client, request, context)
295
310
  @async_requests << response
296
311
  response
297
312
  end
298
313
 
299
314
  def sync_request(request, context, &block)
300
- response = Response.new(request, context, block)
301
- begin
302
- @client.execute request, response, response.context
315
+ response = response_object_for(@client, request, context, &block)
316
+ if block_given?
317
+ response.call
318
+ else
303
319
  response
304
- rescue Java::JavaNet::SocketTimeoutException, Java::OrgApacheHttpConn::ConnectTimeoutException, Java::OrgApacheHttp::NoHttpResponseException => e
305
- raise Manticore::Timeout.new(e.get_cause)
306
- rescue Java::JavaNet::SocketException => e
307
- raise Manticore::SocketException.new(e.get_cause)
308
- rescue Java::OrgApacheHttpClient::ClientProtocolException, Java::JavaxNetSsl::SSLHandshakeException,
309
- Java::OrgApacheHttpConn::HttpHostConnectException, Java::JavaxNetSsl::SSLException => e
310
- raise Manticore::ClientProtocolException.new(e.get_cause)
311
- rescue Java::JavaNet::UnknownHostException => e
312
- raise Manticore::ResolutionFailure.new(e.get_cause)
313
320
  end
321
+ end
314
322
 
323
+ def response_object_for(client, request, context, &block)
324
+ request_uri = request.getURI.to_s
325
+ if @stubs.key?(request_uri)
326
+ StubbedResponse.new(client, request, context, &block).stub( @stubs[request_uri] )
327
+ else
328
+ Response.new(client, request, context, &block)
329
+ end
315
330
  end
316
331
 
317
332
  def uri_from_url_and_options(url, options)