mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 09:10:40 -05:00
Refactor WebSockets (#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
This commit is contained in:
parent
41bdd096f0
commit
385f866a54
4 changed files with 87 additions and 73 deletions
31
ws/mod.ts
31
ws/mod.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,7 +63,7 @@ export class SocketClosedError extends Error {}
|
|||
|
||||
export type WebSocketFrame = {
|
||||
isLastFrame: boolean;
|
||||
opcode: number;
|
||||
opcode: OpCode;
|
||||
mask?: Uint8Array;
|
||||
payload: Uint8Array;
|
||||
};
|
||||
|
@ -65,8 +71,8 @@ export type WebSocketFrame = {
|
|||
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) {
|
||||
|
|
52
ws/sha1.ts
52
ws/sha1.ts
|
@ -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");
|
||||
});
|
||||
|
|
66
ws/test.ts
66
ws/test.ts
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue