# Copyright (C) 2014, all contributors # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt) require_relative 'server_helper' require 'openssl' class TestSSL < Testcase ENV["N"].to_i > 1 and parallelize_me! include ServerHelper r, w = IO.pipe FAST_NB = begin :wait_readable == r.read_nonblock(1, exception: false) rescue false end r.close w.close # copied from test/openssl/utils.rb in Ruby: TEST_KEY_DH1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_ -----BEGIN DH PARAMETERS----- MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0 pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC -----END DH PARAMETERS----- _end_of_pem_ TEST_KEY_DH1024.priv_key = OpenSSL::BN.new("48561834C67E65FFD2A9B47F41" \ "E5E78FDC95C387428FDB1E4B0188B64D1643C3A8D3455B945B7E8C4D166010C7C2" \ "CE23BFB9BEF43D0348FE7FA5284B0225E7FE1537546D114E3D8A4411B9B9351AB4" \ "51E1A358F50ED61B1F00DA29336EEBBD649980AC86D76AF8BBB065298C2052672E" \ "EF3EF13AB47A15275FC2836F3AC74CEA", 16) def setup unless FAST_NB skip "missing exception-free non-blocking IO in " \ "#{RUBY_ENGINE} #{RUBY_VERSION}" end server_helper_setup end def teardown server_helper_teardown end def ssl_client(host, port) ctx = OpenSSL::SSL::SSLContext.new ctx.ciphers = "ADH" s = TCPSocket.new(host, port) ssl = OpenSSL::SSL::SSLSocket.new(s, ctx) ssl.connect ssl.sync_close = true ssl end def srv_ctx ctx = OpenSSL::SSL::SSLContext.new ctx.ciphers = "ADH" ctx.tmp_dh_callback = proc { TEST_KEY_DH1024 } ctx end def test_ssl_basic err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1] ctx = srv_ctx pid = mkserver(cfg) do cfg.instance_eval do ru = lambda { |_| [ 200, {'Content-Length'=>'2'}, ['HI'] ] } app(:rack, ru) { listen "#{host}:#{port}", ssl_ctx: ctx } logger(Logger.new(err.path)) end end client = ssl_client(host, port) client.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") buf = '' Timeout.timeout(60) do buf << client.readpartial(111) until buf =~ /HI\z/ end head, body = buf.split("\r\n\r\n", 2) assert_equal "HI", body assert_match %r{\AHTTP/1\.\d 200 OK\r\n}, head client.write("GET / HTTP/1.0\r\n\r\n") head, body = client.read.split("\r\n\r\n", 2) assert_equal "HI", body assert_match %r{\AHTTP/1\.\d 200 OK\r\n}, head ensure client.close if client quit_wait(pid) end def test_ssl_hijack err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1] ctx = srv_ctx pid = mkserver(cfg) do cfg.instance_eval do ru = lambda do |env| io = env['rack.hijack'].call Thread.new(io) do |s| s.write "HTTP/1.1 201 Switching Protocols\r\n\r\n" case req = s.gets when "inspect\n" s.puts(s.instance_variable_get(:@ssl).inspect) when "remote_address\n" s.puts(s.remote_address.inspect) when "each\n" line = '' s.each do |l| l.strip! line << l break if l == 'd' end s.puts line when "missing\n" begin s.any_old_invalid_test_method s.puts "FAIL" rescue => e s.puts "#{e.class}: #{e.message}" end when nil s.close else p [ :ERR, req ] end until s.closed? end [ 200, DieIfUsed, DieIfUsed ] end app(:rack, ru) { listen "#{host}:#{port}", ssl_ctx: ctx } logger(Logger.new(err.path)) end end client = ssl_client(host, port) client.write("GET / HTTP/1.0\r\n\r\n") Timeout.timeout(60) do assert_equal "HTTP/1.1 201 Switching Protocols\r\n", client.gets assert_equal "\r\n", client.gets client.puts "inspect" assert_match %r{SSLSocket}, client.gets client.puts "remote_address" assert_equal client.to_io.local_address.inspect, client.gets.strip client.puts "missing" assert_match %r{NoMethodError}, client.gets client.puts "each" %w(a b c d).each { |x| client.puts(x) } assert_equal "abcd", client.gets.strip end ensure client.close if client quit_wait(pid) end end if defined?(OpenSSL)