open-next-cdk
Version:
Deploy a NextJS app using OpenNext packaging to serverless AWS using CDK
160 lines (159 loc) • 6.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeHttp2Handler = void 0;
const protocol_http_1 = require("@smithy/protocol-http");
const querystring_builder_1 = require("@smithy/querystring-builder");
const http2_1 = require("http2");
const get_transformed_headers_1 = require("./get-transformed-headers");
const node_http2_connection_manager_1 = require("./node-http2-connection-manager");
const write_request_body_1 = require("./write-request-body");
class NodeHttp2Handler {
constructor(options) {
this.metadata = { handlerProtocol: "h2" };
this.connectionManager = new node_http2_connection_manager_1.NodeHttp2ConnectionManager({});
this.configProvider = new Promise((resolve, reject) => {
if (typeof options === "function") {
options()
.then((opts) => {
resolve(opts || {});
})
.catch(reject);
}
else {
resolve(options || {});
}
});
}
destroy() {
this.connectionManager.destroy();
}
async handle(request, { abortSignal } = {}) {
if (!this.config) {
this.config = await this.configProvider;
this.connectionManager.setDisableConcurrentStreams(this.config.disableConcurrentStreams || false);
if (this.config.maxConcurrentStreams) {
this.connectionManager.setMaxConcurrentStreams(this.config.maxConcurrentStreams);
}
}
const { requestTimeout, disableConcurrentStreams } = this.config;
return new Promise((_resolve, _reject) => {
var _a, _b, _c;
let fulfilled = false;
let writeRequestBodyPromise = undefined;
const resolve = async (arg) => {
await writeRequestBodyPromise;
_resolve(arg);
};
const reject = async (arg) => {
await writeRequestBodyPromise;
_reject(arg);
};
if (abortSignal === null || abortSignal === void 0 ? void 0 : abortSignal.aborted) {
fulfilled = true;
const abortError = new Error("Request aborted");
abortError.name = "AbortError";
reject(abortError);
return;
}
const { hostname, method, port, protocol, query } = request;
let auth = "";
if (request.username != null || request.password != null) {
const username = (_a = request.username) !== null && _a !== void 0 ? _a : "";
const password = (_b = request.password) !== null && _b !== void 0 ? _b : "";
auth = `${username}:${password}@`;
}
const authority = `${protocol}//${auth}${hostname}${port ? `:${port}` : ""}`;
const requestContext = { destination: new URL(authority) };
const session = this.connectionManager.lease(requestContext, {
requestTimeout: (_c = this.config) === null || _c === void 0 ? void 0 : _c.sessionTimeout,
disableConcurrentStreams: disableConcurrentStreams || false,
});
const rejectWithDestroy = (err) => {
if (disableConcurrentStreams) {
this.destroySession(session);
}
fulfilled = true;
reject(err);
};
const queryString = (0, querystring_builder_1.buildQueryString)(query || {});
let path = request.path;
if (queryString) {
path += `?${queryString}`;
}
if (request.fragment) {
path += `#${request.fragment}`;
}
const req = session.request({
...request.headers,
[http2_1.constants.HTTP2_HEADER_PATH]: path,
[http2_1.constants.HTTP2_HEADER_METHOD]: method,
});
session.ref();
req.on("response", (headers) => {
const httpResponse = new protocol_http_1.HttpResponse({
statusCode: headers[":status"] || -1,
headers: (0, get_transformed_headers_1.getTransformedHeaders)(headers),
body: req,
});
fulfilled = true;
resolve({ response: httpResponse });
if (disableConcurrentStreams) {
session.close();
this.connectionManager.deleteSession(authority, session);
}
});
if (requestTimeout) {
req.setTimeout(requestTimeout, () => {
req.close();
const timeoutError = new Error(`Stream timed out because of no activity for ${requestTimeout} ms`);
timeoutError.name = "TimeoutError";
rejectWithDestroy(timeoutError);
});
}
if (abortSignal) {
abortSignal.onabort = () => {
req.close();
const abortError = new Error("Request aborted");
abortError.name = "AbortError";
rejectWithDestroy(abortError);
};
}
req.on("frameError", (type, code, id) => {
rejectWithDestroy(new Error(`Frame type id ${type} in stream id ${id} has failed with code ${code}.`));
});
req.on("error", rejectWithDestroy);
req.on("aborted", () => {
rejectWithDestroy(new Error(`HTTP/2 stream is abnormally aborted in mid-communication with result code ${req.rstCode}.`));
});
req.on("close", () => {
session.unref();
if (disableConcurrentStreams) {
session.destroy();
}
if (!fulfilled) {
rejectWithDestroy(new Error("Unexpected error: http2 request did not get a response"));
}
});
writeRequestBodyPromise = (0, write_request_body_1.writeRequestBody)(req, request, requestTimeout);
});
}
updateHttpClientConfig(key, value) {
this.config = undefined;
this.configProvider = this.configProvider.then((config) => {
return {
...config,
[key]: value,
};
});
}
httpHandlerConfigs() {
var _a;
return (_a = this.config) !== null && _a !== void 0 ? _a : {};
}
destroySession(session) {
if (!session.destroyed) {
session.destroy();
}
}
}
exports.NodeHttp2Handler = NodeHttp2Handler;