aws-crt
Version:
NodeJS/browser bindings to the aws-c-* libraries
698 lines • 26.5 kB
JavaScript
"use strict";
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpClientConnectionManager = exports.HttpClientStream = exports.HttpClientConnection = exports.HttpRequest = exports.HttpProxyOptions = exports.HttpHeaders = exports.HttpProxyAuthenticationType = void 0;
/**
*
* A module containing support for creating http connections and making requests on them.
*
* @packageDocumentation
* @module http
* @mergeTarget
*/
var http_1 = require("../common/http");
var http_2 = require("../common/http");
Object.defineProperty(exports, "HttpProxyAuthenticationType", { enumerable: true, get: function () { return http_2.HttpProxyAuthenticationType; } });
var event_1 = require("../common/event");
var error_1 = require("./error");
var axios = require("axios");
var io_1 = require("./io");
var util_utf8_browser_1 = require("@aws-sdk/util-utf8-browser");
/**
* A collection of HTTP headers
*
* @category HTTP
*/
var HttpHeaders = /** @class */ (function () {
/** Construct from a collection of [name, value] pairs
*
* @param headers list of HttpHeader values to seat in this object
*/
function HttpHeaders(headers) {
var e_1, _a;
if (headers === void 0) { headers = []; }
// Map from "header": [["HeAdEr", "value1"], ["HEADER", "value2"], ["header", "value3"]]
this.headers = {};
try {
for (var headers_1 = __values(headers), headers_1_1 = headers_1.next(); !headers_1_1.done; headers_1_1 = headers_1.next()) {
var header = headers_1_1.value;
this.add(header[0], header[1]);
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (headers_1_1 && !headers_1_1.done && (_a = headers_1.return)) _a.call(headers_1);
}
finally { if (e_1) throw e_1.error; }
}
}
Object.defineProperty(HttpHeaders.prototype, "length", {
/**
* Fetches the total length of all headers
*
* @returns the total length of all headers
*/
get: function () {
var length = 0;
for (var key in this.headers) {
length += this.headers[key].length;
}
return length;
},
enumerable: false,
configurable: true
});
/**
* Add a name/value pair
* @param name The header name
* @param value The header value
*/
HttpHeaders.prototype.add = function (name, value) {
var values = this.headers[name.toLowerCase()];
if (values) {
values.push([name, value]);
}
else {
this.headers[name.toLowerCase()] = [[name, value]];
}
};
/**
* Set a name/value pair, replacing any existing values for the name
* @param name - The header name
* @param value - The header value
*/
HttpHeaders.prototype.set = function (name, value) {
this.headers[name.toLowerCase()] = [[name, value]];
};
/**
* Get the list of values for the given name
* @param name - The header name to look for
* @return List of values, or empty list if none exist
*/
HttpHeaders.prototype.get_values = function (name) {
var e_2, _a;
var values = [];
var values_list = this.headers[name.toLowerCase()] || [];
try {
for (var values_list_1 = __values(values_list), values_list_1_1 = values_list_1.next(); !values_list_1_1.done; values_list_1_1 = values_list_1.next()) {
var entry = values_list_1_1.value;
values.push(entry[1]);
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (values_list_1_1 && !values_list_1_1.done && (_a = values_list_1.return)) _a.call(values_list_1);
}
finally { if (e_2) throw e_2.error; }
}
return values;
};
/**
* Gets the first value for the given name, ignoring any additional values
* @param name - The header name to look for
* @param default_value - Value returned if no values are found for the given name
* @return The first header value, or default if no values exist
*/
HttpHeaders.prototype.get = function (name, default_value) {
if (default_value === void 0) { default_value = ""; }
var values = this.headers[name.toLowerCase()];
if (!values) {
return default_value;
}
return values[0][1] || default_value;
};
/**
* Removes all values for the given name
* @param name - The header to remove all values for
*/
HttpHeaders.prototype.remove = function (name) {
delete this.headers[name.toLowerCase()];
};
/**
* Removes a specific name/value pair
* @param name - The header name to remove
* @param value - The header value to remove
*/
HttpHeaders.prototype.remove_value = function (name, value) {
var key = name.toLowerCase();
var values = this.headers[key];
for (var idx = 0; idx < values.length; ++idx) {
var entry = values[idx];
if (entry[1] === value) {
if (values.length === 1) {
delete this.headers[key];
}
else {
delete values[idx];
}
return;
}
}
};
/** Clears the entire header set */
HttpHeaders.prototype.clear = function () {
this.headers = {};
};
/**
* Iterator. Allows for:
* let headers = new HttpHeaders();
* ...
* for (const header of headers) { }
*/
HttpHeaders.prototype[Symbol.iterator] = function () {
var _a, _b, _i, key, values, values_1, values_1_1, entry, e_3_1;
var e_3, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
_a = [];
for (_b in this.headers)
_a.push(_b);
_i = 0;
_d.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 10];
key = _a[_i];
values = this.headers[key];
_d.label = 2;
case 2:
_d.trys.push([2, 7, 8, 9]);
values_1 = (e_3 = void 0, __values(values)), values_1_1 = values_1.next();
_d.label = 3;
case 3:
if (!!values_1_1.done) return [3 /*break*/, 6];
entry = values_1_1.value;
return [4 /*yield*/, entry];
case 4:
_d.sent();
_d.label = 5;
case 5:
values_1_1 = values_1.next();
return [3 /*break*/, 3];
case 6: return [3 /*break*/, 9];
case 7:
e_3_1 = _d.sent();
e_3 = { error: e_3_1 };
return [3 /*break*/, 9];
case 8:
try {
if (values_1_1 && !values_1_1.done && (_c = values_1.return)) _c.call(values_1);
}
finally { if (e_3) throw e_3.error; }
return [7 /*endfinally*/];
case 9:
_i++;
return [3 /*break*/, 1];
case 10: return [2 /*return*/];
}
});
};
/** @internal */
HttpHeaders.prototype._flatten = function () {
var e_4, _a;
var flattened = [];
try {
for (var _b = __values(this), _c = _b.next(); !_c.done; _c = _b.next()) {
var pair = _c.value;
flattened.push(pair);
}
}
catch (e_4_1) { e_4 = { error: e_4_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_4) throw e_4.error; }
}
return flattened;
};
return HttpHeaders;
}());
exports.HttpHeaders = HttpHeaders;
/**
* Options used when connecting to an HTTP endpoint via a proxy
*
* @category HTTP
*/
var HttpProxyOptions = /** @class */ (function (_super) {
__extends(HttpProxyOptions, _super);
function HttpProxyOptions() {
return _super !== null && _super.apply(this, arguments) || this;
}
return HttpProxyOptions;
}(http_1.CommonHttpProxyOptions));
exports.HttpProxyOptions = HttpProxyOptions;
/**
* Represents a request to a web server from a client
*
* @category HTTP
*/
var HttpRequest = /** @class */ (function () {
/**
* Constructor for the HttpRequest class
*
* @param method The verb to use for the request (i.e. GET, POST, PUT, DELETE, HEAD)
* @param path The URI of the request
* @param headers Additional custom headers to send to the server
* @param body The request body, in the case of a POST or PUT request
*/
function HttpRequest(method, path, headers, body) {
if (headers === void 0) { headers = new HttpHeaders(); }
this.method = method;
this.path = path;
this.headers = headers;
this.body = body;
}
return HttpRequest;
}());
exports.HttpRequest = HttpRequest;
/**
* Represents an HTTP connection from a client to a server
*
* @category HTTP
*/
var HttpClientConnection = /** @class */ (function (_super) {
__extends(HttpClientConnection, _super);
/**
* Http connection constructor, signature synced to native version for compatibility
*
* @param bootstrap - (native only) leave undefined
* @param host_name - endpoint to connection with
* @param port - port to connect to
* @param socketOptions - (native only) leave undefined
* @param tlsOptions - instantiate for TLS, but actual value is unused in browse implementation
* @param proxyOptions - options to control proxy usage when establishing the connection
*/
function HttpClientConnection(bootstrap, host_name, port, socketOptions, tlsOptions, proxyOptions) {
var _this = _super.call(this) || this;
_this.cork();
_this.bootstrap = bootstrap;
_this.socket_options = socketOptions;
_this.tls_options = tlsOptions;
_this.proxy_options = proxyOptions;
var scheme = (_this.tls_options || port === 443) ? 'https' : 'http';
_this.axios_options = {
baseURL: "".concat(scheme, "://").concat(host_name, ":").concat(port, "/")
};
if (_this.proxy_options) {
_this.axios_options.proxy = {
host: _this.proxy_options.host_name,
port: _this.proxy_options.port,
};
if (_this.proxy_options.auth_method == http_1.HttpProxyAuthenticationType.Basic) {
_this.axios_options.proxy.auth = {
username: _this.proxy_options.auth_username || "",
password: _this.proxy_options.auth_password || "",
};
}
}
_this._axios = axios.default.create(_this.axios_options);
setTimeout(function () {
_this.emit('connect');
}, 0);
return _this;
}
// Override to allow uncorking on ready
HttpClientConnection.prototype.on = function (event, listener) {
var _this = this;
_super.prototype.on.call(this, event, listener);
if (event == 'connect') {
setTimeout(function () {
_this.uncork();
}, 0);
}
return this;
};
/**
* Make a client initiated request to this connection.
* @param request - The HttpRequest to attempt on this connection
* @returns A new stream that will deliver events for the request
*/
HttpClientConnection.prototype.request = function (request) {
return stream_request(this, request);
};
/**
* Ends the connection
*/
HttpClientConnection.prototype.close = function () {
this.emit('close');
this._axios = undefined;
};
/**
* Emitted when the connection is connected and ready to start streams
*
* @event
*/
HttpClientConnection.CONNECT = 'connect';
/**
* Emitted when an error occurs on the connection
*
* @event
*/
HttpClientConnection.ERROR = 'error';
/**
* Emitted when the connection has completed
*
* @event
*/
HttpClientConnection.CLOSE = 'close';
return HttpClientConnection;
}(event_1.BufferedEventEmitter));
exports.HttpClientConnection = HttpClientConnection;
function stream_request(connection, request) {
if (request == null || request == undefined) {
throw new error_1.CrtError("HttpClientConnection stream_request: request not defined");
}
var _to_object = function (headers) {
var e_5, _a;
// browsers refuse to let users configure host or user-agent
var forbidden_headers = ['host', 'user-agent'];
var obj = {};
try {
for (var headers_2 = __values(headers), headers_2_1 = headers_2.next(); !headers_2_1.done; headers_2_1 = headers_2.next()) {
var header = headers_2_1.value;
if (forbidden_headers.indexOf(header[0].toLowerCase()) != -1) {
continue;
}
obj[header[0]] = headers.get(header[0]);
}
}
catch (e_5_1) { e_5 = { error: e_5_1 }; }
finally {
try {
if (headers_2_1 && !headers_2_1.done && (_a = headers_2.return)) _a.call(headers_2);
}
finally { if (e_5) throw e_5.error; }
}
return obj;
};
var body = (request.body) ? request.body.data : undefined;
var stream = HttpClientStream._create(connection);
stream.connection._axios.request({
url: request.path,
method: request.method.toLowerCase(),
headers: _to_object(request.headers),
body: body
}).then(function (response) {
stream._on_response(response);
}).catch(function (error) {
stream._on_error(error);
});
return stream;
}
/**
* Represents a single http message exchange (request/response) in HTTP.
*
* NOTE: Binding either the ready or response event will uncork any buffered events and start
* event delivery
*
* @category HTTP
*/
var HttpClientStream = /** @class */ (function (_super) {
__extends(HttpClientStream, _super);
function HttpClientStream(connection) {
var _this = _super.call(this) || this;
_this.connection = connection;
_this.cork();
return _this;
}
/**
* HTTP status code returned from the server.
* @return Either the status code, or undefined if the server response has not arrived yet.
*/
HttpClientStream.prototype.status_code = function () {
return this.response_status_code;
};
/**
* Begin sending the request.
*
* The stream does nothing until this is called. Call activate() when you
* are ready for its callbacks and events to fire.
*/
HttpClientStream.prototype.activate = function () {
var _this = this;
setTimeout(function () {
_this.uncork();
}, 0);
};
HttpClientStream.prototype.on = function (event, listener) {
return _super.prototype.on.call(this, event, listener);
};
// Private helpers for stream_request()
/** @internal */
HttpClientStream._create = function (connection) {
return new HttpClientStream(connection);
};
// Convert axios' single response into a series of events
/** @internal */
HttpClientStream.prototype._on_response = function (response) {
this.response_status_code = response.status;
var headers = new HttpHeaders();
for (var header in response.headers) {
headers.add(header, response.headers[header]);
}
this.emit('response', this.response_status_code, headers);
var data = response.data;
if (data && !(data instanceof ArrayBuffer)) {
data = (0, util_utf8_browser_1.fromUtf8)(data.toString());
}
this.emit('data', data);
this.emit('end');
};
// Gather as much information as possible from the axios error
// and pass it on to the user
/** @internal */
HttpClientStream.prototype._on_error = function (error) {
var info = "";
if (error.response) {
this.response_status_code = error.response.status;
info += "status_code=".concat(error.response.status);
if (error.response.headers) {
info += " headers=".concat(JSON.stringify(error.response.headers));
}
if (error.response.data) {
info += " data=".concat(error.response.data);
}
}
else {
info = "No response from server";
}
this.connection.close();
this.emit('error', new Error("msg=".concat(error.message, ", connection=").concat(JSON.stringify(this.connection), ", info=").concat(info)));
};
/**
* Emitted when the http response headers have arrived.
*
* @event
*/
HttpClientStream.RESPONSE = 'response';
/**
* Emitted when http response data is available.
*
* @event
*/
HttpClientStream.DATA = 'data';
/**
* Emitted when an error occurs in stream processing
*
* @event
*/
HttpClientStream.ERROR = 'error';
/**
* Emitted when the stream has completed
*
* @event
*/
HttpClientStream.END = 'end';
return HttpClientStream;
}(event_1.BufferedEventEmitter));
exports.HttpClientStream = HttpClientStream;
/**
* Creates, manages, and vends connections to a given host/port endpoint
*
* @category HTTP
*/
var HttpClientConnectionManager = /** @class */ (function () {
/**
* Constructor for the HttpClientConnectionManager class. Signature stays in sync with native implementation
* for compatibility purposes (leads to some useless params)
*
* @param bootstrap - (native only) leave undefined
* @param host - endpoint to pool connections for
* @param port - port to connect to
* @param max_connections - maximum allowed connection count
* @param initial_window_size - (native only) leave as zero
* @param socket_options - (native only) leave null
* @param tls_opts - if not null TLS will be used, otherwise plain http will be used
* @param proxy_options - configuration for establishing connections through a proxy
*/
function HttpClientConnectionManager(bootstrap, host, port, max_connections, initial_window_size, socket_options, tls_opts, proxy_options) {
this.bootstrap = bootstrap;
this.host = host;
this.port = port;
this.max_connections = max_connections;
this.initial_window_size = initial_window_size;
this.socket_options = socket_options;
this.tls_opts = tls_opts;
this.proxy_options = proxy_options;
this.pending_connections = new Set();
this.live_connections = new Set();
this.free_connections = [];
this.pending_requests = [];
}
HttpClientConnectionManager.prototype.remove = function (connection) {
this.pending_connections.delete(connection);
this.live_connections.delete(connection);
var free_idx = this.free_connections.indexOf(connection);
if (free_idx != -1) {
this.free_connections.splice(free_idx, 1);
}
};
HttpClientConnectionManager.prototype.resolve = function (connection) {
var request = this.pending_requests.shift();
if (request) {
request.resolve(connection);
}
else {
this.free_connections.push(connection);
}
};
HttpClientConnectionManager.prototype.reject = function (error) {
var request = this.pending_requests.shift();
if (request) {
request.reject(error);
}
};
HttpClientConnectionManager.prototype.pump = function () {
var _this = this;
if (this.pending_requests.length == 0) {
return;
}
// Try to service the request with a free connection
{
var connection_1 = this.free_connections.pop();
if (connection_1) {
return this.resolve(connection_1);
}
}
// If there's no more room, nothing can be resolved right now
if ((this.live_connections.size + this.pending_connections.size) == this.max_connections) {
return;
}
// There's room, create a new connection
var connection = new HttpClientConnection(new io_1.ClientBootstrap(), this.host, this.port, this.socket_options, this.tls_opts, this.proxy_options);
this.pending_connections.add(connection);
var on_connect = function () {
_this.pending_connections.delete(connection);
_this.live_connections.add(connection);
_this.free_connections.push(connection);
_this.resolve(connection);
};
var on_error = function (error) {
if (_this.pending_connections.has(connection)) {
// Connection never connected, error it out
return _this.reject(new error_1.CrtError(error));
}
// If the connection errors after use, get it out of rotation and replace it
_this.remove(connection);
_this.pump();
};
var on_close = function () {
_this.remove(connection);
_this.pump();
};
connection.on('connect', on_connect);
connection.on('error', on_error);
connection.on('close', on_close);
};
/**
* Vends a connection from the pool
* @returns A promise that results in an HttpClientConnection. When done with the connection, return
* it via {@link release}
*/
HttpClientConnectionManager.prototype.acquire = function () {
var _this = this;
return new Promise(function (resolve, reject) {
_this.pending_requests.push({
resolve: resolve,
reject: reject
});
_this.pump();
});
};
/**
* Returns an unused connection to the pool
* @param connection - The connection to return
*/
HttpClientConnectionManager.prototype.release = function (connection) {
this.free_connections.push(connection);
this.pump();
};
/** Closes all connections and rejects all pending requests */
HttpClientConnectionManager.prototype.close = function () {
this.pending_requests.forEach(function (request) {
request.reject(new error_1.CrtError('HttpClientConnectionManager shutting down'));
});
};
return HttpClientConnectionManager;
}());
exports.HttpClientConnectionManager = HttpClientConnectionManager;
//# sourceMappingURL=http.js.map