manticore 0.2.0-java → 0.2.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: e0ee8e57988bc31f65a09326d862683aed668e2a
4
- data.tar.gz: 7d7247a8d5087e9630b2ffed540abd16f4930bbd
3
+ metadata.gz: 79bd078a381a202fd2e4e022b384b454fdb80242
4
+ data.tar.gz: 892736cf30a0c490cb92cf690e5e8aa7eb881368
5
5
  SHA512:
6
- metadata.gz: 0728f990ebb06a7251267eb121d1be4bf108a3b13fb742a62289eea6d41f4d376125b79878d2ccb8bed83158783fba1170f826f14fd2b443f31d52c64cff5580
7
- data.tar.gz: 3b32c8017107ce22d1da41b56faa3fab5e3a57749cd47814c86ed2632d771c7d923637e1c359b0f28cf0855089f3343ba921d47b0a0b79d8336c85f63b5999be
6
+ metadata.gz: 03a61af6c2dce93aaa68088b9e605cdcb06c8813a182d04ef17152893947b9e9b58b4031561eab68376eb044e08afcd2aa29bd0cc6448f1fa10a4b786b664e71
7
+ data.tar.gz: 54e578d3b61ac6db04cb60fb13919ddbf157ca0b664e5a066d56eb2ac727ff7798b644ead04e054c74820123a93ec1607c128b1eee9a60dd445d86c8851ae0ac
@@ -2,9 +2,10 @@ Copyright (c) 2013 Chris Heald
2
2
 
3
3
  Some JARs distributed with this project are licensed under the Apache 2.0 License. See APACHE-LICENSE-2.0.txt for details:
4
4
 
5
- lib/jars/commons-logging-1.1.3.jar
6
- lib/jars/httpclient-4.3.2.jar
7
- lib/jars/httpcore-4.3.1.jar
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
8
9
 
9
10
  All other code distributed with this project is licensed under the MIT License as follows:
10
11
 
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/cheald/manticore.png?branch=master)](https://travis-ci.org/cheald/manticore)
4
4
 
5
- Manticore is a HTTP client with the fast, robust HTTP client built on the Apache HTTPClient libraries. It is only compatible with JRuby.
5
+ Manticore is a fast, robust HTTP client built on the Apache HTTPClient libraries. It is only compatible with JRuby.
6
6
 
7
7
  ## Installation
8
8
 
@@ -34,8 +34,9 @@ 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
+ * Authentication
38
+ * Proxy support
37
39
  * SSL
38
- * Much more!
39
40
 
40
41
  ## Usage
41
42
 
@@ -36,11 +36,6 @@ public class Manticore implements Library {
36
36
  super(ruby, rubyClass);
37
37
  }
38
38
 
39
- // @JRubyMethod
40
- // public IRubyObject initialize(ThreadContext context) {
41
- // return context.nil;
42
- // }
43
-
44
39
  @JRubyMethod(name = "read_entity")
45
40
  public IRubyObject readEntity(ThreadContext context, IRubyObject rEntity, Block block) throws IOException {
46
41
  HttpEntity entity = (HttpEntity)rEntity.toJava(HttpEntity.class);
@@ -60,13 +55,11 @@ public class Manticore implements Library {
60
55
  }
61
56
  }
62
57
 
63
- // @JRubyMethod(name = "read_entity")
64
58
  private IRubyObject readWholeEntity(ThreadContext context, HttpEntity entity, Encoding encoding) throws IOException {
65
59
  ByteList bl = new ByteList(EntityUtils.toByteArray(entity), false);
66
60
  return RubyString.newStringShared(context.getRuntime(), bl, encoding);
67
61
  }
68
62
 
69
- // @JRubyMethod(name = "stream_entity")
70
63
  private IRubyObject streamEntity(ThreadContext context, HttpEntity entity, Encoding encoding, Block block) throws IOException {
71
64
  InputStream instream = entity.getContent();
72
65
  if (instream == null) { return null; }
@@ -84,7 +77,6 @@ public class Manticore implements Library {
84
77
  byte[] tmp = new byte[4096];
85
78
  int l;
86
79
  while((l = instream.read(tmp)) != -1) {
87
- // String str = new String(tmp, charset);
88
80
  block.call( context, RubyString.newString(context.getRuntime(), new ByteList(tmp, true), encoding) );
89
81
  }
90
82
  } finally {
@@ -3,6 +3,7 @@ require 'java'
3
3
  require_relative "./jar/httpcore-4.3.1"
4
4
  require_relative "./jar/httpclient-4.3.2-patched"
5
5
  require_relative "./jar/commons-logging-1.1.3"
6
+ require_relative "./jar/commons-codec-1.6.jar"
6
7
  require_relative "./jar/manticore-ext"
7
8
 
8
9
  org.manticore.Manticore.new.load(JRuby.runtime, false)
@@ -22,9 +23,16 @@ module Manticore
22
23
  # Friendly wrapper for various Java ClientProtocolExceptions
23
24
  class ClientProtocolException < ManticoreException; end
24
25
 
26
+ # DNS resolution failure
27
+ class ResolutionFailure < ManticoreException; end
28
+
29
+ # Socket breaks, etc
30
+ class SocketException < ManticoreException; end
31
+
25
32
  require_relative './manticore/client'
26
33
  require_relative './manticore/response'
27
34
  require_relative './manticore/async_response'
35
+ require_relative './manticore/cookie'
28
36
  require_relative './manticore/facade'
29
37
 
30
38
  include Facade
@@ -21,14 +21,20 @@ module Manticore
21
21
  # @private
22
22
  # Implementation of Callable#call
23
23
  def call
24
+ ex = nil
24
25
  begin
25
26
  @client.execute @request, self, @context
26
- self
27
+ return self
27
28
  rescue Java::JavaNet::SocketTimeoutException, Java::OrgApacheHttpConn::ConnectTimeoutException, Java::OrgApacheHttp::NoHttpResponseException => e
28
- @handlers[:failure].call( Manticore::Timeout.new(e.get_cause) )
29
- rescue Java::OrgApacheHttpClient::ClientProtocolException => e
30
- @handlers[:failure].call( Manticore::ClientProtocolException.new(e.get_cause) )
29
+ ex = Manticore::Timeout.new(e.get_cause)
30
+ rescue Java::JavaNet::SocketException => e
31
+ ex = Manticore::SocketException.new(e.get_cause)
32
+ rescue Java::OrgApacheHttpClient::ClientProtocolException, Java::JavaxNetSsl::SSLHandshakeException, Java::OrgApacheHttpConn::HttpHostConnectException => e
33
+ ex = Manticore::ClientProtocolException.new(e.get_cause)
34
+ rescue Java::JavaNet::UnknownHostException => e
35
+ ex = Manticore::ResolutionFailure.new(e.get_cause)
31
36
  end
37
+ @handlers[:failure].call ex
32
38
  end
33
39
 
34
40
  # Set handler for success responses
@@ -4,6 +4,51 @@ module Manticore
4
4
  # General Timeout exception thrown for various Manticore timeouts
5
5
  class Timeout < ManticoreException; end
6
6
 
7
+ # @!macro [new] http_method_shared
8
+ # @param url [String] URL to request
9
+ # @param options [Hash]
10
+ # @option options [Hash] params Hash of options to pass as request parameters
11
+ # @option options [Hash] headers Hash of options to pass as additional request headers
12
+ # @option options [String] proxy Proxy host in form: http://proxy.org:1234
13
+ # @option options [Hash] proxy Proxy host in form: {host: 'proxy.org'[, port: 80[, scheme: 'http']]}
14
+ # @option options [URI] proxy Proxy host as a URI object
15
+ # @option options [Integer] connect_timeout Request-specific connect timeout
16
+ # @option options [Integer] socket_timeout Request-specific socket timeout
17
+ # @option options [Integer] request_timeout Request-specific request timeout
18
+ # @option options [Integer] max_redirects Request-specific maximum redirect limit
19
+ # @option options [Boolean] follow_redirects Specify whether this request should follow redirects
20
+ #
21
+ # @!macro [new] http_request_exceptions
22
+ # @raise [Manticore::Timeout] on socket, connection, or response timeout
23
+ # @raise [Manticore::SocketException] on internal socket exception (ie, unexpected socket closure)
24
+ # @raise [Manticore::ClientProtocolException] on protocol errors such as an SSL handshake failure or connection exception
25
+ # @raise [Manticore::ResolutionFailure] on DNS resolution failure
26
+ # @return [Response]
27
+ #
28
+ # @!macro [new] http_method_shared_async
29
+ # @example Simple usage
30
+ # client.$0("http://example.com/some/resource", params: {foo: "bar"}, headers: {"X-Custom-Header" => "whee"}).
31
+ # on_success {|response|
32
+ # # Do something with response.body, response.code, etc
33
+ # }.on_failure {|exception|
34
+ # # Handle request exception
35
+ # }
36
+ # client.execute!
37
+ #
38
+ # @!macro [new] http_method_shared_sync
39
+ # @example Simple usage
40
+ # client.$0("http://example.com/some/resource", params: {foo: "bar"}, headers: {"X-Custom-Header" => "whee"})
41
+ # @macro http_method_shared
42
+ # @macro http_request_exceptions
43
+ #
44
+ # @!macro [new] http_method_shared_async_with_body
45
+ # @macro http_method_shared_async
46
+ # @option options [Hash] body Hash of options to pass as request body
47
+ #
48
+ # @!macro [new] http_method_shared_sync_with_body
49
+ # @macro http_method_shared_sync
50
+ # @option options [Hash] body Hash of options to pass as request body
51
+
7
52
  # Core Manticore client, with a backing {http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.html PoolingHttpClientConnectionManager}
8
53
  class Client
9
54
  include_package "org.apache.http.client.methods"
@@ -17,10 +62,10 @@ module Manticore
17
62
  include_package "org.apache.http.message"
18
63
  include_package "org.apache.http.params"
19
64
  include_package "org.apache.http.protocol"
65
+ include_package "org.apache.http.auth"
20
66
  include_package "java.util.concurrent"
21
- java_import 'java.util.concurrent.TimeUnit'
22
- java_import 'java.util.concurrent.CountDownLatch'
23
- java_import 'java.util.concurrent.LinkedBlockingQueue'
67
+ include_package "org.apache.http.client.protocol"
68
+ java_import "org.apache.http.HttpHost"
24
69
 
25
70
  # The default maximum pool size for requests
26
71
  DEFAULT_MAX_POOL_SIZE = 50
@@ -57,18 +102,23 @@ module Manticore
57
102
  # @option options [integer] pool_max_per_route (2) Sets the maximum number of active connections for a given target endpoint
58
103
  # @option options [boolean] cookies (true) enable or disable automatic cookie management between requests
59
104
  # @option options [boolean] compression (true) enable or disable transparent gzip/deflate support
60
- # @option options [integer] request_timeout (60) Sets the timeout for requests. Raises Manticore::Timeout on failure.
105
+ # @option options [integer] request_timeout (60) Sets the timeout for requests. Raises {Manticore::Timeout} on failure.
61
106
  # @option options [integer] connect_timeout (10) Sets the timeout for connections. Raises Manticore::Timeout on failure.
62
107
  # @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.
63
108
  # @option options [integer] request_timeout (60) Sets the timeout for a given request. Raises Manticore::Timeout on failure.
64
109
  # @option options [integer] max_redirects (5) Sets the maximum number of redirects to follow.
65
110
  # @option options [boolean] expect_continue (false) Enable support for HTTP 100
66
111
  # @option options [boolean] stale_check (false) Enable support for stale connection checking. Adds overhead.
112
+ # @option options [String] proxy Proxy host in form: http://proxy.org:1234
113
+ # @option options [Hash] proxy Proxy host in form: {host: 'proxy.org'[, port: 80[, scheme: 'http']]}
114
+ # @option options [URI] proxy Proxy host as a URI object
67
115
  def initialize(options = {})
68
116
  builder = client_builder
69
117
  builder.set_user_agent options.fetch(:user_agent, "Manticore #{VERSION}")
70
- builder.disable_cookie_management unless options.fetch(:cookies, false)
118
+ @use_cookies = options.fetch(:cookies, false)
119
+ builder.disable_cookie_management unless @use_cookies
71
120
  builder.disable_content_compression if options.fetch(:compression, true) == false
121
+ builder.set_proxy get_proxy_host(options[:proxy]) if options.key?(:proxy)
72
122
 
73
123
  # This should make it easier to reuse connections
74
124
  builder.disable_connection_state
@@ -98,81 +148,41 @@ module Manticore
98
148
  ### Sync methods
99
149
 
100
150
  # Perform a HTTP GET request
101
- # @param url [String] URL to request
102
- # @param options [Hash]
103
- # @option options [Hash] params Hash of options to pass as request parameters
104
- # @option options [Hash] headers Hash of options to pass as additional request headers
105
- #
106
- # @return [Response]
151
+ # @macro http_method_shared_sync
107
152
  def get(url, options = {}, &block)
108
153
  request HttpGet, url, options, &block
109
154
  end
110
155
 
111
156
  # Perform a HTTP PUT request
112
- # @param url [String] URL to request
113
- # @param options [Hash]
114
- # @option options [Hash] params Hash of options to pass as request parameters
115
- # @option options [Hash] body Hash of options to pass as request body
116
- # @option options [Hash] headers Hash of options to pass as additional request headers
117
- #
118
- # @return [Response]
157
+ # @macro http_method_shared_sync_with_body
119
158
  def put(url, options = {}, &block)
120
159
  request HttpPut, url, options, &block
121
160
  end
122
161
 
123
162
  # Perform a HTTP HEAD request
124
- # @param url [String] URL to request
125
- # @param options [Hash]
126
- # @option options [Hash] params Hash of options to pass as request parameters
127
- # @option options [Hash] headers Hash of options to pass as additional request headers
128
- #
129
- # @return [Response]
163
+ # @macro http_method_shared_sync
130
164
  def head(url, options = {}, &block)
131
165
  request HttpHead, url, options, &block
132
166
  end
133
167
 
134
168
  # Perform a HTTP POST request
135
- # @param url [String] URL to request
136
- # @param options [Hash]
137
- # @option options [Hash] params Hash of options to pass as request parameters
138
- # @option options [Hash] body Hash of options to pass as request body
139
- # @option options [Hash] headers Hash of options to pass as additional request headers
140
- #
141
- # @return [Response]
169
+ # @macro http_method_shared_sync_with_body
142
170
  def post(url, options = {}, &block)
143
171
  request HttpPost, url, options, &block
144
172
  end
145
173
 
146
174
  # Perform a HTTP DELETE request
147
- # @param url [String] URL to request
148
- # @param options [Hash]
149
- # @option options [Hash] params Hash of options to pass as request parameters
150
- # @option options [Hash] headers Hash of options to pass as additional request headers
151
- #
152
- # @return [Response]
175
+ # @macro http_method_shared_sync
153
176
  def delete(url, options = {}, &block)
154
177
  request HttpDelete, url, options, &block
155
178
  end
156
179
 
157
- # Perform a HTTP OPTIONS request
158
- # @param url [String] URL to request
159
- # @param options [Hash]
160
- # @option options [Hash] params Hash of options to pass as request parameters
161
- # @option options [Hash] headers Hash of options to pass as additional request headers
162
- #
163
- # @return [Response]
180
+ # @macro http_method_shared_sync
164
181
  def options(url, options = {}, &block)
165
182
  request HttpOptions, url, options, &block
166
183
  end
167
184
 
168
- # Perform a HTTP PATCH request
169
- # @param url [String] URL to request
170
- # @param options [Hash]
171
- # @option options [Hash] params Hash of options to pass as request parameters
172
- # @option options [Hash] body Hash of options to pass as request body
173
- # @option options [Hash] headers Hash of options to pass as additional request headers
174
- #
175
- # @return [Response]
185
+ # @macro http_method_shared_sync_with_body
176
186
  def patch(url, options = {}, &block)
177
187
  request HttpPatch, url, options, &block
178
188
  end
@@ -180,81 +190,43 @@ module Manticore
180
190
  ### Async methods
181
191
 
182
192
  # Queue an asynchronous HTTP GET request
183
- # @param url [String] URL to request
184
- # @param options [Hash]
185
- # @option options [Hash] params Hash of options to pass as request parameters
186
- # @option options [Hash] headers Hash of options to pass as additional request headers
187
- #
188
- # @return [Response]
193
+ # @macro http_method_shared_async
189
194
  def async_get(url, options = {}, &block)
190
195
  get url, options.merge(async: true), &block
191
196
  end
192
197
 
193
198
  # Queue an asynchronous HTTP HEAD request
194
- # @param url [String] URL to request
195
- # @param options [Hash]
196
- # @option options [Hash] params Hash of options to pass as request parameters
197
- # @option options [Hash] headers Hash of options to pass as additional request headers
198
- #
199
- # @return [Response]
199
+ # @macro http_method_shared_async
200
200
  def async_head(url, options = {}, &block)
201
201
  head url, options.merge(async: true), &block
202
202
  end
203
203
 
204
204
  # Queue an asynchronous HTTP PUT request
205
- # @param url [String] URL to request
206
- # @param options [Hash]
207
- # @option options [Hash] params Hash of options to pass as request parameters
208
- # @option options [Hash] body Hash of options to pass as request body
209
- # @option options [Hash] headers Hash of options to pass as additional request headers
210
- #
211
- # @return [Response]
205
+ # @macro http_method_shared_async_with_body
212
206
  def async_put(url, options = {}, &block)
213
207
  put url, options.merge(async: true), &block
214
208
  end
215
209
 
216
210
  # Queue an asynchronous HTTP POST request
217
- # @param url [String] URL to request
218
- # @param options [Hash]
219
- # @option options [Hash] params Hash of options to pass as request parameters
220
- # @option options [Hash] body Hash of options to pass as request body
221
- # @option options [Hash] headers Hash of options to pass as additional request headers
222
- #
223
- # @return [Response]
211
+ # @macro http_method_shared_async_with_body
224
212
  def async_post(url, options = {}, &block)
225
213
  post url, options.merge(async: true), &block
226
214
  end
227
215
 
228
216
  # Queue an asynchronous HTTP DELETE request
229
- # @param url [String] URL to request
230
- # @param options [Hash]
231
- # @option options [Hash] params Hash of options to pass as request parameters
232
- # @option options [Hash] headers Hash of options to pass as additional request headers
233
- #
234
- # @return [Response]
217
+ # @macro http_method_shared_async
235
218
  def async_delete(url, options = {}, &block)
236
219
  delete url, options.merge(async: true), &block
237
220
  end
238
221
 
239
222
  # Queue an asynchronous HTTP OPTIONS request
240
- # @param url [String] URL to request
241
- # @param options [Hash]
242
- # @option options [Hash] params Hash of options to pass as request parameters
243
- # @option options [Hash] headers Hash of options to pass as additional request headers
244
- #
245
- # @return [Response]
223
+ # @macro http_method_shared_async
246
224
  def async_options(url, options = {}, &block)
247
225
  options url, options.merge(async: true), &block
248
226
  end
249
227
 
250
228
  # Queue an asynchronous HTTP PATCH request
251
- # @param url [String] URL to request
252
- # @param options [Hash]
253
- # @option options [Hash] params Hash of options to pass as request parameters
254
- # @option options [Hash] body Hash of options to pass as request body
255
- # @option options [Hash] headers Hash of options to pass as additional request headers
256
- #
257
- # @return [Response]
229
+ # @macro http_method_shared_async_with_body
258
230
  def async_patch(url, options = {}, &block)
259
231
  patch url, options.merge(async: true), &block
260
232
  end
@@ -309,30 +281,37 @@ module Manticore
309
281
  end
310
282
 
311
283
  def request(klass, url, options, &block)
312
- req = request_from_options(klass, url, options)
284
+ req, context = request_from_options(klass, url, options)
313
285
  if options.delete(:async)
314
- async_request req, &block
286
+ async_request req, context, &block
315
287
  else
316
- sync_request req, &block
288
+ sync_request req, context, &block
317
289
  end
318
290
  end
319
291
 
320
- def async_request(request, &block)
292
+ def async_request(request, context, &block)
321
293
  create_executor_if_needed
322
- response = AsyncResponse.new(@client, request, BasicHttpContext.new, block)
294
+ response = AsyncResponse.new(@client, request, context, block)
323
295
  @async_requests << response
324
296
  response
325
297
  end
326
298
 
327
- def sync_request(request, &block)
328
- response = Response.new(request, BasicHttpContext.new, block)
299
+ def sync_request(request, context, &block)
300
+ response = Response.new(request, context, block)
329
301
  begin
330
302
  @client.execute request, response, response.context
303
+ response
331
304
  rescue Java::JavaNet::SocketTimeoutException, Java::OrgApacheHttpConn::ConnectTimeoutException, Java::OrgApacheHttp::NoHttpResponseException => e
332
305
  raise Manticore::Timeout.new(e.get_cause)
333
- rescue Java::OrgApacheHttpClient::ClientProtocolException => e
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
334
310
  raise Manticore::ClientProtocolException.new(e.get_cause)
311
+ rescue Java::JavaNet::UnknownHostException => e
312
+ raise Manticore::ResolutionFailure.new(e.get_cause)
335
313
  end
314
+
336
315
  end
337
316
 
338
317
  def uri_from_url_and_options(url, options)
@@ -363,11 +342,58 @@ module Manticore
363
342
  end
364
343
  end
365
344
 
345
+ if options.key?(:proxy) || options.key?(:connect_timeout) || options.key?(:socket_timeout) || options.key?(:max_redirects) || options.key?(:follow_redirects)
346
+ config = RequestConfig.custom()
347
+ config.set_proxy get_proxy_host(options[:proxy]) if options[:proxy]
348
+ config.set_connect_timeout options[:connect_timeout] if options[:connect_timeout]
349
+ config.set_socket_timeout options[:socket_timeout] if options[:socket_timeout]
350
+ config.set_max_redirects options[:max_redirects] if options[:max_redirects]
351
+ config.set_redirects_enabled !!options[:follow_redirects] if options.fetch(:follow_redirects, nil) != nil
352
+ req.set_config config.build
353
+ end
354
+
366
355
  if options[:headers]
367
356
  options[:headers].each {|k, v| req.set_header k, v }
368
357
  end
369
358
 
370
- req
359
+ context = HttpClientContext.new
360
+ auth_from_options(options, context) if options.key? :auth
361
+
362
+ if @use_cookies == :per_request
363
+ store = BasicCookieStore.new
364
+ context.setAttribute(ClientContext.COOKIE_STORE, store)
365
+ end
366
+
367
+ return req, context
368
+ end
369
+
370
+ def get_proxy_host(opt)
371
+ host = nil
372
+ if opt.is_a? String
373
+ uri = URI.parse(opt)
374
+ if uri.host
375
+ get_proxy_host uri
376
+ else
377
+ uri = URI.parse("http://#{opt}")
378
+ get_proxy_host uri
379
+ end
380
+ elsif opt.is_a? Hash
381
+ HttpHost.new(opt[:host], (opt[:port] || 80).to_i, opt[:scheme] || "http")
382
+ elsif opt.is_a? URI
383
+ opt.scheme ||= "http"
384
+ opt.port ||= 80
385
+ HttpHost.new(opt.host, opt.port, opt.scheme)
386
+ end
387
+ end
388
+
389
+ def auth_from_options(options, context)
390
+ if options[:auth]
391
+ provider = BasicCredentialsProvider.new
392
+ username = options[:auth][:user] || options[:auth][:username]
393
+ password = options[:auth][:pass] || options[:auth][:password]
394
+ provider.set_credentials AuthScope::ANY, UsernamePasswordCredentials.new(username, password)
395
+ context.set_credentials_provider(provider)
396
+ end
371
397
  end
372
398
 
373
399
  def hash_to_entity(hash)
@@ -0,0 +1,87 @@
1
+ module Manticore
2
+ # @!attribute [r] comment
3
+ # @return [String] Returns the comment describing the purpose of this cookie, or nil if no such comment has been defined.
4
+ # @!attribute [r] comment_url
5
+ # @return [String] If a user agent (web browser) presents this cookie to a user, the cookie's purpose will be described by the information at this URL.
6
+ # @!attribute [r] domain
7
+ # @return [String] Returns domain attribute of the cookie.
8
+ # @!attribute [r] expiry_date
9
+ # @return [Time] Returns the expiration Date of the cookie, or nil if none exists.
10
+ # @!attribute [r] name
11
+ # @return [String] Returns the name of the cookie.
12
+ # @!attribute [r] path
13
+ # @return [String] Returns the path attribute of the cookie.
14
+ # @!attribute [r] ports
15
+ # @return [Array<Integer>] Returns the ports attribute of the cookie.
16
+ # @!attribute [r] value
17
+ # @return [Array<Integer>] Returns the cookie value
18
+ # @!attribute [r] spec_version
19
+ # @return [Integer] Returns the version of the cookie specification to which this cookie conforms.
20
+ class Cookie
21
+ # @private
22
+ # Create a Manticore::Cookie wrapper from a org.apache.http.cookie.Cookie
23
+ def self.from_java(cookie)
24
+ if cookie.get_expiry_date
25
+ expiry = Time.at(cookie.get_expiry_date / 1000)
26
+ end
27
+
28
+ new(
29
+ comment: cookie.get_comment,
30
+ comment_url: cookie.getCommentURL,
31
+ domain: cookie.get_domain,
32
+ expiry_date: expiry,
33
+ name: cookie.get_name,
34
+ path: cookie.get_path,
35
+ ports: cookie.get_ports.to_a,
36
+ value: cookie.get_value,
37
+ secure: cookie.is_secure,
38
+ persistent: cookie.is_persistent,
39
+ spec_version: cookie.get_version
40
+ )
41
+ end
42
+
43
+ attr_reader :comment, :comment_url, :domain, :expiry_date, :name, :path, :ports, :value, :spec_version
44
+
45
+ def initialize(args)
46
+ @comment = args.fetch(:comment, nil)
47
+ @comment_url = args.fetch(:comment_url, nil)
48
+ @domain = args.fetch(:domain, nil)
49
+ @expiry_date = args.fetch(:expiry_date, nil)
50
+ @name = args.fetch(:name, nil)
51
+ @path = args.fetch(:path, nil)
52
+ @ports = args.fetch(:ports, nil)
53
+ @value = args.fetch(:value, nil)
54
+ @secure = args.fetch(:secure, nil)
55
+ @persistent = args.fetch(:persistent, nil)
56
+ @spec_version = args.fetch(:spec_version, nil)
57
+ end
58
+
59
+ # @param date [Time] Time to compare against
60
+ #
61
+ # @return [Boolean] Whether this cookie is expired at the comparison date
62
+ def expired?(date = Time.now)
63
+ @expiry_date > date
64
+ end
65
+
66
+ # Whether this is a HTTPS-only cookie
67
+ #
68
+ # @return [Boolean]
69
+ def secure?
70
+ @secure
71
+ end
72
+
73
+ # Whether this is a persistent (session-only) cookie
74
+ #
75
+ # @return [Boolean]
76
+ def persistent?
77
+ @persistent
78
+ end
79
+
80
+ # Whether this is a session-only cookie
81
+ #
82
+ # @return [Boolean]
83
+ def session?
84
+ !@persistent
85
+ end
86
+ end
87
+ end
@@ -88,6 +88,18 @@ module Manticore
88
88
  (@headers["content-length"] || -1).to_i
89
89
  end
90
90
 
91
+ # Returns an array of {Manticore::Cookie Cookies} associated with this request's execution context
92
+ #
93
+ # @return [Array<Manticore::Cookie>]
94
+ def cookies
95
+ @context.get_cookie_store.get_cookies.inject({}) do |all, java_cookie|
96
+ c = Cookie.from_java(java_cookie)
97
+ all[c.name] ||= []
98
+ all[c.name] << c
99
+ all
100
+ end
101
+ end
102
+
91
103
  private
92
104
 
93
105
  def encode(string, charset)
@@ -1,3 +1,3 @@
1
1
  module Manticore
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -26,6 +26,83 @@ describe Manticore::Client do
26
26
  json["headers"]["Accept-Encoding"].should match("gzip")
27
27
  end
28
28
 
29
+ it "should authenticate" do
30
+ client.get(local_server("/auth")).code.should == 401
31
+ client.get(local_server("/auth"), auth: {user: "user", pass: "pass"}).code.should == 200
32
+ end
33
+
34
+ it "should proxy" do
35
+ j = JSON.parse(client.get(local_server("/proxy"), proxy: "http://localhost:55442").body)
36
+ j["server_port"].should == 55442
37
+ j["uri"]["port"].should == 55441
38
+ end
39
+
40
+ context "when client-wide cookie management is disabled" do
41
+ let(:client) { Manticore::Client.new cookies: false }
42
+
43
+ it "should persist cookies across multiple redirects from a single request" do
44
+ response = client.get(local_server("/cookies/1/2"))
45
+ response.final_url.to_s.should == local_server("/cookies/2/2")
46
+ response.cookies["x"].should be_nil
47
+ response.headers["set-cookie"].should match(/1/)
48
+ end
49
+
50
+ it "should not persist cookies between requests" do
51
+ response = client.get(local_server("/cookies/1/2"))
52
+ response.final_url.to_s.should == local_server("/cookies/2/2")
53
+ response.cookies["x"].should be_nil
54
+ response.headers["set-cookie"].should match(/1/)
55
+
56
+ response = client.get(local_server("/cookies/1/2"))
57
+ response.final_url.to_s.should == local_server("/cookies/2/2")
58
+ response.cookies["x"].should be_nil
59
+ response.headers["set-cookie"].should match(/1/)
60
+ end
61
+ end
62
+
63
+ context "when client-wide cookie management is set to per-request" do
64
+ let(:client) { Manticore::Client.new cookies: :per_request }
65
+
66
+ it "should persist cookies across multiple redirects from a single request" do
67
+ response = client.get(local_server("/cookies/1/2"))
68
+ response.final_url.to_s.should == local_server("/cookies/2/2")
69
+ response.headers["set-cookie"].should match(/2/)
70
+ response.cookies["x"].first.value.should == "2"
71
+ end
72
+
73
+ it "should not persist cookies between requests" do
74
+ response = client.get(local_server("/cookies/1/2"))
75
+ response.final_url.to_s.should == local_server("/cookies/2/2")
76
+ response.headers["set-cookie"].should match(/2/)
77
+ response.cookies["x"].first.value.should == "2"
78
+
79
+ response = client.get(local_server("/cookies/1/2"))
80
+ response.final_url.to_s.should == local_server("/cookies/2/2")
81
+ response.headers["set-cookie"].should match(/2/)
82
+ response.cookies["x"].first.value.should == "2"
83
+ end
84
+ end
85
+
86
+ context "when client-wide cookie management is enabled" do
87
+ let(:client) { Manticore::Client.new cookies: true }
88
+
89
+ it "should persist cookies across multiple redirects from a single request" do
90
+ response = client.get(local_server("/cookies/1/2"))
91
+ response.final_url.to_s.should == local_server("/cookies/2/2")
92
+ response.cookies["x"].first.value.should == "2"
93
+ end
94
+
95
+ it "should persist cookies between requests" do
96
+ response = client.get(local_server("/cookies/1/2"))
97
+ response.final_url.to_s.should == local_server("/cookies/2/2")
98
+ response.cookies["x"].first.value.should == "2"
99
+
100
+ response = client.get(local_server("/cookies/1/2"))
101
+ response.final_url.to_s.should == local_server("/cookies/2/2")
102
+ response.cookies["x"].first.value.should == "4"
103
+ end
104
+ end
105
+
29
106
  context "when compression is disabled" do
30
107
  let(:client) {
31
108
  Manticore::Client.new do |client, request_config|
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Manticore::Cookie do
4
+ context "created from a Client request" do
5
+ let(:client) { Manticore::Client.new cookies: true }
6
+ subject {
7
+ response = client.get(local_server("/cookies/1/2"))
8
+ response.final_url.to_s.should == local_server("/cookies/2/2")
9
+ response.cookies["x"].first
10
+ }
11
+
12
+ its(:name) { should be_a String }
13
+ its(:value) { should be_a String }
14
+ its(:path) { should be_a String }
15
+ its(:domain) { should be_a String }
16
+ end
17
+ end
@@ -38,10 +38,27 @@ def start_server(port = PORT)
38
38
  request[:body] = read_nonblock stream.socket
39
39
  end
40
40
 
41
- if request[:headers]["X-Redirect"] && request[:uri][:path] != request[:headers]["X-Redirect"]
41
+ content_type = request[:headers]["X-Content-Type"] || "text/plain"
42
+ if request[:uri][:path] == "/auth"
43
+ if request[:headers]["Authorization"] == "Basic dXNlcjpwYXNz"
44
+ payload = JSON.dump(request)
45
+ [200, {'Content-Type' => content_type, "Content-Length" => payload.length}, [payload]]
46
+ else
47
+ [401, {'WWW-Authenticate' => 'Basic realm="test"'}, [""]]
48
+ end
49
+ elsif match = request[:uri][:path].match(/\/cookies\/(\d)\/(\d)/)
50
+ cookie_value = (request[:headers]["Cookie"] || "x=0").split("=").last.to_i
51
+ if match[1].to_i == match[2].to_i
52
+ [200, {"Set-Cookie" => "x=#{cookie_value + 1}; Path=/"}, [""]]
53
+ else
54
+ [301, {"Set-Cookie" => "x=#{cookie_value + 1}; Path=/", "Location" => "/cookies/#{match[1].to_i + 1}/#{match[2]}"}, [""]]
55
+ end
56
+ elsif request[:uri][:path] == "/proxy"
57
+ payload = JSON.dump(request.merge(server_port: port))
58
+ [200, {'Content-Type' => content_type, "Content-Length" => payload.length}, [payload]]
59
+ elsif request[:headers]["X-Redirect"] && request[:uri][:path] != request[:headers]["X-Redirect"]
42
60
  [301, {"Location" => local_server( request[:headers]["X-Redirect"] )}, [""]]
43
61
  else
44
- content_type = request[:headers]["X-Content-Type"] || "text/plain"
45
62
  if request[:headers]["Accept-Encoding"] && request[:headers]["Accept-Encoding"].match("gzip")
46
63
  out = StringIO.new('', "w")
47
64
  io = Zlib::GzipWriter.new(out, 2)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: manticore
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: java
6
6
  authors:
7
7
  - Chris Heald
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-18 00:00:00.000000000 Z
11
+ date: 2014-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -67,6 +67,7 @@ files:
67
67
  - README.md
68
68
  - Rakefile
69
69
  - ext/manticore/org/manticore/Manticore.java
70
+ - lib/jar/commons-codec-1.6.jar
70
71
  - lib/jar/commons-logging-1.1.3.jar
71
72
  - lib/jar/httpclient-4.3.2-patched.jar
72
73
  - lib/jar/httpcore-4.3.1.jar
@@ -75,11 +76,13 @@ files:
75
76
  - lib/manticore.rb
76
77
  - lib/manticore/async_response.rb
77
78
  - lib/manticore/client.rb
79
+ - lib/manticore/cookie.rb
78
80
  - lib/manticore/facade.rb
79
81
  - lib/manticore/response.rb
80
82
  - lib/manticore/version.rb
81
83
  - manticore.gemspec
82
84
  - spec/manticore/client_spec.rb
85
+ - spec/manticore/cookie_spec.rb
83
86
  - spec/manticore/facade_spec.rb
84
87
  - spec/manticore/response_spec.rb
85
88
  - spec/spec_helper.rb
@@ -109,6 +112,7 @@ specification_version: 4
109
112
  summary: Manticore is an HTTP client built on the Apache HttpCore components
110
113
  test_files:
111
114
  - spec/manticore/client_spec.rb
115
+ - spec/manticore/cookie_spec.rb
112
116
  - spec/manticore/facade_spec.rb
113
117
  - spec/manticore/response_spec.rb
114
118
  - spec/spec_helper.rb