hot_tub 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +3 -0
- data/Gemfile +0 -1
- data/README.md +78 -75
- data/hot_tub.gemspec +4 -0
- data/lib/hot_tub/known_clients.rb +47 -0
- data/lib/hot_tub/pool.rb +39 -69
- data/lib/hot_tub/session.rb +36 -21
- data/lib/hot_tub/version.rb +1 -1
- data/lib/hot_tub.rb +5 -1
- data/spec/helpers/moc_client.rb +33 -0
- data/spec/helpers/server.rb +38 -0
- data/spec/pool_spec.rb +75 -14
- data/spec/session_spec.rb +58 -31
- data/spec/spec_helper.rb +11 -36
- metadata +57 -4
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,23 @@
|
|
1
1
|
# HotTub [![Build Status](https://travis-ci.org/JoshMcKin/hot_tub.png?branch=master)](https://travis-ci.org/JoshMcKin/hot_tub)
|
2
|
-
A simple thread-safe connection
|
2
|
+
A simple thread-safe connection pool and sessions gem. Out-of-the-box support for [Excon](https://github.com/geemus/excon) and
|
3
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
|
5
|
-
|
4
|
+
There are a couple Ruby connection pooling libraries out there but HotTub differs from most with all its features.
|
5
|
+
|
6
|
+
## Features
|
7
|
+
|
8
|
+
### HotTub::Pool
|
9
|
+
* Thread safe
|
10
|
+
* Lazy clients/connections (created only when necessary)
|
11
|
+
* Can be used with any client library
|
12
|
+
* Support for cleaning dirty resources
|
13
|
+
* Set to expand pool under load that is eventually reaped back down to set size (never_block), can be disabled
|
14
|
+
* Attempts to close clients/connections at_exit
|
15
|
+
|
16
|
+
### HotTub::Sessions
|
17
|
+
* Thread safe
|
18
|
+
* The same api as HotTub::Pool
|
19
|
+
* Can be used with HotTub::Pool or any client library
|
20
|
+
* Attempts to close clients/connections at_exit
|
6
21
|
|
7
22
|
## Installation
|
8
23
|
|
@@ -10,105 +25,93 @@ HotTub is available through [Rubygems](https://rubygems.org/gems/hot_tub) and ca
|
|
10
25
|
|
11
26
|
$ gem install hot_tub
|
12
27
|
|
13
|
-
##
|
28
|
+
## Rails setup
|
14
29
|
|
15
|
-
|
30
|
+
Add hot_tub to your gemfile:
|
31
|
+
|
32
|
+
gem 'hot_tub'
|
16
33
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@@pool = HotTub::Pool.new({:size => 10}) { Excon.new("http://somewebservice.com") }
|
21
|
-
def self.fetch_results(url,query={})
|
22
|
-
@@pool.run |connection|
|
23
|
-
connection.get(:query => query).body
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
MyClass.fetch_results({:foo => "goo"}) # => "Some reponse"
|
34
|
+
Run bundle:
|
35
|
+
|
36
|
+
bundle install
|
28
37
|
|
29
|
-
|
38
|
+
Configure Logger by creating a hot_tub.rb initializer and adding the following:
|
39
|
+
|
40
|
+
HotTub.logger = Rails.logger
|
30
41
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@@pool = HotTub::Pool.new(:size => 12) { EM::HttpRequest.new("http://somewebservice.com") }
|
35
|
-
def async_post_results(query = {})
|
36
|
-
@@pool.run do |connection|
|
37
|
-
connection.aget(:query => results, :keepalive => true)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
42
|
+
# Usage
|
43
|
+
|
44
|
+
## HotTub::Pool
|
41
45
|
|
46
|
+
### EM-Http-Request
|
47
|
+
require 'hot_tub'
|
48
|
+
require 'em-synchrony'
|
49
|
+
require 'em-synchrony/em-http'
|
42
50
|
EM.synchrony do {
|
43
|
-
|
51
|
+
pool = HotTub::Pool.new(:size => 12) { EM::HttpRequest.new("http://somewebservice.com") }
|
52
|
+
pool.run { |clnt| clnt.aget(:query => results, :keepalive => true) }
|
44
53
|
EM.stop
|
45
54
|
}
|
46
55
|
|
47
56
|
### Other
|
48
|
-
|
49
|
-
lambdas, if they are not defined they are ignored.
|
57
|
+
You can use any library you want with HotTub::Pool, regardless of that clients thread-safety status.
|
58
|
+
Close and clean can be defined at initialization with lambdas, if they are not defined they are ignored.
|
50
59
|
|
60
|
+
url = "http://test12345.com"
|
61
|
+
pool = HotTub::Pool.new({:size => 10, :close => lambda {|clnt| clnt.close}}) { MyHttpLib.new }
|
62
|
+
pool.run { |clnt| clnt.get(@@url,query).body }
|
63
|
+
|
64
|
+
## HotTub::Sessions Usage
|
65
|
+
HotTub::Sessions are a synchronized hash of clients/pools and are implemented similar HotTub::Pool.
|
66
|
+
For example, Excon is thread safe but you set a single url at the client level so sessions
|
67
|
+
are handy if you need to access multiple urls but would prefer a single object.
|
68
|
+
|
69
|
+
require 'hot_tub'
|
51
70
|
require 'excon'
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
71
|
+
# Our client block must accept the url argument
|
72
|
+
sessions = HotTub::Sessions.new {|url| Excon.new(url) }
|
73
|
+
|
74
|
+
sessions.run("http://somewebservice.com") do |clnt|
|
75
|
+
puts clnt.get(:query => {:some => 'stuff'}).response_header.status
|
76
|
+
end
|
77
|
+
sessions.run("https://someotherwebservice.com") do |clnt|
|
78
|
+
puts clnt.get(:query => {:other => 'stuff'}).response_header.status
|
79
|
+
end
|
80
|
+
sessions.run("https://127.0.0.1:5252") do |clnt|
|
81
|
+
puts clnt.get.response_header.status
|
60
82
|
end
|
61
83
|
|
62
|
-
|
84
|
+
### HotTub::Sessions with HotTub::Pool
|
85
|
+
Suppose you have a client that is not thread safe, you can use HotTub::Pool with HotTub::Sessions to get what you need.
|
86
|
+
|
87
|
+
require 'hot_tub'
|
88
|
+
require "em-synchrony"
|
89
|
+
require "em-synchrony/em-http"
|
90
|
+
# Our client block must accept the url argument
|
63
91
|
|
64
|
-
|
65
|
-
|
66
|
-
can change paths the client domain cannot change. HotTub::Session allows you create a session object that initializes
|
67
|
-
seperate pools for your various domains based on URI. Options are passed to the pool when each pool is initialized.
|
92
|
+
EM.synchrony do {
|
93
|
+
sessions = HotTub::Sessions.new {|url| HotTub::Pool.new(:size => 12) { EM::HttpRequest.new(url, :inactivity_timeout => 0) }}
|
68
94
|
|
69
|
-
|
70
|
-
|
71
|
-
# Our client block must accept the url argument
|
72
|
-
@@sessons = HotTub::Sessions.new(:size => 10) {|url| { Excon.new(url) }
|
73
|
-
def async_post_results(query = {})
|
74
|
-
@@sessons.run("http://somewebservice.com") do |connection|
|
75
|
-
puts connection.run(: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
|
95
|
+
sessions.run("http://somewebservice.com") do |clnt|
|
96
|
+
puts clnt.get(:query => results).response_header.status
|
80
97
|
end
|
81
|
-
|
82
|
-
|
83
|
-
## Sessions without Pool
|
84
|
-
If you have a client that is thread safe but does not support sessions you can implement sessions similarly. Excon
|
85
|
-
is thread safe but you are using a single client, so you may loose preformance with multithreading.
|
86
|
-
|
87
|
-
require 'excon'
|
88
|
-
class MyClass
|
89
|
-
# Our client block must accept the url argument
|
90
|
-
@@sessons = HotTub::Sessions.new(:with_pool => false) {|url| Excon.new(url) }
|
91
|
-
def async_post_results(query = {})
|
92
|
-
@@sessons.run("http://somewebservice.com") do |connection|
|
93
|
-
puts connection.get(:query => results).response_header.status
|
94
|
-
end
|
95
|
-
@@sessons.run("https://someotherwebservice.com") do |connection|
|
96
|
-
puts connection.get(:query => results).response_header.status
|
97
|
-
end
|
98
|
+
sessions.run("https://someotherwebservice.com") do |clnt|
|
99
|
+
puts clnt.get(:query => results).response_header.status
|
98
100
|
end
|
99
|
-
|
101
|
+
EM.stop
|
102
|
+
}
|
100
103
|
|
101
104
|
## Related
|
102
105
|
|
103
106
|
* [EM-Http-Request](https://github.com/igrigorik/em-http-request)
|
104
107
|
* [EM-Synchrony](https://github.com/igrigorik/em-synchrony)
|
105
|
-
* [Excon](https://github.com/geemus/excon)
|
106
|
-
* [HTTPClient](https://github.com/nahi/httpclient) A thread safe http client that supports sessions all by itself.
|
108
|
+
* [Excon](https://github.com/geemus/excon) Thread safe with its own built in pool.
|
109
|
+
* [HTTPClient](https://github.com/nahi/httpclient) A thread safe http client that supports pools and sessions all by itself.
|
107
110
|
|
108
111
|
## Other Pooling Gem
|
109
112
|
|
110
113
|
* [ConnectionPool](https://github.com/mperham/connection_pool)
|
111
|
-
* [EM-Synchrony](https://github.com/igrigorik/em-synchrony) has a
|
114
|
+
* [EM-Synchrony](https://github.com/igrigorik/em-synchrony) has a pool feature
|
112
115
|
|
113
116
|
## Contributing to HotTub
|
114
117
|
|
data/hot_tub.gemspec
CHANGED
@@ -13,7 +13,11 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.description = %q{A simple thread-safe http connection pooling gem. Http client options include HTTPClient and EM-Http-Request}
|
14
14
|
|
15
15
|
s.rubyforge_project = "hot_tub"
|
16
|
+
|
16
17
|
s.add_development_dependency "rspec"
|
18
|
+
s.add_development_dependency "sinatra"
|
19
|
+
s.add_development_dependency "puma"
|
20
|
+
s.add_development_dependency "excon"
|
17
21
|
|
18
22
|
s.files = `git ls-files`.split("\n")
|
19
23
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module HotTub
|
2
|
+
module KnownClients
|
3
|
+
KNOWN_CLIENTS = {
|
4
|
+
"Excon::Connection" => {
|
5
|
+
:close => lambda { |clnt| clnt.reset }
|
6
|
+
},
|
7
|
+
'EventMachine::HttpConnection' => {
|
8
|
+
:close => lambda { |clnt|
|
9
|
+
if clnt.conn
|
10
|
+
clnt.conn.close_connection
|
11
|
+
clnt.instance_variable_set(:@deferred, true)
|
12
|
+
end
|
13
|
+
},
|
14
|
+
:clean => lambda { |clnt|
|
15
|
+
if clnt.conn && clnt.conn.error?
|
16
|
+
HotTub.logger.info "Sanitizing connection : #{EventMachine::report_connection_error_status(clnt.conn.instance_variable_get(:@signature))}"
|
17
|
+
clnt.conn.close_connection
|
18
|
+
clnt.instance_variable_set(:@deferred, true)
|
19
|
+
end
|
20
|
+
clnt
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
attr_accessor :options
|
25
|
+
# Attempts to clean the provided client, checking the options first for a clean block
|
26
|
+
# then checking the known clients
|
27
|
+
def clean_client(clnt)
|
28
|
+
return @options[:clean].call(clnt) if @options && @options[:clean] && @options[:clean].is_a?(Proc)
|
29
|
+
if settings = KNOWN_CLIENTS[clnt.class.name]
|
30
|
+
settings[:clean].call(clnt) if settings[:clean].is_a?(Proc)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Attempts to close the provided client, checking the options first for a close block
|
35
|
+
# then checking the known clients
|
36
|
+
def close_client(clnt)
|
37
|
+
return @options[:close].call(clnt) if @options && @options[:close] && @options[:close].is_a?(Proc)
|
38
|
+
if settings = KNOWN_CLIENTS[clnt.class.name]
|
39
|
+
begin
|
40
|
+
settings[:close].call(clnt) if settings[:close].is_a?(Proc)
|
41
|
+
rescue => e
|
42
|
+
HotTub.logger.error "There was an error close one of your #{self.class.name} connections: #{e}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/hot_tub/pool.rb
CHANGED
@@ -1,49 +1,25 @@
|
|
1
1
|
module HotTub
|
2
2
|
class Pool
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
:close => lambda { |clnt| clnt.reset }
|
7
|
-
},
|
8
|
-
'EventMachine::HttpConnection' => {
|
9
|
-
:close => lambda { |clnt|
|
10
|
-
if clnt.conn
|
11
|
-
clnt.conn.close_connection
|
12
|
-
clnt.instance_variable_set(:@deferred, true)
|
13
|
-
end
|
14
|
-
},
|
15
|
-
:clean => lambda { |clnt|
|
16
|
-
if clnt.conn && clnt.conn.error?
|
17
|
-
HotTub.logger.info "Sanitizing connection : #{EventMachine::report_connection_error_status(clnt.conn.instance_variable_get(:@signature))}"
|
18
|
-
clnt.conn.close_connection
|
19
|
-
clnt.instance_variable_set(:@deferred, true)
|
20
|
-
end
|
21
|
-
clnt
|
22
|
-
}
|
23
|
-
}
|
24
|
-
}
|
25
|
-
|
3
|
+
include HotTub::KnownClients
|
4
|
+
attr_reader :current_size, :fetching_client, :last_activity
|
5
|
+
|
26
6
|
# Thread-safe lazy connection pool
|
27
7
|
#
|
28
|
-
# == Example Excon
|
29
|
-
# pool = HotTub::Pool.new(:size => 25) { Excon.new('http://test.com') }
|
30
|
-
# pool.run {|clnt| clnt.get.body }
|
31
|
-
#
|
32
8
|
# == Example EM-Http-Request
|
33
9
|
# pool = HotTub::Pool.new { EM::HttpRequest.new("http://somewebservice.com") }
|
34
10
|
# pool.run {|clnt| clnt.get(:keepalive => true).body }
|
35
11
|
#
|
36
|
-
# HotTub::Pool defaults never_block to true, which means if run out of
|
12
|
+
# HotTub::Pool defaults never_block to true, which means if we run out of
|
37
13
|
# connections simply create a new client to continue operations.
|
38
|
-
# The pool
|
39
|
-
#
|
40
|
-
#
|
14
|
+
# The pool will grow and extra connections will be resued until activity dies down.
|
15
|
+
# If you would like to block and possibly throw an exception rather than temporarily
|
16
|
+
# grow the set :size, set :never_block to false; blocking_timeout defaults to 10 seconds.
|
41
17
|
#
|
42
18
|
# == Example without #never_block (will BlockingTimeout exception)
|
43
|
-
# pool = HotTub::Pool.new(:size => 1, :never_block => false, :blocking_timeout => 0.5) {
|
19
|
+
# pool = HotTub::Pool.new(:size => 1, :never_block => false, :blocking_timeout => 0.5) { EM::HttpRequest.new("http://somewebservice.com") }
|
44
20
|
#
|
45
21
|
# begin
|
46
|
-
# pool.run {|clnt| clnt.get.body }
|
22
|
+
# pool.run {|clnt| clnt.get(:keepalive => true).body }
|
47
23
|
# rescue HotTub::BlockingTimeout => e
|
48
24
|
# puts "Our pool ran out: {e}"
|
49
25
|
# end
|
@@ -61,8 +37,9 @@ module HotTub
|
|
61
37
|
}.merge(options)
|
62
38
|
@pool = []
|
63
39
|
@current_size = 0
|
64
|
-
@
|
65
|
-
@
|
40
|
+
@mutex = (HotTub.em_synchrony? ? EM::Synchrony::Thread::Mutex.new : Mutex.new)
|
41
|
+
@last_activity = Time.now
|
42
|
+
@fetching_client = false
|
66
43
|
end
|
67
44
|
|
68
45
|
# Hand off to client.run
|
@@ -80,13 +57,12 @@ module HotTub
|
|
80
57
|
# Calls close on all connections and reset the pools
|
81
58
|
def close_all
|
82
59
|
@mutex.synchronize do
|
83
|
-
while clnt = @
|
60
|
+
while clnt = @pool.pop
|
84
61
|
begin
|
85
62
|
close_client(clnt)
|
86
63
|
rescue => e
|
87
64
|
HotTub.logger.error "There was an error close one of your HotTub::Pool connections: #{e}"
|
88
65
|
end
|
89
|
-
@pool.delete(clnt)
|
90
66
|
end
|
91
67
|
@current_size = 0
|
92
68
|
end
|
@@ -107,26 +83,8 @@ module HotTub
|
|
107
83
|
clnt
|
108
84
|
end
|
109
85
|
|
110
|
-
# Attempts to clean the provided client, checking the options first for a clean block
|
111
|
-
# then checking the known clients
|
112
|
-
def clean_client(clnt)
|
113
|
-
return @options[:clean].call(clnt) if @options[:clean] if @options[:clean].is_a?(Proc)
|
114
|
-
if settings = KNOWN_CLIENTS[clnt.class.name]
|
115
|
-
settings[:clean].call(clnt) if settings[:clean].is_a?(Proc)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# Attempts to close the provided client, checking the options first for a close block
|
120
|
-
# then checking the known clients
|
121
|
-
def close_client(clnt)
|
122
|
-
return @options[:close].call(clnt) if @options[:close] if @options[:close].is_a?(Proc)
|
123
|
-
if settings = KNOWN_CLIENTS[clnt.class.name]
|
124
|
-
settings[:close].call(clnt) if settings[:close].is_a?(Proc)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
86
|
def raise_alarm
|
129
|
-
message = "Could not fetch a free client in time. Consider increasing your pool size
|
87
|
+
message = "Could not fetch a free client in time. Consider increasing your pool size."
|
130
88
|
HotTub.logger.error message
|
131
89
|
raise BlockingTimeout, message
|
132
90
|
end
|
@@ -134,47 +92,59 @@ module HotTub
|
|
134
92
|
# Safely add client back to pool
|
135
93
|
def push(clnt)
|
136
94
|
@mutex.synchronize do
|
137
|
-
|
138
|
-
@pool << clnt
|
139
|
-
else
|
140
|
-
@clients.delete(clnt)
|
141
|
-
close_client(clnt)
|
142
|
-
end
|
95
|
+
@pool << clnt
|
143
96
|
end
|
144
97
|
nil # make sure never return the pool
|
145
98
|
end
|
146
99
|
|
147
100
|
# Safely pull client from pool, adding if allowed
|
148
101
|
def pop
|
102
|
+
@fetching_client = true # kill reap_pool
|
149
103
|
@mutex.synchronize do
|
150
104
|
add if add?
|
151
|
-
clnt = @pool.pop
|
105
|
+
clnt = @pool.pop # get warm connection
|
152
106
|
if (clnt.nil? && @options[:never_block])
|
153
|
-
|
154
|
-
clnt =
|
107
|
+
add
|
108
|
+
clnt = @pool.pop
|
155
109
|
end
|
110
|
+
@fetching_client = false
|
156
111
|
clnt
|
157
112
|
end
|
113
|
+
ensure
|
114
|
+
reap_pool if reap_pool?
|
158
115
|
end
|
159
116
|
|
160
117
|
# create a new client from base client
|
161
118
|
def new_client
|
162
|
-
|
163
|
-
@clients << clnt
|
164
|
-
clnt
|
119
|
+
@client_block.call
|
165
120
|
end
|
166
121
|
|
167
122
|
# Only want to add a client if the pool is empty in keeping with
|
168
123
|
# a lazy model.
|
169
124
|
def add?
|
170
|
-
(@pool.length == 0 && @
|
125
|
+
(@pool.length == 0 && (@options[:size] > @current_size))
|
171
126
|
end
|
172
127
|
|
173
128
|
def add
|
174
129
|
HotTub.logger.info "Adding HotTub client: #{@client.class.name} to pool"
|
130
|
+
@last_activity = Time.now
|
175
131
|
@current_size += 1
|
176
132
|
@pool << new_client
|
177
133
|
end
|
134
|
+
|
135
|
+
def reap_pool?
|
136
|
+
(!@fetching_client && (@current_size > @options[:size]) && ((@last_activity + (600)) < Time.now))
|
137
|
+
end
|
138
|
+
|
139
|
+
# Remove extra connections from front of pool
|
140
|
+
def reap_pool
|
141
|
+
@mutex.synchronize do
|
142
|
+
if reap_pool? && clnt = @pool.shift
|
143
|
+
@current_size -= 1
|
144
|
+
close_client(clnt)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
178
148
|
end
|
179
149
|
class BlockingTimeout < StandardError;end
|
180
150
|
end
|
data/lib/hot_tub/session.rb
CHANGED
@@ -1,42 +1,42 @@
|
|
1
1
|
require 'uri'
|
2
2
|
module HotTub
|
3
3
|
class Session
|
4
|
-
|
4
|
+
include HotTub::KnownClients
|
5
5
|
# A HotTub::Session is a synchronized hash used to separate pools/clients by their domain.
|
6
6
|
# Excon and 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.
|
7
|
+
# manage multiple pools like when a process need to connect to various AWS resources. You can use any client
|
8
|
+
# you choose, but make sure you client is threadsafe.
|
8
9
|
# Example:
|
9
10
|
#
|
10
|
-
# sessions = HotTub::Session.new
|
11
|
+
# sessions = HotTub::Session.new { |url| Excon.new(url) }
|
11
12
|
#
|
12
13
|
# sessions.run("http://wwww.yahoo.com") do |conn|
|
13
|
-
# p conn.head.
|
14
|
+
# p conn.head.status
|
14
15
|
# end
|
15
16
|
#
|
16
17
|
# sessions.run("https://wwww.google.com") do |conn|
|
17
|
-
# p conn.head.
|
18
|
+
# p conn.head.status
|
18
19
|
# end
|
19
20
|
#
|
20
|
-
#
|
21
|
-
# If you have your own client class you can use sessions but your client class must initialize similar to
|
22
|
-
# EmHttpRequest, accepting a URI and options see: hot_tub/clients/em_http_request_client.rb
|
23
|
-
# Example Custom Client:
|
21
|
+
# Example with Pool:
|
24
22
|
#
|
25
|
-
# sessions = HotTub::
|
23
|
+
# sessions = HotTub::Pool.new(:size => 12) { EM::HttpRequest.new("http://somewebservice.com") }
|
26
24
|
#
|
27
|
-
# sessions.run("
|
28
|
-
# p conn.head.response_header.status
|
25
|
+
# sessions.run("http://wwww.yahoo.com") do |conn|
|
26
|
+
# p conn.head.response_header.status
|
29
27
|
# end
|
30
28
|
#
|
31
29
|
# sessions.run("https://wwww.google.com") do |conn|
|
32
|
-
# p conn.head.response_header.status
|
30
|
+
# p conn.head.response_header.status
|
33
31
|
# end
|
32
|
+
#
|
34
33
|
def initialize(options={},&client_block)
|
35
34
|
raise ArgumentError, "HotTub::Sessions requre a block on initialization that accepts a single argument" unless block_given?
|
36
|
-
|
35
|
+
at_exit { close_all } # close connections at exit
|
36
|
+
@options = options || {}
|
37
37
|
@client_block = client_block
|
38
38
|
@sessions = Hash.new
|
39
|
-
@mutex = (HotTub.
|
39
|
+
@mutex = (HotTub.em_synchrony? ? EM::Synchrony::Thread::Mutex.new : Mutex.new)
|
40
40
|
end
|
41
41
|
|
42
42
|
# Synchronizes initialization of our sessions
|
@@ -45,20 +45,35 @@ module HotTub
|
|
45
45
|
key = to_key(url)
|
46
46
|
return @sessions[key] if @sessions[key]
|
47
47
|
@mutex.synchronize do
|
48
|
-
if @
|
49
|
-
|
50
|
-
else
|
51
|
-
@sessions[key] ||= @client_block.call(url)
|
52
|
-
end
|
48
|
+
@sessions[key] = @client_block.call(url) if @sessions[key].nil?
|
49
|
+
@sessions[key]
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
56
53
|
def run(url,&block)
|
57
54
|
session = sessions(url)
|
58
|
-
return session.run(&block) if
|
55
|
+
return session.run(&block) if session.is_a?(HotTub::Pool)
|
59
56
|
block.call(sessions(url))
|
60
57
|
end
|
61
58
|
|
59
|
+
# Calls close on all pools/clients in sessions
|
60
|
+
def close_all
|
61
|
+
@sessions.each do |key,clnt|
|
62
|
+
if clnt.is_a?(HotTub::Pool)
|
63
|
+
clnt.close_all
|
64
|
+
else
|
65
|
+
begin
|
66
|
+
close_client(clnt)
|
67
|
+
rescue => e
|
68
|
+
HotTub.logger.error "There was an error close one of your HotTub::Session clients: #{e}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@mutex.synchronize do
|
72
|
+
@sessions[key] = nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
62
77
|
private
|
63
78
|
|
64
79
|
def to_key(url)
|
data/lib/hot_tub/version.rb
CHANGED
data/lib/hot_tub.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'thread'
|
2
|
-
require 'timeout'
|
3
2
|
require 'logger'
|
4
3
|
require "hot_tub/version"
|
4
|
+
require "hot_tub/known_clients"
|
5
5
|
require "hot_tub/pool"
|
6
6
|
require "hot_tub/session"
|
7
7
|
|
@@ -19,6 +19,10 @@ module HotTub
|
|
19
19
|
(defined?(EM) && EM::reactor_running?)
|
20
20
|
end
|
21
21
|
|
22
|
+
def self.em_synchrony?
|
23
|
+
(self.em? && defined?(EM::Synchrony))
|
24
|
+
end
|
25
|
+
|
22
26
|
def self.jruby?
|
23
27
|
(defined?(JRUBY_VERSION))
|
24
28
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class MocClient
|
2
|
+
def initialize(url=nil,options={})
|
3
|
+
@close = false
|
4
|
+
@clean = false
|
5
|
+
end
|
6
|
+
|
7
|
+
# Perform an IO
|
8
|
+
def get
|
9
|
+
return `sleep #{self.class.sleep_time}; echo "that was slow IO"`
|
10
|
+
end
|
11
|
+
|
12
|
+
def close
|
13
|
+
@close = true
|
14
|
+
end
|
15
|
+
|
16
|
+
def closed?
|
17
|
+
@close == true
|
18
|
+
end
|
19
|
+
|
20
|
+
def clean
|
21
|
+
@clean = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def cleaned?
|
25
|
+
@clean == true
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def sleep_time
|
30
|
+
0.2
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'puma'
|
3
|
+
module HotTub
|
4
|
+
class Server < Sinatra::Base
|
5
|
+
|
6
|
+
def self.run
|
7
|
+
@events = Puma::Events.new STDOUT, STDERR
|
8
|
+
@server = Puma::Server.new HotTub::Server.new, @events
|
9
|
+
@server.min_threads = 10
|
10
|
+
@server.max_threads = 100
|
11
|
+
@server.add_tcp_listener '127.0.0.1', 9595
|
12
|
+
@server.run
|
13
|
+
end
|
14
|
+
|
15
|
+
set :server, 'puma'
|
16
|
+
set :port, 9595
|
17
|
+
|
18
|
+
get '/data/:amount' do |amount|
|
19
|
+
(('x' * amount.to_i ) << Random.new.rand(0..999999).to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.teardown
|
23
|
+
@server.stop(true) if @server
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.size
|
27
|
+
10_000
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.path
|
31
|
+
'/data/' << size.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.url
|
35
|
+
'http://127.0.0.1:9595' << path
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/pool_spec.rb
CHANGED
@@ -87,10 +87,8 @@ describe HotTub::Pool do
|
|
87
87
|
end
|
88
88
|
it "should reset pool" do
|
89
89
|
@pool.current_size.should eql(5)
|
90
|
-
@pool.instance_variable_get(:@clients).length.should eql(5)
|
91
90
|
@pool.instance_variable_get(:@pool).length.should eql(5)
|
92
91
|
@pool.close_all
|
93
|
-
@pool.instance_variable_get(:@clients).length.should eql(0)
|
94
92
|
@pool.instance_variable_get(:@pool).length.should eql(0)
|
95
93
|
@pool.current_size.should eql(0)
|
96
94
|
end
|
@@ -136,9 +134,57 @@ describe HotTub::Pool do
|
|
136
134
|
end
|
137
135
|
end
|
138
136
|
|
137
|
+
context ':never_block' do
|
138
|
+
context 'is true' do
|
139
|
+
it "should add connections to pool as necessary" do
|
140
|
+
pool = HotTub::Pool.new({:size => 1}) { MocClient.new }
|
141
|
+
threads = []
|
142
|
+
5.times.each do
|
143
|
+
threads << Thread.new do
|
144
|
+
pool.run{|connection| connection.get }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
sleep(0.01)
|
148
|
+
threads.each do |t|
|
149
|
+
t.join
|
150
|
+
end
|
151
|
+
(pool.current_size > 1).should be_true
|
152
|
+
end
|
153
|
+
end
|
154
|
+
context 'is false' do
|
155
|
+
it "should not add connections to pool beyond specified size" do
|
156
|
+
pool = HotTub::Pool.new({:size => 1, :never_block => false, :blocking_timeout => 10}) { MocClient.new }
|
157
|
+
threads = []
|
158
|
+
3.times.each do
|
159
|
+
threads << Thread.new do
|
160
|
+
pool.run{|connection| connection.get }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
sleep(0.01)
|
164
|
+
threads.each do |t|
|
165
|
+
t.join
|
166
|
+
end
|
167
|
+
pool.current_size.should eql(1)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#reap_pool' do
|
173
|
+
context 'current_size is greater than :size' do
|
174
|
+
it "should remove a connection from the pool" do
|
175
|
+
pool = HotTub::Pool.new({:size => 1}) { MocClient.new }
|
176
|
+
pool.instance_variable_set(:@last_activity,(Time.now - 601))
|
177
|
+
pool.instance_variable_set(:@pool, [MocClient.new,MocClient.new])
|
178
|
+
pool.instance_variable_set(:@current_size,2)
|
179
|
+
pool.send(:reap_pool)
|
180
|
+
pool.current_size.should eql(1)
|
181
|
+
pool.instance_variable_get(:@pool).length.should eql(1)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
139
186
|
context 'thread safety' do
|
140
187
|
it "should work" do
|
141
|
-
url = "https://www.google.com/"
|
142
188
|
pool = HotTub::Pool.new({:size => 10}) { MocClient.new }
|
143
189
|
failed = false
|
144
190
|
lambda {
|
@@ -153,7 +199,7 @@ describe HotTub::Pool do
|
|
153
199
|
t.join
|
154
200
|
end
|
155
201
|
}.should_not raise_error
|
156
|
-
pool.instance_variable_get(:@pool).length
|
202
|
+
(pool.instance_variable_get(:@pool).length >= 10).should be_true # make sure work got done
|
157
203
|
end
|
158
204
|
end
|
159
205
|
|
@@ -169,9 +215,9 @@ describe HotTub::Pool do
|
|
169
215
|
end
|
170
216
|
end
|
171
217
|
|
172
|
-
context 'Excon' do
|
218
|
+
context 'Excon' do # Excon has its own pool, but just need to test with a real non-EM library
|
173
219
|
before(:each) do
|
174
|
-
@pool = HotTub::Pool.new(:size => 10) { Excon.new(
|
220
|
+
@pool = HotTub::Pool.new(:size => 10) { Excon.new(HotTub::Server.url)}
|
175
221
|
end
|
176
222
|
it "should work" do
|
177
223
|
result = nil
|
@@ -180,11 +226,23 @@ describe HotTub::Pool do
|
|
180
226
|
end
|
181
227
|
context 'threads' do
|
182
228
|
it "should work" do
|
183
|
-
url = "https://www.google.com/"
|
184
229
|
failed = false
|
185
230
|
threads = []
|
186
231
|
lambda {
|
187
|
-
|
232
|
+
15.times.each do
|
233
|
+
threads << Thread.new do
|
234
|
+
@pool.run{|connection| Thread.current[:status] = connection.head.status }
|
235
|
+
end
|
236
|
+
end
|
237
|
+
sleep(0.01)
|
238
|
+
threads.each do |t|
|
239
|
+
t.join
|
240
|
+
end
|
241
|
+
}.should_not raise_error
|
242
|
+
# Reuse and run reaper
|
243
|
+
@pool.instance_variable_set(:@last_activity,(Time.now - 601))
|
244
|
+
lambda {
|
245
|
+
10.times.each do
|
188
246
|
threads << Thread.new do
|
189
247
|
@pool.run{|connection| Thread.current[:status] = connection.head.status }
|
190
248
|
end
|
@@ -194,9 +252,9 @@ describe HotTub::Pool do
|
|
194
252
|
t.join
|
195
253
|
end
|
196
254
|
}.should_not raise_error
|
197
|
-
@pool.instance_variable_get(:@pool).length
|
255
|
+
(@pool.instance_variable_get(:@pool).length == 10).should be_true # make sure work got done
|
198
256
|
results = threads.collect{ |t| t[:status]}
|
199
|
-
results.length.should eql(
|
257
|
+
results.length.should eql(25) # make sure all threads are present
|
200
258
|
results.uniq.should eql([200]) # make sure all returned status 200
|
201
259
|
end
|
202
260
|
end
|
@@ -205,7 +263,7 @@ describe HotTub::Pool do
|
|
205
263
|
unless HotTub.jruby?
|
206
264
|
context 'EM:HTTPRequest' do
|
207
265
|
before(:each) do
|
208
|
-
@url =
|
266
|
+
@url = HotTub::Server.url
|
209
267
|
end
|
210
268
|
|
211
269
|
it "should work" do
|
@@ -216,6 +274,7 @@ describe HotTub::Pool do
|
|
216
274
|
c.run { |conn| status << conn.ahead(:keepalive => true).response_header.status}
|
217
275
|
c.run { |conn| status << conn.head(:keepalive => true).response_header.status}
|
218
276
|
status.should eql([200,0,200])
|
277
|
+
c.close_all
|
219
278
|
EM.stop
|
220
279
|
end
|
221
280
|
end
|
@@ -223,14 +282,15 @@ describe HotTub::Pool do
|
|
223
282
|
context 'fibers' do
|
224
283
|
it "should work" do
|
225
284
|
EM.synchrony do
|
226
|
-
url = "https://www.google.com/"
|
227
285
|
pool = HotTub::Pool.new({:size => 5}) {EM::HttpRequest.new(@url)}
|
228
286
|
failed = false
|
229
287
|
fibers = []
|
230
288
|
lambda {
|
231
289
|
10.times.each do
|
232
290
|
fibers << Fiber.new do
|
233
|
-
pool.run{|connection|
|
291
|
+
pool.run{|connection|
|
292
|
+
s = connection.head(:keepalive => true).response_header.status
|
293
|
+
failed = true unless s == 200}
|
234
294
|
end
|
235
295
|
end
|
236
296
|
fibers.each do |f|
|
@@ -248,8 +308,9 @@ describe HotTub::Pool do
|
|
248
308
|
end
|
249
309
|
end
|
250
310
|
}.should_not raise_error
|
251
|
-
pool.instance_variable_get(:@pool).length
|
311
|
+
(pool.instance_variable_get(:@pool).length >= 5).should be_true #make sure work got done
|
252
312
|
failed.should be_false # Make sure our requests worked
|
313
|
+
pool.close_all
|
253
314
|
EM.stop
|
254
315
|
end
|
255
316
|
end
|
data/spec/session_spec.rb
CHANGED
@@ -2,6 +2,10 @@ require 'spec_helper'
|
|
2
2
|
require 'hot_tub/session'
|
3
3
|
require 'uri'
|
4
4
|
require 'time'
|
5
|
+
unless HotTub.jruby?
|
6
|
+
require "em-synchrony"
|
7
|
+
require "em-synchrony/em-http"
|
8
|
+
end
|
5
9
|
describe HotTub::Session do
|
6
10
|
|
7
11
|
context 'initialized without a block' do
|
@@ -16,14 +20,6 @@ describe HotTub::Session do
|
|
16
20
|
@sessions = HotTub::Session.new { |url| MocClient.new(url) }
|
17
21
|
end
|
18
22
|
|
19
|
-
context 'default options' do
|
20
|
-
describe ':with_pool' do
|
21
|
-
it "with_pool should be true" do
|
22
|
-
@sessions.instance_variable_get(:@options)[:with_pool].should be_true
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
23
|
describe '#to_url' do
|
28
24
|
context "passed URL string" do
|
29
25
|
it "should return key with URI scheme-domain" do
|
@@ -48,32 +44,23 @@ describe HotTub::Session do
|
|
48
44
|
end
|
49
45
|
|
50
46
|
describe '#sessions' do
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@sessions.sessions(@url)
|
55
|
-
sessions = @sessions.instance_variable_get(:@sessions)
|
56
|
-
sessions.length.should eql(1)
|
57
|
-
sessions.first[1].should be_a(HotTub::Pool)
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should pass options to pool" do
|
61
|
-
with_pool_options = HotTub::Session.new(:size => 13, :never_block => false) { |url| MocClient.new(url) }
|
47
|
+
context 'HotTub::Pool as client' do
|
48
|
+
it "should add a new client for the url" do
|
49
|
+
with_pool_options = HotTub::Session.new { |url| HotTub::Pool.new(:size => 13) { MocClient.new(url) } }
|
62
50
|
with_pool_options.sessions(@url)
|
63
51
|
sessions = with_pool_options.instance_variable_get(:@sessions)
|
64
|
-
|
65
|
-
|
66
|
-
pool_options[:never_block].should be_false
|
52
|
+
sessions.length.should eql(1)
|
53
|
+
sessions.first[1].should be_a(HotTub::Pool)
|
67
54
|
end
|
68
55
|
end
|
69
56
|
|
70
|
-
context '
|
57
|
+
context 'other clients' do
|
71
58
|
it "should add a new client for the url" do
|
72
|
-
no_pool = HotTub::Session.new
|
59
|
+
no_pool = HotTub::Session.new { |url| Excon.new(url) }
|
73
60
|
no_pool.sessions(@url)
|
74
61
|
sessions = no_pool.instance_variable_get(:@sessions)
|
75
62
|
sessions.length.should eql(1)
|
76
|
-
sessions.first[1].should be_a(
|
63
|
+
sessions.first[1].should be_a(Excon::Connection)
|
77
64
|
end
|
78
65
|
end
|
79
66
|
|
@@ -81,14 +68,14 @@ describe HotTub::Session do
|
|
81
68
|
it "should set key with URI scheme-domain" do
|
82
69
|
@sessions.sessions(@url)
|
83
70
|
sessions = @sessions.instance_variable_get(:@sessions)
|
84
|
-
sessions["#{@uri.scheme}-#{@uri.host}"].should be_a(
|
71
|
+
sessions["#{@uri.scheme}-#{@uri.host}"].should be_a(MocClient)
|
85
72
|
end
|
86
73
|
end
|
87
74
|
context "passed URI" do
|
88
75
|
it "should set key with URI scheme-domain" do
|
89
76
|
@sessions.sessions(@uri)
|
90
77
|
sessions = @sessions.instance_variable_get(:@sessions)
|
91
|
-
sessions["#{@uri.scheme}-#{@uri.host}"].should be_a(
|
78
|
+
sessions["#{@uri.scheme}-#{@uri.host}"].should be_a(MocClient)
|
92
79
|
end
|
93
80
|
end
|
94
81
|
end
|
@@ -105,11 +92,11 @@ describe HotTub::Session do
|
|
105
92
|
end
|
106
93
|
end
|
107
94
|
|
108
|
-
context '
|
95
|
+
context 'threads' do
|
109
96
|
it "should work" do
|
110
|
-
url =
|
111
|
-
url2 = "http://www.
|
112
|
-
session = HotTub::Session.new { |url|
|
97
|
+
url = HotTub::Server.url
|
98
|
+
url2 = "http://www.yahoo.com/"
|
99
|
+
session = HotTub::Session.new { |url| Excon.new(url)}
|
113
100
|
failed = false
|
114
101
|
start_time = Time.now
|
115
102
|
stop_time = nil
|
@@ -136,5 +123,45 @@ describe HotTub::Session do
|
|
136
123
|
session.instance_variable_get(:@sessions).keys.length.should eql(2) # make sure sessions were created
|
137
124
|
end
|
138
125
|
end
|
126
|
+
|
127
|
+
unless HotTub.jruby?
|
128
|
+
context 'fibers' do
|
129
|
+
it "should work" do
|
130
|
+
EM.synchrony do
|
131
|
+
sessions = HotTub::Session.new {|url| HotTub::Pool.new({:size => 5}) {EM::HttpRequest.new(url)}}
|
132
|
+
failed = false
|
133
|
+
fibers = []
|
134
|
+
lambda {
|
135
|
+
10.times.each do
|
136
|
+
fibers << Fiber.new do
|
137
|
+
sessions.run(@url) {|connection|
|
138
|
+
s = connection.head(:keepalive => true).response_header.status
|
139
|
+
failed = true unless s == 200}
|
140
|
+
end
|
141
|
+
end
|
142
|
+
fibers.each do |f|
|
143
|
+
f.resume
|
144
|
+
end
|
145
|
+
loop do
|
146
|
+
done = true
|
147
|
+
fibers.each do |f|
|
148
|
+
done = false if f.alive?
|
149
|
+
end
|
150
|
+
if done
|
151
|
+
break
|
152
|
+
else
|
153
|
+
EM::Synchrony.sleep(0.01)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
}.should_not raise_error
|
157
|
+
sessions.instance_variable_get(:@sessions).keys.length.should eql(1)
|
158
|
+
(sessions.sessions(@url).instance_variable_get(:@pool).length >= 5).should be_true #make sure work got done
|
159
|
+
failed.should be_false # Make sure our requests worked
|
160
|
+
sessions.close_all
|
161
|
+
EM.stop
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
139
166
|
end
|
140
167
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,45 +3,20 @@ require 'rspec'
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'logger'
|
5
5
|
require 'excon'
|
6
|
+
require 'helpers/moc_client'
|
7
|
+
require 'helpers/server'
|
8
|
+
require 'net/https'
|
9
|
+
|
6
10
|
# Requires supporting files with custom matchers and macros, etc,
|
7
11
|
# in ./support/ and its subdirectories.
|
8
12
|
#Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
13
|
HotTub.logger.level = Logger::ERROR
|
10
14
|
|
11
15
|
RSpec.configure do |config|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
# Perform an IO
|
22
|
-
def get
|
23
|
-
return `sleep #{self.class.sleep_time}; echo "that was slow IO"`
|
24
|
-
end
|
25
|
-
|
26
|
-
def close
|
27
|
-
@close = true
|
28
|
-
end
|
29
|
-
|
30
|
-
def closed?
|
31
|
-
@close == true
|
32
|
-
end
|
33
|
-
|
34
|
-
def clean
|
35
|
-
@clean = true
|
36
|
-
end
|
37
|
-
|
38
|
-
def cleaned?
|
39
|
-
@clean == true
|
40
|
-
end
|
41
|
-
|
42
|
-
class << self
|
43
|
-
def sleep_time
|
44
|
-
0.5
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
16
|
+
config.before(:suite) do
|
17
|
+
HotTub::Server.run
|
18
|
+
end
|
19
|
+
config.after(:suite) do
|
20
|
+
HotTub::Server.teardown
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hot_tub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -27,6 +27,54 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sinatra
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: puma
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: excon
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
30
78
|
description: A simple thread-safe http connection pooling gem. Http client options
|
31
79
|
include HTTPClient and EM-Http-Request
|
32
80
|
email:
|
@@ -44,9 +92,12 @@ files:
|
|
44
92
|
- Rakefile
|
45
93
|
- hot_tub.gemspec
|
46
94
|
- lib/hot_tub.rb
|
95
|
+
- lib/hot_tub/known_clients.rb
|
47
96
|
- lib/hot_tub/pool.rb
|
48
97
|
- lib/hot_tub/session.rb
|
49
98
|
- lib/hot_tub/version.rb
|
99
|
+
- spec/helpers/moc_client.rb
|
100
|
+
- spec/helpers/server.rb
|
50
101
|
- spec/pool_spec.rb
|
51
102
|
- spec/session_spec.rb
|
52
103
|
- spec/spec_helper.rb
|
@@ -65,7 +116,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
116
|
version: '0'
|
66
117
|
segments:
|
67
118
|
- 0
|
68
|
-
hash:
|
119
|
+
hash: -1949508601036359576
|
69
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
121
|
none: false
|
71
122
|
requirements:
|
@@ -74,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
125
|
version: '0'
|
75
126
|
segments:
|
76
127
|
- 0
|
77
|
-
hash:
|
128
|
+
hash: -1949508601036359576
|
78
129
|
requirements: []
|
79
130
|
rubyforge_project: hot_tub
|
80
131
|
rubygems_version: 1.8.25
|
@@ -82,6 +133,8 @@ signing_key:
|
|
82
133
|
specification_version: 3
|
83
134
|
summary: A simple thread-safe http connection pooling gem.
|
84
135
|
test_files:
|
136
|
+
- spec/helpers/moc_client.rb
|
137
|
+
- spec/helpers/server.rb
|
85
138
|
- spec/pool_spec.rb
|
86
139
|
- spec/session_spec.rb
|
87
140
|
- spec/spec_helper.rb
|