about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@yhbt.net>2010-10-05 15:26:57 -0700
committerEric Wong <e@yhbt.net>2010-10-05 15:26:57 -0700
commitb168cc894037620cab82fa82f3ab37a3aab81570 (patch)
tree0ea6bd75fe8442f548c5923705f186f0bb553c26
parent870ada92db7071c7982913e508ac35b97d6e8761 (diff)
downloadkgio-b168cc894037620cab82fa82f3ab37a3aab81570.tar.gz
Except EOFError is gently raised to not include a huge
backtrace.  Large backtraces can be a performance problem on
busy servers that malicious clients may exploit to deny service.
-rw-r--r--ext/kgio/read_write.c38
-rw-r--r--test/lib_read_write.rb10
2 files changed, 48 insertions, 0 deletions
diff --git a/ext/kgio/read_write.c b/ext/kgio/read_write.c
index 7f1748a..7466c91 100644
--- a/ext/kgio/read_write.c
+++ b/ext/kgio/read_write.c
@@ -10,6 +10,17 @@ static VALUE mKgio_WaitReadable, mKgio_WaitWritable;
 #  define USE_MSG_DONTWAIT
 #endif
 
+NORETURN(static void my_eof_error(void));
+
+static void my_eof_error(void)
+{
+        VALUE exc = rb_exc_new2(rb_eEOFError, "");
+        VALUE bt = rb_ary_new();
+
+        rb_funcall(exc, rb_intern("set_backtrace"), 1, bt);
+        rb_exc_raise(exc);
+}
+
 static void prepare_read(struct io_args *a, int argc, VALUE *argv, VALUE io)
 {
         VALUE length;
@@ -91,6 +102,18 @@ static VALUE kgio_read(int argc, VALUE *argv, VALUE io)
 }
 
 /*
+ * Same as Kgio::PipeMethods#kgio_read, except EOFError is raised
+ * on EOF without a backtrace
+ */
+static VALUE kgio_read_bang(int argc, VALUE *argv, VALUE io)
+{
+        VALUE rv = my_read(1, argc, argv, io);
+
+        if (NIL_P(rv)) my_eof_error();
+        return rv;
+}
+
+/*
  * call-seq:
  *
  *        io.kgio_tryread(maxlen)           ->  buffer
@@ -133,6 +156,18 @@ static VALUE kgio_recv(int argc, VALUE *argv, VALUE io)
 }
 
 /*
+ * Same as Kgio::SocketMethods#kgio_read, except EOFError is raised
+ * on EOF without a backtrace
+ */
+static VALUE kgio_recv_bang(int argc, VALUE *argv, VALUE io)
+{
+        VALUE rv = my_recv(1, argc, argv, io);
+
+        if (NIL_P(rv)) my_eof_error();
+        return rv;
+}
+
+/*
  * This method may be optimized on some systems (e.g. GNU/Linux) to use
  * MSG_DONTWAIT to avoid explicitly setting the O_NONBLOCK flag via fcntl.
  * Otherwise this is the same as Kgio::PipeMethods#kgio_tryread
@@ -143,6 +178,7 @@ static VALUE kgio_tryrecv(int argc, VALUE *argv, VALUE io)
 }
 #else /* ! USE_MSG_DONTWAIT */
 #  define kgio_recv kgio_read
+#  define kgio_recv_bang kgio_read_bang
 #  define kgio_tryrecv kgio_tryread
 #endif /* USE_MSG_DONTWAIT */
 
@@ -299,6 +335,7 @@ void init_kgio_read_write(VALUE mKgio)
          */
         mPipeMethods = rb_define_module_under(mKgio, "PipeMethods");
         rb_define_method(mPipeMethods, "kgio_read", kgio_read, -1);
+        rb_define_method(mPipeMethods, "kgio_read!", kgio_read_bang, -1);
         rb_define_method(mPipeMethods, "kgio_write", kgio_write, 1);
         rb_define_method(mPipeMethods, "kgio_tryread", kgio_tryread, -1);
         rb_define_method(mPipeMethods, "kgio_trywrite", kgio_trywrite, 1);
@@ -312,6 +349,7 @@ void init_kgio_read_write(VALUE mKgio)
          */
         mSocketMethods = rb_define_module_under(mKgio, "SocketMethods");
         rb_define_method(mSocketMethods, "kgio_read", kgio_recv, -1);
+        rb_define_method(mSocketMethods, "kgio_read!", kgio_recv_bang, -1);
         rb_define_method(mSocketMethods, "kgio_write", kgio_send, 1);
         rb_define_method(mSocketMethods, "kgio_tryread", kgio_tryrecv, -1);
         rb_define_method(mSocketMethods, "kgio_trywrite", kgio_trysend, 1);
diff --git a/test/lib_read_write.rb b/test/lib_read_write.rb
index 1c8088f..bb5ec42 100644
--- a/test/lib_read_write.rb
+++ b/test/lib_read_write.rb
@@ -21,6 +21,16 @@ module LibReadWriteTest
     assert_nil @rd.kgio_read(5)
   end
 
+  def test_read_bang_eof
+    @wr.close
+    begin
+      @rd.kgio_read!(5)
+      assert false, "should never get here (line:#{__LINE__})"
+    rescue EOFError => e
+      assert_equal [], e.backtrace
+    end
+  end
+
   def test_tryread_eof
     @wr.close
     assert_nil @rd.kgio_tryread(5)