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                             5053x 5053x 5053x 5053x 5053x 5053x       5041x 1x       5040x   5040x 5036x 5036x   5040x     5040x   5053x       6279x       1005x       245x       6039x       1248x       5037x       5037x       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);
	}
}