mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* io.c (io_read_nonblock): support non-blocking reads without raising
exceptions. As in: `io.read_nonblock(size, exception: false)` [ruby-core:38666] [Feature #5138] * ext/openssl/ossl_ssl.c (ossl_ssl_read_internal): ditto * ext/stringio/stringio.c (strio_sysread): ditto * io.c (rb_io_write_nonblock): support non-blocking writes without raising an exception. * ext/openssl/ossl_ssl.c (ossl_ssl_write_internal): ditto * test/openssl/test_pair.rb (class OpenSSL): tests * test/ruby/test_io.rb (class TestIO): ditto * test/socket/test_nonblock.rb (class TestSocketNonblock): ditto * test/stringio/test_stringio.rb (class TestStringIO): ditto git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42695 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
		
							parent
							
								
									eadad2c900
								
							
						
					
					
						commit
						988ca60565
					
				
					 9 changed files with 330 additions and 55 deletions
				
			
		
							
								
								
									
										15
									
								
								ChangeLog
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								ChangeLog
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,3 +1,18 @@
 | 
			
		|||
Tue Aug 27 07:35:05 2013  Aaron Patterson <aaron@tenderlovemaking.com>
 | 
			
		||||
 | 
			
		||||
	* io.c (io_read_nonblock): support non-blocking reads without raising
 | 
			
		||||
	  exceptions. As in: `io.read_nonblock(size, exception: false)`
 | 
			
		||||
	  [ruby-core:38666] [Feature #5138]
 | 
			
		||||
	* ext/openssl/ossl_ssl.c (ossl_ssl_read_internal): ditto
 | 
			
		||||
	* ext/stringio/stringio.c (strio_sysread): ditto
 | 
			
		||||
	* io.c (rb_io_write_nonblock): support non-blocking writes without
 | 
			
		||||
	  raising an exception.
 | 
			
		||||
	* ext/openssl/ossl_ssl.c (ossl_ssl_write_internal): ditto
 | 
			
		||||
	* test/openssl/test_pair.rb (class OpenSSL): tests
 | 
			
		||||
	* test/ruby/test_io.rb (class TestIO): ditto
 | 
			
		||||
	* test/socket/test_nonblock.rb (class TestSocketNonblock): ditto
 | 
			
		||||
	* test/stringio/test_stringio.rb (class TestStringIO): ditto
 | 
			
		||||
 | 
			
		||||
Tue Aug 27 05:24:34 2013  Eric Hodel  <drbrain@segment7.net>
 | 
			
		||||
 | 
			
		||||
	* lib/rubygems:  Import RubyGems 2.1.0 Release Candidate
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -161,7 +161,7 @@ module OpenSSL::Buffering
 | 
			
		|||
  # when the peer requests a new TLS/SSL handshake.  See openssl the FAQ for
 | 
			
		||||
  # more details.  http://www.openssl.org/support/faq.html
 | 
			
		||||
 | 
			
		||||
  def read_nonblock(maxlen, buf=nil)
 | 
			
		||||
  def read_nonblock(maxlen, buf=nil, exception: true)
 | 
			
		||||
    if maxlen == 0
 | 
			
		||||
      if buf
 | 
			
		||||
        buf.clear
 | 
			
		||||
| 
						 | 
				
			
			@ -171,7 +171,7 @@ module OpenSSL::Buffering
 | 
			
		|||
      end
 | 
			
		||||
    end
 | 
			
		||||
    if @rbuffer.empty?
 | 
			
		||||
      return sysread_nonblock(maxlen, buf)
 | 
			
		||||
      return sysread_nonblock(maxlen, buf, exception: exception)
 | 
			
		||||
    end
 | 
			
		||||
    ret = consume_rbuff(maxlen)
 | 
			
		||||
    if buf
 | 
			
		||||
| 
						 | 
				
			
			@ -370,9 +370,9 @@ module OpenSSL::Buffering
 | 
			
		|||
  # is when the peer requests a new TLS/SSL handshake.  See the openssl FAQ
 | 
			
		||||
  # for more details.  http://www.openssl.org/support/faq.html
 | 
			
		||||
 | 
			
		||||
  def write_nonblock(s)
 | 
			
		||||
  def write_nonblock(s, exception: true)
 | 
			
		||||
    flush
 | 
			
		||||
    syswrite_nonblock(s)
 | 
			
		||||
    syswrite_nonblock(s, exception: exception)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  ##
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,6 +103,8 @@ static const char *ossl_ssl_attrs[] = {
 | 
			
		|||
 | 
			
		||||
ID ID_callback_state;
 | 
			
		||||
 | 
			
		||||
static VALUE sym_exception;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * SSLContext class
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -1373,10 +1375,16 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
 | 
			
		|||
{
 | 
			
		||||
    SSL *ssl;
 | 
			
		||||
    int ilen, nread = 0;
 | 
			
		||||
    int no_exception = 0;
 | 
			
		||||
    VALUE len, str;
 | 
			
		||||
    rb_io_t *fptr;
 | 
			
		||||
    VALUE opts = Qnil;
 | 
			
		||||
 | 
			
		||||
    rb_scan_args(argc, argv, "11:", &len, &str, &opts);
 | 
			
		||||
 | 
			
		||||
    if (!NIL_P(opts) && Qfalse == rb_hash_aref(opts, sym_exception))
 | 
			
		||||
	no_exception = 1;
 | 
			
		||||
 | 
			
		||||
    rb_scan_args(argc, argv, "11", &len, &str);
 | 
			
		||||
    ilen = NUM2INT(len);
 | 
			
		||||
    if(NIL_P(str)) str = rb_str_new(0, ilen);
 | 
			
		||||
    else{
 | 
			
		||||
| 
						 | 
				
			
			@ -1397,17 +1405,23 @@ ossl_ssl_read_internal(int argc, VALUE *argv, VALUE self, int nonblock)
 | 
			
		|||
	    case SSL_ERROR_NONE:
 | 
			
		||||
		goto end;
 | 
			
		||||
	    case SSL_ERROR_ZERO_RETURN:
 | 
			
		||||
		if (no_exception) { return Qnil; }
 | 
			
		||||
		rb_eof_error();
 | 
			
		||||
	    case SSL_ERROR_WANT_WRITE:
 | 
			
		||||
		if (no_exception) { return ID2SYM(rb_intern("wait_writable")); }
 | 
			
		||||
                write_would_block(nonblock);
 | 
			
		||||
                rb_io_wait_writable(FPTR_TO_FD(fptr));
 | 
			
		||||
                continue;
 | 
			
		||||
	    case SSL_ERROR_WANT_READ:
 | 
			
		||||
		if (no_exception) { return ID2SYM(rb_intern("wait_readable")); }
 | 
			
		||||
                read_would_block(nonblock);
 | 
			
		||||
                rb_io_wait_readable(FPTR_TO_FD(fptr));
 | 
			
		||||
		continue;
 | 
			
		||||
	    case SSL_ERROR_SYSCALL:
 | 
			
		||||
		if(ERR_peek_error() == 0 && nread == 0) rb_eof_error();
 | 
			
		||||
		if(ERR_peek_error() == 0 && nread == 0) {
 | 
			
		||||
		    if (no_exception) { return Qnil; }
 | 
			
		||||
		    rb_eof_error();
 | 
			
		||||
		}
 | 
			
		||||
		rb_sys_fail(0);
 | 
			
		||||
	    default:
 | 
			
		||||
		ossl_raise(eSSLError, "SSL_read");
 | 
			
		||||
| 
						 | 
				
			
			@ -1445,9 +1459,11 @@ ossl_ssl_read(int argc, VALUE *argv, VALUE self)
 | 
			
		|||
 * call-seq:
 | 
			
		||||
 *    ssl.sysread_nonblock(length) => string
 | 
			
		||||
 *    ssl.sysread_nonblock(length, buffer) => buffer
 | 
			
		||||
 *    ssl.sysread_nonblock(length[, buffer [, opts]) => buffer
 | 
			
		||||
 *
 | 
			
		||||
 * A non-blocking version of #sysread.  Raises an SSLError if reading would
 | 
			
		||||
 * block.
 | 
			
		||||
 * block.  If "exception: false" is passed, this method returns a symbol of
 | 
			
		||||
 * :wait_writable, :wait_writable, or nil, rather than raising an exception.
 | 
			
		||||
 *
 | 
			
		||||
 * Reads +length+ bytes from the SSL connection.  If a pre-allocated +buffer+
 | 
			
		||||
 * is provided the data will be written into it.
 | 
			
		||||
| 
						 | 
				
			
			@ -1459,7 +1475,7 @@ ossl_ssl_read_nonblock(int argc, VALUE *argv, VALUE self)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
ossl_ssl_write_internal(VALUE self, VALUE str, int nonblock)
 | 
			
		||||
ossl_ssl_write_internal(VALUE self, VALUE str, int nonblock, int no_exception)
 | 
			
		||||
{
 | 
			
		||||
    SSL *ssl;
 | 
			
		||||
    int nwrite = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1476,10 +1492,12 @@ ossl_ssl_write_internal(VALUE self, VALUE str, int nonblock)
 | 
			
		|||
	    case SSL_ERROR_NONE:
 | 
			
		||||
		goto end;
 | 
			
		||||
	    case SSL_ERROR_WANT_WRITE:
 | 
			
		||||
		if (no_exception) { return ID2SYM(rb_intern("wait_writable")); }
 | 
			
		||||
                write_would_block(nonblock);
 | 
			
		||||
                rb_io_wait_writable(FPTR_TO_FD(fptr));
 | 
			
		||||
                continue;
 | 
			
		||||
	    case SSL_ERROR_WANT_READ:
 | 
			
		||||
		if (no_exception) { return ID2SYM(rb_intern("wait_readable")); }
 | 
			
		||||
                read_would_block(nonblock);
 | 
			
		||||
                rb_io_wait_readable(FPTR_TO_FD(fptr));
 | 
			
		||||
                continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -1509,7 +1527,7 @@ ossl_ssl_write_internal(VALUE self, VALUE str, int nonblock)
 | 
			
		|||
static VALUE
 | 
			
		||||
ossl_ssl_write(VALUE self, VALUE str)
 | 
			
		||||
{
 | 
			
		||||
    return ossl_ssl_write_internal(self, str, 0);
 | 
			
		||||
    return ossl_ssl_write_internal(self, str, 0, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -1520,9 +1538,18 @@ ossl_ssl_write(VALUE self, VALUE str)
 | 
			
		|||
 * SSLError if writing would block.
 | 
			
		||||
 */
 | 
			
		||||
static VALUE
 | 
			
		||||
ossl_ssl_write_nonblock(VALUE self, VALUE str)
 | 
			
		||||
ossl_ssl_write_nonblock(int argc, VALUE *argv, VALUE self)
 | 
			
		||||
{
 | 
			
		||||
    return ossl_ssl_write_internal(self, str, 1);
 | 
			
		||||
    VALUE str;
 | 
			
		||||
    VALUE opts = Qnil;
 | 
			
		||||
    int no_exception = 0;
 | 
			
		||||
 | 
			
		||||
    rb_scan_args(argc, argv, "1:", &str, &opts);
 | 
			
		||||
 | 
			
		||||
    if (!NIL_P(opts) && Qfalse == rb_hash_aref(opts, sym_exception))
 | 
			
		||||
	no_exception = 1;
 | 
			
		||||
 | 
			
		||||
    return ossl_ssl_write_internal(self, str, 1, no_exception);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -2168,7 +2195,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_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);
 | 
			
		||||
| 
						 | 
				
			
			@ -2239,4 +2266,6 @@ Init_ossl_ssl()
 | 
			
		|||
    ossl_ssl_def_const(OP_PKCS1_CHECK_2);
 | 
			
		||||
    ossl_ssl_def_const(OP_NETSCAPE_CA_DN_BUG);
 | 
			
		||||
    ossl_ssl_def_const(OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG);
 | 
			
		||||
 | 
			
		||||
    sym_exception = ID2SYM(rb_intern("exception"));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -119,6 +119,8 @@ typedef char strio_flags_check[(STRIO_READABLE/FMODE_READABLE == STRIO_WRITABLE/
 | 
			
		|||
#define READABLE(strio) STRIO_MODE_SET_P(strio, READABLE)
 | 
			
		||||
#define WRITABLE(strio) STRIO_MODE_SET_P(strio, WRITABLE)
 | 
			
		||||
 | 
			
		||||
static VALUE sym_exception;
 | 
			
		||||
 | 
			
		||||
static struct StringIO*
 | 
			
		||||
readable(VALUE strio)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -1327,7 +1329,6 @@ strio_read(int argc, VALUE *argv, VALUE self)
 | 
			
		|||
 * call-seq:
 | 
			
		||||
 *   strio.sysread(integer[, outbuf])    -> string
 | 
			
		||||
 *   strio.readpartial(integer[, outbuf])    -> string
 | 
			
		||||
 *   strio.read_nonblock(integer[, outbuf])    -> string
 | 
			
		||||
 *
 | 
			
		||||
 * Similar to #read, but raises +EOFError+ at end of string instead of
 | 
			
		||||
 * returning +nil+, as well as IO#sysread does.
 | 
			
		||||
| 
						 | 
				
			
			@ -1342,8 +1343,50 @@ strio_sysread(int argc, VALUE *argv, VALUE self)
 | 
			
		|||
    return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * call-seq:
 | 
			
		||||
 *   strio.read_nonblock(integer[, outbuf [, opts]])    -> string
 | 
			
		||||
 *
 | 
			
		||||
 * Similar to #read, but raises +EOFError+ at end of string unless the
 | 
			
		||||
 * +exception: false+ option is passed in.
 | 
			
		||||
 */
 | 
			
		||||
static VALUE
 | 
			
		||||
strio_read_nonblock(int argc, VALUE *argv, VALUE self)
 | 
			
		||||
{
 | 
			
		||||
    VALUE opts = Qnil;
 | 
			
		||||
    int no_exception = 0;
 | 
			
		||||
 | 
			
		||||
    rb_scan_args(argc, argv, "11:", NULL, NULL, &opts);
 | 
			
		||||
 | 
			
		||||
    if (!NIL_P(opts)) {
 | 
			
		||||
	argc--;
 | 
			
		||||
 | 
			
		||||
	if (Qfalse == rb_hash_aref(opts, sym_exception))
 | 
			
		||||
	    no_exception = 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VALUE val = strio_read(argc, argv, self);
 | 
			
		||||
    if (NIL_P(val)) {
 | 
			
		||||
	if (no_exception)
 | 
			
		||||
	    return Qnil;
 | 
			
		||||
	else
 | 
			
		||||
	    rb_eof_error();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define strio_syswrite rb_io_write
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
strio_syswrite_nonblock(int argc, VALUE *argv, VALUE self)
 | 
			
		||||
{
 | 
			
		||||
    VALUE str;
 | 
			
		||||
 | 
			
		||||
    rb_scan_args(argc, argv, "10:", &str, NULL);
 | 
			
		||||
    return strio_syswrite(self, str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define strio_isatty strio_false
 | 
			
		||||
 | 
			
		||||
#define strio_pid strio_nil
 | 
			
		||||
| 
						 | 
				
			
			@ -1542,7 +1585,7 @@ Init_stringio()
 | 
			
		|||
	rb_define_method(mReadable, "readline", strio_readline, -1);
 | 
			
		||||
	rb_define_method(mReadable, "sysread", strio_sysread, -1);
 | 
			
		||||
	rb_define_method(mReadable, "readpartial", strio_sysread, -1);
 | 
			
		||||
	rb_define_method(mReadable, "read_nonblock", strio_sysread, -1);
 | 
			
		||||
	rb_define_method(mReadable, "read_nonblock", strio_read_nonblock, -1);
 | 
			
		||||
	rb_include_module(StringIO, mReadable);
 | 
			
		||||
    }
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -1552,7 +1595,9 @@ Init_stringio()
 | 
			
		|||
	rb_define_method(mWritable, "printf", strio_printf, -1);
 | 
			
		||||
	rb_define_method(mWritable, "puts", strio_puts, -1);
 | 
			
		||||
	rb_define_method(mWritable, "syswrite", strio_syswrite, 1);
 | 
			
		||||
	rb_define_method(mWritable, "write_nonblock", strio_syswrite, 1);
 | 
			
		||||
	rb_define_method(mWritable, "write_nonblock", strio_syswrite_nonblock, -1);
 | 
			
		||||
	rb_include_module(StringIO, mWritable);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sym_exception = ID2SYM(rb_intern("exception"));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										106
									
								
								io.c
									
										
									
									
									
								
							
							
						
						
									
										106
									
								
								io.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -159,7 +159,7 @@ static VALUE argf;
 | 
			
		|||
 | 
			
		||||
static ID id_write, id_read, id_getc, id_flush, id_readpartial, id_set_encoding;
 | 
			
		||||
static VALUE sym_mode, sym_perm, sym_extenc, sym_intenc, sym_encoding, sym_open_args;
 | 
			
		||||
static VALUE sym_textmode, sym_binmode, sym_autoclose;
 | 
			
		||||
static VALUE sym_textmode, sym_binmode, sym_autoclose, sym_exception;
 | 
			
		||||
static VALUE sym_SET, sym_CUR, sym_END;
 | 
			
		||||
#ifdef SEEK_DATA
 | 
			
		||||
static VALUE sym_DATA;
 | 
			
		||||
| 
						 | 
				
			
			@ -2413,14 +2413,14 @@ read_internal_call(VALUE arg)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock)
 | 
			
		||||
io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock, int no_exception)
 | 
			
		||||
{
 | 
			
		||||
    rb_io_t *fptr;
 | 
			
		||||
    VALUE length, str;
 | 
			
		||||
    long n, len;
 | 
			
		||||
    struct read_internal_arg arg;
 | 
			
		||||
 | 
			
		||||
    rb_scan_args(argc, argv, "11", &length, &str);
 | 
			
		||||
    rb_scan_args(argc, argv, "11:", &length, &str, NULL);
 | 
			
		||||
 | 
			
		||||
    if ((len = NUM2LONG(length)) < 0) {
 | 
			
		||||
	rb_raise(rb_eArgError, "negative length %ld given", len);
 | 
			
		||||
| 
						 | 
				
			
			@ -2452,8 +2452,12 @@ io_getpartial(int argc, VALUE *argv, VALUE io, int nonblock)
 | 
			
		|||
        if (n < 0) {
 | 
			
		||||
            if (!nonblock && rb_io_wait_readable(fptr->fd))
 | 
			
		||||
                goto again;
 | 
			
		||||
            if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN))
 | 
			
		||||
                rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "read would block");
 | 
			
		||||
            if (nonblock && (errno == EWOULDBLOCK || errno == EAGAIN)) {
 | 
			
		||||
                if (no_exception)
 | 
			
		||||
                    return ID2SYM(rb_intern("wait_readable"));
 | 
			
		||||
                else
 | 
			
		||||
		    rb_readwrite_sys_fail(RB_IO_WAIT_READABLE, "read would block");
 | 
			
		||||
            }
 | 
			
		||||
            rb_sys_fail_path(fptr->pathv);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -2529,7 +2533,7 @@ io_readpartial(int argc, VALUE *argv, VALUE io)
 | 
			
		|||
{
 | 
			
		||||
    VALUE ret;
 | 
			
		||||
 | 
			
		||||
    ret = io_getpartial(argc, argv, io, 0);
 | 
			
		||||
    ret = io_getpartial(argc, argv, io, 0, 0);
 | 
			
		||||
    if (NIL_P(ret))
 | 
			
		||||
        rb_eof_error();
 | 
			
		||||
    return ret;
 | 
			
		||||
| 
						 | 
				
			
			@ -2590,16 +2594,62 @@ static VALUE
 | 
			
		|||
io_read_nonblock(int argc, VALUE *argv, VALUE io)
 | 
			
		||||
{
 | 
			
		||||
    VALUE ret;
 | 
			
		||||
    VALUE opts = Qnil;
 | 
			
		||||
    int no_exception = 0;
 | 
			
		||||
 | 
			
		||||
    ret = io_getpartial(argc, argv, io, 1);
 | 
			
		||||
    if (NIL_P(ret))
 | 
			
		||||
        rb_eof_error();
 | 
			
		||||
    rb_scan_args(argc, argv, "11:", NULL, NULL, &opts);
 | 
			
		||||
 | 
			
		||||
    if (!NIL_P(opts) && Qfalse == rb_hash_aref(opts, sym_exception))
 | 
			
		||||
	no_exception = 1;
 | 
			
		||||
 | 
			
		||||
    ret = io_getpartial(argc, argv, io, 1, no_exception);
 | 
			
		||||
 | 
			
		||||
    if (NIL_P(ret)) {
 | 
			
		||||
	if (no_exception)
 | 
			
		||||
	    return Qnil;
 | 
			
		||||
	else
 | 
			
		||||
	    rb_eof_error();
 | 
			
		||||
    }
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
io_write_nonblock(VALUE io, VALUE str, int no_exception)
 | 
			
		||||
{
 | 
			
		||||
    rb_io_t *fptr;
 | 
			
		||||
    long n;
 | 
			
		||||
 | 
			
		||||
    if (!RB_TYPE_P(str, T_STRING))
 | 
			
		||||
	str = rb_obj_as_string(str);
 | 
			
		||||
 | 
			
		||||
    io = GetWriteIO(io);
 | 
			
		||||
    GetOpenFile(io, fptr);
 | 
			
		||||
    rb_io_check_writable(fptr);
 | 
			
		||||
 | 
			
		||||
    if (io_fflush(fptr) < 0)
 | 
			
		||||
        rb_sys_fail(0);
 | 
			
		||||
 | 
			
		||||
    rb_io_set_nonblock(fptr);
 | 
			
		||||
    n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
 | 
			
		||||
 | 
			
		||||
    if (n == -1) {
 | 
			
		||||
        if (errno == EWOULDBLOCK || errno == EAGAIN) {
 | 
			
		||||
	    if (no_exception) {
 | 
			
		||||
		return ID2SYM(rb_intern("wait_writable"));
 | 
			
		||||
	    } else {
 | 
			
		||||
		rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE, "write would block");
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
        rb_sys_fail_path(fptr->pathv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return LONG2FIX(n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  call-seq:
 | 
			
		||||
 *     ios.write_nonblock(string)   -> integer
 | 
			
		||||
 *     ios.write_nonblock(string [, options])   -> integer
 | 
			
		||||
 *
 | 
			
		||||
 *  Writes the given string to <em>ios</em> using
 | 
			
		||||
 *  the write(2) system call after O_NONBLOCK is set for
 | 
			
		||||
| 
						 | 
				
			
			@ -2648,34 +2698,25 @@ io_read_nonblock(int argc, VALUE *argv, VALUE io)
 | 
			
		|||
 *  according to the kind of the IO object.
 | 
			
		||||
 *  In such cases, write_nonblock raises <code>Errno::EBADF</code>.
 | 
			
		||||
 *
 | 
			
		||||
 *  By specifying `exception: false`, the options hash allows you to indicate
 | 
			
		||||
 *  that write_nonblock should not raise an IO::WaitWritable exception, but
 | 
			
		||||
 *  return the symbol :wait_writable instead.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static VALUE
 | 
			
		||||
rb_io_write_nonblock(VALUE io, VALUE str)
 | 
			
		||||
rb_io_write_nonblock(int argc, VALUE *argv, VALUE io)
 | 
			
		||||
{
 | 
			
		||||
    rb_io_t *fptr;
 | 
			
		||||
    long n;
 | 
			
		||||
    VALUE str;
 | 
			
		||||
    VALUE opts = Qnil;
 | 
			
		||||
    int no_exceptions = 0;
 | 
			
		||||
 | 
			
		||||
    if (!RB_TYPE_P(str, T_STRING))
 | 
			
		||||
	str = rb_obj_as_string(str);
 | 
			
		||||
    rb_scan_args(argc, argv, "10:", &str, &opts);
 | 
			
		||||
 | 
			
		||||
    io = GetWriteIO(io);
 | 
			
		||||
    GetOpenFile(io, fptr);
 | 
			
		||||
    rb_io_check_writable(fptr);
 | 
			
		||||
    if (!NIL_P(opts) && Qfalse == rb_hash_aref(opts, sym_exception))
 | 
			
		||||
	no_exceptions = 1;
 | 
			
		||||
 | 
			
		||||
    if (io_fflush(fptr) < 0)
 | 
			
		||||
        rb_sys_fail(0);
 | 
			
		||||
 | 
			
		||||
    rb_io_set_nonblock(fptr);
 | 
			
		||||
    n = write(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
 | 
			
		||||
 | 
			
		||||
    if (n == -1) {
 | 
			
		||||
        if (errno == EWOULDBLOCK || errno == EAGAIN)
 | 
			
		||||
            rb_readwrite_sys_fail(RB_IO_WAIT_WRITABLE, "write would block");
 | 
			
		||||
        rb_sys_fail_path(fptr->pathv);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return LONG2FIX(n);
 | 
			
		||||
    return io_write_nonblock(io, str, no_exceptions);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -10809,7 +10850,7 @@ argf_getpartial(int argc, VALUE *argv, VALUE argf, int nonblock)
 | 
			
		|||
			 RUBY_METHOD_FUNC(0), Qnil, rb_eEOFError, (VALUE)0);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        tmp = io_getpartial(argc, argv, ARGF.current_file, nonblock);
 | 
			
		||||
        tmp = io_getpartial(argc, argv, ARGF.current_file, nonblock, 0);
 | 
			
		||||
    }
 | 
			
		||||
    if (NIL_P(tmp)) {
 | 
			
		||||
        if (ARGF.next_p == -1) {
 | 
			
		||||
| 
						 | 
				
			
			@ -11851,7 +11892,7 @@ Init_IO(void)
 | 
			
		|||
    rb_define_method(rb_cIO, "readlines",  rb_io_readlines, -1);
 | 
			
		||||
 | 
			
		||||
    rb_define_method(rb_cIO, "read_nonblock",  io_read_nonblock, -1);
 | 
			
		||||
    rb_define_method(rb_cIO, "write_nonblock", rb_io_write_nonblock, 1);
 | 
			
		||||
    rb_define_method(rb_cIO, "write_nonblock", rb_io_write_nonblock, -1);
 | 
			
		||||
    rb_define_method(rb_cIO, "readpartial",  io_readpartial, -1);
 | 
			
		||||
    rb_define_method(rb_cIO, "read",  io_read, -1);
 | 
			
		||||
    rb_define_method(rb_cIO, "write", io_write_m, 1);
 | 
			
		||||
| 
						 | 
				
			
			@ -12056,4 +12097,5 @@ Init_IO(void)
 | 
			
		|||
#ifdef SEEK_HOLE
 | 
			
		||||
    sym_HOLE = ID2SYM(rb_intern("HOLE"));
 | 
			
		||||
#endif
 | 
			
		||||
    sym_exception = ID2SYM(rb_intern("exception"));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -156,19 +156,46 @@ class OpenSSL::TestPair < Test::Unit::TestCase
 | 
			
		|||
      ret = nil
 | 
			
		||||
      assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10) }
 | 
			
		||||
      assert_equal("def\n", ret)
 | 
			
		||||
      s1.close
 | 
			
		||||
      assert_raise(EOFError) { s2.read_nonblock(10) }
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_read_nonblock_no_exception
 | 
			
		||||
    ssl_pair {|s1, s2|
 | 
			
		||||
      assert_equal :wait_readable, s2.read_nonblock(10, exception: false)
 | 
			
		||||
      s1.write "abc\ndef\n"
 | 
			
		||||
      IO.select([s2])
 | 
			
		||||
      assert_equal("ab", s2.read_nonblock(2, exception: false))
 | 
			
		||||
      assert_equal("c\n", s2.gets)
 | 
			
		||||
      ret = nil
 | 
			
		||||
      assert_nothing_raised("[ruby-core:20298]") { ret = s2.read_nonblock(10, exception: false) }
 | 
			
		||||
      assert_equal("def\n", ret)
 | 
			
		||||
      s1.close
 | 
			
		||||
      assert_equal(nil, s2.read_nonblock(10, exception: false))
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def write_nonblock(socket, meth, str)
 | 
			
		||||
    ret = socket.send(meth, str)
 | 
			
		||||
    ret.is_a?(Symbol) ? 0 : ret
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def write_nonblock_no_ex(socket, str)
 | 
			
		||||
    ret = socket.write_nonblock str, exception: false
 | 
			
		||||
    ret.is_a?(Symbol) ? 0 : ret
 | 
			
		||||
  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)
 | 
			
		||||
        n += write_nonblock s1, :write_nonblock, "a" * 100000
 | 
			
		||||
        n += write_nonblock s1, :write_nonblock, "b" * 100000
 | 
			
		||||
        n += write_nonblock s1, :write_nonblock, "c" * 100000
 | 
			
		||||
        n += write_nonblock s1, :write_nonblock, "d" * 100000
 | 
			
		||||
        n += write_nonblock s1, :write_nonblock, "e" * 100000
 | 
			
		||||
        n += write_nonblock s1, :write_nonblock, "f" * 100000
 | 
			
		||||
      rescue IO::WaitWritable
 | 
			
		||||
      end
 | 
			
		||||
      s1.close
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +203,20 @@ class OpenSSL::TestPair < Test::Unit::TestCase
 | 
			
		|||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_write_nonblock_no_exceptions
 | 
			
		||||
    ssl_pair {|s1, s2|
 | 
			
		||||
      n = 0
 | 
			
		||||
      n += write_nonblock_no_ex s1, "a" * 100000
 | 
			
		||||
      n += write_nonblock_no_ex s1, "b" * 100000
 | 
			
		||||
      n += write_nonblock_no_ex s1, "c" * 100000
 | 
			
		||||
      n += write_nonblock_no_ex s1, "d" * 100000
 | 
			
		||||
      n += write_nonblock_no_ex s1, "e" * 100000
 | 
			
		||||
      n += write_nonblock_no_ex s1, "f" * 100000
 | 
			
		||||
      s1.close
 | 
			
		||||
      assert_equal(n, s2.read.length)
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_write_nonblock_with_buffered_data
 | 
			
		||||
    ssl_pair {|s1, s2|
 | 
			
		||||
      s1.write "foo"
 | 
			
		||||
| 
						 | 
				
			
			@ -186,6 +227,16 @@ class OpenSSL::TestPair < Test::Unit::TestCase
 | 
			
		|||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_write_nonblock_with_buffered_data_no_exceptions
 | 
			
		||||
    ssl_pair {|s1, s2|
 | 
			
		||||
      s1.write "foo"
 | 
			
		||||
      s1.write_nonblock("bar", exception: false)
 | 
			
		||||
      s1.write "baz"
 | 
			
		||||
      s1.close
 | 
			
		||||
      assert_equal("foobarbaz", s2.read)
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_connect_accept_nonblock
 | 
			
		||||
    host = "127.0.0.1"
 | 
			
		||||
    port = 0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1205,6 +1205,16 @@ class TestIO < Test::Unit::TestCase
 | 
			
		|||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_write_nonblock_simple_no_exceptions
 | 
			
		||||
    skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
 | 
			
		||||
    pipe(proc do |w|
 | 
			
		||||
      w.write_nonblock('1', exception: false)
 | 
			
		||||
      w.close
 | 
			
		||||
    end, proc do |r|
 | 
			
		||||
      assert_equal("1", r.read)
 | 
			
		||||
    end)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_read_nonblock_error
 | 
			
		||||
    return if !have_nonblock?
 | 
			
		||||
    skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
 | 
			
		||||
| 
						 | 
				
			
			@ -1215,6 +1225,41 @@ class TestIO < Test::Unit::TestCase
 | 
			
		|||
        assert_kind_of(IO::WaitReadable, $!)
 | 
			
		||||
      end
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    with_pipe {|r, w|
 | 
			
		||||
      begin
 | 
			
		||||
        r.read_nonblock 4096, ""
 | 
			
		||||
      rescue Errno::EWOULDBLOCK
 | 
			
		||||
        assert_kind_of(IO::WaitReadable, $!)
 | 
			
		||||
      end
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_read_nonblock_no_exceptions
 | 
			
		||||
    return if !have_nonblock?
 | 
			
		||||
    skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
 | 
			
		||||
    with_pipe {|r, w|
 | 
			
		||||
      assert_equal :wait_readable, r.read_nonblock(4096, exception: false)
 | 
			
		||||
      w.puts "HI!"
 | 
			
		||||
      assert_equal "HI!\n", r.read_nonblock(4096, exception: false)
 | 
			
		||||
      w.close
 | 
			
		||||
      assert_equal nil, r.read_nonblock(4096, exception: false)
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_read_nonblock_with_buffer_no_exceptions
 | 
			
		||||
    return if !have_nonblock?
 | 
			
		||||
    skip "IO#read_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
 | 
			
		||||
    with_pipe {|r, w|
 | 
			
		||||
      assert_equal :wait_readable, r.read_nonblock(4096, "", exception: false)
 | 
			
		||||
      w.puts "HI!"
 | 
			
		||||
      buf = "buf"
 | 
			
		||||
      value = r.read_nonblock(4096, buf, exception: false)
 | 
			
		||||
      assert_equal value, "HI!\n"
 | 
			
		||||
      assert buf.equal?(value)
 | 
			
		||||
      w.close
 | 
			
		||||
      assert_equal nil, r.read_nonblock(4096, "", exception: false)
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_write_nonblock_error
 | 
			
		||||
| 
						 | 
				
			
			@ -1231,6 +1276,20 @@ class TestIO < Test::Unit::TestCase
 | 
			
		|||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_write_nonblock_no_exceptions
 | 
			
		||||
    return if !have_nonblock?
 | 
			
		||||
    skip "IO#write_nonblock is not supported on file/pipe." if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
 | 
			
		||||
    with_pipe {|r, w|
 | 
			
		||||
      loop {
 | 
			
		||||
        ret = w.write_nonblock("a"*100000, exception: false)
 | 
			
		||||
        if ret.is_a?(Symbol)
 | 
			
		||||
          assert_equal :wait_writable, ret
 | 
			
		||||
          break
 | 
			
		||||
        end
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_gets
 | 
			
		||||
    pipe(proc do |w|
 | 
			
		||||
      w.write "foobarbaz"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -190,6 +190,20 @@ class TestSocketNonblock < Test::Unit::TestCase
 | 
			
		|||
    s.close if s
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_read_nonblock_no_exception
 | 
			
		||||
    c, s = tcp_pair
 | 
			
		||||
    assert_equal :wait_readable, c.read_nonblock(100, exception: false)
 | 
			
		||||
    assert_equal :wait_readable, s.read_nonblock(100, exception: false)
 | 
			
		||||
    c.write("abc")
 | 
			
		||||
    IO.select [s]
 | 
			
		||||
    assert_equal("a", s.read_nonblock(1, exception: false))
 | 
			
		||||
    assert_equal("bc", s.read_nonblock(100, exception: false))
 | 
			
		||||
    assert_equal :wait_readable, s.read_nonblock(100, exception: false)
 | 
			
		||||
  ensure
 | 
			
		||||
    c.close if c
 | 
			
		||||
    s.close if s
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
=begin
 | 
			
		||||
  def test_write_nonblock
 | 
			
		||||
    c, s = tcp_pair
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,6 +89,14 @@ class TestStringIO < Test::Unit::TestCase
 | 
			
		|||
    f.close unless f.closed?
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_write_nonblock_no_exceptions
 | 
			
		||||
    s = ""
 | 
			
		||||
    f = StringIO.new(s, "w")
 | 
			
		||||
    f.write_nonblock("foo", exception: false)
 | 
			
		||||
    f.close
 | 
			
		||||
    assert_equal("foo", s)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_write_nonblock
 | 
			
		||||
    s = ""
 | 
			
		||||
    f = StringIO.new(s, "w")
 | 
			
		||||
| 
						 | 
				
			
			@ -437,7 +445,7 @@ class TestStringIO < Test::Unit::TestCase
 | 
			
		|||
    f = StringIO.new("\u3042\u3044")
 | 
			
		||||
    assert_raise(ArgumentError) { f.readpartial(-1) }
 | 
			
		||||
    assert_raise(ArgumentError) { f.readpartial(1, 2, 3) }
 | 
			
		||||
    assert_equal("\u3042\u3044", f.readpartial)
 | 
			
		||||
    assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(100))
 | 
			
		||||
    f.rewind
 | 
			
		||||
    assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.readpartial(f.size))
 | 
			
		||||
    f.rewind
 | 
			
		||||
| 
						 | 
				
			
			@ -450,7 +458,19 @@ class TestStringIO < Test::Unit::TestCase
 | 
			
		|||
    f = StringIO.new("\u3042\u3044")
 | 
			
		||||
    assert_raise(ArgumentError) { f.read_nonblock(-1) }
 | 
			
		||||
    assert_raise(ArgumentError) { f.read_nonblock(1, 2, 3) }
 | 
			
		||||
    assert_equal("\u3042\u3044", f.read_nonblock)
 | 
			
		||||
    assert_equal("\u3042\u3044".force_encoding("BINARY"), f.read_nonblock(100))
 | 
			
		||||
    assert_raise(EOFError) { f.read_nonblock(10) }
 | 
			
		||||
    f.rewind
 | 
			
		||||
    assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def test_read_nonblock_no_exceptions
 | 
			
		||||
    f = StringIO.new("\u3042\u3044")
 | 
			
		||||
    assert_raise(ArgumentError) { f.read_nonblock(-1, exception: false) }
 | 
			
		||||
    assert_raise(ArgumentError) { f.read_nonblock(1, 2, 3, exception: false) }
 | 
			
		||||
    assert_raise(ArgumentError) { f.read_nonblock }
 | 
			
		||||
    assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(100, exception: false))
 | 
			
		||||
    assert_equal(nil, f.read_nonblock(10, exception: false))
 | 
			
		||||
    f.rewind
 | 
			
		||||
    assert_equal("\u3042\u3044".force_encoding(Encoding::ASCII_8BIT), f.read_nonblock(f.size))
 | 
			
		||||
    f.rewind
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue