UNPKG

@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
"use strict"; 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;