anycable-rails 0.6.3 → 1.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -100
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +37 -36
  5. data/lib/action_cable/subscription_adapter/any_cable.rb +2 -1
  6. data/lib/anycable/rails.rb +37 -2
  7. data/lib/anycable/rails/actioncable/channel.rb +7 -0
  8. data/lib/anycable/rails/actioncable/connection.rb +70 -50
  9. data/lib/anycable/rails/actioncable/connection/persistent_session.rb +30 -0
  10. data/lib/anycable/rails/actioncable/connection/serializable_identification.rb +42 -0
  11. data/lib/anycable/rails/actioncable/remote_connections.rb +11 -0
  12. data/lib/anycable/rails/channel_state.rb +48 -0
  13. data/lib/anycable/rails/compatibility.rb +10 -11
  14. data/lib/anycable/rails/compatibility/rubocop.rb +0 -1
  15. data/lib/anycable/rails/compatibility/rubocop/config/default.yml +3 -1
  16. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/instance_vars.rb +1 -1
  17. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/stream_from.rb +4 -4
  18. data/lib/anycable/rails/config.rb +8 -4
  19. data/lib/anycable/rails/rack.rb +56 -0
  20. data/lib/anycable/rails/railtie.rb +17 -10
  21. data/lib/anycable/rails/refinements/subscriptions.rb +5 -0
  22. data/lib/anycable/rails/session_proxy.rb +79 -0
  23. data/lib/anycable/rails/version.rb +1 -1
  24. data/lib/generators/anycable/download/USAGE +14 -0
  25. data/lib/generators/anycable/download/download_generator.rb +77 -0
  26. data/lib/generators/anycable/setup/USAGE +2 -0
  27. data/lib/generators/anycable/setup/setup_generator.rb +246 -0
  28. data/lib/generators/anycable/setup/templates/Procfile.dev +3 -0
  29. data/lib/generators/anycable/setup/templates/config/anycable.yml.tt +43 -0
  30. data/lib/generators/anycable/setup/templates/config/cable.yml.tt +11 -0
  31. data/lib/generators/anycable/setup/templates/config/initializers/anycable.rb.tt +9 -0
  32. data/lib/generators/anycable/with_os_helpers.rb +55 -0
  33. metadata +48 -43
  34. data/lib/anycable/rails/compatibility/rubocop/cops/anycable/remote_disconnect.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12096ddf13bcb4893853e537017bc5478c8c65c7f8aea4a21af221d5e4cf1ce3
4
- data.tar.gz: 252d1fc76c09a902229b192afd61c020503a78bf773d53a8dc2e409e1a984310
3
+ metadata.gz: 36b6e47a498bfc2d1156e4635a5c30f60258c925142c59ad837c1b6708f1ff73
4
+ data.tar.gz: f27c94856df0db9d178537e8ac75598f8a355b338e725a80a5cb06a6e0ac610c
5
5
  SHA512:
6
- metadata.gz: 951b2d8c75b50dd632c7e7e49b36bf8c233d5c9d7335e0213aa5c291d4cf17bb7c9999f9bdf54bcdd2a29753d471b5b6ed3397e3403b07f7e7dba7f2d3e32a37
7
- data.tar.gz: e1df94db573c5557784937b2a44152e2e1cb59f0fd00d8532bd539e5fe8cf80a1abbf2dd605cb3025e622fddd23f59bdeb652256859d6f41b7b4462967e190c7
6
+ metadata.gz: 28b72e3f6a9950d3a95eb8d4f1c6a59f24a704ff63ac50384ffb2d22aa97c9f6b6e1f0c882a89cc6a0fd9ac22ea553a381514d56885f6f1cce41e7146153ab24
7
+ data.tar.gz: ac7c402d2b57a332ced9372a4546709d6b41a0fb082c823d292b87c8b11e4bf741047cbc10258f68acd228712f0ca974b6723246bd3ddc689920f656b1488974
@@ -1,124 +1,44 @@
1
1
  # Change log
2
2
 
3
- ## master
3
+ ## 1.0.0.rc1 (2020-06-10)
4
4
 
5
- ## 0.6.3 (2019-03-26)
5
+ - Add `state_attr_accessor` for channels. ([@palkan][])
6
6
 
7
- - Fix connection factory reloading for development sake. ([@sponomarev][])
7
+ Just like `attr_accessor` but "persists" the state between RPC calls.
8
8
 
9
- - Add `:anycable` subscription adapter alias. ([@sponomarev][])
9
+ - Add `Channel#stop_stream_from` support. ([@palkan][])
10
10
 
11
- - Don't set AnyCable connection factory for incompatible adapter ([@sponomarev][])
11
+ - Add `RemoteConnections` support. ([@palkan][])
12
12
 
13
- `anycable` server won't start with unpatched vanilla `ApplicationCable::Connection`.
13
+ - Add `AnyCable::Rails.enabled?` method which returns true if Action Cable uses AnyCable adapter. ([@palkan][])
14
14
 
15
- Motivation in [#74](https://github.com/anycable/anycable-rails/issues/74).
15
+ - Add `anycable:download` generator to download `anycable-go` binary. ([@palkan][])
16
16
 
17
- - Fix instance detection inside complex cases in compatibility cops ([@sponomarev][])
17
+ - **Ruby 2.5+ is required**. ([@palkan][])
18
18
 
19
- ## 0.6.2 (2019-01-10)
19
+ - Support `disconnect` messages. ([@palkan][])
20
20
 
21
- - Fixed `anycable` 0.6.1 compatibility. ([@palkan][])
21
+ Added in Rails 6 (see [PR#34194](https://github.com/rails/rails/pull/34194)).
22
22
 
23
- - Broadcast logs to STDOUT in development only when server is running. ([@palkan][])
23
+ - Add ability to persist _dirty_ `request.session` between RPC calls. ([@palkan][])
24
24
 
25
- Fixes #59.
25
+ This feature emulates the Action Cable behaviour where it's possible to use `request.session` as a shared Hash-like store.
26
+ This could be used by some applications (e.g., [StimulusReflex](https://github.com/hopsoft/stimulus_reflex)-based).
26
27
 
27
- ## 0.6.1 (2018-11-15)
28
+ You must turn this feature on by setting `persistent_session_enabled: true` in the AnyCable configuration.
28
29
 
29
- - Fix regression introduced in [e64a366e](https://github.com/anycable/anycable-rails/commit/e64a366ea21293925e0c5c0b8e6595d65d5d0981#diff-fd0e56a6e825002eac978507c3581af7R14) ([@palkan][])
30
+ - Add ability to use Rack middlewares when build a request for a connection. ([@bibendi][])
30
31
 
31
- `Connection` patch could be loaded after `identify_by` is called, thus breaking
32
- identifiers.
32
+ - Add set up generator to configure a Rails application by running `bin/rails g anycable:setup`. ([@bibendi][])
33
33
 
34
- ## 0.6.0 (2018-11-15)
34
+ - Require a minimum version of Ruby when installing the gem. ([@bibendi][])
35
35
 
36
- **NOTE**: this version has been yanked from RubyGems due to the regression bug. Use 0.6.1.
36
+ - Add ability to develop the gem with Docker. ([@bibendi][])
37
37
 
38
- - [PR #56](https://github.com/anycable/anycable-rails/pull/56) Request verification based on ActionCable config. ([@DmitryTsepelev][])
39
-
40
- - Add WS server session ID to log tags if present. ([@palkan][])
41
-
42
- - Support tagged logging. ([@palkan][])
43
-
44
- - Action Cable monkey-patches are only loaded in the context of AnyCable CLI. ([@palkan][])
45
-
46
- No need to think about `requie` and `group` for `anycable-rails`, just add it to Gemfile.
47
-
48
- - Add `:any_cable` subscription adapter for Action Cable. ([@palkan][])
49
-
50
- Use `:any_cable` adapter for Action Cable to broadcast data to AnyCable.
51
-
52
- No more `pubsub` monkey-patches 🎉.
53
-
54
- - Added Rails executor/reloader support. ([@palkan][])
55
-
56
- - **[Breaking]** No more generators. ([@palkan][])
57
-
58
- No need to generate AnyCable runner script since `anycable` gem ships with
59
- the CLI.
60
-
61
- - Add dynamic (`AnyCable::CompatibilityError`) compatibility checks. ([@DmitryTsepelev][])
62
-
63
- - Added static (RuboCop) compatibility checks. ([@DmitryTsepelev][])
64
-
65
- See https://github.com/anycable/anycable-rails/issues/52
66
-
67
- ## 0.5.4 (2018-06-13)
68
-
69
- - Fix duplicate logs in development. ([@palkan][])
70
-
71
- Fixes https://github.com/anycable/anycable_demo/issues/5.
72
-
73
- ## 0.5.3
74
-
75
- - Fix return value of `Connection#handle_close`. ([@palkan][])
76
-
77
- Should always be `true`, we do not expect a failure here.
78
-
79
- ## 0.5.2
80
-
81
- - Add config/anycable.yml to Rails generator. ([@alekseyl][])
82
-
83
- ## 0.5.1
84
-
85
- - Improve Rails integration. ([@palkan][])
86
-
87
- Log to STDOUT in development.
88
- Make order of initializers more deterministic.
89
- Show warning if AnyCable is loaded after application initialization.
90
-
91
- ## 0.5.0
92
-
93
- - [#17](https://github.com/anycable/anycable-rails/issues/17) Refactor logging. ([@palkan][])
94
-
95
- Use Rails logger everywhere.
96
-
97
- Add access logs ([anycable/anycable#20](https://github.com/anycable/anycable/issues/20)).
98
-
99
- ## 0.4.7
100
-
101
- - Minor fixes. ([@palkan][])
102
-
103
- ## 0.4.6
104
-
105
- - Disable mounting default Action Cable server when AnyCable is loaded. ([@palkan][])
106
-
107
- ## 0.4.5
108
-
109
- - Handle tagged logger. ([@palkan][])
110
-
111
- Ignore tagged logger features ('cause we do not have _persistent_ logger).
112
-
113
- ## 0.4.4
114
-
115
- - Fix bug with ActiveRecord connections (https://github.com/anycable/anycable/issues/9). ([@palkan][])
116
-
117
- ## 0.4.0
118
-
119
- - Initial version. ([@palkan][])
38
+ See [Changelog](https://github.com/anycable/anycable-rails/blob/0-6-stable/CHANGELOG.md) for versions <1.0.0.
120
39
 
121
40
  [@palkan]: https://github.com/palkan
122
41
  [@alekseyl]: https://github.com/alekseyl
123
42
  [@DmitryTsepelev]: https://github.com/DmitryTsepelev
124
43
  [@sponomarev]: https://github.com/sponomarev
44
+ [@bibendi]: https://github.com/bibendi
@@ -1,4 +1,4 @@
1
- Copyright 2017 palkan
1
+ Copyright 2017-2020 palkan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
- [![GitPitch](https://gitpitch.com/assets/badge.svg)](https://gitpitch.com/anycable/anycable/master?grs=github) [![Gem Version](https://badge.fury.io/rb/anycable-rails.svg)](https://rubygems.org/gems/anycable-rails) [![Build Status](https://travis-ci.org/anycable/anycable-rails.svg?branch=master)](https://travis-ci.org/anycable/anycable-rails)
1
+ [![GitPitch](https://gitpitch.com/assets/badge.svg)](https://gitpitch.com/anycable/anycable/master?grs=github)
2
+ [![Gem Version](https://badge.fury.io/rb/anycable-rails.svg)](https://rubygems.org/gems/anycable-rails)
3
+ [![Build](https://github.com/anycable/anycable-rails/workflows/Build/badge.svg)](https://github.com/anycable/anycable-rails/actions)
2
4
  [![Gitter](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/anycable/Lobby)
3
- [![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](https://docs.anycable.io/#using_with_rails)
5
+ [![Documentation](https://img.shields.io/badge/docs-link-brightgreen.svg)](https://docs.anycable.io/v1/#/ruby/rails)
4
6
 
5
7
  # AnyCable Rails
6
8
 
@@ -10,27 +12,23 @@ With AnyCable you can use channels, client-side JS, broadcasting - (almost) all
10
12
 
11
13
  You can even use Action Cable in development and not be afraid of [compatibility issues](#compatibility).
12
14
 
13
- 💾 [Example Application](https://github.com/anycable/anycable_demo)
15
+ **Important** This is a readme for the upcoming v1.0 release. For v0.6.x see the readme from the [0-6-stable](https://github.com/anycable/anycable-rails/tree/0-6-stable) branch.
14
16
 
15
- 📑 [Documentation](https://docs.anycable.io).
17
+ <!-- 💾 [Example Application](https://github.com/anycable/anycable_demo) -->
16
18
 
19
+ 📑 [Documentation](https://docs.anycable.io/v1/#/ruby/rails).
17
20
 
18
21
  <a href="https://evilmartians.com/">
19
22
  <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
20
23
 
21
24
  ## Requirements
22
25
 
23
- - Ruby ~> 2.4; **NOTE:** for Ruby 2.6 use RC version of `google-protobuf` gem. In your Gemfile: `gem "google-protobuf", '>=3.7.0.rc.2'`
24
- - Rails ~> 5.0;
25
- - Redis (see [other options]() for broadcasting)
26
-
27
- ## How It Works?
28
-
29
- <img src="https://trello-attachments.s3.amazonaws.com/5781e0ed48e4679e302833d3/820x987/5b6a305417b04e20e75f49c5816e027c/Anycable_vs_ActionCable_copy.jpg" width="400" />
26
+ - Ruby >= 2.5
27
+ - Rails >= 5.2
28
+ - Redis (see [other options](https://github.com/anycable/anycable/issues/2) for broadcasting)
30
29
 
31
30
  ## Usage
32
31
 
33
-
34
32
  Add `anycable-rails` gem to your Gemfile:
35
33
 
36
34
  ```ruby
@@ -40,14 +38,25 @@ gem "anycable-rails"
40
38
  gem "redis", ">= 4.0"
41
39
  ```
42
40
 
43
- (and don't forget to run `bundle install`).
41
+ ### Interactive set up
44
42
 
45
- Next, specify AnyCable subscription adapter for Action Cable:
43
+ After the gem was installed, you can run an interactive wizard to configure your Rails application for using with AnyCable by running a generator:
44
+
45
+ ```sh
46
+ bundle exec rails g anycable:setup
47
+ ```
48
+
49
+ ### Manual set up
50
+
51
+ Specify AnyCable subscription adapter for Action Cable:
46
52
 
47
53
  ```yml
48
54
  # config/cable.yml
49
- production:
55
+ development:
50
56
  adapter: any_cable # or anycable
57
+
58
+ production:
59
+ adapter: any_cable
51
60
  ```
52
61
 
53
62
  and specify AnyCable WebSocket server URL:
@@ -64,9 +73,9 @@ config.action_cable.url = "ws://localhost:3334/cable"
64
73
  config.action_cable.url = "wss://ws.example.com/cable"
65
74
  ```
66
75
 
67
- hen, run AnyCable RPC server:
76
+ Then, run AnyCable RPC server:
68
77
 
69
- ```ruby
78
+ ```sh
70
79
  $ bundle exec anycable
71
80
 
72
81
  # don't forget to provide Rails env
@@ -74,38 +83,30 @@ $ bundle exec anycable
74
83
  $ RAILS_ENV=production bundle exec anycable
75
84
  ```
76
85
 
77
- And, finally, run AnyCable WebSocket server, e.g. [anycable-go](https://docs.anycable.io/#go_getting_started.md):
86
+ And, finally, run AnyCable WebSocket server, e.g. [anycable-go](https://docs.anycable.io/v1/#/anycable-go/getting_started):
78
87
 
79
88
  ```sh
80
89
  anycable-go --host=localhost --port=3334
81
90
  ```
82
91
 
83
- See [documentation](https://docs.anycable.io/#using_with_rails) for more information on AnyCable + Rails usage.
92
+ See [documentation](https://docs.anycable.io/v1/#/ruby/rails) for more information on AnyCable + Rails usage.
84
93
 
85
94
  ## Action Cable Compatibility
86
95
 
87
- See [documentation](https://docs.anycable.io/#compatibility).
88
-
89
- ## Links
96
+ See [documentation](https://docs.anycable.io/v1/#/ruby/compatibility).
90
97
 
91
- - [AnyCable: Action Cable on steroids!](https://evilmartians.com/chronicles/anycable-actioncable-on-steroids)
92
-
93
- - [From Action to Any](https://medium.com/@leshchuk/from-action-to-any-1e8d863dd4cf) by [@alekseyl](https://github.com/alekseyl)
94
-
95
- ## Talks
98
+ ## Contributing
96
99
 
97
- - One cable to rule them all, RubyKaigi 2018, [slides](https://speakerdeck.com/palkan/rubykaigi-2018-anycable-one-cable-to-rule-them-all) and [video](https://www.youtube.com/watch?v=jXCPuNICT8s) (EN)
100
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/anycable/anycable-rails](https://github.com/anycable/anycable-rails).
98
101
 
99
- - Wroc_Love.rb 2018 [slides](https://speakerdeck.com/palkan/wroc-love-dot-rb-2018-cables-cables-cables) and [video](https://www.youtube.com/watch?v=AUxFFOehiy0) (EN)
102
+ ## Development
100
103
 
101
- ## Compatible WebSocket servers
104
+ If you are familiar with Docker, you can use [DIP](https://github.com/bibendi/dip) to start developing the gem quickly.
102
105
 
103
- - [AnyCable Go](https://github.com/anycable/anycable-go)
104
- - [ErlyCable](https://github.com/anycable/erlycable)
106
+ ## License
105
107
 
106
- ## Contributing
108
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
107
109
 
108
- Bug reports and pull requests are welcome on GitHub at https://github.com/anycable/anycable-rails.
110
+ ## Security Contact
109
111
 
110
- ## License
111
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
112
+ To report a security vulnerability, please contact us at `anycable@evilmartians.com`. We will coordinate the fix and disclosure.
@@ -7,7 +7,8 @@ module ActionCable
7
7
  # AnyCable subscription adapter delegates broadcasts
8
8
  # to AnyCable
9
9
  class AnyCable < Base
10
- def initialize(*); end
10
+ def initialize(*)
11
+ end
11
12
 
12
13
  def broadcast(channel, payload)
13
14
  ::AnyCable.broadcast(channel, payload)
@@ -3,6 +3,9 @@
3
3
  require "anycable"
4
4
  require "anycable/rails/version"
5
5
  require "anycable/rails/config"
6
+ require "anycable/rails/rack"
7
+
8
+ require "globalid"
6
9
 
7
10
  module AnyCable
8
11
  # Rails handler for AnyCable
@@ -11,8 +14,40 @@ module AnyCable
11
14
 
12
15
  ADAPTER_ALIASES = %w[any_cable anycable].freeze
13
16
 
14
- def self.compatible_adapter?(adapter)
15
- ADAPTER_ALIASES.include?(adapter)
17
+ class << self
18
+ def enabled?
19
+ adapter = ::ActionCable.server.config.cable&.fetch("adapter", nil)
20
+ compatible_adapter?(adapter)
21
+ end
22
+
23
+ def compatible_adapter?(adapter)
24
+ ADAPTER_ALIASES.include?(adapter)
25
+ end
26
+
27
+ # Serialize connection/channel state variable to string
28
+ # using GlobalID where possible or JSON (if json: true)
29
+ def serialize(obj, json: false)
30
+ obj.try(:to_gid_param) || (json ? obj.to_json : obj)
31
+ end
32
+
33
+ # Deserialize previously serialized value from string to
34
+ # Ruby object.
35
+ # If the resulting object is a Hash, make it indifferent
36
+ def deserialize(str, json: false)
37
+ str.yield_self do |val|
38
+ next unless val.is_a?(String)
39
+
40
+ gval = GlobalID::Locator.locate(val)
41
+ return gval if gval
42
+
43
+ next val unless json
44
+
45
+ JSON.parse(val)
46
+ end.yield_self do |val|
47
+ next val.with_indifferent_access if val.is_a?(Hash)
48
+ val
49
+ end
50
+ end
16
51
  end
17
52
  end
18
53
  end
@@ -9,6 +9,9 @@ module ActionCable
9
9
 
10
10
  public :handle_subscribe, :subscription_rejected?
11
11
 
12
+ # Action Cable calls this method from inside the Subscriptions#add
13
+ # method, which we use to initialize the channel instance.
14
+ # We don't want to invoke `subscribed` callbacks every time we do that.
12
15
  def subscribe_to_channel
13
16
  # noop
14
17
  end
@@ -25,6 +28,10 @@ module ActionCable
25
28
  connection.socket.subscribe identifier, broadcasting
26
29
  end
27
30
 
31
+ def stop_stream_from(broadcasting)
32
+ connection.socket.unsubscribe identifier, broadcasting
33
+ end
34
+
28
35
  def stop_all_streams
29
36
  connection.socket.unsubscribe_from_all identifier
30
37
  end
@@ -1,43 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "action_cable/connection"
4
+ require "anycable/rails/actioncable/connection/serializable_identification"
4
5
  require "anycable/rails/refinements/subscriptions"
5
6
  require "anycable/rails/actioncable/channel"
7
+ require "anycable/rails/actioncable/remote_connections"
8
+ require "anycable/rails/session_proxy"
6
9
 
7
10
  module ActionCable
8
11
  module Connection
9
12
  # rubocop: disable Metrics/ClassLength
10
13
  class Base # :nodoc:
11
- # We store logger tags in identifiers to be able
14
+ # We store logger tags in the connection state to be able
12
15
  # to re-use them in the subsequent calls
13
16
  LOG_TAGS_IDENTIFIER = "__ltags__"
14
17
 
15
18
  using AnyCable::Refinements::Subscriptions
16
19
 
20
+ include SerializableIdentification
21
+
17
22
  attr_reader :socket
18
23
 
24
+ delegate :env, :session, to: :request
25
+
19
26
  class << self
20
27
  def call(socket, **options)
21
- new(socket, options)
28
+ new(socket, nil, options)
22
29
  end
30
+ end
23
31
 
24
- def identified_by(*identifiers)
25
- super
26
- Array(identifiers).each do |identifier|
27
- define_method(identifier) do
28
- instance_variable_get(:"@#{identifier}") || fetch_identifier(identifier)
29
- end
30
- end
32
+ def initialize(socket, env, identifiers: "{}", subscriptions: [])
33
+ if env
34
+ # If env is set, then somehow we're in the context of Action Cable
35
+ # Return and print a warning in #process
36
+ @request = ActionDispatch::Request.new(env)
37
+ return
31
38
  end
32
- end
33
39
 
34
- def initialize(socket, identifiers: "{}", subscriptions: [])
35
40
  @ids = ActiveSupport::JSON.decode(identifiers)
36
41
 
37
- @ltags = ids.delete(LOG_TAGS_IDENTIFIER)
42
+ @ltags = socket.cstate.read(LOG_TAGS_IDENTIFIER).yield_self do |raw_tags|
43
+ next unless raw_tags
44
+ ActiveSupport::JSON.decode(raw_tags)
45
+ end
38
46
 
39
47
  @cached_ids = {}
40
- @env = socket.env
41
48
  @coder = ActiveSupport::JSON
42
49
  @socket = socket
43
50
  @subscriptions = ActionCable::Connection::Subscriptions.new(self)
@@ -46,15 +53,32 @@ module ActionCable
46
53
  subscriptions.each { |id| @subscriptions.fetch(id) }
47
54
  end
48
55
 
56
+ def process
57
+ # Use Rails logger here to print to stdout in development
58
+ logger.error invalid_request_message
59
+ logger.info finished_request_message
60
+ [404, {"Content-Type" => "text/plain"}, ["Page not found"]]
61
+ end
62
+
63
+ def invalid_request_message
64
+ "You're trying to connect to Action Cable server while using AnyCable. " \
65
+ "See https://docs.anycable.io/v1/#/troubleshooting?id=server-raises-an-argumenterror-exception-when-client-tries-to-connect"
66
+ end
67
+
49
68
  def handle_open
50
69
  logger.info started_request_message if access_logs?
51
70
 
52
- verify_origin!
71
+ verify_origin! || return
53
72
 
54
73
  connect if respond_to?(:connect)
74
+
75
+ socket.cstate.write(LOG_TAGS_IDENTIFIER, fetch_ltags.to_json)
76
+
55
77
  send_welcome_message
56
78
  rescue ActionCable::Connection::Authorization::UnauthorizedError
57
- reject_request
79
+ reject_request(
80
+ ActionCable::INTERNAL[:disconnect_reasons]&.[](:unauthorized) || "unauthorized"
81
+ )
58
82
  end
59
83
 
60
84
  def handle_close
@@ -84,7 +108,12 @@ module ActionCable
84
108
  end
85
109
  # rubocop:enable Metrics/MethodLength
86
110
 
87
- def close
111
+ def close(reason: nil, reconnect: nil)
112
+ transmit(
113
+ type: ActionCable::INTERNAL[:message_types].fetch(:disconnect, "disconnect"),
114
+ reason: reason,
115
+ reconnect: reconnect
116
+ )
88
117
  socket.close
89
118
  end
90
119
 
@@ -92,37 +121,14 @@ module ActionCable
92
121
  socket.transmit encode(cable_message)
93
122
  end
94
123
 
95
- # Generate identifiers info.
96
- # Converts GlobalID compatible vars to corresponding global IDs params.
97
- def identifiers_hash
98
- obj = { LOG_TAGS_IDENTIFIER => fetch_ltags }
99
-
100
- identifiers.each_with_object(obj) do |id, acc|
101
- obj = instance_variable_get("@#{id}")
102
- next unless obj
103
-
104
- acc[id] = obj.try(:to_gid_param) || obj
105
- end.compact
106
- end
107
-
108
- def identifiers_json
109
- identifiers_hash.to_json
110
- end
111
-
112
- # Fetch identifier and deserialize if neccessary
113
- def fetch_identifier(name)
114
- @cached_ids[name] ||= @cached_ids.fetch(name) do
115
- val = ids[name.to_s]
116
- next val unless val.is_a?(String)
117
-
118
- GlobalID::Locator.locate(val) || val
119
- end
120
- end
121
-
122
124
  def logger
123
125
  @logger ||= TaggedLoggerProxy.new(AnyCable.logger, tags: ltags || [])
124
126
  end
125
127
 
128
+ def request
129
+ @request ||= build_rack_request
130
+ end
131
+
126
132
  private
127
133
 
128
134
  attr_reader :ids, :ltags
@@ -158,19 +164,33 @@ module ActionCable
158
164
  end
159
165
 
160
166
  def verify_origin!
161
- return unless socket.env.key?("HTTP_ORIGIN")
167
+ return true unless socket.env.key?("HTTP_ORIGIN")
162
168
 
163
- return if allow_request_origin?
169
+ return true if allow_request_origin?
164
170
 
165
- raise(
166
- ActionCable::Connection::Authorization::UnauthorizedError,
167
- "Origin is not allowed"
171
+ reject_request(
172
+ ActionCable::INTERNAL[:disconnect_reasons]&.[](:invalid_request) || "invalid_request"
168
173
  )
174
+ false
169
175
  end
170
176
 
171
- def reject_request
177
+ def reject_request(reason, reconnect = false)
172
178
  logger.info finished_request_message("Rejected") if access_logs?
173
- close
179
+ close(
180
+ reason: reason,
181
+ reconnect: reconnect
182
+ )
183
+ end
184
+
185
+ def build_rack_request
186
+ environment = Rails.application.env_config.merge(socket.env)
187
+ AnyCable::Rails::Rack.app.call(environment)
188
+
189
+ ActionDispatch::Request.new(environment)
190
+ end
191
+
192
+ def request_loaded?
193
+ instance_variable_defined?(:@request)
174
194
  end
175
195
  end
176
196
  # rubocop:enable Metrics/ClassLength