palava_machine 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.
@@ -0,0 +1,66 @@
1
+ require_relative 'version'
2
+ require_relative 'manager'
3
+ require_relative 'server'
4
+
5
+ require 'local_port'
6
+
7
+
8
+ module PalavaMachine::Runner
9
+
10
+ BANNER = <<BANNER
11
+
12
+ ### # # # # # #
13
+ # # # # # # # # # # #
14
+ ### # # # # # # # # #
15
+ # # # # # # # # # # # # #
16
+ # # # ### # # # # #
17
+
18
+ BANNER
19
+
20
+ CliOptions = Struct.new(
21
+ :port,
22
+ :db,
23
+ :address,
24
+ :shutdown_timeout,
25
+ :redis_address,
26
+ :mongo_address
27
+ ) do
28
+ def initialize(*argv)
29
+ self.port = (argv[0] || 4233).to_i
30
+ self.db = (argv[1] || 0).to_i
31
+ self.address = argv[2] || "0.0.0.0"
32
+ self.shutdown_timeout = (argv[3] || 3).to_i
33
+ self.redis_address = argv[4] || 'localhost:6379'
34
+ self.mongo_address = argv[5] || 'localhost:27017'
35
+ end
36
+ end
37
+
38
+ class << self
39
+ def run(cli_options = {})
40
+ puts BANNER
41
+
42
+ PalavaMachine::Server.new(
43
+ PalavaMachine::Manager.new(extract_manager_options(cli_options)),
44
+ extract_server_options(cli_options),
45
+ ).run
46
+ end
47
+
48
+ def parse_cli_options(argv = ARGV)
49
+ CliOptions.new(*argv)
50
+ end
51
+
52
+ def extract_server_options(cli_options)
53
+ {
54
+ host: cli_options.address,
55
+ port: LocalPort.next_free_one(cli_options.port),
56
+ shutdown_timeout: cli_options.shutdown_timeout,
57
+ }
58
+ end
59
+
60
+ def extract_manager_options(cli_options)
61
+ {
62
+ db: cli_options.db,
63
+ }
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,64 @@
1
+ require_relative 'version'
2
+ require_relative '../palava_machine'
3
+ require_relative 'client_message'
4
+
5
+ require_relative 'server/core_events'
6
+ require_relative 'server/core_support'
7
+ require_relative 'server/verify_handshake'
8
+ require_relative 'server/server_info'
9
+
10
+ require 'em-websocket'
11
+
12
+
13
+ module PalavaMachine
14
+ class Server
15
+ DEFAULT_FEATURES = [
16
+ CoreSupport,
17
+ CoreEvents,
18
+ # VerifyHandshake,
19
+ ServerInfo,
20
+ ]
21
+
22
+ attr_reader :manager, :features, :options
23
+
24
+ def ws_open(ws, handshake) end
25
+ def ws_message(ws, message) end
26
+ def ws_error(ws, error) end
27
+ def ws_close(ws, close) end
28
+ def em_init() end
29
+ def em_error(e) end
30
+ def em_sigterm() end
31
+ def em_sigint() end
32
+
33
+ def initialize(given_manager, given_options)
34
+ include_features(given_options.delete(:features))
35
+ @manager = given_manager
36
+ @options = given_options
37
+ end
38
+
39
+ def run
40
+ EM.run{
41
+ em_init
42
+ trap(:TERM){ em_sigterm }
43
+ trap(:INT){ em_sigint }
44
+
45
+ EM::WebSocket.run(options){ |ws|
46
+ ws.onopen{ |handshake| ws_open(ws, handshake) }
47
+ ws.onmessage{ |message| ws_message(ws, message) }
48
+ ws.onclose{ |why| ws_close(ws, why) }
49
+ ws.onerror{ |error| ws_error(ws, error) }
50
+ EM.error_handler{ |e| em_error(e) }
51
+ }
52
+ }
53
+ end
54
+
55
+
56
+ private
57
+
58
+
59
+ def include_features(given_features)
60
+ @features = given_features || DEFAULT_FEATURES
61
+ @features.each{ |f| self.singleton_class.send(:include, f) }
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,49 @@
1
+ module PalavaMachine
2
+ class Server
3
+ module CoreEvents
4
+ def ws_open(ws, _)
5
+ ws_open_announce(ws)
6
+ rescue MessageError => e
7
+ send_error(ws, e)
8
+ ws.close 4242
9
+ end
10
+
11
+ def ws_message(ws, message)
12
+ ws_message_action(ws, ws_message_parse(ws, message))
13
+ rescue MessageParsingError, MessageError => e
14
+ send_error(ws, e)
15
+ end
16
+
17
+ def ws_close(ws, why)
18
+ ws_close_unannounce(ws)
19
+ rescue MessageError => e
20
+ warn "*** Error while closing connection *** #{e.class} ***\n" + e.message + "\n " + e.backtrace*"\n "
21
+ end
22
+
23
+ def ws_error(ws, e)
24
+ warn "*** Socket Error *** #{e.class} ***\n" + (e.message || "") + "\n " + (e.backtrace && e.backtrace*"\n ")
25
+ ws.close 4242
26
+ end
27
+
28
+ def em_init
29
+ em_init_pm
30
+ end
31
+
32
+ def em_error(e)
33
+ if e.is_a? MessageError
34
+ send_error(e.ws, e)
35
+ else
36
+ raise e
37
+ end
38
+ end
39
+
40
+ def em_sigterm
41
+ stop!
42
+ end
43
+
44
+ def em_sigint
45
+ stop! 0
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,50 @@
1
+ module PalavaMachine
2
+ class Server
3
+ module CoreSupport
4
+ def ws_open_announce(ws)
5
+ manager.announce_connection(ws)
6
+ end
7
+
8
+ def ws_message_parse(ws, message)
9
+ connection_id = manager.connections.get_connection_id(ws) or raise MessageError.new(ws), 'unknown connection'
10
+ ClientMessage.new(message, connection_id)
11
+ end
12
+
13
+ def ws_message_action(ws, event)
14
+ manager.debug "#{event.connection_id} <#{event.name}>"
15
+ manager.public_send(event.name, event.connection_id, *event.arguments)
16
+ end
17
+
18
+ def ws_close_unannounce(ws)
19
+ manager.unannounce_connection(ws)
20
+ end
21
+
22
+ def em_init_pm
23
+ manager.info "Starting RTC Socket Server on port #{options[:port]}"
24
+ manager.initialize_in_em
25
+ end
26
+
27
+ def send_error(ws, e)
28
+ ws.send_text({
29
+ event: 'error',
30
+ message: e.message
31
+ }.to_json) # TODO deactivate message in production?
32
+ end
33
+
34
+ def stop!(timeout = options[:shutdown_timeout])
35
+ manager.warn "Stopping Machine"
36
+ if timeout.to_i == 0
37
+ manager.shutdown!
38
+ EM.stop
39
+ else
40
+ manager.announce_shutdown(timeout)
41
+ EM.next_tick do
42
+ manager.shutdown!(timeout)
43
+ EM.stop
44
+ end
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,20 @@
1
+ module PalavaMachine
2
+ class Server
3
+ module ServerInfo
4
+ def ws_message_action(ws, event)
5
+ if event.name == :info
6
+ send_server_info(ws)
7
+ else
8
+ super(ws, event)
9
+ end
10
+ end
11
+
12
+ def send_server_info(ws)
13
+ ws.send_text({
14
+ event: 'info',
15
+ protocol_version: PalavaMachine::PROTOCOL_VERSION,
16
+ }.to_json)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ module PalavaMachine
2
+ class Server
3
+ class HandshakeError < StandardError; end
4
+
5
+ module VerifyHandshake
6
+ def ws_open(ws, handshake)
7
+ verify_handshake(handshake)
8
+ super(ws, handshake)
9
+ rescue HandshakeError => e
10
+ manager.error "HandshakeError for #{ws.hash}\n#{e.inspect}"
11
+ send_error(ws, e)
12
+ ws.close 4242
13
+ end
14
+
15
+ def verify_handshake(handshake)
16
+ # Other: Access properties on the EM::WebSocket::Handshake object, e.g. path, query_string, origin, headers
17
+ if handshake.headers['Sec-WebSocket-Protocol'] != PalavaMachine.protocol_identifier
18
+ raise HandshakeError, "incompatible sub-protocol: #{ handshake.headers['Sec-WebSocket-Protocol'] }"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ require_relative 'version'
2
+
3
+ require 'securerandom'
4
+
5
+ module PalavaMachine
6
+ class SocketStore
7
+ include Enumerable
8
+
9
+ def initialize(connections = {})
10
+ @connections = connections.dup
11
+ end
12
+
13
+ def register_connection(ws)
14
+ @connections[ws] = SecureRandom.uuid
15
+ end
16
+
17
+ def unregister_connection(ws)
18
+ @connections.delete(ws)
19
+ end
20
+
21
+ def get_connection_id(ws)
22
+ @connections[ws]
23
+ end
24
+
25
+ def get_connection(id)
26
+ @connections.key(id)
27
+ end
28
+ alias [] get_connection
29
+
30
+ def each(&block)
31
+ @connections.each(&block)
32
+ end
33
+
34
+ def sockets
35
+ @connections.keys
36
+ end
37
+
38
+ def ids
39
+ @connections.values
40
+ end
41
+
42
+ def dup
43
+ SocketStore.new(@connections) # TODO verify (shallow)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,8 @@
1
+ module PalavaMachine
2
+ VERSION = "1.0.0"
3
+ PROTOCOL_VERSION = "1.0.0"
4
+
5
+ def self.protocol_identifier
6
+ 'palava.%s' % PROTOCOL_VERSION.to_f
7
+ end
8
+ end
@@ -0,0 +1,40 @@
1
+ # -*- encoding: utf-8 -*-
2
+ name = 'palava_machine'
3
+
4
+ require File.dirname(__FILE__) + "/lib/#{name}/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.required_ruby_version = '>= 1.9.2'
8
+ s.name = name
9
+ s.version = PalavaMachine::VERSION
10
+ s.authors = ["Jan Lelis", "Marius Melzer", "Stephan Thamm", "Kilian Ulbrich"]
11
+ s.email = "contact@palava.tv"
12
+ s.homepage = 'https://github.com/palavatv/palava-machine'
13
+ s.summary = "The machine behind palava."
14
+ s.description = "A WebRTC Signaling Server implemented with WebSockets, EventMachine and Redis Pub-Sub"
15
+ s.files = Dir.glob(%w[{lib,test}/**/*.rb bin/* [A-Z]*.{txt,rdoc} ext/**/*.{rb,c} features/**/*]) + %w{Rakefile palava_machine.gemspec Gemfile}
16
+ s.extra_rdoc_files = ["ReadMe.md", "ChangeLog.md", "ProtocolChangeLog.md"]
17
+ s.executables = ['palava-machine', 'palava-machine-daemon']
18
+
19
+ s.add_dependency 'em-websocket'
20
+
21
+ s.add_dependency 'hiredis', '~> 0.4.5'
22
+ s.add_dependency 'em-hiredis', '~> 0.2.1'
23
+
24
+ s.add_dependency 'redis', '>= 2.2.0'
25
+ s.add_dependency 'resque'
26
+ s.add_dependency 'resque-scheduler'
27
+ s.add_dependency 'mongo'
28
+ s.add_dependency 'bson_ext'
29
+
30
+ s.add_dependency 'bundler'
31
+ s.add_dependency 'daemons'
32
+ s.add_dependency 'logger-colors'
33
+ s.add_dependency 'local_port'
34
+ s.add_dependency 'rake'
35
+ s.add_dependency 'whiskey_disk'
36
+
37
+ s.add_development_dependency 'rspec'
38
+ # s.add_development_dependency 'debugger'
39
+ end
40
+
metadata ADDED
@@ -0,0 +1,282 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: palava_machine
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan Lelis
8
+ - Marius Melzer
9
+ - Stephan Thamm
10
+ - Kilian Ulbrich
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2013-10-10 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: em-websocket
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: hiredis
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ~>
35
+ - !ruby/object:Gem::Version
36
+ version: 0.4.5
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.4.5
44
+ - !ruby/object:Gem::Dependency
45
+ name: em-hiredis
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ~>
49
+ - !ruby/object:Gem::Version
50
+ version: 0.2.1
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: 0.2.1
58
+ - !ruby/object:Gem::Dependency
59
+ name: redis
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: 2.2.0
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: 2.2.0
72
+ - !ruby/object:Gem::Dependency
73
+ name: resque
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ - !ruby/object:Gem::Dependency
87
+ name: resque-scheduler
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ type: :runtime
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ - !ruby/object:Gem::Dependency
101
+ name: mongo
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ! '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ type: :runtime
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ - !ruby/object:Gem::Dependency
115
+ name: bson_ext
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ type: :runtime
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ! '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ - !ruby/object:Gem::Dependency
129
+ name: bundler
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ type: :runtime
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: daemons
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ! '>='
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ type: :runtime
150
+ prerelease: false
151
+ version_requirements: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ! '>='
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ - !ruby/object:Gem::Dependency
157
+ name: logger-colors
158
+ requirement: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ! '>='
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ type: :runtime
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ! '>='
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ - !ruby/object:Gem::Dependency
171
+ name: local_port
172
+ requirement: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ! '>='
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ type: :runtime
178
+ prerelease: false
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ! '>='
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ - !ruby/object:Gem::Dependency
185
+ name: rake
186
+ requirement: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - ! '>='
189
+ - !ruby/object:Gem::Version
190
+ version: '0'
191
+ type: :runtime
192
+ prerelease: false
193
+ version_requirements: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ - !ruby/object:Gem::Dependency
199
+ name: whiskey_disk
200
+ requirement: !ruby/object:Gem::Requirement
201
+ requirements:
202
+ - - ! '>='
203
+ - !ruby/object:Gem::Version
204
+ version: '0'
205
+ type: :runtime
206
+ prerelease: false
207
+ version_requirements: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ! '>='
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ - !ruby/object:Gem::Dependency
213
+ name: rspec
214
+ requirement: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - ! '>='
217
+ - !ruby/object:Gem::Version
218
+ version: '0'
219
+ type: :development
220
+ prerelease: false
221
+ version_requirements: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - ! '>='
224
+ - !ruby/object:Gem::Version
225
+ version: '0'
226
+ description: A WebRTC Signaling Server implemented with WebSockets, EventMachine and
227
+ Redis Pub-Sub
228
+ email: contact@palava.tv
229
+ executables:
230
+ - palava-machine
231
+ - palava-machine-daemon
232
+ extensions: []
233
+ extra_rdoc_files:
234
+ - ReadMe.md
235
+ - ChangeLog.md
236
+ - ProtocolChangeLog.md
237
+ files:
238
+ - lib/palava_machine.rb
239
+ - lib/palava_machine/manager.rb
240
+ - lib/palava_machine/version.rb
241
+ - lib/palava_machine/server/verify_handshake.rb
242
+ - lib/palava_machine/server/server_info.rb
243
+ - lib/palava_machine/server/core_events.rb
244
+ - lib/palava_machine/server/core_support.rb
245
+ - lib/palava_machine/runner.rb
246
+ - lib/palava_machine/jobs.rb
247
+ - lib/palava_machine/client_message.rb
248
+ - lib/palava_machine/server.rb
249
+ - lib/palava_machine/jobs/export_stats_job.rb
250
+ - lib/palava_machine/socket_store.rb
251
+ - bin/palava-machine-daemon
252
+ - bin/palava-machine
253
+ - Rakefile
254
+ - palava_machine.gemspec
255
+ - Gemfile
256
+ - ReadMe.md
257
+ - ChangeLog.md
258
+ - ProtocolChangeLog.md
259
+ homepage: https://github.com/palavatv/palava-machine
260
+ licenses: []
261
+ metadata: {}
262
+ post_install_message:
263
+ rdoc_options: []
264
+ require_paths:
265
+ - lib
266
+ required_ruby_version: !ruby/object:Gem::Requirement
267
+ requirements:
268
+ - - ! '>='
269
+ - !ruby/object:Gem::Version
270
+ version: 1.9.2
271
+ required_rubygems_version: !ruby/object:Gem::Requirement
272
+ requirements:
273
+ - - ! '>='
274
+ - !ruby/object:Gem::Version
275
+ version: '0'
276
+ requirements: []
277
+ rubyforge_project:
278
+ rubygems_version: 2.0.8
279
+ signing_key:
280
+ specification_version: 4
281
+ summary: The machine behind palava.
282
+ test_files: []