Skip to content

Commit

Permalink
feat: Support Unix Domain Sockets (#4176)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsouto18 authored Mar 23, 2020
1 parent b924e5a commit 70a5034
Show file tree
Hide file tree
Showing 13 changed files with 740 additions and 268 deletions.
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ sys-info = "=0.5.8" # 0.5.9 and 0.5.10 are broken on windows.
sourcemap = "5.0.0"
tempfile = "3.1.0"
termcolor = "1.1.0"
tokio = { version = "0.2.13", features = ["rt-core", "tcp", "udp", "process", "fs", "blocking", "sync", "io-std", "macros", "time"] }
tokio = { version = "0.2.13", features = ["rt-core", "tcp", "udp", "uds", "process", "fs", "blocking", "sync", "io-std", "macros", "time"] }
tokio-rustls = "0.13.0"
url = "2.1.1"
utime = "0.2.1"
Expand Down
5 changes: 1 addition & 4 deletions cli/js/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,9 @@ export {
export { metrics, Metrics } from "./ops/runtime.ts";
export { mkdirSync, mkdir, MkdirOptions } from "./ops/fs/mkdir.ts";
export {
Addr,
connect,
listen,
recvfrom,
UDPConn,
UDPAddr,
DatagramConn,
Listener,
Conn,
ShutdownMode,
Expand Down
88 changes: 45 additions & 43 deletions cli/js/lib.deno.ns.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1546,21 +1546,18 @@ declare namespace Deno {
*
* Requires `allow-plugin` permission. */
export function openPlugin(filename: string): Plugin;

export type Transport = "tcp" | "udp";

export interface Addr {
transport: Transport;
export interface NetAddr {
transport: "tcp" | "udp";
hostname: string;
port: number;
}

export interface UDPAddr {
port: number;
transport?: Transport;
hostname?: string;
export interface UnixAddr {
transport: "unix" | "unixpacket";
address: string;
}

export type Addr = NetAddr | UnixAddr;
/** **UNSTABLE**: Maybe remove `ShutdownMode` entirely.
*
* Corresponds to `SHUT_RD`, `SHUT_WR`, `SHUT_RDWR` on POSIX-like systems.
Expand All @@ -1585,26 +1582,18 @@ declare namespace Deno {
*/
export function shutdown(rid: number, how: ShutdownMode): void;

/** **UNSTABLE**: new API, yet to be vetted.
*
* Waits for the next message to the passed `rid` and writes it on the passed
* `Uint8Array`.
*
* Resolves to the number of bytes written and the remote address. */
export function recvfrom(rid: number, p: Uint8Array): Promise<[number, Addr]>;

/** **UNSTABLE**: new API, yet to be vetted.
*
* A generic transport listener for message-oriented protocols. */
export interface UDPConn extends AsyncIterable<[Uint8Array, Addr]> {
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: UDPAddr): Promise<void>;
send(p: Uint8Array, addr: Addr): Promise<void>;
/** UNSTABLE: new API, yet to be vetted.
*
* Close closes the socket. Any pending message promises will be rejected
Expand All @@ -1624,6 +1613,7 @@ declare namespace Deno {
close(): void;
/** Return the address of the `Listener`. */
readonly addr: Addr;

[Symbol.asyncIterator](): AsyncIterator<Conn>;
}

Expand All @@ -1648,13 +1638,12 @@ declare namespace Deno {
/** 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;
/** Either `"tcp"` or `"udp"`. Defaults to `"tcp"`.
*
* In the future: `"tcp4"`, `"tcp6"`, `"udp4"`, `"udp6"`, `"ip"`, `"ip4"`,
* `"ip6"`, `"unix"`, `"unixgram"`, and `"unixpacket"`. */
transport?: Transport;
}

export interface UnixListenOptions {
/** A Path to the Unix Socket. */
address: string;
}
/** **UNSTABLE**: new API
*
* Listen announces on the local transport address.
Expand All @@ -1672,32 +1661,41 @@ declare namespace Deno {
*
* Listen announces on the local transport address.
*
* Deno.listen({ port: 80 })
* Deno.listen({ hostname: "192.0.2.1", port: 80 })
* Deno.listen({ hostname: "[2001:db8::1]", port: 80 });
* Deno.listen({ hostname: "golang.org", port: 80, transport: "tcp" });
* Deno.listen({ address: "/foo/bar.sock", transport: "unix" })
*
* Requires `allow-read` permission. */
export function listen(
options: UnixListenOptions & { transport: "unix" }
): Listener;
/** **UNSTABLE**: new API
*
* Listen announces on the local transport address.
*
* Deno.listen({ port: 80, transport: "udp" })
* Deno.listen({ hostname: "golang.org", port: 80, transport: "udp" });
*
* Requires `allow-net` permission. */
export function listen(
options: ListenOptions & { transport: "udp" }
): UDPConn;
): DatagramConn;
/** **UNSTABLE**: new API
*
* Listen announces on the local transport address.
*
* Deno.listen({ port: 80 })
* Deno.listen({ hostname: "192.0.2.1", port: 80 })
* Deno.listen({ hostname: "[2001:db8::1]", port: 80 });
* Deno.listen({ hostname: "golang.org", port: 80, transport: "tcp" });
* Deno.listen({ address: "/foo/bar.sock", transport: "unixpacket" })
*
* Requires `allow-net` permission. */
export function listen(options: ListenOptions): Listener | UDPConn;
* Requires `allow-read` permission. */
export function listen(
options: UnixListenOptions & { transport: "unixpacket" }
): DatagramConn;

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
Expand All @@ -1714,11 +1712,12 @@ declare namespace Deno {
/** 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;
/** Either `"tcp"` or `"udp"`. Defaults to `"tcp"`.
*
* In the future: `"tcp4"`, `"tcp6"`, `"udp4"`, `"udp6"`, `"ip"`, `"ip4"`,
* `"ip6"`, `"unix"`, `"unixgram"`, and `"unixpacket"`. */
transport?: Transport;
transport?: "tcp";
}

export interface UnixConnectOptions {
transport: "unix";
address: string;
}

/**
Expand All @@ -1728,10 +1727,13 @@ declare namespace Deno {
* 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 conn4 = await Deno.connect({ hostname: "golang.org", port: 80, transport: "tcp" });
* const conn5 = await Deno.connect({ address: "/foo/bar.sock", transport: "unix" });
*
* Requires `allow-net` permission. */
export function connect(options: ConnectOptions): Promise<Conn>;
* Requires `allow-net` permission for "tcp" and `allow-read` for unix. */
export function connect(
options: ConnectOptions | UnixConnectOptions
): Promise<Conn>;

export interface ConnectTLSOptions {
/** The port to connect to. */
Expand Down
117 changes: 70 additions & 47 deletions cli/js/net.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,13 @@ import { EOF, Reader, Writer, Closer } from "./io.ts";
import { read, write } from "./ops/io.ts";
import { close } from "./ops/resources.ts";
import * as netOps from "./ops/net.ts";
import { Transport } from "./ops/net.ts";
export { ShutdownMode, shutdown, Transport } from "./ops/net.ts";
import { Addr } from "./ops/net.ts";
export { ShutdownMode, shutdown, NetAddr, UnixAddr } from "./ops/net.ts";

export interface Addr {
transport: Transport;
hostname: string;
port: number;
}

export interface UDPAddr {
transport?: Transport;
hostname?: string;
port: number;
}

export interface UDPConn extends AsyncIterable<[Uint8Array, Addr]> {
export interface DatagramConn extends AsyncIterable<[Uint8Array, Addr]> {
receive(p?: Uint8Array): Promise<[Uint8Array, Addr]>;

send(p: Uint8Array, addr: UDPAddr): Promise<void>;
send(p: Uint8Array, addr: Addr): Promise<void>;

close(): void;

Expand Down Expand Up @@ -73,7 +61,7 @@ export class ListenerImpl implements Listener {
constructor(readonly rid: number, readonly addr: Addr) {}

async accept(): Promise<Conn> {
const res = await netOps.accept(this.rid);
const res = await netOps.accept(this.rid, this.addr.transport);
return new ConnImpl(res.rid, res.remoteAddr, res.localAddr);
}

Expand All @@ -95,15 +83,7 @@ export class ListenerImpl implements Listener {
}
}

export async function recvfrom(
rid: number,
p: Uint8Array
): Promise<[number, Addr]> {
const { size, remoteAddr } = await netOps.receive(rid, p);
return [size, remoteAddr];
}

export class UDPConnImpl implements UDPConn {
export class DatagramImpl implements DatagramConn {
constructor(
readonly rid: number,
readonly addr: Addr,
Expand All @@ -112,14 +92,18 @@ export class UDPConnImpl implements UDPConn {

async receive(p?: Uint8Array): Promise<[Uint8Array, Addr]> {
const buf = p || new Uint8Array(this.bufSize);
const [size, remoteAddr] = await recvfrom(this.rid, buf);
const { size, remoteAddr } = await netOps.receive(
this.rid,
this.addr.transport,
buf
);
const sub = buf.subarray(0, size);
return [sub, remoteAddr];
}

async send(p: Uint8Array, addr: UDPAddr): Promise<void> {
async send(p: Uint8Array, addr: Addr): Promise<void> {
const remote = { hostname: "127.0.0.1", transport: "udp", ...addr };
if (remote.transport !== "udp") throw Error("Remote transport must be UDP");

const args = { ...remote, rid: this.rid };
await netOps.send(args as netOps.SendRequest, p);
}
Expand Down Expand Up @@ -153,38 +137,77 @@ export interface Conn extends Reader, Writer, Closer {
export interface ListenOptions {
port: number;
hostname?: string;
transport?: Transport;
transport?: "tcp" | "udp";
}

export interface UnixListenOptions {
transport: "unix" | "unixpacket";
address: string;
}

export function listen(
options: ListenOptions & { transport?: "tcp" }
): Listener;
export function listen(options: ListenOptions & { transport: "udp" }): UDPConn;
export function listen({
port,
hostname = "0.0.0.0",
transport = "tcp"
}: ListenOptions): Listener | UDPConn {
const res = netOps.listen({ port, hostname, transport });

if (transport === "tcp") {
export function listen(
options: UnixListenOptions & { transport: "unix" }
): Listener;
export function listen(
options: ListenOptions & { transport: "udp" }
): DatagramConn;
export function listen(
options: UnixListenOptions & { transport: "unixpacket" }
): DatagramConn;
export function listen(
options: ListenOptions | UnixListenOptions
): Listener | DatagramConn {
let res;

if (options.transport === "unix" || options.transport === "unixpacket") {
res = netOps.listen(options);
} else {
res = netOps.listen({
transport: "tcp",
hostname: "127.0.0.1",
...(options as ListenOptions)
});
}

if (
!options.transport ||
options.transport === "tcp" ||
options.transport === "unix"
) {
return new ListenerImpl(res.rid, res.localAddr);
} else {
return new UDPConnImpl(res.rid, res.localAddr);
return new DatagramImpl(res.rid, res.localAddr);
}
}

export interface ConnectOptions {
port: number;
hostname?: string;
transport?: Transport;
transport?: "tcp";
}
export interface UnixConnectOptions {
transport: "unix";
address: string;
}
export async function connect(options: UnixConnectOptions): Promise<Conn>;
export async function connect(options: ConnectOptions): Promise<Conn>;
export async function connect(
options: ConnectOptions | UnixConnectOptions
): Promise<Conn> {
let res;

if (options.transport === "unix") {
res = await netOps.connect(options);
} else {
res = await netOps.connect({
transport: "tcp",
hostname: "127.0.0.1",
...options
});
}

export async function connect({
port,
hostname = "127.0.0.1",
transport = "tcp"
}: ConnectOptions): Promise<Conn> {
const res = await netOps.connect({ port, hostname, transport });
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
}
Loading

0 comments on commit 70a5034

Please sign in to comment.