raindrops 0.5.0 → 0.6.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/GIT-VERSION-GEN +1 -1
- data/examples/zbatery.conf.rb +1 -1
- data/ext/raindrops/extconf.rb +2 -3
- data/ext/raindrops/linux_inet_diag.c +28 -6
- data/lib/raindrops/watcher.rb +37 -18
- data/test/test_inet_diag_socket.rb +3 -0
- data/test/test_watcher.rb +14 -0
- metadata +19 -3
data/GIT-VERSION-GEN
CHANGED
data/examples/zbatery.conf.rb
CHANGED
@@ -8,6 +8,6 @@ log_dir = "/var/log/zbatery"
|
|
8
8
|
if File.writable?(log_dir) && File.directory?(log_dir)
|
9
9
|
stderr_path "#{log_dir}/raindrops-demo.stderr.log"
|
10
10
|
stdout_path "#{log_dir}/raindrops-demo.stdout.log"
|
11
|
-
user "www-data", "www-data"
|
12
11
|
listen "/tmp/.raindrops"
|
12
|
+
pid "/tmp/.raindrops.pid"
|
13
13
|
end
|
data/ext/raindrops/extconf.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
|
3
|
+
dir_config('atomic_ops')
|
3
4
|
have_func('mmap', 'sys/mman.h') or abort 'mmap() not found'
|
4
5
|
have_func('munmap', 'sys/mman.h') or abort 'munmap() not found'
|
5
6
|
|
@@ -8,8 +9,8 @@ have_func('mremap', 'sys/mman.h')
|
|
8
9
|
|
9
10
|
$CPPFLAGS += " -D_BSD_SOURCE -D_XOPEN_SOURCE=600 "
|
10
11
|
have_func("getpagesize", "unistd.h")
|
11
|
-
have_func("rb_struct_alloc_noinit")
|
12
12
|
have_func('rb_thread_blocking_region')
|
13
|
+
have_func('rb_thread_io_blocking_region')
|
13
14
|
|
14
15
|
checking_for "GCC 4+ atomic builtins" do
|
15
16
|
src = <<SRC
|
@@ -43,6 +44,4 @@ Users of Debian-based distros may run:
|
|
43
44
|
|
44
45
|
apt-get install libatomic-ops-dev
|
45
46
|
SRC
|
46
|
-
|
47
|
-
dir_config('raindrops')
|
48
47
|
create_makefile('raindrops_ext')
|
@@ -15,6 +15,7 @@
|
|
15
15
|
/* partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
|
16
16
|
#ifndef HAVE_RB_THREAD_BLOCKING_REGION
|
17
17
|
# include <rubysig.h>
|
18
|
+
# define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
|
18
19
|
typedef void rb_unblock_function_t(void *);
|
19
20
|
typedef VALUE rb_blocking_function_t(void *);
|
20
21
|
static VALUE
|
@@ -32,12 +33,18 @@ rb_thread_blocking_region(
|
|
32
33
|
}
|
33
34
|
#endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
|
34
35
|
|
36
|
+
#ifndef HAVE_RB_THREAD_IO_BLOCKING_REGION
|
37
|
+
# define rb_thread_io_blocking_region(fn,data,fd) \
|
38
|
+
rb_thread_blocking_region((fn),(data),RUBY_UBF_IO,0)
|
39
|
+
#endif /* HAVE_RB_THREAD_IO_BLOCKING_REGION */
|
40
|
+
|
35
41
|
#include <assert.h>
|
36
42
|
#include <errno.h>
|
37
43
|
#include <sys/socket.h>
|
38
44
|
#include <sys/types.h>
|
39
45
|
#include <netdb.h>
|
40
46
|
#include <unistd.h>
|
47
|
+
#include <fcntl.h>
|
41
48
|
#include <string.h>
|
42
49
|
#include <asm/types.h>
|
43
50
|
#include <netinet/in.h>
|
@@ -69,6 +76,21 @@ struct nogvl_args {
|
|
69
76
|
int fd;
|
70
77
|
};
|
71
78
|
|
79
|
+
#ifdef SOCK_CLOEXEC
|
80
|
+
# define my_SOCK_RAW (SOCK_RAW|SOCK_CLOEXEC)
|
81
|
+
# define FORCE_CLOEXEC(v) (v)
|
82
|
+
#else
|
83
|
+
# define my_SOCK_RAW SOCK_RAW
|
84
|
+
static VALUE FORCE_CLOEXEC(VALUE io)
|
85
|
+
{
|
86
|
+
int fd = my_fileno(io);
|
87
|
+
int flags = fcntl(fd, F_SETFD, FD_CLOEXEC);
|
88
|
+
if (flags == -1)
|
89
|
+
rb_sys_fail("fcntl(F_SETFD, FD_CLOEXEC)");
|
90
|
+
return io;
|
91
|
+
}
|
92
|
+
#endif
|
93
|
+
|
72
94
|
/*
|
73
95
|
* call-seq:
|
74
96
|
* Raindrops::InetDiagSocket.new -> Socket
|
@@ -78,11 +100,12 @@ struct nogvl_args {
|
|
78
100
|
static VALUE ids_s_new(VALUE klass)
|
79
101
|
{
|
80
102
|
VALUE argv[3];
|
103
|
+
|
81
104
|
argv[0] = INT2NUM(AF_NETLINK);
|
82
|
-
argv[1] = INT2NUM(
|
105
|
+
argv[1] = INT2NUM(my_SOCK_RAW);
|
83
106
|
argv[2] = INT2NUM(NETLINK_INET_DIAG);
|
84
107
|
|
85
|
-
return rb_call_super(3, argv);
|
108
|
+
return FORCE_CLOEXEC(rb_call_super(3, argv));
|
86
109
|
}
|
87
110
|
|
88
111
|
/* creates a Ruby ListenStats Struct based on our internal listen_stats */
|
@@ -180,8 +203,7 @@ static struct listen_stats *stats_for(st_table *table, struct inet_diag_msg *r)
|
|
180
203
|
case AF_INET6: {
|
181
204
|
struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&ss;
|
182
205
|
in6->sin6_port = r->id.idiag_sport;
|
183
|
-
memcpy(&in6->sin6_addr.
|
184
|
-
&r->id.idiag_src, sizeof(__be32[4]));
|
206
|
+
memcpy(&in6->sin6_addr, &r->id.idiag_src, sizeof(__be32[4]));
|
185
207
|
keylen = INET6_ADDRSTRLEN;
|
186
208
|
/* [ ] */
|
187
209
|
alloca_len = 1 + keylen + 1 + 1 + portlen;
|
@@ -543,7 +565,7 @@ static VALUE tcp_stats(struct nogvl_args *args, VALUE addr)
|
|
543
565
|
gen_bytecode(&args->iov[2], &query_addr);
|
544
566
|
|
545
567
|
memset(&args->stats, 0, sizeof(struct listen_stats));
|
546
|
-
nl_errcheck(
|
568
|
+
nl_errcheck(rb_thread_io_blocking_region(diag, args, args->fd));
|
547
569
|
|
548
570
|
return rb_listen_stats(&args->stats);
|
549
571
|
}
|
@@ -610,7 +632,7 @@ static VALUE tcp_listener_stats(int argc, VALUE *argv, VALUE self)
|
|
610
632
|
"addr must be an array of strings, a string, or nil");
|
611
633
|
}
|
612
634
|
|
613
|
-
nl_errcheck(
|
635
|
+
nl_errcheck(rb_thread_io_blocking_region(diag, &args, args.fd));
|
614
636
|
|
615
637
|
st_foreach(args.table, NIL_P(addrs) ? st_to_hash : st_AND_hash, rv);
|
616
638
|
st_free_table(args.table);
|
data/lib/raindrops/watcher.rb
CHANGED
@@ -35,28 +35,28 @@ require "aggregate"
|
|
35
35
|
# Returns a plain text summary + histogram with X-* HTTP headers for
|
36
36
|
# active connections.
|
37
37
|
#
|
38
|
-
# e.g.: curl http://
|
38
|
+
# e.g.: curl http://raindrops-demo.bogomips.org/active/0.0.0.0%3A80.txt
|
39
39
|
#
|
40
40
|
# === GET /active/$LISTENER.html
|
41
41
|
#
|
42
42
|
# Returns an HTML summary + histogram with X-* HTTP headers for
|
43
43
|
# active connections.
|
44
44
|
#
|
45
|
-
# e.g.: curl http://
|
45
|
+
# e.g.: curl http://raindrops-demo.bogomips.org/active/0.0.0.0%3A80.html
|
46
46
|
#
|
47
47
|
# === GET /queued/$LISTENER.txt
|
48
48
|
#
|
49
49
|
# Returns a plain text summary + histogram with X-* HTTP headers for
|
50
50
|
# queued connections.
|
51
51
|
#
|
52
|
-
# e.g.: curl http://
|
52
|
+
# e.g.: curl http://raindrops-demo.bogomips.org/queued/0.0.0.0%3A80.txt
|
53
53
|
#
|
54
54
|
# === GET /queued/$LISTENER.html
|
55
55
|
#
|
56
56
|
# Returns an HTML summary + histogram with X-* HTTP headers for
|
57
57
|
# queued connections.
|
58
58
|
#
|
59
|
-
# e.g.: curl http://
|
59
|
+
# e.g.: curl http://raindrops-demo.bogomips.org/queued/0.0.0.0%3A80.html
|
60
60
|
#
|
61
61
|
# === GET /tail/$LISTENER.txt?active_min=1&queued_min=1
|
62
62
|
#
|
@@ -90,7 +90,7 @@ require "aggregate"
|
|
90
90
|
#
|
91
91
|
# There is a server running this app at http://raindrops-demo.bogomips.org/
|
92
92
|
# The Raindrops::Middleware demo is also accessible at
|
93
|
-
#
|
93
|
+
# http://raindrops-demo.bogomips.org/_raindrops
|
94
94
|
#
|
95
95
|
# The demo server is only limited to 30 users, so be sure not to abuse it
|
96
96
|
# by using the /tail/ endpoint too much.
|
@@ -99,6 +99,7 @@ class Raindrops::Watcher
|
|
99
99
|
attr_reader :snapshot
|
100
100
|
include Rack::Utils
|
101
101
|
include Raindrops::Linux
|
102
|
+
DOC_URL = "http://raindrops.bogomips.org/Raindrops/Watcher.html"
|
102
103
|
|
103
104
|
def initialize(opts = {})
|
104
105
|
@tcp_listeners = @unix_listeners = nil
|
@@ -167,14 +168,14 @@ class Raindrops::Watcher
|
|
167
168
|
def active_stats(addr) # :nodoc:
|
168
169
|
@lock.synchronize do
|
169
170
|
tmp = @active[addr] or return
|
170
|
-
[ @resets[addr], tmp.dup ]
|
171
|
+
[ @snapshot[0], @resets[addr], tmp.dup ]
|
171
172
|
end
|
172
173
|
end
|
173
174
|
|
174
175
|
def queued_stats(addr) # :nodoc:
|
175
176
|
@lock.synchronize do
|
176
177
|
tmp = @queued[addr] or return
|
177
|
-
[ @resets[addr], tmp.dup ]
|
178
|
+
[ @snapshot[0], @resets[addr], tmp.dup ]
|
178
179
|
end
|
179
180
|
end
|
180
181
|
|
@@ -199,16 +200,17 @@ class Raindrops::Watcher
|
|
199
200
|
end
|
200
201
|
|
201
202
|
def histogram_txt(agg)
|
202
|
-
reset_at, agg = *agg
|
203
|
+
updated_at, reset_at, agg = *agg
|
203
204
|
headers = agg_to_hash(reset_at, agg)
|
204
205
|
body = agg.to_s
|
205
206
|
headers["Content-Type"] = "text/plain"
|
207
|
+
headers["Expires"] = (updated_at + @delay).httpdate
|
206
208
|
headers["Content-Length"] = bytesize(body).to_s
|
207
209
|
[ 200, headers, [ body ] ]
|
208
210
|
end
|
209
211
|
|
210
212
|
def histogram_html(agg, addr)
|
211
|
-
reset_at, agg = *agg
|
213
|
+
updated_at, reset_at, agg = *agg
|
212
214
|
headers = agg_to_hash(reset_at, agg)
|
213
215
|
body = "<html>" \
|
214
216
|
"<head><title>#{hostname} - #{escape_html addr}</title></head>" \
|
@@ -220,6 +222,7 @@ class Raindrops::Watcher
|
|
220
222
|
"<input type='submit' name='x' value='reset' /></form>" \
|
221
223
|
"</body>"
|
222
224
|
headers["Content-Type"] = "text/html"
|
225
|
+
headers["Expires"] = (updated_at + @delay).httpdate
|
223
226
|
headers["Content-Length"] = bytesize(body).to_s
|
224
227
|
[ 200, headers, [ body ] ]
|
225
228
|
end
|
@@ -259,7 +262,7 @@ class Raindrops::Watcher
|
|
259
262
|
when %r{\A/reset/(.+)\z}
|
260
263
|
reset!(env, unescape($1))
|
261
264
|
else
|
262
|
-
|
265
|
+
not_found
|
263
266
|
end
|
264
267
|
end
|
265
268
|
|
@@ -285,6 +288,7 @@ class Raindrops::Watcher
|
|
285
288
|
headers = {
|
286
289
|
"Content-Type" => "text/html",
|
287
290
|
"Last-Modified" => updated_at.httpdate,
|
291
|
+
"Expires" => (updated_at + @delay).httpdate,
|
288
292
|
}
|
289
293
|
body = "<html><head>" \
|
290
294
|
"<title>#{hostname} - all interfaces</title>" \
|
@@ -295,13 +299,24 @@ class Raindrops::Watcher
|
|
295
299
|
all.map do |addr,stats|
|
296
300
|
e_addr = escape addr
|
297
301
|
"<tr>" \
|
298
|
-
"<td><a href='/tail/#{e_addr}.txt'
|
299
|
-
|
300
|
-
|
302
|
+
"<td><a href='/tail/#{e_addr}.txt' " \
|
303
|
+
"title='"tail" output in real time'" \
|
304
|
+
">#{escape_html addr}</a></td>" \
|
305
|
+
"<td><a href='/active/#{e_addr}.html' " \
|
306
|
+
"title='show active connection stats'>#{stats.active}</a></td>" \
|
307
|
+
"<td><a href='/queued/#{e_addr}.html' " \
|
308
|
+
"title='show queued connection stats'>#{stats.queued}</a></td>" \
|
301
309
|
"<td><form action='/reset/#{e_addr}' method='post'>" \
|
302
|
-
"<input
|
310
|
+
"<input title='reset statistics' " \
|
311
|
+
"type='submit' name='x' value='x' /></form></td>" \
|
303
312
|
"</tr>" \
|
304
|
-
end.join << "</table
|
313
|
+
end.join << "</table>" \
|
314
|
+
"<p>" \
|
315
|
+
"This is running the #{self.class}</a> service, see " \
|
316
|
+
"<a href='#{DOC_URL}'>#{DOC_URL}</a> " \
|
317
|
+
"for more information and options." \
|
318
|
+
"</p>" \
|
319
|
+
"</body></html>"
|
305
320
|
headers["Content-Length"] = bytesize(body).to_s
|
306
321
|
[ 200, headers, [ body ] ]
|
307
322
|
end
|
@@ -309,7 +324,6 @@ class Raindrops::Watcher
|
|
309
324
|
def tail(addr, env)
|
310
325
|
Tailer.new(self, addr, env).finish
|
311
326
|
end
|
312
|
-
# :startdoc:
|
313
327
|
|
314
328
|
# This is the response body returned for "/tail/$ADDRESS.txt". This
|
315
329
|
# must use a multi-threaded Rack server with streaming response support.
|
@@ -333,7 +347,11 @@ class Raindrops::Watcher
|
|
333
347
|
end
|
334
348
|
|
335
349
|
def finish
|
336
|
-
headers = {
|
350
|
+
headers = {
|
351
|
+
"Content-Type" => "text/plain",
|
352
|
+
"Cache-Control" => "no-transform",
|
353
|
+
"Expires" => Time.at(0).httpdate,
|
354
|
+
}
|
337
355
|
headers["Transfer-Encoding"] = "chunked" if @chunk
|
338
356
|
[ 200, headers, self ]
|
339
357
|
end
|
@@ -353,10 +371,11 @@ class Raindrops::Watcher
|
|
353
371
|
end
|
354
372
|
end
|
355
373
|
|
356
|
-
# shuts down the background thread
|
374
|
+
# shuts down the background thread, only for tests
|
357
375
|
def shutdown
|
358
376
|
@socket = nil
|
359
377
|
@thr.join if @thr
|
360
378
|
@thr = nil
|
361
379
|
end
|
380
|
+
# :startdoc:
|
362
381
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
require 'test/unit'
|
3
3
|
require 'raindrops'
|
4
|
+
require 'fcntl'
|
4
5
|
$stderr.sync = $stdout.sync = true
|
5
6
|
|
6
7
|
class TestInetDiagSocket < Test::Unit::TestCase
|
@@ -8,6 +9,8 @@ class TestInetDiagSocket < Test::Unit::TestCase
|
|
8
9
|
sock = Raindrops::InetDiagSocket.new
|
9
10
|
assert_kind_of Socket, sock
|
10
11
|
assert_kind_of Fixnum, sock.fileno
|
12
|
+
flags = sock.fcntl(Fcntl::F_GETFD)
|
13
|
+
assert_equal Fcntl::FD_CLOEXEC, flags & Fcntl::FD_CLOEXEC
|
11
14
|
assert_nil sock.close
|
12
15
|
end
|
13
16
|
end if RUBY_PLATFORM =~ /linux/
|
data/test/test_watcher.rb
CHANGED
@@ -50,6 +50,20 @@ class TestWatcher < Test::Unit::TestCase
|
|
50
50
|
check_headers(resp.headers)
|
51
51
|
end
|
52
52
|
|
53
|
+
def test_queued_txt
|
54
|
+
resp = @req.get "/queued/#@addr.txt"
|
55
|
+
assert_equal 200, resp.status.to_i
|
56
|
+
assert_equal "text/plain", resp.headers["Content-Type"]
|
57
|
+
check_headers(resp.headers)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_queued_html
|
61
|
+
resp = @req.get "/queued/#@addr.html"
|
62
|
+
assert_equal 200, resp.status.to_i
|
63
|
+
assert_equal "text/html", resp.headers["Content-Type"]
|
64
|
+
check_headers(resp.headers)
|
65
|
+
end
|
66
|
+
|
53
67
|
def test_reset
|
54
68
|
resp = @req.post "/reset/#@addr"
|
55
69
|
assert_equal 302, resp.status.to_i
|
metadata
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: raindrops
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 7
|
4
5
|
prerelease:
|
5
|
-
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 0.6.0
|
6
11
|
platform: ruby
|
7
12
|
authors:
|
8
13
|
- raindrops hackers
|
@@ -10,7 +15,7 @@ autorequire:
|
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
17
|
|
13
|
-
date: 2011-03-
|
18
|
+
date: 2011-03-21 00:00:00 +00:00
|
14
19
|
default_executable:
|
15
20
|
dependencies:
|
16
21
|
- !ruby/object:Gem::Dependency
|
@@ -21,6 +26,11 @@ dependencies:
|
|
21
26
|
requirements:
|
22
27
|
- - ~>
|
23
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
- 10
|
24
34
|
version: 1.0.10
|
25
35
|
type: :development
|
26
36
|
version_requirements: *id001
|
@@ -130,17 +140,23 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
140
|
requirements:
|
131
141
|
- - ">="
|
132
142
|
- !ruby/object:Gem::Version
|
143
|
+
hash: 3
|
144
|
+
segments:
|
145
|
+
- 0
|
133
146
|
version: "0"
|
134
147
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
148
|
none: false
|
136
149
|
requirements:
|
137
150
|
- - ">="
|
138
151
|
- !ruby/object:Gem::Version
|
152
|
+
hash: 3
|
153
|
+
segments:
|
154
|
+
- 0
|
139
155
|
version: "0"
|
140
156
|
requirements: []
|
141
157
|
|
142
158
|
rubyforge_project: rainbows
|
143
|
-
rubygems_version: 1.6.
|
159
|
+
rubygems_version: 1.6.1
|
144
160
|
signing_key:
|
145
161
|
specification_version: 3
|
146
162
|
summary: real-time stats for preforking Rack servers
|