Home Reference Source Test

src/main/generic/network/NetworkConfig.js

class NetworkConfig {
    /**
     * @returns {NetworkConfig}
     */
    static getDefault() {
        return PlatformUtils.supportsWebRTC()
            ? new RtcNetworkConfig()
            : new DumbNetworkConfig();
    }

    /**
     * @constructor
     * @param {number} protocolMask
     */
    constructor(protocolMask) {
        /** @type {number} */
        this._protocolMask = protocolMask;

        /**
         * @type {KeyPair}
         * @protected
         */
        this._keyPair = null;

        /**
         * @type {PeerId}
         * @protected
         */
        this._peerId = null;

        /**
         * @type {Services}
         * @protected
         */
        this._services = null;

        /** @type {string} */
        this._appAgent = null;
    }

    /**
     * @returns {void}
     */
    async initPersistent() {
        const db = await PeerKeyStore.getPersistent();
        await this._init(db);
    }

    /**
     * @returns {void}
     */
    async initVolatile() {
        const db = PeerKeyStore.createVolatile();
        await this._init(db);
    }

    /**
     * @private
     * @param {PeerKeyStore} db
     * @returns {void}
     */
    async _init(db) {
        if (this._keyPair) {
            return;
        }

        /** @type {KeyPair} */
        let keys = await db.get('keys');
        if (!keys) {
            keys = KeyPair.generate();
            await db.put('keys', keys);
        }

        this._keyPair = keys;
        this._peerId = keys.publicKey.toPeerId();
    }

    /**
     * @type {number}
     */
    get protocol() {
        throw new Error('Unimplemented');
    }

    /**
     * Used for filtering peer addresses by protocols.
     * @type {number}
     */
    get protocolMask() {
        return this._protocolMask;
    }

    /**
     * @type {KeyPair}
     */
    get keyPair() {
        return this._keyPair;
    }

    /**
     * @type {PublicKey}
     */
    get publicKey() {
        return this._keyPair.publicKey;
    }

    /**
     * @type {PeerId}
     */
    get peerId() {
        return this._peerId;
    }

    /**
     * @type {Services}
     */
    get services() {
        return this._services;
    }

    /**
     * @param {Services} services
     */
    set services(services) {
        this._services = services;
    }

    /**
     * @type {PeerAddress}
     */
    get peerAddress() {
        throw new Error('Not implemented');
    }

    /**
     * @param {number} protocol
     * @returns {boolean}
     */
    canConnect(protocol) {
        return (protocol & this._protocolMask) !== 0;
    }

    /** @type {string} */
    get appAgent() {
        return this._appAgent;
    }

    /** @type {string} */
    set appAgent(value) {
        this._appAgent = value;
    }
}
Class.register(NetworkConfig);

class WsNetworkConfig extends NetworkConfig {
    /**
     * @constructor
     * @param {string} host
     * @param {number} port
     * @param {{enabled: boolean, port: number, addresses: Array.<string>, header: string}} reverseProxy
     */
    constructor(host, port, reverseProxy) {
        super(Protocol.WS | Protocol.WSS);
        this._host = host;
        this._port = port;
        this._reverseProxy = reverseProxy;
    }

    /**
     * @type {number}
     * @override
     */
    get protocol() {
        return Protocol.WS;
    }

    /**
     * @type {number}
     */
    get port() {
        return this._port;
    }

    /**
     * @type {{enabled: boolean, port: number, addresses: Array.<string>, header: string}}
     */
    get reverseProxy() {
        return this._reverseProxy;
    }

    /**
     * @type {WsPeerAddress|WssPeerAddress}
     * @override
     */
    get peerAddress() {
        if (!this._services || !this._keyPair) {
            throw new Error('PeerAddress is not configured.');
        }

        const port = this._reverseProxy.enabled ? this._reverseProxy.port : this._port;
        const peerAddress = new WsPeerAddress(
            this._services.provided, Date.now(), NetAddress.UNSPECIFIED,
            this.publicKey, /*distance*/ 0,
            this._host, port);

        if (!peerAddress.globallyReachable()) {
            throw new Error('PeerAddress not globally reachable.');
        }

        peerAddress.signature = Signature.create(this._keyPair.privateKey, this.publicKey, peerAddress.serializeContent());
        return peerAddress;
    }

    /**
     * @type {boolean}
     */
    get secure() {
        return false;
    }
}
Class.register(WsNetworkConfig);

class WssNetworkConfig extends WsNetworkConfig {
    /**
     * @constructor
     * @param {string} host
     * @param {number} port
     * @param {string} [key]
     * @param {string} [cert]
     * @param {{enabled: boolean, port: number, addresses: Array.<string>, header: string}} reverseProxy
     */
    constructor(host, port, key, cert, reverseProxy) {
        super(host, port, reverseProxy);
        this._key = key;
        this._cert = cert;

        /** @type {{key: string, cert: string}} */
        this._ssl = {
            key: this._key,
            cert: this._cert
        };
    }

    /**
     * @type {number}
     * @override
     */
    get protocol() {
        return Protocol.WSS;
    }

    /**
     * @type {?{key: string, cert: string}}
     */
    get ssl() {
        return this._ssl;
    }

    /**
     * @type {WsPeerAddress|WssPeerAddress}
     * @override
     */
    get peerAddress() {
        if (!this._services || !this._keyPair) {
            throw new Error('PeerAddress is not configured.');
        }

        const port = this._reverseProxy.enabled ? this._reverseProxy.port : this._port;
        const peerAddress = new WssPeerAddress(
            this._services.provided, Date.now(), NetAddress.UNSPECIFIED,
            this.publicKey, /*distance*/ 0,
            this._host, port);

        if (!peerAddress.globallyReachable()) {
            throw new Error('PeerAddress not globally reachable.');
        }

        peerAddress.signature = Signature.create(this._keyPair.privateKey, this.publicKey, peerAddress.serializeContent());
        return peerAddress;
    }

    /**
     * @type {boolean}
     */
    get secure() {
        return true;
    }
}
Class.register(WssNetworkConfig);

class RtcNetworkConfig extends NetworkConfig {
    /**
     * @constructor
     */
    constructor() {
        super((PlatformUtils.supportsWS() ? (Protocol.WS | Protocol.WSS) : Protocol.WSS) | Protocol.RTC);
        this._rtcConfig = {
            iceServers: [
                { urls: 'stun:stun.l.google.com:19302' },
                { urls: 'stun:stun.nimiq-network.com:19302' }
            ]
        };
    }

    /**
     * @type {number}
     * @override
     */
    get protocol() {
        return Protocol.RTC;
    }

    /**
     * @returns {?RTCConfiguration}
     */
    get rtcConfig() {
        return this._rtcConfig;
    }

    /**
     * @type {RtcPeerAddress}
     * @override
     */
    get peerAddress() {
        if (!this._services || !this._keyPair) {
            throw new Error('PeerAddress is not configured.');
        }

        const peerAddress = new RtcPeerAddress(
            this._services.provided, Date.now(), NetAddress.UNSPECIFIED,
            this.publicKey, /*distance*/ 0);
        peerAddress.signature = Signature.create(this._keyPair.privateKey, this.publicKey, peerAddress.serializeContent());
        return peerAddress;
    }
}
Class.register(RtcNetworkConfig);

class DumbNetworkConfig extends NetworkConfig {
    /**
     * @constructor
     */
    constructor() {
        // Browsers served through https only speak WSS. Everything else should also support WS.
        super(PlatformUtils.supportsWS() ? (Protocol.WS | Protocol.WSS) : Protocol.WSS);
    }

    /**
     * @type {number}
     * @override
     */
    get protocol() {
        return Protocol.DUMB;
    }

    /**
     * @type {DumbPeerAddress}
     * @override
     */
    get peerAddress() {
        if (!this._services || !this._keyPair) {
            throw new Error('PeerAddress is not configured.');
        }

        const peerAddress = new DumbPeerAddress(
            this._services.provided, Date.now(), NetAddress.UNSPECIFIED,
            this.publicKey, /*distance*/ 0);
        peerAddress.signature = Signature.create(this._keyPair.privateKey, this.publicKey, peerAddress.serializeContent());
        return peerAddress;
    }
}
Class.register(DumbNetworkConfig);