vinesmod 0.4.5
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.
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +43 -0
- data/Rakefile +57 -0
- data/bin/vines +93 -0
- data/conf/certs/README +39 -0
- data/conf/certs/ca-bundle.crt +3366 -0
- data/conf/config.rb +149 -0
- data/lib/vines.rb +197 -0
- data/lib/vines/cluster.rb +246 -0
- data/lib/vines/cluster/connection.rb +26 -0
- data/lib/vines/cluster/publisher.rb +55 -0
- data/lib/vines/cluster/pubsub.rb +92 -0
- data/lib/vines/cluster/sessions.rb +125 -0
- data/lib/vines/cluster/subscriber.rb +108 -0
- data/lib/vines/command/bcrypt.rb +12 -0
- data/lib/vines/command/cert.rb +50 -0
- data/lib/vines/command/init.rb +68 -0
- data/lib/vines/command/register.rb +27 -0
- data/lib/vines/command/restart.rb +12 -0
- data/lib/vines/command/schema.rb +24 -0
- data/lib/vines/command/start.rb +28 -0
- data/lib/vines/command/stop.rb +18 -0
- data/lib/vines/command/unregister.rb +27 -0
- data/lib/vines/config.rb +213 -0
- data/lib/vines/config/host.rb +119 -0
- data/lib/vines/config/port.rb +132 -0
- data/lib/vines/config/pubsub.rb +108 -0
- data/lib/vines/contact.rb +111 -0
- data/lib/vines/daemon.rb +78 -0
- data/lib/vines/error.rb +150 -0
- data/lib/vines/jid.rb +95 -0
- data/lib/vines/kit.rb +35 -0
- data/lib/vines/log.rb +24 -0
- data/lib/vines/router.rb +179 -0
- data/lib/vines/stanza.rb +175 -0
- data/lib/vines/stanza/iq.rb +48 -0
- data/lib/vines/stanza/iq/auth.rb +18 -0
- data/lib/vines/stanza/iq/disco_info.rb +45 -0
- data/lib/vines/stanza/iq/disco_items.rb +29 -0
- data/lib/vines/stanza/iq/error.rb +16 -0
- data/lib/vines/stanza/iq/ping.rb +16 -0
- data/lib/vines/stanza/iq/private_storage.rb +83 -0
- data/lib/vines/stanza/iq/query.rb +10 -0
- data/lib/vines/stanza/iq/register.rb +42 -0
- data/lib/vines/stanza/iq/result.rb +16 -0
- data/lib/vines/stanza/iq/roster.rb +140 -0
- data/lib/vines/stanza/iq/session.rb +17 -0
- data/lib/vines/stanza/iq/vcard.rb +56 -0
- data/lib/vines/stanza/iq/version.rb +25 -0
- data/lib/vines/stanza/message.rb +43 -0
- data/lib/vines/stanza/presence.rb +156 -0
- data/lib/vines/stanza/presence/error.rb +23 -0
- data/lib/vines/stanza/presence/probe.rb +37 -0
- data/lib/vines/stanza/presence/subscribe.rb +42 -0
- data/lib/vines/stanza/presence/subscribed.rb +51 -0
- data/lib/vines/stanza/presence/unavailable.rb +15 -0
- data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
- data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
- data/lib/vines/stanza/pubsub.rb +22 -0
- data/lib/vines/stanza/pubsub/create.rb +39 -0
- data/lib/vines/stanza/pubsub/delete.rb +41 -0
- data/lib/vines/stanza/pubsub/publish.rb +66 -0
- data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
- data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
- data/lib/vines/storage.rb +188 -0
- data/lib/vines/storage/local.rb +165 -0
- data/lib/vines/storage/null.rb +39 -0
- data/lib/vines/storage/sql.rb +260 -0
- data/lib/vines/store.rb +94 -0
- data/lib/vines/stream.rb +247 -0
- data/lib/vines/stream/client.rb +84 -0
- data/lib/vines/stream/client/auth.rb +74 -0
- data/lib/vines/stream/client/auth_restart.rb +29 -0
- data/lib/vines/stream/client/bind.rb +72 -0
- data/lib/vines/stream/client/bind_restart.rb +24 -0
- data/lib/vines/stream/client/closed.rb +13 -0
- data/lib/vines/stream/client/ready.rb +17 -0
- data/lib/vines/stream/client/session.rb +210 -0
- data/lib/vines/stream/client/start.rb +27 -0
- data/lib/vines/stream/client/tls.rb +38 -0
- data/lib/vines/stream/component.rb +58 -0
- data/lib/vines/stream/component/handshake.rb +26 -0
- data/lib/vines/stream/component/ready.rb +23 -0
- data/lib/vines/stream/component/start.rb +19 -0
- data/lib/vines/stream/http.rb +157 -0
- data/lib/vines/stream/http/auth.rb +22 -0
- data/lib/vines/stream/http/bind.rb +32 -0
- data/lib/vines/stream/http/bind_restart.rb +37 -0
- data/lib/vines/stream/http/ready.rb +29 -0
- data/lib/vines/stream/http/request.rb +172 -0
- data/lib/vines/stream/http/session.rb +120 -0
- data/lib/vines/stream/http/sessions.rb +65 -0
- data/lib/vines/stream/http/start.rb +23 -0
- data/lib/vines/stream/parser.rb +78 -0
- data/lib/vines/stream/sasl.rb +92 -0
- data/lib/vines/stream/server.rb +150 -0
- data/lib/vines/stream/server/auth.rb +13 -0
- data/lib/vines/stream/server/auth_restart.rb +13 -0
- data/lib/vines/stream/server/final_restart.rb +21 -0
- data/lib/vines/stream/server/outbound/auth.rb +31 -0
- data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth_result.rb +32 -0
- data/lib/vines/stream/server/outbound/final_features.rb +28 -0
- data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/start.rb +20 -0
- data/lib/vines/stream/server/outbound/tls.rb +30 -0
- data/lib/vines/stream/server/outbound/tls_result.rb +34 -0
- data/lib/vines/stream/server/ready.rb +24 -0
- data/lib/vines/stream/server/start.rb +13 -0
- data/lib/vines/stream/server/tls.rb +13 -0
- data/lib/vines/stream/state.rb +60 -0
- data/lib/vines/token_bucket.rb +55 -0
- data/lib/vines/user.rb +123 -0
- data/lib/vines/version.rb +5 -0
- data/lib/vines/xmpp_server.rb +43 -0
- data/vines.gemspec +36 -0
- data/web/404.html +51 -0
- data/web/apple-touch-icon.png +0 -0
- data/web/chat/coffeescripts/chat.coffee +362 -0
- data/web/chat/coffeescripts/init.coffee +15 -0
- data/web/chat/index.html +16 -0
- data/web/chat/javascripts/app.js +1 -0
- data/web/chat/stylesheets/chat.css +144 -0
- data/web/favicon.png +0 -0
- data/web/lib/coffeescripts/button.coffee +25 -0
- data/web/lib/coffeescripts/contact.coffee +32 -0
- data/web/lib/coffeescripts/filter.coffee +49 -0
- data/web/lib/coffeescripts/layout.coffee +30 -0
- data/web/lib/coffeescripts/login.coffee +68 -0
- data/web/lib/coffeescripts/logout.coffee +5 -0
- data/web/lib/coffeescripts/navbar.coffee +84 -0
- data/web/lib/coffeescripts/notification.coffee +14 -0
- data/web/lib/coffeescripts/router.coffee +40 -0
- data/web/lib/coffeescripts/session.coffee +229 -0
- data/web/lib/coffeescripts/transfer.coffee +106 -0
- data/web/lib/images/dark-gray.png +0 -0
- data/web/lib/images/default-user.png +0 -0
- data/web/lib/images/light-gray.png +0 -0
- data/web/lib/images/logo-large.png +0 -0
- data/web/lib/images/logo-small.png +0 -0
- data/web/lib/images/white.png +0 -0
- data/web/lib/javascripts/base.js +12 -0
- data/web/lib/javascripts/icons.js +110 -0
- data/web/lib/javascripts/jquery.cookie.js +91 -0
- data/web/lib/javascripts/jquery.js +4 -0
- data/web/lib/javascripts/raphael.js +6 -0
- data/web/lib/javascripts/strophe.js +1 -0
- data/web/lib/stylesheets/base.css +385 -0
- data/web/lib/stylesheets/login.css +68 -0
- metadata +423 -0
data/conf/config.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# This is the Vines XMPP server configuration file. Restart the server with
|
4
|
+
# 'vines restart' after updating this file.
|
5
|
+
|
6
|
+
Vines::Config.configure do
|
7
|
+
# Set the logging level to debug, info, warn, error, or fatal. The debug
|
8
|
+
# level logs all XML sent and received by the server.
|
9
|
+
log :info
|
10
|
+
|
11
|
+
# Set the directory in which to look for virtual hosts' TLS certificates.
|
12
|
+
# This is optional and defaults to the conf/certs directory created during
|
13
|
+
# `vines init`.
|
14
|
+
certs 'conf/certs'
|
15
|
+
|
16
|
+
# Each host element below is a virtual host domain name that this server will
|
17
|
+
# service. Hosts can share storage configurations or use separate databases.
|
18
|
+
# TLS encryption is mandatory so each host must have a <domain>.crt and
|
19
|
+
# <domain>.key file in the conf/certs directory. A self-signed certificate can
|
20
|
+
# be generated for a virtual host domain with the 'vines cert <domain.tld>'
|
21
|
+
# command. Change the example, 'wonderland.lit', domain name to your actual
|
22
|
+
# domain.
|
23
|
+
#
|
24
|
+
# The private_storage attribute allows clients to store XML fragments
|
25
|
+
# on the server, using the XEP-0049 Private XML Storage feature.
|
26
|
+
#
|
27
|
+
# The pubsub attribute defines the XEP-0060 Publish-Subscribe services hosted
|
28
|
+
# at these virtual host domains. In the example below, pubsub services are
|
29
|
+
# available at games.wonderland.lit and scores.wonderland.lit as well as
|
30
|
+
# games.verona.lit and scores.verona.lit.
|
31
|
+
#
|
32
|
+
# Shared storage example:
|
33
|
+
# host 'verona.lit', 'wonderland.lit' do
|
34
|
+
# private_storage false
|
35
|
+
# cross_domain_messages false
|
36
|
+
# storage 'fs' do
|
37
|
+
# dir 'data'
|
38
|
+
# end
|
39
|
+
# components 'tea' => 'secr3t',
|
40
|
+
# 'cake' => 'passw0rd'
|
41
|
+
# pubsub 'games', 'scores'
|
42
|
+
# end
|
43
|
+
|
44
|
+
host 'wonderland.lit' do
|
45
|
+
cross_domain_messages false
|
46
|
+
private_storage false
|
47
|
+
storage 'fs' do
|
48
|
+
dir 'data'
|
49
|
+
end
|
50
|
+
# components 'tea' => 'secr3t',
|
51
|
+
# 'cake' => 'passw0rd'
|
52
|
+
# pubsub 'games', 'scores'
|
53
|
+
end
|
54
|
+
|
55
|
+
# Configure the client-to-server port. The max_resources_per_account attribute
|
56
|
+
# limits how many concurrent connections one user can have to the server.
|
57
|
+
client '0.0.0.0', 5222 do
|
58
|
+
max_stanza_size 65536
|
59
|
+
max_resources_per_account 5
|
60
|
+
end
|
61
|
+
|
62
|
+
# Configure the server-to-server port. The max_stanza_size attribute should be
|
63
|
+
# much larger than the setting for client-to-server. Add domain names to the
|
64
|
+
# 'hosts' white-list attribute to allow those servers to connect. Any connection
|
65
|
+
# attempt from a host not in this list will be denied.
|
66
|
+
server '0.0.0.0', 5269 do
|
67
|
+
max_stanza_size 131072
|
68
|
+
hosts []
|
69
|
+
end
|
70
|
+
|
71
|
+
# Configure the built-in HTTP server that serves static files and responds to
|
72
|
+
# XEP-0124 BOSH requests. This allows HTTP clients to connect to
|
73
|
+
# the XMPP server.
|
74
|
+
#
|
75
|
+
# The root attribute defines the web server's document root (default 'web/').
|
76
|
+
# It will only serve files out of this directory.
|
77
|
+
#
|
78
|
+
# The bind attribute defines the URL to which BOSH clients must POST their
|
79
|
+
# XMPP stanza requests (default /xmpp).
|
80
|
+
#
|
81
|
+
# The vroute attribute defines the value of the vroute cookie sent in each
|
82
|
+
# response that uniquely identifies this HTTP server. Reverse proxy servers
|
83
|
+
# (nginx/apache) can use this cookie to implement sticky sessions. This is
|
84
|
+
# only needed if the server is clustered behind a reverse proxy.
|
85
|
+
http '0.0.0.0', 5280 do
|
86
|
+
bind '/xmpp'
|
87
|
+
max_stanza_size 65536
|
88
|
+
max_resources_per_account 5
|
89
|
+
root 'web'
|
90
|
+
vroute ''
|
91
|
+
end
|
92
|
+
|
93
|
+
# Configure the XEP-0114 external component port. Component sub-domains and
|
94
|
+
# their passwords are defined with their virtual host entries above.
|
95
|
+
component '0.0.0.0', 5347 do
|
96
|
+
max_stanza_size 131072
|
97
|
+
end
|
98
|
+
|
99
|
+
# Configure the redis connection used to form a cluster of server instances,
|
100
|
+
# serving the same chat domains across many different machines.
|
101
|
+
#cluster do
|
102
|
+
# host 'redis.wonderland.lit'
|
103
|
+
# port 6379
|
104
|
+
# database 0
|
105
|
+
# password ''
|
106
|
+
#end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Available storage implementations:
|
110
|
+
|
111
|
+
#storage 'fs' do
|
112
|
+
# dir 'data'
|
113
|
+
#end
|
114
|
+
|
115
|
+
#storage 'couchdb' do
|
116
|
+
# host 'localhost'
|
117
|
+
# port 6984
|
118
|
+
# database 'xmpp'
|
119
|
+
# tls true
|
120
|
+
# username ''
|
121
|
+
# password ''
|
122
|
+
#end
|
123
|
+
|
124
|
+
#storage 'mongodb' do
|
125
|
+
# host 'localhost', 27017
|
126
|
+
# host 'localhost', 27018 # optional, connects to replica set
|
127
|
+
# database 'xmpp'
|
128
|
+
# tls true
|
129
|
+
# username ''
|
130
|
+
# password ''
|
131
|
+
# pool 5
|
132
|
+
#end
|
133
|
+
|
134
|
+
#storage 'redis' do
|
135
|
+
# host 'localhost'
|
136
|
+
# port 6379
|
137
|
+
# database 0
|
138
|
+
# password ''
|
139
|
+
#end
|
140
|
+
|
141
|
+
#storage 'sql' do
|
142
|
+
# adapter 'postgresql'
|
143
|
+
# host 'localhost'
|
144
|
+
# port 5432
|
145
|
+
# database 'xmpp'
|
146
|
+
# username ''
|
147
|
+
# password ''
|
148
|
+
# pool 5
|
149
|
+
#end
|
data/lib/vines.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
NAMESPACES = {
|
5
|
+
:stream => 'http://etherx.jabber.org/streams'.freeze,
|
6
|
+
:client => 'jabber:client'.freeze,
|
7
|
+
:server => 'jabber:server'.freeze,
|
8
|
+
:component => 'jabber:component:accept'.freeze,
|
9
|
+
:roster => 'jabber:iq:roster'.freeze,
|
10
|
+
:register => 'jabber:iq:register'.freeze,
|
11
|
+
:non_sasl => 'jabber:iq:auth'.freeze,
|
12
|
+
:storage => 'jabber:iq:private'.freeze,
|
13
|
+
:version => 'jabber:iq:version'.freeze,
|
14
|
+
:sasl => 'urn:ietf:params:xml:ns:xmpp-sasl'.freeze,
|
15
|
+
:tls => 'urn:ietf:params:xml:ns:xmpp-tls'.freeze,
|
16
|
+
:bind => 'urn:ietf:params:xml:ns:xmpp-bind'.freeze,
|
17
|
+
:session => 'urn:ietf:params:xml:ns:xmpp-session'.freeze,
|
18
|
+
:ping => 'urn:xmpp:ping'.freeze,
|
19
|
+
:pubsub => 'http://jabber.org/protocol/pubsub'.freeze,
|
20
|
+
:pubsub_event => 'http://jabber.org/protocol/pubsub#event'.freeze,
|
21
|
+
:pubsub_create => 'http://jabber.org/protocol/pubsub#create-nodes'.freeze,
|
22
|
+
:pubsub_delete => 'http://jabber.org/protocol/pubsub#delete-nodes'.freeze,
|
23
|
+
:pubsub_instant => 'http://jabber.org/protocol/pubsub#instant-nodes'.freeze,
|
24
|
+
:pubsub_item_ids => 'http://jabber.org/protocol/pubsub#item-ids'.freeze,
|
25
|
+
:pubsub_publish => 'http://jabber.org/protocol/pubsub#publish'.freeze,
|
26
|
+
:pubsub_subscribe => 'http://jabber.org/protocol/pubsub#subscribe'.freeze,
|
27
|
+
:disco_items => 'http://jabber.org/protocol/disco#items'.freeze,
|
28
|
+
:disco_info => 'http://jabber.org/protocol/disco#info'.freeze,
|
29
|
+
:http_bind => 'http://jabber.org/protocol/httpbind'.freeze,
|
30
|
+
:bosh => 'urn:xmpp:xbosh'.freeze,
|
31
|
+
:vcard => 'vcard-temp'.freeze,
|
32
|
+
:si => 'http://jabber.org/protocol/si'.freeze,
|
33
|
+
:byte_streams => 'http://jabber.org/protocol/bytestreams'.freeze
|
34
|
+
}.freeze
|
35
|
+
|
36
|
+
module Log
|
37
|
+
@@logger = nil
|
38
|
+
def log
|
39
|
+
unless @@logger
|
40
|
+
@@logger = Logger.new(STDOUT)
|
41
|
+
@@logger.level = Logger::INFO
|
42
|
+
@@logger.progname = 'vines'
|
43
|
+
@@logger.formatter = Class.new(Logger::Formatter) do
|
44
|
+
def initialize
|
45
|
+
@time = "%Y-%m-%dT%H:%M:%SZ".freeze
|
46
|
+
@fmt = "[%s] %5s -- %s: %s\n".freeze
|
47
|
+
end
|
48
|
+
def call(severity, time, program, msg)
|
49
|
+
@fmt % [time.utc.strftime(@time), severity, program, msg2str(msg)]
|
50
|
+
end
|
51
|
+
end.new
|
52
|
+
end
|
53
|
+
@@logger
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
%w[
|
59
|
+
active_record
|
60
|
+
base64
|
61
|
+
bcrypt
|
62
|
+
digest/sha1
|
63
|
+
em-http
|
64
|
+
em-hiredis
|
65
|
+
eventmachine
|
66
|
+
fiber
|
67
|
+
fileutils
|
68
|
+
http/parser
|
69
|
+
logger
|
70
|
+
nokogiri
|
71
|
+
openssl
|
72
|
+
resolv
|
73
|
+
set
|
74
|
+
socket
|
75
|
+
uri
|
76
|
+
yaml
|
77
|
+
UPnP
|
78
|
+
|
79
|
+
vines/log
|
80
|
+
vines/jid
|
81
|
+
|
82
|
+
vines/stanza
|
83
|
+
vines/stanza/iq
|
84
|
+
vines/stanza/iq/query
|
85
|
+
vines/stanza/iq/auth
|
86
|
+
vines/stanza/iq/disco_info
|
87
|
+
vines/stanza/iq/disco_items
|
88
|
+
vines/stanza/iq/error
|
89
|
+
vines/stanza/iq/ping
|
90
|
+
vines/stanza/iq/private_storage
|
91
|
+
vines/stanza/iq/register
|
92
|
+
vines/stanza/iq/result
|
93
|
+
vines/stanza/iq/roster
|
94
|
+
vines/stanza/iq/session
|
95
|
+
vines/stanza/iq/vcard
|
96
|
+
vines/stanza/iq/version
|
97
|
+
vines/stanza/message
|
98
|
+
vines/stanza/presence
|
99
|
+
vines/stanza/presence/error
|
100
|
+
vines/stanza/presence/probe
|
101
|
+
vines/stanza/presence/subscribe
|
102
|
+
vines/stanza/presence/subscribed
|
103
|
+
vines/stanza/presence/unavailable
|
104
|
+
vines/stanza/presence/unsubscribe
|
105
|
+
vines/stanza/presence/unsubscribed
|
106
|
+
vines/stanza/pubsub
|
107
|
+
vines/stanza/pubsub/create
|
108
|
+
vines/stanza/pubsub/delete
|
109
|
+
vines/stanza/pubsub/publish
|
110
|
+
vines/stanza/pubsub/subscribe
|
111
|
+
vines/stanza/pubsub/unsubscribe
|
112
|
+
|
113
|
+
vines/storage
|
114
|
+
vines/storage/local
|
115
|
+
vines/storage/null
|
116
|
+
vines/storage/sql
|
117
|
+
|
118
|
+
vines/config
|
119
|
+
vines/config/host
|
120
|
+
vines/config/port
|
121
|
+
vines/config/pubsub
|
122
|
+
|
123
|
+
vines/store
|
124
|
+
vines/contact
|
125
|
+
vines/daemon
|
126
|
+
vines/error
|
127
|
+
vines/kit
|
128
|
+
vines/router
|
129
|
+
vines/token_bucket
|
130
|
+
vines/user
|
131
|
+
vines/version
|
132
|
+
vines/xmpp_server
|
133
|
+
|
134
|
+
vines/cluster
|
135
|
+
vines/cluster/connection
|
136
|
+
vines/cluster/publisher
|
137
|
+
vines/cluster/pubsub
|
138
|
+
vines/cluster/sessions
|
139
|
+
vines/cluster/subscriber
|
140
|
+
|
141
|
+
vines/stream
|
142
|
+
vines/stream/sasl
|
143
|
+
vines/stream/state
|
144
|
+
vines/stream/parser
|
145
|
+
|
146
|
+
vines/stream/client
|
147
|
+
vines/stream/client/session
|
148
|
+
vines/stream/client/start
|
149
|
+
vines/stream/client/tls
|
150
|
+
vines/stream/client/auth_restart
|
151
|
+
vines/stream/client/auth
|
152
|
+
vines/stream/client/bind_restart
|
153
|
+
vines/stream/client/bind
|
154
|
+
vines/stream/client/ready
|
155
|
+
vines/stream/client/closed
|
156
|
+
|
157
|
+
vines/stream/component
|
158
|
+
vines/stream/component/start
|
159
|
+
vines/stream/component/handshake
|
160
|
+
vines/stream/component/ready
|
161
|
+
|
162
|
+
vines/stream/http
|
163
|
+
vines/stream/http/session
|
164
|
+
vines/stream/http/sessions
|
165
|
+
vines/stream/http/request
|
166
|
+
vines/stream/http/start
|
167
|
+
vines/stream/http/auth
|
168
|
+
vines/stream/http/bind_restart
|
169
|
+
vines/stream/http/bind
|
170
|
+
vines/stream/http/ready
|
171
|
+
|
172
|
+
vines/stream/server
|
173
|
+
vines/stream/server/start
|
174
|
+
vines/stream/server/tls
|
175
|
+
vines/stream/server/auth_restart
|
176
|
+
vines/stream/server/auth
|
177
|
+
vines/stream/server/final_restart
|
178
|
+
vines/stream/server/ready
|
179
|
+
|
180
|
+
vines/stream/server/outbound/start
|
181
|
+
vines/stream/server/outbound/tls
|
182
|
+
vines/stream/server/outbound/tls_result
|
183
|
+
vines/stream/server/outbound/auth_restart
|
184
|
+
vines/stream/server/outbound/auth
|
185
|
+
vines/stream/server/outbound/auth_result
|
186
|
+
vines/stream/server/outbound/final_restart
|
187
|
+
vines/stream/server/outbound/final_features
|
188
|
+
|
189
|
+
vines/command/bcrypt
|
190
|
+
vines/command/cert
|
191
|
+
vines/command/init
|
192
|
+
vines/command/restart
|
193
|
+
vines/command/schema
|
194
|
+
vines/command/start
|
195
|
+
vines/command/stop
|
196
|
+
vines/command/register
|
197
|
+
].each {|f| require f }
|
@@ -0,0 +1,246 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
# Server instances may be connected to one another in a cluster so they
|
5
|
+
# can host a single chat domain, or set of domains, across many servers,
|
6
|
+
# transparently to users. A redis database is used for the session routing
|
7
|
+
# table, mapping JIDs to their node's location. Redis pubsub channels are
|
8
|
+
# used to communicate amongst nodes.
|
9
|
+
#
|
10
|
+
# Using a shared in-memory cache, like redis, rather than synchronizing the
|
11
|
+
# cache to each node, allows us to add cluster nodes dynamically, without
|
12
|
+
# updating all other nodes' config files. It also greatly reduces the amount
|
13
|
+
# of memory required by the chat server processes.
|
14
|
+
class Cluster
|
15
|
+
include Vines::Log
|
16
|
+
|
17
|
+
attr_reader :id
|
18
|
+
|
19
|
+
%w[host port database password].each do |name|
|
20
|
+
define_method(name) do |*args|
|
21
|
+
if args.first
|
22
|
+
@connection.send("#{name}=", args.first)
|
23
|
+
else
|
24
|
+
@connection.send(name)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(config, &block)
|
30
|
+
@config, @id = config, Kit.uuid
|
31
|
+
@connection = Connection.new
|
32
|
+
@sessions = Sessions.new(self)
|
33
|
+
@publisher = Publisher.new(self)
|
34
|
+
@subscriber = Subscriber.new(self)
|
35
|
+
@pubsub = PubSub.new(self)
|
36
|
+
instance_eval(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Join this node to the cluster by broadcasting its state to the
|
40
|
+
# other nodes, subscribing to redis channels, and scheduling periodic
|
41
|
+
# heartbeat broadcasts. This method must be called after initialize
|
42
|
+
# or this node will not be a cluster member.
|
43
|
+
def start
|
44
|
+
@connection.connect
|
45
|
+
@publisher.broadcast(:online)
|
46
|
+
@subscriber.subscribe
|
47
|
+
|
48
|
+
EM.add_periodic_timer(1) { heartbeat }
|
49
|
+
|
50
|
+
at_exit do
|
51
|
+
@publisher.broadcast(:offline)
|
52
|
+
@sessions.delete_all(@id)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns any streams hosted at remote nodes for these JIDs. The streams act
|
57
|
+
# like normal EM::Connections, but are actually proxies that route stanzas
|
58
|
+
# over redis pubsub channels to remote nodes.
|
59
|
+
def remote_sessions(*jids)
|
60
|
+
@sessions.find(*jids).map do |session|
|
61
|
+
StreamProxy.new(self, session)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Persist the user's session to the shared redis cache so that other cluster
|
66
|
+
# nodes can locate the node hosting this user's connection and route messages
|
67
|
+
# to them.
|
68
|
+
def save_session(jid, attrs)
|
69
|
+
@sessions.save(jid, attrs)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Remove this user from the cluster routing table so that no further stanzas
|
73
|
+
# may be routed to them. This must be called when the user's session is
|
74
|
+
# terminated, either by logout or stream disconnect.
|
75
|
+
def delete_session(jid)
|
76
|
+
@sessions.delete(jid)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Remove all user sessions from the routing table associated with the
|
80
|
+
# given node ID. Cluster nodes call this themselves during normal shutdown.
|
81
|
+
# However, if a node dies without being properly shutdown, the other nodes
|
82
|
+
# will cleanup its sessions when they detect the node is offline.
|
83
|
+
def delete_sessions(node)
|
84
|
+
@sessions.delete_all(node)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Notify the session store that this node is still alive. The node
|
88
|
+
# broadcasts its current time, so all cluster members' clocks don't
|
89
|
+
# necessarily need to be in sync.
|
90
|
+
def poke(node, time)
|
91
|
+
@sessions.poke(node, time)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Send the stanza to the node hosting the user's session. The stanza is
|
95
|
+
# published to the channel to which the remote node is listening for
|
96
|
+
# messages.
|
97
|
+
def route(stanza, node)
|
98
|
+
@publisher.route(stanza, node)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Notify the remote node that the user's roster has changed and it should
|
102
|
+
# reload the user from storage.
|
103
|
+
def update_user(jid, node)
|
104
|
+
@publisher.update_user(jid, node)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Return the shared redis connection for most queries to use.
|
108
|
+
def connection
|
109
|
+
@connection.connect
|
110
|
+
end
|
111
|
+
|
112
|
+
# Create a new redis connection.
|
113
|
+
def connect
|
114
|
+
@connection.create
|
115
|
+
end
|
116
|
+
|
117
|
+
# Turn an asynchronous redis query into a blocking call by pausing the
|
118
|
+
# fiber in which this code is running. Return the result of the query
|
119
|
+
# from this method, rather than passing it to a callback block.
|
120
|
+
def query(name, *args)
|
121
|
+
fiber, yielding = Fiber.current, true
|
122
|
+
req = connection.send(name, *args)
|
123
|
+
req.errback { fiber.resume rescue yielding = false }
|
124
|
+
req.callback {|response| fiber.resume(response) }
|
125
|
+
Fiber.yield if yielding
|
126
|
+
end
|
127
|
+
|
128
|
+
# Return the connected streams for this user, without any proxy streams
|
129
|
+
# to remote cluster nodes (locally connected streams only).
|
130
|
+
def connected_resources(jid)
|
131
|
+
@config.router.connected_resources(jid, jid, false)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Return the Storage implementation for this domain or nil if the
|
135
|
+
# domain is not hosted here.
|
136
|
+
def storage(domain)
|
137
|
+
@config.storage(domain)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Create a pubsub topic (a.k.a. node), in the given domain, to which
|
141
|
+
# messages may be published. The domain argument will be one of the
|
142
|
+
# configured pubsub subdomains in conf/config.rb (e.g. games.wonderland.lit,
|
143
|
+
# topics.wonderland.lit, etc).
|
144
|
+
def add_pubsub_node(domain, node)
|
145
|
+
@pubsub.add_node(domain, node)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Remove a pubsub topic so messages may no longer be broadcast to it.
|
149
|
+
def delete_pubsub_node(domain, node)
|
150
|
+
@pubsub.delete_node(domain, node)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Subscribe the JID to the pubsub topic so it will receive any messages
|
154
|
+
# published to it.
|
155
|
+
def subscribe_pubsub(domain, node, jid)
|
156
|
+
@pubsub.subscribe(domain, node, jid)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Unsubscribe the JID from the pubsub topic, deregistering its interest
|
160
|
+
# in receiving any messages published to it.
|
161
|
+
def unsubscribe_pubsub(domain, node, jid)
|
162
|
+
@pubsub.unsubscribe(domain, node, jid)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Unsubscribe the JID from all pubsub topics. This is useful when the
|
166
|
+
# JID's session ends by logout or disconnect.
|
167
|
+
def unsubscribe_all_pubsub(domain, jid)
|
168
|
+
@pubsub.unsubscribe_all(domain, jid)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Return true if the pubsub topic exists and messages may be published to it.
|
172
|
+
def pubsub_node?(domain, node)
|
173
|
+
@pubsub.node?(domain, node)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Return true if the JID is a registered subscriber to the pubsub topic and
|
177
|
+
# messages published to it should be routed to the JID.
|
178
|
+
def pubsub_subscribed?(domain, node, jid)
|
179
|
+
@pubsub.subscribed?(domain, node, jid)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Return a list of JIDs subscribed to the pubsub topic.
|
183
|
+
def pubsub_subscribers(domain, node)
|
184
|
+
@pubsub.subscribers(domain, node)
|
185
|
+
end
|
186
|
+
|
187
|
+
private
|
188
|
+
|
189
|
+
# Call this method once per second to broadcast this node's heartbeat and
|
190
|
+
# expire stale user sessions. This method must not raise exceptions or the
|
191
|
+
# timer will stop.
|
192
|
+
def heartbeat
|
193
|
+
@publisher.broadcast(:heartbeat)
|
194
|
+
@sessions.expire
|
195
|
+
rescue => e
|
196
|
+
log.error("Cluster session cleanup failed: #{e}")
|
197
|
+
end
|
198
|
+
|
199
|
+
# StreamProxy behaves like an EM::Connection so that stanzas may be sent to
|
200
|
+
# remote nodes just as they are to locally connected streams. The rest of the
|
201
|
+
# system doesn't know or care that these "streams" send their traffic over
|
202
|
+
# redis pubsub channels.
|
203
|
+
class StreamProxy
|
204
|
+
attr_reader :user
|
205
|
+
|
206
|
+
def initialize(cluster, session)
|
207
|
+
@cluster, @user = cluster, UserProxy.new(cluster, session)
|
208
|
+
@node, @available, @interested, @presence =
|
209
|
+
session.values_at('node', 'available', 'interested', 'presence')
|
210
|
+
|
211
|
+
unless @presence.nil? || @presence.empty?
|
212
|
+
@presence = Nokogiri::XML(@presence).root rescue nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def available?
|
217
|
+
@available
|
218
|
+
end
|
219
|
+
|
220
|
+
def interested?
|
221
|
+
@interested
|
222
|
+
end
|
223
|
+
|
224
|
+
def last_broadcast_presence
|
225
|
+
@presence
|
226
|
+
end
|
227
|
+
|
228
|
+
def write(stanza)
|
229
|
+
@cluster.route(stanza, @node)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Proxy User#update_from calls to remote cluster nodes over redis
|
234
|
+
# pubsub channels.
|
235
|
+
class UserProxy < User
|
236
|
+
def initialize(cluster, session)
|
237
|
+
super(jid: session['jid'])
|
238
|
+
@cluster, @node = cluster, session['node']
|
239
|
+
end
|
240
|
+
|
241
|
+
def update_from(user)
|
242
|
+
@cluster.update_user(@jid.bare, @node)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|