manticore 0.2.0-java → 0.2.1-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|
[](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
|