Codects: Simple Web Server
Welcome to another installment of Codects, my weekly coding adventure where I take on new challenges to learn something cool. Last week we dove into Linux and built my own simple UNIX-based shell. This week, I decided to build something practical and foundational in terms of web development—a simple web server in Python. While there are plenty of robust servers out there, like Apache and Nginx , building your own server from scratch is a great way to understand how the web actually works, so that’s what I did.
The Starting Point #
I started off with the goal of creating a server that could handle both GET
and POST
requests. The GET
request would serve up a basic HTML page, and the POST
request would allow users to submit a form, which would then display a personalized message.
Setting Up the Server #
To begin, I used Python’s http.server
and socketserver
modules. These are built-in and make setting up a basic server incredibly straightforward. The http.server.SimpleHTTPRequestHandler
class, in particular, provides a simple way to handle HTTP requests, which is perfect for this project.
I defined a custom request handler class called MyRequestHandler
that extends SimpleHTTPRequestHandler
. This class is where the magic happens—it handles incoming requests and sends back the appropriate responses.
import http.server
import socketserver
import urllib.parse
PORT = 8000
class MyRequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self) -> None:
print(f"Received GET request for {self.path}")
if self.path == '/':
self.path = "index.html"
return super().do_GET()
def do_POST(self) -> None:
print(f"Received POST request")
content_length = int(self.headers["Content-Length"])
post_data = self.rfile.read(content_length)
post_data = urllib.parse.parse_qs(post_data.decode("utf-8"))
name = post_data.get("name", [''])[0]
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
response = f"""
<!DOCTYPE html>
<html>
<body>
<h1>Hello, {name}!</h1>
<a href="/">Go back</a>
</body>
</html>
"""
self.wfile.write(response.encode("utf-8"))
with socketserver.TCPServer(("", PORT), MyRequestHandler) as httpd:
print(f"Server started on port {str(PORT)}")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nShutting down the server...")
httpd.server_close()
Handling GET
Requests #
The first thing I needed to do was handle GET
requests. These are the requests that the browser sends when you type a URL into the address bar or click on a link. In this case, I wanted my server to respond with an HTML page when someone accessed the root directory /
.
At first, I encountered an issue where my server wasn’t serving the correct file. The problem was a simple mistake: I had accidentally used ==
instead of =
when assigning self.path
. Once I fixed that, the server correctly served up index.html
when accessed at /
.
Crafting the HTML Page #
For the HTML, I kept it simple but functional. The page included a form where users could input their name. When the form is submitted, it sends a GET
request with the user’s name as a query parameter.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Web Server</title>
</head>
<body>
<h1>Welcome to my simple web server</h1>
<p>This is a custom HTML page served by my Python web server</p>
<form>
<label for="name">Your Name:</label>
<input type="text" id="name" name="name">
<input type="submit" value="Submit">
</form>
</body>
</html>
Handling POST
Requests #
Next, I needed to handle POST
requests. These requests are typically used when you submit a form on a website. I wanted my server to capture the user’s name from the form and then display a personalized greeting.
This part was a bit trickier. I had to figure out how to read the data from the request and then parse it. Python’s urllib.parse
module came in handy here. It allowed me to easily decode the data and extract the user’s input.
After parsing the data, I created a simple HTML response that included the user’s name. This response was then sent back to the browser, where it would display the personalized message.
What’s the Result? #
After a few rounds of testing and debugging, the server was up and running! When I accessed the server and submitted my name through the form, I was greeted with a message that said, “Hello, Sami!”
This project was a great way to understand the basics of how a web server works. Even though it’s a simple implementation, it touches on some core concepts like handling HTTP requests, serving static files, and processing form data. Plus, it’s always fun to see your code in action on a web page!
The code is on GitHub , so feel free to check it out and maybe even try building on it. There’s always more to explore, like adding support for different types of content or handling more complex routing. That’s pretty much for this week edition of Codects, until next time!
Sami Elsayed is a Senior at TJHSST, and the current Lead Sysadmin at the tjCSL. He’s the Co-Founder of the Cardinal Development Organization, and the current Head Writer of “The Techbook.”