vines 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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 that you provide.
6
- LDAP authentication can be used so user names and passwords aren't stored in the chat
7
- database. SSL encryption is mandatory on all client and server connections.
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. The root attribute defines the web server's document root.
94
- # It will only serve files out of this directory. The bind attribute defines
95
- # the URL to which BOSH clients must POST their XMPP stanza requests.
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
@@ -192,7 +192,7 @@ module Vines
192
192
  def heartbeat
193
193
  @publisher.broadcast(:heartbeat)
194
194
  @sessions.expire
195
- rescue Exception => e
195
+ rescue => e
196
196
  log.error("Cluster session cleanup failed: #{e}")
197
197
  end
198
198
 
@@ -52,7 +52,7 @@ module Vines
52
52
  when ALL then to_all(doc)
53
53
  when @channel then to_node(doc)
54
54
  end
55
- rescue Exception => e
55
+ rescue => e
56
56
  log.error("Cluster subscription message failed: #{e}")
57
57
  end
58
58
 
@@ -24,7 +24,7 @@ module Vines
24
24
 
25
25
  begin
26
26
  user = storage.ldap.authenticate(jid, password)
27
- rescue Exception => e
27
+ rescue => e
28
28
  raise "LDAP connection failed: #{e.message}"
29
29
  end
30
30
 
@@ -15,7 +15,7 @@ module Vines
15
15
  end
16
16
  begin
17
17
  storage.create_schema
18
- rescue Exception => e
18
+ rescue => e
19
19
  raise "Schema creation failed: #{e.message}"
20
20
  end
21
21
  end
@@ -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 Exception => e
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 Exception => e
221
+ rescue => e
222
222
  log.error("LDAP authentication failed: #{e.message}")
223
223
  nil
224
224
  end
data/lib/vines/stream.rb CHANGED
@@ -210,7 +210,7 @@ module Vines
210
210
  else
211
211
  state.node(node)
212
212
  end
213
- rescue Exception => e
213
+ rescue => e
214
214
  error(e)
215
215
  end
216
216
 
@@ -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 Exception => e
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
- ].join("\r\n")
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
@@ -56,7 +56,7 @@ module Vines
56
56
  @sessions.each_value do |session|
57
57
  session.close if session.expired?
58
58
  end
59
- rescue Exception => e
59
+ rescue => e
60
60
  log.error("Expired session cleanup failed: #{e}")
61
61
  end
62
62
  end
@@ -42,7 +42,7 @@ module Vines
42
42
  rr = srv.shift
43
43
  opts = {to: to, from: from, srv: srv, callback: callback}
44
44
  EM.connect(rr.target.to_s, rr.port, Server, config, opts)
45
- rescue Exception => e
45
+ rescue => e
46
46
  connect(config, to, from, srv, callback)
47
47
  end
48
48
  end
data/lib/vines/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module Vines
4
- VERSION = '0.4.1'
4
+ VERSION = '0.4.2'
5
5
  end
@@ -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
- class RequestTest < MiniTest::Unit::TestCase
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
- def setup
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
- def teardown
24
+ after do
24
25
  File.delete(PASSWORD)
25
26
  File.delete(INDEX)
26
27
  end
27
28
 
28
- def test_copies_request_info_from_parser
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
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
- def test_reply_with_file_404
41
- request = Vines::Stream::Http::Request.new(@stream, @parser, '<html></html>')
42
-
43
- headers = [
44
- "HTTP/1.1 404 Not Found",
45
- "Content-Length: 0"
46
- ].join("\r\n")
47
-
48
- @stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
49
-
50
- request.reply_with_file(Dir.pwd)
51
- assert @stream.verify
52
- assert @parser.verify
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
- def test_reply_with_file_directory_traversal
56
- parser = MiniTest::Mock.new
57
- parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
58
- parser.expect(:http_method, 'GET')
59
- parser.expect(:request_path, '/../passwords')
60
- parser.expect(:request_url, '/../passwords')
61
- parser.expect(:query_string, '')
62
-
63
- request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
64
-
65
- headers = [
66
- "HTTP/1.1 404 Not Found",
67
- "Content-Length: 0"
68
- ].join("\r\n")
69
-
70
- @stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
71
-
72
- request.reply_with_file(Dir.pwd)
73
- assert @stream.verify
74
- assert parser.verify
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
- def test_reply_with_file_for_directory_serves_index_html
78
- parser = MiniTest::Mock.new
79
- parser.expect(:headers, {'Content-Type' => 'text/html', 'Host' => 'wonderland.lit'})
80
- parser.expect(:http_method, 'GET')
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
- request.reply_with_file(Dir.pwd)
99
- assert @stream.verify
100
- assert parser.verify
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
- def test_reply_with_file_redirects_for_missing_slash
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
- request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
112
-
113
- headers = [
114
- "HTTP/1.1 301 Moved Permanently",
115
- "Content-Length: 0",
116
- "Location: http://wonderland.lit/http/?ok=true"
117
- ].join("\r\n")
118
-
119
- @stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
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
- ].join("\r\n")
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.1
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-13 00:00:00.000000000Z
12
+ date: 2012-03-23 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
16
- requirement: &70170672828760 !ruby/object:Gem::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: *70170672828760
24
+ version_requirements: *70138792234080
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: bcrypt-ruby
27
- requirement: &70170672827940 !ruby/object:Gem::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: *70170672827940
35
+ version_requirements: *70138792233060
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: em-http-request
38
- requirement: &70170672827180 !ruby/object:Gem::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: *70170672827180
46
+ version_requirements: *70138792232280
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: em-hiredis
49
- requirement: &70170672826400 !ruby/object:Gem::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: *70170672826400
57
+ version_requirements: *70138792231120
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: eventmachine
60
- requirement: &70170672825440 !ruby/object:Gem::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: *70170672825440
68
+ version_requirements: *70138792229940
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: http_parser.rb
71
- requirement: &70170672824420 !ruby/object:Gem::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: *70170672824420
79
+ version_requirements: *70138792228960
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: mongo
82
- requirement: &70170672823540 !ruby/object:Gem::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: *70170672823540
90
+ version_requirements: *70138792228000
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: bson_ext
93
- requirement: &70170672821440 !ruby/object:Gem::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: *70170672821440
101
+ version_requirements: *70138792227000
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: net-ldap
104
- requirement: &70170672820560 !ruby/object:Gem::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: *70170672820560
112
+ version_requirements: *70138792226000
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: nokogiri
115
- requirement: &70170672819680 !ruby/object:Gem::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: *70170672819680
123
+ version_requirements: *70138792204220
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: minitest
126
- requirement: &70170672818920 !ruby/object:Gem::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: *70170672818920
134
+ version_requirements: *70138792203520
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: coffee-script
137
- requirement: &70170672818080 !ruby/object:Gem::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: *70170672818080
145
+ version_requirements: *70138792202860
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: coffee-script-source
148
- requirement: &70170672817220 !ruby/object:Gem::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: *70170672817220
156
+ version_requirements: *70138792202020
157
157
  - !ruby/object:Gem::Dependency
158
158
  name: uglifier
159
- requirement: &70170672816260 !ruby/object:Gem::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: *70170672816260
167
+ version_requirements: *70138792201220
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: rake
170
- requirement: &70170672815280 !ruby/object:Gem::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: *70170672815280
178
+ version_requirements: *70138792200500
179
179
  - !ruby/object:Gem::Dependency
180
180
  name: sqlite3
181
- requirement: &70170672814360 !ruby/object:Gem::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: *70170672814360
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