manticore 0.4.4-java → 0.5.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
  SHA1:
3
- metadata.gz: e35496eb223648d01aea2dbe4132d88102b599f6
4
- data.tar.gz: d87ffe86b45313d8836354c9df4419e3c91c9fd9
3
+ metadata.gz: 9cf2ddebb1afdaa3ee6044c1796c7ec917ba6485
4
+ data.tar.gz: 452f05d5e780e13b05f2fd7c004c4cc1c858c67f
5
5
  SHA512:
6
- metadata.gz: e03381476da9b771bf8961c0767312639147d4bc15435879144f4d37960dcac693bc07c43ab9c963fefe846df76163569483040230d852e1038dcff58157fd7a
7
- data.tar.gz: 011f97038fd79f71cb3b734639fa468f3395073efe5697d838543ecbe38f26da94ee005726379fedd54cfac9789d9271169a168b0930b1096568d8d98e5a4901
6
+ metadata.gz: 53c89c946b6d9ec034dc14282b6bd415cac0dba8c82ffee2f0508d3db17f123c874bce492f79efbc6813111767459f942fc902cf2b6a8a1fd3480f670262d8c6
7
+ data.tar.gz: 0ecd5d823d6e778e57ebfcf5ee63209f5cf5e67620348a18616ea80ca5751054d577c283ad99392b580933849e2bac9cae1dfa3d277e87be24b6e1da12625b50
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/.travis.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  language: ruby
2
+ sudo: false
2
3
  rvm:
3
4
  - jruby-1.7
4
5
  - jruby-9.0.0.0
data/CHANGELOG.md CHANGED
@@ -1,13 +1,23 @@
1
1
  ## v0.5
2
2
 
3
- ### v0.5.0 (pending, hc-4.5 branch)
3
+ ### v0.5.1 (pending)
4
+
5
+ ### v0.5.0
4
6
 
5
7
  * Upgrade to HTTPClient and HTTPCore 4.5
8
+ * BREAKING CHANGE: Background request usage has changed. See [this commit](https://github.com/cheald/manticore/commit/174e2004d1865c201daf77494d50ab66527c12aa) for details.
9
+ * Client#async is now soft-deprecated in favor of Client#parallel or Client#batch, as the latter two more accurately reflect the intended usage. Client#background is for
10
+ "don't block the calling thread"-style asynchronous calls.
11
+ * Manticore now uses jar-dependencies to install the HTTPComponents et al jars during gem installation, rather than shipping them in the gem
12
+ * DELETEs may now post entity bodies to allow spec-violate behavior similar to GETs (thanks @hobodave)
6
13
 
7
14
  ## v0.4
8
15
 
9
16
  ## v0.4.5 (pending, master branch)
10
17
 
18
+ * If you pass a block to background request creation, the background request is yielded before being queued on the executor. This allows you to attach
19
+ on_success, etc handlers. This is a stopgap change that is backwards compatible with 0.4.x and will be changing in the 0.5.x release.
20
+
11
21
  ## v0.4.4
12
22
 
13
23
  * Manticore now treats post bodies with binary encodings as binary byte lists rather than strings with an encoding
data/Gemfile CHANGED
@@ -9,4 +9,4 @@ gem "rspec-its"
9
9
  gem "httpclient", "~> 2.3"
10
10
  gem "rack", "~> 1.5"
11
11
  gem "rake-compiler"
12
- gem "gserver"
12
+ gem "gserver"
data/LICENSE.txt CHANGED
@@ -1,13 +1,4 @@
1
- Copyright (c) 2013 Chris Heald
2
-
3
- Some JARs distributed with this project are licensed under the Apache 2.0 License. See APACHE-LICENSE-2.0.txt for details:
4
-
5
- lib/jar/commons-logging-1.1.3.jar
6
- lib/jar/httpclient-4.3.2.jar
7
- lib/jar/httpcore-4.3.1.jar
8
- lib/jar/commons-codec-1.6.jar
9
-
10
- All other code distributed with this project is licensed under the MIT License as follows:
1
+ Copyright (c) 2015 Chris Heald
11
2
 
12
3
  Permission is hereby granted, free of charge, to any person obtaining
13
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -136,36 +136,41 @@ internal CA, and would proxy all requests through an internal server.
136
136
  Creating pools is expensive, so you don't want to be doing it for each request. Instead, you should set up your pools once and then re-use them.
137
137
  Clients and their backing pools are thread-safe, so feel free to set them up once before you start performing parallel operations.
138
138
 
139
+ ### Background requests
140
+
141
+ You might want to fire off requests without blocking your calling thread. You can do this with `Client#background`:
142
+
143
+ ```ruby
144
+ response = client.background.get("http://google.com")
145
+ .on_success {|response| puts response.code }
146
+ response.call # The request is now running, but the calling thread isn't blocked. The on_success handler will be evaluated whenever
147
+ # the request completes.
148
+ ```
139
149
 
140
150
  ### Parallel execution
141
151
 
142
- Manticore can perform concurrent execution of multiple requests.
152
+ Manticore can perform concurrent execution of multiple requests. In previous versions of Manticore, this was called "async". We now call these
153
+ "parallel" or "batch" requests. `Client#async`, `Client#parallel`, and `Client#batch` are equivalent.
143
154
 
144
155
  ```ruby
145
156
  client = Manticore::Client.new
146
157
 
147
158
  # These aren't actually executed until #execute! is called.
148
- # You can define response handlers in a block when you queue the request:
149
- client.async.get("http://www.google.com") {|req|
150
- req.on_success do |response|
151
- puts response.body
152
- end
153
-
154
- req.on_failure do |exception|
155
- puts "Boom! #{exception.message}"
156
- end
157
- }
158
-
159
- # ...or by invoking the method on the queued response returned:
160
- response = client.async.get("http://www.yahoo.com")
159
+ # You can define response handlers on the not-yet-resolved response object:
160
+ response = client.parallel.get("http://www.yahoo.com")
161
161
  response.on_success do |response|
162
162
  puts "The length of the Yahoo! homepage is #{response.body.length}"
163
163
  end
164
164
 
165
+ response.on_failure do |response|
166
+ puts "http://www.nooooooooooooooo.com/"
167
+ end
168
+
165
169
  # ...or even by chaining them onto the call
166
- client.async.get("http://bing.com").
167
- on_success {|r| puts r.code }.
168
- on_failure {|e| puts "on noes!"}
170
+ client.parallel.get("http://bing.com")
171
+ .on_success {|r| puts r.code }
172
+ .on_failure {|e| puts "on noes!" }
173
+ .on_complete { puts "Job's done!" }
169
174
 
170
175
  client.execute!
171
176
  ```
@@ -175,36 +180,56 @@ client.execute!
175
180
  Manticore attempts to avoid doing any actual work until right before you need results. As a result,
176
181
  responses are lazy-evaluated as late as possible. The following rules apply:
177
182
 
178
- 1. Synchronous responses are evaluted when you call an accessor on them, like `#body` or `#headers`.
179
- 2. Synchronous responses which pass a handler block are evaluated immediately.
180
- 3. Asynchronous responses are always evaluated when you call `Client#execute!`
181
- 4. Background responses are always immediately evaluated, but return a `Future`.
183
+ 1. Synchronous and parallel/batch responses are synchronously evaluted when you call an accessor on them, like `#body` or `#headers`, or invoke them with `#call`
184
+ 2. Synchronous and background responses which pass a handler block are evaluated immediately. Sync responses will block the calling thread until complete,
185
+ while background responses will not block the calling thread.
186
+ 3. Parallel/batch responses are evaluated when you call `Client#execute!`. Responses which have been previously evaluted by calling an accessor like `#body` or
187
+ which have been manually called with `#call` will not be re-requested.
188
+ 4. Background responses are evaluated when you call `#call` and return a `Future`, on which you can call `#get` to synchronously get the resolved response.
182
189
 
183
- As a result, with the exception of background requests, this allows you to attach handlers to synchronous
184
- and asynchronous responses in the same fashion:
190
+ As a result, this allows you to attach handlers to synchronous, background, and parallel responses in the same fashion:
185
191
 
186
192
  ```ruby
187
- # Response doesn't evaluate when you call get, since you don't need any results from it yet
188
- response = client.get("http://google.com").on_success {|r| "Success handler!" }
189
- # As soon as you request #body, the response will evaluate to a result.
190
- body = response.body
191
-
192
- response = client.async.get("http://google.com").on_success {|r| "Success handler!" }
193
- client.execute!
194
- body = response.body
193
+ ## Standard/sync requests
194
+ response = client.get("http://google.com")
195
+ response.on_success {|r| puts "Success!" } # Because the response isn't running yet, we can attach a handler
196
+ body = response.body # When you access information from the response, the request finally runs
197
+
198
+ ## Parallel requests
199
+ response1 = client.parallel.get("http://google.com")
200
+ response2 = client.parallel.get("http://yahoo.com")
201
+ response1.on_success {|response| puts "Yay!" }
202
+ response2.on_failure {|exception| puts "Whoops!" }
203
+ client.execute! # Nothing runs until we call Client#execute!
204
+ body = response1.body # Now the responses are resolved and we can get information from them
205
+
206
+ ## Background requests
207
+ request = client.background.get("http://google.com")
208
+ request.on_success {|r| puts "Success!" } # We can attach handlers before the request is kicked off
209
+ future = request.call # We invoke #call on it to fire it off.
210
+ response = future.get # You can get the Response via Future#get. This will block the calling thread until it resolves, though.
195
211
  ```
196
212
 
197
- If you want to make a response that is not lazy-evaluated, you can either pass a handler block to it, or you can
198
- call `#call` on the resulting response:
213
+ If you want to immediately evaluate a request, you can either pass a handler block to it, or you can call `#call` to fire it off:
199
214
 
200
215
  ```ruby
201
216
  # This will evaluate immediately
202
- client.get("http://google.com") {r| r.body }
217
+ client.get("http://google.com") {|r| r.body }
203
218
 
204
219
  # As will this, via explicit invocation of #call
205
220
  client.get("http://google.com").call
206
221
  ```
207
222
 
223
+ For batch/parallel/async requests, a passed block will be treated as the on_success handler, but will not cause the
224
+ request to be immediately invoked:
225
+
226
+ ```ruby
227
+ # This will not evaluate yet
228
+ client.batch.get("http://google.com") {|r| puts "Fetched Google" }
229
+ # ...but now it does.
230
+ client.execute!
231
+ ```
232
+
208
233
  ### Stubbing
209
234
 
210
235
  Manticore provides a stubbing interface somewhat similar to Typhoeus'
@@ -217,18 +242,18 @@ end
217
242
  client.clear_stubs!
218
243
  ```
219
244
 
220
- This works for async requests as well:
245
+ This works for parallel/batch/async requests as well:
221
246
 
222
247
  ```ruby
223
248
  client.stub("http://google.com", body: "response body", code: 200)
224
249
 
225
250
  # The request to google.com returns a stub as expected
226
- client.async.get("http://google.com").on_success do |response|
251
+ client.parallel.get("http://google.com").on_success do |response|
227
252
  response.should be_a Manticore::ResponseStub
228
253
  end
229
254
 
230
255
  # Since yahoo.com isn't stubbed, a full request will be performed
231
- client.async.get("http://yahoo.com").on_success do |response|
256
+ client.parallel.get("http://yahoo.com").on_success do |response|
232
257
  response.should be_a Manticore::Response
233
258
  end
234
259
  client.clear_stubs!
@@ -243,10 +268,10 @@ client.respond_with(body: "body").get("http://google.com") do |response|
243
268
  end
244
269
  ```
245
270
 
246
- You can also chain proxies to, say, stub an async request:
271
+ You can also chain proxies to, say, stub an parallel request:
247
272
 
248
273
  ```ruby
249
- response = client.async.respond_with(body: "response body").get("http://google.com")
274
+ response = client.parallel.respond_with(body: "response body").get("http://google.com")
250
275
  client.execute!
251
276
 
252
277
  response.body.should == "response body"
@@ -266,17 +291,6 @@ end
266
291
  client.unstub("http://google.com")
267
292
  ```
268
293
 
269
- ### Background requests
270
-
271
- You might want to fire-and-forget requests without blocking your calling thread. You can do this with `Client#background`:
272
-
273
- ```ruby
274
- future = client.background.get("http://google.com")
275
- # The request is now running, but the calling thread isn't blocked
276
- # Do whatever stuff you need to right now. At some point, if you want the result of the request, you can call `Future#get`:
277
- response = future.get
278
- ```
279
-
280
294
  ### Faraday Adapter
281
295
 
282
296
  Manticore includes a Faraday adapter. To use it:
data/Rakefile CHANGED
@@ -7,20 +7,24 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
7
7
  end
8
8
  task :default => [:generate_certs, :spec]
9
9
 
10
- require 'rake/javaextensiontask'
10
+ # Download and vendor the jars needed
11
+ require 'jar_installer'
12
+ task :install_jars do
13
+ Jars::JarInstaller.new.send :do_install, true, true
14
+ end
11
15
 
12
- # Dependency jars for the Kerrigan ext build
13
- jars = [
14
- "#{ENV['MY_RUBY_HOME']}/lib/jruby.jar",
15
- "lib/jar/httpcore-4.3.3.jar",
16
- "lib/jar/httpclient-4.3.6.jar"
17
- ]
16
+ ## Build the Manticore extensions into a jar. You may need to install_jars first
17
+ # Dependency jars for the Manticore ext build
18
+ require 'rake/javaextensiontask'
19
+ jars = ["#{ENV['MY_RUBY_HOME']}/lib/jruby.jar"] + Dir.glob("lib/**/*.jar")
20
+ jars.reject! {|j| j.match("manticore-ext") }
18
21
  Rake::JavaExtensionTask.new do |ext|
19
22
  ext.name = "manticore-ext"
20
- ext.lib_dir = "lib/jar"
23
+ ext.lib_dir = "lib/org/manticore"
21
24
  ext.classpath = jars.map {|x| File.expand_path x}.join ':'
22
25
  end
23
26
 
27
+ # Generate all the stuff we need for a full test run
24
28
  task :generate_certs do
25
29
  root = File.expand_path("../spec/ssl", __FILE__)
26
30
  openssl = `which openssl`.strip
@@ -0,0 +1,27 @@
1
+ package org.manticore;
2
+
3
+ import java.net.URI;
4
+ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
5
+
6
+ public class HttpDeleteWithEntity extends HttpEntityEnclosingRequestBase {
7
+ public final static String METHOD_NAME = "DELETE";
8
+
9
+ public HttpDeleteWithEntity() {
10
+ super();
11
+ }
12
+
13
+ public HttpDeleteWithEntity(URI url) {
14
+ super();
15
+ setURI(url);
16
+ }
17
+
18
+ public HttpDeleteWithEntity(String url) {
19
+ super();
20
+ setURI(URI.create(url));
21
+ }
22
+
23
+ @Override
24
+ public String getMethod() {
25
+ return METHOD_NAME;
26
+ }
27
+ }
@@ -1,17 +1,23 @@
1
1
  package org.manticore;
2
2
 
3
3
  import java.net.URI;
4
- import org.apache.http.client.methods.HttpPost;
4
+ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
5
5
 
6
- public class HttpGetWithEntity extends HttpPost {
6
+ public class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {
7
7
  public final static String METHOD_NAME = "GET";
8
8
 
9
+ public HttpGetWithEntity() {
10
+ super();
11
+ }
12
+
9
13
  public HttpGetWithEntity(URI url) {
10
- super(url);
14
+ super();
15
+ setURI(url);
11
16
  }
12
17
 
13
18
  public HttpGetWithEntity(String url) {
14
- super(url);
19
+ super();
20
+ setURI(URI.create(url));
15
21
  }
16
22
 
17
23
  @Override
data/lib/manticore.rb CHANGED
@@ -2,17 +2,8 @@ require 'java'
2
2
  require 'uri'
3
3
  require 'cgi'
4
4
 
5
- jars = ["httpcore-4.3.3", "httpclient-4.3.6", "commons-logging-1.1.3", "commons-codec-1.6.jar", "httpmime-4.3.6.jar"]
6
- jars.each do |jar|
7
- begin
8
- require_relative "./jar/#{jar}"
9
- rescue LoadError
10
- raise "Unable to load #{jar}; is there another version of it in your classpath?"
11
- end
12
- end
13
-
14
- # 4.3.x
15
- require_relative "./jar/manticore-ext"
5
+ require_relative "./manticore_jars.rb"
6
+ require_relative "./org/manticore/manticore-ext"
16
7
 
17
8
  org.manticore.Manticore.new.load(JRuby.runtime, false)
18
9
 
@@ -57,4 +48,10 @@ module Manticore
57
48
 
58
49
  include Facade
59
50
  include_http_client
60
- end
51
+
52
+ def self.disable_httpcomponents_logging!
53
+ props = Java::JavaLang::System.properties
54
+ props.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog")
55
+ props.setProperty("org.apache.commons.logging.simplelog.log.org.apache.http", "error")
56
+ end
57
+ end
@@ -1,5 +1,4 @@
1
1
  require 'thread'
2
- require 'singleton'
3
2
  require 'base64'
4
3
 
5
4
  module Manticore
@@ -87,6 +86,7 @@ module Manticore
87
86
  java_import "org.apache.http.HttpHost"
88
87
  java_import "javax.net.ssl.SSLContext"
89
88
  java_import "org.manticore.HttpGetWithEntity"
89
+ java_import "org.manticore.HttpDeleteWithEntity"
90
90
  java_import "org.apache.http.auth.UsernamePasswordCredentials"
91
91
 
92
92
  include ProxiesInterface
@@ -101,6 +101,8 @@ module Manticore
101
101
  DEFAULT_EXPECT_CONTINUE = false
102
102
  DEFAULT_STALE_CHECK = false
103
103
 
104
+ attr_reader :client
105
+
104
106
  # Create a new HTTP client with a backing request pool. if you pass a block to the initializer, the underlying
105
107
  # {http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/HttpClientBuilder.html HttpClientBuilder}
106
108
  # and {http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/config/RequestConfig.Builder.html RequestConfig.Builder}
@@ -134,6 +136,7 @@ module Manticore
134
136
  # on GET, HEAD, PUT, DELETE, OPTIONS, and TRACE
135
137
  # @option options [boolean] expect_continue (false) Enable support for HTTP 100
136
138
  # @option options [boolean] stale_check (false) Enable support for stale connection checking. Adds overhead.
139
+ # @option options [integer] check_connection_timeout (15_000) Connections that haven't been used in this many milliseconds will be validated before being used. Set to a negative number to disable.
137
140
  # @option options [String] proxy Proxy host in form: http://proxy.org:1234
138
141
  # @option options [Hash] proxy Proxy host in form: {host: 'proxy.org'[, port: 80[, scheme: 'http'[, user: 'username@host', password: 'password']]]}
139
142
  # @option options [Hash] proxy Proxy host in form: {url: 'http://proxy.org:1234'[, user: 'username@host', password: 'password']]]}
@@ -249,7 +252,7 @@ module Manticore
249
252
  # @macro http_method_shared_sync
250
253
  def delete(url, options = {}, &block)
251
254
  options = treat_params_as_query(options)
252
- request HttpDelete, url, options, &block
255
+ request HttpDeleteWithEntity, url, options, &block
253
256
  end
254
257
 
255
258
  # Perform a HTTP OPTIONS request
@@ -361,11 +364,11 @@ module Manticore
361
364
  def pool(options = {})
362
365
  @pool ||= begin
363
366
  @max_pool_size = options.fetch(:pool_max, DEFAULT_MAX_POOL_SIZE)
364
- cm = pool_builder options
365
- cm.set_default_max_per_route options.fetch(:pool_max_per_route, @max_pool_size)
366
- cm.set_max_total @max_pool_size
367
- IdleConnectionReaper.instance.monitor(cm)
368
- cm
367
+ pool_builder(options).tap do |cm|
368
+ cm.set_validate_after_inactivity options.fetch(:check_connection_timeout, 15_000)
369
+ cm.set_default_max_per_route options.fetch(:pool_max_per_route, @max_pool_size)
370
+ cm.set_max_total @max_pool_size
371
+ end
369
372
  end
370
373
  end
371
374
 
@@ -377,37 +380,32 @@ module Manticore
377
380
 
378
381
  def request(klass, url, options, &block)
379
382
  req, context = request_from_options(klass, url, options)
380
- if options.delete(:async)
381
- async_request req, context
382
- else
383
- sync_request req, context, &block
383
+ async = options.delete(:async)
384
+ background = options.delete(:async_background)
385
+ create_executor_if_needed if (background || async)
386
+ response = response_object_for(req, context, &block)
387
+
388
+ if async
389
+ @async_requests << response
390
+ elsif background
391
+ response.background = true
384
392
  end
385
- end
386
393
 
387
- def async_request(request, context)
388
- create_executor_if_needed
389
- response = response_object_for(@client, request, context)
390
- @async_requests << response
391
- response
392
- end
393
-
394
- def sync_request(request, context, &block)
395
- response = response_object_for(@client, request, context, &block)
396
- if block_given?
394
+ if block_given? && (background || !async)
397
395
  response.call
398
396
  else
399
397
  response
400
398
  end
401
399
  end
402
400
 
403
- def response_object_for(client, request, context, &block)
401
+ def response_object_for(request, context, &block)
404
402
  request_uri = request.getURI.to_s
405
403
 
406
404
  match_key = @stubs.keys.find { |k| request_uri.match(k) }
407
405
  if match_key
408
- StubbedResponse.new(client, request, context, &block).stub( @stubs[match_key] )
406
+ StubbedResponse.new(self, request, context, &block).stub( @stubs[match_key] )
409
407
  else
410
- Response.new(client, request, context, &block)
408
+ Response.new(self, request, context, &block)
411
409
  end
412
410
  end
413
411
 
@@ -421,8 +419,7 @@ module Manticore
421
419
  def request_from_options(klass, url, options)
422
420
  req = klass.new uri_from_url_and_options(url, options).to_s
423
421
 
424
- if ( options[:params] || options[:body] || options[:entity]) &&
425
- ( req.instance_of?(HttpPost) || req.instance_of?(HttpPatch) || req.instance_of?(HttpPut) || req.instance_of?(HttpGetWithEntity))
422
+ if ( options[:params] || options[:body] || options[:entity]) && req.kind_of?(HttpEntityEnclosingRequestBase)
426
423
  if options[:params]
427
424
  pairs = struct_to_name_value_pairs(options[:params])
428
425
  encoding = minimum_encoding_for options[:params].to_s
@@ -662,29 +659,4 @@ module Manticore
662
659
  super(exception, executionCount, context)
663
660
  end
664
661
  end
665
-
666
- class IdleConnectionReaper
667
- include Singleton
668
- def initialize
669
- @mutex = Mutex.new
670
- @pools = []
671
- @running = Java::JavaUtilConcurrentAtomic::AtomicBoolean.new(true)
672
- @thread = Thread.new do
673
- while @running.get
674
- @mutex.synchronize { @pools.each(&:closeExpiredConnections) }
675
- sleep 5000
676
- end
677
- end
678
- at_exit { shutdown }
679
- end
680
-
681
- def monitor(pool)
682
- @mutex.synchronize { @pools << pool }
683
- end
684
-
685
- def shutdown
686
- @running.set(false)
687
- @thread.wakeup
688
- end
689
- end
690
662
  end
@@ -9,6 +9,8 @@ module Manticore
9
9
  def async
10
10
  AsyncProxy.new(self)
11
11
  end
12
+ alias_method :parallel, :async
13
+ alias_method :batch, :async
12
14
 
13
15
  # Causes the next request to be made immediately in the background
14
16
  def background
@@ -48,8 +50,7 @@ module Manticore
48
50
  class BackgroundProxy < BaseProxy
49
51
  %w(get put head post delete options patch).each do |func|
50
52
  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
+ @client.send(func, url, options.merge(async_background: true), &block)
53
54
  end
54
55
  end
55
56
  end
@@ -24,17 +24,26 @@ module Manticore
24
24
  # @param options Hash Options to be passed to the underlying shared client, if it has not been created yet.
25
25
  # @return nil
26
26
  def include_http_client(options = {}, &block)
27
- if shared_pool = options.delete(:shared_pool)
28
- @manticore_facade = Manticore.instance_variable_get("@manticore_facade")
29
- else
30
- @manticore_facade = Manticore::Client.new(options, &block)
31
- end
27
+ @__manticore_facade_options = [options, block]
32
28
  class << self
33
29
  extend Forwardable
34
- def_delegators "@manticore_facade", :get, :head, :put, :post, :delete, :options, :patch
30
+ def_delegators "__manticore_facade", :get, :head, :put, :post, :delete, :options, :patch, :http
35
31
  end
36
32
  nil
37
33
  end
34
+
35
+ private
36
+
37
+ def __manticore_facade
38
+ @manticore_facade ||= begin
39
+ options, block = *@__manticore_facade_options
40
+ if shared_pool = options.delete(:shared_pool)
41
+ Manticore.send(:__manticore_facade)
42
+ else
43
+ Manticore::Client.new(options, &block)
44
+ end
45
+ end
46
+ end
38
47
  end
39
48
  end
40
49
  end
@@ -20,10 +20,12 @@ module Manticore
20
20
  include ResponseHandler
21
21
  include Callable
22
22
 
23
- attr_reader :context, :request, :callback_result, :called
23
+ attr_accessor :background
24
+ attr_reader :context, :request, :callback_result, :called, :future
24
25
 
25
26
  # Creates a new Response
26
27
  #
28
+ # @param client [Manticore::Client] The client that was used to create this response
27
29
  # @param request [HttpRequestBase] The underlying request object
28
30
  # @param context [HttpContext] The underlying HttpContext
29
31
  def initialize(client, request, context, &block)
@@ -41,10 +43,11 @@ module Manticore
41
43
  # Implementation of Callable#call
42
44
  # Used by Manticore::Client to invoke the request tied to this response.
43
45
  def call
46
+ return background! if @background
44
47
  raise "Already called" if @called
45
48
  @called = true
46
49
  begin
47
- @client.execute @request, self, @context
50
+ @client.client.execute @request, self, @context
48
51
  rescue Java::JavaNet::SocketTimeoutException => e
49
52
  ex = Manticore::SocketTimeout
50
53
  rescue Java::OrgApacheHttpConn::ConnectTimeoutException => e
@@ -76,10 +79,6 @@ module Manticore
76
79
  end
77
80
  end
78
81
 
79
- def fire_and_forget
80
- @client.executor.submit self
81
- end
82
-
83
82
  # Fetch the final resolved URL for this response. Will call the request if it has not been called yet.
84
83
  #
85
84
  # @return [String]
@@ -200,7 +199,7 @@ module Manticore
200
199
  alias_method :failure, :on_failure
201
200
  alias_method :fail, :on_failure
202
201
 
203
- # Set handler for cancelled requests
202
+ # Set handler for cancelled requests. NB: Not actually used right now?
204
203
  # @param block Proc which will be invoked on a on a cancelled response.
205
204
  #
206
205
  # @return self
@@ -212,9 +211,9 @@ module Manticore
212
211
  alias_method :cancellation, :on_cancelled
213
212
  alias_method :on_cancellation, :on_cancelled
214
213
 
215
- # Set handler for cancelled requests
216
- # @param block Proc which will be invoked on a on a cancelled response.
217
- #
214
+ # Set handler for completed requests
215
+ # @param block Proc which will be invoked on a on a completed response. This handler will be called
216
+ # regardless of request success/failure.
218
217
  # @return self
219
218
  def on_complete(&block)
220
219
  @handlers[:complete] = Array(@handlers[:complete]).compact + [block]
@@ -230,6 +229,11 @@ module Manticore
230
229
 
231
230
  private
232
231
 
232
+ def background!
233
+ @background = false
234
+ @future ||= @client.executor.java_method(:submit, [java.util.concurrent.Callable.java_class]).call(self)
235
+ end
236
+
233
237
  # Implementation of {http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/ResponseHandler.html#handleResponse(org.apache.http.HttpResponse) ResponseHandler#handleResponse}
234
238
  # @param response [Response] The underlying Java Response object
235
239
  def handleResponse(response)
@@ -242,7 +246,11 @@ module Manticore
242
246
  end
243
247
 
244
248
  def call_once
249
+ is_background = @background
245
250
  call unless called?
251
+ # If this is a background request, then we don't want to allow the usage of sync methods, as it's probably a semantic error. We could resolve the future
252
+ # but that'll probably result in blocking foreground threads unintentionally. Fail loudly.
253
+ raise RuntimeError.new("Cannot call synchronous methods on a background response. Use an on_success handler instead.") if is_background && @future
246
254
  @called = true
247
255
  end
248
256
 
@@ -1,3 +1,3 @@
1
1
  module Manticore
2
- VERSION = "0.4.4"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -0,0 +1,8 @@
1
+ # this is a generated file, to avoid over-writing it just delete this comment
2
+ require 'jar_dependencies'
3
+
4
+ require_jar( 'org.apache.httpcomponents', 'httpclient', '4.5' )
5
+ require_jar( 'org.apache.httpcomponents', 'httpcore', '4.4.1' )
6
+ require_jar( 'commons-codec', 'commons-codec', '1.9' )
7
+ require_jar( 'commons-logging', 'commons-logging', '1.2' )
8
+ require_jar( 'org.apache.httpcomponents', 'httpmime', '4.5' )
Binary file
data/manticore.gemspec CHANGED
@@ -27,4 +27,8 @@ Gem::Specification.new do |spec|
27
27
 
28
28
  spec.add_development_dependency "bundler", "~> 1.3"
29
29
  spec.add_development_dependency "rake"
30
+
31
+ spec.add_runtime_dependency "jar-dependencies"
32
+ spec.requirements << "jar org.apache.httpcomponents:httpclient, 4.5"
33
+ spec.requirements << "jar org.apache.httpcomponents:httpmime, 4.5"
30
34
  end
@@ -75,7 +75,7 @@ describe Manticore::Client do
75
75
  describe Manticore::Client::BackgroundProxy do
76
76
  it "does not block execution" do
77
77
  anchor = Time.now.to_f
78
- future = client.background.get("http://localhost:55441/?sleep=1.5")
78
+ future = client.background.get("http://localhost:55441/?sleep=1.5").call
79
79
  expect(Time.now.to_f - anchor).to be < 1.0
80
80
 
81
81
  response = future.get
@@ -83,10 +83,25 @@ describe Manticore::Client do
83
83
  expect(response.body).to match(/sleep=1.5/)
84
84
  end
85
85
 
86
- it "returns a future" do
86
+ it "returns a response proxy" do
87
87
  response = client.background.get("http://localhost:55441/")
88
- expect(response).to be_a Java::JavaUtilConcurrent::FutureTask
89
- response.get
88
+ expect(response).to be_a Manticore::Response
89
+
90
+ success = false
91
+ response.on_success do
92
+ success = true
93
+ end
94
+
95
+ response.call.get
96
+ expect(success).to eq true
97
+ end
98
+
99
+ describe "#call" do
100
+ it "returns a future" do
101
+ response = client.background.get("http://localhost:55441/").call
102
+ expect(response).to be_a Java::JavaUtilConcurrent::FutureTask
103
+ response.get
104
+ end
90
105
  end
91
106
  end
92
107
  end
@@ -462,6 +462,18 @@ describe Manticore::Client do
462
462
  end
463
463
  end
464
464
 
465
+ describe "#delete" do
466
+ it "works" do
467
+ response = client.delete(local_server)
468
+ expect(JSON.load(response.body)["method"]).to eq "DELETE"
469
+ end
470
+
471
+ it "sends a body" do
472
+ response = client.delete(local_server, body: "This is a delete body")
473
+ expect(JSON.load(response.body)["body"]).to eq "This is a delete body"
474
+ end
475
+ end
476
+
465
477
  describe "#head" do
466
478
  it "works" do
467
479
  response = client.head(local_server)
@@ -634,15 +646,18 @@ describe Manticore::Client do
634
646
  @server = Thread.new do
635
647
  loop do
636
648
  client = @socket.accept
637
- client.puts([
638
- "HTTP/1.1 200 OK",
639
- "Keep-Alive: timeout=3000",
640
- "Connection: Keep-Alive",
641
- "Content-Length: 6",
642
- "",
643
- "Hello!"
644
- ].join("\n"))
645
- client.close
649
+ begin
650
+ client.puts([
651
+ "HTTP/1.1 200 OK",
652
+ "Keep-Alive: timeout=3000",
653
+ "Connection: Keep-Alive",
654
+ "Content-Length: 6",
655
+ "",
656
+ "Hello!"
657
+ ].join("\n"))
658
+ client.close
659
+ rescue StandardError => e
660
+ end
646
661
  end
647
662
  end
648
663
  end
@@ -707,6 +722,70 @@ describe Manticore::Client do
707
722
  end
708
723
  end
709
724
 
725
+ describe "request types:" do
726
+ describe "sync" do
727
+ it "returns a response" do
728
+ expect(client.get(local_server)).to be_a Manticore::Response
729
+ end
730
+
731
+ it "evaluates immediately when given a block" do
732
+ called = false
733
+ expect { client.get(local_server) { called = true } }.to change { called }.to(true)
734
+ end
735
+
736
+ it "can be evaluated synchronously on-demand" do
737
+ expect(client.get(local_server).body).to be_a String
738
+ end
739
+ end
740
+
741
+ describe "async" do
742
+ it "returns a response" do
743
+ expect(client.async.get(local_server)).to be_a Manticore::Response
744
+ end
745
+
746
+ it "works with `parallel`" do
747
+ expect(client.parallel.get(local_server)).to be_a Manticore::Response
748
+ end
749
+
750
+ it "works with `batch`" do
751
+ expect(client.parallel.get(local_server)).to be_a Manticore::Response
752
+ end
753
+
754
+ it "does not evaluate immediately when given a block" do
755
+ called = false
756
+ expect { client.async.get(local_server) { called = true }; sleep 0.5 }.to_not change { called }
757
+ end
758
+
759
+ it "can be evaluated synchronously on-demand" do
760
+ expect(client.async.get(local_server).body).to be_a String
761
+ end
762
+ end
763
+
764
+ describe "background" do
765
+ it "returns a response" do
766
+ expect( client.background.get(local_server) ).to be_a Manticore::Response
767
+ end
768
+
769
+ it "has the background flag set" do
770
+ expect( client.background.get(local_server).background ).to eq true
771
+ end
772
+
773
+ it "returns a future when called" do
774
+ expect( client.background.get(local_server).call ).to be_a Java::JavaUtilConcurrent::FutureTask
775
+ end
776
+
777
+ it "evaluates immediately when given a block" do
778
+ called = false
779
+ expect { (client.background.get(local_server) { called = true }).get }.to change { called }.to(true)
780
+ end
781
+
782
+ it "raises an exception when a sync method is called on it" do
783
+ called = false
784
+ expect { client.background.get(local_server).body }.to raise_exception(RuntimeError)
785
+ end
786
+ end
787
+ end
788
+
710
789
  def get_connection(client, uri, &block)
711
790
  java_import "java.util.concurrent.TimeUnit"
712
791
  host = URI.parse(uri).host
@@ -22,13 +22,18 @@ describe Manticore::Facade do
22
22
  end
23
23
 
24
24
  it "does not use the shared client by default" do
25
- expect(extended_class.instance_variable_get("@manticore_facade").object_id).to_not eq \
26
- Manticore.instance_variable_get("@manticore_facade").object_id
25
+ expect(extended_class.send(:__manticore_facade).object_id).to_not eq \
26
+ Manticore.send(:__manticore_facade).object_id
27
27
  end
28
28
 
29
29
  it "is able to use the shared client" do
30
- expect(extended_shared_class.instance_variable_get("@manticore_facade").object_id).to eq \
31
- Manticore.instance_variable_get("@manticore_facade").object_id
30
+ expect(extended_shared_class.send(:__manticore_facade).object_id).to eq \
31
+ Manticore.send(:__manticore_facade).object_id
32
+ end
33
+
34
+ it "should work with #http" do
35
+ result = JSON.parse extended_class.http(:get, local_server).body
36
+ expect(result["method"]).to eq "GET"
32
37
  end
33
38
  end
34
39
 
@@ -46,4 +46,45 @@ describe Manticore::Response do
46
46
  expect { client.get(local_server).call rescue nil }.to_not change { client.pool_stats[:available] }
47
47
  end
48
48
  end
49
+
50
+ context "given a success handler" do
51
+ let(:responses) { {} }
52
+ let(:response) do
53
+ client.get(url)
54
+ .on_success {|resp| responses[:success] = true }
55
+ .on_failure {responses[:failure] = true }
56
+ .on_complete {responses[:complete] = true }
57
+ end
58
+
59
+ context "a succeeded request" do
60
+ let(:url) { local_server }
61
+ it "runs the success handler" do
62
+ expect { response.call }.to change { responses[:success] }.to true
63
+ end
64
+
65
+ it "does not run the failure handler" do
66
+ expect { response.call }.to_not change { responses[:failure] }
67
+ end
68
+
69
+ it "runs the completed handler" do
70
+ expect { response.call }.to change { responses[:complete] }.to true
71
+ end
72
+ end
73
+
74
+ context "a failed request" do
75
+ let(:url) { local_server("/failearly") }
76
+ it "does not run the success handler" do
77
+ expect { response.call }.to_not change { responses[:success] }
78
+ end
79
+
80
+ it "runs the failure handler" do
81
+ expect { response.call }.to change { responses[:failure] }.to true
82
+ end
83
+
84
+ it "runs the completed handler" do
85
+ expect { response.call }.to change { responses[:complete] }.to true
86
+ end
87
+ end
88
+
89
+ end
49
90
  end
data/spec/spec_helper.rb CHANGED
@@ -8,6 +8,7 @@ require 'webrick'
8
8
  require 'webrick/https'
9
9
  require 'openssl'
10
10
  require 'rspec/its'
11
+ require 'logger'
11
12
 
12
13
  PORT = 55441
13
14
 
@@ -31,7 +32,7 @@ end
31
32
  def start_server(port = PORT)
32
33
  @servers ||= {}
33
34
  @servers[port] = Thread.new {
34
- Net::HTTP::Server.run(port: port) do |request, stream|
35
+ Net::HTTP::Server.run(port: port, log: Logger.new("/dev/null")) do |request, stream|
35
36
 
36
37
  query = Rack::Utils.parse_query(request[:uri][:query].to_s)
37
38
  if query["sleep"]
@@ -72,9 +73,9 @@ def start_server(port = PORT)
72
73
  if request[:headers]["Accept-Encoding"] && request[:headers]["Accept-Encoding"].match("gzip")
73
74
  out = StringIO.new('', "w")
74
75
  io = Zlib::GzipWriter.new(out, 2)
75
-
76
+
76
77
  request[:body] = Base64.encode64(request[:body]) if request[:headers]["X-Base64"]
77
-
78
+
78
79
  io.write JSON.dump(request)
79
80
  io.close
80
81
  payload = out.string
@@ -127,6 +128,8 @@ RSpec.configure do |c|
127
128
  start_server 55442
128
129
  start_ssl_server 55444
129
130
  start_ssl_server 55445, :SSLVerifyClient => OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT, :SSLCACertificateFile => File.expand_path("../ssl/root-ca.crt", __FILE__)
131
+
132
+ Manticore.disable_httpcomponents_logging!
130
133
  }
131
134
 
132
135
  c.after(:suite) { stop_servers }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: manticore
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4
4
+ version: 0.5.0
5
5
  platform: java
6
6
  authors:
7
7
  - Chris Heald
@@ -30,12 +30,12 @@ cert_chain:
30
30
  cnyabLOcGIKZNxvnSfwOuCBnjgoSOyJi/n48n1s+OPB/OmPJoWmhKu2DO4sUb4+K
31
31
  /3Mhp5UWSl9SmDR1
32
32
  -----END CERTIFICATE-----
33
- date: 2015-08-20 00:00:00.000000000 Z
33
+ date: 2015-10-07 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  requirement: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.3'
41
41
  name: bundler
@@ -43,13 +43,13 @@ dependencies:
43
43
  type: :development
44
44
  version_requirements: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - "~>"
46
+ - - ~>
47
47
  - !ruby/object:Gem::Version
48
48
  version: '1.3'
49
49
  - !ruby/object:Gem::Dependency
50
50
  requirement: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  name: rake
@@ -57,7 +57,21 @@ dependencies:
57
57
  type: :development
58
58
  version_requirements: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - ">="
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ name: jar-dependencies
70
+ prerelease: false
71
+ type: :runtime
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
61
75
  - !ruby/object:Gem::Version
62
76
  version: '0'
63
77
  description: Manticore is an HTTP client built on the Apache HttpCore components
@@ -67,24 +81,19 @@ executables: []
67
81
  extensions: []
68
82
  extra_rdoc_files: []
69
83
  files:
70
- - ".gitignore"
71
- - ".travis.yml"
84
+ - .gitignore
85
+ - .travis.yml
72
86
  - APACHE-LICENSE-2.0.txt
73
87
  - CHANGELOG.md
74
88
  - Gemfile
75
89
  - LICENSE.txt
76
90
  - README.md
77
91
  - Rakefile
92
+ - ext/manticore/org/manticore/HttpDeleteWithEntity.java
78
93
  - ext/manticore/org/manticore/HttpGetWithEntity.java
79
94
  - ext/manticore/org/manticore/Manticore.java
80
95
  - gem-public_cert.pem
81
96
  - lib/faraday/adapter/manticore.rb
82
- - lib/jar/commons-codec-1.6.jar
83
- - lib/jar/commons-logging-1.1.3.jar
84
- - lib/jar/httpclient-4.3.6.jar
85
- - lib/jar/httpcore-4.3.3.jar
86
- - lib/jar/httpmime-4.3.6.jar
87
- - lib/jar/manticore-ext.jar
88
97
  - lib/manticore.rb
89
98
  - lib/manticore/client.rb
90
99
  - lib/manticore/client/proxies.rb
@@ -94,6 +103,8 @@ files:
94
103
  - lib/manticore/response.rb
95
104
  - lib/manticore/stubbed_response.rb
96
105
  - lib/manticore/version.rb
106
+ - lib/manticore_jars.rb
107
+ - lib/org/manticore/manticore-ext.jar
97
108
  - manticore.gemspec
98
109
  - spec/manticore/client_proxy_spec.rb
99
110
  - spec/manticore/client_spec.rb
@@ -113,15 +124,17 @@ require_paths:
113
124
  - lib
114
125
  required_ruby_version: !ruby/object:Gem::Requirement
115
126
  requirements:
116
- - - ">="
127
+ - - '>='
117
128
  - !ruby/object:Gem::Version
118
129
  version: '0'
119
130
  required_rubygems_version: !ruby/object:Gem::Requirement
120
131
  requirements:
121
- - - ">="
132
+ - - '>='
122
133
  - !ruby/object:Gem::Version
123
134
  version: '0'
124
- requirements: []
135
+ requirements:
136
+ - jar org.apache.httpcomponents:httpclient, 4.5
137
+ - jar org.apache.httpcomponents:httpmime, 4.5
125
138
  rubyforge_project:
126
139
  rubygems_version: 2.4.8
127
140
  signing_key:
@@ -136,4 +149,3 @@ test_files:
136
149
  - spec/manticore/stubbed_response_spec.rb
137
150
  - spec/spec_helper.rb
138
151
  - spec/ssl/.keepme
139
- has_rdoc:
metadata.gz.sig CHANGED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file