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 CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.5.0.GIT
4
+ DEF_VER=v0.6.0.GIT
5
5
 
6
6
  LF='
7
7
  '
@@ -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
@@ -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(SOCK_RAW);
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.in6_u.u6_addr32,
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(rb_thread_blocking_region(diag, args, 0, 0));
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(rb_thread_blocking_region(diag, &args, NULL, 0));
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);
@@ -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://example.com/active/0.0.0.0%3A80.txt
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://example.com/active/0.0.0.0%3A80.html
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://example.com/queued/0.0.0.0%3A80.txt
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://example.com/queued/0.0.0.0%3A80.html
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
- # http://raindrops-demo.bogomips.org/_raindrops
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
- Rack::Response.new(["Not Found"], 404).finish
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'>#{escape_html addr}</a></td>" \
299
- "<td><a href='/active/#{e_addr}.html'>#{stats.active}</a></td>" \
300
- "<td><a href='/queued/#{e_addr}.html'>#{stats.queued}</a></td>" \
302
+ "<td><a href='/tail/#{e_addr}.txt' " \
303
+ "title='&quot;tail&quot; 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 type='submit' name='x' value='x' /></form></td>" \
310
+ "<input title='reset statistics' " \
311
+ "type='submit' name='x' value='x' /></form></td>" \
303
312
  "</tr>" \
304
- end.join << "</table></body></html>"
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 = { "Content-Type" => "text/plain" }
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
- version: 0.5.0
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-17 00:00:00 +00:00
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.2
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