# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/components/interface' module Contrast module Api module Communication # Wraps all connection data to speedracer class Speedracer include Contrast::Api::Communication::ServiceLifecycle include Contrast::Components::Interface access_component :contrast_service, :logging, :app_context attr_reader :status, :response_processor, :socket_client, :ensure_running def initialize @status = Contrast::Api::Communication::ConnectionStatus.new @socket_client = Contrast::Api::Communication::SocketClient.new @response_processor = Contrast::Api::Communication::ResponseProcessor.new @ensure_running = Mutex.new end def ensure_startup! return if status.connected? ensure_running.synchronize do if CONTRAST_SERVICE.use_bundled_service? logger.info('Attempting to start local service') unless attempt_local_service_startup logger.error('Failed to start local service') return end end unless status.startup_messages_sent? startup_responses = send_initialization_messages return false unless startup_responses startup_responses.each { |response| response_processor.process(response) } end end end def return_response event send_to_speedracer(event) do |response| return response end end def process_internally event send_to_speedracer(event) do |response| response_processor.process(response) end end private def send_to_speedracer event ensure_startup! logger.debug_with_time(event.cs__class.name) do response = socket_client.send_one event status.success! yield response end rescue StandardError => e status.failure! logger.error('Unable to send message.', e, event_id: event.__id__, event_type: event.cs__class.name) nil end def send_initialization_messages agent_startup_msg = APP_CONTEXT.build_agent_startup_message app_startup_msg = APP_CONTEXT.build_app_startup_message logger.debug('Preparing to send startup messages') # 1 initial attempt, + 3 potential retries. # The agent-service-api REQUIREMENTS.md spec mandates this #. 4.times do log_send_event(agent_startup_msg) next unless (agent_response = socket_client.send_one(agent_startup_msg)) # Connection was successful log_send_event(app_startup_msg) app_response = socket_client.send_one(app_startup_msg) status.success! logger.debug('Successfully sent startup messages to service.') return [agent_response, app_response] end status.failure! logger.error('Could not send agent startup message context') nil rescue StandardError => e logger.error('Could not build service context', e) status.failure! nil end def log_send_event event logger.debug('Immediately sending event.', event_id: event.__id__, event_type: event.cs__class.name) end end end end end