raindrops 0.16.0 → 0.19.2

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.
@@ -14,8 +14,7 @@ module Raindrops::Linux
14
14
  # The standard proc path for active UNIX domain sockets, feel free to call
15
15
  # String#replace on this if your /proc is mounted in a non-standard location
16
16
  # for whatever reason
17
- PROC_NET_UNIX_ARGS = %w(/proc/net/unix)
18
- defined?(::Encoding) and PROC_NET_UNIX_ARGS.push({ :encoding => "binary" })
17
+ PROC_NET_UNIX_ARGS = [ '/proc/net/unix', { encoding: "binary" }]
19
18
 
20
19
  # Get ListenStats from an array of +paths+
21
20
  #
@@ -42,11 +41,11 @@ def unix_listener_stats(paths = nil)
42
41
  else
43
42
  paths = paths.map do |path|
44
43
  path = path.dup
45
- path.force_encoding(Encoding::BINARY) if defined?(Encoding)
44
+ path.force_encoding(Encoding::BINARY)
46
45
  if File.symlink?(path)
47
46
  link = path
48
47
  path = File.readlink(link)
49
- path.force_encoding(Encoding::BINARY) if defined?(Encoding)
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 @@ def unix_listener_stats(paths = nil)
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
@@ -62,9 +62,9 @@
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.
@@ -27,9 +27,9 @@ def to_path
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)
@@ -8,7 +8,7 @@
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 @@
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 @@
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 @@ def agg_to_hash(reset_at, agg, current, peak)
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 @@ def histogram_html(agg, addr)
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 @@ def index
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 @@ def initialize(rdmon, addr, env) # :nodoc:
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/pkg.mk CHANGED
@@ -60,7 +60,7 @@ doc:: .document .olddoc.yml $(pkg_extra) $(PLACEHOLDERS)
60
60
  -find lib -type f -name '*.rbc' -exec rm -f '{}' ';'
61
61
  -find ext -type f -name '*.rbc' -exec rm -f '{}' ';'
62
62
  $(RM) -r doc
63
- $(RDOC) -f oldweb
63
+ $(RDOC) -f dark216
64
64
  $(OLDDOC) merge
65
65
  install -m644 COPYING doc/COPYING
66
66
  install -m644 NEWS doc/NEWS
@@ -127,7 +127,8 @@ publish_doc:
127
127
  -git set-file-times
128
128
  $(MAKE) doc
129
129
  $(MAKE) doc_gz
130
- $(RSYNC) -av doc/ $(RSYNC_DEST)/
130
+ $(RSYNC) -av doc/ $(RSYNC_DEST)/ \
131
+ --exclude index.html* --exclude created.rid*
131
132
  git ls-files | xargs touch
132
133
  endif
133
134
 
data/raindrops.gemspec CHANGED
@@ -1,30 +1,26 @@
1
1
  # -*- encoding: binary -*-
2
- ENV["VERSION"] or abort "VERSION= must be specified"
3
- manifest = File.readlines('.manifest').map! { |x| x.chomp! }
2
+ manifest = File.exist?('.manifest') ?
3
+ IO.readlines('.manifest').map!(&:chomp!) : `git ls-files`.split("\n")
4
4
  test_files = manifest.grep(%r{\Atest/test_.*\.rb\z})
5
- require 'olddoc'
6
- extend Olddoc::Gemspec
7
- name, summary, title = readme_metadata
8
5
 
9
6
  Gem::Specification.new do |s|
10
7
  s.name = %q{raindrops}
11
- s.version = ENV["VERSION"].dup
12
-
8
+ s.version = (ENV["VERSION"] ||= '0.18.0').dup
13
9
  s.authors = ["raindrops hackers"]
14
- s.description = readme_description
15
- s.email = %q{raindrops@bogomips.org}
10
+ s.description = File.read('README').split("\n\n")[1]
11
+ s.email = %q{raindrops-public@yhbt.net}
16
12
  s.extensions = %w(ext/raindrops/extconf.rb)
17
- s.extra_rdoc_files = extra_rdoc_files(manifest)
13
+ s.extra_rdoc_files = IO.readlines('.document').map!(&:chomp!).keep_if do |f|
14
+ File.exist?(f)
15
+ end
18
16
  s.files = manifest
19
- s.homepage = Olddoc.config['rdoc_url']
20
- s.summary = summary
17
+ s.homepage = 'https://yhbt.net/raindrops/'
18
+ s.summary = 'real-time stats for preforking Rack servers'
19
+ s.required_ruby_version = '>= 1.9.3'
21
20
  s.test_files = test_files
22
21
  s.add_development_dependency('aggregate', '~> 0.2')
23
22
  s.add_development_dependency('test-unit', '~> 3.0')
24
- s.add_development_dependency('io-extra', [ '~> 1.2', '>= 1.2.3'])
25
23
  s.add_development_dependency('posix_mq', '~> 2.0')
26
- s.add_development_dependency('rack', '~> 1.2')
27
- s.add_development_dependency('olddoc', '~> 1.0')
28
-
24
+ s.add_development_dependency('rack', [ '>= 1.2', '< 3.0' ])
29
25
  s.licenses = %w(LGPL-2.1+)
30
26
  end
data/test/ipv6_enabled.rb CHANGED
@@ -2,8 +2,8 @@ def ipv6_enabled?
2
2
  tmp = TCPServer.new(ENV["TEST_HOST6"] || '::1', 0)
3
3
  tmp.close
4
4
  true
5
- rescue => e
6
- warn "skipping IPv6 tests, host does not seem to be IPv6 enabled:"
7
- warn " #{e.class}: #{e}"
8
- false
5
+ rescue => e
6
+ warn "skipping IPv6 tests, host does not seem to be IPv6 enabled:"
7
+ warn " #{e.class}: #{e}"
8
+ false
9
9
  end
@@ -3,7 +3,7 @@
3
3
  pmq = begin
4
4
  Raindrops::Aggregate::PMQ
5
5
  rescue LoadError => e
6
- warn "W: #{e} skipping test"
6
+ warn "W: #{e} skipping #{__FILE__}"
7
7
  false
8
8
  end
9
9
  if RUBY_VERSION.to_f < 1.9
@@ -8,7 +8,7 @@ class TestInetDiagSocket < Test::Unit::TestCase
8
8
  def test_new
9
9
  sock = Raindrops::InetDiagSocket.new
10
10
  assert_kind_of Socket, sock
11
- assert_kind_of Fixnum, sock.fileno
11
+ assert_kind_of Integer, sock.fileno
12
12
  flags = sock.fcntl(Fcntl::F_GETFD)
13
13
  assert_equal Fcntl::FD_CLOEXEC, flags & Fcntl::FD_CLOEXEC
14
14
  assert_nil sock.close
@@ -7,7 +7,7 @@
7
7
  pmq = begin
8
8
  Raindrops::Aggregate::PMQ
9
9
  rescue LoadError => e
10
- warn "W: #{e} skipping test"
10
+ warn "W: #{e} skipping #{__FILE__}"
11
11
  false
12
12
  end
13
13
  if RUBY_VERSION.to_f < 1.9
data/test/test_linux.rb CHANGED
@@ -76,6 +76,7 @@ def test_unix_all_unused
76
76
 
77
77
  assert_equal 0, stats[tmp.path].active
78
78
  assert_equal 0, stats[tmp.path].queued
79
+ us.close
79
80
  end
80
81
 
81
82
  def test_unix_resolves_symlinks
@@ -151,8 +152,8 @@ def test_tcp_reuse_sock
151
152
  assert_equal 1, stats.size
152
153
  assert_equal 0, stats[addr].queued
153
154
  assert_equal 1, stats[addr].active
154
- ensure
155
- nlsock.close
155
+ ensure
156
+ nlsock.close
156
157
  end
157
158
 
158
159
  def test_tcp_multi
@@ -37,7 +37,7 @@ def test_leak
37
37
  end
38
38
  cur_kb = rss_kb
39
39
  p [ :cur_kb, cur_kb ]
40
- ensure
41
- s.close
40
+ ensure
41
+ s.close
42
42
  end
43
43
  end if ENV["STRESS"].to_i != 0
@@ -134,7 +134,7 @@ def test_resize_mremap
134
134
  assert_equal 0, rd[rd.capa - 1]
135
135
  assert_equal 1, rd.incr(rd.capa - 1)
136
136
  assert_raises(ArgumentError) { rd[rd.capa] }
137
- rescue RangeError
137
+ rescue RangeError
138
138
  end # if RUBY_PLATFORM =~ /linux/
139
139
 
140
140
  def test_evaporate
@@ -5,15 +5,15 @@
5
5
  require 'socket'
6
6
  require 'pp'
7
7
  $stderr.sync = $stdout.sync = true
8
- class TestLinuxTCP_Info < Test::Unit::TestCase
8
+ class TestTCP_Info < Test::Unit::TestCase
9
9
 
10
10
  TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
11
11
 
12
12
  # Linux kernel commit 5ee3afba88f5a79d0bff07ddd87af45919259f91
13
13
  TCP_INFO_useful_listenq = `uname -r`.strip >= '2.6.24'
14
14
 
15
-
16
- def test_tcp_server
15
+ def test_tcp_server_unacked
16
+ return if RUBY_PLATFORM !~ /linux/ # unacked not implemented on others...
17
17
  s = TCPServer.new(TEST_ADDR, 0)
18
18
  rv = Raindrops::TCP_Info.new s
19
19
  c = TCPSocket.new TEST_ADDR, s.addr[1]
@@ -29,10 +29,8 @@ def test_tcp_server
29
29
  tmp.get!(s)
30
30
  assert_equal before, tmp.object_id
31
31
 
32
- ensure
33
- c.close if c
34
- a.close if a
35
- s.close
32
+ ensure
33
+ [ c, a, s ].compact.each(&:close)
36
34
  end
37
35
 
38
36
  def test_accessors
@@ -42,12 +40,14 @@ def test_accessors
42
40
  assert tcp_info_methods.size >= 32
43
41
  tcp_info_methods.each do |m|
44
42
  next if m.to_sym == :get!
43
+ next if ! tmp.respond_to?(m)
45
44
  val = tmp.__send__ m
46
45
  assert_kind_of Integer, val
47
46
  assert val >= 0
48
47
  end
49
- ensure
50
- s.close
48
+ assert tmp.respond_to?(:state), 'every OS knows about TCP state, right?'
49
+ ensure
50
+ s.close
51
51
  end
52
52
 
53
53
  def test_tcp_server_delayed
@@ -60,9 +60,29 @@ def test_tcp_server_delayed
60
60
  a = s.accept
61
61
  i = Raindrops::TCP_Info.new(a)
62
62
  assert i.last_data_recv >= delay_ms, "#{i.last_data_recv} < #{delay_ms}"
63
- ensure
64
- c.close if c
65
- a.close if a
66
- s.close
63
+ ensure
64
+ c.close if c
65
+ a.close if a
66
+ s.close
67
+ end
68
+
69
+ def test_tcp_server_state_closed
70
+ s = TCPServer.new(TEST_ADDR, 0)
71
+ c = TCPSocket.new(TEST_ADDR, s.addr[1])
72
+ i = Raindrops::TCP_Info.allocate
73
+ a = s.accept
74
+ i.get!(a)
75
+ state = i.state
76
+ if Raindrops.const_defined?(:TCP)
77
+ assert_equal state, Raindrops::TCP[:ESTABLISHED]
78
+ end
79
+ c = c.close
80
+ sleep(0.01) # wait for kernel to update state
81
+ i.get!(a)
82
+ assert_not_equal state, i.state
83
+ ensure
84
+ s.close if s
85
+ c.close if c
86
+ a.close if a
67
87
  end
68
- end if RUBY_PLATFORM =~ /linux/
88
+ end if defined? Raindrops::TCP_Info
data/test/test_watcher.rb CHANGED
@@ -2,6 +2,11 @@
2
2
  require "test/unit"
3
3
  require "rack"
4
4
  require "raindrops"
5
+ begin
6
+ require 'aggregate'
7
+ rescue LoadError => e
8
+ warn "W: #{e} skipping #{__FILE__}"
9
+ end
5
10
 
6
11
  class TestWatcher < Test::Unit::TestCase
7
12
  TEST_ADDR = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
@@ -113,28 +118,28 @@ def test_tail_queued_min
113
118
 
114
119
  def test_x_current_header
115
120
  env = @req.class.env_for "/active/#@addr.txt"
116
- status, headers, body = @app.call(env)
121
+ _status, headers, _body = @app.call(env)
117
122
  assert_equal "0", headers["X-Current"], headers.inspect
118
123
 
119
124
  env = @req.class.env_for "/queued/#@addr.txt"
120
- status, headers, body = @app.call(env)
125
+ _status, headers, _body = @app.call(env)
121
126
  assert_equal "1", headers["X-Current"], headers.inspect
122
127
 
123
128
  @ios << @srv.accept
124
129
  sleep 0.1
125
130
 
126
131
  env = @req.class.env_for "/queued/#@addr.txt"
127
- status, headers, body = @app.call(env)
132
+ _status, headers, _body = @app.call(env)
128
133
  assert_equal "0", headers["X-Current"], headers.inspect
129
134
 
130
135
  env = @req.class.env_for "/active/#@addr.txt"
131
- status, headers, body = @app.call(env)
136
+ _status, headers, _body = @app.call(env)
132
137
  assert_equal "1", headers["X-Current"], headers.inspect
133
138
  end
134
139
 
135
140
  def test_peaks
136
141
  env = @req.class.env_for "/active/#@addr.txt"
137
- status, headers, body = @app.call(env.dup)
142
+ _status, headers, _body = @app.call(env.dup)
138
143
  start = headers["X-First-Peak-At"]
139
144
  assert headers["X-First-Peak-At"], headers.inspect
140
145
  assert headers["X-Last-Peak-At"], headers.inspect
@@ -143,14 +148,14 @@ def test_peaks
143
148
  before = headers["X-Last-Peak-At"]
144
149
 
145
150
  env = @req.class.env_for "/queued/#@addr.txt"
146
- status, headers, body = @app.call(env)
151
+ _status, headers, _body = @app.call(env)
147
152
  assert_nothing_raised { Time.parse(headers["X-First-Peak-At"]) }
148
153
  assert_nothing_raised { Time.parse(headers["X-Last-Peak-At"]) }
149
154
  assert_equal before, headers["X-Last-Peak-At"], "should not change"
150
155
 
151
156
  sleep 2
152
157
  env = @req.class.env_for "/active/#@addr.txt"
153
- status, headers, body = @app.call(env.dup)
158
+ _status, headers, _body = @app.call(env.dup)
154
159
  assert_equal before, headers["X-Last-Peak-At"], headers.inspect
155
160
 
156
161
  @ios << @srv.accept
@@ -162,7 +167,7 @@ def test_peaks
162
167
  end
163
168
  sleep 0.1
164
169
  env = @req.class.env_for "/queued/#@addr.txt"
165
- status, headers, body = @app.call(env.dup)
170
+ _status, headers, _body = @app.call(env.dup)
166
171
  assert headers["X-Last-Peak-At"], headers.inspect
167
172
  assert_nothing_raised { Time.parse(headers["X-Last-Peak-At"]) }
168
173
  assert before != headers["X-Last-Peak-At"]
@@ -172,10 +177,10 @@ def test_peaks
172
177
  sleep 2
173
178
 
174
179
  env = @req.class.env_for "/queued/#@addr.txt"
175
- status, headers, body = @app.call(env)
180
+ _status, headers, _body = @app.call(env)
176
181
  assert_equal "0", headers["X-Current"]
177
182
  assert_nothing_raised { Time.parse(headers["X-Last-Peak-At"]) }
178
183
  assert_equal queued_before, headers["X-Last-Peak-At"], "should not change"
179
184
  assert_equal start, headers["X-First-Peak-At"]
180
185
  end
181
- end if RUBY_PLATFORM =~ /linux/
186
+ end if RUBY_PLATFORM =~ /linux/ && defined?(Aggregate)