diff options
Diffstat (limited to 'cli/js/net.ts')
-rw-r--r-- | cli/js/net.ts | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/cli/js/net.ts b/cli/js/net.ts new file mode 100644 index 000000000..a7ad2b73c --- /dev/null +++ b/cli/js/net.ts @@ -0,0 +1,205 @@ +// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import { EOF, Reader, Writer, Closer } from "./io.ts"; +import { notImplemented } from "./util.ts"; +import { read, write, close } from "./files.ts"; +import * as dispatch from "./dispatch.ts"; +import { sendSync, sendAsync } from "./dispatch_json.ts"; + +export type Transport = "tcp"; +// TODO support other types: +// export type Transport = "tcp" | "tcp4" | "tcp6" | "unix" | "unixpacket"; + +// TODO(ry) Replace 'address' with 'hostname' and 'port', similar to DialOptions +// and ListenOptions. +export interface Addr { + transport: Transport; + address: string; +} + +/** A Listener is a generic transport listener for stream-oriented protocols. */ +export interface Listener extends AsyncIterator<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`. */ + addr(): Addr; + + [Symbol.asyncIterator](): AsyncIterator<Conn>; +} + +enum ShutdownMode { + // See http://man7.org/linux/man-pages/man2/shutdown.2.html + // Corresponding to SHUT_RD, SHUT_WR, SHUT_RDWR + Read = 0, + Write, + ReadWrite // unused +} + +function shutdown(rid: number, how: ShutdownMode): void { + sendSync(dispatch.OP_SHUTDOWN, { rid, how }); +} + +export class ConnImpl implements Conn { + constructor( + readonly rid: number, + readonly remoteAddr: string, + readonly localAddr: string + ) {} + + write(p: Uint8Array): Promise<number> { + return write(this.rid, p); + } + + read(p: Uint8Array): Promise<number | EOF> { + return read(this.rid, p); + } + + close(): void { + close(this.rid); + } + + /** closeRead shuts down (shutdown(2)) the reading side of the TCP connection. + * Most callers should just use close(). + */ + closeRead(): void { + shutdown(this.rid, ShutdownMode.Read); + } + + /** closeWrite shuts down (shutdown(2)) the writing side of the TCP + * connection. Most callers should just use close(). + */ + closeWrite(): void { + shutdown(this.rid, ShutdownMode.Write); + } +} + +class ListenerImpl implements Listener { + constructor( + readonly rid: number, + private transport: Transport, + private localAddr: string + ) {} + + async accept(): Promise<Conn> { + const res = await sendAsync(dispatch.OP_ACCEPT, { rid: this.rid }); + return new ConnImpl(res.rid, res.remoteAddr, res.localAddr); + } + + close(): void { + close(this.rid); + } + + addr(): Addr { + return { + transport: this.transport, + address: this.localAddr + }; + } + + async next(): Promise<IteratorResult<Conn>> { + return { + done: false, + value: await this.accept() + }; + } + + [Symbol.asyncIterator](): AsyncIterator<Conn> { + return this; + } +} + +export interface Conn extends Reader, Writer, Closer { + /** The local address of the connection. */ + localAddr: string; + /** The remote address of the connection. */ + remoteAddr: string; + /** The resource ID of the connection. */ + rid: number; + /** Shuts down (`shutdown(2)`) the reading side of the TCP connection. Most + * callers should just use `close()`. + */ + closeRead(): void; + /** Shuts down (`shutdown(2)`) the writing side of the TCP connection. Most + * callers should just use `close()`. + */ + closeWrite(): void; +} + +export interface ListenOptions { + port: number; + hostname?: string; + transport?: Transport; +} + +/** Listen announces on the local transport address. + * + * @param options + * @param options.port The port to connect to. (Required.) + * @param options.hostname A literal IP address or host name that can be + * resolved to an IP address. If not specified, defaults to 0.0.0.0 + * @param options.transport Defaults to "tcp". Later we plan to add "tcp4", + * "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unix", "unixgram" and + * "unixpacket". + * + * Examples: + * + * listen({ port: 80 }) + * listen({ hostname: "192.0.2.1", port: 80 }) + * listen({ hostname: "[2001:db8::1]", port: 80 }); + * listen({ hostname: "golang.org", port: 80, transport: "tcp" }) + */ +export function listen(options: ListenOptions): Listener { + const hostname = options.hostname || "0.0.0.0"; + const transport = options.transport || "tcp"; + const res = sendSync(dispatch.OP_LISTEN, { + hostname, + port: options.port, + transport + }); + return new ListenerImpl(res.rid, transport, res.localAddr); +} + +export interface DialOptions { + port: number; + hostname?: string; + transport?: Transport; +} + +/** Dial connects to the address on the named transport. + * + * @param options + * @param options.port The port to connect to. (Required.) + * @param options.hostname A literal IP address or host name that can be + * resolved to an IP address. If not specified, defaults to 127.0.0.1 + * @param options.transport Defaults to "tcp". Later we plan to add "tcp4", + * "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unix", "unixgram" and + * "unixpacket". + * + * Examples: + * + * dial({ port: 80 }) + * dial({ hostname: "192.0.2.1", port: 80 }) + * dial({ hostname: "[2001:db8::1]", port: 80 }); + * dial({ hostname: "golang.org", port: 80, transport: "tcp" }) + */ +export async function dial(options: DialOptions): Promise<Conn> { + const res = await sendAsync(dispatch.OP_DIAL, { + hostname: options.hostname || "127.0.0.1", + port: options.port, + transport: options.transport || "tcp" + }); + return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!); +} + +/** **RESERVED** */ +export async function connect( + _transport: Transport, + _address: string +): Promise<Conn> { + return notImplemented(); +} |