manticore 0.3.0-java → 0.3.1-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: 0078d294015d5ca40a8e4afadc48973fb9246de1
4
- data.tar.gz: bcff310d2a92c1e592cac51f63859a3ee483b63f
3
+ metadata.gz: 60f70fa96f93b6d9092b0455c139ae2aaf077fcf
4
+ data.tar.gz: ea9ab057b99806987c30ff3198cefd1d0d6ae455
5
5
  SHA512:
6
- metadata.gz: ef8bfdbaf46d56810e0f137fb4cf96425a46bc22ecf157f9451093bcf9acf721ff4cff86161593ec7c95112a64793856e5345c19454569d0b3ca457c3216d99f
7
- data.tar.gz: cd2bcc65d8d377d8622fae139e0f4c5e91816bcd0cea3926c8d1a5503eda2e68c28ef179cae5f153a389cd8c1474bb5ffbb3f789408e197f8d297b385b741e0a
6
+ metadata.gz: 24112c6208d50c27c68745340cf29c87f6cc37a8abeebb3bc051d2dc37ba883465f78e027e3dc4b5d77122c2bc9c2d9c2b6377607715bff5c953e38f0171ddb1
7
+ data.tar.gz: db3909740b474447d5467290992aad8b51fa34c39006714d14848650686f3ffaa095c3e8674df2f7ae0c834d7ac8fda4cc291485e790f9f3f44289a2041a647e
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## v0.3
2
- ### v0.3.0 (pending)
2
+ ### v0.3.1
3
+ * Added `automatic_retries` (default 3) parameter to client. The client will automatically retry requests that failed
4
+ due to socket exceptions and empty responses up to this number of times. The most practical effect of this setting is
5
+ to automatically retry when the pool reuses a connection that a client unexpectedly closed.
6
+ * Added `request_timeout` to the RequestConfig used to construct requests.
7
+ * Fixed implementation of the `:query` parameter for GET, HEAD, and DELETE requests.
8
+
9
+ ### v0.3.0
3
10
 
4
11
  * Major refactor of `Response`/`AsyncResponse` to eliminate redundant code. `AsyncResponse` has been removed and
5
12
  its functionality has been rolled into `Response`.
data/Gemfile CHANGED
@@ -3,8 +3,8 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in manticore.gemspec
4
4
  gemspec
5
5
 
6
- gem "net-http-server"
7
- gem "rspec"
8
- gem "httpclient"
9
- gem "rack"
6
+ gem "net-http-server", "~> 0.2"
7
+ gem "rspec", "~> 2.14"
8
+ gem "httpclient", "~> 2.3"
9
+ gem "rack", "~> 1.5"
10
10
  gem "rake-compiler"
@@ -77,7 +77,7 @@ public class Manticore implements Library {
77
77
  byte[] tmp = new byte[4096];
78
78
  int l;
79
79
  while((l = instream.read(tmp)) != -1) {
80
- block.call( context, RubyString.newString(context.getRuntime(), new ByteList(tmp, true), encoding) );
80
+ block.call( context, RubyString.newString(context.getRuntime(), new ByteList(tmp, 0, l, true), encoding) );
81
81
  }
82
82
  } finally {
83
83
  instream.close();
Binary file
Binary file
Binary file
data/lib/manticore.rb CHANGED
@@ -3,7 +3,7 @@ require 'uri'
3
3
  require 'cgi'
4
4
  require 'cgi/cookie'
5
5
 
6
- jars = ["httpcore-4.3.1", "httpclient-4.3.2-patched", "commons-logging-1.1.3", "commons-codec-1.6.jar"]
6
+ jars = ["httpcore-4.3.1", "httpclient-4.3.2-patched", "commons-logging-1.1.3", "commons-codec-1.6.jar", "httpmime-4.3.2.jar"]
7
7
  jars.each do |jar|
8
8
  begin
9
9
  require_relative "./jar/#{jar}"
@@ -70,7 +70,9 @@ module Manticore
70
70
  include_package "org.apache.http.auth"
71
71
  include_package "java.util.concurrent"
72
72
  include_package "org.apache.http.client.protocol"
73
+ include_package 'org.apache.http.conn.ssl'
73
74
  java_import "org.apache.http.HttpHost"
75
+
74
76
  include ProxiesInterface
75
77
 
76
78
  # The default maximum pool size for requests
@@ -108,8 +110,10 @@ module Manticore
108
110
  # @option options [integer] request_timeout (60) Sets the timeout for requests. Raises {Manticore::Timeout} on failure.
109
111
  # @option options [integer] connect_timeout (10) Sets the timeout for connections. Raises Manticore::Timeout on failure.
110
112
  # @option options [integer] socket_timeout (10) Sets SO_TIMEOUT for open connections. A value of 0 is an infinite timeout. Raises Manticore::Timeout on failure.
113
+ # @option options [boolean] tcp_no_delay (true) Enable or disable Nagle's algorithm
111
114
  # @option options [integer] request_timeout (60) Sets the timeout for a given request. Raises Manticore::Timeout on failure.
112
115
  # @option options [integer] max_redirects (5) Sets the maximum number of redirects to follow.
116
+ # @option options [integer] automatic_retries (3) Sets the number of times the client will automatically retry failed requests.
113
117
  # @option options [boolean] expect_continue (false) Enable support for HTTP 100
114
118
  # @option options [boolean] stale_check (false) Enable support for stale connection checking. Adds overhead.
115
119
  # @option options [String] proxy Proxy host in form: http://proxy.org:1234
@@ -126,6 +130,20 @@ module Manticore
126
130
  builder.disable_content_compression if options.fetch(:compression, true) == false
127
131
  builder.set_proxy get_proxy_host(options[:proxy]) if options.key?(:proxy)
128
132
 
133
+ builder.set_retry_handler do |exception, executionCount, context|
134
+ if (executionCount > options.fetch(:automatic_retries, 3))
135
+ false
136
+ else
137
+ case exception
138
+ when Java::OrgApacheHttp::NoHttpResponseException, Java::JavaNet::SocketException
139
+ context.setAttribute "retryCount", executionCount
140
+ true
141
+ else
142
+ false
143
+ end
144
+ end
145
+ end
146
+
129
147
  # This should make it easier to reuse connections
130
148
  # TODO: Determine what this actually does!
131
149
  # builder.disable_connection_state
@@ -138,7 +156,8 @@ module Manticore
138
156
  end
139
157
 
140
158
  socket_config_builder = SocketConfig.custom
141
- socket_config_builder.setSoTimeout( options.fetch(:socket_timeout, DEFAULT_SOCKET_TIMEOUT) * 1000 )
159
+ socket_config_builder.set_so_timeout( options.fetch(:socket_timeout, DEFAULT_SOCKET_TIMEOUT) * 1000 )
160
+ socket_config_builder.set_tcp_no_delay( options.fetch(:tcp_no_delay, true) )
142
161
  builder.set_default_socket_config socket_config_builder.build
143
162
 
144
163
  builder.set_connection_manager pool(options)
@@ -269,16 +288,24 @@ module Manticore
269
288
  HttpClientBuilder.create
270
289
  end
271
290
 
272
- def pool_builder
273
- PoolingHttpClientConnectionManager.new
291
+ def pool_builder(options)
292
+ if options.fetch(:ignore_ssl_validation, false)
293
+ context = SSLContexts.custom.load_trust_material(nil, TrustSelfSignedStrategy.new).build
294
+ sslsf = SSLConnectionSocketFactory.new(context, SSLConnectionSocketFactory::ALLOW_ALL_HOSTNAME_VERIFIER)
295
+ registry = RegistryBuilder.create.register("https", sslsf).build
296
+ PoolingHttpClientConnectionManager.new(registry)
297
+ else
298
+ PoolingHttpClientConnectionManager.new
299
+ end
274
300
  end
275
301
 
276
302
  def pool(options = {})
277
303
  @pool ||= begin
278
304
  @max_pool_size = options.fetch(:pool_max, DEFAULT_MAX_POOL_SIZE)
279
- cm = pool_builder
305
+ cm = pool_builder options
280
306
  cm.set_default_max_per_route options.fetch(:pool_max_per_route, @max_pool_size)
281
307
  cm.set_max_total @max_pool_size
308
+
282
309
  Thread.new {
283
310
  loop {
284
311
  cm.closeExpiredConnections
@@ -332,12 +359,12 @@ module Manticore
332
359
  def uri_from_url_and_options(url, options)
333
360
  uri = Addressable::URI.parse url
334
361
  if options[:query]
335
- uri.query_values ||= {}
362
+ v = uri.query_values || {}
336
363
  case options[:query]
337
364
  when Hash
338
- uri.query_values.merge! options[:query]
365
+ uri.query_values = v.merge options[:query]
339
366
  when String
340
- uri.query_values.merge! CGI.parse(options[:query])
367
+ uri.query_values = v.merge CGI.parse(options[:query])
341
368
  else
342
369
  raise "Queries must be hashes or strings"
343
370
  end
@@ -348,22 +375,25 @@ module Manticore
348
375
  def request_from_options(klass, url, options)
349
376
  req = klass.new uri_from_url_and_options(url, options).to_s
350
377
 
351
- if ( options[:params] || options[:body] ) &&
378
+ if ( options[:params] || options[:body] || options[:entity]) &&
352
379
  ( req.instance_of?(HttpPost) || req.instance_of?(HttpPatch) || req.instance_of?(HttpPut) )
353
380
  if options[:params]
354
381
  req.set_entity hash_to_entity(options[:params])
355
382
  elsif options[:body]
356
383
  req.set_entity StringEntity.new(options[:body])
384
+ elsif options[:entity]
385
+ req.set_entity options[:entity]
357
386
  end
358
387
  end
359
388
 
360
389
  if options.key?(:proxy) || options.key?(:connect_timeout) || options.key?(:socket_timeout) || options.key?(:max_redirects) || options.key?(:follow_redirects)
361
390
  config = RequestConfig.custom()
362
- config.set_proxy get_proxy_host(options[:proxy]) if options[:proxy]
363
- config.set_connect_timeout options[:connect_timeout] if options[:connect_timeout]
364
- config.set_socket_timeout options[:socket_timeout] if options[:socket_timeout]
365
- config.set_max_redirects options[:max_redirects] if options[:max_redirects]
366
- config.set_redirects_enabled !!options[:follow_redirects] if options.fetch(:follow_redirects, nil) != nil
391
+ config.set_proxy get_proxy_host(options[:proxy]) if options[:proxy]
392
+ config.set_connect_timeout options[:connect_timeout] if options[:connect_timeout]
393
+ config.set_socket_timeout options[:socket_timeout] if options[:socket_timeout]
394
+ config.set_max_redirects options[:max_redirects] if options[:max_redirects]
395
+ config.set_redirects_enabled !!options[:follow_redirects] if options.fetch(:follow_redirects, nil) != nil
396
+ config.set_connection_request_timeout options[:request_timeout] if options[:request_timeout]
367
397
  req.set_config config.build
368
398
  end
369
399
 
@@ -47,11 +47,12 @@ module Manticore
47
47
  @client.execute @request, self, @context
48
48
  execute_complete
49
49
  return self
50
- rescue Java::JavaNet::SocketTimeoutException, Java::OrgApacheHttpConn::ConnectTimeoutException, Java::OrgApacheHttp::NoHttpResponseException => e
50
+ rescue Java::JavaNet::SocketTimeoutException, Java::OrgApacheHttpConn::ConnectTimeoutException => e
51
51
  ex = Manticore::Timeout.new(e.get_cause)
52
52
  rescue Java::JavaNet::SocketException => e
53
53
  ex = Manticore::SocketException.new(e.get_cause)
54
- rescue Java::OrgApacheHttpClient::ClientProtocolException, Java::JavaxNetSsl::SSLHandshakeException, Java::OrgApacheHttpConn::HttpHostConnectException => e
54
+ rescue Java::OrgApacheHttpClient::ClientProtocolException, Java::JavaxNetSsl::SSLHandshakeException, Java::OrgApacheHttpConn::HttpHostConnectException,
55
+ Java::OrgApacheHttp::NoHttpResponseException, Java::OrgApacheHttp::ConnectionClosedException => e
55
56
  ex = Manticore::ClientProtocolException.new(e.get_cause)
56
57
  rescue Java::JavaNet::UnknownHostException => e
57
58
  ex = Manticore::ResolutionFailure.new(e.get_cause)
@@ -194,6 +195,10 @@ module Manticore
194
195
  alias_method :completed, :on_complete
195
196
  alias_method :on_completed, :on_complete
196
197
 
198
+ def times_retried
199
+ @context.get_attribute("retryCount") || 0
200
+ end
201
+
197
202
  private
198
203
 
199
204
  # Implementation of {http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/ResponseHandler.html#handleResponse(org.apache.http.HttpResponse) ResponseHandler#handleResponse}
@@ -1,3 +1,3 @@
1
1
  module Manticore
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
@@ -1,6 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
+ java_import 'org.apache.http.entity.mime.MultipartEntityBuilder'
4
+ java_import 'org.apache.http.entity.ContentType'
5
+
3
6
  describe Manticore::Client do
7
+
4
8
  let(:client) { Manticore::Client.new }
5
9
 
6
10
  it "should fetch a URL and return a response" do
@@ -37,6 +41,24 @@ describe Manticore::Client do
37
41
  j["uri"]["port"].should == 55441
38
42
  end
39
43
 
44
+ describe "ignore_ssl_validation" do
45
+ context "when on" do
46
+ let(:client) { Manticore::Client.new ignore_ssl_validation: true }
47
+
48
+ it "should not break on SSL validation errors" do
49
+ expect { client.get("https://localhost:55444/").body }.to_not raise_exception
50
+ end
51
+ end
52
+
53
+ context "when off" do
54
+ let(:client) { Manticore::Client.new ignore_ssl_validation: false }
55
+
56
+ it "should break on SSL validation errors" do
57
+ expect { client.get("https://localhost:55444/").call }.to raise_exception(Manticore::ClientProtocolException)
58
+ end
59
+ end
60
+ end
61
+
40
62
  describe "lazy evaluation" do
41
63
  it "should not call synchronous requests by default" do
42
64
  req = client.get(local_server)
@@ -189,6 +211,13 @@ describe Manticore::Client do
189
211
  response = client.get(local_server)
190
212
  JSON.load(response.body)["method"].should == "GET"
191
213
  end
214
+
215
+ context "with a query" do
216
+ it "should work" do
217
+ response = client.get local_server, query: {foo: "bar"}
218
+ CGI.parse(JSON.load(response.body)["uri"]["query"])["foo"].should == ["bar"]
219
+ end
220
+ end
192
221
  end
193
222
 
194
223
  describe "#post" do
@@ -206,6 +235,13 @@ describe Manticore::Client do
206
235
  response = client.post(local_server, params: {key: "value"})
207
236
  JSON.load(response.body)["body"].should == "key=value"
208
237
  end
238
+
239
+ it "should send an arbitrary entity" do
240
+ f = open(__FILE__, "r").to_inputstream
241
+ multipart_entity = MultipartEntityBuilder.create.add_text_body("foo", "bar").add_binary_body("whatever", f , ContentType::TEXT_PLAIN, __FILE__)
242
+ response = client.post(local_server, entity: multipart_entity.build)
243
+ response.body.should match("should send an arbitrary entity")
244
+ end
209
245
  end
210
246
 
211
247
  describe "#put" do
@@ -258,14 +294,16 @@ describe Manticore::Client do
258
294
 
259
295
  describe "#execute!" do
260
296
  it "should perform multiple concurrent requests" do
261
- @times = []
262
- [55441, 55442].each do |port|
297
+ futures = [55441, 55442].map do |port|
263
298
  client.async.get("http://localhost:#{port}/?sleep=1").
264
- on_success {|response| @times << Time.now.to_f }
299
+ on_success do |response|
300
+ Time.now.to_f
301
+ end
265
302
  end
266
303
 
267
304
  client.execute!
268
- @times[0].should be_within(0.5).of(@times[1])
305
+ values = futures.map(&:callback_result)
306
+ (values[0] - values[1]).abs.should < 0.25
269
307
  end
270
308
 
271
309
  it "should return the results of the handler blocks" do
@@ -349,6 +387,74 @@ describe Manticore::Client do
349
387
  end
350
388
  end
351
389
 
390
+ context "with a misbehaving endpoint" do
391
+ before do
392
+ @socket = TCPServer.new 4567
393
+ @server = Thread.new do
394
+ puts "Accepting"
395
+ loop do
396
+ client = @socket.accept
397
+ client.puts([
398
+ "HTTP/1.1 200 OK",
399
+ "Keep-Alive: timeout=3000",
400
+ "Connection: Keep-Alive",
401
+ "Content-Length: 6",
402
+ "",
403
+ "Hello!"
404
+ ].join("\n"))
405
+ client.close
406
+ end
407
+ end
408
+ end
409
+
410
+ let(:client) { Manticore::Client.new keepalive: true, pool_max: 1 }
411
+
412
+ it "should retry 3 times by default" do
413
+ # The first time, reply with keepalive, then close the connection
414
+ # The second connection should succeed
415
+
416
+ request1 = client.get("http://localhost:4567/")
417
+ request2 = client.get("http://localhost:4567/")
418
+ expect { request1.call }.to_not raise_exception
419
+ expect { request2.call }.to_not raise_exception
420
+
421
+ request1.times_retried.should == 0
422
+ request2.times_retried.should == 1
423
+ end
424
+
425
+ context "when the max retry is restrictive" do
426
+ let(:client) { Manticore::Client.new keepalive: true, pool_max: 1, automatic_retries: 0 }
427
+
428
+ it "should retry 0 times and fail on the second request" do
429
+ # The first time, reply with keepalive, then close the connection
430
+ # The second connection should succeed
431
+ expect { client.get("http://localhost:4567/").call }.to_not raise_exception
432
+ expect { client.get("http://localhost:4567/").call }.to raise_exception(Manticore::SocketException)
433
+ end
434
+ end
435
+
436
+ context "when keepalive is off" do
437
+ let(:client) { Manticore::Client.new keepalive: false, pool_max: 1 }
438
+
439
+ it "should succeed without any retries" do
440
+ # The first time, reply with keepalive, then close the connection
441
+ # The second connection should succeed
442
+ request1 = client.get("http://localhost:4567/")
443
+ request2 = client.get("http://localhost:4567/")
444
+ expect { request1.call }.to_not raise_exception
445
+ expect { request2.call }.to_not raise_exception
446
+
447
+ request1.times_retried.should == 0
448
+ request2.times_retried.should == 0
449
+ end
450
+ end
451
+
452
+ after do
453
+ Thread.kill @server
454
+ @socket.close
455
+ end
456
+ end
457
+
352
458
  def get_connection(client, uri, &block)
353
459
  java_import "java.util.concurrent.TimeUnit"
354
460
  host = URI.parse(uri).host
data/spec/spec_helper.rb CHANGED
@@ -4,6 +4,8 @@ require 'manticore'
4
4
  require 'zlib'
5
5
  require 'json'
6
6
  require 'rack'
7
+ require 'webrick'
8
+ require 'webrick/https'
7
9
 
8
10
  PORT = 55441
9
11
 
@@ -34,7 +36,7 @@ def start_server(port = PORT)
34
36
  sleep(query["sleep"].to_f)
35
37
  end
36
38
 
37
- if cl = request[:headers]["Content-Length"]
39
+ if cl = request[:headers]["Content-Length"] || request[:headers]["Transfer-Encoding"] == "chunked"
38
40
  request[:body] = read_nonblock stream.socket
39
41
  end
40
42
 
@@ -46,6 +48,9 @@ def start_server(port = PORT)
46
48
  else
47
49
  [401, {'WWW-Authenticate' => 'Basic realm="test"'}, [""]]
48
50
  end
51
+ elsif request[:uri][:path] == "/failearly"
52
+ # Return an invalid HTTP response
53
+ []
49
54
  elsif match = request[:uri][:path].match(/\/cookies\/(\d)\/(\d)/)
50
55
  cookie_value = (request[:headers]["Cookie"] || "x=0").split("=").last.to_i
51
56
  if match[1].to_i == match[2].to_i
@@ -82,6 +87,21 @@ def stop_servers
82
87
  @servers.values.each(&:kill) if @servers
83
88
  end
84
89
 
90
+ def start_ssl_server(port)
91
+ cert_name = [
92
+ %w[CN localhost],
93
+ ]
94
+ @servers[port] = Thread.new {
95
+ server = WEBrick::HTTPServer.new(:Port => port, :SSLEnable => true, :SSLCertName => cert_name, :Logger => WEBrick::Log.new("/dev/null"))
96
+ server.mount_proc "/" do |req, res|
97
+ res.body = "hello!"
98
+ end
99
+
100
+ server.start
101
+ puts "Server started?"
102
+ }
103
+ end
104
+
85
105
  RSpec.configure do |c|
86
106
  require 'net/http/server'
87
107
 
@@ -89,6 +109,7 @@ RSpec.configure do |c|
89
109
  @server = {}
90
110
  start_server 55441
91
111
  start_server 55442
112
+ start_ssl_server 55444
92
113
  }
93
114
 
94
115
  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.3.0
4
+ version: 0.3.1
5
5
  platform: java
6
6
  authors:
7
7
  - Chris Heald
@@ -30,7 +30,7 @@ cert_chain:
30
30
  E7PWS50D9moUJ6xWcemf0qKYC87qBFh0ng73awjG9uf+13lMslqJRMtek8C92cvh
31
31
  +R9zgQlbeNjy9O1i
32
32
  -----END CERTIFICATE-----
33
- date: 2014-03-28 00:00:00.000000000 Z
33
+ date: 2014-08-08 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: addressable
@@ -95,6 +95,7 @@ files:
95
95
  - lib/jar/commons-logging-1.1.3.jar
96
96
  - lib/jar/httpclient-4.3.2-patched.jar
97
97
  - lib/jar/httpcore-4.3.1.jar
98
+ - lib/jar/httpmime-4.3.2.jar
98
99
  - lib/jar/lazy_decompressing_stream.patch
99
100
  - lib/jar/manticore-ext.jar
100
101
  - lib/manticore.rb
@@ -133,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
134
  version: '0'
134
135
  requirements: []
135
136
  rubyforge_project:
136
- rubygems_version: 2.2.1
137
+ rubygems_version: 2.2.2
137
138
  signing_key:
138
139
  specification_version: 4
139
140
  summary: Manticore is an HTTP client built on the Apache HttpCore components
metadata.gz.sig CHANGED
Binary file