diff --git a/Cargo.lock b/Cargo.lock index 5e7fed2724..8c946a9de9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -536,6 +536,7 @@ dependencies = [ "deno_doc", "deno_fetch", "deno_lint", + "deno_net", "deno_runtime", "deno_timers", "deno_url", @@ -702,17 +703,37 @@ dependencies = [ "swc_ecmascript", ] +[[package]] +name = "deno_net" +version = "0.1.0" +dependencies = [ + "bytes", + "deno_core", + "http", + "hyper", + "lazy_static", + "log", + "rustls", + "serde", + "tokio", + "tokio-util", + "trust-dns-proto", + "trust-dns-resolver", + "webpki", + "webpki-roots", +] + [[package]] name = "deno_runtime" version = "0.18.0" dependencies = [ "atty", - "bytes", "deno_broadcast_channel", "deno_console", "deno_core", "deno_crypto", "deno_fetch", + "deno_net", "deno_timers", "deno_url", "deno_web", @@ -735,18 +756,12 @@ dependencies = [ "percent-encoding", "regex", "ring", - "rustls", "serde", "sys-info", "termcolor", "test_util", "tokio", - "tokio-util", - "trust-dns-proto", - "trust-dns-resolver", "uuid", - "webpki", - "webpki-roots", "winapi 0.3.9", "winres", ] diff --git a/Cargo.toml b/Cargo.toml index 178d9de9cf..d7025513cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "extensions/console", "extensions/crypto", "extensions/fetch", + "extensions/net", "extensions/timers", "extensions/url", "extensions/web", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 8052f06a5e..c46bfb17a8 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -25,6 +25,7 @@ deno_console = { version = "0.10.0", path = "../extensions/console" } deno_core = { version = "0.92.0", path = "../core" } deno_crypto = { version = "0.24.0", path = "../extensions/crypto" } deno_fetch = { version = "0.32.0", path = "../extensions/fetch" } +deno_net = { version = "0.1.0", path = "../extensions/net" } deno_timers = { version = "0.8.0", path = "../extensions/timers" } deno_url = { version = "0.10.0", path = "../extensions/url" } deno_web = { version = "0.41.0", path = "../extensions/web" } diff --git a/cli/build.rs b/cli/build.rs index 5e872ab2ce..f932d5eff3 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -68,6 +68,9 @@ fn create_compiler_snapshot( "deno.broadcast_channel", deno_broadcast_channel::get_declaration(), ); + op_crate_libs.insert("deno.net", deno_net::get_declaration()); + op_crate_libs + .insert("deno.net_unstable", deno_net::get_unstable_declaration()); // ensure we invalidate the build properly. for (_, path) in op_crate_libs.iter() { @@ -302,6 +305,14 @@ fn main() { "cargo:rustc-env=DENO_BROADCAST_CHANNEL_LIB_PATH={}", deno_broadcast_channel::get_declaration().display() ); + println!( + "cargo:rustc-env=DENO_NET_LIB_PATH={}", + deno_net::get_declaration().display() + ); + println!( + "cargo:rustc-env=DENO_NET_UNSTABLE_LIB_PATH={}", + deno_net::get_unstable_declaration().display() + ); println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap()); println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); diff --git a/cli/dts/lib.deno.ns.d.ts b/cli/dts/lib.deno.ns.d.ts index e838585865..e1c558f379 100644 --- a/cli/dts/lib.deno.ns.d.ts +++ b/cli/dts/lib.deno.ns.d.ts @@ -2,6 +2,7 @@ /// /// +/// /** Deno provides extra properties on `import.meta`. These are included here * to ensure that these are still available when using the Deno namespace in @@ -1784,149 +1785,6 @@ declare namespace Deno { * Requires `allow-write` permission. */ export function truncate(name: string, len?: number): Promise; - export interface NetAddr { - transport: "tcp" | "udp"; - hostname: string; - port: number; - } - - export interface UnixAddr { - transport: "unix" | "unixpacket"; - path: string; - } - - export type Addr = NetAddr | UnixAddr; - - /** A generic network listener for stream-oriented protocols. */ - export interface Listener extends AsyncIterable { - /** Waits for and resolves to the next connection to the `Listener`. */ - accept(): Promise; - /** Close closes the listener. Any pending accept promises will be rejected - * with errors. */ - close(): void; - /** Return the address of the `Listener`. */ - readonly addr: Addr; - - /** Return the rid of the `Listener`. */ - readonly rid: number; - - [Symbol.asyncIterator](): AsyncIterableIterator; - } - - export interface Conn extends Reader, Writer, Closer { - /** The local address of the connection. */ - readonly localAddr: Addr; - /** The remote address of the connection. */ - readonly remoteAddr: Addr; - /** The resource ID of the connection. */ - readonly rid: number; - /** Shuts down (`shutdown(2)`) the write side of the connection. Most - * callers should just use `close()`. */ - closeWrite(): Promise; - } - - export interface ListenOptions { - /** The port to listen on. */ - port: number; - /** A literal IP address or host name that can be resolved to an IP address. - * If not specified, defaults to `0.0.0.0`. */ - hostname?: string; - } - - /** Listen announces on the local transport address. - * - * ```ts - * const listener1 = Deno.listen({ port: 80 }) - * const listener2 = Deno.listen({ hostname: "192.0.2.1", port: 80 }) - * const listener3 = Deno.listen({ hostname: "[2001:db8::1]", port: 80 }); - * const listener4 = Deno.listen({ hostname: "golang.org", port: 80, transport: "tcp" }); - * ``` - * - * Requires `allow-net` permission. */ - export function listen( - options: ListenOptions & { transport?: "tcp" }, - ): Listener; - - export interface ListenTlsOptions extends ListenOptions { - /** Server certificate file. */ - certFile: string; - /** Server public key file. */ - keyFile: string; - - transport?: "tcp"; - } - - /** Listen announces on the local transport address over TLS (transport layer - * security). - * - * ```ts - * const lstnr = Deno.listenTls({ port: 443, certFile: "./server.crt", keyFile: "./server.key" }); - * ``` - * - * Requires `allow-net` permission. */ - export function listenTls(options: ListenTlsOptions): Listener; - - export interface ConnectOptions { - /** The port to connect to. */ - port: number; - /** A literal IP address or host name that can be resolved to an IP address. - * If not specified, defaults to `127.0.0.1`. */ - hostname?: string; - transport?: "tcp"; - } - - /** - * Connects to the hostname (default is "127.0.0.1") and port on the named - * transport (default is "tcp"), and resolves to the connection (`Conn`). - * - * ```ts - * const conn1 = await Deno.connect({ port: 80 }); - * const conn2 = await Deno.connect({ hostname: "192.0.2.1", port: 80 }); - * const conn3 = await Deno.connect({ hostname: "[2001:db8::1]", port: 80 }); - * const conn4 = await Deno.connect({ hostname: "golang.org", port: 80, transport: "tcp" }); - * ``` - * - * Requires `allow-net` permission for "tcp". */ - export function connect(options: ConnectOptions): Promise; - - export interface ConnectTlsOptions { - /** The port to connect to. */ - port: number; - /** A literal IP address or host name that can be resolved to an IP address. - * If not specified, defaults to `127.0.0.1`. */ - hostname?: string; - /** Server certificate file. */ - certFile?: string; - } - - /** Establishes a secure connection over TLS (transport layer security) using - * an optional cert file, hostname (default is "127.0.0.1") and port. The - * cert file is optional and if not included Mozilla's root certificates will - * be used (see also https://github.com/ctz/webpki-roots for specifics) - * - * ```ts - * const conn1 = await Deno.connectTls({ port: 80 }); - * const conn2 = await Deno.connectTls({ certFile: "./certs/my_custom_root_CA.pem", hostname: "192.0.2.1", port: 80 }); - * const conn3 = await Deno.connectTls({ hostname: "[2001:db8::1]", port: 80 }); - * const conn4 = await Deno.connectTls({ certFile: "./certs/my_custom_root_CA.pem", hostname: "golang.org", port: 80}); - * ``` - * - * Requires `allow-net` permission. - */ - export function connectTls(options: ConnectTlsOptions): Promise; - - /** Shutdown socket send operations. - * - * Matches behavior of POSIX shutdown(3). - * - * ```ts - * const listener = Deno.listen({ port: 80 }); - * const conn = await listener.accept(); - * Deno.shutdown(conn.rid); - * ``` - */ - export function shutdown(rid: number): Promise; - export interface Metrics { opsDispatched: number; opsDispatchedSync: number; diff --git a/cli/dts/lib.deno.unstable.d.ts b/cli/dts/lib.deno.unstable.d.ts index 1d01a748ef..ac03e695cb 100644 --- a/cli/dts/lib.deno.unstable.d.ts +++ b/cli/dts/lib.deno.unstable.d.ts @@ -2,6 +2,7 @@ /// /// +/// declare namespace Deno { /** @@ -812,232 +813,6 @@ declare namespace Deno { mtime: number | Date, ): Promise; - /** The type of the resource record. - * Only the listed types are supported currently. */ - export type RecordType = - | "A" - | "AAAA" - | "ANAME" - | "CNAME" - | "MX" - | "PTR" - | "SRV" - | "TXT"; - - export interface ResolveDnsOptions { - /** The name server to be used for lookups. - * If not specified, defaults to the system configuration e.g. `/etc/resolv.conf` on Unix. */ - nameServer?: { - /** The IP address of the name server */ - ipAddr: string; - /** The port number the query will be sent to. - * If not specified, defaults to 53. */ - port?: number; - }; - } - - /** If `resolveDns` is called with "MX" record type specified, it will return an array of this interface. */ - export interface MXRecord { - preference: number; - exchange: string; - } - - /** If `resolveDns` is called with "SRV" record type specified, it will return an array of this interface. */ - export interface SRVRecord { - priority: number; - weight: number; - port: number; - target: string; - } - - export function resolveDns( - query: string, - recordType: "A" | "AAAA" | "ANAME" | "CNAME" | "PTR", - options?: ResolveDnsOptions, - ): Promise; - - export function resolveDns( - query: string, - recordType: "MX", - options?: ResolveDnsOptions, - ): Promise; - - export function resolveDns( - query: string, - recordType: "SRV", - options?: ResolveDnsOptions, - ): Promise; - - export function resolveDns( - query: string, - recordType: "TXT", - options?: ResolveDnsOptions, - ): Promise; - - /** ** UNSTABLE**: new API, yet to be vetted. - * - * Performs DNS resolution against the given query, returning resolved records. - * Fails in the cases such as: - * - the query is in invalid format - * - the options have an invalid parameter, e.g. `nameServer.port` is beyond the range of 16-bit unsigned integer - * - timed out - * - * ```ts - * const a = await Deno.resolveDns("example.com", "A"); - * - * const aaaa = await Deno.resolveDns("example.com", "AAAA", { - * nameServer: { ipAddr: "8.8.8.8", port: 1234 }, - * }); - * ``` - * - * Requires `allow-net` permission. - */ - export function resolveDns( - query: string, - recordType: RecordType, - options?: ResolveDnsOptions, - ): Promise; - - /** **UNSTABLE**: new API, yet to be vetted. - * - * A generic transport listener for message-oriented protocols. */ - export interface DatagramConn extends AsyncIterable<[Uint8Array, Addr]> { - /** **UNSTABLE**: new API, yet to be vetted. - * - * Waits for and resolves to the next message to the `UDPConn`. */ - receive(p?: Uint8Array): Promise<[Uint8Array, Addr]>; - /** UNSTABLE: new API, yet to be vetted. - * - * Sends a message to the target. */ - send(p: Uint8Array, addr: Addr): Promise; - /** UNSTABLE: new API, yet to be vetted. - * - * Close closes the socket. Any pending message promises will be rejected - * with errors. */ - close(): void; - /** Return the address of the `UDPConn`. */ - readonly addr: Addr; - [Symbol.asyncIterator](): AsyncIterableIterator<[Uint8Array, Addr]>; - } - - export interface UnixListenOptions { - /** A Path to the Unix Socket. */ - path: string; - } - - /** **UNSTABLE**: new API, yet to be vetted. - * - * Listen announces on the local transport address. - * - * ```ts - * const listener = Deno.listen({ path: "/foo/bar.sock", transport: "unix" }) - * ``` - * - * Requires `allow-read` and `allow-write` permission. */ - export function listen( - options: UnixListenOptions & { transport: "unix" }, - ): Listener; - - /** **UNSTABLE**: new API, yet to be vetted - * - * Listen announces on the local transport address. - * - * ```ts - * const listener1 = Deno.listenDatagram({ - * port: 80, - * transport: "udp" - * }); - * const listener2 = Deno.listenDatagram({ - * hostname: "golang.org", - * port: 80, - * transport: "udp" - * }); - * ``` - * - * Requires `allow-net` permission. */ - export function listenDatagram( - options: ListenOptions & { transport: "udp" }, - ): DatagramConn; - - /** **UNSTABLE**: new API, yet to be vetted - * - * Listen announces on the local transport address. - * - * ```ts - * const listener = Deno.listenDatagram({ - * path: "/foo/bar.sock", - * transport: "unixpacket" - * }); - * ``` - * - * Requires `allow-read` and `allow-write` permission. */ - export function listenDatagram( - options: UnixListenOptions & { transport: "unixpacket" }, - ): DatagramConn; - - export interface UnixConnectOptions { - transport: "unix"; - path: string; - } - - /** **UNSTABLE**: The unix socket transport is unstable as a new API yet to - * be vetted. The TCP transport is considered stable. - * - * Connects to the hostname (default is "127.0.0.1") and port on the named - * transport (default is "tcp"), and resolves to the connection (`Conn`). - * - * ```ts - * const conn1 = await Deno.connect({ port: 80 }); - * const conn2 = await Deno.connect({ hostname: "192.0.2.1", port: 80 }); - * const conn3 = await Deno.connect({ hostname: "[2001:db8::1]", port: 80 }); - * const conn4 = await Deno.connect({ hostname: "golang.org", port: 80, transport: "tcp" }); - * const conn5 = await Deno.connect({ path: "/foo/bar.sock", transport: "unix" }); - * ``` - * - * Requires `allow-net` permission for "tcp" and `allow-read` for "unix". */ - export function connect( - options: ConnectOptions | UnixConnectOptions, - ): Promise; - - export interface StartTlsOptions { - /** A literal IP address or host name that can be resolved to an IP address. - * If not specified, defaults to `127.0.0.1`. */ - hostname?: string; - /** Server certificate file. */ - certFile?: string; - } - - /** **UNSTABLE**: new API, yet to be vetted. - * - * Start TLS handshake from an existing connection using - * an optional cert file, hostname (default is "127.0.0.1"). The - * cert file is optional and if not included Mozilla's root certificates will - * be used (see also https://github.com/ctz/webpki-roots for specifics) - * Using this function requires that the other end of the connection is - * prepared for TLS handshake. - * - * ```ts - * const conn = await Deno.connect({ port: 80, hostname: "127.0.0.1" }); - * const tlsConn = await Deno.startTls(conn, { certFile: "./certs/my_custom_root_CA.pem", hostname: "localhost" }); - * ``` - * - * Requires `allow-net` permission. - */ - export function startTls( - conn: Conn, - options?: StartTlsOptions, - ): Promise; - - export interface ListenTlsOptions { - /** **UNSTABLE**: new API, yet to be vetted. - * - * Application-Layer Protocol Negotiation (ALPN) protocols to announce to - * the client. If not specified, no ALPN extension will be included in the - * TLS handshake. - */ - alpnProtocols?: string[]; - } - /** **UNSTABLE**: The `signo` argument may change to require the Deno.Signal * enum. * @@ -1182,36 +957,6 @@ declare namespace Deno { bytesReceived: number; } - export interface RequestEvent { - readonly request: Request; - respondWith(r: Response | Promise): Promise; - } - - export interface HttpConn extends AsyncIterable { - readonly rid: number; - - nextRequest(): Promise; - close(): void; - } - - /** **UNSTABLE**: new API, yet to be vetted. - * - * Services HTTP requests given a TCP or TLS socket. - * - * ```ts - * const conn = await Deno.connect({ port: 80, hostname: "127.0.0.1" }); - * const httpConn = Deno.serveHttp(conn); - * const e = await httpConn.nextRequest(); - * if (e) { - * e.respondWith(new Response("Hello World")); - * } - * ``` - * - * If `httpConn.nextRequest()` encounters an error or returns `null` - * then the underlying HttpConn resource is closed automatically. - */ - export function serveHttp(conn: Conn): HttpConn; - /** **UNSTABLE**: New option, yet to be vetted. */ export interface TestDefinition { /** Specifies the permissions that should be used to run the test. diff --git a/cli/main.rs b/cli/main.rs index 38db7d13f3..f1cf67ac4e 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -334,12 +334,14 @@ pub fn get_types(unstable: bool) -> String { crate::tsc::DENO_WEBSTORAGE_LIB, crate::tsc::DENO_CRYPTO_LIB, crate::tsc::DENO_BROADCAST_CHANNEL_LIB, + crate::tsc::DENO_NET_LIB, crate::tsc::SHARED_GLOBALS_LIB, crate::tsc::WINDOW_LIB, ]; if unstable { types.push(crate::tsc::UNSTABLE_NS_LIB); + types.push(crate::tsc::DENO_NET_UNSTABLE_LIB); } types.join("\n") diff --git a/cli/tests/integration/mod.rs b/cli/tests/integration/mod.rs index fab2f20a5e..c11d26dc9c 100644 --- a/cli/tests/integration/mod.rs +++ b/cli/tests/integration/mod.rs @@ -2,9 +2,9 @@ use crate::itest; use deno_core::url; -use deno_runtime::ops::tls::rustls; -use deno_runtime::ops::tls::webpki; -use deno_runtime::ops::tls::TlsStream; +use deno_runtime::deno_net::ops_tls::rustls; +use deno_runtime::deno_net::ops_tls::webpki; +use deno_runtime::deno_net::ops_tls::TlsStream; use std::fs; use std::io::BufReader; use std::io::Cursor; diff --git a/cli/tsc.rs b/cli/tsc.rs index 3e7974a975..59b4ac81a4 100644 --- a/cli/tsc.rs +++ b/cli/tsc.rs @@ -44,6 +44,9 @@ pub static DENO_WEBSTORAGE_LIB: &str = pub static DENO_CRYPTO_LIB: &str = include_str!(env!("DENO_CRYPTO_LIB_PATH")); pub static DENO_BROADCAST_CHANNEL_LIB: &str = include_str!(env!("DENO_BROADCAST_CHANNEL_LIB_PATH")); +pub static DENO_NET_LIB: &str = include_str!(env!("DENO_NET_LIB_PATH")); +pub static DENO_NET_UNSTABLE_LIB: &str = + include_str!(env!("DENO_NET_UNSTABLE_LIB_PATH")); pub static SHARED_GLOBALS_LIB: &str = include_str!("dts/lib.deno.shared_globals.d.ts"); pub static WINDOW_LIB: &str = include_str!("dts/lib.deno.window.d.ts"); diff --git a/core/core.js b/core/core.js index 8e06a3e45e..9ce5638699 100644 --- a/core/core.js +++ b/core/core.js @@ -132,6 +132,23 @@ opSync("op_print", str, isErr); } + // Some "extensions" rely on "BadResource" and "Interrupted" errors in the + // JS code (eg. "deno_net") so they are provided in "Deno.core" but later + // reexported on "Deno.errors" + class BadResource extends Error { + constructor(msg) { + super(msg); + this.name = "BadResource"; + } + } + + class Interrupted extends Error { + constructor(msg) { + super(msg); + this.name = "Interrupted"; + } + } + // Provide bootstrap namespace window.__bootstrap = {}; // Extra Deno.core.* exports @@ -146,5 +163,7 @@ registerErrorClass, handleAsyncMsgFromRust, syncOpsCache, + BadResource, + Interrupted, }); })(this); diff --git a/runtime/js/30_net.js b/extensions/net/01_net.js similarity index 89% rename from runtime/js/30_net.js rename to extensions/net/01_net.js index 2d4b1e48e5..9a531bd947 100644 --- a/runtime/js/30_net.js +++ b/extensions/net/01_net.js @@ -3,11 +3,25 @@ ((window) => { const core = window.Deno.core; - const { errors } = window.__bootstrap.errors; - const { read, write } = window.__bootstrap.io; + const { BadResource } = core; + + async function read( + rid, + buffer, + ) { + if (buffer.length === 0) { + return 0; + } + const nread = await core.opAsync("op_net_read_async", rid, buffer); + return nread === 0 ? null : nread; + } + + async function write(rid, data) { + return await core.opAsync("op_net_write_async", rid, data); + } function shutdown(rid) { - return core.opAsync("op_shutdown", rid); + return core.opAsync("op_net_shutdown", rid); } function opAccept(rid, transport) { @@ -104,7 +118,7 @@ try { conn = await this.accept(); } catch (error) { - if (error instanceof errors.BadResource) { + if (error instanceof BadResource) { return { value: undefined, done: true }; } throw error; @@ -171,7 +185,7 @@ try { yield await this.receive(); } catch (err) { - if (err instanceof errors.BadResource) { + if (err instanceof BadResource) { break; } throw err; diff --git a/runtime/js/40_tls.js b/extensions/net/02_tls.js similarity index 100% rename from runtime/js/40_tls.js rename to extensions/net/02_tls.js diff --git a/runtime/js/40_http.js b/extensions/net/03_http.js similarity index 96% rename from runtime/js/40_http.js rename to extensions/net/03_http.js index 5aa57238b6..d5054bd1a2 100644 --- a/runtime/js/40_http.js +++ b/extensions/net/03_http.js @@ -5,8 +5,8 @@ const { InnerBody } = window.__bootstrap.fetchBody; const { Response, fromInnerRequest, toInnerResponse, newInnerRequest } = window.__bootstrap.fetch; - const errors = window.__bootstrap.errors.errors; const core = window.Deno.core; + const { BadResource, Interrupted } = core; const { ReadableStream } = window.__bootstrap.streams; const abortSignal = window.__bootstrap.abortSignal; @@ -42,9 +42,9 @@ // a generic `BadResource` error. Instead store this error and replace // those with it. this[connErrorSymbol] = error; - if (error instanceof errors.BadResource) { + if (error instanceof BadResource) { return null; - } else if (error instanceof errors.Interrupted) { + } else if (error instanceof Interrupted) { return null; } else if (error.message.includes("connection closed")) { return null; @@ -159,7 +159,7 @@ ], respBody instanceof Uint8Array ? respBody : null); } catch (error) { const connError = httpConn[connErrorSymbol]; - if (error instanceof errors.BadResource && connError != null) { + if (error instanceof BadResource && connError != null) { // deno-lint-ignore no-ex-assign error = new connError.constructor(connError.message); } @@ -192,7 +192,7 @@ ); } catch (error) { const connError = httpConn[connErrorSymbol]; - if (error instanceof errors.BadResource && connError != null) { + if (error instanceof BadResource && connError != null) { // deno-lint-ignore no-ex-assign error = new connError.constructor(connError.message); } diff --git a/runtime/js/40_net_unstable.js b/extensions/net/04_net_unstable.js similarity index 100% rename from runtime/js/40_net_unstable.js rename to extensions/net/04_net_unstable.js diff --git a/extensions/net/Cargo.toml b/extensions/net/Cargo.toml new file mode 100644 index 0000000000..9d5e97bbba --- /dev/null +++ b/extensions/net/Cargo.toml @@ -0,0 +1,31 @@ +# Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +[package] +name = "deno_net" +version = "0.1.0" +edition = "2018" +description = "Networking for Deno" +authors = ["the Deno authors"] +license = "MIT" +readme = "README.md" +repository = "https://github.com/denoland/deno" + +[lib] +path = "lib.rs" + +[dependencies] +deno_core = { version = "0.92.0", path = "../../core" } + +bytes = "1" +log = "0.4.14" +lazy_static = "1.4.0" +http = "0.2.3" +hyper = { version = "0.14.9", features = ["server", "stream", "http1", "http2", "runtime"] } +rustls = "0.19.0" +serde = { version = "1.0.125", features = ["derive"] } +tokio = { version = "1.7.1", features = ["full"] } +tokio-util = { version = "0.6", features = ["io"] } +webpki = "0.21.4" +webpki-roots = "0.21.1" +trust-dns-proto = "0.20.3" +trust-dns-resolver = { version = "0.20.3", features = ["tokio-runtime", "serde-config"] } diff --git a/extensions/net/README.md b/extensions/net/README.md new file mode 100644 index 0000000000..cdd8923e1c --- /dev/null +++ b/extensions/net/README.md @@ -0,0 +1,30 @@ +# deno_net + +This crate implements networking APIs. + +This crate depends on following extensions: + +- "deno_web" +- "deno_fetch" + +Following ops are provided: + +- "op_net_read_async" +- "op_net_write_async" +- "op_net_shutdown" +- "op_accept" +- "op_connect" +- "op_listen" +- "op_datagram_receive" +- "op_datagram_send" +- "op_dns_resolve" +- "op_start_tls" +- "op_connect_tls" +- "op_listen_tls" +- "op_accept_tls" +- "op_http_start" +- "op_http_request_next" +- "op_http_request_read" +- "op_http_response" +- "op_http_response_write" +- "op_http_response_close" diff --git a/extensions/net/io.rs b/extensions/net/io.rs new file mode 100644 index 0000000000..fc10d7e990 --- /dev/null +++ b/extensions/net/io.rs @@ -0,0 +1,232 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +use crate::ops_tls as tls; +use deno_core::error::null_opbuf; +use deno_core::error::AnyError; +use deno_core::error::{bad_resource_id, not_supported}; +use deno_core::op_async; +use deno_core::AsyncMutFuture; +use deno_core::AsyncRefCell; +use deno_core::CancelHandle; +use deno_core::CancelTryFuture; +use deno_core::OpPair; +use deno_core::OpState; +use deno_core::RcRef; +use deno_core::Resource; +use deno_core::ResourceId; +use deno_core::ZeroCopyBuf; +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; +use tokio::io::AsyncRead; +use tokio::io::AsyncReadExt; +use tokio::io::AsyncWrite; +use tokio::io::AsyncWriteExt; +use tokio::net::tcp; + +#[cfg(unix)] +use tokio::net::unix; + +pub fn init() -> Vec { + vec![ + ("op_net_read_async", op_async(op_read_async)), + ("op_net_write_async", op_async(op_write_async)), + ("op_net_shutdown", op_async(op_shutdown)), + ] +} + +/// A full duplex resource has a read and write ends that are completely +/// independent, like TCP/Unix sockets and TLS streams. +#[derive(Debug)] +pub struct FullDuplexResource { + rd: AsyncRefCell, + wr: AsyncRefCell, + // When a full-duplex resource is closed, all pending 'read' ops are + // canceled, while 'write' ops are allowed to complete. Therefore only + // 'read' futures should be attached to this cancel handle. + cancel_handle: CancelHandle, +} + +impl FullDuplexResource +where + R: AsyncRead + Unpin + 'static, + W: AsyncWrite + Unpin + 'static, +{ + pub fn new((rd, wr): (R, W)) -> Self { + Self { + rd: rd.into(), + wr: wr.into(), + cancel_handle: Default::default(), + } + } + + pub fn into_inner(self) -> (R, W) { + (self.rd.into_inner(), self.wr.into_inner()) + } + + pub fn rd_borrow_mut(self: &Rc) -> AsyncMutFuture { + RcRef::map(self, |r| &r.rd).borrow_mut() + } + + pub fn wr_borrow_mut(self: &Rc) -> AsyncMutFuture { + RcRef::map(self, |r| &r.wr).borrow_mut() + } + + pub fn cancel_handle(self: &Rc) -> RcRef { + RcRef::map(self, |r| &r.cancel_handle) + } + + pub fn cancel_read_ops(&self) { + self.cancel_handle.cancel() + } + + pub async fn read( + self: &Rc, + buf: &mut [u8], + ) -> Result { + let mut rd = self.rd_borrow_mut().await; + let nread = rd.read(buf).try_or_cancel(self.cancel_handle()).await?; + Ok(nread) + } + + pub async fn write(self: &Rc, buf: &[u8]) -> Result { + let mut wr = self.wr_borrow_mut().await; + let nwritten = wr.write(buf).await?; + Ok(nwritten) + } + + pub async fn shutdown(self: &Rc) -> Result<(), AnyError> { + let mut wr = self.wr_borrow_mut().await; + wr.shutdown().await?; + Ok(()) + } +} + +pub type TcpStreamResource = + FullDuplexResource; + +impl Resource for TcpStreamResource { + fn name(&self) -> Cow { + "tcpStream".into() + } + + fn close(self: Rc) { + self.cancel_read_ops(); + } +} + +pub type TlsStreamResource = FullDuplexResource; + +impl Resource for TlsStreamResource { + fn name(&self) -> Cow { + "tlsStream".into() + } + + fn close(self: Rc) { + self.cancel_read_ops(); + } +} + +#[cfg(unix)] +pub type UnixStreamResource = + FullDuplexResource; + +#[cfg(not(unix))] +pub struct UnixStreamResource; + +#[cfg(not(unix))] +impl UnixStreamResource { + pub async fn read( + self: &Rc, + _buf: &mut [u8], + ) -> Result { + unreachable!() + } + pub async fn write(self: &Rc, _buf: &[u8]) -> Result { + unreachable!() + } + pub async fn shutdown(self: &Rc) -> Result<(), AnyError> { + unreachable!() + } + pub fn cancel_read_ops(&self) { + unreachable!() + } +} + +impl Resource for UnixStreamResource { + fn name(&self) -> Cow { + "unixStream".into() + } + + fn close(self: Rc) { + self.cancel_read_ops(); + } +} + +async fn op_read_async( + state: Rc>, + rid: ResourceId, + buf: Option, +) -> Result { + let buf = &mut buf.ok_or_else(null_opbuf)?; + let resource = state + .borrow() + .resource_table + .get_any(rid) + .ok_or_else(bad_resource_id)?; + let nread = if let Some(s) = resource.downcast_rc::() { + s.read(buf).await? + } else if let Some(s) = resource.downcast_rc::() { + s.read(buf).await? + } else if let Some(s) = resource.downcast_rc::() { + s.read(buf).await? + } else { + return Err(not_supported()); + }; + Ok(nread as u32) +} + +async fn op_write_async( + state: Rc>, + rid: ResourceId, + buf: Option, +) -> Result { + let buf = &buf.ok_or_else(null_opbuf)?; + let resource = state + .borrow() + .resource_table + .get_any(rid) + .ok_or_else(bad_resource_id)?; + let nwritten = if let Some(s) = resource.downcast_rc::() { + s.write(buf).await? + } else if let Some(s) = resource.downcast_rc::() { + s.write(buf).await? + } else if let Some(s) = resource.downcast_rc::() { + s.write(buf).await? + } else { + return Err(not_supported()); + }; + Ok(nwritten as u32) +} + +async fn op_shutdown( + state: Rc>, + rid: ResourceId, + _: (), +) -> Result<(), AnyError> { + let resource = state + .borrow() + .resource_table + .get_any(rid) + .ok_or_else(bad_resource_id)?; + if let Some(s) = resource.downcast_rc::() { + s.shutdown().await?; + } else if let Some(s) = resource.downcast_rc::() { + s.shutdown().await?; + } else if let Some(s) = resource.downcast_rc::() { + s.shutdown().await?; + } else { + return Err(not_supported()); + } + Ok(()) +} diff --git a/extensions/net/lib.deno_net.d.ts b/extensions/net/lib.deno_net.d.ts new file mode 100644 index 0000000000..25397f9607 --- /dev/null +++ b/extensions/net/lib.deno_net.d.ts @@ -0,0 +1,149 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +/// +/// + +declare namespace Deno { + export interface NetAddr { + transport: "tcp" | "udp"; + hostname: string; + port: number; + } + + export interface UnixAddr { + transport: "unix" | "unixpacket"; + path: string; + } + + export type Addr = NetAddr | UnixAddr; + + /** A generic network listener for stream-oriented protocols. */ + export interface Listener extends AsyncIterable { + /** Waits for and resolves to the next connection to the `Listener`. */ + accept(): Promise; + /** Close closes the listener. Any pending accept promises will be rejected + * with errors. */ + close(): void; + /** Return the address of the `Listener`. */ + readonly addr: Addr; + + /** Return the rid of the `Listener`. */ + readonly rid: number; + + [Symbol.asyncIterator](): AsyncIterableIterator; + } + + export interface Conn extends Reader, Writer, Closer { + /** The local address of the connection. */ + readonly localAddr: Addr; + /** The remote address of the connection. */ + readonly remoteAddr: Addr; + /** The resource ID of the connection. */ + readonly rid: number; + /** Shuts down (`shutdown(2)`) the write side of the connection. Most + * callers should just use `close()`. */ + closeWrite(): Promise; + } + + export interface ListenOptions { + /** The port to listen on. */ + port: number; + /** A literal IP address or host name that can be resolved to an IP address. + * If not specified, defaults to `0.0.0.0`. */ + hostname?: string; + } + + /** Listen announces on the local transport address. + * + * ```ts + * const listener1 = Deno.listen({ port: 80 }) + * const listener2 = Deno.listen({ hostname: "192.0.2.1", port: 80 }) + * const listener3 = Deno.listen({ hostname: "[2001:db8::1]", port: 80 }); + * const listener4 = Deno.listen({ hostname: "golang.org", port: 80, transport: "tcp" }); + * ``` + * + * Requires `allow-net` permission. */ + export function listen( + options: ListenOptions & { transport?: "tcp" }, + ): Listener; + + export interface ListenTlsOptions extends ListenOptions { + /** Server certificate file. */ + certFile: string; + /** Server public key file. */ + keyFile: string; + + transport?: "tcp"; + } + + /** Listen announces on the local transport address over TLS (transport layer + * security). + * + * ```ts + * const lstnr = Deno.listenTls({ port: 443, certFile: "./server.crt", keyFile: "./server.key" }); + * ``` + * + * Requires `allow-net` permission. */ + export function listenTls(options: ListenTlsOptions): Listener; + + export interface ConnectOptions { + /** The port to connect to. */ + port: number; + /** A literal IP address or host name that can be resolved to an IP address. + * If not specified, defaults to `127.0.0.1`. */ + hostname?: string; + transport?: "tcp"; + } + + /** + * Connects to the hostname (default is "127.0.0.1") and port on the named + * transport (default is "tcp"), and resolves to the connection (`Conn`). + * + * ```ts + * const conn1 = await Deno.connect({ port: 80 }); + * const conn2 = await Deno.connect({ hostname: "192.0.2.1", port: 80 }); + * const conn3 = await Deno.connect({ hostname: "[2001:db8::1]", port: 80 }); + * const conn4 = await Deno.connect({ hostname: "golang.org", port: 80, transport: "tcp" }); + * ``` + * + * Requires `allow-net` permission for "tcp". */ + export function connect(options: ConnectOptions): Promise; + + export interface ConnectTlsOptions { + /** The port to connect to. */ + port: number; + /** A literal IP address or host name that can be resolved to an IP address. + * If not specified, defaults to `127.0.0.1`. */ + hostname?: string; + /** Server certificate file. */ + certFile?: string; + } + + /** Establishes a secure connection over TLS (transport layer security) using + * an optional cert file, hostname (default is "127.0.0.1") and port. The + * cert file is optional and if not included Mozilla's root certificates will + * be used (see also https://github.com/ctz/webpki-roots for specifics) + * + * ```ts + * const conn1 = await Deno.connectTls({ port: 80 }); + * const conn2 = await Deno.connectTls({ certFile: "./certs/my_custom_root_CA.pem", hostname: "192.0.2.1", port: 80 }); + * const conn3 = await Deno.connectTls({ hostname: "[2001:db8::1]", port: 80 }); + * const conn4 = await Deno.connectTls({ certFile: "./certs/my_custom_root_CA.pem", hostname: "golang.org", port: 80}); + * ``` + * + * Requires `allow-net` permission. + */ + export function connectTls(options: ConnectTlsOptions): Promise; + + /** Shutdown socket send operations. + * + * Matches behavior of POSIX shutdown(3). + * + * ```ts + * const listener = Deno.listen({ port: 80 }); + * const conn = await listener.accept(); + * Deno.shutdown(conn.rid); + * ``` + */ + export function shutdown(rid: number): Promise; +} diff --git a/extensions/net/lib.deno_net.unstable.d.ts b/extensions/net/lib.deno_net.unstable.d.ts new file mode 100644 index 0000000000..905a7acc17 --- /dev/null +++ b/extensions/net/lib.deno_net.unstable.d.ts @@ -0,0 +1,262 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +/// +/// + +declare namespace Deno { + /** The type of the resource record. + * Only the listed types are supported currently. */ + export type RecordType = + | "A" + | "AAAA" + | "ANAME" + | "CNAME" + | "MX" + | "PTR" + | "SRV" + | "TXT"; + + export interface ResolveDnsOptions { + /** The name server to be used for lookups. + * If not specified, defaults to the system configuration e.g. `/etc/resolv.conf` on Unix. */ + nameServer?: { + /** The IP address of the name server */ + ipAddr: string; + /** The port number the query will be sent to. + * If not specified, defaults to 53. */ + port?: number; + }; + } + + /** If `resolveDns` is called with "MX" record type specified, it will return an array of this interface. */ + export interface MXRecord { + preference: number; + exchange: string; + } + + /** If `resolveDns` is called with "SRV" record type specified, it will return an array of this interface. */ + export interface SRVRecord { + priority: number; + weight: number; + port: number; + target: string; + } + + export function resolveDns( + query: string, + recordType: "A" | "AAAA" | "ANAME" | "CNAME" | "PTR", + options?: ResolveDnsOptions, + ): Promise; + + export function resolveDns( + query: string, + recordType: "MX", + options?: ResolveDnsOptions, + ): Promise; + + export function resolveDns( + query: string, + recordType: "SRV", + options?: ResolveDnsOptions, + ): Promise; + + export function resolveDns( + query: string, + recordType: "TXT", + options?: ResolveDnsOptions, + ): Promise; + + /** ** UNSTABLE**: new API, yet to be vetted. +* +* Performs DNS resolution against the given query, returning resolved records. +* Fails in the cases such as: +* - the query is in invalid format +* - the options have an invalid parameter, e.g. `nameServer.port` is beyond the range of 16-bit unsigned integer +* - timed out +* +* ```ts +* const a = await Deno.resolveDns("example.com", "A"); +* +* const aaaa = await Deno.resolveDns("example.com", "AAAA", { +* nameServer: { ipAddr: "8.8.8.8", port: 1234 }, +* }); +* ``` +* +* Requires `allow-net` permission. + */ + export function resolveDns( + query: string, + recordType: RecordType, + options?: ResolveDnsOptions, + ): Promise; + + /** **UNSTABLE**: new API, yet to be vetted. +* +* A generic transport listener for message-oriented protocols. */ + export interface DatagramConn extends AsyncIterable<[Uint8Array, Addr]> { + /** **UNSTABLE**: new API, yet to be vetted. + * + * Waits for and resolves to the next message to the `UDPConn`. */ + receive(p?: Uint8Array): Promise<[Uint8Array, Addr]>; + /** UNSTABLE: new API, yet to be vetted. + * + * Sends a message to the target. */ + send(p: Uint8Array, addr: Addr): Promise; + /** UNSTABLE: new API, yet to be vetted. + * + * Close closes the socket. Any pending message promises will be rejected + * with errors. */ + close(): void; + /** Return the address of the `UDPConn`. */ + readonly addr: Addr; + [Symbol.asyncIterator](): AsyncIterableIterator<[Uint8Array, Addr]>; + } + + export interface UnixListenOptions { + /** A Path to the Unix Socket. */ + path: string; + } + + /** **UNSTABLE**: new API, yet to be vetted. +* +* Listen announces on the local transport address. +* +* ```ts +* const listener = Deno.listen({ path: "/foo/bar.sock", transport: "unix" }) +* ``` +* +* Requires `allow-read` and `allow-write` permission. */ + export function listen( + options: UnixListenOptions & { transport: "unix" }, + ): Listener; + + /** **UNSTABLE**: new API, yet to be vetted +* +* Listen announces on the local transport address. +* +* ```ts +* const listener1 = Deno.listenDatagram({ +* port: 80, +* transport: "udp" +* }); +* const listener2 = Deno.listenDatagram({ +* hostname: "golang.org", +* port: 80, +* transport: "udp" +* }); +* ``` +* +* Requires `allow-net` permission. */ + export function listenDatagram( + options: ListenOptions & { transport: "udp" }, + ): DatagramConn; + + /** **UNSTABLE**: new API, yet to be vetted +* +* Listen announces on the local transport address. +* +* ```ts +* const listener = Deno.listenDatagram({ +* path: "/foo/bar.sock", +* transport: "unixpacket" +* }); +* ``` +* +* Requires `allow-read` and `allow-write` permission. */ + export function listenDatagram( + options: UnixListenOptions & { transport: "unixpacket" }, + ): DatagramConn; + + export interface UnixConnectOptions { + transport: "unix"; + path: string; + } + + /** **UNSTABLE**: The unix socket transport is unstable as a new API yet to +* be vetted. The TCP transport is considered stable. +* +* Connects to the hostname (default is "127.0.0.1") and port on the named +* transport (default is "tcp"), and resolves to the connection (`Conn`). +* +* ```ts +* const conn1 = await Deno.connect({ port: 80 }); +* const conn2 = await Deno.connect({ hostname: "192.0.2.1", port: 80 }); +* const conn3 = await Deno.connect({ hostname: "[2001:db8::1]", port: 80 }); +* const conn4 = await Deno.connect({ hostname: "golang.org", port: 80, transport: "tcp" }); +* const conn5 = await Deno.connect({ path: "/foo/bar.sock", transport: "unix" }); +* ``` +* +* Requires `allow-net` permission for "tcp" and `allow-read` for "unix". */ + export function connect( + options: ConnectOptions | UnixConnectOptions, + ): Promise; + + export interface StartTlsOptions { + /** A literal IP address or host name that can be resolved to an IP address. + * If not specified, defaults to `127.0.0.1`. */ + hostname?: string; + /** Server certificate file. */ + certFile?: string; + } + + /** **UNSTABLE**: new API, yet to be vetted. +* +* Start TLS handshake from an existing connection using +* an optional cert file, hostname (default is "127.0.0.1"). The +* cert file is optional and if not included Mozilla's root certificates will +* be used (see also https://github.com/ctz/webpki-roots for specifics) +* Using this function requires that the other end of the connection is +* prepared for TLS handshake. +* +* ```ts +* const conn = await Deno.connect({ port: 80, hostname: "127.0.0.1" }); +* const tlsConn = await Deno.startTls(conn, { certFile: "./certs/my_custom_root_CA.pem", hostname: "localhost" }); +* ``` +* +* Requires `allow-net` permission. + */ + export function startTls( + conn: Conn, + options?: StartTlsOptions, + ): Promise; + + export interface ListenTlsOptions { + /** **UNSTABLE**: new API, yet to be vetted. + * + * Application-Layer Protocol Negotiation (ALPN) protocols to announce to + * the client. If not specified, no ALPN extension will be included in the + * TLS handshake. + */ + alpnProtocols?: string[]; + } + + export interface RequestEvent { + readonly request: Request; + respondWith(r: Response | Promise): Promise; + } + + export interface HttpConn extends AsyncIterable { + readonly rid: number; + + nextRequest(): Promise; + close(): void; + } + + /** **UNSTABLE**: new API, yet to be vetted. + * + * Services HTTP requests given a TCP or TLS socket. + * + * ```ts + * const conn = await Deno.connect({ port: 80, hostname: "127.0.0.1" }); + * const httpConn = Deno.serveHttp(conn); + * const e = await httpConn.nextRequest(); + * if (e) { + * e.respondWith(new Response("Hello World")); + * } + * ``` + * + * If `httpConn.nextRequest()` encounters an error or returns `null` + * then the underlying HttpConn resource is closed automatically. + */ + export function serveHttp(conn: Conn): HttpConn; +} diff --git a/extensions/net/lib.rs b/extensions/net/lib.rs new file mode 100644 index 0000000000..d1e836fce3 --- /dev/null +++ b/extensions/net/lib.rs @@ -0,0 +1,113 @@ +// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. + +pub mod io; +pub mod ops; +pub mod ops_http; +pub mod ops_tls; +#[cfg(unix)] +pub mod ops_unix; +pub mod resolve_addr; + +use deno_core::error::AnyError; +use deno_core::include_js_files; +use deno_core::Extension; +use deno_core::OpState; +use std::cell::RefCell; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; + +pub trait NetPermissions { + fn check_net>( + &mut self, + _host: &(T, Option), + ) -> Result<(), AnyError>; + fn check_read(&mut self, _p: &Path) -> Result<(), AnyError>; + fn check_write(&mut self, _p: &Path) -> Result<(), AnyError>; +} + +/// For use with this crate when the user does not want permission checks. +pub struct NoNetPermissions; + +impl NetPermissions for NoNetPermissions { + fn check_net>( + &mut self, + _host: &(T, Option), + ) -> Result<(), AnyError> { + Ok(()) + } + + fn check_read(&mut self, _p: &Path) -> Result<(), AnyError> { + Ok(()) + } + + fn check_write(&mut self, _p: &Path) -> Result<(), AnyError> { + Ok(()) + } +} + +/// `UnstableChecker` is a struct so it can be placed inside `GothamState`; +/// using type alias for a bool could work, but there's a high chance +/// that there might be another type alias pointing to a bool, which +/// would override previously used alias. +pub struct UnstableChecker { + pub unstable: bool, +} + +impl UnstableChecker { + /// Quits the process if the --unstable flag was not provided. + /// + /// This is intentionally a non-recoverable check so that people cannot probe + /// for unstable APIs from stable programs. + // NOTE(bartlomieju): keep in sync with `cli/program_state.rs` + pub fn check_unstable(&self, api_name: &str) { + if !self.unstable { + eprintln!( + "Unstable API '{}'. The --unstable flag must be provided.", + api_name + ); + std::process::exit(70); + } + } +} +/// Helper for checking unstable features. Used for sync ops. +pub fn check_unstable(state: &OpState, api_name: &str) { + state.borrow::().check_unstable(api_name) +} + +/// Helper for checking unstable features. Used for async ops. +pub fn check_unstable2(state: &Rc>, api_name: &str) { + let state = state.borrow(); + state.borrow::().check_unstable(api_name) +} + +pub fn get_declaration() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_net.d.ts") +} + +pub fn get_unstable_declaration() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_net.unstable.d.ts") +} + +pub fn init(unstable: bool) -> Extension { + let mut ops_to_register = vec![]; + ops_to_register.extend(io::init()); + ops_to_register.extend(ops::init::

()); + ops_to_register.extend(ops_tls::init::

()); + ops_to_register.extend(ops_http::init()); + + Extension::builder() + .js(include_js_files!( + prefix "deno:extensions/net", + "01_net.js", + "02_tls.js", + "03_http.js", + "04_net_unstable.js", + )) + .ops(ops_to_register) + .state(move |state| { + state.put(UnstableChecker { unstable }); + Ok(()) + }) + .build() +} diff --git a/runtime/ops/net.rs b/extensions/net/ops.rs similarity index 92% rename from runtime/ops/net.rs rename to extensions/net/ops.rs index c9195aab75..a02bbf91a7 100644 --- a/runtime/ops/net.rs +++ b/extensions/net/ops.rs @@ -1,8 +1,9 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use crate::ops::io::TcpStreamResource; -use crate::permissions::Permissions; + +use crate::io::TcpStreamResource; use crate::resolve_addr::resolve_addr; use crate::resolve_addr::resolve_addr_sync; +use crate::NetPermissions; use deno_core::error::bad_resource; use deno_core::error::custom_error; use deno_core::error::generic_error; @@ -14,7 +15,7 @@ use deno_core::op_sync; use deno_core::AsyncRefCell; use deno_core::CancelHandle; use deno_core::CancelTryFuture; -use deno_core::Extension; +use deno_core::OpPair; use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; @@ -39,23 +40,21 @@ use trust_dns_resolver::system_conf; use trust_dns_resolver::AsyncResolver; #[cfg(unix)] -use super::net_unix; +use super::ops_unix as net_unix; #[cfg(unix)] -use crate::ops::io::UnixStreamResource; +use crate::io::UnixStreamResource; #[cfg(unix)] use std::path::Path; -pub fn init() -> Extension { - Extension::builder() - .ops(vec![ - ("op_accept", op_async(op_accept)), - ("op_connect", op_async(op_connect)), - ("op_listen", op_sync(op_listen)), - ("op_datagram_receive", op_async(op_datagram_receive)), - ("op_datagram_send", op_async(op_datagram_send)), - ("op_dns_resolve", op_async(op_dns_resolve)), - ]) - .build() +pub fn init() -> Vec { + vec![ + ("op_accept", op_async(op_accept)), + ("op_connect", op_async(op_connect::

)), + ("op_listen", op_sync(op_listen::

)), + ("op_datagram_receive", op_async(op_datagram_receive)), + ("op_datagram_send", op_async(op_datagram_send::

)), + ("op_dns_resolve", op_async(op_dns_resolve::

)), + ] } #[derive(Serialize)] @@ -216,11 +215,14 @@ struct SendArgs { transport_args: ArgsEnum, } -async fn op_datagram_send( +async fn op_datagram_send( state: Rc>, args: SendArgs, zero_copy: Option, -) -> Result { +) -> Result +where + NP: NetPermissions + 'static, +{ let zero_copy = zero_copy.ok_or_else(null_opbuf)?; let zero_copy = zero_copy.clone(); @@ -232,9 +234,8 @@ async fn op_datagram_send( } if transport == "udp" => { { let mut s = state.borrow_mut(); - s.borrow_mut::() - .net - .check(&(&args.hostname, Some(args.port)))?; + s.borrow_mut::() + .check_net(&(&args.hostname, Some(args.port)))?; } let addr = resolve_addr(&args.hostname, args.port) .await? @@ -259,7 +260,7 @@ async fn op_datagram_send( let address_path = Path::new(&args.path); { let mut s = state.borrow_mut(); - s.borrow_mut::().write.check(&address_path)?; + s.borrow_mut::().check_write(&address_path)?; } let resource = state .borrow() @@ -285,11 +286,14 @@ struct ConnectArgs { transport_args: ArgsEnum, } -async fn op_connect( +async fn op_connect( state: Rc>, args: ConnectArgs, _: (), -) -> Result { +) -> Result +where + NP: NetPermissions + 'static, +{ match args { ConnectArgs { transport, @@ -298,9 +302,8 @@ async fn op_connect( { let mut state_ = state.borrow_mut(); state_ - .borrow_mut::() - .net - .check(&(&args.hostname, Some(args.port)))?; + .borrow_mut::() + .check_net(&(&args.hostname, Some(args.port)))?; } let addr = resolve_addr(&args.hostname, args.port) .await? @@ -335,14 +338,8 @@ async fn op_connect( super::check_unstable2(&state, "Deno.connect"); { let mut state_ = state.borrow_mut(); - state_ - .borrow_mut::() - .read - .check(&address_path)?; - state_ - .borrow_mut::() - .write - .check(&address_path)?; + state_.borrow_mut::().check_read(&address_path)?; + state_.borrow_mut::().check_write(&address_path)?; } let path = args.path; let unix_stream = net_unix::UnixStream::connect(Path::new(&path)).await?; @@ -451,11 +448,14 @@ fn listen_udp( Ok((rid, local_addr)) } -fn op_listen( +fn op_listen( state: &mut OpState, args: ListenArgs, _: (), -) -> Result { +) -> Result +where + NP: NetPermissions + 'static, +{ match args { ListenArgs { transport, @@ -466,9 +466,8 @@ fn op_listen( super::check_unstable(state, "Deno.listenDatagram"); } state - .borrow_mut::() - .net - .check(&(&args.hostname, Some(args.port)))?; + .borrow_mut::() + .check_net(&(&args.hostname, Some(args.port)))?; } let addr = resolve_addr_sync(&args.hostname, args.port)? .next() @@ -512,9 +511,9 @@ fn op_listen( if transport == "unixpacket" { super::check_unstable(state, "Deno.listenDatagram"); } - let permissions = state.borrow_mut::(); - permissions.read.check(&address_path)?; - permissions.write.check(&address_path)?; + let permissions = state.borrow_mut::(); + permissions.check_read(&address_path)?; + permissions.check_write(&address_path)?; } let (rid, local_addr) = if transport == "unix" { net_unix::listen_unix(state, &address_path)? @@ -592,11 +591,14 @@ pub struct NameServer { port: u16, } -async fn op_dns_resolve( +async fn op_dns_resolve( state: Rc>, args: ResolveAddrArgs, _: (), -) -> Result, AnyError> { +) -> Result, AnyError> +where + NP: NetPermissions + 'static, +{ let ResolveAddrArgs { query, record_type, @@ -621,14 +623,14 @@ async fn op_dns_resolve( { let mut s = state.borrow_mut(); - let perm = s.borrow_mut::(); + let perm = s.borrow_mut::(); // Checks permission against the name servers which will be actually queried. for ns in config.name_servers() { let socker_addr = &ns.socket_addr; let ip = socker_addr.ip().to_string(); let port = socker_addr.port(); - perm.net.check(&(ip, Some(port)))?; + perm.check_net(&(ip, Some(port)))?; } } diff --git a/runtime/ops/http.rs b/extensions/net/ops_http.rs similarity index 96% rename from runtime/ops/http.rs rename to extensions/net/ops_http.rs index 01658c8020..54e06c3a7a 100644 --- a/runtime/ops/http.rs +++ b/extensions/net/ops_http.rs @@ -1,8 +1,8 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use crate::ops::io::TcpStreamResource; -use crate::ops::io::TlsStreamResource; -use crate::ops::tls::TlsStream; +use crate::io::TcpStreamResource; +use crate::io::TlsStreamResource; +use crate::ops_tls::TlsStream; use deno_core::error::bad_resource_id; use deno_core::error::null_opbuf; use deno_core::error::type_error; @@ -17,7 +17,7 @@ use deno_core::AsyncRefCell; use deno_core::ByteString; use deno_core::CancelHandle; use deno_core::CancelTryFuture; -use deno_core::Extension; +use deno_core::OpPair; use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; @@ -46,17 +46,15 @@ use tokio::net::TcpStream; use tokio::sync::oneshot; use tokio_util::io::StreamReader; -pub fn init() -> Extension { - Extension::builder() - .ops(vec![ - ("op_http_start", op_sync(op_http_start)), - ("op_http_request_next", op_async(op_http_request_next)), - ("op_http_request_read", op_async(op_http_request_read)), - ("op_http_response", op_async(op_http_response)), - ("op_http_response_write", op_async(op_http_response_write)), - ("op_http_response_close", op_async(op_http_response_close)), - ]) - .build() +pub fn init() -> Vec { + vec![ + ("op_http_start", op_sync(op_http_start)), + ("op_http_request_next", op_async(op_http_request_next)), + ("op_http_request_read", op_async(op_http_request_read)), + ("op_http_response", op_async(op_http_response)), + ("op_http_response_write", op_async(op_http_response_write)), + ("op_http_response_close", op_async(op_http_response_close)), + ] } struct ServiceInner { diff --git a/runtime/ops/tls.rs b/extensions/net/ops_tls.rs similarity index 95% rename from runtime/ops/tls.rs rename to extensions/net/ops_tls.rs index c3f554856d..701c5d1a18 100644 --- a/runtime/ops/tls.rs +++ b/extensions/net/ops_tls.rs @@ -3,14 +3,14 @@ pub use rustls; pub use webpki; -use crate::ops::io::TcpStreamResource; -use crate::ops::io::TlsStreamResource; -use crate::ops::net::IpAddr; -use crate::ops::net::OpAddr; -use crate::ops::net::OpConn; -use crate::permissions::Permissions; +use crate::io::TcpStreamResource; +use crate::io::TlsStreamResource; +use crate::ops::IpAddr; +use crate::ops::OpAddr; +use crate::ops::OpConn; use crate::resolve_addr::resolve_addr; use crate::resolve_addr::resolve_addr_sync; +use crate::NetPermissions; use deno_core::error::bad_resource; use deno_core::error::bad_resource_id; use deno_core::error::custom_error; @@ -31,7 +31,7 @@ use deno_core::op_sync; use deno_core::AsyncRefCell; use deno_core::CancelHandle; use deno_core::CancelTryFuture; -use deno_core::Extension; +use deno_core::OpPair; use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; @@ -662,15 +662,13 @@ impl Write for ImplementWriteTrait<'_, TcpStream> { } } -pub fn init() -> Extension { - Extension::builder() - .ops(vec![ - ("op_start_tls", op_async(op_start_tls)), - ("op_connect_tls", op_async(op_connect_tls)), - ("op_listen_tls", op_sync(op_listen_tls)), - ("op_accept_tls", op_async(op_accept_tls)), - ]) - .build() +pub fn init() -> Vec { + vec![ + ("op_start_tls", op_async(op_start_tls::

)), + ("op_connect_tls", op_async(op_connect_tls::

)), + ("op_listen_tls", op_sync(op_listen_tls::

)), + ("op_accept_tls", op_async(op_accept_tls)), + ] } #[derive(Deserialize)] @@ -690,11 +688,14 @@ struct StartTlsArgs { hostname: String, } -async fn op_start_tls( +async fn op_start_tls( state: Rc>, args: StartTlsArgs, _: (), -) -> Result { +) -> Result +where + NP: NetPermissions + 'static, +{ let rid = args.rid; let hostname = match &*args.hostname { "" => "localhost", @@ -705,10 +706,10 @@ async fn op_start_tls( { super::check_unstable2(&state, "Deno.startTls"); let mut s = state.borrow_mut(); - let permissions = s.borrow_mut::(); - permissions.net.check(&(hostname, Some(0)))?; + let permissions = s.borrow_mut::(); + permissions.check_net(&(hostname, Some(0)))?; if let Some(path) = cert_file { - permissions.read.check(Path::new(path))?; + permissions.check_read(Path::new(path))?; } } @@ -763,11 +764,14 @@ async fn op_start_tls( }) } -async fn op_connect_tls( +async fn op_connect_tls( state: Rc>, args: ConnectTlsArgs, _: (), -) -> Result { +) -> Result +where + NP: NetPermissions + 'static, +{ assert_eq!(args.transport, "tcp"); let hostname = match &*args.hostname { "" => "localhost", @@ -778,10 +782,10 @@ async fn op_connect_tls( { let mut s = state.borrow_mut(); - let permissions = s.borrow_mut::(); - permissions.net.check(&(hostname, Some(port)))?; + let permissions = s.borrow_mut::(); + permissions.check_net(&(hostname, Some(port)))?; if let Some(path) = cert_file { - permissions.read.check(Path::new(path))?; + permissions.check_read(Path::new(path))?; } } @@ -912,11 +916,14 @@ pub struct ListenTlsArgs { alpn_protocols: Option>, } -fn op_listen_tls( +fn op_listen_tls( state: &mut OpState, args: ListenTlsArgs, _: (), -) -> Result { +) -> Result +where + NP: NetPermissions + 'static, +{ assert_eq!(args.transport, "tcp"); let hostname = &*args.hostname; let port = args.port; @@ -924,10 +931,10 @@ fn op_listen_tls( let key_file = &*args.key_file; { - let permissions = state.borrow_mut::(); - permissions.net.check(&(hostname, Some(port)))?; - permissions.read.check(Path::new(cert_file))?; - permissions.read.check(Path::new(key_file))?; + let permissions = state.borrow_mut::(); + permissions.check_net(&(hostname, Some(port)))?; + permissions.check_read(Path::new(cert_file))?; + permissions.check_read(Path::new(key_file))?; } let mut tls_config = ServerConfig::new(NoClientAuth::new()); diff --git a/runtime/ops/net_unix.rs b/extensions/net/ops_unix.rs similarity index 90% rename from runtime/ops/net_unix.rs rename to extensions/net/ops_unix.rs index d56dc76d95..9dfcc231ec 100644 --- a/runtime/ops/net_unix.rs +++ b/extensions/net/ops_unix.rs @@ -1,12 +1,11 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use super::utils::into_string; -use crate::ops::io::UnixStreamResource; -use crate::ops::net::AcceptArgs; -use crate::ops::net::OpAddr; -use crate::ops::net::OpConn; -use crate::ops::net::OpPacket; -use crate::ops::net::ReceiveArgs; +use crate::io::UnixStreamResource; +use crate::ops::AcceptArgs; +use crate::ops::OpAddr; +use crate::ops::OpConn; +use crate::ops::OpPacket; +use crate::ops::ReceiveArgs; use deno_core::error::bad_resource; use deno_core::error::custom_error; use deno_core::error::null_opbuf; @@ -29,6 +28,14 @@ use tokio::net::UnixDatagram; use tokio::net::UnixListener; pub use tokio::net::UnixStream; +/// A utility function to map OsStrings to Strings +pub fn into_string(s: std::ffi::OsString) -> Result { + s.into_string().map_err(|s| { + let message = format!("File name or path {:?} is not valid UTF-8", s); + custom_error("InvalidData", message) + }) +} + struct UnixListenerResource { listener: AsyncRefCell, cancel: CancelHandle, diff --git a/runtime/resolve_addr.rs b/extensions/net/resolve_addr.rs similarity index 100% rename from runtime/resolve_addr.rs rename to extensions/net/resolve_addr.rs diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index bf1c24847c..b3f1527061 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -23,6 +23,7 @@ deno_console = { version = "0.10.0", path = "../extensions/console" } deno_core = { version = "0.92.0", path = "../core" } deno_crypto = { version = "0.24.0", path = "../extensions/crypto" } deno_fetch = { version = "0.32.0", path = "../extensions/fetch" } +deno_net = { version = "0.1.0", path = "../extensions/net" } deno_timers = { version = "0.8.0", path = "../extensions/timers" } deno_url = { version = "0.10.0", path = "../extensions/url" } deno_web = { version = "0.41.0", path = "../extensions/web" } @@ -41,6 +42,7 @@ deno_console = { version = "0.10.0", path = "../extensions/console" } deno_core = { version = "0.92.0", path = "../core" } deno_crypto = { version = "0.24.0", path = "../extensions/crypto" } deno_fetch = { version = "0.32.0", path = "../extensions/fetch" } +deno_net = { version = "0.1.0", path = "../extensions/net" } deno_timers = { version = "0.8.0", path = "../extensions/timers" } deno_url = { version = "0.10.0", path = "../extensions/url" } deno_web = { version = "0.41.0", path = "../extensions/web" } @@ -50,7 +52,6 @@ deno_websocket = { version = "0.15.0", path = "../extensions/websocket" } deno_webstorage = { version = "0.5.0", path = "../extensions/webstorage" } atty = "0.2.14" -bytes = "1" dlopen = "0.1.8" encoding_rs = "0.8.28" filetime = "0.2.14" @@ -64,17 +65,11 @@ notify = "5.0.0-pre.7" percent-encoding = "2.1.0" regex = "1.4.3" ring = "0.16.20" -rustls = "0.19.0" serde = { version = "1.0.125", features = ["derive"] } sys-info = "0.9.0" termcolor = "1.1.2" tokio = { version = "1.7.1", features = ["full"] } -tokio-util = { version = "0.6", features = ["io"] } uuid = { version = "0.8.2", features = ["v4"] } -webpki = "0.21.4" -webpki-roots = "0.21.1" -trust-dns-proto = "0.20.3" -trust-dns-resolver = { version = "0.20.3", features = ["tokio-runtime", "serde-config"] } [target.'cfg(windows)'.dependencies] fwdansi = "1.1.0" diff --git a/runtime/build.rs b/runtime/build.rs index 7d086b0450..3e8f8e5b0e 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -59,6 +59,7 @@ fn create_runtime_snapshot(snapshot_path: &Path, files: Vec) { deno_broadcast_channel::InMemoryBroadcastChannel::default(), false, // No --unstable. ), + deno_net::init::(false), // No --unstable. ]; let js_runtime = JsRuntime::new(RuntimeOptions { diff --git a/runtime/js/01_errors.js b/runtime/js/01_errors.js index a46a0a1494..d59bd7adbf 100644 --- a/runtime/js/01_errors.js +++ b/runtime/js/01_errors.js @@ -2,6 +2,9 @@ "use strict"; ((window) => { + const core = window.Deno.core; + const { BadResource, Interrupted } = core; + class NotFound extends Error { constructor(msg) { super(msg); @@ -86,13 +89,6 @@ } } - class Interrupted extends Error { - constructor(msg) { - super(msg); - this.name = "Interrupted"; - } - } - class WriteZero extends Error { constructor(msg) { super(msg); @@ -107,13 +103,6 @@ } } - class BadResource extends Error { - constructor(msg) { - super(msg); - this.name = "BadResource"; - } - } - class Http extends Error { constructor(msg) { super(msg); diff --git a/runtime/lib.rs b/runtime/lib.rs index 63829c2d25..aa95aefbca 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -4,6 +4,7 @@ pub use deno_broadcast_channel; pub use deno_console; pub use deno_crypto; pub use deno_fetch; +pub use deno_net; pub use deno_timers; pub use deno_url; pub use deno_web; @@ -20,7 +21,6 @@ pub mod js; pub mod metrics; pub mod ops; pub mod permissions; -pub mod resolve_addr; pub mod tokio_util; pub mod web_worker; pub mod worker; diff --git a/runtime/ops/io.rs b/runtime/ops/io.rs index 18279c0eb2..e188464664 100644 --- a/runtime/ops/io.rs +++ b/runtime/ops/io.rs @@ -1,6 +1,5 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use crate::ops::tls; use deno_core::error::null_opbuf; use deno_core::error::resource_unavailable; use deno_core::error::AnyError; @@ -17,6 +16,9 @@ use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ZeroCopyBuf; +use deno_net::io::TcpStreamResource; +use deno_net::io::TlsStreamResource; +use deno_net::io::UnixStreamResource; use std::borrow::Cow; use std::cell::RefCell; use std::io::Read; @@ -26,13 +28,10 @@ use tokio::io::AsyncRead; use tokio::io::AsyncReadExt; use tokio::io::AsyncWrite; use tokio::io::AsyncWriteExt; -use tokio::net::tcp; use tokio::process; #[cfg(unix)] use std::os::unix::io::FromRawFd; -#[cfg(unix)] -use tokio::net::unix; #[cfg(windows)] use std::os::windows::io::FromRawHandle; @@ -238,70 +237,6 @@ where } } -/// A full duplex resource has a read and write ends that are completely -/// independent, like TCP/Unix sockets and TLS streams. -#[derive(Debug)] -pub struct FullDuplexResource { - rd: AsyncRefCell, - wr: AsyncRefCell, - // When a full-duplex resource is closed, all pending 'read' ops are - // canceled, while 'write' ops are allowed to complete. Therefore only - // 'read' futures should be attached to this cancel handle. - cancel_handle: CancelHandle, -} - -impl FullDuplexResource -where - R: AsyncRead + Unpin + 'static, - W: AsyncWrite + Unpin + 'static, -{ - pub fn new((rd, wr): (R, W)) -> Self { - Self { - rd: rd.into(), - wr: wr.into(), - cancel_handle: Default::default(), - } - } - - pub fn into_inner(self) -> (R, W) { - (self.rd.into_inner(), self.wr.into_inner()) - } - - pub fn rd_borrow_mut(self: &Rc) -> AsyncMutFuture { - RcRef::map(self, |r| &r.rd).borrow_mut() - } - - pub fn wr_borrow_mut(self: &Rc) -> AsyncMutFuture { - RcRef::map(self, |r| &r.wr).borrow_mut() - } - - pub fn cancel_handle(self: &Rc) -> RcRef { - RcRef::map(self, |r| &r.cancel_handle) - } - - pub fn cancel_read_ops(&self) { - self.cancel_handle.cancel() - } - - async fn read(self: &Rc, buf: &mut [u8]) -> Result { - let mut rd = self.rd_borrow_mut().await; - let nread = rd.read(buf).try_or_cancel(self.cancel_handle()).await?; - Ok(nread) - } - - async fn write(self: &Rc, buf: &[u8]) -> Result { - let mut wr = self.wr_borrow_mut().await; - let nwritten = wr.write(buf).await?; - Ok(nwritten) - } - - async fn shutdown(self: &Rc) -> Result<(), AnyError> { - let mut wr = self.wr_borrow_mut().await; - wr.shutdown().await?; - Ok(()) - } -} - pub type ChildStdinResource = WriteOnlyResource; impl Resource for ChildStdinResource { @@ -334,64 +269,6 @@ impl Resource for ChildStderrResource { } } -pub type TcpStreamResource = - FullDuplexResource; - -impl Resource for TcpStreamResource { - fn name(&self) -> Cow { - "tcpStream".into() - } - - fn close(self: Rc) { - self.cancel_read_ops(); - } -} - -pub type TlsStreamResource = FullDuplexResource; - -impl Resource for TlsStreamResource { - fn name(&self) -> Cow { - "tlsStream".into() - } - - fn close(self: Rc) { - self.cancel_read_ops(); - } -} - -#[cfg(unix)] -pub type UnixStreamResource = - FullDuplexResource; - -#[cfg(not(unix))] -struct UnixStreamResource; - -#[cfg(not(unix))] -impl UnixStreamResource { - async fn read(self: &Rc, _buf: &mut [u8]) -> Result { - unreachable!() - } - async fn write(self: &Rc, _buf: &[u8]) -> Result { - unreachable!() - } - async fn shutdown(self: &Rc) -> Result<(), AnyError> { - unreachable!() - } - fn cancel_read_ops(&self) { - unreachable!() - } -} - -impl Resource for UnixStreamResource { - fn name(&self) -> Cow { - "unixStream".into() - } - - fn close(self: Rc) { - self.cancel_read_ops(); - } -} - #[derive(Debug, Default)] pub struct StdFileResource { pub fs_file: diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs index b05a911808..c940207802 100644 --- a/runtime/ops/mod.rs +++ b/runtime/ops/mod.rs @@ -2,18 +2,13 @@ pub mod fs; pub mod fs_events; -pub mod http; pub mod io; -pub mod net; -#[cfg(unix)] -mod net_unix; pub mod os; pub mod permissions; pub mod plugin; pub mod process; pub mod runtime; pub mod signal; -pub mod tls; pub mod tty; mod utils; pub mod web_worker; diff --git a/runtime/permissions.rs b/runtime/permissions.rs index f8385e2013..d78e200764 100644 --- a/runtime/permissions.rs +++ b/runtime/permissions.rs @@ -962,6 +962,23 @@ impl Permissions { } } +impl deno_net::NetPermissions for Permissions { + fn check_net>( + &mut self, + host: &(T, Option), + ) -> Result<(), AnyError> { + self.net.check(host) + } + + fn check_read(&mut self, path: &Path) -> Result<(), AnyError> { + self.read.check(path) + } + + fn check_write(&mut self, path: &Path) -> Result<(), AnyError> { + self.write.check(path) + } +} + impl deno_fetch::FetchPermissions for Permissions { fn check_net_url(&mut self, url: &url::Url) -> Result<(), AnyError> { self.net.check_url(url) diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index a3a062221d..ac87d285b5 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -330,14 +330,12 @@ impl WebWorker { vec![ ops::fs_events::init(), ops::fs::init(), - ops::net::init(), + deno_net::init::(options.unstable), ops::os::init(), - ops::http::init(), ops::permissions::init(), ops::plugin::init(), ops::process::init(), ops::signal::init(), - ops::tls::init(), ops::tty::init(), ops::io::init_stdio(), ] diff --git a/runtime/worker.rs b/runtime/worker.rs index 9dfdcc8252..567e752536 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -120,16 +120,14 @@ impl MainWorker { ops::worker_host::init(options.create_web_worker_cb.clone()), ops::fs_events::init(), ops::fs::init(), - ops::http::init(), ops::io::init(), ops::io::init_stdio(), - ops::net::init(), + deno_net::init::(options.unstable), ops::os::init(), ops::permissions::init(), ops::plugin::init(), ops::process::init(), ops::signal::init(), - ops::tls::init(), ops::tty::init(), // Permissions ext (worker specific state) perm_ext,