pubcontrol 0.2.0 → 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 +4 -4
- data/lib/{pccbhandler.rb → pcccbhandler.rb} +3 -3
- data/lib/pubcontrol.rb +32 -159
- data/lib/pubcontrolclient.rb +188 -0
- metadata +3 -3
- data/lib/pubcontrolset.rb +0 -72
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 72a4849cda3e8c5921a09ff97df674726be6bc06
|
4
|
+
data.tar.gz: 53cb42aa569100d5f2d788545bcdd8a43435e904
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c6bec7d9dd4efd0806012f471449ef6086a5c97427fb13ce8ca26290a679d0809e94e953ec1f294dfc4a01f3f7c10c42c517ab409ef90e19705767fe2911a56
|
7
|
+
data.tar.gz: beda44c933432151d9ccc7b7fa73430c789830ca5f1e9db0f76a4a7fb5d9ec56def1c61913d551799c429af14c2b7884a96cafb8edf5de1f081088e30418e6b6
|
@@ -1,11 +1,11 @@
|
|
1
|
-
#
|
1
|
+
# pcccbhandler.rb
|
2
2
|
# ~~~~~~~~~
|
3
|
-
# This module implements the
|
3
|
+
# This module implements the PubControlClientCallbackHandler class.
|
4
4
|
# :authors: Konstantin Bokarius.
|
5
5
|
# :copyright: (c) 2015 by Fanout, Inc.
|
6
6
|
# :license: MIT, see LICENSE for more details.
|
7
7
|
|
8
|
-
class
|
8
|
+
class PubControlClientCallbackHandler
|
9
9
|
def initialize(num_calls, callback)
|
10
10
|
@num_calls = num_calls
|
11
11
|
@callback = callback
|
data/lib/pubcontrol.rb
CHANGED
@@ -5,187 +5,60 @@
|
|
5
5
|
# :copyright: (c) 2015 by Fanout, Inc.
|
6
6
|
# :license: MIT, see LICENSE for more details.
|
7
7
|
|
8
|
-
require 'algorithms'
|
9
|
-
require 'thread'
|
10
|
-
require 'base64'
|
11
|
-
require 'jwt'
|
12
|
-
require 'json'
|
13
|
-
require 'net/http'
|
14
|
-
require_relative 'item.rb'
|
15
8
|
require_relative 'format.rb'
|
16
|
-
require_relative '
|
9
|
+
require_relative 'item.rb'
|
10
|
+
require_relative 'pubcontrolclient.rb'
|
11
|
+
require_relative 'pcccbhandler.rb'
|
17
12
|
|
18
13
|
class PubControl
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
@lock = Mutex.new
|
24
|
-
@thread = nil
|
25
|
-
@thread_cond = nil
|
26
|
-
@thread_mutex = nil
|
27
|
-
@req_queue = Containers::Deque.new
|
28
|
-
@auth_basic_user = nil
|
29
|
-
@auth_basic_pass = nil
|
30
|
-
@auth_jwt_claim = nil
|
31
|
-
@auth_jwt_key = nil
|
32
|
-
end
|
33
|
-
|
34
|
-
def set_auth_basic(username, password)
|
35
|
-
@lock.synchronize do
|
36
|
-
@auth_basic_user = username
|
37
|
-
@auth_basic_pass = password
|
14
|
+
def initialize(config=nil)
|
15
|
+
@clients = Array.new
|
16
|
+
if !config.nil?
|
17
|
+
apply_config(config)
|
38
18
|
end
|
39
19
|
end
|
40
20
|
|
41
|
-
def
|
42
|
-
@
|
43
|
-
@auth_jwt_claim = claim
|
44
|
-
@auth_jwt_key = key
|
45
|
-
end
|
21
|
+
def remove_all_clients
|
22
|
+
@clients = Array.new
|
46
23
|
end
|
47
24
|
|
48
|
-
def
|
49
|
-
|
50
|
-
export['channel'] = channel
|
51
|
-
uri = nil
|
52
|
-
auth = nil
|
53
|
-
@lock.synchronize do
|
54
|
-
uri = @uri
|
55
|
-
auth = gen_auth_header
|
56
|
-
end
|
57
|
-
PubControl.pubcall(uri, auth, [export])
|
25
|
+
def add_client(client)
|
26
|
+
@clients.push(client)
|
58
27
|
end
|
59
28
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
uri = nil
|
64
|
-
auth = nil
|
65
|
-
@lock.synchronize do
|
66
|
-
uri = @uri
|
67
|
-
auth = gen_auth_header
|
68
|
-
ensure_thread
|
29
|
+
def apply_config(config)
|
30
|
+
if !config.is_a?(Array)
|
31
|
+
config = [config]
|
69
32
|
end
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
@lock.synchronize do
|
75
|
-
if !@thread.nil?
|
76
|
-
queue_req(['stop'])
|
77
|
-
@thread.join
|
78
|
-
@thread = nil
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def pubworker
|
84
|
-
quit = false
|
85
|
-
while !quit do
|
86
|
-
@thread_mutex.lock
|
87
|
-
if @req_queue.length == 0
|
88
|
-
@thread_cond.wait(@thread_mutex)
|
89
|
-
if @req_queue.length == 0
|
90
|
-
@thread_mutex.unlock
|
91
|
-
next
|
92
|
-
end
|
93
|
-
end
|
94
|
-
reqs = Array.new
|
95
|
-
while @req_queue.length > 0 and reqs.length < 10 do
|
96
|
-
m = @req_queue.pop_front
|
97
|
-
if m[0] == 'stop'
|
98
|
-
quit = true
|
99
|
-
break
|
100
|
-
end
|
101
|
-
reqs.push([m[1], m[2], m[3], m[4]])
|
102
|
-
end
|
103
|
-
@thread_mutex.unlock
|
104
|
-
if reqs.length > 0
|
105
|
-
PubControl.pubbatch(reqs)
|
33
|
+
config.each do |entry|
|
34
|
+
pub = PubControlClient.new(entry['uri'])
|
35
|
+
if entry.key?('iss')
|
36
|
+
pub.set_auth_jwt({'iss' => entry['iss']}, entry['key'])
|
106
37
|
end
|
38
|
+
@clients.push(pub)
|
107
39
|
end
|
108
40
|
end
|
109
41
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
113
|
-
content['items'] = items
|
114
|
-
request = Net::HTTP::Post.new(uri.request_uri)
|
115
|
-
request.body = content.to_json
|
116
|
-
if !auth_header.nil?
|
117
|
-
request['Authorization'] = auth_header
|
118
|
-
end
|
119
|
-
request['Content-Type'] = 'application/json'
|
120
|
-
use_ssl = uri.scheme == 'https'
|
121
|
-
response = Net::HTTP.start(uri.host, uri.port, use_ssl: use_ssl) do |http|
|
122
|
-
http.request(request)
|
123
|
-
end
|
124
|
-
# REVIEW: HTTPSuccess does not include 3xx status codes.
|
125
|
-
if !response.kind_of? Net::HTTPSuccess
|
126
|
-
raise 'failed to publish: ' + response.class.to_s + ' ' +
|
127
|
-
response.message
|
42
|
+
def finish
|
43
|
+
@clients.each do |pub|
|
44
|
+
pub.finish
|
128
45
|
end
|
129
46
|
end
|
130
47
|
|
131
|
-
def
|
132
|
-
|
133
|
-
|
134
|
-
auth_header = reqs[0][1]
|
135
|
-
items = Array.new
|
136
|
-
callbacks = Array.new
|
137
|
-
reqs.each do |req|
|
138
|
-
items.push(req[2])
|
139
|
-
callbacks.push(req[3])
|
140
|
-
end
|
141
|
-
begin
|
142
|
-
PubControl.pubcall(uri, auth_header, items)
|
143
|
-
result = [true, '']
|
144
|
-
rescue => e
|
145
|
-
result = [false, e.message]
|
146
|
-
end
|
147
|
-
callbacks.each do |callback|
|
148
|
-
if !callback.nil?
|
149
|
-
callback.call(result[0], result[1])
|
150
|
-
end
|
48
|
+
def publish(channel, item)
|
49
|
+
@clients.each do |pub|
|
50
|
+
pub.publish(channel, item)
|
151
51
|
end
|
152
52
|
end
|
153
53
|
|
154
|
-
def
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
def gen_auth_header
|
161
|
-
if !@auth_basic_user.nil?
|
162
|
-
return 'Basic ' + Base64.encode64(
|
163
|
-
'#{@auth_basic_user}:#{@auth_basic_pass}')
|
164
|
-
elsif !@auth_jwt_claim.nil?
|
165
|
-
if !@auth_jwt_claim.has_key?('exp')
|
166
|
-
claim = @auth_jwt_claim.clone
|
167
|
-
claim['exp'] = PubControl.timestamp_utcnow + 3600
|
168
|
-
else
|
169
|
-
claim = @auth_jwt_claim
|
170
|
-
end
|
171
|
-
return 'Bearer ' + JWT.encode(claim, @auth_jwt_key)
|
172
|
-
else
|
173
|
-
return nil
|
54
|
+
def publish_async(channel, item, callback=nil)
|
55
|
+
cb = nil
|
56
|
+
if !callback.nil?
|
57
|
+
cb = PubControlClientCallbackHandler.new(@clients.length, callback).
|
58
|
+
handler_method_symbol
|
174
59
|
end
|
175
|
-
|
176
|
-
|
177
|
-
def ensure_thread
|
178
|
-
if @thread.nil?
|
179
|
-
@thread_cond = ConditionVariable.new
|
180
|
-
@thread_mutex = Mutex.new
|
181
|
-
@thread = Thread.new { pubworker }
|
60
|
+
@clients.each do |pub|
|
61
|
+
pub.publish_async(channel, item, cb)
|
182
62
|
end
|
183
63
|
end
|
184
|
-
|
185
|
-
def queue_req(req)
|
186
|
-
@thread_mutex.lock
|
187
|
-
@req_queue.push_back(req)
|
188
|
-
@thread_cond.signal
|
189
|
-
@thread_mutex.unlock
|
190
|
-
end
|
191
64
|
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# pubcontrolclient.rb
|
2
|
+
# ~~~~~~~~~
|
3
|
+
# This module implements the PubControlClient class.
|
4
|
+
# :authors: Konstantin Bokarius.
|
5
|
+
# :copyright: (c) 2015 by Fanout, Inc.
|
6
|
+
# :license: MIT, see LICENSE for more details.
|
7
|
+
|
8
|
+
require 'algorithms'
|
9
|
+
require 'thread'
|
10
|
+
require 'base64'
|
11
|
+
require 'jwt'
|
12
|
+
require 'json'
|
13
|
+
require 'net/http'
|
14
|
+
require_relative 'item.rb'
|
15
|
+
|
16
|
+
class PubControlClient
|
17
|
+
attr_accessor :req_queue
|
18
|
+
|
19
|
+
def initialize(uri)
|
20
|
+
@uri = uri
|
21
|
+
@lock = Mutex.new
|
22
|
+
@thread = nil
|
23
|
+
@thread_cond = nil
|
24
|
+
@thread_mutex = nil
|
25
|
+
@req_queue = Containers::Deque.new
|
26
|
+
@auth_basic_user = nil
|
27
|
+
@auth_basic_pass = nil
|
28
|
+
@auth_jwt_claim = nil
|
29
|
+
@auth_jwt_key = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_auth_basic(username, password)
|
33
|
+
@lock.synchronize do
|
34
|
+
@auth_basic_user = username
|
35
|
+
@auth_basic_pass = password
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_auth_jwt(claim, key)
|
40
|
+
@lock.synchronize do
|
41
|
+
@auth_jwt_claim = claim
|
42
|
+
@auth_jwt_key = key
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def publish(channel, item)
|
47
|
+
export = item.export
|
48
|
+
export['channel'] = channel
|
49
|
+
uri = nil
|
50
|
+
auth = nil
|
51
|
+
@lock.synchronize do
|
52
|
+
uri = @uri
|
53
|
+
auth = gen_auth_header
|
54
|
+
end
|
55
|
+
PubControlClient.pubcall(uri, auth, [export])
|
56
|
+
end
|
57
|
+
|
58
|
+
def publish_async(channel, item, callback=nil)
|
59
|
+
export = item.export
|
60
|
+
export['channel'] = channel
|
61
|
+
uri = nil
|
62
|
+
auth = nil
|
63
|
+
@lock.synchronize do
|
64
|
+
uri = @uri
|
65
|
+
auth = gen_auth_header
|
66
|
+
ensure_thread
|
67
|
+
end
|
68
|
+
queue_req(['pub', uri, auth, export, callback])
|
69
|
+
end
|
70
|
+
|
71
|
+
def finish
|
72
|
+
@lock.synchronize do
|
73
|
+
if !@thread.nil?
|
74
|
+
queue_req(['stop'])
|
75
|
+
@thread.join
|
76
|
+
@thread = nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def pubworker
|
82
|
+
quit = false
|
83
|
+
while !quit do
|
84
|
+
@thread_mutex.lock
|
85
|
+
if @req_queue.length == 0
|
86
|
+
@thread_cond.wait(@thread_mutex)
|
87
|
+
if @req_queue.length == 0
|
88
|
+
@thread_mutex.unlock
|
89
|
+
next
|
90
|
+
end
|
91
|
+
end
|
92
|
+
reqs = Array.new
|
93
|
+
while @req_queue.length > 0 and reqs.length < 10 do
|
94
|
+
m = @req_queue.pop_front
|
95
|
+
if m[0] == 'stop'
|
96
|
+
quit = true
|
97
|
+
break
|
98
|
+
end
|
99
|
+
reqs.push([m[1], m[2], m[3], m[4]])
|
100
|
+
end
|
101
|
+
@thread_mutex.unlock
|
102
|
+
if reqs.length > 0
|
103
|
+
PubControlClient.pubbatch(reqs)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.pubcall(uri, auth_header, items)
|
109
|
+
uri = URI(uri + '/publish/')
|
110
|
+
content = Hash.new
|
111
|
+
content['items'] = items
|
112
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
113
|
+
request.body = content.to_json
|
114
|
+
if !auth_header.nil?
|
115
|
+
request['Authorization'] = auth_header
|
116
|
+
end
|
117
|
+
request['Content-Type'] = 'application/json'
|
118
|
+
use_ssl = uri.scheme == 'https'
|
119
|
+
response = Net::HTTP.start(uri.host, uri.port, use_ssl: use_ssl) do |http|
|
120
|
+
http.request(request)
|
121
|
+
end
|
122
|
+
if !response.kind_of? Net::HTTPSuccess
|
123
|
+
raise 'failed to publish: ' + response.class.to_s + ' ' +
|
124
|
+
response.message
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.pubbatch(reqs)
|
129
|
+
raise 'reqs length == 0' unless reqs.length > 0
|
130
|
+
uri = reqs[0][0]
|
131
|
+
auth_header = reqs[0][1]
|
132
|
+
items = Array.new
|
133
|
+
callbacks = Array.new
|
134
|
+
reqs.each do |req|
|
135
|
+
items.push(req[2])
|
136
|
+
callbacks.push(req[3])
|
137
|
+
end
|
138
|
+
begin
|
139
|
+
PubControlClient.pubcall(uri, auth_header, items)
|
140
|
+
result = [true, '']
|
141
|
+
rescue => e
|
142
|
+
result = [false, e.message]
|
143
|
+
end
|
144
|
+
callbacks.each do |callback|
|
145
|
+
if !callback.nil?
|
146
|
+
callback.call(result[0], result[1])
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.timestamp_utcnow
|
152
|
+
return Time.now.utc.to_i
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def gen_auth_header
|
158
|
+
if !@auth_basic_user.nil?
|
159
|
+
return 'Basic ' + Base64.encode64(
|
160
|
+
'#{@auth_basic_user}:#{@auth_basic_pass}')
|
161
|
+
elsif !@auth_jwt_claim.nil?
|
162
|
+
if !@auth_jwt_claim.has_key?('exp')
|
163
|
+
claim = @auth_jwt_claim.clone
|
164
|
+
claim['exp'] = PubControlClient.timestamp_utcnow + 3600
|
165
|
+
else
|
166
|
+
claim = @auth_jwt_claim
|
167
|
+
end
|
168
|
+
return 'Bearer ' + JWT.encode(claim, @auth_jwt_key)
|
169
|
+
else
|
170
|
+
return nil
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def ensure_thread
|
175
|
+
if @thread.nil?
|
176
|
+
@thread_cond = ConditionVariable.new
|
177
|
+
@thread_mutex = Mutex.new
|
178
|
+
@thread = Thread.new { pubworker }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def queue_req(req)
|
183
|
+
@thread_mutex.lock
|
184
|
+
@req_queue.push_back(req)
|
185
|
+
@thread_cond.signal
|
186
|
+
@thread_mutex.unlock
|
187
|
+
end
|
188
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pubcontrol
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Bokarius
|
@@ -46,9 +46,9 @@ extra_rdoc_files: []
|
|
46
46
|
files:
|
47
47
|
- lib/format.rb
|
48
48
|
- lib/item.rb
|
49
|
-
- lib/
|
49
|
+
- lib/pcccbhandler.rb
|
50
50
|
- lib/pubcontrol.rb
|
51
|
-
- lib/
|
51
|
+
- lib/pubcontrolclient.rb
|
52
52
|
homepage: http://rubygems.org/gems/pubcontrol
|
53
53
|
licenses:
|
54
54
|
- MIT
|
data/lib/pubcontrolset.rb
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
# pubcontrolset.rb
|
2
|
-
# ~~~~~~~~~
|
3
|
-
# This module implements the PubControlSet class.
|
4
|
-
# :authors: Konstantin Bokarius.
|
5
|
-
# :copyright: (c) 2015 by Fanout, Inc.
|
6
|
-
# :license: MIT, see LICENSE for more details.
|
7
|
-
|
8
|
-
require_relative 'pubcontrol.rb'
|
9
|
-
require_relative 'pccbhandler.rb'
|
10
|
-
|
11
|
-
class PubControlSet
|
12
|
-
def initialize
|
13
|
-
@pubs = Array.new
|
14
|
-
at_exit { finish }
|
15
|
-
end
|
16
|
-
|
17
|
-
def clear
|
18
|
-
@pubs = Array.new
|
19
|
-
end
|
20
|
-
|
21
|
-
def add(pub)
|
22
|
-
@pubs.push(pub)
|
23
|
-
end
|
24
|
-
|
25
|
-
def apply_config(config)
|
26
|
-
config.each do |entry|
|
27
|
-
pub = PubControl.new(entry['uri'])
|
28
|
-
if entry.key?('iss')
|
29
|
-
pub.set_auth_jwt({'iss' => entry['iss']}, entry['key'])
|
30
|
-
end
|
31
|
-
@pubs.push(pub)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def apply_grip_config(config)
|
36
|
-
config.each do |entry|
|
37
|
-
if !entry.key?('control_uri')
|
38
|
-
next
|
39
|
-
end
|
40
|
-
pub = PubControl.new(entry['control_uri'])
|
41
|
-
if !entry.key?('control_iss')
|
42
|
-
pub.set_auth_jwt({'iss' => entry['control_iss']}, entry['key'])
|
43
|
-
end
|
44
|
-
@pubs.push(pub)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def publish(channel, item)
|
49
|
-
@pubs.each do |pub|
|
50
|
-
pub.publish(channel, item)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def publish_async(channel, item, callback=nil)
|
55
|
-
cb = nil
|
56
|
-
if !callback.nil?
|
57
|
-
cb = PubControlCallbackHandler.new(@pubs.length, callback).
|
58
|
-
handler_method_symbol
|
59
|
-
end
|
60
|
-
@pubs.each do |pub|
|
61
|
-
pub.publish_async(channel, item, cb)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def finish
|
68
|
-
@pubs.each do |pub|
|
69
|
-
pub.finish
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|