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.7.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.7-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.7-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.7.tar.gz.

File metadata

  • Download URL: quic_portal-0.1.7.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.7.tar.gz
Algorithm Hash digest
SHA256 55a34fa541f2283d7dfaa464b83a2193f0bcb1347fc4a20cf480d0bbb328246b
MD5 af5f5913f75871e63bd722d52812352f
BLAKE2b-256 68dd4013c224d71d42c521bf2ca563f92384a7a991810bb7607fd69d75723200

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for quic_portal-0.1.7-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 a9648934a37947f173d626a408238fddd316205db5078b10433051985f5bb60d
MD5 d2346bb8ab2b55e593e1c9686abf74c3
BLAKE2b-256 805759948dbcff11250946c93543df99915bc84018d4328d6f3c20b272b814ef

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for quic_portal-0.1.7-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 24f38fde850247b2d90d4cbf55f75a7450e14c8eda88b1fb187ead72a08822eb
MD5 c859ef3da80d50c9897a0c64fe79a288
BLAKE2b-256 bc2d1ab9c5db3f5a3e135037ffbcfd41c86c97ed4337405fba58f81ac2a08411

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