Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dial/listen API change #3000

Merged
merged 3 commits into from
Sep 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions cli/ops/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ pub fn op_accept(

#[derive(Deserialize)]
struct DialArgs {
network: String,
address: String,
transport: String,
hostname: String,
port: u16,
}

pub fn op_dial(
Expand All @@ -59,9 +60,11 @@ pub fn op_dial(
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
let args: DialArgs = serde_json::from_value(args)?;
let network = args.network;
assert_eq!(network, "tcp"); // TODO Support others.
let address = args.address;
assert_eq!(args.transport, "tcp"); // TODO Support others.

// TODO(ry) Using format! is suboptimal here. Better would be if
// state.check_net and resolve_addr() took hostname and port directly.
let address = format!("{}:{}", args.hostname, args.port);

state.check_net(&address)?;

Expand Down Expand Up @@ -117,8 +120,9 @@ pub fn op_shutdown(

#[derive(Deserialize)]
struct ListenArgs {
network: String,
address: String,
transport: String,
hostname: String,
port: u16,
}

pub fn op_listen(
Expand All @@ -127,10 +131,11 @@ pub fn op_listen(
_zero_copy: Option<PinnedBuf>,
) -> Result<JsonOp, ErrBox> {
let args: ListenArgs = serde_json::from_value(args)?;
assert_eq!(args.transport, "tcp");

let network = args.network;
assert_eq!(network, "tcp");
let address = args.address;
// TODO(ry) Using format! is suboptimal here. Better would be if
// state.check_net and resolve_addr() took hostname and port directly.
let address = format!("{}:{}", args.hostname, args.port);

state.check_net(&address)?;

Expand Down
3 changes: 2 additions & 1 deletion cli/tests/echo_server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { args, listen, copy } = Deno;
const addr = args[1] || "127.0.0.1:4544";
const listener = listen("tcp", addr);
const [hostname, port] = addr.split(":");
const listener = listen({ hostname, port: Number(port) });
console.log("listening on", addr);
listener.accept().then(
async (conn): Promise<void> => {
Expand Down
2 changes: 1 addition & 1 deletion js/deps/https/deno.land/std
Submodule std updated from 6663e6 to 43aafb
2 changes: 1 addition & 1 deletion js/fetch_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ testPerm({ net: true }, async function fetchUserAgent(): Promise<void> {

/*
function bufferServer(addr: string): Deno.Buffer {
const listener = Deno.listen("tcp", addr);
const listener = Deno.listen(addr);
const buf = new Deno.Buffer();
listener.accept().then(async conn => {
const p1 = buf.readFrom(conn);
Expand Down
83 changes: 44 additions & 39 deletions js/lib.deno_runtime.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,11 +914,12 @@ declare namespace Deno {

// @url js/net.d.ts

type Network = "tcp";
type Transport = "tcp";
interface Addr {
network: Network;
transport: Transport;
address: string;
}

/** A Listener is a generic network listener for stream-oriented protocols. */
export interface Listener extends AsyncIterator<Conn> {
/** Waits for and resolves to the next connection to the `Listener`. */
Expand Down Expand Up @@ -947,52 +948,56 @@ declare namespace Deno {
*/
closeWrite(): void;
}
/** Listen announces on the local network address.

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

/** Listen announces on the local transport address.
*
* The network must be `tcp`, `tcp4`, `tcp6`, `unix` or `unixpacket`.
* @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".
*
* For TCP networks, if the host in the address parameter is empty or a literal
* unspecified IP address, `listen()` listens on all available unicast and
* anycast IP addresses of the local system. To only use IPv4, use network
* `tcp4`. The address can use a host name, but this is not recommended,
* because it will create a listener for at most one of the host's IP
* addresses. If the port in the address parameter is empty or `0`, as in
* `127.0.0.1:` or `[::1]:0`, a port number is automatically chosen. The
* `addr()` method of `Listener` can be used to discover the chosen port.
* Examples:
*
* See `dial()` for a description of the network and address parameters.
* 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(network: Network, address: string): Listener;
/** Dial connects to the address on the named network.
*
* Supported networks are only `tcp` currently.
*
* TODO: `tcp4` (IPv4-only), `tcp6` (IPv6-only), `udp`, `udp4` (IPv4-only),
* `udp6` (IPv6-only), `ip`, `ip4` (IPv4-only), `ip6` (IPv6-only), `unix`,
* `unixgram` and `unixpacket`.
export function listen(options: ListenOptions): Listener;

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

/** Dial connects to the address on the named transport.
*
* For TCP and UDP networks, the address has the form `host:port`. The host must
* be a literal IP address, or a host name that can be resolved to IP addresses.
* The port must be a literal port number or a service name. If the host is a
* literal IPv6 address it must be enclosed in square brackets, as in
* `[2001:db8::1]:80` or `[fe80::1%zone]:80`. The zone specifies the scope of
* the literal IPv6 address as defined in RFC 4007. The functions JoinHostPort
* and SplitHostPort manipulate a pair of host and port in this form. When using
* TCP, and the host resolves to multiple IP addresses, Dial will try each IP
* address in order until one succeeds.
* @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("tcp", "golang.org:http")
* dial("tcp", "192.0.2.1:http")
* dial("tcp", "198.51.100.1:80")
* dial("udp", "[2001:db8::1]:domain")
* dial("udp", "[fe80::1%lo0]:53")
* dial("tcp", ":80")
* 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 function dial(network: Network, address: string): Promise<Conn>;
/** **RESERVED** */
export function connect(_network: Network, _address: string): Promise<Conn>;
export function dial(options: DialOptions): Promise<Conn>;

// @url js/metrics.d.ts

Expand Down
100 changes: 55 additions & 45 deletions js/net.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import { read, write, close } from "./files.ts";
import * as dispatch from "./dispatch.ts";
import { sendSync, sendAsync } from "./dispatch_json.ts";

export type Network = "tcp";
export type Transport = "tcp";
// TODO support other types:
// export type Network = "tcp" | "tcp4" | "tcp6" | "unix" | "unixpacket";
// export type Transport = "tcp" | "tcp4" | "tcp6" | "unix" | "unixpacket";

// TODO(ry) Replace 'address' with 'hostname' and 'port', similar to DialOptions
// and ListenOptions.
export interface Addr {
network: Network;
transport: Transport;
address: string;
}

/** A Listener is a generic network listener for stream-oriented protocols. */
/** 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>;
Expand Down Expand Up @@ -79,7 +81,7 @@ class ConnImpl implements Conn {
class ListenerImpl implements Listener {
constructor(
readonly rid: number,
private network: Network,
private transport: Transport,
private localAddr: string
) {}

Expand All @@ -94,7 +96,7 @@ class ListenerImpl implements Listener {

addr(): Addr {
return {
network: this.network,
transport: this.transport,
address: this.localAddr
};
}
Expand Down Expand Up @@ -128,62 +130,70 @@ export interface Conn extends Reader, Writer, Closer {
closeWrite(): void;
}

/** Listen announces on the local network address.
export interface ListenOptions {
port: number;
hostname?: string;
transport?: Transport;
}
const listenDefaults = { hostname: "0.0.0.0", transport: "tcp" };

/** Listen announces on the local transport address.
*
* The network must be `tcp`, `tcp4`, `tcp6`, `unix` or `unixpacket`.
* @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".
*
* For TCP networks, if the host in the address parameter is empty or a literal
* unspecified IP address, `listen()` listens on all available unicast and
* anycast IP addresses of the local system. To only use IPv4, use network
* `tcp4`. The address can use a host name, but this is not recommended,
* because it will create a listener for at most one of the host's IP
* addresses. If the port in the address parameter is empty or `0`, as in
* `127.0.0.1:` or `[::1]:0`, a port number is automatically chosen. The
* `addr()` method of `Listener` can be used to discover the chosen port.
* Examples:
*
* See `dial()` for a description of the network and address parameters.
* 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(network: Network, address: string): Listener {
const res = sendSync(dispatch.OP_LISTEN, { network, address });
return new ListenerImpl(res.rid, network, res.localAddr);
export function listen(options: ListenOptions): Listener {
options = Object.assign(listenDefaults, options);
const res = sendSync(dispatch.OP_LISTEN, options);
return new ListenerImpl(res.rid, options.transport, res.localAddr);
}

/** Dial connects to the address on the named network.
*
* Supported networks are only `tcp` currently.
*
* TODO: `tcp4` (IPv4-only), `tcp6` (IPv6-only), `udp`, `udp4` (IPv4-only),
* `udp6` (IPv6-only), `ip`, `ip4` (IPv4-only), `ip6` (IPv6-only), `unix`,
* `unixgram` and `unixpacket`.
export interface DialOptions {
port: number;
hostname?: string;
transport?: Transport;
}
const dialDefaults = { hostname: "127.0.0.1", transport: "tcp" };

/** Dial connects to the address on the named transport.
*
* For TCP and UDP networks, the address has the form `host:port`. The host must
* be a literal IP address, or a host name that can be resolved to IP addresses.
* The port must be a literal port number or a service name. If the host is a
* literal IPv6 address it must be enclosed in square brackets, as in
* `[2001:db8::1]:80` or `[fe80::1%zone]:80`. The zone specifies the scope of
* the literal IPv6 address as defined in RFC 4007. The functions JoinHostPort
* and SplitHostPort manipulate a pair of host and port in this form. When using
* TCP, and the host resolves to multiple IP addresses, Dial will try each IP
* address in order until one succeeds.
* @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("tcp", "golang.org:http")
* dial("tcp", "192.0.2.1:http")
* dial("tcp", "198.51.100.1:80")
* dial("udp", "[2001:db8::1]:domain")
* dial("udp", "[fe80::1%lo0]:53")
* dial("tcp", ":80")
* 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(network: Network, address: string): Promise<Conn> {
const res = await sendAsync(dispatch.OP_DIAL, { network, address });
export async function dial(options: DialOptions): Promise<Conn> {
options = Object.assign(dialDefaults, options);
const res = await sendAsync(dispatch.OP_DIAL, options);
// TODO(bartlomieju): add remoteAddr and localAddr on Rust side
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
}

/** **RESERVED** */
export async function connect(
_network: Network,
_transport: Transport,
_address: string
): Promise<Conn> {
return notImplemented();
Expand Down
Loading