Split WebSocket event in multiple lines before processing

This commit is contained in:
Sebastian Serth
2021-10-10 15:12:45 +02:00
parent 1403fc03c4
commit cc98dc2229
3 changed files with 22 additions and 16 deletions

View File

@ -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

View File

@ -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

View File

@ -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)