Skip to content

Instantly share code, notes, and snippets.

@hugotkk
Last active April 18, 2023 22:58
Show Gist options
  • Save hugotkk/ce89d68704cf66a6338087f4352fb450 to your computer and use it in GitHub Desktop.
Save hugotkk/ce89d68704cf66a6338087f4352fb450 to your computer and use it in GitHub Desktop.
Lab - WebSocket Gateway
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Chat Client</title>
</head>
<body>
<h1>WebSocket Chat Client</h1>
<label for="roomId">Room ID:</label>
<input type="text" id="roomId" placeholder="Enter room ID">
<button id="join">Join</button>
<button id="leave">Leave</button>
<br><br>
<label for="message">Message:</label>
<input type="text" id="message" placeholder="Enter your message">
<button id="send">Send</button>
<br><br>
<div id="messages"></div>
<script>
const url = "wss://d8ck9i69wa.execute-api.us-east-1.amazonaws.com/production";
const socket = new WebSocket(url);
const joinBtn = document.getElementById("join");
const leaveBtn = document.getElementById("leave");
const sendBtn = document.getElementById("send");
const roomIdInput = document.getElementById("roomId");
const messageInput = document.getElementById("message");
const messagesDiv = document.getElementById("messages");
joinBtn.addEventListener("click", () => {
const roomId = roomIdInput.value;
console.log(roomId);
socket.send(JSON.stringify({ action: "join", roomId }));
});
leaveBtn.addEventListener("click", () => {
const roomId = roomIdInput.value;
socket.send(JSON.stringify({ action: "leave", roomId }));
});
sendBtn.addEventListener("click", () => {
const message = messageInput.value;
const roomId = roomIdInput.value;
socket.send(JSON.stringify({ action: "sendAll", roomId, message }));
});
socket.addEventListener("message", (event) => {
console.log(event);
const data = JSON.parse(event.data);
const messageElement = document.createElement("div");
messageElement.innerText = data.message;
messagesDiv.appendChild(messageElement);
});
</script>
</body>
</html>
import { DynamoDBClient, PutItemCommand, GetItemCommand, DeleteItemCommand, UpdateItemCommand } from "@aws-sdk/client-dynamodb";
import { ApiGatewayManagementApi, PostToConnectionCommand } from "@aws-sdk/client-apigatewaymanagementapi";
const dynamodbClient = new DynamoDBClient({ region: process.env.AWS_REGION });
const apiGatewayClient = new ApiGatewayManagementApi({ apiVersion: "2018-11-29", endpoint: process.env.ENDPOINT });
export const handler = async (event, context) => {
const { connectionId, domainName, stage } = event.requestContext;
const action = event.requestContext.routeKey;
let body = {};
if (event.body) {
body = JSON.parse(event.body);
}
switch (action) {
case "$connect":
// Save the connection ID to DynamoDB
await saveConnection(connectionId);
break;
case "$disconnect":
// Remove the connection ID from DynamoDB
await removeConnection(connectionId);
break;
case "join":
// Add the connection ID to a specific room in DynamoDB
const roomId = body.roomId;
await addConnectionToRoom(connectionId, roomId);
break;
case "leave":
// Remove the connection ID from a specific room in DynamoDB
const leaveRoomId = body.roomId;
await removeConnectionFromRoom(connectionId, leaveRoomId);
break;
case "send":
// Send a message to a specific connection ID
const sendConnectionId = body.connectionId;
const message = body.message;
await sendMessage(connectionId, sendConnectionId, message);
break;
case "sendAll":
// Send a message to all connections in a specific room
const sendAllRoomId = body.roomId;
const messageAll = body.message;
await sendToRoom(connectionId, sendAllRoomId, messageAll);
break;
default:
console.log(`Unsupported action: ${action}`);
}
return {
"isBase64Encoded": false,
"statusCode": 200,
"body": "OK",
};
};
async function saveConnection(connectionId) {
const putParams = {
TableName: "websocket-connections",
Item: {
connectionId: { S: connectionId },
},
};
const putCommand = new PutItemCommand(putParams);
await dynamodbClient.send(putCommand);
}
async function removeConnection(connectionId) {
const deleteParams = {
TableName: "websocket-connections",
Key: {
connectionId: { S: connectionId },
},
};
const deleteCommand = new DeleteItemCommand(deleteParams);
await dynamodbClient.send(deleteCommand);
}
async function removeConnectionFromRoom(connectionId, roomId) {
const getParams = {
TableName: "websocket-rooms",
Key: {
roomId: { S: roomId },
},
};
const getCommand = new GetItemCommand(getParams);
const { Item } = await dynamodbClient.send(getCommand);
if (!Item) {
console.log(`Room not found: ${roomId}`);
return
}
}
async function addConnectionToRoom(connectionId, roomId) {
const getParams = {
TableName: "websocket-rooms",
Key: {
roomId: { S: roomId },
},
};
try {
const getCommand = new GetItemCommand(getParams);
const { Item } = await dynamodbClient.send(getCommand);
if (Item) {
// Room exists, add the connection ID to the room
const updateParams = {
TableName: "websocket-rooms",
Key: {
roomId: { S: roomId },
},
UpdateExpression: "ADD connectionIds :connectionId",
ExpressionAttributeValues: {
":connectionId": { SS: [connectionId] },
},
};
const updateCommand = new UpdateItemCommand(updateParams);
await dynamodbClient.send(updateCommand);
console.log(`Added connection ${connectionId} to room ${roomId}`);
} else {
// Room does not exist, create a new room with the connection ID
const putParams = {
TableName: "websocket-rooms",
Item: {
roomId: { S: roomId },
connectionIds: { SS: [connectionId] },
},
};
const putCommand = new PutItemCommand(putParams);
await dynamodbClient.send(putCommand);
console.log(`Created room ${roomId} with connection ${connectionId}`);
}
} catch (err) {
console.error(err);
}
};
async function sendMessage(connectionId, sendConnectionId, message) {
const postParams = {
ConnectionId: sendConnectionId,
Data: message,
};
const postCommand = new PostToConnectionCommand(postParams);
await apiGatewayClient.send(postCommand);
}
async function sendToRoom(connectionId, roomId, message) {
const getParams = {
TableName: "websocket-rooms",
Key: {
roomId: { S: roomId },
},
};
const getCommand = new GetItemCommand(getParams);
const { Item } = await dynamodbClient.send(getCommand);
if (!Item) {
console.log(`Room not found: $ { roomId }`);
return;
}
const connectionIds = Item.connectionIds.SS;
const postCommands = connectionIds.map((id) => {
return {
ConnectionId: id,
Data: JSON.stringify({ roomId, message }),
};
});
const postPromises = postCommands.map((command) => {
const postCommand = new PostToConnectionCommand(command);
return apiGatewayClient.send(postCommand);
});
try {
await Promise.all(postPromises);
} catch (err) {
}
}
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"dynamodb:PutItem",
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:UpdateItem",
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:us-east-1:059035646743:d8ck9i69wa/production/*",
"arn:aws:dynamodb:us-east-1:059035646743:table/websocket-connections",
"arn:aws:dynamodb:us-east-1:059035646743:table/websocket-rooms"
]
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"execute-api:Invoke",
"execute-api:ManageConnections"
],
"Resource": "arn:aws:execute-api:us-east-1:059035646743:d8ck9i69wa/production/*"
}
]
}
import http.server
import socketserver
PORT = 80
Handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
print(f"Serving HTTP on port {PORT}")
httpd.serve_forever()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment