Charles
Charles

Reputation: 165

Access underlying TCP streams during call using OkHttp

I've implemented a client for the Docker API.

The endpoint for attaching to a container's output is a bit unusual in that you need to hijack the underlying TCP stream and use that to read the output from the container.

Documentation for API endpoint: https://docs.docker.com/engine/api/v1.37/#operation/ContainerAttach (Unfortunately the web socket version of this endpoint is broken on OS X, so I can't use that.)

I've been using this code to get access to the streams up to and including OkHttp v3.13.1:

class ConnectionHijacker : Interceptor {
    var source: BufferedSource? = null
    var sink: BufferedSink? = null

    override fun intercept(chain: Interceptor.Chain): Response {
        val connection = chain.connection() as RealConnection
        val streams = connection.newWebSocketStreams(connection.allocations.single().get())

        sink = streams.sink
        source = streams.source

        return chain.proceed(chain.request())
    }
}

This works perfectly.

However, in more recent versions, the OkHttp API has changed significantly. Something like this works with v1.13.1 and compiles with later versions, but does not give me any output in the stream:

class ConnectionHijacker : Interceptor {
    var source: BufferedSource? = null
    var sink: BufferedSink? = null

    override fun intercept(chain: Interceptor.Chain): Response {
        val connection = chain.connection() as RealConnection

        sink = connection.sink
        source = connection.source

        return chain.proceed(chain.request())
    }

    private val RealConnection.sink: BufferedSink
        get() {
            val property = RealConnection::class.declaredMemberProperties.single { it.name == "sink" }
            property.isAccessible = true
            return property.get(this) as BufferedSink
        }

    private val RealConnection.source: BufferedSource
        get() {
            val property = RealConnection::class.declaredMemberProperties.single { it.name == "source" }
            property.isAccessible = true
            return property.get(this) as BufferedSource
        }
}

I realise this is a hack on top of another hack and completely unsupported, but does anyone have any ideas on how I can make this work?

Upvotes: 1

Views: 240

Answers (1)

Jesse Wilson
Jesse Wilson

Reputation: 40603

If you can use HTTP/2, then take a look at RequestBody.isDuplex(). It'll let you access the request output and the response input to do what you like.

Upvotes: 1

Related Questions