summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--NEWS3
-rw-r--r--ext/openssl/lib/openssl/buffering.rb43
-rw-r--r--ext/openssl/ossl_ssl.c38
-rw-r--r--test/openssl/test_pair.rb27
5 files changed, 113 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index 835cd5df44..e089077eac 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Sat Mar 21 02:37:07 2009 Tanaka Akira <akr@fsij.org>
+
+ * ext/openssl/lib/openssl/buffering.rb
+ (OpenSSL::Buffering#write_nonblock): new method.
+
+ * ext/openssl/ossl_ssl.c (ossl_ssl_write_nonblock): new method.
+ (ossl_ssl_write_internal): defined.
+ (ossl_ssl_write): use ossl_ssl_write_internal.
+
Fri Mar 20 18:25:25 2009 Nobuyoshi Nakada <nobu@ruby-lang.org>
* win32/win32.c (errmap): added ERROR_MOD_NOT_FOUND.
diff --git a/NEWS b/NEWS
index cf16ab0798..e250aa5d02 100644
--- a/NEWS
+++ b/NEWS
@@ -92,7 +92,8 @@ with all sufficient information, see the ChangeLog file.
* openssl
* new method:
- * Buffering#read_nonblock
+ * OpenSSL::Buffering#read_nonblock
+ * OpenSSL::Buffering#write_nonblock
* socket
diff --git a/ext/openssl/lib/openssl/buffering.rb b/ext/openssl/lib/openssl/buffering.rb
index 8e67fd7e83..3028fe5b52 100644
--- a/ext/openssl/lib/openssl/buffering.rb
+++ b/ext/openssl/lib/openssl/buffering.rb
@@ -116,6 +116,7 @@ module Buffering
#
# So OpenSSL::Buffering#read_nonblock needs two rescue clause as follows.
#
+ # # emulates blocking read (readpartial).
# begin
# result = ssl.read_nonblock(maxlen)
# rescue IO::WaitReadable
@@ -249,6 +250,48 @@ module Buffering
s.length
end
+ # Writes _str_ in the non-blocking manner.
+ #
+ # If there are buffered data, it is flushed at first.
+ # This may block.
+ #
+ # write_nonblock returns number of bytes written to the SSL connection.
+ #
+ # When no data can be written without blocking,
+ # It raises OpenSSL::SSL::SSLError extended by
+ # IO::WaitReadable or IO::WaitWritable.
+ #
+ # IO::WaitReadable means SSL needs to read internally.
+ # So write_nonblock should be called again after
+ # underlying IO is readable.
+ #
+ # IO::WaitWritable means SSL needs to write internally.
+ # So write_nonblock should be called again after
+ # underlying IO is writable.
+ #
+ # So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows.
+ #
+ # # emulates blocking write.
+ # begin
+ # result = ssl.write_nonblock(str)
+ # rescue IO::WaitReadable
+ # IO.select([io])
+ # retry
+ # rescue IO::WaitWritable
+ # IO.select(nil, [io])
+ # retry
+ # end
+ #
+ # Note that one reason that write_nonblock read from a underlying IO
+ # is the peer requests a new TLS/SSL handshake.
+ # See openssl FAQ for more details.
+ # http://www.openssl.org/support/faq.html
+ #
+ def write_nonblock(s)
+ flush
+ syswrite_nonblock(s)
+ end
+
def << (s)
do_write(s)
self
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 61b30d3afb..4c2477b16b 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -1180,13 +1180,8 @@ ossl_ssl_read_nonblock(int argc, VALUE *argv, VALUE self)
return ossl_ssl_read_internal(argc, argv, self, 1);
}
-
-/*
- * call-seq:
- * ssl.syswrite(string) => integer
- */
static VALUE
-ossl_ssl_write(VALUE self, VALUE str)
+ossl_ssl_write_internal(VALUE self, VALUE str, int nonblock)
{
SSL *ssl;
int nwrite = 0;
@@ -1203,9 +1198,19 @@ ossl_ssl_write(VALUE self, VALUE str)
case SSL_ERROR_NONE:
goto end;
case SSL_ERROR_WANT_WRITE:
+ if (nonblock) {
+ VALUE exc = ossl_exc_new(eSSLError, "write would block");
+ rb_extend_object(exc, rb_mWaitWritable);
+ rb_exc_raise(exc);
+ }
rb_io_wait_writable(FPTR_TO_FD(fptr));
continue;
case SSL_ERROR_WANT_READ:
+ if (nonblock) {
+ VALUE exc = ossl_exc_new(eSSLError, "read would block");
+ rb_extend_object(exc, rb_mWaitReadable);
+ rb_exc_raise(exc);
+ }
rb_io_wait_readable(FPTR_TO_FD(fptr));
continue;
case SSL_ERROR_SYSCALL:
@@ -1227,6 +1232,26 @@ ossl_ssl_write(VALUE self, VALUE str)
/*
* call-seq:
+ * ssl.syswrite(string) => integer
+ */
+static VALUE
+ossl_ssl_write(VALUE self, VALUE str)
+{
+ return ossl_ssl_write_internal(self, str, 0);
+}
+
+/*
+ * call-seq:
+ * ssl.syswrite_nonblock(string) => integer
+ */
+static VALUE
+ossl_ssl_write_nonblock(VALUE self, VALUE str)
+{
+ return ossl_ssl_write_internal(self, str, 1);
+}
+
+/*
+ * call-seq:
* ssl.sysclose => nil
*/
static VALUE
@@ -1545,6 +1570,7 @@ Init_ossl_ssl()
rb_define_method(cSSLSocket, "sysread", ossl_ssl_read, -1);
rb_define_private_method(cSSLSocket, "sysread_nonblock", ossl_ssl_read_nonblock, -1);
rb_define_method(cSSLSocket, "syswrite", ossl_ssl_write, 1);
+ rb_define_private_method(cSSLSocket, "syswrite_nonblock", ossl_ssl_write_nonblock, 1);
rb_define_method(cSSLSocket, "sysclose", ossl_ssl_close, 0);
rb_define_method(cSSLSocket, "cert", ossl_ssl_get_cert, 0);
rb_define_method(cSSLSocket, "peer_cert", ossl_ssl_get_peer_cert, 0);
diff --git a/test/openssl/test_pair.rb b/test/openssl/test_pair.rb
index e5beebc502..a6f1e4f55d 100644
--- a/test/openssl/test_pair.rb
+++ b/test/openssl/test_pair.rb
@@ -165,6 +165,33 @@ class OpenSSL::TestPair < Test::Unit::TestCase
}
end
+ def test_write_nonblock
+ ssl_pair {|s1, s2|
+ n = 0
+ begin
+ n += s1.write_nonblock("a" * 100000)
+ n += s1.write_nonblock("b" * 100000)
+ n += s1.write_nonblock("c" * 100000)
+ n += s1.write_nonblock("d" * 100000)
+ n += s1.write_nonblock("e" * 100000)
+ n += s1.write_nonblock("f" * 100000)
+ rescue IO::WaitWritable
+ end
+ s1.close
+ assert_equal(n, s2.read.length)
+ }
+ end
+
+ def test_write_nonblock_with_buffered_data
+ ssl_pair {|s1, s2|
+ s1.write "foo"
+ s1.write_nonblock("bar")
+ s1.write "baz"
+ s1.close
+ assert_equal("foobarbaz", s2.read)
+ }
+ end
+
end
end