aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--ext/socket/option.c91
-rw-r--r--test/socket/test_socket.rb17
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 <akr@fsij.org>
+
+ * 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 <akr@fsij.org>
* 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)
+ * #=> #<Socket::Option: UNSPEC SOCKET LINGER on 10sec>
+ *
+ */
+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")