Creating a Simple HTTP Server using Python

Creating a Simple HTTP Server using Python

Introduction

Do you know we can create a web application using core Python without using any of the most popular frameworks in Python like Django, Flask, etc?

We can simply use Python built-in module called http.server that handles different types of HTTP methods like GET, POST, HEAD, and OPTIONS.

Here in this blog post, we create a simple HTML form that takes user inputs as POST requests and displays that saved records from GET requests.

For saving the user inputs we will use simple database SQLite3 which is supported by default in Python.

Implementation

Now let’s start the actual implementation by creating a new directory called Python Server and I assumed that you have already installed Python in your local machine.

$ mkdir Python_Server
$ cd Python_Server/ 
/Python_Server$ code .

Note I am using VS Code as an editor and Linux OS.

Create directories inside the Python_Server directory to structure the code in understandable format by dividing separate sections for HTML templates and database.

/Python_Server$ mkdir database
/Python_Server$ mkdir templates

The file structure for this project looks like this:

.
├── database
│   ├── database.py
│   └── user_records.db
├── templates
│   ├── form.html
│   └── show_records.html
└── web_server.py

Now create two HTML templates: form.html and show_records.html inside the templates directory.

form.html

<!DOCTYPE html>
<html>

<body>

    <h2>Python HTTP Server Form</h2>

    <form method="POST" , enctype="multipart/form-data" action='/success'>
        <label for="full_name">Full Name:</label><br>
        <input type="text" name="full_name" value="John Doe"><br>
        <label for="country">Country:</label><br>
        <input type="text" name="country" value="XYZ"><br><br>
        <input type="submit" value="Submit">
    </form>

</body>

</html>

show_records.html

<!DOCTYPE html>
<html>

<head>
    <style>
        table {
            font-family: arial, sans-serif;
            border-collapse: collapse;
            width: 100%;
        }

        td,
        th {
            border: 1px solid #dddddd;
            text-align: left;
            padding: 8px;
        }

        tr:nth-child(even) {
            background-color: #dddddd;
        }
    </style>
</head>

<body>

    <h2>Python HTTP Server (Records in sqlite3 database)</h2>

    <table>
        <tr>
            <th>Name</th>
            <th>Country</th>
        </tr>
        {{user_records}}
    </table>

</body>

</html>

 

Creating a sqlite3 database in Python is easy, we don’t have to install any third-party dependencies. Simply create a new python file called database.py inside the database directory as:

database.py

from sqlite3 import connect
from sqlite3.dbapi2 import Cursor

DB_NAME = "database/user_records.db"  # database name


# create database inside database folder if not exists
connection = connect(DB_NAME)

cursor = connection.cursor()


def create_table():
    """function to create table inside database"""
    # create table user inside database if not exists
    table_script = '''CREATE TABLE IF NOT EXISTS User(
                    full_name VARCHAR(255),
                    country VARCHAR(150)
                );
                '''
    cursor.executescript(table_script)
    connection.commit()


def insert_record(fullname, country):
    """function to insert record inside table"""
    cursor.execute("INSERT INTO User(full_name, country) VALUES(?, ?)",
                   (fullname, country))
    connection.commit()


def fetch_records():
    """function to fetch User records"""
    data = cursor.execute("SELECT * FROM User")
    return data

 

Up to this point, we created HTML templates and set up a database according to our requirements with two fields to store i.e full_name and country.

Now let’s import database.py module functions into a new python file called web_server.py that handles GET and POST requests respectively.

web_server.py

import sys
import cgi
from http.server import HTTPServer, SimpleHTTPRequestHandler
from database.database import create_table, insert_record, fetch_records

HOST_NAME = "localhost"
PORT = 8080


def read_html_template(path):
    """function to read HTML file"""
    try:
        with open(path) as f:
            file = f.read()
    except Exception as e:
        file = e
    return file


def show_records(self):
    """function to show records in template"""
    file = read_html_template(self.path)

    # fetch records from database
    table_data = fetch_records()

    table_row = ""
    for data in table_data:
        table_row += "<tr>"
        for item in data:
            table_row += "<td>"
            table_row += item
            table_row += "</td>"
        table_row += "</tr>"
    # replace {{user_records}} in template by table_row
    file = file.replace("{{user_records}}", table_row)
    self.send_response(200, "OK")
    self.end_headers()
    self.wfile.write(bytes(file, "utf-8"))


class PythonServer(SimpleHTTPRequestHandler):
    """Python HTTP Server that handles GET and POST requests"""

    def do_GET(self):
        if self.path == '/':
            self.path = './templates/form.html'
            file = read_html_template(self.path)
            self.send_response(200, "OK")
            self.end_headers()
            self.wfile.write(bytes(file, "utf-8"))

        if self.path == '/show_records':
            self.path = './templates/show_records.html'

            # call show_records function to show users entered
            show_records(self)

    def do_POST(self):
        if self.path == '/success':

            ctype, pdict = cgi.parse_header(self.headers.get('content-type'))
            pdict['boundary'] = bytes(pdict['boundary'], 'utf-8')

            if ctype == 'multipart/form-data':
                fields = cgi.parse_multipart(self.rfile, pdict)
                full_name = fields.get("full_name")[0]
                country = fields.get("country")[0]

                # create table User if it runs first time else not
                create_table()

                # insert record into User table
                insert_record(full_name, country)

                html = f"<html><head></head><body><h1>Form data successfully recorded!!!</h1></body></html>"

                self.send_response(200, "OK")
                self.end_headers()
                self.wfile.write(bytes(html, "utf-8"))


if __name__ == "__main__":
    server = HTTPServer((HOST_NAME, PORT), PythonServer)
    print(f"Server started http://{HOST_NAME}:{PORT}")

    try:
        server.serve_forever()
    except KeyboardInterrupt:
        server.server_close()
        print("Server stopped successfully")
        sys.exit(0)

 

Run the Python server by running web_server.py script as:

/Python_Server$ python3 web_server.py 

When you execute, the prompt message shows in the terminal as:

Server started http://localhost:8080

Go to your browser and open the prompted URL and you will see the form appear when you hit that URL as:

form_python_server

Add some data into the form and see the records that you have entered in the URL: http://localhost:8080/show_records as:

show_records_of_db

 

Conclusion

Hence, we successfully created a simple HTTP server using core Python that handles form data through POST method and the data into sqlite3 database. We also display all the stored data into the /show_records URL.

Note: http.server python method is not recommended for production use cases as it only implements basic security checks.

If you have any questions regarding this post, feel free to drop a message in the comment section.

For the code of this project, check out the Github repo.

Reference: Python http.server

Leave a Reply