Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct the client Websocket handshake #1508

Merged
merged 3 commits into from
Jun 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion source/vibe/http/client.d
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,8 @@ final class HTTPClientResponse : HTTPResponse {
enforce(statusCode == HTTPStatus.switchingProtocols, "Server did not send a 101 - Switching Protocols response");
string *resNewProto = "Upgrade" in headers;
enforce(resNewProto, "Server did not send an Upgrade header");
enforce(!new_protocol.length || *resNewProto == new_protocol, "Expected Upgrade: " ~ new_protocol ~", received Upgrade: " ~ *resNewProto);
enforce(!new_protocol.length || !icmp(*resNewProto, new_protocol),
"Expected Upgrade: " ~ new_protocol ~", received Upgrade: " ~ *resNewProto);
auto stream = new ConnectionProxyStream(m_client.m_stream, m_client.m_conn);
m_client.m_responding = false;
m_client = null;
Expand Down
60 changes: 30 additions & 30 deletions source/vibe/http/websockets.d
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ WebSocket connectWebSocket(URL url, HTTPClientSettings settings = defaultSetting
s_connections.put(tuple(ckey, pool));
}

auto challengeKey = generateChallengeKey();
auto rng = new SystemRNG;
auto challengeKey = generateChallengeKey(rng);
auto answerKey = computeAcceptKey(challengeKey);
auto cl = pool.lockConnection();
auto res = cl.request((scope req){
Expand All @@ -124,7 +125,7 @@ WebSocket connectWebSocket(URL url, HTTPClientSettings settings = defaultSetting
enforce(key !is null, "Response is missing the Sec-WebSocket-Accept header.");
enforce(*key == answerKey, "Response has wrong accept key");
auto conn = res.switchProtocol("websocket");
auto ws = new WebSocket(conn, null, false);
auto ws = new WebSocket(conn, null, rng);
return ws;
}

Expand All @@ -134,7 +135,8 @@ void connectWebSocket(URL url, scope void delegate(scope WebSocket sock) del, HT
bool use_tls = (url.schema == "wss") ? true : false;
url.schema = use_tls ? "https" : "http";

auto challengeKey = generateChallengeKey();
scope rng = new SystemRNG;
auto challengeKey = generateChallengeKey(rng);
auto answerKey = computeAcceptKey(challengeKey);

requestHTTP(url,
Expand All @@ -151,7 +153,7 @@ void connectWebSocket(URL url, scope void delegate(scope WebSocket sock) del, HT
enforce(key !is null, "Response is missing the Sec-WebSocket-Accept header.");
enforce(*key == answerKey, "Response has wrong accept key");
res.switchProtocol("websocket", (conn) {
scope ws = new WebSocket(conn, null, false);
scope ws = new WebSocket(conn, null, rng);
del(ws);
});
}
Expand Down Expand Up @@ -199,7 +201,7 @@ void handleWebSocket(scope WebSocketHandshakeDelegate on_handshake, scope HTTPSe
res.headers["Connection"] = "Upgrade";
ConnectionStream conn = res.switchProtocol("websocket");

WebSocket socket = new WebSocket(conn, req);
WebSocket socket = new WebSocket(conn, req, null);
try {
on_handshake(socket);
} catch (Exception e) {
Expand Down Expand Up @@ -253,7 +255,7 @@ HTTPServerRequestDelegateS handleWebSockets(WebSocketHandshakeDelegate on_handsh
res.headers["Connection"] = "Upgrade";
res.switchProtocol("websocket", (scope conn) {
// TODO: put back 'scope' once it is actually enforced by DMD
/*scope*/ auto socket = new WebSocket(conn, req);
/*scope*/ auto socket = new WebSocket(conn, req, null);
try on_handshake(socket);
catch (Exception e) {
logDiagnostic("WebSocket handler failed: %s", e.msg);
Expand Down Expand Up @@ -306,17 +308,24 @@ final class WebSocket {
uint m_lastPingIndex;
bool m_pongReceived;
bool m_pongSkipped;
bool m_isServer = true;
SystemRNG m_rng;
}

this(ConnectionStream conn, in HTTPServerRequest request, bool is_server = true)
/**
* Private constructor, called from `connectWebSocket`.
*
* Params:
* conn = Underlying connection string
* request = HTTP request used to establish the connection
* rng = Source of entropy to use. If null, assume we're a server socket
*/
private this(ConnectionStream conn, in HTTPServerRequest request,
SystemRNG rng)
{
m_conn = conn;
m_request = request;
m_isServer = is_server;
assert(m_conn);
m_rng = new SystemRNG;
m_rng = rng;
m_writeMutex = new InterruptibleTaskMutex;
m_readMutex = new InterruptibleTaskMutex;
m_readCondition = new InterruptibleTaskCondition(m_readMutex);
Expand Down Expand Up @@ -413,7 +422,7 @@ final class WebSocket {
{
m_writeMutex.performLocked!({
enforceEx!WebSocketException(!m_sentCloseFrame, "WebSocket connection already actively closed.");
scope message = new OutgoingWebSocketMessage(m_conn, frameOpcode, m_rng, m_isServer);
scope message = new OutgoingWebSocketMessage(m_conn, frameOpcode, m_rng);
scope(exit) message.finalize();
sender(message);
});
Expand All @@ -435,7 +444,6 @@ final class WebSocket {
m_writeMutex.performLocked!({
m_sentCloseFrame = true;
Frame frame;
frame.isServer = m_isServer;
frame.opcode = FrameOpcode.close;
if(code != 0)
frame.payload = std.bitmanip.nativeToBigEndian(code) ~ cast(const ubyte[])reason;
Expand Down Expand Up @@ -549,7 +557,6 @@ final class WebSocket {
m_writeMutex.performLocked!({
m_pongReceived = false;
Frame ping;
ping.isServer = m_isServer;
ping.opcode = FrameOpcode.ping;
ping.fin = true;
ping.payload = nativeToLittleEndian(++m_lastPingIndex);
Expand All @@ -569,16 +576,13 @@ final class OutgoingWebSocketMessage : OutputStream {
FrameOpcode m_frameOpcode;
Appender!(ubyte[]) m_buffer;
bool m_finalized = false;
bool m_isServer;
}

this( Stream conn, FrameOpcode frameOpcode, SystemRNG rng, bool is_server = true )
this( Stream conn, FrameOpcode frameOpcode, SystemRNG rng )
{
assert(conn !is null);
assert(rng !is null);
m_conn = conn;
m_frameOpcode = frameOpcode;
m_isServer = is_server;
m_rng = rng;
}

Expand All @@ -592,7 +596,6 @@ final class OutgoingWebSocketMessage : OutputStream {
{
assert(!m_finalized);
Frame frame;
frame.isServer = m_isServer;
frame.opcode = m_frameOpcode;
frame.fin = false;
frame.payload = m_buffer.data;
Expand All @@ -607,7 +610,6 @@ final class OutgoingWebSocketMessage : OutputStream {
m_finalized = true;

Frame frame;
frame.isServer = m_isServer;
frame.fin = true;
frame.opcode = m_frameOpcode;
frame.payload = m_buffer.data;
Expand All @@ -620,7 +622,6 @@ final class OutgoingWebSocketMessage : OutputStream {
{
writeDefault(stream, nbytes);
}

}


Expand All @@ -637,7 +638,6 @@ final class IncomingWebSocketMessage : InputStream {
this(Stream conn, SystemRNG rng)
{
assert(conn !is null);
assert(rng !is null);
m_conn = conn;
m_rng = rng;
readFrame();
Expand Down Expand Up @@ -721,8 +721,6 @@ struct Frame {
bool fin;
FrameOpcode opcode;
ubyte[] payload;
bool isServer = true;


void writeFrame(OutputStream stream, SystemRNG sys_rng)
{
Expand All @@ -736,7 +734,7 @@ struct Frame {
rng.put(firstByte);

auto b1 = 0;
if (!isServer) {
if (sys_rng) {
b1 = 0x80;
}

Expand All @@ -752,7 +750,7 @@ struct Frame {
rng.put(std.bitmanip.nativeToBigEndian(payload.length));
}

if (!isServer) {
if (sys_rng) {
sys_rng.read(buff);
rng.put(buff);
for (size_t i = 0; i < payload.length; i++) {
Expand Down Expand Up @@ -806,12 +804,14 @@ struct Frame {
}
}

private string generateChallengeKey()
/**
* Generate a challenge key for the protocol upgrade phase.
*/
private string generateChallengeKey(scope SystemRNG rng)
{
auto uuid = randomUUID().toString();
immutable(ubyte)[] b = uuid.representation;
auto result = Base64.encode(b);
return to!(string)(result);
ubyte[16] buffer;
rng.read(buffer);
return Base64.encode(buffer);
}

private string computeAcceptKey(string challengekey)
Expand Down