189 lines
7.6 KiB
JavaScript
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;
|