Skip to content
This repository was archived by the owner on Jan 28, 2025. It is now read-only.

Compat Layer status code fixes #437

Merged
merged 7 commits into from
Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ describe("compatLayer.response", () => {
res.end();
});

it("[Promise] statusCode statusCode=200 by default", () => {
expect.assertions(1);

const { res, responsePromise } = create({
requestContext: {
path: "/"
},
headers: {}
});

res.end();

return responsePromise.then(response => {
expect(response.statusCode).toEqual(200);
});
});

it("[Promise] statusCode statusCode=200", () => {
expect.assertions(1);

Expand Down Expand Up @@ -452,4 +469,21 @@ describe("compatLayer.response", () => {
expect(response.isBase64Encoded).toEqual(true);
});
});

it("response does not have a body if only statusCode is set", () => {
const { res, responsePromise } = create({
requestContext: {
path: "/"
},
headers: {}
});

res.statusCode = 204;
res.end();

return responsePromise.then(response => {
expect(response.body).not.toBeDefined();
expect(response.statusCode).toEqual(204);
});
});
});
18 changes: 13 additions & 5 deletions packages/apigw-lambda-compat/lib/compatLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ const http = require("http");
const reqResMapper = (event, callback) => {
const base64Support = process.env.BINARY_SUPPORT === "yes";
const response = {
body: Buffer.from(""),
isBase64Encoded: base64Support,
statusCode: 200,
multiValueHeaders: {}
};
let responsePromise;
Expand Down Expand Up @@ -80,6 +78,10 @@ const reqResMapper = (event, callback) => {
if (headers) res.headers = Object.assign(res.headers, headers);
};
res.write = chunk => {
if (!response.body) {
response.body = Buffer.from("");
}

response.body = Buffer.concat([
Buffer.isBuffer(response.body)
? response.body
Expand All @@ -105,9 +107,15 @@ const reqResMapper = (event, callback) => {

const onResEnd = (callback, resolve) => text => {
if (text) res.write(text);
response.body = Buffer.from(response.body).toString(
base64Support ? "base64" : undefined
);
if (!res.statusCode) {
res.statusCode = 200;
}

if (response.body) {
response.body = Buffer.from(response.body).toString(
base64Support ? "base64" : undefined
);
}
response.multiValueHeaders = res.headers;
res.writeHead(response.statusCode);
fixApiGatewayMultipleHeaders();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe("Response Tests", () => {
});

it("statusCode statusCode=200", () => {
expect.assertions(1);
expect.assertions(2);

const { res, responsePromise } = create({
request: {
Expand All @@ -33,6 +33,24 @@ describe("Response Tests", () => {
res.statusCode = 200;
res.end();

return responsePromise.then(response => {
expect(response.status).toEqual(200);
expect(response.statusDescription).toEqual("OK");
});
});

it("statusCode statusCode=200 by default", () => {
expect.assertions(1);

const { res, responsePromise } = create({
request: {
uri: "/",
headers: {}
}
});

res.end();

return responsePromise.then(response => {
expect(response.status).toEqual(200);
});
Expand Down Expand Up @@ -265,6 +283,8 @@ describe("Response Tests", () => {
});

it(`res.write('ok')`, () => {
expect.assertions(2);

const { res, responsePromise } = create({
request: {
path: "/",
Expand All @@ -277,10 +297,13 @@ describe("Response Tests", () => {

return responsePromise.then(response => {
expect(response.body).toEqual("b2s=");
expect(response.bodyEncoding).toEqual("base64");
});
});

it(`res.end('ok')`, () => {
expect.assertions(1);

const { res, responsePromise } = create({
request: {
path: "/",
Expand All @@ -296,6 +319,8 @@ describe("Response Tests", () => {
});

it(`gzips`, () => {
expect.assertions(2);

const gzipSpy = jest.spyOn(zlib, "gzipSync");
gzipSpy.mockReturnValueOnce(Buffer.from("ok-gzipped"));

Expand Down Expand Up @@ -324,4 +349,25 @@ describe("Response Tests", () => {
expect(response.body).toEqual("b2stZ3ppcHBlZA=="); // "ok-gzipped" base64 encoded
});
});

it("response does not have a body if only statusCode is set", () => {
expect.assertions(4);

const { res, responsePromise } = create({
request: {
path: "/",
headers: {}
}
});

res.statusCode = 204;
res.end();

return responsePromise.then(response => {
expect(response.body).not.toBeDefined();
expect(response.bodyEncoding).not.toBeDefined();
expect(response.status).toEqual(204);
expect(response.statusDescription).toEqual("No Content");
});
});
});
84 changes: 77 additions & 7 deletions packages/lambda-at-edge-compat/next-aws-cloudfront.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,63 @@ const readOnlyCloudFrontHeaders = {
via: true
};

const HttpStatusCodes = {
202: "Accepted",
502: "Bad Gateway",
400: "Bad Request",
409: "Conflict",
100: "Continue",
201: "Created",
417: "Expectation Failed",
424: "Failed Dependency",
403: "Forbidden",
504: "Gateway Timeout",
410: "Gone",
505: "HTTP Version Not Supported",
418: "I'm a teapot",
419: "Insufficient Space on Resource",
507: "Insufficient Storage",
500: "Server Error",
411: "Length Required",
423: "Locked",
420: "Method Failure",
405: "Method Not Allowed",
301: "Moved Permanently",
302: "Moved Temporarily",
207: "Multi-Status",
300: "Multiple Choices",
511: "Network Authentication Required",
204: "No Content",
203: "Non Authoritative Information",
406: "Not Acceptable",
404: "Not Found",
501: "Not Implemented",
304: "Not Modified",
200: "OK",
206: "Partial Content",
402: "Payment Required",
308: "Permanent Redirect",
412: "Precondition Failed",
428: "Precondition Required",
102: "Processing",
407: "Proxy Authentication Required",
431: "Request Header Fields Too Large",
408: "Request Timeout",
413: "Request Entity Too Large",
414: "Request-URI Too Long",
416: "Requested Range Not Satisfiable",
205: "Reset Content",
303: "See Other",
503: "Service Unavailable",
101: "Switching Protocols",
307: "Temporary Redirect",
429: "Too Many Requests",
401: "Unauthorized",
422: "Unprocessable Entity",
415: "Unsupported Media Type",
305: "Use Proxy"
};

const toCloudFrontHeaders = headers => {
const result = {};

Expand Down Expand Up @@ -83,10 +140,6 @@ const handler = event => {
const { request: cfRequest } = event;

const response = {
body: Buffer.from(""),
bodyEncoding: "base64",
status: 200,
statusDescription: "OK",
headers: {}
};

Expand Down Expand Up @@ -141,31 +194,48 @@ const handler = event => {
},
set(statusCode) {
response.status = statusCode;
response.statusDescription = HttpStatusCodes[statusCode];
}
});

res.headers = {};
res.writeHead = (status, headers) => {
response.status = status;

if (headers) {
res.headers = Object.assign(res.headers, headers);
}
};
res.write = chunk => {
if (!response.body) {
response.body = Buffer.from("");
}

response.body = Buffer.concat([
response.body,
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
]);
};

let gz = isGzipSupported(headers);

const responsePromise = new Promise(resolve => {
res.end = text => {
if (text) res.write(text);

if (!res.statusCode) {
res.statusCode = 200;
}

res.finished = true;
response.body = gz
? zlib.gzipSync(response.body).toString("base64")
: Buffer.from(response.body).toString("base64");

if (response.body) {
response.bodyEncoding = "base64";
response.body = gz
? zlib.gzipSync(response.body).toString("base64")
: Buffer.from(response.body).toString("base64");
}

response.headers = toCloudFrontHeaders(res.headers);

if (gz) {
Expand Down