Skip to main content

QUIC library with NAT traversal

Project description

QUIC Portal (experimental)

⚠️ Warning: This library is experimental and not intended for production use.

High-performance QUIC communication library with automatic NAT traversal within Modal applications.

Current features

  • Automatic NAT traversal: Built-in STUN discovery and UDP hole punching, using Modal Dict for rendezvous.
  • High-performance QUIC: Rust-based implementation for maximum throughput and minimal latency
  • Simple synchronous API: Easy-to-use Portal class with static methods for server/client creation. WebSocket-style messaging.

Upcoming roadmap

  • TODO: Keep-alive: Enable initial pings to establish server on create_server/create_client. Also add background pings to keep connection alive.
  • TODO: Improved NAT traversal: Handle more complex client-side NATs using port scanning + birthday technique. Currently only supports clients behind "easy" NATs.
  • TODO: Shared server certificates: Use a modal.Dict to share server/client certificates, to mutually validate identity.
  • TODO: Tunable QUIC settings: Expose currently hardcoded bandwidth optimization as a parameter in Python class.

Installation

# Install from PyPi (only certain wheels built)
pip install quic-portal
# Install from source (requires Rust toolchain)
git clone <repository>
cd quic-portal
pip install .

Quick Start

Usage with Modal

import modal
from quic_portal import Portal

app = modal.App("my-quic-app")

@app.function()
def server_function(coord_dict: modal.Dict):
    # Create server with automatic NAT traversal
    portal = Portal.create_server(dict=coord_dict, local_port=5555)
    
    # Receive and echo messages
    while True:
        data = portal.recv(timeout_ms=10000)
        if data:
            message = data.decode("utf-8")
            print(f"Received: {message}")
            portal.send(f"Echo: {message}".encode("utf-8"))

@app.function()
def client_function(coord_dict: modal.Dict):
    # Create client with automatic NAT traversal
    portal = Portal.create_client(dict=coord_dict, local_port=5556)
    
    # Send messages
    portal.send(b"Hello, QUIC!")
    response = portal.recv(timeout_ms=5000)
    if response:
        print(f"Got response: {response.decode('utf-8')}")

@app.local_entrypoint()
def main(local: bool = False):
    # Create coordination dict
    with modal.Dict.ephemeral() as coord_dict:
        # Start server
        server_task = server_function.spawn(coord_dict)
        
        # Run client
        if local:
            # Run test between local environment and remote container.
            client_function.local(coord_dict)
        else:
            # Run test between two containers.
            client_function.remote(coord_dict)
        
        server_task.cancel()

Manual NAT Traversal

For advanced use cases where you handle NAT traversal yourself, or the server has a public IP:

from quic_portal import Portal

# After NAT hole punching is complete...
# Server side
server = Portal()
server.listen(5555)

# Client side  
client = Portal()
client.connect("server_ip", 5555, 5556)

# WebSocket-style messaging
client.send(b"Hello!")
response = server.recv(timeout_ms=1000)

API Reference

Portal Class

Static Methods

Portal.create_server(dict, local_port=5555, stun_server=("stun.ekiga.net", 3478), punch_timeout=15)

Create a server portal with automatic NAT traversal. Synchronous operation.

Parameters:

  • dict (modal.Dict or dict): Modal Dict or regular dict for peer coordination
  • local_port (int): Local port for QUIC server (default: 5555)
  • stun_server (tuple): STUN server for NAT discovery (default: ("stun.ekiga.net", 3478))
  • punch_timeout (int): Timeout in seconds for NAT punching (default: 15)

Returns: Connected Portal instance ready for communication

Portal.create_client(dict, local_port=5556, stun_server=("stun.ekiga.net", 3478), punch_timeout=15)

Create a client portal with automatic NAT traversal. Synchronous operation.

Parameters:

  • dict (modal.Dict or dict): Modal Dict or regular dict for peer coordination (must be same as server)
  • local_port (int): Local port for QUIC client (default: 5556)
  • stun_server (tuple): STUN server for NAT discovery (default: ("stun.ekiga.net", 3478))
  • punch_timeout (int): Timeout in seconds for NAT punching (default: 15)

Returns: Connected Portal instance ready for communication

Instance Methods

send(data: Union[bytes, str]) -> None

Send data over QUIC connection (WebSocket-style). Synchronous operation.

recv(timeout_ms: Optional[int] = None) -> Optional[bytes]

Receive data from QUIC connection. Blocks until message arrives or timeout. Synchronous operation.

Parameters:

  • timeout_ms (int, optional): Timeout in milliseconds (None for blocking)

Returns: Received data as bytes, or None if timeout

connect(server_ip: str, server_port: int, local_port: int) -> None

Connect to a QUIC server (for manual NAT traversal). Synchronous operation.

Parameters:

  • server_ip (str): Server IP address
  • server_port (int): Server port
  • local_port (int): Local port to bind to
listen(local_port: int) -> None

Start QUIC server and wait for connection (for manual NAT traversal). Synchronous operation.

Parameters:

  • local_port (int): Local port to bind to
is_connected() -> bool

Check if connected to peer.

close() -> None

Close the connection and clean up resources.

Examples

See the examples/ directory for complete working examples:

  • modal_simple.py - Basic server/client communication
  • modal_benchmark.py - Performance benchmarking

Requirements

  • Python 3.8+
  • Modal (for automatic NAT traversal)
  • Rust toolchain (for building from source)

Third-party Libraries

This project uses code from:

  • pynat by Ariel Antonitis, licensed under MIT License

License

MIT License

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

quic_portal-0.1.8.tar.gz (33.3 kB view details)

Uploaded Source

Built Distributions

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

quic_portal-0.1.8-cp38-abi3-manylinux_2_34_x86_64.whl (1.2 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.34+ x86-64

quic_portal-0.1.8-cp38-abi3-macosx_11_0_arm64.whl (1.0 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

Details for the file quic_portal-0.1.8.tar.gz.

File metadata

  • Download URL: quic_portal-0.1.8.tar.gz
  • Upload date:
  • Size: 33.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.8.6

File hashes

Hashes for quic_portal-0.1.8.tar.gz
Algorithm Hash digest
SHA256 87e78a117813add44a2f9da3c7c4f195f95d49345a677bc54f86b0a4de5d0a8c
MD5 1152dd5992448fc3c8f8e0c0a7bb22e4
BLAKE2b-256 44a7d8592465218efaeb3231fd81a1ad0a40b162ec2cc877fdec093919f3736b

See more details on using hashes here.

File details

Details for the file quic_portal-0.1.8-cp38-abi3-manylinux_2_34_x86_64.whl.

File metadata

File hashes

Hashes for quic_portal-0.1.8-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 5be5d591f6c17b24923bc42d1b687ac026429552319a0638409ceff4451b8b36
MD5 a7e622b6507e0efba8b691b753a8efb0
BLAKE2b-256 887c4c34fcf22e6490a2d1f355ae6172a2d9703c9115d479d758befafd00c178

See more details on using hashes here.

File details

Details for the file quic_portal-0.1.8-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for quic_portal-0.1.8-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 b84efdc6a2425066e2122f3e05a46f9e75102f31cc1ceb95279ee7b9fd4f67a6
MD5 81ecff5bb510b59e3ad3e507eb89c210
BLAKE2b-256 3e23a8b577de3484796572cf47ab87f8eaef120e6ece9e748acbd7fb42b7cc39

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