Skip to content

wku/fpy3

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FPY3: High-Performance HTTP/3 Server

fpy3 is a high-performance asynchronous HTTP/3 (QUIC) server for Python 3.12+, built on MsQuic, nghttp3 and uvloop.

Features

  • HTTP/3 and QUIC - native support via MsQuic and nghttp3
  • HTTP/1.1 Fallback - automatic support for older browsers
  • Alt-Svc Discovery - automatic upgrade to HTTP/3
  • ASGI 3.0 - full compatibility via ASGIServer
  • High Performance - critical paths implemented in C

Installation

Requirements

  • Python 3.12+
  • GCC/Clang, CMake, Ninja, Autotools, pkg-config
  • OpenSSL dev headers

Local Build

# 1. Build dependencies (MsQuic, nghttp3)
./scripts/build_deps.sh

# 2. Install package
pip install .

Libraries libmsquic.so.2 and libnghttp3.so.9 are bundled into the package automatically.

Docker

cd examples/docker_https
# Generate certificates (see "Certificates" section)
docker-compose up --build

Certificates

TLS certificates are required for HTTPS/QUIC.

Local Development (mkcert)

# Install mkcert (Ubuntu/Debian)
sudo apt install libnss3-tools
curl -JLO "https://dl.filippo.io/mkcert/latest?for=linux/amd64"
chmod +x mkcert-v*-linux-amd64
sudo mv mkcert-v*-linux-amd64 /usr/local/bin/mkcert

# Create local CA
mkcert -install

# Generate certificates
mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1

Production (Let's Encrypt)

Use certbot or an ACME client to obtain real certificates.

Usage

Quick Start (ASGI)

File hello_world.py:

import asyncio
import uvloop
from fpy3.asgi import ASGIServer

async def app(scope, receive, send):
    if scope['type'] == 'http':
        body = f"Hello World from FPY over HTTP/{scope['http_version']}!".encode()
        await send({
            'type': 'http.response.start',
            'status': 200,
            'headers': [(b'content-type', b'text/plain'), (b'server', b'fpy3-asgi')]
        })
        await send({
            'type': 'http.response.body',
            'body': body
        })

async def main():
    server = ASGIServer(app, debug=True)
    server.start("0.0.0.0", 8080)
    await asyncio.Event().wait()

if __name__ == "__main__":
    uvloop.install()
    asyncio.run(main())

Running

export LD_LIBRARY_PATH=$(pwd)/vendor/dist/lib
python3.12 hello_world.py --debug --host 0.0.0.0 --port 8080

Testing

HTTP/1.1 (curl)

# Check content
curl -k https://127.0.0.1:8080/

# Check headers (including Alt-Svc)
curl -k -I https://127.0.0.1:8080/

Expected output:

HTTP/1.1 200 OK
Alt-Svc: h3=":8080"; ma=3600
Content-Length: 40
content-type: text/plain

HTTP/3 (Python client)

export LD_LIBRARY_PATH=$(pwd)/vendor/dist/lib
python3.12 test_http3_client.py

HTTP/3 (Chrome with forced QUIC)

# Close all Chrome windows
pkill -9 chrome

# Launch with flag
google-chrome \
  --user-data-dir=/tmp/chrome_quic_test \
  --origin-to-force-quic-on=127.0.0.1:8080 \
  --ignore-certificate-errors \
  https://127.0.0.1:8080

In DevTools (F12) -> Network -> "Protocol" column should show h3.

HTTP/3 (Chrome with Alt-Svc discovery)

For operation without flags, the browser must trust the certificate:

  1. Find the path to Root CA:

    mkcert -CAROOT
  2. Import into Chrome:

    • chrome://settings/certificates -> Authorities -> Import
    • Select rootCA.pem from the path above
    • Enable "Trust for websites"
  3. Restart Chrome and open https://127.0.0.1:8080/

Docker Examples

Simple HTTPS (docker_https)

cd examples/docker_https

# Generate certificates
mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1

docker-compose up --build

With Traefik (docker_traefik_nip)

cd examples/docker_traefik_nip

# Generate wildcard certificate
mkcert -key-file key.pem -cert-file cert.pem "*.127.0.0.1.nip.io" localhost 127.0.0.1

docker-compose up --build

Open https://app.127.0.0.1.nip.io/

Architecture

                    +------------------+
Client (HTTP/1.1) ->|  TCP Listener    |-> ASGI App -> HTTP/1.1 Response + Alt-Svc
                    |  (asyncio ssl)   |
                    +------------------+
                           |
                           v (Alt-Svc upgrade)
                    +------------------+
Client (HTTP/3)   ->|  QUIC Listener   |-> ASGI App -> HTTP/3 Response
                    |  (MsQuic)        |
                    +------------------+

Troubleshooting

ImportError: libmsquic.so.2

# Make sure dependencies are built
./scripts/build_deps.sh

# Reinstall package
pip install --force-reinstall .

ERR_QUIC_PROTOCOL_ERROR in browser

Browser does not trust the certificate. Import Root CA into the browser (see "HTTP/3 Chrome with Alt-Svc discovery" section).

Port already in use

pkill -f hello_world.py

API Reference

ASGIServer

from fpy3.asgi import ASGIServer

server = ASGIServer(app, loop=None, debug=False)
server.start(host, port)
  • app - ASGI 3.0 callable
  • loop - asyncio event loop (optional)
  • debug - enable debug logs

ASGI Scope

{
    'type': 'http',
    'asgi': {'version': '3.0', 'spec_version': '2.3'},
    'http_version': '3' | '1.1',
    'server': (host, port),
    'client': (host, port),
    'scheme': 'https',
    'method': 'GET' | 'POST' | ...,
    'path': '/...',
    'query_string': b'...',
    'headers': [(name, value), ...]
}

About

FPY3 High-Performance Python HTTP/3 Server

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors