gripcontrol 1.2.1 → 1.2.2

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: cae2f828396f43ecec0430ca4dec5ed6bb3781a6
4
- data.tar.gz: b232a252079b157d246c9d33f409511a1d6d3bc4
3
+ metadata.gz: 58499f9d93b611ba458fbdeab01a6e124f22d944
4
+ data.tar.gz: a56f77a99c922f9399d4b926eccea80d7e108305
5
5
  SHA512:
6
- metadata.gz: 82727271f42df23c2758106b287364d0522afa37eb8697dce5d9da997ee311fcda9b18d06192618be998ab25962ead3fd499eb4ecc930a017217ba4dbc02cdb5
7
- data.tar.gz: 5f83386f017dd97bc2337176c0b596d3ee201dc5721ca8a7ebebfdd2956aa9dfa234b440fe45152ad12dbc410bc099c05759e36f1d0fab1937568f5d0ee5282b
6
+ metadata.gz: ee47b91038d0645aec69a8b41123737df2e18866f700dc8474217ed097738a22e3757ebc945812091a6db9caec1b927017f148e45a0bd48042b9ff5d9f318e8e
7
+ data.tar.gz: dc032e0a364295699fa43049edfe498a08b476812161ea8a314f3c507b4a21045b47b04131b7129ed25aa75fb547e60c77219765314cd1045bc31d617b842948
data/lib/channel.rb CHANGED
@@ -5,10 +5,13 @@
5
5
  # :copyright: (c) 2015 by Fanout, Inc.
6
6
  # :license: MIT, see LICENSE for more details.
7
7
 
8
+ # The Channel class is used to represent a channel in for a GRIP proxy and
9
+ # tracks the previous ID of the last message.
8
10
  class Channel
9
11
  attr_accessor :name
10
12
  attr_accessor :prev_id
11
13
 
14
+ # Initialize with the channel name and an optional previous ID.
12
15
  def initialize(name, prev_id=nil)
13
16
  @name = name
14
17
  @prev_id = prev_id
data/lib/gripcontrol.rb CHANGED
@@ -17,55 +17,30 @@ require_relative 'websocketevent.rb'
17
17
  require_relative 'grippubcontrol.rb'
18
18
  require_relative 'response.rb'
19
19
 
20
+ # The GripControl class provides functionality that is used in conjunction
21
+ # with GRIP proxies. This includes facilitating the creation of hold
22
+ # instructions for HTTP long-polling and HTTP streaming, parsing GRIP URIs
23
+ # into config objects, validating the GRIP-SIG header coming from GRIP
24
+ # proxies, creating GRIP channel headers, and also WebSocket-over-HTTP
25
+ # features such as encoding/decoding web socket events and generating
26
+ # control messages.
20
27
  class GripControl
28
+
29
+ # Create GRIP hold instructions for the specified mode, channels, response
30
+ # and optional timeout value. The channel parameter can be specified as
31
+ # either a string representing the channel name, a Channel instance or an
32
+ # array of Channel instances. The response parameter can be specified as
33
+ # either a string representing the response body or a Response instance.
21
34
  def self.create_hold(mode, channels, response, timeout=nil)
22
35
  hold = Hash.new
23
36
  hold['mode'] = mode
24
- if channels.is_a?(Channel)
25
- channels = [channels]
26
- elsif channels.is_a?(String)
27
- channels = [Channel.new(channels)]
28
- end
29
- raise 'channels.length equal to 0' unless channels.length > 0
30
- ichannels = []
31
- channels.each do |channel|
32
- if channel.is_a?(String)
33
- channel = Channel(channel)
34
- end
35
- ichannel = Hash.new
36
- ichannel['name'] = channel.name
37
- if !channel.prev_id.nil?
38
- ichannel['prev-id'] = channel.prev_id
39
- end
40
- ichannels.push(ichannel)
41
- end
37
+ channels = GripControl.parse_channels(channels)
38
+ ichannels = GripControl.get_hold_channels(channels)
42
39
  hold['channels'] = ichannels
43
40
  if !timeout.nil?
44
41
  hold['timeout'] = timeout
45
42
  end
46
- iresponse = nil
47
- if !response.nil?
48
- if response.is_a?(String)
49
- response = Response(nil, nil, nil, response)
50
- end
51
- iresponse = Hash.new
52
- if !response.code.nil?
53
- iresponse['code'] = response.code
54
- end
55
- if !response.reason.nil?
56
- iresponse['reason'] = response.reason
57
- end
58
- if !response.headers.nil? and response.headers.length > 0
59
- iresponse['headers'] = response.headers
60
- end
61
- if !response.body.nil?
62
- if response.body.encoding.name == 'ASCII-8BIT'
63
- iresponse['body-bin'] = Base64.encode64(response.body)
64
- else
65
- iresponse['body'] = response.body
66
- end
67
- end
68
- end
43
+ iresponse = GripControl.get_hold_response(response)
69
44
  instruct = Hash.new
70
45
  instruct['hold'] = hold
71
46
  if !iresponse.nil?
@@ -74,9 +49,17 @@ class GripControl
74
49
  return instruct.to_json
75
50
  end
76
51
 
52
+ # Parse the specified GRIP URI into a config object that can then be passed
53
+ # to the GripPubControl class. The URI can include 'iss' and 'key' JWT
54
+ # authentication query parameters as well as any other required query string
55
+ # parameters. The JWT 'key' query parameter can be provided as-is or in base64
56
+ # encoded format.
77
57
  def self.parse_grip_uri(uri)
78
58
  uri = URI(uri)
79
- params = CGI.parse(uri.query)
59
+ params = {}
60
+ if (uri.query)
61
+ params = CGI.parse(uri.query)
62
+ end
80
63
  iss = nil
81
64
  key = nil
82
65
  if params.key?('iss')
@@ -93,7 +76,7 @@ class GripControl
93
76
  qs = []
94
77
  params.map do |name,values|
95
78
  values.map do |value|
96
- qs.push('#{CGI.escape name}=#{CGI.escape value}')
79
+ qs.push("#{CGI.escape name}=#{CGI.escape value}")
97
80
  end
98
81
  end
99
82
  qs = qs.join('&')
@@ -119,6 +102,9 @@ class GripControl
119
102
  return out
120
103
  end
121
104
 
105
+ # Validate the specified JWT token and key. This method is used to validate
106
+ # the GRIP-SIG header coming from GRIP proxies such as Pushpin or Fanout.io.
107
+ # Note that the token expiration is also verified.
122
108
  def self.validate_sig(token, key)
123
109
  token = token.encode('utf-8')
124
110
  begin
@@ -135,13 +121,13 @@ class GripControl
135
121
  return true
136
122
  end
137
123
 
124
+ # Create a GRIP channel header for the specified channels. The channels
125
+ # parameter can be specified as a string representing the channel name,
126
+ # a Channel instance, or an array of Channel instances. The returned GRIP
127
+ # channel header is used when sending instructions to GRIP proxies via
128
+ # HTTP headers.
138
129
  def self.create_grip_channel_header(channels)
139
- if channels.is_a?(Channel)
140
- channels = [channels]
141
- elsif channels.is_a?(String)
142
- channels = [Channel.new(channels)]
143
- end
144
- raise 'channels.length equal to 0' unless channels.length > 0
130
+ channels = parse_channels(channels)
145
131
  parts = []
146
132
  channels.each do |channel|
147
133
  s = channel.name
@@ -153,14 +139,23 @@ class GripControl
153
139
  return parts.join(', ')
154
140
  end
155
141
 
142
+ # A convenience method for creating GRIP hold response instructions for HTTP
143
+ # long-polling. This method simply passes the specified parameters to the
144
+ # create_hold method with 'response' as the hold mode.
156
145
  def self.create_hold_response(channels, response=nil, timeout=nil)
157
146
  return GripControl.create_hold('response', channels, response, timeout)
158
147
  end
159
148
 
149
+ # A convenience method for creating GRIP hold stream instructions for HTTP
150
+ # streaming. This method simply passes the specified parameters to the
151
+ # create_hold method with 'stream' as the hold mode.
160
152
  def self.create_hold_stream(channels, response=nil)
161
153
  return create_hold('stream', channels, response)
162
154
  end
163
155
 
156
+ # Decode the specified HTTP request body into an array of WebSocketEvent
157
+ # instances when using the WebSocket-over-HTTP protocol. A RuntimeError
158
+ # is raised if the format is invalid.
164
159
  def self.decode_websocket_events(body)
165
160
  out = []
166
161
  start = 0
@@ -187,6 +182,9 @@ class GripControl
187
182
  return out
188
183
  end
189
184
 
185
+ # Encode the specified array of WebSocketEvent instances. The returned string
186
+ # value should then be passed to a GRIP proxy in the body of an HTTP response
187
+ # when using the WebSocket-over-HTTP protocol.
190
188
  def self.encode_websocket_events(events)
191
189
  out = ''
192
190
  events.each do |event|
@@ -200,6 +198,10 @@ class GripControl
200
198
  return out
201
199
  end
202
200
 
201
+ # Generate a WebSocket control message with the specified type and optional
202
+ # arguments. WebSocket control messages are passed to GRIP proxies and
203
+ # example usage includes subscribing/unsubscribing a WebSocket connection
204
+ # to/from a channel.
203
205
  def self.websocket_control_message(type, args=nil)
204
206
  if !args.nil?
205
207
  out = Marshal.load(Marshal.dump(args))
@@ -209,4 +211,66 @@ class GripControl
209
211
  out['type'] = type
210
212
  return out.to_json
211
213
  end
214
+
215
+ private
216
+
217
+ # Parse the specified parameter into an array of Channel instances. The
218
+ # specified parameter can either be a string, a Channel instance, or
219
+ # an array of Channel instances.
220
+ def self.parse_channels(channels)
221
+ if channels.is_a?(Channel)
222
+ channels = [channels]
223
+ elsif channels.is_a?(String)
224
+ channels = [Channel.new(channels)]
225
+ end
226
+ raise 'channels.length equal to 0' unless channels.length > 0
227
+ return channels
228
+ end
229
+
230
+ # Get an array of hashes representing the specified channels parameter. The
231
+ # resulting array is used for creating GRIP proxy hold instructions.
232
+ def self.get_hold_channels(channels)
233
+ ichannels = []
234
+ channels.each do |channel|
235
+ if channel.is_a?(String)
236
+ channel = Channel(channel)
237
+ end
238
+ ichannel = Hash.new
239
+ ichannel['name'] = channel.name
240
+ if !channel.prev_id.nil?
241
+ ichannel['prev-id'] = channel.prev_id
242
+ end
243
+ ichannels.push(ichannel)
244
+ end
245
+ return ichannels
246
+ end
247
+
248
+ # Get a hash representing the specified response parameter. The
249
+ # resulting hash is used for creating GRIP proxy hold instructions.
250
+ def self.get_hold_response(response)
251
+ iresponse = nil
252
+ if !response.nil?
253
+ if response.is_a?(String)
254
+ response = Response.new(nil, nil, nil, response)
255
+ end
256
+ iresponse = Hash.new
257
+ if !response.code.nil?
258
+ iresponse['code'] = response.code
259
+ end
260
+ if !response.reason.nil?
261
+ iresponse['reason'] = response.reason
262
+ end
263
+ if !response.headers.nil? and response.headers.length > 0
264
+ iresponse['headers'] = response.headers
265
+ end
266
+ if !response.body.nil?
267
+ if response.body.clone.force_encoding("UTF-8").valid_encoding?
268
+ iresponse['body'] = response.body
269
+ else
270
+ iresponse['body-bin'] = Base64.encode64(response.body)
271
+ end
272
+ end
273
+ end
274
+ return iresponse
275
+ end
212
276
  end
@@ -7,11 +7,19 @@
7
7
 
8
8
  require 'pubcontrol'
9
9
 
10
+ # The GripPubControl class allows consumers to easily publish HTTP response
11
+ # and HTTP stream format messages to GRIP proxies. Configuring GripPubControl
12
+ # is slightly different from configuring PubControl in that the 'uri' and
13
+ # 'iss' keys in each config entry should have a 'control_' prefix.
14
+ # GripPubControl inherits from PubControl and therefore also provides all
15
+ # of the same functionality.
10
16
  class GripPubControl < PubControl
11
17
  alias super_add_client add_client
12
18
  alias super_publish publish
13
19
  alias super_publish_async publish_async
14
20
 
21
+ # Initialize with or without a configuration. A configuration can be applied
22
+ # after initialization via the apply_grip_config method.
15
23
  def initialize(config=nil)
16
24
  @clients = Array.new
17
25
  if !config.nil?
@@ -19,6 +27,11 @@ class GripPubControl < PubControl
19
27
  end
20
28
  end
21
29
 
30
+ # Apply the specified configuration to this GripPubControl instance. The
31
+ # configuration object can either be a hash or an array of hashes where
32
+ # each hash corresponds to a single PubControlClient instance. Each hash
33
+ # will be parsed and a PubControlClient will be created either using just
34
+ # a URI or a URI and JWT authentication information.
22
35
  def apply_grip_config(config)
23
36
  if !config.is_a?(Array)
24
37
  config = [config]
@@ -35,6 +48,12 @@ class GripPubControl < PubControl
35
48
  end
36
49
  end
37
50
 
51
+ # Synchronously publish an HTTP response format message to all of the
52
+ # configured PubControlClients with a specified channel, message, and
53
+ # optional ID and previous ID. Note that the 'http_response' parameter can
54
+ # be provided as either an HttpResponseFormat instance or a string (in which
55
+ # case an HttpResponseFormat instance will automatically be created and
56
+ # have the 'body' field set to the specified string).
38
57
  def publish_http_response(channel, http_response, id=nil, prev_id=nil)
39
58
  if http_response.is_a?(String)
40
59
  http_response = HttpResponseFormat.new(nil, nil, nil, http_response)
@@ -43,6 +62,14 @@ class GripPubControl < PubControl
43
62
  super_publish(channel, item)
44
63
  end
45
64
 
65
+ # Asynchronously publish an HTTP response format message to all of the
66
+ # configured PubControlClients with a specified channel, message, and
67
+ # optional ID, previous ID, and callback. Note that the 'http_response'
68
+ # parameter can be provided as either an HttpResponseFormat instance or
69
+ # a string (in which case an HttpResponseFormat instance will automatically
70
+ # be created and have the 'body' field set to the specified string). When
71
+ # specified, the callback method will be called after publishing is complete
72
+ # and passed a result and error message (if an error was encountered).
46
73
  def publish_http_response_async(channel, http_response, id=nil,
47
74
  prev_id=nil, callback=nil)
48
75
  if http_response.is_a?(String)
@@ -52,6 +79,12 @@ class GripPubControl < PubControl
52
79
  super_publish_async(channel, item, callback)
53
80
  end
54
81
 
82
+ # Synchronously publish an HTTP stream format message to all of the
83
+ # configured PubControlClients with a specified channel, message, and
84
+ # optional ID and previous ID. Note that the 'http_stream' parameter can
85
+ # be provided as either an HttpStreamFormat instance or a string (in which
86
+ # case an HttStreamFormat instance will automatically be created and
87
+ # have the 'content' field set to the specified string).
55
88
  def publish_http_stream(channel, http_stream, id=nil, prev_id=nil)
56
89
  if http_stream.is_a?(String)
57
90
  http_stream = HttpStreamFormat.new(http_stream)
@@ -60,6 +93,14 @@ class GripPubControl < PubControl
60
93
  super_publish(channel, item)
61
94
  end
62
95
 
96
+ # Asynchronously publish an HTTP stream format message to all of the
97
+ # configured PubControlClients with a specified channel, message, and
98
+ # optional ID, previous ID, and callback. Note that the 'http_stream'
99
+ # parameter can be provided as either an HttpStreamFormat instance or
100
+ # a string (in which case an HttpStreamFormat instance will automatically
101
+ # be created and have the 'content' field set to the specified string). When
102
+ # specified, the callback method will be called after publishing is complete
103
+ # and passed a result and error message (if an error was encountered).
63
104
  def publish_http_stream_async(channel, http_stream, id=nil,
64
105
  prev_id=nil, callback=nil)
65
106
  if http_stream.is_a?(String)
@@ -8,12 +8,16 @@
8
8
  require 'base64'
9
9
  require 'pubcontrol'
10
10
 
11
+ # The HttpResponseFormat class is the format used to publish messages to
12
+ # HTTP response clients connected to a GRIP proxy.
11
13
  class HttpResponseFormat < Format
12
14
  attr_accessor :code
13
15
  attr_accessor :reason
14
16
  attr_accessor :headers
15
17
  attr_accessor :body
16
18
 
19
+ # Initialize with the message code, reason, headers, and body to send
20
+ # to the client when the message is publishing.
17
21
  def initialize(code=nil, reason=nil, headers=nil, body=nil)
18
22
  @code = code
19
23
  @reason = reason
@@ -21,10 +25,14 @@ class HttpResponseFormat < Format
21
25
  @body = body
22
26
  end
23
27
 
28
+ # The name used when publishing this format.
24
29
  def name
25
30
  return 'http-response'
26
31
  end
27
32
 
33
+ # Export the message into the required format and include only the fields
34
+ # that are set. The body is exported as base64 if the text is encoded as
35
+ # binary.
28
36
  def export
29
37
  out = Hash.new
30
38
  if !@code.nil?
@@ -37,10 +45,10 @@ class HttpResponseFormat < Format
37
45
  out['headers'] = @headers
38
46
  end
39
47
  if !@body.nil?
40
- if @body.encoding.name == 'ASCII-8BIT'
41
- out['body-bin'] = Base64.encode64(@body)
42
- else
48
+ if @body.clone.force_encoding("UTF-8").valid_encoding?
43
49
  out['body'] = @body
50
+ else
51
+ out['body-bin'] = Base64.encode64(@body)
44
52
  end
45
53
  end
46
54
  return out
@@ -8,10 +8,15 @@
8
8
  require 'base64'
9
9
  require 'pubcontrol'
10
10
 
11
+ # The HttpStreamFormat class is the format used to publish messages to
12
+ # HTTP stream clients connected to a GRIP proxy.
11
13
  class HttpStreamFormat < Format
12
14
  attr_accessor :content
13
15
  attr_accessor :close
14
16
 
17
+ # Initialize with either the message content or a boolean indicating that
18
+ # the streaming connection should be closed. If neither the content nor
19
+ # the boolean flag is set then an error will be raised.
15
20
  def initialize(content=nil, close=false)
16
21
  @content = content
17
22
  @close = close
@@ -20,19 +25,23 @@ class HttpStreamFormat < Format
20
25
  end
21
26
  end
22
27
 
28
+ # The name used when publishing this format.
23
29
  def name
24
30
  return 'http-stream'
25
31
  end
26
32
 
33
+ # Exports the message in the required format depending on whether the
34
+ # message content is binary or not, or whether the connection should
35
+ # be closed.
27
36
  def export
28
37
  out = Hash.new
29
38
  if @close
30
39
  out['action'] = 'close'
31
40
  else
32
- if @content.encoding.name == 'ASCII-8BIT'
33
- out['content-bin'] = Base64.encode64(@content)
34
- else
41
+ if @content.clone.force_encoding("UTF-8").valid_encoding?
35
42
  out['content'] = @content
43
+ else
44
+ out['content-bin'] = Base64.encode64(@content)
36
45
  end
37
46
  end
38
47
  return out
data/lib/response.rb CHANGED
@@ -5,12 +5,18 @@
5
5
  # :copyright: (c) 2015 by Fanout, Inc.
6
6
  # :license: MIT, see LICENSE for more details.
7
7
 
8
+ # The Response class is used to represent a set of HTTP response data.
9
+ # Populated instances of this class are serialized to JSON and passed
10
+ # to the GRIP proxy in the body. The GRIP proxy then parses the message
11
+ # and deserialized the JSON into an HTTP response that is passed back
12
+ # to the client.
8
13
  class Response
9
14
  attr_accessor :code
10
15
  attr_accessor :reason
11
16
  attr_accessor :headers
12
17
  attr_accessor :body
13
18
 
19
+ # Initialize with an HTTP response code, reason, headers, and body.
14
20
  def initialize(code=nil, reason=nil, headers=nil, body=nil)
15
21
  @code = code
16
22
  @reason = reason
@@ -5,10 +5,14 @@
5
5
  # :copyright: (c) 2015 by Fanout, Inc.
6
6
  # :license: MIT, see LICENSE for more details.
7
7
 
8
+ # The WebSocketEvent class represents WebSocket event information that is
9
+ # used with the GRIP WebSocket-over-HTTP protocol. It includes information
10
+ # about the type of event as well as an optional content field.
8
11
  class WebSocketEvent
9
12
  attr_accessor :type
10
13
  attr_accessor :content
11
14
 
15
+ # Initialize with a specified event type and optional content information.
12
16
  def initialize(type, content=nil)
13
17
  @type = type
14
18
  @content = content
@@ -8,18 +8,25 @@
8
8
  require 'base64'
9
9
  require 'pubcontrol'
10
10
 
11
+ # The WebSocketMessageFormat class is the format used to publish data to
12
+ # WebSocket clients connected to GRIP proxies.
11
13
  class WebSocketMessageFormat < Format
12
14
  attr_accessor :content
13
15
 
16
+ # Initialize with the message content and a flag indicating whether the
17
+ # message content should be sent as base64-encoded binary data.
14
18
  def initialize(content, binary=false)
15
19
  @content = content
16
20
  @binary = binary
17
21
  end
18
22
 
23
+ # The name used when publishing this format.
19
24
  def name
20
25
  return 'ws-message'
21
26
  end
22
27
 
28
+ # Exports the message in the required format depending on whether the
29
+ # message content is binary or not.
23
30
  def export
24
31
  out = Hash.new
25
32
  if @binary
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gripcontrol
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Bokarius
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-18 00:00:00.000000000 Z
11
+ date: 2015-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pubcontrol