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/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