All files / src p2p_request.ts

100% Statements 25/25
100% Branches 6/6
100% Functions 9/9
100% Lines 25/25

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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                            42x                                       42x                             827x 827x 827x 827x 827x 827x       815x 1x       814x   814x 810x 810x   814x     814x   827x       802x       1x       1x       812x       1x       811x       811x       4x      
/*
 * Copyright © 2019 Lisk Foundation
 *
 * See the LICENSE file at the top-level directory of this distribution
 * for licensing information.
 *
 * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
 * no part of this software, including this file, may be copied, modified,
 * propagated, or distributed except according to the terms contained in the
 * LICENSE file.
 *
 * Removal or modification of this copyright notice is prohibited.
 *
 */
import { RPCResponseAlreadySentError } from './errors';
import { P2PResponsePacket } from './p2p_types';
 
export interface RequestOptions {
	readonly procedure: string;
	readonly data: unknown;
	readonly id: string;
	readonly rate: number;
	productivity: {
		// tslint:disable-next-line: readonly-keyword
		requestCounter: number;
		// tslint:disable-next-line: readonly-keyword
		responseCounter: number;
		// tslint:disable-next-line: readonly-keyword
		responseRate: number;
		// tslint:disable-next-line: readonly-keyword
		lastResponded: number;
	};
}
 
export class P2PRequest {
	private readonly _procedure: string;
	private readonly _data: unknown;
	private readonly _respondCallback: (
		responseError?: Error,
		responseData?: P2PResponsePacket,
	) => void;
	private readonly _peerId: string;
	private _wasResponseSent: boolean;
	private readonly _rate: number;
 
	public constructor(
		options: RequestOptions,
		respondCallback: (responseError?: Error, responseData?: unknown) => void,
	) {
		this._procedure = options.procedure;
		this._data = options.data;
		this._peerId = options.id;
		this._rate = options.rate;
		options.productivity.requestCounter += 1;
		this._respondCallback = (
			responseError?: Error,
			responsePacket?: P2PResponsePacket,
		) => {
			if (this._wasResponseSent) {
				throw new RPCResponseAlreadySentError(
					`A response has already been sent for the request procedure <<${options.procedure}>>`,
				);
			}
			this._wasResponseSent = true;
			// We assume peer performed useful work and update peer response rate
			if (!responseError && responsePacket) {
				options.productivity.lastResponded = Date.now();
				options.productivity.responseCounter += 1;
			}
			options.productivity.responseRate =
				options.productivity.responseCounter /
				options.productivity.requestCounter;
			respondCallback(responseError, responsePacket);
		};
		this._wasResponseSent = false;
	}
 
	public get procedure(): string {
		return this._procedure;
	}
 
	public get data(): unknown {
		return this._data;
	}
 
	public get rate(): number {
		return this._rate;
	}
 
	public get peerId(): string {
		return this._peerId;
	}
 
	public get wasResponseSent(): boolean {
		return this._wasResponseSent;
	}
 
	public end(responseData?: unknown): void {
		const responsePacket: P2PResponsePacket = {
			data: responseData,
			peerId: this.peerId,
		};
		this._respondCallback(undefined, responsePacket);
	}
 
	public error(responseError: Error): void {
		this._respondCallback(responseError);
	}
}