From cc98dc22298de0e5fe8e4fbee504af752f054563 Mon Sep 17 00:00:00 2001 From: Sebastian Serth Date: Sun, 10 Oct 2021 15:12:45 +0200 Subject: [PATCH] Split WebSocket event in multiple lines before processing --- lib/runner/connection.rb | 24 ++++++++++++-------- lib/runner/strategy/docker_container_pool.rb | 10 ++++---- lib/runner/strategy/poseidon.rb | 4 ++-- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/runner/connection.rb b/lib/runner/connection.rb index 659d769f..bca9cfea 100644 --- a/lib/runner/connection.rb +++ b/lib/runner/connection.rb @@ -78,16 +78,22 @@ class Runner::Connection def on_message(raw_event) Rails.logger.debug { "#{Time.zone.now.getutc}: Receiving from #{@socket.url}: #{raw_event.data.inspect}" } - event = decode(raw_event) - return unless BACKEND_OUTPUT_SCHEMA.valid?(event) + # The WebSocket connection might group multiple lines. For further processing, we require all lines + # to be processed separately. Therefore, we split the lines by each newline character not part of an enclosed + # substring either in single or double quotes (e.g., within a JSON) + # Inspired by https://stackoverflow.com/questions/13040585/split-string-by-spaces-properly-accounting-for-quotes-and-backslashes-ruby + raw_event.data.scan(/(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*'|[^"\n])+/).each do |event_data| + event = decode(event_data) + next unless BACKEND_OUTPUT_SCHEMA.valid?(event) - event = event.deep_symbolize_keys - message_type = event[:type].to_sym - if WEBSOCKET_MESSAGE_TYPES.include?(message_type) - __send__("handle_#{message_type}", event) - else - @error = Runner::Error::UnexpectedResponse.new("Unknown WebSocket message type: #{message_type}") - close(:error) + event = event.deep_symbolize_keys + message_type = event[:type].to_sym + if WEBSOCKET_MESSAGE_TYPES.include?(message_type) + __send__("handle_#{message_type}", event) + else + @error = Runner::Error::UnexpectedResponse.new("Unknown WebSocket message type: #{message_type}") + close(:error) + end end end diff --git a/lib/runner/strategy/docker_container_pool.rb b/lib/runner/strategy/docker_container_pool.rb index bddf75af..aae831cd 100644 --- a/lib/runner/strategy/docker_container_pool.rb +++ b/lib/runner/strategy/docker_container_pool.rb @@ -121,9 +121,9 @@ class Runner::Strategy::DockerContainerPool < Runner::Strategy "#{data}\n" end - def decode(raw_event) - case raw_event.data - when /@#{@strategy.container_id[0..11]}/ + def decode(event_data) + case event_data + when /(@#{@strategy.container_id[0..11]}|#exit)/ # Assume correct termination for now and return exit code 0 # TODO: Can we use the actual exit code here? @exit_code = 0 @@ -135,11 +135,11 @@ class Runner::Strategy::DockerContainerPool < Runner::Strategy when /\*\*\*\*\*\*\*\*\*\*\*\*\* Module/ # Identification of PyLint output, change stream back to stdout and return event @stream = 'stdout' - {'type' => @stream, 'data' => raw_event.data} + {'type' => @stream, 'data' => event_data} when /#{@strategy.command}/ when /bash: cmd:canvasevent: command not found/ else - {'type' => @stream, 'data' => raw_event.data} + {'type' => @stream, 'data' => event_data} end end end diff --git a/lib/runner/strategy/poseidon.rb b/lib/runner/strategy/poseidon.rb index 90699400..7eeef0aa 100644 --- a/lib/runner/strategy/poseidon.rb +++ b/lib/runner/strategy/poseidon.rb @@ -151,8 +151,8 @@ class Runner::Strategy::Poseidon < Runner::Strategy end class Connection < Runner::Connection - def decode(raw_event) - JSON.parse(raw_event.data) + def decode(event_data) + JSON.parse(event_data) rescue JSON::ParserError => e @error = Runner::Error::UnexpectedResponse.new("The WebSocket message from Poseidon could not be decoded to JSON: #{e.inspect}") close(:error)