* ext/openssl/ossl_ssl.c (ossl_start_ssl): should wait for that

the underlying IO become readable or writable if the error was
  SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. [ruby-dev:25795]

* ext/openssl/ossl_ssl.c (ossl_ssl_read, ossl_ssl_write): ditto.

* ext/openssl/lib/openssl/buffering.rb
  (Buffering#consume_rbuf): pointless eof flag resetting is deleted.
  (Buffering#read): should return an empty string if the specified
  size is zero.
  (Buffering#readpartial): new method.
  (Buffering#readline): fix typo.
  (Buffering#getc): return the first character of string correctly.
  (Buffering#readchar): fix typo.
  (Buffering#eof?): should read again it the input buffer is empty.
  (Buffering#do_write): should rescue Errno::EAGAIN.
  (Buffering#puts): use "\n" as the output field separator.

* ext/openssl/extconf.rb: get rid of GNUmakefile generation.

* text/openssl/test_pair.rb: test for IO like methods.

* test/ruby/ut_eof.rb: test about empty file.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@8081 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
gotoyuzo 2005-03-05 21:36:11 +00:00
Родитель 4b0f7cecc4
Коммит a9fb0817a1
6 изменённых файлов: 461 добавлений и 61 удалений

Просмотреть файл

@ -1,3 +1,29 @@
Sun Mar 6 06:34:31 2005 GOTOU Yuuzou <gotoyuzo@notwork.org>
* ext/openssl/ossl_ssl.c (ossl_start_ssl): should wait for that
the underlying IO become readable or writable if the error was
SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE. [ruby-dev:25795]
* ext/openssl/ossl_ssl.c (ossl_ssl_read, ossl_ssl_write): ditto.
* ext/openssl/lib/openssl/buffering.rb
(Buffering#consume_rbuf): pointless eof flag resetting is deleted.
(Buffering#read): should return an empty string if the specified
size is zero.
(Buffering#readpartial): new method.
(Buffering#readline): fix typo.
(Buffering#getc): return the first character of string correctly.
(Buffering#readchar): fix typo.
(Buffering#eof?): should read again it the input buffer is empty.
(Buffering#do_write): should rescue Errno::EAGAIN.
(Buffering#puts): use "\n" as the output field separator.
* ext/openssl/extconf.rb: get rid of GNUmakefile generation.
* text/openssl/test_pair.rb: test for IO like methods.
* test/ruby/ut_eof.rb: test about empty file.
Sat Mar 5 17:48:31 2005 Hirokazu Yamamoto <ocean@m2.ccsnet.ne.jp> Sat Mar 5 17:48:31 2005 Hirokazu Yamamoto <ocean@m2.ccsnet.ne.jp>
* dir.c (rb_glob): fixed mismatch of argument. * dir.c (rb_glob): fixed mismatch of argument.

Просмотреть файл

@ -113,33 +113,5 @@ have_struct_member("EVP_CIPHER_CTX", "engine", "openssl/evp.h")
have_struct_member("X509_ATTRIBUTE", "single", "openssl/x509.h") have_struct_member("X509_ATTRIBUTE", "single", "openssl/x509.h")
message "=== Checking done. ===\n" message "=== Checking done. ===\n"
$distcleanfiles << "GNUmakefile" << "dep"
create_makefile("openssl") create_makefile("openssl")
if /gcc/ =~ CONFIG["CC"]
File.open("GNUmakefile", "w") {|f|
f.print <<EOD
include Makefile
SRCS = $(OBJS:.o=.c)
test-link: $(OBJS)
$(CC) $(DLDFLAGS) #{OUTFLAG}.testlink $(OBJS) $(LIBPATH) $(LIBS) $(LOCAL_LIBS)
@$(RM) .testlink
@echo "Done."
dep:
$(CC) $(CFLAGS) $(CPPFLAGS) -c $(SRCS) -MM | \\
$(RUBY) -p -e 'BEGIN{S = []' \\
-e 'while !ARGV.empty? and /^(\\w+)=(.*)/ =~ ARGV[0]' \\
-e 'S << [/\#{Regexp.quote($$2)}\\//, "$$(\#{$$1})/"]' \\
-e 'ARGV.shift' \\
-e 'end' \\
-e '}' -e 'S.each(&method(:gsub!))' -- \\
'topdir=$(topdir)' 'srcdir=$(srcdir)' 'hdrdir=$(hdrdir)' \\
> dep
include dep
EOD
}
end
message "Done.\n" message "Done.\n"

Просмотреть файл

@ -41,7 +41,6 @@ module Buffering
def consume_rbuff(size=nil) def consume_rbuff(size=nil)
if @rbuffer.size == 0 if @rbuffer.size == 0
@eof = nil
nil nil
else else
size = @rbuffer.size unless size size = @rbuffer.size unless size
@ -54,6 +53,14 @@ module Buffering
public public
def read(size=nil, buf=nil) def read(size=nil, buf=nil)
if size == 0
if buf
buf.clear
return buf
else
return ""
end
end
fill_rbuff unless defined? @rbuffer fill_rbuff unless defined? @rbuffer
@eof ||= nil @eof ||= nil
until @eof until @eof
@ -68,6 +75,31 @@ module Buffering
(size && ret.empty?) ? nil : ret (size && ret.empty?) ? nil : ret
end end
def readpartial(maxlen, buf=nil)
if maxlen == 0
if buf
buf.clear
return buf
else
return ""
end
end
if !defined?(@rbuffer) || @rbuffer.size == 0
begin
return sysread(maxlen, buf)
rescue Errno::EAGAIN
retry
end
end
ret = consume_rbuff(maxlen)
if buf
buf.replace(ret)
ret = buf
end
raise EOFError if ret.empty?
ret
end
def gets(eol=$/) def gets(eol=$/)
fill_rbuff unless defined? @rbuffer fill_rbuff unless defined? @rbuffer
idx = @rbuffer.index(eol) idx = @rbuffer.index(eol)
@ -101,13 +133,13 @@ module Buffering
end end
def readline(eol=$/) def readline(eol=$/)
raise EOFErorr if eof? raise EOFError if eof?
gets(eol) gets(eol)
end end
def getc def getc
c = read(1) c = read(1)
c ? c.to_i : nil c ? c[0] : nil
end end
def each_byte def each_byte
@ -117,7 +149,7 @@ module Buffering
end end
def readchar def readchar
raise EOFErorr if eof? raise EOFError if eof?
getc getc
end end
@ -127,6 +159,7 @@ module Buffering
def eof? def eof?
@eof ||= nil @eof ||= nil
fill_rbuff if !@eof && (!defined?(@rbuffer) || @rbuffer.size == 0)
@eof && @rbuffer.size == 0 @eof && @rbuffer.size == 0
end end
alias eof eof? alias eof eof?
@ -144,7 +177,12 @@ module Buffering
remain = idx ? idx + $/.size : @wbuffer.length remain = idx ? idx + $/.size : @wbuffer.length
nwritten = 0 nwritten = 0
while remain > 0 while remain > 0
nwrote = syswrite(@wbuffer[nwritten,remain]) str = @wbuffer[nwritten,remain]
begin
nwrote = syswrite(str)
rescue Errno::EAGAIN
retry
end
remain -= nwrote remain -= nwrote
nwritten += nwrote nwritten += nwrote
end end
@ -166,10 +204,13 @@ module Buffering
def puts(*args) def puts(*args)
s = "" s = ""
if args.empty?
s << "\n"
end
args.each{|arg| args.each{|arg|
s << arg.to_s s << arg.to_s
unless /#{$/}\z/o =~ s if $/ && /\n\z/ !~ s
s << $/ s << "\n"
end end
} }
do_write(s) do_write(s)

Просмотреть файл

@ -433,52 +433,49 @@ ossl_ssl_setup(VALUE self)
return Qtrue; return Qtrue;
} }
static void static VALUE
ossl_start_ssl(SSL *ssl, int (*func)()) ossl_start_ssl(VALUE self, int (*func)())
{ {
SSL *ssl;
OpenFile *fptr;
VALUE cb;
int ret; int ret;
Data_Get_Struct(self, SSL, ssl);
GetOpenFile(ossl_ssl_get_io(self), fptr);
cb = ossl_sslctx_get_verify_cb(ossl_ssl_get_ctx(self));
SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)cb);
for(;;){ for(;;){
if((ret = func(ssl)) > 0) break; if((ret = func(ssl)) > 0) break;
switch(SSL_get_error(ssl, ret)){ switch(SSL_get_error(ssl, ret)){
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
rb_io_wait_writable(fptr->fd);
continue;
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
rb_thread_schedule(); rb_io_wait_readable(fptr->fd);
continue; continue;
case SSL_ERROR_SYSCALL:
rb_sys_fail(0);
default: default:
ossl_raise(eSSLError, NULL); ossl_raise(eSSLError, NULL);
} }
} }
return self;
} }
static VALUE static VALUE
ossl_ssl_connect(VALUE self) ossl_ssl_connect(VALUE self)
{ {
SSL *ssl;
VALUE cb;
ossl_ssl_setup(self); ossl_ssl_setup(self);
Data_Get_Struct(self, SSL, ssl); return ossl_start_ssl(self, SSL_connect);
cb = ossl_sslctx_get_verify_cb(ossl_ssl_get_ctx(self));
SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)cb);
ossl_start_ssl(ssl, SSL_connect);
return self;
} }
static VALUE static VALUE
ossl_ssl_accept(VALUE self) ossl_ssl_accept(VALUE self)
{ {
SSL *ssl;
VALUE cb;
ossl_ssl_setup(self); ossl_ssl_setup(self);
Data_Get_Struct(self, SSL, ssl); return ossl_start_ssl(self, SSL_accept);
cb = ossl_sslctx_get_verify_cb(ossl_ssl_get_ctx(self));
SSL_set_ex_data(ssl, ossl_ssl_ex_vcb_idx, (void *)cb);
ossl_start_ssl(ssl, SSL_accept);
return self;
} }
static VALUE static VALUE
@ -512,8 +509,10 @@ ossl_ssl_read(int argc, VALUE *argv, VALUE self)
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
rb_eof_error(); rb_eof_error();
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
rb_io_wait_writable(fptr->fd);
continue;
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
rb_thread_schedule(); rb_io_wait_readable(fptr->fd);
continue; continue;
case SSL_ERROR_SYSCALL: case SSL_ERROR_SYSCALL:
if(ERR_peek_error() == 0 && nread == 0) rb_eof_error(); if(ERR_peek_error() == 0 && nread == 0) rb_eof_error();
@ -542,9 +541,11 @@ ossl_ssl_write(VALUE self, VALUE str)
{ {
SSL *ssl; SSL *ssl;
int nwrite = 0; int nwrite = 0;
OpenFile *fptr;
StringValue(str); StringValue(str);
Data_Get_Struct(self, SSL, ssl); Data_Get_Struct(self, SSL, ssl);
GetOpenFile(ossl_ssl_get_io(self), fptr);
if (ssl) { if (ssl) {
for (;;){ for (;;){
@ -553,11 +554,13 @@ ossl_ssl_write(VALUE self, VALUE str)
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
goto end; goto end;
case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_WRITE:
rb_io_wait_writable(fptr->fd);
continue;
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
rb_thread_schedule(); rb_io_wait_readable(fptr->fd);
continue; continue;
case SSL_ERROR_SYSCALL: case SSL_ERROR_SYSCALL:
rb_eof_error(); rb_sys_fail(0);
default: default:
ossl_raise(eSSLError, "SSL_write:"); ossl_raise(eSSLError, "SSL_write:");
} }

332
test/openssl/test_pair.rb Normal file
Просмотреть файл

@ -0,0 +1,332 @@
begin
require "openssl"
rescue LoadError
end
require 'test/unit'
if defined?(OpenSSL)
require 'socket'
dir = File.expand_path(__FILE__)
2.times {dir = File.dirname(dir)}
$:.replace([File.join(dir, "ruby")] | $:)
require 'ut_eof'
module SSLPair
def server
host = "127.0.0.1"
port = 0
key = OpenSSL::PKey::RSA.new(512)
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 0
name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]])
cert.subject = name
cert.issuer = name
cert.not_before = Time.now
cert.not_after = Time.now + 3600
cert.public_key = key.public_key
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
cert.extensions = [
ef.create_extension("basicConstraints","CA:FALSE"),
ef.create_extension("subjectKeyIdentifier","hash"),
ef.create_extension("extendedKeyUsage","serverAuth"),
ef.create_extension("keyUsage",
"keyEncipherment,dataEncipherment,digitalSignature")
]
ef.issuer_certificate = cert
cert.add_extension ef.create_extension("authorityKeyIdentifier",
"keyid:always,issuer:always")
cert.sign(key, OpenSSL::Digest::SHA1.new)
ctx = OpenSSL::SSL::SSLContext.new()
ctx.key = key
ctx.cert = cert
tcps = TCPServer.new(host, port)
ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
return ssls
end
def client(port)
host = "127.0.0.1"
ctx = OpenSSL::SSL::SSLContext.new()
s = TCPSocket.new(host, port)
ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
ssl.connect
ssl.sync_close = true
ssl
end
def ssl_pair
ssls = server
th = Thread.new {
ns = ssls.accept
ssls.close
ns
}
port = ssls.to_io.addr[1]
c = client(port)
s = th.value
if block_given?
begin
yield c, s
ensure
c.close unless c.closed?
s.close unless s.closed?
end
else
return c, s
end
end
end
class OpenSSL::TestEOF1 < Test::Unit::TestCase
include TestEOF
include SSLPair
def open_file(content)
s1, s2 = ssl_pair
Thread.new { s2 << content; s2.close }
yield s1
end
end
class OpenSSL::TestEOF2 < Test::Unit::TestCase
include TestEOF
include SSLPair
def open_file(content)
s1, s2 = ssl_pair
Thread.new { s1 << content; s1.close }
yield s2
end
end
class OpenSSL::TestPair < Test::Unit::TestCase
include SSLPair
def test_getc
ssl_pair {|s1, s2|
s1 << "a"
assert_equal(?a, s2.getc)
}
end
def test_readpartial
ssl_pair {|s1, s2|
s2.write "a\nbcd"
assert_equal("a\n", s1.gets)
assert_equal("bcd", s1.readpartial(10))
s2.write "efg"
assert_equal("efg", s1.readpartial(10))
s2.close
assert_raise(EOFError) { s1.readpartial(10) }
assert_raise(EOFError) { s1.readpartial(10) }
assert_equal("", s1.readpartial(0))
}
end
def test_readall
ssl_pair {|s1, s2|
s2.close
assert_equal("", s1.read)
}
end
def test_readline
ssl_pair {|s1, s2|
s2.close
assert_raise(EOFError) { s1.readline }
}
end
def test_puts_meta
ssl_pair {|s1, s2|
begin
old = $/
$/ = '*'
s1.puts 'a'
ensure
$/ = old
end
s1.close
assert_equal("a\n", s2.read)
}
end
def test_puts_empty
ssl_pair {|s1, s2|
s1.puts
s1.close
assert_equal("\n", s2.read)
}
end
end
end
begin
require "openssl"
rescue LoadError
end
require 'test/unit'
if defined?(OpenSSL)
require 'socket'
dir = File.expand_path(__FILE__)
2.times {dir = File.dirname(dir)}
$:.replace([File.join(dir, "ruby")] | $:)
require 'ut_eof'
module SSLPair
def server
host = "127.0.0.1"
port = 0
key = OpenSSL::PKey::RSA.new(512)
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 0
name = OpenSSL::X509::Name.new([["C","JP"],["O","TEST"],["CN","localhost"]])
cert.subject = name
cert.issuer = name
cert.not_before = Time.now
cert.not_after = Time.now + 3600
cert.public_key = key.public_key
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
cert.extensions = [
ef.create_extension("basicConstraints","CA:FALSE"),
ef.create_extension("subjectKeyIdentifier","hash"),
ef.create_extension("extendedKeyUsage","serverAuth"),
ef.create_extension("keyUsage",
"keyEncipherment,dataEncipherment,digitalSignature")
]
ef.issuer_certificate = cert
cert.add_extension ef.create_extension("authorityKeyIdentifier",
"keyid:always,issuer:always")
cert.sign(key, OpenSSL::Digest::SHA1.new)
ctx = OpenSSL::SSL::SSLContext.new()
ctx.key = key
ctx.cert = cert
tcps = TCPServer.new(host, port)
ssls = OpenSSL::SSL::SSLServer.new(tcps, ctx)
return ssls
end
def client(port)
host = "127.0.0.1"
ctx = OpenSSL::SSL::SSLContext.new()
s = TCPSocket.new(host, port)
ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
ssl.connect
ssl.sync_close = true
ssl
end
def ssl_pair
ssls = server
th = Thread.new {
ns = ssls.accept
ssls.close
ns
}
port = ssls.to_io.addr[1]
c = client(port)
s = th.value
if block_given?
begin
yield c, s
ensure
c.close unless c.closed?
s.close unless s.closed?
end
else
return c, s
end
end
end
class OpenSSL::TestEOF1 < Test::Unit::TestCase
include TestEOF
include SSLPair
def open_file(content)
s1, s2 = ssl_pair
Thread.new { s2 << content; s2.close }
yield s1
end
end
class OpenSSL::TestEOF2 < Test::Unit::TestCase
include TestEOF
include SSLPair
def open_file(content)
s1, s2 = ssl_pair
Thread.new { s1 << content; s1.close }
yield s2
end
end
class OpenSSL::TestPair < Test::Unit::TestCase
include SSLPair
def test_getc
ssl_pair {|s1, s2|
s1 << "a"
assert_equal(?a, s2.getc)
}
end
def test_readpartial
ssl_pair {|s1, s2|
s2.write "a\nbcd"
assert_equal("a\n", s1.gets)
assert_equal("bcd", s1.readpartial(10))
s2.write "efg"
assert_equal("efg", s1.readpartial(10))
s2.close
assert_raise(EOFError) { s1.readpartial(10) }
assert_raise(EOFError) { s1.readpartial(10) }
assert_equal("", s1.readpartial(0))
}
end
def test_readall
ssl_pair {|s1, s2|
s2.close
assert_equal("", s1.read)
}
end
def test_readline
ssl_pair {|s1, s2|
s2.close
assert_raise(EOFError) { s1.readline }
}
end
def test_puts_meta
ssl_pair {|s1, s2|
begin
old = $/
$/ = '*'
s1.puts 'a'
ensure
$/ = old
end
s1.close
assert_equal("a\n", s2.read)
}
end
def test_puts_empty
ssl_pair {|s1, s2|
s1.puts
s1.close
assert_equal("\n", s2.read)
}
end
end
end

Просмотреть файл

@ -83,6 +83,32 @@ module TestEOF
} }
end end
def test_eof_2
open_file("") {|f|
assert_equal("", f.read)
assert(f.eof?)
}
end
def test_eof_3
open_file("") {|f|
assert(f.eof?)
}
end
def test_eof_2
open_file("") {|f|
assert_equal("", f.read)
assert(f.eof?)
}
end
def test_eof_3
open_file("") {|f|
assert(f.eof?)
}
end
module Seek module Seek
def open_file_seek(content, pos) def open_file_seek(content, pos)
open_file(content) do |f| open_file(content) do |f|