Skip to content
Permalink
Browse files

Refactor WebSockets (denoland#173)

* Use assert.equal instead of deprecated assertEqual
* Replace let with const where possible
* Add WebSocketMessage type
* Use OpCode in WebSocketFrame
* Use const where possible in WS
* Separate sha1 tests, use const instead of let
  • Loading branch information...
sh7dm authored and ry committed Mar 4, 2019
1 parent 41bdd09 commit 385f866a545176261806d75184e2ef97fa0d5269
Showing with 87 additions and 73 deletions.
  1. +21 −10 ws/mod.ts
  2. +25 −27 ws/sha1.ts
  3. +8 −3 ws/sha1_test.ts
  4. +33 −33 ws/test.ts
@@ -43,10 +43,16 @@ export function isWebSocketPongEvent(a): a is WebSocketPongEvent {
return Array.isArray(a) && a[0] === "pong" && a[1] instanceof Uint8Array;
}

export type WebSocketMessage = string | Uint8Array;

// TODO move this to common/util module
export function append(a: Uint8Array, b: Uint8Array) {
if (a == null || !a.length) return b;
if (b == null || !b.length) return a;
if (a == null || !a.length) {
return b;
}
if (b == null || !b.length) {
return a;
}
const output = new Uint8Array(a.length + b.length);
output.set(a, 0);
output.set(b, a.length);
@@ -57,16 +63,16 @@ export class SocketClosedError extends Error {}

export type WebSocketFrame = {
isLastFrame: boolean;
opcode: number;
opcode: OpCode;
mask?: Uint8Array;
payload: Uint8Array;
};

export type WebSocket = {
readonly isClosed: boolean;
receive(): AsyncIterableIterator<WebSocketEvent>;
send(data: string | Uint8Array): Promise<void>;
ping(data?: string | Uint8Array): Promise<void>;
send(data: WebSocketMessage): Promise<void>;
ping(data?: WebSocketMessage): Promise<void>;
close(code: number, reason?: string): Promise<void>;
};

@@ -118,11 +124,12 @@ class WebSocketImpl implements WebSocket {
case OpCode.Pong:
yield ["pong", frame.payload] as WebSocketPongEvent;
break;
default:
}
}
}

async send(data: string | Uint8Array): Promise<void> {
async send(data: WebSocketMessage): Promise<void> {
if (this.isClosed) {
throw new SocketClosedError("socket has been closed");
}
@@ -141,7 +148,7 @@ class WebSocketImpl implements WebSocket {
);
}

async ping(data: string | Uint8Array): Promise<void> {
async ping(data: WebSocketMessage): Promise<void> {
const payload = typeof data === "string" ? this.encoder.encode(data) : data;
await writeFrame(
{
@@ -188,7 +195,10 @@ class WebSocketImpl implements WebSocket {
}

private ensureSocketClosed(): Error {
if (this.isClosed) return;
if (this.isClosed) {
return;
}

try {
this.conn.close();
} catch (e) {
@@ -203,7 +213,7 @@ export async function* receiveFrame(
conn: Conn
): AsyncIterableIterator<WebSocketFrame> {
let receiving = true;
let isLastFrame = true;
const isLastFrame = true;
const reader = new BufReader(conn);
while (receiving) {
const frame = await readFrame(reader);
@@ -241,12 +251,13 @@ export async function* receiveFrame(
case OpCode.Pong:
yield frame;
break;
default:
}
}
}

export async function writeFrame(frame: WebSocketFrame, writer: Writer) {
let payloadLength = frame.payload.byteLength;
const payloadLength = frame.payload.byteLength;
let header: Uint8Array;
const hasMask = frame.mask ? 0x80 : 0;
if (payloadLength < 126) {
@@ -61,12 +61,12 @@ export class Sha1 {
notString = false;
message = String(data);
}
let code,
index = 0,
i,
start = this._start,
length = message.length || 0,
blocks = this._blocks;
let code;
let index = 0;
let i;
const start = this._start;
const length = message.length || 0;
const blocks = this._blocks;

while (index < length) {
if (this._hashed) {
@@ -125,8 +125,8 @@ export class Sha1 {
return;
}
this._finalized = true;
let blocks = this._blocks,
i = this._lastByteIndex;
const blocks = this._blocks;
const i = this._lastByteIndex;
blocks[16] = this._block;
blocks[i >> 2] |= EXTRA[i & 3];
this._block = blocks[16];
@@ -143,15 +143,13 @@ export class Sha1 {
}

hash() {
let a = this._h0,
b = this._h1,
c = this._h2,
d = this._h3,
e = this._h4;
let f,
j,
t,
blocks = this._blocks;
let a = this._h0;
let b = this._h1;
let c = this._h2;
let d = this._h3;
let e = this._h4;
let f, j, t;
const blocks = this._blocks;

for (j = 16; j < 80; ++j) {
t = blocks[j - 3] ^ blocks[j - 8] ^ blocks[j - 14] ^ blocks[j - 16];
@@ -276,11 +274,11 @@ export class Sha1 {
hex() {
this.finalize();

let h0 = this._h0,
h1 = this._h1,
h2 = this._h2,
h3 = this._h3,
h4 = this._h4;
const h0 = this._h0;
const h1 = this._h1;
const h2 = this._h2;
const h3 = this._h3;
const h4 = this._h4;

return (
HEX_CHARS[(h0 >> 28) & 0x0f] +
@@ -333,11 +331,11 @@ export class Sha1 {
digest() {
this.finalize();

let h0 = this._h0,
h1 = this._h1,
h2 = this._h2,
h3 = this._h3,
h4 = this._h4;
const h0 = this._h0;
const h1 = this._h1;
const h2 = this._h2;
const h3 = this._h3;
const h4 = this._h4;

return [
(h0 >> 24) & 0xff,
@@ -3,16 +3,21 @@ import { assert, test } from "../testing/mod.ts";
import { Sha1 } from "./sha1.ts";

test(function testSha1() {
let sha1 = new Sha1();
const sha1 = new Sha1();
sha1.update("abcde");
assert.equal(sha1.toString(), "03de6c570bfe24bfc328ccd7ca46b76eadaf4334");
});

test(function testSha1WithArray() {
const data = Uint8Array.of(0x61, 0x62, 0x63, 0x64, 0x65);
sha1 = new Sha1();
const sha1 = new Sha1();
sha1.update(data);
assert.equal(sha1.toString(), "03de6c570bfe24bfc328ccd7ca46b76eadaf4334");
});

sha1 = new Sha1();
test(function testSha1WithBuffer() {
const data = Uint8Array.of(0x61, 0x62, 0x63, 0x64, 0x65);
const sha1 = new Sha1();
sha1.update(data.buffer);
assert.equal(sha1.toString(), "03de6c570bfe24bfc328ccd7ca46b76eadaf4334");
});
@@ -3,7 +3,7 @@ import "./sha1_test.ts";

const { Buffer } = Deno;
import { BufReader } from "../io/bufio.ts";
import { assert, assertEqual, test } from "../testing/mod.ts";
import { test, assert } from "../testing/mod.ts";
import {
acceptable,
createSecAccept,
@@ -18,10 +18,10 @@ test(async function testReadUnmaskedTextFrame() {
new Buffer(new Uint8Array([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]))
);
const frame = await readFrame(buf);
assertEqual(frame.opcode, OpCode.TextFrame);
assertEqual(frame.mask, undefined);
assertEqual(new Buffer(frame.payload).toString(), "Hello");
assertEqual(frame.isLastFrame, true);
assert.equal(frame.opcode, OpCode.TextFrame);
assert.equal(frame.mask, undefined);
assert.equal(new Buffer(frame.payload).toString(), "Hello");
assert.equal(frame.isLastFrame, true);
});

test(async function testReadMakedTextFrame() {
@@ -45,10 +45,10 @@ test(async function testReadMakedTextFrame() {
);
const frame = await readFrame(buf);
console.dir(frame);
assertEqual(frame.opcode, OpCode.TextFrame);
assert.equal(frame.opcode, OpCode.TextFrame);
unmask(frame.payload, frame.mask);
assertEqual(new Buffer(frame.payload).toString(), "Hello");
assertEqual(frame.isLastFrame, true);
assert.equal(new Buffer(frame.payload).toString(), "Hello");
assert.equal(frame.isLastFrame, true);
});

test(async function testReadUnmaskedSplittedTextFrames() {
@@ -59,15 +59,15 @@ test(async function testReadUnmaskedSplittedTextFrames() {
new Buffer(new Uint8Array([0x80, 0x02, 0x6c, 0x6f]))
);
const [f1, f2] = await Promise.all([readFrame(buf1), readFrame(buf2)]);
assertEqual(f1.isLastFrame, false);
assertEqual(f1.mask, undefined);
assertEqual(f1.opcode, OpCode.TextFrame);
assertEqual(new Buffer(f1.payload).toString(), "Hel");
assert.equal(f1.isLastFrame, false);
assert.equal(f1.mask, undefined);
assert.equal(f1.opcode, OpCode.TextFrame);
assert.equal(new Buffer(f1.payload).toString(), "Hel");

assertEqual(f2.isLastFrame, true);
assertEqual(f2.mask, undefined);
assertEqual(f2.opcode, OpCode.Continue);
assertEqual(new Buffer(f2.payload).toString(), "lo");
assert.equal(f2.isLastFrame, true);
assert.equal(f2.mask, undefined);
assert.equal(f2.opcode, OpCode.Continue);
assert.equal(new Buffer(f2.payload).toString(), "lo");
});

test(async function testReadUnmaksedPingPongFrame() {
@@ -76,8 +76,8 @@ test(async function testReadUnmaksedPingPongFrame() {
new Buffer(new Uint8Array([0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]))
);
const ping = await readFrame(buf);
assertEqual(ping.opcode, OpCode.Ping);
assertEqual(new Buffer(ping.payload).toString(), "Hello");
assert.equal(ping.opcode, OpCode.Ping);
assert.equal(new Buffer(ping.payload).toString(), "Hello");

const buf2 = new BufReader(
new Buffer(
@@ -97,42 +97,42 @@ test(async function testReadUnmaksedPingPongFrame() {
)
);
const pong = await readFrame(buf2);
assertEqual(pong.opcode, OpCode.Pong);
assert.equal(pong.opcode, OpCode.Pong);
assert(pong.mask !== undefined);
unmask(pong.payload, pong.mask);
assertEqual(new Buffer(pong.payload).toString(), "Hello");
assert.equal(new Buffer(pong.payload).toString(), "Hello");
});

test(async function testReadUnmaksedBigBinaryFrame() {
let a = [0x82, 0x7e, 0x01, 0x00];
const a = [0x82, 0x7e, 0x01, 0x00];
for (let i = 0; i < 256; i++) {
a.push(i);
}
const buf = new BufReader(new Buffer(new Uint8Array(a)));
const bin = await readFrame(buf);
assertEqual(bin.opcode, OpCode.BinaryFrame);
assertEqual(bin.isLastFrame, true);
assertEqual(bin.mask, undefined);
assertEqual(bin.payload.length, 256);
assert.equal(bin.opcode, OpCode.BinaryFrame);
assert.equal(bin.isLastFrame, true);
assert.equal(bin.mask, undefined);
assert.equal(bin.payload.length, 256);
});

test(async function testReadUnmaskedBigBigBinaryFrame() {
let a = [0x82, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00];
const a = [0x82, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00];
for (let i = 0; i < 0xffff; i++) {
a.push(i);
}
const buf = new BufReader(new Buffer(new Uint8Array(a)));
const bin = await readFrame(buf);
assertEqual(bin.opcode, OpCode.BinaryFrame);
assertEqual(bin.isLastFrame, true);
assertEqual(bin.mask, undefined);
assertEqual(bin.payload.length, 0xffff + 1);
assert.equal(bin.opcode, OpCode.BinaryFrame);
assert.equal(bin.isLastFrame, true);
assert.equal(bin.mask, undefined);
assert.equal(bin.payload.length, 0xffff + 1);
});

test(async function testCreateSecAccept() {
const nonce = "dGhlIHNhbXBsZSBub25jZQ==";
const d = createSecAccept(nonce);
assertEqual(d, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
assert.equal(d, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
});

test(function testAcceptable() {
@@ -142,7 +142,7 @@ test(function testAcceptable() {
"sec-websocket-key": "aaa"
})
});
assertEqual(ret, true);
assert.equal(ret, true);
});

const invalidHeaders = [
@@ -157,6 +157,6 @@ test(function testAcceptableInvalid() {
const ret = acceptable({
headers: new Headers(pat)
});
assertEqual(ret, false);
assert.equal(ret, false);
}
});

0 comments on commit 385f866

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.