235 lines
5.9 KiB
Ruby
235 lines
5.9 KiB
Ruby
|
# encoding: BINARY
|
||
|
|
||
|
require 'helper'
|
||
|
|
||
|
describe "WebSocket server draft76" do
|
||
|
include EM::SpecHelper
|
||
|
default_timeout 1
|
||
|
|
||
|
before :each do
|
||
|
@request = {
|
||
|
:port => 80,
|
||
|
:method => "GET",
|
||
|
:path => "/demo",
|
||
|
:headers => {
|
||
|
'Host' => 'example.com',
|
||
|
'Connection' => 'Upgrade',
|
||
|
'Sec-WebSocket-Key2' => '12998 5 Y3 1 .P00',
|
||
|
'Sec-WebSocket-Protocol' => 'sample',
|
||
|
'Upgrade' => 'WebSocket',
|
||
|
'Sec-WebSocket-Key1' => '4 @1 46546xW%0l 1 5',
|
||
|
'Origin' => 'http://example.com'
|
||
|
},
|
||
|
:body => '^n:ds[4U'
|
||
|
}
|
||
|
|
||
|
@response = {
|
||
|
:headers => {
|
||
|
"Upgrade" => "WebSocket",
|
||
|
"Connection" => "Upgrade",
|
||
|
"Sec-WebSocket-Location" => "ws://example.com/demo",
|
||
|
"Sec-WebSocket-Origin" => "http://example.com",
|
||
|
"Sec-WebSocket-Protocol" => "sample"
|
||
|
},
|
||
|
:body => "8jKS\'y:G*Co,Wxa-"
|
||
|
}
|
||
|
end
|
||
|
|
||
|
def start_client
|
||
|
client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
|
||
|
client.send_data(format_request(@request))
|
||
|
yield client if block_given?
|
||
|
return client
|
||
|
end
|
||
|
|
||
|
it_behaves_like "a websocket server" do
|
||
|
let(:version) { 76 }
|
||
|
end
|
||
|
|
||
|
it "should send back the correct handshake response" do
|
||
|
em {
|
||
|
start_server
|
||
|
|
||
|
start_client { |connection|
|
||
|
connection.onopen {
|
||
|
connection.handshake_response.lines.sort.
|
||
|
should == format_response(@response).lines.sort
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
it "should send closing frame back and close the connection after recieving closing frame" do
|
||
|
em {
|
||
|
start_server
|
||
|
|
||
|
connection = start_client
|
||
|
|
||
|
# Send closing frame after handshake complete
|
||
|
connection.onopen {
|
||
|
connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
|
||
|
}
|
||
|
|
||
|
# Check that this causes a termination string to be returned and the
|
||
|
# connection close
|
||
|
connection.onclose {
|
||
|
connection.packets[0].should ==
|
||
|
EM::WebSocket::Handler76::TERMINATE_STRING
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
it "should ignore any data received after the closing frame" do
|
||
|
em {
|
||
|
start_server { |ws|
|
||
|
# Fail if foobar message is received
|
||
|
ws.onmessage { |msg|
|
||
|
fail
|
||
|
}
|
||
|
}
|
||
|
|
||
|
connection = start_client
|
||
|
|
||
|
# Send closing frame after handshake complete, followed by another msg
|
||
|
connection.onopen {
|
||
|
connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
|
||
|
connection.send('foobar')
|
||
|
}
|
||
|
|
||
|
connection.onclose {
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
it "should accept null bytes within the frame after a line return" do
|
||
|
em {
|
||
|
start_server { |ws|
|
||
|
ws.onmessage { |msg|
|
||
|
msg.should == "\n\000"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
connection = start_client
|
||
|
|
||
|
# Send closing frame after handshake complete
|
||
|
connection.onopen {
|
||
|
connection.send_data("\000\n\000\377")
|
||
|
connection.send_data(EM::WebSocket::Handler76::TERMINATE_STRING)
|
||
|
}
|
||
|
|
||
|
connection.onclose {
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
it "should handle unreasonable frame lengths by calling onerror callback" do
|
||
|
em {
|
||
|
start_server { |server|
|
||
|
server.onerror { |error|
|
||
|
error.should be_an_instance_of EM::WebSocket::WSMessageTooBigError
|
||
|
error.message.should == "Frame length too long (1180591620717411303296 bytes)"
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
|
||
|
client = start_client
|
||
|
|
||
|
# This particular frame indicates a message length of
|
||
|
# 1180591620717411303296 bytes. Such a message would previously cause
|
||
|
# a "bignum too big to convert into `long'" error.
|
||
|
# However it is clearly unreasonable and should be rejected.
|
||
|
client.onopen {
|
||
|
client.send_data("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00")
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
it "should handle impossible frames by calling onerror callback" do
|
||
|
em {
|
||
|
start_server { |server|
|
||
|
server.onerror { |error|
|
||
|
error.should be_an_instance_of EM::WebSocket::WSProtocolError
|
||
|
error.message.should == "Invalid frame received"
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
|
||
|
client = start_client
|
||
|
|
||
|
client.onopen {
|
||
|
client.send_data("foobar") # Does not start with \x00 or \xff
|
||
|
}
|
||
|
}
|
||
|
end
|
||
|
|
||
|
it "should handle invalid http requests by raising HandshakeError passed to onerror callback" do
|
||
|
em {
|
||
|
start_server { |server|
|
||
|
server.onerror { |error|
|
||
|
error.should be_an_instance_of EM::WebSocket::HandshakeError
|
||
|
error.message.should == "Invalid HTTP header: Could not parse data entirely (1 != 29)"
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
|
||
|
client = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
|
||
|
client.send_data("This is not a HTTP header\r\n\r\n")
|
||
|
}
|
||
|
end
|
||
|
|
||
|
it "should handle handshake request split into two TCP packets" do
|
||
|
em {
|
||
|
start_server
|
||
|
|
||
|
# Create a fake client which sends draft 76 handshake
|
||
|
connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
|
||
|
data = format_request(@request)
|
||
|
# Sends first half of the request
|
||
|
connection.send_data(data[0...(data.length / 2)])
|
||
|
|
||
|
connection.onopen {
|
||
|
connection.handshake_response.lines.sort.
|
||
|
should == format_response(@response).lines.sort
|
||
|
done
|
||
|
}
|
||
|
|
||
|
EM.add_timer(0.1) do
|
||
|
# Sends second half of the request
|
||
|
connection.send_data(data[(data.length / 2)..-1])
|
||
|
end
|
||
|
}
|
||
|
end
|
||
|
|
||
|
it "should report that close codes are not supported" do
|
||
|
em {
|
||
|
start_server { |ws|
|
||
|
ws.onopen {
|
||
|
ws.supports_close_codes?.should == false
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
start_client
|
||
|
}
|
||
|
end
|
||
|
|
||
|
it "should call onclose when the server closes the connection [antiregression]" do
|
||
|
em {
|
||
|
start_server { |ws|
|
||
|
ws.onopen {
|
||
|
EM.add_timer(0.1) {
|
||
|
ws.close()
|
||
|
}
|
||
|
}
|
||
|
ws.onclose {
|
||
|
done
|
||
|
}
|
||
|
}
|
||
|
start_client
|
||
|
}
|
||
|
end
|
||
|
end
|