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/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 sockaddr_in addr = { 0 };
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
- addr.sin_family = AF_INET;
63
- addr.sin_port = htons((unsigned short)NUM2INT(port));
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
- switch (inet_pton(AF_INET, StringValuePtr(ip), &addr.sin_addr)) {
66
- case 1:
67
- return my_connect(klass, io_wait, PF_INET, &addr, sizeof(addr));
68
- case -1:
69
- rb_sys_fail("inet_pton");
70
- }
71
- rb_raise(rb_eArgError, "invalid address: %s", StringValuePtr(ip));
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 Qnil;
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 sockaddr_in *)(sockaddr))->sin_family) {
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
- have_struct_member("rb_io_t", "fd", "ruby/io.h")
7
- have_struct_member("rb_io_t", "mode", "ruby/io.h")
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 "missing/ancient_ruby.h"
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
@@ -6,4 +6,5 @@ void Init_kgio_ext(void)
6
6
  init_kgio_read_write();
7
7
  init_kgio_connect();
8
8
  init_kgio_accept();
9
+ init_kgio_autopush();
9
10
  }
File without changes
@@ -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, rb_intern("set_backtrace"), 1, bt);
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
- rb_sys_fail(msg);
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.0.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
@@ -10,11 +10,19 @@ module LibReadWriteTest
10
10
 
11
11
  def teardown
12
12
  assert_nothing_raised do
13
- @rd.close unless @rd.closed?
14
- @wr.close unless @wr.closed?
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