1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

feat: Add "deno_net" extension (#11150)

This commits moves implementation of net related APIs available on "Deno"
namespace to "deno_net" extension.

Following APIs were moved:
- Deno.listen()
- Deno.connect()
- Deno.listenTls()
- Deno.serveHttp()
- Deno.shutdown()
- Deno.resolveDns()
- Deno.listenDatagram()
- Deno.startTls()
- Deno.Conn
- Deno.Listener
- Deno.DatagramConn
This commit is contained in:
Bartek Iwańczuk 2021-06-29 01:43:03 +02:00 committed by GitHub
parent 30cba24848
commit 38a7128cdd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 1049 additions and 679 deletions

29
Cargo.lock generated
View file

@ -536,6 +536,7 @@ dependencies = [
"deno_doc", "deno_doc",
"deno_fetch", "deno_fetch",
"deno_lint", "deno_lint",
"deno_net",
"deno_runtime", "deno_runtime",
"deno_timers", "deno_timers",
"deno_url", "deno_url",
@ -702,17 +703,37 @@ dependencies = [
"swc_ecmascript", "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]] [[package]]
name = "deno_runtime" name = "deno_runtime"
version = "0.18.0" version = "0.18.0"
dependencies = [ dependencies = [
"atty", "atty",
"bytes",
"deno_broadcast_channel", "deno_broadcast_channel",
"deno_console", "deno_console",
"deno_core", "deno_core",
"deno_crypto", "deno_crypto",
"deno_fetch", "deno_fetch",
"deno_net",
"deno_timers", "deno_timers",
"deno_url", "deno_url",
"deno_web", "deno_web",
@ -735,18 +756,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
"regex", "regex",
"ring", "ring",
"rustls",
"serde", "serde",
"sys-info", "sys-info",
"termcolor", "termcolor",
"test_util", "test_util",
"tokio", "tokio",
"tokio-util",
"trust-dns-proto",
"trust-dns-resolver",
"uuid", "uuid",
"webpki",
"webpki-roots",
"winapi 0.3.9", "winapi 0.3.9",
"winres", "winres",
] ]

View file

@ -12,6 +12,7 @@ members = [
"extensions/console", "extensions/console",
"extensions/crypto", "extensions/crypto",
"extensions/fetch", "extensions/fetch",
"extensions/net",
"extensions/timers", "extensions/timers",
"extensions/url", "extensions/url",
"extensions/web", "extensions/web",

View file

@ -25,6 +25,7 @@ deno_console = { version = "0.10.0", path = "../extensions/console" }
deno_core = { version = "0.92.0", path = "../core" } deno_core = { version = "0.92.0", path = "../core" }
deno_crypto = { version = "0.24.0", path = "../extensions/crypto" } deno_crypto = { version = "0.24.0", path = "../extensions/crypto" }
deno_fetch = { version = "0.32.0", path = "../extensions/fetch" } 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_timers = { version = "0.8.0", path = "../extensions/timers" }
deno_url = { version = "0.10.0", path = "../extensions/url" } deno_url = { version = "0.10.0", path = "../extensions/url" }
deno_web = { version = "0.41.0", path = "../extensions/web" } deno_web = { version = "0.41.0", path = "../extensions/web" }

View file

@ -68,6 +68,9 @@ fn create_compiler_snapshot(
"deno.broadcast_channel", "deno.broadcast_channel",
deno_broadcast_channel::get_declaration(), 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. // ensure we invalidate the build properly.
for (_, path) in op_crate_libs.iter() { for (_, path) in op_crate_libs.iter() {
@ -302,6 +305,14 @@ fn main() {
"cargo:rustc-env=DENO_BROADCAST_CHANNEL_LIB_PATH={}", "cargo:rustc-env=DENO_BROADCAST_CHANNEL_LIB_PATH={}",
deno_broadcast_channel::get_declaration().display() 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=TARGET={}", env::var("TARGET").unwrap());
println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap()); println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap());

View file

@ -2,6 +2,7 @@
/// <reference no-default-lib="true" /> /// <reference no-default-lib="true" />
/// <reference lib="esnext" /> /// <reference lib="esnext" />
/// <reference lib="deno.net" />
/** Deno provides extra properties on `import.meta`. These are included here /** Deno provides extra properties on `import.meta`. These are included here
* to ensure that these are still available when using the Deno namespace in * to ensure that these are still available when using the Deno namespace in
@ -1784,149 +1785,6 @@ declare namespace Deno {
* Requires `allow-write` permission. */ * Requires `allow-write` permission. */
export function truncate(name: string, len?: number): Promise<void>; export function truncate(name: string, len?: number): Promise<void>;
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<Conn> {
/** Waits for and resolves to the next connection to the `Listener`. */
accept(): Promise<Conn>;
/** 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<Conn>;
}
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<void>;
}
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<Conn>;
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<Conn>;
/** 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<void>;
export interface Metrics { export interface Metrics {
opsDispatched: number; opsDispatched: number;
opsDispatchedSync: number; opsDispatchedSync: number;

View file

@ -2,6 +2,7 @@
/// <reference no-default-lib="true" /> /// <reference no-default-lib="true" />
/// <reference lib="deno.ns" /> /// <reference lib="deno.ns" />
/// <reference lib="deno.net_unstable" />
declare namespace Deno { declare namespace Deno {
/** /**
@ -812,232 +813,6 @@ declare namespace Deno {
mtime: number | Date, mtime: number | Date,
): Promise<void>; ): Promise<void>;
/** 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<string[]>;
export function resolveDns(
query: string,
recordType: "MX",
options?: ResolveDnsOptions,
): Promise<MXRecord[]>;
export function resolveDns(
query: string,
recordType: "SRV",
options?: ResolveDnsOptions,
): Promise<SRVRecord[]>;
export function resolveDns(
query: string,
recordType: "TXT",
options?: ResolveDnsOptions,
): Promise<string[][]>;
/** ** 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<string[] | MXRecord[] | SRVRecord[] | string[][]>;
/** **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<number>;
/** 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<Conn>;
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<Conn>;
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 /** **UNSTABLE**: The `signo` argument may change to require the Deno.Signal
* enum. * enum.
* *
@ -1182,36 +957,6 @@ declare namespace Deno {
bytesReceived: number; bytesReceived: number;
} }
export interface RequestEvent {
readonly request: Request;
respondWith(r: Response | Promise<Response>): Promise<void>;
}
export interface HttpConn extends AsyncIterable<RequestEvent> {
readonly rid: number;
nextRequest(): Promise<RequestEvent | null>;
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. */ /** **UNSTABLE**: New option, yet to be vetted. */
export interface TestDefinition { export interface TestDefinition {
/** Specifies the permissions that should be used to run the test. /** Specifies the permissions that should be used to run the test.

View file

@ -334,12 +334,14 @@ pub fn get_types(unstable: bool) -> String {
crate::tsc::DENO_WEBSTORAGE_LIB, crate::tsc::DENO_WEBSTORAGE_LIB,
crate::tsc::DENO_CRYPTO_LIB, crate::tsc::DENO_CRYPTO_LIB,
crate::tsc::DENO_BROADCAST_CHANNEL_LIB, crate::tsc::DENO_BROADCAST_CHANNEL_LIB,
crate::tsc::DENO_NET_LIB,
crate::tsc::SHARED_GLOBALS_LIB, crate::tsc::SHARED_GLOBALS_LIB,
crate::tsc::WINDOW_LIB, crate::tsc::WINDOW_LIB,
]; ];
if unstable { if unstable {
types.push(crate::tsc::UNSTABLE_NS_LIB); types.push(crate::tsc::UNSTABLE_NS_LIB);
types.push(crate::tsc::DENO_NET_UNSTABLE_LIB);
} }
types.join("\n") types.join("\n")

View file

@ -2,9 +2,9 @@
use crate::itest; use crate::itest;
use deno_core::url; use deno_core::url;
use deno_runtime::ops::tls::rustls; use deno_runtime::deno_net::ops_tls::rustls;
use deno_runtime::ops::tls::webpki; use deno_runtime::deno_net::ops_tls::webpki;
use deno_runtime::ops::tls::TlsStream; use deno_runtime::deno_net::ops_tls::TlsStream;
use std::fs; use std::fs;
use std::io::BufReader; use std::io::BufReader;
use std::io::Cursor; use std::io::Cursor;

View file

@ -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_CRYPTO_LIB: &str = include_str!(env!("DENO_CRYPTO_LIB_PATH"));
pub static DENO_BROADCAST_CHANNEL_LIB: &str = pub static DENO_BROADCAST_CHANNEL_LIB: &str =
include_str!(env!("DENO_BROADCAST_CHANNEL_LIB_PATH")); 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 = pub static SHARED_GLOBALS_LIB: &str =
include_str!("dts/lib.deno.shared_globals.d.ts"); include_str!("dts/lib.deno.shared_globals.d.ts");
pub static WINDOW_LIB: &str = include_str!("dts/lib.deno.window.d.ts"); pub static WINDOW_LIB: &str = include_str!("dts/lib.deno.window.d.ts");

View file

@ -132,6 +132,23 @@
opSync("op_print", str, isErr); 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 // Provide bootstrap namespace
window.__bootstrap = {}; window.__bootstrap = {};
// Extra Deno.core.* exports // Extra Deno.core.* exports
@ -146,5 +163,7 @@
registerErrorClass, registerErrorClass,
handleAsyncMsgFromRust, handleAsyncMsgFromRust,
syncOpsCache, syncOpsCache,
BadResource,
Interrupted,
}); });
})(this); })(this);

View file

@ -3,11 +3,25 @@
((window) => { ((window) => {
const core = window.Deno.core; const core = window.Deno.core;
const { errors } = window.__bootstrap.errors; const { BadResource } = core;
const { read, write } = window.__bootstrap.io;
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) { function shutdown(rid) {
return core.opAsync("op_shutdown", rid); return core.opAsync("op_net_shutdown", rid);
} }
function opAccept(rid, transport) { function opAccept(rid, transport) {
@ -104,7 +118,7 @@
try { try {
conn = await this.accept(); conn = await this.accept();
} catch (error) { } catch (error) {
if (error instanceof errors.BadResource) { if (error instanceof BadResource) {
return { value: undefined, done: true }; return { value: undefined, done: true };
} }
throw error; throw error;
@ -171,7 +185,7 @@
try { try {
yield await this.receive(); yield await this.receive();
} catch (err) { } catch (err) {
if (err instanceof errors.BadResource) { if (err instanceof BadResource) {
break; break;
} }
throw err; throw err;

View file

@ -5,8 +5,8 @@
const { InnerBody } = window.__bootstrap.fetchBody; const { InnerBody } = window.__bootstrap.fetchBody;
const { Response, fromInnerRequest, toInnerResponse, newInnerRequest } = const { Response, fromInnerRequest, toInnerResponse, newInnerRequest } =
window.__bootstrap.fetch; window.__bootstrap.fetch;
const errors = window.__bootstrap.errors.errors;
const core = window.Deno.core; const core = window.Deno.core;
const { BadResource, Interrupted } = core;
const { ReadableStream } = window.__bootstrap.streams; const { ReadableStream } = window.__bootstrap.streams;
const abortSignal = window.__bootstrap.abortSignal; const abortSignal = window.__bootstrap.abortSignal;
@ -42,9 +42,9 @@
// a generic `BadResource` error. Instead store this error and replace // a generic `BadResource` error. Instead store this error and replace
// those with it. // those with it.
this[connErrorSymbol] = error; this[connErrorSymbol] = error;
if (error instanceof errors.BadResource) { if (error instanceof BadResource) {
return null; return null;
} else if (error instanceof errors.Interrupted) { } else if (error instanceof Interrupted) {
return null; return null;
} else if (error.message.includes("connection closed")) { } else if (error.message.includes("connection closed")) {
return null; return null;
@ -159,7 +159,7 @@
], respBody instanceof Uint8Array ? respBody : null); ], respBody instanceof Uint8Array ? respBody : null);
} catch (error) { } catch (error) {
const connError = httpConn[connErrorSymbol]; const connError = httpConn[connErrorSymbol];
if (error instanceof errors.BadResource && connError != null) { if (error instanceof BadResource && connError != null) {
// deno-lint-ignore no-ex-assign // deno-lint-ignore no-ex-assign
error = new connError.constructor(connError.message); error = new connError.constructor(connError.message);
} }
@ -192,7 +192,7 @@
); );
} catch (error) { } catch (error) {
const connError = httpConn[connErrorSymbol]; const connError = httpConn[connErrorSymbol];
if (error instanceof errors.BadResource && connError != null) { if (error instanceof BadResource && connError != null) {
// deno-lint-ignore no-ex-assign // deno-lint-ignore no-ex-assign
error = new connError.constructor(connError.message); error = new connError.constructor(connError.message);
} }

31
extensions/net/Cargo.toml Normal file
View file

@ -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"] }

30
extensions/net/README.md Normal file
View file

@ -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"

232
extensions/net/io.rs Normal file
View file

@ -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<OpPair> {
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<R, W> {
rd: AsyncRefCell<R>,
wr: AsyncRefCell<W>,
// 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<R, W> FullDuplexResource<R, W>
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<Self>) -> AsyncMutFuture<R> {
RcRef::map(self, |r| &r.rd).borrow_mut()
}
pub fn wr_borrow_mut(self: &Rc<Self>) -> AsyncMutFuture<W> {
RcRef::map(self, |r| &r.wr).borrow_mut()
}
pub fn cancel_handle(self: &Rc<Self>) -> RcRef<CancelHandle> {
RcRef::map(self, |r| &r.cancel_handle)
}
pub fn cancel_read_ops(&self) {
self.cancel_handle.cancel()
}
pub async fn read(
self: &Rc<Self>,
buf: &mut [u8],
) -> Result<usize, AnyError> {
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<Self>, buf: &[u8]) -> Result<usize, AnyError> {
let mut wr = self.wr_borrow_mut().await;
let nwritten = wr.write(buf).await?;
Ok(nwritten)
}
pub async fn shutdown(self: &Rc<Self>) -> Result<(), AnyError> {
let mut wr = self.wr_borrow_mut().await;
wr.shutdown().await?;
Ok(())
}
}
pub type TcpStreamResource =
FullDuplexResource<tcp::OwnedReadHalf, tcp::OwnedWriteHalf>;
impl Resource for TcpStreamResource {
fn name(&self) -> Cow<str> {
"tcpStream".into()
}
fn close(self: Rc<Self>) {
self.cancel_read_ops();
}
}
pub type TlsStreamResource = FullDuplexResource<tls::ReadHalf, tls::WriteHalf>;
impl Resource for TlsStreamResource {
fn name(&self) -> Cow<str> {
"tlsStream".into()
}
fn close(self: Rc<Self>) {
self.cancel_read_ops();
}
}
#[cfg(unix)]
pub type UnixStreamResource =
FullDuplexResource<unix::OwnedReadHalf, unix::OwnedWriteHalf>;
#[cfg(not(unix))]
pub struct UnixStreamResource;
#[cfg(not(unix))]
impl UnixStreamResource {
pub async fn read(
self: &Rc<Self>,
_buf: &mut [u8],
) -> Result<usize, AnyError> {
unreachable!()
}
pub async fn write(self: &Rc<Self>, _buf: &[u8]) -> Result<usize, AnyError> {
unreachable!()
}
pub async fn shutdown(self: &Rc<Self>) -> Result<(), AnyError> {
unreachable!()
}
pub fn cancel_read_ops(&self) {
unreachable!()
}
}
impl Resource for UnixStreamResource {
fn name(&self) -> Cow<str> {
"unixStream".into()
}
fn close(self: Rc<Self>) {
self.cancel_read_ops();
}
}
async fn op_read_async(
state: Rc<RefCell<OpState>>,
rid: ResourceId,
buf: Option<ZeroCopyBuf>,
) -> Result<u32, AnyError> {
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::<TcpStreamResource>() {
s.read(buf).await?
} else if let Some(s) = resource.downcast_rc::<TlsStreamResource>() {
s.read(buf).await?
} else if let Some(s) = resource.downcast_rc::<UnixStreamResource>() {
s.read(buf).await?
} else {
return Err(not_supported());
};
Ok(nread as u32)
}
async fn op_write_async(
state: Rc<RefCell<OpState>>,
rid: ResourceId,
buf: Option<ZeroCopyBuf>,
) -> Result<u32, AnyError> {
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::<TcpStreamResource>() {
s.write(buf).await?
} else if let Some(s) = resource.downcast_rc::<TlsStreamResource>() {
s.write(buf).await?
} else if let Some(s) = resource.downcast_rc::<UnixStreamResource>() {
s.write(buf).await?
} else {
return Err(not_supported());
};
Ok(nwritten as u32)
}
async fn op_shutdown(
state: Rc<RefCell<OpState>>,
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::<TcpStreamResource>() {
s.shutdown().await?;
} else if let Some(s) = resource.downcast_rc::<TlsStreamResource>() {
s.shutdown().await?;
} else if let Some(s) = resource.downcast_rc::<UnixStreamResource>() {
s.shutdown().await?;
} else {
return Err(not_supported());
}
Ok(())
}

149
extensions/net/lib.deno_net.d.ts vendored Normal file
View file

@ -0,0 +1,149 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
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<Conn> {
/** Waits for and resolves to the next connection to the `Listener`. */
accept(): Promise<Conn>;
/** 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<Conn>;
}
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<void>;
}
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<Conn>;
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<Conn>;
/** 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<void>;
}

View file

@ -0,0 +1,262 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
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<string[]>;
export function resolveDns(
query: string,
recordType: "MX",
options?: ResolveDnsOptions,
): Promise<MXRecord[]>;
export function resolveDns(
query: string,
recordType: "SRV",
options?: ResolveDnsOptions,
): Promise<SRVRecord[]>;
export function resolveDns(
query: string,
recordType: "TXT",
options?: ResolveDnsOptions,
): Promise<string[][]>;
/** ** 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<string[] | MXRecord[] | SRVRecord[] | string[][]>;
/** **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<number>;
/** 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<Conn>;
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<Conn>;
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<Response>): Promise<void>;
}
export interface HttpConn extends AsyncIterable<RequestEvent> {
readonly rid: number;
nextRequest(): Promise<RequestEvent | null>;
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;
}

113
extensions/net/lib.rs Normal file
View file

@ -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<T: AsRef<str>>(
&mut self,
_host: &(T, Option<u16>),
) -> 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<T: AsRef<str>>(
&mut self,
_host: &(T, Option<u16>),
) -> 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::<UnstableChecker>().check_unstable(api_name)
}
/// Helper for checking unstable features. Used for async ops.
pub fn check_unstable2(state: &Rc<RefCell<OpState>>, api_name: &str) {
let state = state.borrow();
state.borrow::<UnstableChecker>().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<P: NetPermissions + 'static>(unstable: bool) -> Extension {
let mut ops_to_register = vec![];
ops_to_register.extend(io::init());
ops_to_register.extend(ops::init::<P>());
ops_to_register.extend(ops_tls::init::<P>());
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()
}

View file

@ -1,8 +1,9 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // 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;
use crate::resolve_addr::resolve_addr_sync; use crate::resolve_addr::resolve_addr_sync;
use crate::NetPermissions;
use deno_core::error::bad_resource; use deno_core::error::bad_resource;
use deno_core::error::custom_error; use deno_core::error::custom_error;
use deno_core::error::generic_error; use deno_core::error::generic_error;
@ -14,7 +15,7 @@ use deno_core::op_sync;
use deno_core::AsyncRefCell; use deno_core::AsyncRefCell;
use deno_core::CancelHandle; use deno_core::CancelHandle;
use deno_core::CancelTryFuture; use deno_core::CancelTryFuture;
use deno_core::Extension; use deno_core::OpPair;
use deno_core::OpState; use deno_core::OpState;
use deno_core::RcRef; use deno_core::RcRef;
use deno_core::Resource; use deno_core::Resource;
@ -39,23 +40,21 @@ use trust_dns_resolver::system_conf;
use trust_dns_resolver::AsyncResolver; use trust_dns_resolver::AsyncResolver;
#[cfg(unix)] #[cfg(unix)]
use super::net_unix; use super::ops_unix as net_unix;
#[cfg(unix)] #[cfg(unix)]
use crate::ops::io::UnixStreamResource; use crate::io::UnixStreamResource;
#[cfg(unix)] #[cfg(unix)]
use std::path::Path; use std::path::Path;
pub fn init() -> Extension { pub fn init<P: NetPermissions + 'static>() -> Vec<OpPair> {
Extension::builder() vec![
.ops(vec![ ("op_accept", op_async(op_accept)),
("op_accept", op_async(op_accept)), ("op_connect", op_async(op_connect::<P>)),
("op_connect", op_async(op_connect)), ("op_listen", op_sync(op_listen::<P>)),
("op_listen", op_sync(op_listen)), ("op_datagram_receive", op_async(op_datagram_receive)),
("op_datagram_receive", op_async(op_datagram_receive)), ("op_datagram_send", op_async(op_datagram_send::<P>)),
("op_datagram_send", op_async(op_datagram_send)), ("op_dns_resolve", op_async(op_dns_resolve::<P>)),
("op_dns_resolve", op_async(op_dns_resolve)), ]
])
.build()
} }
#[derive(Serialize)] #[derive(Serialize)]
@ -216,11 +215,14 @@ struct SendArgs {
transport_args: ArgsEnum, transport_args: ArgsEnum,
} }
async fn op_datagram_send( async fn op_datagram_send<NP>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
args: SendArgs, args: SendArgs,
zero_copy: Option<ZeroCopyBuf>, zero_copy: Option<ZeroCopyBuf>,
) -> Result<usize, AnyError> { ) -> Result<usize, AnyError>
where
NP: NetPermissions + 'static,
{
let zero_copy = zero_copy.ok_or_else(null_opbuf)?; let zero_copy = zero_copy.ok_or_else(null_opbuf)?;
let zero_copy = zero_copy.clone(); let zero_copy = zero_copy.clone();
@ -232,9 +234,8 @@ async fn op_datagram_send(
} if transport == "udp" => { } if transport == "udp" => {
{ {
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
s.borrow_mut::<Permissions>() s.borrow_mut::<NP>()
.net .check_net(&(&args.hostname, Some(args.port)))?;
.check(&(&args.hostname, Some(args.port)))?;
} }
let addr = resolve_addr(&args.hostname, args.port) let addr = resolve_addr(&args.hostname, args.port)
.await? .await?
@ -259,7 +260,7 @@ async fn op_datagram_send(
let address_path = Path::new(&args.path); let address_path = Path::new(&args.path);
{ {
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
s.borrow_mut::<Permissions>().write.check(&address_path)?; s.borrow_mut::<NP>().check_write(&address_path)?;
} }
let resource = state let resource = state
.borrow() .borrow()
@ -285,11 +286,14 @@ struct ConnectArgs {
transport_args: ArgsEnum, transport_args: ArgsEnum,
} }
async fn op_connect( async fn op_connect<NP>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
args: ConnectArgs, args: ConnectArgs,
_: (), _: (),
) -> Result<OpConn, AnyError> { ) -> Result<OpConn, AnyError>
where
NP: NetPermissions + 'static,
{
match args { match args {
ConnectArgs { ConnectArgs {
transport, transport,
@ -298,9 +302,8 @@ async fn op_connect(
{ {
let mut state_ = state.borrow_mut(); let mut state_ = state.borrow_mut();
state_ state_
.borrow_mut::<Permissions>() .borrow_mut::<NP>()
.net .check_net(&(&args.hostname, Some(args.port)))?;
.check(&(&args.hostname, Some(args.port)))?;
} }
let addr = resolve_addr(&args.hostname, args.port) let addr = resolve_addr(&args.hostname, args.port)
.await? .await?
@ -335,14 +338,8 @@ async fn op_connect(
super::check_unstable2(&state, "Deno.connect"); super::check_unstable2(&state, "Deno.connect");
{ {
let mut state_ = state.borrow_mut(); let mut state_ = state.borrow_mut();
state_ state_.borrow_mut::<NP>().check_read(&address_path)?;
.borrow_mut::<Permissions>() state_.borrow_mut::<NP>().check_write(&address_path)?;
.read
.check(&address_path)?;
state_
.borrow_mut::<Permissions>()
.write
.check(&address_path)?;
} }
let path = args.path; let path = args.path;
let unix_stream = net_unix::UnixStream::connect(Path::new(&path)).await?; let unix_stream = net_unix::UnixStream::connect(Path::new(&path)).await?;
@ -451,11 +448,14 @@ fn listen_udp(
Ok((rid, local_addr)) Ok((rid, local_addr))
} }
fn op_listen( fn op_listen<NP>(
state: &mut OpState, state: &mut OpState,
args: ListenArgs, args: ListenArgs,
_: (), _: (),
) -> Result<OpConn, AnyError> { ) -> Result<OpConn, AnyError>
where
NP: NetPermissions + 'static,
{
match args { match args {
ListenArgs { ListenArgs {
transport, transport,
@ -466,9 +466,8 @@ fn op_listen(
super::check_unstable(state, "Deno.listenDatagram"); super::check_unstable(state, "Deno.listenDatagram");
} }
state state
.borrow_mut::<Permissions>() .borrow_mut::<NP>()
.net .check_net(&(&args.hostname, Some(args.port)))?;
.check(&(&args.hostname, Some(args.port)))?;
} }
let addr = resolve_addr_sync(&args.hostname, args.port)? let addr = resolve_addr_sync(&args.hostname, args.port)?
.next() .next()
@ -512,9 +511,9 @@ fn op_listen(
if transport == "unixpacket" { if transport == "unixpacket" {
super::check_unstable(state, "Deno.listenDatagram"); super::check_unstable(state, "Deno.listenDatagram");
} }
let permissions = state.borrow_mut::<Permissions>(); let permissions = state.borrow_mut::<NP>();
permissions.read.check(&address_path)?; permissions.check_read(&address_path)?;
permissions.write.check(&address_path)?; permissions.check_write(&address_path)?;
} }
let (rid, local_addr) = if transport == "unix" { let (rid, local_addr) = if transport == "unix" {
net_unix::listen_unix(state, &address_path)? net_unix::listen_unix(state, &address_path)?
@ -592,11 +591,14 @@ pub struct NameServer {
port: u16, port: u16,
} }
async fn op_dns_resolve( async fn op_dns_resolve<NP>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
args: ResolveAddrArgs, args: ResolveAddrArgs,
_: (), _: (),
) -> Result<Vec<DnsReturnRecord>, AnyError> { ) -> Result<Vec<DnsReturnRecord>, AnyError>
where
NP: NetPermissions + 'static,
{
let ResolveAddrArgs { let ResolveAddrArgs {
query, query,
record_type, record_type,
@ -621,14 +623,14 @@ async fn op_dns_resolve(
{ {
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
let perm = s.borrow_mut::<Permissions>(); let perm = s.borrow_mut::<NP>();
// Checks permission against the name servers which will be actually queried. // Checks permission against the name servers which will be actually queried.
for ns in config.name_servers() { for ns in config.name_servers() {
let socker_addr = &ns.socket_addr; let socker_addr = &ns.socket_addr;
let ip = socker_addr.ip().to_string(); let ip = socker_addr.ip().to_string();
let port = socker_addr.port(); let port = socker_addr.port();
perm.net.check(&(ip, Some(port)))?; perm.check_net(&(ip, Some(port)))?;
} }
} }

View file

@ -1,8 +1,8 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use crate::ops::io::TcpStreamResource; use crate::io::TcpStreamResource;
use crate::ops::io::TlsStreamResource; use crate::io::TlsStreamResource;
use crate::ops::tls::TlsStream; use crate::ops_tls::TlsStream;
use deno_core::error::bad_resource_id; use deno_core::error::bad_resource_id;
use deno_core::error::null_opbuf; use deno_core::error::null_opbuf;
use deno_core::error::type_error; use deno_core::error::type_error;
@ -17,7 +17,7 @@ use deno_core::AsyncRefCell;
use deno_core::ByteString; use deno_core::ByteString;
use deno_core::CancelHandle; use deno_core::CancelHandle;
use deno_core::CancelTryFuture; use deno_core::CancelTryFuture;
use deno_core::Extension; use deno_core::OpPair;
use deno_core::OpState; use deno_core::OpState;
use deno_core::RcRef; use deno_core::RcRef;
use deno_core::Resource; use deno_core::Resource;
@ -46,17 +46,15 @@ use tokio::net::TcpStream;
use tokio::sync::oneshot; use tokio::sync::oneshot;
use tokio_util::io::StreamReader; use tokio_util::io::StreamReader;
pub fn init() -> Extension { pub fn init() -> Vec<OpPair> {
Extension::builder() vec![
.ops(vec![ ("op_http_start", op_sync(op_http_start)),
("op_http_start", op_sync(op_http_start)), ("op_http_request_next", op_async(op_http_request_next)),
("op_http_request_next", op_async(op_http_request_next)), ("op_http_request_read", op_async(op_http_request_read)),
("op_http_request_read", op_async(op_http_request_read)), ("op_http_response", op_async(op_http_response)),
("op_http_response", op_async(op_http_response)), ("op_http_response_write", op_async(op_http_response_write)),
("op_http_response_write", op_async(op_http_response_write)), ("op_http_response_close", op_async(op_http_response_close)),
("op_http_response_close", op_async(op_http_response_close)), ]
])
.build()
} }
struct ServiceInner { struct ServiceInner {

View file

@ -3,14 +3,14 @@
pub use rustls; pub use rustls;
pub use webpki; pub use webpki;
use crate::ops::io::TcpStreamResource; use crate::io::TcpStreamResource;
use crate::ops::io::TlsStreamResource; use crate::io::TlsStreamResource;
use crate::ops::net::IpAddr; use crate::ops::IpAddr;
use crate::ops::net::OpAddr; use crate::ops::OpAddr;
use crate::ops::net::OpConn; use crate::ops::OpConn;
use crate::permissions::Permissions;
use crate::resolve_addr::resolve_addr; use crate::resolve_addr::resolve_addr;
use crate::resolve_addr::resolve_addr_sync; use crate::resolve_addr::resolve_addr_sync;
use crate::NetPermissions;
use deno_core::error::bad_resource; use deno_core::error::bad_resource;
use deno_core::error::bad_resource_id; use deno_core::error::bad_resource_id;
use deno_core::error::custom_error; use deno_core::error::custom_error;
@ -31,7 +31,7 @@ use deno_core::op_sync;
use deno_core::AsyncRefCell; use deno_core::AsyncRefCell;
use deno_core::CancelHandle; use deno_core::CancelHandle;
use deno_core::CancelTryFuture; use deno_core::CancelTryFuture;
use deno_core::Extension; use deno_core::OpPair;
use deno_core::OpState; use deno_core::OpState;
use deno_core::RcRef; use deno_core::RcRef;
use deno_core::Resource; use deno_core::Resource;
@ -662,15 +662,13 @@ impl Write for ImplementWriteTrait<'_, TcpStream> {
} }
} }
pub fn init() -> Extension { pub fn init<P: NetPermissions + 'static>() -> Vec<OpPair> {
Extension::builder() vec![
.ops(vec![ ("op_start_tls", op_async(op_start_tls::<P>)),
("op_start_tls", op_async(op_start_tls)), ("op_connect_tls", op_async(op_connect_tls::<P>)),
("op_connect_tls", op_async(op_connect_tls)), ("op_listen_tls", op_sync(op_listen_tls::<P>)),
("op_listen_tls", op_sync(op_listen_tls)), ("op_accept_tls", op_async(op_accept_tls)),
("op_accept_tls", op_async(op_accept_tls)), ]
])
.build()
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -690,11 +688,14 @@ struct StartTlsArgs {
hostname: String, hostname: String,
} }
async fn op_start_tls( async fn op_start_tls<NP>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
args: StartTlsArgs, args: StartTlsArgs,
_: (), _: (),
) -> Result<OpConn, AnyError> { ) -> Result<OpConn, AnyError>
where
NP: NetPermissions + 'static,
{
let rid = args.rid; let rid = args.rid;
let hostname = match &*args.hostname { let hostname = match &*args.hostname {
"" => "localhost", "" => "localhost",
@ -705,10 +706,10 @@ async fn op_start_tls(
{ {
super::check_unstable2(&state, "Deno.startTls"); super::check_unstable2(&state, "Deno.startTls");
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
let permissions = s.borrow_mut::<Permissions>(); let permissions = s.borrow_mut::<NP>();
permissions.net.check(&(hostname, Some(0)))?; permissions.check_net(&(hostname, Some(0)))?;
if let Some(path) = cert_file { 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<NP>(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
args: ConnectTlsArgs, args: ConnectTlsArgs,
_: (), _: (),
) -> Result<OpConn, AnyError> { ) -> Result<OpConn, AnyError>
where
NP: NetPermissions + 'static,
{
assert_eq!(args.transport, "tcp"); assert_eq!(args.transport, "tcp");
let hostname = match &*args.hostname { let hostname = match &*args.hostname {
"" => "localhost", "" => "localhost",
@ -778,10 +782,10 @@ async fn op_connect_tls(
{ {
let mut s = state.borrow_mut(); let mut s = state.borrow_mut();
let permissions = s.borrow_mut::<Permissions>(); let permissions = s.borrow_mut::<NP>();
permissions.net.check(&(hostname, Some(port)))?; permissions.check_net(&(hostname, Some(port)))?;
if let Some(path) = cert_file { 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<Vec<String>>, alpn_protocols: Option<Vec<String>>,
} }
fn op_listen_tls( fn op_listen_tls<NP>(
state: &mut OpState, state: &mut OpState,
args: ListenTlsArgs, args: ListenTlsArgs,
_: (), _: (),
) -> Result<OpConn, AnyError> { ) -> Result<OpConn, AnyError>
where
NP: NetPermissions + 'static,
{
assert_eq!(args.transport, "tcp"); assert_eq!(args.transport, "tcp");
let hostname = &*args.hostname; let hostname = &*args.hostname;
let port = args.port; let port = args.port;
@ -924,10 +931,10 @@ fn op_listen_tls(
let key_file = &*args.key_file; let key_file = &*args.key_file;
{ {
let permissions = state.borrow_mut::<Permissions>(); let permissions = state.borrow_mut::<NP>();
permissions.net.check(&(hostname, Some(port)))?; permissions.check_net(&(hostname, Some(port)))?;
permissions.read.check(Path::new(cert_file))?; permissions.check_read(Path::new(cert_file))?;
permissions.read.check(Path::new(key_file))?; permissions.check_read(Path::new(key_file))?;
} }
let mut tls_config = ServerConfig::new(NoClientAuth::new()); let mut tls_config = ServerConfig::new(NoClientAuth::new());

View file

@ -1,12 +1,11 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
use super::utils::into_string; use crate::io::UnixStreamResource;
use crate::ops::io::UnixStreamResource; use crate::ops::AcceptArgs;
use crate::ops::net::AcceptArgs; use crate::ops::OpAddr;
use crate::ops::net::OpAddr; use crate::ops::OpConn;
use crate::ops::net::OpConn; use crate::ops::OpPacket;
use crate::ops::net::OpPacket; use crate::ops::ReceiveArgs;
use crate::ops::net::ReceiveArgs;
use deno_core::error::bad_resource; use deno_core::error::bad_resource;
use deno_core::error::custom_error; use deno_core::error::custom_error;
use deno_core::error::null_opbuf; use deno_core::error::null_opbuf;
@ -29,6 +28,14 @@ use tokio::net::UnixDatagram;
use tokio::net::UnixListener; use tokio::net::UnixListener;
pub use tokio::net::UnixStream; pub use tokio::net::UnixStream;
/// A utility function to map OsStrings to Strings
pub fn into_string(s: std::ffi::OsString) -> Result<String, AnyError> {
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 { struct UnixListenerResource {
listener: AsyncRefCell<UnixListener>, listener: AsyncRefCell<UnixListener>,
cancel: CancelHandle, cancel: CancelHandle,

View file

@ -23,6 +23,7 @@ deno_console = { version = "0.10.0", path = "../extensions/console" }
deno_core = { version = "0.92.0", path = "../core" } deno_core = { version = "0.92.0", path = "../core" }
deno_crypto = { version = "0.24.0", path = "../extensions/crypto" } deno_crypto = { version = "0.24.0", path = "../extensions/crypto" }
deno_fetch = { version = "0.32.0", path = "../extensions/fetch" } 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_timers = { version = "0.8.0", path = "../extensions/timers" }
deno_url = { version = "0.10.0", path = "../extensions/url" } deno_url = { version = "0.10.0", path = "../extensions/url" }
deno_web = { version = "0.41.0", path = "../extensions/web" } 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_core = { version = "0.92.0", path = "../core" }
deno_crypto = { version = "0.24.0", path = "../extensions/crypto" } deno_crypto = { version = "0.24.0", path = "../extensions/crypto" }
deno_fetch = { version = "0.32.0", path = "../extensions/fetch" } 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_timers = { version = "0.8.0", path = "../extensions/timers" }
deno_url = { version = "0.10.0", path = "../extensions/url" } deno_url = { version = "0.10.0", path = "../extensions/url" }
deno_web = { version = "0.41.0", path = "../extensions/web" } 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" } deno_webstorage = { version = "0.5.0", path = "../extensions/webstorage" }
atty = "0.2.14" atty = "0.2.14"
bytes = "1"
dlopen = "0.1.8" dlopen = "0.1.8"
encoding_rs = "0.8.28" encoding_rs = "0.8.28"
filetime = "0.2.14" filetime = "0.2.14"
@ -64,17 +65,11 @@ notify = "5.0.0-pre.7"
percent-encoding = "2.1.0" percent-encoding = "2.1.0"
regex = "1.4.3" regex = "1.4.3"
ring = "0.16.20" ring = "0.16.20"
rustls = "0.19.0"
serde = { version = "1.0.125", features = ["derive"] } serde = { version = "1.0.125", features = ["derive"] }
sys-info = "0.9.0" sys-info = "0.9.0"
termcolor = "1.1.2" termcolor = "1.1.2"
tokio = { version = "1.7.1", features = ["full"] } tokio = { version = "1.7.1", features = ["full"] }
tokio-util = { version = "0.6", features = ["io"] }
uuid = { version = "0.8.2", features = ["v4"] } 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] [target.'cfg(windows)'.dependencies]
fwdansi = "1.1.0" fwdansi = "1.1.0"

View file

@ -59,6 +59,7 @@ fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<PathBuf>) {
deno_broadcast_channel::InMemoryBroadcastChannel::default(), deno_broadcast_channel::InMemoryBroadcastChannel::default(),
false, // No --unstable. false, // No --unstable.
), ),
deno_net::init::<deno_net::NoNetPermissions>(false), // No --unstable.
]; ];
let js_runtime = JsRuntime::new(RuntimeOptions { let js_runtime = JsRuntime::new(RuntimeOptions {

View file

@ -2,6 +2,9 @@
"use strict"; "use strict";
((window) => { ((window) => {
const core = window.Deno.core;
const { BadResource, Interrupted } = core;
class NotFound extends Error { class NotFound extends Error {
constructor(msg) { constructor(msg) {
super(msg); super(msg);
@ -86,13 +89,6 @@
} }
} }
class Interrupted extends Error {
constructor(msg) {
super(msg);
this.name = "Interrupted";
}
}
class WriteZero extends Error { class WriteZero extends Error {
constructor(msg) { constructor(msg) {
super(msg); super(msg);
@ -107,13 +103,6 @@
} }
} }
class BadResource extends Error {
constructor(msg) {
super(msg);
this.name = "BadResource";
}
}
class Http extends Error { class Http extends Error {
constructor(msg) { constructor(msg) {
super(msg); super(msg);

View file

@ -4,6 +4,7 @@ pub use deno_broadcast_channel;
pub use deno_console; pub use deno_console;
pub use deno_crypto; pub use deno_crypto;
pub use deno_fetch; pub use deno_fetch;
pub use deno_net;
pub use deno_timers; pub use deno_timers;
pub use deno_url; pub use deno_url;
pub use deno_web; pub use deno_web;
@ -20,7 +21,6 @@ pub mod js;
pub mod metrics; pub mod metrics;
pub mod ops; pub mod ops;
pub mod permissions; pub mod permissions;
pub mod resolve_addr;
pub mod tokio_util; pub mod tokio_util;
pub mod web_worker; pub mod web_worker;
pub mod worker; pub mod worker;

View file

@ -1,6 +1,5 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // 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::null_opbuf;
use deno_core::error::resource_unavailable; use deno_core::error::resource_unavailable;
use deno_core::error::AnyError; use deno_core::error::AnyError;
@ -17,6 +16,9 @@ use deno_core::RcRef;
use deno_core::Resource; use deno_core::Resource;
use deno_core::ResourceId; use deno_core::ResourceId;
use deno_core::ZeroCopyBuf; 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::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::io::Read; use std::io::Read;
@ -26,13 +28,10 @@ use tokio::io::AsyncRead;
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
use tokio::io::AsyncWrite; use tokio::io::AsyncWrite;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tokio::net::tcp;
use tokio::process; use tokio::process;
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::io::FromRawFd; use std::os::unix::io::FromRawFd;
#[cfg(unix)]
use tokio::net::unix;
#[cfg(windows)] #[cfg(windows)]
use std::os::windows::io::FromRawHandle; 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<R, W> {
rd: AsyncRefCell<R>,
wr: AsyncRefCell<W>,
// 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<R, W> FullDuplexResource<R, W>
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<Self>) -> AsyncMutFuture<R> {
RcRef::map(self, |r| &r.rd).borrow_mut()
}
pub fn wr_borrow_mut(self: &Rc<Self>) -> AsyncMutFuture<W> {
RcRef::map(self, |r| &r.wr).borrow_mut()
}
pub fn cancel_handle(self: &Rc<Self>) -> RcRef<CancelHandle> {
RcRef::map(self, |r| &r.cancel_handle)
}
pub fn cancel_read_ops(&self) {
self.cancel_handle.cancel()
}
async fn read(self: &Rc<Self>, buf: &mut [u8]) -> Result<usize, AnyError> {
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<Self>, buf: &[u8]) -> Result<usize, AnyError> {
let mut wr = self.wr_borrow_mut().await;
let nwritten = wr.write(buf).await?;
Ok(nwritten)
}
async fn shutdown(self: &Rc<Self>) -> Result<(), AnyError> {
let mut wr = self.wr_borrow_mut().await;
wr.shutdown().await?;
Ok(())
}
}
pub type ChildStdinResource = WriteOnlyResource<process::ChildStdin>; pub type ChildStdinResource = WriteOnlyResource<process::ChildStdin>;
impl Resource for ChildStdinResource { impl Resource for ChildStdinResource {
@ -334,64 +269,6 @@ impl Resource for ChildStderrResource {
} }
} }
pub type TcpStreamResource =
FullDuplexResource<tcp::OwnedReadHalf, tcp::OwnedWriteHalf>;
impl Resource for TcpStreamResource {
fn name(&self) -> Cow<str> {
"tcpStream".into()
}
fn close(self: Rc<Self>) {
self.cancel_read_ops();
}
}
pub type TlsStreamResource = FullDuplexResource<tls::ReadHalf, tls::WriteHalf>;
impl Resource for TlsStreamResource {
fn name(&self) -> Cow<str> {
"tlsStream".into()
}
fn close(self: Rc<Self>) {
self.cancel_read_ops();
}
}
#[cfg(unix)]
pub type UnixStreamResource =
FullDuplexResource<unix::OwnedReadHalf, unix::OwnedWriteHalf>;
#[cfg(not(unix))]
struct UnixStreamResource;
#[cfg(not(unix))]
impl UnixStreamResource {
async fn read(self: &Rc<Self>, _buf: &mut [u8]) -> Result<usize, AnyError> {
unreachable!()
}
async fn write(self: &Rc<Self>, _buf: &[u8]) -> Result<usize, AnyError> {
unreachable!()
}
async fn shutdown(self: &Rc<Self>) -> Result<(), AnyError> {
unreachable!()
}
fn cancel_read_ops(&self) {
unreachable!()
}
}
impl Resource for UnixStreamResource {
fn name(&self) -> Cow<str> {
"unixStream".into()
}
fn close(self: Rc<Self>) {
self.cancel_read_ops();
}
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct StdFileResource { pub struct StdFileResource {
pub fs_file: pub fs_file:

View file

@ -2,18 +2,13 @@
pub mod fs; pub mod fs;
pub mod fs_events; pub mod fs_events;
pub mod http;
pub mod io; pub mod io;
pub mod net;
#[cfg(unix)]
mod net_unix;
pub mod os; pub mod os;
pub mod permissions; pub mod permissions;
pub mod plugin; pub mod plugin;
pub mod process; pub mod process;
pub mod runtime; pub mod runtime;
pub mod signal; pub mod signal;
pub mod tls;
pub mod tty; pub mod tty;
mod utils; mod utils;
pub mod web_worker; pub mod web_worker;

View file

@ -962,6 +962,23 @@ impl Permissions {
} }
} }
impl deno_net::NetPermissions for Permissions {
fn check_net<T: AsRef<str>>(
&mut self,
host: &(T, Option<u16>),
) -> 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 { impl deno_fetch::FetchPermissions for Permissions {
fn check_net_url(&mut self, url: &url::Url) -> Result<(), AnyError> { fn check_net_url(&mut self, url: &url::Url) -> Result<(), AnyError> {
self.net.check_url(url) self.net.check_url(url)

View file

@ -330,14 +330,12 @@ impl WebWorker {
vec![ vec![
ops::fs_events::init(), ops::fs_events::init(),
ops::fs::init(), ops::fs::init(),
ops::net::init(), deno_net::init::<Permissions>(options.unstable),
ops::os::init(), ops::os::init(),
ops::http::init(),
ops::permissions::init(), ops::permissions::init(),
ops::plugin::init(), ops::plugin::init(),
ops::process::init(), ops::process::init(),
ops::signal::init(), ops::signal::init(),
ops::tls::init(),
ops::tty::init(), ops::tty::init(),
ops::io::init_stdio(), ops::io::init_stdio(),
] ]

View file

@ -120,16 +120,14 @@ impl MainWorker {
ops::worker_host::init(options.create_web_worker_cb.clone()), ops::worker_host::init(options.create_web_worker_cb.clone()),
ops::fs_events::init(), ops::fs_events::init(),
ops::fs::init(), ops::fs::init(),
ops::http::init(),
ops::io::init(), ops::io::init(),
ops::io::init_stdio(), ops::io::init_stdio(),
ops::net::init(), deno_net::init::<Permissions>(options.unstable),
ops::os::init(), ops::os::init(),
ops::permissions::init(), ops::permissions::init(),
ops::plugin::init(), ops::plugin::init(),
ops::process::init(), ops::process::init(),
ops::signal::init(), ops::signal::init(),
ops::tls::init(),
ops::tty::init(), ops::tty::init(),
// Permissions ext (worker specific state) // Permissions ext (worker specific state)
perm_ext, perm_ext,