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
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Http
|
6
|
+
class Request
|
7
|
+
attr_reader :rid, :stream
|
8
|
+
|
9
|
+
def initialize(stream, rid, content_type)
|
10
|
+
@stream, @rid, @content = stream, rid, content_type
|
11
|
+
@received = Time.now
|
12
|
+
end
|
13
|
+
|
14
|
+
# Return the number of seconds since this request was received.
|
15
|
+
def age
|
16
|
+
Time.now - @received
|
17
|
+
end
|
18
|
+
|
19
|
+
# Send an HTTP 200 OK response wrapping the XMPP node content back
|
20
|
+
# to the client.
|
21
|
+
def reply(node)
|
22
|
+
body = node.to_s
|
23
|
+
header = [
|
24
|
+
"HTTP/1.1 200 OK",
|
25
|
+
"Content-Type: #{@content}",
|
26
|
+
"Content-Length: #{body.bytesize}"
|
27
|
+
].join("\r\n")
|
28
|
+
@stream.stream_write([header, body].join("\r\n\r\n"))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Http
|
6
|
+
class Session < Client::Session
|
7
|
+
include Nokogiri::XML
|
8
|
+
|
9
|
+
attr_accessor :content_type, :hold, :inactivity, :wait
|
10
|
+
|
11
|
+
CONTENT_TYPE = 'text/xml; charset=utf-8'.freeze
|
12
|
+
|
13
|
+
def initialize(stream)
|
14
|
+
super
|
15
|
+
@state = Http::Start.new(stream)
|
16
|
+
@inactivity, @wait, @hold = 20, 60, 1
|
17
|
+
@replied = Time.now
|
18
|
+
@requests, @responses = [], []
|
19
|
+
@content_type = CONTENT_TYPE
|
20
|
+
end
|
21
|
+
|
22
|
+
def close
|
23
|
+
Sessions.delete(@id)
|
24
|
+
router.delete(self)
|
25
|
+
@requests.each {|req| req.stream.close_connection }
|
26
|
+
@requests.clear
|
27
|
+
@responses.clear
|
28
|
+
@state = Client::Closed.new(nil)
|
29
|
+
@unbound = true
|
30
|
+
@available = false
|
31
|
+
broadcast_unavailable
|
32
|
+
end
|
33
|
+
|
34
|
+
def ready?
|
35
|
+
@state.class == Http::Ready
|
36
|
+
end
|
37
|
+
|
38
|
+
def requests
|
39
|
+
@requests.clone
|
40
|
+
end
|
41
|
+
|
42
|
+
def expired?
|
43
|
+
respond_to_expired_requests
|
44
|
+
@requests.empty? && (Time.now - @replied > @inactivity)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Resume this session from its most recent state with a new client
|
48
|
+
# stream and incoming node.
|
49
|
+
def resume(stream, node)
|
50
|
+
stream.session.requests.each do |req|
|
51
|
+
request(req)
|
52
|
+
end
|
53
|
+
stream.session = self
|
54
|
+
@state.stream = stream
|
55
|
+
@state.node(node)
|
56
|
+
end
|
57
|
+
|
58
|
+
def request(request)
|
59
|
+
if @responses.any?
|
60
|
+
request.reply(wrap_body(@responses.join))
|
61
|
+
@replied = Time.now
|
62
|
+
@responses.clear
|
63
|
+
else
|
64
|
+
while @requests.size >= @hold
|
65
|
+
@requests.shift.reply(wrap_body(''))
|
66
|
+
@replied = Time.now
|
67
|
+
end
|
68
|
+
@requests << request
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Send an HTTP 200 OK response wrapping the XMPP node content back
|
73
|
+
# to the client.
|
74
|
+
def reply(node)
|
75
|
+
@requests.shift.reply(node)
|
76
|
+
@replied = Time.now
|
77
|
+
end
|
78
|
+
|
79
|
+
# Write the XMPP node to the client stream after wrapping it in a BOSH
|
80
|
+
# body tag. If there's a waiting request, the node is written
|
81
|
+
# immediately. If not, it's queued until the next request arrives.
|
82
|
+
def write(node)
|
83
|
+
if request = @requests.shift
|
84
|
+
request.reply(wrap_body(node))
|
85
|
+
@replied = Time.now
|
86
|
+
else
|
87
|
+
@responses << node.to_s
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def unbind!(stream)
|
92
|
+
@requests.reject! {|req| req.stream == stream }
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def respond_to_expired_requests
|
98
|
+
expired = @requests.select {|req| req.age > @wait }
|
99
|
+
expired.each do |request|
|
100
|
+
request.reply(wrap_body(''))
|
101
|
+
@requests.delete(request)
|
102
|
+
@replied = Time.now
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def wrap_body(data)
|
107
|
+
doc = Document.new
|
108
|
+
doc.create_element('body') do |node|
|
109
|
+
node.add_namespace(nil, NAMESPACES[:http_bind])
|
110
|
+
node.inner_html = data.to_s
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Http
|
6
|
+
# Sessions is a cache of Http::Session objects for transient HTTP
|
7
|
+
# connections. The cache is monitored for expired client connections.
|
8
|
+
class Sessions
|
9
|
+
include Vines::Log
|
10
|
+
|
11
|
+
@@instance = nil
|
12
|
+
def self.instance
|
13
|
+
@@instance ||= self.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.[](sid)
|
17
|
+
instance[sid]
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.[]=(sid, session)
|
21
|
+
instance[sid] = session
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.delete(sid)
|
25
|
+
instance.delete(sid)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@sessions = {}
|
30
|
+
start_timer
|
31
|
+
end
|
32
|
+
|
33
|
+
def []=(sid, session)
|
34
|
+
@sessions[sid] = session
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](sid)
|
38
|
+
@sessions[sid]
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(sid)
|
42
|
+
@sessions.delete(sid)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Check for expired clients to cleanup every second.
|
48
|
+
def start_timer
|
49
|
+
@timer ||= EventMachine::PeriodicTimer.new(1) { cleanup }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Remove cached information for all expired connections. An expired
|
53
|
+
# HTTP client is one that has no queued requests and has had no activity
|
54
|
+
# for over 20 seconds.
|
55
|
+
def cleanup
|
56
|
+
@sessions.each_value do |session|
|
57
|
+
session.close if session.expired?
|
58
|
+
end
|
59
|
+
rescue Exception => e
|
60
|
+
log.error("Expired session cleanup failed: #{e}")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stream
|
5
|
+
class Http
|
6
|
+
class Start < State
|
7
|
+
def initialize(stream, success=Auth)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def node(node)
|
12
|
+
raise StreamErrors::NotAuthorized unless body?(node)
|
13
|
+
if session = Sessions[node['sid']]
|
14
|
+
session.resume(stream, node)
|
15
|
+
else
|
16
|
+
stream.start(node)
|
17
|
+
advance
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/vines/stream/http.rb
CHANGED
@@ -3,108 +3,150 @@
|
|
3
3
|
module Vines
|
4
4
|
class Stream
|
5
5
|
class Http < Client
|
6
|
-
|
7
|
-
include Vines::Log
|
8
|
-
|
9
|
-
attr_accessor :last_broadcast_presence, :last_activity
|
6
|
+
attr_accessor :session
|
10
7
|
|
11
8
|
def initialize(config)
|
12
|
-
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
super
|
10
|
+
@session = Http::Session.new(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def post_init
|
14
|
+
super
|
15
|
+
router.delete(self)
|
16
|
+
@parser = ::Http::Parser.new.tap do |p|
|
17
|
+
body = ''
|
18
|
+
p.on_body = proc {|data| body << data }
|
19
|
+
p.on_message_complete = proc {
|
20
|
+
process_request(body)
|
21
|
+
body = ''
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return true if this session ID matches the stream's session ID. Clients
|
27
|
+
# are only allowed one session per stream so they must send the same
|
28
|
+
# session ID on each request.
|
29
|
+
def valid_session?(sid)
|
30
|
+
@session.id == sid
|
21
31
|
end
|
22
32
|
|
23
|
-
def
|
24
|
-
@
|
33
|
+
def max_stanza_size
|
34
|
+
@config[:http].max_stanza_size
|
25
35
|
end
|
26
36
|
|
27
|
-
def
|
28
|
-
@
|
37
|
+
def max_resources_per_account
|
38
|
+
@config[:http].max_resources_per_account
|
29
39
|
end
|
30
40
|
|
31
|
-
def
|
32
|
-
#
|
33
|
-
if
|
34
|
-
|
35
|
-
|
41
|
+
def process_request(body)
|
42
|
+
# proxy server ping
|
43
|
+
if body.empty?
|
44
|
+
req = Request.new(self, nil, 'text/plain')
|
45
|
+
req.reply('online')
|
46
|
+
close_connection_after_writing
|
47
|
+
else
|
48
|
+
body = Nokogiri::XML(body).root
|
49
|
+
req = Request.new(self, body['rid'], @session.content_type)
|
50
|
+
@session.request(req)
|
51
|
+
@nodes.push(body)
|
36
52
|
end
|
37
|
-
rescue InvalidRequest => e
|
38
|
-
error(StreamErrors::NotWellFormed.new)
|
39
53
|
end
|
40
54
|
|
55
|
+
# Alias the Stream#write method before overriding it so we can call
|
56
|
+
# it later from a Session instance.
|
57
|
+
alias :stream_write :write
|
58
|
+
|
59
|
+
# Override Stream#write to queue stanzas rather than immediately writing
|
60
|
+
# to the stream. Stanza responses must be paired with a queued request.
|
41
61
|
def write(data)
|
42
|
-
@
|
62
|
+
@session.write(data)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return an array of Node objects inside the body element.
|
66
|
+
# TODO This parses the XML again just to strip namespaces. Figure out
|
67
|
+
# Nokogiri namespace handling instead.
|
68
|
+
def parse_body(body)
|
69
|
+
body.namespace = nil
|
70
|
+
body.elements.map do |node|
|
71
|
+
Nokogiri::XML(node.to_s.sub(' xmlns="jabber:client"', '')).root
|
72
|
+
end
|
43
73
|
end
|
44
74
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
75
|
+
def start(node)
|
76
|
+
domain, type, hold, wait, rid = %w[to content hold wait rid].map {|a| (node[a] || '').strip }
|
77
|
+
version = node.attribute_with_ns('version', NAMESPACES[:bosh]).value rescue nil
|
78
|
+
|
79
|
+
@session.inactivity = 20
|
80
|
+
@session.domain = domain
|
81
|
+
@session.content_type = type unless type.empty?
|
82
|
+
@session.hold = hold.to_i unless hold.empty?
|
83
|
+
@session.wait = wait.to_i unless wait.empty?
|
84
|
+
|
85
|
+
raise StreamErrors::UndefinedCondition.new('rid required') if rid.empty?
|
86
|
+
raise StreamErrors::UnsupportedVersion unless version == '1.0'
|
87
|
+
raise StreamErrors::HostUnknown unless @config.vhost?(domain)
|
88
|
+
raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:http_bind]
|
89
|
+
|
90
|
+
Sessions[@session.id] = @session
|
91
|
+
router << @session
|
92
|
+
send_stream_header
|
50
93
|
end
|
51
94
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
95
|
+
def terminate
|
96
|
+
doc = Nokogiri::XML::Document.new
|
97
|
+
node = doc.create_element('body',
|
98
|
+
'type' => 'terminate',
|
99
|
+
'xmlns' => NAMESPACES[:http_bind])
|
100
|
+
@session.reply(node)
|
101
|
+
close_stream
|
56
102
|
end
|
57
103
|
|
58
|
-
|
59
|
-
if request.body.string.empty?
|
60
|
-
#Respond to proxy servers' status pings
|
61
|
-
log.info("A status request has been received.")
|
62
|
-
send_data("Online")
|
63
|
-
close_connection_after_writing
|
64
|
-
return
|
65
|
-
end
|
66
|
-
body = Nokogiri::XML(request.body.string).root
|
67
|
-
body.namespace = nil
|
68
|
-
#TODO: Confirm this is a valid body stanza.
|
69
|
-
# If it isn't a body, return proxy ping result
|
70
|
-
|
71
|
-
if body['sid']
|
72
|
-
@http_state = @@http_states[body['sid']]
|
73
|
-
unless @http_state
|
74
|
-
log.info("Client was not found #{body['sid']}")
|
75
|
-
send_bosh_error
|
76
|
-
return
|
77
|
-
end
|
78
|
-
@domain = @http_state.domain
|
79
|
-
@user = @http_state.user
|
80
|
-
@http_state.request(body['rid'])
|
81
|
-
if body['restart']
|
82
|
-
@http_state.handle_restart
|
83
|
-
router << @http_state
|
84
|
-
@state = Bind.new(self)
|
85
|
-
end
|
104
|
+
private
|
86
105
|
|
87
|
-
|
88
|
-
|
106
|
+
def send_stream_header
|
107
|
+
doc = Nokogiri::XML::Document.new
|
108
|
+
node = doc.create_element('body',
|
109
|
+
'charsets' => 'UTF-8',
|
110
|
+
'from' => @session.domain,
|
111
|
+
'hold' => @session.hold,
|
112
|
+
'inactivity' => @session.inactivity,
|
113
|
+
'polling' => '5',
|
114
|
+
'requests' => '2',
|
115
|
+
'sid' => @session.id,
|
116
|
+
'ver' => '1.6',
|
117
|
+
'wait' => @session.wait,
|
118
|
+
'xmpp:version' => '1.0',
|
119
|
+
'xmlns' => NAMESPACES[:http_bind],
|
120
|
+
'xmlns:xmpp' => NAMESPACES[:bosh],
|
121
|
+
'xmlns:stream' => NAMESPACES[:stream])
|
122
|
+
|
123
|
+
node << doc.create_element('stream:features') do |el|
|
124
|
+
el << doc.create_element('mechanisms') do |mechanisms|
|
125
|
+
mechanisms.default_namespace = NAMESPACES[:sasl]
|
126
|
+
mechanisms << doc.create_element('mechanism', 'PLAIN')
|
89
127
|
end
|
90
|
-
else
|
91
|
-
self.setup_new_client(body['rid'], body['to'])
|
92
128
|
end
|
129
|
+
@session.reply(node)
|
93
130
|
end
|
94
131
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
132
|
+
# Override +Stream#send_stream_error+ to wrap the error XML in a BOSH
|
133
|
+
# terminate body tag.
|
134
|
+
def send_stream_error(e)
|
135
|
+
doc = Nokogiri::XML::Document.new
|
136
|
+
node = doc.create_element('body',
|
137
|
+
'condition' => 'remote-stream-error',
|
138
|
+
'type' => 'terminate',
|
139
|
+
'xmlns' => NAMESPACES[:http_bind],
|
140
|
+
'xmlns:stream' => NAMESPACES[:stream])
|
141
|
+
node.inner_html = e.to_xml
|
142
|
+
@session.reply(node)
|
104
143
|
end
|
105
144
|
|
106
|
-
|
107
|
-
|
145
|
+
# Override +Stream#close_stream+ to simply close the connection without
|
146
|
+
# writing a closing stream tag.
|
147
|
+
def close_stream
|
148
|
+
close_connection_after_writing
|
149
|
+
@session.close
|
108
150
|
end
|
109
151
|
end
|
110
152
|
end
|
data/lib/vines/stream/server.rb
CHANGED
@@ -57,7 +57,8 @@ module Vines
|
|
57
57
|
@srv = options[:srv]
|
58
58
|
@callback = options[:callback]
|
59
59
|
@outbound = @remote_domain && @domain
|
60
|
-
|
60
|
+
start = @outbound ? Outbound::Start.new(self) : Start.new(self)
|
61
|
+
advance(start)
|
61
62
|
end
|
62
63
|
|
63
64
|
def post_init
|
@@ -73,9 +74,13 @@ module Vines
|
|
73
74
|
close_connection unless cert_domain_matches?(@remote_domain)
|
74
75
|
end
|
75
76
|
|
77
|
+
def stream_type
|
78
|
+
:server
|
79
|
+
end
|
80
|
+
|
76
81
|
def unbind
|
77
82
|
super
|
78
|
-
if @outbound &&
|
83
|
+
if @outbound && !ready?
|
79
84
|
Server.connect(@config, @remote_domain, @domain, @srv, @callback)
|
80
85
|
end
|
81
86
|
end
|
@@ -92,7 +97,7 @@ module Vines
|
|
92
97
|
end
|
93
98
|
|
94
99
|
def ready?
|
95
|
-
|
100
|
+
state.class == Server::Ready
|
96
101
|
end
|
97
102
|
|
98
103
|
def start(node)
|
data/lib/vines/stream/state.rb
CHANGED
@@ -9,8 +9,9 @@ module Vines
|
|
9
9
|
include Nokogiri::XML
|
10
10
|
include Vines::Log
|
11
11
|
|
12
|
-
|
12
|
+
attr_accessor :stream
|
13
13
|
|
14
|
+
BODY = 'body'.freeze
|
14
15
|
STREAM = 'stream'.freeze
|
15
16
|
|
16
17
|
def initialize(stream, success=nil)
|
@@ -43,6 +44,10 @@ module Vines
|
|
43
44
|
node.name == STREAM && namespace(node) == NAMESPACES[:stream]
|
44
45
|
end
|
45
46
|
|
47
|
+
def body?(node)
|
48
|
+
node.name == BODY && namespace(node) == NAMESPACES[:http_bind]
|
49
|
+
end
|
50
|
+
|
46
51
|
def namespace(node)
|
47
52
|
node.namespace ? node.namespace.href : nil
|
48
53
|
end
|
data/lib/vines/stream.rb
CHANGED
@@ -10,13 +10,12 @@ module Vines
|
|
10
10
|
ERROR = 'error'.freeze
|
11
11
|
PAD = 20
|
12
12
|
|
13
|
+
attr_reader :domain
|
13
14
|
attr_accessor :user
|
14
15
|
|
15
16
|
def post_init
|
16
17
|
router << self
|
17
|
-
@remote_addr, @local_addr =
|
18
|
-
addr ? Socket.unpack_sockaddr_in(addr)[0, 2].reverse.join(':') : 'unknown'
|
19
|
-
end
|
18
|
+
@remote_addr, @local_addr = addresses
|
20
19
|
@user, @closed, @stanza_size = nil, false, 0
|
21
20
|
@bucket = TokenBucket.new(100, 10)
|
22
21
|
@store = Store.new
|
@@ -49,20 +48,10 @@ module Vines
|
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
52
|
-
# Send the stanza to all recipients, stamping it with from and
|
53
|
-
# to addresses first.
|
54
|
-
def broadcast(stanza, recipients)
|
55
|
-
stanza['from'] = @user.jid.to_s
|
56
|
-
recipients.each do |recipient|
|
57
|
-
stanza['to'] = recipient.user.jid.to_s
|
58
|
-
recipient.write(stanza)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
51
|
# Returns the storage system for the domain. If no domain is given,
|
63
52
|
# the stream's storage mechanism is returned.
|
64
|
-
def storage(domain
|
65
|
-
@config.vhosts[domain]
|
53
|
+
def storage(domain=nil)
|
54
|
+
@config.vhosts[domain || self.domain]
|
66
55
|
end
|
67
56
|
|
68
57
|
# Reload the user's information into their active connections. Call this
|
@@ -114,6 +103,8 @@ module Vines
|
|
114
103
|
log.info { "Streams connected: #{router.size}" }
|
115
104
|
end
|
116
105
|
|
106
|
+
# Advance the stream's state machine to the new state. XML nodes received
|
107
|
+
# by the stream will be passed to this state's +node+ method.
|
117
108
|
def advance(state)
|
118
109
|
@state = state
|
119
110
|
end
|
@@ -126,11 +117,11 @@ module Vines
|
|
126
117
|
when SaslError, StanzaError
|
127
118
|
write(e.to_xml)
|
128
119
|
when StreamError
|
129
|
-
|
120
|
+
send_stream_error(e)
|
130
121
|
close_stream
|
131
122
|
else
|
132
123
|
log.error(e)
|
133
|
-
|
124
|
+
send_stream_error(StreamErrors::InternalServerError.new)
|
134
125
|
close_stream
|
135
126
|
end
|
136
127
|
end
|
@@ -141,6 +132,21 @@ module Vines
|
|
141
132
|
|
142
133
|
private
|
143
134
|
|
135
|
+
# Return the remote and local socket addresses used by this connection.
|
136
|
+
def addresses
|
137
|
+
[get_peername, get_sockname].map do |addr|
|
138
|
+
addr ? Socket.unpack_sockaddr_in(addr)[0, 2].reverse.join(':') : 'unknown'
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Write the StreamError's xml to the stream. Subclasses can override
|
143
|
+
# this method with custom error writing behavior.
|
144
|
+
def send_stream_error(e)
|
145
|
+
write(e.to_xml)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Write a closing stream tag to the stream then close the stream. Subclasses
|
149
|
+
# can override this method for custom close behavior.
|
144
150
|
def close_stream
|
145
151
|
write('</stream:stream>')
|
146
152
|
close_connection_after_writing
|
@@ -170,7 +176,7 @@ module Vines
|
|
170
176
|
if error?(node)
|
171
177
|
close_stream
|
172
178
|
else
|
173
|
-
|
179
|
+
state.node(node)
|
174
180
|
end
|
175
181
|
rescue Exception => e
|
176
182
|
error(e)
|
@@ -191,8 +197,14 @@ module Vines
|
|
191
197
|
["#{label} stanza:".ljust(PAD), from, to, node])
|
192
198
|
end
|
193
199
|
|
200
|
+
# Returns the current state of the stream's state machine. Provided as a
|
201
|
+
# method so subclasses can override the behavior.
|
202
|
+
def state
|
203
|
+
@state
|
204
|
+
end
|
205
|
+
|
194
206
|
def tls_files
|
195
|
-
%w[crt key].map {|ext| File.join(VINES_ROOT, 'conf', 'certs', "#{
|
207
|
+
%w[crt key].map {|ext| File.join(VINES_ROOT, 'conf', 'certs', "#{domain}.#{ext}") }
|
196
208
|
end
|
197
209
|
end
|
198
210
|
end
|
data/lib/vines/user.rb
CHANGED
@@ -16,12 +16,10 @@ module Vines
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def <=>(user)
|
19
|
-
self.jid.to_s <=> user.jid.to_s
|
19
|
+
user.is_a?(User) ? self.jid.to_s <=> user.jid.to_s : nil
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
user.is_a?(User) && self == user
|
24
|
-
end
|
22
|
+
alias :eql? :==
|
25
23
|
|
26
24
|
def hash
|
27
25
|
jid.to_s.hash
|
data/lib/vines/version.rb
CHANGED
data/lib/vines.rb
CHANGED
@@ -52,12 +52,12 @@ end
|
|
52
52
|
em-redis
|
53
53
|
eventmachine
|
54
54
|
fiber
|
55
|
+
http/parser
|
55
56
|
logger
|
56
57
|
net/ldap
|
57
58
|
nokogiri
|
58
59
|
openssl
|
59
60
|
socket
|
60
|
-
thin
|
61
61
|
uri
|
62
62
|
yaml
|
63
63
|
|
@@ -108,6 +108,7 @@ end
|
|
108
108
|
vines/stream/parser
|
109
109
|
|
110
110
|
vines/stream/client
|
111
|
+
vines/stream/client/session
|
111
112
|
vines/stream/client/start
|
112
113
|
vines/stream/client/tls
|
113
114
|
vines/stream/client/auth_restart
|
@@ -123,9 +124,14 @@ end
|
|
123
124
|
vines/stream/component/ready
|
124
125
|
|
125
126
|
vines/stream/http
|
126
|
-
vines/stream/http/
|
127
|
-
vines/stream/http/
|
128
|
-
vines/stream/http/
|
127
|
+
vines/stream/http/session
|
128
|
+
vines/stream/http/sessions
|
129
|
+
vines/stream/http/request
|
130
|
+
vines/stream/http/start
|
131
|
+
vines/stream/http/auth
|
132
|
+
vines/stream/http/bind_restart
|
133
|
+
vines/stream/http/bind
|
134
|
+
vines/stream/http/ready
|
129
135
|
|
130
136
|
vines/stream/server
|
131
137
|
vines/stream/server/start
|