2025-11-14 06:32:36 -07:00

189 lines
7.6 KiB
JavaScript

'use strict';
var protocolHttp = require('@smithy/protocol-http');
var endpointCache = require('@aws-sdk/endpoint-cache');
const ENV_ENDPOINT_DISCOVERY = ["AWS_ENABLE_ENDPOINT_DISCOVERY", "AWS_ENDPOINT_DISCOVERY_ENABLED"];
const CONFIG_ENDPOINT_DISCOVERY = "endpoint_discovery_enabled";
const isFalsy = (value) => ["false", "0"].indexOf(value) >= 0;
const NODE_ENDPOINT_DISCOVERY_CONFIG_OPTIONS = {
environmentVariableSelector: (env) => {
for (let i = 0; i < ENV_ENDPOINT_DISCOVERY.length; i++) {
const envKey = ENV_ENDPOINT_DISCOVERY[i];
if (envKey in env) {
const value = env[envKey];
if (value === "") {
throw Error(`Environment variable ${envKey} can't be empty of undefined, got "${value}"`);
}
return !isFalsy(value);
}
}
},
configFileSelector: (profile) => {
if (CONFIG_ENDPOINT_DISCOVERY in profile) {
const value = profile[CONFIG_ENDPOINT_DISCOVERY];
if (value === undefined) {
throw Error(`Shared config entry ${CONFIG_ENDPOINT_DISCOVERY} can't be undefined, got "${value}"`);
}
return !isFalsy(value);
}
},
default: undefined,
};
const getCacheKey = async (commandName, config, options) => {
const { accessKeyId } = await config.credentials();
const { identifiers } = options;
return JSON.stringify({
...(accessKeyId && { accessKeyId }),
...(identifiers && {
commandName,
identifiers: Object.entries(identifiers)
.sort()
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}),
}),
});
};
const requestQueue = {};
const updateDiscoveredEndpointInCache = async (config, options) => new Promise((resolve, reject) => {
const { endpointCache } = config;
const { cacheKey, commandName, identifiers } = options;
const endpoints = endpointCache.get(cacheKey);
if (endpoints && endpoints.length === 1 && endpoints[0].Address === "") {
if (options.isDiscoveredEndpointRequired) {
if (!requestQueue[cacheKey])
requestQueue[cacheKey] = [];
requestQueue[cacheKey].push({ resolve, reject });
}
else {
resolve();
}
}
else if (endpoints && endpoints.length > 0) {
resolve();
}
else {
const placeholderEndpoints = [{ Address: "", CachePeriodInMinutes: 1 }];
endpointCache.set(cacheKey, placeholderEndpoints);
const command = new options.endpointDiscoveryCommandCtor({
Operation: commandName.slice(0, -7),
Identifiers: identifiers,
});
const handler = command.resolveMiddleware(options.clientStack, config, options.options);
handler(command)
.then((result) => {
endpointCache.set(cacheKey, result.output.Endpoints);
if (requestQueue[cacheKey]) {
requestQueue[cacheKey].forEach(({ resolve }) => {
resolve();
});
delete requestQueue[cacheKey];
}
resolve();
})
.catch((error) => {
endpointCache.delete(cacheKey);
const errorToThrow = Object.assign(new Error(`The operation to discover endpoint failed.` +
` Please retry, or provide a custom endpoint and disable endpoint discovery to proceed.`), { reason: error });
if (requestQueue[cacheKey]) {
requestQueue[cacheKey].forEach(({ reject }) => {
reject(errorToThrow);
});
delete requestQueue[cacheKey];
}
if (options.isDiscoveredEndpointRequired) {
reject(errorToThrow);
}
else {
endpointCache.set(cacheKey, placeholderEndpoints);
resolve();
}
});
}
});
const endpointDiscoveryMiddleware = (config, middlewareConfig) => (next, context) => async (args) => {
if (config.isCustomEndpoint) {
if (config.isClientEndpointDiscoveryEnabled) {
throw new Error(`Custom endpoint is supplied; endpointDiscoveryEnabled must not be true.`);
}
return next(args);
}
const { endpointDiscoveryCommandCtor } = config;
const { isDiscoveredEndpointRequired, identifiers } = middlewareConfig;
const clientName = context.clientName;
const commandName = context.commandName;
const isEndpointDiscoveryEnabled = await config.endpointDiscoveryEnabled();
const cacheKey = await getCacheKey(commandName, config, { identifiers });
if (isDiscoveredEndpointRequired) {
if (isEndpointDiscoveryEnabled === false) {
throw new Error(`Endpoint Discovery is disabled but ${commandName} on ${clientName} requires it.` +
` Please check your configurations.`);
}
await updateDiscoveredEndpointInCache(config, {
...middlewareConfig,
commandName,
cacheKey,
endpointDiscoveryCommandCtor,
});
}
else if (isEndpointDiscoveryEnabled) {
updateDiscoveredEndpointInCache(config, {
...middlewareConfig,
commandName,
cacheKey,
endpointDiscoveryCommandCtor,
});
}
const { request } = args;
if (cacheKey && protocolHttp.HttpRequest.isInstance(request)) {
const endpoint = config.endpointCache.getEndpoint(cacheKey);
if (endpoint) {
request.hostname = endpoint;
}
}
return next(args);
};
const endpointDiscoveryMiddlewareOptions = {
name: "endpointDiscoveryMiddleware",
step: "build",
tags: ["ENDPOINT_DISCOVERY"],
override: true,
};
const getEndpointDiscoveryPlugin = (pluginConfig, middlewareConfig) => ({
applyToStack: (commandStack) => {
commandStack.add(endpointDiscoveryMiddleware(pluginConfig, middlewareConfig), endpointDiscoveryMiddlewareOptions);
},
});
const getEndpointDiscoveryRequiredPlugin = (pluginConfig, middlewareConfig) => ({
applyToStack: (commandStack) => {
commandStack.add(endpointDiscoveryMiddleware(pluginConfig, { ...middlewareConfig, isDiscoveredEndpointRequired: true }), endpointDiscoveryMiddlewareOptions);
},
});
const getEndpointDiscoveryOptionalPlugin = (pluginConfig, middlewareConfig) => ({
applyToStack: (commandStack) => {
commandStack.add(endpointDiscoveryMiddleware(pluginConfig, { ...middlewareConfig, isDiscoveredEndpointRequired: false }), endpointDiscoveryMiddlewareOptions);
},
});
const resolveEndpointDiscoveryConfig = (input, { endpointDiscoveryCommandCtor }) => {
const { endpointCacheSize, endpointDiscoveryEnabled, endpointDiscoveryEnabledProvider } = input;
return Object.assign(input, {
endpointDiscoveryCommandCtor,
endpointCache: new endpointCache.EndpointCache(endpointCacheSize ?? 1000),
endpointDiscoveryEnabled: endpointDiscoveryEnabled !== undefined
? () => Promise.resolve(endpointDiscoveryEnabled)
: endpointDiscoveryEnabledProvider,
isClientEndpointDiscoveryEnabled: endpointDiscoveryEnabled !== undefined,
});
};
exports.NODE_ENDPOINT_DISCOVERY_CONFIG_OPTIONS = NODE_ENDPOINT_DISCOVERY_CONFIG_OPTIONS;
exports.endpointDiscoveryMiddlewareOptions = endpointDiscoveryMiddlewareOptions;
exports.getEndpointDiscoveryOptionalPlugin = getEndpointDiscoveryOptionalPlugin;
exports.getEndpointDiscoveryPlugin = getEndpointDiscoveryPlugin;
exports.getEndpointDiscoveryRequiredPlugin = getEndpointDiscoveryRequiredPlugin;
exports.resolveEndpointDiscoveryConfig = resolveEndpointDiscoveryConfig;