In this assignment you will build the foundation of the server that you will develop throughout the semester. All 4 homework assignments will continue to build on the same web app. The front end of the app will be provided to you, and we will release new features to the front end for each homework. You can clone the starter code for this assignment using the link below.
GitHub Repository link: https://github.com/CSE-312/CSE312-Server
Sample Deployment: https://312demo.nico.engineer/
You should never need to modify this front end. The grader in Autolab will simulate this front end code so any changes you make may affect the grading of your submissions (ie. Do not change the front end to fix your issues). Your tasks throughout the 4 homework assignments will be to build the corresponding end points on the back end that are called by the front end.
This section outlines all the requirements for the learning objective portion of this assignment. You must complete all of these requirements. The learning objective is autograded in Autolab and you may submit as many times as you need until you earn credit.
This learning objective will guide you through building the foundation of a server that understands HTTP, communicates with a database, implements a chat feature, and utilizes cookies to track user sessions.
Complete the Request class in the util package of the starter code. The constructor has been set up and contains all the instance variables that should be populated by parsing the input which will be an HTTP request in bytes. Your task is to parse the bytes and set the 'body', 'method', 'path', 'http_version', 'headers', and 'cookies' instance variables. Note that the body must be set as bytes while the other values will be set as str's (headers and cookies will map str to str). More details about each instance variable:
Something to keep in mind, for the Request, Response, and Router classes, you must not access any files or call any functions that access any files. The first round tests that we run on your classes do not have access to your folder structure, and thus will throw an error if you try to open any files.
Create a Response class in the util package of the starter code. Unlike the Request class, there are no specific instance variables that need to be set. Instead, the spec is to write specific methods (You will need instance variables to write these methods, but you may choose their names and they won't be accessed by the grader)
In the response class, implement the following methods:
You'll set up a Router class that will help you organize the code of your server throughout the semester. It is recommended that you study the provided hello path from the starter code and write at least one path (eg. hosting the HTML file at "GET /") without using the router to gain a better understanding of the structure of a server before implementing your Router class.
After Homework 1 you are welcome to make changes to the design of your router, but you are required to use a router throughout all homework assignments. The organization this router provides will prove useful as your server code gains more complexity.
Write a class named Router in a file named util/router.py with the following methods and functionality:
This is the first task where you'll start adding functionality to your server (The three classes you've written will make it easier for you develop your server throughout the semester). Review the code in server.py to see how it uses your Router and Request classes. Then, add a route to your router that will host all files in the public directory.
To accomplish this task, you should add a route that will match any path starting with "/public", then extract the file path from the rest of the path and serve that files contents in the response. For example, if you receive a request for the path "/public/imgs/dog.jpg", you should read the file "/public/imgs/dog.jpg", create a Response object, call bytes on the response and give it the contents of the file, call to_data to get the final response, then send the response using the handlers send_all method.
All files must be served with the correct MIME type. You may use the file extensions to determine these MIME types. For this HW, you only need to handle the file extensions that appear in the provided public directory. (Note: .ico is an image with MIME type "image/x-icon"). There are 7 total types you need to handle.
In addition to hosting all the files in the public directory, you will add paths for each of the pages of
the app. For each of these pages, you will need to render an HTML template (Don't panic, this comes down to
reading 2 files and doing 1 find and replace). There is an HTML template in "public/layout/layout.html".
This template contains all the structure of the app including menus, navigation, metadata, imports, etc.
This template has one placeholder that is exactly the string
Add the following special paths that require rendering HTML using layout.html as described above:
At this point, you can run your app and visit "http://localhost:8080" in your browser to see the provided front end. We will add more pages in each of the homework assignments using the same template.html.
Security: The X-Content-Type-Options: nosniff header must be set on all responses (Please double/triple check this header for the exact spelling and syntax. If you are off by 1 character, the browser will not disable MIME type sniffing which renders your header useless and can be a security issue). It is recommended that you add this header in your Response class so you'll never forget it
UTF-8: Some files contain emojis that will be displayed when the page loads. These characters must display properly.
404: If a request is received for any path that should not serve content, return a 404 response with a message saying that the content was not found. This can be a plain text message.
The front end contains a chat feature that will send specific requests to your server. To enable this feature, implement the following end points. Note that you will need to use both a database and cookies to complete all of these end points. All functionality must persist through a server restart, and you must use a MongoDB database. During grading, there will be a MongoDB database running on localhost port 27017.
To test your app with a database, you can either install MongoDB on your device, use the provided docker-compose.db-only.yml file with Docker, use Docker in the command line, or any other way you have to run MongoDB on localhost.
Creates a new chat message. The frontend will send a POST request to your backend at the route `/api/chats`. Listed below is the format expected. The entirety of the body of the request will be a JSON string
Request (JSON): {"content": string}
Response: 200 OK with a message of exactly "Great work sending a chat message!!"
You may assume the request is properly formatted
When a message is sent, you should create a unique id for the message and store it in your database along with the author of the post. See the spec for the GET request to see the expected format of a chat message.
If the same user creates multiple posts, all posts must have the same author. This should be tracked using a cookie that is set and tracked by your server. The name of this cookie must be exactly "session" and contain a unique id used to identify this user. This cookie must be set when they send their first message (ie. do not set the cookie when they first load your homepage. Some users will only use your API and not your front end)
Since we don't have user accounts yet, you can choose random author names for users for now. The names must be different for different users, but can be randomly generated. You are allowed to use the uuid package to generate ids and tokens if you'd like.
Retrieves all chat messages. The frontend will send a GET request to your backend at the route `/api/chats`. Listed below is the format expected.
Response (JSON): {"messages": [{"author": string, "id": string, "content": string, "updated": boolean}, ...]}
The `updated` parameter that is sent back in the list of messages represents if the message has ever been updated. If it has it must be set to true. When a message is first created, this should be set to false.
Updates an existing message with new content. The frontend will send a PATCH request to your backend at the route `/api/chats/{id}`. Listed below is the format expected.
Request (JSON): {"content": string}
Errors:
After a message has been updated using this endpoint, its "updated" field must be set to true
Deletes an existing message from chat history. The frontend will send a DELETE request to your backend at the route `/api/chats/{id}`. Listed below is the format expected.
Errors:
After a message is deleted by its author, it must never be sent by the GET endpoint again.
Security: You must escape any HTML in the users' messages. Since your users can submit any text they want, a malicious user could submit HTML tags that attack other users. You cannot allow this. You must escape any submitted HTML so it displays as plain text instead of being rendered by the browser.
Users can add/remove reactions to chat messages (group reactions, will display like 2 👍 1 👎)
If you click on a chat message, you will be able to choose an emoji as your reaction. You can click on the emoji to remove your reaction. To enable this feature, add/modify the following end points
Updates an existing message with the new emoji. Called when a user reacts to a message. A user reacts to the same message with multiple emojis, by hitting this end point multiple times, but they cannot react to the same message with the same emoji multiple times. If you receive a request from the same user trying to react to a message with same emoji a second time, you should respond with a 403. You may choose the message for this response
The content of the request will be JSON in the format:
Request (JSON): {"emoji": "👍"}
Emojis can be treated as single character string.
Errors:
A user is removing an emoji reaction that they previously added.
The content of the request will be JSON in the format:
Request (JSON): {"emoji": "👍"}
Errors:
Modify the chats endpoint to add the emojis to all messages. Each message will have another field for "reactions" containing a list of user ids that have reacted with that emoji. You have control over the ids as long as they are unique for each user.
For example: "reactions": {"👻": ["63fc690d-ea3a-4349-ba51-0c645af40453"], "🫠": ["eda92e0a-eb7a-430b-a938-916d2102b480", "63fc690d-ea3a-4349-ba51-0c645af40453"]}
Allow users to change their nickname and retroactively change their name in all their old messages
Changes the users nickname to the name in the body of the request. This will change the nickname on all messages sent by the user in the past and future.
The content of the request will be JSON in the format:
Request (JSON): {"nickname": string}
When chat messages are served, each message from this user will now contain a field "nickname" containing their chosen name.
Use dicebear to generate profile pictures and save them for each user. These images must be consistent for each user and users will have different images. When a user first visits your app, or when they first send a message in chat, you will generate a random profile picture for them and save this image as part of their profile. Whenever they send a chat message, that message must contain their profile picture in an "imageURL" field.
Example field to be added to chat messages - "imageURL": "/public/imgs/profile-pics/eda92e0a-eb7a-430b-a938-916d2102b480.svg"
You are allowed to use the requests library when making requests to the dicebear API. If you use requests, or any external library, be sure to add it to your requirements.txt.
Submit all files for your server to Autolab in a .zip file (A .rar or .tar file is not a .zip file!)
If you used any external libraries, be sure to add them to your requirements.txt. Autolab will install all dependencies in this file, and no other dependencies, before starting your server.
It is strongly recommended that you download and test your submission after submitting. To do this, download your zip file into a new directory, unzip your zip file, enter the directory where the files were unzipped, run docker compose up, then navigate to localhost:8080 in your browser. This simulates exactly what the TAs will do during grading.
Each objective will be scored on a 0-3 scale as follows:
3 (Complete) | Clearly correct. Following the testing procedure results in all expected behavior |
2 (Complete) | Mostly correct, but with some minor issues. Following the testing procedure does not give the exact expected results, but all features are functional |
1 (Incomplete) | Not all features outlined in this document are functional, but an honest attempt was made to complete the objective. Following the testing procedure gives an incorrect result, or no results at all, during any step. This includes issues running Docker or docker-compose even if the code for the objective is correct |
0.3 (Incomplete) | The objective would earn a 3, but a security risk was found while testing |
0.2 (Incomplete) | The objective would earn a 2, but a security risk was found while testing |
0.1 (Incomplete) | The objective would earn a 1, but a security risk was found while testing |
0 (Incomplete) | No attempt to complete the objective or violation of the assignment (Ex. Using an HTTP library) |
Note that for your final grade there is no difference between a 2 and 3, or a 0 and a 1. The numeric score is meant to give you more feedback on your work.
3 | Objective Complete |
2 | Objective Complete |
1 | Objective Not Complete |
0 | Objective Not Complete |
Autograded objectives are graded on a pass/fail basis with grades of 3.0 or 0.0.
For each objective for which you earned a 0.3 or 0.2, you will still have an opportunity to earn credit for the objective by submitting an essay about the security issue you exposed. These essays must:
Any submission that does not meet all these criteria will be rejected and your objective will remain incomplete.
Due Date: Security essays are due 1-week after grades are released.
Any essay may be subject to an interview with the course staff to verify that you understand the importance of the security issue that you exposed. If an interview is required, you will be contacted by the course staff for scheduling. Decisions of whether or not an interview is required will be made at the discretion of the course staff.
When you don't have to write an essay: