@bytecodealliance/preview2-shim 0.20.2 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PollState } from "./worker-thread.js";
|
|
2
|
-
import { Socket as TcpSocket } from "node:net";
|
|
2
|
+
import { Server, Socket as TcpSocket } from "node:net";
|
|
3
3
|
import { IpSocketAddress } from "../../types/interfaces/wasi-sockets-network.js";
|
|
4
4
|
interface PendingAccept {
|
|
5
5
|
tcpSocket: TcpSocket | null;
|
|
@@ -10,8 +10,11 @@ interface TcpSocketRecord {
|
|
|
10
10
|
state: number;
|
|
11
11
|
future: number | null;
|
|
12
12
|
tcpSocket: TcpSocket | null;
|
|
13
|
+
server: Server | null;
|
|
14
|
+
localAddress: IpSocketAddress | null;
|
|
13
15
|
listenBacklogSize: number;
|
|
14
|
-
|
|
16
|
+
keepAlive: boolean;
|
|
17
|
+
keepAliveIdleTime: number;
|
|
15
18
|
pendingAccepts: PendingAccept[];
|
|
16
19
|
pollState: PollState | null;
|
|
17
20
|
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { createFuture, createReadableStream, createReadableStreamPollState, createWritableStream, futureDispose, futureTakeValue, pollStateReady, verifyPollsDroppedForDrop, } from "./worker-thread.js";
|
|
2
2
|
import process from "node:process";
|
|
3
|
-
|
|
4
|
-
const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
|
|
5
|
-
import { convertSocketError, convertSocketErrorCode, ipSocketAddress, isIPv4MappedAddress, isMulticastIpAddress, isUnicastIpAddress, isWildcardAddress, noLookup, serializeIpAddress, SOCKET_STATE_BIND, SOCKET_STATE_BOUND, SOCKET_STATE_CLOSED, SOCKET_STATE_CONNECT, SOCKET_STATE_CONNECTION, SOCKET_STATE_INIT, SOCKET_STATE_LISTEN, SOCKET_STATE_LISTENER, } from "./worker-sockets.js";
|
|
3
|
+
import { convertSocketError, ipSocketAddress, isIPv4MappedAddress, isMulticastIpAddress, isUnicastIpAddress, isWildcardAddress, noLookup, serializeIpAddress, SOCKET_STATE_BIND, SOCKET_STATE_BOUND, SOCKET_STATE_CLOSED, SOCKET_STATE_CONNECT, SOCKET_STATE_CONNECTION, SOCKET_STATE_INIT, SOCKET_STATE_LISTEN, SOCKET_STATE_LISTENER, } from "./worker-sockets.js";
|
|
6
4
|
import { Server, Socket as TcpSocket } from "node:net";
|
|
7
5
|
const win = process.platform === "win32";
|
|
8
6
|
export const tcpSockets = new Map();
|
|
9
7
|
let tcpSocketCnt = 0;
|
|
10
8
|
export function createTcpSocket() {
|
|
11
|
-
const handle = new TCP(TCPConstants.SOCKET);
|
|
12
9
|
tcpSockets.set(++tcpSocketCnt, {
|
|
13
10
|
state: SOCKET_STATE_INIT,
|
|
14
11
|
future: null,
|
|
15
12
|
tcpSocket: null,
|
|
13
|
+
server: null,
|
|
14
|
+
localAddress: null,
|
|
16
15
|
listenBacklogSize: 128,
|
|
17
|
-
|
|
16
|
+
keepAlive: false,
|
|
17
|
+
keepAliveIdleTime: 0,
|
|
18
18
|
pendingAccepts: [],
|
|
19
19
|
pollState: {
|
|
20
20
|
ready: true,
|
|
@@ -44,7 +44,7 @@ export function socketTcpFinish(id, fromState, toState) {
|
|
|
44
44
|
socket.state = toState;
|
|
45
45
|
// for the listener, we must immediately transition back to unresolved
|
|
46
46
|
if (toState === SOCKET_STATE_LISTENER) {
|
|
47
|
-
socket.pollState.ready =
|
|
47
|
+
socket.pollState.ready = socket.pendingAccepts.length > 0;
|
|
48
48
|
}
|
|
49
49
|
return val;
|
|
50
50
|
}
|
|
@@ -60,26 +60,77 @@ export function socketTcpBindStart(id, localAddress, family) {
|
|
|
60
60
|
throw "invalid-argument";
|
|
61
61
|
}
|
|
62
62
|
socket.state = SOCKET_STATE_BIND;
|
|
63
|
-
|
|
64
|
-
socket.future = createFuture((async () => {
|
|
63
|
+
socket.future = createFuture(new Promise((resolve, reject) => {
|
|
65
64
|
const address = serializeIpAddress(localAddress);
|
|
66
65
|
const port = localAddress.val.port;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
// node:net has no public standalone TCP bind API. Keep a server open
|
|
67
|
+
// to reserve the endpoint, then reuse it for listen or close it for connect.
|
|
68
|
+
const server = (socket.server = createTcpServer(socket));
|
|
69
|
+
function handleErr(err) {
|
|
70
|
+
server.off("listening", handleListen);
|
|
71
|
+
reject(convertSocketError(err));
|
|
72
72
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
throw convertSocketErrorCode(-code);
|
|
73
|
+
function handleListen() {
|
|
74
|
+
server.off("error", handleErr);
|
|
75
|
+
const boundAddress = server.address();
|
|
76
|
+
if (!boundAddress || typeof boundAddress === "string") {
|
|
77
|
+
reject("invalid-state");
|
|
78
|
+
return;
|
|
80
79
|
}
|
|
80
|
+
socket.localAddress = ipSocketAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
|
|
81
|
+
resolve();
|
|
81
82
|
}
|
|
82
|
-
|
|
83
|
+
server.once("listening", handleListen);
|
|
84
|
+
server.once("error", handleErr);
|
|
85
|
+
server.listen({
|
|
86
|
+
host: address ?? undefined,
|
|
87
|
+
port,
|
|
88
|
+
backlog: socket.listenBacklogSize,
|
|
89
|
+
ipv6Only: family === "ipv6",
|
|
90
|
+
});
|
|
91
|
+
}), socket.pollState);
|
|
92
|
+
}
|
|
93
|
+
function createTcpServer(socket) {
|
|
94
|
+
const server = new Server({
|
|
95
|
+
pauseOnConnect: true,
|
|
96
|
+
allowHalfOpen: true,
|
|
97
|
+
});
|
|
98
|
+
server.on("connection", (tcpSocket) => {
|
|
99
|
+
// The reservation is already listening at the OS level, so reject any
|
|
100
|
+
// connections that arrive before the WASI socket enters its listen state.
|
|
101
|
+
if (socket.state !== SOCKET_STATE_LISTEN && socket.state !== SOCKET_STATE_LISTENER) {
|
|
102
|
+
tcpSocket.destroy();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
pollStateReady(socket.pollState);
|
|
106
|
+
const pollState = createReadableStreamPollState(tcpSocket);
|
|
107
|
+
socket.pendingAccepts.push({
|
|
108
|
+
tcpSocket,
|
|
109
|
+
err: null,
|
|
110
|
+
pollState,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
server.on("error", (err) => {
|
|
114
|
+
if (socket.state === SOCKET_STATE_LISTEN || socket.state === SOCKET_STATE_LISTENER) {
|
|
115
|
+
pollStateReady(socket.pollState);
|
|
116
|
+
socket.pendingAccepts.push({
|
|
117
|
+
tcpSocket: null,
|
|
118
|
+
err,
|
|
119
|
+
pollState: null,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return server;
|
|
124
|
+
}
|
|
125
|
+
function closeTcpServer(socket) {
|
|
126
|
+
const server = socket.server;
|
|
127
|
+
socket.server = null;
|
|
128
|
+
if (!server) {
|
|
129
|
+
return Promise.resolve();
|
|
130
|
+
}
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
133
|
+
});
|
|
83
134
|
}
|
|
84
135
|
export function socketTcpConnectStart(id, remoteAddress, family) {
|
|
85
136
|
const socket = tcpSockets.get(id);
|
|
@@ -95,71 +146,44 @@ export function socketTcpConnectStart(id, remoteAddress, family) {
|
|
|
95
146
|
throw "invalid-argument";
|
|
96
147
|
}
|
|
97
148
|
socket.state = SOCKET_STATE_CONNECT;
|
|
98
|
-
socket.future = createFuture(
|
|
149
|
+
socket.future = createFuture((async () => {
|
|
150
|
+
const localAddress = socket.localAddress;
|
|
151
|
+
await closeTcpServer(socket);
|
|
99
152
|
const tcpSocket = (socket.tcpSocket = new TcpSocket({
|
|
100
|
-
// @ts-expect-error Not present in type definition
|
|
101
|
-
handle: socket.handle,
|
|
102
|
-
pauseOnCreate: true,
|
|
103
153
|
allowHalfOpen: true,
|
|
104
154
|
}));
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
155
|
+
tcpSocket.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
|
|
156
|
+
await new Promise((resolve, reject) => {
|
|
157
|
+
function handleErr(err) {
|
|
158
|
+
tcpSocket.off("connect", handleConnect);
|
|
159
|
+
reject(convertSocketError(err));
|
|
160
|
+
}
|
|
161
|
+
function handleConnect() {
|
|
162
|
+
tcpSocket.off("error", handleErr);
|
|
163
|
+
resolve();
|
|
164
|
+
}
|
|
165
|
+
tcpSocket.once("connect", handleConnect);
|
|
166
|
+
tcpSocket.once("error", handleErr);
|
|
167
|
+
tcpSocket.connect({
|
|
168
|
+
port: remoteAddress.val.port,
|
|
169
|
+
host: serializeIpAddress(remoteAddress) ?? undefined,
|
|
170
|
+
lookup: noLookup,
|
|
171
|
+
localAddress: localAddress
|
|
172
|
+
? (serializeIpAddress(localAddress) ?? undefined)
|
|
173
|
+
: undefined,
|
|
174
|
+
localPort: localAddress?.val.port,
|
|
175
|
+
});
|
|
119
176
|
});
|
|
120
|
-
|
|
177
|
+
return [createReadableStream(tcpSocket), createWritableStream(tcpSocket)];
|
|
178
|
+
})(), socket.pollState);
|
|
121
179
|
}
|
|
122
180
|
export function socketTcpListenStart(id) {
|
|
123
181
|
const socket = tcpSockets.get(id);
|
|
124
182
|
if (socket.state !== SOCKET_STATE_BOUND) {
|
|
125
183
|
throw "invalid-state";
|
|
126
184
|
}
|
|
127
|
-
const { handle } = socket;
|
|
128
185
|
socket.state = SOCKET_STATE_LISTEN;
|
|
129
|
-
socket.future = createFuture(
|
|
130
|
-
const server = new Server({
|
|
131
|
-
pauseOnConnect: true,
|
|
132
|
-
allowHalfOpen: true,
|
|
133
|
-
});
|
|
134
|
-
function handleErr(err) {
|
|
135
|
-
server.off("listening", handleListen);
|
|
136
|
-
reject(convertSocketError(err));
|
|
137
|
-
}
|
|
138
|
-
function handleListen() {
|
|
139
|
-
server.off("error", handleErr);
|
|
140
|
-
server.on("connection", (tcpSocket) => {
|
|
141
|
-
pollStateReady(socket.pollState);
|
|
142
|
-
const pollState = createReadableStreamPollState(tcpSocket);
|
|
143
|
-
socket.pendingAccepts.push({
|
|
144
|
-
tcpSocket,
|
|
145
|
-
err: null,
|
|
146
|
-
pollState,
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
server.on("error", (err) => {
|
|
150
|
-
pollStateReady(socket.pollState);
|
|
151
|
-
socket.pendingAccepts.push({
|
|
152
|
-
tcpSocket: null,
|
|
153
|
-
err,
|
|
154
|
-
pollState: null,
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
resolve();
|
|
158
|
-
}
|
|
159
|
-
server.once("listening", handleListen);
|
|
160
|
-
server.once("error", handleErr);
|
|
161
|
-
server.listen(handle, socket.listenBacklogSize);
|
|
162
|
-
}), socket.pollState);
|
|
186
|
+
socket.future = createFuture(Promise.resolve(), socket.pollState);
|
|
163
187
|
}
|
|
164
188
|
export function socketTcpAccept(id) {
|
|
165
189
|
const socket = tcpSockets.get(id);
|
|
@@ -181,9 +205,11 @@ export function socketTcpAccept(id) {
|
|
|
181
205
|
state: SOCKET_STATE_CONNECTION,
|
|
182
206
|
future: null,
|
|
183
207
|
tcpSocket: accept.tcpSocket,
|
|
208
|
+
server: null,
|
|
209
|
+
localAddress: null,
|
|
184
210
|
listenBacklogSize: 128,
|
|
185
|
-
|
|
186
|
-
|
|
211
|
+
keepAlive: false,
|
|
212
|
+
keepAliveIdleTime: 0,
|
|
187
213
|
pendingAccepts: [],
|
|
188
214
|
pollState: accept.pollState,
|
|
189
215
|
});
|
|
@@ -207,22 +233,22 @@ export function socketTcpSetListenBacklogSize(id, backlogSize) {
|
|
|
207
233
|
socket.listenBacklogSize = Number(backlogSize);
|
|
208
234
|
}
|
|
209
235
|
export function socketTcpGetLocalAddress(id) {
|
|
210
|
-
const
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
236
|
+
const socket = tcpSockets.get(id);
|
|
237
|
+
const address = socket.tcpSocket?.address();
|
|
238
|
+
if (address && typeof address !== "string" && "family" in address) {
|
|
239
|
+
return ipSocketAddress(address.family.toLowerCase(), address.address, address.port);
|
|
240
|
+
}
|
|
241
|
+
if (socket.localAddress) {
|
|
242
|
+
return socket.localAddress;
|
|
215
243
|
}
|
|
216
|
-
|
|
244
|
+
throw "invalid-state";
|
|
217
245
|
}
|
|
218
246
|
export function socketTcpGetRemoteAddress(id) {
|
|
219
|
-
const {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (code !== 0) {
|
|
223
|
-
throw convertSocketErrorCode(-code);
|
|
247
|
+
const { tcpSocket } = tcpSockets.get(id);
|
|
248
|
+
if (!tcpSocket?.remoteFamily || !tcpSocket.remoteAddress || !tcpSocket.remotePort) {
|
|
249
|
+
throw "invalid-state";
|
|
224
250
|
}
|
|
225
|
-
return ipSocketAddress(
|
|
251
|
+
return ipSocketAddress(tcpSocket.remoteFamily.toLowerCase(), tcpSocket.remoteAddress, tcpSocket.remotePort);
|
|
226
252
|
}
|
|
227
253
|
export function socketTcpShutdown(id, _shutdownType) {
|
|
228
254
|
const socket = tcpSockets.get(id);
|
|
@@ -237,15 +263,15 @@ export function socketTcpShutdown(id, _shutdownType) {
|
|
|
237
263
|
}
|
|
238
264
|
}
|
|
239
265
|
export function socketTcpSetKeepAlive(id, { keepAlive, keepAliveIdleTime }) {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
266
|
+
const socket = tcpSockets.get(id);
|
|
267
|
+
socket.keepAlive = keepAlive;
|
|
268
|
+
socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
|
|
269
|
+
socket.tcpSocket?.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
|
|
245
270
|
}
|
|
246
271
|
export function socketTcpDispose(id) {
|
|
247
272
|
const socket = tcpSockets.get(id);
|
|
248
273
|
verifyPollsDroppedForDrop(socket.pollState, "tcp socket");
|
|
249
|
-
socket.
|
|
274
|
+
socket.server?.close();
|
|
275
|
+
socket.tcpSocket?.destroy();
|
|
250
276
|
tcpSockets.delete(id);
|
|
251
277
|
}
|
|
@@ -719,18 +719,19 @@ async function getDefaultReceiveBufferSize() {
|
|
|
719
719
|
}
|
|
720
720
|
//#endregion
|
|
721
721
|
//#region dist/io/worker-socket-tcp.js
|
|
722
|
-
const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
|
|
723
722
|
const win = process.platform === "win32";
|
|
724
723
|
const tcpSockets = /* @__PURE__ */ new Map();
|
|
725
724
|
let tcpSocketCnt = 0;
|
|
726
725
|
function createTcpSocket() {
|
|
727
|
-
const handle = new TCP(TCPConstants.SOCKET);
|
|
728
726
|
tcpSockets.set(++tcpSocketCnt, {
|
|
729
727
|
state: SOCKET_STATE_INIT,
|
|
730
728
|
future: null,
|
|
731
729
|
tcpSocket: null,
|
|
730
|
+
server: null,
|
|
731
|
+
localAddress: null,
|
|
732
732
|
listenBacklogSize: 128,
|
|
733
|
-
|
|
733
|
+
keepAlive: false,
|
|
734
|
+
keepAliveIdleTime: 0,
|
|
734
735
|
pendingAccepts: [],
|
|
735
736
|
pollState: {
|
|
736
737
|
ready: true,
|
|
@@ -753,7 +754,7 @@ function socketTcpFinish(id, fromState, toState) {
|
|
|
753
754
|
throw val;
|
|
754
755
|
} else {
|
|
755
756
|
socket.state = toState;
|
|
756
|
-
if (toState === SOCKET_STATE_LISTENER) socket.pollState.ready =
|
|
757
|
+
if (toState === SOCKET_STATE_LISTENER) socket.pollState.ready = socket.pendingAccepts.length > 0;
|
|
757
758
|
return val;
|
|
758
759
|
}
|
|
759
760
|
}
|
|
@@ -762,85 +763,109 @@ function socketTcpBindStart(id, localAddress, family) {
|
|
|
762
763
|
if (socket.state !== SOCKET_STATE_INIT) throw "invalid-state";
|
|
763
764
|
if (family !== localAddress.tag || !isUnicastIpAddress(localAddress) || isIPv4MappedAddress(localAddress)) throw "invalid-argument";
|
|
764
765
|
socket.state = SOCKET_STATE_BIND;
|
|
765
|
-
|
|
766
|
-
socket.future = createFuture((async () => {
|
|
766
|
+
socket.future = createFuture(new Promise((resolve, reject) => {
|
|
767
767
|
const address = serializeIpAddress(localAddress);
|
|
768
768
|
const port = localAddress.val.port;
|
|
769
|
-
const
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
if (code !== 0) throw convertSocketErrorCode(-code);
|
|
769
|
+
const server = socket.server = createTcpServer(socket);
|
|
770
|
+
function handleErr(err) {
|
|
771
|
+
server.off("listening", handleListen);
|
|
772
|
+
reject(convertSocketError(err));
|
|
774
773
|
}
|
|
775
|
-
|
|
774
|
+
function handleListen() {
|
|
775
|
+
server.off("error", handleErr);
|
|
776
|
+
const boundAddress = server.address();
|
|
777
|
+
if (!boundAddress || typeof boundAddress === "string") {
|
|
778
|
+
reject("invalid-state");
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
socket.localAddress = ipSocketAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
|
|
782
|
+
resolve();
|
|
783
|
+
}
|
|
784
|
+
server.once("listening", handleListen);
|
|
785
|
+
server.once("error", handleErr);
|
|
786
|
+
server.listen({
|
|
787
|
+
host: address ?? void 0,
|
|
788
|
+
port,
|
|
789
|
+
backlog: socket.listenBacklogSize,
|
|
790
|
+
ipv6Only: family === "ipv6"
|
|
791
|
+
});
|
|
792
|
+
}), socket.pollState);
|
|
793
|
+
}
|
|
794
|
+
function createTcpServer(socket) {
|
|
795
|
+
const server = new Server({
|
|
796
|
+
pauseOnConnect: true,
|
|
797
|
+
allowHalfOpen: true
|
|
798
|
+
});
|
|
799
|
+
server.on("connection", (tcpSocket) => {
|
|
800
|
+
if (socket.state !== SOCKET_STATE_LISTEN && socket.state !== SOCKET_STATE_LISTENER) {
|
|
801
|
+
tcpSocket.destroy();
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
pollStateReady(socket.pollState);
|
|
805
|
+
const pollState = createReadableStreamPollState(tcpSocket);
|
|
806
|
+
socket.pendingAccepts.push({
|
|
807
|
+
tcpSocket,
|
|
808
|
+
err: null,
|
|
809
|
+
pollState
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
server.on("error", (err) => {
|
|
813
|
+
if (socket.state === SOCKET_STATE_LISTEN || socket.state === SOCKET_STATE_LISTENER) {
|
|
814
|
+
pollStateReady(socket.pollState);
|
|
815
|
+
socket.pendingAccepts.push({
|
|
816
|
+
tcpSocket: null,
|
|
817
|
+
err,
|
|
818
|
+
pollState: null
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
return server;
|
|
823
|
+
}
|
|
824
|
+
function closeTcpServer(socket) {
|
|
825
|
+
const server = socket.server;
|
|
826
|
+
socket.server = null;
|
|
827
|
+
if (!server) return Promise.resolve();
|
|
828
|
+
return new Promise((resolve, reject) => {
|
|
829
|
+
server.close((err) => err ? reject(err) : resolve());
|
|
830
|
+
});
|
|
776
831
|
}
|
|
777
832
|
function socketTcpConnectStart(id, remoteAddress, family) {
|
|
778
833
|
const socket = tcpSockets.get(id);
|
|
779
834
|
if (socket.state !== SOCKET_STATE_INIT && socket.state !== SOCKET_STATE_BOUND) throw "invalid-state";
|
|
780
835
|
if (isWildcardAddress(remoteAddress) || family !== remoteAddress.tag || !isUnicastIpAddress(remoteAddress) || isMulticastIpAddress(remoteAddress) || remoteAddress.val.port === 0 || isIPv4MappedAddress(remoteAddress)) throw "invalid-argument";
|
|
781
836
|
socket.state = SOCKET_STATE_CONNECT;
|
|
782
|
-
socket.future = createFuture(
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
837
|
+
socket.future = createFuture((async () => {
|
|
838
|
+
const localAddress = socket.localAddress;
|
|
839
|
+
await closeTcpServer(socket);
|
|
840
|
+
const tcpSocket = socket.tcpSocket = new Socket({ allowHalfOpen: true });
|
|
841
|
+
tcpSocket.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
|
|
842
|
+
await new Promise((resolve, reject) => {
|
|
843
|
+
function handleErr(err) {
|
|
844
|
+
tcpSocket.off("connect", handleConnect);
|
|
845
|
+
reject(convertSocketError(err));
|
|
846
|
+
}
|
|
847
|
+
function handleConnect() {
|
|
848
|
+
tcpSocket.off("error", handleErr);
|
|
849
|
+
resolve();
|
|
850
|
+
}
|
|
851
|
+
tcpSocket.once("connect", handleConnect);
|
|
852
|
+
tcpSocket.once("error", handleErr);
|
|
853
|
+
tcpSocket.connect({
|
|
854
|
+
port: remoteAddress.val.port,
|
|
855
|
+
host: serializeIpAddress(remoteAddress) ?? void 0,
|
|
856
|
+
lookup: noLookup,
|
|
857
|
+
localAddress: localAddress ? serializeIpAddress(localAddress) ?? void 0 : void 0,
|
|
858
|
+
localPort: localAddress?.val.port
|
|
859
|
+
});
|
|
802
860
|
});
|
|
803
|
-
|
|
861
|
+
return [createReadableStream(tcpSocket), createWritableStream(tcpSocket)];
|
|
862
|
+
})(), socket.pollState);
|
|
804
863
|
}
|
|
805
864
|
function socketTcpListenStart(id) {
|
|
806
865
|
const socket = tcpSockets.get(id);
|
|
807
866
|
if (socket.state !== SOCKET_STATE_BOUND) throw "invalid-state";
|
|
808
|
-
const { handle } = socket;
|
|
809
867
|
socket.state = SOCKET_STATE_LISTEN;
|
|
810
|
-
socket.future = createFuture(
|
|
811
|
-
const server = new Server({
|
|
812
|
-
pauseOnConnect: true,
|
|
813
|
-
allowHalfOpen: true
|
|
814
|
-
});
|
|
815
|
-
function handleErr(err) {
|
|
816
|
-
server.off("listening", handleListen);
|
|
817
|
-
reject(convertSocketError(err));
|
|
818
|
-
}
|
|
819
|
-
function handleListen() {
|
|
820
|
-
server.off("error", handleErr);
|
|
821
|
-
server.on("connection", (tcpSocket) => {
|
|
822
|
-
pollStateReady(socket.pollState);
|
|
823
|
-
const pollState = createReadableStreamPollState(tcpSocket);
|
|
824
|
-
socket.pendingAccepts.push({
|
|
825
|
-
tcpSocket,
|
|
826
|
-
err: null,
|
|
827
|
-
pollState
|
|
828
|
-
});
|
|
829
|
-
});
|
|
830
|
-
server.on("error", (err) => {
|
|
831
|
-
pollStateReady(socket.pollState);
|
|
832
|
-
socket.pendingAccepts.push({
|
|
833
|
-
tcpSocket: null,
|
|
834
|
-
err,
|
|
835
|
-
pollState: null
|
|
836
|
-
});
|
|
837
|
-
});
|
|
838
|
-
resolve();
|
|
839
|
-
}
|
|
840
|
-
server.once("listening", handleListen);
|
|
841
|
-
server.once("error", handleErr);
|
|
842
|
-
server.listen(handle, socket.listenBacklogSize);
|
|
843
|
-
}), socket.pollState);
|
|
868
|
+
socket.future = createFuture(Promise.resolve(), socket.pollState);
|
|
844
869
|
}
|
|
845
870
|
function socketTcpAccept(id) {
|
|
846
871
|
const socket = tcpSockets.get(id);
|
|
@@ -856,8 +881,11 @@ function socketTcpAccept(id) {
|
|
|
856
881
|
state: SOCKET_STATE_CONNECTION,
|
|
857
882
|
future: null,
|
|
858
883
|
tcpSocket: accept.tcpSocket,
|
|
884
|
+
server: null,
|
|
885
|
+
localAddress: null,
|
|
859
886
|
listenBacklogSize: 128,
|
|
860
|
-
|
|
887
|
+
keepAlive: false,
|
|
888
|
+
keepAliveIdleTime: 0,
|
|
861
889
|
pendingAccepts: [],
|
|
862
890
|
pollState: accept.pollState
|
|
863
891
|
});
|
|
@@ -874,18 +902,16 @@ function socketTcpSetListenBacklogSize(id, backlogSize) {
|
|
|
874
902
|
socket.listenBacklogSize = Number(backlogSize);
|
|
875
903
|
}
|
|
876
904
|
function socketTcpGetLocalAddress(id) {
|
|
877
|
-
const
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
if (
|
|
881
|
-
|
|
905
|
+
const socket = tcpSockets.get(id);
|
|
906
|
+
const address = socket.tcpSocket?.address();
|
|
907
|
+
if (address && typeof address !== "string" && "family" in address) return ipSocketAddress(address.family.toLowerCase(), address.address, address.port);
|
|
908
|
+
if (socket.localAddress) return socket.localAddress;
|
|
909
|
+
throw "invalid-state";
|
|
882
910
|
}
|
|
883
911
|
function socketTcpGetRemoteAddress(id) {
|
|
884
|
-
const {
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
if (code !== 0) throw convertSocketErrorCode(-code);
|
|
888
|
-
return ipSocketAddress(out.family.toLowerCase(), out.address, out.port);
|
|
912
|
+
const { tcpSocket } = tcpSockets.get(id);
|
|
913
|
+
if (!tcpSocket?.remoteFamily || !tcpSocket.remoteAddress || !tcpSocket.remotePort) throw "invalid-state";
|
|
914
|
+
return ipSocketAddress(tcpSocket.remoteFamily.toLowerCase(), tcpSocket.remoteAddress, tcpSocket.remotePort);
|
|
889
915
|
}
|
|
890
916
|
function socketTcpShutdown(id, _shutdownType) {
|
|
891
917
|
const socket = tcpSockets.get(id);
|
|
@@ -894,14 +920,16 @@ function socketTcpShutdown(id, _shutdownType) {
|
|
|
894
920
|
else socket.tcpSocket?.destroy();
|
|
895
921
|
}
|
|
896
922
|
function socketTcpSetKeepAlive(id, { keepAlive, keepAliveIdleTime }) {
|
|
897
|
-
const
|
|
898
|
-
|
|
899
|
-
|
|
923
|
+
const socket = tcpSockets.get(id);
|
|
924
|
+
socket.keepAlive = keepAlive;
|
|
925
|
+
socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
|
|
926
|
+
socket.tcpSocket?.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
|
|
900
927
|
}
|
|
901
928
|
function socketTcpDispose(id) {
|
|
902
929
|
const socket = tcpSockets.get(id);
|
|
903
930
|
verifyPollsDroppedForDrop(socket.pollState, "tcp socket");
|
|
904
|
-
socket.
|
|
931
|
+
socket.server?.close();
|
|
932
|
+
socket.tcpSocket?.destroy();
|
|
905
933
|
tcpSockets.delete(id);
|
|
906
934
|
}
|
|
907
935
|
//#endregion
|