mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 04:52:26 -05:00
Compare commits
3 commits
922069fe6c
...
1a369707bc
Author | SHA1 | Date | |
---|---|---|---|
|
1a369707bc | ||
|
0d3d4f5466 | ||
|
ad87d6f4b0 |
24 changed files with 1997 additions and 25 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -2013,9 +2013,12 @@ dependencies = [
|
||||||
"quinn",
|
"quinn",
|
||||||
"rustls-tokio-stream",
|
"rustls-tokio-stream",
|
||||||
"serde",
|
"serde",
|
||||||
|
"sha2",
|
||||||
"socket2",
|
"socket2",
|
||||||
"thiserror 2.0.3",
|
"thiserror 2.0.3",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"url",
|
||||||
|
"web-transport-proto",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -8869,6 +8872,18 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-transport-proto"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a3806ea43df5817f0d90618c842d28db5946bc18a5db0659b2275c2be48d472"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"http 1.1.0",
|
||||||
|
"thiserror 1.0.64",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-root-certs"
|
name = "webpki-root-certs"
|
||||||
version = "0.26.6"
|
version = "0.26.6"
|
||||||
|
|
|
@ -115,10 +115,16 @@ exec deno {} "$@"
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_installer_root() -> Result<PathBuf, io::Error> {
|
fn get_installer_root() -> Result<PathBuf, AnyError> {
|
||||||
if let Ok(env_dir) = env::var("DENO_INSTALL_ROOT") {
|
if let Some(env_dir) = env::var_os("DENO_INSTALL_ROOT") {
|
||||||
if !env_dir.is_empty() {
|
if !env_dir.is_empty() {
|
||||||
return canonicalize_path_maybe_not_exists(&PathBuf::from(env_dir));
|
let env_dir = PathBuf::from(env_dir);
|
||||||
|
return canonicalize_path_maybe_not_exists(&env_dir).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"Canonicalizing DENO_INSTALL_ROOT ('{}').",
|
||||||
|
env_dir.display()
|
||||||
|
)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Note: on Windows, the $HOME environment variable may be set by users or by
|
// Note: on Windows, the $HOME environment variable may be set by users or by
|
||||||
|
@ -587,11 +593,22 @@ async fn resolve_shim_data(
|
||||||
let copy_path = get_hidden_file_with_ext(&file_path, "deno.json");
|
let copy_path = get_hidden_file_with_ext(&file_path, "deno.json");
|
||||||
executable_args.push("--config".to_string());
|
executable_args.push("--config".to_string());
|
||||||
executable_args.push(copy_path.to_str().unwrap().to_string());
|
executable_args.push(copy_path.to_str().unwrap().to_string());
|
||||||
extra_files.push((
|
let mut config_text = fs::read_to_string(config_path)
|
||||||
copy_path,
|
.with_context(|| format!("error reading {config_path}"))?;
|
||||||
fs::read_to_string(config_path)
|
// always remove the import map field because when someone specifies `--import-map` we
|
||||||
.with_context(|| format!("error reading {config_path}"))?,
|
// don't want that file to be attempted to be loaded and when they don't specify that
|
||||||
));
|
// (which is just something we haven't implemented yet)
|
||||||
|
if let Some(new_text) = remove_import_map_field_from_text(&config_text) {
|
||||||
|
if flags.import_map_path.is_none() {
|
||||||
|
log::warn!(
|
||||||
|
"{} \"importMap\" field in the specified config file we be ignored. Use the --import-map flag instead.",
|
||||||
|
crate::colors::yellow("Warning"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
config_text = new_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
extra_files.push((copy_path, config_text));
|
||||||
} else {
|
} else {
|
||||||
executable_args.push("--no-config".to_string());
|
executable_args.push("--no-config".to_string());
|
||||||
}
|
}
|
||||||
|
@ -631,6 +648,16 @@ async fn resolve_shim_data(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_import_map_field_from_text(config_text: &str) -> Option<String> {
|
||||||
|
let value =
|
||||||
|
jsonc_parser::cst::CstRootNode::parse(config_text, &Default::default())
|
||||||
|
.ok()?;
|
||||||
|
let root_value = value.object_value()?;
|
||||||
|
let import_map_value = root_value.get("importMap")?;
|
||||||
|
import_map_value.remove();
|
||||||
|
Some(value.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
fn get_hidden_file_with_ext(file_path: &Path, ext: &str) -> PathBuf {
|
fn get_hidden_file_with_ext(file_path: &Path, ext: &str) -> PathBuf {
|
||||||
// use a dot file to prevent the file from showing up in some
|
// use a dot file to prevent the file from showing up in some
|
||||||
// users shell auto-complete since this directory is on the PATH
|
// users shell auto-complete since this directory is on the PATH
|
||||||
|
@ -1585,4 +1612,17 @@ mod tests {
|
||||||
assert!(!file_path.exists());
|
assert!(!file_path.exists());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_remove_import_map_field_from_text() {
|
||||||
|
assert_eq!(
|
||||||
|
remove_import_map_field_from_text(
|
||||||
|
r#"{
|
||||||
|
"importMap": "./value.json"
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
"{}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1481,9 +1481,15 @@ delete Object.prototype.__proto__;
|
||||||
options: SNAPSHOT_COMPILE_OPTIONS,
|
options: SNAPSHOT_COMPILE_OPTIONS,
|
||||||
host,
|
host,
|
||||||
});
|
});
|
||||||
|
const errors = ts.getPreEmitDiagnostics(TS_SNAPSHOT_PROGRAM);
|
||||||
assert(
|
assert(
|
||||||
ts.getPreEmitDiagnostics(TS_SNAPSHOT_PROGRAM).length === 0,
|
errors.length === 0,
|
||||||
"lib.d.ts files have errors",
|
`lib.d.ts files have errors:\n${
|
||||||
|
ts.formatDiagnosticsWithColorAndContext(
|
||||||
|
errors,
|
||||||
|
host,
|
||||||
|
)
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// remove this now that we don't need it anymore for warming up tsc
|
// remove this now that we don't need it anymore for warming up tsc
|
||||||
|
|
|
@ -34,6 +34,8 @@ import {
|
||||||
op_quic_send_stream_get_id,
|
op_quic_send_stream_get_id,
|
||||||
op_quic_send_stream_get_priority,
|
op_quic_send_stream_get_priority,
|
||||||
op_quic_send_stream_set_priority,
|
op_quic_send_stream_set_priority,
|
||||||
|
op_webtransport_accept,
|
||||||
|
op_webtransport_connect,
|
||||||
} from "ext:core/ops";
|
} from "ext:core/ops";
|
||||||
import {
|
import {
|
||||||
getReadableStreamResourceBacking,
|
getReadableStreamResourceBacking,
|
||||||
|
@ -50,6 +52,7 @@ const {
|
||||||
const {
|
const {
|
||||||
ObjectPrototypeIsPrototypeOf,
|
ObjectPrototypeIsPrototypeOf,
|
||||||
PromisePrototypeThen,
|
PromisePrototypeThen,
|
||||||
|
ReflectConstruct,
|
||||||
Symbol,
|
Symbol,
|
||||||
SymbolAsyncIterator,
|
SymbolAsyncIterator,
|
||||||
SafePromisePrototypeFinally,
|
SafePromisePrototypeFinally,
|
||||||
|
@ -205,6 +208,9 @@ class QuicIncoming {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let webtransportConnect;
|
||||||
|
let webtransportAccept;
|
||||||
|
|
||||||
class QuicConn {
|
class QuicConn {
|
||||||
#resource;
|
#resource;
|
||||||
#bidiStream = null;
|
#bidiStream = null;
|
||||||
|
@ -309,6 +315,43 @@ class QuicConn {
|
||||||
close({ closeCode = 0, reason = "" } = { __proto__: null }) {
|
close({ closeCode = 0, reason = "" } = { __proto__: null }) {
|
||||||
op_quic_connection_close(this.#resource, closeCode, reason);
|
op_quic_connection_close(this.#resource, closeCode, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
webtransportConnect = async function webtransportConnect(conn, url) {
|
||||||
|
const {
|
||||||
|
0: connectTxRid,
|
||||||
|
1: connectRxRid,
|
||||||
|
2: settingsTxRid,
|
||||||
|
3: settingsRxRid,
|
||||||
|
} = await op_webtransport_connect(conn.#resource, url);
|
||||||
|
const connect = new QuicBidirectionalStream(
|
||||||
|
connectTxRid,
|
||||||
|
connectRxRid,
|
||||||
|
conn.closed,
|
||||||
|
);
|
||||||
|
const settingsTx = writableStream(settingsTxRid, conn.closed);
|
||||||
|
const settingsRx = readableStream(settingsRxRid, conn.closed);
|
||||||
|
return { connect, settingsTx, settingsRx };
|
||||||
|
};
|
||||||
|
|
||||||
|
webtransportAccept = async function webtransportAccept(conn) {
|
||||||
|
const {
|
||||||
|
0: url,
|
||||||
|
1: connectTxRid,
|
||||||
|
2: connectRxRid,
|
||||||
|
3: settingsTxRid,
|
||||||
|
4: settingsRxRid,
|
||||||
|
} = await op_webtransport_accept(conn.#resource);
|
||||||
|
const connect = new QuicBidirectionalStream(
|
||||||
|
connectTxRid,
|
||||||
|
connectRxRid,
|
||||||
|
conn.closed,
|
||||||
|
);
|
||||||
|
const settingsTx = writableStream(settingsTxRid, conn.closed);
|
||||||
|
const settingsRx = readableStream(settingsRxRid, conn.closed);
|
||||||
|
return { url, connect, settingsTx, settingsRx };
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class QuicSendStream extends WritableStream {
|
class QuicSendStream extends WritableStream {
|
||||||
|
@ -345,7 +388,11 @@ function readableStream(rid, closed) {
|
||||||
SafePromisePrototypeFinally(closed, () => {
|
SafePromisePrototypeFinally(closed, () => {
|
||||||
core.tryClose(rid);
|
core.tryClose(rid);
|
||||||
});
|
});
|
||||||
return readableStreamForRid(rid, true, QuicReceiveStream);
|
return readableStreamForRid(
|
||||||
|
rid,
|
||||||
|
true,
|
||||||
|
(...args) => ReflectConstruct(QuicReceiveStream, args),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function writableStream(rid, closed) {
|
function writableStream(rid, closed) {
|
||||||
|
@ -353,7 +400,11 @@ function writableStream(rid, closed) {
|
||||||
SafePromisePrototypeFinally(closed, () => {
|
SafePromisePrototypeFinally(closed, () => {
|
||||||
core.tryClose(rid);
|
core.tryClose(rid);
|
||||||
});
|
});
|
||||||
return writableStreamForRid(rid, true, QuicSendStream);
|
return writableStreamForRid(
|
||||||
|
rid,
|
||||||
|
true,
|
||||||
|
(...args) => ReflectConstruct(QuicSendStream, args),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class QuicBidirectionalStream {
|
class QuicBidirectionalStream {
|
||||||
|
@ -421,6 +472,7 @@ function connectQuic(options) {
|
||||||
caCerts: options.caCerts,
|
caCerts: options.caCerts,
|
||||||
alpnProtocols: options.alpnProtocols,
|
alpnProtocols: options.alpnProtocols,
|
||||||
serverName: options.serverName,
|
serverName: options.serverName,
|
||||||
|
serverCertificateHashes: options.serverCertificateHashes,
|
||||||
},
|
},
|
||||||
transportOptions(options),
|
transportOptions(options),
|
||||||
keyPair,
|
keyPair,
|
||||||
|
@ -448,4 +500,6 @@ export {
|
||||||
QuicListener,
|
QuicListener,
|
||||||
QuicReceiveStream,
|
QuicReceiveStream,
|
||||||
QuicSendStream,
|
QuicSendStream,
|
||||||
|
webtransportAccept,
|
||||||
|
webtransportConnect,
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,6 +24,9 @@ pin-project.workspace = true
|
||||||
quinn = { version = "0.11.6", default-features = false, features = ["runtime-tokio", "rustls", "ring"] }
|
quinn = { version = "0.11.6", default-features = false, features = ["runtime-tokio", "rustls", "ring"] }
|
||||||
rustls-tokio-stream.workspace = true
|
rustls-tokio-stream.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
sha2.workspace = true
|
||||||
socket2.workspace = true
|
socket2.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
url.workspace = true
|
||||||
|
web-transport-proto = "0.2.3"
|
||||||
|
|
|
@ -196,6 +196,8 @@ deno_core::extension!(deno_net,
|
||||||
quic::op_quic_send_stream_get_id,
|
quic::op_quic_send_stream_get_id,
|
||||||
quic::op_quic_send_stream_get_priority,
|
quic::op_quic_send_stream_get_priority,
|
||||||
quic::op_quic_send_stream_set_priority,
|
quic::op_quic_send_stream_set_priority,
|
||||||
|
quic::webtransport::op_webtransport_accept,
|
||||||
|
quic::webtransport::op_webtransport_connect,
|
||||||
],
|
],
|
||||||
esm = [ "01_net.js", "02_tls.js" ],
|
esm = [ "01_net.js", "02_tls.js" ],
|
||||||
lazy_loaded_esm = [ "03_quic.js" ],
|
lazy_loaded_esm = [ "03_quic.js" ],
|
||||||
|
|
344
ext/net/quic.rs
344
ext/net/quic.rs
|
@ -93,12 +93,27 @@ pub enum QuicError {
|
||||||
#[class("BadResource")]
|
#[class("BadResource")]
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
ClosedStream(#[from] quinn::ClosedStream),
|
ClosedStream(#[from] quinn::ClosedStream),
|
||||||
|
#[class(generic)]
|
||||||
|
#[error("{0}")]
|
||||||
|
ReadError(#[from] quinn::ReadError),
|
||||||
|
#[class(generic)]
|
||||||
|
#[error("{0}")]
|
||||||
|
WriteError(#[from] quinn::WriteError),
|
||||||
#[class("BadResource")]
|
#[class("BadResource")]
|
||||||
#[error("Invalid {0} resource")]
|
#[error("Invalid {0} resource")]
|
||||||
BadResource(&'static str),
|
BadResource(&'static str),
|
||||||
#[class(range)]
|
#[class(range)]
|
||||||
#[error("Connection has reached the maximum number of concurrent outgoing {0} streams")]
|
#[error("Connection has reached the maximum number of concurrent outgoing {0} streams")]
|
||||||
MaxStreams(&'static str),
|
MaxStreams(&'static str),
|
||||||
|
#[class(generic)]
|
||||||
|
#[error("Peer does not support WebTransport")]
|
||||||
|
WebTransportPeerUnsupported,
|
||||||
|
#[class(generic)]
|
||||||
|
#[error("{0}")]
|
||||||
|
WebTransportSettingsError(#[from] web_transport_proto::SettingsError),
|
||||||
|
#[class(generic)]
|
||||||
|
#[error("{0}")]
|
||||||
|
WebTransportConnectError(#[from] web_transport_proto::ConnectError),
|
||||||
#[class(inherit)]
|
#[class(inherit)]
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Other(#[from] JsErrorBox),
|
Other(#[from] JsErrorBox),
|
||||||
|
@ -475,6 +490,14 @@ struct ConnectArgs {
|
||||||
ca_certs: Option<Vec<String>>,
|
ca_certs: Option<Vec<String>>,
|
||||||
alpn_protocols: Option<Vec<String>>,
|
alpn_protocols: Option<Vec<String>>,
|
||||||
server_name: Option<String>,
|
server_name: Option<String>,
|
||||||
|
server_certificate_hashes: Option<Vec<CertificateHash>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct CertificateHash {
|
||||||
|
algorithm: String,
|
||||||
|
value: JsBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2]
|
#[op2]
|
||||||
|
@ -515,13 +538,28 @@ where
|
||||||
.map(|s| s.into_bytes())
|
.map(|s| s.into_bytes())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut tls_config = create_client_config(
|
let mut tls_config = if let Some(hashes) = args.server_certificate_hashes {
|
||||||
|
deno_tls::rustls::ClientConfig::builder()
|
||||||
|
.dangerous()
|
||||||
|
.with_custom_certificate_verifier(Arc::new(
|
||||||
|
webtransport::ServerFingerprints::new(
|
||||||
|
hashes
|
||||||
|
.into_iter()
|
||||||
|
.filter(|h| h.algorithm.to_lowercase() == "sha-256")
|
||||||
|
.map(|h| h.value.to_vec())
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.with_no_client_auth()
|
||||||
|
} else {
|
||||||
|
create_client_config(
|
||||||
root_cert_store,
|
root_cert_store,
|
||||||
ca_certs,
|
ca_certs,
|
||||||
unsafely_ignore_certificate_errors,
|
unsafely_ignore_certificate_errors,
|
||||||
key_pair.take(),
|
key_pair.take(),
|
||||||
SocketUse::GeneralSsl,
|
SocketUse::GeneralSsl,
|
||||||
)?;
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(alpn_protocols) = args.alpn_protocols {
|
if let Some(alpn_protocols) = args.alpn_protocols {
|
||||||
tls_config.alpn_protocols =
|
tls_config.alpn_protocols =
|
||||||
|
@ -925,3 +963,305 @@ pub(crate) fn op_quic_recv_stream_get_id(
|
||||||
let stream_id = quinn::VarInt::from(resource.stream_id).into_inner();
|
let stream_id = quinn::VarInt::from(resource.stream_id).into_inner();
|
||||||
Ok(stream_id)
|
Ok(stream_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) mod webtransport {
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2023 Luke Curley
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
// https://github.com/kixelated/web-transport-rs
|
||||||
|
|
||||||
|
use deno_core::futures::try_join;
|
||||||
|
use deno_tls::rustls;
|
||||||
|
use rustls::client::danger::ServerCertVerifier;
|
||||||
|
use rustls::crypto::verify_tls12_signature;
|
||||||
|
use rustls::crypto::verify_tls13_signature;
|
||||||
|
use rustls::crypto::CryptoProvider;
|
||||||
|
use sha2::Digest;
|
||||||
|
use sha2::Sha256;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
async fn exchange_settings(
|
||||||
|
state: Rc<RefCell<OpState>>,
|
||||||
|
conn: quinn::Connection,
|
||||||
|
) -> Result<(u32, u32), QuicError> {
|
||||||
|
use web_transport_proto::Settings;
|
||||||
|
use web_transport_proto::SettingsError;
|
||||||
|
|
||||||
|
let settings_tx_rid = async {
|
||||||
|
let mut tx = conn.open_uni().await?;
|
||||||
|
|
||||||
|
let mut settings = Settings::default();
|
||||||
|
settings.enable_webtransport(1);
|
||||||
|
|
||||||
|
let mut buf = vec![];
|
||||||
|
settings.encode(&mut buf);
|
||||||
|
tx.write_all(&buf).await?;
|
||||||
|
|
||||||
|
let rid = state
|
||||||
|
.borrow_mut()
|
||||||
|
.resource_table
|
||||||
|
.add(SendStreamResource::new(tx));
|
||||||
|
|
||||||
|
Ok(rid)
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings_rx_rid = async {
|
||||||
|
let mut rx = conn.accept_uni().await?;
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let chunk = rx.read_chunk(usize::MAX, true).await?;
|
||||||
|
let chunk = chunk.ok_or(QuicError::WebTransportPeerUnsupported)?;
|
||||||
|
buf.extend_from_slice(&chunk.bytes);
|
||||||
|
|
||||||
|
let mut limit = std::io::Cursor::new(&buf);
|
||||||
|
|
||||||
|
let settings = match Settings::decode(&mut limit) {
|
||||||
|
Ok(settings) => settings,
|
||||||
|
Err(SettingsError::UnexpectedEnd) => continue,
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if settings.supports_webtransport() == 0 {
|
||||||
|
return Err(QuicError::WebTransportPeerUnsupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rid = state
|
||||||
|
.borrow_mut()
|
||||||
|
.resource_table
|
||||||
|
.add(RecvStreamResource::new(rx));
|
||||||
|
|
||||||
|
Ok(rid)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (settings_tx_rid, settings_rx_rid) =
|
||||||
|
try_join!(settings_tx_rid, settings_rx_rid)?;
|
||||||
|
|
||||||
|
Ok((settings_tx_rid, settings_rx_rid))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op2(async)]
|
||||||
|
#[serde]
|
||||||
|
pub(crate) async fn op_webtransport_connect(
|
||||||
|
state: Rc<RefCell<OpState>>,
|
||||||
|
#[cppgc] connection_resource: &ConnectionResource,
|
||||||
|
#[string] url: String,
|
||||||
|
) -> Result<(u32, u32, u32, u32), QuicError> {
|
||||||
|
use web_transport_proto::ConnectError;
|
||||||
|
use web_transport_proto::ConnectRequest;
|
||||||
|
use web_transport_proto::ConnectResponse;
|
||||||
|
|
||||||
|
let conn = connection_resource.0.clone();
|
||||||
|
let url = url::Url::parse(&url).unwrap();
|
||||||
|
|
||||||
|
let (settings_tx_rid, settings_rx_rid) =
|
||||||
|
exchange_settings(state.clone(), conn.clone()).await?;
|
||||||
|
|
||||||
|
let (connect_tx_rid, connect_rx_rid) = {
|
||||||
|
let (mut tx, mut rx) = conn.open_bi().await?;
|
||||||
|
|
||||||
|
let request = ConnectRequest { url: url.clone() };
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
request.encode(&mut buf);
|
||||||
|
tx.write_all(&buf).await?;
|
||||||
|
|
||||||
|
buf.clear();
|
||||||
|
loop {
|
||||||
|
let chunk = rx.read_chunk(usize::MAX, true).await?;
|
||||||
|
let chunk = chunk.ok_or(QuicError::WebTransportPeerUnsupported)?;
|
||||||
|
buf.extend_from_slice(&chunk.bytes);
|
||||||
|
|
||||||
|
let mut limit = std::io::Cursor::new(&buf);
|
||||||
|
|
||||||
|
let res = match ConnectResponse::decode(&mut limit) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(ConnectError::UnexpectedEnd) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if res.status != 200 {
|
||||||
|
return Err(QuicError::WebTransportPeerUnsupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state = state.borrow_mut();
|
||||||
|
let tx_rid = state.resource_table.add(SendStreamResource::new(tx));
|
||||||
|
let rx_rid = state.resource_table.add(RecvStreamResource::new(rx));
|
||||||
|
|
||||||
|
(tx_rid, rx_rid)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
connect_tx_rid,
|
||||||
|
connect_rx_rid,
|
||||||
|
settings_tx_rid,
|
||||||
|
settings_rx_rid,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[op2(async)]
|
||||||
|
#[serde]
|
||||||
|
pub(crate) async fn op_webtransport_accept(
|
||||||
|
state: Rc<RefCell<OpState>>,
|
||||||
|
#[cppgc] connection_resource: &ConnectionResource,
|
||||||
|
) -> Result<(String, u32, u32, u32, u32), QuicError> {
|
||||||
|
use web_transport_proto::ConnectError;
|
||||||
|
use web_transport_proto::ConnectRequest;
|
||||||
|
use web_transport_proto::ConnectResponse;
|
||||||
|
|
||||||
|
let conn = connection_resource.0.clone();
|
||||||
|
|
||||||
|
let (settings_tx_rid, settings_rx_rid) =
|
||||||
|
exchange_settings(state.clone(), conn.clone()).await?;
|
||||||
|
|
||||||
|
let (url, connect_tx_rid, connect_rx_rid) = {
|
||||||
|
let (mut tx, mut rx) = conn.accept_bi().await?;
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
|
||||||
|
let req = loop {
|
||||||
|
let chunk = rx.read_chunk(usize::MAX, true).await?;
|
||||||
|
let chunk = chunk.ok_or(QuicError::WebTransportPeerUnsupported)?;
|
||||||
|
buf.extend_from_slice(&chunk.bytes);
|
||||||
|
|
||||||
|
let mut limit = std::io::Cursor::new(&buf);
|
||||||
|
|
||||||
|
let req = match ConnectRequest::decode(&mut limit) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(ConnectError::UnexpectedEnd) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(e) => return Err(e.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
break req;
|
||||||
|
};
|
||||||
|
|
||||||
|
buf.clear();
|
||||||
|
let resp = ConnectResponse {
|
||||||
|
status: 200u16.try_into().unwrap(),
|
||||||
|
};
|
||||||
|
resp.encode(&mut buf);
|
||||||
|
tx.write_all(&buf).await?;
|
||||||
|
|
||||||
|
let mut state = state.borrow_mut();
|
||||||
|
let tx_rid = state.resource_table.add(SendStreamResource::new(tx));
|
||||||
|
let rx_rid = state.resource_table.add(RecvStreamResource::new(rx));
|
||||||
|
|
||||||
|
(req.url, tx_rid, rx_rid)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
url.to_string(),
|
||||||
|
connect_tx_rid,
|
||||||
|
connect_rx_rid,
|
||||||
|
settings_tx_rid,
|
||||||
|
settings_rx_rid,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct ServerFingerprints {
|
||||||
|
fingerprints: Vec<Vec<u8>>,
|
||||||
|
provider: CryptoProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerFingerprints {
|
||||||
|
pub(crate) fn new(fingerprints: Vec<Vec<u8>>) -> ServerFingerprints {
|
||||||
|
Self {
|
||||||
|
fingerprints,
|
||||||
|
provider: rustls::crypto::ring::default_provider(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerCertVerifier for ServerFingerprints {
|
||||||
|
fn verify_server_cert(
|
||||||
|
&self,
|
||||||
|
end_entity: &rustls::pki_types::CertificateDer<'_>,
|
||||||
|
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
|
||||||
|
_server_name: &rustls::pki_types::ServerName<'_>,
|
||||||
|
_ocsp_response: &[u8],
|
||||||
|
_now: rustls::pki_types::UnixTime,
|
||||||
|
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
|
||||||
|
let cert_hash = Sha256::digest(end_entity);
|
||||||
|
|
||||||
|
if self
|
||||||
|
.fingerprints
|
||||||
|
.iter()
|
||||||
|
.any(|fingerprint| fingerprint == cert_hash.as_slice())
|
||||||
|
{
|
||||||
|
return Ok(rustls::client::danger::ServerCertVerified::assertion());
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(rustls::Error::InvalidCertificate(
|
||||||
|
rustls::CertificateError::UnknownIssuer,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls12_signature(
|
||||||
|
&self,
|
||||||
|
message: &[u8],
|
||||||
|
cert: &rustls::pki_types::CertificateDer<'_>,
|
||||||
|
dss: &rustls::DigitallySignedStruct,
|
||||||
|
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error>
|
||||||
|
{
|
||||||
|
verify_tls12_signature(
|
||||||
|
message,
|
||||||
|
cert,
|
||||||
|
dss,
|
||||||
|
&self.provider.signature_verification_algorithms,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls13_signature(
|
||||||
|
&self,
|
||||||
|
message: &[u8],
|
||||||
|
cert: &rustls::pki_types::CertificateDer<'_>,
|
||||||
|
dss: &rustls::DigitallySignedStruct,
|
||||||
|
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error>
|
||||||
|
{
|
||||||
|
verify_tls13_signature(
|
||||||
|
message,
|
||||||
|
cert,
|
||||||
|
dss,
|
||||||
|
&self.provider.signature_verification_algorithms,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
|
||||||
|
self
|
||||||
|
.provider
|
||||||
|
.signature_verification_algorithms
|
||||||
|
.supported_schemes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -908,8 +908,8 @@ const _original = Symbol("[[original]]");
|
||||||
* @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true.
|
* @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true.
|
||||||
* @returns {ReadableStream<Uint8Array>}
|
* @returns {ReadableStream<Uint8Array>}
|
||||||
*/
|
*/
|
||||||
function readableStreamForRid(rid, autoClose = true, Super, onError) {
|
function readableStreamForRid(rid, autoClose = true, cfn, onError) {
|
||||||
const stream = new (Super ?? ReadableStream)(_brand);
|
const stream = cfn ? cfn(_brand) : new ReadableStream(_brand);
|
||||||
stream[_resourceBacking] = { rid, autoClose };
|
stream[_resourceBacking] = { rid, autoClose };
|
||||||
|
|
||||||
const tryClose = () => {
|
const tryClose = () => {
|
||||||
|
@ -1134,8 +1134,8 @@ async function readableStreamCollectIntoUint8Array(stream) {
|
||||||
* @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true.
|
* @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true.
|
||||||
* @returns {ReadableStream<Uint8Array>}
|
* @returns {ReadableStream<Uint8Array>}
|
||||||
*/
|
*/
|
||||||
function writableStreamForRid(rid, autoClose = true, Super) {
|
function writableStreamForRid(rid, autoClose = true, cfn) {
|
||||||
const stream = new (Super ?? WritableStream)(_brand);
|
const stream = cfn ? cfn(_brand) : new WritableStream(_brand);
|
||||||
stream[_resourceBacking] = { rid, autoClose };
|
stream[_resourceBacking] = { rid, autoClose };
|
||||||
|
|
||||||
const tryClose = () => {
|
const tryClose = () => {
|
||||||
|
|
218
ext/web/lib.deno_web.d.ts
vendored
218
ext/web/lib.deno_web.d.ts
vendored
|
@ -1378,3 +1378,221 @@ declare var ImageData: {
|
||||||
settings?: ImageDataSettings,
|
settings?: ImageDataSettings,
|
||||||
): ImageData;
|
): ImageData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
interface WebTransportCloseInfo {
|
||||||
|
closeCode?: number;
|
||||||
|
reason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
interface WebTransportErrorOptions {
|
||||||
|
source?: WebTransportErrorSource;
|
||||||
|
streamErrorCode?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
interface WebTransportHash {
|
||||||
|
algorithm?: string;
|
||||||
|
value?: BufferSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
interface WebTransportOptions {
|
||||||
|
allowPooling?: boolean;
|
||||||
|
congestionControl?: WebTransportCongestionControl;
|
||||||
|
requireUnreliable?: boolean;
|
||||||
|
serverCertificateHashes?: WebTransportHash[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
interface WebTransportSendStreamOptions {
|
||||||
|
sendGroup?: WebTransportSendGroup;
|
||||||
|
sendOrder?: number;
|
||||||
|
waitUntilAvailable?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport)
|
||||||
|
* @category Platform
|
||||||
|
*/
|
||||||
|
interface WebTransport {
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport/closed) */
|
||||||
|
readonly closed: Promise<WebTransportCloseInfo>;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport/datagrams) */
|
||||||
|
readonly datagrams: WebTransportDatagramDuplexStream;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport/incomingBidirectionalStreams) */
|
||||||
|
readonly incomingBidirectionalStreams: ReadableStream<
|
||||||
|
WebTransportBidirectionalStream
|
||||||
|
>;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport/incomingUnidirectionalStreams) */
|
||||||
|
readonly incomingUnidirectionalStreams: ReadableStream<
|
||||||
|
WebTransportReceiveStream
|
||||||
|
>;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport/ready) */
|
||||||
|
readonly ready: Promise<undefined>;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport/close) */
|
||||||
|
close(closeInfo?: WebTransportCloseInfo): void;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport/createBidirectionalStream) */
|
||||||
|
createBidirectionalStream(
|
||||||
|
options?: WebTransportSendStreamOptions,
|
||||||
|
): Promise<WebTransportBidirectionalStream>;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport/createUnidirectionalStream) */
|
||||||
|
createUnidirectionalStream(
|
||||||
|
options?: WebTransportSendStreamOptions,
|
||||||
|
): Promise<WebTransportSendStream>;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransport/createSendGroup) */
|
||||||
|
createSendGroup(): WebTransportSendGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
declare var WebTransport: {
|
||||||
|
prototype: WebTransport;
|
||||||
|
new (url: string | URL, options?: WebTransportOptions): WebTransport;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportBidirectionalStream)
|
||||||
|
* @category Platform
|
||||||
|
*/
|
||||||
|
interface WebTransportBidirectionalStream {
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportBidirectionalStream/readable) */
|
||||||
|
readonly readable: WebTransportReceiveStream;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportBidirectionalStream/writable) */
|
||||||
|
readonly writable: WebTransportSendStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
declare var WebTransportBidirectionalStream: {
|
||||||
|
prototype: WebTransportBidirectionalStream;
|
||||||
|
new (): WebTransportBidirectionalStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportDatagramDuplexStream)
|
||||||
|
* @category Platform
|
||||||
|
*/
|
||||||
|
interface WebTransportDatagramDuplexStream {
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportDatagramDuplexStream/incomingHighWaterMark) */
|
||||||
|
incomingHighWaterMark: number;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportDatagramDuplexStream/incomingMaxAge) */
|
||||||
|
incomingMaxAge: number | null;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportDatagramDuplexStream/maxDatagramSize) */
|
||||||
|
readonly maxDatagramSize: number;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportDatagramDuplexStream/outgoingHighWaterMark) */
|
||||||
|
outgoingHighWaterMark: number;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportDatagramDuplexStream/outgoingMaxAge) */
|
||||||
|
outgoingMaxAge: number | null;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportDatagramDuplexStream/readable) */
|
||||||
|
readonly readable: WebTransportReceiveStream;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportDatagramDuplexStream/writable) */
|
||||||
|
readonly writable: WebTransportSendStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
declare var WebTransportDatagramDuplexStream: {
|
||||||
|
prototype: WebTransportDatagramDuplexStream;
|
||||||
|
new (): WebTransportDatagramDuplexStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportSendStream)
|
||||||
|
* @category Platform
|
||||||
|
*/
|
||||||
|
interface WebTransportSendStream extends WritableStream<Uint8Array> {
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportSendStream/sendOrder) */
|
||||||
|
sendOrder: number;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportSendStream/sendGroup) */
|
||||||
|
sendGroup?: WebTransportSendGroup;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportSendStream/getStats) */
|
||||||
|
getStats(): Promise<WebTransportSendStreamStats>;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportSendStream/getWriter) */
|
||||||
|
getWriter(): WebTransportWriter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
declare var WebTransportSendStream: {
|
||||||
|
prototype: WebTransportSendStream;
|
||||||
|
new (): WebTransportSendStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
interface WebTransportSendStreamStats {
|
||||||
|
bytesWritten: number;
|
||||||
|
bytesSent: number;
|
||||||
|
bytesAcknowledged: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportWriter)
|
||||||
|
* @category Platform
|
||||||
|
*/
|
||||||
|
interface WebTransportWriter extends WritableStreamDefaultWriter<Uint8Array> {
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportWriter/atomicWrite) */
|
||||||
|
atomicWrite(chunk: any): Promise<undefined>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
declare var WebTransportWriter: {
|
||||||
|
prototype: WebTransportWriter;
|
||||||
|
new (): WebTransportWriter;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportReceiveStream)
|
||||||
|
* @category Platform
|
||||||
|
*/
|
||||||
|
interface WebTransportReceiveStream extends ReadableStream<Uint8Array> {
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportReceiveStream/getStats) */
|
||||||
|
getStats(): Promise<WebTransportReceiveStreamStats>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
declare var WebTransportReceiveStream: {
|
||||||
|
prototype: WebTransportReceiveStream;
|
||||||
|
new (): WebTransportReceiveStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
interface WebTransportReceiveStreamStats {
|
||||||
|
bytesReceived: number;
|
||||||
|
bytesRead: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportSendGroup)
|
||||||
|
* @category Platform
|
||||||
|
*/
|
||||||
|
interface WebTransportSendGroup {
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportSendGroup/getStats) */
|
||||||
|
getStats(): Promise<WebTransportSendStreamStats>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
declare var WebTransportSendGroup: {
|
||||||
|
prototype: WebTransportSendGroup;
|
||||||
|
new (): WebTransportSendGroup;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportError)
|
||||||
|
* @category Platform
|
||||||
|
*/
|
||||||
|
interface WebTransportError extends DOMException {
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportError/source) */
|
||||||
|
readonly source: WebTransportErrorSource;
|
||||||
|
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/WebTransportError/streamErrorCode) */
|
||||||
|
readonly streamErrorCode: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
declare var WebTransportError: {
|
||||||
|
prototype: WebTransportError;
|
||||||
|
new (message?: string, options?: WebTransportErrorOptions): WebTransportError;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
type WebTransportCongestionControl = "default" | "low-latency" | "throughput";
|
||||||
|
|
||||||
|
/** @category Platform */
|
||||||
|
type WebTransportErrorSource = "session" | "stream";
|
||||||
|
|
|
@ -113,6 +113,7 @@ deno_core::extension!(deno_web,
|
||||||
"15_performance.js",
|
"15_performance.js",
|
||||||
"16_image_data.js",
|
"16_image_data.js",
|
||||||
],
|
],
|
||||||
|
lazy_loaded_esm = [ "webtransport.js" ],
|
||||||
options = {
|
options = {
|
||||||
blob_store: Arc<BlobStore>,
|
blob_store: Arc<BlobStore>,
|
||||||
maybe_location: Option<Url>,
|
maybe_location: Option<Url>,
|
||||||
|
|
1125
ext/web/webtransport.js
Normal file
1125
ext/web/webtransport.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -324,9 +324,15 @@ converters.short = createIntegerConversion(16, { unsigned: false });
|
||||||
converters["unsigned short"] = createIntegerConversion(16, {
|
converters["unsigned short"] = createIntegerConversion(16, {
|
||||||
unsigned: true,
|
unsigned: true,
|
||||||
});
|
});
|
||||||
|
converters["unsigned short?"] = createNullableConverter(
|
||||||
|
converters["unsigned short"],
|
||||||
|
);
|
||||||
|
|
||||||
converters.long = createIntegerConversion(32, { unsigned: false });
|
converters.long = createIntegerConversion(32, { unsigned: false });
|
||||||
converters["unsigned long"] = createIntegerConversion(32, { unsigned: true });
|
converters["unsigned long"] = createIntegerConversion(32, { unsigned: true });
|
||||||
|
converters["unsigned long?"] = createNullableConverter(
|
||||||
|
converters["unsigned long"],
|
||||||
|
);
|
||||||
|
|
||||||
converters["long long"] = createLongLongConversion(64, { unsigned: false });
|
converters["long long"] = createLongLongConversion(64, { unsigned: false });
|
||||||
converters["unsigned long long"] = createLongLongConversion(64, {
|
converters["unsigned long long"] = createLongLongConversion(64, {
|
||||||
|
@ -398,6 +404,10 @@ converters["unrestricted double"] = (V, _prefix, _context, _opts) => {
|
||||||
return x;
|
return x;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
converters["unrestricted double?"] = createNullableConverter(
|
||||||
|
converters["unrestricted double"],
|
||||||
|
);
|
||||||
|
|
||||||
converters.DOMString = function (
|
converters.DOMString = function (
|
||||||
V,
|
V,
|
||||||
prefix,
|
prefix,
|
||||||
|
|
|
@ -34,6 +34,7 @@ import * as telemetry from "ext:deno_telemetry/telemetry.ts";
|
||||||
const { ObjectDefineProperties } = primordials;
|
const { ObjectDefineProperties } = primordials;
|
||||||
|
|
||||||
const loadQuic = core.createLazyLoader("ext:deno_net/03_quic.js");
|
const loadQuic = core.createLazyLoader("ext:deno_net/03_quic.js");
|
||||||
|
const loadWebTransport = core.createLazyLoader("ext:deno_web/webtransport.js");
|
||||||
|
|
||||||
const denoNs = {
|
const denoNs = {
|
||||||
Process: process.Process,
|
Process: process.Process,
|
||||||
|
@ -198,6 +199,10 @@ ObjectDefineProperties(denoNsUnstableById[unstableIds.net], {
|
||||||
loadQuic,
|
loadQuic,
|
||||||
),
|
),
|
||||||
QuicIncoming: core.propWritableLazyLoaded((q) => q.QuicIncoming, loadQuic),
|
QuicIncoming: core.propWritableLazyLoaded((q) => q.QuicIncoming, loadQuic),
|
||||||
|
upgradeWebTransport: core.propWritableLazyLoaded(
|
||||||
|
(wt) => wt.upgradeWebTransport,
|
||||||
|
loadWebTransport,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
// denoNsUnstableById[unstableIds.unsafeProto] = { __proto__: null }
|
// denoNsUnstableById[unstableIds.unsafeProto] = { __proto__: null }
|
||||||
|
|
|
@ -39,6 +39,7 @@ import * as webgpuSurface from "ext:deno_webgpu/02_surface.js";
|
||||||
import { unstableIds } from "ext:runtime/90_deno_ns.js";
|
import { unstableIds } from "ext:runtime/90_deno_ns.js";
|
||||||
|
|
||||||
const loadImage = core.createLazyLoader("ext:deno_canvas/01_image.js");
|
const loadImage = core.createLazyLoader("ext:deno_canvas/01_image.js");
|
||||||
|
const loadWebTransport = core.createLazyLoader("ext:deno_web/webtransport.js");
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
|
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
|
||||||
const windowOrWorkerGlobalScope = {
|
const windowOrWorkerGlobalScope = {
|
||||||
|
@ -298,6 +299,34 @@ unstableForWindowOrWorkerGlobalScope[unstableIds.broadcastChannel] = {
|
||||||
unstableForWindowOrWorkerGlobalScope[unstableIds.net] = {
|
unstableForWindowOrWorkerGlobalScope[unstableIds.net] = {
|
||||||
WebSocketStream: core.propNonEnumerable(webSocketStream.WebSocketStream),
|
WebSocketStream: core.propNonEnumerable(webSocketStream.WebSocketStream),
|
||||||
WebSocketError: core.propNonEnumerable(webSocketStream.WebSocketError),
|
WebSocketError: core.propNonEnumerable(webSocketStream.WebSocketError),
|
||||||
|
WebTransport: core.propNonEnumerableLazyLoaded(
|
||||||
|
(wt) => wt.WebTransport,
|
||||||
|
loadWebTransport,
|
||||||
|
),
|
||||||
|
WebTransportBidirectionalStream: core.propNonEnumerableLazyLoaded(
|
||||||
|
(wt) => wt.WebTransportBidirectionalStream,
|
||||||
|
loadWebTransport,
|
||||||
|
),
|
||||||
|
WebTransportDatagramDuplexStream: core.propNonEnumerableLazyLoaded(
|
||||||
|
(wt) => wt.WebTransportDatagramDuplexStream,
|
||||||
|
loadWebTransport,
|
||||||
|
),
|
||||||
|
WebTransportReceiveStream: core.propNonEnumerableLazyLoaded(
|
||||||
|
(wt) => wt.WebTransportReceiveStream,
|
||||||
|
loadWebTransport,
|
||||||
|
),
|
||||||
|
WebTransportSendGroup: core.propNonEnumerableLazyLoaded(
|
||||||
|
(wt) => wt.WebTransportSendGroup,
|
||||||
|
loadWebTransport,
|
||||||
|
),
|
||||||
|
WebTransportSendStream: core.propNonEnumerableLazyLoaded(
|
||||||
|
(wt) => wt.WebTransportSendStream,
|
||||||
|
loadWebTransport,
|
||||||
|
),
|
||||||
|
WebTransportError: core.propNonEnumerableLazyLoaded(
|
||||||
|
(wt) => wt.WebTransportError,
|
||||||
|
loadWebTransport,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
unstableForWindowOrWorkerGlobalScope[unstableIds.webgpu] = {};
|
unstableForWindowOrWorkerGlobalScope[unstableIds.webgpu] = {};
|
||||||
|
|
|
@ -5762,7 +5762,7 @@ fn lsp_jsr_auto_import_completion() {
|
||||||
json!({ "triggerKind": 1 }),
|
json!({ "triggerKind": 1 }),
|
||||||
);
|
);
|
||||||
assert!(!list.is_incomplete);
|
assert!(!list.is_incomplete);
|
||||||
assert_eq!(list.items.len(), 268);
|
assert_eq!(list.items.len(), 276);
|
||||||
let item = list.items.iter().find(|i| i.label == "add").unwrap();
|
let item = list.items.iter().find(|i| i.label == "add").unwrap();
|
||||||
assert_eq!(&item.label, "add");
|
assert_eq!(&item.label, "add");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -5842,7 +5842,7 @@ fn lsp_jsr_auto_import_completion_import_map() {
|
||||||
json!({ "triggerKind": 1 }),
|
json!({ "triggerKind": 1 }),
|
||||||
);
|
);
|
||||||
assert!(!list.is_incomplete);
|
assert!(!list.is_incomplete);
|
||||||
assert_eq!(list.items.len(), 268);
|
assert_eq!(list.items.len(), 276);
|
||||||
let item = list.items.iter().find(|i| i.label == "add").unwrap();
|
let item = list.items.iter().find(|i| i.label == "add").unwrap();
|
||||||
assert_eq!(&item.label, "add");
|
assert_eq!(&item.label, "add");
|
||||||
assert_eq!(json!(&item.label_details), json!({ "description": "add" }));
|
assert_eq!(json!(&item.label_details), json!({ "description": "add" }));
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"args": "install -g --root ./folder --config deno.json main.ts --name my-cli",
|
||||||
|
"output": "install.out"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"importMap": "./import_map.json"
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
Warning "importMap" field in the specified config file we be ignored. Use the --import-map flag instead.
|
||||||
|
✅ Successfully installed my-cli
|
||||||
|
[WILDCARD]
|
|
@ -0,0 +1 @@
|
||||||
|
console.log(1);
|
5
tests/specs/run/webtransport/__test__.jsonc
Normal file
5
tests/specs/run/webtransport/__test__.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"args": "run --quiet --unstable-net -A main.ts",
|
||||||
|
"output": "",
|
||||||
|
"exitCode": 0
|
||||||
|
}
|
4
tests/specs/run/webtransport/deno.json
Normal file
4
tests/specs/run/webtransport/deno.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"lock": false,
|
||||||
|
"importMap": "../../../../import_map.json"
|
||||||
|
}
|
100
tests/specs/run/webtransport/main.ts
Normal file
100
tests/specs/run/webtransport/main.ts
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
import { decodeBase64 } from "@std/encoding/base64";
|
||||||
|
import { assertEquals } from "@std/assert";
|
||||||
|
|
||||||
|
const cert = Deno.readTextFileSync("../../../testdata/tls/localhost.crt");
|
||||||
|
const certHash = await crypto.subtle.digest(
|
||||||
|
"SHA-256",
|
||||||
|
decodeBase64(cert.split("\n").slice(1, -2).join("")),
|
||||||
|
);
|
||||||
|
|
||||||
|
const server = new Deno.QuicEndpoint({
|
||||||
|
hostname: "localhost",
|
||||||
|
port: 0,
|
||||||
|
});
|
||||||
|
const listener = server.listen({
|
||||||
|
cert,
|
||||||
|
key: Deno.readTextFileSync("../../../testdata/tls/localhost.key"),
|
||||||
|
alpnProtocols: ["h3"],
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
for await (const conn of listener) {
|
||||||
|
const wt = await Deno.upgradeWebTransport(conn);
|
||||||
|
|
||||||
|
assertEquals(wt.url, `https://localhost:${server.addr.port}/path`);
|
||||||
|
|
||||||
|
wt.ready.then(() => {
|
||||||
|
(async () => {
|
||||||
|
for await (const bidi of wt.incomingBidirectionalStreams) {
|
||||||
|
bidi.readable.pipeTo(bidi.writable).catch(() => {});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
for await (const stream of wt.incomingUnidirectionalStreams) {
|
||||||
|
const out = await wt.createUnidirectionalStream();
|
||||||
|
stream.pipeTo(out).catch(() => {});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
wt.datagrams.readable.pipeTo(wt.datagrams.writable);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
const client = new WebTransport(`https://localhost:${server.addr.port}/path`, {
|
||||||
|
serverCertificateHashes: [{
|
||||||
|
algorithm: "sha-256",
|
||||||
|
value: certHash,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
client.ready.then(async () => {
|
||||||
|
const bi = await client.createBidirectionalStream();
|
||||||
|
|
||||||
|
{
|
||||||
|
const writer = bi.writable.getWriter();
|
||||||
|
await writer.write(new Uint8Array([1, 0, 1, 0]));
|
||||||
|
writer.releaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const reader = bi.readable.getReader();
|
||||||
|
assertEquals(await reader.read(), {
|
||||||
|
value: new Uint8Array([1, 0, 1, 0]),
|
||||||
|
done: false,
|
||||||
|
});
|
||||||
|
reader.releaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const uni = await client.createUnidirectionalStream();
|
||||||
|
const writer = uni.getWriter();
|
||||||
|
await writer.write(new Uint8Array([0, 2, 0, 2]));
|
||||||
|
writer.releaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const uni =
|
||||||
|
(await client.incomingUnidirectionalStreams.getReader().read()).value;
|
||||||
|
const reader = uni.getReader();
|
||||||
|
assertEquals(await reader.read(), {
|
||||||
|
value: new Uint8Array([0, 2, 0, 2]),
|
||||||
|
done: false,
|
||||||
|
});
|
||||||
|
reader.releaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.datagrams.writable.getWriter().write(
|
||||||
|
new Uint8Array([3, 0, 3, 0]),
|
||||||
|
);
|
||||||
|
assertEquals(await client.datagrams.readable.getReader().read(), {
|
||||||
|
value: new Uint8Array([3, 0, 3, 0]),
|
||||||
|
done: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
client.close();
|
||||||
|
server.close();
|
||||||
|
});
|
|
@ -233,6 +233,7 @@
|
||||||
"ext:deno_web/14_compression.js": "../ext/web/14_compression.js",
|
"ext:deno_web/14_compression.js": "../ext/web/14_compression.js",
|
||||||
"ext:deno_web/15_performance.js": "../ext/web/15_performance.js",
|
"ext:deno_web/15_performance.js": "../ext/web/15_performance.js",
|
||||||
"ext:deno_web/16_image_data.js": "../ext/web/16_image_data.js",
|
"ext:deno_web/16_image_data.js": "../ext/web/16_image_data.js",
|
||||||
|
"ext:deno_web/webtransport.js": "../ext/web/webtransport.js",
|
||||||
"ext:deno_webidl/00_webidl.js": "../ext/webidl/00_webidl.js",
|
"ext:deno_webidl/00_webidl.js": "../ext/webidl/00_webidl.js",
|
||||||
"ext:deno_websocket/01_websocket.js": "../ext/websocket/01_websocket.js",
|
"ext:deno_websocket/01_websocket.js": "../ext/websocket/01_websocket.js",
|
||||||
"ext:deno_websocket/02_websocketstream.js": "../ext/websocket/02_websocketstream.js",
|
"ext:deno_websocket/02_websocketstream.js": "../ext/websocket/02_websocketstream.js",
|
||||||
|
|
Loading…
Add table
Reference in a new issue