Bidirectional bridge connecting TCP/HTTP/WebSocket services to the Zenoh distributed data bus. Export backends as Zenoh services or import Zenoh services as TCP listeners. Supports HTTP/HTTPS routing by Host header or SNI.
  • Rust 97.2%
  • Shell 1.9%
  • Just 0.7%
  • Dockerfile 0.2%
Find a file
2026-04-08 13:32:39 +02:00
.cargo initial commit 2025-10-26 18:35:38 +01:00
.config Http import/export impl 2025-10-30 10:22:00 +01:00
.github/workflows ci: run tests with single thread to avoid race conditions 2025-12-26 22:20:45 +01:00
docs refactor: transport trait abstraction and reliability improvements (Plans 08-10) 2026-02-22 20:36:12 +01:00
src cargo fmt 2026-04-08 13:32:39 +02:00
tests docs: add nlink-lab network topology tests to documentation 2026-04-08 13:31:53 +02:00
.gitignore cargo update 2025-12-20 12:51:05 +01:00
Cargo.lock chore: upgrade zenoh to 1.8.0 and run cargo fmt 2026-04-08 12:21:08 +02:00
Cargo.toml chore: upgrade zenoh to 1.8.0 and run cargo fmt 2026-04-08 12:21:08 +02:00
CHANGELOG.md chore: upgrade zenoh to 1.8.0 and run cargo fmt 2026-04-08 12:21:08 +02:00
CLAUDE.md docs: add nlink-lab network topology tests to documentation 2026-04-08 13:31:53 +02:00
deny.toml initial commit 2025-10-26 18:35:38 +01:00
docker-compose.yml initial commit 2025-10-26 18:35:38 +01:00
Dockerfile initial commit 2025-10-26 18:35:38 +01:00
justfile initial commit 2025-10-26 18:35:38 +01:00
LICENSE-MIT chore: add MIT license 2025-12-26 21:46:21 +01:00
README.md docs: add nlink-lab network topology tests to documentation 2026-04-08 13:31:53 +02:00

Zenoh TCP Bridge

A bidirectional bridge that connects TCP services to the Zenoh distributed data bus. Export TCP backends as Zenoh services or import Zenoh services as TCP listeners.

Overview

This bridge enables:

  • Export Mode: Expose TCP backend services (e.g., databases, APIs) over Zenoh
  • Import Mode: Make Zenoh services accessible via TCP listeners
  • Multiple Services: Run multiple exports and imports simultaneously in a single bridge instance
  • Automatic Connection Management: Lazy connections with liveliness detection

Features

Core Functionality

  • Lazy Connections: Backend connections are created only when clients connect (export mode)
  • Per-Client Isolation: Each client gets dedicated Zenoh pub/sub channels and backend connection
  • Concurrent Services: Handle multiple services in one bridge process
  • Flexible Configuration: Command-line arguments or Zenoh config files
  • WebSocket Support: Bridge WebSocket backends alongside TCP services
  • Graceful Shutdown: Clean shutdown via CancellationToken with per-task tracking and connection draining
  • Backend Reconnection: Exponential backoff retry when backends become unavailable
  • Input Validation: Early CLI validation (buffer size, timeouts, spec formats, log options) with clear error messages
  • Strict TLS Parsing: RFC 6066/1035-compliant SNI validation with size bounds checking

HTTP/HTTPS Routing

  • DNS-Based Routing: Route HTTP requests by Host header to different backends
  • SNI-Based HTTPS: Route HTTPS traffic by SNI without terminating TLS
  • Automatic Protocol Detection: Detects HTTP vs HTTPS automatically
  • DNS Normalization: Case-insensitive, port-aware routing
  • Multiple Backends: One listener can route to N different HTTP/HTTPS servers
  • End-to-End TLS: HTTPS traffic is never decrypted by the bridge
  • Per-Request Multiroute: HTTP/1.1 keep-alive connections can route to different backends per request
  • Optional TLS Termination: Decrypt HTTPS at the bridge with --https-terminate (requires tls-termination feature)

Protocol Auto-Detection

  • Auto-Import Mode: Single listener detects TLS/HTTPS, HTTP, WebSocket, or raw TCP from the first bytes
  • Zero Configuration: No need to pre-declare protocol per listener

Liveliness Detection

  • Automatic client presence tracking using Zenoh liveliness tokens
  • Clean disconnection handling and resource cleanup
  • Backend connection lifecycle tied to client presence

Multiple Zenoh Modes

  • Peer Mode (default): Direct peer-to-peer communication
  • Client Mode: Connect to existing Zenoh routers
  • Router Mode: Act as a Zenoh router

Architecture

Export Mode

TCP Backend (e.g., HTTP server on :8003)
    ↕
Zenoh Bridge (Export)
    ↕
Zenoh Network
    ↕
Zenoh Bridge (Import)
    ↕
TCP Client connects to :8002

The exporter:

  1. Monitors for client liveliness tokens on {service}/clients/{client_id}
  2. Creates backend connection when client appears
  3. Subscribes to {service}/tx/{client_id} (data from client)
  4. Publishes to {service}/rx/{client_id} (data to client)
  5. Cleans up when client disconnects

Import Mode

The importer:

  1. Listens for TCP connections on specified address
  2. For each connection, creates unique client ID
  3. Declares liveliness token at {service}/clients/{client_id}
  4. Publishes to {service}/tx/{client_id} (data from TCP client)
  5. Subscribes to {service}/rx/{client_id} (data to TCP client)
  6. Undeclares liveliness on disconnection

Quick Start

Export a TCP Service

Make a local HTTP server accessible over Zenoh:

# Terminal 1: Start your HTTP server
python3 -m http.server 8003

# Terminal 2: Export it as "webserver" service
zenoh-bridge-tcp --export 'webserver/127.0.0.1:8003'

Import a Zenoh Service

Make the Zenoh service accessible via TCP:

# Terminal 3: Import "webserver" and listen on :8002
zenoh-bridge-tcp --import 'webserver/127.0.0.1:8002'

# Terminal 4: Test with curl
curl http://127.0.0.1:8002

Multiple Services

Run multiple exports/imports in one bridge:

zenoh-bridge-tcp \
  --export 'api/127.0.0.1:3000' \
  --export 'db/127.0.0.1:5432' \
  --import 'frontend/0.0.0.0:8080' \
  --mode peer

Building

Prerequisites

  • Rust 1.85 or later (edition 2024)

Build

cargo build --release

The binary will be at target/release/zenoh-bridge-tcp.

Build with TLS Termination

cargo build --release --features tls-termination

Run Tests

# Run all tests (recommended: use nextest for better test isolation)
cargo nextest run

# Run unit tests only
cargo test --lib

# Run a specific integration test suite
cargo nextest run --test http_routing_integration

Usage

Command Line Options

zenoh-bridge-tcp [OPTIONS]

Options:
  -c, --config <FILE>                        Path to Zenoh configuration file (JSON5)
      --export <SPEC>                        Export TCP backend as Zenoh service
                                             Format: 'service_name/backend_addr'
                                             Example: 'myapi/127.0.0.1:3000'
      --import <SPEC>                        Import Zenoh service as TCP listener
                                             Format: 'service_name/listen_addr'
                                             Example: 'myapi/0.0.0.0:8080'
      --http-export <SPEC>                   Export HTTP backend with DNS-based routing
                                             Format: 'service_name/dns/backend_addr'
                                             Example: 'http-service/api.example.com/127.0.0.1:8000'
      --http-import <SPEC>                   Import HTTP service with DNS-based routing
                                             Format: 'service_name/listen_addr'
                                             Example: 'http-service/0.0.0.0:8080'
      --http-multiroute-import <SPEC>        Import HTTP with per-request Host routing
                                             Format: 'service_name/listen_addr'
                                             Example: 'http-service/0.0.0.0:8080'
      --auto-import <SPEC>                   Auto-detect protocol and route accordingly
                                             Format: 'service_name/listen_addr'
                                             Example: 'myservice/0.0.0.0:8080'
      --https-terminate <SPEC>               Import HTTPS with TLS termination (feature: tls-termination)
                                             Format: 'service_name/listen_addr'
                                             Example: 'https-service/0.0.0.0:8443'
      --tls-cert <PATH>                      TLS certificate for --https-terminate
      --tls-key <PATH>                       TLS private key for --https-terminate
      --ws-export <SPEC>                     Export WebSocket backend as Zenoh service
                                             Format: 'service_name/ws_url'
                                             Example: 'myws/ws://127.0.0.1:9000'
      --ws-import <SPEC>                     Import Zenoh service as WebSocket listener
                                             Format: 'service_name/listen_addr'
                                             Example: 'myws/0.0.0.0:8080'
  -m, --mode <MODE>                          Zenoh mode: peer, client, or router [default: peer]
  -e, --connect <ENDPOINT>                   Zenoh connect endpoint (e.g., tcp/localhost:7447)
  -l, --listen <ENDPOINT>                    Zenoh listen endpoint (e.g., tcp/0.0.0.0:7447)
      --buffer-size <BYTES>                  Buffer size for read operations [default: 65536]
      --read-timeout <SECS>                  Timeout for reading headers [default: 10]
      --drain-timeout <SECS>                 Connection drain timeout [default: 5]
      --log-level <LEVEL>                    Log level: trace, debug, info, warn, error [default: info]
      --log-format <FORMAT>                  Log format: pretty, compact, json [default: pretty]
  -h, --help                                 Print help
  -V, --version                              Print version

Export Specification Format

service_name/backend_address:port

Examples:

  • webserver/127.0.0.1:8080 - Export local web server
  • api/backend-host:3000 - Export remote API
  • postgres/localhost:5432 - Export PostgreSQL database

Import Specification Format

service_name/listen_address:port

Examples:

  • webserver/0.0.0.0:8080 - Listen on all interfaces
  • api/127.0.0.1:3000 - Listen only on localhost
  • service/[::]:8080 - Listen on IPv6

Configuration File

Use a Zenoh configuration file for advanced settings:

zenoh-bridge-tcp \
  --config zenoh-config.json5 \
  --export 'myservice/127.0.0.1:8003' \
  --import 'myservice/0.0.0.0:8002'

Examples

Example 1: Traditional TCP Bridge for HTTP

# Terminal 1: Start HTTP server
python3 -m http.server 8003

# Terminal 2: Export side
zenoh-bridge-tcp --export 'http/127.0.0.1:8003'

# Terminal 3: Import side (can be on different machine)
zenoh-bridge-tcp --import 'http/127.0.0.1:8002' --connect tcp/exporter-host:7447

# Terminal 4: Test
curl http://127.0.0.1:8002

Example 2: HTTP Routing with Multiple Backends

# Terminal 1: Start multiple HTTP servers
python3 -m http.server 8001  # API backend
python3 -m http.server 8002  # Web backend

# Terminal 2: Export API backend for api.example.com
zenoh-bridge-tcp --http-export 'http-service/api.example.com/127.0.0.1:8001'

# Terminal 3: Export Web backend for web.example.com
zenoh-bridge-tcp --http-export 'http-service/web.example.com/127.0.0.1:8002'

# Terminal 4: Import (single listener routes to both backends)
zenoh-bridge-tcp --http-import 'http-service/0.0.0.0:8080'

# Terminal 5: Test routing by Host header
curl -H "Host: api.example.com" http://127.0.0.1:8080/  # -> API backend
curl -H "Host: web.example.com" http://127.0.0.1:8080/  # -> Web backend

Example 3: HTTPS Routing with SNI

# Start HTTPS backends with certificates
# (Configure your HTTPS servers for api.example.com and web.example.com)

# Export HTTPS backends
zenoh-bridge-tcp --http-export 'https-service/api.example.com/127.0.0.1:8443'
zenoh-bridge-tcp --http-export 'https-service/web.example.com/127.0.0.1:8444'

# Import (automatically detects HTTPS via SNI)
zenoh-bridge-tcp --http-import 'https-service/0.0.0.0:8443'

# Test - SNI determines routing (no TLS termination!)
curl https://api.example.com:8443/ --resolve api.example.com:8443:127.0.0.1
curl https://web.example.com:8443/ --resolve web.example.com:8443:127.0.0.1

Example 4: WebSocket Bridge

# Terminal 1: Start WebSocket echo server (using websocat or similar)
websocat -s 127.0.0.1:9000

# Terminal 2: Export WebSocket backend
zenoh-bridge-tcp --ws-export 'wsecho/ws://127.0.0.1:9000'

# Terminal 3: Import as WebSocket listener
zenoh-bridge-tcp --ws-import 'wsecho/0.0.0.0:8080'

# Terminal 4: Connect WebSocket client
websocat ws://127.0.0.1:8080
# Type messages - they go through Zenoh to the WebSocket backend

Example 5: Netcat Echo Test

# Terminal 1: Start netcat server
nc -l 8003

# Terminal 2: Export
zenoh-bridge-tcp --export 'echo/127.0.0.1:8003'

# Terminal 3: Import
zenoh-bridge-tcp --import 'echo/127.0.0.1:8002'

# Terminal 4: Client
nc 127.0.0.1 8002
# Type messages - they go through Zenoh to the server

Example 6: Multiple Services

zenoh-bridge-tcp \
  --export 'web/127.0.0.1:8080' \
  --export 'api/127.0.0.1:3000' \
  --import 'frontend/0.0.0.0:9001' \
  --import 'admin/0.0.0.0:9002' \
  --mode peer

Example 7: With Zenoh Router

# Terminal 1: Start Zenoh router
zenohd

# Terminal 2: Export side (client mode)
zenoh-bridge-tcp \
  --export 'service/127.0.0.1:8003' \
  --mode client \
  --connect tcp/localhost:7447

# Terminal 3: Import side (client mode)
zenoh-bridge-tcp \
  --import 'service/0.0.0.0:8002' \
  --mode client \
  --connect tcp/localhost:7447

Docker Deployment

Build and run with Docker:

# Build image
docker build -t zenoh-bridge-tcp .

# Run with docker-compose
docker-compose up -d

# Test multi-bridge setup
docker-compose --profile multi-bridge up -d

Testing

The project includes comprehensive integration tests. Use cargo nextest run for best results (parallel execution with process isolation).

Test Suites

  • tests/export_import_integration.rs - Core export/import functionality
  • tests/tcp_sanity_tests.rs - Basic TCP sanity checks
  • tests/http_integration.rs - HTTP/HTTPS service bridging
  • tests/liveliness_integration.rs - Liveliness detection
  • tests/multi_service_integration.rs - Multiple concurrent services
  • tests/http_routing_integration.rs - HTTP routing with multiple backends
  • tests/https_routing_integration.rs - HTTPS routing with SNI
  • tests/http_edge_cases.rs - Edge cases and error handling
  • tests/http_multiroute_integration.rs - Per-request HTTP multiroute
  • tests/ws_integration.rs - WebSocket bridging
  • tests/drain_integration.rs - Connection drain on shutdown
  • tests/auto_import_integration.rs - Protocol auto-detection
  • tests/https_termination_integration.rs - TLS termination
  • tests/stress_test.rs - Load and stress testing
  • tests/bug_demonstrations.rs - Verification tests for 16 audit bug fixes
  • tests/coverage_integration.rs - Large messages, partial transfers, concurrent clients, rapid connect/disconnect

Run tests:

# All tests (recommended)
cargo nextest run

# Unit tests only
cargo test --lib

# Specific test suite
cargo nextest run --test http_routing_integration

End-to-end tests using nlink-lab to create isolated network namespaces with realistic WAN conditions. These require Linux with network namespace support and nlink-lab installed on the host.

# Raw TCP multi-hop: client -> import bridge -> zenoh -> export bridge -> backend
./tests/nlink/run-multi-hop-test.sh

# HTTP host-header routing multi-hop: multiple backends, single import listener
./tests/nlink/run-multi-hop-http-test.sh

# With WAN simulation
./tests/nlink/run-multi-hop-test.sh --wan-delay 100ms --wan-loss 1%

# Skip cargo build if already built
./tests/nlink/run-multi-hop-test.sh --skip-build

See tests/nlink/README.md for debugging tips and topology details.

See tests/README.md for detailed testing documentation and docs/HTTP_ROUTING_GUIDE.md for the HTTP/HTTPS routing guide.

Zenoh Key Expression Design

The bridge uses a structured key expression pattern:

Traditional TCP Mode

{service_name}/tx/{client_id}      # Client -> Backend data
{service_name}/rx/{client_id}      # Backend -> Client data
{service_name}/clients/{client_id} # Liveliness token

HTTP/HTTPS Mode (DNS-based routing)

{service_name}/{dns}/tx/{client_id}      # Client -> Backend data (for specific DNS)
{service_name}/{dns}/rx/{client_id}      # Backend -> Client data (for specific DNS)
{service_name}/{dns}/clients/{client_id} # Liveliness token (per DNS)
{service_name}/{dns}/available           # Backend availability signal

Each TCP connection gets a unique client_id, ensuring isolation between clients. In HTTP/HTTPS mode, the {dns} component (extracted from Host header or SNI) enables routing to multiple backends through a single listener.

Logging

Control log verbosity with CLI flags or RUST_LOG environment variable:

# Using CLI flags (recommended)
zenoh-bridge-tcp --log-level debug --export 'service/127.0.0.1:8003'

# JSON format for production/log aggregation
zenoh-bridge-tcp --log-level info --log-format json --export 'service/127.0.0.1:8003'

# Compact format (less verbose)
zenoh-bridge-tcp --log-format compact --export 'service/127.0.0.1:8003'

# Using RUST_LOG (takes precedence over --log-level)
RUST_LOG=debug zenoh-bridge-tcp --export 'service/127.0.0.1:8003'

# Module-specific with RUST_LOG
RUST_LOG=zenoh_bridge_tcp=debug,zenoh=warn zenoh-bridge-tcp --export 'service/127.0.0.1:8003'

Log Formats

  • pretty (default): Human-readable with colors
  • compact: Single-line format, less verbose
  • json: Structured JSON, ideal for log aggregation (ELK, Loki, etc.)

Performance Considerations

  • Buffer Size: 64KB (65,536 bytes) default per connection
  • Concurrent Connections: Limited by system resources (file descriptors, memory)
  • Latency: Adds ~1-2ms overhead vs direct TCP (depends on Zenoh setup)
  • Throughput: Tested with HTTP, HTTPS, and raw TCP; handles typical workloads well

Use Cases

Traditional TCP Bridging

  • Service Discovery: Expose services without static IP addresses
  • Network Abstraction: Abstract away network topology
  • Cloud-Edge Bridging: Connect edge devices to cloud services via Zenoh
  • Legacy Integration: Make TCP services Zenoh-native
  • Multi-Region Deployment: Leverage Zenoh's peer-to-peer or routed mesh
  • Protocol Bridging: Connect TCP clients to Zenoh-based backends

HTTP/HTTPS Routing

  • Virtual Host Routing: Route HTTP traffic by hostname to different backends
  • Multi-Tenant SaaS: Single listener routes customers to their dedicated backends
  • API Gateway: Route API requests by domain to microservices
  • SNI-Based Load Distribution: Distribute HTTPS traffic without TLS termination
  • Development/Staging Environments: Route by hostname to different environments
  • Hybrid Cloud: Route traffic to backends across different networks via Zenoh

Dependencies

Core dependencies:

  • zenoh 1.8.0 - Zenoh distributed data bus
  • zenoh-ext - Extended pub/sub with reliability features
  • tokio - Async runtime
  • tokio-util - CancellationToken for graceful shutdown
  • clap - Command-line parsing
  • anyhow / thiserror - Error handling
  • tracing / tracing-subscriber - Structured logging (pretty, compact, json)
  • httparse - HTTP/1.x parser (for HTTP routing)
  • tls-parser - TLS/SNI parser (for HTTPS routing)
  • tokio-tungstenite - WebSocket support
  • futures-util - Async stream utilities
  • backon - Retry with exponential backoff
  • uuid - Unique client ID generation

Development/test dependencies include: axum, hyper, rustls, reqwest, futures for protocol testing.

Version Information

  • Current Version: 0.5.0
  • Zenoh Version: 1.8.0
  • Rust Edition: 2024
  • MSRV: 1.85 (required by edition 2024)

Quality Tools

The project uses standard Rust quality tools:

# Format code
cargo fmt

# Lint
cargo clippy

# Check dependencies
cargo deny check

Configuration files:

  • deny.toml - Dependency auditing

Feature Highlights

HTTP/HTTPS Routing Architecture

The HTTP/HTTPS routing feature enables DNS-based service discovery and routing:

How it works:

  1. Import side listens for HTTP/HTTPS connections
  2. Protocol detection: Automatically detects HTTP (text) vs HTTPS (TLS)
  3. DNS extraction:
    • HTTP: Parses Host header
    • HTTPS: Extracts SNI from TLS ClientHello (before encryption)
  4. DNS normalization: Converts to lowercase, strips default ports (80/443)
  5. Backend discovery: Queries Zenoh for backends registered with that DNS
  6. Routing: Forwards to correct backend via DNS-specific Zenoh keys
  7. Pass-through: For HTTPS, TLS handshake and data pass through unchanged

Benefits:

  • One listener -> N backends (multi-tenant)
  • No configuration changes needed for new backends
  • HTTPS works without TLS termination (end-to-end encryption)
  • Automatic DNS normalization (case-insensitive, port-aware)
  • Backend availability detection (HTTP 502 when unavailable)

Documentation:

Contributing

Contributions welcome! Please:

  1. Run cargo fmt before committing
  2. Ensure cargo clippy passes
  3. Add tests for new features
  4. Update documentation as needed

License

Licensed under the MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT).

Additional Resources