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 +4 -4
- data/LICENSE.txt +4 -3
- data/README.md +3 -2
- data/ext/manticore/org/manticore/Manticore.java +0 -8
- data/lib/jar/commons-codec-1.6.jar +0 -0
- data/lib/manticore.rb +8 -0
- data/lib/manticore/async_response.rb +10 -4
- data/lib/manticore/client.rb +132 -106
- data/lib/manticore/cookie.rb +87 -0
- data/lib/manticore/response.rb +12 -0
- data/lib/manticore/version.rb +1 -1
- data/spec/manticore/client_spec.rb +77 -0
- data/spec/manticore/cookie_spec.rb +17 -0
- data/spec/spec_helper.rb +19 -2
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79bd078a381a202fd2e4e022b384b454fdb80242
|
4
|
+
data.tar.gz: 892736cf30a0c490cb92cf690e5e8aa7eb881368
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03a61af6c2dce93aaa68088b9e605cdcb06c8813a182d04ef17152893947b9e9b58b4031561eab68376eb044e08afcd2aa29bd0cc6448f1fa10a4b786b664e71
|
7
|
+
data.tar.gz: 54e578d3b61ac6db04cb60fb13919ddbf157ca0b664e5a066d56eb2ac727ff7798b644ead04e054c74820123a93ec1607c128b1eee9a60dd445d86c8851ae0ac
|
data/LICENSE.txt
CHANGED
@@ -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/
|
6
|
-
lib/
|
7
|
-
lib/
|
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
|
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 {
|
Binary file
|
data/lib/manticore.rb
CHANGED
@@ -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
|
-
|
29
|
-
rescue Java::
|
30
|
-
|
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
|
data/lib/manticore/client.rb
CHANGED
@@ -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
|
-
|
22
|
-
java_import
|
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
|
-
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
#
|
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
|
-
#
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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
|
-
# @
|
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,
|
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,
|
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::
|
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
|
-
|
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
|
data/lib/manticore/response.rb
CHANGED
@@ -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)
|
data/lib/manticore/version.rb
CHANGED
@@ -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
|
data/spec/spec_helper.rb
CHANGED
@@ -38,10 +38,27 @@ def start_server(port = PORT)
|
|
38
38
|
request[:body] = read_nonblock stream.socket
|
39
39
|
end
|
40
40
|
|
41
|
-
|
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.
|
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-
|
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
|