Operating and Troubleshooting WebSockets

 

Author: Eike Stepper

This article focuses on day-to-day ownership, shutdown, and diagnosis of Net4j WS/WSS connections. For Jetty deployment and TLS setup, see the related Operator's Guide articles linked below.

Table of Contents

1 Resource Ownership and Lifecycle
2 Graceful Shutdown
3 Diagnosing Connection and Upgrade Failures
4 TLS and Proxy Diagnosis
5 Operational Checklist

1  Resource Ownership and Lifecycle

An IWSConnector is an individual Net4j client connection and can be closed on its own. A managed container owns the products it creates, including factories, acceptors, and connectors; deactivate the container only when its owner is shutting down that container. A shared application container must remain active when one connector closes.

The application owns any custom JDK HttpClient supplied to a provider and must keep it alive while the provider's connectors use it. Java 11 has no HttpClient.close(); Java 21 adds client lifecycle methods. Stop its Net4j connectors before using those newer methods. The application that embeds Jetty owns and stops its Server and listeners.

CloseOneConnector.java      
IManagedContainer sharedContainer = ContainerUtil.createInitializedContainer();
IWSConnector connectorToClose = null;
IWSConnector remainingConnector = null;

try
{
  connectorToClose = WSUtil.getConnector(sharedContainer, URI.create("ws://gateway.example.net/net4j"), "public");
  remainingConnector = WSUtil.getConnector(sharedContainer, URI.create("ws://backup.example.net/net4j"), "public");

  connectorToClose.connect(5_000);
  remainingConnector.connect(5_000);

  connectorToClose.close();
  connectorToClose = null;
  // The remaining connector still uses the shared container after the first connector closes.
}
finally
{
  try
  {
    if (connectorToClose != null)
    {
      connectorToClose.close();
    }
  }
  finally
  {
    try
    {
      if (remainingConnector != null)
      {
        remainingConnector.close();
      }
    }
    finally
    {
      // This snippet owns the example container; a real shared-container owner does this at application shutdown.
      LifecycleUtil.deactivate(sharedContainer);
    }
  }
}

2  Graceful Shutdown

Stop producing new application messages first. Close or drain Net4j protocols and connectors, stop admitting new HTTP upgrades, then stop the application-owned Jetty server. Deactivate the acceptor and its container after the server no longer accepts upgrades. For a container shared with other transports, close only the WebSocket products owned by the stopping component; let the container owner choose when the complete container stops.

Cleanup should use nested finally blocks so an exception while stopping one component does not skip the remaining resource cleanup. Keep HTTP client shutdown after the connectors that borrow the client have stopped. Deploying WebSocket Servers

3  Diagnosing Connection and Upgrade Failures

First verify that the WS core bundle is resolved and that the container contains the provider type named in the connector description. A missing custom provider is reported as an error identifying the message-connection provider type. Next check the endpoint scheme, route name, service path, and whether the expected acceptor is active.

Jetty routing requires the decoded path suffix and the Net4jAcceptor header to name the same acceptor. An unknown, inactive, or mismatched route returns HTTP 404. A request whose secure state does not match its WS/WSS acceptor returns HTTP 403. Check HTTPS termination and trusted forwarding configuration when WSS gets a 403.

For JDK client failures, inspect the complete exception cause chain from connector activation: it can distinguish a provider lookup error, certificate validation error, proxy connection failure, and HTTP upgrade rejection. Do not log complete connector descriptions when they carry cookies.

4  TLS and Proxy Diagnosis

For a certificate error, verify the service host against the certificate name, confirm the server sends its intermediates, check expiry, and inspect the trust store used by the selected provider. For a proxy failure, verify the configured ProxySelector, proxy reachability, and whether the Authenticator receives a proxy or origin challenge. Basic authentication over HTTP CONNECT may be disabled by the JDK unless its JVM-wide disabled-schemes setting is changed; assess the security implications before changing that setting.

The Net4j WS and Jetty adapter bundles declare debug tracing options, but the current WS implementation does not emit transport events through those tracers. Do not expect enabling those options to produce handshake diagnostics. Use the connector exception cause chain and the application's configured Jetty/JDK logging facilities.

5  Operational Checklist

Before release, verify the deployed WS feature and optional Jetty adapter version; confirm that every client provider is registered in the connector's container; keep provider types distinct when clients have different trust or proxy rules; and test each acceptor name over the intended scheme. Confirm certificate renewal and trust-store deployment, cookie expiry and secrecy, proxy credential rotation, bounded connection timeouts, and coordinated shutdown.

During an incident, record the endpoint scheme and host, acceptor name, provider type, connector state, status code, and exception causes. Redact cookie values, credentials, and full descriptions before storing diagnostics.