kgio 2.1.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.manifest +11 -2
- data/.wrongdoc.yml +4 -0
- data/ChangeLog +810 -531
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +4 -147
- data/LATEST +25 -0
- data/NEWS +49 -9
- data/README +2 -2
- data/Rakefile +2 -2
- data/ext/kgio/accept.c +64 -20
- data/ext/kgio/{missing/ancient_ruby.h → ancient_ruby.h} +0 -0
- data/ext/kgio/autopush.c +203 -0
- data/ext/kgio/connect.c +29 -14
- data/ext/kgio/extconf.rb +13 -2
- data/ext/kgio/kgio.h +7 -1
- data/ext/kgio/kgio_ext.c +1 -0
- data/ext/kgio/{missing/accept4.h → missing_accept4.h} +0 -0
- data/ext/kgio/read_write.c +58 -3
- data/kgio.gemspec +2 -1
- data/pkg.mk +168 -0
- data/test/lib_read_write.rb +10 -2
- data/test/test_autopush.rb +165 -0
- data/test/test_kgio_addr.rb +19 -0
- data/test/test_no_dns_on_tcp_connect.rb +13 -0
- data/test/test_singleton_read_write.rb +21 -0
- data/test/test_tcp6_client_read_server_write.rb +23 -0
- metadata +41 -12
data/GIT-VERSION-FILE
CHANGED
@@ -1 +1 @@
|
|
1
|
-
GIT_VERSION = 2.
|
1
|
+
GIT_VERSION = 2.2.0
|
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -1,153 +1,10 @@
|
|
1
|
-
# use GNU Make to run tests in parallel, and without depending on RubyGems
|
2
1
|
all::
|
3
|
-
|
4
|
-
RAKE = rake
|
5
|
-
RSYNC = rsync
|
6
|
-
|
7
|
-
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
8
|
-
@./GIT-VERSION-GEN
|
9
|
-
-include GIT-VERSION-FILE
|
10
|
-
-include local.mk
|
11
|
-
ifeq ($(DLEXT),) # "so" for Linux
|
12
|
-
DLEXT := $(shell $(RUBY) -rrbconfig -e 'puts Config::CONFIG["DLEXT"]')
|
13
|
-
endif
|
14
|
-
ifeq ($(RUBY_VERSION),)
|
15
|
-
RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
|
16
|
-
endif
|
17
|
-
|
18
|
-
install:
|
19
|
-
$(prep_setup_rb)
|
20
|
-
$(RM) -r .install-tmp
|
21
|
-
mkdir .install-tmp
|
22
|
-
$(RUBY) setup.rb all
|
23
|
-
$(RM) $^
|
24
|
-
$(RM) -r .install-tmp
|
25
|
-
$(prep_setup_rb)
|
26
|
-
|
27
|
-
setup_rb_files := .config InstalledFiles
|
28
|
-
prep_setup_rb := @-$(RM) $(setup_rb_files);$(MAKE) -C $(ext) clean
|
29
|
-
|
30
|
-
clean:
|
31
|
-
-$(MAKE) -C ext/kgio clean
|
32
|
-
$(RM) $(setup_rb_files) ext/kgio/Makefile
|
33
|
-
|
34
|
-
pkg_extra := GIT-VERSION-FILE NEWS ChangeLog LATEST
|
35
|
-
ChangeLog: GIT-VERSION-FILE .wrongdoc.yml
|
36
|
-
wrongdoc prepare
|
37
|
-
|
38
|
-
.manifest: ChangeLog
|
39
|
-
(git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \
|
40
|
-
LC_ALL=C sort > $@+
|
41
|
-
cmp $@+ $@ || mv $@+ $@
|
42
|
-
$(RM) $@+
|
43
|
-
|
44
|
-
doc: .document .wrongdoc.yml
|
45
|
-
find lib ext -type f -name '*.rbc' -exec rm -f '{}' ';'
|
46
|
-
$(RM) -r doc
|
47
|
-
wrongdoc all
|
48
|
-
install -m644 COPYING doc/COPYING
|
49
|
-
install -m644 $(shell grep '^[A-Z]' .document) doc/
|
50
|
-
|
51
|
-
ifneq ($(VERSION),)
|
2
|
+
RSYNC_DEST := bogomips.org:/srv/bogomips/kgio
|
52
3
|
rfproject := rainbows
|
53
4
|
rfpackage := kgio
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
release_changes := release_changes-$(VERSION)
|
58
|
-
|
59
|
-
release-notes: $(release_notes)
|
60
|
-
release-changes: $(release_changes)
|
61
|
-
$(release_changes):
|
62
|
-
wrongdoc release_changes > $@+
|
63
|
-
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
64
|
-
$(release_notes):
|
65
|
-
wrongdoc release_notes > $@+
|
66
|
-
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
67
|
-
|
68
|
-
# ensures we're actually on the tagged $(VERSION), only used for release
|
69
|
-
verify:
|
70
|
-
test x"$(shell umask)" = x0022
|
71
|
-
git rev-parse --verify refs/tags/v$(VERSION)^{}
|
72
|
-
git diff-index --quiet HEAD^0
|
73
|
-
test `git rev-parse --verify HEAD^0` = \
|
74
|
-
`git rev-parse --verify refs/tags/v$(VERSION)^{}`
|
75
|
-
|
76
|
-
fix-perms:
|
77
|
-
-git ls-tree -r HEAD | awk '/^100644 / {print $$NF}' | xargs chmod 644
|
78
|
-
-git ls-tree -r HEAD | awk '/^100755 / {print $$NF}' | xargs chmod 755
|
79
|
-
|
80
|
-
gem: $(pkggem)
|
81
|
-
|
82
|
-
install-gem: $(pkggem)
|
83
|
-
gem install $(CURDIR)/$<
|
84
|
-
|
85
|
-
$(pkggem): manifest fix-perms
|
86
|
-
gem build $(rfpackage).gemspec
|
87
|
-
mkdir -p pkg
|
88
|
-
mv $(@F) $@
|
89
|
-
|
90
|
-
$(pkgtgz): distdir = $(basename $@)
|
91
|
-
$(pkgtgz): HEAD = v$(VERSION)
|
92
|
-
$(pkgtgz): manifest fix-perms
|
93
|
-
@test -n "$(distdir)"
|
94
|
-
$(RM) -r $(distdir)
|
95
|
-
mkdir -p $(distdir)
|
96
|
-
tar cf - `cat .manifest` | (cd $(distdir) && tar xf -)
|
97
|
-
cd pkg && tar cf - $(basename $(@F)) | gzip -9 > $(@F)+
|
98
|
-
mv $@+ $@
|
99
|
-
|
100
|
-
package: $(pkgtgz) $(pkggem)
|
101
|
-
|
102
|
-
test-release: verify package $(release_notes) $(release_changes)
|
103
|
-
release: verify package $(release_notes) $(release_changes)
|
104
|
-
# make tgz release on RubyForge
|
105
|
-
rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
|
106
|
-
$(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
|
107
|
-
# push gem to RubyGems.org
|
108
|
-
gem push $(pkggem)
|
109
|
-
# in case of gem downloads from RubyForge releases page
|
110
|
-
-rubyforge add_file \
|
111
|
-
$(rfproject) $(rfpackage) $(VERSION) $(pkggem)
|
5
|
+
include pkg.mk
|
6
|
+
ifneq ($(VERSION),)
|
7
|
+
release::
|
112
8
|
$(RAKE) raa_update VERSION=$(VERSION)
|
113
9
|
$(RAKE) publish_news VERSION=$(VERSION)
|
114
|
-
else
|
115
|
-
gem install-gem: GIT-VERSION-FILE
|
116
|
-
$(MAKE) $@ VERSION=$(GIT_VERSION)
|
117
10
|
endif
|
118
|
-
|
119
|
-
ext := ext/kgio/kgio_ext.$(DLEXT)
|
120
|
-
ext/kgio/Makefile: ext/kgio/extconf.rb
|
121
|
-
cd $(@D) && $(RUBY) extconf.rb
|
122
|
-
|
123
|
-
$(ext): $(wildcard ext/kgio/*.[ch] ext/kgio/*/*.h) ext/kgio/Makefile
|
124
|
-
$(MAKE) -C $(@D)
|
125
|
-
|
126
|
-
all:: test
|
127
|
-
|
128
|
-
build: $(ext)
|
129
|
-
test_units := $(wildcard test/test_*.rb)
|
130
|
-
test: test-unit
|
131
|
-
test-unit: $(test_units)
|
132
|
-
$(test_units): build
|
133
|
-
$(RUBY) -I lib:ext/kgio $@
|
134
|
-
|
135
|
-
# this requires GNU coreutils variants
|
136
|
-
publish_doc:
|
137
|
-
-git set-file-times
|
138
|
-
$(MAKE) doc
|
139
|
-
find doc/images -type f | \
|
140
|
-
TZ=UTC xargs touch -d '1970-01-01 00:00:00' doc/rdoc.css
|
141
|
-
$(MAKE) doc_gz
|
142
|
-
chmod 644 $$(find doc -type f)
|
143
|
-
$(RSYNC) -av doc/ bogomips.org:/srv/bogomips/kgio/
|
144
|
-
git ls-files | xargs touch
|
145
|
-
|
146
|
-
# Create gzip variants of the same timestamp as the original so nginx
|
147
|
-
# "gzip_static on" can serve the gzipped versions directly.
|
148
|
-
doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
|
149
|
-
doc_gz:
|
150
|
-
for i in $(docs); do \
|
151
|
-
gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
152
|
-
|
153
|
-
.PHONY: .FORCE-GIT-VERSION-FILE doc manifest test $(test_units)
|
data/LATEST
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
=== kgio 2.2.0 - kinder, gentler I/O for the Internets / 2011-02-04 03:07 UTC
|
2
|
+
|
3
|
+
* sockets accept()ed by a TCP_NOPUSH/TCP_CORK listener
|
4
|
+
automatically flush on kgio_*read calls if there is pending
|
5
|
+
data. "Kgio.autopush = false" disables this globally,
|
6
|
+
and Kgio::Socket also get "kgio_autopush=" to enable/disable
|
7
|
+
on a per-object individual basis.
|
8
|
+
|
9
|
+
* ECONNRESET exceptions get empty backtraces for kgio_*read.
|
10
|
+
There's nothing a programmer can do about these, so there's
|
11
|
+
no point in going through the expensive backtrace generation
|
12
|
+
process.
|
13
|
+
|
14
|
+
* Kgio.try* singleton methods added for working with non-Kgio
|
15
|
+
enhanced objects. No more needing to use Object#extend
|
16
|
+
and blowing away your method cache to make existing I/O
|
17
|
+
objects kinder and gentler.
|
18
|
+
|
19
|
+
* IPv6 support should be complete, systems without a native
|
20
|
+
getaddrinfo(3) are now unsupported (and will remain so
|
21
|
+
unless somebody complains).
|
22
|
+
|
23
|
+
There should be no other backwards-incompatible changes other
|
24
|
+
than requiring getaddrinfo(3) and friends for IPv6 support.
|
25
|
+
|
data/NEWS
CHANGED
@@ -1,4 +1,44 @@
|
|
1
|
-
=== 2.
|
1
|
+
=== kgio 2.2.0 - kinder, gentler I/O for the Internets / 2011-02-04 03:07 UTC
|
2
|
+
|
3
|
+
* sockets accept()ed by a TCP_NOPUSH/TCP_CORK listener
|
4
|
+
automatically flush on kgio_*read calls if there is pending
|
5
|
+
data. "Kgio.autopush = false" disables this globally,
|
6
|
+
and Kgio::Socket also get "kgio_autopush=" to enable/disable
|
7
|
+
on a per-object individual basis.
|
8
|
+
|
9
|
+
* ECONNRESET exceptions get empty backtraces for kgio_*read.
|
10
|
+
There's nothing a programmer can do about these, so there's
|
11
|
+
no point in going through the expensive backtrace generation
|
12
|
+
process.
|
13
|
+
|
14
|
+
* Kgio.try* singleton methods added for working with non-Kgio
|
15
|
+
enhanced objects. No more needing to use Object#extend
|
16
|
+
and blowing away your method cache to make existing I/O
|
17
|
+
objects kinder and gentler.
|
18
|
+
|
19
|
+
* IPv6 support should be complete, systems without a native
|
20
|
+
getaddrinfo(3) are now unsupported (and will remain so
|
21
|
+
unless somebody complains).
|
22
|
+
|
23
|
+
There should be no other backwards-incompatible changes other
|
24
|
+
than requiring getaddrinfo(3) and friends for IPv6 support.
|
25
|
+
|
26
|
+
=== kgio 2.1.1 - one small Rubinius fix / 2010-12-26 02:08 UTC
|
27
|
+
|
28
|
+
We now avoid errno side-effects in kgio_wait_*able methods.
|
29
|
+
This affects Rubinius, but may affect other Ruby platforms
|
30
|
+
(particularly those that use stdio) as well.
|
31
|
+
|
32
|
+
=== kgio 2.1.0 - accept improvements and fixes / 2010-12-26 01:07 UTC
|
33
|
+
|
34
|
+
kgio_accept and kgio_tryaccept now take an optional argument
|
35
|
+
to override the default Kgio::Socket class that is returned.
|
36
|
+
|
37
|
+
These methods also fall back to using regular accept() if
|
38
|
+
kgio was built on a system with accept4() and later run on
|
39
|
+
a system without accept4().
|
40
|
+
|
41
|
+
=== kgio 2.0.0 - major internal API changes / 2010-11-19 01:18 UTC
|
2
42
|
|
3
43
|
(no code changes from 2.0.0pre1)
|
4
44
|
|
@@ -19,7 +59,7 @@
|
|
19
59
|
add default kgio_wait_*able methods
|
20
60
|
switch entirely to kgio_wait_*able methods
|
21
61
|
|
22
|
-
=== 2.0.0pre1 / 2010-11-18 23:16 UTC
|
62
|
+
=== kgio 2.0.0pre1 - major internal API changes / 2010-11-18 23:16 UTC
|
23
63
|
|
24
64
|
This release should make Kgio easier and more consistent
|
25
65
|
to use across a variety of libraries/applications.
|
@@ -38,20 +78,20 @@
|
|
38
78
|
add default kgio_wait_*able methods
|
39
79
|
switch entirely to kgio_wait_*able methods
|
40
80
|
|
41
|
-
=== 1.3.1 / 2010-10-08 22:20 UTC
|
81
|
+
=== kgio 1.3.1 - fix zero-length reads / 2010-10-08 22:20 UTC
|
42
82
|
|
43
83
|
kgio_read and kgio_tryread will now return an empty string when
|
44
84
|
a length of zero is specified instead of nil (which would signal
|
45
85
|
an EOF). This emulates the behavior of IO#read, IO#readpartial,
|
46
86
|
IO#sysread, IO#read_nonblock in core Ruby for consistency.
|
47
87
|
|
48
|
-
=== 1.3.0 / 2010-10-08 03:03 UTC
|
88
|
+
=== kgio 1.3.0 - bug and usability fixes / 2010-10-08 03:03 UTC
|
49
89
|
|
50
90
|
* make Kgio::WaitWritable and Kgio::WaitReadable symbols
|
51
91
|
* trywrite: fix stupid off-by-one error causing corrupt writes
|
52
92
|
on retries
|
53
93
|
|
54
|
-
=== 1.2.1 / 2010-10-07 07:20 UTC
|
94
|
+
=== kgio 1.2.1 - doc and *BSD workarounds / 2010-10-07 07:20 UTC
|
55
95
|
|
56
96
|
This fixes our accept4() wrapper which did not work as expected
|
57
97
|
on some *BSD-based systems due to fcntl(fd, F_GETFL) returning
|
@@ -60,7 +100,7 @@
|
|
60
100
|
|
61
101
|
Also some RDoc fixes.
|
62
102
|
|
63
|
-
=== 1.2.0 / 2010-10-05 23:14 UTC
|
103
|
+
=== kgio 1.2.0 - cleanups and minor improvements / 2010-10-05 23:14 UTC
|
64
104
|
|
65
105
|
The C extension is now split into several files for
|
66
106
|
ease-of-maintenance.
|
@@ -69,17 +109,17 @@
|
|
69
109
|
Errno::EPIPE, Errno::ECONNRESET) are now less expensive as they
|
70
110
|
are generated without backtraces.
|
71
111
|
|
72
|
-
=== 1.1.0 / 2010-09-29 01:17 UTC
|
112
|
+
=== kgio 1.1.0 - flexible accept methods / 2010-09-29 01:17 UTC
|
73
113
|
|
74
114
|
* alternate classes may now be returned by accept/tryaccept
|
75
115
|
by setting Kgio.accept_class=
|
76
116
|
|
77
|
-
=== 1.0.1 / 2010-09-28 03:00 UTC
|
117
|
+
=== kgio 1.0.1 - compatibility fixes / 2010-09-28 03:00 UTC
|
78
118
|
|
79
119
|
* add compatibility for ancient Rubies (1.8.6)
|
80
120
|
* linux: fix accept4() support for newer Linux
|
81
121
|
|
82
|
-
=== 1.0.0 / 2010-09-28 00:29 UTC
|
122
|
+
=== kgio 1.0.0 - initial release / 2010-09-28 00:29 UTC
|
83
123
|
|
84
124
|
A kinder, gentler I/O library for Ruby
|
85
125
|
|
data/README
CHANGED
@@ -45,13 +45,13 @@ You may also install it via RubyGems.org:
|
|
45
45
|
You can get the latest source via git from the following locations
|
46
46
|
(these versions may not be stable):
|
47
47
|
|
48
|
-
git://
|
48
|
+
git://bogomips.org/kgio.git
|
49
49
|
git://repo.or.cz/kgio.git (mirror)
|
50
50
|
|
51
51
|
You may browse the code from the web and download the latest snapshot
|
52
52
|
tarballs here:
|
53
53
|
|
54
|
-
* http://
|
54
|
+
* http://bogomips.org/kgio.git (cgit)
|
55
55
|
* http://repo.or.cz/w/kgio.git (gitweb)
|
56
56
|
|
57
57
|
See the HACKING guide on how to contribute and build prerelease gems
|
data/Rakefile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
-
cgit_url = "http://
|
3
|
-
git_url = 'git://
|
2
|
+
cgit_url = "http://bogomips.org/kgio.git"
|
3
|
+
git_url = 'git://bogomips.org/kgio.git'
|
4
4
|
|
5
5
|
desc "post news article to rubyforge"
|
6
6
|
task :publish_news do
|
data/ext/kgio/accept.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#include "kgio.h"
|
2
|
-
#include "
|
2
|
+
#include "missing_accept4.h"
|
3
3
|
#include "sock_for_fd.h"
|
4
4
|
|
5
5
|
static VALUE localhost;
|
@@ -133,14 +133,21 @@ static VALUE acceptor(int argc, const VALUE *argv)
|
|
133
133
|
rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
|
134
134
|
}
|
135
135
|
|
136
|
+
#if defined(__linux__)
|
137
|
+
# define post_accept kgio_autopush_accept
|
138
|
+
#else
|
139
|
+
# define post_accept(a,b) for(;0;)
|
140
|
+
#endif
|
141
|
+
|
136
142
|
static VALUE
|
137
|
-
my_accept(VALUE
|
143
|
+
my_accept(VALUE accept_io, VALUE klass,
|
138
144
|
struct sockaddr *addr, socklen_t *addrlen, int nonblock)
|
139
145
|
{
|
140
146
|
int client;
|
147
|
+
VALUE client_io;
|
141
148
|
struct accept_args a;
|
142
149
|
|
143
|
-
a.fd = my_fileno(
|
150
|
+
a.fd = my_fileno(accept_io);
|
144
151
|
a.addr = addr;
|
145
152
|
a.addrlen = addrlen;
|
146
153
|
retry:
|
@@ -175,20 +182,55 @@ retry:
|
|
175
182
|
rb_sys_fail("accept");
|
176
183
|
}
|
177
184
|
}
|
178
|
-
|
185
|
+
client_io = sock_for_fd(klass, client);
|
186
|
+
post_accept(accept_io, client_io);
|
187
|
+
return client_io;
|
179
188
|
}
|
180
189
|
|
181
|
-
static
|
190
|
+
static VALUE in_addr_set(VALUE io, struct sockaddr_storage *addr, socklen_t len)
|
191
|
+
{
|
192
|
+
VALUE host;
|
193
|
+
int host_len, rc;
|
194
|
+
char *host_ptr;
|
195
|
+
|
196
|
+
switch (addr->ss_family) {
|
197
|
+
case AF_INET:
|
198
|
+
host_len = (long)INET_ADDRSTRLEN;
|
199
|
+
break;
|
200
|
+
case AF_INET6:
|
201
|
+
host_len = (long)INET6_ADDRSTRLEN;
|
202
|
+
break;
|
203
|
+
default:
|
204
|
+
rb_raise(rb_eRuntimeError, "unsupported address family");
|
205
|
+
}
|
206
|
+
host = rb_str_new(NULL, host_len);
|
207
|
+
host_ptr = RSTRING_PTR(host);
|
208
|
+
rc = getnameinfo((struct sockaddr *)addr, len,
|
209
|
+
host_ptr, host_len, NULL, 0, NI_NUMERICHOST);
|
210
|
+
if (rc != 0)
|
211
|
+
rb_raise(rb_eRuntimeError, "getnameinfo: %s", gai_strerror(rc));
|
212
|
+
rb_str_set_len(host, strlen(host_ptr));
|
213
|
+
return rb_ivar_set(io, iv_kgio_addr, host);
|
214
|
+
}
|
215
|
+
|
216
|
+
/*
|
217
|
+
* call-seq:
|
218
|
+
*
|
219
|
+
* io.kgio_addr! => refreshes the given sock address
|
220
|
+
*/
|
221
|
+
static VALUE addr_bang(VALUE io)
|
182
222
|
{
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
223
|
+
int fd = my_fileno(io);
|
224
|
+
struct sockaddr_storage addr;
|
225
|
+
socklen_t len = sizeof(struct sockaddr_storage);
|
226
|
+
|
227
|
+
if (getpeername(fd, (struct sockaddr *)&addr, &len) != 0)
|
228
|
+
rb_sys_fail("getpeername");
|
229
|
+
|
230
|
+
if (addr.ss_family == AF_UNIX)
|
231
|
+
return rb_ivar_set(io, iv_kgio_addr, localhost);
|
232
|
+
|
233
|
+
return in_addr_set(io, &addr, len);
|
192
234
|
}
|
193
235
|
|
194
236
|
/*
|
@@ -210,13 +252,13 @@ static void in_addr_set(VALUE io, struct sockaddr_in *addr)
|
|
210
252
|
*/
|
211
253
|
static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE io)
|
212
254
|
{
|
213
|
-
struct
|
214
|
-
socklen_t addrlen = sizeof(struct
|
255
|
+
struct sockaddr_storage addr;
|
256
|
+
socklen_t addrlen = sizeof(struct sockaddr_storage);
|
215
257
|
VALUE klass = acceptor(argc, argv);
|
216
258
|
VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 1);
|
217
259
|
|
218
260
|
if (!NIL_P(rv))
|
219
|
-
in_addr_set(rv, &addr);
|
261
|
+
in_addr_set(rv, &addr, addrlen);
|
220
262
|
return rv;
|
221
263
|
}
|
222
264
|
|
@@ -240,12 +282,12 @@ static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE io)
|
|
240
282
|
*/
|
241
283
|
static VALUE tcp_accept(int argc, VALUE *argv, VALUE io)
|
242
284
|
{
|
243
|
-
struct
|
244
|
-
socklen_t addrlen = sizeof(struct
|
285
|
+
struct sockaddr_storage addr;
|
286
|
+
socklen_t addrlen = sizeof(struct sockaddr_storage);
|
245
287
|
VALUE klass = acceptor(argc, argv);
|
246
288
|
VALUE rv = my_accept(io, klass, (struct sockaddr *)&addr, &addrlen, 0);
|
247
289
|
|
248
|
-
in_addr_set(rv, &addr);
|
290
|
+
in_addr_set(rv, &addr, addrlen);
|
249
291
|
return rv;
|
250
292
|
}
|
251
293
|
|
@@ -402,6 +444,8 @@ void init_kgio_accept(void)
|
|
402
444
|
cClientSocket = cKgio_Socket;
|
403
445
|
mSocketMethods = rb_const_get(mKgio, rb_intern("SocketMethods"));
|
404
446
|
|
447
|
+
rb_define_method(mSocketMethods, "kgio_addr!", addr_bang, 0);
|
448
|
+
|
405
449
|
rb_define_singleton_method(mKgio, "accept_cloexec?", get_cloexec, 0);
|
406
450
|
rb_define_singleton_method(mKgio, "accept_cloexec=", set_cloexec, 1);
|
407
451
|
rb_define_singleton_method(mKgio, "accept_nonblock?", get_nonblock, 0);
|
File without changes
|
data/ext/kgio/autopush.c
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
/*
|
2
|
+
* We use a very basic strategy to use TCP_CORK semantics optimally
|
3
|
+
* in most TCP servers: On corked sockets, we will uncork on recv()
|
4
|
+
* if there was a previous send(). Otherwise we do not fiddle
|
5
|
+
* with TCP_CORK at all.
|
6
|
+
*
|
7
|
+
* Under Linux, we can rely on TCP_CORK being inherited in an
|
8
|
+
* accept()-ed client socket so we can avoid syscalls for each
|
9
|
+
* accept()-ed client if we know the accept() socket corks.
|
10
|
+
*
|
11
|
+
* This module does NOTHING for client TCP sockets, we only deal
|
12
|
+
* with accept()-ed sockets right now.
|
13
|
+
*/
|
14
|
+
|
15
|
+
#include "kgio.h"
|
16
|
+
#include <netinet/tcp.h>
|
17
|
+
|
18
|
+
/*
|
19
|
+
* As of FreeBSD 4.5, TCP_NOPUSH == TCP_CORK
|
20
|
+
* ref: http://dotat.at/writing/nopush.html
|
21
|
+
* We won't care for older FreeBSD since nobody runs Ruby on them...
|
22
|
+
*/
|
23
|
+
#ifdef TCP_CORK
|
24
|
+
# define KGIO_NOPUSH TCP_CORK
|
25
|
+
#elif defined(TCP_NOPUSH)
|
26
|
+
# define KGIO_NOPUSH TCP_NOPUSH
|
27
|
+
#endif
|
28
|
+
|
29
|
+
#ifdef KGIO_NOPUSH
|
30
|
+
static ID id_autopush_state;
|
31
|
+
static int enabled = 1;
|
32
|
+
|
33
|
+
enum autopush_state {
|
34
|
+
AUTOPUSH_STATE_ACCEPTOR_IGNORE = -1,
|
35
|
+
AUTOPUSH_STATE_IGNORE = 0,
|
36
|
+
AUTOPUSH_STATE_WRITER = 1,
|
37
|
+
AUTOPUSH_STATE_WRITTEN = 2,
|
38
|
+
AUTOPUSH_STATE_ACCEPTOR = 3
|
39
|
+
};
|
40
|
+
|
41
|
+
#if defined(R_CAST) && \
|
42
|
+
defined(HAVE_TYPE_STRUCT_RFILE) && \
|
43
|
+
defined(HAVE_TYPE_STRUCT_ROBJECT) && \
|
44
|
+
((SIZEOF_STRUCT_RFILE + SIZEOF_INT) <= (SIZEOF_STRUCT_ROBJECT))
|
45
|
+
|
46
|
+
struct AutopushSocket {
|
47
|
+
struct RFile rfile;
|
48
|
+
enum autopush_state autopush_state;
|
49
|
+
};
|
50
|
+
|
51
|
+
static enum autopush_state state_get(VALUE io)
|
52
|
+
{
|
53
|
+
return ((struct AutopushSocket *)(io))->autopush_state;
|
54
|
+
}
|
55
|
+
|
56
|
+
static void state_set(VALUE io, enum autopush_state state)
|
57
|
+
{
|
58
|
+
((struct AutopushSocket *)(io))->autopush_state = state;
|
59
|
+
}
|
60
|
+
#else
|
61
|
+
static enum autopush_state state_get(VALUE io)
|
62
|
+
{
|
63
|
+
VALUE val;
|
64
|
+
|
65
|
+
if (rb_ivar_defined(io, id_autopush_state) == Qfalse)
|
66
|
+
return AUTOPUSH_STATE_IGNORE;
|
67
|
+
val = rb_ivar_get(io, id_autopush_state);
|
68
|
+
|
69
|
+
return (enum autopush_state)NUM2INT(val);
|
70
|
+
}
|
71
|
+
|
72
|
+
static void state_set(VALUE io, enum autopush_state state)
|
73
|
+
{
|
74
|
+
rb_ivar_set(io, id_autopush_state, INT2NUM(state));
|
75
|
+
}
|
76
|
+
#endif /* IVAR fallback */
|
77
|
+
|
78
|
+
static enum autopush_state detect_acceptor_state(VALUE io);
|
79
|
+
static void push_pending_data(VALUE io);
|
80
|
+
|
81
|
+
static VALUE s_get_autopush(VALUE self)
|
82
|
+
{
|
83
|
+
return enabled ? Qtrue : Qfalse;
|
84
|
+
}
|
85
|
+
|
86
|
+
static VALUE s_set_autopush(VALUE self, VALUE val)
|
87
|
+
{
|
88
|
+
enabled = RTEST(val);
|
89
|
+
|
90
|
+
return val;
|
91
|
+
}
|
92
|
+
|
93
|
+
static VALUE autopush_get(VALUE io)
|
94
|
+
{
|
95
|
+
return state_get(io) <= 0 ? Qfalse : Qtrue;
|
96
|
+
}
|
97
|
+
|
98
|
+
static VALUE autopush_set(VALUE io, VALUE vbool)
|
99
|
+
{
|
100
|
+
int fd = my_fileno(io);
|
101
|
+
int val;
|
102
|
+
socklen_t len = sizeof(val);
|
103
|
+
|
104
|
+
if (RTEST(vbool))
|
105
|
+
state_set(io, AUTOPUSH_STATE_WRITER);
|
106
|
+
else
|
107
|
+
state_set(io, AUTOPUSH_STATE_IGNORE);
|
108
|
+
return vbool;
|
109
|
+
}
|
110
|
+
|
111
|
+
void init_kgio_autopush(void)
|
112
|
+
{
|
113
|
+
VALUE mKgio = rb_define_module("Kgio");
|
114
|
+
VALUE tmp;
|
115
|
+
|
116
|
+
rb_define_singleton_method(mKgio, "autopush?", s_get_autopush, 0);
|
117
|
+
rb_define_singleton_method(mKgio, "autopush=", s_set_autopush, 1);
|
118
|
+
|
119
|
+
tmp = rb_define_module_under(mKgio, "SocketMethods");
|
120
|
+
rb_define_method(tmp, "kgio_autopush=", autopush_set, 1);
|
121
|
+
rb_define_method(tmp, "kgio_autopush?", autopush_get, 0);
|
122
|
+
|
123
|
+
id_autopush_state = rb_intern("@kgio_autopush_state");
|
124
|
+
}
|
125
|
+
|
126
|
+
/*
|
127
|
+
* called after a successful write, just mark that we've put something
|
128
|
+
* in the skb and will need to uncork on the next write.
|
129
|
+
*/
|
130
|
+
void kgio_autopush_send(VALUE io)
|
131
|
+
{
|
132
|
+
if (state_get(io) == AUTOPUSH_STATE_WRITER)
|
133
|
+
state_set(io, AUTOPUSH_STATE_WRITTEN);
|
134
|
+
}
|
135
|
+
|
136
|
+
/* called on successful accept() */
|
137
|
+
void kgio_autopush_accept(VALUE accept_io, VALUE client_io)
|
138
|
+
{
|
139
|
+
enum autopush_state acceptor_state;
|
140
|
+
|
141
|
+
if (!enabled)
|
142
|
+
return;
|
143
|
+
acceptor_state = state_get(accept_io);
|
144
|
+
if (acceptor_state == AUTOPUSH_STATE_IGNORE)
|
145
|
+
acceptor_state = detect_acceptor_state(accept_io);
|
146
|
+
if (acceptor_state == AUTOPUSH_STATE_ACCEPTOR)
|
147
|
+
state_set(client_io, AUTOPUSH_STATE_WRITER);
|
148
|
+
else
|
149
|
+
state_set(client_io, AUTOPUSH_STATE_IGNORE);
|
150
|
+
}
|
151
|
+
|
152
|
+
void kgio_autopush_recv(VALUE io)
|
153
|
+
{
|
154
|
+
if (enabled && (state_get(io) == AUTOPUSH_STATE_WRITTEN)) {
|
155
|
+
push_pending_data(io);
|
156
|
+
state_set(io, AUTOPUSH_STATE_WRITER);
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
static enum autopush_state detect_acceptor_state(VALUE io)
|
161
|
+
{
|
162
|
+
int corked = 0;
|
163
|
+
int fd = my_fileno(io);
|
164
|
+
socklen_t optlen = sizeof(int);
|
165
|
+
enum autopush_state state;
|
166
|
+
|
167
|
+
if (getsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &corked, &optlen) != 0) {
|
168
|
+
if (errno != EOPNOTSUPP)
|
169
|
+
rb_sys_fail("getsockopt(TCP_CORK/TCP_NOPUSH)");
|
170
|
+
errno = 0;
|
171
|
+
state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
|
172
|
+
} else if (corked) {
|
173
|
+
state = AUTOPUSH_STATE_ACCEPTOR;
|
174
|
+
} else {
|
175
|
+
state = AUTOPUSH_STATE_ACCEPTOR_IGNORE;
|
176
|
+
}
|
177
|
+
state_set(io, state);
|
178
|
+
|
179
|
+
return state;
|
180
|
+
}
|
181
|
+
|
182
|
+
/*
|
183
|
+
* checks to see if we've written anything since the last recv()
|
184
|
+
* If we have, uncork the socket and immediately recork it.
|
185
|
+
*/
|
186
|
+
static void push_pending_data(VALUE io)
|
187
|
+
{
|
188
|
+
int optval = 0;
|
189
|
+
const socklen_t optlen = sizeof(int);
|
190
|
+
const int fd = my_fileno(io);
|
191
|
+
|
192
|
+
if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
|
193
|
+
rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 0)");
|
194
|
+
/* immediately recork */
|
195
|
+
optval = 1;
|
196
|
+
if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0)
|
197
|
+
rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 1)");
|
198
|
+
}
|
199
|
+
#else /* !KGIO_NOPUSH */
|
200
|
+
void init_kgio_autopush(void)
|
201
|
+
{
|
202
|
+
}
|
203
|
+
#endif /* ! KGIO_NOPUSH */
|