raindrops 0.13.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.document +1 -2
  3. data/.gitattributes +4 -0
  4. data/.gitignore +1 -1
  5. data/.manifest +7 -5
  6. data/.olddoc.yml +16 -0
  7. data/GIT-VERSION-FILE +1 -1
  8. data/GIT-VERSION-GEN +1 -1
  9. data/GNUmakefile +1 -2
  10. data/LATEST +6 -11
  11. data/LICENSE +3 -3
  12. data/NEWS +158 -0
  13. data/README +33 -40
  14. data/TODO +2 -0
  15. data/archive/.gitignore +3 -0
  16. data/archive/slrnpull.conf +4 -0
  17. data/examples/linux-listener-stats.rb +1 -2
  18. data/examples/watcher_demo.ru +1 -1
  19. data/examples/yahns.conf.rb +30 -0
  20. data/examples/zbatery.conf.rb +4 -1
  21. data/ext/raindrops/extconf.rb +107 -2
  22. data/ext/raindrops/linux_inet_diag.c +94 -101
  23. data/ext/raindrops/raindrops.c +75 -21
  24. data/ext/raindrops/tcp_info.c +245 -0
  25. data/lib/raindrops/aggregate/last_data_recv.rb +1 -5
  26. data/lib/raindrops/aggregate/pmq.rb +23 -17
  27. data/lib/raindrops/aggregate.rb +1 -1
  28. data/lib/raindrops/linux.rb +5 -6
  29. data/lib/raindrops/middleware/proxy.rb +2 -2
  30. data/lib/raindrops/middleware.rb +4 -6
  31. data/lib/raindrops/watcher.rb +13 -13
  32. data/lib/raindrops.rb +25 -1
  33. data/pkg.mk +26 -50
  34. data/raindrops.gemspec +14 -21
  35. data/test/ipv6_enabled.rb +4 -4
  36. data/test/test_aggregate_pmq.rb +1 -1
  37. data/test/test_inet_diag_socket.rb +1 -1
  38. data/test/test_last_data_recv_unicorn.rb +1 -1
  39. data/test/test_linux.rb +10 -2
  40. data/test/test_linux_all_tcp_listen_stats_leak.rb +2 -2
  41. data/test/test_linux_ipv6.rb +8 -0
  42. data/test/test_raindrops.rb +43 -1
  43. data/test/{test_linux_tcp_info.rb → test_tcp_info.rb} +34 -14
  44. data/test/test_watcher.rb +15 -10
  45. metadata +59 -171
  46. data/.wrongdoc.yml +0 -6
  47. data/ChangeLog +0 -1920
  48. data/Rakefile +0 -28
  49. data/ext/raindrops/linux_tcp_info.c +0 -173
@@ -0,0 +1,245 @@
1
+ #include <ruby.h>
2
+ #include <sys/socket.h>
3
+ #include <netinet/in.h>
4
+ #if defined(HAVE_LINUX_TCP_H)
5
+ # include <linux/tcp.h>
6
+ #else
7
+ # if defined(HAVE_NETINET_TCP_H)
8
+ # include <netinet/tcp.h>
9
+ # endif
10
+ # if defined(HAVE_NETINET_TCP_FSM_H)
11
+ # include <netinet/tcp_fsm.h>
12
+ # endif
13
+ #endif
14
+
15
+ #ifdef HAVE_TYPE_STRUCT_TCP_INFO
16
+ #include "my_fileno.h"
17
+
18
+ CFUNC_tcp_info_tcpi_state
19
+ CFUNC_tcp_info_tcpi_ca_state
20
+ CFUNC_tcp_info_tcpi_retransmits
21
+ CFUNC_tcp_info_tcpi_probes
22
+ CFUNC_tcp_info_tcpi_backoff
23
+ CFUNC_tcp_info_tcpi_options
24
+ CFUNC_tcp_info_tcpi_snd_wscale
25
+ CFUNC_tcp_info_tcpi_rcv_wscale
26
+ CFUNC_tcp_info_tcpi_rto
27
+ CFUNC_tcp_info_tcpi_ato
28
+ CFUNC_tcp_info_tcpi_snd_mss
29
+ CFUNC_tcp_info_tcpi_rcv_mss
30
+ CFUNC_tcp_info_tcpi_unacked
31
+ CFUNC_tcp_info_tcpi_sacked
32
+ CFUNC_tcp_info_tcpi_lost
33
+ CFUNC_tcp_info_tcpi_retrans
34
+ CFUNC_tcp_info_tcpi_fackets
35
+ CFUNC_tcp_info_tcpi_last_data_sent
36
+ CFUNC_tcp_info_tcpi_last_ack_sent
37
+ CFUNC_tcp_info_tcpi_last_data_recv
38
+ CFUNC_tcp_info_tcpi_last_ack_recv
39
+ CFUNC_tcp_info_tcpi_pmtu
40
+ CFUNC_tcp_info_tcpi_rcv_ssthresh
41
+ CFUNC_tcp_info_tcpi_rtt
42
+ CFUNC_tcp_info_tcpi_rttvar
43
+ CFUNC_tcp_info_tcpi_snd_ssthresh
44
+ CFUNC_tcp_info_tcpi_snd_cwnd
45
+ CFUNC_tcp_info_tcpi_advmss
46
+ CFUNC_tcp_info_tcpi_reordering
47
+ CFUNC_tcp_info_tcpi_rcv_rtt
48
+ CFUNC_tcp_info_tcpi_rcv_space
49
+ CFUNC_tcp_info_tcpi_total_retrans
50
+
51
+ static size_t tcpi_memsize(const void *ptr)
52
+ {
53
+ return sizeof(struct tcp_info);
54
+ }
55
+
56
+ static const rb_data_type_t tcpi_type = {
57
+ "tcp_info",
58
+ { NULL, RUBY_TYPED_DEFAULT_FREE, tcpi_memsize, /* reserved */ },
59
+ /* parent, data, [ flags ] */
60
+ };
61
+
62
+ static VALUE alloc(VALUE klass)
63
+ {
64
+ struct tcp_info *info;
65
+
66
+ return TypedData_Make_Struct(klass, struct tcp_info, &tcpi_type, info);
67
+ }
68
+
69
+ /*
70
+ * call-seq:
71
+ *
72
+ * Raindrops::TCP_Info.new(tcp_socket) -> TCP_Info object
73
+ *
74
+ * Reads a TCP_Info object from any given +tcp_socket+. See the tcp(7)
75
+ * manpage and /usr/include/linux/tcp.h for more details.
76
+ */
77
+ static VALUE init(VALUE self, VALUE io)
78
+ {
79
+ int fd = my_fileno(io);
80
+ struct tcp_info *info = DATA_PTR(self);
81
+ socklen_t len = (socklen_t)sizeof(struct tcp_info);
82
+ int rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, info, &len);
83
+
84
+ if (rc != 0)
85
+ rb_sys_fail("getsockopt");
86
+
87
+ return self;
88
+ }
89
+
90
+ void Init_raindrops_tcp_info(void)
91
+ {
92
+ VALUE cRaindrops = rb_define_class("Raindrops", rb_cObject);
93
+ VALUE cTCP_Info;
94
+
95
+ /*
96
+ * Document-class: Raindrops::TCP_Info
97
+ *
98
+ * This is used to wrap "struct tcp_info" as described in tcp(7)
99
+ * and /usr/include/linux/tcp.h. The following readers methods
100
+ * are defined corresponding to the "tcpi_" fields in the
101
+ * tcp_info struct.
102
+ *
103
+ * As of raindrops 0.18.0+, this is supported on FreeBSD and OpenBSD
104
+ * systems as well as Linux, although not all fields exist or
105
+ * match the documentation, below.
106
+ *
107
+ * In particular, the +last_data_recv+ field is useful for measuring
108
+ * the amount of time a client spent in the listen queue before
109
+ * +accept()+, but only if +TCP_DEFER_ACCEPT+ is used with the
110
+ * listen socket (it is on by default in Unicorn).
111
+ *
112
+ * - state
113
+ * - ca_state
114
+ * - retransmits
115
+ * - probes
116
+ * - backoff
117
+ * - options
118
+ * - snd_wscale
119
+ * - rcv_wscale
120
+ * - rto
121
+ * - ato
122
+ * - snd_mss
123
+ * - rcv_mss
124
+ * - unacked
125
+ * - sacked
126
+ * - lost
127
+ * - retrans
128
+ * - fackets
129
+ * - last_data_sent
130
+ * - last_ack_sent
131
+ * - last_data_recv
132
+ * - last_ack_recv
133
+ * - pmtu
134
+ * - rcv_ssthresh
135
+ * - rtt
136
+ * - rttvar
137
+ * - snd_ssthresh
138
+ * - snd_cwnd
139
+ * - advmss
140
+ * - reordering
141
+ * - rcv_rtt
142
+ * - rcv_space
143
+ * - total_retrans
144
+ *
145
+ * https://kernel.org/doc/man-pages/online/pages/man7/tcp.7.html
146
+ */
147
+ cTCP_Info = rb_define_class_under(cRaindrops, "TCP_Info", rb_cObject);
148
+ rb_define_alloc_func(cTCP_Info, alloc);
149
+ rb_define_private_method(cTCP_Info, "initialize", init, 1);
150
+
151
+ /*
152
+ * Document-method: Raindrops::TCP_Info#get!
153
+ *
154
+ * call-seq:
155
+ *
156
+ * info = Raindrops::TCP_Info.new(tcp_socket)
157
+ * info.get!(tcp_socket)
158
+ *
159
+ * Update an existing TCP_Info objects with the latest stats
160
+ * from the given socket. This even allows sharing TCP_Info
161
+ * objects between different sockets to avoid garbage.
162
+ */
163
+ rb_define_method(cTCP_Info, "get!", init, 1);
164
+
165
+ DEFINE_METHOD_tcp_info_tcpi_state;
166
+ DEFINE_METHOD_tcp_info_tcpi_ca_state;
167
+ DEFINE_METHOD_tcp_info_tcpi_retransmits;
168
+ DEFINE_METHOD_tcp_info_tcpi_probes;
169
+ DEFINE_METHOD_tcp_info_tcpi_backoff;
170
+ DEFINE_METHOD_tcp_info_tcpi_options;
171
+ DEFINE_METHOD_tcp_info_tcpi_snd_wscale;
172
+ DEFINE_METHOD_tcp_info_tcpi_rcv_wscale;
173
+ DEFINE_METHOD_tcp_info_tcpi_rto;
174
+ DEFINE_METHOD_tcp_info_tcpi_ato;
175
+ DEFINE_METHOD_tcp_info_tcpi_snd_mss;
176
+ DEFINE_METHOD_tcp_info_tcpi_rcv_mss;
177
+ DEFINE_METHOD_tcp_info_tcpi_unacked;
178
+ DEFINE_METHOD_tcp_info_tcpi_sacked;
179
+ DEFINE_METHOD_tcp_info_tcpi_lost;
180
+ DEFINE_METHOD_tcp_info_tcpi_retrans;
181
+ DEFINE_METHOD_tcp_info_tcpi_fackets;
182
+ DEFINE_METHOD_tcp_info_tcpi_last_data_sent;
183
+ DEFINE_METHOD_tcp_info_tcpi_last_ack_sent;
184
+ DEFINE_METHOD_tcp_info_tcpi_last_data_recv;
185
+ DEFINE_METHOD_tcp_info_tcpi_last_ack_recv;
186
+ DEFINE_METHOD_tcp_info_tcpi_pmtu;
187
+ DEFINE_METHOD_tcp_info_tcpi_rcv_ssthresh;
188
+ DEFINE_METHOD_tcp_info_tcpi_rtt;
189
+ DEFINE_METHOD_tcp_info_tcpi_rttvar;
190
+ DEFINE_METHOD_tcp_info_tcpi_snd_ssthresh;
191
+ DEFINE_METHOD_tcp_info_tcpi_snd_cwnd;
192
+ DEFINE_METHOD_tcp_info_tcpi_advmss;
193
+ DEFINE_METHOD_tcp_info_tcpi_reordering;
194
+ DEFINE_METHOD_tcp_info_tcpi_rcv_rtt;
195
+ DEFINE_METHOD_tcp_info_tcpi_rcv_space;
196
+ DEFINE_METHOD_tcp_info_tcpi_total_retrans;
197
+
198
+ #ifdef RAINDROPS_TCP_STATES_ALL_KNOWN
199
+
200
+ /*
201
+ * Document-const: Raindrops::TCP
202
+ *
203
+ * This is a frozen hash storing the numeric values
204
+ * maps platform-independent symbol keys to
205
+ * platform-dependent numeric values. These states
206
+ * are all valid values for the Raindrops::TCP_Info#state field.
207
+ *
208
+ * The platform-independent names of the keys in this hash are:
209
+ *
210
+ * - :ESTABLISHED
211
+ * - :SYN_SENT
212
+ * - :SYN_RECV
213
+ * - :FIN_WAIT1
214
+ * - :FIN_WAIT2
215
+ * - :TIME_WAIT
216
+ * - :CLOSE
217
+ * - :CLOSE_WAIT
218
+ * - :LAST_ACK
219
+ * - :LISTEN
220
+ * - :CLOSING
221
+ *
222
+ * This is only supported on platforms where TCP_Info is supported,
223
+ * currently FreeBSD, OpenBSD, and Linux-based systems.
224
+ */
225
+ {
226
+ #define TCPSET(n,v) rb_hash_aset(tcp, ID2SYM(rb_intern(#n)), INT2NUM(v))
227
+ VALUE tcp = rb_hash_new();
228
+ TCPSET(ESTABLISHED, RAINDROPS_TCP_ESTABLISHED);
229
+ TCPSET(SYN_SENT, RAINDROPS_TCP_SYN_SENT);
230
+ TCPSET(SYN_RECV, RAINDROPS_TCP_SYN_RECV);
231
+ TCPSET(FIN_WAIT1, RAINDROPS_TCP_FIN_WAIT1);
232
+ TCPSET(FIN_WAIT2, RAINDROPS_TCP_FIN_WAIT2);
233
+ TCPSET(TIME_WAIT, RAINDROPS_TCP_TIME_WAIT);
234
+ TCPSET(CLOSE, RAINDROPS_TCP_CLOSE);
235
+ TCPSET(CLOSE_WAIT, RAINDROPS_TCP_CLOSE_WAIT);
236
+ TCPSET(LAST_ACK, RAINDROPS_TCP_LAST_ACK);
237
+ TCPSET(LISTEN, RAINDROPS_TCP_LISTEN);
238
+ TCPSET(CLOSING, RAINDROPS_TCP_CLOSING);
239
+ #undef TCPSET
240
+ OBJ_FREEZE(tcp);
241
+ rb_define_const(cRaindrops, "TCP", tcp);
242
+ }
243
+ #endif
244
+ }
245
+ #endif /* HAVE_STRUCT_TCP_INFO */
@@ -13,10 +13,6 @@ require "socket"
13
13
  # - Kgio::TCPServer#kgio_accept
14
14
  # - Kgio::TCPServer#kgio_tryaccept
15
15
  module Raindrops::Aggregate::LastDataRecv
16
- # :stopdoc:
17
- TCP_Info = Raindrops::TCP_Info
18
- # :startdoc:
19
-
20
16
  # The integer value of +last_data_recv+ is sent to this object.
21
17
  # This is usually a duck type compatible with the \Aggregate class,
22
18
  # but can be *anything* that accepts the *<<* method.
@@ -78,7 +74,7 @@ module Raindrops::Aggregate::LastDataRecv
78
74
  # +last_data_recv+ to be accurate
79
75
  def count!(io)
80
76
  if io
81
- x = TCP_Info.new(io)
77
+ x = Raindrops::TCP_Info.new(io)
82
78
  @raindrops_aggregate << x.last_data_recv
83
79
  end
84
80
  io
@@ -3,10 +3,10 @@ require "tempfile"
3
3
  require "aggregate"
4
4
  require "posix_mq"
5
5
  require "fcntl"
6
- require "io/extra"
7
6
  require "thread"
7
+ require "stringio"
8
8
 
9
- # \Aggregate + POSIX message queues support for Ruby 1.9 and \Linux
9
+ # \Aggregate + POSIX message queues support for Ruby 1.9+ and \Linux
10
10
  #
11
11
  # This class is duck-type compatible with \Aggregate and allows us to
12
12
  # aggregate and share statistics from multiple processes/threads aided
@@ -14,12 +14,11 @@ require "thread"
14
14
  # Raindrops::LastDataRecv Rack application, but can be used independently
15
15
  # on compatible Runtimes.
16
16
  #
17
- # Unlike the core of raindrops, this is only supported on Ruby 1.9 and
18
- # Linux 2.6. Using this class requires the following additional RubyGems
17
+ # Unlike the core of raindrops, this is only supported on Ruby 1.9+ and
18
+ # Linux 2.6+. Using this class requires the following additional RubyGems
19
19
  # or libraries:
20
20
  #
21
21
  # * aggregate (tested with 0.2.2)
22
- # * io-extra (tested with 1.2.3)
23
22
  # * posix_mq (tested with 1.0.0)
24
23
  #
25
24
  # == Design
@@ -39,9 +38,9 @@ class Raindrops::Aggregate::PMQ
39
38
  # :stopdoc:
40
39
  # These constants are for Linux. This is designed for aggregating
41
40
  # TCP_INFO.
42
- RDLOCK = [ Fcntl::F_RDLCK ].pack("s @256")
43
- WRLOCK = [ Fcntl::F_WRLCK ].pack("s @256")
44
- UNLOCK = [ Fcntl::F_UNLCK ].pack("s @256")
41
+ RDLOCK = [ Fcntl::F_RDLCK ].pack("s @256".freeze).freeze
42
+ WRLOCK = [ Fcntl::F_WRLCK ].pack("s @256".freeze).freeze
43
+ UNLOCK = [ Fcntl::F_UNLCK ].pack("s @256".freeze).freeze
45
44
  # :startdoc:
46
45
 
47
46
  # returns the number of dropped messages sent to a POSIX message
@@ -84,6 +83,7 @@ class Raindrops::Aggregate::PMQ
84
83
  @wr = File.open(t.path, "wb")
85
84
  @rd = File.open(t.path, "rb")
86
85
  end
86
+ @wr.sync = true
87
87
  @cached_aggregate = @aggregate
88
88
  flush_master
89
89
  @mq_send = if opts[:lossy]
@@ -142,8 +142,8 @@ class Raindrops::Aggregate::PMQ
142
142
  warn "Unhandled exception in #{__FILE__}:#{__LINE__}: #{e}"
143
143
  break
144
144
  end while true
145
- ensure
146
- flush_master
145
+ ensure
146
+ flush_master
147
147
  end
148
148
 
149
149
  # Loads the last shared \Aggregate from the master thread/process
@@ -151,7 +151,10 @@ class Raindrops::Aggregate::PMQ
151
151
  @cached_aggregate ||= begin
152
152
  flush
153
153
  Marshal.load(synchronize(@rd, RDLOCK) do |rd|
154
- IO.pread rd.fileno, rd.stat.size, 0
154
+ dst = StringIO.new
155
+ dst.binmode
156
+ IO.copy_stream(rd, dst, rd.size, 0)
157
+ dst.string
155
158
  end)
156
159
  end
157
160
  end
@@ -163,7 +166,8 @@ class Raindrops::Aggregate::PMQ
163
166
  dump = Marshal.dump @aggregate
164
167
  synchronize(@wr, WRLOCK) do |wr|
165
168
  wr.truncate 0
166
- IO.pwrite wr.fileno, dump, 0
169
+ wr.rewind
170
+ wr.write(dump)
167
171
  end
168
172
  end
169
173
 
@@ -171,24 +175,26 @@ class Raindrops::Aggregate::PMQ
171
175
  # worker thread or process
172
176
  def stop_master_loop
173
177
  sleep 0.1 until mq_send(false)
174
- rescue Errno::EINTR
175
- retry
178
+ rescue Errno::EINTR
179
+ retry
176
180
  end
177
181
 
178
182
  def lock! io, type # :nodoc:
179
183
  io.fcntl Fcntl::F_SETLKW, type
180
- rescue Errno::EINTR
181
- retry
184
+ rescue Errno::EINTR
185
+ retry
182
186
  end
183
187
 
184
188
  # we use both a mutex for thread-safety and fcntl lock for process-safety
185
189
  def synchronize io, type # :nodoc:
186
190
  @mutex.synchronize do
187
191
  begin
192
+ type = type.dup
188
193
  lock! io, type
189
194
  yield io
190
195
  ensure
191
- lock! io, UNLOCK
196
+ lock! io, type.replace(UNLOCK)
197
+ type.clear
192
198
  end
193
199
  end
194
200
  end
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
2
  #
3
- # raindrops may use the {aggregate}[http://github.com/josephruscio/aggregate]
3
+ # raindrops may use the {aggregate}[https://github.com/josephruscio/aggregate]
4
4
  # RubyGem to aggregate statistics from TCP_Info lookups.
5
5
  module Raindrops::Aggregate
6
6
  autoload :PMQ, "raindrops/aggregate/pmq"
@@ -8,15 +8,13 @@
8
8
  # Instead of snapshotting, Raindrops::Aggregate::LastDataRecv may be used
9
9
  # to aggregate statistics from +all+ accepted sockets as they arrive
10
10
  # based on the +last_data_recv+ field in Raindrops::TCP_Info
11
- require 'pathname'
12
11
 
13
12
  module Raindrops::Linux
14
13
 
15
14
  # The standard proc path for active UNIX domain sockets, feel free to call
16
15
  # String#replace on this if your /proc is mounted in a non-standard location
17
16
  # for whatever reason
18
- PROC_NET_UNIX_ARGS = %w(/proc/net/unix)
19
- defined?(::Encoding) and PROC_NET_UNIX_ARGS.push({ :encoding => "binary" })
17
+ PROC_NET_UNIX_ARGS = [ '/proc/net/unix', { encoding: "binary" }]
20
18
 
21
19
  # Get ListenStats from an array of +paths+
22
20
  #
@@ -43,10 +41,11 @@ module Raindrops::Linux
43
41
  else
44
42
  paths = paths.map do |path|
45
43
  path = path.dup
46
- path.force_encoding(Encoding::BINARY) if defined?(Encoding)
44
+ path.force_encoding(Encoding::BINARY)
47
45
  if File.symlink?(path)
48
46
  link = path
49
- path = Pathname.new(link).realpath.to_s
47
+ path = File.readlink(link)
48
+ path.force_encoding(Encoding::BINARY)
50
49
  rv[link] = rv[path] # vivify ListenerStats
51
50
  else
52
51
  rv[path] # vivify ListenerStats
@@ -57,7 +56,7 @@ module Raindrops::Linux
57
56
  paths = /^\w+: \d+ \d+ (\d+) \d+ (\d+)\s+\d+ (#{paths.join('|')})$/n
58
57
 
59
58
  # no point in pread since we can't stat for size on this file
60
- File.read(*PROC_NET_UNIX_ARGS).scan(paths) do |s|
59
+ File.read(PROC_NET_UNIX_ARGS[0], encoding: 'binary').scan(paths) do |s|
61
60
  path = s[-1]
62
61
  case s[0]
63
62
  when "00000000" # client sockets
@@ -27,9 +27,9 @@ class Raindrops::Middleware::Proxy
27
27
 
28
28
  # Rack servers use +respond_to?+ to check for the presence of +close+
29
29
  # and +to_path+ methods.
30
- def respond_to?(m)
30
+ def respond_to?(m, include_all = false)
31
31
  m = m.to_sym
32
- :close == m || @body.respond_to?(m)
32
+ :close == m || @body.respond_to?(m, include_all)
33
33
  end
34
34
 
35
35
  # Avoid breaking users of non-standard extensions (e.g. #body)
@@ -62,9 +62,9 @@ require 'raindrops'
62
62
  # = Demo Server
63
63
  #
64
64
  # There is a server running this middleware (and Watcher) at
65
- # http://raindrops-demo.bogomips.org/_raindrops
65
+ # https://yhbt.net/raindrops-demo/_raindrops
66
66
  #
67
- # Also check out the Watcher demo at http://raindrops-demo.bogomips.org/
67
+ # Also check out the Watcher demo at https://yhbt.net/raindrops-demo/
68
68
  #
69
69
  # The demo server is only limited to 30 users, so be sure not to abuse it
70
70
  # by using the /tail/ endpoint too much.
@@ -77,11 +77,9 @@ class Raindrops::Middleware
77
77
  # and both counters are updated atomically.
78
78
  #
79
79
  # This is supported on all operating systems supported by Raindrops
80
- class Stats < Raindrops::Struct.new(:calling, :writing)
81
- end
80
+ Stats = Raindrops::Struct.new(:calling, :writing)
82
81
 
83
82
  # :stopdoc:
84
- PATH_INFO = "PATH_INFO"
85
83
  require "raindrops/middleware/proxy"
86
84
  # :startdoc:
87
85
 
@@ -111,7 +109,7 @@ class Raindrops::Middleware
111
109
 
112
110
  # standard Rack endpoint
113
111
  def call(env) # :nodoc:
114
- env[PATH_INFO] == @path and return stats_response
112
+ env['PATH_INFO'] == @path and return stats_response
115
113
  begin
116
114
  @stats.incr_calling
117
115
 
@@ -8,7 +8,7 @@ require "aggregate"
8
8
  # Raindrops::Watcher is a stand-alone Rack application for watching
9
9
  # any number of TCP and UNIX listeners (all of them by default).
10
10
  #
11
- # It depends on the {Aggregate RubyGem}[http://rubygems.org/gems/aggregate]
11
+ # It depends on the {Aggregate RubyGem}[https://rubygems.org/gems/aggregate]
12
12
  #
13
13
  # In your Rack config.ru:
14
14
  #
@@ -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://raindrops-demo.bogomips.org/active/0.0.0.0%3A80.txt
38
+ # e.g.: curl https://yhbt.net/raindrops-demo/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://raindrops-demo.bogomips.org/active/0.0.0.0%3A80.html
45
+ # e.g.: curl https://yhbt.net/raindrops-demo/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://raindrops-demo.bogomips.org/queued/0.0.0.0%3A80.txt
52
+ # e.g.: curl https://yhbt.net/raindrops-demo/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://raindrops-demo.bogomips.org/queued/0.0.0.0%3A80.html
59
+ # e.g.: curl https://yhbt.net/raindrops-demo/queued/0.0.0.0%3A80.html
60
60
  #
61
61
  # === POST /reset/$LISTENER
62
62
  #
@@ -95,9 +95,9 @@ require "aggregate"
95
95
  #
96
96
  # = Demo Server
97
97
  #
98
- # There is a server running this app at http://raindrops-demo.bogomips.org/
98
+ # There is a server running this app at https://yhbt.net/raindrops-demo/
99
99
  # The Raindrops::Middleware demo is also accessible at
100
- # http://raindrops-demo.bogomips.org/_raindrops
100
+ # https://yhbt.net/raindrops-demo/_raindrops
101
101
  #
102
102
  # The demo server is only limited to 30 users, so be sure not to abuse it
103
103
  # by using the /tail/ endpoint too much.
@@ -106,7 +106,7 @@ class Raindrops::Watcher
106
106
  attr_reader :snapshot
107
107
  include Rack::Utils
108
108
  include Raindrops::Linux
109
- DOC_URL = "http://raindrops.bogomips.org/Raindrops/Watcher.html"
109
+ DOC_URL = "https://yhbt.net/raindrops/Raindrops/Watcher.html"
110
110
  Peak = Struct.new(:first, :last)
111
111
 
112
112
  def initialize(opts = {})
@@ -244,10 +244,10 @@ class Raindrops::Watcher
244
244
  def histogram_txt(agg)
245
245
  updated_at, reset_at, agg, current, peak = *agg
246
246
  headers = agg_to_hash(reset_at, agg, current, peak)
247
- body = agg.to_s
247
+ body = agg.to_s # 7-bit ASCII-clean
248
248
  headers["Content-Type"] = "text/plain"
249
249
  headers["Expires"] = (updated_at + @delay).httpdate
250
- headers["Content-Length"] = bytesize(body).to_s
250
+ headers["Content-Length"] = body.size.to_s
251
251
  [ 200, headers, [ body ] ]
252
252
  end
253
253
 
@@ -265,7 +265,7 @@ class Raindrops::Watcher
265
265
  "</body>"
266
266
  headers["Content-Type"] = "text/html"
267
267
  headers["Expires"] = (updated_at + @delay).httpdate
268
- headers["Content-Length"] = bytesize(body).to_s
268
+ headers["Content-Length"] = body.size.to_s
269
269
  [ 200, headers, [ body ] ]
270
270
  end
271
271
 
@@ -364,7 +364,7 @@ class Raindrops::Watcher
364
364
  "for more information and options." \
365
365
  "</p>" \
366
366
  "</body></html>"
367
- headers["Content-Length"] = bytesize(body).to_s
367
+ headers["Content-Length"] = body.size.to_s
368
368
  [ 200, headers, [ body ] ]
369
369
  end
370
370
 
@@ -382,7 +382,7 @@ class Raindrops::Watcher
382
382
  q = Rack::Utils.parse_query env["QUERY_STRING"]
383
383
  @active_min = q["active_min"].to_i
384
384
  @queued_min = q["queued_min"].to_i
385
- len = Rack::Utils.bytesize(addr)
385
+ len = addr.size
386
386
  len = 35 if len > 35
387
387
  @fmt = "%20s % #{len}s % 10u % 10u\n"
388
388
  case env["HTTP_VERSION"]
data/lib/raindrops.rb CHANGED
@@ -12,7 +12,7 @@
12
12
  # Unlike many classes in this package, the core Raindrops class is
13
13
  # intended to be portable to all reasonably modern *nix systems
14
14
  # supporting mmap(). Please let us know if you have portability
15
- # issues, patches or pull requests at mailto:raindrops@librelist.org
15
+ # issues, patches or pull requests at mailto:raindrops-public@yhbt.net
16
16
  class Raindrops
17
17
 
18
18
  # Used to represent the number of +active+ and +queued+ sockets for
@@ -36,6 +36,30 @@ class Raindrops
36
36
  def total
37
37
  active + queued
38
38
  end
39
+ end unless defined? ListenStats
40
+
41
+ # call-seq:
42
+ # Raindrops.new(size, io: nil) -> raindrops object
43
+ #
44
+ # Initializes a Raindrops object to hold +size+ counters. +size+ is
45
+ # only a hint and the actual number of counters the object has is
46
+ # dependent on the CPU model, number of cores, and page size of
47
+ # the machine. The actual size of the object will always be equal
48
+ # or greater than the specified +size+.
49
+ # If +io+ is provided, then the Raindrops memory will be backed by
50
+ # the specified file; otherwise, it will allocate anonymous memory.
51
+ # The IO object must respond to +truncate+, as this is used to set
52
+ # the size of the file.
53
+ # If +zero+ is provided, then the memory region is zeroed prior to
54
+ # returning. This is only meaningful if +io+ is also provided; in
55
+ # that case it controls whether any existing counter values in +io+
56
+ # are retained (false) or whether it is entirely zeroed (true).
57
+ def initialize(size, io: nil, zero: false)
58
+ # This ruby wrapper exists to handle the keyword-argument handling,
59
+ # which is otherwise kind of awkward in C. We delegate the keyword
60
+ # arguments to the _actual_ initialize implementation as positional
61
+ # args.
62
+ initialize_cimpl(size, io, zero)
39
63
  end
40
64
 
41
65
  autoload :Linux, 'raindrops/linux'