$30
Simplifying Assumptions
You can assume the following throughout this assignment:
● The FIN bit will always be 1 and you’ll read the entire frame in one read from your TCP socket (no buffering)
● The 3 reserved bits will always be 0
● You can ignore any frames with an opcode that is not 1
● Additional WebSocket headers are compatible with what we discussed in class (ie. You don’t have to check the Sec-WebSocket-Version header)
For this assignment, add the following HTML to the home page of your app.
<label for="chat-name"Name: </label
<input id="chat-name" type="text" name="name"
<br/
<label for="chat-comment"Comment: </label
<input id="chat-comment" type="text" name="comment"
<button onclick="sendMessage()"Chat</button
<div id="chat"</div
You may make changes to the front-end provided throughout this document as long as the expected functionality exists in your submission (ie. You can change the HTML/JS [and add CSS] as long as there’s a clear way to submit a name/comment to a chat that uses WebSockets)
Objective 1: WebSocket Handshake
Add the following line of code to the JavaScript of your home page and implement the handshake of the WebSocket protocol at the path “/websocket”.
socket = new WebSocket('ws://' + window.location.host + '/websocket');
This line will make a GET request to the path “/websocket” and attempt to upgrade the TCP socket to a WebSocket connection.
Testing Procedure
Start your server using Docker, or “docker-compose up” if a docker-compose.yml file exists in your submission
Open a browser and navigate to http://localhost:<local_port/ (<local_port will be 8080 if using docker-compose)
Open the network tab of the browser console (refresh page if necessary)
Verify that there is a successful WebConnection connection with a response code of 101
Open a second browser tab and repeat steps 2-4 to verify that the server support multiple simultaneous WebSocket connections
Objective 2: WebSocket Chat
Enable users to chat using their WebSocket connections to your server with the following front end JavaScript:
// Establish a WebSocket connection with the server
const socket = new WebSocket('ws://' + window.location.host + '/websocket');
// Call the addMessage function whenever data is received from the server over the WebSocket
socket.onmessage = addMessage;
// Allow users to send messages by pressing enter instead of clicking the Send button
document.addEventListener("keypress", function (event) {
if (event.code === "Enter") {
sendMessage();
}
});
// Read the name/comment the user is sending to chat and send it to the server over the WebSocket as a JSON string
// Called whenever the user clicks the Send button or pressed enter
function sendMessage() {
const chatName = document.getElementById("chat-name").value;
const chatBox = document.getElementById("chat-comment");
const comment = chatBox.value;
chatBox.value = "";
chatBox.focus();
if(comment !== "") {
socket.send(JSON.stringify({'username': chatName, 'comment': comment}));
}
}
// Called when the server sends a new message over the WebSocket and renders that message so the user can read it
function addMessage(message) {
const chatMessage = JSON.parse(message.data);
let chat = document.getElementById('chat');
chat.innerHTML += "<b" + chatMessage['username'] + "</b: " + chatMessage["comment"] + "<br/";
}
Note: As always, feel free to modify this code to fit the needs of your app as long as you still send at least two pieces of information to the server that have been imputed by the user. For example, this code sends the data as a JSON String representing an object with keys “username” and “comment”. You may use a different format for the data that is sent to/received from the server.
For this objective you will parse WebSocket frames that are received from any open WebSocket connection, parse the bits of the frame to read the payload, then send a WebSocket frame to all connected WebSocket clients containing the new message.
Security: You must escape any HTML in the user’s submissions. Be aware that escaping HTML will change the length of your payload!
Suggestion: There are a few separate steps involved in this objective. It will help your debugging process if you implement and test this functionality 1 step at a time. For example, make sure you can read and parse frames properly before trying to send frames and try sending a frame only to the user sending a message before broadcasting the message to all users.
Testing Procedure
Start your server using Docker, or “docker-compose up” if a docker-compose.yml file exists in your submission
Open a browser and navigate to http://localhost:<local_port/ (<local_port will be 8080 if using docker-compose)
Open at least 2 browser tabs and enter chat messages is each. Ensure that at least one message has a payload length <126 and at least one has payload length =126 (payloads over 1024 bytes will not be tested)
Verify that each user can see both their own messages and messages sent by other users in real time
Verify that the messages have been sent/received using a WebSocket connection
Security: Verify that the submitted HTML displays as text and is not rendered as HTML
Objective 3: Chat History
When a user first establishes a WebSocket connect, send them all of the chat history over the new WebSocket connection. For this objective you may use data structures to store the chat history. You can/should send each message in its own WebSocket frame.
Testing Procedure
Start your server using Docker, or “docker-compose up” if a docker-compose.yml file exists in your submission
Open a browser and navigate to http://localhost:<local_port/ (<local_port will be 8080 if using docker-compose)
Send several messages to the chat
Open a second browser tab
Verify that the previously submitted message appear when the page loads
Verify that the messages were received via a WebSocket connection
Objective 4: Persistent Chat History with a Database
Add a docker-compose.yml file that runs a database and maps local port 8080 to your app. Use this database to store all of the chat history for your app. This will allow the chat history to persist after a server restart.
Testing Procedure
Start your server using “docker-compose up”
Open a browser and navigate to http://localhost:8080
Send several messages to the chat
Stop the server using “docker-compose down”
Start your server using “docker-compose up”
Open a browser and navigate to http://localhost:8080
Verify that the messages sent in step 3 are visible on the page
Check the code the ensure that a database is being used via docker-compose (ie. File IO, SQLite, etc cannot be used for this objective)
Bonus Objective: Images in Chat
Add an option to send an image to the chat using WebSockets. You have much freedom in how you design your app and code for this objective and it will require modifying the front end as well as the server.
Testing Procedure
Start your server using “docker-compose up”
Open two browser tabs and navigate to http://localhost:8080 on each
If there is an option to upload a file, upload a jpg image to the chat in one tab
Verify that the image appears in the other tab
Verify that all most communication after the page load occurs over WebSockets (The image should be served via an HTTP request after the socket sends a chat message containing an unescaped img element. All other communication after the page load must be over WebSockets including the image upload. You must maintain control of the img elements to ensure attackers cannot inject HTML into your app)
Warning: This bonus is very difficult and will require several concepts that were not covered in lecture (eg. how to send a file without a POST request from a form; how to send binary data over a WebSocket). You should only attempt this objective if you are confident in all the material of the course and are looking for a difficult challenge.