lambda-live-debugger
Version:
Debug Lambda functions locally like it is running in the cloud
294 lines (293 loc) • 12.9 kB
JavaScript
import { NormalizedSchema, translateTraits } from "@smithy/core/schema";
import { splitEvery, splitHeader } from "@smithy/core/serde";
import { HttpRequest } from "@smithy/protocol-http";
import { sdkStreamMixin } from "@smithy/util-stream";
import { collectBody } from "./collect-stream-body";
import { extendedEncodeURIComponent } from "./extended-encode-uri-component";
import { HttpProtocol } from "./HttpProtocol";
export class HttpBindingProtocol extends HttpProtocol {
async serializeRequest(operationSchema, _input, context) {
const input = {
...(_input ?? {}),
};
const serializer = this.serializer;
const query = {};
const headers = {};
const endpoint = await context.endpoint();
const ns = NormalizedSchema.of(operationSchema?.input);
const payloadMemberNames = [];
const payloadMemberSchemas = [];
let hasNonHttpBindingMember = false;
let payload;
const request = new HttpRequest({
protocol: "",
hostname: "",
port: undefined,
path: "",
fragment: undefined,
query: query,
headers: headers,
body: undefined,
});
if (endpoint) {
this.updateServiceEndpoint(request, endpoint);
this.setHostPrefix(request, operationSchema, input);
const opTraits = translateTraits(operationSchema.traits);
if (opTraits.http) {
request.method = opTraits.http[0];
const [path, search] = opTraits.http[1].split("?");
if (request.path == "/") {
request.path = path;
}
else {
request.path += path;
}
const traitSearchParams = new URLSearchParams(search ?? "");
Object.assign(query, Object.fromEntries(traitSearchParams));
}
}
for (const [memberName, memberNs] of ns.structIterator()) {
const memberTraits = memberNs.getMergedTraits() ?? {};
const inputMemberValue = input[memberName];
if (inputMemberValue == null && !memberNs.isIdempotencyToken()) {
if (memberTraits.httpLabel) {
if (request.path.includes(`{${memberName}+}`) || request.path.includes(`{${memberName}}`)) {
throw new Error(`No value provided for input HTTP label: ${memberName}.`);
}
}
continue;
}
if (memberTraits.httpPayload) {
const isStreaming = memberNs.isStreaming();
if (isStreaming) {
const isEventStream = memberNs.isStructSchema();
if (isEventStream) {
if (input[memberName]) {
payload = await this.serializeEventStream({
eventStream: input[memberName],
requestSchema: ns,
});
}
}
else {
payload = inputMemberValue;
}
}
else {
serializer.write(memberNs, inputMemberValue);
payload = serializer.flush();
}
delete input[memberName];
}
else if (memberTraits.httpLabel) {
serializer.write(memberNs, inputMemberValue);
const replacement = serializer.flush();
if (request.path.includes(`{${memberName}+}`)) {
request.path = request.path.replace(`{${memberName}+}`, replacement.split("/").map(extendedEncodeURIComponent).join("/"));
}
else if (request.path.includes(`{${memberName}}`)) {
request.path = request.path.replace(`{${memberName}}`, extendedEncodeURIComponent(replacement));
}
delete input[memberName];
}
else if (memberTraits.httpHeader) {
serializer.write(memberNs, inputMemberValue);
headers[memberTraits.httpHeader.toLowerCase()] = String(serializer.flush());
delete input[memberName];
}
else if (typeof memberTraits.httpPrefixHeaders === "string") {
for (const [key, val] of Object.entries(inputMemberValue)) {
const amalgam = memberTraits.httpPrefixHeaders + key;
serializer.write([memberNs.getValueSchema(), { httpHeader: amalgam }], val);
headers[amalgam.toLowerCase()] = serializer.flush();
}
delete input[memberName];
}
else if (memberTraits.httpQuery || memberTraits.httpQueryParams) {
this.serializeQuery(memberNs, inputMemberValue, query);
delete input[memberName];
}
else {
hasNonHttpBindingMember = true;
payloadMemberNames.push(memberName);
payloadMemberSchemas.push(memberNs);
}
}
if (hasNonHttpBindingMember && input) {
const [namespace, name] = (ns.getName(true) ?? "#Unknown").split("#");
const requiredMembers = ns.getSchema()[6];
const payloadSchema = [
3,
namespace,
name,
ns.getMergedTraits(),
payloadMemberNames,
payloadMemberSchemas,
undefined,
];
if (requiredMembers) {
payloadSchema[6] = requiredMembers;
}
else {
payloadSchema.pop();
}
serializer.write(payloadSchema, input);
payload = serializer.flush();
}
request.headers = headers;
request.query = query;
request.body = payload;
return request;
}
serializeQuery(ns, data, query) {
const serializer = this.serializer;
const traits = ns.getMergedTraits();
if (traits.httpQueryParams) {
for (const [key, val] of Object.entries(data)) {
if (!(key in query)) {
const valueSchema = ns.getValueSchema();
Object.assign(valueSchema.getMergedTraits(), {
...traits,
httpQuery: key,
httpQueryParams: undefined,
});
this.serializeQuery(valueSchema, val, query);
}
}
return;
}
if (ns.isListSchema()) {
const sparse = !!ns.getMergedTraits().sparse;
const buffer = [];
for (const item of data) {
serializer.write([ns.getValueSchema(), traits], item);
const serializable = serializer.flush();
if (sparse || serializable !== undefined) {
buffer.push(serializable);
}
}
query[traits.httpQuery] = buffer;
}
else {
serializer.write([ns, traits], data);
query[traits.httpQuery] = serializer.flush();
}
}
async deserializeResponse(operationSchema, context, response) {
const deserializer = this.deserializer;
const ns = NormalizedSchema.of(operationSchema.output);
const dataObject = {};
if (response.statusCode >= 300) {
const bytes = await collectBody(response.body, context);
if (bytes.byteLength > 0) {
Object.assign(dataObject, await deserializer.read(15, bytes));
}
await this.handleError(operationSchema, context, response, dataObject, this.deserializeMetadata(response));
throw new Error("@smithy/core/protocols - HTTP Protocol error handler failed to throw.");
}
for (const header in response.headers) {
const value = response.headers[header];
delete response.headers[header];
response.headers[header.toLowerCase()] = value;
}
const nonHttpBindingMembers = await this.deserializeHttpMessage(ns, context, response, dataObject);
if (nonHttpBindingMembers.length) {
const bytes = await collectBody(response.body, context);
if (bytes.byteLength > 0) {
const dataFromBody = await deserializer.read(ns, bytes);
for (const member of nonHttpBindingMembers) {
if (dataFromBody[member] != null) {
dataObject[member] = dataFromBody[member];
}
}
}
}
else if (nonHttpBindingMembers.discardResponseBody) {
await collectBody(response.body, context);
}
dataObject.$metadata = this.deserializeMetadata(response);
return dataObject;
}
async deserializeHttpMessage(schema, context, response, arg4, arg5) {
let dataObject;
if (arg4 instanceof Set) {
dataObject = arg5;
}
else {
dataObject = arg4;
}
let discardResponseBody = true;
const deserializer = this.deserializer;
const ns = NormalizedSchema.of(schema);
const nonHttpBindingMembers = [];
for (const [memberName, memberSchema] of ns.structIterator()) {
const memberTraits = memberSchema.getMemberTraits();
if (memberTraits.httpPayload) {
discardResponseBody = false;
const isStreaming = memberSchema.isStreaming();
if (isStreaming) {
const isEventStream = memberSchema.isStructSchema();
if (isEventStream) {
dataObject[memberName] = await this.deserializeEventStream({
response,
responseSchema: ns,
});
}
else {
dataObject[memberName] = sdkStreamMixin(response.body);
}
}
else if (response.body) {
const bytes = await collectBody(response.body, context);
if (bytes.byteLength > 0) {
dataObject[memberName] = await deserializer.read(memberSchema, bytes);
}
}
}
else if (memberTraits.httpHeader) {
const key = String(memberTraits.httpHeader).toLowerCase();
const value = response.headers[key];
if (null != value) {
if (memberSchema.isListSchema()) {
const headerListValueSchema = memberSchema.getValueSchema();
headerListValueSchema.getMergedTraits().httpHeader = key;
let sections;
if (headerListValueSchema.isTimestampSchema() &&
headerListValueSchema.getSchema() === 4) {
sections = splitEvery(value, ",", 2);
}
else {
sections = splitHeader(value);
}
const list = [];
for (const section of sections) {
list.push(await deserializer.read(headerListValueSchema, section.trim()));
}
dataObject[memberName] = list;
}
else {
dataObject[memberName] = await deserializer.read(memberSchema, value);
}
}
}
else if (memberTraits.httpPrefixHeaders !== undefined) {
dataObject[memberName] = {};
for (const [header, value] of Object.entries(response.headers)) {
if (header.startsWith(memberTraits.httpPrefixHeaders)) {
const valueSchema = memberSchema.getValueSchema();
valueSchema.getMergedTraits().httpHeader = header;
dataObject[memberName][header.slice(memberTraits.httpPrefixHeaders.length)] = await deserializer.read(valueSchema, value);
}
}
}
else if (memberTraits.httpResponseCode) {
dataObject[memberName] = response.statusCode;
}
else {
nonHttpBindingMembers.push(memberName);
}
}
nonHttpBindingMembers.discardResponseBody = discardResponseBody;
return nonHttpBindingMembers;
}
}