oracle-nosqldb
Version:
Node.js driver for Oracle NoSQL Database
143 lines (122 loc) • 4.56 kB
JavaScript
/*-
* Copyright (c) 2018, 2025 Oracle and/or its affiliates. All rights reserved.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* https://oss.oracle.com/licenses/upl/
*/
const assert = require('assert');
const isPosInt32OrZero = require('../../utils').isPosInt32OrZero;
const Utils = require('./utils');
//Base class for providers that cache profile.
//Derived classes need to implement the following:
//this._profile - currently cached profile
//this._isCurrentProfileValid() - if current profile is valid, only called
//when this._profile exists
//async this._doRefresh() - refreshes current profile and related data
class CachedProfileProvider {
isProfileValid(profile) {
assert(profile != null);
return profile === this._profile && this._isCurrentProfileValid();
}
async getProfile(needRefresh) {
if (needRefresh || this._profile == null ||
!this._isCurrentProfileValid()) {
await this._doRefresh(needRefresh);
}
return this._profile;
}
}
//Base class for providers that allow refreshing profile in the background.
//Derived classes need to implement the following:
//this._profile - currently cached profile
//this._refreshAheadMs - (optional) interval in ms before expiration when to
//do background refresh
//this._isCurrentProfileValid() - if current profile is valid
//this._getCurrentDuration() - get duration in ms of current profile, assuming
//it was just retrieved and is valid
//async this._refreshProfile() - refresh of the profile in
//foreground or background
class RefreshableProfileProvider extends CachedProfileProvider {
async _doRefresh(isBackground) {
//Avoid multiple concurrent requests for profile refresh.
if (this._profilePromise == null) {
this._profilePromise = this._refreshProfile();
}
try {
await this._profilePromise;
} catch(err) {
if (isBackground) {
//If error occurred during background refresh, we don't throw
//and don't reschedule next refresh.
return;
} else {
throw err;
}
} finally {
if (this._refreshTimer != null) {
clearTimeout(this._refreshTimer);
}
this._profilePromise = null;
}
if (!this._refreshAheadMs) {
return;
}
const dur = this._getCurrentDuration();
if (!Number.isFinite(dur)) {
return;
}
const refreshInterval = dur - this._refreshAheadMs;
if (refreshInterval <= 0) {
return;
}
this._refreshTimer = setTimeout(
() => this._refreshProfile(true, true), refreshInterval);
}
close() {
if (this._refreshTimer != null) {
clearTimeout(this._refreshTimer);
this._refreshTimer = null;
}
}
}
//Base class for security token-based providers that allow refresh in the
//background.
//Derived classes need to implement the following:
//async this._getSecurityToken() - retrieve security token
//this._getPrivateKey() - get private key for the profile
//this._profileExtraInit() - (optional) Initialize other properties in the
//current profile (this._profile), other than keyId and privateKey
class RefreshableTokenProvider extends RefreshableProfileProvider {
constructor(opt) {
super();
assert (opt != null);
this._refreshAheadMs = opt.securityTokenRefreshAheadMs;
assert(isPosInt32OrZero(this._refreshAheadMs));
this._expireBeforeMs = opt.securityTokenExpireBeforeMs;
assert(isPosInt32OrZero(this._expireBeforeMs));
}
async _refreshProfile() {
const val = await this._getSecurityToken();
this._token = Utils.parseSecurityToken(val);
this._profile = {
keyId: 'ST$' + this._token.value,
privateKey: this._getPrivateKey()
};
this._profileExtraInit();
}
//only called when this._profile exists
_isCurrentProfileValid() {
assert(this._token != null);
return Utils.isSecurityTokenValid(this._token, this._expireBeforeMs);
}
_getCurrentDuration() {
return Utils.getSecurityTokenExpiration(this._token,
this._expireBeforeMs) - Date.now();
}
_profileExtraInit() {}
}
module.exports = {
CachedProfileProvider,
RefreshableProfileProvider,
RefreshableTokenProvider
};