forked from denoland/deno
-
Notifications
You must be signed in to change notification settings - Fork 0
/
net.ts
193 lines (172 loc) · 6.51 KB
/
net.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { ReadResult, Reader, Writer, Closer } from "./io";
import * as msg from "gen/cli/msg_generated";
import { assert, notImplemented } from "./util";
import * as dispatch from "./dispatch";
import * as flatbuffers from "./flatbuffers";
import { read, write, close } from "./files";
export type Network = "tcp";
// TODO support other types:
// export type Network = "tcp" | "tcp4" | "tcp6" | "unix" | "unixpacket";
// TODO Support finding network from Addr, see https://golang.org/pkg/net/#Addr
export type Addr = string;
/** A Listener is a generic network listener for stream-oriented protocols. */
export interface Listener {
/** 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;
}
enum ShutdownMode {
// See http:https://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 {
const builder = flatbuffers.createBuilder();
const inner = msg.Shutdown.createShutdown(builder, rid, how);
const baseRes = dispatch.sendSync(builder, msg.Any.Shutdown, inner);
assert(baseRes == null);
}
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<ReadResult> {
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) {}
async accept(): Promise<Conn> {
const builder = flatbuffers.createBuilder();
const inner = msg.Accept.createAccept(builder, this.rid);
const baseRes = await dispatch.sendAsync(builder, msg.Any.Accept, inner);
assert(baseRes != null);
assert(msg.Any.NewConn === baseRes!.innerType());
const res = new msg.NewConn();
assert(baseRes!.inner(res) != null);
return new ConnImpl(res.rid(), res.remoteAddr()!, res.localAddr()!);
}
close(): void {
close(this.rid);
}
addr(): Addr {
return notImplemented();
}
}
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;
}
/** Listen announces on the local network address.
*
* The network must be `tcp`, `tcp4`, `tcp6`, `unix` or `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.
*
* See `dial()` for a description of the network and address parameters.
*/
export function listen(network: Network, address: string): Listener {
const builder = flatbuffers.createBuilder();
const network_ = builder.createString(network);
const address_ = builder.createString(address);
const inner = msg.Listen.createListen(builder, network_, address_);
const baseRes = dispatch.sendSync(builder, msg.Any.Listen, inner);
assert(baseRes != null);
assert(msg.Any.ListenRes === baseRes!.innerType());
const res = new msg.ListenRes();
assert(baseRes!.inner(res) != null);
return new ListenerImpl(res.rid());
}
/** 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`.
*
* 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.
*
* 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")
*/
export async function dial(network: Network, address: string): Promise<Conn> {
const builder = flatbuffers.createBuilder();
const network_ = builder.createString(network);
const address_ = builder.createString(address);
const inner = msg.Dial.createDial(builder, network_, address_);
const baseRes = await dispatch.sendAsync(builder, msg.Any.Dial, inner);
assert(baseRes != null);
assert(msg.Any.NewConn === baseRes!.innerType());
const res = new msg.NewConn();
assert(baseRes!.inner(res) != null);
return new ConnImpl(res.rid(), res.remoteAddr()!, res.localAddr()!);
}
/** **RESERVED** */
export async function connect(
_network: Network,
_address: string
): Promise<Conn> {
return notImplemented();
}