diaspora-vines 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/README.md +7 -0
- data/Rakefile +23 -0
- data/bin/vines +4 -0
- data/conf/certs/README +39 -0
- data/conf/certs/ca-bundle.crt +3895 -0
- data/conf/config.rb +42 -0
- data/lib/vines/cli.rb +132 -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/cluster.rb +246 -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/ldap.rb +38 -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/config/host.rb +125 -0
- data/lib/vines/config/port.rb +132 -0
- data/lib/vines/config/pubsub.rb +108 -0
- data/lib/vines/config.rb +223 -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 +23 -0
- data/lib/vines/log.rb +24 -0
- data/lib/vines/router.rb +179 -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/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/iq.rb +48 -0
- data/lib/vines/stanza/message.rb +40 -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/presence.rb +141 -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/stanza/pubsub.rb +22 -0
- data/lib/vines/stanza.rb +175 -0
- data/lib/vines/storage/ldap.rb +71 -0
- data/lib/vines/storage/local.rb +139 -0
- data/lib/vines/storage/null.rb +39 -0
- data/lib/vines/storage/sql.rb +138 -0
- data/lib/vines/storage.rb +239 -0
- data/lib/vines/store.rb +110 -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/client.rb +84 -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/component.rb +58 -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/http.rb +157 -0
- data/lib/vines/stream/parser.rb +79 -0
- data/lib/vines/stream/sasl.rb +128 -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/server.rb +150 -0
- data/lib/vines/stream/state.rb +60 -0
- data/lib/vines/stream.rb +247 -0
- data/lib/vines/token_bucket.rb +55 -0
- data/lib/vines/user.rb +123 -0
- data/lib/vines/version.rb +6 -0
- data/lib/vines/xmpp_server.rb +25 -0
- data/lib/vines.rb +203 -0
- data/test/cluster/publisher_test.rb +57 -0
- data/test/cluster/sessions_test.rb +47 -0
- data/test/cluster/subscriber_test.rb +109 -0
- data/test/config/host_test.rb +369 -0
- data/test/config/pubsub_test.rb +187 -0
- data/test/config_test.rb +732 -0
- data/test/contact_test.rb +102 -0
- data/test/error_test.rb +58 -0
- data/test/ext/nokogiri.rb +14 -0
- data/test/jid_test.rb +147 -0
- data/test/kit_test.rb +31 -0
- data/test/router_test.rb +243 -0
- data/test/stanza/iq/disco_info_test.rb +78 -0
- data/test/stanza/iq/disco_items_test.rb +49 -0
- data/test/stanza/iq/private_storage_test.rb +184 -0
- data/test/stanza/iq/roster_test.rb +229 -0
- data/test/stanza/iq/session_test.rb +25 -0
- data/test/stanza/iq/vcard_test.rb +146 -0
- data/test/stanza/iq/version_test.rb +64 -0
- data/test/stanza/iq_test.rb +70 -0
- data/test/stanza/message_test.rb +126 -0
- data/test/stanza/presence/probe_test.rb +50 -0
- data/test/stanza/presence/subscribe_test.rb +83 -0
- data/test/stanza/pubsub/create_test.rb +116 -0
- data/test/stanza/pubsub/delete_test.rb +169 -0
- data/test/stanza/pubsub/publish_test.rb +309 -0
- data/test/stanza/pubsub/subscribe_test.rb +205 -0
- data/test/stanza/pubsub/unsubscribe_test.rb +148 -0
- data/test/stanza_test.rb +85 -0
- data/test/storage/ldap_test.rb +201 -0
- data/test/storage/local_test.rb +59 -0
- data/test/storage/mock_redis.rb +97 -0
- data/test/storage/null_test.rb +29 -0
- data/test/storage/storage_tests.rb +182 -0
- data/test/storage_test.rb +85 -0
- data/test/store_test.rb +130 -0
- data/test/stream/client/auth_test.rb +137 -0
- data/test/stream/client/ready_test.rb +47 -0
- data/test/stream/client/session_test.rb +27 -0
- data/test/stream/component/handshake_test.rb +52 -0
- data/test/stream/component/ready_test.rb +103 -0
- data/test/stream/component/start_test.rb +39 -0
- data/test/stream/http/auth_test.rb +70 -0
- data/test/stream/http/ready_test.rb +86 -0
- data/test/stream/http/request_test.rb +209 -0
- data/test/stream/http/sessions_test.rb +49 -0
- data/test/stream/http/start_test.rb +50 -0
- data/test/stream/parser_test.rb +122 -0
- data/test/stream/sasl_test.rb +195 -0
- data/test/stream/server/auth_test.rb +61 -0
- data/test/stream/server/outbound/auth_test.rb +75 -0
- data/test/stream/server/ready_test.rb +98 -0
- data/test/test_helper.rb +42 -0
- data/test/token_bucket_test.rb +44 -0
- data/test/user_test.rb +96 -0
- data/vines.gemspec +30 -0
- metadata +387 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stream::Http::Ready do
|
6
|
+
subject { Vines::Stream::Http::Ready.new(stream, nil) }
|
7
|
+
let(:stream) { MiniTest::Mock.new }
|
8
|
+
let(:alice) { Vines::User.new(jid: 'alice@wonderland.lit') }
|
9
|
+
let(:hatter) { Vines::User.new(jid: 'hatter@wonderland.lit') }
|
10
|
+
let(:config) do
|
11
|
+
Vines::Config.new do
|
12
|
+
host 'wonderland.lit' do
|
13
|
+
storage(:fs) { dir Dir.tmpdir }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "raises when body element is missing" do
|
19
|
+
node = node('<presence type="unavailable"/>')
|
20
|
+
stream.expect :valid_session?, true, [nil]
|
21
|
+
-> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises when namespace is missing" do
|
25
|
+
node = node('<body rid="42" sid="12"/>')
|
26
|
+
stream.expect :valid_session?, true, ['12']
|
27
|
+
-> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
|
28
|
+
end
|
29
|
+
|
30
|
+
it "raises when rid attribute is missing" do
|
31
|
+
node = node('<body xmlns="http://jabber.org/protocol/httpbind" sid="12"/>')
|
32
|
+
stream.expect :valid_session?, true, ['12']
|
33
|
+
-> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
|
34
|
+
end
|
35
|
+
|
36
|
+
it "raises when session id is invalid" do
|
37
|
+
stream.expect :valid_session?, false, ['12']
|
38
|
+
node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
|
39
|
+
-> { subject.node(node) }.must_raise Vines::StreamErrors::NotAuthorized
|
40
|
+
stream.verify
|
41
|
+
end
|
42
|
+
|
43
|
+
it "processes when body element is empty" do
|
44
|
+
node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
|
45
|
+
stream.expect :valid_session?, true, ['12']
|
46
|
+
stream.expect :parse_body, [], [node]
|
47
|
+
subject.node(node)
|
48
|
+
stream.verify
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'when receiving multiple stanzas in one body element' do
|
52
|
+
let(:recipient) { MiniTest::Mock.new }
|
53
|
+
let(:bogus) { node('<message type="bogus">raises stanza error</message>') }
|
54
|
+
let(:ok) { node('<message to="hatter@wonderland.lit">but processes this message</message>') }
|
55
|
+
let(:xml) { node(%Q{<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12">#{bogus}#{ok}</body>}) }
|
56
|
+
let(:raises) { Vines::Stanza.from_node(bogus, stream) }
|
57
|
+
let(:processes) { Vines::Stanza.from_node(ok, stream) }
|
58
|
+
|
59
|
+
before do
|
60
|
+
recipient.expect :user, hatter
|
61
|
+
recipient.expect :write, nil, [Vines::Stanza::Message]
|
62
|
+
|
63
|
+
stream.expect :valid_session?, true, ['12']
|
64
|
+
stream.expect :parse_body, [raises, processes], [xml]
|
65
|
+
stream.expect :error, nil, [Vines::StanzaErrors::BadRequest]
|
66
|
+
stream.expect :config, config
|
67
|
+
stream.expect :user, alice
|
68
|
+
stream.expect :connected_resources, [recipient], [hatter.jid]
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'processes all stanzas' do
|
72
|
+
subject.node(xml)
|
73
|
+
stream.verify
|
74
|
+
recipient.verify
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "terminates the session" do
|
79
|
+
node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12" type="terminate"/>')
|
80
|
+
stream.expect :valid_session?, true, ['12']
|
81
|
+
stream.expect :parse_body, [], [node]
|
82
|
+
stream.expect :terminate, nil
|
83
|
+
subject.node(node)
|
84
|
+
stream.verify
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stream::Http::Request do
|
6
|
+
PASSWORD = File.expand_path('../passwords')
|
7
|
+
INDEX = File.expand_path('index.html')
|
8
|
+
|
9
|
+
before do
|
10
|
+
File.open(PASSWORD, 'w') {|f| f.puts '/etc/passwd contents' }
|
11
|
+
File.open(INDEX, 'w') {|f| f.puts 'index.html contents' }
|
12
|
+
|
13
|
+
@stream = MiniTest::Mock.new
|
14
|
+
@parser = MiniTest::Mock.new
|
15
|
+
@parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
|
16
|
+
@parser.expect(:http_method, 'GET')
|
17
|
+
@parser.expect(:request_path, '/blogs/12')
|
18
|
+
@parser.expect(:request_url, '/blogs/12?ok=true')
|
19
|
+
@parser.expect(:query_string, 'ok=true')
|
20
|
+
end
|
21
|
+
|
22
|
+
after do
|
23
|
+
File.delete(PASSWORD)
|
24
|
+
File.delete(INDEX)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'initialize' do
|
28
|
+
it 'copies request info from parser' do
|
29
|
+
request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
|
30
|
+
assert_equal request.headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'}
|
31
|
+
assert_equal request.method, 'GET'
|
32
|
+
assert_equal request.path, '/blogs/12'
|
33
|
+
assert_equal request.url, '/blogs/12?ok=true'
|
34
|
+
assert_equal request.query, 'ok=true'
|
35
|
+
assert_equal request.body, '<html></html>'
|
36
|
+
assert @stream.verify
|
37
|
+
assert @parser.verify
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'reply_with_file' do
|
42
|
+
it 'returns 404 file not found' do
|
43
|
+
request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
|
44
|
+
headers = [
|
45
|
+
"HTTP/1.1 404 Not Found",
|
46
|
+
"Content-Length: 0"
|
47
|
+
].join("\r\n")
|
48
|
+
|
49
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
50
|
+
|
51
|
+
request.reply_with_file(Dir.pwd)
|
52
|
+
assert @stream.verify
|
53
|
+
assert @parser.verify
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'prevents directory traversal with 404 response' do
|
57
|
+
parser = MiniTest::Mock.new
|
58
|
+
parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
|
59
|
+
parser.expect(:http_method, 'GET')
|
60
|
+
parser.expect(:request_path, '/../passwords')
|
61
|
+
parser.expect(:request_url, '/../passwords')
|
62
|
+
parser.expect(:query_string, '')
|
63
|
+
|
64
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
65
|
+
|
66
|
+
headers = [
|
67
|
+
"HTTP/1.1 404 Not Found",
|
68
|
+
"Content-Length: 0"
|
69
|
+
].join("\r\n")
|
70
|
+
|
71
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
72
|
+
|
73
|
+
request.reply_with_file(Dir.pwd)
|
74
|
+
assert @stream.verify
|
75
|
+
assert parser.verify
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'serves index.html for directory request' do
|
79
|
+
parser = MiniTest::Mock.new
|
80
|
+
parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
|
81
|
+
parser.expect(:http_method, 'GET')
|
82
|
+
parser.expect(:request_path, '/')
|
83
|
+
parser.expect(:request_url, '/?ok=true')
|
84
|
+
parser.expect(:query_string, 'ok=true')
|
85
|
+
|
86
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
87
|
+
|
88
|
+
mtime = File.mtime(INDEX).utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
89
|
+
headers = [
|
90
|
+
"HTTP/1.1 200 OK",
|
91
|
+
'Content-Type: text/html; charset="utf-8"',
|
92
|
+
"Content-Length: 20",
|
93
|
+
"Last-Modified: #{mtime}"
|
94
|
+
].join("\r\n")
|
95
|
+
|
96
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
97
|
+
@stream.expect(:stream_write, nil, ["index.html contents\n"])
|
98
|
+
|
99
|
+
request.reply_with_file(Dir.pwd)
|
100
|
+
assert @stream.verify
|
101
|
+
assert parser.verify
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'redirects for missing trailing slash' do
|
105
|
+
parser = MiniTest::Mock.new
|
106
|
+
parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
|
107
|
+
parser.expect(:http_method, 'GET')
|
108
|
+
parser.expect(:request_path, '/http')
|
109
|
+
parser.expect(:request_url, '/http?ok=true')
|
110
|
+
parser.expect(:query_string, 'ok=true')
|
111
|
+
|
112
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
113
|
+
|
114
|
+
headers = [
|
115
|
+
"HTTP/1.1 301 Moved Permanently",
|
116
|
+
"Content-Length: 0",
|
117
|
+
"Location: http://wonderland.lit/http/?ok=true"
|
118
|
+
].join("\r\n")
|
119
|
+
|
120
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
121
|
+
# so the /http url above will work
|
122
|
+
request.reply_with_file(File.expand_path('../../', __FILE__))
|
123
|
+
assert @stream.verify
|
124
|
+
assert parser.verify
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'reply_to_options' do
|
129
|
+
it 'returns cors headers' do
|
130
|
+
parser = MiniTest::Mock.new
|
131
|
+
parser.expect(:headers, {
|
132
|
+
'Content-Type' => 'text/xml',
|
133
|
+
'Host' => 'wonderland.lit',
|
134
|
+
'Origin' => 'remote.wonderland.lit',
|
135
|
+
'Access-Control-Request-Headers' => 'Content-Type, Origin'})
|
136
|
+
parser.expect(:http_method, 'OPTIONS')
|
137
|
+
parser.expect(:request_path, '/xmpp')
|
138
|
+
parser.expect(:request_url, '/xmpp')
|
139
|
+
parser.expect(:query_string, '')
|
140
|
+
|
141
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '')
|
142
|
+
|
143
|
+
headers = [
|
144
|
+
"HTTP/1.1 200 OK",
|
145
|
+
"Content-Length: 0",
|
146
|
+
"Access-Control-Allow-Origin: *",
|
147
|
+
"Access-Control-Allow-Methods: POST, GET, OPTIONS",
|
148
|
+
"Access-Control-Allow-Headers: Content-Type, Origin",
|
149
|
+
"Access-Control-Max-Age: 2592000"
|
150
|
+
].join("\r\n")
|
151
|
+
|
152
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
153
|
+
request.reply_to_options
|
154
|
+
assert @stream.verify
|
155
|
+
assert parser.verify
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe 'reply' do
|
160
|
+
it 'returns set-cookie header when vroute is defined' do
|
161
|
+
reply_with_cookie('v1')
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'does not return set-cookie header when vroute is undefined' do
|
165
|
+
reply_with_cookie('')
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def reply_with_cookie(cookie)
|
172
|
+
config = Vines::Config.new do
|
173
|
+
host 'wonderland.lit' do
|
174
|
+
storage(:fs) { dir Dir.tmpdir }
|
175
|
+
end
|
176
|
+
http '0.0.0.0', 5280 do
|
177
|
+
vroute cookie
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
parser = MiniTest::Mock.new
|
182
|
+
parser.expect(:headers, {
|
183
|
+
'Content-Type' => 'text/xml',
|
184
|
+
'Host' => 'wonderland.lit',
|
185
|
+
'Origin' => 'remote.wonderland.lit'})
|
186
|
+
parser.expect(:http_method, 'POST')
|
187
|
+
parser.expect(:request_path, '/xmpp')
|
188
|
+
parser.expect(:request_url, '/xmpp')
|
189
|
+
parser.expect(:query_string, '')
|
190
|
+
|
191
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '')
|
192
|
+
message = '<message>hello</message>'
|
193
|
+
|
194
|
+
headers = [
|
195
|
+
"HTTP/1.1 200 OK",
|
196
|
+
"Access-Control-Allow-Origin: *",
|
197
|
+
"Content-Type: application/xml",
|
198
|
+
"Content-Length: 24",
|
199
|
+
]
|
200
|
+
headers << "Set-Cookie: vroute=#{cookie}; path=/; HttpOnly" unless cookie.empty?
|
201
|
+
headers = headers.join("\r\n")
|
202
|
+
|
203
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n#{message}"])
|
204
|
+
@stream.expect(:config, config)
|
205
|
+
request.reply(message, 'application/xml')
|
206
|
+
assert @stream.verify
|
207
|
+
assert parser.verify
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stream::Http::Sessions do
|
6
|
+
class MockSessions < Vines::Stream::Http::Sessions
|
7
|
+
def start_timer
|
8
|
+
# do nothing
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@sessions = MockSessions.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_session_add_and_delete
|
17
|
+
session = "session"
|
18
|
+
assert_nil @sessions['42']
|
19
|
+
@sessions['42'] = session
|
20
|
+
assert_equal session, @sessions['42']
|
21
|
+
@sessions.delete('42')
|
22
|
+
assert_nil @sessions['42']
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_access_singleton_through_class_methods
|
26
|
+
session = "session"
|
27
|
+
assert_nil MockSessions['42']
|
28
|
+
MockSessions['42'] = session
|
29
|
+
assert_equal session, MockSessions['42']
|
30
|
+
MockSessions.delete('42')
|
31
|
+
assert_nil MockSessions['42']
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_cleanup
|
35
|
+
live = MiniTest::Mock.new
|
36
|
+
live.expect(:expired?, false)
|
37
|
+
|
38
|
+
dead = MiniTest::Mock.new
|
39
|
+
dead.expect(:expired?, true)
|
40
|
+
dead.expect(:close, nil)
|
41
|
+
|
42
|
+
@sessions['live'] = live
|
43
|
+
@sessions['dead'] = dead
|
44
|
+
|
45
|
+
@sessions.send(:cleanup)
|
46
|
+
assert live.verify
|
47
|
+
assert dead.verify
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stream::Http::Start do
|
6
|
+
before do
|
7
|
+
@stream = MiniTest::Mock.new
|
8
|
+
@state = Vines::Stream::Http::Start.new(@stream)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_missing_body_raises_error
|
12
|
+
node = node('<presence type="unavailable"/>')
|
13
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_body_with_missing_namespace_raises_error
|
17
|
+
node = node('<body rid="42" sid="12"/>')
|
18
|
+
assert_raises(Vines::StreamErrors::NotAuthorized) { @state.node(node) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_missing_session_starts_stream
|
22
|
+
EM.run do
|
23
|
+
node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="12"/>')
|
24
|
+
@stream.expect(:start, nil, [node])
|
25
|
+
@stream.expect(:advance, nil, [Vines::Stream::Http::Auth.new(@stream)])
|
26
|
+
@state.node(node)
|
27
|
+
assert @stream.verify
|
28
|
+
EM.stop
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_valid_session_resumes_stream
|
33
|
+
EM.run do
|
34
|
+
node = node('<body xmlns="http://jabber.org/protocol/httpbind" rid="42" sid="123"/>')
|
35
|
+
session = MiniTest::Mock.new
|
36
|
+
session.expect(:resume, nil, [@stream, node])
|
37
|
+
Vines::Stream::Http::Sessions['123'] = session
|
38
|
+
@state.node(node)
|
39
|
+
assert @stream.verify
|
40
|
+
assert session.verify
|
41
|
+
EM.stop
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def node(xml)
|
48
|
+
Nokogiri::XML(xml).root
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Vines::Stream::Parser do
|
6
|
+
STREAM_START = '<stream:stream to="wonderland.lit" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams">'.freeze
|
7
|
+
|
8
|
+
before do
|
9
|
+
@events = []
|
10
|
+
@parser = Vines::Stream::Parser.new.tap do |p|
|
11
|
+
p.stream_open {|el| @events << el }
|
12
|
+
p.stream_close { @events << :close }
|
13
|
+
p.stanza {|el| @events << el }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_xpath_to_subclass
|
18
|
+
expected = []
|
19
|
+
stanzas = [
|
20
|
+
['<message></message>', Vines::Stanza::Message],
|
21
|
+
['<presence/>', Vines::Stanza::Presence],
|
22
|
+
['<presence type="bogus"/>', Vines::Stanza::Presence],
|
23
|
+
['<presence type="error"/>', Vines::Stanza::Presence::Error],
|
24
|
+
['<presence type="probe"/>', Vines::Stanza::Presence::Probe],
|
25
|
+
['<presence type="subscribe"/>', Vines::Stanza::Presence::Subscribe],
|
26
|
+
['<presence type="subscribed"/>', Vines::Stanza::Presence::Subscribed],
|
27
|
+
['<presence type="unavailable"/>', Vines::Stanza::Presence::Unavailable],
|
28
|
+
['<presence type="unsubscribe"/>', Vines::Stanza::Presence::Unsubscribe],
|
29
|
+
['<presence type="unsubscribed"/>', Vines::Stanza::Presence::Unsubscribed],
|
30
|
+
['<iq id="42" type="get"><query xmlns="http://jabber.org/protocol/disco#info"></query></iq>', Vines::Stanza::Iq::Query::DiscoInfo],
|
31
|
+
['<iq id="42" type="get"><query xmlns="http://jabber.org/protocol/disco#items"></query></iq>', Vines::Stanza::Iq::Query::DiscoItems],
|
32
|
+
['<iq id="42" type="error"></iq>', Vines::Stanza::Iq::Error],
|
33
|
+
['<iq id="42" type="get"><query xmlns="jabber:iq:private"/></iq>', Vines::Stanza::Iq::PrivateStorage],
|
34
|
+
['<iq id="42" type="set"><query xmlns="jabber:iq:private"/></iq>', Vines::Stanza::Iq::PrivateStorage],
|
35
|
+
['<iq id="42" type="get"><ping xmlns="urn:xmpp:ping"/></iq>', Vines::Stanza::Iq::Ping],
|
36
|
+
['<iq id="42" type="result"></iq>', Vines::Stanza::Iq::Result],
|
37
|
+
['<iq id="42" type="get"><query xmlns="jabber:iq:roster"/></iq>', Vines::Stanza::Iq::Query::Roster],
|
38
|
+
['<iq id="42" type="set"><query xmlns="jabber:iq:roster"/></iq>', Vines::Stanza::Iq::Query::Roster],
|
39
|
+
['<iq id="42" type="set"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>', Vines::Stanza::Iq::Session],
|
40
|
+
['<iq id="42" type="get"><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq::Vcard],
|
41
|
+
['<iq type="get"><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq],
|
42
|
+
['<iq id="42"><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq],
|
43
|
+
['<iq><vCard xmlns="vcard-temp"/></iq>', Vines::Stanza::Iq],
|
44
|
+
['<bogus/>', NilClass],
|
45
|
+
]
|
46
|
+
@parser << STREAM_START
|
47
|
+
stanzas.each do |stanza, klass|
|
48
|
+
@parser << stanza
|
49
|
+
expected << klass
|
50
|
+
end
|
51
|
+
@parser << '</stream:stream>'
|
52
|
+
assert_equal 'stream', @events.shift.name
|
53
|
+
assert_equal :close, @events.pop
|
54
|
+
assert_equal expected.size, @events.size
|
55
|
+
@events.each_with_index do |ev, ix|
|
56
|
+
assert_equal expected[ix], Vines::Stanza.from_node(ev, nil).class
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_stream_namespace_with_default_prefix
|
61
|
+
@parser << STREAM_START
|
62
|
+
assert_equal 1, @events.size
|
63
|
+
stream = @events.shift
|
64
|
+
assert_equal 'stream', stream.name
|
65
|
+
refute_nil stream.namespace
|
66
|
+
assert_equal 'stream', stream.namespace.prefix
|
67
|
+
assert_equal 'http://etherx.jabber.org/streams', stream.namespace.href
|
68
|
+
expected = {'xmlns' => 'jabber:client', 'xmlns:stream' => 'http://etherx.jabber.org/streams'}
|
69
|
+
assert_equal expected, stream.namespaces
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_stanzas_ignore_default_namespace
|
73
|
+
@parser << STREAM_START
|
74
|
+
@parser << '<message to="alice@wonderland.lit">hello!</message>'
|
75
|
+
assert_equal 2, @events.size
|
76
|
+
@events.shift # discard stream
|
77
|
+
msg = @events.shift
|
78
|
+
assert_equal 'message', msg.name
|
79
|
+
assert msg.namespaces.empty?
|
80
|
+
assert_nil msg.namespace
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_nested_elements_have_namespace
|
84
|
+
@parser << STREAM_START
|
85
|
+
@parser << %q{
|
86
|
+
<iq from='alice@wonderland.lit/tea' id='42' type='set'>
|
87
|
+
<query xmlns='jabber:iq:roster'>
|
88
|
+
<item jid='hatter@wonderland.lit' name='Mad Hatter'>
|
89
|
+
<group>Tea Party</group>
|
90
|
+
</item>
|
91
|
+
</query>
|
92
|
+
</iq>
|
93
|
+
}
|
94
|
+
assert_equal 2, @events.size
|
95
|
+
@events.shift # discard stream
|
96
|
+
iq = @events.shift
|
97
|
+
assert_equal 'iq', iq.name
|
98
|
+
assert iq.namespaces.empty?
|
99
|
+
assert_nil iq.namespace
|
100
|
+
|
101
|
+
query = iq.elements.first
|
102
|
+
refute_nil query.namespace
|
103
|
+
assert_nil query.namespace.prefix
|
104
|
+
assert_equal 'jabber:iq:roster', query.namespace.href
|
105
|
+
expected = {'xmlns' => 'jabber:iq:roster'}
|
106
|
+
assert_equal expected, query.namespaces
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_error_stanzas_have_stream_namespace
|
110
|
+
@parser << STREAM_START
|
111
|
+
@parser << '<stream:error><not-well-formed xmlns="urn:ietf:params:xml:ns:xmpp-streams"/></stream:error>'
|
112
|
+
assert_equal 2, @events.size
|
113
|
+
@events.shift # discard stream
|
114
|
+
error = @events.shift
|
115
|
+
assert_equal 'error', error.name
|
116
|
+
refute_nil error.namespace
|
117
|
+
assert_equal 'stream', error.namespace.prefix
|
118
|
+
assert_equal 'http://etherx.jabber.org/streams', error.namespace.href
|
119
|
+
expected = {'xmlns:stream' => 'http://etherx.jabber.org/streams'}
|
120
|
+
assert_equal expected, error.namespaces
|
121
|
+
end
|
122
|
+
end
|