vines 0.4.9 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|