An HTTP server listens for client requests (usually from web browsers or API clients) and responds with appropriate data. While Python provides built-in web frameworks like Flask and Django, you can also create a custom HTTP server using Python’s socket
module.
Why Build a Custom HTTP Server?
✔ Learn how HTTP servers work internally
✔ Handle specific requirements that existing frameworks don’t support
✔ Lightweight alternative to full-fledged frameworks
1. Understanding HTTP Requests and Responses
Structure of an HTTP Request
A basic HTTP request from a client (e.g., a web browser) consists of:
GET / HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0
Accept: text/html
Structure of an HTTP Response
A basic HTTP response consists of:
HTTP/1.1 200 OK
Content-Type: text/html
<html><body><h1>Hello, World!</h1></body></html>
The response must follow HTTP standards, including status codes and headers.
2. Creating a Basic HTTP Server with socket
A simple HTTP server that listens for incoming requests and responds with a basic HTML page.
import socket
HOST = 'localhost'
PORT = 8080
# Create a socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print(f"Server running on {HOST}:{PORT}")
while True:
client_socket, addr = server_socket.accept()
print(f"Connected by {addr}")
request = client_socket.recv(1024).decode()
print(f"Request:\n{request}")
# Basic HTTP response
response = """\
HTTP/1.1 200 OK
Content-Type: text/html
<html><body><h1>Hello, World!</h1></body></html>
"""
client_socket.send(response.encode())
client_socket.close()
✔ Listens on port 8080
✔ Handles HTTP GET requests
✔ Sends a simple HTML response
3. Handling Different HTTP Methods
Let’s enhance the server to handle GET and POST requests.
import socket
def handle_request(request):
headers = request.split("\r\n")
method, path, _ = headers[0].split()
if method == "GET":
response_body = "<h1>This is a GET response</h1>"
elif method == "POST":
response_body = "<h1>This is a POST response</h1>"
else:
response_body = "<h1>Method Not Allowed</h1>"
response = f"""\
HTTP/1.1 200 OK
Content-Type: text/html
{response_body}
"""
return response.encode()
HOST, PORT = "localhost", 8080
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print(f"Server running on {HOST}:{PORT}")
while True:
client_socket, addr = server_socket.accept()
request = client_socket.recv(1024).decode()
response = handle_request(request)
client_socket.send(response)
client_socket.close()
✔ Processes GET and POST methods
✔ Returns different responses based on the request type
4. Multi-Threaded HTTP Server
To handle multiple client requests simultaneously, let’s use multi-threading.
import socket
import threading
def handle_client(client_socket):
request = client_socket.recv(1024).decode()
print(f"Request:\n{request}")
response = """\
HTTP/1.1 200 OK
Content-Type: text/html
<html><body><h1>Multi-Threaded Server</h1></body></html>
"""
client_socket.send(response.encode())
client_socket.close()
HOST, PORT = "localhost", 8080
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print(f"Server running on {HOST}:{PORT}")
while True:
client_socket, addr = server_socket.accept()
thread = threading.Thread(target=handle_client, args=(client_socket,))
thread.start()
✔ Handles multiple requests concurrently
✔ Each client request is processed in a separate thread
5. Adding Routing for Custom Endpoints
A simple router to handle different URL paths.
import socket
def handle_request(request):
headers = request.split("\r\n")
method, path, _ = headers[0].split()
if path == "/":
response_body = "<h1>Welcome to the Homepage</h1>"
elif path == "/about":
response_body = "<h1>About Page</h1>"
else:
response_body = "<h1>404 - Page Not Found</h1>"
response = f"""\
HTTP/1.1 200 OK
Content-Type: text/html
{response_body}
"""
return response.encode()
HOST, PORT = "localhost", 8080
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((HOST, PORT))
server_socket.listen(5)
print(f"Server running on {HOST}:{PORT}")
while True:
client_socket, addr = server_socket.accept()
request = client_socket.recv(1024).decode()
response = handle_request(request)
client_socket.send(response)
client_socket.close()
✔ Routes requests based on URL path
✔ Returns 404 for invalid pages
6. Using http.server
Module
Python has a built-in HTTP server module that simplifies development.
from http.server import SimpleHTTPRequestHandler, HTTPServer
class MyHandler(SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == "/":
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"<h1>Custom HTTP Server</h1>")
elif self.path == "/about":
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(b"<h1>About Page</h1>")
else:
self.send_response(404)
self.end_headers()
self.wfile.write(b"<h1>404 - Not Found</h1>")
server = HTTPServer(("localhost", 8080), MyHandler)
print("Server running on port 8080...")
server.serve_forever()
✔ Uses SimpleHTTPRequestHandler
to handle requests.
✔ Implements custom GET request handling.
7. Summary
Feature | Basic Socket Server | Multi-Threaded Server | http.server Module |
---|---|---|---|
Complexity | Low | Moderate | Very Low |
Multi-Threading | No | Yes | No |
Routing Support | Manual | Manual | Built-in |
Best For | Learning, Basics | Handling multiple clients | Quick HTTP servers |