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/ext/kgio/connect.c
CHANGED
@@ -57,20 +57,37 @@ my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen)
|
|
57
57
|
|
58
58
|
static VALUE tcp_connect(VALUE klass, VALUE ip, VALUE port, int io_wait)
|
59
59
|
{
|
60
|
-
struct
|
60
|
+
struct addrinfo hints;
|
61
|
+
struct sockaddr_storage addr;
|
62
|
+
int rc;
|
63
|
+
struct addrinfo *res;
|
64
|
+
VALUE sock;
|
65
|
+
const char *ipname = StringValuePtr(ip);
|
66
|
+
char ipport[6];
|
67
|
+
unsigned uport = FIX2UINT(port);
|
61
68
|
|
62
|
-
|
63
|
-
|
69
|
+
rc = snprintf(ipport, sizeof(ipport), "%u", uport);
|
70
|
+
if (rc >= (int)sizeof(ipport) || rc <= 0)
|
71
|
+
rb_raise(rb_eArgError, "invalid TCP port: %u", uport);
|
72
|
+
hints.ai_family = AF_UNSPEC;
|
73
|
+
hints.ai_socktype = SOCK_STREAM;
|
74
|
+
hints.ai_protocol = IPPROTO_TCP;
|
75
|
+
/* disallow non-deterministic DNS lookups */
|
76
|
+
hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
|
64
77
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
78
|
+
rc = getaddrinfo(ipname, ipport, &hints, &res);
|
79
|
+
if (rc != 0)
|
80
|
+
rb_raise(rb_eArgError, "getaddrinfo(%s:%s): %s",
|
81
|
+
ipname, ipport, gai_strerror(rc));
|
82
|
+
|
83
|
+
/* copy needed data and free ASAP to avoid needing rb_ensure */
|
84
|
+
hints.ai_family = res->ai_family;
|
85
|
+
hints.ai_addrlen = res->ai_addrlen;
|
86
|
+
memcpy(&addr, res->ai_addr, res->ai_addrlen);
|
87
|
+
freeaddrinfo(res);
|
72
88
|
|
73
|
-
return
|
89
|
+
return my_connect(klass, io_wait, hints.ai_family,
|
90
|
+
&addr, hints.ai_addrlen);
|
74
91
|
}
|
75
92
|
|
76
93
|
/*
|
@@ -173,12 +190,10 @@ static VALUE stream_connect(VALUE klass, VALUE addr, int io_wait)
|
|
173
190
|
} else {
|
174
191
|
rb_raise(rb_eTypeError, "invalid address");
|
175
192
|
}
|
176
|
-
switch (((struct
|
193
|
+
switch (((struct sockaddr_storage *)(sockaddr))->ss_family) {
|
177
194
|
case AF_UNIX: domain = PF_UNIX; break;
|
178
195
|
case AF_INET: domain = PF_INET; break;
|
179
|
-
#ifdef AF_INET6 /* IPv6 support incomplete */
|
180
196
|
case AF_INET6: domain = PF_INET6; break;
|
181
|
-
#endif /* AF_INET6 */
|
182
197
|
default:
|
183
198
|
rb_raise(rb_eArgError, "invalid address family");
|
184
199
|
}
|
data/ext/kgio/extconf.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
$CPPFLAGS << ' -D_GNU_SOURCE'
|
3
|
+
$CPPFLAGS << ' -DPOSIX_C_SOURCE=1'
|
3
4
|
|
5
|
+
have_func("getaddrinfo", %w(sys/types.h sys/socket.h netdb.h)) or
|
6
|
+
abort "getaddrinfo required"
|
7
|
+
have_func("getnameinfo", %w(sys/socket.h netdb.h)) or
|
8
|
+
abort "getnameinfo required"
|
9
|
+
have_type("struct sockaddr_storage", %w(sys/types.h sys/socket.h)) or
|
10
|
+
abort "struct sockaddr_storage required"
|
4
11
|
have_func('accept4', %w(sys/socket.h))
|
5
12
|
if have_header('ruby/io.h')
|
6
|
-
|
7
|
-
have_struct_member("rb_io_t", "
|
13
|
+
rubyio = %w(ruby.h ruby/io.h)
|
14
|
+
have_struct_member("rb_io_t", "fd", rubyio)
|
15
|
+
have_struct_member("rb_io_t", "mode", rubyio)
|
8
16
|
else
|
9
17
|
rubyio = %w(ruby.h rubyio.h)
|
10
18
|
rb_io_t = have_type("OpenFile", rubyio) ? "OpenFile" : "rb_io_t"
|
@@ -13,6 +21,9 @@ else
|
|
13
21
|
have_struct_member(rb_io_t, "mode", rubyio)
|
14
22
|
have_func('rb_fdopen')
|
15
23
|
end
|
24
|
+
have_type("struct RFile", rubyio) and check_sizeof("struct RFile", rubyio)
|
25
|
+
have_type("struct RObject") and check_sizeof("struct RObject")
|
26
|
+
check_sizeof("int")
|
16
27
|
have_func('rb_io_ascii8bit_binmode')
|
17
28
|
have_func('rb_thread_blocking_region')
|
18
29
|
have_func('rb_str_set_len')
|
data/ext/kgio/kgio.h
CHANGED
@@ -16,8 +16,9 @@
|
|
16
16
|
#include <unistd.h>
|
17
17
|
#include <arpa/inet.h>
|
18
18
|
#include <assert.h>
|
19
|
+
#include <netdb.h>
|
19
20
|
|
20
|
-
#include "
|
21
|
+
#include "ancient_ruby.h"
|
21
22
|
#include "nonblock.h"
|
22
23
|
#include "my_fileno.h"
|
23
24
|
|
@@ -33,6 +34,11 @@ void init_kgio_wait(void);
|
|
33
34
|
void init_kgio_read_write(void);
|
34
35
|
void init_kgio_accept(void);
|
35
36
|
void init_kgio_connect(void);
|
37
|
+
void init_kgio_autopush(void);
|
38
|
+
|
39
|
+
void kgio_autopush_accept(VALUE, VALUE);
|
40
|
+
void kgio_autopush_recv(VALUE);
|
41
|
+
void kgio_autopush_send(VALUE);
|
36
42
|
|
37
43
|
VALUE kgio_call_wait_writable(VALUE io);
|
38
44
|
VALUE kgio_call_wait_readable(VALUE io);
|
data/ext/kgio/kgio_ext.c
CHANGED
File without changes
|
data/ext/kgio/read_write.c
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#include "kgio.h"
|
2
2
|
static VALUE sym_wait_readable, sym_wait_writable;
|
3
3
|
static VALUE eErrno_EPIPE, eErrno_ECONNRESET;
|
4
|
+
static ID id_set_backtrace;
|
4
5
|
|
5
6
|
/*
|
6
7
|
* we know MSG_DONTWAIT works properly on all stream sockets under Linux
|
@@ -14,13 +15,14 @@ static VALUE eErrno_EPIPE, eErrno_ECONNRESET;
|
|
14
15
|
NORETURN(static void raise_empty_bt(VALUE, const char *));
|
15
16
|
NORETURN(static void my_eof_error(void));
|
16
17
|
NORETURN(static void wr_sys_fail(const char *));
|
18
|
+
NORETURN(static void rd_sys_fail(const char *));
|
17
19
|
|
18
20
|
static void raise_empty_bt(VALUE err, const char *msg)
|
19
21
|
{
|
20
22
|
VALUE exc = rb_exc_new2(err, msg);
|
21
23
|
VALUE bt = rb_ary_new();
|
22
24
|
|
23
|
-
rb_funcall(exc,
|
25
|
+
rb_funcall(exc, id_set_backtrace, 1, bt);
|
24
26
|
rb_exc_raise(exc);
|
25
27
|
}
|
26
28
|
|
@@ -42,6 +44,15 @@ static void wr_sys_fail(const char *msg)
|
|
42
44
|
rb_sys_fail(msg);
|
43
45
|
}
|
44
46
|
|
47
|
+
static void rd_sys_fail(const char *msg)
|
48
|
+
{
|
49
|
+
if (errno == ECONNRESET) {
|
50
|
+
errno = 0;
|
51
|
+
raise_empty_bt(eErrno_ECONNRESET, msg);
|
52
|
+
}
|
53
|
+
rb_sys_fail(msg);
|
54
|
+
}
|
55
|
+
|
45
56
|
static void prepare_read(struct io_args *a, int argc, VALUE *argv, VALUE io)
|
46
57
|
{
|
47
58
|
VALUE length;
|
@@ -78,7 +89,7 @@ static int read_check(struct io_args *a, long n, const char *msg, int io_wait)
|
|
78
89
|
return 0;
|
79
90
|
}
|
80
91
|
}
|
81
|
-
|
92
|
+
rd_sys_fail(msg);
|
82
93
|
}
|
83
94
|
rb_str_set_len(a->buf, n);
|
84
95
|
if (n == 0)
|
@@ -164,6 +175,7 @@ static VALUE my_recv(int io_wait, int argc, VALUE *argv, VALUE io)
|
|
164
175
|
long n;
|
165
176
|
|
166
177
|
prepare_read(&a, argc, argv, io);
|
178
|
+
kgio_autopush_recv(io);
|
167
179
|
|
168
180
|
if (a.len > 0) {
|
169
181
|
retry:
|
@@ -320,6 +332,8 @@ retry:
|
|
320
332
|
n = (long)send(a.fd, a.ptr, a.len, MSG_DONTWAIT);
|
321
333
|
if (write_check(&a, n, "send", io_wait) != 0)
|
322
334
|
goto retry;
|
335
|
+
if (TYPE(a.buf) != T_SYMBOL)
|
336
|
+
kgio_autopush_send(io);
|
323
337
|
return a.buf;
|
324
338
|
}
|
325
339
|
|
@@ -347,6 +361,44 @@ static VALUE kgio_trysend(VALUE io, VALUE str)
|
|
347
361
|
# define kgio_trysend kgio_trywrite
|
348
362
|
#endif /* ! USE_MSG_DONTWAIT */
|
349
363
|
|
364
|
+
/*
|
365
|
+
* call-seq:
|
366
|
+
*
|
367
|
+
* Kgio.tryread(io, maxlen) -> buffer
|
368
|
+
* Kgio.tryread(io, maxlen, buffer) -> buffer
|
369
|
+
*
|
370
|
+
* Returns nil on EOF.
|
371
|
+
* Returns :wait_readable if EAGAIN is encountered.
|
372
|
+
*
|
373
|
+
* Maybe used in place of PipeMethods#kgio_tryread for non-Kgio objects
|
374
|
+
*/
|
375
|
+
static VALUE s_tryread(int argc, VALUE *argv, VALUE mod)
|
376
|
+
{
|
377
|
+
if (argc <= 1)
|
378
|
+
rb_raise(rb_eArgError, "wrong number of arguments");
|
379
|
+
return my_read(0, argc - 1, &argv[1], argv[0]);
|
380
|
+
}
|
381
|
+
|
382
|
+
/*
|
383
|
+
* call-seq:
|
384
|
+
*
|
385
|
+
* Kgio.trywrite(io, str) -> nil or :wait_writable
|
386
|
+
*
|
387
|
+
* Returns nil if the write was completed in full.
|
388
|
+
*
|
389
|
+
* Returns a String containing the unwritten portion if EAGAIN
|
390
|
+
* was encountered, but some portion was successfully written.
|
391
|
+
*
|
392
|
+
* Returns :wait_writable if EAGAIN is encountered and nothing
|
393
|
+
* was written.
|
394
|
+
*
|
395
|
+
* Maybe used in place of PipeMethods#kgio_trywrite for non-Kgio objects
|
396
|
+
*/
|
397
|
+
static VALUE s_trywrite(VALUE mod, VALUE io, VALUE str)
|
398
|
+
{
|
399
|
+
return my_write(io, str, 0);
|
400
|
+
}
|
401
|
+
|
350
402
|
void init_kgio_read_write(void)
|
351
403
|
{
|
352
404
|
VALUE mPipeMethods, mSocketMethods;
|
@@ -356,6 +408,9 @@ void init_kgio_read_write(void)
|
|
356
408
|
sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
|
357
409
|
sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
|
358
410
|
|
411
|
+
rb_define_singleton_method(mKgio, "tryread", s_tryread, -1);
|
412
|
+
rb_define_singleton_method(mKgio, "trywrite", s_trywrite, 2);
|
413
|
+
|
359
414
|
/*
|
360
415
|
* Document-module: Kgio::PipeMethods
|
361
416
|
*
|
@@ -390,7 +445,7 @@ void init_kgio_read_write(void)
|
|
390
445
|
* Kgio::LOCALHOST constant for UNIX domain sockets.
|
391
446
|
*/
|
392
447
|
rb_define_attr(mSocketMethods, "kgio_addr", 1, 1);
|
393
|
-
|
448
|
+
id_set_backtrace = rb_intern("set_backtrace");
|
394
449
|
eErrno_EPIPE = rb_const_get(rb_mErrno, rb_intern("EPIPE"));
|
395
450
|
eErrno_ECONNRESET = rb_const_get(rb_mErrno, rb_intern("ECONNRESET"));
|
396
451
|
rb_include_module(mPipeMethods, mWaiters);
|
data/kgio.gemspec
CHANGED
@@ -21,7 +21,8 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.test_files = Dir['test/test_*.rb']
|
22
22
|
s.extensions = %w(ext/kgio/extconf.rb)
|
23
23
|
|
24
|
-
s.add_development_dependency('wrongdoc', '~> 1.
|
24
|
+
s.add_development_dependency('wrongdoc', '~> 1.4')
|
25
|
+
s.add_development_dependency('strace_me', '~> 1.0')
|
25
26
|
|
26
27
|
# s.license = %w(LGPL) # disabled for compatibility with older RubyGems
|
27
28
|
end
|
data/pkg.mk
ADDED
@@ -0,0 +1,168 @@
|
|
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 Config::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
|
+
endif
|
48
|
+
|
49
|
+
pkg_extra := GIT-VERSION-FILE NEWS ChangeLog LATEST
|
50
|
+
ChangeLog: GIT-VERSION-FILE .wrongdoc.yml
|
51
|
+
$(WRONGDOC) prepare
|
52
|
+
|
53
|
+
manifest:
|
54
|
+
$(RM) .manifest
|
55
|
+
$(MAKE) .manifest
|
56
|
+
|
57
|
+
.manifest: ChangeLog
|
58
|
+
(git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \
|
59
|
+
LC_ALL=C sort > $@+
|
60
|
+
cmp $@+ $@ || mv $@+ $@
|
61
|
+
$(RM) $@+
|
62
|
+
|
63
|
+
doc:: .document .wrongdoc.yml
|
64
|
+
find lib -type f -name '*.rbc' -exec rm -f '{}' ';'
|
65
|
+
-find ext -type f -name '*.rbc' -exec rm -f '{}' ';'
|
66
|
+
$(RM) -r doc
|
67
|
+
$(WRONGDOC) all
|
68
|
+
install -m644 COPYING doc/COPYING
|
69
|
+
install -m644 $(shell grep '^[A-Z]' .document) doc/
|
70
|
+
|
71
|
+
ifneq ($(VERSION),)
|
72
|
+
pkggem := pkg/$(rfpackage)-$(VERSION).gem
|
73
|
+
pkgtgz := pkg/$(rfpackage)-$(VERSION).tgz
|
74
|
+
release_notes := release_notes-$(VERSION)
|
75
|
+
release_changes := release_changes-$(VERSION)
|
76
|
+
|
77
|
+
release-notes: $(release_notes)
|
78
|
+
release-changes: $(release_changes)
|
79
|
+
$(release_changes):
|
80
|
+
$(WRONGDOC) release_changes > $@+
|
81
|
+
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
82
|
+
$(release_notes):
|
83
|
+
$(WRONGDOC) release_notes > $@+
|
84
|
+
$(VISUAL) $@+ && test -s $@+ && mv $@+ $@
|
85
|
+
|
86
|
+
# ensures we're actually on the tagged $(VERSION), only used for release
|
87
|
+
verify:
|
88
|
+
test x"$(shell umask)" = x0022
|
89
|
+
git rev-parse --verify refs/tags/v$(VERSION)^{}
|
90
|
+
git diff-index --quiet HEAD^0
|
91
|
+
test $$(git rev-parse --verify HEAD^0) = \
|
92
|
+
$$(git rev-parse --verify refs/tags/v$(VERSION)^{})
|
93
|
+
|
94
|
+
fix-perms:
|
95
|
+
-git ls-tree -r HEAD | awk '/^100644 / {print $$NF}' | xargs chmod 644
|
96
|
+
-git ls-tree -r HEAD | awk '/^100755 / {print $$NF}' | xargs chmod 755
|
97
|
+
|
98
|
+
gem: $(pkggem)
|
99
|
+
|
100
|
+
install-gem: $(pkggem)
|
101
|
+
gem install $(CURDIR)/$<
|
102
|
+
|
103
|
+
$(pkggem): manifest fix-perms
|
104
|
+
gem build $(rfpackage).gemspec
|
105
|
+
mkdir -p pkg
|
106
|
+
mv $(@F) $@
|
107
|
+
|
108
|
+
$(pkgtgz): distdir = $(basename $@)
|
109
|
+
$(pkgtgz): HEAD = v$(VERSION)
|
110
|
+
$(pkgtgz): manifest fix-perms
|
111
|
+
@test -n "$(distdir)"
|
112
|
+
$(RM) -r $(distdir)
|
113
|
+
mkdir -p $(distdir)
|
114
|
+
tar cf - $$(cat .manifest) | (cd $(distdir) && tar xf -)
|
115
|
+
cd pkg && tar cf - $(basename $(@F)) | gzip -9 > $(@F)+
|
116
|
+
mv $@+ $@
|
117
|
+
|
118
|
+
package: $(pkgtgz) $(pkggem)
|
119
|
+
|
120
|
+
test-release:: verify package $(release_notes) $(release_changes)
|
121
|
+
# make tgz release on RubyForge
|
122
|
+
@echo rubyforge add_release -f \
|
123
|
+
-n $(release_notes) -a $(release_changes) \
|
124
|
+
$(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
|
125
|
+
@echo gem push $(pkggem)
|
126
|
+
@echo rubyforge add_file \
|
127
|
+
$(rfproject) $(rfpackage) $(VERSION) $(pkggem)
|
128
|
+
release:: verify package $(release_notes) $(release_changes)
|
129
|
+
# make tgz release on RubyForge
|
130
|
+
rubyforge add_release -f -n $(release_notes) -a $(release_changes) \
|
131
|
+
$(rfproject) $(rfpackage) $(VERSION) $(pkgtgz)
|
132
|
+
# push gem to RubyGems.org
|
133
|
+
gem push $(pkggem)
|
134
|
+
# in case of gem downloads from RubyForge releases page
|
135
|
+
rubyforge add_file \
|
136
|
+
$(rfproject) $(rfpackage) $(VERSION) $(pkggem)
|
137
|
+
else
|
138
|
+
gem install-gem: GIT-VERSION-FILE
|
139
|
+
$(MAKE) $@ VERSION=$(GIT_VERSION)
|
140
|
+
endif
|
141
|
+
|
142
|
+
all:: test
|
143
|
+
test_units := $(wildcard test/test_*.rb)
|
144
|
+
test: test-unit
|
145
|
+
test-unit: $(test_units)
|
146
|
+
$(test_units): build
|
147
|
+
$(RUBY) -I $(lib) $@
|
148
|
+
|
149
|
+
# this requires GNU coreutils variants
|
150
|
+
ifneq ($(RSYNC_DEST),)
|
151
|
+
publish_doc:
|
152
|
+
-git set-file-times
|
153
|
+
$(MAKE) doc
|
154
|
+
find doc/images -type f | \
|
155
|
+
TZ=UTC xargs touch -d '1970-01-01 00:00:06' doc/rdoc.css
|
156
|
+
$(MAKE) doc_gz
|
157
|
+
$(RSYNC) -av doc/ $(RSYNC_DEST)/
|
158
|
+
git ls-files | xargs touch
|
159
|
+
endif
|
160
|
+
|
161
|
+
# Create gzip variants of the same timestamp as the original so nginx
|
162
|
+
# "gzip_static on" can serve the gzipped versions directly.
|
163
|
+
doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
|
164
|
+
doc_gz:
|
165
|
+
for i in $(docs); do \
|
166
|
+
gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
167
|
+
|
168
|
+
.PHONY: all .FORCE-GIT-VERSION-FILE doc test $(test_units) manifest
|
data/test/lib_read_write.rb
CHANGED
@@ -10,11 +10,19 @@ module LibReadWriteTest
|
|
10
10
|
|
11
11
|
def teardown
|
12
12
|
assert_nothing_raised do
|
13
|
-
@rd.close
|
14
|
-
@wr.close
|
13
|
+
@rd.close if defined?(@rd) && ! @rd.closed?
|
14
|
+
@wr.close if defined?(@wr) && ! @wr.closed?
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def test_write_empty
|
19
|
+
assert_nil @wr.kgio_write("")
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_trywrite_empty
|
23
|
+
assert_nil @wr.kgio_trywrite("")
|
24
|
+
end
|
25
|
+
|
18
26
|
def test_read_zero
|
19
27
|
assert_equal "", @rd.kgio_read(0)
|
20
28
|
buf = "foo"
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'test/unit'
|
3
|
+
RUBY_PLATFORM =~ /linux/ and require 'strace'
|
4
|
+
$-w = true
|
5
|
+
require 'kgio'
|
6
|
+
|
7
|
+
class TestAutopush < Test::Unit::TestCase
|
8
|
+
TCP_CORK = 3
|
9
|
+
TCP_NOPUSH = 4
|
10
|
+
|
11
|
+
def setup
|
12
|
+
Kgio.autopush = false
|
13
|
+
assert_equal false, Kgio.autopush?
|
14
|
+
|
15
|
+
@host = ENV["TEST_HOST"] || '127.0.0.1'
|
16
|
+
@srv = Kgio::TCPServer.new(@host, 0)
|
17
|
+
assert_nothing_raised {
|
18
|
+
@srv.setsockopt(Socket::IPPROTO_TCP, TCP_CORK, 1)
|
19
|
+
} if RUBY_PLATFORM =~ /linux/
|
20
|
+
assert_nothing_raised {
|
21
|
+
@srv.setsockopt(Socket::IPPROTO_TCP, TCP_NOPUSH, 1)
|
22
|
+
} if RUBY_PLATFORM =~ /freebsd/
|
23
|
+
@port = @srv.addr[1]
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_autopush_accessors
|
27
|
+
Kgio.autopush = true
|
28
|
+
opt = RUBY_PLATFORM =~ /freebsd/ ? TCP_NOPUSH : TCP_CORK
|
29
|
+
s = Kgio::TCPSocket.new(@host, @port)
|
30
|
+
assert_equal 0, s.getsockopt(Socket::IPPROTO_TCP, opt).unpack('i')[0]
|
31
|
+
assert ! s.kgio_autopush?
|
32
|
+
s.kgio_autopush = true
|
33
|
+
assert s.kgio_autopush?
|
34
|
+
assert_nothing_raised { s.kgio_write 'asdf' }
|
35
|
+
assert_equal :wait_readable, s.kgio_tryread(1)
|
36
|
+
assert s.kgio_autopush?
|
37
|
+
assert_equal 1, s.getsockopt(Socket::IPPROTO_TCP, opt).unpack('i')[0]
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_autopush_true_unix
|
41
|
+
Kgio.autopush = true
|
42
|
+
tmp = Tempfile.new('kgio_unix')
|
43
|
+
@path = tmp.path
|
44
|
+
File.unlink(@path)
|
45
|
+
tmp.close rescue nil
|
46
|
+
@srv = Kgio::UNIXServer.new(@path)
|
47
|
+
@rd = Kgio::UNIXSocket.new(@path)
|
48
|
+
t0 = nil
|
49
|
+
if defined?(Strace)
|
50
|
+
io, err = Strace.me { @wr = @srv.kgio_accept }
|
51
|
+
assert_nil err
|
52
|
+
rc = nil
|
53
|
+
io, err = Strace.me {
|
54
|
+
t0 = Time.now
|
55
|
+
@wr.kgio_write "HI\n"
|
56
|
+
rc = @wr.kgio_tryread 666
|
57
|
+
}
|
58
|
+
assert_nil err
|
59
|
+
lines = io.readlines
|
60
|
+
assert lines.grep(/TCP_CORK/).empty?, lines.inspect
|
61
|
+
else
|
62
|
+
assert_nothing_raised do
|
63
|
+
@wr = @srv.kgio_accept
|
64
|
+
t0 = Time.now
|
65
|
+
@wr.kgio_write "HI\n"
|
66
|
+
rc = @wr.kgio_tryread 666
|
67
|
+
end
|
68
|
+
end
|
69
|
+
assert_equal "HI\n", @rd.kgio_read(3)
|
70
|
+
diff = Time.now - t0
|
71
|
+
assert(diff < 0.200, "nopush on UNIX sockets? diff=#{diff} > 200ms")
|
72
|
+
assert_equal :wait_readable, rc
|
73
|
+
ensure
|
74
|
+
File.unlink(@path) rescue nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_autopush_false
|
78
|
+
Kgio.autopush = nil
|
79
|
+
assert_equal false, Kgio.autopush?
|
80
|
+
|
81
|
+
@wr = Kgio::TCPSocket.new(@host, @port)
|
82
|
+
if defined?(Strace)
|
83
|
+
io, err = Strace.me { @rd = @srv.kgio_accept }
|
84
|
+
assert_nil err
|
85
|
+
lines = io.readlines
|
86
|
+
assert lines.grep(/TCP_CORK/).empty?, lines.inspect
|
87
|
+
assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0]
|
88
|
+
else
|
89
|
+
@rd = @srv.kgio_accept
|
90
|
+
end
|
91
|
+
|
92
|
+
rbuf = "..."
|
93
|
+
t0 = Time.now
|
94
|
+
@rd.kgio_write "HI\n"
|
95
|
+
@wr.kgio_read(3, rbuf)
|
96
|
+
diff = Time.now - t0
|
97
|
+
assert(diff >= 0.200, "nopush broken? diff=#{diff} > 200ms")
|
98
|
+
assert_equal "HI\n", rbuf
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_autopush_true
|
102
|
+
Kgio.autopush = true
|
103
|
+
assert_equal true, Kgio.autopush?
|
104
|
+
@wr = Kgio::TCPSocket.new(@host, @port)
|
105
|
+
|
106
|
+
if defined?(Strace)
|
107
|
+
io, err = Strace.me { @rd = @srv.kgio_accept }
|
108
|
+
assert_nil err
|
109
|
+
lines = io.readlines
|
110
|
+
assert_equal 1, lines.grep(/TCP_CORK/).size, lines.inspect
|
111
|
+
assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0]
|
112
|
+
else
|
113
|
+
@rd = @srv.kgio_accept
|
114
|
+
end
|
115
|
+
|
116
|
+
@wr.write "HI\n"
|
117
|
+
rbuf = ""
|
118
|
+
if defined?(Strace)
|
119
|
+
io, err = Strace.me { @rd.kgio_read(3, rbuf) }
|
120
|
+
assert_nil err
|
121
|
+
lines = io.readlines
|
122
|
+
assert lines.grep(/TCP_CORK/).empty?, lines.inspect
|
123
|
+
assert_equal "HI\n", rbuf
|
124
|
+
else
|
125
|
+
assert_equal "HI\n", @rd.kgio_read(3, rbuf)
|
126
|
+
end
|
127
|
+
|
128
|
+
t0 = Time.now
|
129
|
+
@rd.kgio_write "HI2U2\n"
|
130
|
+
@rd.kgio_write "HOW\n"
|
131
|
+
rc = false
|
132
|
+
|
133
|
+
if defined?(Strace)
|
134
|
+
io, err = Strace.me { rc = @rd.kgio_tryread(666) }
|
135
|
+
else
|
136
|
+
rc = @rd.kgio_tryread(666)
|
137
|
+
end
|
138
|
+
|
139
|
+
@wr.readpartial(666, rbuf)
|
140
|
+
rbuf == "HI2U2\nHOW\n" or warn "rbuf=#{rbuf.inspect} looking bad?"
|
141
|
+
diff = Time.now - t0
|
142
|
+
assert(diff < 0.200, "time diff=#{diff} >= 200ms")
|
143
|
+
assert_equal :wait_readable, rc
|
144
|
+
if defined?(Strace)
|
145
|
+
assert_nil err
|
146
|
+
lines = io.readlines
|
147
|
+
assert_equal 2, lines.grep(/TCP_CORK/).size, lines.inspect
|
148
|
+
end
|
149
|
+
assert_nothing_raised { @wr.close }
|
150
|
+
assert_nothing_raised { @rd.close }
|
151
|
+
|
152
|
+
@wr = Kgio::TCPSocket.new(@host, @port)
|
153
|
+
if defined?(Strace)
|
154
|
+
io, err = Strace.me { @rd = @srv.kgio_accept }
|
155
|
+
assert_nil err
|
156
|
+
lines = io.readlines
|
157
|
+
assert lines.grep(/TCP_CORK/).empty?,"optimization fail: #{lines.inspect}"
|
158
|
+
assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0]
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def teardown
|
163
|
+
Kgio.autopush = false
|
164
|
+
end
|
165
|
+
end if RUBY_PLATFORM =~ /linux|freebsd/
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
require 'test/unit'
|
3
|
+
$-w = true
|
4
|
+
require 'kgio'
|
5
|
+
|
6
|
+
class TestKgioAddr < Test::Unit::TestCase
|
7
|
+
def test_tcp
|
8
|
+
addr = ENV["TEST_HOST"] || '127.0.0.1'
|
9
|
+
tcp = TCPServer.new(addr, 0)
|
10
|
+
port = tcp.addr[1]
|
11
|
+
client = Kgio::TCPSocket.new(addr, port)
|
12
|
+
accepted = tcp.accept
|
13
|
+
assert ! accepted.instance_eval { defined?(@kgio_addr) }
|
14
|
+
accepted.extend Kgio::SocketMethods
|
15
|
+
s = accepted.kgio_addr!
|
16
|
+
assert_equal addr, s
|
17
|
+
assert_equal addr, accepted.instance_variable_get(:@kgio_addr)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
$-w = true
|
3
|
+
require 'kgio'
|
4
|
+
|
5
|
+
class TestNoDnsOnTcpConnect < Test::Unit::TestCase
|
6
|
+
def test_connect_remote
|
7
|
+
assert_raises(ArgumentError) { Kgio::TCPSocket.new("example.com", 666) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_connect_localhost
|
11
|
+
assert_raises(ArgumentError) { Kgio::TCPSocket.new("localhost", 666) }
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
$-w = true
|
3
|
+
require 'kgio'
|
4
|
+
|
5
|
+
class TestSingletonReadWrite < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_unix_socketpair
|
8
|
+
a, b = UNIXSocket.pair
|
9
|
+
assert_nothing_raised { Kgio.trywrite(a, "HELLO") }
|
10
|
+
buf = ""
|
11
|
+
assert_equal "HELLO", Kgio.tryread(b, 5, buf)
|
12
|
+
assert_equal "HELLO", buf
|
13
|
+
assert_equal :wait_readable, Kgio.tryread(b, 5)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_arg_error
|
17
|
+
assert_raises(ArgumentError) { Kgio.tryread }
|
18
|
+
assert_raises(ArgumentError) { Kgio.tryread($stdin) }
|
19
|
+
assert_raises(ArgumentError) { Kgio.trywrite($stdout) }
|
20
|
+
end
|
21
|
+
end
|