Skip to content

Commit

Permalink
Add a WebSockets API behind CloudFront
Browse files Browse the repository at this point in the history
  • Loading branch information
douglasnaphas committed Mar 19, 2022
1 parent 5a94759 commit 0ce5876
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 16 deletions.
41 changes: 41 additions & 0 deletions backend/connect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const DynamoDB = require("aws-sdk/clients/dynamodb");
const schema = require("../schema");

exports.handler = async function (event, context, callback) {
console.log("connect handler called");
console.log("event:");
console.log(event);
console.log("context:");
console.log(context);
const db = new DynamoDB.DocumentClient();
const now = new Date();
var putParams = {
TableName: process.env.TABLE_NAME,
Item: {
[schema.PARTITION_KEY]: `CHECK?PARAMS`, // need to see if event has params
[schema.SORT_KEY]:
`${schema.CONNECT}` +
`${schema.SEPARATOR}` +
`${event.requestContext.connectionId}`,
[schema.CONNECTION_ID]: event.requestContext.connectionId,
[schema.DATE]: now.toISOString(),
[schema.MS]: now.getTime()
},
};

try {
// Insert incoming connection id in the WebSocket
await db.put(putParams).promise();

return {
statusCode: 200,
body: "Connected",
};
} catch (e) {
console.error("connect error!", e);
return {
statusCode: 501,
body: "Failed to connect: " + JSON.stringify(e),
};
}
};
13 changes: 13 additions & 0 deletions backend/default.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const schema = require("../schema");

exports.handler = async function (event, context, callback) {
console.log("default handler called");
console.log("event:");
console.log(event);
console.log("context:");
console.log(context);
return {
statusCode: 200,
body: "defaulted",
};
};
41 changes: 41 additions & 0 deletions backend/disconnect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const DynamoDB = require("aws-sdk/clients/dynamodb");
const schema = require("../schema");

exports.handler = async function (event, context, callback) {
console.log("disconnect handler called");
console.log("event:");
console.log(event);
console.log("context:");
console.log(context);
const db = new DynamoDB.DocumentClient();
const now = new Date();
var putParams = {
TableName: process.env.TABLE_NAME,
Item: {
[schema.PARTITION_KEY]: `CHECK?PARAMS`, // need to see if event has params
[schema.SORT_KEY]:
`${schema.DISCONNECT}` +
`${schema.SEPARATOR}` +
`${event.requestContext.connectionId}`,
[schema.CONNECTION_ID]: event.requestContext.connectionId,
[schema.DATE]: now.toISOString(),
[schema.MS]: now.getTime()
},
};

try {
// Insert incoming connection id in the WebSocket
await db.put(putParams).promise();

return {
statusCode: 200,
body: "Disconnected",
};
} catch (e) {
console.error("disconnect error!", e);
return {
statusCode: 501,
body: "Failed to disconnect: " + JSON.stringify(e),
};
}
};
8 changes: 8 additions & 0 deletions backend/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ const schema = {
OPAQUE_COOKIE_EXPIRATION_MILLISECONDS: "cookie_expiration_ms",
OPAQUE_COOKIE_ISSUED_DATE: "cookie_issued_date",
OPAQUE_COOKIE_EXPIRATION_DATE: "cookie_expiration_date",
// WebSockets
CONNECTION: "connection",
EVENT: "event", // CONNECT or DISCONNECT
CONNECT: "connect",
DISCONNECT: "disconnect",
DATE: "date",
MS: "ms",
CONNECTION_ID: "connection_id"
};

module.exports = schema;
72 changes: 72 additions & 0 deletions lib/madliberation-webapp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { aws_iam as iam } from "aws-cdk-lib";
import { aws_certificatemanager as acm } from "aws-cdk-lib";
import { aws_route53 as route53 } from "aws-cdk-lib";
import { aws_route53_targets as targets } from "aws-cdk-lib";
import * as apigwv2 from "@aws-cdk/aws-apigatewayv2-alpha";
import * as apigwv2i from "@aws-cdk/aws-apigatewayv2-integrations-alpha";
const schema = require("../backend/schema");

export interface MadLiberationWebappProps extends StackProps {
Expand Down Expand Up @@ -424,6 +426,73 @@ export class MadliberationWebapp extends Stack {
cfnAliasWWWRecordSet.setIdentifier = "mlwebapp-www-cf-alias";
}

const makeWSHandler = (prefix: string) =>
new lambda.Function(this, `${prefix}Handler`, {
runtime: lambda.Runtime.NODEJS_14_X,
handler: `${prefix.toLowerCase()}.handler`,
code: lambda.Code.fromAsset("backend"),
memorySize: 3000,
environment: {
NODE_ENV: "production",
TABLE_NAME: sedersTable.tableName,
},
timeout: Duration.seconds(20),
});
const connectHandler = makeWSHandler("Connect");
const disconnectHandler = makeWSHandler("Disconnet");
const defaultHandler = makeWSHandler("Default");
const webSocketApi = new apigwv2.WebSocketApi(this, "WSAPI", {
connectRouteOptions: {
integration: new apigwv2i.WebSocketLambdaIntegration(
"ConnectIntegration",
connectHandler
),
},
disconnectRouteOptions: {
integration: new apigwv2i.WebSocketLambdaIntegration(
"DisconnectIntegration",
disconnectHandler
),
},
defaultRouteOptions: {
integration: new apigwv2i.WebSocketLambdaIntegration(
"DefaultIntegration",
defaultHandler
),
},
});
const stageName = "ws";
const wsStage = new apigwv2.WebSocketStage(this, "WSStage", {
stageName,
webSocketApi,
autoDeploy: true,
});
distro.addBehavior(
`/${stageName}/*`,
new origins.HttpOrigin(
`${webSocketApi.apiId}.execute-api.${this.region}.${this.urlSuffix}`,
{
protocolPolicy: cloudfront.OriginProtocolPolicy.HTTPS_ONLY,
}
),
{
allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
originRequestPolicy: new cloudfront.OriginRequestPolicy(
this,
"WSOriginRequestPolicy",
{
headerBehavior: cloudfront.OriginRequestHeaderBehavior.allowList(
"Sec-WebSocket-Extensions",
"Sec-WebSocket-Key",
"Sec-WebSocket-Version"
),
}
),
}
);

const scriptsBucket = new MadLiberationBucket(this, "ScriptsBucket", {
versioned: true,
});
Expand Down Expand Up @@ -464,5 +533,8 @@ export class MadliberationWebapp extends Stack {
value: scriptsBucket.bucketName,
});
new CfnOutput(this, "TableName", { value: sedersTable.tableName });
new CfnOutput(this, "WSAPIEndpoint", {
value: webSocketApi.apiEndpoint,
});
}
}
Loading

0 comments on commit 0ce5876

Please sign in to comment.