Skip to main content

Tiny lightweight, flexible HTTP framework.

Project description

PyMicroHTTP

PyMicroHTTP is a lightweight, flexible HTTP framework built from scratch in Python. It provides a simple way to create HTTP services without heavy external dependencies, making it ideal for learning purposes or small projects.

NOTE: this is a toy project and not production ready.

Content Table

PyMicroHTTP

Features

  • Built on raw TCP sockets
  • Routing with HTTP verb and path matching
  • Middleware support with easy chaining
  • JSON response handling
  • Zero external dependencies

Installation

You can install the package via pip:

$ pip install pymicrohttp

Quick Start

Here's a simple example to get you started:

from pymicrohttp.server import Server

s = Server()

@s.register('GET /hello')
def hello(request):
    return {"message": "Hello, World!"}

@s.register('GET /hello/:name')
def hello_name(request):
    name = request['params'].get('name')
    return {"message": f"Hello, {name}!"}


if __name__ == "__main__":
    s.start_server(port=8080)

Run this script, and you'll have a server running on http://localhost:8080. Access it with:

curl http://localhost:8080/hello

Routing

Routes are defined using the @s.register decorator:

@s.register('GET /ping')
def ping_handler(request):
    return "pong"

Following this syntax:

VERB /<PATH>

Supported verbs:

  • *: to match any verb
  • GET
  • POST
  • PUT
  • PATCH
  • DELETE
  • HEAD
  • OPTIONS

With a signle space separating between the verb and the request path.

Example:

@s.register('POST /login')
def login_handler(request):
    try:
        body = json.loads(request['body'])
        if 'username' not in body or 'password' not in body:
            # do somthing
    except:
        return { 'error': 'invalid data' }

Path parameters

You can declare dynamic path params using a colon, for example:

GET /users/:group/:channel

To read these params you can access them via the request object:

@s.register('GET /users/:group/:channel')
def handler(request):
    ...
    group = request['params']['group']
    channel = request['params']['channel']
    ...

Query parameters

You can read query parameters via the request obejct:

@s.register('GET /products')
def handler(request):
    ...
    name = request['query'].get('name', '')
    category = request['query'].get('category', 'shoes')
    ...

Note that it is better to use .get(key, default_value) because query params are optional and may not exist, and accessing them without the .get() method may result in key errors.

Request Object

The request object is a dict containing these key and value:

{
    'verb':    ...
    'path':    ...
    'body':    ...
    'headers': ... # { 'key': 'value' }
    'params':  ... # { 'key': 'value' }
    'query':   ... # { 'key': 'value' }
}

You can access it via the handler:

@s.register('* /ping')
def ping_handler(request):
    # accessing request headers
    if 'double' in request['headers']:
        return "pong-pong"
    return "pong"

Examples:

  1. Accessing headers:

    # say hello
    s.register('GET /hello/:name')
    def hello(request):
        name = request['params']['name']
        return "Hello " + name
    
  2. Accessing dynamic path params:

    # say hello `n` times
    s.register('GET /hello/:name/:n')
    def hello(request):
        name, n = request['params']['name'], request['params']['n']
        return "Hello " * int(n) + name
    
  3. Accessing query params:

    # say hello `n` times
    # read n from query params
    # with default value of 3
    s.register('GET /hello/:name')
    def hello(request):
        name = request['params']['name']
        n = request['query'].get('n', 3)
        return "Hello " * n + name
    

Response Handling

The framework supports different types of responses:

  1. Dictionary (automatically converted to JSON):

    return {"key": "value"}
    
  2. String:

    return "Hello, World!"
    
  3. Tuple for custom status codes and headers:

    return "Not Found", 404
    # or
    return "Created", 201, {"Location": "/resource/1"}
    

Middleware

Middleware functions can be used to add functionality to your routes:

def log_middleware(next):
    def handler(request):
        print(f"Request: {request['verb']} {request['path']}")
        return next(request)
    return handler

@s.register('GET /logged', log_middleware)
def logged_route(request):
    return {"message": "This is a logged route"}

Before all

If you want to run a middleware before every single request you can use the s.beforeAll() decorator:

@s.beforeAll()
def logger(next):
    def handler(request):
        verb, path = request['verb'], request['path']
        print(f'{datetime.datetime.now()} {verb} {path}')
        return next(request)
    return handler

Middleware chaining

You can chain multiple middlwares together

def log_middleware(next):
    def handler(request):
        # do your logging logic here
        return next(request)
    return handler

def auth_middleware(next):
    def handler(request):
        # do your auth logic here
        return next(request)
    return handler

@s.register('GET /protected', [log_middleware, auth_middleware])
def protected_route(request):
    return {"message": "This is a protected route"}

Running the Server

To run the server:

if __name__ == "__main__":
    s = Server()
    # Register your routes here
    s.start_server(port=8080)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pymicrohttp-0.1.3.tar.gz (6.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pymicrohttp-0.1.3-py3-none-any.whl (7.5 kB view details)

Uploaded Python 3

File details

Details for the file pymicrohttp-0.1.3.tar.gz.

File metadata

  • Download URL: pymicrohttp-0.1.3.tar.gz
  • Upload date:
  • Size: 6.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.11.4

File hashes

Hashes for pymicrohttp-0.1.3.tar.gz
Algorithm Hash digest
SHA256 d7292f554ae0d020f01426c3a11814d6d4ea14d6d2613e17c32e3165d18a993f
MD5 2a75be5515c2771d93664428647341d3
BLAKE2b-256 ca0552142de6dc1898ad25602adb3e36f1ba0704108232d9ef860401ce524df3

See more details on using hashes here.

File details

Details for the file pymicrohttp-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: pymicrohttp-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 7.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.11.4

File hashes

Hashes for pymicrohttp-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 98098ab954cd901f86a044fd9f266e869ac67fa13e563a1feced1ea4c8f271ae
MD5 1043b88b46222752ab759f2e7b425eb1
BLAKE2b-256 a40ecd533107aa518f0869e63b66d16f66e3f43dc5d490d7a249271b154c9c5e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page