How to Create a Simple Web Server with Python using pre existing libraries

  • 2024-03-25 13:47:20.243441
  • Coding
  • 192 Hits

Ever wondered how simple it is to create a web server nowadays? Well I did and you’re about to find out too!

This is made just for fun as an experiment wouldn't use it in production, personally I'd go with Nginx!

Getting Started

First, create a Python file and insert the following code. And it's done, you already have a functional web server that listens on http://localhost:999.

from http.server import BaseHTTPRequestHandler, HTTPServer

hostName = "localhost"
serverPort = 999

class MyWebServer(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()
        self.wfile.write(bytes("<html><head><title>Martin example Web Server</title> <style>body{background-color:#1a1916;color:white;}</style> </head>", "utf-8"))
        self.wfile.write(bytes(f"<p>Request: {self.path}</p>", "utf-8"))
        self.wfile.write(bytes("<body>", "utf-8"))
        self.wfile.write(bytes("<p>Martin's example web server.</p>", "utf-8"))
        self.wfile.write(bytes("</body></html>", "utf-8"))
        
if __name__ == "__main__":
    webServer = HTTPServer((hostName, serverPort), MyWebServer)
    print(f"Server running at http://{hostName}:{serverPort}")
    try:
        webServer.serve_forever()
    except KeyboardInterrupt:
        pass
    webServer.server_close()
    print("Server stopped.")

but that's isn't enough is it?

Let's take it a step further by implementing a bit more robust webserver to handle multiple websites. In the following example the server is still running on locahost:999.

However, we’ve added a feature to capture incoming requests for specific domains through the headers.

Then simply by getting each recursive directory we are able to load an entire static website with unlimited subdirectories and images.


from http.server import BaseHTTPRequestHandler, HTTPServer

hostName = "localhost"
serverPort = 999

class MyWebServer(BaseHTTPRequestHandler):
    def do_GET(self):
        path = self.path
        
        type = MyWebServer.GetType(path)
        
        if "martinkirilov.com" in self.headers.get("Host", ""):
            root_dir = f"C:\\MartinKirilov"
                 
            self.send_response(200)
            self.send_header("Content-type", type)
            self.end_headers()     
            
            self.wfile.write(bytes(MyWebServer.GetDir(root_dir,path)))
                
        elif "mkirilov.com" in self.headers.get("Host", ""):
            # Handle requests for mkirilov.com
            self.send_response(200)
            self.send_header("Content-type", "text/plain")
            self.end_headers()
            self.wfile.write(b"Hello from mkirilov.com!")
        else:
            # Handle other requests
            self.send_response(404)
            self.send_header("Content-type", "text/plain")
            self.end_headers()
            self.wfile.write(b"Page not found")
            
    def GetType(path):
        type = ''
        if path.__contains__(".css"):
            type = "text/css"
        elif path.__contains__(".js"):
            type = "text/javascript"
        else:
            type = "text/html"
            
        return type
    
    def GetDir(root_dir, path):
        response = b''
        if path == "/":
            try:
                with open(f"{root_dir}\\index.html", "rb") as file:
                    response =  file.read()
            except:
                print("missing index") 
        else:
            try:
                file_path = f"{root_dir}"+ path.replace("/","\\")
                with open(file_path, "rb") as file:
                    response = file.read()
            except:
                print(f"An exception occurred with {path}") 
        return response


webServer = HTTPServer((hostName, serverPort), MyWebServer)
print(f"Server started at http://{hostName}:{serverPort}")
try:
    webServer.serve_forever()
except KeyboardInterrupt:
    pass
webServer.server_close()
print("Server stopped.")


Theoretical future Improvements

In theory, we could get rid of the if statements and configuring the server to read one or multiple config files simultaneously. Additionally, we could enhance performance by caching the directories in memory, thereby saving time otherwise spent on repeatedly opening them. Lastly, adding support for HTTPS and running each site on its own thread could significantly improve our web server’s performance and security.


Stay tuned for more exciting experiments and happy coding!