sockudo 1.0.0
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/CHANGELOG.md +137 -0
- data/LICENSE +20 -0
- data/README.md +289 -0
- data/lib/sockudo/channel.rb +179 -0
- data/lib/sockudo/client.rb +593 -0
- data/lib/sockudo/request.rb +112 -0
- data/lib/sockudo/resource.rb +36 -0
- data/lib/sockudo/utils.rb +34 -0
- data/lib/sockudo/version.rb +3 -0
- data/lib/sockudo/webhook.rb +110 -0
- data/lib/sockudo.rb +77 -0
- metadata +208 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b7a967b624337aaad0668a3c7fba48c18ca82dbcbf61f48d2637ab6c468a8932
|
|
4
|
+
data.tar.gz: 6fca4167728cd368e7e0dc93fb3c2986604e2fc35c8fa762c7e2e2606fc33a08
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1211b8badb256617a74201e39d6c7579a9351cf575a2fd68ee3bbdbada7b877ce6ddcb1ddfb34c37253bd276d8db470e9340f6150c75c653cbf70622aff78fa3
|
|
7
|
+
data.tar.gz: bbe0e470d9c7cd52119693f250139eeac6ace3b0b54f01a293aed1db9003e5260ff9f7681cf0783fcbd64d967b68e431b75ab8e4783c1c31904838f002d7c54c
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.0.4
|
|
4
|
+
|
|
5
|
+
* [ADDED] Add `authenticate_user` method for user authentication flow
|
|
6
|
+
|
|
7
|
+
## 2.0.3
|
|
8
|
+
|
|
9
|
+
* [FIXED] Corrected the channels limit when publishing events. Upped from 10 to 100.
|
|
10
|
+
|
|
11
|
+
## 2.0.2
|
|
12
|
+
|
|
13
|
+
* [CHANGED] made encryption_master_key_base64 globally configurable
|
|
14
|
+
|
|
15
|
+
## 2.0.1
|
|
16
|
+
|
|
17
|
+
* [CHANGED] Only include lib and essential docs in gem.
|
|
18
|
+
|
|
19
|
+
## 2.0.0
|
|
20
|
+
|
|
21
|
+
* [CHANGED] Use TLS by default.
|
|
22
|
+
* [REMOVED] Support for Ruby 2.4 and 2.5.
|
|
23
|
+
* [FIXED] Handle empty or nil configuration.
|
|
24
|
+
* [REMOVED] Legacy Push Notification integration.
|
|
25
|
+
* [ADDED] Stalebot and Github actions.
|
|
26
|
+
|
|
27
|
+
## 1.4.3
|
|
28
|
+
|
|
29
|
+
* [FIXED] Remove newline from end of base64 encoded strings, some decoders don't like
|
|
30
|
+
them.
|
|
31
|
+
|
|
32
|
+
## 1.4.2
|
|
33
|
+
==================
|
|
34
|
+
|
|
35
|
+
* [FIXED] Return `shared_secret` to support authenticating encrypted channels. Thanks
|
|
36
|
+
@Benjaminpjacobs
|
|
37
|
+
|
|
38
|
+
## 1.4.1
|
|
39
|
+
|
|
40
|
+
* [CHANGED] Remove rbnacl from dependencies so we don't get errors when it isn't
|
|
41
|
+
required. Thanks @y-yagi!
|
|
42
|
+
|
|
43
|
+
## 1.4.0
|
|
44
|
+
|
|
45
|
+
* [ADDED] Support for end-to-end encryption.
|
|
46
|
+
|
|
47
|
+
## 1.3.3
|
|
48
|
+
|
|
49
|
+
* [CHANGED] Rewording to clarify "Pusher Channels" or simply "Channels" product name.
|
|
50
|
+
|
|
51
|
+
## 1.3.2
|
|
52
|
+
|
|
53
|
+
* [FIXED] Return a specific error for "Request Entity Too Large" (body over 10KB).
|
|
54
|
+
* [ADDED] Add a `use_tls` option for SSL (defaults to false).
|
|
55
|
+
* [ADDED] Add a `from_url` client method (in addition to existing `from_env` option).
|
|
56
|
+
* [CHANGED] Improved documentation and fixed typos.
|
|
57
|
+
* [ADDED] Add Ruby 2.4 to test matrix.
|
|
58
|
+
|
|
59
|
+
## 1.3.1
|
|
60
|
+
|
|
61
|
+
* [FIXED] Added missing client batch methods to default client delegations
|
|
62
|
+
* [CHANGED] Document raised exception in the `authenticate` method
|
|
63
|
+
* [FIXED] Fixes em-http-request from using v2.5.0 of `addressable` breaking builds.
|
|
64
|
+
|
|
65
|
+
## 1.3.0
|
|
66
|
+
|
|
67
|
+
* [ADDED] Add support for sending push notifications on up to 10 interests.
|
|
68
|
+
|
|
69
|
+
## 1.2.1
|
|
70
|
+
|
|
71
|
+
* [FIXED] Fixes Rails 5 compatibility. Use duck-typing to detect request object
|
|
72
|
+
|
|
73
|
+
## 1.2.0
|
|
74
|
+
|
|
75
|
+
* [CHANGED] Minor release for Native notifications
|
|
76
|
+
|
|
77
|
+
## 1.2.0.rc1
|
|
78
|
+
|
|
79
|
+
* [ADDED] Add support for Native notifications
|
|
80
|
+
|
|
81
|
+
## 1.1.0
|
|
82
|
+
|
|
83
|
+
* [ADDED] Add support for batch events
|
|
84
|
+
|
|
85
|
+
## 1.0.0
|
|
86
|
+
|
|
87
|
+
* [CHANGED] No breaking changes, this release is just to follow semver and show that we
|
|
88
|
+
are stable.
|
|
89
|
+
|
|
90
|
+
## 0.18.0
|
|
91
|
+
|
|
92
|
+
* [ADDED] Introduce `Pusher::Client.from_env`
|
|
93
|
+
* [FIXED] Improve error handling on missing config
|
|
94
|
+
|
|
95
|
+
## 0.17.0
|
|
96
|
+
|
|
97
|
+
* [ADDED] Introduce the `cluster` option.
|
|
98
|
+
|
|
99
|
+
## 0.16.0
|
|
100
|
+
|
|
101
|
+
* [CHANGED] Bump httpclient version to 2.7
|
|
102
|
+
* [REMOVED] Ruby 1.8.7 is not supported anymore.
|
|
103
|
+
|
|
104
|
+
## 0.15.2
|
|
105
|
+
|
|
106
|
+
* [CHANGED] Documented `Pusher.channel_info`, `Pusher.channels`
|
|
107
|
+
* [ADDED] Added `Pusher.channel_users`
|
|
108
|
+
|
|
109
|
+
## 0.15.1
|
|
110
|
+
|
|
111
|
+
* [FIXED] Fixed a bug where the `authenticate` method added in 0.15.0 wasn't exposed on the Pusher class.
|
|
112
|
+
|
|
113
|
+
## 0.15.0
|
|
114
|
+
|
|
115
|
+
* [ADDED] Added `Pusher.authenticate` method for authenticating private and presence channels.
|
|
116
|
+
This is prefered over the older `Pusher['a_channel'].authenticate(...)` style.
|
|
117
|
+
|
|
118
|
+
## 0.14.6
|
|
119
|
+
|
|
120
|
+
* [CHANGED] Updated to use the `pusher-signature` gem instead of `signature`.
|
|
121
|
+
This resolves namespace related issues.
|
|
122
|
+
|
|
123
|
+
## 0.14.5
|
|
124
|
+
|
|
125
|
+
* [SECURITY] Prevent auth delegation trough crafted socket IDs
|
|
126
|
+
|
|
127
|
+
## 0.14.4
|
|
128
|
+
|
|
129
|
+
* [SECURITY] Prevent timing attack, update signature to v0.1.8
|
|
130
|
+
* [SECURITY] Prevent POODLE. Disable SSLv3, update httpclient to v2.5
|
|
131
|
+
* [FIXED] Fix channel name character limit.
|
|
132
|
+
* [ADDED] Adds support for listing users on a presence channel
|
|
133
|
+
|
|
134
|
+
## 0.14.2
|
|
135
|
+
|
|
136
|
+
* [CHANGED] Bump httpclient to v2.4. See #62 (POODLE SSL)
|
|
137
|
+
* [CHANGED] Fix limited channel count at README.md. Thanks @tricknotes
|
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2010-2013 Pusher
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# sockudo
|
|
2
|
+
|
|
3
|
+
Official Ruby server SDK for [Sockudo](https://github.com/sockudo/sockudo) — a fast, self-hosted WebSocket server with full Pusher HTTP API compatibility.
|
|
4
|
+
|
|
5
|
+
## Supported Platforms
|
|
6
|
+
|
|
7
|
+
- **Ruby 3.0 or greater**
|
|
8
|
+
- Rails and other Ruby frameworks are supported provided you are running a supported Ruby version.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
Add to your Gemfile:
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
gem 'sockudo'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then run `bundle install`, or install directly:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
gem install sockudo
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Getting Started
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
require 'sockudo'
|
|
28
|
+
|
|
29
|
+
sockudo = Sockudo::Client.new(
|
|
30
|
+
app_id: 'your-app-id',
|
|
31
|
+
key: 'your-app-key',
|
|
32
|
+
secret: 'your-app-secret',
|
|
33
|
+
host: '127.0.0.1',
|
|
34
|
+
port: 6001,
|
|
35
|
+
use_tls: false
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
sockudo.trigger('my-channel', 'my-event', { message: 'hello world' })
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
### Instance Configuration
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
sockudo = Sockudo::Client.new(
|
|
47
|
+
app_id: 'your-app-id',
|
|
48
|
+
key: 'your-app-key',
|
|
49
|
+
secret: 'your-app-secret',
|
|
50
|
+
host: '127.0.0.1',
|
|
51
|
+
port: 6001,
|
|
52
|
+
use_tls: false
|
|
53
|
+
)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`use_tls` is optional and defaults to `true`. It sets the scheme and port accordingly. A custom `port` value takes precedence over `use_tls`.
|
|
57
|
+
|
|
58
|
+
### Global Configuration
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
Sockudo.app_id = 'your-app-id'
|
|
62
|
+
Sockudo.key = 'your-app-key'
|
|
63
|
+
Sockudo.secret = 'your-app-secret'
|
|
64
|
+
Sockudo.host = '127.0.0.1'
|
|
65
|
+
Sockudo.port = 6001
|
|
66
|
+
Sockudo.use_tls = false
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### From Environment Variable
|
|
70
|
+
|
|
71
|
+
If `SOCKUDO_URL` is set in the environment, use `from_env` to configure automatically:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
# Reads SOCKUDO_URL in the form: http://KEY:SECRET@HOST:PORT/apps/APP_ID
|
|
75
|
+
sockudo = Sockudo::Client.from_env
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Global configuration is also automatically read from `SOCKUDO_URL` when set.
|
|
79
|
+
|
|
80
|
+
### HTTP Proxy
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
Sockudo.http_proxy = 'http://user:password@proxy-host:8080'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Publishing Events
|
|
87
|
+
|
|
88
|
+
### Single Channel
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
sockudo.trigger('my-channel', 'my-event', { message: 'hello world' })
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Multiple Channels
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
sockudo.trigger(['channel-1', 'channel-2'], 'my-event', { message: 'hello world' })
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Up to 10 channels per call.
|
|
101
|
+
|
|
102
|
+
### Excluding a Socket Recipient
|
|
103
|
+
|
|
104
|
+
Pass a `socket_id` option to prevent the triggering connection from receiving its own event:
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
sockudo.trigger('my-channel', 'my-event', { message: 'hello' }, { socket_id: '123.456' })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Batch Events
|
|
111
|
+
|
|
112
|
+
Send multiple events in a single HTTP request (up to 10 per call):
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
sockudo.trigger_batch([
|
|
116
|
+
{ channel: 'channel-1', name: 'event-1', data: { x: 1 } },
|
|
117
|
+
{ channel: 'channel-2', name: 'event-2', data: { x: 2 } },
|
|
118
|
+
{ channel: 'channel-3', name: 'event-3', data: { x: 3 } },
|
|
119
|
+
])
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Idempotent Publishing
|
|
123
|
+
|
|
124
|
+
Pass an `idempotency_key` to safely retry publishes without causing duplicate deliveries:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
sockudo.trigger(
|
|
128
|
+
'my-channel',
|
|
129
|
+
'my-event',
|
|
130
|
+
{ message: 'hello' },
|
|
131
|
+
{ idempotency_key: 'order-shipped-order-789' }
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The server deduplicates publishes with the same key within the configured window.
|
|
136
|
+
|
|
137
|
+
## Channel Authorization
|
|
138
|
+
|
|
139
|
+
### Private Channel
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
auth = sockudo.authenticate('private-my-channel', params[:socket_id])
|
|
143
|
+
# Returns JSON: {"auth":"key:signature"}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Presence Channel
|
|
147
|
+
|
|
148
|
+
```ruby
|
|
149
|
+
auth = sockudo.authenticate(
|
|
150
|
+
'presence-my-channel',
|
|
151
|
+
params[:socket_id],
|
|
152
|
+
user_id: 'user-123',
|
|
153
|
+
user_info: { name: 'Jane Doe', role: 'admin' }
|
|
154
|
+
)
|
|
155
|
+
# Returns JSON: {"auth":"key:signature","channel_data":"..."}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## User Authentication
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
auth = sockudo.authenticate_user(params[:socket_id], { id: 'user-123', name: 'Jane Doe' })
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Webhooks
|
|
165
|
+
|
|
166
|
+
Create a webhook object from a `Rack::Request` (available as `request` in Rails controllers and Sinatra handlers):
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
webhook = sockudo.webhook(request)
|
|
170
|
+
|
|
171
|
+
if webhook.valid?
|
|
172
|
+
webhook.events.each do |event|
|
|
173
|
+
case event['name']
|
|
174
|
+
when 'channel_occupied'
|
|
175
|
+
puts "Channel occupied: #{event['channel']}"
|
|
176
|
+
when 'channel_vacated'
|
|
177
|
+
puts "Channel vacated: #{event['channel']}"
|
|
178
|
+
when 'client_event'
|
|
179
|
+
puts "Client event: #{event['event']} on #{event['channel']}"
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
render plain: 'ok'
|
|
184
|
+
else
|
|
185
|
+
render plain: 'invalid', status: 401
|
|
186
|
+
end
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Application State
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
# Info about a channel
|
|
193
|
+
info = sockudo.channel_info('my-channel')
|
|
194
|
+
occupied = info[:occupied]
|
|
195
|
+
|
|
196
|
+
# User count for a presence channel
|
|
197
|
+
info = sockudo.channel_info('presence-my-channel', info: 'user_count')
|
|
198
|
+
user_count = info[:user_count]
|
|
199
|
+
|
|
200
|
+
# List channels (optionally filtered)
|
|
201
|
+
result = sockudo.channels(filter_by_prefix: 'presence-')
|
|
202
|
+
|
|
203
|
+
# Users in a presence channel
|
|
204
|
+
result = sockudo.channel_users('presence-my-channel')
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Async Requests
|
|
208
|
+
|
|
209
|
+
There are two reasons to use async methods: avoiding blocking in a request-response cycle, or running inside an event loop.
|
|
210
|
+
|
|
211
|
+
### With EventMachine
|
|
212
|
+
|
|
213
|
+
Add `em-http-request` to your Gemfile and run with the EventMachine reactor active (e.g. inside Thin):
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
sockudo.get_async('/channels').callback { |response|
|
|
217
|
+
puts response[:channels].inspect
|
|
218
|
+
}.errback { |error|
|
|
219
|
+
puts "Error: #{error}"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
sockudo.trigger_async('my-channel', 'my-event', { message: 'hello' }).callback { |response|
|
|
223
|
+
puts 'Triggered'
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Without EventMachine (Threaded)
|
|
228
|
+
|
|
229
|
+
If the EventMachine reactor is not running, async requests are made using threads managed by `httpclient`. An `HTTPClient::Connection` object is returned immediately.
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
sockudo.trigger_async('my-channel', 'my-event', { message: 'hello' })
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Error Handling
|
|
236
|
+
|
|
237
|
+
All errors are descendants of `Sockudo::Error`:
|
|
238
|
+
|
|
239
|
+
```ruby
|
|
240
|
+
begin
|
|
241
|
+
sockudo.trigger('my-channel', 'my-event', { message: 'hello' })
|
|
242
|
+
rescue Sockudo::AuthenticationError => e
|
|
243
|
+
# invalid credentials
|
|
244
|
+
rescue Sockudo::HTTPError => e
|
|
245
|
+
# network or HTTP-level error
|
|
246
|
+
rescue Sockudo::Error => e
|
|
247
|
+
# catch-all
|
|
248
|
+
end
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Logging
|
|
252
|
+
|
|
253
|
+
Assign any logger compatible with Ruby's standard `Logger` interface:
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
# Rails
|
|
257
|
+
Sockudo.logger = Rails.logger
|
|
258
|
+
|
|
259
|
+
# Default: logs at INFO level to STDOUT
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## Testing
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
bundle install
|
|
266
|
+
bundle exec rake spec
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Pusher SDK Compatibility
|
|
270
|
+
|
|
271
|
+
Sockudo implements the full Pusher HTTP API. If you prefer to use the official `pusher` gem or are migrating from Pusher, point it at your Sockudo instance:
|
|
272
|
+
|
|
273
|
+
```ruby
|
|
274
|
+
require 'pusher'
|
|
275
|
+
|
|
276
|
+
pusher = Pusher::Client.new(
|
|
277
|
+
app_id: 'your-app-id',
|
|
278
|
+
key: 'your-app-key',
|
|
279
|
+
secret: 'your-app-secret',
|
|
280
|
+
host: '127.0.0.1',
|
|
281
|
+
port: 6001
|
|
282
|
+
)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
All standard Pusher SDK calls work against a self-hosted Sockudo server without modification.
|
|
286
|
+
|
|
287
|
+
## License
|
|
288
|
+
|
|
289
|
+
MIT
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
require 'openssl'
|
|
2
|
+
require 'multi_json'
|
|
3
|
+
|
|
4
|
+
module Sockudo
|
|
5
|
+
# Delegates operations for a specific channel from a client
|
|
6
|
+
class Channel
|
|
7
|
+
attr_reader :name
|
|
8
|
+
INVALID_CHANNEL_REGEX = /[^A-Za-z0-9_\-=@,.;]/
|
|
9
|
+
|
|
10
|
+
def initialize(_, name, client = Sockudo)
|
|
11
|
+
if Sockudo::Channel::INVALID_CHANNEL_REGEX.match(name)
|
|
12
|
+
raise Sockudo::Error, "Illegal channel name '#{name}'"
|
|
13
|
+
elsif name.length > 200
|
|
14
|
+
raise Sockudo::Error, "Channel name too long (limit 164 characters) '#{name}'"
|
|
15
|
+
end
|
|
16
|
+
@name = name
|
|
17
|
+
@client = client
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Trigger event asynchronously using EventMachine::HttpRequest
|
|
21
|
+
#
|
|
22
|
+
# [Deprecated] This method will be removed in a future gem version. Please
|
|
23
|
+
# switch to Sockudo.trigger_async or Sockudo::Client#trigger_async instead
|
|
24
|
+
#
|
|
25
|
+
# @param (see #trigger!)
|
|
26
|
+
# @return [EM::DefaultDeferrable]
|
|
27
|
+
# Attach a callback to be notified of success (with no parameters).
|
|
28
|
+
# Attach an errback to be notified of failure (with an error parameter
|
|
29
|
+
# which includes the HTTP status code returned)
|
|
30
|
+
# @raise [LoadError] unless em-http-request gem is available
|
|
31
|
+
# @raise [Sockudo::Error] unless the eventmachine reactor is running. You
|
|
32
|
+
# probably want to run your application inside a server such as thin
|
|
33
|
+
#
|
|
34
|
+
def trigger_async(event_name, data, socket_id = nil)
|
|
35
|
+
params = {}
|
|
36
|
+
if socket_id
|
|
37
|
+
validate_socket_id(socket_id)
|
|
38
|
+
params[:socket_id] = socket_id
|
|
39
|
+
end
|
|
40
|
+
@client.trigger_async(name, event_name, data, params)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Trigger event
|
|
44
|
+
#
|
|
45
|
+
# [Deprecated] This method will be removed in a future gem version. Please
|
|
46
|
+
# switch to Sockudo.trigger or Sockudo::Client#trigger instead
|
|
47
|
+
#
|
|
48
|
+
# @example
|
|
49
|
+
# begin
|
|
50
|
+
# Sockudo['my-channel'].trigger!('an_event', {:some => 'data'})
|
|
51
|
+
# rescue Sockudo::Error => e
|
|
52
|
+
# # Do something on error
|
|
53
|
+
# end
|
|
54
|
+
#
|
|
55
|
+
# @param data [Object] Event data to be triggered in javascript.
|
|
56
|
+
# Objects other than strings will be converted to JSON
|
|
57
|
+
# @param socket_id Allows excluding a given socket_id from receiving the
|
|
58
|
+
# event - see http://sockudo.com/docs/publisher_api_guide/publisher_excluding_recipients for more info
|
|
59
|
+
#
|
|
60
|
+
# @raise [Sockudo::Error] on invalid Sockudo response - see the error message for more details
|
|
61
|
+
# @raise [Sockudo::HTTPError] on any error raised inside http client - the original error is available in the original_error attribute
|
|
62
|
+
#
|
|
63
|
+
def trigger!(event_name, data, socket_id = nil)
|
|
64
|
+
params = {}
|
|
65
|
+
if socket_id
|
|
66
|
+
validate_socket_id(socket_id)
|
|
67
|
+
params[:socket_id] = socket_id
|
|
68
|
+
end
|
|
69
|
+
@client.trigger(name, event_name, data, params)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Trigger event, catching and logging any errors.
|
|
73
|
+
#
|
|
74
|
+
# [Deprecated] This method will be removed in a future gem version. Please
|
|
75
|
+
# switch to Sockudo.trigger or Sockudo::Client#trigger instead
|
|
76
|
+
#
|
|
77
|
+
# @note CAUTION! No exceptions will be raised on failure
|
|
78
|
+
# @param (see #trigger!)
|
|
79
|
+
#
|
|
80
|
+
def trigger(event_name, data, socket_id = nil)
|
|
81
|
+
trigger!(event_name, data, socket_id)
|
|
82
|
+
rescue Sockudo::Error => e
|
|
83
|
+
Sockudo.logger.error("#{e.message} (#{e.class})")
|
|
84
|
+
Sockudo.logger.debug(e.backtrace.join("\n"))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Request info for a channel
|
|
88
|
+
#
|
|
89
|
+
# @example Response
|
|
90
|
+
# [{:occupied=>true, :subscription_count => 12}]
|
|
91
|
+
#
|
|
92
|
+
# @param info [Array] Array of attributes required (as lowercase strings)
|
|
93
|
+
# @return [Hash] Hash of requested attributes for this channel
|
|
94
|
+
# @raise [Sockudo::Error] on invalid Sockudo response - see the error message for more details
|
|
95
|
+
# @raise [Sockudo::HTTPError] on any error raised inside http client - the original error is available in the original_error attribute
|
|
96
|
+
#
|
|
97
|
+
def info(attributes = [])
|
|
98
|
+
@client.channel_info(name, :info => attributes.join(','))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Request users for a presence channel
|
|
102
|
+
# Only works on presence channels (see: http://sockudo.com/docs/client_api_guide/client_presence_channels and https://sockudo.com/docs/rest_api)
|
|
103
|
+
#
|
|
104
|
+
# @example Response
|
|
105
|
+
# [{:id=>"4"}]
|
|
106
|
+
#
|
|
107
|
+
# @param params [Hash] Hash of parameters for the API - see REST API docs
|
|
108
|
+
# @return [Hash] Array of user hashes for this channel
|
|
109
|
+
# @raise [Sockudo::Error] on invalid Sockudo response - see the error message for more details
|
|
110
|
+
# @raise [Sockudo::HTTPError] on any error raised inside Net::HTTP - the original error is available in the original_error attribute
|
|
111
|
+
#
|
|
112
|
+
def users(params = {})
|
|
113
|
+
@client.channel_users(name, params)[:users]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Compute authentication string required as part of the authentication
|
|
117
|
+
# endpoint response. Generally the authenticate method should be used in
|
|
118
|
+
# preference to this one
|
|
119
|
+
#
|
|
120
|
+
# @param socket_id [String] Each Sockudo socket connection receives a
|
|
121
|
+
# unique socket_id. This is sent from sockudo.js to your server when
|
|
122
|
+
# channel authentication is required.
|
|
123
|
+
# @param custom_string [String] Allows signing additional data
|
|
124
|
+
# @return [String]
|
|
125
|
+
#
|
|
126
|
+
# @raise [Sockudo::Error] if socket_id or custom_string invalid
|
|
127
|
+
#
|
|
128
|
+
def authentication_string(socket_id, custom_string = nil)
|
|
129
|
+
string_to_sign = [socket_id, name, custom_string].compact.map(&:to_s).join(':')
|
|
130
|
+
|
|
131
|
+
_authentication_string(socket_id, string_to_sign, @client.authentication_token, custom_string)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Generate the expected response for an authentication endpoint.
|
|
135
|
+
# See http://sockudo.com/docs/authenticating_users for details.
|
|
136
|
+
#
|
|
137
|
+
# @example Private channels
|
|
138
|
+
# render :json => Sockudo['private-my_channel'].authenticate(params[:socket_id])
|
|
139
|
+
#
|
|
140
|
+
# @example Presence channels
|
|
141
|
+
# render :json => Sockudo['presence-my_channel'].authenticate(params[:socket_id], {
|
|
142
|
+
# :user_id => current_user.id, # => required
|
|
143
|
+
# :user_info => { # => optional - for example
|
|
144
|
+
# :name => current_user.name,
|
|
145
|
+
# :email => current_user.email
|
|
146
|
+
# }
|
|
147
|
+
# })
|
|
148
|
+
#
|
|
149
|
+
# @param socket_id [String]
|
|
150
|
+
# @param custom_data [Hash] used for example by private channels
|
|
151
|
+
#
|
|
152
|
+
# @return [Hash]
|
|
153
|
+
#
|
|
154
|
+
# @raise [Sockudo::Error] if socket_id or custom_data is invalid
|
|
155
|
+
#
|
|
156
|
+
# @private Custom data is sent to server as JSON-encoded string
|
|
157
|
+
#
|
|
158
|
+
def authenticate(socket_id, custom_data = nil)
|
|
159
|
+
custom_data = MultiJson.encode(custom_data) if custom_data
|
|
160
|
+
auth = authentication_string(socket_id, custom_data)
|
|
161
|
+
r = {:auth => auth}
|
|
162
|
+
r[:channel_data] = custom_data if custom_data
|
|
163
|
+
r
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def shared_secret(encryption_master_key)
|
|
167
|
+
return unless encryption_master_key
|
|
168
|
+
|
|
169
|
+
secret_string = @name + encryption_master_key
|
|
170
|
+
digest = OpenSSL::Digest::SHA256.new
|
|
171
|
+
digest << secret_string
|
|
172
|
+
digest.digest
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
private
|
|
176
|
+
|
|
177
|
+
include Sockudo::Utils
|
|
178
|
+
end
|
|
179
|
+
end
|