-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
277: Building a Minimal HTTP Server with http.server
Whenever I need to move a few files between machines on a local network or quickly test a frontend build, my instinct is to reach for the one-liner: python -m http.server. It's the "quick and dirty" gold standard. But there's a wall you hit very quickly with that approach. The moment you need the server to actually do something—like check a database, return a specific JSON response, or handle a POST request—that one-liner becomes useless. You can't inject logic into a command-line flag.
The convenience of the one-liner
If you've used the module from the terminal, you know it just serves the current directory. It's essentially a read-only file explorer. If you try to replicate this in code, you might be tempted to just instantiate HTTPServer with SimpleHTTPRequestHandler. It looks clean, and it works for static files, but you're essentially building a black box. You have no control over the headers, you can't route requests to different functions, and you certainly can't implement any business logic. For a developer, that's a dead end. You're relying on the default behavior of the library rather than defining the behavior of your application.
Adding intelligence via the Handler
The better way—and the way I'd expect to see in a professional internal tool—is to subclass BaseHTTPRequestHandler. This is where you actually get to define how the server responds to the world. Instead of just handing over a file from a folder, you override the do_GET or do_POST methods. I find this approach far more satisfying because it mirrors how actual web frameworks work, just without the thousand dependencies.
Take a look at how we can turn a dumb file server into a simple API that reports a system status. By overriding do_GET, we can inspect the self.path variable to decide what to send back. I usually suggest sending a 200 OK status and setting the Content-type to application/json, because almost everything in the modern ecosystem expects JSON, not plain text.
import http.server
import socketserver
import json
class StatusHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/status':
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
# Imagine this comes from a real system check
response = {"status": "online", "version": "1.0.4", "load": "low"}
self.wfile.write(json.dumps(response).encode('utf-8'))
else:
self.send_error(404, "Path not found")
PORT = 8000
with socketserver.TCPServer(("", PORT), StatusHandler) as httpd:
print(f"Serving status API at port {PORT}")
httpd.serve_forever()
Where this breaks under pressure
Now, I have to give you a warning: do not put this in production. I've seen junior devs try to use http.server for a lightweight public API because "it has no dependencies." That is a recipe for a disaster. This server is single-threaded. If one request takes five seconds to process, every other user is stuck waiting in line. It's also not hardened against security vulnerabilities like directory traversal or denial-of-service attacks.
The trade-off here is simplicity versus robustness. You use http.server when you need a tool that's "good enough" for a local environment, a mock server for a unit test, or a quick prototype that only you will be hitting. When you need concurrency, middleware, or security, that's when you move to something like FastAPI or Flask. Use the right tool for the scale of the problem.
📋 Practical Task
Build a Custom JSON Greeting Server
Your task is to create a minimal HTTP server using BaseHTTPRequestHandler that acts as a personalized greeting service. The server should handle two specific GET routes:
/greet: Should return a JSON object{"message": "Hello, guest!"}with a 200 OK status./time: Should return a JSON object containing the current server time (use thedatetimemodule) with a 200 OK status.- Any other path: Should return a 404 error.
Ensure you set the Content-type header to application/json for the successful responses and that the server runs on port 8080.
There are no comments for now.