raindrops 0.4.1 → 0.5.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/.document +2 -1
- data/.gitignore +4 -0
- data/.wrongdoc.yml +4 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -196
- data/Gemfile +7 -0
- data/LICENSE +1 -1
- data/README +17 -47
- data/Rakefile +0 -104
- data/examples/linux-listener-stats.rb +123 -0
- data/examples/{config.ru → middleware.ru} +1 -1
- data/examples/watcher.ru +4 -0
- data/examples/watcher_demo.ru +13 -0
- data/examples/zbatery.conf.rb +13 -0
- data/ext/raindrops/extconf.rb +5 -0
- data/ext/raindrops/linux_inet_diag.c +449 -151
- data/ext/raindrops/linux_tcp_info.c +170 -0
- data/ext/raindrops/my_fileno.h +36 -0
- data/ext/raindrops/raindrops.c +232 -20
- data/lib/raindrops.rb +20 -7
- data/lib/raindrops/aggregate.rb +8 -0
- data/lib/raindrops/aggregate/last_data_recv.rb +86 -0
- data/lib/raindrops/aggregate/pmq.rb +239 -0
- data/lib/raindrops/last_data_recv.rb +100 -0
- data/lib/raindrops/linux.rb +26 -16
- data/lib/raindrops/middleware.rb +112 -41
- data/lib/raindrops/middleware/proxy.rb +34 -0
- data/lib/raindrops/struct.rb +15 -0
- data/lib/raindrops/watcher.rb +362 -0
- data/pkg.mk +171 -0
- data/raindrops.gemspec +10 -20
- data/test/ipv6_enabled.rb +10 -0
- data/test/rack_unicorn.rb +12 -0
- data/test/test_aggregate_pmq.rb +65 -0
- data/test/test_inet_diag_socket.rb +13 -0
- data/test/test_last_data_recv_unicorn.rb +69 -0
- data/test/test_linux.rb +55 -57
- data/test/test_linux_all_tcp_listen_stats.rb +66 -0
- data/test/test_linux_all_tcp_listen_stats_leak.rb +43 -0
- data/test/test_linux_ipv6.rb +158 -0
- data/test/test_linux_tcp_info.rb +61 -0
- data/test/test_middleware.rb +15 -2
- data/test/test_middleware_unicorn.rb +37 -0
- data/test/test_middleware_unicorn_ipv6.rb +37 -0
- data/test/test_raindrops.rb +65 -1
- data/test/test_raindrops_gc.rb +23 -1
- data/test/test_watcher.rb +85 -0
- metadata +69 -22
- data/examples/linux-tcp-listener-stats.rb +0 -44
data/pkg.mk
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
RUBY = ruby
|
2
|
+
RAKE = rake
|
3
|
+
RSYNC = rsync
|
4
|
+
WRONGDOC = wrongdoc
|
5
|
+
|
6
|
+
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
7
|
+
@./GIT-VERSION-GEN
|
8
|
+
-include GIT-VERSION-FILE
|
9
|
+
-include local.mk
|
10
|
+
DLEXT := $(shell $(RUBY) -rrbconfig -e 'puts RbConfig::CONFIG["DLEXT"]')
|
11
|
+
RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
|
12
|
+
RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
|
13
|
+
lib := lib
|
14
|
+
|
15
|
+
ifeq ($(shell test -f script/isolate_for_tests && echo t),t)
|
16
|
+
isolate_libs := tmp/isolate/$(RUBY_ENGINE)-$(RUBY_VERSION)/isolate.mk
|
17
|
+
$(isolate_libs): script/isolate_for_tests
|
18
|
+
@$(RUBY) script/isolate_for_tests
|
19
|
+
-include $(isolate_libs)
|
20
|
+
lib := $(lib):$(ISOLATE_LIBS)
|
21
|
+
endif
|
22
|
+
|
23
|
+
ext := $(firstword $(wildcard ext/*))
|
24
|
+
ifneq ($(ext),)
|
25
|
+
ext_pfx := tmp/ext/$(RUBY_ENGINE)-$(RUBY_VERSION)
|
26
|
+
ext_h := $(wildcard $(ext)/*/*.h $(ext)/*.h)
|
27
|
+
ext_src := $(wildcard $(ext)/*.c $(ext_h))
|
28
|
+
ext_pfx_src := $(addprefix $(ext_pfx)/,$(ext_src))
|
29
|
+
ext_d := $(ext_pfx)/$(ext)/.d
|
30
|
+
$(ext)/extconf.rb: $(wildcard $(ext)/*.h)
|
31
|
+
@>> $@
|
32
|
+
$(ext_d):
|
33
|
+
@mkdir -p $(@D)
|
34
|
+
@> $@
|
35
|
+
$(ext_pfx)/$(ext)/%: $(ext)/% $(ext_d)
|
36
|
+
install -m 644 $< $@
|
37
|
+
$(ext_pfx)/$(ext)/Makefile: $(ext)/extconf.rb $(ext_d) $(ext_h)
|
38
|
+
$(RM) -f $(@D)/*.o
|
39
|
+
cd $(@D) && $(RUBY) $(CURDIR)/$(ext)/extconf.rb
|
40
|
+
ext_sfx := _ext.$(DLEXT)
|
41
|
+
ext_dl := $(ext_pfx)/$(ext)/$(notdir $(ext)_ext.$(DLEXT))
|
42
|
+
$(ext_dl): $(ext_src) $(ext_pfx_src) $(ext_pfx)/$(ext)/Makefile
|
43
|
+
@echo $^ == $@
|
44
|
+
$(MAKE) -C $(@D)
|
45
|
+
lib := $(lib):$(ext_pfx)/$(ext)
|
46
|
+
build: $(ext_dl)
|
47
|
+
else
|
48
|
+
build:
|
49
|
+
endif
|
50
|
+
|
51
|
+
pkg_extra += GIT-VERSION-FILE NEWS ChangeLog LATEST
|
52
|
+
ChangeLog: GIT-VERSION-FILE .wrongdoc.yml
|
53
|
+
$(WRONGDOC) prepare
|
54
|
+
NEWS LATEST: ChangeLog
|
55
|
+
|
56
|
+
manifest:
|
57
|
+
$(RM) .manifest
|
58
|
+
$(MAKE) .manifest
|
59
|
+
|
60
|
+
.manifest: $(pkg_extra)
|
61
|
+
(git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \
|
62
|
+
LC_ALL=C sort > $@+
|
63
|
+
cmp $@+ $@ || mv $@+ $@
|
64
|
+
$(RM) $@+
|
65
|
+
|
66
|
+
doc:: .document .wrongdoc.yml $(pkg_extra)
|
67
|
+
-find lib -type f -name '*.rbc' -exec rm -f '{}' ';'
|
68
|
+
-find ext -type f -name '*.rbc' -exec rm -f '{}' ';'
|
69
|
+
$(RM) -r doc
|
70
|
+
$(WRONGDOC) all
|
71
|
+
install -m644 COPYING doc/COPYING
|
72
|
+
install -m644 $(shell grep '^[A-Z]' .document) doc/
|
73
|
+
|
74
|
+
ifneq ($(VERSION),)
|
75
|
+
pkggem := pkg/$(rfpackage)-$(VERSION).gem
|
76
|
+
pkgtgz := pkg/$(rfpackage)-$(VERSION).tgz
|
77
|
+
release_notes := release_notes-$(VERSION)
|
78
|
+
release_changes := release_changes-$(VERSION)
|
79
|
+
|
80
|
+
release-notes: $(release_notes)
|
81
|
+
release-changes: $(release_changes)
|
82
|
+
$(release_changes):
|
83
|
+
$(WRONGDOC) release_changes > $@+
|
84
|
+
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
85
|
+
$(release_notes):
|
86
|
+
$(WRONGDOC) release_notes > $@+
|
87
|
+
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
88
|
+
|
89
|
+
# ensures we're actually on the tagged $(VERSION), only used for release
|
90
|
+
verify:
|
91
|
+
test x"$(shell umask)" = x0022
|
92
|
+
git rev-parse --verify refs/tags/v$(VERSION)^{}
|
93
|
+
git diff-index --quiet HEAD^0
|
94
|
+
test $$(git rev-parse --verify HEAD^0) = \
|
95
|
+
$$(git rev-parse --verify refs/tags/v$(VERSION)^{})
|
96
|
+
|
97
|
+
fix-perms:
|
98
|
+
-git ls-tree -r HEAD | awk '/^100644 / {print $$NF}' | xargs chmod 644
|
99
|
+
-git ls-tree -r HEAD | awk '/^100755 / {print $$NF}' | xargs chmod 755
|
100
|
+
|
101
|
+
gem: $(pkggem)
|
102
|
+
|
103
|
+
install-gem: $(pkggem)
|
104
|
+
gem install $(CURDIR)/$<
|
105
|
+
|
106
|
+
$(pkggem): manifest fix-perms
|
107
|
+
gem build $(rfpackage).gemspec
|
108
|
+
mkdir -p pkg
|
109
|
+
mv $(@F) $@
|
110
|
+
|
111
|
+
$(pkgtgz): distdir = $(basename $@)
|
112
|
+
$(pkgtgz): HEAD = v$(VERSION)
|
113
|
+
$(pkgtgz): manifest fix-perms
|
114
|
+
@test -n "$(distdir)"
|
115
|
+
$(RM) -r $(distdir)
|
116
|
+
mkdir -p $(distdir)
|
117
|
+
tar cf - $$(cat .manifest) | (cd $(distdir) && tar xf -)
|
118
|
+
cd pkg && tar cf - $(basename $(@F)) | gzip -9 > $(@F)+
|
119
|
+
mv $@+ $@
|
120
|
+
|
121
|
+
package: $(pkgtgz) $(pkggem)
|
122
|
+
|
123
|
+
test-release:: verify package $(release_notes) $(release_changes)
|
124
|
+
# make tgz release on RubyForge
|
125
|
+
@echo rubyforge add_release -f \
|
126
|
+
-n $(release_notes) -a $(release_changes) \
|
127
|
+
$(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
|
128
|
+
@echo gem push $(pkggem)
|
129
|
+
@echo rubyforge add_file \
|
130
|
+
$(rfproject) $(rfpackage) $(VERSION) $(pkggem)
|
131
|
+
release:: verify package $(release_notes) $(release_changes)
|
132
|
+
# make tgz release on RubyForge
|
133
|
+
rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
|
134
|
+
$(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
|
135
|
+
# push gem to RubyGems.org
|
136
|
+
gem push $(pkggem)
|
137
|
+
# in case of gem downloads from RubyForge releases page
|
138
|
+
rubyforge add_file \
|
139
|
+
$(rfproject) $(rfpackage) $(VERSION) $(pkggem)
|
140
|
+
else
|
141
|
+
gem install-gem: GIT-VERSION-FILE
|
142
|
+
$(MAKE) $@ VERSION=$(GIT_VERSION)
|
143
|
+
endif
|
144
|
+
|
145
|
+
all:: test
|
146
|
+
test_units := $(wildcard test/test_*.rb)
|
147
|
+
test: test-unit
|
148
|
+
test-unit: $(test_units)
|
149
|
+
$(test_units): build
|
150
|
+
$(RUBY) -I $(lib) $@ $(RUBY_TEST_OPTS)
|
151
|
+
|
152
|
+
# this requires GNU coreutils variants
|
153
|
+
ifneq ($(RSYNC_DEST),)
|
154
|
+
publish_doc:
|
155
|
+
-git set-file-times
|
156
|
+
$(MAKE) doc
|
157
|
+
find doc/images -type f | \
|
158
|
+
TZ=UTC xargs touch -d '1970-01-01 00:00:06' doc/rdoc.css
|
159
|
+
$(MAKE) doc_gz
|
160
|
+
$(RSYNC) -av doc/ $(RSYNC_DEST)/
|
161
|
+
git ls-files | xargs touch
|
162
|
+
endif
|
163
|
+
|
164
|
+
# Create gzip variants of the same timestamp as the original so nginx
|
165
|
+
# "gzip_static on" can serve the gzipped versions directly.
|
166
|
+
doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
|
167
|
+
doc_gz:
|
168
|
+
for i in $(docs); do \
|
169
|
+
gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
170
|
+
|
171
|
+
.PHONY: all .FORCE-GIT-VERSION-FILE doc test $(test_units) manifest
|
data/raindrops.gemspec
CHANGED
@@ -1,38 +1,28 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
|
3
2
|
ENV["VERSION"] or abort "VERSION= must be specified"
|
4
3
|
manifest = File.readlines('.manifest').map! { |x| x.chomp! }
|
5
4
|
test_files = manifest.grep(%r{\Atest/test_.*\.rb\z})
|
5
|
+
require 'wrongdoc'
|
6
|
+
extend Wrongdoc::Gemspec
|
7
|
+
name, summary, title = readme_metadata
|
6
8
|
|
7
9
|
Gem::Specification.new do |s|
|
8
10
|
s.name = %q{raindrops}
|
9
|
-
s.version = ENV["VERSION"]
|
11
|
+
s.version = ENV["VERSION"].dup
|
10
12
|
|
11
13
|
s.authors = ["raindrops hackers"]
|
12
14
|
s.date = Time.now.utc.strftime('%Y-%m-%d')
|
13
|
-
s.description =
|
15
|
+
s.description = readme_description
|
14
16
|
s.email = %q{raindrops@librelist.com}
|
15
17
|
s.extensions = %w(ext/raindrops/extconf.rb)
|
16
|
-
|
17
|
-
s.extra_rdoc_files = File.readlines('.document').map! do |x|
|
18
|
-
x.chomp!
|
19
|
-
if File.directory?(x)
|
20
|
-
manifest.grep(%r{\A#{x}/})
|
21
|
-
elsif File.file?(x)
|
22
|
-
x
|
23
|
-
else
|
24
|
-
nil
|
25
|
-
end
|
26
|
-
end.flatten.compact
|
27
|
-
|
18
|
+
s.extra_rdoc_files = extra_rdoc_files(manifest)
|
28
19
|
s.files = manifest
|
29
|
-
s.homepage =
|
30
|
-
s.summary =
|
31
|
-
s.rdoc_options =
|
32
|
-
s.require_paths = %w(lib)
|
20
|
+
s.homepage = Wrongdoc.config[:rdoc_url]
|
21
|
+
s.summary = summary
|
22
|
+
s.rdoc_options = rdoc_options
|
33
23
|
s.rubyforge_project = %q{rainbows}
|
34
|
-
|
35
24
|
s.test_files = test_files
|
25
|
+
s.add_development_dependency('bundler', '~> 1.0.10')
|
36
26
|
|
37
27
|
# s.licenses = %w(LGPLv3) # accessor not compatible with older RubyGems
|
38
28
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require "test/unit"
|
3
|
+
require "raindrops"
|
4
|
+
require "rack"
|
5
|
+
require "rack/lobster"
|
6
|
+
require "open-uri"
|
7
|
+
begin
|
8
|
+
require "unicorn"
|
9
|
+
require "rack/lobster"
|
10
|
+
rescue LoadError => e
|
11
|
+
warn "W: #{e} skipping test since Rack or Unicorn was not found"
|
12
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "raindrops"
|
3
|
+
pmq = begin
|
4
|
+
Raindrops::Aggregate::PMQ
|
5
|
+
rescue => LoadError
|
6
|
+
warn "W: #{e} skipping test"
|
7
|
+
false
|
8
|
+
end
|
9
|
+
if RUBY_VERSION.to_f < 1.9
|
10
|
+
pmq = false
|
11
|
+
warn "W: skipping #{__FILE__}, only Ruby 1.9 supported for now"
|
12
|
+
end
|
13
|
+
|
14
|
+
Thread.abort_on_exception = true
|
15
|
+
|
16
|
+
class TestAggregatePMQ < Test::Unit::TestCase
|
17
|
+
|
18
|
+
def setup
|
19
|
+
@queue = "/test.#{rand}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
POSIX_MQ.unlink @queue
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_run
|
27
|
+
pmq = Raindrops::Aggregate::PMQ.new :queue => @queue
|
28
|
+
thr = Thread.new { pmq.master_loop }
|
29
|
+
agg = Aggregate.new
|
30
|
+
(1..10).each { |i| pmq << i; agg << i }
|
31
|
+
pmq.stop_master_loop
|
32
|
+
assert thr.join
|
33
|
+
assert_equal agg.count, pmq.count
|
34
|
+
assert_equal agg.mean, pmq.mean
|
35
|
+
assert_equal agg.std_dev, pmq.std_dev
|
36
|
+
assert_equal agg.min, pmq.min
|
37
|
+
assert_equal agg.max, pmq.max
|
38
|
+
assert_equal agg.to_s, pmq.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_multi_process
|
42
|
+
nr_workers = 4
|
43
|
+
nr = 100
|
44
|
+
pmq = Raindrops::Aggregate::PMQ.new :queue => @queue
|
45
|
+
pid = fork { pmq.master_loop }
|
46
|
+
workers = (1..nr_workers).map {
|
47
|
+
fork {
|
48
|
+
(1..nr).each { |i| pmq << i }
|
49
|
+
pmq.flush
|
50
|
+
}
|
51
|
+
}
|
52
|
+
workers.each { |pid| assert Process.waitpid2(pid).last.success? }
|
53
|
+
pmq.stop_master_loop
|
54
|
+
assert Process.waitpid2(pid).last.success?
|
55
|
+
assert_equal 400, pmq.count
|
56
|
+
agg = Aggregate.new
|
57
|
+
(1..nr_workers).map { (1..nr).each { |i| agg << i } }
|
58
|
+
assert_equal agg.to_s, pmq.to_s
|
59
|
+
assert_equal agg.mean, pmq.mean
|
60
|
+
assert_equal agg.std_dev, pmq.std_dev
|
61
|
+
assert_equal agg.min, pmq.min
|
62
|
+
assert_equal agg.max, pmq.max
|
63
|
+
assert_equal agg.to_s, pmq.to_s
|
64
|
+
end
|
65
|
+
end if pmq
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
require 'raindrops'
|
4
|
+
$stderr.sync = $stdout.sync = true
|
5
|
+
|
6
|
+
class TestInetDiagSocket < Test::Unit::TestCase
|
7
|
+
def test_new
|
8
|
+
sock = Raindrops::InetDiagSocket.new
|
9
|
+
assert_kind_of Socket, sock
|
10
|
+
assert_kind_of Fixnum, sock.fileno
|
11
|
+
assert_nil sock.close
|
12
|
+
end
|
13
|
+
end if RUBY_PLATFORM =~ /linux/
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require "./test/rack_unicorn"
|
3
|
+
require "tempfile"
|
4
|
+
require "net/http"
|
5
|
+
|
6
|
+
$stderr.sync = $stdout.sync = true
|
7
|
+
pmq = begin
|
8
|
+
Raindrops::Aggregate::PMQ
|
9
|
+
rescue => LoadError
|
10
|
+
warn "W: #{e} skipping test"
|
11
|
+
false
|
12
|
+
end
|
13
|
+
if RUBY_VERSION.to_f < 1.9
|
14
|
+
pmq = false
|
15
|
+
warn "W: skipping test=#{__FILE__}, only Ruby 1.9 supported for now"
|
16
|
+
end
|
17
|
+
|
18
|
+
class TestLastDataRecvUnicorn < Test::Unit::TestCase
|
19
|
+
def setup
|
20
|
+
@queue = "/test.#{rand}"
|
21
|
+
@host = ENV["UNICORN_TEST_ADDR"] || "127.0.0.1"
|
22
|
+
@sock = TCPServer.new @host, 0
|
23
|
+
@port = @sock.addr[1]
|
24
|
+
ENV["UNICORN_FD"] = @sock.fileno.to_s
|
25
|
+
@host_with_port = "#@host:#@port"
|
26
|
+
@cfg = Tempfile.new 'unicorn_config_file'
|
27
|
+
@cfg.puts "require 'raindrops'"
|
28
|
+
@cfg.puts "preload_app true"
|
29
|
+
ENV['RAINDROPS_MQUEUE'] = @queue
|
30
|
+
# @cfg.puts "worker_processes 4"
|
31
|
+
@opts = { :listeners => [ @host_with_port ], :config_file => @cfg.path }
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_auto_listener
|
35
|
+
@srv = fork {
|
36
|
+
Thread.abort_on_exception = true
|
37
|
+
app = %q!Rack::Builder.new do
|
38
|
+
map("/ldr") { run Raindrops::LastDataRecv.new }
|
39
|
+
map("/") { run Rack::Lobster.new }
|
40
|
+
end.to_app!
|
41
|
+
def app.arity; 0; end
|
42
|
+
def app.call; eval self; end
|
43
|
+
Unicorn.run(app, @opts)
|
44
|
+
}
|
45
|
+
400.times { assert_kind_of Net::HTTPSuccess, get("/") }
|
46
|
+
resp = get("/ldr")
|
47
|
+
# # p(resp.methods - Object.methods)
|
48
|
+
# resp.each_header { |k,v| p [k, "=" , v] }
|
49
|
+
assert resp.header["x-count"]
|
50
|
+
assert resp.header["x-min"]
|
51
|
+
assert resp.header["x-max"]
|
52
|
+
assert resp.header["x-mean"]
|
53
|
+
assert resp.header["x-std-dev"]
|
54
|
+
assert resp.header["x-outliers-low"]
|
55
|
+
assert resp.header["x-outliers-high"]
|
56
|
+
assert resp.body.size > 0
|
57
|
+
end
|
58
|
+
|
59
|
+
def get(path)
|
60
|
+
Net::HTTP.start(@host, @port) { |http| http.get path }
|
61
|
+
end
|
62
|
+
|
63
|
+
def teardown
|
64
|
+
Process.kill :QUIT, @srv
|
65
|
+
_, status = Process.waitpid2 @srv
|
66
|
+
assert status.success?
|
67
|
+
POSIX_MQ.unlink @queue
|
68
|
+
end
|
69
|
+
end if defined?(Unicorn) && RUBY_PLATFORM =~ /linux/ && pmq
|
data/test/test_linux.rb
CHANGED
@@ -39,9 +39,29 @@ class TestLinux < Test::Unit::TestCase
|
|
39
39
|
assert_equal 1, stats[tmp.path].queued
|
40
40
|
end
|
41
41
|
|
42
|
+
def test_unix_all
|
43
|
+
tmp = Tempfile.new("\xde\xad\xbe\xef") # valid path, really :)
|
44
|
+
File.unlink(tmp.path)
|
45
|
+
us = UNIXServer.new(tmp.path)
|
46
|
+
uc0 = UNIXSocket.new(tmp.path)
|
47
|
+
stats = unix_listener_stats
|
48
|
+
assert_equal 0, stats[tmp.path].active
|
49
|
+
assert_equal 1, stats[tmp.path].queued
|
50
|
+
|
51
|
+
uc1 = UNIXSocket.new(tmp.path)
|
52
|
+
stats = unix_listener_stats
|
53
|
+
assert_equal 0, stats[tmp.path].active
|
54
|
+
assert_equal 2, stats[tmp.path].queued
|
55
|
+
|
56
|
+
ua0 = us.accept
|
57
|
+
stats = unix_listener_stats
|
58
|
+
assert_equal 1, stats[tmp.path].active
|
59
|
+
assert_equal 1, stats[tmp.path].queued
|
60
|
+
end
|
61
|
+
|
42
62
|
def test_tcp
|
43
|
-
|
44
|
-
|
63
|
+
s = TCPServer.new(TEST_ADDR, 0)
|
64
|
+
port = s.addr[1]
|
45
65
|
addr = "#{TEST_ADDR}:#{port}"
|
46
66
|
addrs = [ addr ]
|
47
67
|
stats = tcp_listener_stats(addrs)
|
@@ -62,10 +82,36 @@ class TestLinux < Test::Unit::TestCase
|
|
62
82
|
assert_equal 1, stats[addr].active
|
63
83
|
end
|
64
84
|
|
85
|
+
def test_tcp_reuse_sock
|
86
|
+
nlsock = Raindrops::InetDiagSocket.new
|
87
|
+
s = TCPServer.new(TEST_ADDR, 0)
|
88
|
+
port = s.addr[1]
|
89
|
+
addr = "#{TEST_ADDR}:#{port}"
|
90
|
+
addrs = [ addr ]
|
91
|
+
stats = tcp_listener_stats(addrs, nlsock)
|
92
|
+
assert_equal 1, stats.size
|
93
|
+
assert_equal 0, stats[addr].queued
|
94
|
+
assert_equal 0, stats[addr].active
|
95
|
+
|
96
|
+
c = TCPSocket.new(TEST_ADDR, port)
|
97
|
+
stats = tcp_listener_stats(addrs, nlsock)
|
98
|
+
assert_equal 1, stats.size
|
99
|
+
assert_equal 1, stats[addr].queued
|
100
|
+
assert_equal 0, stats[addr].active
|
101
|
+
|
102
|
+
sc = s.accept
|
103
|
+
stats = tcp_listener_stats(addrs, nlsock)
|
104
|
+
assert_equal 1, stats.size
|
105
|
+
assert_equal 0, stats[addr].queued
|
106
|
+
assert_equal 1, stats[addr].active
|
107
|
+
ensure
|
108
|
+
nlsock.close
|
109
|
+
end
|
110
|
+
|
65
111
|
def test_tcp_multi
|
66
|
-
|
67
|
-
|
68
|
-
|
112
|
+
s1 = TCPServer.new(TEST_ADDR, 0)
|
113
|
+
s2 = TCPServer.new(TEST_ADDR, 0)
|
114
|
+
port1, port2 = s1.addr[1], s2.addr[1]
|
69
115
|
addr1, addr2 = "#{TEST_ADDR}:#{port1}", "#{TEST_ADDR}:#{port2}"
|
70
116
|
addrs = [ addr1, addr2 ]
|
71
117
|
stats = tcp_listener_stats(addrs)
|
@@ -127,10 +173,10 @@ class TestLinux < Test::Unit::TestCase
|
|
127
173
|
def test_tcp_stress_test
|
128
174
|
nr_proc = 32
|
129
175
|
nr_sock = 500
|
130
|
-
|
176
|
+
s = TCPServer.new(TEST_ADDR, 0)
|
177
|
+
port = s.addr[1]
|
131
178
|
addr = "#{TEST_ADDR}:#{port}"
|
132
179
|
addrs = [ addr ]
|
133
|
-
s = TCPServer.new(TEST_ADDR, port)
|
134
180
|
rda, wra = IO.pipe
|
135
181
|
rdb, wrb = IO.pipe
|
136
182
|
|
@@ -138,7 +184,7 @@ class TestLinux < Test::Unit::TestCase
|
|
138
184
|
fork do
|
139
185
|
rda.close
|
140
186
|
wrb.close
|
141
|
-
socks = (1..nr_sock).
|
187
|
+
socks = (1..nr_sock).map { s.accept }
|
142
188
|
wra.syswrite('.')
|
143
189
|
wra.close
|
144
190
|
rdb.sysread(1) # wait for parent to nuke us
|
@@ -149,7 +195,7 @@ class TestLinux < Test::Unit::TestCase
|
|
149
195
|
fork do
|
150
196
|
rda.close
|
151
197
|
wrb.close
|
152
|
-
socks = (1..nr_sock).
|
198
|
+
socks = (1..nr_sock).map { TCPSocket.new(TEST_ADDR, port) }
|
153
199
|
wra.syswrite('.')
|
154
200
|
wra.close
|
155
201
|
rdb.sysread(1) # wait for parent to nuke us
|
@@ -177,52 +223,4 @@ class TestLinux < Test::Unit::TestCase
|
|
177
223
|
statuses = Process.waitall
|
178
224
|
statuses.each { |(pid,status)| assert status.success?, status.inspect }
|
179
225
|
end if ENV["STRESS"].to_i != 0
|
180
|
-
|
181
|
-
private
|
182
|
-
|
183
|
-
# Stolen from Unicorn, also a version of this is used by the Rainbows!
|
184
|
-
# test suite.
|
185
|
-
# unused_port provides an unused port on +addr+ usable for TCP that is
|
186
|
-
# guaranteed to be unused across all compatible tests on that system. It
|
187
|
-
# prevents race conditions by using a lock file other tests
|
188
|
-
# will see. This is required if you perform several builds in parallel
|
189
|
-
# with a continuous integration system or run tests in parallel via
|
190
|
-
# gmake. This is NOT guaranteed to be race-free if you run other
|
191
|
-
# systems that bind to random ports for testing (but the window
|
192
|
-
# for a race condition is very small). You may also set UNICORN_TEST_ADDR
|
193
|
-
# to override the default test address (127.0.0.1).
|
194
|
-
def unused_port(addr = TEST_ADDR)
|
195
|
-
retries = 100
|
196
|
-
base = 5000
|
197
|
-
port = sock = nil
|
198
|
-
begin
|
199
|
-
begin
|
200
|
-
port = base + rand(32768 - base)
|
201
|
-
while port == 8080
|
202
|
-
port = base + rand(32768 - base)
|
203
|
-
end
|
204
|
-
|
205
|
-
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
206
|
-
sock.bind(Socket.pack_sockaddr_in(port, addr))
|
207
|
-
sock.listen(5)
|
208
|
-
rescue Errno::EADDRINUSE, Errno::EACCES
|
209
|
-
sock.close rescue nil
|
210
|
-
retry if (retries -= 1) >= 0
|
211
|
-
end
|
212
|
-
|
213
|
-
# since we'll end up closing the random port we just got, there's a race
|
214
|
-
# condition could allow the random port we just chose to reselect itself
|
215
|
-
# when running tests in parallel with gmake. Create a lock file while
|
216
|
-
# we have the port here to ensure that does not happen .
|
217
|
-
lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
|
218
|
-
lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
|
219
|
-
at_exit { File.unlink(lock_path) rescue nil }
|
220
|
-
rescue Errno::EEXIST
|
221
|
-
sock.close rescue nil
|
222
|
-
retry
|
223
|
-
end
|
224
|
-
sock.close rescue nil
|
225
|
-
port
|
226
|
-
end
|
227
|
-
|
228
226
|
end if RUBY_PLATFORM =~ /linux/
|