fluence-js/src/internal/FluenceConnection.ts

157 lines
4.9 KiB
TypeScript
Raw Normal View History

2020-05-14 15:20:39 +03:00
/*
2020-05-14 17:30:17 +03:00
* Copyright 2020 Fluence Labs Limited
2020-05-14 15:20:39 +03:00
*
2020-05-14 17:30:17 +03:00
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
2020-05-14 15:20:39 +03:00
*
2020-05-14 17:30:17 +03:00
* http://www.apache.org/licenses/LICENSE-2.0
2020-05-14 15:20:39 +03:00
*
2020-05-14 17:30:17 +03:00
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
2020-05-14 15:20:39 +03:00
*/
// @ts-ignore
2020-12-23 17:24:22 +03:00
import Websockets from 'libp2p-websockets';
// @ts-ignore
2020-12-23 17:24:22 +03:00
import Mplex from 'libp2p-mplex';
import Lib2p2Peer from 'libp2p';
2020-12-23 17:24:22 +03:00
import { decode, encode } from 'it-length-prefixed';
import { pipe } from 'it-pipe';
2020-09-15 12:09:13 +03:00
import * as log from 'loglevel';
import { Noise } from '@chainsafe/libp2p-noise';
import { Particle } from './Particle';
import PeerId from 'peer-id';
import { Multiaddr } from 'multiaddr';
// @ts-ignore
2021-04-13 15:11:52 +03:00
import { all as allow_all } from 'libp2p-websockets/src/filters';
import { Connection } from 'libp2p-interfaces/src/topology';
import Buffer from './Buffer';
2020-05-14 15:20:39 +03:00
2021-08-24 17:37:03 +03:00
export const PROTOCOL_NAME = '/fluence/particle/2.0.0';
2020-05-14 15:20:39 +03:00
/**
* Options to configure fluence connection
*/
export interface FluenceConnectionOptions {
/**
* Peer id of the Fluence Peer
*/
peerId: PeerId;
/**
* Multiaddress of the relay to make connection to
*/
relayAddress: Multiaddr;
/**
* The dialing timeout in milliseconds
*/
dialTimeoutMs?: number;
/**
* Handler for incoming particles from the connection
*/
onIncomingParticle: (p: Particle) => void;
}
2020-05-14 15:20:39 +03:00
export class FluenceConnection {
constructor(private _lib2p2Peer: Lib2p2Peer, private _relayAddress: Multiaddr) {}
2021-04-13 15:11:52 +03:00
private _connection?: Connection;
2021-04-13 15:11:52 +03:00
static async createConnection(options: FluenceConnectionOptions): Promise<FluenceConnection> {
const transportKey = Websockets.prototype[Symbol.toStringTag];
const lib2p2Peer = await Lib2p2Peer.create({
peerId: options.peerId,
2020-05-14 15:20:39 +03:00
modules: {
transport: [Websockets],
streamMuxer: [Mplex],
connEncryption: [new Noise()],
2020-05-14 15:20:39 +03:00
},
2021-04-13 15:11:52 +03:00
config: {
transport: {
[transportKey]: {
filter: allow_all,
},
},
2021-04-13 15:11:52 +03:00
},
dialer: {
dialTimeout: options?.dialTimeoutMs,
},
2020-05-14 15:20:39 +03:00
});
lib2p2Peer.handle([PROTOCOL_NAME], async ({ connection, stream }) => {
pipe(
stream.source,
// @ts-ignore
decode(),
async (source: AsyncIterable<string>) => {
try {
for await (const msg of source) {
try {
const particle = Particle.fromString(msg);
options.onIncomingParticle(particle);
} catch (e) {
log.error('error on handling a new incoming message: ' + e);
}
}
} catch (e) {
log.debug('connection closed: ' + e);
2020-05-14 15:20:39 +03:00
}
},
);
});
2020-05-14 15:20:39 +03:00
const relayAddress = options.relayAddress;
2020-05-14 15:20:39 +03:00
return new FluenceConnection(lib2p2Peer, relayAddress);
2020-05-14 15:20:39 +03:00
}
async disconnect() {
await this._lib2p2Peer.stop();
2020-05-14 15:20:39 +03:00
}
async sendParticle(particle: Particle): Promise<void> {
particle.logTo('debug', 'sending particle:');
/*
TODO:: find out why this doesn't work and a new connection has to be established each time
if (this._connection.streams.length !== 1) {
throw new Error('Incorrect number of streams in FluenceConnection');
}
2020-09-28 17:01:49 +03:00
const sink = this._connection.streams[0].sink;
*/
2020-05-14 15:20:39 +03:00
const conn = await this._lib2p2Peer.dialProtocol(this._relayAddress, PROTOCOL_NAME);
const sink = conn.stream.sink;
2020-05-14 15:20:39 +03:00
pipe(
// force new line
[Buffer.from(particle.toString(), 'utf8')],
2020-05-14 15:20:39 +03:00
encode(),
sink,
2020-05-14 15:20:39 +03:00
);
}
async connect() {
await this._lib2p2Peer.start();
log.debug(`dialing to the node with client's address: ` + this._lib2p2Peer.peerId.toB58String());
try {
this._connection = await this._lib2p2Peer.dial(this._relayAddress);
} catch (e: any) {
if (e.name === 'AggregateError' && e._errors?.length === 1) {
const error = e._errors[0];
throw new Error(`Error dialing node ${this._relayAddress}:\n${error.code}\n${error.message}`);
} else {
throw e;
}
}
}
2020-06-19 14:29:06 +03:00
}