You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
3.9 KiB
TypeScript
149 lines
3.9 KiB
TypeScript
import { default as insecureNodeFetch } from 'node-fetch';
|
|
import pRetry from 'p-retry';
|
|
import { HTTPError, NotFoundError } from '../../utils/errors';
|
|
import { Snode } from '../../../data/data';
|
|
|
|
import { ERROR_421_HANDLED_RETRY_REQUEST, Onions, snodeHttpsAgent, SnodeResponse } from './onions';
|
|
import { APPLICATION_JSON } from '../../../types/MIME';
|
|
import https from 'https';
|
|
import { clone } from 'lodash';
|
|
|
|
export interface LokiFetchOptions {
|
|
method: 'GET' | 'POST';
|
|
body: string | null;
|
|
agent: https.Agent | null;
|
|
headers: Record<string, string>;
|
|
}
|
|
|
|
/**
|
|
* A small wrapper around node-fetch which deserializes response
|
|
* returns insecureNodeFetch response or false
|
|
*/
|
|
async function lokiFetch({
|
|
options,
|
|
url,
|
|
associatedWith,
|
|
targetNode,
|
|
timeout,
|
|
}: {
|
|
url: string;
|
|
options: LokiFetchOptions;
|
|
targetNode?: Snode;
|
|
associatedWith?: string;
|
|
timeout: number;
|
|
}): Promise<undefined | SnodeResponse> {
|
|
const method = options.method || 'GET';
|
|
|
|
const fetchOptions = {
|
|
...options,
|
|
timeout,
|
|
method,
|
|
};
|
|
|
|
try {
|
|
// Absence of targetNode indicates that we want a direct connection
|
|
// (e.g. to connect to a seed node for the first time)
|
|
const useOnionRequests =
|
|
window.sessionFeatureFlags?.useOnionRequests === undefined
|
|
? true
|
|
: window.sessionFeatureFlags?.useOnionRequests;
|
|
if (useOnionRequests && targetNode) {
|
|
const fetchResult = await Onions.lokiOnionFetch({
|
|
targetNode,
|
|
body: fetchOptions.body,
|
|
headers: fetchOptions.headers,
|
|
associatedWith,
|
|
});
|
|
if (!fetchResult) {
|
|
return undefined;
|
|
}
|
|
return fetchResult;
|
|
}
|
|
|
|
if (url.match(/https:\/\//)) {
|
|
// import that this does not get set in lokiFetch fetchOptions
|
|
fetchOptions.agent = snodeHttpsAgent;
|
|
}
|
|
|
|
fetchOptions.headers = {
|
|
'User-Agent': 'WhatsApp',
|
|
'Accept-Language': 'en-us',
|
|
'Content-Type': APPLICATION_JSON,
|
|
};
|
|
|
|
window?.log?.warn(`insecureNodeFetch => lokiFetch of ${url}`);
|
|
|
|
const response = await insecureNodeFetch(url, {
|
|
...fetchOptions,
|
|
body: fetchOptions.body || undefined,
|
|
agent: fetchOptions.agent || undefined,
|
|
});
|
|
if (!response.ok) {
|
|
throw new HTTPError('Loki_rpc error', response);
|
|
}
|
|
const result = await response.text();
|
|
|
|
return {
|
|
body: result,
|
|
status: response.status,
|
|
bodyBinary: null,
|
|
};
|
|
} catch (e) {
|
|
if (e.code === 'ENOTFOUND') {
|
|
throw new NotFoundError('Failed to resolve address', e);
|
|
}
|
|
if (e.message === ERROR_421_HANDLED_RETRY_REQUEST) {
|
|
throw new pRetry.AbortError(ERROR_421_HANDLED_RETRY_REQUEST);
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function will throw for a few reasons.
|
|
* The loki-important ones are
|
|
* -> if we try to make a request to a path which fails too many times => user will need to retry himself
|
|
* -> if the targetNode gets too many errors => we will need to try to do this request again with another target node
|
|
* The
|
|
*/
|
|
export async function snodeRpc(
|
|
{
|
|
method,
|
|
params,
|
|
targetNode,
|
|
associatedWith,
|
|
timeout = 10000,
|
|
}: {
|
|
method: string;
|
|
params: Record<string, any> | Array<Record<string, any>>;
|
|
targetNode: Snode;
|
|
associatedWith?: string;
|
|
timeout?: number;
|
|
} //the user pubkey this call is for. if the onion request fails, this is used to handle the error for this user swarm for instance
|
|
): Promise<undefined | SnodeResponse> {
|
|
const url = `https://${targetNode.ip}:${targetNode.port}/storage_rpc/v1`;
|
|
|
|
const body = {
|
|
jsonrpc: '2.0',
|
|
method,
|
|
params: clone(params),
|
|
};
|
|
|
|
console.warn('snodeRPC', body);
|
|
|
|
const fetchOptions: LokiFetchOptions = {
|
|
method: 'POST',
|
|
body: JSON.stringify(body),
|
|
headers: { 'Content-Type': APPLICATION_JSON },
|
|
agent: null,
|
|
};
|
|
|
|
return lokiFetch({
|
|
url,
|
|
options: fetchOptions,
|
|
targetNode,
|
|
associatedWith,
|
|
timeout,
|
|
});
|
|
}
|