vines 0.4.9 → 0.4.10
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 +4 -4
- data/README.md +1 -1
- data/lib/vines.rb +1 -0
- data/lib/vines/cluster/subscriber.rb +27 -2
- data/lib/vines/storage.rb +157 -80
- data/lib/vines/store.rb +58 -15
- data/lib/vines/stream.rb +71 -13
- data/lib/vines/stream/client.rb +10 -6
- data/lib/vines/stream/component.rb +3 -3
- data/lib/vines/stream/http.rb +35 -9
- data/lib/vines/stream/http/request.rb +26 -5
- data/lib/vines/stream/http/session.rb +8 -0
- data/lib/vines/stream/server.rb +13 -7
- data/lib/vines/version.rb +1 -1
- data/test/store_test.rb +80 -48
- data/test/stream/http/request_test.rb +45 -60
- data/vines.gemspec +6 -6
- metadata +12 -13
@@ -25,13 +25,22 @@ module Vines
|
|
25
25
|
|
26
26
|
attr_reader :stream, :body, :headers, :method, :path, :url, :query
|
27
27
|
|
28
|
+
# Create a new request parsed from an HTTP client connection. We'll try
|
29
|
+
# to keep this request open until there are stanzas available to send
|
30
|
+
# as a response.
|
31
|
+
#
|
32
|
+
# stream - The Stream::Http client connection that received the request.
|
33
|
+
# parser - The Http::Parser that parsed the HTTP request.
|
34
|
+
# body - The String request body.
|
28
35
|
def initialize(stream, parser, body)
|
29
|
-
|
36
|
+
uri = URI(parser.request_url)
|
37
|
+
@stream = stream
|
38
|
+
@body = body
|
30
39
|
@headers = parser.headers
|
31
40
|
@method = parser.http_method
|
32
|
-
@path = parser.request_path
|
33
41
|
@url = parser.request_url
|
34
|
-
@
|
42
|
+
@path = uri.path
|
43
|
+
@query = uri.query
|
35
44
|
@received = Time.now
|
36
45
|
end
|
37
46
|
|
@@ -44,10 +53,12 @@ module Vines
|
|
44
53
|
# directory. Take care to prevent directory traversal attacks with paths
|
45
54
|
# like ../../../etc/passwd. Use the If-Modified-Since request header
|
46
55
|
# to implement caching.
|
56
|
+
#
|
57
|
+
# Returns nothing.
|
47
58
|
def reply_with_file(dir)
|
48
59
|
path = File.expand_path(File.join(dir, @path))
|
49
60
|
|
50
|
-
#
|
61
|
+
# Redirect requests missing a slash so relative links work.
|
51
62
|
if File.directory?(path) && !@path.end_with?('/')
|
52
63
|
send_status(301, MOVED, "Location: #{redirect_uri}")
|
53
64
|
return
|
@@ -69,6 +80,8 @@ module Vines
|
|
69
80
|
|
70
81
|
# Send an HTTP 200 OK response wrapping the XMPP node content back
|
71
82
|
# to the client.
|
83
|
+
#
|
84
|
+
# Returns nothing.
|
72
85
|
def reply(node, content_type)
|
73
86
|
body = node.to_s
|
74
87
|
header = [
|
@@ -90,6 +103,8 @@ module Vines
|
|
90
103
|
# Send a 200 OK response, allowing any origin domain to connect to the
|
91
104
|
# server, in response to CORS preflight OPTIONS requests. This allows
|
92
105
|
# any web application using strophe.js to connect to our BOSH port.
|
106
|
+
#
|
107
|
+
# Returns nothing.
|
93
108
|
def reply_to_options
|
94
109
|
allow = @headers['Access-Control-Request-Headers']
|
95
110
|
headers = [
|
@@ -107,6 +122,8 @@ module Vines
|
|
107
122
|
# wasn't sent by the client, just return the relative path that
|
108
123
|
# was requested. The Location response header must contain the fully
|
109
124
|
# qualified URI, but most browsers will accept relative paths as well.
|
125
|
+
#
|
126
|
+
# Returns the String URL.
|
110
127
|
def redirect_uri
|
111
128
|
host = headers['Host']
|
112
129
|
uri = "#{path}/"
|
@@ -137,6 +154,8 @@ module Vines
|
|
137
154
|
# Stream the contents of the file to the client in a 200 OK response.
|
138
155
|
# Send a Last-Modified response header so clients can send us an
|
139
156
|
# If-Modified-Since request header for caching.
|
157
|
+
#
|
158
|
+
# Returns nothing.
|
140
159
|
def send_file(path, status=200, message='OK')
|
141
160
|
header = [
|
142
161
|
"HTTP/1.1 #{status} #{message}",
|
@@ -162,6 +181,8 @@ module Vines
|
|
162
181
|
# HTTP server. Reverse proxy servers (nginx/apache) can use this cookie
|
163
182
|
# to implement sticky sessions. Return nil if vroute was not set in
|
164
183
|
# config.rb and no cookie should be sent.
|
184
|
+
#
|
185
|
+
# Returns a String cookie value or nil if disabled.
|
165
186
|
def vroute_cookie
|
166
187
|
route = @stream.config[:http].vroute
|
167
188
|
route ? "Set-Cookie: vroute=#{route}; path=/; HttpOnly" : nil
|
@@ -169,4 +190,4 @@ module Vines
|
|
169
190
|
end
|
170
191
|
end
|
171
192
|
end
|
172
|
-
end
|
193
|
+
end
|
@@ -73,6 +73,10 @@ module Vines
|
|
73
73
|
|
74
74
|
# Send an HTTP 200 OK response wrapping the XMPP node content back
|
75
75
|
# to the client.
|
76
|
+
#
|
77
|
+
# node - The XML::Node to send to the client.
|
78
|
+
#
|
79
|
+
# Returns nothing.
|
76
80
|
def reply(node)
|
77
81
|
if request = @requests.shift
|
78
82
|
request.reply(node, @content_type)
|
@@ -83,6 +87,10 @@ module Vines
|
|
83
87
|
# Write the XMPP node to the client stream after wrapping it in a BOSH
|
84
88
|
# body tag. If there's a waiting request, the node is written
|
85
89
|
# immediately. If not, it's queued until the next request arrives.
|
90
|
+
#
|
91
|
+
# data - The XML String or XML::Node to send in the next HTTP response.
|
92
|
+
#
|
93
|
+
# Returns nothing.
|
86
94
|
def write(node)
|
87
95
|
if request = @requests.shift
|
88
96
|
request.reply(wrap_body(node), @content_type)
|
data/lib/vines/stream/server.rb
CHANGED
@@ -125,23 +125,29 @@ module Vines
|
|
125
125
|
|
126
126
|
private
|
127
127
|
|
128
|
-
# The
|
128
|
+
# The `to` and `from` domain addresses set on the initial stream header
|
129
129
|
# must not change during stream restarts. This prevents a server from
|
130
130
|
# authenticating as one domain, then sending stanzas from users in a
|
131
131
|
# different domain.
|
132
|
+
#
|
133
|
+
# to - The String domain the other server thinks we are.
|
134
|
+
# from - The String domain the other server is asserting as its identity.
|
135
|
+
#
|
136
|
+
# Returns true if the other server is misbehaving and its connection
|
137
|
+
# should be closed.
|
132
138
|
def domain_change?(to, from)
|
133
139
|
to != @domain || from != @remote_domain
|
134
140
|
end
|
135
141
|
|
136
142
|
def send_stream_header
|
137
143
|
attrs = {
|
138
|
-
'xmlns'
|
144
|
+
'xmlns' => NAMESPACES[:server],
|
139
145
|
'xmlns:stream' => NAMESPACES[:stream],
|
140
|
-
'xml:lang'
|
141
|
-
'id'
|
142
|
-
'from'
|
143
|
-
'to'
|
144
|
-
'version'
|
146
|
+
'xml:lang' => 'en',
|
147
|
+
'id' => Kit.uuid,
|
148
|
+
'from' => @domain,
|
149
|
+
'to' => @remote_domain,
|
150
|
+
'version' => '1.0'
|
145
151
|
}
|
146
152
|
write "<stream:stream %s>" % attrs.to_a.map{|k,v| "#{k}='#{v}'"}.join(' ')
|
147
153
|
end
|
data/lib/vines/version.rb
CHANGED
data/test/store_test.rb
CHANGED
@@ -3,96 +3,113 @@
|
|
3
3
|
require 'test_helper'
|
4
4
|
|
5
5
|
describe Vines::Store do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
File.open("#{dir}/wonderland.lit.crt", 'w') {|f| f.write(domain) }
|
11
|
-
File.open("#{dir}/wonderland.lit.key", 'w') {|f| f.write(key) }
|
12
|
-
|
13
|
-
wildcard, key = certificate('*.wonderland.lit')
|
14
|
-
File.open("#{dir}/wildcard.lit.crt", 'w') {|f| f.write(wildcard) }
|
15
|
-
File.open("#{dir}/wildcard.lit.key", 'w') {|f| f.write(key) }
|
6
|
+
let(:dir) { 'conf/certs' }
|
7
|
+
let(:domain_pair) { certificate('wonderland.lit') }
|
8
|
+
let(:wildcard_pair) { certificate('*.wonderland.lit') }
|
9
|
+
subject { Vines::Store.new(dir) }
|
16
10
|
|
17
|
-
|
11
|
+
before do
|
12
|
+
@files =
|
13
|
+
save('wonderland.lit', domain_pair) +
|
14
|
+
save('wildcard.lit', wildcard_pair) +
|
15
|
+
save('duplicate.lit', domain_pair)
|
18
16
|
end
|
19
17
|
|
20
18
|
after do
|
21
|
-
|
22
|
-
name = "conf/certs/#{f}"
|
19
|
+
@files.each do |name|
|
23
20
|
File.delete(name) if File.exists?(name)
|
24
21
|
end
|
25
22
|
end
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
describe 'creating a store' do
|
25
|
+
it 'parses certificate files' do
|
26
|
+
refute subject.certs.empty?
|
27
|
+
assert_equal OpenSSL::X509::Certificate, subject.certs.first.class
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'ignores expired certificates' do
|
31
|
+
assert subject.certs.all? {|c| c.not_after > Time.new }
|
32
|
+
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
+
it 'does not raise an error for duplicate certificates' do
|
35
|
+
assert Vines::Store.new(dir)
|
36
|
+
end
|
34
37
|
end
|
35
38
|
|
36
39
|
describe 'files_for_domain' do
|
37
40
|
it 'handles invalid input' do
|
38
|
-
assert_nil
|
39
|
-
assert_nil
|
41
|
+
assert_nil subject.files_for_domain(nil)
|
42
|
+
assert_nil subject.files_for_domain('')
|
40
43
|
end
|
41
44
|
|
42
45
|
it 'finds files by name' do
|
43
|
-
refute_nil
|
44
|
-
cert, key =
|
46
|
+
refute_nil subject.files_for_domain('wonderland.lit')
|
47
|
+
cert, key = subject.files_for_domain('wonderland.lit')
|
45
48
|
assert_certificate_matches_key cert, key
|
46
49
|
assert_equal 'wonderland.lit.crt', File.basename(cert)
|
47
50
|
assert_equal 'wonderland.lit.key', File.basename(key)
|
48
51
|
end
|
49
52
|
|
50
53
|
it 'finds files for wildcard' do
|
51
|
-
refute_nil
|
52
|
-
cert, key =
|
54
|
+
refute_nil subject.files_for_domain('foo.wonderland.lit')
|
55
|
+
cert, key = subject.files_for_domain('foo.wonderland.lit')
|
53
56
|
assert_certificate_matches_key cert, key
|
54
57
|
assert_equal 'wildcard.lit.crt', File.basename(cert)
|
55
58
|
assert_equal 'wildcard.lit.key', File.basename(key)
|
56
59
|
end
|
57
60
|
end
|
58
61
|
|
62
|
+
describe 'trusted?' do
|
63
|
+
it 'does not trust malformed certificates' do
|
64
|
+
refute subject.trusted?('bogus')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'does not trust unsigned certificates' do
|
68
|
+
pair = certificate('something.lit')
|
69
|
+
refute subject.trusted?(pair.cert)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
59
73
|
describe 'domain?' do
|
60
74
|
it 'handles invalid input' do
|
61
|
-
|
62
|
-
refute
|
63
|
-
refute
|
64
|
-
refute
|
65
|
-
refute
|
66
|
-
assert
|
75
|
+
pair = certificate('wonderland.lit')
|
76
|
+
refute subject.domain?(nil, nil)
|
77
|
+
refute subject.domain?(pair.cert, nil)
|
78
|
+
refute subject.domain?(pair.cert, '')
|
79
|
+
refute subject.domain?(nil, '')
|
80
|
+
assert subject.domain?(pair.cert, 'wonderland.lit')
|
67
81
|
end
|
68
82
|
|
69
83
|
it 'verifies certificate subject domains' do
|
70
|
-
|
71
|
-
refute
|
72
|
-
refute
|
73
|
-
assert
|
84
|
+
pair = certificate('wonderland.lit')
|
85
|
+
refute subject.domain?(pair.cert, 'bogus')
|
86
|
+
refute subject.domain?(pair.cert, 'www.wonderland.lit')
|
87
|
+
assert subject.domain?(pair.cert, 'wonderland.lit')
|
74
88
|
end
|
75
89
|
|
76
90
|
it 'verifies certificate subject alt domains' do
|
77
|
-
|
78
|
-
refute
|
79
|
-
refute
|
80
|
-
assert
|
81
|
-
assert
|
91
|
+
pair = certificate('wonderland.lit', 'www.wonderland.lit')
|
92
|
+
refute subject.domain?(pair.cert, 'bogus')
|
93
|
+
refute subject.domain?(pair.cert, 'tea.wonderland.lit')
|
94
|
+
assert subject.domain?(pair.cert, 'www.wonderland.lit')
|
95
|
+
assert subject.domain?(pair.cert, 'wonderland.lit')
|
82
96
|
end
|
83
97
|
|
84
98
|
it 'verifies certificate wildcard domains' do
|
85
|
-
|
86
|
-
refute
|
87
|
-
refute
|
88
|
-
assert
|
89
|
-
assert
|
90
|
-
assert
|
99
|
+
pair = certificate('wonderland.lit', '*.wonderland.lit')
|
100
|
+
refute subject.domain?(pair.cert, 'bogus')
|
101
|
+
refute subject.domain?(pair.cert, 'one.two.wonderland.lit')
|
102
|
+
assert subject.domain?(pair.cert, 'tea.wonderland.lit')
|
103
|
+
assert subject.domain?(pair.cert, 'www.wonderland.lit')
|
104
|
+
assert subject.domain?(pair.cert, 'wonderland.lit')
|
91
105
|
end
|
92
106
|
end
|
93
107
|
|
94
108
|
private
|
95
109
|
|
110
|
+
# A public certificate + private key pair.
|
111
|
+
Pair = Struct.new(:cert, :key)
|
112
|
+
|
96
113
|
def assert_certificate_matches_key(cert, key)
|
97
114
|
refute_nil cert
|
98
115
|
refute_nil key
|
@@ -102,7 +119,7 @@ describe Vines::Store do
|
|
102
119
|
end
|
103
120
|
|
104
121
|
def certificate(domain, altname=nil)
|
105
|
-
#
|
122
|
+
# Use small key so tests are fast.
|
106
123
|
key = OpenSSL::PKey::RSA.generate(256)
|
107
124
|
|
108
125
|
name = OpenSSL::X509::Name.parse("/C=US/ST=Colorado/L=Denver/O=Test/CN=#{domain}")
|
@@ -125,6 +142,21 @@ describe Vines::Store do
|
|
125
142
|
].map {|k, v| factory.create_ext(k, v) }
|
126
143
|
end
|
127
144
|
|
128
|
-
|
145
|
+
Pair.new(cert.to_pem, key.to_pem)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Write the domain's certificate and private key files to the filesystem for
|
149
|
+
# the store to use.
|
150
|
+
#
|
151
|
+
# domain - The domain name String to use in the file name (e.g. wonderland.lit).
|
152
|
+
# pair - The Pair containing the public certificate and private key data.
|
153
|
+
#
|
154
|
+
# Returns a String Array of file names that were written.
|
155
|
+
def save(domain, pair)
|
156
|
+
crt = File.expand_path("#{domain}.crt", dir)
|
157
|
+
key = File.expand_path("#{domain}.key", dir)
|
158
|
+
File.open(crt, 'w') {|f| f.write(pair.cert) }
|
159
|
+
File.open(key, 'w') {|f| f.write(pair.key) }
|
160
|
+
[crt, key]
|
129
161
|
end
|
130
162
|
end
|
@@ -3,20 +3,13 @@
|
|
3
3
|
require 'test_helper'
|
4
4
|
|
5
5
|
describe Vines::Stream::Http::Request do
|
6
|
-
PASSWORD = File.expand_path('../passwords')
|
7
|
-
INDEX = File.expand_path('index.html')
|
6
|
+
PASSWORD = File.expand_path('../passwords').freeze
|
7
|
+
INDEX = File.expand_path('index.html').freeze
|
8
8
|
|
9
9
|
before do
|
10
10
|
File.open(PASSWORD, 'w') {|f| f.puts '/etc/passwd contents' }
|
11
11
|
File.open(INDEX, 'w') {|f| f.puts 'index.html contents' }
|
12
|
-
|
13
12
|
@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
13
|
end
|
21
14
|
|
22
15
|
after do
|
@@ -26,21 +19,26 @@ describe Vines::Stream::Http::Request do
|
|
26
19
|
|
27
20
|
describe 'initialize' do
|
28
21
|
it 'copies request info from parser' do
|
29
|
-
|
30
|
-
|
22
|
+
headers = ['Host: wonderland.lit', 'Content-Type: text/html']
|
23
|
+
parser = parser('GET', '/blogs/12?ok=true', headers)
|
24
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
25
|
+
|
26
|
+
assert_equal request.headers, {'Host' => 'wonderland.lit', 'Content-Type' => 'text/html'}
|
31
27
|
assert_equal request.method, 'GET'
|
32
28
|
assert_equal request.path, '/blogs/12'
|
33
29
|
assert_equal request.url, '/blogs/12?ok=true'
|
34
30
|
assert_equal request.query, 'ok=true'
|
35
31
|
assert_equal request.body, '<html></html>'
|
36
32
|
assert @stream.verify
|
37
|
-
assert @parser.verify
|
38
33
|
end
|
39
34
|
end
|
40
35
|
|
41
36
|
describe 'reply_with_file' do
|
42
37
|
it 'returns 404 file not found' do
|
43
|
-
|
38
|
+
headers = ['Host: wonderland.lit', 'Content-Type: text/html']
|
39
|
+
parser = parser('GET', '/blogs/12?ok=true', headers)
|
40
|
+
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
41
|
+
|
44
42
|
headers = [
|
45
43
|
"HTTP/1.1 404 Not Found",
|
46
44
|
"Content-Length: 0"
|
@@ -50,17 +48,11 @@ describe Vines::Stream::Http::Request do
|
|
50
48
|
|
51
49
|
request.reply_with_file(Dir.pwd)
|
52
50
|
assert @stream.verify
|
53
|
-
assert @parser.verify
|
54
51
|
end
|
55
52
|
|
56
53
|
it 'prevents directory traversal with 404 response' do
|
57
|
-
|
58
|
-
parser
|
59
|
-
parser.expect(:http_method, 'GET')
|
60
|
-
parser.expect(:request_path, '/../passwords')
|
61
|
-
parser.expect(:request_url, '/../passwords')
|
62
|
-
parser.expect(:query_string, '')
|
63
|
-
|
54
|
+
headers = ['Host: wonderland.lit', 'Content-Type: text/html']
|
55
|
+
parser = parser('GET', '/../passwords', headers)
|
64
56
|
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
65
57
|
|
66
58
|
headers = [
|
@@ -72,17 +64,11 @@ describe Vines::Stream::Http::Request do
|
|
72
64
|
|
73
65
|
request.reply_with_file(Dir.pwd)
|
74
66
|
assert @stream.verify
|
75
|
-
assert parser.verify
|
76
67
|
end
|
77
68
|
|
78
69
|
it 'serves index.html for directory request' do
|
79
|
-
|
80
|
-
parser
|
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
|
-
|
70
|
+
headers = ['Host: wonderland.lit', 'Content-Type: text/html']
|
71
|
+
parser = parser('GET', '/?ok=true', headers)
|
86
72
|
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
87
73
|
|
88
74
|
mtime = File.mtime(INDEX).utc.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
@@ -98,17 +84,11 @@ describe Vines::Stream::Http::Request do
|
|
98
84
|
|
99
85
|
request.reply_with_file(Dir.pwd)
|
100
86
|
assert @stream.verify
|
101
|
-
assert parser.verify
|
102
87
|
end
|
103
88
|
|
104
89
|
it 'redirects for missing trailing slash' do
|
105
|
-
|
106
|
-
parser
|
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
|
-
|
90
|
+
headers = ['Host: wonderland.lit', 'Content-Type: text/html']
|
91
|
+
parser = parser('GET', '/http?ok=true', headers)
|
112
92
|
request = Vines::Stream::Http::Request.new(@stream, parser, '<html></html>')
|
113
93
|
|
114
94
|
headers = [
|
@@ -121,23 +101,18 @@ describe Vines::Stream::Http::Request do
|
|
121
101
|
# so the /http url above will work
|
122
102
|
request.reply_with_file(File.expand_path('../../', __FILE__))
|
123
103
|
assert @stream.verify
|
124
|
-
assert parser.verify
|
125
104
|
end
|
126
105
|
end
|
127
106
|
|
128
107
|
describe 'reply_to_options' do
|
129
108
|
it 'returns cors headers' do
|
130
|
-
|
131
|
-
|
132
|
-
'
|
133
|
-
'
|
134
|
-
'
|
135
|
-
|
136
|
-
parser
|
137
|
-
parser.expect(:request_path, '/xmpp')
|
138
|
-
parser.expect(:request_url, '/xmpp')
|
139
|
-
parser.expect(:query_string, '')
|
140
|
-
|
109
|
+
headers = [
|
110
|
+
'Content-Type: text/xml',
|
111
|
+
'Host: wonderland.lit',
|
112
|
+
'Origin: remote.wonderland.lit',
|
113
|
+
'Access-Control-Request-Headers: Content-Type, Origin'
|
114
|
+
]
|
115
|
+
parser = parser('OPTIONS', '/xmpp', headers)
|
141
116
|
request = Vines::Stream::Http::Request.new(@stream, parser, '')
|
142
117
|
|
143
118
|
headers = [
|
@@ -152,7 +127,6 @@ describe Vines::Stream::Http::Request do
|
|
152
127
|
@stream.expect(:stream_write, nil, ["#{headers}\r\n\r\n"])
|
153
128
|
request.reply_to_options
|
154
129
|
assert @stream.verify
|
155
|
-
assert parser.verify
|
156
130
|
end
|
157
131
|
end
|
158
132
|
|
@@ -168,6 +142,21 @@ describe Vines::Stream::Http::Request do
|
|
168
142
|
|
169
143
|
private
|
170
144
|
|
145
|
+
# Create a parser that has completed one valid HTTP request.
|
146
|
+
#
|
147
|
+
# method - The HTTP method String (e.g. 'GET', 'POST').
|
148
|
+
# url - The request URL String (e.g. '/blogs/12?ok=true').
|
149
|
+
# headers - The optional Array of request headers.
|
150
|
+
#
|
151
|
+
# Returns an Http::Parser.
|
152
|
+
def parser(method, url, headers = [])
|
153
|
+
head = ["#{method} #{url} HTTP/1.1"].concat(headers).join("\r\n")
|
154
|
+
body = '<html></html>'
|
155
|
+
Http::Parser.new.tap do |parser|
|
156
|
+
parser << "%s\r\n\r\n%s" % [head, body]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
171
160
|
def reply_with_cookie(cookie)
|
172
161
|
config = Vines::Config.new do
|
173
162
|
host 'wonderland.lit' do
|
@@ -178,15 +167,12 @@ describe Vines::Stream::Http::Request do
|
|
178
167
|
end
|
179
168
|
end
|
180
169
|
|
181
|
-
|
182
|
-
|
183
|
-
'
|
184
|
-
'
|
185
|
-
|
186
|
-
parser
|
187
|
-
parser.expect(:request_path, '/xmpp')
|
188
|
-
parser.expect(:request_url, '/xmpp')
|
189
|
-
parser.expect(:query_string, '')
|
170
|
+
headers = [
|
171
|
+
'Content-Type: text/xml',
|
172
|
+
'Host: wonderland.lit',
|
173
|
+
'Origin: remote.wonderland.lit'
|
174
|
+
]
|
175
|
+
parser = parser('POST', '/xmpp', headers)
|
190
176
|
|
191
177
|
request = Vines::Stream::Http::Request.new(@stream, parser, '')
|
192
178
|
message = '<message>hello</message>'
|
@@ -204,6 +190,5 @@ describe Vines::Stream::Http::Request do
|
|
204
190
|
@stream.expect(:config, config)
|
205
191
|
request.reply(message, 'application/xml')
|
206
192
|
assert @stream.verify
|
207
|
-
assert parser.verify
|
208
193
|
end
|
209
194
|
end
|