require 'logstash-logger' describe LogStashLogger::Device::TCP do include_context 'device' let(:tcp_socket) { double('TCPSocket') } let(:ssl_socket) { double('SSLSocket') } before(:each) do allow(TCPSocket).to receive(:new) { tcp_socket } allow(tcp_socket).to receive(:sync=) allow(OpenSSL::SSL::SSLSocket).to receive(:new) { ssl_socket } allow(ssl_socket).to receive(:connect) allow(ssl_socket).to receive(:post_connection_check) allow(ssl_tcp_device).to receive(:warn) end context "when not using SSL" do it "writes to a TCP socket" do expect(tcp_socket).to receive(:write) tcp_device.write('test') end it "returns false for #use_ssl?" do expect(tcp_device.use_ssl?).to be_falsey end it "exposes the TCP socket via #io" do expect(tcp_device.io).to eq tcp_socket end end context "when using SSL" do it "writes to an SSL TCP socket" do expect(ssl_socket).to receive(:write) ssl_tcp_device.write('test') end it "returns true for #use_ssl?" do expect(ssl_tcp_device.use_ssl?).to be_truthy end it "exposes the SSL socket via #io" do expect(ssl_tcp_device.io).to eq ssl_socket end context 'hostname validation' do let(:ssl_context) { double('test_ssl_context', verify_mode: OpenSSL::SSL::VERIFY_PEER) } let(:ssl_tcp_options) { { type: :tcp, port: port, sync: true, ssl_context: ssl_context } } context 'is enabled by default' do let(:ssl_tcp_device) { LogStashLogger::Device.new(ssl_tcp_options) } it 'validates' do expect(ssl_tcp_device.send(:verify_hostname?)).to be_truthy expect(ssl_socket).to receive(:post_connection_check).with HOST ssl_tcp_device.connect end end context 'is disabled explicitly' do let(:ssl_tcp_device) { LogStashLogger::Device.new(ssl_tcp_options.merge(verify_hostname: false)) } it 'does not validate' do expect(ssl_tcp_device.send(:verify_hostname?)).to be_falsey expect(ssl_socket).not_to receive(:post_connection_check) ssl_tcp_device.connect end end context 'is implicitly enabled by providing a hostname' do let(:hostname) { 'www.example.com' } let(:ssl_tcp_device) { LogStashLogger::Device.new(ssl_tcp_options.merge(verify_hostname: hostname)) } it 'validates with supplied hostname' do expect(ssl_socket).to receive(:post_connection_check).with hostname ssl_tcp_device.connect end end end context 'with a provided SSL context' do let(:ssl_context) { double('test_ssl_context', verify_mode: OpenSSL::SSL::VERIFY_PEER) } let(:ssl_tcp_device) { LogStashLogger::Device.new(type: :tcp, port: port, sync: true, ssl_context: ssl_context) } it 'creates the socket using that context' do expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_context) ssl_tcp_device.connect end it 'implicitly sets @use_ssl to true' do expect(ssl_tcp_device.use_ssl?).to be_truthy end context 'and :ssl_enable explicitly set to false' do let(:ssl_tcp_device) { LogStashLogger::Device.new(type: :tcp, port: port, sync: true, ssl_enable: false, ssl_context: ssl_context) } it 'explicitly sets @use_ssl to false' do expect(ssl_tcp_device.use_ssl?).to be_falsey end end end context 'without a provided SSL context' do it 'ssl_context returns nil' do expect(ssl_tcp_device.ssl_context).to be_nil end end context 'only providing a certificate file' do let(:ssl_tcp_device) { LogStashLogger::Device.new(type: :tcp, port: port, ssl_enable: true, sync: true, ssl_certificate: '/path/to/cert.pem') } it 'implicitly uses a context with the configured certificate' do expect(ssl_tcp_device.ssl_context.cert).to eq('/path/to/cert.pem') end end end end