vines 0.4.1 → 0.4.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.
- data/README +4 -3
- data/Rakefile +2 -2
- data/conf/config.rb +13 -3
- data/lib/vines/cluster.rb +1 -1
- data/lib/vines/cluster/subscriber.rb +1 -1
- data/lib/vines/command/ldap.rb +1 -1
- data/lib/vines/command/schema.rb +1 -1
- data/lib/vines/config/port.rb +16 -0
- data/lib/vines/storage.rb +2 -2
- data/lib/vines/stream.rb +1 -1
- data/lib/vines/stream/client/auth.rb +1 -1
- data/lib/vines/stream/http/request.rb +12 -2
- data/lib/vines/stream/http/sessions.rb +1 -1
- data/lib/vines/stream/server.rb +1 -1
- data/lib/vines/version.rb +1 -1
- data/test/stream/http/request_test.rb +152 -122
- metadata +34 -34
data/README
CHANGED
@@ -2,9 +2,10 @@
|
|
2
2
|
|
3
3
|
Vines is an XMPP chat server that supports thousands of simultaneous connections
|
4
4
|
by using EventMachine for asynchronous IO. User data is stored in a SQL database,
|
5
|
-
CouchDB, Redis, the file system, or a custom storage implementation
|
6
|
-
LDAP authentication can be used so user names and passwords
|
7
|
-
database. SSL encryption is mandatory on all client
|
5
|
+
CouchDB, MongoDB, Redis, the file system, or a custom storage implementation
|
6
|
+
that you provide. LDAP authentication can be used so user names and passwords
|
7
|
+
aren't stored in the chat database. SSL encryption is mandatory on all client
|
8
|
+
and server connections.
|
8
9
|
|
9
10
|
The Vines XMPP server includes a web chat client. The web application is available
|
10
11
|
immediately after starting the chat server at http://localhost:5280/chat/.
|
data/Rakefile
CHANGED
@@ -29,7 +29,7 @@ is mandatory on all client and server connections."
|
|
29
29
|
s.email = %w[david@negativecode.com]
|
30
30
|
s.homepage = "http://www.getvines.org"
|
31
31
|
|
32
|
-
s.test_files = FileList["test/**/*"]
|
32
|
+
s.test_files = FileList["test/**/*"].to_a
|
33
33
|
s.executables = %w[vines]
|
34
34
|
s.require_path = "lib"
|
35
35
|
|
@@ -57,7 +57,7 @@ end
|
|
57
57
|
desc 'Build distributable packages'
|
58
58
|
task :build => :assets do
|
59
59
|
# create package task after assets are generated so they're included in FileList
|
60
|
-
spec.files = FileList['[A-Z]*', '{bin,lib,conf,web}/**/*']
|
60
|
+
spec.files = FileList['[A-Z]*', '{bin,lib,conf,web}/**/*'].to_a
|
61
61
|
Gem::PackageTask.new(spec).define
|
62
62
|
Rake::Task['gem'].invoke
|
63
63
|
end
|
data/conf/config.rb
CHANGED
@@ -90,14 +90,24 @@ Vines::Config.configure do
|
|
90
90
|
|
91
91
|
# Configure the built-in HTTP server that serves static files and responds to
|
92
92
|
# XEP-0124 BOSH requests. This allows HTTP clients to connect to
|
93
|
-
# the XMPP server.
|
94
|
-
#
|
95
|
-
#
|
93
|
+
# the XMPP server.
|
94
|
+
#
|
95
|
+
# The root attribute defines the web server's document root (default 'web/').
|
96
|
+
# It will only serve files out of this directory.
|
97
|
+
#
|
98
|
+
# The bind attribute defines the URL to which BOSH clients must POST their
|
99
|
+
# XMPP stanza requests (default /xmpp).
|
100
|
+
#
|
101
|
+
# The vroute attribute defines the value of the vroute cookie sent in each
|
102
|
+
# response that uniquely identifies this HTTP server. Reverse proxy servers
|
103
|
+
# (nginx/apache) can use this cookie to implement sticky sessions. This is
|
104
|
+
# only needed if the server is clustered behind a reverse proxy.
|
96
105
|
http '0.0.0.0', 5280 do
|
97
106
|
bind '/xmpp'
|
98
107
|
max_stanza_size 65536
|
99
108
|
max_resources_per_account 5
|
100
109
|
root 'web'
|
110
|
+
vroute ''
|
101
111
|
end
|
102
112
|
|
103
113
|
# Configure the XEP-0114 external component port. Component sub-domains and
|
data/lib/vines/cluster.rb
CHANGED
data/lib/vines/command/ldap.rb
CHANGED
data/lib/vines/command/schema.rb
CHANGED
data/lib/vines/config/port.rb
CHANGED
@@ -104,6 +104,22 @@ module Vines
|
|
104
104
|
@settings[:bind]
|
105
105
|
end
|
106
106
|
end
|
107
|
+
|
108
|
+
def vroute(id=nil)
|
109
|
+
if id
|
110
|
+
id = id.to_s.strip
|
111
|
+
@settings[:vroute] = id.empty? ? nil : id
|
112
|
+
else
|
113
|
+
@settings[:vroute]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def start
|
118
|
+
super
|
119
|
+
if config.cluster? && vroute.nil?
|
120
|
+
log.warn("vroute sticky session cookie not set")
|
121
|
+
end
|
122
|
+
end
|
107
123
|
end
|
108
124
|
|
109
125
|
class ComponentPort < Port
|
data/lib/vines/storage.rb
CHANGED
@@ -42,7 +42,7 @@ module Vines
|
|
42
42
|
op = proc do
|
43
43
|
begin
|
44
44
|
method(old).call(*args)
|
45
|
-
rescue
|
45
|
+
rescue => e
|
46
46
|
log.error("Thread pool operation failed: #{e.message}")
|
47
47
|
nil
|
48
48
|
end
|
@@ -218,7 +218,7 @@ module Vines
|
|
218
218
|
op = proc do
|
219
219
|
begin
|
220
220
|
ldap.authenticate(username, password)
|
221
|
-
rescue
|
221
|
+
rescue => e
|
222
222
|
log.error("LDAP authentication failed: #{e.message}")
|
223
223
|
nil
|
224
224
|
end
|
data/lib/vines/stream.rb
CHANGED
@@ -58,7 +58,7 @@ module Vines
|
|
58
58
|
begin
|
59
59
|
user = stream.storage.authenticate(jid, password)
|
60
60
|
finish(user || SaslErrors::NotAuthorized.new)
|
61
|
-
rescue
|
61
|
+
rescue => e
|
62
62
|
log.error("Failed to authenticate: #{e.to_s}")
|
63
63
|
finish(SaslErrors::TemporaryAuthFailure.new)
|
64
64
|
end
|
@@ -75,8 +75,9 @@ module Vines
|
|
75
75
|
"HTTP/1.1 200 OK",
|
76
76
|
"Access-Control-Allow-Origin: *",
|
77
77
|
"Content-Type: #{content_type}",
|
78
|
-
"Content-Length: #{body.bytesize}"
|
79
|
-
|
78
|
+
"Content-Length: #{body.bytesize}",
|
79
|
+
vroute_cookie
|
80
|
+
].compact.join("\r\n")
|
80
81
|
@stream.stream_write([header, body].join("\r\n\r\n"))
|
81
82
|
end
|
82
83
|
|
@@ -156,6 +157,15 @@ module Vines
|
|
156
157
|
ext = File.extname(path).sub('.', '')
|
157
158
|
CONTENT_TYPES[ext] || TEXT_PLAIN
|
158
159
|
end
|
160
|
+
|
161
|
+
# Provide a vroute cookie in each response that uniquely identifies this
|
162
|
+
# HTTP server. Reverse proxy servers (nginx/apache) can use this cookie
|
163
|
+
# to implement sticky sessions. Return nil if vroute was not set in
|
164
|
+
# config.rb and no cookie should be sent.
|
165
|
+
def vroute_cookie
|
166
|
+
route = @stream.config[:http].vroute
|
167
|
+
route ? "Set-Cookie: vroute=#{route}; path=/; HttpOnly" : nil
|
168
|
+
end
|
159
169
|
end
|
160
170
|
end
|
161
171
|
end
|
data/lib/vines/stream/server.rb
CHANGED
data/lib/vines/version.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
+
require 'tmpdir'
|
3
4
|
require 'vines'
|
4
5
|
require 'minitest/autorun'
|
5
6
|
|
6
|
-
|
7
|
+
describe Vines::Stream::Http::Request do
|
7
8
|
PASSWORD = File.expand_path('../passwords')
|
8
9
|
INDEX = File.expand_path('index.html')
|
9
10
|
|
10
|
-
|
11
|
+
before do
|
11
12
|
File.open(PASSWORD, 'w') {|f| f.puts '/etc/passwd contents' }
|
12
13
|
File.open(INDEX, 'w') {|f| f.puts 'index.html contents' }
|
13
14
|
|
@@ -20,139 +21,165 @@ class RequestTest < MiniTest::Unit::TestCase
|
|
20
21
|
@parser.expect(:query_string, 'ok=true')
|
21
22
|
end
|
22
23
|
|
23
|
-
|
24
|
+
after do
|
24
25
|
File.delete(PASSWORD)
|
25
26
|
File.delete(INDEX)
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
-
request
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
29
|
+
describe 'initialize' do
|
30
|
+
it 'copies request info from parser' do
|
31
|
+
request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
|
32
|
+
assert_equal request.headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'}
|
33
|
+
assert_equal request.method, 'GET'
|
34
|
+
assert_equal request.path, '/blogs/12'
|
35
|
+
assert_equal request.url, '/blogs/12?ok=true'
|
36
|
+
assert_equal request.query, 'ok=true'
|
37
|
+
assert_equal request.body, '<html></html>'
|
38
|
+
assert @stream.verify
|
39
|
+
assert @parser.verify
|
40
|
+
end
|
38
41
|
end
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
43
|
+
describe 'reply_with_file' do
|
44
|
+
it 'returns 404 file not found' do
|
45
|
+
request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
|
46
|
+
headers = [
|
47
|
+
"HTTP/1.1 404 Not Found",
|
48
|
+
"Content-Length: 0"
|
49
|
+
].join("\r\n")
|
50
|
+
|
51
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
52
|
+
|
53
|
+
request.reply_with_file(Dir.pwd)
|
54
|
+
assert @stream.verify
|
55
|
+
assert @parser.verify
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'prevents directory traversal with 404 response' do
|
59
|
+
parser = MiniTest::Mock.new
|
60
|
+
parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
|
61
|
+
parser.expect(:http_method, 'GET')
|
62
|
+
parser.expect(:request_path, '/../passwords')
|
63
|
+
parser.expect(:request_url, '/../passwords')
|
64
|
+
parser.expect(:query_string, '')
|
65
|
+
|
66
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
67
|
+
|
68
|
+
headers = [
|
69
|
+
"HTTP/1.1 404 Not Found",
|
70
|
+
"Content-Length: 0"
|
71
|
+
].join("\r\n")
|
72
|
+
|
73
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
74
|
+
|
75
|
+
request.reply_with_file(Dir.pwd)
|
76
|
+
assert @stream.verify
|
77
|
+
assert parser.verify
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'serves index.html for directory request' do
|
81
|
+
parser = MiniTest::Mock.new
|
82
|
+
parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
|
83
|
+
parser.expect(:http_method, 'GET')
|
84
|
+
parser.expect(:request_path, '/')
|
85
|
+
parser.expect(:request_url, '/?ok=true')
|
86
|
+
parser.expect(:query_string, 'ok=true')
|
87
|
+
|
88
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
89
|
+
|
90
|
+
mtime = File.mtime(INDEX).utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
91
|
+
headers = [
|
92
|
+
"HTTP/1.1 200 OK",
|
93
|
+
'Content-Type: text/html; charset="utf-8"',
|
94
|
+
"Content-Length: 20",
|
95
|
+
"Last-Modified: #{mtime}"
|
96
|
+
].join("\r\n")
|
97
|
+
|
98
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
99
|
+
@stream.expect(:stream_write, nil, ["index.html contents\n"])
|
100
|
+
|
101
|
+
request.reply_with_file(Dir.pwd)
|
102
|
+
assert @stream.verify
|
103
|
+
assert parser.verify
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'redirects for missing trailing slash' do
|
107
|
+
parser = MiniTest::Mock.new
|
108
|
+
parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
|
109
|
+
parser.expect(:http_method, 'GET')
|
110
|
+
parser.expect(:request_path, '/http')
|
111
|
+
parser.expect(:request_url, '/http?ok=true')
|
112
|
+
parser.expect(:query_string, 'ok=true')
|
113
|
+
|
114
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
115
|
+
|
116
|
+
headers = [
|
117
|
+
"HTTP/1.1 301 Moved Permanently",
|
118
|
+
"Content-Length: 0",
|
119
|
+
"Location: http://wonderland.lit/http/?ok=true"
|
120
|
+
].join("\r\n")
|
121
|
+
|
122
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
123
|
+
# so the /http url above will work
|
124
|
+
request.reply_with_file(File.expand_path('../../', __FILE__))
|
125
|
+
assert @stream.verify
|
126
|
+
assert parser.verify
|
127
|
+
end
|
53
128
|
end
|
54
129
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
130
|
+
describe 'reply_to_options' do
|
131
|
+
it 'returns cors headers' do
|
132
|
+
parser = MiniTest::Mock.new
|
133
|
+
parser.expect(:headers, {
|
134
|
+
'Content-Type' => 'text/xml',
|
135
|
+
'Host' => 'wonderland.lit',
|
136
|
+
'Origin' => 'remote.wonderland.lit',
|
137
|
+
'Access-Control-Request-Headers' => 'Content-Type, Origin'})
|
138
|
+
parser.expect(:http_method, 'OPTIONS')
|
139
|
+
parser.expect(:request_path, '/xmpp')
|
140
|
+
parser.expect(:request_url, '/xmpp')
|
141
|
+
parser.expect(:query_string, '')
|
142
|
+
|
143
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '')
|
144
|
+
|
145
|
+
headers = [
|
146
|
+
"HTTP/1.1 200 OK",
|
147
|
+
"Content-Length: 0",
|
148
|
+
"Access-Control-Allow-Origin: *",
|
149
|
+
"Access-Control-Allow-Methods: POST, GET, OPTIONS",
|
150
|
+
"Access-Control-Allow-Headers: Content-Type, Origin",
|
151
|
+
"Access-Control-Max-Age: 2592000"
|
152
|
+
].join("\r\n")
|
153
|
+
|
154
|
+
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
155
|
+
request.reply_to_options
|
156
|
+
assert @stream.verify
|
157
|
+
assert parser.verify
|
158
|
+
end
|
75
159
|
end
|
76
160
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
parser.expect(:request_path, '/')
|
82
|
-
parser.expect(:request_url, '/?ok=true')
|
83
|
-
parser.expect(:query_string, 'ok=true')
|
84
|
-
|
85
|
-
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
86
|
-
|
87
|
-
mtime = File.mtime(INDEX).utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
88
|
-
headers = [
|
89
|
-
"HTTP/1.1 200 OK",
|
90
|
-
'Content-Type: text/html; charset="utf-8"',
|
91
|
-
"Content-Length: 20",
|
92
|
-
"Last-Modified: #{mtime}"
|
93
|
-
].join("\r\n")
|
94
|
-
|
95
|
-
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
96
|
-
@stream.expect(:stream_write, nil, ["index.html contents\n"])
|
161
|
+
describe 'reply' do
|
162
|
+
it 'returns set-cookie header when vroute is defined' do
|
163
|
+
reply_with_cookie('v1')
|
164
|
+
end
|
97
165
|
|
98
|
-
|
99
|
-
|
100
|
-
|
166
|
+
it 'does not return set-cookie header when vroute is undefined' do
|
167
|
+
reply_with_cookie('')
|
168
|
+
end
|
101
169
|
end
|
102
170
|
|
103
|
-
|
104
|
-
parser = MiniTest::Mock.new
|
105
|
-
parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
|
106
|
-
parser.expect(:http_method, 'GET')
|
107
|
-
parser.expect(:request_path, '/http')
|
108
|
-
parser.expect(:request_url, '/http?ok=true')
|
109
|
-
parser.expect(:query_string, 'ok=true')
|
171
|
+
private
|
110
172
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
# so the /http url above will work
|
121
|
-
request.reply_with_file(File.expand_path('../../', __FILE__))
|
122
|
-
assert @stream.verify
|
123
|
-
assert parser.verify
|
124
|
-
end
|
125
|
-
|
126
|
-
def test_reply_to_options
|
127
|
-
parser = MiniTest::Mock.new
|
128
|
-
parser.expect(:headers, {
|
129
|
-
'Content-Type' => 'text/xml',
|
130
|
-
'Host' => 'wonderland.lit',
|
131
|
-
'Origin' => 'remote.wonderland.lit',
|
132
|
-
'Access-Control-Request-Headers' => 'Content-Type, Origin'})
|
133
|
-
parser.expect(:http_method, 'OPTIONS')
|
134
|
-
parser.expect(:request_path, '/xmpp')
|
135
|
-
parser.expect(:request_url, '/xmpp')
|
136
|
-
parser.expect(:query_string, '')
|
137
|
-
|
138
|
-
request = Vines::Stream::Http::Request.new(@stream, parser, '')
|
139
|
-
|
140
|
-
headers = [
|
141
|
-
"HTTP/1.1 200 OK",
|
142
|
-
"Content-Length: 0",
|
143
|
-
"Access-Control-Allow-Origin: *",
|
144
|
-
"Access-Control-Allow-Methods: POST, GET, OPTIONS",
|
145
|
-
"Access-Control-Allow-Headers: Content-Type, Origin",
|
146
|
-
"Access-Control-Max-Age: 2592000"
|
147
|
-
].join("\r\n")
|
148
|
-
|
149
|
-
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
150
|
-
request.reply_to_options
|
151
|
-
assert @stream.verify
|
152
|
-
assert parser.verify
|
153
|
-
end
|
173
|
+
def reply_with_cookie(cookie)
|
174
|
+
config = Vines::Config.new do
|
175
|
+
host 'wonderland.lit' do
|
176
|
+
storage(:fs) { dir Dir.tmpdir }
|
177
|
+
end
|
178
|
+
http '0.0.0.0', 5280 do
|
179
|
+
vroute cookie
|
180
|
+
end
|
181
|
+
end
|
154
182
|
|
155
|
-
def test_reply
|
156
183
|
parser = MiniTest::Mock.new
|
157
184
|
parser.expect(:headers, {
|
158
185
|
'Content-Type' => 'text/xml',
|
@@ -170,10 +197,13 @@ class RequestTest < MiniTest::Unit::TestCase
|
|
170
197
|
"HTTP/1.1 200 OK",
|
171
198
|
"Access-Control-Allow-Origin: *",
|
172
199
|
"Content-Type: application/xml",
|
173
|
-
"Content-Length: 24"
|
174
|
-
]
|
200
|
+
"Content-Length: 24",
|
201
|
+
]
|
202
|
+
headers << "Set-Cookie: vroute=#{cookie}; path=/; HttpOnly" unless cookie.empty?
|
203
|
+
headers = headers.join("\r\n")
|
175
204
|
|
176
205
|
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n#{message}"])
|
206
|
+
@stream.expect(:config, config)
|
177
207
|
request.reply(message, 'application/xml')
|
178
208
|
assert @stream.verify
|
179
209
|
assert parser.verify
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vines
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-03-
|
12
|
+
date: 2012-03-23 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &70138792234080 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.2.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70138792234080
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: bcrypt-ruby
|
27
|
-
requirement: &
|
27
|
+
requirement: &70138792233060 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.0.1
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70138792233060
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: em-http-request
|
38
|
-
requirement: &
|
38
|
+
requirement: &70138792232280 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 1.0.1
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70138792232280
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: em-hiredis
|
49
|
-
requirement: &
|
49
|
+
requirement: &70138792231120 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.1.0
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70138792231120
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: eventmachine
|
60
|
-
requirement: &
|
60
|
+
requirement: &70138792229940 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 0.12.10
|
66
66
|
type: :runtime
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70138792229940
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: http_parser.rb
|
71
|
-
requirement: &
|
71
|
+
requirement: &70138792228960 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 0.5.3
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70138792228960
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: mongo
|
82
|
-
requirement: &
|
82
|
+
requirement: &70138792228000 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: 1.5.2
|
88
88
|
type: :runtime
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70138792228000
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: bson_ext
|
93
|
-
requirement: &
|
93
|
+
requirement: &70138792227000 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ~>
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: 1.5.2
|
99
99
|
type: :runtime
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70138792227000
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: net-ldap
|
104
|
-
requirement: &
|
104
|
+
requirement: &70138792226000 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ~>
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: 0.2.2
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *70138792226000
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: nokogiri
|
115
|
-
requirement: &
|
115
|
+
requirement: &70138792204220 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ~>
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: 1.4.7
|
121
121
|
type: :runtime
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *70138792204220
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: minitest
|
126
|
-
requirement: &
|
126
|
+
requirement: &70138792203520 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ~>
|
@@ -131,10 +131,10 @@ dependencies:
|
|
131
131
|
version: 2.11.2
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *70138792203520
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: coffee-script
|
137
|
-
requirement: &
|
137
|
+
requirement: &70138792202860 !ruby/object:Gem::Requirement
|
138
138
|
none: false
|
139
139
|
requirements:
|
140
140
|
- - ~>
|
@@ -142,10 +142,10 @@ dependencies:
|
|
142
142
|
version: 2.2.0
|
143
143
|
type: :development
|
144
144
|
prerelease: false
|
145
|
-
version_requirements: *
|
145
|
+
version_requirements: *70138792202860
|
146
146
|
- !ruby/object:Gem::Dependency
|
147
147
|
name: coffee-script-source
|
148
|
-
requirement: &
|
148
|
+
requirement: &70138792202020 !ruby/object:Gem::Requirement
|
149
149
|
none: false
|
150
150
|
requirements:
|
151
151
|
- - ~>
|
@@ -153,10 +153,10 @@ dependencies:
|
|
153
153
|
version: 1.2.0
|
154
154
|
type: :development
|
155
155
|
prerelease: false
|
156
|
-
version_requirements: *
|
156
|
+
version_requirements: *70138792202020
|
157
157
|
- !ruby/object:Gem::Dependency
|
158
158
|
name: uglifier
|
159
|
-
requirement: &
|
159
|
+
requirement: &70138792201220 !ruby/object:Gem::Requirement
|
160
160
|
none: false
|
161
161
|
requirements:
|
162
162
|
- - ~>
|
@@ -164,10 +164,10 @@ dependencies:
|
|
164
164
|
version: 1.2.3
|
165
165
|
type: :development
|
166
166
|
prerelease: false
|
167
|
-
version_requirements: *
|
167
|
+
version_requirements: *70138792201220
|
168
168
|
- !ruby/object:Gem::Dependency
|
169
169
|
name: rake
|
170
|
-
requirement: &
|
170
|
+
requirement: &70138792200500 !ruby/object:Gem::Requirement
|
171
171
|
none: false
|
172
172
|
requirements:
|
173
173
|
- - ! '>='
|
@@ -175,10 +175,10 @@ dependencies:
|
|
175
175
|
version: '0'
|
176
176
|
type: :development
|
177
177
|
prerelease: false
|
178
|
-
version_requirements: *
|
178
|
+
version_requirements: *70138792200500
|
179
179
|
- !ruby/object:Gem::Dependency
|
180
180
|
name: sqlite3
|
181
|
-
requirement: &
|
181
|
+
requirement: &70138792199520 !ruby/object:Gem::Requirement
|
182
182
|
none: false
|
183
183
|
requirements:
|
184
184
|
- - ! '>='
|
@@ -186,7 +186,7 @@ dependencies:
|
|
186
186
|
version: '0'
|
187
187
|
type: :development
|
188
188
|
prerelease: false
|
189
|
-
version_requirements: *
|
189
|
+
version_requirements: *70138792199520
|
190
190
|
description: ! 'Vines is an XMPP chat server that supports thousands of
|
191
191
|
|
192
192
|
simultaneous connections by using EventMachine for asynchronous IO. User data
|