openTunnel method

Future<TunnelHandle> openTunnel({
  1. required int targetPort,
  2. String nodeId = '',
  3. String targetHost = 'localhost',
  4. int? publicPort,
  5. bool local = false,
  6. bool secure = false,
  7. TunnelProtocol protocol = TunnelProtocol.tcp,
  8. TunnelCacheOptions? cache,
  9. TunnelHttpTimeouts? timeouts,
})

Opens a tunnel that exposes targetPort — reachable by nodeId, or this client's own machine when local is set — on a public Hub port. When publicPort is given the Hub validates it against its configured range; otherwise the Hub allocates one in range. Completes once the Hub confirms, or throws TunnelRejectedException if refused.

For a local tunnel this client must stay connected to serve the forwarded connections (it dials its own targetHost:targetPort for each).

With protocol TunnelProtocol.http the Hub adds forwarding headers (X-Forwarded-*, Forwarded, X-Real-IP, Via, X-Request-Id and X-OmnyShell-*) to every request it relays to the target. Check the returned TunnelHandle.protocol: an older Hub opens it as plain TCP.

An HTTP tunnel can also keep an in-memory response cache on the Hub (RFC 9111; the Hub clamps its size — see TunnelHandle.cache for what was granted) and enforces timeouts (the Hub's defaults when null). Both require TunnelProtocol.http; the Hub rejects them otherwise.

Implementation

Future<TunnelHandle> openTunnel({
  required int targetPort,
  String nodeId = '',
  String targetHost = 'localhost',
  int? publicPort,
  bool local = false,
  bool secure = false,
  TunnelProtocol protocol = TunnelProtocol.tcp,
  TunnelCacheOptions? cache,
  TunnelHttpTimeouts? timeouts,
}) {
  _ensureConnected();
  final id = newId();
  final effectiveNode = local ? TunnelOpenRequest.localNode : nodeId;
  final completer = Completer<TunnelHandle>();
  _pendingTunnelOpens[id] = (
    completer: completer,
    nodeId: effectiveNode,
    targetPort: targetPort,
  );
  _connection!.send(
    ControlFrame(
      TunnelOpenRequest(
        requestId: id,
        nodeId: effectiveNode,
        targetHost: targetHost,
        targetPort: targetPort,
        publicPort: publicPort,
        secure: secure,
        protocol: protocol,
        cache: cache,
        timeouts: timeouts,
      ),
    ),
  );
  return _rpcTimeout(
    completer.future,
    () => _pendingTunnelOpens.remove(id),
    what: 'node "$effectiveNode"',
    timeout: const Duration(seconds: 30),
  );
}