From ffe57003cced19304faa62ae96d00ff55392b430 Mon Sep 17 00:00:00 2001 From: matz Date: Thu, 4 Dec 2008 07:26:28 +0000 Subject: [PATCH] * 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] * ext/tk/lib/tk/menu.rb: TkOptionMenubutton.new fails to treat git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@20494 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 8 +++- ext/openssl/ossl_ssl.c | 82 +++++++++++++++++++++++++++++++++++++--- test/openssl/test_ssl.rb | 15 ++++++++ 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 + + * 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 - * 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 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 #include +#if defined(HAVE_FCNTL_H) || defined(_WIN32) +#include +#elif defined(HAVE_SYS_FCNTL_H) +#include +#endif + #if defined(HAVE_UNISTD_H) # include /* for read(), and write() */ #endif @@ -1004,6 +1010,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; @@ -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)