import { default as insecureNodeFetch } from 'node-fetch' ;
import { Snode } from '../../data/data' ;
import { lokiOnionFetch , snodeHttpsAgent , SnodeResponse } from './onions' ;
interface FetchOptions {
method : string ;
body? : string ;
agent? : any ;
}
// A small wrapper around node-fetch which deserializes response
// returns insecureNodeFetch response or false
async function lokiFetch (
url : string ,
options : FetchOptions ,
targetNode? : Snode ,
associatedWith? : string
) : Promise < undefined | SnodeResponse > {
const timeout = 10000 ;
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)
if ( window . lokiFeatureFlags . useOnionRequests && targetNode ) {
const fetchResult = await lokiOnionFetch ( targetNode , fetchOptions . body , associatedWith ) ;
if ( ! fetchResult ) {
return undefined ;
}
return fetchResult ;
}
if ( url . match ( /https:\/\// ) ) {
// import that this does not get set in lokiFetch fetchOptions
fetchOptions . agent = snodeHttpsAgent ;
}
window ? . log ? . warn ( ` insecureNodeFetch => lokiFetch of ${ url } ` ) ;
const response = await insecureNodeFetch ( url , fetchOptions ) ;
if ( ! response . ok ) {
throw new window . textsecure . HTTPError ( 'Loki_rpc error' , response ) ;
}
const result = await response . text ( ) ;
return {
body : result ,
status : response.status ,
} ;
} catch ( e ) {
if ( e . code === 'ENOTFOUND' ) {
throw new window . textsecure . NotFoundError ( 'Failed to resolve address' , e ) ;
}
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 : string ,
params : any ,
targetNode : Snode ,
associatedWith? : string //the user pubkey this call is for. if the onion request fails, this is used to handle the error for this user swarm for isntance
) : Promise < undefined | SnodeResponse > {
const url = ` https:// ${ targetNode . ip } : ${ targetNode . port } /storage_rpc/v1 ` ;
// TODO: The jsonrpc and body field will be ignored on storage server
if ( params . pubKey ) {
// Ensure we always take a copy
// tslint:disable-next-line no-parameter-reassignment
params = {
. . . params ,
pubKey : window.getStoragePubKey ( params . pubKey ) ,
} ;
}
const body = {
jsonrpc : '2.0' ,
id : '0' ,
method ,
params ,
} ;
const fetchOptions = {
method : 'POST' ,
body : JSON.stringify ( body ) ,
headers : {
'Content-Type' : 'application/json' ,
} ,
} ;
return lokiFetch ( url , fetchOptions , targetNode , associatedWith ) ;
}