1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-25 08:39:09 -05:00

Refactorings & improvments for ws module (denoland/deno_std#141)

Original: 0d2429cdf2
This commit is contained in:
Max Graey 2019-01-21 17:31:57 +02:00 committed by Ryan Dahl
parent 88ecb4d167
commit 52fdf581a8

View file

@ -40,6 +40,16 @@ export function isWebSocketPongEvent(a): a is WebSocketPongEvent {
return Array.isArray(a) && a[0] === "pong" && a[1] instanceof Uint8Array; return Array.isArray(a) && a[0] === "pong" && a[1] instanceof 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;
const output = new Uint8Array(a.length + b.length);
output.set(a, 0);
output.set(b, a.length);
return output;
}
export class SocketClosedError extends Error {} export class SocketClosedError extends Error {}
export type WebSocketFrame = { export type WebSocketFrame = {
@ -189,10 +199,12 @@ export async function* receiveFrame(
conn: Conn conn: Conn
): AsyncIterableIterator<WebSocketFrame> { ): AsyncIterableIterator<WebSocketFrame> {
let receiving = true; let receiving = true;
let isLastFrame = true;
const reader = new BufReader(conn); const reader = new BufReader(conn);
while (receiving) { while (receiving) {
const frame = await readFrame(reader); const frame = await readFrame(reader);
switch (frame.opcode) { const { opcode, payload } = frame;
switch (opcode) {
case OpCodeTextFrame: case OpCodeTextFrame:
case OpCodeBinaryFrame: case OpCodeBinaryFrame:
case OpCodeContinue: case OpCodeContinue:
@ -201,9 +213,9 @@ export async function* receiveFrame(
case OpCodeClose: case OpCodeClose:
await writeFrame( await writeFrame(
{ {
isLastFrame: true, opcode,
opcode: OpCodeClose, payload,
payload: frame.payload isLastFrame
}, },
conn conn
); );
@ -214,9 +226,9 @@ export async function* receiveFrame(
case OpcodePing: case OpcodePing:
await writeFrame( await writeFrame(
{ {
isLastFrame: true, payload,
opcode: OpcodePong, isLastFrame,
payload: frame.payload opcode: OpcodePong
}, },
conn conn
); );
@ -232,41 +244,37 @@ export async function* receiveFrame(
export async function writeFrame(frame: WebSocketFrame, writer: Writer) { export async function writeFrame(frame: WebSocketFrame, writer: Writer) {
let payloadLength = frame.payload.byteLength; let payloadLength = frame.payload.byteLength;
let header: Uint8Array; let header: Uint8Array;
const hasMask = (frame.mask ? 1 : 0) << 7; const hasMask = frame.mask ? 0x80 : 0;
if (payloadLength < 126) { if (payloadLength < 126) {
header = new Uint8Array([ header = new Uint8Array([
(0b1000 << 4) | frame.opcode, 0x80 | frame.opcode,
hasMask | payloadLength hasMask | payloadLength
]); ]);
} else if (payloadLength < 0xffff) { } else if (payloadLength < 0xffff) {
header = new Uint8Array([ header = new Uint8Array([
(0b1000 << 4) | frame.opcode, 0x80 | frame.opcode,
hasMask | 0b01111110, hasMask | 0b01111110,
payloadLength >>> 8, payloadLength >>> 8,
payloadLength & 0x00ff payloadLength & 0x00ff
]); ]);
} else { } else {
header = new Uint8Array([ header = new Uint8Array([
(0b1000 << 4) | frame.opcode, 0x80 | frame.opcode,
hasMask | 0b01111111, hasMask | 0b01111111,
...sliceLongToBytes(payloadLength) ...sliceLongToBytes(payloadLength)
]); ]);
} }
if (frame.mask) { unmask(frame.payload, frame.mask);
unmask(frame.payload, frame.mask); const bytes = append(header, frame.payload);
}
const bytes = new Uint8Array(header.length + payloadLength);
bytes.set(header, 0);
bytes.set(frame.payload, header.length);
const w = new BufWriter(writer); const w = new BufWriter(writer);
await w.write(bytes); await w.write(bytes);
await w.flush(); await w.flush();
} }
export function unmask(payload: Uint8Array, mask: Uint8Array) { export function unmask(payload: Uint8Array, mask?: Uint8Array) {
if (mask) { if (mask) {
for (let i = 0; i < payload.length; i++) { for (let i = 0, len = payload.length; i < len; i++) {
payload[i] ^= mask[i % 4]; payload[i] ^= mask![i & 3];
} }
} }
} }
@ -302,11 +310,7 @@ export function createSecAccept(nonce: string) {
const sha1 = new Sha1(); const sha1 = new Sha1();
sha1.update(nonce + kGUID); sha1.update(nonce + kGUID);
const bytes = sha1.digest(); const bytes = sha1.digest();
const hash = bytes.reduce( return btoa(String.fromCharCode.apply(String, bytes));
(data, byte) => data + String.fromCharCode(byte),
""
);
return btoa(hash);
} }
export async function readFrame(buf: BufReader): Promise<WebSocketFrame> { export async function readFrame(buf: BufReader): Promise<WebSocketFrame> {