oceanex-slanger 0.7.1
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 +7 -0
- data/README.md +231 -0
- data/bin/slanger +137 -0
- data/lib/slanger/api.rb +5 -0
- data/lib/slanger/api/event.rb +16 -0
- data/lib/slanger/api/event_publisher.rb +21 -0
- data/lib/slanger/api/request_validation.rb +105 -0
- data/lib/slanger/api/server.rb +56 -0
- data/lib/slanger/channel.rb +104 -0
- data/lib/slanger/config.rb +27 -0
- data/lib/slanger/connection.rb +46 -0
- data/lib/slanger/handler.rb +117 -0
- data/lib/slanger/logger.rb +8 -0
- data/lib/slanger/presence_channel.rb +140 -0
- data/lib/slanger/presence_subscription.rb +33 -0
- data/lib/slanger/private_subscription.rb +9 -0
- data/lib/slanger/redis.rb +65 -0
- data/lib/slanger/service.rb +20 -0
- data/lib/slanger/subscription.rb +53 -0
- data/lib/slanger/version.rb +3 -0
- data/lib/slanger/web_socket_server.rb +38 -0
- data/lib/slanger/webhook.rb +31 -0
- data/slanger.rb +23 -0
- data/spec/have_attributes.rb +65 -0
- data/spec/integration/channel_spec.rb +113 -0
- data/spec/integration/integration_spec.rb +67 -0
- data/spec/integration/presence_channel_spec.rb +148 -0
- data/spec/integration/private_channel_spec.rb +77 -0
- data/spec/integration/replaced_handler_spec.rb +23 -0
- data/spec/integration/ssl_spec.rb +18 -0
- data/spec/server.crt +12 -0
- data/spec/server.key +15 -0
- data/spec/slanger_helper_methods.rb +107 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/unit/channel_spec.rb +105 -0
- data/spec/unit/request_validation_spec.rb +71 -0
- data/spec/unit/webhook_spec.rb +42 -0
- metadata +392 -0
| @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            #encoding: utf-8
         | 
| 2 | 
            +
            require "spec_helper"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe "Integration" do
         | 
| 5 | 
            +
              before(:each) { start_slanger }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              context "connecting with invalid credentials" do
         | 
| 8 | 
            +
                it "sends an error message" do
         | 
| 9 | 
            +
                  messages = em_stream(key: "bogus_key") do |websocket, messages|
         | 
| 10 | 
            +
                    websocket.callback { EM.stop }
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
                  expect(messages).to have_attributes count: 1, last_event: "pusher:error",
         | 
| 13 | 
            +
                                                      connection_established: false, id_present: false
         | 
| 14 | 
            +
                  messages.first["data"] == "Could not find app by key bogus_key"
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              context "connecting with valid credentials" do
         | 
| 19 | 
            +
                it "should succeed and include activity_timeout value in handshake" do
         | 
| 20 | 
            +
                  messages = em_stream do |websocket, messages|
         | 
| 21 | 
            +
                    websocket.callback { EM.stop }
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                  expect(messages).to have_attributes activity_timeout: Slanger::Config.activity_timeout,
         | 
| 24 | 
            +
                                                      connection_established: true, id_present: true
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              context "connect with valid protocol version" do
         | 
| 29 | 
            +
                it "should connect successfuly" do
         | 
| 30 | 
            +
                  messages = em_stream do |websocket, messages|
         | 
| 31 | 
            +
                    websocket.callback { EM.stop }
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                  expect(messages).to have_attributes connection_established: true, id_present: true
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              context "connect with invalid protocol version" do
         | 
| 38 | 
            +
                it "should not connect successfuly with version bigger than supported" do
         | 
| 39 | 
            +
                  messages = em_stream(protocol: "20") do |websocket, messages|
         | 
| 40 | 
            +
                    websocket.callback { EM.stop }
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                  expect(messages).to have_attributes connection_established: false, id_present: false,
         | 
| 43 | 
            +
                                                      last_event: "pusher:error"
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                it "should not connect successfuly without specified version" do
         | 
| 47 | 
            +
                  messages = em_stream(protocol: nil) do |websocket, messages|
         | 
| 48 | 
            +
                    websocket.callback { EM.stop }
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
                  expect(messages).to have_attributes connection_established: false, id_present: false,
         | 
| 51 | 
            +
                                                      last_event: "pusher:error"
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              context "given invalid JSON as input" do
         | 
| 56 | 
            +
                it "should not crash" do
         | 
| 57 | 
            +
                  messages = em_stream do |websocket, messages|
         | 
| 58 | 
            +
                    websocket.callback do
         | 
| 59 | 
            +
                      websocket.send("{ event: 'pusher:subscribe', data: { channel: 'MY_CHANNEL'} }23123")
         | 
| 60 | 
            +
                      EM.next_tick { EM.stop }
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  EM.run { new_websocket.tap { |u| u.stream { EM.next_tick { EM.stop } } } }
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         | 
| @@ -0,0 +1,148 @@ | |
| 1 | 
            +
            #encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "spec_helper"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            describe "Integration" do
         | 
| 6 | 
            +
              before(:each) { start_slanger }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              describe "presence channels:" do
         | 
| 9 | 
            +
                context "subscribing without channel data" do
         | 
| 10 | 
            +
                  context "and bogus authentication credentials" do
         | 
| 11 | 
            +
                    it "sends back an error message" do
         | 
| 12 | 
            +
                      messages = em_stream do |websocket, messages|
         | 
| 13 | 
            +
                        case messages.length
         | 
| 14 | 
            +
                        when 1
         | 
| 15 | 
            +
                          websocket.send({ event: "pusher:subscribe", data: { channel: "presence-channel", auth: "bogus" } }.to_json)
         | 
| 16 | 
            +
                        else
         | 
| 17 | 
            +
                          EM.stop
         | 
| 18 | 
            +
                        end
         | 
| 19 | 
            +
                      end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      expect(messages).to have_attributes connection_established: true, id_present: true,
         | 
| 22 | 
            +
                                                          count: 2,
         | 
| 23 | 
            +
                                                          last_event: "pusher:error"
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      expect(JSON.parse(messages.last["data"])["message"]).to match /^Invalid signature: Expected HMAC SHA256 hex digest of/
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                context "subscribing with channel data" do
         | 
| 31 | 
            +
                  context "and bogus authentication credentials" do
         | 
| 32 | 
            +
                    it "sends back an error message" do
         | 
| 33 | 
            +
                      messages = em_stream do |websocket, messages|
         | 
| 34 | 
            +
                        case messages.length
         | 
| 35 | 
            +
                        when 1
         | 
| 36 | 
            +
                          websocket.send({ event: "pusher:subscribe", data: {
         | 
| 37 | 
            +
                            channel: "presence-lel",
         | 
| 38 | 
            +
                            auth: "boog",
         | 
| 39 | 
            +
                            channel_data: {
         | 
| 40 | 
            +
                              user_id: "barry",
         | 
| 41 | 
            +
                            },
         | 
| 42 | 
            +
                          }.to_json }.to_json)
         | 
| 43 | 
            +
                        else
         | 
| 44 | 
            +
                          EM.stop
         | 
| 45 | 
            +
                        end
         | 
| 46 | 
            +
                      end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                      expect(messages).to have_attributes first_event: "pusher:connection_established", count: 2,
         | 
| 49 | 
            +
                                                          id_present: true
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                      # Channel id should be in the payload
         | 
| 52 | 
            +
                      expect(messages.last["event"]).to eq("pusher:error")
         | 
| 53 | 
            +
                      expect(JSON.parse(messages.last["data"])["message"]).to match /^Invalid signature: Expected HMAC SHA256 hex digest of/
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
                  end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  context "with genuine authentication credentials" do
         | 
| 58 | 
            +
                    it "sends back a success message" do
         | 
| 59 | 
            +
                      messages = em_stream do |websocket, messages|
         | 
| 60 | 
            +
                        case messages.length
         | 
| 61 | 
            +
                        when 1
         | 
| 62 | 
            +
                          send_subscribe(user: websocket,
         | 
| 63 | 
            +
                                         user_id: "0f177369a3b71275d25ab1b44db9f95f",
         | 
| 64 | 
            +
                                         name: "SG",
         | 
| 65 | 
            +
                                         message: messages.first)
         | 
| 66 | 
            +
                        else
         | 
| 67 | 
            +
                          EM.stop
         | 
| 68 | 
            +
                        end
         | 
| 69 | 
            +
                      end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                      expect(messages).to have_attributes connection_established: true, count: 2
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                      expect(messages.last).to eq({ "channel" => "presence-channel",
         | 
| 74 | 
            +
                                                    "event" => "pusher_internal:subscription_succeeded",
         | 
| 75 | 
            +
                                                    "data" => "{\"presence\":{\"count\":1,\"ids\":[\"0f177369a3b71275d25ab1b44db9f95f\"],\"hash\":{\"0f177369a3b71275d25ab1b44db9f95f\":{\"name\":\"SG\"}}}}" })
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    context "with more than one subscriber subscribed to the channel" do
         | 
| 79 | 
            +
                      it "sends a member added message to the existing subscribers" do
         | 
| 80 | 
            +
                        messages = em_stream do |user1, messages|
         | 
| 81 | 
            +
                          case messages.length
         | 
| 82 | 
            +
                          when 1
         | 
| 83 | 
            +
                            send_subscribe(user: user1,
         | 
| 84 | 
            +
                                           user_id: "0f177369a3b71275d25ab1b44db9f95f",
         | 
| 85 | 
            +
                                           name: "SG",
         | 
| 86 | 
            +
                                           message: messages.first)
         | 
| 87 | 
            +
                          when 2
         | 
| 88 | 
            +
                            new_websocket.tap do |u|
         | 
| 89 | 
            +
                              u.stream do |message|
         | 
| 90 | 
            +
                                message = JSON.parse(message)
         | 
| 91 | 
            +
                                if message["event"] == "pusher:connection_established"
         | 
| 92 | 
            +
                                  send_subscribe \
         | 
| 93 | 
            +
                                    user: u, user_id: "37960509766262569d504f02a0ee986d",
         | 
| 94 | 
            +
                                    name: "CHROME", message: message
         | 
| 95 | 
            +
                                end
         | 
| 96 | 
            +
                              end
         | 
| 97 | 
            +
                            end
         | 
| 98 | 
            +
                          else
         | 
| 99 | 
            +
                            EM.stop
         | 
| 100 | 
            +
                          end
         | 
| 101 | 
            +
                        end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                        expect(messages).to have_attributes connection_established: true, count: 3
         | 
| 104 | 
            +
                        # Channel id should be in the payload
         | 
| 105 | 
            +
                        expect(messages[1]).to eq({ "channel" => "presence-channel", "event" => "pusher_internal:subscription_succeeded",
         | 
| 106 | 
            +
                                                    "data" => "{\"presence\":{\"count\":1,\"ids\":[\"0f177369a3b71275d25ab1b44db9f95f\"],\"hash\":{\"0f177369a3b71275d25ab1b44db9f95f\":{\"name\":\"SG\"}}}}" })
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                        expect(messages.last).to eq({ "channel" => "presence-channel", "event" => "pusher_internal:member_added",
         | 
| 109 | 
            +
                                                      "data" => { "user_id" => "37960509766262569d504f02a0ee986d", "user_info" => { "name" => "CHROME" } } })
         | 
| 110 | 
            +
                      end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                      it "does not send multiple member added and member removed messages if one subscriber opens multiple connections, i.e. multiple browser tabs." do
         | 
| 113 | 
            +
                        messages = em_stream do |user1, messages|
         | 
| 114 | 
            +
                          case messages.length
         | 
| 115 | 
            +
                          when 1
         | 
| 116 | 
            +
                            send_subscribe(user: user1,
         | 
| 117 | 
            +
                                           user_id: "0f177369a3b71275d25ab1b44db9f95f",
         | 
| 118 | 
            +
                                           name: "SG",
         | 
| 119 | 
            +
                                           message: messages.first)
         | 
| 120 | 
            +
                          when 2
         | 
| 121 | 
            +
                            10.times do
         | 
| 122 | 
            +
                              new_websocket.tap do |u|
         | 
| 123 | 
            +
                                u.stream do |message|
         | 
| 124 | 
            +
                                  # remove stream callback
         | 
| 125 | 
            +
                                  ## close the connection in the next tick as soon as subscription is acknowledged
         | 
| 126 | 
            +
                                  u.stream { EM.next_tick { u.close_connection } }
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                                  send_subscribe({ user: u,
         | 
| 129 | 
            +
                                                   user_id: "37960509766262569d504f02a0ee986d",
         | 
| 130 | 
            +
                                                   name: "CHROME",
         | 
| 131 | 
            +
                                                   message: JSON.parse(message) })
         | 
| 132 | 
            +
                                end
         | 
| 133 | 
            +
                              end
         | 
| 134 | 
            +
                            end
         | 
| 135 | 
            +
                          when 4
         | 
| 136 | 
            +
                            EM.next_tick { EM.stop }
         | 
| 137 | 
            +
                          end
         | 
| 138 | 
            +
                        end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                        # There should only be one set of presence messages sent to the refernce user for the second user.
         | 
| 141 | 
            +
                        expect(messages.one? { |message| message["event"] == "pusher_internal:member_added" && message["data"]["user_id"] == "37960509766262569d504f02a0ee986d" }).to eq(true)
         | 
| 142 | 
            +
                        expect(messages.one? { |message| message["event"] == "pusher_internal:member_removed" && message["data"]["user_id"] == "37960509766262569d504f02a0ee986d" }).to eq(true)
         | 
| 143 | 
            +
                      end
         | 
| 144 | 
            +
                    end
         | 
| 145 | 
            +
                  end
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
              end
         | 
| 148 | 
            +
            end
         | 
| @@ -0,0 +1,77 @@ | |
| 1 | 
            +
            #encoding: utf-8
         | 
| 2 | 
            +
            require "spec_helper"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            describe "Integration" do
         | 
| 5 | 
            +
              before(:each) { start_slanger }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              describe "private channels" do
         | 
| 8 | 
            +
                context "with valid authentication credentials:" do
         | 
| 9 | 
            +
                  it "accepts the subscription request" do
         | 
| 10 | 
            +
                    messages = em_stream do |websocket, messages|
         | 
| 11 | 
            +
                      case messages.length
         | 
| 12 | 
            +
                      when 1
         | 
| 13 | 
            +
                        private_channel websocket, messages.first
         | 
| 14 | 
            +
                      else
         | 
| 15 | 
            +
                        EM.stop
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    expect(messages).to have_attributes connection_established: true,
         | 
| 20 | 
            +
                                                        count: 2,
         | 
| 21 | 
            +
                                                        id_present: true,
         | 
| 22 | 
            +
                                                        last_event: "pusher_internal:subscription_succeeded"
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                context "with bogus authentication credentials:" do
         | 
| 27 | 
            +
                  it "sends back an error message" do
         | 
| 28 | 
            +
                    messages = em_stream do |websocket, messages|
         | 
| 29 | 
            +
                      case messages.length
         | 
| 30 | 
            +
                      when 1
         | 
| 31 | 
            +
                        websocket.send({ event: "pusher:subscribe",
         | 
| 32 | 
            +
                                        data: { channel: "private-channel",
         | 
| 33 | 
            +
                                                auth: "bogus" } }.to_json)
         | 
| 34 | 
            +
                      else
         | 
| 35 | 
            +
                        EM.stop
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    expect(messages).to have_attributes connection_established: true, count: 2, id_present: true, last_event: "pusher:error"
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    expect(JSON.parse(messages.last["data"])["message"]).to match /^Invalid signature: Expected HMAC SHA256 hex digest of/
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                describe "client events" do
         | 
| 46 | 
            +
                  it "sends event to other channel subscribers" do
         | 
| 47 | 
            +
                    client1_messages, client2_messages = [], []
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    em_thread do
         | 
| 50 | 
            +
                      client1, client2 = new_websocket, new_websocket
         | 
| 51 | 
            +
                      client2_messages, client1_messages = [], []
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                      stream(client1, client1_messages) do |message|
         | 
| 54 | 
            +
                        case client1_messages.length
         | 
| 55 | 
            +
                        when 1
         | 
| 56 | 
            +
                          private_channel client1, client1_messages.first
         | 
| 57 | 
            +
                        when 3
         | 
| 58 | 
            +
                          EM.next_tick { EM.stop }
         | 
| 59 | 
            +
                        end
         | 
| 60 | 
            +
                      end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                      stream(client2, client2_messages) do |message|
         | 
| 63 | 
            +
                        case client2_messages.length
         | 
| 64 | 
            +
                        when 1
         | 
| 65 | 
            +
                          private_channel client2, client2_messages.first
         | 
| 66 | 
            +
                        when 2
         | 
| 67 | 
            +
                          client2.send({ event: "client-something", data: { some: "stuff" }, channel: "private-channel" }.to_json)
         | 
| 68 | 
            +
                        end
         | 
| 69 | 
            +
                      end
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    expect(client1_messages.one? { |m| m["event"] == "client-something" }).to eq(true)
         | 
| 73 | 
            +
                    expect(client2_messages.none? { |m| m["event"] == "client-something" }).to eq(true)
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
            end
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            require "spec_helper"
         | 
| 2 | 
            +
            require "lib/slanger/handler.rb"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class ReplacedHandler < Slanger::Handler
         | 
| 5 | 
            +
              def authenticate
         | 
| 6 | 
            +
                super
         | 
| 7 | 
            +
                send_payload nil, "pusher:info", { message: "Welcome!" }
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
            end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            describe "Replacable handler" do
         | 
| 12 | 
            +
              it "says welcome" do
         | 
| 13 | 
            +
                start_slanger_with_options socket_handler: ReplacedHandler
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                msgs = em_stream do |websocket, messages|
         | 
| 16 | 
            +
                  if messages.length == 2
         | 
| 17 | 
            +
                    EM.stop
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                expect(msgs.last).to eq({ "event" => "pusher:info", "data" => "{\"message\":\"Welcome!\"}" })
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            require "spec_helper"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe "Integration" do
         | 
| 4 | 
            +
              describe "Slanger when configured to use SSL" do
         | 
| 5 | 
            +
                it "encrypts the connection" do
         | 
| 6 | 
            +
                  start_slanger_with_options tls_options: {
         | 
| 7 | 
            +
                    cert_chain_file: "spec/server.crt",
         | 
| 8 | 
            +
                    private_key_file: "spec/server.key",
         | 
| 9 | 
            +
                  }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  socket = TCPSocket.new("0.0.0.0", 8080)
         | 
| 12 | 
            +
                  expected_cert = OpenSSL::X509::Certificate.new(File.open("spec/server.crt"))
         | 
| 13 | 
            +
                  ssl_socket = OpenSSL::SSL::SSLSocket.new(socket)
         | 
| 14 | 
            +
                  ssl_socket.connect
         | 
| 15 | 
            +
                  expect(ssl_socket.peer_cert.to_s).to eq(expected_cert.to_s)
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
    
        data/spec/server.crt
    ADDED
    
    | @@ -0,0 +1,12 @@ | |
| 1 | 
            +
            -----BEGIN CERTIFICATE-----
         | 
| 2 | 
            +
            MIIBvzCCASgCCQCsMkmDVYNDETANBgkqhkiG9w0BAQUFADAkMQswCQYDVQQGEwJE
         | 
| 3 | 
            +
            RTEVMBMGA1UEAwwMc2xhbmdlci50ZXN0MB4XDTEyMDQxMTE2NDMwNloXDTE5MDIx
         | 
| 4 | 
            +
            NDE2NDMwNlowJDELMAkGA1UEBhMCREUxFTATBgNVBAMMDHNsYW5nZXIudGVzdDCB
         | 
| 5 | 
            +
            nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAmlTxrcqXw+hbdjnpNENgx4p6T+x7
         | 
| 6 | 
            +
            SgN/5ti3+gr5ZJElebEdJdGymM/KK817GFhLuYSEv72oEVitC1ISCfo/iOu4S71Y
         | 
| 7 | 
            +
            sGsrdyPVl3cDswSkvmo27J3rtAbY1fNDs68YFAQGH8wlQtPSPvd9KBKg0klafsDU
         | 
| 8 | 
            +
            VvDYjQ+XZ2+ZKZECAwEAATANBgkqhkiG9w0BAQUFAAOBgQCHGqUddcsTfvV0Nk3F
         | 
| 9 | 
            +
            zZ5kGvAiZ02MUourZ4GVs5uBYtkIrQ7HAlQbHAbC8d7e0UVgcTwUKgwpw/RfNR/O
         | 
| 10 | 
            +
            Ho/zn7lPciLQ7VMnOZ2+MfbJ2HIFgZL6qH1gTcQpBW4s3gKR5hFpaGJ+8l/cmEWj
         | 
| 11 | 
            +
            AvywaOcSLex+q0OwZaiusiDorg==
         | 
| 12 | 
            +
            -----END CERTIFICATE-----
         | 
    
        data/spec/server.key
    ADDED
    
    | @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            -----BEGIN RSA PRIVATE KEY-----
         | 
| 2 | 
            +
            MIICXQIBAAKBgQCaVPGtypfD6Ft2Oek0Q2DHinpP7HtKA3/m2Lf6CvlkkSV5sR0l
         | 
| 3 | 
            +
            0bKYz8orzXsYWEu5hIS/vagRWK0LUhIJ+j+I67hLvViwayt3I9WXdwOzBKS+ajbs
         | 
| 4 | 
            +
            neu0BtjV80OzrxgUBAYfzCVC09I+930oEqDSSVp+wNRW8NiND5dnb5kpkQIDAQAB
         | 
| 5 | 
            +
            AoGABGzBDSGM3mIQFUCtzgDMiowO27HFCyc0iJLYG4QrCFYdA/MvCcGMZFM40a6v
         | 
| 6 | 
            +
            g9AsQ6JoB/NRGUY4l+V/fOe+4Iuycf8+vN1mrSVR1lTjy/mOwj900pc4ff6cDv6S
         | 
| 7 | 
            +
            bI/hm4BNiuj8OD11R+ZK07Lo1iCzBkAy53RkTFcBk74MYgECQQDMyAMT0DhlRBqD
         | 
| 8 | 
            +
            4vrPF+GZ+rMYpeTjuNDOZphIwzxpv70uyh2RNg+7F6U91Qxz6vpbIkz8Zf4TgdwM
         | 
| 9 | 
            +
            u/rroktxAkEAwO6wyzidm2yrPMPtnwxDIYnH/ETdNraa3JyHsjXsQwGAIG80+hCv
         | 
| 10 | 
            +
            QfCA/LmvNOm/Mpe1EyiAeI1/YJp4a2xwIQJBAKysFpQ1ZehVtbnxwaSwMWXiE/Q7
         | 
| 11 | 
            +
            pjYyl7cCoXPxVFai+8WhXa8dE8Shmo75v2dbAsGnuZy177jJLiB6vYjFL7ECQQCI
         | 
| 12 | 
            +
            Zri7lLVo8zVFasgO0F6N0ZmAMzeqvQNTwZ72UcVNwjvRso3j1fPyTJUFGEpUwIWa
         | 
| 13 | 
            +
            wUMV3mal1HQf2lYUrL/BAkBBtXqOLFHINHUmffdRSV/2HECYXHazb6lAnL8nnQX0
         | 
| 14 | 
            +
            vin2ujCli9mcYWnrY7zwlXdAxgQv5Q2ByQT9Fd8S7FjA
         | 
| 15 | 
            +
            -----END RSA PRIVATE KEY-----
         | 
| @@ -0,0 +1,107 @@ | |
| 1 | 
            +
            module SlangerHelperMethods
         | 
| 2 | 
            +
              def start_slanger_with_options(options = {})
         | 
| 3 | 
            +
                # Fork service. Our integration tests MUST block the main thread because we want to wait for i/o to finish.
         | 
| 4 | 
            +
                @server_pid = EM.fork_reactor do
         | 
| 5 | 
            +
                  Thin::Logging.silent = true
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                  opts = { host: "0.0.0.0",
         | 
| 8 | 
            +
                           api_port: "4567",
         | 
| 9 | 
            +
                           websocket_port: "8080",
         | 
| 10 | 
            +
                           app_key: "765ec374ae0a69f4ce44",
         | 
| 11 | 
            +
                           secret: "your-pusher-secret",
         | 
| 12 | 
            +
                           activity_timeout: 100 }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  Slanger::Config.load opts.merge(options)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                  Slanger::Service.run
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
                wait_for_slanger
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              alias start_slanger start_slanger_with_options
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              def stop_slanger
         | 
| 24 | 
            +
                # Ensure Slanger is properly stopped. No orphaned processes allowed!
         | 
| 25 | 
            +
                Process.kill "SIGKILL", @server_pid
         | 
| 26 | 
            +
                Process.wait @server_pid
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              def wait_for_slanger(opts = {})
         | 
| 30 | 
            +
                opts = { port: 8080 }.update opts
         | 
| 31 | 
            +
                begin
         | 
| 32 | 
            +
                  TCPSocket.new("0.0.0.0", opts[:port]).close
         | 
| 33 | 
            +
                rescue
         | 
| 34 | 
            +
                  sleep 0.005
         | 
| 35 | 
            +
                  retry
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              def new_websocket(opts = {})
         | 
| 40 | 
            +
                opts = { key: Pusher.key, protocol: 7 }.update opts
         | 
| 41 | 
            +
                uri = "ws://0.0.0.0:8080/app/#{opts[:key]}?client=js&version=1.8.5&protocol=#{opts[:protocol]}"
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                EM::HttpRequest.new(uri).get.tap { |ws| ws.errback &errback }
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def em_stream(opts = {})
         | 
| 47 | 
            +
                messages = []
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                em_thread do
         | 
| 50 | 
            +
                  websocket = new_websocket opts
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  stream(websocket, messages) do |message|
         | 
| 53 | 
            +
                    yield websocket, messages
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                return messages
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              def em_thread
         | 
| 61 | 
            +
                Thread.new do
         | 
| 62 | 
            +
                  EM.run do
         | 
| 63 | 
            +
                    yield
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end.join
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
              def stream(websocket, messages)
         | 
| 69 | 
            +
                websocket.stream do |message|
         | 
| 70 | 
            +
                  messages << JSON.parse(message)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  yield message
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              def send_subscribe(options)
         | 
| 77 | 
            +
                info = { user_id: options[:user_id], user_info: { name: options[:name] } }
         | 
| 78 | 
            +
                socket_id = JSON.parse(options[:message]["data"])["socket_id"]
         | 
| 79 | 
            +
                to_sign = [socket_id, "presence-channel", info.to_json].join ":"
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                digest = OpenSSL::Digest::SHA256.new
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                options[:user].send({
         | 
| 84 | 
            +
                  event: "pusher:subscribe",
         | 
| 85 | 
            +
                  data: {
         | 
| 86 | 
            +
                    auth: [Pusher.key, OpenSSL::HMAC.hexdigest(digest, Pusher.secret, to_sign)].join(":"),
         | 
| 87 | 
            +
                    channel_data: info.to_json,
         | 
| 88 | 
            +
                    channel: "presence-channel",
         | 
| 89 | 
            +
                  },
         | 
| 90 | 
            +
                }.to_json)
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              def private_channel(websocket, message)
         | 
| 94 | 
            +
                socket_id = JSON.parse(message["data"])["socket_id"]
         | 
| 95 | 
            +
                to_sign = [socket_id, "private-channel"].join ":"
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                digest = OpenSSL::Digest::SHA256.new
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                websocket.send({
         | 
| 100 | 
            +
                  event: "pusher:subscribe",
         | 
| 101 | 
            +
                  data: {
         | 
| 102 | 
            +
                    auth: [Pusher.key, OpenSSL::HMAC.hexdigest(digest, Pusher.secret, to_sign)].join(":"),
         | 
| 103 | 
            +
                    channel: "private-channel",
         | 
| 104 | 
            +
                  },
         | 
| 105 | 
            +
                }.to_json)
         | 
| 106 | 
            +
              end
         | 
| 107 | 
            +
            end
         |