kgio RubyGem user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
From: Eric Wong <normalperson@yhbt.net>
To: kgio@librelist.com
Subject: [PATCH] add support for recv() with MSG_PEEK
Date: Sun, 6 Feb 2011 22:01:41 +0000	[thread overview]
Message-ID: <20110206220141.GA30218@dcvr.yhbt.net> (raw)
In-Reply-To: <20110206220141.GA30218@dcvr.yhbt.net>

Kgio.trypeek, kgio_trypeek and kgio_peek methods are added
for using with sockets.
---

 I just pushed this out to kgio.git, it's probably better for writing
 HTTP clients/proxies or anything else that requires you slurp off
 headers...

 ext/kgio/read_write.c |   72 +++++++++++++++++++++++++++++++++++++++++++++++++
 test/test_peek.rb     |   35 +++++++++++++++++++++++
 2 files changed, 107 insertions(+), 0 deletions(-)
 create mode 100644 test/test_peek.rb

diff --git a/ext/kgio/read_write.c b/ext/kgio/read_write.c
index 9f7bce8..54e5c82 100644
--- a/ext/kgio/read_write.c
+++ b/ext/kgio/read_write.c
@@ -10,6 +10,9 @@ static ID id_set_backtrace;
  */
 #if defined(__linux__) && ! defined(USE_MSG_DONTWAIT)
 #  define USE_MSG_DONTWAIT
+static const int peek_flags = MSG_DONTWAIT|MSG_PEEK;
+#else
+static const int peek_flags = MSG_PEEK;
 #endif
 
 NORETURN(static void raise_empty_bt(VALUE, const char *));
@@ -223,6 +226,72 @@ static VALUE kgio_tryrecv(int argc, VALUE *argv, VALUE io)
 #  define kgio_tryrecv kgio_tryread
 #endif /* USE_MSG_DONTWAIT */
 
+static VALUE my_peek(int io_wait, int argc, VALUE *argv, VALUE io)
+{
+	struct io_args a;
+	long n;
+
+	prepare_read(&a, argc, argv, io);
+	kgio_autopush_recv(io);
+
+	if (a.len > 0) {
+		if (peek_flags == MSG_PEEK)
+			set_nonblocking(a.fd);
+retry:
+		n = (long)recv(a.fd, a.ptr, a.len, peek_flags);
+		if (read_check(&a, n, "recv(MSG_PEEK)", io_wait) != 0)
+			goto retry;
+	}
+	return a.buf;
+}
+
+/*
+ * call-seq:
+ *
+ *	socket.kgio_trypeek(maxlen)           ->  buffer
+ *	socket.kgio_trypeek(maxlen, buffer)   ->  buffer
+ *
+ * Like kgio_tryread, except it uses MSG_PEEK so it does not drain the
+ * socket buffer.  A subsequent read of any type (including another peek)
+ * will return the same data.
+ */
+static VALUE kgio_trypeek(int argc, VALUE *argv, VALUE io)
+{
+	return my_peek(0, argc, argv, io);
+}
+
+/*
+ * call-seq:
+ *
+ *	socket.kgio_peek(maxlen)           ->  buffer
+ *	socket.kgio_peek(maxlen, buffer)   ->  buffer
+ *
+ * Like kgio_read, except it uses MSG_PEEK so it does not drain the
+ * socket buffer.  A subsequent read of any type (including another peek)
+ * will return the same data.
+ */
+static VALUE kgio_peek(int argc, VALUE *argv, VALUE io)
+{
+	return my_peek(1, argc, argv, io);
+}
+
+/*
+ * call-seq:
+ *
+ *	Kgio.trypeek(socket, maxlen)           ->  buffer
+ *	Kgio.trypeek(socket, maxlen, buffer)   ->  buffer
+ *
+ * Like Kgio.tryread, except it uses MSG_PEEK so it does not drain the
+ * socket buffer.  This can only be used on sockets and not pipe objects.
+ * Maybe used in place of SocketMethods#kgio_trypeek for non-Kgio objects
+ */
+static VALUE s_trypeek(int argc, VALUE *argv, VALUE mod)
+{
+	if (argc <= 1)
+		rb_raise(rb_eArgError, "wrong number of arguments");
+	return my_peek(0, argc - 1, &argv[1], argv[0]);
+}
+
 static void prepare_write(struct io_args *a, VALUE io, VALUE str)
 {
 	a->buf = (TYPE(str) == T_STRING) ? str : rb_obj_as_string(str);
@@ -410,6 +479,7 @@ void init_kgio_read_write(void)
 
 	rb_define_singleton_method(mKgio, "tryread", s_tryread, -1);
 	rb_define_singleton_method(mKgio, "trywrite", s_trywrite, 2);
+	rb_define_singleton_method(mKgio, "trypeek", s_trypeek, -1);
 
 	/*
 	 * Document-module: Kgio::PipeMethods
@@ -438,6 +508,8 @@ void init_kgio_read_write(void)
 	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);
+	rb_define_method(mSocketMethods, "kgio_trypeek", kgio_trypeek, -1);
+	rb_define_method(mSocketMethods, "kgio_peek", kgio_peek, -1);
 
 	/*
 	 * Returns the client IPv4 address of the socket in dotted quad
diff --git a/test/test_peek.rb b/test/test_peek.rb
new file mode 100644
index 0000000..9d4475d
--- /dev/null
+++ b/test/test_peek.rb
@@ -0,0 +1,35 @@
+require 'test/unit'
+$-w = true
+require 'kgio'
+
+class TestPeek < Test::Unit::TestCase
+  class EIEIO < Errno::EIO
+  end
+
+  def teardown
+    @rd.close
+    @wr.close
+  end
+
+  def test_peek
+    @rd, @wr = Kgio::UNIXSocket.pair
+    @wr.kgio_write "HELLO"
+    assert_equal "HELLO", @rd.kgio_peek(5)
+    assert_equal "HELLO", @rd.kgio_trypeek(5)
+    assert_equal "HELLO", @rd.kgio_read(5)
+    assert_equal :wait_readable, @rd.kgio_trypeek(5)
+    def @rd.kgio_wait_readable
+      raise EIEIO
+    end
+    assert_raises(EIEIO) { @rd.kgio_peek(5) }
+  end
+
+  def test_peek_singleton
+    @rd, @wr = UNIXSocket.pair
+    @wr.syswrite "HELLO"
+    assert_equal "HELLO", Kgio.trypeek(@rd, 666)
+    assert_equal "HELLO", Kgio.trypeek(@rd, 666)
+    assert_equal "HELLO", Kgio.tryread(@rd, 666)
+    assert_equal :wait_readable, Kgio.trypeek(@rd, 5)
+  end
+end
-- 
Eric Wong


           reply	other threads:[~2011-02-06 22:02 UTC|newest]

Thread overview: expand[flat|nested]  mbox.gz  Atom feed
 [parent not found: <20110206220141.GA30218@dcvr.yhbt.net>]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://yhbt.net/kgio/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20110206220141.GA30218@dcvr.yhbt.net \
    --to=normalperson@yhbt.net \
    --cc=kgio@librelist.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://yhbt.net/kgio.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).