2021-10-17 10:36:14 +02:00
// @flow
2022-05-05 18:29:14 +02:00
// import analytics from 'analytics';
Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.
However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.
## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).
By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.
Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).
For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-04-30 07:27:43 +02:00
import { FETCH _TIMEOUT } from 'constants/errors' ;
2021-11-24 15:33:34 +01:00
import { NO _AUTH , X _LBRY _AUTH _TOKEN } from 'constants/token' ;
Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.
However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.
## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).
By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.
Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).
For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-04-30 07:27:43 +02:00
import fetchWithTimeout from 'util/fetch' ;
2021-11-24 15:33:34 +01:00
2021-10-17 10:36:14 +02:00
require ( 'proxy-polyfill' ) ;
const CHECK _DAEMON _STARTED _TRY _NUMBER = 200 ;
2022-04-29 13:27:04 +02:00
2021-10-17 10:36:14 +02:00
// Basic LBRY sdk connection config
// Offers a proxy to call LBRY sdk methods
const Lbry = {
isConnected : false ,
connectPromise : null ,
daemonConnectionString : 'http://localhost:5279' ,
alternateConnectionString : '' ,
methodsUsingAlternateConnectionString : [ ] ,
apiRequestHeaders : { 'Content-Type' : 'application/json-rpc' } ,
// Allow overriding daemon connection string (e.g. to `/api/proxy` for lbryweb)
setDaemonConnectionString : ( value : string ) => {
Lbry . daemonConnectionString = value ;
} ,
setApiHeader : ( key : string , value : string ) => {
Lbry . apiRequestHeaders = Object . assign ( Lbry . apiRequestHeaders , { [ key ] : value } ) ;
} ,
unsetApiHeader : ( key ) => {
Object . keys ( Lbry . apiRequestHeaders ) . includes ( key ) && delete Lbry . apiRequestHeaders [ key ] ;
} ,
// Allow overriding Lbry methods
overrides : { } ,
setOverride : ( methodName , newMethod ) => {
Lbry . overrides [ methodName ] = newMethod ;
} ,
getApiRequestHeaders : ( ) => Lbry . apiRequestHeaders ,
// Returns a human readable media type based on the content type or extension of a file that is returned by the sdk
getMediaType : ( contentType : ? string , fileName : ? string ) => {
if ( fileName ) {
const formats = [
[ /\.(mp4|m4v|webm|flv|f4v|ogv)$/i , 'video' ] ,
[ /\.(mp3|m4a|aac|wav|flac|ogg|opus)$/i , 'audio' ] ,
[ /\.(jpeg|jpg|png|gif|svg|webp)$/i , 'image' ] ,
[ /\.(h|go|ja|java|js|jsx|c|cpp|cs|css|rb|scss|sh|php|py)$/i , 'script' ] ,
[ /\.(html|json|csv|txt|log|md|markdown|docx|pdf|xml|yml|yaml)$/i , 'document' ] ,
[ /\.(pdf|odf|doc|docx|epub|org|rtf)$/i , 'e-book' ] ,
[ /\.(stl|obj|fbx|gcode)$/i , '3D-file' ] ,
[ /\.(cbr|cbt|cbz)$/i , 'comic-book' ] ,
[ /\.(lbry)$/i , 'application' ] ,
] ;
const res = formats . reduce ( ( ret , testpair ) => {
switch ( testpair [ 0 ] . test ( ret ) ) {
case true :
return testpair [ 1 ] ;
default :
return ret ;
}
} , fileName ) ;
return res === fileName ? 'unknown' : res ;
} else if ( contentType ) {
// $FlowFixMe
return /^[^/]+/ . exec ( contentType ) [ 0 ] ;
}
return 'unknown' ;
} ,
//
// Lbry SDK Methods
// https://lbry.tech/api/sdk
//
status : ( params = { } ) => daemonCallWithResult ( 'status' , params ) ,
stop : ( ) => daemonCallWithResult ( 'stop' , { } ) ,
version : ( ) => daemonCallWithResult ( 'version' , { } ) ,
// Claim fetching and manipulation
2021-11-24 15:33:34 +01:00
resolve : ( params ) => daemonCallWithResult ( 'resolve' , params , searchRequiresAuth ) ,
2021-10-17 10:36:14 +02:00
get : ( params ) => daemonCallWithResult ( 'get' , params ) ,
2021-11-24 15:33:34 +01:00
claim _search : ( params ) => daemonCallWithResult ( 'claim_search' , params , searchRequiresAuth ) ,
2021-10-17 10:36:14 +02:00
claim _list : ( params ) => daemonCallWithResult ( 'claim_list' , params ) ,
channel _create : ( params ) => daemonCallWithResult ( 'channel_create' , params ) ,
channel _update : ( params ) => daemonCallWithResult ( 'channel_update' , params ) ,
channel _import : ( params ) => daemonCallWithResult ( 'channel_import' , params ) ,
channel _list : ( params ) => daemonCallWithResult ( 'channel_list' , params ) ,
stream _abandon : ( params ) => daemonCallWithResult ( 'stream_abandon' , params ) ,
stream _list : ( params ) => daemonCallWithResult ( 'stream_list' , params ) ,
channel _abandon : ( params ) => daemonCallWithResult ( 'channel_abandon' , params ) ,
channel _sign : ( params ) => daemonCallWithResult ( 'channel_sign' , params ) ,
support _create : ( params ) => daemonCallWithResult ( 'support_create' , params ) ,
support _list : ( params ) => daemonCallWithResult ( 'support_list' , params ) ,
stream _repost : ( params ) => daemonCallWithResult ( 'stream_repost' , params ) ,
collection _resolve : ( params ) => daemonCallWithResult ( 'collection_resolve' , params ) ,
collection _list : ( params ) => daemonCallWithResult ( 'collection_list' , params ) ,
collection _create : ( params ) => daemonCallWithResult ( 'collection_create' , params ) ,
collection _update : ( params ) => daemonCallWithResult ( 'collection_update' , params ) ,
// File fetching and manipulation
file _list : ( params = { } ) => daemonCallWithResult ( 'file_list' , params ) ,
file _delete : ( params = { } ) => daemonCallWithResult ( 'file_delete' , params ) ,
file _set _status : ( params = { } ) => daemonCallWithResult ( 'file_set_status' , params ) ,
blob _delete : ( params = { } ) => daemonCallWithResult ( 'blob_delete' , params ) ,
blob _list : ( params = { } ) => daemonCallWithResult ( 'blob_list' , params ) ,
file _reflect : ( params = { } ) => daemonCallWithResult ( 'file_reflect' , params ) ,
// Wallet utilities
wallet _balance : ( params = { } ) => daemonCallWithResult ( 'wallet_balance' , params ) ,
wallet _decrypt : ( ) => daemonCallWithResult ( 'wallet_decrypt' , { } ) ,
wallet _encrypt : ( params = { } ) => daemonCallWithResult ( 'wallet_encrypt' , params ) ,
wallet _unlock : ( params = { } ) => daemonCallWithResult ( 'wallet_unlock' , params ) ,
wallet _list : ( params = { } ) => daemonCallWithResult ( 'wallet_list' , params ) ,
wallet _send : ( params = { } ) => daemonCallWithResult ( 'wallet_send' , params ) ,
wallet _status : ( params = { } ) => daemonCallWithResult ( 'wallet_status' , params ) ,
address _is _mine : ( params = { } ) => daemonCallWithResult ( 'address_is_mine' , params ) ,
address _unused : ( params = { } ) => daemonCallWithResult ( 'address_unused' , params ) ,
address _list : ( params = { } ) => daemonCallWithResult ( 'address_list' , params ) ,
transaction _list : ( params = { } ) => daemonCallWithResult ( 'transaction_list' , params ) ,
utxo _release : ( params = { } ) => daemonCallWithResult ( 'utxo_release' , params ) ,
support _abandon : ( params = { } ) => daemonCallWithResult ( 'support_abandon' , params ) ,
purchase _list : ( params = { } ) => daemonCallWithResult ( 'purchase_list' , params ) ,
txo _list : ( params = { } ) => daemonCallWithResult ( 'txo_list' , params ) ,
account _list : ( params = { } ) => daemonCallWithResult ( 'account_list' , params ) ,
account _set : ( params = { } ) => daemonCallWithResult ( 'account_set' , params ) ,
sync _hash : ( params = { } ) => daemonCallWithResult ( 'sync_hash' , params ) ,
sync _apply : ( params = { } ) => daemonCallWithResult ( 'sync_apply' , params ) ,
// Preferences
preference _get : ( params = { } ) => daemonCallWithResult ( 'preference_get' , params ) ,
preference _set : ( params = { } ) => daemonCallWithResult ( 'preference_set' , params ) ,
// Comments
comment _list : ( params = { } ) => daemonCallWithResult ( 'comment_list' , params ) ,
comment _create : ( params = { } ) => daemonCallWithResult ( 'comment_create' , params ) ,
comment _hide : ( params = { } ) => daemonCallWithResult ( 'comment_hide' , params ) ,
comment _abandon : ( params = { } ) => daemonCallWithResult ( 'comment_abandon' , params ) ,
comment _update : ( params = { } ) => daemonCallWithResult ( 'comment_update' , params ) ,
// Connect to the sdk
connect : ( ) => {
if ( Lbry . connectPromise === null ) {
// $FlowFixMe
Lbry . connectPromise = new Promise ( ( resolve , reject ) => {
let tryNum = 0 ;
// Check every half second to see if the daemon is accepting connections
function checkDaemonStarted ( ) {
tryNum += 1 ;
Lbry . status ( )
. then ( resolve )
. catch ( ( ) => {
if ( tryNum <= CHECK _DAEMON _STARTED _TRY _NUMBER ) {
setTimeout ( checkDaemonStarted , tryNum < 50 ? 400 : 1000 ) ;
} else {
reject ( new Error ( 'Unable to connect to LBRY' ) ) ;
}
} ) ;
}
checkDaemonStarted ( ) ;
} ) ;
}
// Flow thinks this could be empty, but it will always reuturn a promise
// $FlowFixMe
return Lbry . connectPromise ;
} ,
publish : ( params = { } ) =>
new Promise ( ( resolve , reject ) => {
if ( Lbry . overrides . publish ) {
Lbry . overrides . publish ( params ) . then ( resolve , reject ) ;
} else {
apiCall ( 'publish' , params , resolve , reject ) ;
}
} ) ,
} ;
2022-04-29 13:27:04 +02:00
const ApiFailureMgr = {
MAX _FAILED _ATTEMPTS : 5 ,
MAX _FAILED _GAP _MS : 500 ,
BLOCKED _DURATION _MS : 60000 ,
METHODS _TO _LOG : [ 'claim_search' ] , // Can check all, but narrow do claim_search only for now.
failureTimestamps : { } , // { [key: string]: Array<timestamps: number> }
logFailure : function ( method : string , params : ? { } , timestamp : number ) {
if ( this . isListedMethod ( method ) ) {
const key = this . getKey ( method , params ) ;
const ts = this . failureTimestamps [ key ] || [ ] ;
ts . push ( timestamp ) ;
this . failureTimestamps [ key ] = ts ;
}
} ,
logSuccess : function ( method : string , params : ? { } ) {
if ( this . isListedMethod ( method ) ) {
const key = this . getKey ( method , params ) ;
delete this . failureTimestamps [ key ] ;
}
} ,
isFailingAndShouldDrop : function ( method : string , params : ? { } ) {
if ( this . isListedMethod ( method ) ) {
const key = this . getKey ( method , params ) ;
const fts = this . failureTimestamps [ key ] ;
if ( fts && fts . length > this . MAX _FAILED _ATTEMPTS ) {
const ts2 = fts [ fts . length - 1 ] ;
const ts1 = fts [ fts . length - this . MAX _FAILED _ATTEMPTS ] ;
const successivelyFailed = ts2 - ts1 < this . MAX _FAILED _GAP _MS ;
return successivelyFailed && Date . now ( ) - ts2 < this . BLOCKED _DURATION _MS ;
}
}
return false ;
} ,
getKey : function ( method : string , params : ? { } ) {
return method + '/' + JSON . stringify ( params || { } ) ;
} ,
isListedMethod : function ( method : string ) {
return this . METHODS _TO _LOG . includes ( method ) ;
} ,
} ;
Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.
However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.
## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).
By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.
Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).
For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-04-30 07:27:43 +02:00
/ * *
* Returns a customized error message for known scenarios .
* /
function resolveFetchErrorMsg ( method : string , response : Response | string ) {
if ( typeof response === 'object' ) {
2022-03-18 09:42:52 +01:00
// prettier-ignore
switch ( response . status ) {
case 504 : // Gateway timeout
case 524 : // Cloudflare: a timeout occurred
switch ( method ) {
case 'publish' :
Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.
However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.
## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).
By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.
Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).
For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-04-30 07:27:43 +02:00
return _ _ ( '[Publish]: Your action timed out, but may have been completed. Refresh and check your Uploads or Wallet page to confirm after a few minutes.' ) ;
2022-03-18 09:42:52 +01:00
default :
Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.
However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.
## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).
By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.
Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).
For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-04-30 07:27:43 +02:00
return ` ${ method } : ${ response . statusText } ( ${ response . status } ) ` ;
2022-03-18 09:42:52 +01:00
}
default :
Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.
However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.
## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).
By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.
Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).
For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-04-30 07:27:43 +02:00
return ` ${ method } : ${ response . statusText } ( ${ response . status } ) ` ;
2022-03-18 09:42:52 +01:00
}
Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.
However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.
## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).
By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.
Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).
For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-04-30 07:27:43 +02:00
} else if ( response === FETCH _TIMEOUT ) {
return ` ${ method } : Your action timed out, but may have been completed. ` ;
} else {
return ` ${ method } : fetch failed. ` ;
}
}
function checkAndParse ( response : Response , method : string ) {
if ( ! response . ok ) {
const errMsg = resolveFetchErrorMsg ( method , response ) ;
throw Error ( errMsg ) ;
2022-03-18 09:42:52 +01:00
}
2021-10-17 10:36:14 +02:00
if ( response . status >= 200 && response . status < 300 ) {
return response . json ( ) ;
}
2022-03-18 09:42:10 +01:00
2022-03-18 09:42:52 +01:00
return response
. json ( )
. then ( ( json ) => {
if ( json . error ) {
const errorMessage = typeof json . error === 'object' ? json . error . message : json . error ;
return Promise . reject ( new Error ( errorMessage ) ) ;
} else {
return Promise . reject ( new Error ( 'Protocol error with unknown response signature' ) ) ;
}
} )
. catch ( ( ) => {
// If not parsable, throw the initial response rather than letting
// the json failure ("unexpected token at..") pass through.
return Promise . reject ( new Error ( ` ${ method } : ${ response . statusText } ( ${ response . status } , JSON) ` ) ) ;
} ) ;
2021-10-17 10:36:14 +02:00
}
export function apiCall ( method : string , params : ? { } , resolve : Function , reject : Function ) {
2021-11-24 15:33:34 +01:00
let apiRequestHeaders = Lbry . apiRequestHeaders ;
if ( params && params [ NO _AUTH ] ) {
apiRequestHeaders = Object . assign ( { } , Lbry . apiRequestHeaders ) ;
delete apiRequestHeaders [ X _LBRY _AUTH _TOKEN ] ;
delete params [ NO _AUTH ] ;
}
2021-10-17 10:36:14 +02:00
const counter = new Date ( ) . getTime ( ) ;
const options = {
method : 'POST' ,
2021-11-24 15:33:34 +01:00
headers : apiRequestHeaders ,
2021-10-17 10:36:14 +02:00
body : JSON . stringify ( {
jsonrpc : '2.0' ,
method ,
params ,
id : counter ,
} ) ,
} ;
2022-04-29 13:27:04 +02:00
if ( ApiFailureMgr . isFailingAndShouldDrop ( method , params ) ) {
return Promise . reject ( 'Dropped due to successive failures.' ) ;
}
2021-10-17 10:36:14 +02:00
const connectionString = Lbry . methodsUsingAlternateConnectionString . includes ( method )
? Lbry . alternateConnectionString
: Lbry . daemonConnectionString ;
2022-03-18 09:42:10 +01:00
2022-05-05 18:29:14 +02:00
const SDK _FETCH _TIMEOUT _MS = 1800000 ;
Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.
However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.
## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).
By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.
Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).
For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-04-30 07:27:43 +02:00
return fetchWithTimeout ( SDK _FETCH _TIMEOUT _MS , fetch ( connectionString + '?m=' + method , options ) )
2022-03-18 09:42:52 +01:00
. then ( ( response ) => checkAndParse ( response , method ) )
2021-10-17 10:36:14 +02:00
. then ( ( response ) => {
const error = response . error || ( response . result && response . result . error ) ;
2022-04-29 13:27:04 +02:00
if ( error ) {
ApiFailureMgr . logFailure ( method , params , counter ) ;
return reject ( error ) ;
} else {
ApiFailureMgr . logSuccess ( method ) ;
return resolve ( response . result ) ;
}
2021-10-17 10:36:14 +02:00
} )
2022-04-29 13:27:04 +02:00
. catch ( ( err ) => {
ApiFailureMgr . logFailure ( method , params , counter ) ;
2022-05-05 07:07:04 +02:00
if ( err ? . message === FETCH _TIMEOUT ) {
2022-05-05 18:29:14 +02:00
// analytics.error(`${method}: timed out after ${SDK_FETCH_TIMEOUT_MS / 1000}s`);
Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.
However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.
## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).
By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.
Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).
For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-04-30 07:27:43 +02:00
reject ( resolveFetchErrorMsg ( method , FETCH _TIMEOUT ) ) ;
} else {
reject ( err ) ;
}
2022-04-29 13:27:04 +02:00
} ) ;
2021-10-17 10:36:14 +02:00
}
2021-11-24 15:33:34 +01:00
function daemonCallWithResult (
name : string ,
params : ? { } = { } ,
checkAuthNeededFn : ? ( ? { } ) => boolean = undefined
) : Promise < any > {
2021-10-17 10:36:14 +02:00
return new Promise ( ( resolve , reject ) => {
2021-11-24 15:33:34 +01:00
const skipAuth = checkAuthNeededFn ? ! checkAuthNeededFn ( params ) : false ;
2021-10-17 10:36:14 +02:00
apiCall (
name ,
2021-11-24 15:33:34 +01:00
skipAuth ? { ... params , [ NO _AUTH ] : true } : params ,
2021-10-17 10:36:14 +02:00
( result ) => {
resolve ( result ) ;
} ,
reject
) ;
} ) ;
}
// This is only for a fallback
// If there is a Lbry method that is being called by an app, it should be added to /flow-typed/Lbry.js
const lbryProxy = new Proxy ( Lbry , {
get ( target : LbryTypes , name : string ) {
if ( name in target ) {
return target [ name ] ;
}
return ( params = { } ) =>
new Promise ( ( resolve , reject ) => {
apiCall ( name , params , resolve , reject ) ;
} ) ;
} ,
} ) ;
2021-11-24 15:33:34 +01:00
/ * *
* daemonCallWithResult hook that checks if the search option requires the
* auth - token . This hook works for 'resolve' and 'claim_search' .
*
* @ param options
* @ returns { boolean }
* /
function searchRequiresAuth ( options : any ) {
const KEYS _REQUIRE _AUTH = [ 'include_purchase_receipt' , 'include_is_my_output' ] ;
return options && KEYS _REQUIRE _AUTH . some ( ( k ) => options . hasOwnProperty ( k ) ) ;
}
2021-10-17 10:36:14 +02:00
export default lbryProxy ;