require 'spec_helper' require 'openssl' describe OpenSSL::SSL::SSLSocket, :if => RUBY_VERSION >= "1.9.0" do let(:tcp_port) { 34567 } let(:ssl_key) { OpenSSL::PKey::RSA.new(1024) } let(:ssl_cert) do name = OpenSSL::X509::Name.new([%w[CN localhost]]) OpenSSL::X509::Certificate.new.tap do |cert| cert.version = 2 cert.serial = 1 cert.issuer = name cert.subject = name cert.not_before = Time.now cert.not_after = Time.now + (365 * 24 *60 *60) cert.public_key = ssl_key.public_key cert.sign(ssl_key, OpenSSL::Digest::SHA1.new) end end let(:ssl_server_context) do OpenSSL::SSL::SSLContext.new.tap do |ctx| ctx.cert = ssl_cert ctx.key = ssl_key end end let :readable_subject do server = TCPServer.new("localhost", tcp_port) client = TCPSocket.open("localhost", tcp_port) peer = server.accept ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context) ssl_peer.sync_close = true ssl_client = OpenSSL::SSL::SSLSocket.new(client) ssl_client.sync_close = true # SSLSocket#connect and #accept are blocking calls. thread = Thread.new { ssl_client.connect } ssl_peer.accept ssl_peer << "data" thread.join pending "Failed to produce a readable SSL socket" unless select([ssl_client], [], [], 0) ssl_client end let :unreadable_subject do server = TCPServer.new("localhost", tcp_port + 1) client = TCPSocket.new("localhost", tcp_port + 1) peer = server.accept ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context) ssl_peer.sync_close = true ssl_client = OpenSSL::SSL::SSLSocket.new(client) ssl_client.sync_close = true # SSLSocket#connect and #accept are blocking calls. thread = Thread.new { ssl_client.connect } ssl_peer.accept thread.join pending "Failed to produce an unreadable socket" if select([ssl_client], [], [], 0) ssl_client end let :writable_subject do server = TCPServer.new("localhost", tcp_port + 2) client = TCPSocket.new("localhost", tcp_port + 2) peer = server.accept ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context) ssl_peer.sync_close = true ssl_client = OpenSSL::SSL::SSLSocket.new(client) ssl_client.sync_close = true # SSLSocket#connect and #accept are blocking calls. thread = Thread.new { ssl_client.connect } ssl_peer.accept thread.join ssl_client end let :unwritable_subject do server = TCPServer.new("localhost", tcp_port + 3) client = TCPSocket.open("localhost", tcp_port + 3) peer = server.accept ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context) ssl_peer.sync_close = true ssl_client = OpenSSL::SSL::SSLSocket.new(client) ssl_client.sync_close = true # SSLSocket#connect and #accept are blocking calls. thread = Thread.new { ssl_client.connect } ssl_peer.accept thread.join begin _, writers = select [], [ssl_client], [], 0 count = ssl_client.write_nonblock "X" * 1024 expect(count).not_to eq(0) rescue IO::WaitReadable, IO::WaitWritable pending "SSL will report writable but not accept writes" raise if(writers.include? ssl_client) end while writers and writers.include? ssl_client # I think the kernel might manage to drain its buffer a bit even after # the socket first goes unwritable. Attempt to sleep past this and then # attempt to write again sleep 0.1 # Once more for good measure! begin # ssl_client.write_nonblock "X" * 1024 loop { ssl_client.write_nonblock "X" * 1024 } rescue OpenSSL::SSL::SSLError end # Sanity check to make sure we actually produced an unwritable socket # if select([], [ssl_client], [], 0) # pending "Failed to produce an unwritable socket" # end ssl_client end let :pair do pending "figure out why newly created sockets are selecting readable immediately" server = TCPServer.new("localhost", tcp_port + 4) client = TCPSocket.open("localhost", tcp_port + 4) peer = server.accept ssl_peer = OpenSSL::SSL::SSLSocket.new(peer, ssl_server_context) ssl_peer.sync_close = true ssl_client = OpenSSL::SSL::SSLSocket.new(client) ssl_client.sync_close = true # SSLSocket#connect and #accept are blocking calls. thread = Thread.new { ssl_client.connect } ssl_peer.accept [thread.value, ssl_peer] end it_behaves_like "an NIO selectable" it_behaves_like "an NIO selectable stream" end