kgio 2.1.1 → 2.2.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/.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 */
|