anycable 1.0.0.preview2 → 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.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +18 -259
 - data/README.md +8 -9
 - data/lib/anycable.rb +1 -1
 - data/lib/anycable/broadcast_adapters.rb +2 -0
 - data/lib/anycable/broadcast_adapters/base.rb +29 -0
 - data/lib/anycable/broadcast_adapters/http.rb +130 -0
 - data/lib/anycable/broadcast_adapters/redis.rb +7 -6
 - data/lib/anycable/cli.rb +11 -4
 - data/lib/anycable/config.rb +12 -6
 - data/lib/anycable/health_server.rb +4 -2
 - data/lib/anycable/middleware.rb +6 -1
 - data/lib/anycable/middlewares/check_version.rb +1 -1
 - data/lib/anycable/rpc.rb +8 -0
 - data/lib/anycable/rpc/rpc_pb.rb +3 -0
 - data/lib/anycable/rpc_handler.rb +6 -3
 - data/lib/anycable/server.rb +6 -2
 - data/lib/anycable/socket.rb +10 -5
 - data/lib/anycable/version.rb +1 -1
 - metadata +18 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: deebe94f2e541a9d14ccf4a56df910cdb5a2fb17ae6815ffaaa2f761fdcd07ad
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: e96c5b0d28dd7617c2cfa1249fde5bd357465be7596915720c27718f486c4f16
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: b585f2797184d7bc78cc761707b741dcfa8a6b796df91cccd7ac1e44c9210a84852daba838adaaa6b2f1548d2d0f82cf57713b32a4a86d0ffa671f64321b9df1
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: dccd27127eaafc5b7821ba18404546269e9aaf3163faccffaba864e166cb1c19cdc914ccf9de87da963cac447541058e0d7ba906674298f9dda8573ca4e917c8
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,6 +1,20 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Change log
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            ##  
     | 
| 
      
 3 
     | 
    
         
            +
            ## 1.0.0.rc1 (2020-06-10)
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            - Add `Env#istate` and `EnvResponse#istate` to store channel state. ([@palkan][])
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            That would allow to mimic instance variables usage in Action Cable channels.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            - Add `CommandResponse#stopped_streams` to support unsubscribing from particular broadcastings. ([@palkan])
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            `Socket#unsubscribe` is now implemented as well.
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            - Add `AnyCable.broadcast_adapter#broadcast_command` method. ([@palkan][])
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            It could be used to send commands to WS server (e.g., remote disconnect).
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            - Add `:http` broadcasting adapter. ([@palkan][])
         
     | 
| 
       4 
18 
     | 
    
         | 
| 
       5 
19 
     | 
    
         
             
            - **RPC schema has changed**. ([@palkan][])
         
     | 
| 
       6 
20 
     | 
    
         | 
| 
         @@ -22,267 +36,12 @@ Now you can access `request` object in channels, too (e.g., to read headers/cook 
     | 
|
| 
       22 
36 
     | 
    
         | 
| 
       23 
37 
     | 
    
         
             
            See [#71](https://github.com/anycable/anycable/pull/71).
         
     | 
| 
       24 
38 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
            - Relax `anyway_config` dependency. ([@palkan][])
         
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
            ## 0.6.4 (2020-01-24)
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
            - Fix Ruby 2.7 warnings. ([@palkan][])
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
            – Add `REMOTE_ADDR` socket env variable using a synthetic header passed from a websocket
         
     | 
| 
       34 
     | 
    
         
            -
            server. ([@sponomarev][])
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
            Recreating a request object in your custom connection factory using `Rack::Request` or
         
     | 
| 
       37 
     | 
    
         
            -
            `ActionDispatch::Request` (already implemented in [anycable-rails](https://github.com/anycable/anycable-rails))
         
     | 
| 
       38 
     | 
    
         
            -
            gives you an access to `request.ip` with the properly set IP address.
         
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
            - Align socket env to be more compatible with Rack Spec ([@sponomarev][])
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
            Provide as much env details as possible to be able to reconstruct the full
         
     | 
| 
       43 
     | 
    
         
            -
            request object in a custom connection factory.
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
            ## 0.6.3 (2019-03-26)
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
            - Relax `redis` gem version requirement. ([@palkan][])
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
            Use the same restriction as Action Cable does (`>= 3`).
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
            ## 0.6.2 (2019-03-15)
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
            - Add GRPC service method name and message content to exception notifications ([@sponomarev][])
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
            `Anycable.capture_exception` allows accessing GRPC service method name and message content
         
     | 
| 
       56 
     | 
    
         
            -
            on which an exception was captured. It can be used for exceptions grouping in your tracker and
         
     | 
| 
       57 
     | 
    
         
            -
            providing additional data to investigate a root of a problem.
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
            Example:
         
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       62 
     | 
    
         
            -
            AnyCable.capture_exception do |ex, method, message|
         
     | 
| 
       63 
     | 
    
         
            -
              Honeybadger.notify(ex, component: "any_cable", action: method, params: message)
         
     | 
| 
       64 
     | 
    
         
            -
            end
         
     | 
| 
       65 
     | 
    
         
            -
            ```
         
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
            Usage of a handler proc with just a single argument is preserved for the sake of compatibility.
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
            - Add deprecation warning to default host usage ([@sponomarev][])
         
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
            Exposing AnyCable publicly is considered to be harmful and planned to be changed
         
     | 
| 
       72 
     | 
    
         
            -
            in future versions.
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
            - Allow running the server as a detachable daemon ([@sponomarev][])
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
            Server is fully managed by the binary itself.
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
            ```sh
         
     | 
| 
       79 
     | 
    
         
            -
            # Start anycable daemon
         
     | 
| 
       80 
     | 
    
         
            -
            $ bundle exec anycabled start
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
            # Pass cli options to anycable through daemon. Separate daemon options and anycable options with `--`
         
     | 
| 
       83 
     | 
    
         
            -
            $ bundle exec anycabled start -- --rpc-host 127.0.0.1:31337
         
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
            # Stop anycable daemon
         
     | 
| 
       86 
     | 
    
         
            -
            $ bundle exec anycabled stop
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
            # See more anycable daemon options
         
     | 
| 
       89 
     | 
    
         
            -
            $ bundle exec anycabled
         
     | 
| 
       90 
     | 
    
         
            -
            ```
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
            ## 0.6.1 (2019-01-05)
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
            - [Fix #63](https://github.com/anycable/anycable-rails/issues/63) Load `anyway_config` after application boot to make sure that all frameworks dependent functionality is loaded. ([@palkan][])
         
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
            ## 0.6.0 (2018-11-15)
         
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
            ### Features
         
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
            #### Broadcast adapters
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
            AnyCable allows you to use custom broadcasting adapters (Redis is used by default):
         
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       105 
     | 
    
         
            -
            # Specify by name (tries to load `AnyCable::BroadcastAdapters::MyAdapter` from
         
     | 
| 
       106 
     | 
    
         
            -
            # "anycable/broadcast_adapters/my_adapter")
         
     | 
| 
       107 
     | 
    
         
            -
            AnyCable.broadcast_adapter = :my_adapter, {option: "value"}
         
     | 
| 
       108 
     | 
    
         
            -
            # or provide an instance (should respond_to #broadcast)
         
     | 
| 
       109 
     | 
    
         
            -
            AnyCable.broadcast_adapter = MyAdapter.new
         
     | 
| 
       110 
     | 
    
         
            -
            ```
         
     | 
| 
       111 
     | 
    
         
            -
             
     | 
| 
       112 
     | 
    
         
            -
            **Breaking:** to use Redis adapter you must ensure that it is present in your Gemfile; AnyCable gem doesn't have `redis` as a dependency anymore.
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
            #### CLI
         
     | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
       116 
     | 
    
         
            -
            AnyCable now ships with a CLI–`anycable`.
         
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
            Use it to run a gRPC server:
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
            ```sh
         
     | 
| 
       121 
     | 
    
         
            -
            # run anycable and load app from app.rb
         
     | 
| 
       122 
     | 
    
         
            -
            bundle exec anycable -r app.rb
         
     | 
| 
       123 
     | 
    
         
            -
            # or
         
     | 
| 
       124 
     | 
    
         
            -
            bundle exec anycable --require app.rb
         
     | 
| 
       125 
     | 
    
         
            -
            ```
         
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
       127 
     | 
    
         
            -
            All configuration options are also supported as CLI options (see `anycable -h` for more information).
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
            The only required options is the application file to load (`-r/--require`).
         
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
            You can omit it if you want to load an app form `./config/environment.rb` (e.g. with Rails) or `./config/anycable.rb`.
         
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
            AnyCable CLI also allows you to run a separate command (process) from within a RPC server:
         
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
            ```sh
         
     | 
| 
       136 
     | 
    
         
            -
            bundle exec anycable --server-command "anycable-go -p 3334"
         
     | 
| 
       137 
     | 
    
         
            -
            ```
         
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
            #### Configuration
         
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
            - Default server host is changed from `localhost:50051` to `0.0.0.0:50051`
         
     | 
| 
       142 
     | 
    
         
            -
            - Expose gRPC server parameters via `rpc_*` config params:
         
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       145 
     | 
    
         
            -
            AnyCable.configure do |config|
         
     | 
| 
       146 
     | 
    
         
            -
              config.rpc_pool_size = 120
         
     | 
| 
       147 
     | 
    
         
            -
              config.rpc_max_waiting_requests = 10
         
     | 
| 
       148 
     | 
    
         
            -
              # etc
         
     | 
| 
       149 
     | 
    
         
            -
            end
         
     | 
| 
       150 
     | 
    
         
            -
            ```
         
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
            - `REDIS_URL` env is used by default if present (and no `ANYCABLE_REDIS_URL` specified)
         
     | 
| 
       153 
     | 
    
         
            -
            - Make HTTP health check url configurable
         
     | 
| 
       154 
     | 
    
         
            -
            - Add ability to pass Redis Sentinel config as array of string.
         
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
            Now it's possible to pass Sentinel configuration via env vars:
         
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
     | 
    
         
            -
            ```sh
         
     | 
| 
       159 
     | 
    
         
            -
            ANYCABLE_REDIS_SENTINELS=127.0.0.1:26380,127.0.0.1:26381 bundle exec anycable
         
     | 
| 
       160 
     | 
    
         
            -
            ```
         
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
            #### Other
         
     | 
| 
       163 
     | 
    
         
            -
             
     | 
| 
       164 
     | 
    
         
            -
            - Added middlewares support
         
     | 
| 
       165 
     | 
    
         
            -
             
     | 
| 
       166 
     | 
    
         
            -
            See [docs](https://docs.anycable.io/#/./middlewares).
         
     | 
| 
       167 
     | 
    
         
            -
             
     | 
| 
       168 
     | 
    
         
            -
            - Added gRPC health checker.
         
     | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
       170 
     | 
    
         
            -
            See [docs](https://docs.anycable.io/#/./health_checking).
         
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
            - Added hook to run code only within RPC server context.
         
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
       174 
     | 
    
         
            -
            Use `AnyCable.configure_server { ... }` to run code only when RPC server is running.
         
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
            ### API changes
         
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
            **NOTE**: the old API is still working but deprecated (you'll see a notice).
         
     | 
| 
       179 
     | 
    
         
            -
             
     | 
| 
       180 
     | 
    
         
            -
            - Use `AnyCable` instead of `Anycable`
         
     | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
            - New API for registering error handlers:
         
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
       184 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       185 
     | 
    
         
            -
            AnyCable.capture_exception do |ex|
         
     | 
| 
       186 
     | 
    
         
            -
              Honeybadger.notify(ex)
         
     | 
| 
       187 
     | 
    
         
            -
            end
         
     | 
| 
       188 
     | 
    
         
            -
            ```
         
     | 
| 
       189 
     | 
    
         
            -
             
     | 
| 
       190 
     | 
    
         
            -
            - `AnyCable::Server.start` is deprecated
         
     | 
| 
       191 
     | 
    
         
            -
             
     | 
| 
       192 
     | 
    
         
            -
            ## 0.5.2 (2018-09-06)
         
     | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
            - [#48](https://github.com/anycable/anycable/pull/48) Add HTTP health server ([@DarthSim][])
         
     | 
| 
       195 
     | 
    
         
            -
             
     | 
| 
       196 
     | 
    
         
            -
            ## 0.5.1 (2018-06-13)
         
     | 
| 
       197 
     | 
    
         
            -
             
     | 
| 
       198 
     | 
    
         
            -
            Minor fixes.
         
     | 
| 
       199 
     | 
    
         
            -
             
     | 
| 
       200 
     | 
    
         
            -
            ## 0.5.0 (2017-10-21)
         
     | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
       202 
     | 
    
         
            -
            - [#2](https://github.com/anycable/anycable/issues/2) Add support for [Redis Sentinel](https://redis.io/topics/sentinel). ([@accessd][])
         
     | 
| 
       203 
     | 
    
         
            -
             
     | 
| 
       204 
     | 
    
         
            -
            - [#28](https://github.com/anycable/anycable/issues/28) Support arbitrary headers. ([@palkan][])
         
     | 
| 
       205 
     | 
    
         
            -
             
     | 
| 
       206 
     | 
    
         
            -
            Previously we hardcoded only "Cookie" header. Now we add all passed headers by WebSocket server to request env.
         
     | 
| 
       207 
     | 
    
         
            -
             
     | 
| 
       208 
     | 
    
         
            -
            - [#27](https://github.com/anycable/anycable/issues/27) Add `error_msg` to RPC responses. ([@palkan][])
         
     | 
| 
       209 
     | 
    
         
            -
             
     | 
| 
       210 
     | 
    
         
            -
            Now RPC responses has 3 statuses:
         
     | 
| 
       211 
     | 
    
         
            -
             
     | 
| 
       212 
     | 
    
         
            -
            1) `SUCCESS` – successful request, operation succeed
         
     | 
| 
       213 
     | 
    
         
            -
            2) `FAILURE` – successful request, operation failed (e.g. authentication failed)
         
     | 
| 
       214 
     | 
    
         
            -
            3) `ERROR` – request failed (exception raised).
         
     | 
| 
       215 
     | 
    
         
            -
             
     | 
| 
       216 
     | 
    
         
            -
            We provide `error_msg` only when request status is `ERROR`.
         
     | 
| 
       217 
     | 
    
         
            -
             
     | 
| 
       218 
     | 
    
         
            -
            - [#25](https://github.com/anycable/anycable/issues/25) Improve logging and exceptions handling. ([@palkan][])
         
     | 
| 
       219 
     | 
    
         
            -
             
     | 
| 
       220 
     | 
    
         
            -
            Default logger logs to STDOUT with `info` level by default but can be configured to log to file with
         
     | 
| 
       221 
     | 
    
         
            -
            any severity.
         
     | 
| 
       222 
     | 
    
         
            -
             
     | 
| 
       223 
     | 
    
         
            -
            GRPC logging is turned off by default (can be turned on through `log_grpc` configuration parameter).
         
     | 
| 
       224 
     | 
    
         
            -
             
     | 
| 
       225 
     | 
    
         
            -
            `ANYCABLE_DEBUG=1` acts as a shortcut to set `debug` level and turn on GRPC logging.
         
     | 
| 
       226 
     | 
    
         
            -
             
     | 
| 
       227 
     | 
    
         
            -
            Now it's possible to add custom exception handlers (e.g. to notify external exception tracking services).
         
     | 
| 
       228 
     | 
    
         
            -
             
     | 
| 
       229 
     | 
    
         
            -
            More on [Wiki](https://github.com/anycable/anycable/wiki/Logging-&-Exceptions-Handling).
         
     | 
| 
       230 
     | 
    
         
            -
             
     | 
| 
       231 
     | 
    
         
            -
            ## 0.4.6 (2017-05-20)
         
     | 
| 
       232 
     | 
    
         
            -
             
     | 
| 
       233 
     | 
    
         
            -
            - Add `Anycable::Server#stop` method. ([@sadovnik][])
         
     | 
| 
       234 
     | 
    
         
            -
             
     | 
| 
       235 
     | 
    
         
            -
            ## 0.4.5 (2017-03-17)
         
     | 
| 
       236 
     | 
    
         
            -
             
     | 
| 
       237 
     | 
    
         
            -
            - Fixed #11. ([@palkan][])
         
     | 
| 
       238 
     | 
    
         
            -
             
     | 
| 
       239 
     | 
    
         
            -
            ## 0.4.4 (2017-03-06)
         
     | 
| 
       240 
     | 
    
         
            -
             
     | 
| 
       241 
     | 
    
         
            -
            - Handle `StandardError` gracefully in RPC calls. ([@palkan][])
         
     | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
       243 
     | 
    
         
            -
            ## 0.4.3 (2017-02-18)
         
     | 
| 
       244 
     | 
    
         
            -
             
     | 
| 
       245 
     | 
    
         
            -
            - Update `grpc` version dependency to support Ruby 2.4. ([@palkan][])
         
     | 
| 
       246 
     | 
    
         
            -
             
     | 
| 
       247 
     | 
    
         
            -
            ## 0.4.2 (2017-01-28)
         
     | 
| 
       248 
     | 
    
         
            -
             
     | 
| 
       249 
     | 
    
         
            -
            - Change socket streaming API. ([@palkan][])
         
     | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
     | 
    
         
            -
            Add `Socket#subscribe`, `unsubscribe` and `unsubscribe_from_all` methods.
         
     | 
| 
       252 
     | 
    
         
            -
             
     | 
| 
       253 
     | 
    
         
            -
            ## 0.4.1 (2017-01-24)
         
     | 
| 
       254 
     | 
    
         
            -
             
     | 
| 
       255 
     | 
    
         
            -
            - Introduce _fake_ socket instance to handle transmissions and streams. ([@palkan][])
         
     | 
| 
       256 
     | 
    
         
            -
             
     | 
| 
       257 
     | 
    
         
            -
            - Make commands handling more abstract. ([@palkan][])
         
     | 
| 
       258 
     | 
    
         
            -
             
     | 
| 
       259 
     | 
    
         
            -
            We now do not explicitly call channels action but use the only one entrypoint for all commands:
         
     | 
| 
       260 
     | 
    
         
            -
             
     | 
| 
       261 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       262 
     | 
    
         
            -
            connection.handle_channel_command(identifier, command, data)
         
     | 
| 
       263 
     | 
    
         
            -
            ```
         
     | 
| 
       264 
     | 
    
         
            -
             
     | 
| 
       265 
     | 
    
         
            -
            This method should return `true` if command was successful and `false` otherwise.
         
     | 
| 
       266 
     | 
    
         
            -
             
     | 
| 
       267 
     | 
    
         
            -
            ## 0.4.0 (2017-01-22)
         
     | 
| 
       268 
     | 
    
         
            -
             
     | 
| 
       269 
     | 
    
         
            -
            - Refactor RPC API. ([@palkan][])
         
     | 
| 
       270 
     | 
    
         
            -
             
     | 
| 
       271 
     | 
    
         
            -
            Replace `Subscribe`, `Unsubscribe` and `Perform` methods with `Command` method.
         
     | 
| 
       272 
     | 
    
         
            -
             
     | 
| 
       273 
     | 
    
         
            -
            - Extract Rails functionality to separate gem. ([@palkan][])
         
     | 
| 
       274 
     | 
    
         
            -
             
     | 
| 
       275 
     | 
    
         
            -
            All Rails specifics now live here [https://github.com/anycable/anycable-rails](https://github.com/anycable/anycable-rails).
         
     | 
| 
       276 
     | 
    
         
            -
             
     | 
| 
       277 
     | 
    
         
            -
            ## 0.3.0 (2016-12-28)
         
     | 
| 
      
 39 
     | 
    
         
            +
            - Fix building Redis Sentinel config. ([@palkan][])
         
     | 
| 
       278 
40 
     | 
    
         | 
| 
       279 
     | 
    
         
            -
             
     | 
| 
      
 41 
     | 
    
         
            +
            ---
         
     | 
| 
       280 
42 
     | 
    
         | 
| 
       281 
     | 
    
         
            -
             
     | 
| 
      
 43 
     | 
    
         
            +
            See [Changelog](https://github.com/anycable/anycable/blob/0-6-stable/CHANGELOG.md) for versions <1.0.0.
         
     | 
| 
       282 
44 
     | 
    
         | 
| 
       283 
45 
     | 
    
         
             
            [@palkan]: https://github.com/palkan
         
     | 
| 
       284 
     | 
    
         
            -
            [@sadovnik]: https://github.com/sadovnik
         
     | 
| 
       285 
     | 
    
         
            -
            [@accessd]: https://github.com/accessd
         
     | 
| 
       286 
     | 
    
         
            -
            [@DarthSim]: https://github.com/DarthSim
         
     | 
| 
       287 
46 
     | 
    
         
             
            [@sponomarev]: https://github.com/sponomarev
         
     | 
| 
       288 
47 
     | 
    
         
             
            [@bibendi]: https://github.com/bibendi
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -2,28 +2,30 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            [](https://rubygems.org/gems/anycable)
         
     | 
| 
       3 
3 
     | 
    
         
             
            [](https://github.com/anycable/anycable/actions)
         
     | 
| 
       4 
4 
     | 
    
         
             
            [](https://gitter.im/anycable/Lobby)
         
     | 
| 
       5 
     | 
    
         
            -
            [](https://docs.anycable.io)
         
     | 
| 
      
 5 
     | 
    
         
            +
            [](https://docs.anycable.io/v1)
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            # AnyCable
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            <img align="right" height="150" width="129"
         
     | 
| 
       10 
10 
     | 
    
         
             
                 title="AnyCable logo" src="https://docs.anycable.io/assets/images/logo.svg">
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
            AnyCable allows you to use any WebSocket server (written in any language) as a replacement for your Ruby server (such as Faye,  
     | 
| 
      
 12 
     | 
    
         
            +
            AnyCable allows you to use any WebSocket server (written in any language) as a replacement for your Ruby server (such as Faye, Action Cable, etc).
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
            AnyCable uses the same protocol as ActionCable, so you can use its [JavaScript client](https://www.npmjs.com/package/actioncable) without any monkey-patching.
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
      
 16 
     | 
    
         
            +
            **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/tree/0-6-stable) branch.
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
       16 
18 
     | 
    
         
             
            <a href="https://evilmartians.com/">
         
     | 
| 
       17 
19 
     | 
    
         
             
            <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
         
     | 
| 
       18 
20 
     | 
    
         | 
| 
       19 
21 
     | 
    
         
             
            ## Requirements
         
     | 
| 
       20 
22 
     | 
    
         | 
| 
       21 
23 
     | 
    
         
             
            - Ruby >= 2.5
         
     | 
| 
       22 
     | 
    
         
            -
            - Redis (for broadcasting 
     | 
| 
      
 24 
     | 
    
         
            +
            - Redis (for broadcasting **in production**, [discuss other options](https://github.com/anycable/anycable/issues/2) with us!)
         
     | 
| 
       23 
25 
     | 
    
         | 
| 
       24 
26 
     | 
    
         
             
            ## Usage
         
     | 
| 
       25 
27 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
            Check out our 📑 [Documentation](https://docs.anycable.io).
         
     | 
| 
      
 28 
     | 
    
         
            +
            Check out our 📑 [Documentation](https://docs.anycable.io/v1).
         
     | 
| 
       27 
29 
     | 
    
         | 
| 
       28 
30 
     | 
    
         
             
            ## Links
         
     | 
| 
       29 
31 
     | 
    
         | 
| 
         @@ -45,12 +47,9 @@ Check out our 📑 [Documentation](https://docs.anycable.io). 
     | 
|
| 
       45 
47 
     | 
    
         | 
| 
       46 
48 
     | 
    
         
             
            - RailsClub Moscow 2016 [slides](https://speakerdeck.com/palkan/railsclub-moscow-2016-anycable) and [video](https://www.youtube.com/watch?v=-k7GQKuBevY&list=PLiWUIs1hSNeOXZhotgDX7Y7qBsr24cu7o&index=4) (RU)
         
     | 
| 
       47 
49 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
            ##  
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
            - [AnyCable Go](https://github.com/anycable/anycable-go)
         
     | 
| 
       51 
     | 
    
         
            -
            - [ErlyCable](https://github.com/anycable/erlycable)
         
     | 
| 
      
 50 
     | 
    
         
            +
            ## Building
         
     | 
| 
       52 
51 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
      
 52 
     | 
    
         
            +
            ### Generating gRPC files from `.proto`
         
     | 
| 
       54 
53 
     | 
    
         | 
| 
       55 
54 
     | 
    
         
             
            - Install required GRPC gems:
         
     | 
| 
       56 
55 
     | 
    
         | 
    
        data/lib/anycable.rb
    CHANGED
    
    | 
         @@ -67,7 +67,7 @@ module AnyCable 
     | 
|
| 
       67 
67 
     | 
    
         
             
                end
         
     | 
| 
       68 
68 
     | 
    
         | 
| 
       69 
69 
     | 
    
         
             
                def broadcast_adapter
         
     | 
| 
       70 
     | 
    
         
            -
                  self.broadcast_adapter =  
     | 
| 
      
 70 
     | 
    
         
            +
                  self.broadcast_adapter = AnyCable.config.broadcast_adapter.to_sym unless instance_variable_defined?(:@broadcast_adapter)
         
     | 
| 
       71 
71 
     | 
    
         
             
                  @broadcast_adapter
         
     | 
| 
       72 
72 
     | 
    
         
             
                end
         
     | 
| 
       73 
73 
     | 
    
         | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module AnyCable
         
     | 
| 
      
 4 
     | 
    
         
            +
              module BroadcastAdapters
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Base
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def raw_broadcast(_data)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    raise NotImplementedError
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def broadcast(stream, payload)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    raw_broadcast({stream: stream, data: payload}.to_json)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def broadcast_command(command, **payload)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    raw_broadcast({command: command, payload: payload}.to_json)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def announce!
         
     | 
| 
      
 19 
     | 
    
         
            +
                    logger.info "Broadcasting via #{self.class.name}"
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  private
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def logger
         
     | 
| 
      
 25 
     | 
    
         
            +
                    AnyCable.logger
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,130 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "json"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "uri"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "net/http"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module AnyCable
         
     | 
| 
      
 8 
     | 
    
         
            +
              module BroadcastAdapters
         
     | 
| 
      
 9 
     | 
    
         
            +
                # HTTP adapter for broadcasting.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # Example:
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                #   AnyCable.broadast_adapter = :http
         
     | 
| 
      
 14 
     | 
    
         
            +
                #
         
     | 
| 
      
 15 
     | 
    
         
            +
                # It uses configuration from global AnyCable config
         
     | 
| 
      
 16 
     | 
    
         
            +
                # by default.
         
     | 
| 
      
 17 
     | 
    
         
            +
                #
         
     | 
| 
      
 18 
     | 
    
         
            +
                # You can override these params:
         
     | 
| 
      
 19 
     | 
    
         
            +
                #
         
     | 
| 
      
 20 
     | 
    
         
            +
                #   AnyCable.broadcast_adapter = :http, url: "http://ws.example.com/_any_cable_"
         
     | 
| 
      
 21 
     | 
    
         
            +
                class Http < Base
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # Taken from: https://github.com/influxdata/influxdb-ruby/blob/886058079c66d4fd019ad74ca11342fddb0b753d/lib/influxdb/errors.rb#L18
         
     | 
| 
      
 23 
     | 
    
         
            +
                  RECOVERABLE_EXCEPTIONS = [
         
     | 
| 
      
 24 
     | 
    
         
            +
                    Errno::ECONNABORTED,
         
     | 
| 
      
 25 
     | 
    
         
            +
                    Errno::ECONNREFUSED,
         
     | 
| 
      
 26 
     | 
    
         
            +
                    Errno::ECONNRESET,
         
     | 
| 
      
 27 
     | 
    
         
            +
                    Errno::EHOSTUNREACH,
         
     | 
| 
      
 28 
     | 
    
         
            +
                    Errno::EINVAL,
         
     | 
| 
      
 29 
     | 
    
         
            +
                    Errno::ENETUNREACH,
         
     | 
| 
      
 30 
     | 
    
         
            +
                    Net::HTTPBadResponse,
         
     | 
| 
      
 31 
     | 
    
         
            +
                    Net::HTTPHeaderSyntaxError,
         
     | 
| 
      
 32 
     | 
    
         
            +
                    Net::ProtocolError,
         
     | 
| 
      
 33 
     | 
    
         
            +
                    SocketError,
         
     | 
| 
      
 34 
     | 
    
         
            +
                    (OpenSSL::SSL::SSLError if defined?(OpenSSL))
         
     | 
| 
      
 35 
     | 
    
         
            +
                  ].compact.freeze
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  OPEN_TIMEOUT = 5
         
     | 
| 
      
 38 
     | 
    
         
            +
                  READ_TIMEOUT = 10
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  MAX_ATTEMPTS = 3
         
     | 
| 
      
 41 
     | 
    
         
            +
                  DELAY = 2
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  attr_reader :url, :headers, :authorized
         
     | 
| 
      
 44 
     | 
    
         
            +
                  alias authorized? authorized
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  def initialize(url: AnyCable.config.http_broadcast_url, secret: AnyCable.config.http_broadcast_secret)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @url = url
         
     | 
| 
      
 48 
     | 
    
         
            +
                    @headers = {}
         
     | 
| 
      
 49 
     | 
    
         
            +
                    if secret
         
     | 
| 
      
 50 
     | 
    
         
            +
                      headers["Authorization"] = "Bearer #{secret}"
         
     | 
| 
      
 51 
     | 
    
         
            +
                      @authorized = true
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    @uri = URI.parse(url)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    @queue = Queue.new
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def raw_broadcast(payload)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    ensure_thread_is_alive
         
     | 
| 
      
 60 
     | 
    
         
            +
                    queue << payload
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  # Wait for background thread to process all the messages
         
     | 
| 
      
 64 
     | 
    
         
            +
                  # and stop it
         
     | 
| 
      
 65 
     | 
    
         
            +
                  def shutdown
         
     | 
| 
      
 66 
     | 
    
         
            +
                    queue << :stop
         
     | 
| 
      
 67 
     | 
    
         
            +
                    thread.join if thread&.alive?
         
     | 
| 
      
 68 
     | 
    
         
            +
                  rescue Exception => e # rubocop:disable Lint/RescueException
         
     | 
| 
      
 69 
     | 
    
         
            +
                    logger.error "Broadcasting thread exited with exception: #{e.message}"
         
     | 
| 
      
 70 
     | 
    
         
            +
                  end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  def announce!
         
     | 
| 
      
 73 
     | 
    
         
            +
                    logger.info "Broadcasting HTTP url: #{url}#{authorized? ? " (with authorization)" : ""}"
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  private
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  attr_reader :uri, :queue, :thread
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  def ensure_thread_is_alive
         
     | 
| 
      
 81 
     | 
    
         
            +
                    return if thread&.alive?
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                    @thread = Thread.new do
         
     | 
| 
      
 84 
     | 
    
         
            +
                      loop do
         
     | 
| 
      
 85 
     | 
    
         
            +
                        msg = queue.pop
         
     | 
| 
      
 86 
     | 
    
         
            +
                        break if msg == :stop
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                        handle_response perform_request(msg)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      end
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  def perform_request(payload)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    build_http do |http|
         
     | 
| 
      
 95 
     | 
    
         
            +
                      req = Net::HTTP::Post.new(url, {"Content-Type" => "application/json"}.merge(headers))
         
     | 
| 
      
 96 
     | 
    
         
            +
                      req.body = payload
         
     | 
| 
      
 97 
     | 
    
         
            +
                      http.request(req)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    end
         
     | 
| 
      
 99 
     | 
    
         
            +
                  end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                  def handle_response(response)
         
     | 
| 
      
 102 
     | 
    
         
            +
                    return unless response
         
     | 
| 
      
 103 
     | 
    
         
            +
                    return if Net::HTTPCreated === response
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    logger.error "Broadcast request responded with unexpected status: #{response.code}"
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  def build_http
         
     | 
| 
      
 109 
     | 
    
         
            +
                    retry_count = 0
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 112 
     | 
    
         
            +
                      http = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
      
 113 
     | 
    
         
            +
                      http.open_timeout = OPEN_TIMEOUT
         
     | 
| 
      
 114 
     | 
    
         
            +
                      http.read_timeout = READ_TIMEOUT
         
     | 
| 
      
 115 
     | 
    
         
            +
                      http.use_ssl = url.match?(/^https/)
         
     | 
| 
      
 116 
     | 
    
         
            +
                      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
         
     | 
| 
      
 117 
     | 
    
         
            +
                      yield http
         
     | 
| 
      
 118 
     | 
    
         
            +
                    rescue Timeout::Error, *RECOVERABLE_EXCEPTIONS => e
         
     | 
| 
      
 119 
     | 
    
         
            +
                      retry_count += 1
         
     | 
| 
      
 120 
     | 
    
         
            +
                      return logger.error("Broadcast request failed: #{e.message}") if MAX_ATTEMPTS < retry_count
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                      sleep((DELAY**retry_count) * retry_count)
         
     | 
| 
      
 123 
     | 
    
         
            +
                      retry
         
     | 
| 
      
 124 
     | 
    
         
            +
                    ensure
         
     | 
| 
      
 125 
     | 
    
         
            +
                      http.finish if http.started?
         
     | 
| 
      
 126 
     | 
    
         
            +
                    end
         
     | 
| 
      
 127 
     | 
    
         
            +
                  end
         
     | 
| 
      
 128 
     | 
    
         
            +
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
              end
         
     | 
| 
      
 130 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -19,7 +19,7 @@ module AnyCable 
     | 
|
| 
       19 
19 
     | 
    
         
             
                # You can override these params:
         
     | 
| 
       20 
20 
     | 
    
         
             
                #
         
     | 
| 
       21 
21 
     | 
    
         
             
                #   AnyCable.broadcast_adapter = :redis, url: "redis://my_redis", channel: "_any_cable_"
         
     | 
| 
       22 
     | 
    
         
            -
                class Redis
         
     | 
| 
      
 22 
     | 
    
         
            +
                class Redis < Base
         
     | 
| 
       23 
23 
     | 
    
         
             
                  attr_reader :redis_conn, :channel
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
25 
     | 
    
         
             
                  def initialize(
         
     | 
| 
         @@ -31,11 +31,12 @@ module AnyCable 
     | 
|
| 
       31 
31 
     | 
    
         
             
                    @channel = channel
         
     | 
| 
       32 
32 
     | 
    
         
             
                  end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
                  def  
     | 
| 
       35 
     | 
    
         
            -
                    redis_conn.publish(
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
      
 34 
     | 
    
         
            +
                  def raw_broadcast(payload)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    redis_conn.publish(channel, payload)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def announce!
         
     | 
| 
      
 39 
     | 
    
         
            +
                    logger.info "Broadcasting Redis channel: #{channel}"
         
     | 
| 
       39 
40 
     | 
    
         
             
                  end
         
     | 
| 
       40 
41 
     | 
    
         
             
                end
         
     | 
| 
       41 
42 
     | 
    
         
             
              end
         
     | 
    
        data/lib/anycable/cli.rb
    CHANGED
    
    | 
         @@ -183,7 +183,7 @@ module AnyCable 
     | 
|
| 
       183 
183 
     | 
    
         
             
                end
         
     | 
| 
       184 
184 
     | 
    
         | 
| 
       185 
185 
     | 
    
         
             
                def start_pubsub!
         
     | 
| 
       186 
     | 
    
         
            -
                   
     | 
| 
      
 186 
     | 
    
         
            +
                  AnyCable.broadcast_adapter.announce!
         
     | 
| 
       187 
187 
     | 
    
         
             
                end
         
     | 
| 
       188 
188 
     | 
    
         | 
| 
       189 
189 
     | 
    
         
             
                # rubocop: disable Metrics/MethodLength, Metrics/AbcSize
         
     | 
| 
         @@ -313,9 +313,7 @@ module AnyCable 
     | 
|
| 
       313 
313 
     | 
    
         
             
                        -r, --require=path                Location of application file to require, default: "config/environment.rb"
         
     | 
| 
       314 
314 
     | 
    
         
             
                        --server-command=command          Command to run WebSocket server
         
     | 
| 
       315 
315 
     | 
    
         
             
                        --rpc-host=host                   Local address to run gRPC server on, default: "[::]:50051"
         
     | 
| 
       316 
     | 
    
         
            -
                        -- 
     | 
| 
       317 
     | 
    
         
            -
                        --redis-channel=name              Redis channel for broadcasting, default: "__anycable__"
         
     | 
| 
       318 
     | 
    
         
            -
                        --redis-sentinels=<...hosts>      Redis Sentinel followers addresses (as a comma-separated list), default: nil
         
     | 
| 
      
 316 
     | 
    
         
            +
                        --broadcast-adapter=type          Pub/sub adapter type for broadcasts, default: redis
         
     | 
| 
       319 
317 
     | 
    
         
             
                        --log-level=level                 Logging level, default: "info"
         
     | 
| 
       320 
318 
     | 
    
         
             
                        --log-file=path                   Path to log file, default: <none> (log to STDOUT)
         
     | 
| 
       321 
319 
     | 
    
         
             
                        --log-grpc                        Enable gRPC logging (disabled by default)
         
     | 
| 
         @@ -323,6 +321,15 @@ module AnyCable 
     | 
|
| 
       323 
321 
     | 
    
         
             
                        -v, --version                     Print version and exit
         
     | 
| 
       324 
322 
     | 
    
         
             
                        -h, --help                        Show this help
         
     | 
| 
       325 
323 
     | 
    
         | 
| 
      
 324 
     | 
    
         
            +
                    REDIS PUB/SUB OPTIONS
         
     | 
| 
      
 325 
     | 
    
         
            +
                        --redis-url=url                   Redis URL for pub/sub, default: REDIS_URL or "redis://localhost:6379/5"
         
     | 
| 
      
 326 
     | 
    
         
            +
                        --redis-channel=name              Redis channel for broadcasting, default: "__anycable__"
         
     | 
| 
      
 327 
     | 
    
         
            +
                        --redis-sentinels=<...hosts>      Redis Sentinel followers addresses (as a comma-separated list), default: nil
         
     | 
| 
      
 328 
     | 
    
         
            +
             
     | 
| 
      
 329 
     | 
    
         
            +
                    HTTP PUB/SUB OPTIONS
         
     | 
| 
      
 330 
     | 
    
         
            +
                        --http-broadcast-url              HTTP pub/sub endpoint URL, default: "http://localhost:8090/_broadcast"
         
     | 
| 
      
 331 
     | 
    
         
            +
                        --http-broadcast-secret           HTTP pub/sub authorization secret, default: <none> (disabled)
         
     | 
| 
      
 332 
     | 
    
         
            +
             
     | 
| 
       326 
333 
     | 
    
         
             
                    HTTP HEALTH CHECKER OPTIONS
         
     | 
| 
       327 
334 
     | 
    
         
             
                        --http-health-port=port           Port to run HTTP health server on, default: <none> (disabled)
         
     | 
| 
       328 
335 
     | 
    
         
             
                        --http-health-path=path           Endpoint to server health cheks, default: "/health"
         
     | 
    
        data/lib/anycable/config.rb
    CHANGED
    
    | 
         @@ -19,11 +19,18 @@ module AnyCable 
     | 
|
| 
       19 
19 
     | 
    
         
             
                  # See https://github.com/grpc/grpc/blob/f526602bff029b8db50a8d57134d72da33d8a752/include/grpc/impl/codegen/grpc_types.h#L292-L315
         
     | 
| 
       20 
20 
     | 
    
         
             
                  rpc_server_args: {},
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
                  ## PubSub
         
     | 
| 
      
 23 
     | 
    
         
            +
                  broadcast_adapter: :redis,
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
       22 
25 
     | 
    
         
             
                  ### Redis options
         
     | 
| 
       23 
26 
     | 
    
         
             
                  redis_url: ENV.fetch("REDIS_URL", "redis://localhost:6379/5"),
         
     | 
| 
       24 
27 
     | 
    
         
             
                  redis_sentinels: nil,
         
     | 
| 
       25 
28 
     | 
    
         
             
                  redis_channel: "__anycable__",
         
     | 
| 
       26 
29 
     | 
    
         | 
| 
      
 30 
     | 
    
         
            +
                  ### HTTP broadcasting options
         
     | 
| 
      
 31 
     | 
    
         
            +
                  http_broadcast_url: "http://localhost:8090/_broadcast",
         
     | 
| 
      
 32 
     | 
    
         
            +
                  http_broadcast_secret: nil,
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
       27 
34 
     | 
    
         
             
                  ### Logging options
         
     | 
| 
       28 
35 
     | 
    
         
             
                  log_file: nil,
         
     | 
| 
       29 
36 
     | 
    
         
             
                  log_level: :info,
         
     | 
| 
         @@ -91,12 +98,11 @@ module AnyCable 
     | 
|
| 
       91 
98 
     | 
    
         
             
                  {url: redis_url}.tap do |params|
         
     | 
| 
       92 
99 
     | 
    
         
             
                    next if redis_sentinels.nil?
         
     | 
| 
       93 
100 
     | 
    
         | 
| 
       94 
     | 
    
         
            -
                     
     | 
| 
       95 
     | 
    
         
            -
                      redis_sentinels.is_a?(Array)
         
     | 
| 
      
 101 
     | 
    
         
            +
                    sentinels = Array(redis_sentinels)
         
     | 
| 
       96 
102 
     | 
    
         | 
| 
       97 
     | 
    
         
            -
                    next if  
     | 
| 
      
 103 
     | 
    
         
            +
                    next if sentinels.empty?
         
     | 
| 
       98 
104 
     | 
    
         | 
| 
       99 
     | 
    
         
            -
                    params[:sentinels] =  
     | 
| 
      
 105 
     | 
    
         
            +
                    params[:sentinels] = sentinels.map(&method(:parse_sentinel))
         
     | 
| 
       100 
106 
     | 
    
         
             
                  end
         
     | 
| 
       101 
107 
     | 
    
         
             
                end
         
     | 
| 
       102 
108 
     | 
    
         | 
| 
         @@ -113,13 +119,13 @@ module AnyCable 
     | 
|
| 
       113 
119 
     | 
    
         
             
                SENTINEL_RXP = /^([\w\-_]*)\:(\d+)$/.freeze
         
     | 
| 
       114 
120 
     | 
    
         | 
| 
       115 
121 
     | 
    
         
             
                def parse_sentinel(sentinel)
         
     | 
| 
       116 
     | 
    
         
            -
                  return sentinel if sentinel.is_a?(Hash)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  return sentinel.transform_keys!(&:to_sym) if sentinel.is_a?(Hash)
         
     | 
| 
       117 
123 
     | 
    
         | 
| 
       118 
124 
     | 
    
         
             
                  matches = sentinel.match(SENTINEL_RXP)
         
     | 
| 
       119 
125 
     | 
    
         | 
| 
       120 
126 
     | 
    
         
             
                  raise ArgumentError, "Invalid Sentinel value: #{sentinel}" if matches.nil?
         
     | 
| 
       121 
127 
     | 
    
         | 
| 
       122 
     | 
    
         
            -
                  { 
     | 
| 
      
 128 
     | 
    
         
            +
                  {host: matches[1], port: matches[2].to_i}
         
     | 
| 
       123 
129 
     | 
    
         
             
                end
         
     | 
| 
       124 
130 
     | 
    
         
             
              end
         
     | 
| 
       125 
131 
     | 
    
         
             
            end
         
     | 
| 
         @@ -20,7 +20,7 @@ module AnyCable 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                attr_reader :grpc_server, :port, :path, :server
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                def initialize(grpc_server, port:, path: "/health" 
     | 
| 
      
 23 
     | 
    
         
            +
                def initialize(grpc_server, port:, logger: nil, path: "/health")
         
     | 
| 
       24 
24 
     | 
    
         
             
                  @grpc_server = grpc_server
         
     | 
| 
       25 
25 
     | 
    
         
             
                  @port = port
         
     | 
| 
       26 
26 
     | 
    
         
             
                  @path = path
         
     | 
| 
         @@ -48,7 +48,9 @@ module AnyCable 
     | 
|
| 
       48 
48 
     | 
    
         | 
| 
       49 
49 
     | 
    
         
             
                private
         
     | 
| 
       50 
50 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                 
     | 
| 
      
 51 
     | 
    
         
            +
                def logger
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @logger ||= AnyCable.logger
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
       52 
54 
     | 
    
         | 
| 
       53 
55 
     | 
    
         
             
                def build_server
         
     | 
| 
       54 
56 
     | 
    
         
             
                  require "webrick"
         
     | 
    
        data/lib/anycable/middleware.rb
    CHANGED
    
    | 
         @@ -5,7 +5,7 @@ require "grpc" 
     | 
|
| 
       5 
5 
     | 
    
         
             
            module AnyCable
         
     | 
| 
       6 
6 
     | 
    
         
             
              # Middleware is a wrapper over gRPC interceptors
         
     | 
| 
       7 
7 
     | 
    
         
             
              # for request/response calls
         
     | 
| 
       8 
     | 
    
         
            -
              class Middleware < GRPC:: 
     | 
| 
      
 8 
     | 
    
         
            +
              class Middleware < GRPC::ServerInterceptor
         
     | 
| 
       9 
9 
     | 
    
         
             
                def request_response(request: nil, call: nil, method: nil)
         
     | 
| 
       10 
10 
     | 
    
         
             
                  # Call middlewares only for AnyCable service
         
     | 
| 
       11 
11 
     | 
    
         
             
                  return yield unless method.receiver.is_a?(AnyCable::RPCHandler)
         
     | 
| 
         @@ -15,6 +15,11 @@ module AnyCable 
     | 
|
| 
       15 
15 
     | 
    
         
             
                  end
         
     | 
| 
       16 
16 
     | 
    
         
             
                end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
                def server_streamer(**kwargs)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  p kwargs
         
     | 
| 
      
 20 
     | 
    
         
            +
                  yield
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       18 
23 
     | 
    
         
             
                def call(*)
         
     | 
| 
       19 
24 
     | 
    
         
             
                  raise NotImplementedError
         
     | 
| 
       20 
25 
     | 
    
         
             
                end
         
     | 
    
        data/lib/anycable/rpc.rb
    CHANGED
    
    
    
        data/lib/anycable/rpc/rpc_pb.rb
    CHANGED
    
    | 
         @@ -11,9 +11,11 @@ Google::Protobuf::DescriptorPool.generated_pool.build do 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  optional :url, :string, 1
         
     | 
| 
       12 
12 
     | 
    
         
             
                  map :headers, :string, :string, 2
         
     | 
| 
       13 
13 
     | 
    
         
             
                  map :cstate, :string, :string, 3
         
     | 
| 
      
 14 
     | 
    
         
            +
                  map :istate, :string, :string, 4
         
     | 
| 
       14 
15 
     | 
    
         
             
                end
         
     | 
| 
       15 
16 
     | 
    
         
             
                add_message "anycable.EnvResponse" do
         
     | 
| 
       16 
17 
     | 
    
         
             
                  map :cstate, :string, :string, 1
         
     | 
| 
      
 18 
     | 
    
         
            +
                  map :istate, :string, :string, 2
         
     | 
| 
       17 
19 
     | 
    
         
             
                end
         
     | 
| 
       18 
20 
     | 
    
         
             
                add_message "anycable.ConnectionRequest" do
         
     | 
| 
       19 
21 
     | 
    
         
             
                  optional :env, :message, 3, "anycable.Env"
         
     | 
| 
         @@ -40,6 +42,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do 
     | 
|
| 
       40 
42 
     | 
    
         
             
                  repeated :transmissions, :string, 5
         
     | 
| 
       41 
43 
     | 
    
         
             
                  optional :error_msg, :string, 6
         
     | 
| 
       42 
44 
     | 
    
         
             
                  optional :env, :message, 7, "anycable.EnvResponse"
         
     | 
| 
      
 45 
     | 
    
         
            +
                  repeated :stopped_streams, :string, 8
         
     | 
| 
       43 
46 
     | 
    
         
             
                end
         
     | 
| 
       44 
47 
     | 
    
         
             
                add_message "anycable.DisconnectRequest" do
         
     | 
| 
       45 
48 
     | 
    
         
             
                  optional :identifiers, :string, 1
         
     | 
    
        data/lib/anycable/rpc_handler.rb
    CHANGED
    
    | 
         @@ -86,7 +86,8 @@ module AnyCable 
     | 
|
| 
       86 
86 
     | 
    
         
             
                    status: result ? AnyCable::Status::SUCCESS : AnyCable::Status::FAILURE,
         
     | 
| 
       87 
87 
     | 
    
         
             
                    disconnect: socket.closed?,
         
     | 
| 
       88 
88 
     | 
    
         
             
                    stop_streams: socket.stop_streams?,
         
     | 
| 
       89 
     | 
    
         
            -
                    streams: socket.streams,
         
     | 
| 
      
 89 
     | 
    
         
            +
                    streams: socket.streams[:start],
         
     | 
| 
      
 90 
     | 
    
         
            +
                    stopped_streams: socket.streams[:stop],
         
     | 
| 
       90 
91 
     | 
    
         
             
                    transmissions: socket.transmissions,
         
     | 
| 
       91 
92 
     | 
    
         
             
                    env: build_env_response(socket)
         
     | 
| 
       92 
93 
     | 
    
         
             
                  )
         
     | 
| 
         @@ -115,7 +116,8 @@ module AnyCable 
     | 
|
| 
       115 
116 
     | 
    
         
             
                    "REMOTE_ADDR" => request_env.headers.delete("REMOTE_ADDR"),
         
     | 
| 
       116 
117 
     | 
    
         
             
                    "rack.url_scheme" => uri.scheme&.sub(/^ws/, "http"),
         
     | 
| 
       117 
118 
     | 
    
         
             
                    # AnyCable specific fields
         
     | 
| 
       118 
     | 
    
         
            -
                    "anycable.raw_cstate" => request_env.cstate&.to_h
         
     | 
| 
      
 119 
     | 
    
         
            +
                    "anycable.raw_cstate" => request_env.cstate&.to_h,
         
     | 
| 
      
 120 
     | 
    
         
            +
                    "anycable.raw_istate" => request_env.istate&.to_h
         
     | 
| 
       119 
121 
     | 
    
         
             
                  }.delete_if { |_k, v| v.nil? })
         
     | 
| 
       120 
122 
     | 
    
         | 
| 
       121 
123 
     | 
    
         
             
                  env.merge!(build_headers(request_env.headers))
         
     | 
| 
         @@ -158,7 +160,8 @@ module AnyCable 
     | 
|
| 
       158 
160 
     | 
    
         | 
| 
       159 
161 
     | 
    
         
             
                def build_env_response(socket)
         
     | 
| 
       160 
162 
     | 
    
         
             
                  AnyCable::EnvResponse.new(
         
     | 
| 
       161 
     | 
    
         
            -
                    cstate: socket.cstate.changed_fields
         
     | 
| 
      
 163 
     | 
    
         
            +
                    cstate: socket.cstate.changed_fields,
         
     | 
| 
      
 164 
     | 
    
         
            +
                    istate: socket.istate.changed_fields
         
     | 
| 
       162 
165 
     | 
    
         
             
                  )
         
     | 
| 
       163 
166 
     | 
    
         
             
                end
         
     | 
| 
       164 
167 
     | 
    
         | 
    
        data/lib/anycable/server.rb
    CHANGED
    
    | 
         @@ -23,7 +23,7 @@ module AnyCable 
     | 
|
| 
       23 
23 
     | 
    
         
             
              class Server
         
     | 
| 
       24 
24 
     | 
    
         
             
                attr_reader :grpc_server, :host
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
                def initialize(host:, logger:  
     | 
| 
      
 26 
     | 
    
         
            +
                def initialize(host:, logger: nil, **options)
         
     | 
| 
       27 
27 
     | 
    
         
             
                  @logger = logger
         
     | 
| 
       28 
28 
     | 
    
         
             
                  @host = host
         
     | 
| 
       29 
29 
     | 
    
         
             
                  @grpc_server = build_server(options)
         
     | 
| 
         @@ -70,7 +70,11 @@ module AnyCable 
     | 
|
| 
       70 
70 
     | 
    
         | 
| 
       71 
71 
     | 
    
         
             
                private
         
     | 
| 
       72 
72 
     | 
    
         | 
| 
       73 
     | 
    
         
            -
                attr_reader : 
     | 
| 
      
 73 
     | 
    
         
            +
                attr_reader :start_thread
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def logger
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @logger ||= AnyCable.logger
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
       74 
78 
     | 
    
         | 
| 
       75 
79 
     | 
    
         
             
                def build_server(options)
         
     | 
| 
       76 
80 
     | 
    
         
             
                  GRPC::RpcServer.new(**options).tap do |server|
         
     | 
    
        data/lib/anycable/socket.rb
    CHANGED
    
    | 
         @@ -17,6 +17,8 @@ module AnyCable 
     | 
|
| 
       17 
17 
     | 
    
         
             
                    source&.[](key)
         
     | 
| 
       18 
18 
     | 
    
         
             
                  end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
      
 20 
     | 
    
         
            +
                  alias [] read
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
       20 
22 
     | 
    
         
             
                  def write(key, val)
         
     | 
| 
       21 
23 
     | 
    
         
             
                    return if source&.[](key) == val
         
     | 
| 
       22 
24 
     | 
    
         | 
| 
         @@ -26,18 +28,21 @@ module AnyCable 
     | 
|
| 
       26 
28 
     | 
    
         
             
                    source[key] = val
         
     | 
| 
       27 
29 
     | 
    
         
             
                  end
         
     | 
| 
       28 
30 
     | 
    
         | 
| 
      
 31 
     | 
    
         
            +
                  alias []= write
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       29 
33 
     | 
    
         
             
                  def changed_fields
         
     | 
| 
       30 
34 
     | 
    
         
             
                    return unless source && dirty_keys
         
     | 
| 
       31 
35 
     | 
    
         
             
                    source.slice(*dirty_keys)
         
     | 
| 
       32 
36 
     | 
    
         
             
                  end
         
     | 
| 
       33 
37 
     | 
    
         
             
                end
         
     | 
| 
       34 
38 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                attr_reader :transmissions, :env, :cstate
         
     | 
| 
      
 39 
     | 
    
         
            +
                attr_reader :transmissions, :env, :cstate, :istate
         
     | 
| 
       36 
40 
     | 
    
         | 
| 
       37 
41 
     | 
    
         
             
                def initialize(env: nil)
         
     | 
| 
       38 
42 
     | 
    
         
             
                  @transmissions = []
         
     | 
| 
       39 
43 
     | 
    
         
             
                  @env = env
         
     | 
| 
       40 
44 
     | 
    
         
             
                  @cstate = env["anycable.cstate"] = State.new(env["anycable.raw_cstate"])
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @istate = env["anycable.istate"] = State.new(env["anycable.raw_istate"])
         
     | 
| 
       41 
46 
     | 
    
         
             
                end
         
     | 
| 
       42 
47 
     | 
    
         | 
| 
       43 
48 
     | 
    
         
             
                def transmit(websocket_message)
         
     | 
| 
         @@ -45,11 +50,11 @@ module AnyCable 
     | 
|
| 
       45 
50 
     | 
    
         
             
                end
         
     | 
| 
       46 
51 
     | 
    
         | 
| 
       47 
52 
     | 
    
         
             
                def subscribe(_channel, broadcasting)
         
     | 
| 
       48 
     | 
    
         
            -
                  streams << broadcasting
         
     | 
| 
      
 53 
     | 
    
         
            +
                  streams[:start] << broadcasting
         
     | 
| 
       49 
54 
     | 
    
         
             
                end
         
     | 
| 
       50 
55 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                def unsubscribe(_channel,  
     | 
| 
       52 
     | 
    
         
            -
                   
     | 
| 
      
 56 
     | 
    
         
            +
                def unsubscribe(_channel, broadcasting)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  streams[:stop] << broadcasting
         
     | 
| 
       53 
58 
     | 
    
         
             
                end
         
     | 
| 
       54 
59 
     | 
    
         | 
| 
       55 
60 
     | 
    
         
             
                def unsubscribe_from_all(_channel)
         
     | 
| 
         @@ -57,7 +62,7 @@ module AnyCable 
     | 
|
| 
       57 
62 
     | 
    
         
             
                end
         
     | 
| 
       58 
63 
     | 
    
         | 
| 
       59 
64 
     | 
    
         
             
                def streams
         
     | 
| 
       60 
     | 
    
         
            -
                  @streams ||= []
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @streams ||= {start: [], stop: []}
         
     | 
| 
       61 
66 
     | 
    
         
             
                end
         
     | 
| 
       62 
67 
     | 
    
         | 
| 
       63 
68 
     | 
    
         
             
                def close
         
     | 
    
        data/lib/anycable/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: anycable
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1.0.0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.0.0.rc1
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - palkan
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2020- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2020-06-10 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: anyway_config
         
     | 
| 
         @@ -108,6 +108,20 @@ dependencies: 
     | 
|
| 
       108 
108 
     | 
    
         
             
                - - ">="
         
     | 
| 
       109 
109 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       110 
110 
     | 
    
         
             
                    version: '3.5'
         
     | 
| 
      
 111 
     | 
    
         
            +
            - !ruby/object:Gem::Dependency
         
     | 
| 
      
 112 
     | 
    
         
            +
              name: webmock
         
     | 
| 
      
 113 
     | 
    
         
            +
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
      
 114 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 115 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 116 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 117 
     | 
    
         
            +
                    version: '3.8'
         
     | 
| 
      
 118 
     | 
    
         
            +
              type: :development
         
     | 
| 
      
 119 
     | 
    
         
            +
              prerelease: false
         
     | 
| 
      
 120 
     | 
    
         
            +
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
      
 121 
     | 
    
         
            +
                requirements:
         
     | 
| 
      
 122 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
      
 123 
     | 
    
         
            +
                  - !ruby/object:Gem::Version
         
     | 
| 
      
 124 
     | 
    
         
            +
                    version: '3.8'
         
     | 
| 
       111 
125 
     | 
    
         
             
            description: AnyCable is a polyglot replacement for ActionCable-compatible servers
         
     | 
| 
       112 
126 
     | 
    
         
             
            email:
         
     | 
| 
       113 
127 
     | 
    
         
             
            - dementiev.vm@gmail.com
         
     | 
| 
         @@ -126,6 +140,8 @@ files: 
     | 
|
| 
       126 
140 
     | 
    
         
             
            - bin/setup
         
     | 
| 
       127 
141 
     | 
    
         
             
            - lib/anycable.rb
         
     | 
| 
       128 
142 
     | 
    
         
             
            - lib/anycable/broadcast_adapters.rb
         
     | 
| 
      
 143 
     | 
    
         
            +
            - lib/anycable/broadcast_adapters/base.rb
         
     | 
| 
      
 144 
     | 
    
         
            +
            - lib/anycable/broadcast_adapters/http.rb
         
     | 
| 
       129 
145 
     | 
    
         
             
            - lib/anycable/broadcast_adapters/redis.rb
         
     | 
| 
       130 
146 
     | 
    
         
             
            - lib/anycable/cli.rb
         
     | 
| 
       131 
147 
     | 
    
         
             
            - lib/anycable/config.rb
         
     |