lib/anycable/rails/actioncable/connection.rb in anycable-rails-0.6.5 vs lib/anycable/rails/actioncable/connection.rb in anycable-rails-1.0.0.preview1

- old
+ new

@@ -1,23 +1,26 @@ # frozen_string_literal: true require "action_cable/connection" require "anycable/rails/refinements/subscriptions" require "anycable/rails/actioncable/channel" +require "anycable/rails/session_proxy" module ActionCable module Connection # rubocop: disable Metrics/ClassLength class Base # :nodoc: - # We store logger tags in identifiers to be able + # We store logger tags in the connection state to be able # to re-use them in the subsequent calls LOG_TAGS_IDENTIFIER = "__ltags__" using AnyCable::Refinements::Subscriptions attr_reader :socket + delegate :env, :session, to: :request + class << self def call(socket, **options) new(socket, options) end @@ -32,14 +35,16 @@ end def initialize(socket, identifiers: "{}", subscriptions: []) @ids = ActiveSupport::JSON.decode(identifiers) - @ltags = ids.delete(LOG_TAGS_IDENTIFIER) + @ltags = socket.cstate.read(LOG_TAGS_IDENTIFIER).yield_self do |raw_tags| + next unless raw_tags + ActiveSupport::JSON.decode(raw_tags) + end @cached_ids = {} - @env = socket.env @coder = ActiveSupport::JSON @socket = socket @subscriptions = ActionCable::Connection::Subscriptions.new(self) # Initialize channels if any @@ -47,16 +52,21 @@ end def handle_open logger.info started_request_message if access_logs? - verify_origin! + verify_origin! || return connect if respond_to?(:connect) + + socket.cstate.write(LOG_TAGS_IDENTIFIER, fetch_ltags.to_json) + send_welcome_message rescue ActionCable::Connection::Authorization::UnauthorizedError - reject_request + reject_request( + ActionCable::INTERNAL[:disconnect_reasons]&.[](:unauthorized) || "unauthorized" + ) end def handle_close logger.info finished_request_message if access_logs? @@ -82,24 +92,27 @@ false end end # rubocop:enable Metrics/MethodLength - def close + def close(reason: nil, reconnect: nil) + transmit( + type: ActionCable::INTERNAL[:message_types].fetch(:disconnect, "disconnect"), + reason: reason, + reconnect: reconnect + ) socket.close end def transmit(cable_message) socket.transmit encode(cable_message) end # Generate identifiers info. # Converts GlobalID compatible vars to corresponding global IDs params. def identifiers_hash - obj = { LOG_TAGS_IDENTIFIER => fetch_ltags } - - identifiers.each_with_object(obj) do |id, acc| + identifiers.each_with_object({}) do |id, acc| obj = instance_variable_get("@#{id}") next unless obj acc[id] = obj.try(:to_gid_param) || obj end.compact @@ -121,10 +134,14 @@ def logger @logger ||= TaggedLoggerProxy.new(AnyCable.logger, tags: ltags || []) end + def request + @request ||= build_rack_request + end + private attr_reader :ids, :ltags def started_request_message @@ -156,22 +173,36 @@ def server ActionCable.server end def verify_origin! - return unless socket.env.key?("HTTP_ORIGIN") + return true unless socket.env.key?("HTTP_ORIGIN") - return if allow_request_origin? + return true if allow_request_origin? - raise( - ActionCable::Connection::Authorization::UnauthorizedError, - "Origin is not allowed" + reject_request( + ActionCable::INTERNAL[:disconnect_reasons]&.[](:invalid_request) || "invalid_request" ) + false end - def reject_request + def reject_request(reason, reconnect = false) logger.info finished_request_message("Rejected") if access_logs? - close + close( + reason: reason, + reconnect: reconnect + ) + end + + def build_rack_request + environment = Rails.application.env_config.merge(socket.env) + AnyCable::Rails::Rack.app.call(environment) + + ActionDispatch::Request.new(environment) + end + + def request_loaded? + instance_variable_defined?(:@request) end end # rubocop:enable Metrics/ClassLength end end