kjess 1.0.0 → 1.1.0
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/CONTRIBUTING.md +2 -1
- data/HISTORY.rdoc +11 -0
- data/Manifest.txt +2 -0
- data/Rakefile +27 -14
- data/lib/kjess.rb +1 -1
- data/lib/kjess/client.rb +34 -13
- data/lib/kjess/connection.rb +111 -49
- data/lib/kjess/protocol.rb +3 -2
- data/lib/kjess/request/status.rb +8 -1
- data/lib/kjess/response.rb +1 -0
- data/lib/kjess/response/status.rb +19 -0
- data/lib/kjess/socket.rb +291 -0
- data/spec/client_spec.rb +94 -5
- data/spec/kestrel_server.rb +9 -6
- data/spec/response/client_error_spec.rb +9 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/utils.rb +20 -0
- data/tasks/kestrel.rake +2 -2
- metadata +89 -85
data/CONTRIBUTING.md
CHANGED
@@ -35,7 +35,8 @@ easiest way to contribute.
|
|
35
35
|
|
36
36
|
# Contributors
|
37
37
|
|
38
|
-
* Jeremy Hinegardner
|
38
|
+
* Jeremy Hinegardner <https://github.com/copiousfreetime>
|
39
|
+
* Eric Lindvall <https://github.com/eric>
|
39
40
|
|
40
41
|
[GitHub Account]: https://github.com/signup/free "GitHub Signup"
|
41
42
|
[GitHub Issues]: https://github.com/copiousfreetime/kjess/issues "KJess Issues"
|
data/HISTORY.rdoc
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
= KJess Changlog
|
2
|
+
|
3
|
+
== Version 1.1.0 - 2013-01-08
|
4
|
+
|
5
|
+
* Allow the setting of the kestrel server processes ports for testing (eric)
|
6
|
+
* Update development dependencies, new version and missing gems
|
7
|
+
* Re-wrap all network errors with KJess Errors (eric)
|
8
|
+
* Ensure clients are not shared across fork() (eric)
|
9
|
+
* Enforce boolean return value from KJess::Client#set (eric)
|
10
|
+
* Added socket timeout feature (eric)
|
11
|
+
* Added support for tcp keepalive
|
12
|
+
|
2
13
|
== Version 1.0.0 - 2012-10-31
|
3
14
|
|
4
15
|
* Initial Release - Yeah!
|
data/Manifest.txt
CHANGED
@@ -36,10 +36,12 @@ lib/kjess/response/not_stored.rb
|
|
36
36
|
lib/kjess/response/reloaded_config.rb
|
37
37
|
lib/kjess/response/server_error.rb
|
38
38
|
lib/kjess/response/stats.rb
|
39
|
+
lib/kjess/response/status.rb
|
39
40
|
lib/kjess/response/stored.rb
|
40
41
|
lib/kjess/response/unknown.rb
|
41
42
|
lib/kjess/response/value.rb
|
42
43
|
lib/kjess/response/version.rb
|
44
|
+
lib/kjess/socket.rb
|
43
45
|
lib/kjess/stats_cache.rb
|
44
46
|
spec/client_spec.rb
|
45
47
|
spec/kestrel_server.rb
|
data/Rakefile
CHANGED
@@ -19,13 +19,7 @@ namespace :develop do
|
|
19
19
|
require 'rubygems/dependency_installer'
|
20
20
|
installer = Gem::DependencyInstaller.new
|
21
21
|
|
22
|
-
|
23
|
-
# specify ruby version specific dependencies
|
24
|
-
if RUBY_VERSION < "1.9.2"
|
25
|
-
Util.platform_gemspec.add_development_dependency( 'rcov', '~> 0.9.11' )
|
26
|
-
else
|
27
|
-
Util.platform_gemspec.add_development_dependency( 'simplecov', '~> 0.6.4' )
|
28
|
-
end
|
22
|
+
Util.set_coverage_gem
|
29
23
|
|
30
24
|
puts "Installing gem depedencies needed for development"
|
31
25
|
Util.platform_gemspec.dependencies.each do |dep|
|
@@ -194,14 +188,13 @@ This.gemspec['ruby'] = Gem::Specification.new do |spec|
|
|
194
188
|
"--markup", "tomdoc" ]
|
195
189
|
|
196
190
|
# The Runtime Dependencies
|
197
|
-
# FIXME
|
198
|
-
# spec.add_dependency( 'map', '~> 6.2.0')
|
199
191
|
|
200
192
|
# The Development Dependencies
|
201
|
-
spec.add_development_dependency( 'rake' , '~> 0.
|
202
|
-
spec.add_development_dependency( 'minitest' , '~>
|
203
|
-
spec.add_development_dependency( 'rdoc' , '~> 3.12'
|
204
|
-
spec.add_development_dependency( 'zip' ,
|
193
|
+
spec.add_development_dependency( 'rake' , '~> 10.0.3')
|
194
|
+
spec.add_development_dependency( 'minitest' , '~> 4.4.0' )
|
195
|
+
spec.add_development_dependency( 'rdoc' , '~> 3.12' )
|
196
|
+
spec.add_development_dependency( 'zip' , '~> 2.0.2' )
|
197
|
+
spec.add_development_dependency( 'json' , '~> 1.7.6' )
|
205
198
|
end
|
206
199
|
|
207
200
|
|
@@ -211,6 +204,7 @@ This.gemspec_file = "#{This.name}.gemspec"
|
|
211
204
|
# Really this is only here to support those who use bundler
|
212
205
|
desc "Build the #{This.name}.gemspec file"
|
213
206
|
task :gemspec do
|
207
|
+
Util.set_coverage_gem
|
214
208
|
File.open( This.gemspec_file, "wb+" ) do |f|
|
215
209
|
f.write Util.platform_gemspec.to_ruby
|
216
210
|
end
|
@@ -261,7 +255,13 @@ end
|
|
261
255
|
# Load the extra rake tasks
|
262
256
|
#------------------------------------------------------------------------------
|
263
257
|
$: << "." unless $:.include?(".")
|
264
|
-
|
258
|
+
begin
|
259
|
+
load 'tasks/kestrel.rake'
|
260
|
+
rescue LoadError => le
|
261
|
+
Util.task_warning( 'kestrel' )
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
265
|
|
266
266
|
#------------------------------------------------------------------------------
|
267
267
|
# Rakefile Support - This is all the guts and utility methods that are
|
@@ -320,6 +320,19 @@ BEGIN {
|
|
320
320
|
def self.platform_gemspec
|
321
321
|
This.gemspec[This.platform]
|
322
322
|
end
|
323
|
+
|
324
|
+
def self.set_coverage_gem
|
325
|
+
# list these here instead of gem dependencies since there is not a way to
|
326
|
+
# specify ruby version specific dependencies
|
327
|
+
g, v = 'simplecov', '~> 0.7.1'
|
328
|
+
if RUBY_VERSION < "1.9.2"
|
329
|
+
g, v = 'rcov', '~> 1.0.0'
|
330
|
+
end
|
331
|
+
|
332
|
+
if Util.platform_gemspec.dependencies.none? { |s| s.name == g } then
|
333
|
+
Util.platform_gemspec.add_development_dependency( g, v )
|
334
|
+
end
|
335
|
+
end
|
323
336
|
end
|
324
337
|
|
325
338
|
# Hold all the metadata about this project
|
data/lib/kjess.rb
CHANGED
data/lib/kjess/client.rb
CHANGED
@@ -30,7 +30,7 @@ module KJess
|
|
30
30
|
@port = merged[:port]
|
31
31
|
@admin_port = merged[:admin_port]
|
32
32
|
@stats_cache = StatsCache.new( self, merged[:stats_cache_expiration] )
|
33
|
-
@connection
|
33
|
+
@connection = KJess::Connection.new( host, port, merged )
|
34
34
|
end
|
35
35
|
|
36
36
|
# Public: Disconnect from the Kestrel server
|
@@ -77,7 +77,9 @@ module KJess
|
|
77
77
|
# Returns true if successful, false otherwise
|
78
78
|
def set( queue_name, item, expiration = 0 )
|
79
79
|
s = KJess::Request::Set.new( :queue_name => queue_name, :data => item, :expiration => expiration )
|
80
|
-
send_recv( s )
|
80
|
+
resp = send_recv( s )
|
81
|
+
|
82
|
+
return KJess::Response::Stored === resp
|
81
83
|
end
|
82
84
|
|
83
85
|
# Public: Retrieve an item from the given queue
|
@@ -94,10 +96,18 @@ module KJess
|
|
94
96
|
def get( queue_name, opts = {} )
|
95
97
|
opts = opts.merge( :queue_name => queue_name )
|
96
98
|
g = KJess::Request::Get.new( opts )
|
97
|
-
resp = send_recv( g )
|
98
99
|
|
99
|
-
|
100
|
-
|
100
|
+
if opts[:wait_for]
|
101
|
+
wait_for_in_seconds = opts[:wait_for] / 1000
|
102
|
+
else
|
103
|
+
wait_for_in_seconds = 0.1
|
104
|
+
end
|
105
|
+
|
106
|
+
connection.with_additional_read_timeout(wait_for_in_seconds) do
|
107
|
+
resp = send_recv( g )
|
108
|
+
return resp.data if KJess::Response::Value === resp
|
109
|
+
return nil
|
110
|
+
end
|
101
111
|
end
|
102
112
|
|
103
113
|
# Public: Reserve the next item on the queue
|
@@ -176,17 +186,27 @@ module KJess
|
|
176
186
|
#
|
177
187
|
# Returns true if the queue was flushed.
|
178
188
|
def flush( queue_name )
|
179
|
-
|
180
|
-
|
181
|
-
|
189
|
+
# It can take a long time to flush all of the messages
|
190
|
+
# on a server, so we'll set the read timeout to something
|
191
|
+
# much higher than usual.
|
192
|
+
connection.with_additional_read_timeout(60) do
|
193
|
+
req = KJess::Request::Flush.new( :queue_name => queue_name )
|
194
|
+
resp = send_recv( req )
|
195
|
+
return KJess::Response::End === resp
|
196
|
+
end
|
182
197
|
end
|
183
198
|
|
184
199
|
# Public: Remove all items from all queues on the kestrel server
|
185
200
|
#
|
186
201
|
# Returns true.
|
187
202
|
def flush_all
|
188
|
-
|
189
|
-
|
203
|
+
# It can take a long time to flush all of the messages
|
204
|
+
# on a server, so we'll set the read timeout to something
|
205
|
+
# much higher than usual.
|
206
|
+
connection.with_additional_read_timeout(60) do
|
207
|
+
resp = send_recv( KJess::Request::FlushAll.new )
|
208
|
+
return KJess::Response::End === resp
|
209
|
+
end
|
190
210
|
end
|
191
211
|
|
192
212
|
# Public: Have Kestrel reload its config.
|
@@ -215,7 +235,7 @@ module KJess
|
|
215
235
|
#
|
216
236
|
# Returns a String.
|
217
237
|
def status( update_to = nil )
|
218
|
-
resp = send_recv( KJess::Request::Status.new( update_to ) )
|
238
|
+
resp = send_recv( KJess::Request::Status.new( :update_to => update_to ) )
|
219
239
|
raise KJess::Error, "Status command is not supported" if KJess::Response::ClientError === resp
|
220
240
|
return resp.message
|
221
241
|
end
|
@@ -236,6 +256,8 @@ module KJess
|
|
236
256
|
# Returns a Hash
|
237
257
|
def stats!
|
238
258
|
stats = send_recv( KJess::Request::Stats.new )
|
259
|
+
raise KJess::Error, "Problem receiving stats: #{stats.inspect}" unless KJess::Response::Stats === stats
|
260
|
+
|
239
261
|
h = stats.data
|
240
262
|
dump_stats = send_recv( KJess::Request::DumpStats.new )
|
241
263
|
h['queues'] = Hash.new
|
@@ -253,8 +275,7 @@ module KJess
|
|
253
275
|
def ping
|
254
276
|
stats
|
255
277
|
true
|
256
|
-
rescue Errno::ECONNREFUSED
|
257
|
-
puts e
|
278
|
+
rescue Errno::ECONNREFUSED
|
258
279
|
false
|
259
280
|
end
|
260
281
|
|
data/lib/kjess/connection.rb
CHANGED
@@ -1,65 +1,104 @@
|
|
1
1
|
require 'fcntl'
|
2
|
-
require 'socket'
|
3
2
|
require 'resolv'
|
4
3
|
require 'resolv-replace'
|
5
4
|
require 'kjess/error'
|
5
|
+
require 'kjess/socket'
|
6
6
|
|
7
7
|
module KJess
|
8
8
|
# Connection
|
9
9
|
class Connection
|
10
10
|
class Error < KJess::Error; end
|
11
11
|
|
12
|
-
|
12
|
+
# Public: The hostname/ip address to connect to.
|
13
|
+
def host
|
14
|
+
socket.host
|
15
|
+
end
|
13
16
|
|
14
|
-
# Public:
|
15
|
-
|
16
|
-
|
17
|
+
# Public: The port number to connect to. Default 22133
|
18
|
+
def port
|
19
|
+
socket.port
|
20
|
+
end
|
17
21
|
|
18
|
-
# Public
|
19
|
-
|
20
|
-
|
22
|
+
# Public: The timeout for connecting in seconds. Defaults to 2
|
23
|
+
def connect_timeout
|
24
|
+
socket.connect_timeout
|
25
|
+
end
|
21
26
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@socket = nil
|
27
|
+
# Public: The timeout for reading in seconds. Defaults to 2
|
28
|
+
def read_timeout
|
29
|
+
socket.read_timeout
|
26
30
|
end
|
27
31
|
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
# then return it.
|
32
|
-
#
|
33
|
-
# Returns a TCPSocket
|
34
|
-
def socket
|
35
|
-
return @socket if @socket and not @socket.closed?
|
36
|
-
return @socket = connect()
|
32
|
+
# Public: The timeout for writing in seconds. Defaults to 2
|
33
|
+
def write_timeout
|
34
|
+
socket.write_timeout
|
37
35
|
end
|
38
36
|
|
39
|
-
# Internal:
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
sock = TCPSocket.new( host, port )
|
37
|
+
# Internal: return thekeepalive timeout
|
38
|
+
def keepalive_active?
|
39
|
+
socket.keepalive_active?
|
40
|
+
end
|
44
41
|
|
45
|
-
|
46
|
-
|
42
|
+
# Internal: return the keepalive count
|
43
|
+
# The keepalive count
|
44
|
+
def keepalive_count
|
45
|
+
socket.keepalive_count
|
46
|
+
end
|
47
47
|
|
48
|
-
|
49
|
-
|
48
|
+
# Internal: return the keepalive interval
|
49
|
+
def keepalive_interval
|
50
|
+
socket.keepalive_interval
|
51
|
+
end
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
# Internal: return the keepalive idle
|
54
|
+
def keepalive_idle
|
55
|
+
socket.keepalive_idle
|
56
|
+
end
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
58
|
+
# TODO: make port an option at next major version number change
|
59
|
+
def initialize( host, port = 22133, options = {} )
|
60
|
+
if port.is_a?(Hash)
|
61
|
+
options = port
|
62
|
+
port = 22133
|
63
|
+
end
|
64
|
+
|
65
|
+
@options = options.dup
|
66
|
+
@options[:host] = host
|
67
|
+
@options[:port] = Float( port ).to_i
|
68
|
+
@socket = nil
|
69
|
+
@pid = nil
|
70
|
+
@read_buffer = ''
|
71
|
+
end
|
72
|
+
|
73
|
+
# Internal: Adds time to the read timeout
|
74
|
+
#
|
75
|
+
# additional_timeout - additional number of seconds to the read timeout
|
76
|
+
#
|
77
|
+
# Returns nothing
|
78
|
+
def with_additional_read_timeout(additional_timeout, &block)
|
79
|
+
old_read_timeout = socket.read_timeout
|
80
|
+
socket.read_timeout += additional_timeout
|
81
|
+
block.call
|
82
|
+
ensure
|
83
|
+
@read_timeout = old_read_timeout
|
84
|
+
end
|
85
|
+
|
86
|
+
# Internal: Return the socket that is connected to the Kestrel server
|
87
|
+
#
|
88
|
+
# Returns the socket. If the socket is not connected it will connect and
|
89
|
+
# then return it.
|
90
|
+
#
|
91
|
+
# Make sure that we close the socket if we are not the same process that
|
92
|
+
# opened that socket to begin with.
|
93
|
+
#
|
94
|
+
# Returns a KJess::Socket
|
95
|
+
def socket
|
96
|
+
close if @pid && @pid != Process.pid
|
97
|
+
return @socket if @socket and not @socket.closed?
|
98
|
+
@socket = Socket.connect( @options )
|
99
|
+
@pid = Process.pid
|
100
|
+
@read_buffer = ''
|
101
|
+
return @socket
|
63
102
|
end
|
64
103
|
|
65
104
|
# Internal: close the socket if it is not already closed
|
@@ -67,6 +106,7 @@ module KJess
|
|
67
106
|
# Returns nothing
|
68
107
|
def close
|
69
108
|
@socket.close if @socket and not @socket.closed?
|
109
|
+
@read_buffer = ''
|
70
110
|
@socket = nil
|
71
111
|
end
|
72
112
|
|
@@ -85,8 +125,11 @@ module KJess
|
|
85
125
|
#
|
86
126
|
# Returns nothing
|
87
127
|
def write( msg )
|
88
|
-
$stderr.
|
128
|
+
$stderr.puts "--> #{msg}" if $DEBUG
|
89
129
|
socket.write( msg )
|
130
|
+
rescue KJess::Error
|
131
|
+
close
|
132
|
+
raise
|
90
133
|
end
|
91
134
|
|
92
135
|
# Internal: read a single line from the socket
|
@@ -95,25 +138,44 @@ module KJess
|
|
95
138
|
#
|
96
139
|
# Returns a String
|
97
140
|
def readline( eom = Protocol::CRLF )
|
98
|
-
while
|
99
|
-
|
141
|
+
while true
|
142
|
+
while (idx = @read_buffer.index(eom)) == nil
|
143
|
+
@read_buffer << socket.readpartial(10240)
|
144
|
+
end
|
145
|
+
|
146
|
+
line = @read_buffer.slice!(0, idx + eom.length)
|
147
|
+
$stderr.puts "<-- #{line}" if $DEBUG
|
100
148
|
break unless line.strip.length == 0
|
101
149
|
end
|
102
150
|
return line
|
151
|
+
rescue KJess::Error
|
152
|
+
close
|
153
|
+
raise
|
103
154
|
rescue EOFError
|
104
155
|
close
|
105
156
|
return "EOF"
|
157
|
+
rescue => e
|
158
|
+
close
|
159
|
+
raise Error, "Could not read from #{host}:#{port}: #{e.class}: #{e.message}", e.backtrace
|
106
160
|
end
|
107
161
|
|
108
162
|
# Internal: Read from the socket
|
109
163
|
#
|
110
|
-
#
|
164
|
+
# nbytes - this method takes the number of bytes to read
|
111
165
|
#
|
112
166
|
# Returns what IO#read returns
|
113
|
-
def read(
|
114
|
-
|
115
|
-
|
116
|
-
|
167
|
+
def read( nbytes )
|
168
|
+
while @read_buffer.length < nbytes
|
169
|
+
@read_buffer << socket.readpartial(nbytes - @read_buffer.length)
|
170
|
+
end
|
171
|
+
|
172
|
+
result = @read_buffer.slice!(0, nbytes)
|
173
|
+
|
174
|
+
$stderr.puts "<-- #{result}" if $DEBUG
|
175
|
+
return result
|
176
|
+
rescue KJess::Error
|
177
|
+
close
|
178
|
+
raise
|
117
179
|
end
|
118
180
|
end
|
119
181
|
end
|
data/lib/kjess/protocol.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module KJess
|
2
|
-
#
|
2
|
+
# Protocol is the base class that all Kestrel requests and responses are
|
3
3
|
# developed on. it defines the DSL for creating the Request and Response
|
4
4
|
# objects that make up the Protocol.
|
5
5
|
#
|
@@ -14,11 +14,12 @@ module KJess
|
|
14
14
|
#
|
15
15
|
# Returns the name
|
16
16
|
def keyword( name = nil )
|
17
|
+
@keyword = nil unless defined? @keyword
|
17
18
|
if name then
|
18
19
|
register( name )
|
19
20
|
@keyword = name
|
20
21
|
end
|
21
|
-
@keyword
|
22
|
+
@keyword ||= nil
|
22
23
|
end
|
23
24
|
|
24
25
|
# Internal: define or return the arity of this protocol item
|
data/lib/kjess/request/status.rb
CHANGED
@@ -3,6 +3,13 @@ class KJess::Request
|
|
3
3
|
class Status < KJess::Request
|
4
4
|
keyword 'STATUS'
|
5
5
|
arity 1
|
6
|
-
|
6
|
+
valid_responses [ KJess::Response::Status::Up, KJess::Response::Status::Down,
|
7
|
+
KJess::Response::Status::ReadOnly, KJess::Response::Status::Quiescent,
|
8
|
+
KJess::Response::End ]
|
9
|
+
|
10
|
+
def parse_options_to_args( opts )
|
11
|
+
[ opts[:update_to] ]
|
12
|
+
end
|
13
|
+
|
7
14
|
end
|
8
15
|
end
|
data/lib/kjess/response.rb
CHANGED
@@ -70,6 +70,7 @@ require 'kjess/response/not_stored'
|
|
70
70
|
require 'kjess/response/reloaded_config'
|
71
71
|
require 'kjess/response/server_error'
|
72
72
|
require 'kjess/response/stats'
|
73
|
+
require 'kjess/response/status'
|
73
74
|
require 'kjess/response/stored'
|
74
75
|
require 'kjess/response/unknown'
|
75
76
|
require 'kjess/response/value'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class KJess::Response
|
2
|
+
class Status < KJess::Response
|
3
|
+
class Up < KJess::Response::Status
|
4
|
+
keyword "UP"
|
5
|
+
end
|
6
|
+
|
7
|
+
class Down < KJess::Response::Status
|
8
|
+
keyword "DOWN"
|
9
|
+
end
|
10
|
+
|
11
|
+
class Quiescent < KJess::Response::Status
|
12
|
+
keyword "QUIESCENT"
|
13
|
+
end
|
14
|
+
|
15
|
+
class ReadOnly< KJess::Response::Status
|
16
|
+
keyword "READONLY"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/kjess/socket.rb
ADDED
@@ -0,0 +1,291 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module KJess
|
4
|
+
# Socket: A specialized socket that has been configure
|
5
|
+
class Socket
|
6
|
+
class Error < KJess::Error; end
|
7
|
+
class Timeout < Error; end
|
8
|
+
|
9
|
+
# Internal:
|
10
|
+
# The timeout for reading in seconds. Defaults to 2
|
11
|
+
attr_accessor :read_timeout
|
12
|
+
|
13
|
+
# Internal:
|
14
|
+
# The timeout for connecting in seconds. Defaults to 2
|
15
|
+
attr_reader :connect_timeout
|
16
|
+
|
17
|
+
# Internal:
|
18
|
+
# The timeout for writing in seconds. Defaults to 2
|
19
|
+
attr_reader :write_timeout
|
20
|
+
|
21
|
+
# Internal:
|
22
|
+
# The host this socket is connected to
|
23
|
+
attr_reader :host
|
24
|
+
|
25
|
+
# Internal:
|
26
|
+
# The port this socket is connected to
|
27
|
+
attr_reader :port
|
28
|
+
|
29
|
+
# Internal
|
30
|
+
#
|
31
|
+
# Used for setting TCP_KEEPIDLE: overrides tcp_keepalive_time for a single
|
32
|
+
# socket.
|
33
|
+
#
|
34
|
+
# http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
|
35
|
+
#
|
36
|
+
# tcp_keepalive_time:
|
37
|
+
#
|
38
|
+
# The interval between the last data packet sent (simple ACKs are not
|
39
|
+
# considered data) and the first keepalive probe; after the connection is
|
40
|
+
# marked to need keepalive, this counter is not used any further.
|
41
|
+
attr_reader :keepalive_idle
|
42
|
+
|
43
|
+
# Internal
|
44
|
+
#
|
45
|
+
# Used for setting TCP_KEEPINTVL: overrides tcp_keepalive_intvl for a single
|
46
|
+
# socket.
|
47
|
+
#
|
48
|
+
# http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
|
49
|
+
#
|
50
|
+
# tcp_keepalive_intvl:
|
51
|
+
#
|
52
|
+
# The interval between subsequential keepalive probes, regardless of what
|
53
|
+
# the connection has exchanged in the meantime.
|
54
|
+
attr_reader :keepalive_interval
|
55
|
+
|
56
|
+
# Internal
|
57
|
+
#
|
58
|
+
# Used for setting TCP_KEEPCNT: overrides tcp_keepalive_probes for a single
|
59
|
+
# socket.
|
60
|
+
#
|
61
|
+
# http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
|
62
|
+
#
|
63
|
+
# tcp_keepalive_probes:
|
64
|
+
#
|
65
|
+
# The number of unacknowledged probes to send before considering the
|
66
|
+
# connection dead and notifying the application layer.
|
67
|
+
attr_reader :keepalive_count
|
68
|
+
|
69
|
+
|
70
|
+
# Internal: Create and connect to the given location.
|
71
|
+
#
|
72
|
+
# options, same as Constructor
|
73
|
+
#
|
74
|
+
# Returns an instance of KJess::Socket
|
75
|
+
def self.connect( options = {} )
|
76
|
+
s = Socket.new( options )
|
77
|
+
s.connect
|
78
|
+
return s
|
79
|
+
end
|
80
|
+
|
81
|
+
# Internal: Creates a new KJess::Socket
|
82
|
+
def initialize( options = {} )
|
83
|
+
@host = options.fetch(:host)
|
84
|
+
@port = options.fetch(:port)
|
85
|
+
@connect_timeout = options.fetch(:connect_timeout , 2)
|
86
|
+
@read_timeout = options.fetch(:read_timeout , 2)
|
87
|
+
@write_timeout = options.fetch(:write_timeout , 2)
|
88
|
+
@keepalive_active = options.fetch(:keepalive_active , true)
|
89
|
+
@keepalive_idle = options.fetch(:keepalive_idle , 60)
|
90
|
+
@keepalive_interval = options.fetch(:keepalive_interval, 30)
|
91
|
+
@keepalive_count = options.fetch(:keepalive_count , 5)
|
92
|
+
@socket = nil
|
93
|
+
end
|
94
|
+
|
95
|
+
# Internal: Return whether or not the keepalive_active flag is set.
|
96
|
+
def keepalive_active?
|
97
|
+
@keepalive_active
|
98
|
+
end
|
99
|
+
|
100
|
+
# Internal: Low level socket allocation and option configuration
|
101
|
+
#
|
102
|
+
# Using the options from the initializer, a new ::Socket is created that
|
103
|
+
# is:
|
104
|
+
#
|
105
|
+
# TCP, IPv4 only, autoclosing on exit, nagle's algorithm is disabled and has
|
106
|
+
# TCP Keepalive options set if keepalive is supported.
|
107
|
+
#
|
108
|
+
# Returns a new ::Socket instance
|
109
|
+
def blank_socket
|
110
|
+
sock = ::Socket.new(::Socket::AF_INET, ::Socket::SOCK_STREAM, 0)
|
111
|
+
|
112
|
+
# close file descriptors if we exec
|
113
|
+
sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
114
|
+
|
115
|
+
# Disable Nagle's algorithm
|
116
|
+
sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
117
|
+
|
118
|
+
if using_keepalive? then
|
119
|
+
sock.setsockopt( ::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE , true )
|
120
|
+
sock.setsockopt( ::Socket::SOL_TCP, ::Socket::TCP_KEEPIDLE , keepalive_idle )
|
121
|
+
sock.setsockopt( ::Socket::SOL_TCP, ::Socket::TCP_KEEPINTVL, keepalive_interval)
|
122
|
+
sock.setsockopt( ::Socket::SOL_TCP, ::Socket::TCP_KEEPCNT , keepalive_count)
|
123
|
+
end
|
124
|
+
|
125
|
+
return sock
|
126
|
+
end
|
127
|
+
|
128
|
+
# Internal: Return the connected raw Socket.
|
129
|
+
#
|
130
|
+
# If the socket is closed or non-existent it will create and connect again.
|
131
|
+
#
|
132
|
+
# Returns a ::Socket
|
133
|
+
def socket
|
134
|
+
return @socket unless closed?
|
135
|
+
@socket = connect()
|
136
|
+
end
|
137
|
+
|
138
|
+
# Internal: Closes the internal ::Socket
|
139
|
+
#
|
140
|
+
# Returns nothing
|
141
|
+
def close
|
142
|
+
@socket.close unless closed?
|
143
|
+
@socket = nil
|
144
|
+
end
|
145
|
+
|
146
|
+
# Internal: Return true the socket is closed.
|
147
|
+
def closed?
|
148
|
+
return true if @socket.nil?
|
149
|
+
return true if @socket.closed?
|
150
|
+
return false
|
151
|
+
end
|
152
|
+
|
153
|
+
# Internal:
|
154
|
+
#
|
155
|
+
# Connect to the remote host in a non-blocking fashion.
|
156
|
+
#
|
157
|
+
# Raise Error if there is a failure connecting.
|
158
|
+
#
|
159
|
+
# Return the ::Socket on success
|
160
|
+
def connect
|
161
|
+
# Calculate our timeout deadline
|
162
|
+
deadline = Time.now.to_f + connect_timeout
|
163
|
+
|
164
|
+
# Lookup destination address, we only want IPv4 , TCP
|
165
|
+
addrs = ::Socket.getaddrinfo(host, port, ::Socket::AF_INET, ::Socket::SOCK_STREAM )
|
166
|
+
errors = []
|
167
|
+
conn_error = lambda { raise errors.first }
|
168
|
+
sock = nil
|
169
|
+
|
170
|
+
addrs.find( conn_error ) do |addr|
|
171
|
+
sock = connect_or_error( addr, deadline, errors )
|
172
|
+
end
|
173
|
+
return sock
|
174
|
+
end
|
175
|
+
|
176
|
+
# Internal: Connect to the destination or raise an error.
|
177
|
+
#
|
178
|
+
# Connect to the address or capture the error of the connection
|
179
|
+
#
|
180
|
+
# addr - An address returned from Socket.getaddrinfo()
|
181
|
+
# deadline - the after which we should raise a timeout error
|
182
|
+
# errors - a collection of errors to append an error too should we have one.
|
183
|
+
#
|
184
|
+
# Make an attempt to connect to the given address. If it is successful,
|
185
|
+
# return the socket.
|
186
|
+
#
|
187
|
+
# Should the connection fail, append the exception to the errors array and
|
188
|
+
# return false.
|
189
|
+
#
|
190
|
+
def connect_or_error( addr, deadline, errors )
|
191
|
+
timeout = deadline - Time.now.to_f
|
192
|
+
raise Timeout, "Could not connect to #{host}:#{port}" if timeout <= 0
|
193
|
+
return connect_nonblock( addr, timeout )
|
194
|
+
rescue Error => e
|
195
|
+
errors << e
|
196
|
+
return false
|
197
|
+
end
|
198
|
+
|
199
|
+
# Internal: Connect to the give address within the timeout.
|
200
|
+
#
|
201
|
+
# Make an attempt to connect to a single address within the given timeout.
|
202
|
+
#
|
203
|
+
# Return the ::Socket when it is connected, or raise an Error if no
|
204
|
+
# connection was possible.
|
205
|
+
def connect_nonblock( addr, timeout )
|
206
|
+
sockaddr = ::Socket.pack_sockaddr_in(addr[1], addr[3])
|
207
|
+
sock = blank_socket()
|
208
|
+
sock.connect_nonblock( sockaddr )
|
209
|
+
return sock
|
210
|
+
rescue Errno::EINPROGRESS
|
211
|
+
if IO.select(nil, [sock], nil, timeout).nil? then
|
212
|
+
raise Timeout, "Could not connect to #{host}:#{port} within #{timeout} seconds"
|
213
|
+
end
|
214
|
+
return connect_nonblock_finalize( sock, sockaddr )
|
215
|
+
rescue => ex
|
216
|
+
raise Error, "Could not connect to #{host}:#{port}: #{ex.class}: #{ex.message}", ex.backtrace
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
# Internal: Make sure that a non-blocking connect has truely connected.
|
221
|
+
#
|
222
|
+
# Ensure that the given socket is actually connected to the given adddress.
|
223
|
+
#
|
224
|
+
# Returning the socket if it is and raising an Error if it isn't.
|
225
|
+
def connect_nonblock_finalize( sock, sockaddr )
|
226
|
+
sock.connect_nonblock( sockaddr )
|
227
|
+
rescue Errno::EISCONN
|
228
|
+
return sock
|
229
|
+
rescue => ex
|
230
|
+
raise Error, "Could not connect to #{host}:#{port}: #{ex.class}: #{ex.message}", ex.backtrace
|
231
|
+
end
|
232
|
+
|
233
|
+
# Internal: say if we are using TCP Keep Alive or not
|
234
|
+
#
|
235
|
+
# We will return true if the initialization options :keepalive_active is
|
236
|
+
# set to true, and if all the constants that are necessary to use TCP keep
|
237
|
+
# alive are defined.
|
238
|
+
#
|
239
|
+
# It may be the case that on some operating systems that the constants are
|
240
|
+
# not defined, so in that case we do not want to attempt to use tcp keep
|
241
|
+
# alive if we are unable to do so in any case.
|
242
|
+
#
|
243
|
+
# Returns true or false
|
244
|
+
def using_keepalive?
|
245
|
+
using = false
|
246
|
+
if keepalive_active? then
|
247
|
+
using = [ :SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all? do |c|
|
248
|
+
::Socket.const_defined? c
|
249
|
+
end
|
250
|
+
end
|
251
|
+
return using
|
252
|
+
end
|
253
|
+
|
254
|
+
# Internal: Read up to a maxlen of data from the socket and store it in outbuf
|
255
|
+
#
|
256
|
+
# maxlen - the maximum number of bytes to read from the socket
|
257
|
+
# outbuf - the buffer in which to store the bytes.
|
258
|
+
#
|
259
|
+
# Returns the bytes read
|
260
|
+
def readpartial(maxlen, outbuf = nil)
|
261
|
+
return socket.read_nonblock(maxlen, outbuf)
|
262
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNRESET
|
263
|
+
if IO.select([@socket], nil, nil, read_timeout)
|
264
|
+
retry
|
265
|
+
else
|
266
|
+
raise Timeout, "Could not read from #{host}:#{port} in #{read_timeout} seconds"
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Internal: Write the given data to the socket
|
271
|
+
#
|
272
|
+
# buf - the data to write to the socket.
|
273
|
+
#
|
274
|
+
# Raises an error if it is unable to write the data to the socket within the
|
275
|
+
# write_timeout.
|
276
|
+
#
|
277
|
+
# returns nothing
|
278
|
+
def write( buf )
|
279
|
+
until buf.length == 0
|
280
|
+
written = socket.write_nonblock(buf)
|
281
|
+
buf = buf[written, buf.length]
|
282
|
+
end
|
283
|
+
rescue Errno::EWOULDBLOCK, Errno::EINTR, Errno::EAGAIN, Errno::ECONNRESET
|
284
|
+
if IO.select(nil, [socket], nil, write_timeout)
|
285
|
+
retry
|
286
|
+
else
|
287
|
+
raise Timeout, "Could not write to #{host}:#{port} in #{write_timeout} seconds"
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
data/spec/client_spec.rb
CHANGED
@@ -1,15 +1,28 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
#$DEBUG = true
|
4
3
|
describe KJess::Client do
|
5
4
|
before do
|
6
|
-
@
|
5
|
+
@client_version = "2.4.1"
|
6
|
+
@client = KJess::Spec.kjess_client()
|
7
7
|
end
|
8
8
|
|
9
9
|
after do
|
10
10
|
KJess::Spec.reset_server( @client )
|
11
11
|
end
|
12
12
|
|
13
|
+
describe "#initialize" do
|
14
|
+
it "can set keepalive parameters" do
|
15
|
+
client = KJess::Client.new( :port => KJess::Spec.memcache_port,
|
16
|
+
:keepalive_active => true,
|
17
|
+
:keepalive_interval => 1,
|
18
|
+
:keepalive_idle => 900,
|
19
|
+
:keepalive_count => 42)
|
20
|
+
client.connection.keepalive_interval.must_equal 1
|
21
|
+
client.connection.keepalive_idle.must_equal 900
|
22
|
+
client.connection.keepalive_count.must_equal 42
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
13
26
|
describe "connection" do
|
14
27
|
it "knows if it is connected" do
|
15
28
|
@client.ping
|
@@ -26,13 +39,13 @@ describe KJess::Client do
|
|
26
39
|
|
27
40
|
describe "#version" do
|
28
41
|
it "knows the version of the server" do
|
29
|
-
@client.version.must_equal
|
42
|
+
@client.version.must_equal @client_version
|
30
43
|
end
|
31
44
|
end
|
32
45
|
|
33
46
|
describe "#stats" do
|
34
47
|
it "can see the stats on an empty server" do
|
35
|
-
@client.stats['version'].must_equal
|
48
|
+
@client.stats['version'].must_equal @client_version
|
36
49
|
end
|
37
50
|
|
38
51
|
it "sees the stats on a server with queues" do
|
@@ -65,6 +78,12 @@ describe KJess::Client do
|
|
65
78
|
end
|
66
79
|
@client.get( 'set_q_2' ).must_equal 'setspec2'
|
67
80
|
end
|
81
|
+
|
82
|
+
it 'a really long binary item' do
|
83
|
+
binary = (0..255).to_a.pack('c*') * 100
|
84
|
+
@client.set 'set_bin_q', binary
|
85
|
+
@client.get('set_bin_q').must_equal binary
|
86
|
+
end
|
68
87
|
end
|
69
88
|
|
70
89
|
describe "#get" do
|
@@ -253,7 +272,14 @@ describe KJess::Client do
|
|
253
272
|
|
254
273
|
describe "#status" do
|
255
274
|
it "returns the server status" do
|
256
|
-
|
275
|
+
@client.status.must_equal "UP"
|
276
|
+
end
|
277
|
+
|
278
|
+
it "can change the status" do
|
279
|
+
@client.status( "readonly" ).must_equal "END"
|
280
|
+
@client.status.must_equal "READONLY"
|
281
|
+
@client.status( "up" ).must_equal "END"
|
282
|
+
@client.status.must_equal "UP"
|
257
283
|
end
|
258
284
|
end
|
259
285
|
|
@@ -262,4 +288,67 @@ describe KJess::Client do
|
|
262
288
|
@client.ping.must_equal true
|
263
289
|
end
|
264
290
|
end
|
291
|
+
|
292
|
+
describe "connecting to a server on a port that isn't listening" do
|
293
|
+
it "throws an exception" do
|
294
|
+
c = KJess::Connection.new '127.0.0.1', 65521
|
295
|
+
lambda { c.socket }.must_raise KJess::Socket::Error
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
describe "connecting to a server that isn't responding" do
|
300
|
+
it "throws an exception" do
|
301
|
+
c = KJess::Connection.new '127.1.1.1', 65521, :timeout => 0.5
|
302
|
+
lambda { c.socket }.must_raise KJess::Socket::Timeout
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "reading for longer than the timeout" do
|
307
|
+
it "throws an exception" do
|
308
|
+
q = Queue.new
|
309
|
+
t = Thread.new do
|
310
|
+
begin
|
311
|
+
server = TCPServer.new 65520
|
312
|
+
q.enq :go
|
313
|
+
client = server.accept
|
314
|
+
Thread.stop
|
315
|
+
ensure
|
316
|
+
server.close rescue nil
|
317
|
+
client.close rescue nil
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
q.deq
|
322
|
+
c = KJess::Connection.new '127.0.0.1', 65520, :timeout => 0.5
|
323
|
+
|
324
|
+
lambda { c.readline }.must_raise KJess::Socket::Timeout
|
325
|
+
|
326
|
+
t.run
|
327
|
+
t.join
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
describe "writing for longer than the timeout" do
|
332
|
+
it "throws an exception" do
|
333
|
+
q = Queue.new
|
334
|
+
t = Thread.new do
|
335
|
+
begin
|
336
|
+
server = TCPServer.new 65520
|
337
|
+
q.enq :go
|
338
|
+
client = server.accept
|
339
|
+
Thread.stop
|
340
|
+
ensure
|
341
|
+
server.close rescue nil
|
342
|
+
client.close rescue nil
|
343
|
+
end
|
344
|
+
end
|
345
|
+
q.deq
|
346
|
+
c = KJess::Connection.new '127.0.0.1', 65520, :timeout => 0.5
|
347
|
+
|
348
|
+
lambda { c.write('a' * 10000000) }.must_raise KJess::Socket::Timeout
|
349
|
+
|
350
|
+
t.run
|
351
|
+
t.join
|
352
|
+
end
|
353
|
+
end
|
265
354
|
end
|
data/spec/kestrel_server.rb
CHANGED
@@ -6,7 +6,7 @@ module KJess::Spec
|
|
6
6
|
|
7
7
|
class << self
|
8
8
|
def version
|
9
|
-
"2.
|
9
|
+
"2.4.1"
|
10
10
|
end
|
11
11
|
|
12
12
|
def dir
|
@@ -18,7 +18,7 @@ module KJess::Spec
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def jar
|
21
|
-
File.join( dir, "kestrel_2.9.
|
21
|
+
File.join( dir, "kestrel_2.9.2-#{version}.jar" )
|
22
22
|
end
|
23
23
|
|
24
24
|
def queue_path
|
@@ -47,7 +47,9 @@ import net.lag.kestrel.config._
|
|
47
47
|
|
48
48
|
new KestrelConfig {
|
49
49
|
listenAddress = "0.0.0.0"
|
50
|
-
memcacheListenPort =
|
50
|
+
memcacheListenPort = #{KJess::Spec.memcache_port}
|
51
|
+
textListenPort = #{KJess::Spec.text_port}
|
52
|
+
thriftListenPort = #{KJess::Spec.thrift_port}
|
51
53
|
|
52
54
|
queuePath = "#{KJess::Spec::KestrelServer.queue_path}"
|
53
55
|
|
@@ -62,7 +64,7 @@ new KestrelConfig {
|
|
62
64
|
default.maxMemorySize = 128.megabytes
|
63
65
|
default.maxJournalSize = 1.gigabyte
|
64
66
|
|
65
|
-
admin.httpPort =
|
67
|
+
admin.httpPort = #{KJess::Spec.admin_port}
|
66
68
|
|
67
69
|
admin.statsNodes = new StatsConfig {
|
68
70
|
reporters = new TimeSeriesCollectorConfig
|
@@ -80,7 +82,7 @@ _EOC
|
|
80
82
|
end
|
81
83
|
|
82
84
|
def get_response( path )
|
83
|
-
uri = URI.parse( "http://localhost
|
85
|
+
uri = URI.parse( "http://localhost:#{KJess::Spec.admin_port}/#{path}" )
|
84
86
|
resp = Net::HTTP.get_response( uri )
|
85
87
|
JSON.parse( resp.body )
|
86
88
|
end
|
@@ -100,6 +102,7 @@ _EOC
|
|
100
102
|
def is_running?
|
101
103
|
return "pong" == ping
|
102
104
|
rescue Exception => e
|
105
|
+
#$stderr.puts e
|
103
106
|
false
|
104
107
|
end
|
105
108
|
|
@@ -129,9 +132,9 @@ _EOC
|
|
129
132
|
h = get_response( 'shutdown' )
|
130
133
|
return h['response'] == "ok"
|
131
134
|
rescue => e
|
135
|
+
$stderr.puts e
|
132
136
|
false
|
133
137
|
end
|
134
138
|
end
|
135
|
-
|
136
139
|
end
|
137
140
|
end
|
@@ -1,8 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
module KJess::Spec
|
4
|
+
class BadRequest < KJess::Request
|
5
|
+
keyword 'BADREQUEST'
|
6
|
+
arity 1
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
3
10
|
describe KJess::ClientError do
|
4
11
|
before do
|
5
|
-
@client = KJess::
|
12
|
+
@client = KJess::Spec.kjess_client()
|
6
13
|
end
|
7
14
|
|
8
15
|
after do
|
@@ -10,7 +17,7 @@ describe KJess::ClientError do
|
|
10
17
|
end
|
11
18
|
|
12
19
|
it "raises a client error if we send an invalid command" do
|
13
|
-
lambda { @client.send_recv( KJess::
|
20
|
+
lambda { @client.send_recv( KJess::Spec::BadRequest.new ) }.must_raise KJess::ClientError
|
14
21
|
end
|
15
22
|
end
|
16
23
|
|
data/spec/spec_helper.rb
CHANGED
data/spec/utils.rb
CHANGED
@@ -5,6 +5,26 @@ module KJess
|
|
5
5
|
File.expand_path( "..", ROOT )
|
6
6
|
end
|
7
7
|
|
8
|
+
def self.memcache_port
|
9
|
+
ENV['KJESS_MEMCACHE_PORT'] || 33122
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.thrift_port
|
13
|
+
ENV['KJESS_THRIFT_PORT'] || 9992
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.text_port
|
17
|
+
ENV['KJESS_TEXT_PORT'] || 9998
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.admin_port
|
21
|
+
ENV['KJESS_ADMIN_PORT'] || 9999
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.kjess_client
|
25
|
+
KJess::Client.new( :port => memcache_port )
|
26
|
+
end
|
27
|
+
|
8
28
|
def self.reset_server( client )
|
9
29
|
client.flush_all
|
10
30
|
qlist = client.stats['queues']
|
data/tasks/kestrel.rake
CHANGED
@@ -7,10 +7,10 @@ namespace :kestrel do
|
|
7
7
|
require 'uri'
|
8
8
|
require 'net/http'
|
9
9
|
|
10
|
-
url = ::URI.parse("http://robey.github.com/kestrel/download/kestrel-#{KJess::Spec::KestrelServer.
|
10
|
+
url = ::URI.parse("http://robey.github.com/kestrel/download/kestrel-#{KJess::Spec::KestrelServer.version}.zip")
|
11
11
|
|
12
12
|
puts "downloading #{url.to_s} to #{KJess::Spec::KestrelServer.zip} ..."
|
13
|
-
File.open( KJess::Spec::KestrelServer.
|
13
|
+
File.open( KJess::Spec::KestrelServer.zip, "wb+") do |f|
|
14
14
|
res = Net::HTTP.get_response( url )
|
15
15
|
f.write( res.body )
|
16
16
|
end
|
metadata
CHANGED
@@ -1,97 +1,106 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: kjess
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 0
|
10
|
-
version: 1.0.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Jeremy Hinegardner
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-01-09 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: rake
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
18
|
+
requirements:
|
26
19
|
- - ~>
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
- 9
|
32
|
-
- 2
|
33
|
-
- 2
|
34
|
-
version: 0.9.2.2
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 10.0.3
|
35
22
|
type: :development
|
36
|
-
version_requirements: *id001
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: minitest
|
39
23
|
prerelease: false
|
40
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 10.0.3
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
41
33
|
none: false
|
42
|
-
requirements:
|
34
|
+
requirements:
|
43
35
|
- - ~>
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
|
46
|
-
segments:
|
47
|
-
- 3
|
48
|
-
- 3
|
49
|
-
- 0
|
50
|
-
version: 3.3.0
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 4.4.0
|
51
38
|
type: :development
|
52
|
-
version_requirements: *id002
|
53
|
-
- !ruby/object:Gem::Dependency
|
54
|
-
name: rdoc
|
55
39
|
prerelease: false
|
56
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
41
|
none: false
|
58
|
-
requirements:
|
42
|
+
requirements:
|
59
43
|
- - ~>
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 4.4.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rdoc
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.12'
|
66
54
|
type: :development
|
67
|
-
|
68
|
-
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.12'
|
62
|
+
- !ruby/object:Gem::Dependency
|
69
63
|
name: zip
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.0.2
|
70
|
+
type: :development
|
70
71
|
prerelease: false
|
71
|
-
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
73
|
none: false
|
73
|
-
requirements:
|
74
|
+
requirements:
|
74
75
|
- - ~>
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
hash: 11
|
77
|
-
segments:
|
78
|
-
- 2
|
79
|
-
- 0
|
80
|
-
- 2
|
76
|
+
- !ruby/object:Gem::Version
|
81
77
|
version: 2.0.2
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: json
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.7.6
|
82
86
|
type: :development
|
83
|
-
|
84
|
-
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.7.6
|
94
|
+
description: KJess is a pure ruby Kestrel client that supports Kestrel's Memcache
|
95
|
+
style protocol.
|
85
96
|
email: jeremy@copiousfreetime.org
|
86
97
|
executables: []
|
87
|
-
|
88
98
|
extensions: []
|
89
|
-
|
90
|
-
extra_rdoc_files:
|
99
|
+
extra_rdoc_files:
|
91
100
|
- HISTORY.rdoc
|
92
101
|
- Manifest.txt
|
93
102
|
- README.rdoc
|
94
|
-
files:
|
103
|
+
files:
|
95
104
|
- CONTRIBUTING.md
|
96
105
|
- HISTORY.rdoc
|
97
106
|
- LICENSE
|
@@ -130,10 +139,12 @@ files:
|
|
130
139
|
- lib/kjess/response/reloaded_config.rb
|
131
140
|
- lib/kjess/response/server_error.rb
|
132
141
|
- lib/kjess/response/stats.rb
|
142
|
+
- lib/kjess/response/status.rb
|
133
143
|
- lib/kjess/response/stored.rb
|
134
144
|
- lib/kjess/response/unknown.rb
|
135
145
|
- lib/kjess/response/value.rb
|
136
146
|
- lib/kjess/response/version.rb
|
147
|
+
- lib/kjess/socket.rb
|
137
148
|
- lib/kjess/stats_cache.rb
|
138
149
|
- spec/client_spec.rb
|
139
150
|
- spec/kestrel_server.rb
|
@@ -147,41 +158,34 @@ files:
|
|
147
158
|
- tasks/kestrel.rake
|
148
159
|
homepage: http://github.com/copiousfreetime/kjess
|
149
160
|
licenses: []
|
150
|
-
|
151
161
|
post_install_message:
|
152
|
-
rdoc_options:
|
162
|
+
rdoc_options:
|
153
163
|
- --main
|
154
164
|
- README.rdoc
|
155
165
|
- --markup
|
156
166
|
- tomdoc
|
157
|
-
require_paths:
|
167
|
+
require_paths:
|
158
168
|
- lib
|
159
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
169
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
170
|
none: false
|
161
|
-
requirements:
|
162
|
-
- -
|
163
|
-
- !ruby/object:Gem::Version
|
164
|
-
|
165
|
-
|
166
|
-
- 0
|
167
|
-
version: "0"
|
168
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ! '>='
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
176
|
none: false
|
170
|
-
requirements:
|
171
|
-
- -
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
|
174
|
-
segments:
|
175
|
-
- 0
|
176
|
-
version: "0"
|
177
|
+
requirements:
|
178
|
+
- - ! '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
177
181
|
requirements: []
|
178
|
-
|
179
182
|
rubyforge_project:
|
180
183
|
rubygems_version: 1.8.24
|
181
184
|
signing_key:
|
182
185
|
specification_version: 3
|
183
|
-
summary: KJess is a pure ruby Kestrel client that supports Kestrel's Memcache style
|
184
|
-
|
186
|
+
summary: KJess is a pure ruby Kestrel client that supports Kestrel's Memcache style
|
187
|
+
protocol.
|
188
|
+
test_files:
|
185
189
|
- spec/client_spec.rb
|
186
190
|
- spec/kestrel_server.rb
|
187
191
|
- spec/request/set_spec.rb
|