diff --git a/lib/websocket_proxy.rb b/lib/websocket_proxy.rb index 941f33c78480..1e738af8189e 100644 --- a/lib/websocket_proxy.rb +++ b/lib/websocket_proxy.rb @@ -17,7 +17,15 @@ def initialize(env, console, logger) @ws = env['rack.hijack'].call # Set up the socket client for the proxy @sock = TCPSocket.open(@console.host_name, @console.port) - init_ssl if @console.ssl + adapter = case @console.protocol + when 'vnc' + WebsocketSocket + when 'spice' + @console.ssl ? WebsocketSSLSocket : WebsocketSocket + when 'webmks' + WebsocketWebmks + end + @right = adapter.new(@sock, @console) rescue => ex @logger.error(ex) @error = true @@ -25,9 +33,7 @@ def initialize(env, console, logger) @driver.on(:open) { @console.update(:opened => true) } - @driver.on(:message) do |msg| - @ssl ? @ssl.syswrite(msg.data.pack('C*')) : @sock.write(msg.data.pack('C*')) - end + @driver.on(:message) { |msg| @right.issue(msg.data.pack('C*')) } @driver.on(:close) { cleanup } end @@ -44,8 +50,9 @@ def transmit(sockets, is_ws) data = @ws.recv_nonblock(64.kilobytes) @driver.parse(data) else - data = @ssl ? @ssl.sysread(64.kilobytes) : @sock.recv_nonblock(64.kilobytes) - @driver.binary(data) + @right.fetch(64.kilobytes) do |data| + @driver.binary(data) + end end end @@ -69,17 +76,4 @@ def write(string) def vm_id @console ? @console.vm_id : 'unknown' end - - private - - def init_ssl - context = OpenSSL::SSL::SSLContext.new - context.cert = OpenSSL::X509::Certificate.new(File.open('certs/server.cer')) - context.key = OpenSSL::PKey::RSA.new(File.open('certs/server.cer.key')) - context.ssl_version = :SSLv23 - context.verify_depth = OpenSSL::SSL::VERIFY_NONE - @ssl = OpenSSL::SSL::SSLSocket.new(@sock, context) - @ssl.sync_close = true - @ssl.connect - end end diff --git a/lib/websocket_right.rb b/lib/websocket_right.rb new file mode 100644 index 000000000000..998fab73314c --- /dev/null +++ b/lib/websocket_right.rb @@ -0,0 +1,14 @@ +class WebsocketRight + def initialize(socket, model) + @sock = socket + @model = model + end + + def fetch(*) + raise NotImplementedError, 'This should be defined in a subclass' + end + + def issue(*) + raise NotImplementedError, 'This should be defined in a subclass' + end +end diff --git a/lib/websocket_socket.rb b/lib/websocket_socket.rb new file mode 100644 index 000000000000..66cbe092e8b4 --- /dev/null +++ b/lib/websocket_socket.rb @@ -0,0 +1,9 @@ +class WebsocketSocket < WebsocketRight + def fetch(length) + yield(@sock.recv_nonblock(length)) + end + + def issue(data) + @sock.write(data) + end +end diff --git a/lib/websocket_ssl_socket.rb b/lib/websocket_ssl_socket.rb new file mode 100644 index 000000000000..0f16f34015ca --- /dev/null +++ b/lib/websocket_ssl_socket.rb @@ -0,0 +1,22 @@ +class WebsocketSSLSocket < WebsocketRight + def initialize(socket, model) + super(socket, model) + + context = OpenSSL::SSL::SSLContext.new + context.cert = OpenSSL::X509::Certificate.new(File.open('certs/server.cer')) + context.key = OpenSSL::PKey::RSA.new(File.open('certs/server.cer.key')) + context.ssl_version = :SSLv23 + context.verify_depth = OpenSSL::SSL::VERIFY_NONE + @ssl = OpenSSL::SSL::SSLSocket.new(@sock, context) + @ssl.sync_close = true + @ssl.connect + end + + def fetch(length) + yield(@ssl.sysread(length)) + end + + def issue(data) + @ssl.syswrite(data) + end +end diff --git a/lib/websocket_webmks.rb b/lib/websocket_webmks.rb new file mode 100644 index 000000000000..1c936f3340d5 --- /dev/null +++ b/lib/websocket_webmks.rb @@ -0,0 +1,30 @@ +class WebsocketWebmks < WebsocketSSLSocket + attr_accessor :url + + def initialize(socket, model) + super(socket, model) + @url = URI::Generic.build(:scheme => 'wss', + :host => @model.host_name, + :port => @model.port, + :path => "/ticket/#{@model.secret}").to_s + @driver = WebSocket::Driver.client(self, :protocols => ['binary']) + @driver.on(:close) { socket.close unless socket.closed? } + @driver.start + end + + def fetch(length) + # WebSocket::Driver requires an event handler that should be registered only once + @driver.on(:message) { |msg| yield(msg.data) } if @driver.listeners(:message).length.zero? + + data = @ssl.sysread(length) + @driver.parse(data) + end + + def issue(data) + @driver.binary(data) + end + + def write(data) + @ssl.syswrite(data) + end +end diff --git a/spec/lib/websocket_proxy_spec.rb b/spec/lib/websocket_proxy_spec.rb index 8414ff25ffb9..a63098b07ce7 100644 --- a/spec/lib/websocket_proxy_spec.rb +++ b/spec/lib/websocket_proxy_spec.rb @@ -45,14 +45,18 @@ let(:ws) { double } let(:sock) { double } + before do + subject.instance_variable_set(:@driver, driver) + subject.instance_variable_set(:@ws, ws) + subject.instance_variable_set(:@sock, sock) + right = subject.instance_variable_get(:@right) + right.instance_variable_set(:@sock, sock) + end + context 'websocket to socket' do let(:is_ws) { true } it 'reads from the websocket and parses the result' do - subject.instance_variable_set(:@driver, driver) - subject.instance_variable_set(:@ws, ws) - subject.instance_variable_set(:@sock, sock) - expect(ws).to receive(:recv_nonblock).and_return(123) expect(driver).to receive(:parse).with(123) @@ -64,10 +68,6 @@ let(:is_ws) { false } it 'reads from the socket and sends the result to the driver' do - subject.instance_variable_set(:@driver, driver) - subject.instance_variable_set(:@ws, ws) - subject.instance_variable_set(:@sock, sock) - expect(sock).to receive(:recv_nonblock).and_return(123) expect(driver).to receive(:binary).with(123)