1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 07:14:47 -05:00

fix(websocket): spec conformance & WPT (#11010)

This commit is contained in:
Leo K 2021-06-21 12:15:08 +02:00 committed by GitHub
parent 952caa79b3
commit afe89e8850
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 400 additions and 116 deletions

View file

@ -579,6 +579,10 @@
converters.ByteString,
);
converters["sequence<DOMString>"] = createSequenceConverter(
converters.DOMString,
);
function requiredArguments(length, required, opts = {}) {
if (length < required) {
const errMsg = `${

View file

@ -3,28 +3,41 @@
((window) => {
const core = window.Deno.core;
// provided by "deno_web"
const { URL } = window.__bootstrap.url;
const webidl = window.__bootstrap.webidl;
const { HTTP_TOKEN_CODE_POINT_RE } = window.__bootstrap.infra;
webidl.converters["sequence<DOMString> or DOMString"] = (V, opts) => {
// Union for (sequence<DOMString> or DOMString)
if (webidl.type(V) === "Object" && V !== null) {
if (V[Symbol.iterator] !== undefined) {
return webidl.converters["sequence<DOMString>"](V, opts);
}
}
return webidl.converters.DOMString(V, opts);
};
webidl.converters["WebSocketSend"] = (V, opts) => {
// Union for (Blob or ArrayBufferView or ArrayBuffer or USVString)
if (V instanceof Blob) {
return webidl.converters["Blob"](V, opts);
}
if (typeof V === "object") {
if (V instanceof ArrayBuffer || V instanceof SharedArrayBuffer) {
return webidl.converters["ArrayBuffer"](V, opts);
}
if (ArrayBuffer.isView(V)) {
return webidl.converters["ArrayBufferView"](V, opts);
}
}
return webidl.converters["USVString"](V, opts);
};
const CONNECTING = 0;
const OPEN = 1;
const CLOSING = 2;
const CLOSED = 3;
function requiredArguments(
name,
length,
required,
) {
if (length < required) {
const errMsg = `${name} requires at least ${required} argument${
required === 1 ? "" : "s"
}, but only ${length} present`;
throw new TypeError(errMsg);
}
}
/**
* Tries to close the resource (and ignores BadResource errors).
* @param {number} rid
@ -74,14 +87,104 @@
});
}
const _readyState = Symbol("[[readyState]]");
const _url = Symbol("[[url]]");
const _rid = Symbol("[[rid]]");
const _extensions = Symbol("[[extensions]]");
const _protocol = Symbol("[[protocol]]");
const _binaryType = Symbol("[[binaryType]]");
const _bufferedAmount = Symbol("[[bufferedAmount]]");
class WebSocket extends EventTarget {
#readyState = CONNECTING;
[_rid];
[_readyState] = CONNECTING;
get readyState() {
webidl.assertBranded(this, WebSocket);
return this[_readyState];
}
get CONNECTING() {
webidl.assertBranded(this, WebSocket);
return CONNECTING;
}
get OPEN() {
webidl.assertBranded(this, WebSocket);
return OPEN;
}
get CLOSING() {
webidl.assertBranded(this, WebSocket);
return CLOSING;
}
get CLOSED() {
webidl.assertBranded(this, WebSocket);
return CLOSED;
}
[_extensions] = "";
get extensions() {
webidl.assertBranded(this, WebSocket);
return this[_extensions];
}
[_protocol] = "";
get protocol() {
webidl.assertBranded(this, WebSocket);
return this[_protocol];
}
[_url] = "";
get url() {
webidl.assertBranded(this, WebSocket);
return this[_url];
}
[_binaryType] = "blob";
get binaryType() {
webidl.assertBranded(this, WebSocket);
return this[_binaryType];
}
set binaryType(value) {
webidl.assertBranded(this, WebSocket);
value = webidl.converters.DOMString(value, {
prefix: "Failed to set 'binaryType' on 'WebSocket'",
});
if (value === "blob" || value === "arraybuffer") {
this[_binaryType] = value;
}
}
[_bufferedAmount] = 0;
get bufferedAmount() {
webidl.assertBranded(this, WebSocket);
return this[_bufferedAmount];
}
constructor(url, protocols = []) {
super();
requiredArguments("WebSocket", arguments.length, 1);
this[webidl.brand] = webidl.brand;
const prefix = "Failed to construct 'WebSocket'";
webidl.requiredArguments(arguments.length, 1, {
prefix,
});
url = webidl.converters.USVString(url, {
prefix,
context: "Argument 1",
});
protocols = webidl.converters["sequence<DOMString> or DOMString"](
protocols,
{
prefix,
context: "Argument 2",
},
);
const wsURL = new URL(url);
let wsURL;
try {
wsURL = new URL(url);
} catch (e) {
throw new DOMException(e.message, "SyntaxError");
}
if (wsURL.protocol !== "ws:" && wsURL.protocol !== "wss:") {
throw new DOMException(
@ -97,16 +200,16 @@
);
}
this.#url = wsURL.href;
this[_url] = wsURL.href;
core.opSync("op_ws_check_permission", this.#url);
core.opSync("op_ws_check_permission", this[_url]);
if (protocols && typeof protocols === "string") {
if (typeof protocols === "string") {
protocols = [protocols];
}
if (
protocols.some((x) => protocols.indexOf(x) !== protocols.lastIndexOf(x))
protocols.length !== new Set(protocols.map((p) => p.toLowerCase())).size
) {
throw new DOMException(
"Can't supply multiple times the same protocol.",
@ -114,19 +217,28 @@
);
}
if (
protocols.some((protocol) => !HTTP_TOKEN_CODE_POINT_RE.test(protocol))
) {
throw new DOMException(
"Invalid protocol value.",
"SyntaxError",
);
}
core.opAsync("op_ws_create", {
url: wsURL.href,
protocols: protocols.join(", "),
}).then((create) => {
this.#rid = create.rid;
this.#extensions = create.extensions;
this.#protocol = create.protocol;
this[_rid] = create.rid;
this[_extensions] = create.extensions;
this[_protocol] = create.protocol;
if (this.#readyState === CLOSING) {
if (this[_readyState] === CLOSING) {
core.opAsync("op_ws_close", {
rid: this.#rid,
rid: this[_rid],
}).then(() => {
this.#readyState = CLOSED;
this[_readyState] = CLOSED;
const errEvent = new ErrorEvent("error");
errEvent.target = this;
@ -135,10 +247,10 @@
const event = new CloseEvent("close");
event.target = this;
this.dispatchEvent(event);
tryClose(this.#rid);
tryClose(this[_rid]);
});
} else {
this.#readyState = OPEN;
this[_readyState] = OPEN;
const event = new Event("open");
event.target = this;
this.dispatchEvent(event);
@ -146,7 +258,7 @@
this.#eventLoop();
}
}).catch((err) => {
this.#readyState = CLOSED;
this[_readyState] = CLOSED;
const errorEv = new ErrorEvent(
"error",
@ -161,67 +273,29 @@
});
}
get CONNECTING() {
return CONNECTING;
}
get OPEN() {
return OPEN;
}
get CLOSING() {
return CLOSING;
}
get CLOSED() {
return CLOSED;
}
get readyState() {
return this.#readyState;
}
#extensions = "";
#protocol = "";
#url = "";
#rid;
get extensions() {
return this.#extensions;
}
get protocol() {
return this.#protocol;
}
#binaryType = "blob";
get binaryType() {
return this.#binaryType;
}
set binaryType(value) {
if (value === "blob" || value === "arraybuffer") {
this.#binaryType = value;
}
}
#bufferedAmount = 0;
get bufferedAmount() {
return this.#bufferedAmount;
}
get url() {
return this.#url;
}
send(data) {
requiredArguments("WebSocket.send", arguments.length, 1);
webidl.assertBranded(this, WebSocket);
const prefix = "Failed to execute 'send' on 'WebSocket'";
if (this.#readyState != OPEN) {
throw Error("readyState not OPEN");
webidl.requiredArguments(arguments.length, 1, {
prefix,
});
data = webidl.converters.WebSocketSend(data, {
prefix,
context: "Argument 1",
});
if (this[_readyState] !== OPEN) {
throw new DOMException("readyState not OPEN", "InvalidStateError");
}
const sendTypedArray = (ta) => {
this.#bufferedAmount += ta.size;
this[_bufferedAmount] += ta.byteLength;
core.opAsync("op_ws_send", {
rid: this.#rid,
rid: this[_rid],
kind: "binary",
}, ta).then(() => {
this.#bufferedAmount -= ta.size;
this[_bufferedAmount] -= ta.byteLength;
});
};
@ -229,80 +303,94 @@
data.slice().arrayBuffer().then((ab) =>
sendTypedArray(new DataView(ab))
);
} else if (
data instanceof Int8Array || data instanceof Int16Array ||
data instanceof Int32Array || data instanceof Uint8Array ||
data instanceof Uint16Array || data instanceof Uint32Array ||
data instanceof Uint8ClampedArray || data instanceof Float32Array ||
data instanceof Float64Array || data instanceof DataView
) {
} else if (ArrayBuffer.isView(data)) {
sendTypedArray(data);
} else if (data instanceof ArrayBuffer) {
sendTypedArray(new DataView(data));
} else {
const string = String(data);
const d = core.encode(string);
this.#bufferedAmount += d.size;
this[_bufferedAmount] += d.byteLength;
core.opAsync("op_ws_send", {
rid: this.#rid,
rid: this[_rid],
kind: "text",
text: string,
}).then(() => {
this.#bufferedAmount -= d.size;
this[_bufferedAmount] -= d.byteLength;
});
}
}
close(code, reason) {
if (code && !(code === 1000 || (3000 <= code && code < 5000))) {
close(code = undefined, reason = undefined) {
webidl.assertBranded(this, WebSocket);
const prefix = "Failed to execute 'close' on 'WebSocket'";
if (code !== undefined) {
code = webidl.converters["unsigned short"](code, {
prefix,
clamp: true,
context: "Argument 1",
});
}
if (reason !== undefined) {
reason = webidl.converters.USVString(reason, {
prefix,
context: "Argument 2",
});
}
if (
code !== undefined && !(code === 1000 || (3000 <= code && code < 5000))
) {
throw new DOMException(
"The close code must be either 1000 or in the range of 3000 to 4999.",
"NotSupportedError",
"InvalidAccessError",
);
}
if (reason && core.encode(reason).byteLength > 123) {
if (reason !== undefined && core.encode(reason).byteLength > 123) {
throw new DOMException(
"The close reason may not be longer than 123 bytes.",
"SyntaxError",
);
}
if (this.#readyState === CONNECTING) {
this.#readyState = CLOSING;
} else if (this.#readyState === OPEN) {
this.#readyState = CLOSING;
if (this[_readyState] === CONNECTING) {
this[_readyState] = CLOSING;
} else if (this[_readyState] === OPEN) {
this[_readyState] = CLOSING;
core.opAsync("op_ws_close", {
rid: this.#rid,
rid: this[_rid],
code,
reason,
}).then(() => {
this.#readyState = CLOSED;
this[_readyState] = CLOSED;
const event = new CloseEvent("close", {
wasClean: true,
code,
code: code ?? 1005,
reason,
});
event.target = this;
this.dispatchEvent(event);
tryClose(this.#rid);
tryClose(this[_rid]);
});
}
}
async #eventLoop() {
while (this.#readyState === OPEN) {
while (this[_readyState] === OPEN) {
const { kind, value } = await core.opAsync(
"op_ws_next_event",
this.#rid,
this[_rid],
);
switch (kind) {
case "string": {
const event = new MessageEvent("message", {
data: value,
origin: this.#url,
origin: this[_url],
});
event.target = this;
this.dispatchEvent(event);
@ -319,7 +407,7 @@
const event = new MessageEvent("message", {
data,
origin: this.#url,
origin: this[_url],
});
event.target = this;
this.dispatchEvent(event);
@ -327,13 +415,13 @@
}
case "ping": {
core.opAsync("op_ws_send", {
rid: this.#rid,
rid: this[_rid],
kind: "pong",
});
break;
}
case "close": {
this.#readyState = CLOSED;
this[_readyState] = CLOSED;
const event = new CloseEvent("close", {
wasClean: true,
@ -342,11 +430,11 @@
});
event.target = this;
this.dispatchEvent(event);
tryClose(this.#rid);
tryClose(this[_rid]);
break;
}
case "error": {
this.#readyState = CLOSED;
this[_readyState] = CLOSED;
const errorEv = new ErrorEvent("error", {
message: value,
@ -357,7 +445,7 @@
const closeEv = new CloseEvent("close");
closeEv.target = this;
this.dispatchEvent(closeEv);
tryClose(this.#rid);
tryClose(this[_rid]);
break;
}
}
@ -385,5 +473,7 @@
defineEventHandler(WebSocket.prototype, "close");
defineEventHandler(WebSocket.prototype, "open");
webidl.configurePrototype(WebSocket);
window.__bootstrap.webSocket = { WebSocket };
})(this);

View file

@ -1,6 +1,11 @@
{
"check_subdomains": false,
"ssl": {
"type": "pregenerated"
"type": "pregenerated",
"pregenerated": {
"ca_cert_path": "../../tools/wpt/certs/cacert.pem",
"host_cert_path": "../../tools/wpt/certs/web-platform.test.pem",
"host_key_path": "../../tools/wpt/certs/web-platform.test.key"
}
}
}

View file

@ -1023,5 +1023,190 @@
"set-blob.any.html": true,
"set.any.html": true
}
},
"websockets": {
"Close-1000-reason.any.html": true,
"Close-1000-reason.any.html?wpt_flags=h2": false,
"Close-1000-reason.any.html?wss": true,
"Close-1000-verify-code.any.html": true,
"Close-1000-verify-code.any.html?wpt_flags=h2": false,
"Close-1000-verify-code.any.html?wss": true,
"Close-1000.any.html": true,
"Close-1000.any.html?wpt_flags=h2": false,
"Close-1000.any.html?wss": true,
"Close-1005-verify-code.any.html": true,
"Close-1005-verify-code.any.html?wpt_flags=h2": false,
"Close-1005-verify-code.any.html?wss": true,
"Close-1005.any.html": true,
"Close-1005.any.html?wpt_flags=h2": false,
"Close-1005.any.html?wss": true,
"Close-2999-reason.any.html": true,
"Close-2999-reason.any.html?wpt_flags=h2": false,
"Close-2999-reason.any.html?wss": true,
"Close-3000-reason.any.html": true,
"Close-3000-reason.any.html?wpt_flags=h2": false,
"Close-3000-reason.any.html?wss": true,
"Close-3000-verify-code.any.html": true,
"Close-3000-verify-code.any.html?wpt_flags=h2": false,
"Close-3000-verify-code.any.html?wss": true,
"Close-4999-reason.any.html": true,
"Close-4999-reason.any.html?wpt_flags=h2": false,
"Close-4999-reason.any.html?wss": true,
"Close-Reason-124Bytes.any.html": true,
"Close-Reason-124Bytes.any.html?wpt_flags=h2": false,
"Close-Reason-124Bytes.any.html?wss": true,
"Close-onlyReason.any.html": true,
"Close-onlyReason.any.html?wpt_flags=h2": false,
"Close-onlyReason.any.html?wss": true,
"Close-readyState-Closed.any.html": true,
"Close-readyState-Closed.any.html?wpt_flags=h2": false,
"Close-readyState-Closed.any.html?wss": true,
"Close-readyState-Closing.any.html": true,
"Close-readyState-Closing.any.html?wpt_flags=h2": false,
"Close-readyState-Closing.any.html?wss": true,
"Close-reason-unpaired-surrogates.any.html": true,
"Close-reason-unpaired-surrogates.any.html?wpt_flags=h2": false,
"Close-reason-unpaired-surrogates.any.html?wss": true,
"Close-server-initiated-close.any.html": true,
"Close-server-initiated-close.any.html?wpt_flags=h2": false,
"Close-server-initiated-close.any.html?wss": true,
"Close-undefined.any.html": true,
"Close-undefined.any.html?wpt_flags=h2": false,
"Close-undefined.any.html?wss": true,
"Create-asciiSep-protocol-string.any.html": true,
"Create-asciiSep-protocol-string.any.html?wpt_flags=h2": true,
"Create-asciiSep-protocol-string.any.html?wss": true,
"Create-blocked-port.any.html": true,
"Create-blocked-port.any.html?wpt_flags=h2": [
"Basic check"
],
"Create-blocked-port.any.html?wss": true,
"Create-extensions-empty.any.html": true,
"Create-extensions-empty.any.html?wpt_flags=h2": false,
"Create-extensions-empty.any.html?wss": true,
"Create-invalid-urls.any.html": true,
"Create-invalid-urls.any.html?wpt_flags=h2": true,
"Create-invalid-urls.any.html?wss": true,
"Create-non-absolute-url.any.html": true,
"Create-non-absolute-url.any.html?wpt_flags=h2": true,
"Create-non-absolute-url.any.html?wss": true,
"Create-nonAscii-protocol-string.any.html": true,
"Create-nonAscii-protocol-string.any.html?wpt_flags=h2": true,
"Create-nonAscii-protocol-string.any.html?wss": true,
"Create-on-worker-shutdown.any.html": false,
"Create-protocol-with-space.any.html": true,
"Create-protocol-with-space.any.html?wpt_flags=h2": true,
"Create-protocol-with-space.any.html?wss": true,
"Create-protocols-repeated-case-insensitive.any.html": true,
"Create-protocols-repeated-case-insensitive.any.html?wpt_flags=h2": true,
"Create-protocols-repeated-case-insensitive.any.html?wss": true,
"Create-protocols-repeated.any.html": true,
"Create-protocols-repeated.any.html?wpt_flags=h2": true,
"Create-protocols-repeated.any.html?wss": true,
"Create-url-with-space.any.html": true,
"Create-url-with-space.any.html?wpt_flags=h2": true,
"Create-url-with-space.any.html?wss": true,
"Create-valid-url-array-protocols.any.html": true,
"Create-valid-url-array-protocols.any.html?wpt_flags=h2": false,
"Create-valid-url-array-protocols.any.html?wss": true,
"Create-valid-url-binaryType-blob.any.html": true,
"Create-valid-url-binaryType-blob.any.html?wpt_flags=h2": false,
"Create-valid-url-binaryType-blob.any.html?wss": true,
"Create-valid-url-protocol-empty.any.html": true,
"Create-valid-url-protocol-empty.any.html?wpt_flags=h2": true,
"Create-valid-url-protocol-empty.any.html?wss": true,
"Create-valid-url-protocol-setCorrectly.any.html": true,
"Create-valid-url-protocol-setCorrectly.any.html?wpt_flags=h2": false,
"Create-valid-url-protocol-setCorrectly.any.html?wss": true,
"Create-valid-url-protocol-string.any.html": true,
"Create-valid-url-protocol-string.any.html?wpt_flags=h2": false,
"Create-valid-url-protocol-string.any.html?wss": true,
"Create-valid-url-protocol.any.html": true,
"Create-valid-url-protocol.any.html?wpt_flags=h2": false,
"Create-valid-url-protocol.any.html?wss": true,
"Create-valid-url.any.html": true,
"Create-valid-url.any.html?wpt_flags=h2": false,
"Create-valid-url.any.html?wss": true,
"Create-wrong-scheme.any.html": true,
"Create-wrong-scheme.any.html?wpt_flags=h2": true,
"Create-wrong-scheme.any.html?wss": true,
"Send-0byte-data.any.html": true,
"Send-0byte-data.any.html?wpt_flags=h2": false,
"Send-0byte-data.any.html?wss": true,
"Send-65K-data.any.html": true,
"Send-65K-data.any.html?wpt_flags=h2": false,
"Send-65K-data.any.html?wss": true,
"Send-before-open.any.html": true,
"Send-before-open.any.html?wpt_flags=h2": true,
"Send-before-open.any.html?wss": true,
"Send-binary-65K-arraybuffer.any.html": true,
"Send-binary-65K-arraybuffer.any.html?wpt_flags=h2": false,
"Send-binary-65K-arraybuffer.any.html?wss": true,
"Send-binary-arraybuffer.any.html": true,
"Send-binary-arraybuffer.any.html?wpt_flags=h2": false,
"Send-binary-arraybuffer.any.html?wss": true,
"Send-binary-arraybufferview-float32.any.html": true,
"Send-binary-arraybufferview-float32.any.html?wpt_flags=h2": false,
"Send-binary-arraybufferview-float32.any.html?wss": true,
"Send-binary-arraybufferview-float64.any.html": true,
"Send-binary-arraybufferview-float64.any.html?wpt_flags=h2": false,
"Send-binary-arraybufferview-float64.any.html?wss": true,
"Send-binary-arraybufferview-int16-offset.any.html": true,
"Send-binary-arraybufferview-int16-offset.any.html?wpt_flags=h2": false,
"Send-binary-arraybufferview-int16-offset.any.html?wss": true,
"Send-binary-arraybufferview-int32.any.html": true,
"Send-binary-arraybufferview-int32.any.html?wpt_flags=h2": false,
"Send-binary-arraybufferview-int32.any.html?wss": true,
"Send-binary-arraybufferview-int8.any.html": true,
"Send-binary-arraybufferview-int8.any.html?wpt_flags=h2": false,
"Send-binary-arraybufferview-int8.any.html?wss": true,
"Send-binary-arraybufferview-uint16-offset-length.any.html": true,
"Send-binary-arraybufferview-uint16-offset-length.any.html?wpt_flags=h2": false,
"Send-binary-arraybufferview-uint16-offset-length.any.html?wss": true,
"Send-binary-arraybufferview-uint32-offset.any.html": true,
"Send-binary-arraybufferview-uint32-offset.any.html?wpt_flags=h2": false,
"Send-binary-arraybufferview-uint32-offset.any.html?wss": true,
"Send-binary-arraybufferview-uint8-offset-length.any.html": true,
"Send-binary-arraybufferview-uint8-offset-length.any.html?wpt_flags=h2": false,
"Send-binary-arraybufferview-uint8-offset-length.any.html?wss": true,
"Send-binary-arraybufferview-uint8-offset.any.html": true,
"Send-binary-arraybufferview-uint8-offset.any.html?wpt_flags=h2": false,
"Send-binary-arraybufferview-uint8-offset.any.html?wss": true,
"Send-binary-blob.any.html": true,
"Send-binary-blob.any.html?wpt_flags=h2": false,
"Send-binary-blob.any.html?wss": true,
"Send-data.any.html": true,
"Send-data.any.html?wpt_flags=h2": false,
"Send-data.any.html?wss": true,
"Send-null.any.html": true,
"Send-null.any.html?wpt_flags=h2": false,
"Send-null.any.html?wss": true,
"Send-paired-surrogates.any.html": true,
"Send-paired-surrogates.any.html?wpt_flags=h2": false,
"Send-paired-surrogates.any.html?wss": true,
"Send-unicode-data.any.html": true,
"Send-unicode-data.any.html?wpt_flags=h2": false,
"Send-unicode-data.any.html?wss": true,
"Send-unpaired-surrogates.any.html": true,
"Send-unpaired-surrogates.any.html?wpt_flags=h2": false,
"Send-unpaired-surrogates.any.html?wss": true,
"basic-auth.any.html?wpt_flags=h2": false,
"basic-auth.any.html?wss": false,
"binaryType-wrong-value.any.html": true,
"binaryType-wrong-value.any.html?wpt_flags=h2": false,
"binaryType-wrong-value.any.html?wss": true,
"bufferedAmount-unchanged-by-sync-xhr.any.html": false,
"bufferedAmount-unchanged-by-sync-xhr.any.html?wpt_flags=h2": false,
"bufferedAmount-unchanged-by-sync-xhr.any.html?wss": false,
"close-invalid.any.html": true,
"close-invalid.any.html?wpt_flags=h2": true,
"close-invalid.any.html?wss": true,
"constructor.any.html": true,
"constructor.any.html?wpt_flags=h2": true,
"constructor.any.html?wss": true,
"eventhandlers.any.html": false,
"eventhandlers.any.html?wpt_flags=h2": false,
"eventhandlers.any.html?wss": false,
"referrer.any.html": true
}
}