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.3.tar.gz (28.9 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.3-cp38-abi3-manylinux_2_34_x86_64.whl (1.4 MB view details)

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

quic_portal-0.1.3-cp38-abi3-macosx_11_0_arm64.whl (1.2 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

File details

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

File metadata

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

File hashes

Hashes for quic_portal-0.1.3.tar.gz
Algorithm Hash digest
SHA256 d50778329aa595cb8e3fa10217a657454bfa13a46d7fdd40ffd558a3b41feeee
MD5 c37c8e02f9cd699e6983fa30733a2e74
BLAKE2b-256 ed32b5ab92727b776273a66ef1e8c88892fd260d86e2c77c4710eee3f12c152e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for quic_portal-0.1.3-cp38-abi3-manylinux_2_34_x86_64.whl
Algorithm Hash digest
SHA256 b2388691d841379fc9e00385c53af78ef878a58dee221bb6f10a08ce120a747f
MD5 4a3c7f1cbb39cba1366b2324965b65dc
BLAKE2b-256 2184e4b3bbeb528ff27cb934ffcdf95769258b077c3732bd88c6912095b839b6

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for quic_portal-0.1.3-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5bc80b51ce4fc3f8425c2192e96f83dabdedaefb0e075a67017efc044a78ca1c
MD5 5da27c740a25139df10219dbb7e58cd4
BLAKE2b-256 339e738b4ff44de5623e88277e239eef7e44ddfaad55ec0677ec784a70973474

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