From e6f0b1de8953c6c3803a2be9eccf748643d08f21 Mon Sep 17 00:00:00 2001 From: akr Date: Wed, 25 Feb 2009 15:26:25 +0000 Subject: [PATCH] * ext/socket/option.c (sockopt_level): extracted from sockopt_level_m. (sockopt_optname): extracted from sockopt_optname_m. (sockopt_data): apply StringValue. (sockopt_s_linger): new method. (sockopt_linger): new method. (inspect_linger): show onoff value if it is neither 0 nor 1. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@22634 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 9 ++++ ext/socket/option.c | 91 ++++++++++++++++++++++++++++++++++++-- test/socket/test_socket.rb | 17 +++++++ 3 files changed, 113 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3013ccfab4..e8f4e8542b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Thu Feb 26 00:21:21 2009 Tanaka Akira + + * ext/socket/option.c (sockopt_level): extracted from sockopt_level_m. + (sockopt_optname): extracted from sockopt_optname_m. + (sockopt_data): apply StringValue. + (sockopt_s_linger): new method. + (sockopt_linger): new method. + (inspect_linger): show onoff value if it is neither 0 nor 1. + Wed Feb 25 23:23:03 2009 Tanaka Akira * ext/socket/ancdata.c (ancillary_unix_rights): raise diff --git a/ext/socket/option.c b/ext/socket/option.c index 55f849f3dd..3a6a6a2eb6 100644 --- a/ext/socket/option.c +++ b/ext/socket/option.c @@ -83,6 +83,12 @@ sockopt_family_m(VALUE self) return rb_attr_get(self, rb_intern("family")); } +static int +sockopt_level(VALUE self) +{ + return NUM2INT(rb_attr_get(self, rb_intern("level"))); +} + /* * call-seq: * sockopt.level => integer @@ -95,7 +101,13 @@ sockopt_family_m(VALUE self) static VALUE sockopt_level_m(VALUE self) { - return rb_attr_get(self, rb_intern("level")); + return INT2NUM(sockopt_level(self)); +} + +static int +sockopt_optname(VALUE self) +{ + return NUM2INT(rb_attr_get(self, rb_intern("optname"))); } /* @@ -110,7 +122,7 @@ sockopt_level_m(VALUE self) static VALUE sockopt_optname_m(VALUE self) { - return rb_attr_get(self, rb_intern("optname")); + return INT2NUM(sockopt_optname(self)); } /* @@ -125,7 +137,9 @@ sockopt_optname_m(VALUE self) static VALUE sockopt_data(VALUE self) { - return rb_attr_get(self, rb_intern("data")); + VALUE v = rb_attr_get(self, rb_intern("data")); + StringValue(v); + return v; } /* @@ -219,6 +233,67 @@ sockopt_bool(VALUE self) return i == 0 ? Qfalse : Qtrue; } +/* + * call-seq: + * Socket::Option.linger(onoff, secs) => sockopt + * + * Creates a new Socket::Option object for SOL_SOCKET/SO_LINGER. + * + * _onoff_ should be an integer or a boolean. + * + * _secs_ should be the number of seconds. + * + * p Socket::Option.linger(true, 10) + * #=> # + * + */ +static VALUE +sockopt_s_linger(VALUE klass, VALUE vonoff, VALUE vsecs) +{ + VALUE tmp; + struct linger l; + memset(&l, 0, sizeof(l)); + if (!NIL_P(tmp = rb_check_to_integer(vonoff, "to_int"))) + l.l_onoff = NUM2INT(tmp); + else + l.l_onoff = RTEST(vonoff) ? 1 : 0; + l.l_linger = NUM2INT(vsecs); + return sockopt_new(AF_UNSPEC, SOL_SOCKET, SO_LINGER, rb_str_new((char*)&l, sizeof(l))); +} + +/* + * call-seq: + * sockopt.linger => [bool, seconds] + * + * Returns the linger data in _sockopt_ as a pair of boolean and integer. + * + * sockopt = Socket::Option.linger(true, 10) + * p sockopt.linger => [true, 10] + */ +static VALUE +sockopt_linger(VALUE self) +{ + int level = sockopt_level(self); + int optname = sockopt_optname(self); + VALUE data = sockopt_data(self); + struct linger l; + VALUE vonoff, vsecs; + + if (level != SOL_SOCKET || optname != SO_LINGER) + rb_raise(rb_eTypeError, "linger socket option expected"); + if (RSTRING_LEN(data) != sizeof(l)) + rb_raise(rb_eTypeError, "size differ. expected as sizeof(struct linger)=%d but %ld", + (int)sizeof(struct linger), (long)RSTRING_LEN(data)); + memcpy((char*)&l, RSTRING_PTR(data), sizeof(struct linger)); + switch (l.l_onoff) { + case 0: vonoff = Qfalse; break; + case 1: vonoff = Qtrue; break; + default: vonoff = INT2NUM(l.l_onoff); break; + } + vsecs = INT2NUM(l.l_linger); + return rb_assoc_new(vonoff, vsecs); +} + static int inspect_int(int level, int optname, VALUE data, VALUE ret) { @@ -272,7 +347,12 @@ inspect_linger(int level, int optname, VALUE data, VALUE ret) if (RSTRING_LEN(data) == sizeof(struct linger)) { struct linger s; memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); - rb_str_catf(ret, " %s %dsec", s.l_onoff ? "on" : "off", s.l_linger); + switch (s.l_onoff) { + case 0: rb_str_cat2(ret, " off"); break; + case 1: rb_str_cat2(ret, " on"); break; + default: rb_str_catf(ret, " on(%d)", s.l_onoff); break; + } + rb_str_catf(ret, " %dsec", s.l_linger); return 1; } else { @@ -598,6 +678,9 @@ Init_sockopt(void) rb_define_singleton_method(rb_cSockOpt, "bool", sockopt_s_bool, 4); rb_define_method(rb_cSockOpt, "bool", sockopt_bool, 0); + rb_define_singleton_method(rb_cSockOpt, "linger", sockopt_s_linger, 2); + rb_define_method(rb_cSockOpt, "linger", sockopt_linger, 0); + rb_define_method(rb_cSockOpt, "unpack", sockopt_unpack, 1); rb_define_method(rb_cSockOpt, "to_s", sockopt_data, 0); /* compatibility for ruby before 1.9.2 */ diff --git a/test/socket/test_socket.rb b/test/socket/test_socket.rb index b12baaebb3..c4d14e7f36 100644 --- a/test/socket/test_socket.rb +++ b/test/socket/test_socket.rb @@ -273,6 +273,23 @@ class TestSocket < Test::Unit::TestCase } end + def test_linger + opt = Socket::Option.linger(true, 0) + assert_equal([true, 0], opt.linger) + Addrinfo.tcp("127.0.0.1", 0).listen {|serv| + serv.local_address.connect {|s1| + s2, _ = serv.accept + begin + s1.setsockopt(opt) + s1.close + assert_raise(Errno::ECONNRESET) { s2.read } + ensure + s2.close + end + } + } + end + def test_timestamp return if /linux|freebsd|netbsd|openbsd|solaris|darwin/ !~ RUBY_PLATFORM t1 = Time.now.strftime("%Y-%m-%d")