anycable / anycable-rails

AnyCable for Ruby on Rails applications
https://anycable.io
MIT License
499 stars 35 forks source link

request.params is empty, unable to read query parameters #158

Closed x8BitRain closed 3 years ago

x8BitRain commented 3 years ago

Tell us about your environment

Ruby version: 2.6.6

Rails version: 6.0.3

anycable gem version: 0.6.5

anycable-rails gem version: 0.6.5

grpc gem version: 1.28.0

What did you do?

Connected to an anycable socket URL with query parameters.

What did you expect to happen?

Expected to be able to read the query parameters in connection.rb via request.params

What actually happened?

request.params is empty


Hey, I have this issue only in production mode. When developing locally the issue doesn't occur.

When trying to connect to wss://api.test.to:8443/live?uid=yyy&token=xxx

I am usually able to read request.params[:token] with my authentication code:

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private

    def find_verified_user
      auth_header = request.params[:token]
      uid_header = request.params[:uid]

      logger.debug(request.params[:token]) # logs nothing
      logger.debug(request.to_yaml) # logs request object with no parameters included.

      begin
        decoded = JsonWebToken.decode(auth_header) 
      rescue JWT::VerificationError, JWT::ExpiredSignature, JWT::DecodeError => error_string
        logger.debug("AUTH ERROR: " + error_string.to_s)
        return reject_unauthorized_connection # If decoding the JWT failed, reject auth.
      end

      user_decoded = User.find(decoded[:user_id])

      if user_decoded.id === uid_header
        return user_decoded
      else
        return reject_unauthorized_connection
      end
    end

  end
end
version: '3.8'

services:
  app:
    image: app-backend
    build:
      context: .
      dockerfile: Dockerfile
    command: bundle exec rails s -b 0.0.0.0
    depends_on:
      - database
      - redis
    volumes:
      - .:/app:cached
      - gem_cache:/usr/local/bundle/gems:cached
    env_file: production.env
    environment:
      RAILS_ENV: production

  database:
    image: postgres:13-alpine
    ports:
      - '5432:5432'
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:delegated
    healthcheck:
      test: pg_isready -U postgres -h 127.0.0.1
      interval: 5s

  redis:
    image: redis:alpine
    volumes:
      - redis_data:/data
    ports:
      - 6379
    healthcheck:
      test: redis-cli ping
      interval: 1s
      timeout: 3s
      retries: 30

  rpc:
    entrypoint: ["bundle", "exec", "anycable"]
    build:
      context: .
    volumes:
      - .:/app:cached
      - gem_cache:/usr/local/bundle/gems:cached
    env_file: production.env
    environment:
      RAILS_ENV: production
      ANYCABLE_REDIS_URL: redis://redis:6379/0
      ANYCABLE_RPC_HOST: 0.0.0.0:50051
      ANYCABLE_DEBUG: 0
    depends_on:
      app:
        condition: service_started
      database:
        condition: service_healthy
      redis:
        condition: service_healthy

  anycable:
    image: anycable/anycable-go:latest-alpine
    entrypoint: ["anycable-go", "-ssl_cert=/var/ssl/certbot/conf/live/api.pasta.to/fullchain.pem", "-ssl_key=/var/ssl/certbot/conf/live/api.pasta.to/privkey.pem", "--path=/live", "--log_level=debug", "--debug"]
    ports:
      - '8443:8443'
    environment:
      ANYCABLE_HOST: "0.0.0.0"
      ANYCABLE_PORT: 8443
      ANYCABLE_REDIS_URL: redis://redis:6379/0
      ANYCABLE_RPC_HOST: rpc:50051
      ANYCABLE_DEBUG: 1
    volumes:
      - ./ssl:/var/ssl
    depends_on:
      rpc:
        condition: service_started
      app:
        condition: service_started

  nginx:
    image: ymuski/nginx-quic:latest
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
    volumes:
      - ./ssl:/var/ssl
      - ./nginx/prod/nginx.conf:/etc/nginx/nginx.conf
      - ./public:/var/web
    ports:
      - 80:80
      - 443:443
    depends_on:
      app:
        condition: service_started

volumes:
  gem_cache:
  db_data:
  redis_data:
  ssl_root:

production.rb:

Rails.application.configure do
  config.cache_classes = true

  config.eager_load = true

  config.consider_all_requests_local = true

  config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?

  config.active_storage.service = :local

  config.action_cable.url = 'wss://api.test.to:8443/live'

  config.action_cable.disable_request_forgery_protection = true

  config.log_level = :debug

  config.logger = Logger.new(STDOUT)

  config.log_tags = [ :request_id ]

  config.cache_store = :redis_cache_store, {driver: :hiredis, url: "redis://redis:6379/2"}

  config.action_mailer.perform_caching = false

  config.i18n.fallbacks = true

  config.active_support.deprecation = :notify

  config.log_formatter = ::Logger::Formatter.new

  if ENV["RAILS_LOG_TO_STDOUT"].present?
    logger           = ActiveSupport::Logger.new(STDOUT)
    logger.formatter = config.log_formatter
    config.logger    = ActiveSupport::TaggedLogging.new(logger)
  end

  config.active_record.dump_schema_after_migration = false
  config.active_record.verbose_query_logs = true
end

But only in production does reject_unauthorized_connection get returned because request.params is empty.

image

Not sure if this is user error or a bug but it's very strange that this happens only on production. Is there anything that a production configuration could cause anycable to not parse request.params? Thanks!

x8BitRain commented 3 years ago

I was able to resolve this by upgrading to the latest version of anycable-rails.