Last active
April 18, 2023 22:58
-
-
Save hugotkk/ce89d68704cf66a6338087f4352fb450 to your computer and use it in GitHub Desktop.
Lab - WebSocket Gateway
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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/*" | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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