WebSockets 代写案例
At this point, you have built a dynamic web application where users can login and chat with each other using only a TCP socket, your understanding of web protocols, and your programming skills. This is a great accomplishment, but let's take this further and allow users to upload images and interact in real time.
Several changes have been made to the front end in the starter code repository. You should pull these changes, or manually integrate them into your front end if you've customized your app. These changes will be referenced throughout this assignment.
Learning Objective 1: Image Uploads
Allow authenticated users to upload an image as their profile picture. The uploaded image for a user will replace the eagle image on the page.
A third HTML form has been added to the front end below the registration and login forms. This form is designed to send an image to the path "/profile-pic" using the multipart/form-data encoding. Your task is:
1. Add an endpoint to your server that listens for requests at the path "/profile-pic"
2. Process the request based on whether or not the user is authenticated
a. If the request is from an authenticated user (based on a valid auth token):
i. Parse the body of the request and extract the bytes of the image
ii. Save this image as a file on your server
iii. Store the filename of this image in your database as part of this user's profile
b. If the request is from an unauthenticated user, do not process the request and move on to the next step
3. Respond with a 302 redirect to your home page "/"
a. If the user was authenticated, their profile picture should now be displayed on the home page
When a user makes a request for your homepage, use HTML templating to send a page containing their profile image where the eagle originally appeared. If the user is not logged in, or if they have not yet uploaded a profile picture, you should display a default image (or no image) in this place on the page.
When an image is uploaded, your server will save the image as a file. It is recommended that you devise a naming convention for the image files instead of using the names submitted by your users. Naming the images "image1.jpg", "image2.jpg", "image3.jpg", etc is fine. Alternatively, since there is at most 1 image per user, you can name them using the username for that image.
It is ok if your site only handles .jpg images and assumes that every file upload is a .jpg file.
Your uploads must persist through a server restart. You should store your images in files (It's generally bad practice to store large files in a database), and store the filenames in your database. Since your images are stored in files, they will already persist through a restart.
Note: You may need to set up your buffer to complete this objective depending on which browser/version used during testing. Some browsers (Chrome) will send the headers of an HTTP request before sending the body so you will only read the headers the first time you read from the TCP socket. You need to read again to receive the bytes of the image. You can start by testing with very small images to limit the amount of buffering to at most 2 reads from the socket in this objective. Later in this assignment, you will expand this to arbitrarily large files. A very compressed image "elephant-small.jpg" was added to the repo and can be used for testing.
Security: Don't allow the user to request arbitrary files on your server machine. Starting with this objective, you will be hosting content at paths that cannot be hardcoded since you don’t know what images will be uploaded to your site. Even if you replace the file names with your own naming convention (eg. "image1.jpg" "image2.jpg") you still don't know how many images will be uploaded. This means that you must accept some variable from the user that you will use to read, and send, a file from your server. You must ensure that an attacker cannot use this variable to access files that you don’t want them to access. (In this course, it is sufficient to not allow any '/' characters in the file path. Eg. remove any "/" characters from the requested filename after extracting it from the path)
[Optional] Add the profile picture of the user who posted the message next to each message in the chat.
Testing Procedure
1. Start your server using docker compose up
2. Open a browser (Only use Firefox) and navigate to http://localhost:8080/
3. Use the image upload form to upload an image that is smaller than 1kb (Without logging in)
a. Verify that you are taken to the homepage through a redirect and that the default image is still displayed
4. Register an account and login
5. Use the image upload form to upload an image that is smaller than 1kb
a. Verify that you are taken to the homepage through a redirect and that the uploaded image is displayed
6. Open a second Firefox browser in incognito mode
7. Navigate to http://localhost:8080/ in the second browser
a. Verify that the default image appears
8. Register a second account and login
a. Verify that the default image still appears
9. Upload a second image that is smaller than 1kb
a. Verify that you are taken to the homepage through a redirect and that the uploaded image is displayed
10. Go back to the first browser and refresh the page
a. Verify that first uploaded image still appears
11. Restart the server using docker compose restart
12. Refresh both browsers and verify that both images appear as expected for each user
13. Security: Verify that '/' characters are not allowed in the requested filename (Using Postman or curl), or look through the code to ensure that this attack is addressed
14. Security: Look through the code to verify that prepared statements are being used to protect against SQL injection attacks [If SQL is being used]
Learning Objective 2: Live Chat With WebSockets
You can make the following simplifying assumptions when working with WebSockets in this assignment:
● The 3 reserved bits will always be 0
● You can ignore any frames with an opcode that is not bx0001, bx1000, or bx0000
● Additional WebSocket headers are compatible with what we discussed in class (ie. You don’t have to check the Sec-WebSocket-Version header)
In this objective, you will modify the chat feature to use WebSockets instead of HTTP, AJAX, and polling. This will make the chat "live" in that each user will receive new messages immediately [minus network delays] instead of waiting for the next poll request.
You should use the updated front end for this objective and change the "ws" variable in functions.js to true. This will change the frontend to use WebSockets when sending and receiving chat messages. It will still use your "/chat-history" path to retrieve old messages when the page loads, but will receive new messages over a WebSocket connection.
WebSocket Handshake
Implement the handshake of the WebSocket protocol at the path "/websocket".
const socket = new WebSocket('ws://' + window.location.host + '/websocket'); |
This line, which is in the provided JavaScript, will make a GET request to the path "/websocket" and attempt to upgrade the TCP socket to a WebSocket connection.
During this connection process, you must authenticate the user based on their authentication token in their cookies. This is your only chance to authenticate the WebSocket connection so it must be done during this handshake. For the duration of the connection, you can assume any WS frame sent over the connection is authenticated as this user. If they cannot be authenticated, you should still proceed with the connection as a guest user.
You may use libraries to compute the SHA1 hash and Base64 encoding.
WebSocket Frames
The provided JavaScript and HTML will send WebSocket frames containing chat messages when a user submits text to the chat. The payload of each frame will be a JSON string in the format:
{
'messageType': 'chatMessage',
'message': message_submitted_by_user
}
Please note the messageType as your server will handle 4 different messageTypes by the end of this assignment. You should check the messageType and run different code for each different type.
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, including the sender, containing the new message. The message sent by your server must be in the format:
{
'messageType': 'chatMessage',
'username': username_of_the_sender,
'message': html_escaped_message_submitted_by_user,
'id': id_of_the_message
}
For the username, use the username that you authenticated during the WS handshake. If they are not authenticated, set their username to "Guest".
When a frame with an opcode of bx1000 (disconnect) is received, the connection should be severed and removed from any storage on your server. When new messages are received, any disconnected WebSocket connections should not be sent the new message (ie. Disconnects must be handled gracefully).
Security: Don't forget to escape the HTML in your users' comments.
For this objective, it is ok if your server only handles frames with fewer than 126 bytes of payload.
Database: Use your database to store all of the chat history for your app. This will allow the chat history to persist after a server restart.
A GET request is sent to /chat-history when the page loads to request this content and render it to the page. Once you build the /chat-history endpoint properly, you will see the message history appear when the page loads.
Suggestion: There are a few separate steps involved in this objective. It will help your debugging process if you implement and test this functionality one 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 messages to all users.
Testing Procedure
1. Start your server using docker compose up
2. Open a browser and navigate to http://localhost:8080/
3. Open the network tab of the browser console (refresh the page if necessary)
4. Verify that there is a successful WebSocket connection with a response code of 101
5. Open a second browser (Use Chrome and Firefox) and repeat steps 2-4 to verify that the server supports multiple simultaneous WebSocket connections
6. Register accounts and login in both browsers
7. Enter chat several messages with < 50 characters in each browser
a. Verify that each user can see both their own messages, and messages sent by other users in real time
b. Verify that the correct usernames show up with each message
c. Verify that the messages were sent using WebSockets by checking the messages tab of the 101 request
8. Close one browser, then send a message with < 50 characters in the other
a. Verify that the app is still functional and that no errors appeared in the docker compose output
9. Restart the server using docker compose restart
10. Open a new browser tab and navigate to http://localhost:8080/
11. Verify that all messages are visible on the page (chat-history)
12. Refresh the first 2 tabs/browsers and verify the chat history appears as expected
13. Send a message with < 50 characters in the new tab
a. Verify that it appears in all 3 tabs/browsers with a username of "Guest"
14. Send a message with < 50 characters from the first browser/tab
a. Verify that it appears in all 3 tabs/browsers with the authenticated username (ie. Auth tokens persist through a server restart)
15. Security: Verify that the submitted HTML displays as text and is not rendered as HTML
Learning Objective 3: Buffering
Add buffering to both image uploads and WebSocket frames to enable arbitrarily large images and chat messages.
Images: Add buffering to your HTTP requests. Read the content length of the request and buffer until you read the whole body. Your buffering should be able to handle arbitrarily large files. You must use proper buffering for this. Do not increase the TCP buffer size by passing a large int to the recv method.
WebSocket Frames: Add the following features to your WebSocket connections:
● Handle messages of arbitrary size. This includes both messages where the 7-bit payload is set to 126 and 127. We will test with frames larger than 65536 bytes to ensure you handle all three cases. We will not test with payloads > 131000, as chrome sends messages over this size in multiple frames. For an optional challenge, you can handle this case by checking the FIN bit and combining multiple frames for a single message
○ Note: You will need to buffer your WebSocket frames. You should read from the TCP connection once, parse only the headers of the frame, parse the payload length, then check if you've read that many bytes of payload. If not, you must buffer before de-masking and reading the payload.
● Your server must also handle multiple large messages sent back-to-back. If another frame is sent while you are buffering, your last read from the TCP socket may contain the beginning of the next frame. Ensure that you are properly parsing these frames even when one read from the socket contains parts of multiple frames
Testing Procedure
1. Images:
a. Follow the testing procedures of Learning Objective 1 with .jpg images large enough to overflow your TCP buffer
i. TA Note: Do not test with images containing the byte sequences "\r\n" or "\r\n\r\n" this semester (They should still handle these, but since this is an LO instead of an AO this semester it's not required)
b. Check the submitted code to ensure a very large TCP buffer was not used
2. WebSockets:
a. Open at least 3 browser tabs and navigate to http://localhost:8080/ in each
b. Enter chat messages of varying size. Ensure that at least one message has a payload length >=126 but <=65536, at least one has payload length >65536 but <131000
c. Verify that each user can see both their own messages and messages sent by other users in real time
d. Verify that the messages have been sent/received using a WebSocket connection
e. Modify the JavaScript on at least 1 tab to send messages 2 times (Your server should handle any number of times and you should test for that, but for grading we will only send 2) when the user sends a message (These messages will be sent back-to-back such that the second message may be partially read when reading the first message from the TCP socket)
f. Send several messages from the modified tab and verify that all users see all the sent messages twice
g. Security: Verify that the submitted HTML displays as text and is not rendered as HTML
Application Objective: Video Chat Over WebRTC
For this objective, make sure you have all the updated front end code including the webrtc.js file.
Add features to your server so it can function as a WebRTC signaling server for your users. This signaling will occur over your WebSocket connection. There are three different types of messages used to establish WebRTC connections:
{'messageType': 'webRTC-offer', 'offer': offer}
{'messageType': 'webRTC-answer', 'answer': answer}
{'messageType': 'webRTC-candidate', 'candidate': candidate}
Where offer, answer, and candidate are all generated by the WebRTC code built into your browser. Your task is to forward these messages to the other user via their WebSocket connection. To avoid overcomplicating this objective, you may assume that there are exactly 2 WebSocket connections when receiving WebRTC messages. This means that when a message is received on one WebSocket connection, you will send the message to the only other WebSocket connection.
Remember, your server is merely acting as a means for the 2 clients to communicate while they establish a peer-to-peer connection. The content of the messages you handle do not need to be parsed or processed. Your task is to extract the payload of these WebSocket messages, verify that they are not chat messages (and are WebRTC messages), then send the payload to the other WebSocket connection. The clients will do the rest through the front end.
Testing Procedure
1. Start your server using docker compose up
2. Open exactly 2 browser tabs (The same browser will be used for both clients which is either Chrome or Firefox) and navigate to http://localhost:8080/ on each
3. In each tab:
a. Allow the camera/mic to be accessed
b. If the local video has not started on the page load, click the "Start My Video" button
4. In one of the 2 tabs, click the "Start Video Chat" button
5. Verify that 2 videos are show in both of the tabs
a. Both videos will be showing the same feed, but the remote video should be slightly behind the local video which is evidence that there is a video streaming connection between the two tabs. You'll also hear some horrible sounding audio with feedback. Don't worry, that means it's working 🙂
6. Shut down the server by pressing ctrl+c in the terminal running docker compose (or stop the containers using Docker desktop
7. Verify that the video streams are not interrupted. After the signaling server is used to establish the connection, this is a true peer-to-peer stream
Application Objective: HTTPS
Setup your app to listen to HTTP requests on port 80 and HTTPS requests on port 443 using a self-signed certificate.
It is recommended that you use nginx as a reverse proxy that will listen on both ports and forward traffic to your app container.
Testing Procedure
Start your server with docker compose up
Open a browser and navigate to http://localhost
Verify that the home page loads
Open a browser and navigate to https://localhost
Verify that the home page loads. Accept any warning about a self-signed certificate
咨询 Alpha 小助手,获取更多课业帮助