anycable 0.4.6 → 0.5.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 54ab3e54c4fae8665342c024ebd87b6eba1dbf24
4
- data.tar.gz: b137ecc42083c6bf34cf8e91cfb8644bac32a385
3
+ metadata.gz: 2c34f2dcdd663a7a4197be6ae56359d8e1f15fd7
4
+ data.tar.gz: 43eebb1490b11a6fdbf3d7aa65658f8cc25cd48f
5
5
  SHA512:
6
- metadata.gz: c052ba9b5aa8d283ae5da3e55bc538b9de144d8c8fadf6f768d22160a00c2ea566382b2dcb04157e00f2c3e76d08b0fb680acf055a50d183a1bf4184c8aea192
7
- data.tar.gz: 950b9c4ba2f6abf5857f3d9abbeb9183bce9b9b5a524d89c2b002294a06bc7d10a06d9a36573684a1bf6b11a759f0897ff7b09764c3d6805fb40a72636118f18
6
+ metadata.gz: 2a684670f4fe590b9cc9da89c92c94bb42e3168cb531e75ff4a21cda4c4fb39683041bd40da4bbaaaaf8b331910c00453971c8571e302521b016116d1747d868
7
+ data.tar.gz: e0f67df078fa371f6d989ec03d8deced4908e61a1f37c206b1a77ecbff92e675c80f604dc7fa22dc45fbfb9fe3d4227fe505f55bc787c8d37279942325cf96d2
@@ -9,14 +9,21 @@ AllCops:
9
9
  - 'spec/dummy/**/*'
10
10
  - 'tmp/**/*'
11
11
  - 'bench/**/*'
12
+ - 'vendor/**/*'
12
13
  - 'lib/anycable/rpc/**/*'
14
+ - 'Gemfile'
15
+ - 'Rakefile'
16
+ - '*.gemspec'
13
17
  DisplayCopNames: true
14
18
  StyleGuideCopsOnly: false
15
- TargetRubyVersion: 2.3
19
+ TargetRubyVersion: 2.4
16
20
 
17
21
  Style/AccessorMethodName:
18
22
  Enabled: false
19
23
 
24
+ Style/PercentLiteralDelimiters:
25
+ Enabled: false
26
+
20
27
  Style/TrivialAccessors:
21
28
  Enabled: false
22
29
 
@@ -1,5 +1,36 @@
1
1
  # Change log
2
2
 
3
+ ## 0.5.0 (master)
4
+
5
+ - [#2](https://github.com/anycable/anycable/issues/2) Add support for [Redis Sentinel](https://redis.io/topics/sentinel). ([@accessd](https://github.com/accessd))
6
+
7
+ - [#28](https://github.com/anycable/anycable/issues/28) Support arbitrary headers. ([@palkan][])
8
+
9
+ Previously we hardcoded only "Cookie" header. Now we add all passed headers by WebSocket server to request env.
10
+
11
+ - [#27](https://github.com/anycable/anycable/issues/27) Add `error_msg` to RPC responses. ([@palkan][])
12
+
13
+ Now RPC responses has 3 statuses:
14
+
15
+ - `SUCCESS` – successful request, operation succeed
16
+ - `FAILURE` – successful request, operation failed (e.g. authentication failed)
17
+ - `ERROR` – request failed (exception raised).
18
+
19
+ We provide `error_msg` only when request status is `ERROR`.
20
+
21
+ - [#25](https://github.com/anycable/anycable/issues/25) Improve logging and exceptions handling. ([@palkan][])
22
+
23
+ Default logger logs to STDOUT with `info` level by default but can be configured to log to file with
24
+ any severity.
25
+
26
+ GRPC logging is turned off by default (can be turned on through `log_grpc` configuration parameter).
27
+
28
+ `ANYCABLE_DEBUG=1` acts as a shortcut to set `debug` level and turn on GRPC logging.
29
+
30
+ Now it's possible to add custom exception handlers (e.g. to notify external exception tracking services).
31
+
32
+ More on [Wiki](https://github.com/anycable/anycable/wiki/Logging-&-Exceptions-Handling).
33
+
3
34
  ## 0.4.6 (2017-05-20)
4
35
 
5
36
  - Add `Anycable::Server#stop` method. ([@sadovnik][])
@@ -53,4 +84,4 @@ All Rails specifics now live here https://github.com/anycable/anycable-rails.
53
84
  Implement `Disconnect` handler, which invokes `Connection#disconnect` (along with `Channel#unsubscribed` for each subscription).
54
85
 
55
86
  [@palkan]: https://github.com/palkan
56
- [@sadovnik]: https://github.com/sadovnik
87
+ [@sadovnik]: https://github.com/sadovnik
data/Makefile CHANGED
@@ -1,4 +1,5 @@
1
1
  all: build
2
2
 
3
3
  build:
4
- protoc --ruby_out=./lib/anycable/rpc --grpc_out=./lib/anycable/rpc --proto_path=./protos --plugin=protoc-gen-grpc=`which grpc_tools_ruby_protoc_plugin.rb` ./protos/rpc.proto
4
+ grpc_tools_ruby_protoc -I ./protos --ruby_out=./lib/anycable/rpc --grpc_out=./lib/anycable/rpc ./protos/rpc.proto
5
+ sed -i '' '/'rpc_pb'/d' ./lib/anycable/rpc/rpc_services_pb.rb
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  [![GitPitch](https://gitpitch.com/assets/badge.svg)](https://gitpitch.com/anycable/anycable/master?grs=github) [![Gem Version](https://badge.fury.io/rb/anycable.svg)](https://rubygems.org/gems/anycable) [![Build Status](https://travis-ci.org/anycable/anycable.svg?branch=master)](https://travis-ci.org/anycable/anycable) [![Circle CI](https://circleci.com/gh/anycable/anycable/tree/master.svg?style=svg)](https://circleci.com/gh/anycable/anycable/tree/master)
2
+ [![Dependency Status](https://dependencyci.com/github/anycable/anycable/badge)](https://dependencyci.com/github/anycable/anycable)
2
3
  [![Gitter](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/anycable/Lobby)
3
4
 
4
5
  # Anycable
@@ -7,7 +8,7 @@ AnyCable allows you to use any WebSocket server (written in any language) as a r
7
8
 
8
9
  AnyCable uses ActionCable protocol, so you can use ActionCable [JavaScript client](https://www.npmjs.com/package/actioncable) without any monkey-patching.
9
10
 
10
- **NOTE**: Since version 0.4.0 this repository contains only core functionality and connot be used separately as is.
11
+ **NOTE**: Since version 0.4.0 this repository contains only core functionality and cannot be used separately as is.
11
12
  Rails plug-n-play integration has been extracted to [anycable-rails](https://github.com/anycable/anycable-rails) gem.
12
13
 
13
14
  <a href="https://evilmartians.com/">
@@ -40,11 +41,22 @@ Read our [Wiki](https://github.com/anycable/anycable/wiki) for more.
40
41
 
41
42
  Anycable uses [anyway_config](https://github.com/palkan/anyway_config), thus it is also possible to set configuration variables through `secrets.yml` or environment vars.
42
43
 
44
+ ### Example with redis sentinel
45
+
46
+ ```yaml
47
+ rpc_host: "localhost:50123"
48
+ redis_url: "redis://redis-1-1:6379/2"
49
+ redis_sentinels:
50
+ - { host: 'redis-1-1', port: 26379 }
51
+ - { host: 'redis-1-2', port: 26379 }
52
+ - { host: 'redis-1-3', port: 26379 }
53
+ ```
54
+
43
55
  ## ActionCable Compatibility
44
56
 
45
57
  This is the compatibility list for the AnyCable gem, not for AnyCable servers (which may not support some of the features yet).
46
58
 
47
- Feature | Status
59
+ Feature | Status
48
60
  -------------------------|--------
49
61
  Connection Identifiers | +
50
62
  Connection Request (cookies, params) | +
@@ -58,6 +70,21 @@ Streaming | +
58
70
  [Custom stream callbacks](http://edgeapi.rubyonrails.org/classes/ActionCable/Channel/Streams.html) | -
59
71
  Broadcasting | +
60
72
 
73
+ ## Build
74
+
75
+ - Install required GRPC gems:
76
+
77
+ ```
78
+ gem install grpc
79
+ gem install grpc-tools
80
+ ```
81
+
82
+ - Re-generate GRPC files (if necessary):
83
+
84
+ ```
85
+ make
86
+ ```
87
+
61
88
  ## Contributing
62
89
 
63
90
  Bug reports and pull requests are welcome on GitHub at https://github.com/anycable/anycable.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require "rubocop/rake_task"
3
4
 
4
5
  RSpec::Core::RakeTask.new(:spec)
6
+ RuboCop::RakeTask.new
5
7
 
6
- task default: :spec
8
+ task default: [:rubocop, :spec]
@@ -1,4 +1,5 @@
1
- # coding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  lib = File.expand_path('../lib', __FILE__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'anycable/version'
@@ -17,14 +18,15 @@ Gem::Specification.new do |spec|
17
18
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
19
  spec.require_paths = ["lib"]
19
20
 
20
- spec.add_dependency "anyway_config", "~>0.5.0"
21
- spec.add_dependency "grpc", "~> 1.1"
22
- spec.add_dependency "redis", "~> 3.0"
21
+ spec.add_dependency "anyway_config", "~> 1.1"
22
+ spec.add_dependency "grpc", "~> 1.6"
23
+ spec.add_dependency "redis", ">= 3.2"
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1"
25
26
  spec.add_development_dependency "rake", ">= 10.0"
26
27
  spec.add_development_dependency "rack", "~> 2.0"
27
28
  spec.add_development_dependency "rspec", ">= 3.5"
29
+ spec.add_development_dependency "rubocop", ">= 0.50"
28
30
  spec.add_development_dependency "simplecov", ">= 0.3.8"
29
31
  spec.add_development_dependency "pry-byebug"
30
32
  end
@@ -0,0 +1 @@
1
+ *.retry
@@ -0,0 +1,23 @@
1
+ ## Hot to run benchmarks
2
+
3
+ ### Installation
4
+
5
+ - Install [Ansible](http://ansible.com) (`brew install ansible`)
6
+
7
+ - Launch two EC2 instances from `anycable-benchmark-xxx` AMI
8
+
9
+ - Attach more local network interfaces to the _client_ instance
10
+
11
+ - Update `hosts` file and playbooks with required information (IPs, etc)
12
+
13
+ - Prepare the _client_ instance: `ansible-playbook --extra-vars="prepare=True" benchmark.yml`
14
+
15
+ ### Running benchmarks
16
+
17
+ To run a server, e.g. Action Cable: `ansible-playbook --tags action_cable servers.yml`
18
+
19
+ To run a benchmark against it: `ansible-playbook --tags action_cable benchmark.yml`
20
+
21
+ You can also specify benchmark parameters: `ansible-playbook --extra-vars "step_size=1000 steps=10 sample_size=10" benchmark.yml`.
22
+
23
+ **NOTE**: Ansible doesn't support command output streaming, so we can only see the results at the end of the run.
@@ -0,0 +1,9 @@
1
+ [defaults]
2
+ deprecation_warnings = False
3
+ inventory = ./hosts
4
+ allow_world_readable_tmpfiles = True
5
+ gathering=smart
6
+ transport=paramiko
7
+
8
+ [ssh_connection]
9
+ pipelining = True
@@ -0,0 +1,40 @@
1
+ ---
2
+ - name: Benchmark Client
3
+ hosts: benchmark
4
+ sudo: yes
5
+ remote_user: ubuntu
6
+ gather_facts: False
7
+ vars:
8
+ server_host: '172.31.22.14'
9
+ hostname: ws-bench-client
10
+ local_ips: ['172.31.21.241', '172.31.21.242', '172.31.21.243', '172.31.21.244']
11
+ local_ips_str: '-l 172.31.21.241 -l 172.31.21.242 -l 172.31.21.243 -l 172.31.21.244'
12
+ steps: 10
13
+ step_size: 1000
14
+ sample_size: 40
15
+ prepare: False
16
+ tasks:
17
+ - name: Prepare the machine
18
+ when: prepare
19
+ block:
20
+ - hostname:
21
+ name: "{{ hostname }}"
22
+ - lineinfile:
23
+ path: /etc/hosts
24
+ line: '127.0.0.1 {{ hostname }}'
25
+ state: present
26
+ - shell: ip addr add {{ item }}/20 dev eth0
27
+ with_items: "{{ local_ips }}"
28
+ - name: Action Cable benchmark
29
+ become_user: deplo
30
+ shell: bin/websocket-bench broadcast {{ local_ips_str }} --concurrent 4 --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding 200 --total-steps {{ steps }} --origin http://0.0.0.0 ws://{{ server_host }}:3334/cable --server-type=actioncable
31
+ register: bench
32
+ tags: action_cable
33
+ args:
34
+ chdir: /webapps/anycable_bench
35
+ ignore_errors: yes
36
+
37
+ - name: Benchmark results
38
+ debug: var=bench.stdout_lines
39
+ tags:
40
+ - action_cable
@@ -0,0 +1,5 @@
1
+ [benchmark]
2
+ ec2-52-208-189-15.eu-west-1.compute.amazonaws.com ansible_ssh_private_key_file=/Users/palkan/.ssh/macos-dev
3
+
4
+ [servers]
5
+ ec2-52-211-15-119.eu-west-1.compute.amazonaws.com ansible_ssh_private_key_file=/Users/palkan/.ssh/macos-dev
@@ -0,0 +1,29 @@
1
+ ---
2
+ - name: Benchmark Servers
3
+ hosts: servers
4
+ sudo: yes
5
+ remote_user: ubuntu
6
+ gather_facts: False
7
+ vars:
8
+ rails_webc: 8
9
+ tasks:
10
+ - name: Kill servers
11
+ shell: pid=$(lsof -i:{{item}} -t); kill -TERM $pid || kill -KILL $pid
12
+ tags:
13
+ - action_cable
14
+ - anycable
15
+ with_items:
16
+ - "3334"
17
+ ignore_errors: true
18
+ - name: Run Action Cable
19
+ become_user: deplo
20
+ tags: action_cable
21
+ shell: WEB_CONCURRENCY={{ rails_webc }} bundle exec rails s -p 3334 -e production
22
+ args:
23
+ chdir: /webapps/anycable_bench/ruby/action-cable-server
24
+ - name: Run Anycable Go
25
+ become_user: deplo
26
+ tags: anycable
27
+ shell: bundle exec anycable
28
+ args:
29
+ chdir: /webapps/anycable_bench/ruby/action-cable-server/bin
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "anycable/version"
3
4
  require "anycable/config"
4
- require "anycable/server"
5
- require "anycable/pubsub"
6
5
  require "logger"
7
6
 
8
7
  # Anycable allows to use any websocket service (written in any language) as a replacement
@@ -14,12 +13,21 @@ require "logger"
14
13
  # Broadcasting messages to WS is done through Redis Pub/Sub.
15
14
  module Anycable
16
15
  class << self
16
+ # Provide connection factory which
17
+ # is a callable object with build
18
+ # a Connection object
19
+ attr_accessor :connection_factory
20
+
17
21
  def logger=(logger)
18
22
  @logger = logger
19
23
  end
20
24
 
21
25
  def logger
22
- @logger ||= Anycable.config.debug ? Logger.new(STDOUT) : Logger.new('/dev/null')
26
+ return @logger if instance_variable_defined?(:@logger)
27
+ log_output = Anycable.config.log_file || STDOUT
28
+ @logger = Logger.new(log_output).tap do |logger|
29
+ logger.level = Anycable.config.log_level
30
+ end
23
31
  end
24
32
 
25
33
  def config
@@ -30,6 +38,11 @@ module Anycable
30
38
  yield(config) if block_given?
31
39
  end
32
40
 
41
+ def error_handlers
42
+ return @error_handlers if instance_variable_defined?(:@error_handlers)
43
+ @error_handlers = []
44
+ end
45
+
33
46
  def pubsub
34
47
  @pubsub ||= PubSub.new
35
48
  end
@@ -40,3 +53,6 @@ module Anycable
40
53
  end
41
54
  end
42
55
  end
56
+
57
+ require "anycable/server"
58
+ require "anycable/pubsub"
@@ -1,15 +1,27 @@
1
1
  # frozen_string_literal: true
2
- require "anyway"
2
+
3
+ require "anyway_config"
3
4
 
4
5
  module Anycable
5
- # Anycable configuration
6
+ # Anycable configuration.
6
7
  class Config < Anyway::Config
7
8
  config_name :anycable
8
9
 
9
- attr_config :connection_factory,
10
- rpc_host: "localhost:50051",
10
+ attr_config rpc_host: "localhost:50051",
11
11
  redis_url: "redis://localhost:6379/5",
12
- redis_channel: "anycable",
13
- debug: false
12
+ redis_sentinels: [],
13
+ redis_channel: "__anycable__",
14
+ log_file: nil,
15
+ log_level: :info,
16
+ log_grpc: false,
17
+ debug: false # Shortcut to enable GRPC logging and debug level
18
+
19
+ def initialize(*)
20
+ super
21
+ # Set log params if debug is true
22
+ return unless debug
23
+ self.log_level = :debug
24
+ self.log_grpc = true
25
+ end
14
26
  end
15
27
  end
@@ -1,29 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anycable
4
- module Handler
4
+ module Handler # :nodoc:
5
5
  # Handle app-level errors
6
6
  module ExceptionsHandling
7
7
  def connect(*)
8
8
  super
9
- rescue StandardError => e
10
- logger.error(e.message)
11
- Anycable::ConnectionResponse.new(status: Anycable::Status::ERROR)
9
+ rescue StandardError => ex
10
+ handle_exception(ex)
11
+ Anycable::ConnectionResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
12
12
  end
13
13
 
14
14
  def disconnect(*)
15
15
  super
16
- rescue StandardError => e
17
- logger.error(e.message)
18
- Anycable::DisconnectResponse.new(status: Anycable::Status::ERROR)
16
+ rescue StandardError => ex
17
+ handle_exception(ex)
18
+ Anycable::DisconnectResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
19
19
  end
20
20
 
21
21
  def command(*)
22
22
  super
23
- rescue StandardError => e
24
- logger.error(e.message)
25
- Anycable::CommandResponse.new(status: Anycable::Status::ERROR)
23
+ rescue StandardError => ex
24
+ handle_exception(ex)
25
+ Anycable::CommandResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
26
+ end
27
+
28
+ def handle_exception(ex)
29
+ Anycable.error_handlers.each do |handler|
30
+ begin
31
+ handler.call(ex)
32
+ rescue StandardError => ex
33
+ Anycable.logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
34
+ Anycable.logger.error ex
35
+ Anycable.logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
36
+ end
37
+ end
26
38
  end
27
39
  end
40
+
41
+ Anycable.error_handlers << proc { |e| Anycable.logger.error(e.message) }
28
42
  end
29
43
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require "redis"
3
4
  require "json"
4
5
 
@@ -8,7 +9,11 @@ module Anycable
8
9
  attr_reader :redis_conn
9
10
 
10
11
  def initialize
11
- @redis_conn = Redis.new(url: Anycable.config.redis_url)
12
+ redis_config = { url: Anycable.config.redis_url }
13
+ unless Anycable.config.redis_sentinels.empty?
14
+ redis_config[:sentinels] = Anycable.config.redis_sentinels
15
+ end
16
+ @redis_conn = Redis.new(redis_config)
12
17
  end
13
18
 
14
19
  def broadcast(channel, payload)
@@ -12,6 +12,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
12
12
  optional :status, :enum, 1, "anycable.Status"
13
13
  optional :identifiers, :string, 2
14
14
  repeated :transmissions, :string, 3
15
+ optional :error_msg, :string, 4
15
16
  end
16
17
  add_message "anycable.CommandMessage" do
17
18
  optional :command, :string, 1
@@ -25,6 +26,7 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
25
26
  optional :stop_streams, :bool, 3
26
27
  repeated :streams, :string, 4
27
28
  repeated :transmissions, :string, 5
29
+ optional :error_msg, :string, 6
28
30
  end
29
31
  add_message "anycable.DisconnectRequest" do
30
32
  optional :identifiers, :string, 1
@@ -34,10 +36,12 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
34
36
  end
35
37
  add_message "anycable.DisconnectResponse" do
36
38
  optional :status, :enum, 1, "anycable.Status"
39
+ optional :error_msg, :string, 2
37
40
  end
38
41
  add_enum "anycable.Status" do
39
42
  value :ERROR, 0
40
43
  value :SUCCESS, 1
44
+ value :FAILURE, 2
41
45
  end
42
46
  end
43
47
 
@@ -5,8 +5,6 @@ require 'grpc'
5
5
 
6
6
  module Anycable
7
7
  module RPC
8
-
9
- # TODO: add proto service documentation here
10
8
  class Service
11
9
 
12
10
  include GRPC::GenericService
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'anycable/socket'
3
- require 'anycable/rpc/rpc'
4
- require 'anycable/rpc/rpc_services'
4
+ require 'anycable/rpc/rpc_pb'
5
+ require 'anycable/rpc/rpc_services_pb'
5
6
 
6
7
  require 'anycable/handler/exceptions_handling'
7
8
 
@@ -18,12 +19,12 @@ module Anycable
18
19
 
19
20
  socket = build_socket(env: rack_env(request))
20
21
 
21
- connection = factory.create(socket)
22
+ connection = factory.call(socket)
22
23
 
23
24
  connection.handle_open
24
25
 
25
26
  if socket.closed?
26
- Anycable::ConnectionResponse.new(status: Anycable::Status::ERROR)
27
+ Anycable::ConnectionResponse.new(status: Anycable::Status::FAILURE)
27
28
  else
28
29
  Anycable::ConnectionResponse.new(
29
30
  status: Anycable::Status::SUCCESS,
@@ -38,7 +39,7 @@ module Anycable
38
39
 
39
40
  socket = build_socket(env: rack_env(request))
40
41
 
41
- connection = factory.create(
42
+ connection = factory.call(
42
43
  socket,
43
44
  identifiers: request.identifiers,
44
45
  subscriptions: request.subscriptions
@@ -47,7 +48,7 @@ module Anycable
47
48
  if connection.handle_close
48
49
  Anycable::DisconnectResponse.new(status: Anycable::Status::SUCCESS)
49
50
  else
50
- Anycable::DisconnectResponse.new(status: Anycable::Status::ERROR)
51
+ Anycable::DisconnectResponse.new(status: Anycable::Status::FAILURE)
51
52
  end
52
53
  end
53
54
 
@@ -56,7 +57,7 @@ module Anycable
56
57
 
57
58
  socket = build_socket
58
59
 
59
- connection = factory.create(
60
+ connection = factory.call(
60
61
  socket,
61
62
  identifiers: message.connection_identifiers
62
63
  )
@@ -68,7 +69,7 @@ module Anycable
68
69
  )
69
70
 
70
71
  Anycable::CommandResponse.new(
71
- status: result ? Anycable::Status::SUCCESS : Anycable::Status::ERROR,
72
+ status: result ? Anycable::Status::SUCCESS : Anycable::Status::FAILURE,
72
73
  disconnect: socket.closed?,
73
74
  stop_streams: socket.stop_streams?,
74
75
  streams: socket.streams,
@@ -87,24 +88,31 @@ module Anycable
87
88
  'PATH_INFO' => uri.path,
88
89
  'SERVER_PORT' => uri.port.to_s,
89
90
  'HTTP_HOST' => uri.host,
90
- 'HTTP_COOKIE' => request.headers['Cookie'],
91
91
  # Hack to avoid Missing rack.input error
92
92
  'rack.request.form_input' => '',
93
93
  'rack.input' => '',
94
94
  'rack.request.form_hash' => {}
95
- }
95
+ }.merge(build_headers(request.headers))
96
96
  end
97
97
 
98
98
  def build_socket(**options)
99
99
  Anycable::Socket.new(**options)
100
100
  end
101
101
 
102
+ def build_headers(headers)
103
+ headers.each_with_object({}) do |(k, v), obj|
104
+ k = k.upcase
105
+ k.tr!('-', '_')
106
+ obj["HTTP_#{k}"] = v
107
+ end
108
+ end
109
+
102
110
  def logger
103
111
  Anycable.logger
104
112
  end
105
113
 
106
114
  def factory
107
- Anycable.config.connection_factory
115
+ Anycable.connection_factory
108
116
  end
109
117
  end
110
118
  end
@@ -1,31 +1,44 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'grpc'
3
4
  require 'anycable/rpc_handler'
4
5
 
5
- # Set GRPC logger
6
- module GRPC
7
- def self.logger
8
- Anycable.logger
9
- end
10
- end
11
-
12
6
  module Anycable
13
7
  # Wrapper over GRPC server
14
8
  module Server
15
9
  class << self
16
- attr_accessor :grpc_server
10
+ attr_reader :grpc_server
17
11
 
18
12
  def start
19
- @grpc_server = GRPC::RpcServer.new
20
- grpc_server.add_http2_port(Anycable.config.rpc_host, :this_port_is_insecure)
21
- grpc_server.handle(Anycable::RPCHandler)
13
+ log_grpc! if Anycable.config.log_grpc
14
+ @grpc_server ||= build_server
15
+
22
16
  Anycable.logger.info "RPC server is listening on #{Anycable.config.rpc_host}"
23
17
  grpc_server.run_till_terminated
24
18
  end
25
19
 
26
20
  def stop
21
+ return unless running?
27
22
  @grpc_server.stop
28
23
  end
24
+
25
+ def running?
26
+ grpc_server&.running_state == :running
27
+ end
28
+
29
+ # Enable GRPC logging
30
+ def log_grpc!
31
+ GRPC.define_singleton_method(:logger) { Anycable.logger }
32
+ end
33
+
34
+ private
35
+
36
+ def build_server
37
+ GRPC::RpcServer.new.tap do |server|
38
+ server.add_http2_port(Anycable.config.rpc_host, :this_port_is_insecure)
39
+ server.handle(Anycable::RPCHandler)
40
+ end
41
+ end
29
42
  end
30
43
  end
31
44
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Anycable
3
4
  # Socket mock to be used with application connection
4
5
  class Socket
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Anycable
3
- VERSION = "0.4.6"
4
+ VERSION = "0.5.0.rc1"
4
5
  end
@@ -11,6 +11,7 @@ service RPC {
11
11
  enum Status {
12
12
  ERROR = 0;
13
13
  SUCCESS = 1;
14
+ FAILURE = 2;
14
15
  }
15
16
 
16
17
  message ConnectionRequest {
@@ -22,6 +23,7 @@ message ConnectionResponse {
22
23
  Status status = 1;
23
24
  string identifiers = 2;
24
25
  repeated string transmissions = 3;
26
+ string error_msg = 4;
25
27
  }
26
28
 
27
29
  message CommandMessage {
@@ -37,6 +39,7 @@ message CommandResponse {
37
39
  bool stop_streams = 3;
38
40
  repeated string streams = 4;
39
41
  repeated string transmissions = 5;
42
+ string error_msg = 6;
40
43
  }
41
44
 
42
45
  message DisconnectRequest {
@@ -48,4 +51,5 @@ message DisconnectRequest {
48
51
 
49
52
  message DisconnectResponse {
50
53
  Status status = 1;
54
+ string error_msg = 2;
51
55
  }
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: 0.4.6
4
+ version: 0.5.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: 2017-05-20 00:00:00.000000000 Z
11
+ date: 2017-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anyway_config
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.5.0
19
+ version: '1.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.5.0
26
+ version: '1.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: grpc
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.1'
33
+ version: '1.6'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.1'
40
+ version: '1.6'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: redis
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
47
+ version: '3.2'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: '3.2'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: bundler
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -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: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0.50'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0.50'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: simplecov
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -165,7 +179,13 @@ files:
165
179
  - assets/cpu_chart.gif
166
180
  - assets/cpu_chart2.gif
167
181
  - assets/evlms.png
182
+ - benchmarks/.gitignore
168
183
  - benchmarks/2017-02-12.md
184
+ - benchmarks/HowTo.md
185
+ - benchmarks/ansible.cfg
186
+ - benchmarks/benchmark.yml
187
+ - benchmarks/hosts
188
+ - benchmarks/servers.yml
169
189
  - bin/console
170
190
  - bin/setup
171
191
  - circle.yml
@@ -173,8 +193,8 @@ files:
173
193
  - lib/anycable/config.rb
174
194
  - lib/anycable/handler/exceptions_handling.rb
175
195
  - lib/anycable/pubsub.rb
176
- - lib/anycable/rpc/rpc.rb
177
- - lib/anycable/rpc/rpc_services.rb
196
+ - lib/anycable/rpc/rpc_pb.rb
197
+ - lib/anycable/rpc/rpc_services_pb.rb
178
198
  - lib/anycable/rpc_handler.rb
179
199
  - lib/anycable/server.rb
180
200
  - lib/anycable/socket.rb
@@ -195,12 +215,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
195
215
  version: '0'
196
216
  required_rubygems_version: !ruby/object:Gem::Requirement
197
217
  requirements:
198
- - - ">="
218
+ - - ">"
199
219
  - !ruby/object:Gem::Version
200
- version: '0'
220
+ version: 1.3.1
201
221
  requirements: []
202
222
  rubyforge_project:
203
- rubygems_version: 2.6.4
223
+ rubygems_version: 2.6.13
204
224
  signing_key:
205
225
  specification_version: 4
206
226
  summary: Polyglot replacement for ActionCable server