vines 0.1.0 → 0.1.1
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/README +1 -1
- data/Rakefile +12 -2
- data/conf/config.rb +1 -0
- data/lib/vines/config.rb +8 -0
- data/lib/vines/contact.rb +2 -4
- data/lib/vines/error.rb +1 -1
- data/lib/vines/router.rb +26 -18
- data/lib/vines/stanza/presence.rb +3 -1
- data/lib/vines/stanza.rb +8 -1
- data/lib/vines/stream/client/bind.rb +9 -1
- data/lib/vines/stream/client/session.rb +146 -0
- data/lib/vines/stream/client.rb +19 -78
- data/lib/vines/stream/component.rb +6 -2
- 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 +36 -0
- data/lib/vines/stream/http/ready.rb +25 -0
- data/lib/vines/stream/http/request.rb +33 -0
- data/lib/vines/stream/http/session.rb +116 -0
- data/lib/vines/stream/http/sessions.rb +65 -0
- data/lib/vines/stream/http/start.rb +23 -0
- data/lib/vines/stream/http.rb +119 -77
- data/lib/vines/stream/server.rb +8 -3
- data/lib/vines/stream/state.rb +6 -1
- data/lib/vines/stream.rb +31 -19
- data/lib/vines/user.rb +2 -4
- data/lib/vines/version.rb +1 -1
- data/lib/vines.rb +10 -4
- data/test/config_test.rb +34 -33
- data/test/contact_test.rb +42 -0
- data/test/error_test.rb +2 -2
- data/test/jid_test.rb +7 -7
- data/test/kit_test.rb +10 -10
- data/test/rake_test_loader.rb +9 -0
- data/test/router_test.rb +4 -3
- data/test/stanza/iq/roster_test.rb +8 -10
- data/test/stanza/iq/session_test.rb +2 -3
- data/test/stanza/iq/vcard_test.rb +4 -5
- data/test/stanza/message_test.rb +17 -11
- data/test/stanza/presence/subscribe_test.rb +3 -4
- data/test/storage/couchdb_test.rb +9 -10
- data/test/storage/ldap_test.rb +30 -37
- data/test/storage/local_test.rb +6 -6
- data/test/storage/redis_test.rb +6 -6
- data/test/storage/sql_test.rb +5 -5
- data/test/storage/storage_tests.rb +11 -11
- data/test/storage_test.rb +4 -5
- data/test/stream/client/auth_test.rb +15 -16
- data/test/stream/client/ready_test.rb +4 -5
- data/test/stream/client/session_test.rb +21 -0
- data/test/stream/component/handshake_test.rb +6 -7
- data/test/stream/component/ready_test.rb +9 -10
- data/test/stream/component/start_test.rb +6 -7
- data/test/stream/http/auth_test.rb +68 -0
- data/test/stream/http/ready_test.rb +56 -0
- data/test/stream/http/sessions_test.rb +50 -0
- data/test/stream/http/start_test.rb +51 -0
- data/test/stream/parser_test.rb +5 -5
- data/test/stream/server/outbound/auth_test.rb +12 -13
- data/test/stream/server/ready_test.rb +10 -11
- data/test/token_bucket_test.rb +7 -7
- data/test/user_test.rb +8 -6
- metadata +45 -14
- data/lib/vines/stream/http/http_request.rb +0 -22
- data/lib/vines/stream/http/http_state.rb +0 -139
- data/lib/vines/stream/http/http_states.rb +0 -53
data/README
CHANGED
@@ -19,7 +19,7 @@ database. SSL encryption is mandatory on all client and server connections.
|
|
19
19
|
* bcrypt-ruby >= 2.1.4
|
20
20
|
* eventmachine >= 1.0.0
|
21
21
|
* nokogiri >= 1.4.4
|
22
|
-
* ruby >= 1.9.
|
22
|
+
* ruby >= 1.9.2
|
23
23
|
|
24
24
|
== Ubuntu setup
|
25
25
|
$ sudo apt-get install build-essential ruby1.9.1 ruby1.9.1-dev libxml2-dev libxslt-dev
|
data/Rakefile
CHANGED
@@ -31,20 +31,30 @@ all client and server connections."
|
|
31
31
|
s.add_dependency 'em-http-request', '>= 1.0.0.beta.3'
|
32
32
|
s.add_dependency "em-redis", "~> 0.3"
|
33
33
|
s.add_dependency "eventmachine", ">= 1.0.0.beta.3"
|
34
|
+
s.add_dependency "http_parser.rb", "~> 0.5"
|
34
35
|
s.add_dependency "net-ldap", "~> 0.2"
|
35
36
|
s.add_dependency "nokogiri", "~> 1.4"
|
36
|
-
s.add_dependency "thin", "~> 1.2"
|
37
37
|
|
38
|
+
s.add_development_dependency "minitest"
|
38
39
|
s.add_development_dependency "rake"
|
39
40
|
s.add_development_dependency "sqlite3"
|
40
41
|
|
41
|
-
s.required_ruby_version = '>= 1.9.
|
42
|
+
s.required_ruby_version = '>= 1.9.2'
|
42
43
|
end
|
43
44
|
|
44
45
|
Rake::GemPackageTask.new(spec) do |pkg|
|
45
46
|
pkg.need_tar = true
|
46
47
|
end
|
47
48
|
|
49
|
+
module Rake
|
50
|
+
class TestTask
|
51
|
+
# use our custom test loader
|
52
|
+
def rake_loader
|
53
|
+
'test/rake_test_loader.rb'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
48
58
|
Rake::TestTask.new(:test) do |test|
|
49
59
|
test.libs << 'test'
|
50
60
|
test.libs << 'test/storage'
|
data/conf/config.rb
CHANGED
data/lib/vines/config.rb
CHANGED
@@ -167,6 +167,14 @@ module Vines
|
|
167
167
|
@stream = Vines::Stream::Http
|
168
168
|
super(config, host, port, &block)
|
169
169
|
end
|
170
|
+
|
171
|
+
def max_resources_per_account(max=nil)
|
172
|
+
if max
|
173
|
+
@settings[:max_resources_per_account] = max
|
174
|
+
else
|
175
|
+
@settings[:max_resources_per_account]
|
176
|
+
end
|
177
|
+
end
|
170
178
|
end
|
171
179
|
|
172
180
|
class ComponentPort < Port
|
data/lib/vines/contact.rb
CHANGED
@@ -17,12 +17,10 @@ module Vines
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def <=>(contact)
|
20
|
-
self.jid.to_s <=> contact.jid.to_s
|
20
|
+
contact.is_a?(Contact) ? self.jid.to_s <=> contact.jid.to_s : nil
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
contact.is_a?(Contact) && self == contact
|
25
|
-
end
|
23
|
+
alias :eql? :==
|
26
24
|
|
27
25
|
def hash
|
28
26
|
jid.to_s.hash
|
data/lib/vines/error.rb
CHANGED
@@ -98,7 +98,7 @@ module Vines
|
|
98
98
|
module StreamErrors
|
99
99
|
class BadFormat < StreamError; end
|
100
100
|
class BadNamespacePrefix < StreamError; end
|
101
|
-
class
|
101
|
+
class Conflict < StreamError; end
|
102
102
|
class ConnectionTimeout < StreamError; end
|
103
103
|
class HostGone < StreamError; end
|
104
104
|
class HostUnknown < StreamError; end
|
data/lib/vines/router.rb
CHANGED
@@ -8,9 +8,16 @@ module Vines
|
|
8
8
|
class Router
|
9
9
|
ROUTABLE_STANZAS = %w[message iq presence].freeze
|
10
10
|
|
11
|
+
STREAM_TYPES = [:client, :server, :component].freeze
|
12
|
+
STREAM_TYPES.each do |name|
|
13
|
+
define_method "#{name}s" do
|
14
|
+
@streams[name]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
11
18
|
@@instance = nil
|
12
19
|
def self.instance
|
13
|
-
@@instance ||=
|
20
|
+
@@instance ||= self.new
|
14
21
|
end
|
15
22
|
|
16
23
|
def initialize
|
@@ -19,23 +26,12 @@ module Vines
|
|
19
26
|
@pending = Hash.new {|h,k| h[k] = [] }
|
20
27
|
end
|
21
28
|
|
22
|
-
%w[Client Server Component].each do |klass|
|
23
|
-
name = klass.split(/(?=[A-Z])/).join('_').downcase
|
24
|
-
define_method(name + 's') do
|
25
|
-
@streams["Vines::Stream::#{klass}"]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def http_states
|
30
|
-
@streams["Vines::Stream::Http::HttpState"]
|
31
|
-
end
|
32
|
-
|
33
29
|
# Returns streams for all connected resources for this JID. A
|
34
30
|
# resource is considered connected after it has completed authentication
|
35
31
|
# and resource binding.
|
36
32
|
def connected_resources(jid)
|
37
33
|
jid = JID.new(jid)
|
38
|
-
|
34
|
+
clients.select do |stream|
|
39
35
|
stream.connected? && jid == (jid.bare? ? stream.user.jid.bare : stream.user.jid)
|
40
36
|
end
|
41
37
|
end
|
@@ -45,7 +41,7 @@ module Vines
|
|
45
41
|
# This method accepts a single JID or a list of JIDs.
|
46
42
|
def available_resources(*jid)
|
47
43
|
ids = jid.flatten.map {|jid| JID.new(jid).bare }
|
48
|
-
|
44
|
+
clients.select do |stream|
|
49
45
|
stream.available? && ids.include?(stream.user.jid.bare)
|
50
46
|
end
|
51
47
|
end
|
@@ -55,20 +51,24 @@ module Vines
|
|
55
51
|
# This method accepts a single JID or a list of JIDs.
|
56
52
|
def interested_resources(*jid)
|
57
53
|
ids = jid.flatten.map {|jid| JID.new(jid).bare }
|
58
|
-
|
54
|
+
clients.select do |stream|
|
59
55
|
stream.interested? && ids.include?(stream.user.jid.bare)
|
60
56
|
end
|
61
57
|
end
|
62
58
|
|
63
|
-
# Add the connection to the routing table.
|
59
|
+
# Add the connection to the routing table. The connection must return
|
60
|
+
# :client, :server, or :component from its +stream_type+ method so the
|
61
|
+
# router can properly route stanzas to the stream.
|
64
62
|
def <<(connection)
|
63
|
+
type = stream_type(connection)
|
65
64
|
@config ||= connection.config
|
66
|
-
@streams[
|
65
|
+
@streams[type] << connection
|
67
66
|
end
|
68
67
|
|
69
68
|
# Remove the connection from the routing table.
|
70
69
|
def delete(connection)
|
71
|
-
|
70
|
+
type = stream_type(connection)
|
71
|
+
@streams[type].delete(connection)
|
72
72
|
end
|
73
73
|
|
74
74
|
# Send the stanza to the appropriate remote server-to-server stream
|
@@ -121,5 +121,13 @@ module Vines
|
|
121
121
|
stream.ready? && stream.remote_domain == domain
|
122
122
|
end
|
123
123
|
end
|
124
|
+
|
125
|
+
def stream_type(connection)
|
126
|
+
connection.stream_type.tap do |type|
|
127
|
+
unless STREAM_TYPES.include?(type)
|
128
|
+
raise ArgumentError, "unexpected stream type: #{type}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
124
132
|
end
|
125
133
|
end
|
@@ -40,7 +40,9 @@ module Vines
|
|
40
40
|
else
|
41
41
|
stream.user.subscribed_from?(to) ? router.available_resources(to) : []
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
|
+
broadcast(recipients)
|
45
|
+
broadcast(router.available_resources(stream.user.jid))
|
44
46
|
|
45
47
|
if initial
|
46
48
|
stream.available_subscribed_to_resources.each do |recipient|
|
data/lib/vines/stanza.rb
CHANGED
@@ -5,6 +5,7 @@ module Vines
|
|
5
5
|
include Nokogiri::XML
|
6
6
|
|
7
7
|
attr_reader :stream
|
8
|
+
|
8
9
|
MESSAGE = 'message'.freeze
|
9
10
|
@@types = {}
|
10
11
|
|
@@ -24,8 +25,14 @@ module Vines
|
|
24
25
|
@node, @stream = node, stream
|
25
26
|
end
|
26
27
|
|
28
|
+
# Send the stanza to all recipients, stamping it with from and
|
29
|
+
# to addresses first.
|
27
30
|
def broadcast(recipients)
|
28
|
-
|
31
|
+
@node['from'] = stream.user.jid.to_s
|
32
|
+
recipients.each do |recipient|
|
33
|
+
@node['to'] = recipient.user.jid.to_s
|
34
|
+
recipient.write(@node)
|
35
|
+
end
|
29
36
|
end
|
30
37
|
|
31
38
|
def local?
|
@@ -25,13 +25,21 @@ module Vines
|
|
25
25
|
bind << doc.create_element('jid', stream.user.jid.to_s)
|
26
26
|
end
|
27
27
|
end
|
28
|
+
|
28
29
|
stream.write(result)
|
29
|
-
|
30
|
+
send_empty_features
|
30
31
|
advance
|
31
32
|
end
|
32
33
|
|
33
34
|
private
|
34
35
|
|
36
|
+
# Write the final <stream:features/> element to the stream, indicating
|
37
|
+
# stream negotiation is complete and the client is cleared to send
|
38
|
+
# stanzas.
|
39
|
+
def send_empty_features
|
40
|
+
stream.write('<stream:features/>')
|
41
|
+
end
|
42
|
+
|
35
43
|
def bind?(node)
|
36
44
|
node.name == 'iq' && node['type'] == 'set' && node.xpath('ns:bind', 'ns' => NS).any?
|
37
45
|
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Client
|
6
|
+
# A Session tracks the state of a client stream over its lifetime from
|
7
|
+
# negotiation to processing stanzas to shutdown. By disconnecting the
|
8
|
+
# stream's state from the stream, we can allow multiple TCP connections
|
9
|
+
# to access one logical session (e.g. HTTP streams).
|
10
|
+
class Session
|
11
|
+
include Comparable
|
12
|
+
|
13
|
+
attr_accessor :domain, :last_broadcast_presence, :user
|
14
|
+
attr_reader :id, :state
|
15
|
+
|
16
|
+
def initialize(stream)
|
17
|
+
@id = Kit.uuid
|
18
|
+
@state = Client::Start.new(stream)
|
19
|
+
@available = false
|
20
|
+
@domain = nil
|
21
|
+
@last_broadcast_presence = nil
|
22
|
+
@requested_roster = false
|
23
|
+
@unbound = false
|
24
|
+
@user = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def <=>(session)
|
28
|
+
session.is_a?(Session) ? self.id <=> session.id : nil
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :eql? :==
|
32
|
+
|
33
|
+
def hash
|
34
|
+
@id.hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def advance(state)
|
38
|
+
@state = state
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns true if this client has properly authenticated with
|
42
|
+
# the server.
|
43
|
+
def authenticated?
|
44
|
+
!@user.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
def available!
|
48
|
+
@available = true
|
49
|
+
end
|
50
|
+
|
51
|
+
# An available resource has sent initial presence and can
|
52
|
+
# receive presence subscription requests.
|
53
|
+
def available?
|
54
|
+
@available && connected?
|
55
|
+
end
|
56
|
+
|
57
|
+
# A connected resource has authenticated and bound a resource
|
58
|
+
# identifier.
|
59
|
+
def connected?
|
60
|
+
!@unbound && authenticated? && !@user.jid.bare?
|
61
|
+
end
|
62
|
+
|
63
|
+
# An interested resource has requested its roster and can
|
64
|
+
# receive roster pushes.
|
65
|
+
def interested?
|
66
|
+
@requested_roster && connected?
|
67
|
+
end
|
68
|
+
|
69
|
+
def ready?
|
70
|
+
@state.class == Client::Ready
|
71
|
+
end
|
72
|
+
|
73
|
+
def requested_roster!
|
74
|
+
@requested_roster = true
|
75
|
+
end
|
76
|
+
|
77
|
+
def stream_type
|
78
|
+
:client
|
79
|
+
end
|
80
|
+
|
81
|
+
# Called by the stream when its disconnected from the client. The stream
|
82
|
+
# passes itself to this method in case multiple streams are accessing this
|
83
|
+
# session.
|
84
|
+
def unbind!(stream)
|
85
|
+
@unbound = true
|
86
|
+
@available = false
|
87
|
+
broadcast_unavailable
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns streams for available resources to which this user
|
91
|
+
# has successfully subscribed.
|
92
|
+
def available_subscribed_to_resources
|
93
|
+
subscribed = @user.subscribed_to_contacts.map {|c| c.jid }
|
94
|
+
router.available_resources(subscribed)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns streams for available resources that are subscribed
|
98
|
+
# to this user's presence updates.
|
99
|
+
def available_subscribers
|
100
|
+
subscribed = @user.subscribed_from_contacts.map {|c| c.jid }
|
101
|
+
router.available_resources(subscribed)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns contacts hosted at remote servers that are subscribed
|
105
|
+
# to this user's presence updates.
|
106
|
+
def remote_subscribers(to=nil)
|
107
|
+
jid = (to.nil? || to.empty?) ? nil : JID.new(to).bare
|
108
|
+
@user.subscribed_from_contacts.reject do |c|
|
109
|
+
router.local_jid?(c.jid) || (jid && c.jid.bare != jid)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def broadcast_unavailable
|
116
|
+
return unless authenticated?
|
117
|
+
|
118
|
+
doc = Nokogiri::XML::Document.new
|
119
|
+
el = doc.create_element('presence',
|
120
|
+
'from' => @user.jid.to_s,
|
121
|
+
'type' => 'unavailable')
|
122
|
+
|
123
|
+
broadcast(el, available_subscribers)
|
124
|
+
broadcast(el, router.available_resources(@user.jid))
|
125
|
+
|
126
|
+
remote_subscribers.each do |contact|
|
127
|
+
node = el.clone
|
128
|
+
node['to'] = contact.jid.bare.to_s
|
129
|
+
router.route(node) rescue nil # ignore RemoteServerNotFound
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def broadcast(stanza, recipients)
|
134
|
+
recipients.each do |recipient|
|
135
|
+
stanza['to'] = recipient.user.jid.to_s
|
136
|
+
recipient.write(stanza)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def router
|
141
|
+
Router.instance
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/vines/stream/client.rb
CHANGED
@@ -6,22 +6,27 @@ module Vines
|
|
6
6
|
# Implements the XMPP protocol for client-to-server (c2s) streams. This
|
7
7
|
# serves connected streams using the jabber:client namespace.
|
8
8
|
class Client < Stream
|
9
|
-
attr_reader :config
|
10
|
-
attr_accessor :last_broadcast_presence
|
9
|
+
attr_reader :config
|
11
10
|
|
12
11
|
def initialize(config)
|
13
12
|
@config = config
|
14
|
-
@
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@
|
13
|
+
@session = Client::Session.new(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Delegate behavior to the session that's storing our stream state.
|
17
|
+
def method_missing(name, *args)
|
18
|
+
@session.send(name, *args)
|
19
|
+
end
|
20
|
+
|
21
|
+
%w[advance domain state user user=].each do |name|
|
22
|
+
define_method name do |*args|
|
23
|
+
@session.send(name, *args)
|
24
|
+
end
|
20
25
|
end
|
21
26
|
|
22
27
|
def ssl_handshake_completed
|
23
28
|
if get_peer_cert
|
24
|
-
close_connection unless cert_domain_matches?(@domain)
|
29
|
+
close_connection unless cert_domain_matches?(@session.domain)
|
25
30
|
end
|
26
31
|
end
|
27
32
|
|
@@ -34,80 +39,16 @@ module Vines
|
|
34
39
|
end
|
35
40
|
|
36
41
|
def unbind
|
37
|
-
@
|
38
|
-
@available = false
|
39
|
-
if authenticated?
|
40
|
-
doc = Nokogiri::XML::Document.new
|
41
|
-
el = doc.create_element('presence', 'type' => 'unavailable')
|
42
|
-
Stanza::Presence::Unavailable.new(el, self).outbound_broadcast_presence
|
43
|
-
end
|
42
|
+
@session.unbind!(self)
|
44
43
|
super
|
45
44
|
end
|
46
45
|
|
47
|
-
# Returns true if this client has properly authenticated with
|
48
|
-
# the server.
|
49
|
-
def authenticated?
|
50
|
-
!@user.nil?
|
51
|
-
end
|
52
|
-
|
53
|
-
# A connected resource has authenticated and bound a resource
|
54
|
-
# identifier.
|
55
|
-
def connected?
|
56
|
-
!@unbound && authenticated? && !@user.jid.bare?
|
57
|
-
end
|
58
|
-
|
59
|
-
# An available resource has sent initial presence and can
|
60
|
-
# receive presence subscription requests.
|
61
|
-
def available?
|
62
|
-
@available && connected?
|
63
|
-
end
|
64
|
-
|
65
|
-
# An interested resource has requested its roster and can
|
66
|
-
# receive roster pushes.
|
67
|
-
def interested?
|
68
|
-
@requested_roster && connected?
|
69
|
-
end
|
70
|
-
|
71
|
-
def available!
|
72
|
-
@available = true
|
73
|
-
end
|
74
|
-
|
75
|
-
def requested_roster!
|
76
|
-
@requested_roster = true
|
77
|
-
end
|
78
|
-
|
79
|
-
# Returns streams for available resources to which this user
|
80
|
-
# has successfully subscribed.
|
81
|
-
def available_subscribed_to_resources
|
82
|
-
subscribed = @user.subscribed_to_contacts.map {|c| c.jid }
|
83
|
-
router.available_resources(subscribed)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Returns streams for available resources that are subscribed
|
87
|
-
# to this user's presence updates.
|
88
|
-
def available_subscribers
|
89
|
-
subscribed = @user.subscribed_from_contacts.map {|c| c.jid }
|
90
|
-
router.available_resources(subscribed)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Returns contacts hosted at remote servers that are subscribed
|
94
|
-
# to this user's presence updates.
|
95
|
-
def remote_subscribers(to=nil)
|
96
|
-
jid = (to.nil? || to.empty?) ? nil : JID.new(to).bare
|
97
|
-
@user.subscribed_from_contacts.reject do |c|
|
98
|
-
router.local_jid?(c.jid) || (jid && c.jid.bare != jid)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def ready?
|
103
|
-
@state.class == Client::Ready
|
104
|
-
end
|
105
|
-
|
106
46
|
def start(node)
|
107
|
-
|
47
|
+
to, from = %w[to from].map {|a| node[a] }
|
48
|
+
@session.domain = to
|
108
49
|
send_stream_header(from)
|
109
50
|
raise StreamErrors::UnsupportedVersion unless node['version'] == '1.0'
|
110
|
-
raise StreamErrors::HostUnknown unless @config.vhost?(@domain)
|
51
|
+
raise StreamErrors::HostUnknown unless @config.vhost?(@session.domain)
|
111
52
|
raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:client]
|
112
53
|
raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns:stream'] == NAMESPACES[:stream]
|
113
54
|
end
|
@@ -120,7 +61,7 @@ module Vines
|
|
120
61
|
'xmlns:stream' => NAMESPACES[:stream],
|
121
62
|
'xml:lang' => 'en',
|
122
63
|
'id' => Kit.uuid,
|
123
|
-
'from' => @domain,
|
64
|
+
'from' => @session.domain,
|
124
65
|
'version' => '1.0'
|
125
66
|
}
|
126
67
|
attrs['to'] = to if to
|
@@ -13,7 +13,7 @@ module Vines
|
|
13
13
|
@config = config
|
14
14
|
@remote_domain = nil
|
15
15
|
@stream_id = Kit.uuid
|
16
|
-
|
16
|
+
advance(Start.new(self))
|
17
17
|
end
|
18
18
|
|
19
19
|
def max_stanza_size
|
@@ -21,7 +21,11 @@ module Vines
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def ready?
|
24
|
-
|
24
|
+
state.class == Component::Ready
|
25
|
+
end
|
26
|
+
|
27
|
+
def stream_type
|
28
|
+
:component
|
25
29
|
end
|
26
30
|
|
27
31
|
def start(node)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Http
|
6
|
+
class Auth < Client::Auth
|
7
|
+
def initialize(stream, success=BindRestart)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def node(node)
|
12
|
+
unless body?(node) && node['rid'] && stream.valid_session?(node['sid'])
|
13
|
+
raise StreamErrors::NotAuthorized
|
14
|
+
end
|
15
|
+
nodes = stream.parse_body(node)
|
16
|
+
raise StreamErrors::NotAuthorized unless nodes.size == 1
|
17
|
+
super(nodes.first)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Http
|
6
|
+
class Bind < Client::Bind
|
7
|
+
FEATURES = %Q{<stream:features xmlns:stream="#{NAMESPACES[:stream]}"/>}.freeze
|
8
|
+
|
9
|
+
def initialize(stream, success=Ready)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def node(node)
|
14
|
+
unless body?(node) && node['rid'] && stream.valid_session?(node['sid'])
|
15
|
+
raise StreamErrors::NotAuthorized
|
16
|
+
end
|
17
|
+
nodes = stream.parse_body(node)
|
18
|
+
raise StreamErrors::NotAuthorized unless nodes.size == 1
|
19
|
+
super(nodes.first)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Override Client::Bind#send_empty_features to properly namespace the
|
25
|
+
# empty features element.
|
26
|
+
def send_empty_features
|
27
|
+
stream.write(FEATURES)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Http
|
6
|
+
class BindRestart < State
|
7
|
+
def initialize(stream, success=Bind)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def node(node)
|
12
|
+
raise StreamErrors::NotAuthorized unless body?(node) && restart?(node)
|
13
|
+
|
14
|
+
doc = Document.new
|
15
|
+
body = doc.create_element('body') do |el|
|
16
|
+
el.add_namespace(nil, NAMESPACES[:http_bind])
|
17
|
+
el.add_namespace('stream', NAMESPACES[:stream])
|
18
|
+
el << doc.create_element('stream:features') do |features|
|
19
|
+
features << doc.create_element('bind', 'xmlns' => NAMESPACES[:bind])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
stream.reply(body)
|
23
|
+
advance
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def restart?(node)
|
29
|
+
restart = node.attribute_with_ns('restart', NAMESPACES[:bosh]).value rescue nil
|
30
|
+
domain = node['to'] == stream.domain
|
31
|
+
domain && restart == 'true' && node['rid'] && stream.valid_session?(node['sid'])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Http
|
6
|
+
class Ready < Client::Ready
|
7
|
+
RID, SID, TYPE, TERMINATE = %w[rid sid type terminate].map {|s| s.freeze }
|
8
|
+
|
9
|
+
def node(node)
|
10
|
+
unless body?(node) && node[RID] && stream.valid_session?(node[SID])
|
11
|
+
raise StreamErrors::NotAuthorized
|
12
|
+
end
|
13
|
+
stream.parse_body(node).each do |child|
|
14
|
+
super(child)
|
15
|
+
end
|
16
|
+
stream.terminate if terminate?(node)
|
17
|
+
end
|
18
|
+
|
19
|
+
def terminate?(node)
|
20
|
+
node[TYPE] == TERMINATE
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|