hot_tub 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -3
- data/LICENSE.txt +1 -1
- data/README.md +79 -7
- data/http_hot_tub.gemspec +7 -4
- data/lib/hot_tub.rb +11 -7
- data/lib/hot_tub/pool.rb +188 -0
- data/lib/hot_tub/session.rb +58 -117
- data/lib/hot_tub/version.rb +1 -1
- data/spec/pool_spec.rb +241 -0
- data/spec/session_spec.rb +72 -83
- data/spec/spec_helper.rb +29 -1
- metadata +34 -20
- data/lib/hot_tub/clients/client.rb +0 -37
- data/lib/hot_tub/clients/em_synchrony_client.rb +0 -41
- data/lib/hot_tub/clients/excon_client.rb +0 -24
- data/lib/hot_tub/clients/http_client_client.rb +0 -38
- data/spec/em_synchrony_client_spec.rb +0 -47
- data/spec/excon_client_spec.rb +0 -18
- data/spec/http_client_client_spec.rb +0 -16
- data/spec/test_helper_methods.rb +0 -33
data/Gemfile
CHANGED
@@ -1,17 +1,16 @@
|
|
1
|
-
source "
|
1
|
+
source "https://rubygems.org"
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in http_hot_tub.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
group :development do
|
7
7
|
platform :ruby do
|
8
|
+
gem 'debugger'
|
8
9
|
gem 'eventmachine'
|
9
10
|
gem 'em-http-request', '~> 1.0', :require => 'em-http'
|
10
11
|
gem 'em-synchrony', '~> 1.0', :require => ['em-synchrony', 'em-synchrony/em-http']
|
11
|
-
gem "excon"
|
12
12
|
end
|
13
13
|
platform :jruby do
|
14
14
|
gem 'jruby-openssl'
|
15
|
-
gem 'jruby-httpclient'
|
16
15
|
end
|
17
16
|
end
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# HotTub
|
2
|
-
A simple thread-safe pooling gem
|
3
|
-
|
2
|
+
A simple thread-safe connection pooling gem. Supports [HTTPClient](https://github.com/nahi/httpclient) (default) and
|
3
|
+
[EM-Http-Requests](https://github.com/igrigorik/em-http-request) via [EM-Synchrony](https://github.com/igrigorik/em-synchrony).
|
4
|
+
There are a couple Ruby connection pool libraries out there but HotTub differs from most in that its connections are lazy
|
5
|
+
(created only when necessary), accomidates libraries that do not clean their dirty connections automatically, and manages unexpected usage increases by opening new connections rather than just blocking or throwings exception (never_block), although never_block can be disabled.
|
4
6
|
|
5
7
|
## Installation
|
6
8
|
|
@@ -8,16 +10,86 @@ HotTub is available through [Rubygems](https://rubygems.org/gems/hot_tub) and ca
|
|
8
10
|
|
9
11
|
$ gem install hot_tub
|
10
12
|
|
11
|
-
##
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
### Default (HTTPClient)
|
16
|
+
|
17
|
+
class MyClass
|
18
|
+
@@url = "http://test12345.com"
|
19
|
+
@@pool = HotTub::Pool.new({:size => 10})
|
20
|
+
def self.fetch_results(url,query={})
|
21
|
+
@@pool.run |connection|
|
22
|
+
connection.get(@@url,query).body
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
MyClass.fetch_results({:foo => "goo"}) # => "Some reponse"
|
27
|
+
|
28
|
+
### EM-Http-Request
|
29
|
+
|
30
|
+
require "em-synchrony"
|
31
|
+
require "em-synchrony/em-http"
|
32
|
+
class EMClass
|
33
|
+
@@pool = HotTub::Pool.new(:size => 12) { EM::HttpRequest.new("http://somewebservice.com") }
|
34
|
+
def async_post_results(query = {})
|
35
|
+
@@pool.run do |connection|
|
36
|
+
connection.aget(:query => results, :keepalive => true)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
EM.synchrony do {
|
42
|
+
EMClass.async_fetch_results({:foo => "goo"})
|
43
|
+
EM.stop
|
44
|
+
}
|
45
|
+
|
46
|
+
### Other
|
47
|
+
You can use any libary you want. Close and clean can be defined at initialization with
|
48
|
+
lambdas, if they are not defined they are ignored.
|
49
|
+
|
12
50
|
class MyClass
|
13
|
-
@@
|
51
|
+
@@url = "http://test12345.com"
|
52
|
+
@@pool = HotTub::Pool.new({:size => 10, :close => lambda {|clnt| clnt.close}}) { MyHttpLib.new }
|
53
|
+
def self.fetch_results(url,query={})
|
54
|
+
@@pool.run |connection|
|
55
|
+
connection.get(@@url,query).body
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
MyClass.fetch_results({:foo => "goo"}) # => "Some reponse"
|
14
61
|
|
15
|
-
|
16
|
-
|
62
|
+
## Sessions
|
63
|
+
|
64
|
+
HTTPClient has a built in thread-safe sessions feature that allows a single client to access multiple domains.
|
65
|
+
Not all clients have a sessions feature, Em-Http-Request clients are initialized to a single domain and while you
|
66
|
+
can change paths the client domain cannot change. HotTub::Session allows you create a session object that initializes
|
67
|
+
seperate pools for you various domains based on URI.
|
68
|
+
|
69
|
+
# Assuming EM is running
|
70
|
+
require 'hot_tub/clients/em_http_request_client'
|
71
|
+
class EMClass
|
72
|
+
@@sessons = HotTub::Sessions.new {|url| HotTub::EmHttpRequestClient.new(url,{:connect_timeout => 5}) }
|
73
|
+
def async_post_results(query = {})
|
74
|
+
@@sessons.run("http://somewebservice.com") do |connection|
|
75
|
+
puts connection.get(:query => results).response_header.status
|
76
|
+
end
|
77
|
+
@@sessons.run("https://someotherwebservice.com") do |connection|
|
78
|
+
puts connection.get(:query => results).response_header.status
|
79
|
+
end
|
17
80
|
end
|
18
81
|
end
|
19
82
|
|
20
|
-
|
83
|
+
## Related
|
84
|
+
|
85
|
+
* [HTTPClient](https://github.com/nahi/httpclient)
|
86
|
+
* [EM-Http-Request](https://github.com/igrigorik/em-http-request)
|
87
|
+
* [EM-Synchrony](https://github.com/igrigorik/em-synchrony)
|
88
|
+
|
89
|
+
## Other Pooling Gem
|
90
|
+
|
91
|
+
* [ConnectionPool](https://github.com/mperham/connection_pool)
|
92
|
+
* [EM-Synchrony](https://github.com/igrigorik/em-synchrony) has a connection pool feature
|
21
93
|
|
22
94
|
## Contributing to HotTub
|
23
95
|
|
data/http_hot_tub.gemspec
CHANGED
@@ -8,14 +8,17 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["Joshua Mckinney"]
|
9
9
|
s.email = ["joshmckin@gmail.com"]
|
10
10
|
s.homepage = "https://github.com/JoshMcKin/hot_tub"
|
11
|
-
s.
|
12
|
-
s.
|
11
|
+
s.license = "MIT"
|
12
|
+
s.summary = %q{A simple thread-safe http connection pooling gem.}
|
13
|
+
s.description = %q{A simple thread-safe http connection pooling gem. Http client options include HTTPClient and EM-Http-Request}
|
13
14
|
|
14
|
-
s.rubyforge_project = "hot_tub"
|
15
15
|
|
16
|
+
s.rubyforge_project = "hot_tub"
|
17
|
+
s.add_runtime_dependency "httpclient"
|
18
|
+
s.add_development_dependency "rspec"
|
19
|
+
|
16
20
|
s.files = `git ls-files`.split("\n")
|
17
21
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
22
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
23
|
s.require_paths = ["lib"]
|
20
|
-
s.add_development_dependency "rspec"
|
21
24
|
end
|
data/lib/hot_tub.rb
CHANGED
@@ -2,20 +2,24 @@ require 'thread'
|
|
2
2
|
require 'timeout'
|
3
3
|
require 'logger'
|
4
4
|
require "hot_tub/version"
|
5
|
+
require "hot_tub/pool"
|
5
6
|
require "hot_tub/session"
|
6
|
-
require "hot_tub/clients/client"
|
7
|
-
require "hot_tub/clients/em_synchrony_client"
|
8
|
-
require "hot_tub/clients/excon_client"
|
9
|
-
require "hot_tub/clients/http_client_client" if RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
|
10
7
|
|
11
8
|
module HotTub
|
12
|
-
|
9
|
+
@@logger = Logger.new(STDOUT)
|
13
10
|
def self.logger
|
14
11
|
@@logger
|
15
12
|
end
|
16
|
-
|
13
|
+
|
17
14
|
def self.logger=logger
|
18
15
|
@@logger = logger
|
19
16
|
end
|
20
|
-
end
|
21
17
|
|
18
|
+
def self.em?
|
19
|
+
(defined?(EM) && EM::reactor_running?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.jruby?
|
23
|
+
(defined?(JRUBY_VERSION))
|
24
|
+
end
|
25
|
+
end
|
data/lib/hot_tub/pool.rb
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'httpclient'
|
2
|
+
module HotTub
|
3
|
+
class Pool
|
4
|
+
attr_reader :current_size
|
5
|
+
KNOWN_CLIENTS = {
|
6
|
+
"HTTPClient" => {
|
7
|
+
:close => lambda { |clnt|
|
8
|
+
sessions = clnt.instance_variable_get(:@session_manager)
|
9
|
+
sessions.reset_all if sessions
|
10
|
+
}
|
11
|
+
},
|
12
|
+
'EventMachine::HttpConnection' => {
|
13
|
+
:close => lambda { |clnt|
|
14
|
+
if clnt.conn
|
15
|
+
clnt.conn.close_connection
|
16
|
+
clnt.instance_variable_set(:@deferred, true)
|
17
|
+
end
|
18
|
+
},
|
19
|
+
:clean => lambda { |clnt|
|
20
|
+
if clnt.conn && clnt.conn.error?
|
21
|
+
HotTub.logger.info "Sanitizing connection : #{EventMachine::report_connection_error_status(clnt.conn.instance_variable_get(:@signature))}"
|
22
|
+
clnt.conn.close_connection
|
23
|
+
clnt.instance_variable_set(:@deferred, true)
|
24
|
+
end
|
25
|
+
clnt
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
# Generic lazy connection pool of HTTP clients
|
31
|
+
# The default client is HTTPClient.
|
32
|
+
# Clients must respond to :clean, :close, and :run
|
33
|
+
#
|
34
|
+
# == Example (HTTPClient)
|
35
|
+
# pool = HotTub::Pool.new(:size => 25)
|
36
|
+
# pool.run {|clnt| clnt.get('http://test.com').body }
|
37
|
+
#
|
38
|
+
# == Example with different client
|
39
|
+
# pool = HotTub::Pool.new { EM::HttpRequest.new("http://somewebservice.com") }
|
40
|
+
# pool.run {|clnt| clnt.get(:keepalive => true).body }
|
41
|
+
#
|
42
|
+
# HotTub::Pool defaults never_block to true, which means if run out of
|
43
|
+
# connections simply create a new client to continue operations.
|
44
|
+
# The pool size will remain consistent and extra connections will be closed
|
45
|
+
# as they are pushed back. If you would like to throw an exception rather than
|
46
|
+
# add new connections set :never_block to false; blocking_timeout defaults to 10 seconds.
|
47
|
+
#
|
48
|
+
# == Example without #never_block (will BlockingTimeout exception)
|
49
|
+
# pool = HotTub::Pool.new(:size => 1, :never_block => false, :blocking_timeout => 0.5)
|
50
|
+
#
|
51
|
+
# begin
|
52
|
+
# pool.run {|clnt| clnt.get('http://test.com').body }
|
53
|
+
# rescue HotTub::BlockingTimeout => e
|
54
|
+
# puts "Our pool ran out: {e}"
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
def initialize(options={},&client_block)
|
58
|
+
@client_block = (block_given? ? client_block : lambda { HTTPClient.new })
|
59
|
+
@options = {
|
60
|
+
:size => 5,
|
61
|
+
:never_block => true,
|
62
|
+
:blocking_timeout => 10,
|
63
|
+
:close => nil,
|
64
|
+
:clean => nil
|
65
|
+
}.merge(options)
|
66
|
+
@pool = []
|
67
|
+
@current_size = 0
|
68
|
+
@clients = []
|
69
|
+
@mutex = (HotTub.em? ? EM::Synchrony::Thread::Mutex.new : Mutex.new)
|
70
|
+
@blocking_timeout = @options[:blocking_timeout]
|
71
|
+
@never_block = @options[:never_block]
|
72
|
+
@size = @options[:size]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Hand off to client.run
|
76
|
+
def run(&block)
|
77
|
+
clnt = client
|
78
|
+
if block_given?
|
79
|
+
return block.call(clnt) if clnt
|
80
|
+
else
|
81
|
+
raise ArgumentError, 'Run requires a block.'
|
82
|
+
end
|
83
|
+
ensure
|
84
|
+
push(clnt) if clnt
|
85
|
+
end
|
86
|
+
|
87
|
+
# Calls close on all connections and reset the pools
|
88
|
+
def close_all
|
89
|
+
@mutex.synchronize do
|
90
|
+
while clnt = @clients.pop
|
91
|
+
begin
|
92
|
+
close_client(clnt)
|
93
|
+
rescue => e
|
94
|
+
HotTub.logger.error "There was an error close one of your HotTub::Pool connections: #{e}"
|
95
|
+
end
|
96
|
+
@pool.delete(clnt)
|
97
|
+
end
|
98
|
+
@current_size = 0
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
# Returns an instance of the client for this pool.
|
105
|
+
def client
|
106
|
+
clnt = nil
|
107
|
+
alarm = (Time.now + @blocking_timeout)
|
108
|
+
# block until we get an available client or raise Timeout::Error
|
109
|
+
while clnt.nil?
|
110
|
+
raise_alarm if alarm <= Time.now
|
111
|
+
clnt = pop
|
112
|
+
end
|
113
|
+
clean_client(clnt)
|
114
|
+
clnt
|
115
|
+
end
|
116
|
+
|
117
|
+
# Attempts to clean the provided client, checking the options first for a clean block
|
118
|
+
# then checking the known clients
|
119
|
+
def clean_client(clnt)
|
120
|
+
return @options[:clean].call(clnt) if @options[:clean] if @options[:clean].is_a?(Proc)
|
121
|
+
if settings = KNOWN_CLIENTS[clnt.class.name]
|
122
|
+
settings[:clean].call(clnt) if settings[:clean].is_a?(Proc)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
# Attempts to close the provided client, checking the options first for a close block
|
128
|
+
# then checking the known clients
|
129
|
+
def close_client(clnt)
|
130
|
+
return @options[:close].call(clnt) if @options[:close] if @options[:close].is_a?(Proc)
|
131
|
+
if settings = KNOWN_CLIENTS[clnt.class.name]
|
132
|
+
settings[:close].call(clnt) if settings[:close].is_a?(Proc)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def raise_alarm
|
137
|
+
message = "Could not fetch a free client in time. Consider increasing your pool size for #{@client.class.name}."
|
138
|
+
HotTub.logger.error message
|
139
|
+
raise BlockingTimeout, message
|
140
|
+
end
|
141
|
+
|
142
|
+
# Safely add client back to pool
|
143
|
+
def push(clnt)
|
144
|
+
@mutex.synchronize do
|
145
|
+
if @pool.length < @size
|
146
|
+
@pool << clnt
|
147
|
+
else
|
148
|
+
@clients.delete(clnt)
|
149
|
+
close_client(clnt)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
nil # make sure never return the pool
|
153
|
+
end
|
154
|
+
|
155
|
+
# Safely pull client from pool, adding if allowed
|
156
|
+
def pop
|
157
|
+
@mutex.synchronize do
|
158
|
+
add if add?
|
159
|
+
clnt = @pool.pop
|
160
|
+
if (clnt.nil? && @never_block)
|
161
|
+
HotTub.logger.info "Adding never_block client for #{@client.class.name}."
|
162
|
+
clnt = new_client
|
163
|
+
end
|
164
|
+
clnt
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# create a new client from base client
|
169
|
+
def new_client
|
170
|
+
clnt = @client_block.call
|
171
|
+
@clients << clnt
|
172
|
+
clnt
|
173
|
+
end
|
174
|
+
|
175
|
+
# Only want to add a client if the pool is empty in keeping with
|
176
|
+
# a lazy model.
|
177
|
+
def add?
|
178
|
+
(@pool.length == 0 && @current_size <= @size)
|
179
|
+
end
|
180
|
+
|
181
|
+
def add
|
182
|
+
HotTub.logger.info "Adding HotTub client: #{@client.class.name} to pool"
|
183
|
+
@current_size += 1
|
184
|
+
@pool << new_client
|
185
|
+
end
|
186
|
+
end
|
187
|
+
class BlockingTimeout < StandardError;end
|
188
|
+
end
|
data/lib/hot_tub/session.rb
CHANGED
@@ -1,129 +1,70 @@
|
|
1
|
+
require 'uri'
|
1
2
|
module HotTub
|
2
3
|
class Session
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# time default to 0.5 seconds, if you need more consider enlarging your pool
|
10
|
-
# instead of raising this number
|
11
|
-
# :never_block - if set to true, a client will always be returned,
|
12
|
-
# but the pool size will never grow past that :size option, extra clients are closed
|
13
|
-
def initialize(client,options={})
|
14
|
-
@options = {
|
15
|
-
:size => 5,
|
16
|
-
:never_block => false,
|
17
|
-
:blocking_timeout => 0.5
|
18
|
-
}.merge(options || {})
|
19
|
-
@pool = []
|
20
|
-
@pool_data = {:current_size => 0}
|
21
|
-
@client = client
|
22
|
-
@mutex = (@client.respond_to?(:mutex) ? @client.mutex : Mutex.new)
|
23
|
-
end
|
24
|
-
|
25
|
-
# The synchronized pool for all our clients.
|
4
|
+
|
5
|
+
# A HotTub::Session is a synchronized hash used to separate HotTub::Pools by their domain.
|
6
|
+
# EmHttpRequest clients are initialized to a specific domain, so we sometimes need a way to
|
7
|
+
# manage multiple pools like when a process need to connect to various AWS resources. Sessions
|
8
|
+
# are unnecessary for HTTPClient because the client has its own threads safe sessions object.
|
9
|
+
# Example:
|
26
10
|
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
else
|
32
|
-
add_client if add_client?
|
33
|
-
end
|
34
|
-
@pool
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Run the block on the retrieved client. Good for ensure the same client
|
39
|
-
# is used for multiple requests. For HTTP requests make sure you request has
|
40
|
-
# keep-alive properly set for your client
|
41
|
-
# EX:
|
42
|
-
# @pool = HotTub.new(HotTub::ExconClient.new("https://some_web_site.com"))
|
43
|
-
# results = []
|
44
|
-
# @pool.run do |client|
|
45
|
-
# results.push (client.get(:query => {:foo => "bar"}))
|
46
|
-
# results.push (client.get(:query => {:bar => "foo"})) # reuse client
|
11
|
+
# sessions = HotTub::Session.new(:client_options => {:connect_timeout => 10})
|
12
|
+
#
|
13
|
+
# sessions.run("https://wwww.yahoo.com") do |conn|
|
14
|
+
# p conn.head.response_header.status
|
47
15
|
# end
|
48
16
|
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
block.call(client)
|
53
|
-
else
|
54
|
-
raise ArgumentError, 'Run requires a block.'
|
55
|
-
end
|
56
|
-
ensure
|
57
|
-
pool(client) if client
|
58
|
-
end
|
59
|
-
|
60
|
-
|
61
|
-
# Let pool instance respond to client methods. For HTTP request make sure you
|
62
|
-
# requests has keep-alive properly set for your client
|
63
|
-
# EX:
|
64
|
-
# @pool = HotTub.new(HotTub::ExconClient.new("https://some_web_site.com"))
|
65
|
-
# r1 = @pool.get(:query => {:foo => "bar"})
|
66
|
-
# r2 = @pool.get(:query => {:bar => "foo"}) # uses a different client
|
17
|
+
# sessions.run("https://wwww.google.com") do |conn|
|
18
|
+
# p conn.head.response_header.status
|
19
|
+
# end
|
67
20
|
#
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
if @pool.length < @options[:size]
|
95
|
-
@pool << client
|
96
|
-
else
|
97
|
-
HotTub.logger.info "Closed extra client for #{@client.class.name}."
|
98
|
-
client.close # Too hot in the hot tub...
|
99
|
-
end
|
21
|
+
# Other client classes
|
22
|
+
# If you have your own client class you can use sessions but your client class must initialize similar to
|
23
|
+
# EmHttpRequest, accepting a URI and options see: hot_tub/clients/em_http_request_client.rb
|
24
|
+
# Example Custom Client:
|
25
|
+
#
|
26
|
+
# sessions = HotTub::Session.new(:client_class => MyClient, :client_options => {:connect_timeout => 10})
|
27
|
+
#
|
28
|
+
# sessions.run("https://wwww.yahoo.com") do |conn|
|
29
|
+
# p conn.head.response_header.status # => create pool for "https://wwww.yahoo.com"
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# sessions.run("https://wwww.google.com") do |conn|
|
33
|
+
# p conn.head.response_header.status # => create separate pool for "https://wwww.google.com"
|
34
|
+
# end
|
35
|
+
def initialize(options={},&client_block)
|
36
|
+
raise ArgumentError, "HotTub::Sessions requre a block on initialization that accepts a single argument" unless block_given?
|
37
|
+
@client_block = client_block
|
38
|
+
@options = {
|
39
|
+
:size => 5,
|
40
|
+
:never_block => true,
|
41
|
+
:blocking_timeout => 10,
|
42
|
+
:client_class => nil, # EmHttpRequestClient
|
43
|
+
:client_options => {}
|
44
|
+
}.merge(options || {})
|
45
|
+
@sessions = Hash.new
|
46
|
+
@mutex = (HotTub.em? ? EM::Synchrony::Thread::Mutex.new : Mutex.new)
|
100
47
|
end
|
101
|
-
|
102
|
-
#
|
103
|
-
#
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
client = pool.shift
|
113
|
-
if client.nil? && (@options[:never_block])
|
114
|
-
HotTub.logger.info "Adding never_block client for #{@client.class.name}."
|
115
|
-
client = new_client
|
116
|
-
client.mark_temporary
|
48
|
+
|
49
|
+
# Synchronize access to our key hash
|
50
|
+
# expects a url string or URI
|
51
|
+
def sessions(url)
|
52
|
+
@mutex.synchronize do
|
53
|
+
if url.is_a?(String)
|
54
|
+
uri = URI(url)
|
55
|
+
elsif url.is_a?(URI)
|
56
|
+
uri = url
|
57
|
+
else
|
58
|
+
raise ArgumentError, "you must pass a string or a URI object"
|
117
59
|
end
|
60
|
+
@sessions["#{uri.scheme}-#{uri.host}"] ||= HotTub::Pool.new(@options) { @client_block.call(url) }
|
118
61
|
end
|
119
|
-
client.clean
|
120
|
-
client
|
121
62
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
63
|
+
|
64
|
+
# Hand off to pool.run
|
65
|
+
def run(url,&block)
|
66
|
+
pool = sessions(url)
|
67
|
+
pool.run(&block) if pool
|
127
68
|
end
|
128
69
|
end
|
129
|
-
end
|
70
|
+
end
|