quic-portal


Namequic-portal JSON
Version 0.1.12 PyPI version JSON
download
home_pageNone
SummaryQUIC library with NAT traversal
upload_time2025-07-22 16:31:23
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseMIT
keywords quic networking nat-traversal p2p communication
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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: 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.

## Installation

```bash
# Install from PyPi (only certain wheels built)
pip install quic-portal
```

```bash
# Install from source (requires Rust toolchain)
git clone <repository>
cd quic-portal
pip install .
```

## Quick Start

### Usage with Modal

```python
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:

```python
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.9+
- 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 


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "quic-portal",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "quic, networking, nat-traversal, p2p, communication",
    "author": null,
    "author_email": "Richard Gong <richard@modal.com>, Daniel Shaar <daniel@modal.com>",
    "download_url": "https://files.pythonhosted.org/packages/bb/fe/a18d9ba37618911361695cf01db3bce2ca480ce88e93d15b5908289f995f/quic_portal-0.1.12.tar.gz",
    "platform": null,
    "description": "# QUIC Portal (experimental)\n\n> \u26a0\ufe0f **Warning**: This library is experimental and not intended for production use.\n\nHigh-performance QUIC communication library with automatic NAT traversal within Modal applications.\n\n## Current features\n\n- **Automatic NAT traversal**: Built-in STUN discovery and UDP hole punching, using Modal Dict for rendezvous.\n- **High-performance QUIC**: Rust-based implementation for maximum throughput and minimal latency\n- **Simple synchronous API**: Easy-to-use Portal class with static methods for server/client creation. WebSocket-style messaging.\n\n## Upcoming roadmap\n\n- **TODO: Improved NAT traversal**: Handle more complex client-side NATs using port scanning + birthday technique. Currently only supports clients behind \"easy\" NATs.\n- **TODO: Shared server certificates**: Use a modal.Dict to share server/client certificates, to mutually validate identity.\n\n## Installation\n\n```bash\n# Install from PyPi (only certain wheels built)\npip install quic-portal\n```\n\n```bash\n# Install from source (requires Rust toolchain)\ngit clone <repository>\ncd quic-portal\npip install .\n```\n\n## Quick Start\n\n### Usage with Modal\n\n```python\nimport modal\nfrom quic_portal import Portal\n\napp = modal.App(\"my-quic-app\")\n\n@app.function()\ndef server_function(coord_dict: modal.Dict):\n    # Create server with automatic NAT traversal\n    portal = Portal.create_server(dict=coord_dict, local_port=5555)\n    \n    # Receive and echo messages\n    while True:\n        data = portal.recv(timeout_ms=10000)\n        if data:\n            message = data.decode(\"utf-8\")\n            print(f\"Received: {message}\")\n            portal.send(f\"Echo: {message}\".encode(\"utf-8\"))\n\n@app.function()\ndef client_function(coord_dict: modal.Dict):\n    # Create client with automatic NAT traversal\n    portal = Portal.create_client(dict=coord_dict, local_port=5556)\n    \n    # Send messages\n    portal.send(b\"Hello, QUIC!\")\n    response = portal.recv(timeout_ms=5000)\n    if response:\n        print(f\"Got response: {response.decode('utf-8')}\")\n\n@app.local_entrypoint()\ndef main(local: bool = False):\n    # Create coordination dict\n    with modal.Dict.ephemeral() as coord_dict:\n        # Start server\n        server_task = server_function.spawn(coord_dict)\n        \n        # Run client\n        if local:\n            # Run test between local environment and remote container.\n            client_function.local(coord_dict)\n        else:\n            # Run test between two containers.\n            client_function.remote(coord_dict)\n        \n        server_task.cancel()\n```\n\n### Manual NAT Traversal\n\nFor advanced use cases where you handle NAT traversal yourself, or the server has a public IP:\n\n```python\nfrom quic_portal import Portal\n\n# After NAT hole punching is complete...\n# Server side\nserver = Portal()\nserver.listen(5555)\n\n# Client side  \nclient = Portal()\nclient.connect(\"server_ip\", 5555, 5556)\n\n# WebSocket-style messaging\nclient.send(b\"Hello!\")\nresponse = server.recv(timeout_ms=1000)\n```\n\n## API Reference\n\n### Portal Class\n\n#### Static Methods\n\n##### `Portal.create_server(dict, local_port=5555, stun_server=(\"stun.ekiga.net\", 3478), punch_timeout=15)`\n\nCreate a server portal with automatic NAT traversal. **Synchronous operation.**\n\n**Parameters:**\n- `dict` (modal.Dict or dict): Modal Dict or regular dict for peer coordination\n- `local_port` (int): Local port for QUIC server (default: 5555)\n- `stun_server` (tuple): STUN server for NAT discovery (default: (\"stun.ekiga.net\", 3478))\n- `punch_timeout` (int): Timeout in seconds for NAT punching (default: 15)\n\n**Returns:** Connected Portal instance ready for communication\n\n##### `Portal.create_client(dict, local_port=5556, stun_server=(\"stun.ekiga.net\", 3478), punch_timeout=15)`\n\nCreate a client portal with automatic NAT traversal. **Synchronous operation.**\n\n**Parameters:**\n- `dict` (modal.Dict or dict): Modal Dict or regular dict for peer coordination (must be same as server)\n- `local_port` (int): Local port for QUIC client (default: 5556)\n- `stun_server` (tuple): STUN server for NAT discovery (default: (\"stun.ekiga.net\", 3478))\n- `punch_timeout` (int): Timeout in seconds for NAT punching (default: 15)\n\n**Returns:** Connected Portal instance ready for communication\n\n#### Instance Methods\n\n##### `send(data: Union[bytes, str]) -> None`\n\nSend data over QUIC connection (WebSocket-style). **Synchronous operation.**\n\n##### `recv(timeout_ms: Optional[int] = None) -> Optional[bytes]`\n\nReceive data from QUIC connection. Blocks until message arrives or timeout. **Synchronous operation.**\n\n**Parameters:**\n- `timeout_ms` (int, optional): Timeout in milliseconds (None for blocking)\n\n**Returns:** Received data as bytes, or None if timeout\n\n##### `connect(server_ip: str, server_port: int, local_port: int) -> None`\n\nConnect to a QUIC server (for manual NAT traversal). **Synchronous operation.**\n\n**Parameters:**\n- `server_ip` (str): Server IP address\n- `server_port` (int): Server port\n- `local_port` (int): Local port to bind to\n\n##### `listen(local_port: int) -> None`\n\nStart QUIC server and wait for connection (for manual NAT traversal). **Synchronous operation.**\n\n**Parameters:**\n- `local_port` (int): Local port to bind to\n\n##### `is_connected() -> bool`\n\nCheck if connected to peer.\n\n##### `close() -> None`\n\nClose the connection and clean up resources.\n\n## Examples\n\nSee the `examples/` directory for complete working examples:\n\n- `modal_simple.py` - Basic server/client communication\n- `modal_benchmark.py` - Performance benchmarking\n\n## Requirements\n\n- Python 3.9+\n- Modal (for automatic NAT traversal)\n- Rust toolchain (for building from source)\n\n## Third-party Libraries\nThis project uses code from:\n- `pynat` by Ariel Antonitis, licensed under MIT License\n\n## License\n\nMIT License \n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "QUIC library with NAT traversal",
    "version": "0.1.12",
    "project_urls": {
        "Bug Tracker": "https://github.com/gongy/quic-portal/issues",
        "Documentation": "https://github.com/gongy/quic-portal#readme",
        "Homepage": "https://github.com/gongy/quic-portal",
        "Repository": "https://github.com/gongy/quic-portal"
    },
    "split_keywords": [
        "quic",
        " networking",
        " nat-traversal",
        " p2p",
        " communication"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "95f00dbb6f9fa5678a9492456caae2f9149e530a8267d0a4201742bc8a797eba",
                "md5": "b46ccda029d9ca1f4faeef5223e81913",
                "sha256": "5565b9bb6b5b413c4031062d274fd0208c30c401d561cfca4cb8a608a89d3c17"
            },
            "downloads": -1,
            "filename": "quic_portal-0.1.12-cp39-abi3-macosx_11_0_arm64.whl",
            "has_sig": false,
            "md5_digest": "b46ccda029d9ca1f4faeef5223e81913",
            "packagetype": "bdist_wheel",
            "python_version": "cp39",
            "requires_python": ">=3.9",
            "size": 1144081,
            "upload_time": "2025-07-22T16:31:21",
            "upload_time_iso_8601": "2025-07-22T16:31:21.718063Z",
            "url": "https://files.pythonhosted.org/packages/95/f0/0dbb6f9fa5678a9492456caae2f9149e530a8267d0a4201742bc8a797eba/quic_portal-0.1.12-cp39-abi3-macosx_11_0_arm64.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "bbfea18d9ba37618911361695cf01db3bce2ca480ce88e93d15b5908289f995f",
                "md5": "cea2ea6c0d24e230d31a5825ab546ffd",
                "sha256": "1170684d187508e5d9f08cf2b50cb098550bcec2a6226bcff575a375041563f6"
            },
            "downloads": -1,
            "filename": "quic_portal-0.1.12.tar.gz",
            "has_sig": false,
            "md5_digest": "cea2ea6c0d24e230d31a5825ab546ffd",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 33778,
            "upload_time": "2025-07-22T16:31:23",
            "upload_time_iso_8601": "2025-07-22T16:31:23.225964Z",
            "url": "https://files.pythonhosted.org/packages/bb/fe/a18d9ba37618911361695cf01db3bce2ca480ce88e93d15b5908289f995f/quic_portal-0.1.12.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-22 16:31:23",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "gongy",
    "github_project": "quic-portal",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "quic-portal"
}
        
Elapsed time: 1.32692s