@libp2p/tcp 0.0.0 → 1.0.3

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.
package/LICENSE ADDED
@@ -0,0 +1,4 @@
1
+ This project is dual licensed under MIT and Apache-2.0.
2
+
3
+ MIT: https://www.opensource.org/licenses/mit
4
+ Apache-2.0: https://www.apache.org/licenses/license-2.0
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # js-libp2p-tcp <!-- omit in toc -->
2
+
3
+ [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai)
4
+ [![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
5
+ [![](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p)
6
+ [![Discourse posts](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg)](https://discuss.libp2p.io)
7
+ [![](https://img.shields.io/codecov/c/github/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p-tcp)
8
+ [![Build Status](https://github.com/libp2p/js-libp2p-tcp/actions/workflows/js-test-and-release.yml/badge.svg?branch=main)](https://github.com/libp2p/js-libp2p-tcp/actions/workflows/js-test-and-release.yml)
9
+ [![Dependency Status](https://david-dm.org/libp2p/js-libp2p-tcp.svg?style=flat-square)](https://david-dm.org/libp2p/js-libp2p-tcp)
10
+ [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
11
+
12
+ [![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/src/transport/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/transport)
13
+ [![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/src/connection/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/connection)
14
+
15
+ > JavaScript implementation of the TCP module for libp2p. It exposes the [interface-transport](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/transport) for dial/listen. `libp2p-tcp` is a very thin shim that adds support for dialing to a `multiaddr`. This small shim will enable libp2p to use other transports.
16
+
17
+ ## Table of Contents <!-- omit in toc -->
18
+
19
+ - [Install](#install)
20
+ - [npm](#npm)
21
+ - [Usage](#usage)
22
+ - [API](#api)
23
+ - [Transport](#transport)
24
+ - [Connection](#connection)
25
+ - [Contribute](#contribute)
26
+ - [Contribute](#contribute-1)
27
+ - [License](#license)
28
+ - [Contribution](#contribution)
29
+
30
+ ## Install
31
+
32
+ ### npm
33
+
34
+ ```sh
35
+ > npm install @libp2p/tcp
36
+ ```
37
+
38
+ ## Usage
39
+
40
+ ```js
41
+ import { TCP } from '@libp2p/tcp'
42
+ import { Multiaddr } from '@multiformats/multiaddr'
43
+ import pipe from 'it-pipe'
44
+ import { collect } from 'streaming-iterables'
45
+
46
+ // A simple upgrader that just returns the MultiaddrConnection
47
+ const upgrader = {
48
+ upgradeInbound: maConn => maConn,
49
+ upgradeOutbound: maConn => maConn
50
+ }
51
+
52
+ const tcp = new TCP({ upgrader })
53
+
54
+ const listener = tcp.createListener({}, (socket) => {
55
+ console.log('new connection opened')
56
+ pipe(
57
+ ['hello'],
58
+ socket
59
+ )
60
+ })
61
+
62
+ const addr = multiaddr('/ip4/127.0.0.1/tcp/9090')
63
+ await listener.listen(addr)
64
+ console.log('listening')
65
+
66
+ const socket = await tcp.dial(addr)
67
+ const values = await pipe(
68
+ socket,
69
+ collect
70
+ )
71
+ console.log(`Value: ${values.toString()}`)
72
+
73
+ // Close connection after reading
74
+ await listener.close()
75
+ ```
76
+
77
+ Outputs:
78
+
79
+ ```sh
80
+ listening
81
+ new connection opened
82
+ Value: hello
83
+ ```
84
+
85
+ ## API
86
+
87
+ ### Transport
88
+
89
+ [![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/src/transport/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/transport)
90
+
91
+ `libp2p-tcp` accepts TCP addresses as both IPFS and non IPFS encapsulated addresses, i.e:
92
+
93
+ `/ip4/127.0.0.1/tcp/4001`
94
+ `/ip4/127.0.0.1/tcp/4001/ipfs/QmHash`
95
+
96
+ (both for dialing and listening)
97
+
98
+ ### Connection
99
+
100
+ [![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/src/connection/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/connection)
101
+
102
+ ## Contribute
103
+
104
+ Contributions are welcome! The libp2p implementation in JavaScript is a work in progress. As such, there's a few things you can do right now to help out:
105
+
106
+ - [Check out the existing issues](//github.com/libp2p/js-libp2p-tcp/issues).
107
+ - **Perform code reviews**.
108
+ - **Add tests**. There can never be enough tests.
109
+
110
+ Please be aware that all interactions related to libp2p are subject to the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
111
+
112
+ Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
113
+
114
+ ## Contribute
115
+
116
+ The libp2p implementation in JavaScript is a work in progress. As such, there are a few things you can do right now to help out:
117
+
118
+ - Go through the modules and **check out existing issues**. This is especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically.
119
+ - **Perform code reviews**. More eyes will help a) speed the project along b) ensure quality and c) reduce possible future bugs.
120
+
121
+ ## License
122
+
123
+ Licensed under either of
124
+
125
+ * Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / http://www.apache.org/licenses/LICENSE-2.0)
126
+ * MIT ([LICENSE-MIT](LICENSE-MIT) / http://opensource.org/licenses/MIT)
127
+
128
+ ### Contribution
129
+
130
+ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
@@ -0,0 +1,4 @@
1
+ export declare const CODE_P2P = 421;
2
+ export declare const CODE_CIRCUIT = 290;
3
+ export declare const CLOSE_TIMEOUT = 2000;
4
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,QAAQ,MAAM,CAAA;AAC3B,eAAO,MAAM,YAAY,MAAM,CAAA;AAG/B,eAAO,MAAM,aAAa,OAAO,CAAA"}
@@ -0,0 +1,6 @@
1
+ // p2p multi-address code
2
+ export const CODE_P2P = 421;
3
+ export const CODE_CIRCUIT = 290;
4
+ // Time to wait for a connection to close gracefully before destroying it manually
5
+ export const CLOSE_TIMEOUT = 2000;
6
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,CAAA;AAC3B,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAA;AAE/B,kFAAkF;AAClF,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAA"}
@@ -0,0 +1,35 @@
1
+ /// <reference types="node" />
2
+ import net from 'net';
3
+ import type { Transport, Upgrader, ListenerOptions } from '@libp2p/interfaces/transport';
4
+ import type { Multiaddr } from '@multiformats/multiaddr';
5
+ /**
6
+ * @typedef {import('multiaddr').Multiaddr} Multiaddr
7
+ * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
8
+ * @typedef {import('libp2p-interfaces/src/transport/types').Upgrader} Upgrader
9
+ * @typedef {import('libp2p-interfaces/src/transport/types').Listener} Listener
10
+ * @typedef {import('net').Socket} Socket
11
+ */
12
+ interface TCPOptions {
13
+ upgrader: Upgrader;
14
+ }
15
+ interface DialOptions {
16
+ signal?: AbortSignal;
17
+ }
18
+ export declare class TCP implements Transport<DialOptions, ListenerOptions> {
19
+ private readonly _upgrader;
20
+ constructor(options: TCPOptions);
21
+ dial(ma: Multiaddr, options?: DialOptions): Promise<import("@libp2p/interfaces/dist/src/connection").Connection>;
22
+ _connect(ma: Multiaddr, options?: DialOptions): Promise<net.Socket>;
23
+ /**
24
+ * Creates a TCP listener. The provided `handler` function will be called
25
+ * anytime a new incoming Connection has been successfully upgraded via
26
+ * `upgrader.upgradeInbound`.
27
+ */
28
+ createListener(options?: ListenerOptions): import("@libp2p/interfaces/transport").Listener;
29
+ /**
30
+ * Takes a list of `Multiaddr`s and returns only valid TCP addresses
31
+ */
32
+ filter(multiaddrs: Multiaddr[]): Multiaddr[];
33
+ }
34
+ export {};
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA,OAAO,GAAG,MAAM,KAAK,CAAA;AASrB,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAA;AACxF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAKxD;;;;;;GAMG;AAEH,UAAU,UAAU;IAClB,QAAQ,EAAE,QAAQ,CAAA;CACnB;AAED,UAAU,WAAW;IACnB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,qBAAa,GAAI,YAAW,SAAS,CAAC,WAAW,EAAE,eAAe,CAAC;IACjE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;gBAEvB,OAAO,EAAE,UAAU;IAU1B,IAAI,CAAE,EAAE,EAAE,SAAS,EAAE,OAAO,GAAE,WAAgB;IAe9C,QAAQ,CAAE,EAAE,EAAE,SAAS,EAAE,OAAO,GAAE,WAAgB;IA+DxD;;;;OAIG;IACH,cAAc,CAAE,OAAO,GAAE,eAAoB;IAI7C;;OAEG;IACH,MAAM,CAAE,UAAU,EAAE,SAAS,EAAE;CAWhC"}
@@ -0,0 +1,100 @@
1
+ import net from 'net';
2
+ import * as mafmt from '@multiformats/mafmt';
3
+ import errCode from 'err-code';
4
+ import debug from 'debug';
5
+ import { toMultiaddrConnection } from './socket-to-conn.js';
6
+ import { createListener } from './listener.js';
7
+ import { multiaddrToNetConfig } from './utils.js';
8
+ import { AbortError } from 'abortable-iterator';
9
+ import { CODE_CIRCUIT, CODE_P2P } from './constants.js';
10
+ const log = debug('libp2p:tcp');
11
+ export class TCP {
12
+ constructor(options) {
13
+ const { upgrader } = options;
14
+ if (upgrader == null) {
15
+ throw new Error('An upgrader must be provided. See https://github.com/libp2p/interface-transport#upgrader.');
16
+ }
17
+ this._upgrader = upgrader;
18
+ }
19
+ async dial(ma, options = {}) {
20
+ const socket = await this._connect(ma, options);
21
+ // Avoid uncaught errors caused by unstable connections
22
+ socket.on('error', err => {
23
+ log('socket error', err);
24
+ });
25
+ const maConn = toMultiaddrConnection(socket, { remoteAddr: ma, signal: options.signal });
26
+ log('new outbound connection %s', maConn.remoteAddr);
27
+ const conn = await this._upgrader.upgradeOutbound(maConn);
28
+ log('outbound connection %s upgraded', maConn.remoteAddr);
29
+ return conn;
30
+ }
31
+ async _connect(ma, options = {}) {
32
+ if (options.signal?.aborted === true) {
33
+ throw new AbortError();
34
+ }
35
+ return await new Promise((resolve, reject) => {
36
+ const start = Date.now();
37
+ const cOpts = multiaddrToNetConfig(ma);
38
+ log('dialing %j', cOpts);
39
+ const rawSocket = net.connect(cOpts);
40
+ const onError = (err) => {
41
+ err.message = `connection error ${cOpts.host}:${cOpts.port}: ${err.message}`;
42
+ done(err);
43
+ };
44
+ const onTimeout = () => {
45
+ log('connection timeout %s:%s', cOpts.host, cOpts.port);
46
+ const err = errCode(new Error(`connection timeout after ${Date.now() - start}ms`), 'ERR_CONNECT_TIMEOUT');
47
+ // Note: this will result in onError() being called
48
+ rawSocket.emit('error', err);
49
+ };
50
+ const onConnect = () => {
51
+ log('connection opened %j', cOpts);
52
+ done();
53
+ };
54
+ const onAbort = () => {
55
+ log('connection aborted %j', cOpts);
56
+ rawSocket.destroy();
57
+ done(new AbortError());
58
+ };
59
+ const done = (err) => {
60
+ rawSocket.removeListener('error', onError);
61
+ rawSocket.removeListener('timeout', onTimeout);
62
+ rawSocket.removeListener('connect', onConnect);
63
+ if (options.signal != null) {
64
+ options.signal.removeEventListener('abort', onAbort);
65
+ }
66
+ if (err != null) {
67
+ return reject(err);
68
+ }
69
+ resolve(rawSocket);
70
+ };
71
+ rawSocket.on('error', onError);
72
+ rawSocket.on('timeout', onTimeout);
73
+ rawSocket.on('connect', onConnect);
74
+ if (options.signal != null) {
75
+ options.signal.addEventListener('abort', onAbort);
76
+ }
77
+ });
78
+ }
79
+ /**
80
+ * Creates a TCP listener. The provided `handler` function will be called
81
+ * anytime a new incoming Connection has been successfully upgraded via
82
+ * `upgrader.upgradeInbound`.
83
+ */
84
+ createListener(options = {}) {
85
+ return createListener({ upgrader: this._upgrader, ...options });
86
+ }
87
+ /**
88
+ * Takes a list of `Multiaddr`s and returns only valid TCP addresses
89
+ */
90
+ filter(multiaddrs) {
91
+ multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs];
92
+ return multiaddrs.filter(ma => {
93
+ if (ma.protoCodes().includes(CODE_CIRCUIT)) {
94
+ return false;
95
+ }
96
+ return mafmt.TCP.matches(ma.decapsulateCode(CODE_P2P));
97
+ });
98
+ }
99
+ }
100
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Connection } from '@libp2p/interfaces/connection';
2
+ import type { Upgrader, Listener } from '@libp2p/interfaces/transport';
3
+ interface Context {
4
+ handler?: (conn: Connection) => void;
5
+ upgrader: Upgrader;
6
+ }
7
+ /**
8
+ * Create listener
9
+ */
10
+ export declare function createListener(context: Context): Listener;
11
+ export {};
12
+ //# sourceMappingURL=listener.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listener.d.ts","sourceRoot":"","sources":["../../src/listener.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAC/D,OAAO,KAAK,EAAuB,QAAQ,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAuB3F,UAAU,OAAO;IACf,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAA;IACpC,QAAQ,EAAE,QAAQ,CAAA;CACnB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAE,OAAO,EAAE,OAAO,YAsH/C"}
@@ -0,0 +1,130 @@
1
+ import net from 'net';
2
+ import { EventEmitter } from 'events';
3
+ import debug from 'debug';
4
+ import { toMultiaddrConnection } from './socket-to-conn.js';
5
+ import { CODE_P2P } from './constants.js';
6
+ import { getMultiaddrs, multiaddrToNetConfig } from './utils.js';
7
+ const log = Object.assign(debug('libp2p:tcp:listener'), { error: debug('libp2p:tcp:listener:error') });
8
+ /**
9
+ * Attempts to close the given maConn. If a failure occurs, it will be logged
10
+ */
11
+ async function attemptClose(maConn) {
12
+ try {
13
+ await maConn.close();
14
+ }
15
+ catch (err) {
16
+ log.error('an error occurred closing the connection', err);
17
+ }
18
+ }
19
+ /**
20
+ * Create listener
21
+ */
22
+ export function createListener(context) {
23
+ const { handler, upgrader } = context;
24
+ let peerId;
25
+ let listeningAddr;
26
+ const server = Object.assign(net.createServer(socket => {
27
+ // Avoid uncaught errors caused by unstable connections
28
+ socket.on('error', err => {
29
+ log('socket error', err);
30
+ });
31
+ let maConn;
32
+ try {
33
+ maConn = toMultiaddrConnection(socket, { listeningAddr });
34
+ }
35
+ catch (err) {
36
+ log.error('inbound connection failed', err);
37
+ return;
38
+ }
39
+ log('new inbound connection %s', maConn.remoteAddr);
40
+ try {
41
+ upgrader.upgradeInbound(maConn)
42
+ .then((conn) => {
43
+ log('inbound connection %s upgraded', maConn.remoteAddr);
44
+ trackConn(server, maConn, socket);
45
+ if (handler != null) {
46
+ handler(conn);
47
+ }
48
+ listener.emit('connection', conn);
49
+ })
50
+ .catch(async (err) => {
51
+ log.error('inbound connection failed', err);
52
+ await attemptClose(maConn);
53
+ })
54
+ .catch(err => {
55
+ log.error('closing inbound connection failed', err);
56
+ });
57
+ }
58
+ catch (err) {
59
+ log.error('inbound connection failed', err);
60
+ attemptClose(maConn)
61
+ .catch(err => {
62
+ log.error('closing inbound connection failed', err);
63
+ });
64
+ }
65
+ }),
66
+ // Keep track of open connections to destroy in case of timeout
67
+ { __connections: [] });
68
+ const listener = Object.assign(new EventEmitter(), {
69
+ getAddrs: () => {
70
+ let addrs = [];
71
+ const address = server.address();
72
+ if (address == null) {
73
+ throw new Error('Listener is not ready yet');
74
+ }
75
+ if (typeof address === 'string') {
76
+ throw new Error('Incorrect server address type');
77
+ }
78
+ // Because TCP will only return the IPv6 version
79
+ // we need to capture from the passed multiaddr
80
+ if (listeningAddr.toString().startsWith('/ip4')) {
81
+ addrs = addrs.concat(getMultiaddrs('ip4', address.address, address.port));
82
+ }
83
+ else if (address.family === 'IPv6') {
84
+ addrs = addrs.concat(getMultiaddrs('ip6', address.address, address.port));
85
+ }
86
+ return addrs.map(ma => peerId != null ? ma.encapsulate(`/p2p/${peerId}`) : ma);
87
+ },
88
+ listen: async (ma) => {
89
+ listeningAddr = ma;
90
+ peerId = ma.getPeerId();
91
+ if (peerId == null) {
92
+ listeningAddr = ma.decapsulateCode(CODE_P2P);
93
+ }
94
+ return await new Promise((resolve, reject) => {
95
+ const options = multiaddrToNetConfig(listeningAddr);
96
+ server.listen(options, (err) => {
97
+ if (err != null) {
98
+ return reject(err);
99
+ }
100
+ log('Listening on %s', server.address());
101
+ resolve();
102
+ });
103
+ });
104
+ },
105
+ close: async () => {
106
+ if (!server.listening) {
107
+ return;
108
+ }
109
+ await Promise.all([
110
+ server.__connections.map(async (maConn) => await attemptClose(maConn))
111
+ ]);
112
+ await new Promise((resolve, reject) => {
113
+ server.close(err => (err != null) ? reject(err) : resolve());
114
+ });
115
+ }
116
+ });
117
+ server
118
+ .on('listening', () => listener.emit('listening'))
119
+ .on('error', err => listener.emit('error', err))
120
+ .on('close', () => listener.emit('close'));
121
+ return listener;
122
+ }
123
+ function trackConn(server, maConn, socket) {
124
+ server.__connections.push(maConn);
125
+ const untrackConn = () => {
126
+ server.__connections = server.__connections.filter(c => c !== maConn);
127
+ };
128
+ socket.once('close', untrackConn);
129
+ }
130
+ //# sourceMappingURL=listener.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listener.js","sourceRoot":"","sources":["../../src/listener.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Socket } from 'net';
2
+ import type { Multiaddr } from '@multiformats/multiaddr';
3
+ import type { MultiaddrConnection } from '@libp2p/interfaces/transport';
4
+ interface ToConnectionOptions {
5
+ listeningAddr?: Multiaddr;
6
+ remoteAddr?: Multiaddr;
7
+ localAddr?: Multiaddr;
8
+ signal?: AbortSignal;
9
+ }
10
+ /**
11
+ * Convert a socket into a MultiaddrConnection
12
+ * https://github.com/libp2p/interface-transport#multiaddrconnection
13
+ */
14
+ export declare const toMultiaddrConnection: (socket: Socket, options?: ToConnectionOptions | undefined) => MultiaddrConnection;
15
+ export {};
16
+ //# sourceMappingURL=socket-to-conn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-to-conn.d.ts","sourceRoot":"","sources":["../../src/socket-to-conn.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AACjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAIvE,UAAU,mBAAmB;IAC3B,aAAa,CAAC,EAAE,SAAS,CAAA;IACzB,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,WAAY,MAAM,mEAmGnD,CAAA"}
@@ -0,0 +1,94 @@
1
+ import { abortableSource } from 'abortable-iterator';
2
+ import debug from 'debug';
3
+ // @ts-expect-error no types
4
+ import toIterable from 'stream-to-it';
5
+ import { ipPortToMultiaddr as toMultiaddr } from '@libp2p/utils/ip-port-to-multiaddr';
6
+ import { CLOSE_TIMEOUT } from './constants.js';
7
+ const log = debug('libp2p:tcp:socket');
8
+ /**
9
+ * Convert a socket into a MultiaddrConnection
10
+ * https://github.com/libp2p/interface-transport#multiaddrconnection
11
+ */
12
+ export const toMultiaddrConnection = (socket, options) => {
13
+ options = options ?? {};
14
+ // Check if we are connected on a unix path
15
+ if (options.listeningAddr?.getPath() != null) {
16
+ options.remoteAddr = options.listeningAddr;
17
+ }
18
+ if (options.remoteAddr?.getPath() != null) {
19
+ options.localAddr = options.remoteAddr;
20
+ }
21
+ const { sink, source } = toIterable.duplex(socket);
22
+ const maConn = {
23
+ async sink(source) {
24
+ if ((options?.signal) != null) {
25
+ source = abortableSource(source, options.signal);
26
+ }
27
+ try {
28
+ await sink((async function* () {
29
+ for await (const chunk of source) {
30
+ // Convert BufferList to Buffer
31
+ // Sink in StreamMuxer define argument as Uint8Array so chunk type infers as number which can't be sliced
32
+ yield Buffer.isBuffer(chunk) ? chunk : chunk.slice();
33
+ }
34
+ })());
35
+ }
36
+ catch (err) {
37
+ // If aborted we can safely ignore
38
+ if (err.type !== 'aborted') {
39
+ // If the source errored the socket will already have been destroyed by
40
+ // toIterable.duplex(). If the socket errored it will already be
41
+ // destroyed. There's nothing to do here except log the error & return.
42
+ log(err);
43
+ }
44
+ }
45
+ },
46
+ // Missing Type for "abortable"
47
+ source: (options.signal != null) ? abortableSource(source, options.signal) : source,
48
+ // If the remote address was passed, use it - it may have the peer ID encapsulated
49
+ remoteAddr: options.remoteAddr ?? toMultiaddr(socket.remoteAddress ?? '', socket.remotePort ?? ''),
50
+ timeline: { open: Date.now() },
51
+ async close() {
52
+ if (socket.destroyed)
53
+ return;
54
+ return await new Promise((resolve, reject) => {
55
+ const start = Date.now();
56
+ // Attempt to end the socket. If it takes longer to close than the
57
+ // timeout, destroy it manually.
58
+ const timeout = setTimeout(() => {
59
+ const { host, port } = maConn.remoteAddr.toOptions();
60
+ log('timeout closing socket to %s:%s after %dms, destroying it manually', host, port, Date.now() - start);
61
+ if (socket.destroyed) {
62
+ log('%s:%s is already destroyed', host, port);
63
+ }
64
+ else {
65
+ socket.destroy();
66
+ }
67
+ resolve();
68
+ }, CLOSE_TIMEOUT).unref();
69
+ socket.once('close', () => {
70
+ clearTimeout(timeout);
71
+ resolve();
72
+ });
73
+ socket.end((err) => {
74
+ clearTimeout(timeout);
75
+ maConn.timeline.close = Date.now();
76
+ if (err != null) {
77
+ return reject(err);
78
+ }
79
+ resolve();
80
+ });
81
+ });
82
+ }
83
+ };
84
+ socket.once('close', () => {
85
+ // In instances where `close` was not explicitly called,
86
+ // such as an iterable stream ending, ensure we have set the close
87
+ // timeline
88
+ if (maConn.timeline.close == null) {
89
+ maConn.timeline.close = Date.now();
90
+ }
91
+ });
92
+ return maConn;
93
+ };
94
+ //# sourceMappingURL=socket-to-conn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-to-conn.js","sourceRoot":"","sources":["../../src/socket-to-conn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { Multiaddr } from '@multiformats/multiaddr';
2
+ export declare function multiaddrToNetConfig(addr: Multiaddr): import("@multiformats/multiaddr").MultiaddrObject;
3
+ export declare function getMultiaddrs(proto: 'ip4' | 'ip6', ip: string, port: number): Multiaddr[];
4
+ export declare function isAnyAddr(ip: string): boolean;
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAKnD,wBAAgB,oBAAoB,CAAE,IAAI,EAAE,SAAS,qDAWpD;AAED,wBAAgB,aAAa,CAAE,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,eAG5E;AAED,wBAAgB,SAAS,CAAE,EAAE,EAAE,MAAM,WAEpC"}
@@ -0,0 +1,35 @@
1
+ import { Multiaddr } from '@multiformats/multiaddr';
2
+ import os from 'os';
3
+ const ProtoFamily = { ip4: 'IPv4', ip6: 'IPv6' };
4
+ export function multiaddrToNetConfig(addr) {
5
+ const listenPath = addr.getPath();
6
+ // unix socket listening
7
+ if (listenPath != null) {
8
+ // TCP should not return unix socket else need to refactor listener which accepts connection options object
9
+ throw new Error('Unix Sockets are not supported by the TCP transport');
10
+ }
11
+ // tcp listening
12
+ return addr.toOptions();
13
+ }
14
+ export function getMultiaddrs(proto, ip, port) {
15
+ const toMa = (ip) => new Multiaddr(`/${proto}/${ip}/tcp/${port}`);
16
+ return (isAnyAddr(ip) ? getNetworkAddrs(ProtoFamily[proto]) : [ip]).map(toMa);
17
+ }
18
+ export function isAnyAddr(ip) {
19
+ return ['0.0.0.0', '::'].includes(ip);
20
+ }
21
+ const networks = os.networkInterfaces();
22
+ function getNetworkAddrs(family) {
23
+ const addresses = [];
24
+ for (const [, netAddrs] of Object.entries(networks)) {
25
+ if (netAddrs != null) {
26
+ for (const netAddr of netAddrs) {
27
+ if (netAddr.family === family) {
28
+ addresses.push(netAddr.address);
29
+ }
30
+ }
31
+ }
32
+ }
33
+ return addresses;
34
+ }
35
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,MAAM,WAAW,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAA;AAEhD,MAAM,UAAU,oBAAoB,CAAE,IAAe;IACnD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;IAEjC,wBAAwB;IACxB,IAAI,UAAU,IAAI,IAAI,EAAE;QACtB,2GAA2G;QAC3G,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;KACvE;IAED,gBAAgB;IAChB,OAAO,IAAI,CAAC,SAAS,EAAE,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,aAAa,CAAE,KAAoB,EAAE,EAAU,EAAE,IAAY;IAC3E,MAAM,IAAI,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC,IAAI,KAAK,IAAI,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAA;IACzE,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AAC/E,CAAC;AAED,MAAM,UAAU,SAAS,CAAE,EAAU;IACnC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;AACvC,CAAC;AAED,MAAM,QAAQ,GAAG,EAAE,CAAC,iBAAiB,EAAE,CAAA;AAEvC,SAAS,eAAe,CAAE,MAAc;IACtC,MAAM,SAAS,GAAG,EAAE,CAAA;IAEpB,KAAK,MAAM,CAAC,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE;QACnD,IAAI,QAAQ,IAAI,IAAI,EAAE;YACpB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE;gBAC9B,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE;oBAC7B,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;iBAChC;aACF;SACF;KACF;IAED,OAAO,SAAS,CAAA;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,157 @@
1
1
  {
2
2
  "name": "@libp2p/tcp",
3
- "version": "0.0.0",
4
- "description": "",
5
- "main": "index.js",
6
- "license": "(Apache-2.0 OR MIT)"
3
+ "version": "1.0.3",
4
+ "description": "Node.js implementation of the TCP module that libp2p uses, which implements the interface-connection and interface-transport interfaces",
5
+ "license": "Apache-2.0 OR MIT",
6
+ "homepage": "https://github.com/libp2p/js-libp2p-tcp#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/libp2p/js-libp2p-tcp.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/libp2p/js-libp2p-tcp/issues"
13
+ },
14
+ "keywords": [
15
+ "IPFS",
16
+ "TCP",
17
+ "libp2p",
18
+ "network",
19
+ "p2p",
20
+ "peer",
21
+ "peer-to-peer"
22
+ ],
23
+ "engines": {
24
+ "node": ">=16.0.0",
25
+ "npm": ">=7.0.0"
26
+ },
27
+ "type": "module",
28
+ "types": "./dist/src/index.d.ts",
29
+ "files": [
30
+ "src",
31
+ "dist/src",
32
+ "!dist/test",
33
+ "!**/*.tsbuildinfo"
34
+ ],
35
+ "exports": {
36
+ ".": {
37
+ "import": "./dist/src/index.js"
38
+ }
39
+ },
40
+ "eslintConfig": {
41
+ "extends": "ipfs",
42
+ "parserOptions": {
43
+ "sourceType": "module"
44
+ }
45
+ },
46
+ "release": {
47
+ "branches": [
48
+ "master"
49
+ ],
50
+ "plugins": [
51
+ [
52
+ "@semantic-release/commit-analyzer",
53
+ {
54
+ "preset": "conventionalcommits",
55
+ "releaseRules": [
56
+ {
57
+ "breaking": true,
58
+ "release": "major"
59
+ },
60
+ {
61
+ "revert": true,
62
+ "release": "patch"
63
+ },
64
+ {
65
+ "type": "feat",
66
+ "release": "minor"
67
+ },
68
+ {
69
+ "type": "fix",
70
+ "release": "patch"
71
+ },
72
+ {
73
+ "type": "chore",
74
+ "release": "patch"
75
+ },
76
+ {
77
+ "type": "docs",
78
+ "release": "patch"
79
+ },
80
+ {
81
+ "type": "test",
82
+ "release": "patch"
83
+ },
84
+ {
85
+ "scope": "no-release",
86
+ "release": false
87
+ }
88
+ ]
89
+ }
90
+ ],
91
+ [
92
+ "@semantic-release/release-notes-generator",
93
+ {
94
+ "preset": "conventionalcommits",
95
+ "presetConfig": {
96
+ "types": [
97
+ {
98
+ "type": "feat",
99
+ "section": "Features"
100
+ },
101
+ {
102
+ "type": "fix",
103
+ "section": "Bug Fixes"
104
+ },
105
+ {
106
+ "type": "chore",
107
+ "section": "Trivial Changes"
108
+ },
109
+ {
110
+ "type": "docs",
111
+ "section": "Trivial Changes"
112
+ },
113
+ {
114
+ "type": "test",
115
+ "section": "Tests"
116
+ }
117
+ ]
118
+ }
119
+ }
120
+ ],
121
+ "@semantic-release/changelog",
122
+ "@semantic-release/npm",
123
+ "@semantic-release/github",
124
+ "@semantic-release/git"
125
+ ]
126
+ },
127
+ "scripts": {
128
+ "lint": "aegir lint",
129
+ "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js",
130
+ "build": "tsc",
131
+ "pretest": "npm run build",
132
+ "test": "aegir test -f ./dist/test",
133
+ "test:node": "npm run test -- -t node --cov",
134
+ "test:electron-main": "npm run test -- -t electron-main",
135
+ "release": "semantic-release"
136
+ },
137
+ "dependencies": {
138
+ "@libp2p/utils": "^1.0.1",
139
+ "@multiformats/mafmt": "^11.0.0",
140
+ "@multiformats/multiaddr": "^10.1.1",
141
+ "abortable-iterator": "^4.0.2",
142
+ "debug": "^4.3.1",
143
+ "err-code": "^3.0.1",
144
+ "stream-to-it": "^0.2.2"
145
+ },
146
+ "devDependencies": {
147
+ "@libp2p/interface-compliance-tests": "^1.0.1",
148
+ "@libp2p/interfaces": "^1.0.0",
149
+ "@types/debug": "^4.1.5",
150
+ "@types/mocha": "^9.0.0",
151
+ "aegir": "^36.1.3",
152
+ "it-pipe": "^2.0.3",
153
+ "sinon": "^13.0.0",
154
+ "streaming-iterables": "^6.0.0",
155
+ "uint8arrays": "^3.0.0"
156
+ }
7
157
  }
@@ -0,0 +1,6 @@
1
+ // p2p multi-address code
2
+ export const CODE_P2P = 421
3
+ export const CODE_CIRCUIT = 290
4
+
5
+ // Time to wait for a connection to close gracefully before destroying it manually
6
+ export const CLOSE_TIMEOUT = 2000
package/src/index.ts ADDED
@@ -0,0 +1,146 @@
1
+ import net from 'net'
2
+ import * as mafmt from '@multiformats/mafmt'
3
+ import errCode from 'err-code'
4
+ import debug from 'debug'
5
+ import { toMultiaddrConnection } from './socket-to-conn.js'
6
+ import { createListener } from './listener.js'
7
+ import { multiaddrToNetConfig } from './utils.js'
8
+ import { AbortError } from 'abortable-iterator'
9
+ import { CODE_CIRCUIT, CODE_P2P } from './constants.js'
10
+ import type { Transport, Upgrader, ListenerOptions } from '@libp2p/interfaces/transport'
11
+ import type { Multiaddr } from '@multiformats/multiaddr'
12
+ import type { Socket } from 'net'
13
+
14
+ const log = debug('libp2p:tcp')
15
+
16
+ /**
17
+ * @typedef {import('multiaddr').Multiaddr} Multiaddr
18
+ * @typedef {import('libp2p-interfaces/src/connection').Connection} Connection
19
+ * @typedef {import('libp2p-interfaces/src/transport/types').Upgrader} Upgrader
20
+ * @typedef {import('libp2p-interfaces/src/transport/types').Listener} Listener
21
+ * @typedef {import('net').Socket} Socket
22
+ */
23
+
24
+ interface TCPOptions {
25
+ upgrader: Upgrader
26
+ }
27
+
28
+ interface DialOptions {
29
+ signal?: AbortSignal
30
+ }
31
+
32
+ export class TCP implements Transport<DialOptions, ListenerOptions> {
33
+ private readonly _upgrader: Upgrader
34
+
35
+ constructor (options: TCPOptions) {
36
+ const { upgrader } = options
37
+
38
+ if (upgrader == null) {
39
+ throw new Error('An upgrader must be provided. See https://github.com/libp2p/interface-transport#upgrader.')
40
+ }
41
+
42
+ this._upgrader = upgrader
43
+ }
44
+
45
+ async dial (ma: Multiaddr, options: DialOptions = {}) {
46
+ const socket = await this._connect(ma, options)
47
+
48
+ // Avoid uncaught errors caused by unstable connections
49
+ socket.on('error', err => {
50
+ log('socket error', err)
51
+ })
52
+
53
+ const maConn = toMultiaddrConnection(socket, { remoteAddr: ma, signal: options.signal })
54
+ log('new outbound connection %s', maConn.remoteAddr)
55
+ const conn = await this._upgrader.upgradeOutbound(maConn)
56
+ log('outbound connection %s upgraded', maConn.remoteAddr)
57
+ return conn
58
+ }
59
+
60
+ async _connect (ma: Multiaddr, options: DialOptions = {}) {
61
+ if (options.signal?.aborted === true) {
62
+ throw new AbortError()
63
+ }
64
+
65
+ return await new Promise<Socket>((resolve, reject) => {
66
+ const start = Date.now()
67
+ const cOpts = multiaddrToNetConfig(ma)
68
+
69
+ log('dialing %j', cOpts)
70
+ const rawSocket = net.connect(cOpts)
71
+
72
+ const onError = (err: Error) => {
73
+ err.message = `connection error ${cOpts.host}:${cOpts.port}: ${err.message}`
74
+
75
+ done(err)
76
+ }
77
+
78
+ const onTimeout = () => {
79
+ log('connection timeout %s:%s', cOpts.host, cOpts.port)
80
+
81
+ const err = errCode(new Error(`connection timeout after ${Date.now() - start}ms`), 'ERR_CONNECT_TIMEOUT')
82
+ // Note: this will result in onError() being called
83
+ rawSocket.emit('error', err)
84
+ }
85
+
86
+ const onConnect = () => {
87
+ log('connection opened %j', cOpts)
88
+ done()
89
+ }
90
+
91
+ const onAbort = () => {
92
+ log('connection aborted %j', cOpts)
93
+ rawSocket.destroy()
94
+ done(new AbortError())
95
+ }
96
+
97
+ const done = (err?: any) => {
98
+ rawSocket.removeListener('error', onError)
99
+ rawSocket.removeListener('timeout', onTimeout)
100
+ rawSocket.removeListener('connect', onConnect)
101
+
102
+ if (options.signal != null) {
103
+ options.signal.removeEventListener('abort', onAbort)
104
+ }
105
+
106
+ if (err != null) {
107
+ return reject(err)
108
+ }
109
+
110
+ resolve(rawSocket)
111
+ }
112
+
113
+ rawSocket.on('error', onError)
114
+ rawSocket.on('timeout', onTimeout)
115
+ rawSocket.on('connect', onConnect)
116
+
117
+ if (options.signal != null) {
118
+ options.signal.addEventListener('abort', onAbort)
119
+ }
120
+ })
121
+ }
122
+
123
+ /**
124
+ * Creates a TCP listener. The provided `handler` function will be called
125
+ * anytime a new incoming Connection has been successfully upgraded via
126
+ * `upgrader.upgradeInbound`.
127
+ */
128
+ createListener (options: ListenerOptions = {}) {
129
+ return createListener({ upgrader: this._upgrader, ...options })
130
+ }
131
+
132
+ /**
133
+ * Takes a list of `Multiaddr`s and returns only valid TCP addresses
134
+ */
135
+ filter (multiaddrs: Multiaddr[]) {
136
+ multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]
137
+
138
+ return multiaddrs.filter(ma => {
139
+ if (ma.protoCodes().includes(CODE_CIRCUIT)) {
140
+ return false
141
+ }
142
+
143
+ return mafmt.TCP.matches(ma.decapsulateCode(CODE_P2P))
144
+ })
145
+ }
146
+ }
@@ -0,0 +1,170 @@
1
+ import net from 'net'
2
+ import { EventEmitter } from 'events'
3
+ import debug from 'debug'
4
+ import { toMultiaddrConnection } from './socket-to-conn.js'
5
+ import { CODE_P2P } from './constants.js'
6
+ import {
7
+ getMultiaddrs,
8
+ multiaddrToNetConfig
9
+ } from './utils.js'
10
+ import type { Connection } from '@libp2p/interfaces/connection'
11
+ import type { MultiaddrConnection, Upgrader, Listener } from '@libp2p/interfaces/transport'
12
+ import type { Server } from 'net'
13
+ import type { Multiaddr } from '@multiformats/multiaddr'
14
+
15
+ const log = Object.assign(
16
+ debug('libp2p:tcp:listener'),
17
+ { error: debug('libp2p:tcp:listener:error') })
18
+
19
+ interface ServerWithMultiaddrConnections extends Server {
20
+ __connections: MultiaddrConnection[]
21
+ }
22
+
23
+ /**
24
+ * Attempts to close the given maConn. If a failure occurs, it will be logged
25
+ */
26
+ async function attemptClose (maConn: MultiaddrConnection) {
27
+ try {
28
+ await maConn.close()
29
+ } catch (err) {
30
+ log.error('an error occurred closing the connection', err)
31
+ }
32
+ }
33
+
34
+ interface Context {
35
+ handler?: (conn: Connection) => void
36
+ upgrader: Upgrader
37
+ }
38
+
39
+ /**
40
+ * Create listener
41
+ */
42
+ export function createListener (context: Context) {
43
+ const {
44
+ handler, upgrader
45
+ } = context
46
+
47
+ let peerId: string | null
48
+ let listeningAddr: Multiaddr
49
+
50
+ const server: ServerWithMultiaddrConnections = Object.assign(net.createServer(socket => {
51
+ // Avoid uncaught errors caused by unstable connections
52
+ socket.on('error', err => {
53
+ log('socket error', err)
54
+ })
55
+
56
+ let maConn: MultiaddrConnection
57
+ try {
58
+ maConn = toMultiaddrConnection(socket, { listeningAddr })
59
+ } catch (err) {
60
+ log.error('inbound connection failed', err)
61
+ return
62
+ }
63
+
64
+ log('new inbound connection %s', maConn.remoteAddr)
65
+ try {
66
+ upgrader.upgradeInbound(maConn)
67
+ .then((conn) => {
68
+ log('inbound connection %s upgraded', maConn.remoteAddr)
69
+ trackConn(server, maConn, socket)
70
+
71
+ if (handler != null) {
72
+ handler(conn)
73
+ }
74
+
75
+ listener.emit('connection', conn)
76
+ })
77
+ .catch(async err => {
78
+ log.error('inbound connection failed', err)
79
+
80
+ await attemptClose(maConn)
81
+ })
82
+ .catch(err => {
83
+ log.error('closing inbound connection failed', err)
84
+ })
85
+ } catch (err) {
86
+ log.error('inbound connection failed', err)
87
+
88
+ attemptClose(maConn)
89
+ .catch(err => {
90
+ log.error('closing inbound connection failed', err)
91
+ })
92
+ }
93
+ }),
94
+ // Keep track of open connections to destroy in case of timeout
95
+ { __connections: [] })
96
+
97
+ const listener: Listener = Object.assign(new EventEmitter(), {
98
+ getAddrs: () => {
99
+ let addrs: Multiaddr[] = []
100
+ const address = server.address()
101
+
102
+ if (address == null) {
103
+ throw new Error('Listener is not ready yet')
104
+ }
105
+
106
+ if (typeof address === 'string') {
107
+ throw new Error('Incorrect server address type')
108
+ }
109
+
110
+ // Because TCP will only return the IPv6 version
111
+ // we need to capture from the passed multiaddr
112
+ if (listeningAddr.toString().startsWith('/ip4')) {
113
+ addrs = addrs.concat(getMultiaddrs('ip4', address.address, address.port))
114
+ } else if (address.family === 'IPv6') {
115
+ addrs = addrs.concat(getMultiaddrs('ip6', address.address, address.port))
116
+ }
117
+
118
+ return addrs.map(ma => peerId != null ? ma.encapsulate(`/p2p/${peerId}`) : ma)
119
+ },
120
+ listen: async (ma: Multiaddr) => {
121
+ listeningAddr = ma
122
+ peerId = ma.getPeerId()
123
+
124
+ if (peerId == null) {
125
+ listeningAddr = ma.decapsulateCode(CODE_P2P)
126
+ }
127
+
128
+ return await new Promise<void>((resolve, reject) => {
129
+ const options = multiaddrToNetConfig(listeningAddr)
130
+ server.listen(options, (err?: any) => {
131
+ if (err != null) {
132
+ return reject(err)
133
+ }
134
+ log('Listening on %s', server.address())
135
+ resolve()
136
+ })
137
+ })
138
+ },
139
+ close: async () => {
140
+ if (!server.listening) {
141
+ return
142
+ }
143
+
144
+ await Promise.all([
145
+ server.__connections.map(async maConn => await attemptClose(maConn))
146
+ ])
147
+
148
+ await new Promise<void>((resolve, reject) => {
149
+ server.close(err => (err != null) ? reject(err) : resolve())
150
+ })
151
+ }
152
+ })
153
+
154
+ server
155
+ .on('listening', () => listener.emit('listening'))
156
+ .on('error', err => listener.emit('error', err))
157
+ .on('close', () => listener.emit('close'))
158
+
159
+ return listener
160
+ }
161
+
162
+ function trackConn (server: ServerWithMultiaddrConnections, maConn: MultiaddrConnection, socket: net.Socket) {
163
+ server.__connections.push(maConn)
164
+
165
+ const untrackConn = () => {
166
+ server.__connections = server.__connections.filter(c => c !== maConn)
167
+ }
168
+
169
+ socket.once('close', untrackConn)
170
+ }
@@ -0,0 +1,123 @@
1
+ import { abortableSource } from 'abortable-iterator'
2
+ import debug from 'debug'
3
+ // @ts-expect-error no types
4
+ import toIterable from 'stream-to-it'
5
+ import { ipPortToMultiaddr as toMultiaddr } from '@libp2p/utils/ip-port-to-multiaddr'
6
+ import { CLOSE_TIMEOUT } from './constants.js'
7
+ import type { Socket } from 'net'
8
+ import type { Multiaddr } from '@multiformats/multiaddr'
9
+ import type { MultiaddrConnection } from '@libp2p/interfaces/transport'
10
+
11
+ const log = debug('libp2p:tcp:socket')
12
+
13
+ interface ToConnectionOptions {
14
+ listeningAddr?: Multiaddr
15
+ remoteAddr?: Multiaddr
16
+ localAddr?: Multiaddr
17
+ signal?: AbortSignal
18
+ }
19
+
20
+ /**
21
+ * Convert a socket into a MultiaddrConnection
22
+ * https://github.com/libp2p/interface-transport#multiaddrconnection
23
+ */
24
+ export const toMultiaddrConnection = (socket: Socket, options?: ToConnectionOptions) => {
25
+ options = options ?? {}
26
+
27
+ // Check if we are connected on a unix path
28
+ if (options.listeningAddr?.getPath() != null) {
29
+ options.remoteAddr = options.listeningAddr
30
+ }
31
+
32
+ if (options.remoteAddr?.getPath() != null) {
33
+ options.localAddr = options.remoteAddr
34
+ }
35
+
36
+ const { sink, source } = toIterable.duplex(socket)
37
+
38
+ const maConn: MultiaddrConnection = {
39
+ async sink (source) {
40
+ if ((options?.signal) != null) {
41
+ source = abortableSource(source, options.signal)
42
+ }
43
+
44
+ try {
45
+ await sink((async function * () {
46
+ for await (const chunk of source) {
47
+ // Convert BufferList to Buffer
48
+ // Sink in StreamMuxer define argument as Uint8Array so chunk type infers as number which can't be sliced
49
+ yield Buffer.isBuffer(chunk) ? chunk : chunk.slice()
50
+ }
51
+ })())
52
+ } catch (err: any) {
53
+ // If aborted we can safely ignore
54
+ if (err.type !== 'aborted') {
55
+ // If the source errored the socket will already have been destroyed by
56
+ // toIterable.duplex(). If the socket errored it will already be
57
+ // destroyed. There's nothing to do here except log the error & return.
58
+ log(err)
59
+ }
60
+ }
61
+ },
62
+
63
+ // Missing Type for "abortable"
64
+ source: (options.signal != null) ? abortableSource(source, options.signal) : source,
65
+
66
+ // If the remote address was passed, use it - it may have the peer ID encapsulated
67
+ remoteAddr: options.remoteAddr ?? toMultiaddr(socket.remoteAddress ?? '', socket.remotePort ?? ''),
68
+
69
+ timeline: { open: Date.now() },
70
+
71
+ async close () {
72
+ if (socket.destroyed) return
73
+
74
+ return await new Promise((resolve, reject) => {
75
+ const start = Date.now()
76
+
77
+ // Attempt to end the socket. If it takes longer to close than the
78
+ // timeout, destroy it manually.
79
+ const timeout = setTimeout(() => {
80
+ const { host, port } = maConn.remoteAddr.toOptions()
81
+ log(
82
+ 'timeout closing socket to %s:%s after %dms, destroying it manually',
83
+ host,
84
+ port,
85
+ Date.now() - start
86
+ )
87
+
88
+ if (socket.destroyed) {
89
+ log('%s:%s is already destroyed', host, port)
90
+ } else {
91
+ socket.destroy()
92
+ }
93
+
94
+ resolve()
95
+ }, CLOSE_TIMEOUT).unref()
96
+
97
+ socket.once('close', () => {
98
+ clearTimeout(timeout)
99
+ resolve()
100
+ })
101
+ socket.end((err?: Error & { code?: string }) => {
102
+ clearTimeout(timeout)
103
+ maConn.timeline.close = Date.now()
104
+ if (err != null) {
105
+ return reject(err)
106
+ }
107
+ resolve()
108
+ })
109
+ })
110
+ }
111
+ }
112
+
113
+ socket.once('close', () => {
114
+ // In instances where `close` was not explicitly called,
115
+ // such as an iterable stream ending, ensure we have set the close
116
+ // timeline
117
+ if (maConn.timeline.close == null) {
118
+ maConn.timeline.close = Date.now()
119
+ }
120
+ })
121
+
122
+ return maConn
123
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,44 @@
1
+ import { Multiaddr } from '@multiformats/multiaddr'
2
+ import os from 'os'
3
+
4
+ const ProtoFamily = { ip4: 'IPv4', ip6: 'IPv6' }
5
+
6
+ export function multiaddrToNetConfig (addr: Multiaddr) {
7
+ const listenPath = addr.getPath()
8
+
9
+ // unix socket listening
10
+ if (listenPath != null) {
11
+ // TCP should not return unix socket else need to refactor listener which accepts connection options object
12
+ throw new Error('Unix Sockets are not supported by the TCP transport')
13
+ }
14
+
15
+ // tcp listening
16
+ return addr.toOptions()
17
+ }
18
+
19
+ export function getMultiaddrs (proto: 'ip4' | 'ip6', ip: string, port: number) {
20
+ const toMa = (ip: string) => new Multiaddr(`/${proto}/${ip}/tcp/${port}`)
21
+ return (isAnyAddr(ip) ? getNetworkAddrs(ProtoFamily[proto]) : [ip]).map(toMa)
22
+ }
23
+
24
+ export function isAnyAddr (ip: string) {
25
+ return ['0.0.0.0', '::'].includes(ip)
26
+ }
27
+
28
+ const networks = os.networkInterfaces()
29
+
30
+ function getNetworkAddrs (family: string) {
31
+ const addresses = []
32
+
33
+ for (const [, netAddrs] of Object.entries(networks)) {
34
+ if (netAddrs != null) {
35
+ for (const netAddr of netAddrs) {
36
+ if (netAddr.family === family) {
37
+ addresses.push(netAddr.address)
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ return addresses
44
+ }