A custom TCP protocol can travel through an HTTP proxy when that proxy permits CONNECT to the destination. The application protocol does not have to become HTTP: HTTP establishes the tunnel, then the application exchanges its own bytes inside it.

OpenJDK 25 provides an HTTP-proxy path in Socket(Proxy). Its implementation performs the CONNECT exchange and returns a connected socket for the application to use.

Proxy route = new Proxy(
    Proxy.Type.HTTP,
    new InetSocketAddress("proxy.example", 8080));

try (Socket socket = new Socket(route)) {
    socket.setSoTimeout(10000);
    socket.connect(
        InetSocketAddress.createUnresolved("tcp-service.example", 8443),
        5000);
    InputStream input = socket.getInputStream();
    OutputStream output = socket.getOutputStream();
}

This fragment uses classes from java.net and java.io. Replace the two endpoints and perform the protocol exchange before leaving the try block. For a permitted public TCP target, choose HTTP candidates from the HTTP proxy list only after checking CONNECT permission for that target's port. The proxy address is where Java connects first; the address passed to connect identifies the service the proxy should reach.

The unresolved destination preserves its hostname for this implementation's CONNECT request. A resolved InetSocketAddress instead supplies its resolved IP address to that request. That distinction matters when the proxy must resolve the service name or apply hostname-based policy.

What a successful connection leaves you with

The resulting streams carry the tunneled service's data. They do not expose a separate HTTP response that your application should read and discard. Adding a second CONNECT exchange would send HTTP text to the service after the JDK has already established the tunnel.

The example supplies a connection timeout and a socket read timeout; those are separate controls, not one overall deadline for the application session.

CONNECT permission still belongs to the proxy. An endpoint that forwards ordinary web requests may reject the service's port or require authentication before opening a tunnel. Keep those failures distinct from a server that accepts the tunnel but later rejects your application protocol.

If that protocol needs TLS, negotiate TLS through the connected socket with the service's intended hostname and normal certificate checks. CONNECT alone does not encrypt the stream. For an actual HTTP API, an HTTP client remains the more convenient interface because it also handles HTTP messages.

Winndannde jowitiiɗe heen