raindrops 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/
|