summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--ext/openssl/ossl_ssl.c82
-rw-r--r--test/openssl/test_ssl.rb15
3 files changed, 98 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index 06efbd5248..ea975967e7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,12 @@
+Thu Dec 4 16:19:18 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
+
+ * ext/openssl/ossl_ssl.c (ossl_ssl_read_nonblock):
+ OpenSSL::SSL::SSLSocket should implement read_nonblock. a patch
+ from Aaron Patterson in [ruby-core:20277]. fix: #814 [ruby-core:20241]
+
Thu Dec 4 06:04:16 2008 Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
- * ext/tk/lib/tk/menu.rb: TkOptionMenubutton.new fails to treat
+ * ext/tk/lib/tk/menu.rb: TkOptionMenubutton.new fails to treat
'parent' and 'variable' options on a Hash argument.
Wed Dec 3 16:38:11 2008 Akinori MUSHA <knu@iDaemons.org>
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
index 83fd2566d5..d5f6cc95e1 100644
--- a/ext/openssl/ossl_ssl.c
+++ b/ext/openssl/ossl_ssl.c
@@ -14,6 +14,12 @@
#include <rubysig.h>
#include <rubyio.h>
+#if defined(HAVE_FCNTL_H) || defined(_WIN32)
+#include <fcntl.h>
+#elif defined(HAVE_SYS_FCNTL_H)
+#include <sys/fcntl.h>
+#endif
+
#if defined(HAVE_UNISTD_H)
# include <unistd.h> /* for read(), and write() */
#endif
@@ -1005,6 +1011,71 @@ ossl_ssl_accept(VALUE self)
static VALUE
ossl_ssl_read(int argc, VALUE *argv, VALUE self)
{
+ SSL *ssl;
+ int ilen, nread = 0;
+ VALUE len, str;
+ rb_io_t *fptr;
+
+ rb_scan_args(argc, argv, "11", &len, &str);
+ ilen = NUM2INT(len);
+ if(NIL_P(str)) str = rb_str_new(0, ilen);
+ else{
+ StringValue(str);
+ rb_str_modify(str);
+ rb_str_resize(str, ilen);
+ }
+ if(ilen == 0) return str;
+
+ Data_Get_Struct(self, SSL, ssl);
+ GetOpenFile(ossl_ssl_get_io(self), fptr);
+ if (ssl) {
+ if(SSL_pending(ssl) <= 0)
+ rb_thread_wait_fd(FPTR_TO_FD(fptr));
+ for (;;){
+ nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LEN(str));
+ switch(ssl_get_error(ssl, nread)){
+ case SSL_ERROR_NONE:
+ goto end;
+ case SSL_ERROR_ZERO_RETURN:
+ rb_eof_error();
+ case SSL_ERROR_WANT_WRITE:
+ rb_io_wait_writable(FPTR_TO_FD(fptr));
+ continue;
+ case SSL_ERROR_WANT_READ:
+ rb_io_wait_readable(FPTR_TO_FD(fptr));
+ continue;
+ case SSL_ERROR_SYSCALL:
+ if(ERR_peek_error() == 0 && nread == 0) rb_eof_error();
+ rb_sys_fail(0);
+ default:
+ ossl_raise(eSSLError, "SSL_read:");
+ }
+ }
+ }
+ else {
+ rb_warning("SSL session is not started yet.");
+ return rb_funcall(ossl_ssl_get_io(self), rb_intern("read_nonblock"), 2, len, str);
+ }
+
+end:
+ rb_str_set_len(str, nread);
+ OBJ_TAINT(str);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * ssl.read_nonblock(length) => string
+ * ssl.read_nonblock(length, buffer) => buffer
+ *
+ * === Parameters
+ * * +length+ is a positive integer.
+ * * +buffer+ is a string used to store the result.
+ */
+static VALUE
+ossl_ssl_read_nonblock(int argc, VALUE *argv, VALUE self)
+{
SSL *ssl;
int ilen, nread = 0;
VALUE len, str;
@@ -1022,12 +1093,11 @@ ossl_ssl_read(int argc, VALUE *argv, VALUE self)
Data_Get_Struct(self, SSL, ssl);
GetOpenFile(ossl_ssl_get_io(self), fptr);
+ rb_io_set_nonblock(fptr);
if (ssl) {
- if(SSL_pending(ssl) <= 0)
- rb_thread_wait_fd(FPTR_TO_FD(fptr));
for (;;){
nread = SSL_read(ssl, RSTRING_PTR(str), RSTRING_LEN(str));
- switch(ssl_get_error(ssl, nread)){
+ switch(SSL_get_error(ssl, nread)){
case SSL_ERROR_NONE:
goto end;
case SSL_ERROR_ZERO_RETURN:
@@ -1036,7 +1106,7 @@ ossl_ssl_read(int argc, VALUE *argv, VALUE self)
rb_io_wait_writable(FPTR_TO_FD(fptr));
continue;
case SSL_ERROR_WANT_READ:
- rb_io_wait_readable(FPTR_TO_FD(fptr));
+ rb_sys_fail(fptr->path);
continue;
case SSL_ERROR_SYSCALL:
if(ERR_peek_error() == 0 && nread == 0) rb_eof_error();
@@ -1047,9 +1117,8 @@ ossl_ssl_read(int argc, VALUE *argv, VALUE self)
}
}
else {
- ID id_sysread = rb_intern("sysread");
rb_warning("SSL session is not started yet.");
- return rb_funcall(ossl_ssl_get_io(self), id_sysread, 2, len, str);
+ return rb_funcall(ossl_ssl_get_io(self), rb_intern("sysread"), 2, len, str);
}
end:
@@ -1423,6 +1492,7 @@ Init_ossl_ssl()
rb_define_method(cSSLSocket, "sysread", ossl_ssl_read, -1);
rb_define_method(cSSLSocket, "syswrite", ossl_ssl_write, 1);
rb_define_method(cSSLSocket, "sysclose", ossl_ssl_close, 0);
+ rb_define_method(cSSLSocket, "read_nonblock", ossl_ssl_read_nonblock, -1);
rb_define_method(cSSLSocket, "cert", ossl_ssl_get_cert, 0);
rb_define_method(cSSLSocket, "peer_cert", ossl_ssl_get_peer_cert, 0);
rb_define_method(cSSLSocket, "peer_cert_chain", ossl_ssl_get_peer_cert_chain, 0);
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb
index 29d3d19edc..c44354720b 100644
--- a/test/openssl/test_ssl.rb
+++ b/test/openssl/test_ssl.rb
@@ -155,6 +155,21 @@ class OpenSSL::TestSSL < Test::Unit::TestCase
assert_equal(ctx.setup, nil)
end
+ def test_ssl_read_nonblock
+ start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true) { |server, port|
+ sock = TCPSocket.new("127.0.0.1", port)
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
+ ssl.sync_close = true
+ ssl.connect
+ assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { ssl.read_nonblock(100) }
+ ssl.write("abc\n")
+ IO.select [ssl]
+ assert_equal('a', ssl.read_nonblock(1))
+ assert_equal("bc\n", ssl.read_nonblock(100))
+ assert_raise(Errno::EAGAIN, Errno::EWOULDBLOCK) { ssl.read_nonblock(100) }
+ }
+ end
+
def test_connect_and_close
start_server(PORT, OpenSSL::SSL::VERIFY_NONE, true){|server, port|
sock = TCPSocket.new("127.0.0.1", port)