raindrops 0.16.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"
@@ -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 @@ module Raindrops::Linux
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 @@ 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.
@@ -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-public@bogomips.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'
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 @@ require "raindrops"
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 @@ $stderr.sync = $stdout.sync = true
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 @@ class TestLinux < Test::Unit::TestCase
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 @@ class TestLinux < Test::Unit::TestCase
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 @@ class TestLinuxAllTcpListenStatsLeak < Test::Unit::TestCase
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
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'test/unit'
3
3
  require 'raindrops'
4
+ require 'tempfile'
4
5
 
5
6
  class TestRaindrops < Test::Unit::TestCase
6
7
 
@@ -134,7 +135,7 @@ class TestRaindrops < Test::Unit::TestCase
134
135
  assert_equal 0, rd[rd.capa - 1]
135
136
  assert_equal 1, rd.incr(rd.capa - 1)
136
137
  assert_raises(ArgumentError) { rd[rd.capa] }
137
- rescue RangeError
138
+ rescue RangeError
138
139
  end # if RUBY_PLATFORM =~ /linux/
139
140
 
140
141
  def test_evaporate
@@ -162,4 +163,45 @@ class TestRaindrops < Test::Unit::TestCase
162
163
  assert status.success?
163
164
  assert_equal [ 1, 2 ], tmp.to_ary
164
165
  end
166
+
167
+ def test_io_backed
168
+ file = Tempfile.new('test_io_backed')
169
+ rd = Raindrops.new(4, io: file, zero: true)
170
+ rd[0] = 123
171
+ rd[1] = 456
172
+
173
+ assert_equal 123, rd[0]
174
+ assert_equal 456, rd[1]
175
+
176
+ rd.evaporate!
177
+
178
+ file.rewind
179
+ data = file.read
180
+ assert_equal 123, data.unpack('L!')[0]
181
+ assert_equal 456, data[Raindrops::SIZE..data.size].unpack('L!')[0]
182
+ end
183
+
184
+ def test_io_backed_reuse
185
+ file = Tempfile.new('test_io_backed')
186
+ rd = Raindrops.new(4, io: file, zero: true)
187
+ rd[0] = 123
188
+ rd[1] = 456
189
+ rd.evaporate!
190
+
191
+ rd = Raindrops.new(4, io: file, zero: false)
192
+ assert_equal 123, rd[0]
193
+ assert_equal 456, rd[1]
194
+ end
195
+
196
+ def test_iobacked_noreuse
197
+ file = Tempfile.new('test_io_backed')
198
+ rd = Raindrops.new(4, io: file, zero: true)
199
+ rd[0] = 123
200
+ rd[1] = 456
201
+ rd.evaporate!
202
+
203
+ rd = Raindrops.new(4, io: file, zero: true)
204
+ assert_equal 0, rd[0]
205
+ assert_equal 0, rd[1]
206
+ end
165
207
  end
@@ -5,15 +5,15 @@ require 'raindrops'
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 @@ class TestLinuxTCP_Info < Test::Unit::TestCase
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 @@ class TestLinuxTCP_Info < Test::Unit::TestCase
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 @@ class TestLinuxTCP_Info < Test::Unit::TestCase
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