@softchef/cdk-iot-device-management
Version:
IoT device management is composed of things, thing types, thing groups, jobs, files API services. The constructs can be used independently, that are based on full-managed service to create an API Gateway & Lambda function.
132 lines (131 loc) • 5.81 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeHttp2Handler = void 0;
const protocol_http_1 = require("@aws-sdk/protocol-http");
const querystring_builder_1 = require("@aws-sdk/querystring-builder");
const http2_1 = require("http2");
const get_transformed_headers_1 = require("./get-transformed-headers");
const write_request_body_1 = require("./write-request-body");
class NodeHttp2Handler {
constructor({ requestTimeout, sessionTimeout, disableConcurrentStreams } = {}) {
this.metadata = { handlerProtocol: "h2" };
this.requestTimeout = requestTimeout;
this.sessionTimeout = sessionTimeout;
this.disableConcurrentStreams = disableConcurrentStreams;
this.sessionCache = new Map();
}
destroy() {
for (const sessions of this.sessionCache.values()) {
sessions.forEach((session) => this.destroySession(session));
}
this.sessionCache.clear();
}
handle(request, { abortSignal } = {}) {
return new Promise((resolve, rejectOriginal) => {
let fulfilled = false;
if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
fulfilled = true;
const abortError = new Error("Request aborted");
abortError.name = "AbortError";
rejectOriginal(abortError);
return;
}
const { hostname, method, port, protocol, path, query } = request;
const authority = `${protocol}//${hostname}${port ? `:${port}` : ""}`;
const session = this.getSession(authority, this.disableConcurrentStreams || false);
const reject = (err) => {
if (this.disableConcurrentStreams) {
this.destroySession(session);
}
fulfilled = true;
rejectOriginal(err);
};
const queryString = querystring_builder_1.buildQueryString(query || {});
const req = session.request({
...request.headers,
[http2_1.constants.HTTP2_HEADER_PATH]: queryString ? `${path}?${queryString}` : path,
[http2_1.constants.HTTP2_HEADER_METHOD]: method,
});
req.on("response", (headers) => {
const httpResponse = new protocol_http_1.HttpResponse({
statusCode: headers[":status"] || -1,
headers: get_transformed_headers_1.getTransformedHeaders(headers),
body: req,
});
fulfilled = true;
resolve({ response: httpResponse });
if (this.disableConcurrentStreams) {
session.close();
this.deleteSessionFromCache(authority, session);
}
});
const requestTimeout = this.requestTimeout;
if (requestTimeout) {
req.setTimeout(requestTimeout, () => {
req.close();
const timeoutError = new Error(`Stream timed out because of no activity for ${requestTimeout} ms`);
timeoutError.name = "TimeoutError";
reject(timeoutError);
});
}
if (abortSignal) {
abortSignal.onabort = () => {
req.close();
const abortError = new Error("Request aborted");
abortError.name = "AbortError";
reject(abortError);
};
}
req.on("frameError", (type, code, id) => {
reject(new Error(`Frame type id ${type} in stream id ${id} has failed with code ${code}.`));
});
req.on("error", reject);
req.on("aborted", () => {
reject(new Error(`HTTP/2 stream is abnormally aborted in mid-communication with result code ${req.rstCode}.`));
});
req.on("close", () => {
if (this.disableConcurrentStreams) {
session.destroy();
}
if (!fulfilled) {
reject(new Error("Unexpected error: http2 request did not get a response"));
}
});
write_request_body_1.writeRequestBody(req, request);
});
}
getSession(authority, disableConcurrentStreams) {
const sessionCache = this.sessionCache;
const existingSessions = sessionCache.get(authority) || [];
if (existingSessions.length > 0 && !disableConcurrentStreams)
return existingSessions[0];
const newSession = http2_1.connect(authority);
const destroySessionCb = () => {
this.destroySession(newSession);
this.deleteSessionFromCache(authority, newSession);
};
newSession.on("goaway", destroySessionCb);
newSession.on("error", destroySessionCb);
newSession.on("frameError", destroySessionCb);
const sessionTimeout = this.sessionTimeout;
if (sessionTimeout) {
newSession.setTimeout(sessionTimeout, destroySessionCb);
}
existingSessions.push(newSession);
sessionCache.set(authority, existingSessions);
return newSession;
}
destroySession(session) {
if (!session.destroyed) {
session.destroy();
}
}
deleteSessionFromCache(authority, session) {
const existingSessions = this.sessionCache.get(authority) || [];
if (!existingSessions.includes(session)) {
return;
}
this.sessionCache.set(authority, existingSessions.filter((s) => s !== session));
}
}
exports.NodeHttp2Handler = NodeHttp2Handler;