Skip to main content

The Long Road to Connection Hijacking for Docker in OkHttp

·7 mins

In June 2015, someone asked a seemingly simple question in the docker-java1 issue tracker: How can I send STDIN to a running container? That topic sparked nearly ten years of discussion, workarounds, and technical evolution. The problem wasn’t Docker itself, but a hidden mechanism in its Remote API that few HTTP clients could handle properly. This is the story of how persistence, open collaboration, and technical curiosity finally brought proper connection hijacking support to OkHttp—adding one more solid option to Java’s Docker ecosystem.

History, Challenges, and Platform Struggles #

The core issue lies in Docker’s use of Connection Hijacking2 for certain endpoints like attach and exec. After an initial HTTP request, the connection gets “hijacked” into a bidirectional raw stream where STDIN, STDOUT, and STDERR are multiplexed. While this follows RFC 7230 §6.7 legitimately, it’s rare enough that most HTTP clients never needed to handle it—except for WebSockets.

The Four Transport Evolution #

docker-java1, the most popular Java library for Docker integration, experimented with multiple HTTP transports over the years, each with distinct strengths and limitations:

Jersey (≤2015): The original JAX-RS based transport was simple and reliable for basic Docker operations, but had no support for connection hijacking whatsoever.

Netty (December 2015): The first breakthrough came with PR #397 , which introduced Netty as a transport specifically to solve the hijacking problem. It worked beautifully on Linux using Netty’s native epoll transport, finally enabling attach and exec functionality. However, cross-platform support never materialized—macOS lacked Unix domain socket support in Netty at the time, and Windows named pipes were never implemented.

OkHttp (March 2020): The community’s next hope was OkHttp3—lightweight, modern, and cross-platform. docker-java added OkHttp support in version 3.2.0, and shortly after introduced a HijackingInterceptor in PR #1347 . The solution worked, but only by accessing OkHttp’s internal, unsupported APIs through Internal.instance. This fragility was a known compromise from day one. Notably, OkHttp maintainers participated in the docker-java discussions and subsequently opened issue #5874 to add official HTTP/1.1 Upgrade support—though implementation would take several more years.

Apache HttpClient 5 (June 2020): The final transport arrived with comprehensive feature support via PR #1358 , but early versions (5.0-5.1) lacked the necessary APIs for proper Upgrade handling. Only with Apache HttpClient 5.2 GA in November 2022 did the library deliver robust “HTTP protocol upgrade (async)” support, making it the first truly stable, cross-platform solution for hijacking.

The Testcontainers Migration Story #

Testcontainers-java4, another crucial project in the Java-Docker ecosystem, experienced this evolution firsthand. Initially using docker-java’s Jersey transport, they moved to OkHttp around 2018-2019 for its cross-platform promise and lighter footprint. The OkHttp-based solution served them well despite the internal API dependency.

However, by 2021-2022, Testcontainers made a strategic decision to migrate from OkHttp to Apache HttpClient 5. Several factors drove this change:

  • Stability concerns: Relying on OkHttp’s internal APIs (Internal.instance) was inherently fragile and could break with any OkHttp update
  • OkHttp’s Kotlin migration: As OkHttp increasingly adopted Kotlin internally, the Java interoperability story became more complex
  • Feature completeness: Apache HttpClient 5 supported every Docker API use case without requiring workarounds or internal API access
  • Official upgrade support: With HC5’s 5.2 release, connection hijacking became a first-class, documented feature

This migration was completed in early 2022 with PR #5113 , making Apache HttpClient 5 the default transport for Testcontainers.

Platform-Specific Limitations #

Despite all these transport improvements, some fundamental platform challenges remain unresolved:

Linux: Full hijacking support has been available since Netty’s introduction in December 2015. All transports work reliably with Unix domain sockets.

macOS: Support varies by transport and Docker configuration. TCP-based connections generally work, but Unix socket reliability depends on the specific transport implementation. Note that neither docker-java nor Testcontainers officially support alternative runtimes like Colima .

Windows: The most challenging platform remains partially supported. Connection hijacking via Windows Named Pipes has never been properly implemented in any Java Docker client. On Windows, the TCP API is often considered as stable alternative to the native IPC mechanism.

This platform inconsistency has been a persistent source of frustration for developers trying to build truly cross-platform Docker tooling in Java. But hey, at least it kept us busy for a decade!

Personal Reflections on Open Source Collaboration #

Working on this connection hijacking problem over the years taught me valuable lessons about open source development, particularly through my recent collaboration with Square’s OkHttp team. My work on gesellix/docker-client5—an alternative to docker-java—made me stick with the hijacking problem and continue searching for proper solutions rather than accepting workarounds. I have to admit: I’m a big fan of OkHttp due to its simplicity, small footprint, and few transitive dependencies, which is why my docker-client relies solely on OkHttp. Having my own Docker client implementation meant I needed reliable, long-term solutions, not fragile workarounds that could break with library updates.

The Power of Direct Contribution #

For years, the Java Docker community found various solutions to handle OkHttp’s limitations. While docker-java and Testcontainers had working solutions with Apache HttpClient, we also built interceptors, used internal APIs, and accepted fragility as the price of functionality with OkHttp. The breakthrough came when I decided to contribute directly to OkHttp to provide a proper solution for this use case.

In 2024, I began implementing a proper, public connection hijacking API for OkHttp. The maintainers, including Jesse Wilson, were incredibly welcoming and collaborative. Rather than dismissing a niche use case, they embraced the technical challenge and helped refine the implementation. Importantly, OkHttp saw this as an opportunity to refactor their WebSocket implementation to rely on a more generic Socket abstraction—making the feature beneficial beyond just Docker’s use case. This is open source at its best—when curiosity meets initiative, supported by a culture of openness.

Patience as a Technical Skill #

This decade-long journey taught me that some problems require patience, not just technical skill. Libraries mature, maintainers change, and the ecosystem evolves at its own pace. The “right” solution often emerges when the timing aligns—when maintainers have bandwidth, when the technical foundation is ready, and when the community need is clear.

Rushing to implement workarounds (as we did with OkHttp’s internal APIs) provided short-term relief but created long-term technical debt. Sometimes the better path is to engage with upstream projects and accept that the timeline might be measured in years, not sprints.

The Culture of Collaboration #

Square’s OkHttp team exemplifies what makes open source powerful. They could have easily dismissed connection hijacking as too niche or too complex. Instead, they saw it as an interesting technical challenge worth solving properly. This collaborative spirit—choosing engagement over dismissal—is what transforms individual libraries into ecosystem foundations.

Looking Forward #

By 2025, official Connection Hijacking support has finally landed in OkHttp, ending a decade of workarounds. The gesellix/docker-client5 now builds on a clean, OpenAPI-based foundation through docker-client/docker-remote-api6. Docker attach and interactive exec operations are finally stable across all supported platforms.

This isn’t just a technical success—it’s a reminder that open source works best when we engage with the challenges directly, contribute upstream rather than just consuming, and maintain the patience to see problems through to their proper resolution.

The long road to connection hijacking in Java taught me that the most satisfying solutions aren’t the quick fixes or clever workarounds, but the ones that make the entire ecosystem stronger. Sometimes that takes ten years. Sometimes that’s exactly what it should take.

Looking ahead, one remaining challenge is adding proper Windows Named Pipe support for upgraded connections. While the TCP API provides a stable alternative on Windows, native Named Pipe support would complete the cross-platform story and provide true parity with Docker’s native IPC mechanisms.


Thank You #

A special thank you to Square’s OkHttp team — Jesse Wilson, and the entire team — for their openness, technical expertise, and collaborative spirit. Their willingness to embrace a niche use case and turn it into a broader architectural improvement exemplifies the best of open source development.


This article was created with the assistance of Claude AI to help structure the content, organize the historical timeline, and refine the technical explanations.


  1. docker-java/docker-java - The most popular Java Docker client ↩︎ ↩︎

  2. Docker API: Container Attach - Official Docker API documentation ↩︎

  3. square/okhttp - HTTP client for Java and Android ↩︎

  4. testcontainers/testcontainers-java - Integration testing with Docker ↩︎

  5. gesellix/docker-client - Alternative Java Docker client ↩︎ ↩︎

  6. docker-client/docker-remote-api - OpenAPI-based Docker API client ↩︎