jruby-http-reactor 0.5.2

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.
data/README.rdoc ADDED
@@ -0,0 +1,13 @@
1
+ A client that uses the Apache HttpCore NIO library to do HTTP requests.
2
+
3
+ <pre>
4
+ require 'http_reactor'
5
+
6
+ uris = ['http://www.yahoo.com/','http://www.google.com/']
7
+ requests = uris.map { |uri| HttpReactor::Request.new(uri) }
8
+
9
+ HttpReactor::Client.new(requests) do |response, context|
10
+ puts "Response code: #{response.code}"
11
+ puts "Response body: #{response.body}"
12
+ end
13
+ </pre>
data/README.textile ADDED
@@ -0,0 +1,13 @@
1
+ A client that uses the Apache HttpCore NIO library to do HTTP requests.
2
+
3
+ <pre>
4
+ require 'http_reactor'
5
+
6
+ uris = ['http://www.yahoo.com/','http://www.google.com/']
7
+ requests = uris.map { |uri| HttpReactor::Request.new(uri) }
8
+
9
+ HttpReactor::Client.new(requests) do |response, context|
10
+ puts "Response code: #{response.code}"
11
+ puts "Response body: #{response.body}"
12
+ end
13
+ </pre>
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+
4
+ desc 'Default: run tests.'
5
+ task :default => [:test]
6
+
7
+ desc 'Run tests.'
8
+ task :test do
9
+ require File.dirname(__FILE__) + '/test/client_test'
10
+ end
11
+
12
+ desc 'Generate documentation.'
13
+ Rake::RDocTask.new(:rdoc) do |rdoc|
14
+ rdoc.rdoc_dir = 'rdoc'
15
+ rdoc.title = 'JRuby HTTP Reactor'
16
+ rdoc.options << '--line-numbers' << '--inline-source'
17
+ rdoc.rdoc_files.include('README.rdoc')
18
+ rdoc.rdoc_files.include('lib/*.rb')
19
+ rdoc.rdoc_files.include('lib/**/*.rb')
20
+ end
21
+
22
+ begin
23
+ require 'jeweler'
24
+ Jeweler::Tasks.new do |gemspec|
25
+ gemspec.name = "jruby-http-reactor"
26
+ gemspec.summary = "JRuby NIO HTTP client."
27
+ gemspec.email = "anthonyeden@gmail.com"
28
+ gemspec.homepage = "http://github.com/aeden/jruby-http-reactor"
29
+ gemspec.description = ""
30
+ gemspec.authors = ["Anthony Eden"]
31
+ gemspec.files.exclude 'docs/**/*'
32
+ gemspec.files.exclude '.gitignore'
33
+ end
34
+ rescue LoadError
35
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
36
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.2
data/examples/opml.rb ADDED
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env jruby
2
+ #
3
+ # Usage: jruby examples/opml.rb opml.xml
4
+ #
5
+ # Dependencies:
6
+ #
7
+ # hpricot (0.6.164)
8
+ # threadify (1.1.0) (optional, uncomment in code)
9
+
10
+ require 'uri'
11
+ require 'rubygems'
12
+ require 'hpricot'
13
+ #require 'threadify'
14
+ require File.dirname(__FILE__) + '/../lib/http_reactor'
15
+
16
+ def requests
17
+ @requests ||= begin
18
+ xml = File.read(ARGV.pop)
19
+ doc = Hpricot::XML(xml)
20
+ urls = (doc/'outline').map { |outline| outline['xmlUrl'] }
21
+ urls.map { |url_string| HttpReactor::Request.new(URI.parse(url_string)) }
22
+ end
23
+ end
24
+
25
+ #uris.threadify(:each_slice, 1) do |slice|
26
+ HttpReactor::Client.new(requests) do |response, context|
27
+ puts "Response: #{response.status_line.status_code}"
28
+ puts "Content length: #{response.body.length}"
29
+ end
30
+ #end
31
+ puts "Processed #{requests.length} feeds"
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{jruby-http-reactor}
8
+ s.version = "0.5.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Anthony Eden"]
12
+ s.date = %q{2009-10-22}
13
+ s.description = %q{}
14
+ s.email = %q{anthonyeden@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc",
17
+ "README.textile"
18
+ ]
19
+ s.files = [
20
+ "README.rdoc",
21
+ "README.textile",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "examples/opml.rb",
25
+ "jruby-http-reactor.gemspec",
26
+ "lib/http_reactor.rb",
27
+ "lib/http_reactor/client.rb",
28
+ "lib/http_reactor/request.rb",
29
+ "lib/http_reactor/response.rb",
30
+ "test/client_test.rb",
31
+ "test/test_helper.rb",
32
+ "vendor/httpcore-4.0.1.jar",
33
+ "vendor/httpcore-nio-4.0.1.jar"
34
+ ]
35
+ s.homepage = %q{http://github.com/aeden/jruby-http-reactor}
36
+ s.rdoc_options = ["--charset=UTF-8"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.5}
39
+ s.summary = %q{JRuby NIO HTTP client.}
40
+ s.test_files = [
41
+ "test/client_test.rb",
42
+ "test/test_helper.rb",
43
+ "examples/opml.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
51
+ else
52
+ end
53
+ else
54
+ end
55
+ end
@@ -0,0 +1,287 @@
1
+ module HttpReactor #:nodoc:
2
+ class RequestExecutionHandler #:nodoc:
3
+ import org.apache.http.protocol
4
+ import org.apache.http.nio.protocol
5
+ include HttpRequestExecutionHandler
6
+
7
+ REQUEST_SENT = "request-sent"
8
+ RESPONSE_RECEIVED = "response-received"
9
+
10
+ HTTP_TARGET_PATH = 'http_target_path'
11
+ HTTP_TARGET_REQUEST = 'http_target_request'
12
+ IO_REACTOR = "io_reactor"
13
+ REDIRECT_HISTORY = "redirect_history"
14
+
15
+ def initialize(request_count, handler_proc)
16
+ @request_count = request_count
17
+ @handler_proc = handler_proc
18
+ end
19
+
20
+ def initalize_context(context, attachment)
21
+ context.set_attribute(ExecutionContext.HTTP_TARGET_HOST, attachment[:host])
22
+ context.set_attribute(HTTP_TARGET_PATH, attachment[:path])
23
+ context.set_attribute(HTTP_TARGET_REQUEST, attachment[:request])
24
+ context.set_attribute(IO_REACTOR, attachment[:io_reactor])
25
+ context.set_attribute(REDIRECT_HISTORY, attachment[:redirect_history] || [])
26
+ end
27
+
28
+ def finalize_context(context)
29
+ flag = context.get_attribute(RESPONSE_RECEIVED)
30
+ @request_count.count_down() unless flag
31
+ end
32
+
33
+ def submit_request(context)
34
+ target_host = context.get_attribute(ExecutionContext.HTTP_TARGET_HOST);
35
+ target_path = context.get_attribute(HTTP_TARGET_PATH)
36
+ target_request = context.get_attribute(HTTP_TARGET_REQUEST)
37
+ flag = context.get_attribute(REQUEST_SENT);
38
+ if flag.nil?
39
+ # Stick some object into the context
40
+ context.set_attribute(REQUEST_SENT, true);
41
+
42
+ #puts "--------------"
43
+ #puts "Sending request to #{target_host}#{target_path}"
44
+ #puts "--------------"
45
+
46
+ org.apache.http.message.BasicHttpEntityEnclosingRequest.new(
47
+ target_request.method, target_path
48
+ )
49
+ else
50
+ # No new request to submit
51
+ end
52
+ end
53
+
54
+ def handle_response(response, context)
55
+ begin
56
+ res = HttpReactor::Response.new(response)
57
+ case res.code
58
+ when 301, 302
59
+ redirect_to = res.headers['Location']
60
+ redirect_history = context.getAttribute(REDIRECT_HISTORY)
61
+ if redirect_history.include?(redirect_to)
62
+ #puts "Too many redirects"
63
+ context.setAttribute(RESPONSE_RECEIVED, true)
64
+ @request_count.count_down()
65
+ else
66
+ #puts "Redirecting to #{redirect_to}"
67
+ redirect_history << redirect_to
68
+ context.setAttribute(REDIRECT_HISTORY, redirect_history)
69
+ request = context.getAttribute(HTTP_TARGET_REQUEST)
70
+ request.uri = URI.parse(redirect_to)
71
+ io_reactor = context.getAttribute(IO_REACTOR)
72
+ HttpReactor::Client.process_requests([request], io_reactor, @request_count)
73
+ end
74
+ else
75
+ @handler_proc.call(res, context)
76
+ context.setAttribute(RESPONSE_RECEIVED, true)
77
+ # Signal completion of the request execution
78
+ @request_count.count_down()
79
+ end
80
+ rescue => e
81
+ puts "Error handling response: #{e.message}"
82
+ end
83
+ end
84
+ end
85
+
86
+ class SessionRequestCallback #:nodoc:
87
+ include org.apache.http.nio.reactor.SessionRequestCallback
88
+
89
+ def initialize(request_count)
90
+ @request_count = request_count
91
+ end
92
+
93
+ def cancelled(request)
94
+ puts "Connect request cancelled: #{request.remote_address}"
95
+ @request_count.count_down()
96
+ end
97
+
98
+ def completed(request); end
99
+
100
+ def failed(request)
101
+ puts "Connect request failed: #{request.remote_address}"
102
+ @request_count.count_down()
103
+ end
104
+
105
+ def timeout(request)
106
+ puts "Connect request timed out: #{request.remote_address}"
107
+ @request_count.count_down()
108
+ end
109
+ end
110
+
111
+ class EventLogger #:nodoc:
112
+ import org.apache.http.nio.protocol
113
+ include EventListener
114
+ def connection_open(conn)
115
+ puts "Connection open: #{conn}"
116
+ end
117
+ def connection_timeout(conn)
118
+ puts "Connection timed out: #{conn}"
119
+ end
120
+ def connection_closed(conn)
121
+ puts "Connection closed: #{conn}"
122
+ end
123
+ def fatal_i_o_exception(ex, conn)
124
+ puts "Fatal I/O error: #{ex.message}"
125
+ end
126
+ def fatal_protocol_exception(ex, conn)
127
+ puts "HTTP error: #{ex.message}"
128
+ end
129
+ end
130
+
131
+ # An HTTP client that uses the Reactor pattern.
132
+ class Client
133
+ import org.apache.http
134
+ import org.apache.http.params
135
+ import org.apache.http.protocol
136
+ import org.apache.http.nio.protocol
137
+ import org.apache.http.impl.nio
138
+ import org.apache.http.impl.nio.reactor
139
+
140
+ # Create a new HttpReactor client that will request the given URIs.
141
+ #
142
+ # Parameters:
143
+ # * <tt>uris</tt>: An array of URI objects.
144
+ # * <tt>handler_proc</tt>: A Proc that will be called with the response and context
145
+ # * <tt>session_request_callback</tt>: A class that implements the session request
146
+ # callback interface found in the HttpCore library.
147
+ # * <tt>options: A hash of configuration options. See below.
148
+ #
149
+ # The options hash may include the following options
150
+ # * <tt>:so_timeout</tt>: (default = 5 seconds)
151
+ # * <tt>:connection_timeout</tt>: The HTTP connection timeout (default = 10 seconds)
152
+ # * <tt>:socket_buffer_size</tt>: The buffer size (defaults to 8Kb)
153
+ # * <tt>:stale_connection_check</tt>: (defaults to false)
154
+ # * <tt>:tcp_nodelay</tt>: (defaults to true)
155
+ # * <tt>:user_agent</tt>: The user agent string to send (defaults to "JRubyHttpReactor")
156
+ # * <tt>:event_listener</tt>: A class that implements the org.apache.http.nio.protocol interface
157
+ def initialize(requests=[], handler_proc=nil, options={}, &block)
158
+ handler_proc = block if block_given?
159
+ handler_proc ||= default_handler_proc
160
+
161
+ initialize_options(options)
162
+
163
+ params = build_params(options)
164
+
165
+ io_reactor = DefaultConnectingIOReactor.new(2, params);
166
+
167
+ httpproc = BasicHttpProcessor.new;
168
+ httpproc.add_interceptor(RequestContent.new);
169
+ httpproc.add_interceptor(RequestTargetHost.new);
170
+ httpproc.add_interceptor(RequestConnControl.new);
171
+ httpproc.add_interceptor(RequestUserAgent.new);
172
+ httpproc.add_interceptor(RequestExpectContinue.new);
173
+
174
+ # We are going to use this object to synchronize between the
175
+ # I/O event and main threads
176
+ request_counter = java.util.concurrent.CountDownLatch.new(requests.length);
177
+
178
+ handler = BufferingHttpClientHandler.new(
179
+ httpproc,
180
+ RequestExecutionHandler.new(request_counter, handler_proc),
181
+ org.apache.http.impl.DefaultConnectionReuseStrategy.new,
182
+ params
183
+ )
184
+
185
+ handler.event_listener = options[:event_listener].new if options[:event_listener]
186
+
187
+ io_event_dispatch = DefaultClientIOEventDispatch.new(handler, params)
188
+
189
+ Thread.abort_on_exception = true
190
+ t = Thread.new do
191
+ begin
192
+ #puts "Executing IO reactor"
193
+ io_reactor.execute(io_event_dispatch)
194
+ rescue java.io.InterruptedIOException => e
195
+ puts "Interrupted"
196
+ rescue java.io.IOException => e
197
+ puts "I/O error in reactor execution thread: #{e.message}"
198
+ end
199
+ #puts "Shutdown"
200
+ end
201
+
202
+ process_requests(requests, io_reactor, request_counter)
203
+
204
+ # Block until all connections signal
205
+ # completion of the request execution
206
+ request_counter.await()
207
+
208
+ #puts "Shutting down I/O reactor"
209
+
210
+ io_reactor.shutdown()
211
+
212
+ #puts "Done"
213
+ end
214
+
215
+ def process_requests(requests, io_reactor, request_counter)
216
+ HttpReactor::Client.process_requests(requests, io_reactor, request_counter)
217
+ end
218
+
219
+ def self.process_requests(requests, io_reactor, request_counter)
220
+ requests.each do |request|
221
+ uri = request.uri
222
+ attachment = {
223
+ :host => HttpHost.new(uri.host),
224
+ :path => uri.request_uri,
225
+ :request => request,
226
+ :io_reactor => io_reactor
227
+ }
228
+ io_reactor.connect(
229
+ java.net.InetSocketAddress.new(uri.host, uri.port),
230
+ nil,
231
+ attachment,
232
+ SessionRequestCallback.new(request_counter)
233
+ )
234
+ end
235
+ end
236
+
237
+ private
238
+
239
+ def initialize_options(options)
240
+ options[:so_timeout] ||= 5000
241
+ options[:connection_timeout] ||= 10000
242
+ options[:socket_buffer_size] ||= 8 * 1024
243
+ options[:stale_connection_check] ||= false
244
+ options[:tcp_nodelay] ||= true
245
+ options[:user_agent] ||= "JRubyHttpReactor"
246
+ #options[:event_listener] ||= EventLogger
247
+ end
248
+
249
+ def build_params(options)
250
+ params = BasicHttpParams.new
251
+ params.set_int_parameter(
252
+ CoreConnectionPNames.SO_TIMEOUT, options[:so_timeout])
253
+ params.set_int_parameter(
254
+ CoreConnectionPNames.CONNECTION_TIMEOUT, options[:connection_timeout])
255
+ params.set_int_parameter(
256
+ CoreConnectionPNames.SOCKET_BUFFER_SIZE, options[:socket_buffer_size])
257
+ params.set_boolean_parameter(
258
+ CoreConnectionPNames.STALE_CONNECTION_CHECK, options[:stale_connection_check])
259
+ params.set_boolean_parameter(
260
+ CoreConnectionPNames.TCP_NODELAY, options[:tcp_nodelay])
261
+ params.set_parameter(
262
+ CoreProtocolPNames.USER_AGENT, options[:user_agent])
263
+ params
264
+ end
265
+
266
+ def default_handler_proc
267
+ Proc.new { |response, context|
268
+ target_host = context.get_attribute(ExecutionContext.HTTP_TARGET_HOST);
269
+ target_path = context.get_attribute(RequestExecutionHandler::HTTP_TARGET_PATH)
270
+
271
+ entity = response.entity
272
+ begin
273
+ content = org.apache.http.util.EntityUtils.toString(entity)
274
+
275
+ puts "--------------"
276
+ puts "Response from #{target_host}#{target_path}"
277
+ puts response.status_line
278
+ puts "Document length: #{content.length}"
279
+ puts "--------------"
280
+ rescue java.io.IOException => ex
281
+ puts "I/O error in handle_response: #{ex.message}"
282
+ end
283
+ }
284
+ end
285
+
286
+ end
287
+ end
@@ -0,0 +1,16 @@
1
+ module HttpReactor
2
+ # A class that represents an HTTP request.
3
+ class Request
4
+ attr_accessor :uri
5
+ attr_reader :method, :payload
6
+ attr_accessor :extra
7
+
8
+ # Initialize the request object.
9
+ def initialize(uri, method='GET', payload=nil, extra={})
10
+ @uri = uri
11
+ @method = method
12
+ @payload = payload
13
+ @extra = extra
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,67 @@
1
+ module HttpReactor #:nodoc:
2
+ # A class that represents an HTTP response which wraps the
3
+ # Java HTTP NIO response object and provides methods for accessing
4
+ # the data using ruby idioms.
5
+ class Response
6
+ def initialize(response_impl)
7
+ @response_impl = response_impl
8
+ end
9
+
10
+ # Delegates to the HTTP NIO response
11
+ def status_line
12
+ @response_impl.status_line
13
+ end
14
+
15
+ # Delegates to the HTTP NIO response
16
+ def entity
17
+ @response_impl.entity
18
+ end
19
+
20
+ def code
21
+ status_line.status_code
22
+ end
23
+
24
+ # Get the response content type
25
+ def content_type
26
+ @content_type ||= @response_impl.entity.content_type.value
27
+ end
28
+
29
+ # Get the response content length
30
+ def content_length
31
+ @content_length ||= @response_impl.entity.content_length
32
+ end
33
+
34
+ # Access the headers
35
+ def [](name)
36
+ headers[name]
37
+ end
38
+
39
+ def headers
40
+ @headers ||= begin
41
+ h = Hash.new
42
+ @response_impl.all_headers.each do |header|
43
+ if h[header.name]
44
+ h[header.name] = [h[header.name], header.value]
45
+ else
46
+ h[header.name] = header.value
47
+ end
48
+ end
49
+ h
50
+ end
51
+ end
52
+
53
+ # Get the body text
54
+ def body
55
+ @body ||= begin
56
+ begin
57
+ io = Java.java_to_ruby(
58
+ org.jruby.RubyIO.new(JRuby.runtime, entity.content).java_object
59
+ )
60
+ io.read
61
+ rescue Exception => e
62
+ puts "Error in Response#body: #{e.message}"
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,14 @@
1
+ require 'java'
2
+ require File.dirname(__FILE__) + '/../vendor/httpcore-4.0.1.jar'
3
+ require File.dirname(__FILE__) + '/../vendor/httpcore-nio-4.0.1.jar'
4
+
5
+ # The Ruby module that contains wrappers for the the Apache
6
+ # HTTP NIO implementation.
7
+ module HttpReactor
8
+ end
9
+
10
+ $:.unshift(File.dirname(__FILE__))
11
+
12
+ require 'http_reactor/request'
13
+ require 'http_reactor/response'
14
+ require 'http_reactor/client'
@@ -0,0 +1,49 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require 'uri'
3
+ require 'mime/types'
4
+
5
+ class ClientTest < Test::Unit::TestCase
6
+ def requests
7
+ @requests ||= [
8
+ 'http://yahoo.com/',
9
+ 'http://www.google.com/',
10
+ 'http://www.apache.org/',
11
+ 'http://anthony.mp/about_me',
12
+ 'http://search.twitter.com/search?q=jruby'
13
+ ].map { |url_string| HttpReactor::Request.new(URI.parse(url_string)) }
14
+ end
15
+
16
+ def test_new
17
+ assert_nothing_raised do
18
+ HttpReactor::Client.new(requests)
19
+ end
20
+ end
21
+
22
+ def test_proc
23
+ handler = Proc.new do |response, context|
24
+ assert_equal 200, response.code
25
+ assert response.body.length > 0
26
+ mime_type = MIME::Types[response.content_type].first
27
+ assert_equal "text/html", mime_type.content_type
28
+ end
29
+ HttpReactor::Client.new(requests, handler)
30
+ end
31
+
32
+ def test_block
33
+ HttpReactor::Client.new(requests) do |response, context|
34
+ assert_equal 200, response.status_line.status_code
35
+ assert_equal 200, response.code
36
+ mime_type = MIME::Types[response.content_type].first
37
+ assert_equal "text/html", mime_type.content_type
38
+ puts "request ur: #{context.getAttribute('http_target_request').uri}"
39
+ puts "content-length: #{response.content_length}"
40
+ assert response.body.length > 0
41
+ puts "=== HEADERS ==="
42
+ puts response.headers.inspect
43
+ # puts "===== BODY ===="
44
+ # puts response.body
45
+ # puts "==============="
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/http_reactor'
Binary file
Binary file
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jruby-http-reactor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.2
5
+ platform: ruby
6
+ authors:
7
+ - Anthony Eden
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-22 00:00:00 -10:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: ""
17
+ email: anthonyeden@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - README.textile
25
+ files:
26
+ - README.rdoc
27
+ - README.textile
28
+ - Rakefile
29
+ - VERSION
30
+ - examples/opml.rb
31
+ - jruby-http-reactor.gemspec
32
+ - lib/http_reactor.rb
33
+ - lib/http_reactor/client.rb
34
+ - lib/http_reactor/request.rb
35
+ - lib/http_reactor/response.rb
36
+ - test/client_test.rb
37
+ - test/test_helper.rb
38
+ - vendor/httpcore-4.0.1.jar
39
+ - vendor/httpcore-nio-4.0.1.jar
40
+ has_rdoc: true
41
+ homepage: http://github.com/aeden/jruby-http-reactor
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --charset=UTF-8
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.3.5
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: JRuby NIO HTTP client.
68
+ test_files:
69
+ - test/client_test.rb
70
+ - test/test_helper.rb
71
+ - examples/opml.rb