aeden-jruby-http-reactor 0.0.1
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 +1 -0
- data/Rakefile +35 -0
- data/VERSION +1 -0
- data/lib/http_reactor/client.rb +208 -0
- data/lib/http_reactor.rb +12 -0
- data/test/client_test.rb +25 -0
- data/test/test_helper.rb +2 -0
- data/vendor/httpcore-4.0.1.jar +0 -0
- data/vendor/httpcore-nio-4.0.1.jar +0 -0
- metadata +63 -0
data/README.rdoc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
A client that uses the Apache HttpCore NIO library to do stuff.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
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
|
+
end
|
33
|
+
rescue LoadError
|
34
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
35
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,208 @@
|
|
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
|
+
|
12
|
+
def initialize(request_count, handler_proc)
|
13
|
+
@request_count = request_count
|
14
|
+
@handler_proc = handler_proc
|
15
|
+
end
|
16
|
+
|
17
|
+
def initalize_context(context, attachment)
|
18
|
+
context.set_attribute(ExecutionContext.HTTP_TARGET_HOST, attachment[:host]);
|
19
|
+
context.set_attribute(HTTP_TARGET_PATH, attachment[:path])
|
20
|
+
end
|
21
|
+
|
22
|
+
def finalize_context(context)
|
23
|
+
flag = context.get_attribute(RESPONSE_RECEIVED)
|
24
|
+
@request_count.count_down() unless flag
|
25
|
+
end
|
26
|
+
|
27
|
+
def submit_request(context)
|
28
|
+
target_host = context.get_attribute(ExecutionContext.HTTP_TARGET_HOST);
|
29
|
+
target_path = context.get_attribute(HTTP_TARGET_PATH)
|
30
|
+
flag = context.get_attribute(REQUEST_SENT);
|
31
|
+
if flag.nil?
|
32
|
+
# Stick some object into the context
|
33
|
+
context.set_attribute(REQUEST_SENT, true);
|
34
|
+
|
35
|
+
puts "--------------"
|
36
|
+
puts "Sending request to #{target_host}#{target_path}"
|
37
|
+
puts "--------------"
|
38
|
+
|
39
|
+
org.apache.http.message.BasicHttpRequest.new("GET", target_path)
|
40
|
+
else
|
41
|
+
# No new request to submit
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def handle_response(response, context)
|
46
|
+
@handler_proc.call(response, context)
|
47
|
+
|
48
|
+
context.setAttribute(RESPONSE_RECEIVED, true)
|
49
|
+
|
50
|
+
# Signal completion of the request execution
|
51
|
+
@request_count.count_down()
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class SessionRequestCallback #:nodoc:
|
56
|
+
include org.apache.http.nio.reactor.SessionRequestCallback
|
57
|
+
|
58
|
+
def initialize(request_count)
|
59
|
+
@request_count = request_count
|
60
|
+
end
|
61
|
+
|
62
|
+
def cancelled(request)
|
63
|
+
puts "Connect request cancelled: #{request.remote_address}"
|
64
|
+
@request_count.count_down()
|
65
|
+
end
|
66
|
+
|
67
|
+
def completed(request); end
|
68
|
+
|
69
|
+
def failed(request)
|
70
|
+
puts "Connect request failed: #{request.remote_address}"
|
71
|
+
@request_count.count_down()
|
72
|
+
end
|
73
|
+
|
74
|
+
def timeout(request)
|
75
|
+
puts "Connect request timed out: #{request.remote_address}"
|
76
|
+
@request_count.count_down()
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class EventLogger #:nodoc:
|
81
|
+
import org.apache.http.nio.protocol
|
82
|
+
include EventListener
|
83
|
+
def connection_open(conn)
|
84
|
+
puts "Connection open: #{conn}"
|
85
|
+
end
|
86
|
+
def connection_timeout(conn)
|
87
|
+
puts "Connection timed out: #{conn}"
|
88
|
+
end
|
89
|
+
def connection_closed(conn)
|
90
|
+
puts "Connection closed: #{conn}"
|
91
|
+
end
|
92
|
+
def fatalIOException(ex, onn)
|
93
|
+
puts "Fatal I/O error: #{ex.message}"
|
94
|
+
end
|
95
|
+
def fatal_protocol_exception(ex, conn)
|
96
|
+
puts "HTTP error: #{ex.message}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# An HTTP client that uses the Reactor pattern.
|
101
|
+
class Client
|
102
|
+
import org.apache.http
|
103
|
+
import org.apache.http.params
|
104
|
+
import org.apache.http.protocol
|
105
|
+
import org.apache.http.nio.protocol
|
106
|
+
import org.apache.http.impl.nio
|
107
|
+
import org.apache.http.impl.nio.reactor
|
108
|
+
|
109
|
+
# Create a new HttpReactor client that will request the given URIs.
|
110
|
+
#
|
111
|
+
# Parameters:
|
112
|
+
# * <tt>uris</tt>: An array of URI objects.
|
113
|
+
# * <tt>handler_proc</tt>: A Proc that will be called with the response and context
|
114
|
+
# * <tt>session_request_callback</tt>: A class that implements the session request
|
115
|
+
# callback interface found in the HttpCore library.
|
116
|
+
def initialize(uris=[], handler_proc=nil, session_request_callback=SessionRequestCallback)
|
117
|
+
handler_proc ||= default_handler_proc
|
118
|
+
|
119
|
+
params = BasicHttpParams.new
|
120
|
+
params.set_int_parameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
|
121
|
+
params.set_int_parameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000)
|
122
|
+
params.set_int_parameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
|
123
|
+
params.set_boolean_parameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
|
124
|
+
params.set_boolean_parameter(CoreConnectionPNames.TCP_NODELAY, true)
|
125
|
+
params.set_parameter(CoreProtocolPNames.USER_AGENT, "JRubyHttpReactor/0.0.1")
|
126
|
+
|
127
|
+
io_reactor = DefaultConnectingIOReactor.new(2, params);
|
128
|
+
|
129
|
+
httpproc = BasicHttpProcessor.new;
|
130
|
+
httpproc.add_interceptor(RequestContent.new);
|
131
|
+
httpproc.add_interceptor(RequestTargetHost.new);
|
132
|
+
httpproc.add_interceptor(RequestConnControl.new);
|
133
|
+
httpproc.add_interceptor(RequestUserAgent.new);
|
134
|
+
httpproc.add_interceptor(RequestExpectContinue.new);
|
135
|
+
|
136
|
+
# We are going to use this object to synchronize between the
|
137
|
+
# I/O event and main threads
|
138
|
+
request_count = java.util.concurrent.CountDownLatch.new(uris.length);
|
139
|
+
|
140
|
+
handler = BufferingHttpClientHandler.new(
|
141
|
+
httpproc,
|
142
|
+
RequestExecutionHandler.new(request_count, handler_proc),
|
143
|
+
org.apache.http.impl.DefaultConnectionReuseStrategy.new,
|
144
|
+
params
|
145
|
+
)
|
146
|
+
|
147
|
+
handler.event_listener = EventLogger.new
|
148
|
+
|
149
|
+
io_event_dispatch = DefaultClientIOEventDispatch.new(handler, params)
|
150
|
+
|
151
|
+
Thread.abort_on_exception = true
|
152
|
+
t = Thread.new do
|
153
|
+
begin
|
154
|
+
puts "Executing IO reactor"
|
155
|
+
io_reactor.execute(io_event_dispatch)
|
156
|
+
rescue java.io.InterruptedIOException => e
|
157
|
+
puts "Interrupted"
|
158
|
+
rescue java.io.IOException => e
|
159
|
+
puts "I/O error in reactor execution thread: #{e.message}"
|
160
|
+
end
|
161
|
+
puts "Shutdown"
|
162
|
+
end
|
163
|
+
|
164
|
+
uris.each do |uri|
|
165
|
+
io_reactor.connect(
|
166
|
+
java.net.InetSocketAddress.new(uri.host, uri.port),
|
167
|
+
nil,
|
168
|
+
{:host => HttpHost.new(uri.host), :path => uri.path},
|
169
|
+
session_request_callback.new(request_count)
|
170
|
+
)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Block until all connections signal
|
174
|
+
# completion of the request execution
|
175
|
+
request_count.await()
|
176
|
+
|
177
|
+
puts "Shutting down I/O reactor"
|
178
|
+
|
179
|
+
io_reactor.shutdown()
|
180
|
+
|
181
|
+
puts "Done"
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
def default_handler_proc
|
186
|
+
Proc.new { |response, context|
|
187
|
+
target_host = context.get_attribute(ExecutionContext.HTTP_TARGET_HOST);
|
188
|
+
target_path = context.get_attribute(RequestExecutionHandler::HTTP_TARGET_PATH)
|
189
|
+
|
190
|
+
entity = response.entity
|
191
|
+
begin
|
192
|
+
content = org.apache.http.util.EntityUtils.toString(entity)
|
193
|
+
|
194
|
+
puts "--------------"
|
195
|
+
puts "Response from #{target_host}#{target_path}"
|
196
|
+
puts "--------------"
|
197
|
+
puts response.status_line
|
198
|
+
puts "--------------"
|
199
|
+
puts "Document length: #{content.length}"
|
200
|
+
puts "--------------"
|
201
|
+
rescue java.io.IOException => ex
|
202
|
+
puts "I/O error in handle_response: #{ex.message}"
|
203
|
+
end
|
204
|
+
}
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
data/lib/http_reactor.rb
ADDED
@@ -0,0 +1,12 @@
|
|
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/client'
|
data/test/client_test.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class ClientTest < Test::Unit::TestCase
|
4
|
+
def uris
|
5
|
+
@uris ||= [
|
6
|
+
'http://www.yahoo.com/',
|
7
|
+
'http://www.google.com/',
|
8
|
+
'http://www.apache.org/',
|
9
|
+
'http://anthony.mp/about_me'
|
10
|
+
].map { |url_string| URI.parse(url_string) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_new
|
14
|
+
assert_nothing_raised do
|
15
|
+
HttpReactor::Client.new(uris)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_proc
|
20
|
+
handler = Proc.new { |response, context|
|
21
|
+
puts "Response: #{response.status_line.status_code}"
|
22
|
+
}
|
23
|
+
HttpReactor::Client.new(uris, handler)
|
24
|
+
end
|
25
|
+
end
|
data/test/test_helper.rb
ADDED
Binary file
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aeden-jruby-http-reactor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anthony Eden
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-13 00:00:00 -07: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
|
+
files:
|
25
|
+
- README.rdoc
|
26
|
+
- Rakefile
|
27
|
+
- VERSION
|
28
|
+
- lib/http_reactor.rb
|
29
|
+
- lib/http_reactor/client.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
|
+
has_rdoc: true
|
35
|
+
homepage: http://github.com/aeden/jruby-http-reactor
|
36
|
+
licenses:
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options:
|
39
|
+
- --charset=UTF-8
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 1.3.5
|
58
|
+
signing_key:
|
59
|
+
specification_version: 2
|
60
|
+
summary: JRuby NIO HTTP client.
|
61
|
+
test_files:
|
62
|
+
- test/client_test.rb
|
63
|
+
- test/test_helper.rb
|