kjess 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|