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.6.tar.gz (32.6 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.6-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.6-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.6.tar.gz.

File metadata

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

File hashes

Hashes for quic_portal-0.1.6.tar.gz
Algorithm Hash digest
SHA256 8e0b5f9e7a6a2feea73d9cf07a4db46962f4da29e1c30122d35592ca96ed421f
MD5 9e621cd123ec4db715547ee2095fe836
BLAKE2b-256 a6ebb7ea8107498e9cf8950c74fde09eae3caf5cf7f01a4c03d1e31bccb9f56b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for quic_portal-0.1.6-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 c1db5cb2c9112d2fb21655ea660173ee35cd5e636e0568c9aa46e1034f3d31e9
MD5 774d09de1cac41d3aed85565dbb850dc
BLAKE2b-256 14925d15fa31a2391d9790de47445f3d1c411b772bc9d4ab1fb80ddd795f11aa

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for quic_portal-0.1.6-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 1f14508cb643d8a4332387a90326a1df915361528c05beda78ef3da694d4a4c3
MD5 3d87e2fd56434b02e6418b73fcbe8ac2
BLAKE2b-256 59b00f1a18f2b0587d0abe62439c6b940395949558a537ad04cf06f1fb5cf9df

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