Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk-transport-jdkhttp/api/sdk-transport-jdkhttp.api
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public final class org/dexpace/sdk/transport/jdkhttp/JdkHttpTransport$Companion
public final fun builder ()Lorg/dexpace/sdk/transport/jdkhttp/JdkHttpTransport$Builder;
public final fun create (Ljava/net/http/HttpClient;)Lorg/dexpace/sdk/transport/jdkhttp/JdkHttpTransport;
public final fun create (Ljava/net/http/HttpClient;Ljava/time/Duration;)Lorg/dexpace/sdk/transport/jdkhttp/JdkHttpTransport;
public static synthetic fun create$default (Lorg/dexpace/sdk/transport/jdkhttp/JdkHttpTransport$Companion;Ljava/net/http/HttpClient;Ljava/time/Duration;ILjava/lang/Object;)Lorg/dexpace/sdk/transport/jdkhttp/JdkHttpTransport;
}

public final class org/dexpace/sdk/transport/jdkhttp/JdkHttpTransport$HttpVersion : java/lang/Enum {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,23 +236,17 @@ public class JdkHttpTransport private constructor(
* BYO factory: wrap a fully-configured [java.net.http.HttpClient]. The supplied
* client is used verbatim — the SDK does not override connect timeout, redirect
* policy, version, or executor, and [close] will NOT shut down this client (the
* caller owns its lifecycle). No per-request response timeout is applied.
*/
@JvmStatic
public fun create(client: java.net.http.HttpClient): JdkHttpTransport =
JdkHttpTransport(client, null, owned = false, ownedExecutor = null)

/**
* BYO factory with a per-request response timeout. The timeout is applied to every
* outgoing request via [java.net.http.HttpRequest.Builder.timeout] — the JDK client
* does not expose a global response-timeout knob. Pass `null` for no per-request
* timeout; if a timeout is desirable but no specific value is needed, use the
* single-argument [create] overload (which captures `null`).
* caller owns its lifecycle).
*
* [responseTimeout], when non-`null`, is applied to every outgoing request via
* [java.net.http.HttpRequest.Builder.timeout] — the JDK client does not expose a
* global response-timeout knob. Defaults to `null` (no per-request timeout).
*/
@JvmStatic
@JvmOverloads
public fun create(
client: java.net.http.HttpClient,
responseTimeout: Duration?,
responseTimeout: Duration? = null,
): JdkHttpTransport = JdkHttpTransport(client, responseTimeout, owned = false, ownedExecutor = null)

/** Returns a fresh [Builder] for SDK-managed [java.net.http.HttpClient] construction. */
Expand Down Expand Up @@ -493,7 +487,7 @@ public class JdkHttpTransport private constructor(
* from [ProxyOptions] are honoured: hosts the [ProxyOptions] would bypass return
* `Proxy.NO_PROXY`, everything else returns the configured proxy.
*
* Defined as a top-level private inner class so the builder closure stays clean.
* Defined as a private nested class so the builder closure stays clean.
*/
private class NonProxyHostSelector(
private val options: ProxyOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package org.dexpace.sdk.transport.jdkhttp.internal
import org.dexpace.sdk.core.instrumentation.ClientLogger
import org.dexpace.sdk.core.io.Io
import java.io.Closeable
import java.io.FilterInputStream
import java.io.IOException
import java.io.InputStream
import java.io.InterruptedIOException
Expand Down Expand Up @@ -130,8 +131,9 @@ internal object BodyPublishers {
* memory is released when the array is handed off.
*/
private fun eagerPublisher(body: SdkRequestBody): HttpRequest.BodyPublisher {
val bytes = bufferToByteArray(body)
return HttpRequest.BodyPublishers.ofByteArray(bytes)
val buffer = Io.provider.buffer()
body.writeTo(buffer)
return HttpRequest.BodyPublishers.ofByteArray(buffer.snapshot())
}

/**
Expand Down Expand Up @@ -245,16 +247,6 @@ internal object BodyPublishers {
.log("piped body writer failed; reader side will see IOException")
}

/**
* Drains [body] into a fresh in-memory [org.dexpace.sdk.core.io.Buffer] from the active
* [Io.provider] and snapshots it to a byte array.
*/
private fun bufferToByteArray(body: SdkRequestBody): ByteArray {
val buffer = Io.provider.buffer()
body.writeTo(buffer)
return buffer.snapshot()
}

/**
* Wraps a subscription's [PipedInputStream] so that closing the read end also cancels the
* writer [Future] and closes the [PipedOutputStream] write end. Without this, a JDK
Expand All @@ -268,21 +260,15 @@ internal object BodyPublishers {
private val pipeIn: PipedInputStream,
private val pipeOut: PipedOutputStream,
private val writer: Future<*>,
) : InputStream() {
override fun read(): Int = pipeIn.read()

override fun read(
b: ByteArray,
off: Int,
len: Int,
): Int = pipeIn.read(b, off, len)

override fun available(): Int = pipeIn.available()

) : FilterInputStream(pipeIn) {
override fun close() {
// Interrupt/cancel the writer first so a thread parked in PipedOutputStream.write
// wakes up, then close both pipe ends. Order matters: closing pipeOut alone does
// not unblock a writer mid-write, so the cancel(true) is what frees the thread.
//
// Deliberately does NOT call super.close(): FilterInputStream.close() would close
// only the wrapped read end, but teardown here must also cancel the writer and
// close the write end. closeQuietly(pipeIn) below covers the read end.
writer.cancel(true)
closeQuietly(pipeOut)
closeQuietly(pipeIn)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ public class OkHttpTransport private constructor(
*/
private val owned: Boolean,
) : HttpClient, AsyncHttpClient {
private val log: ClientLogger = ClientLogger("org.dexpace.sdk.transport.okhttp.OkHttpTransport")
private val requestAdapter: RequestAdapter = RequestAdapter(log)
private val responseAdapter: ResponseAdapter = ResponseAdapter(log)

Expand Down Expand Up @@ -248,6 +247,15 @@ public class OkHttpTransport private constructor(
}

public companion object {
/**
* Logger shared by the transport and its [Builder]. A single instance is intentional:
* both call sites log under the same fully-qualified category, so one [ClientLogger]
* is the single source of truth for that category. `ClientLogger` wraps a thread-safe
* SLF4J `Logger` and carries no caller-specific state, so sharing it across the
* transport instance and every `Builder` is safe.
*/
private val log: ClientLogger = ClientLogger("org.dexpace.sdk.transport.okhttp.OkHttpTransport")

/**
* BYO factory: wrap a fully-configured [OkHttpClient]. The supplied client is used
* verbatim — the SDK does not override `followRedirects`, timeouts, or interceptors,
Expand All @@ -271,7 +279,6 @@ public class OkHttpTransport private constructor(
* — letting OkHttp follow redirects underneath would double-handle them.
*/
public class Builder internal constructor() : SdkBuilder<OkHttpTransport> {
private val log: ClientLogger = ClientLogger("org.dexpace.sdk.transport.okhttp.OkHttpTransport")
private var connectTimeout: Duration? = null
private var readTimeout: Duration? = null
private var writeTimeout: Duration? = null
Expand Down Expand Up @@ -419,7 +426,7 @@ public class OkHttpTransport private constructor(
* [ProxyOptions] are honoured: hosts the [ProxyOptions] would bypass return
* `Proxy.NO_PROXY`, everything else returns the configured proxy.
*
* Defined as a top-level private inner class so the builder closure stays clean.
* Defined as a private nested class so the builder closure stays clean.
*/
private class NonProxyHostSelector(
private val options: ProxyOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,6 @@ internal class RequestAdapter(
* Shared zero-length body substituted for a body-less POST/PUT/PATCH. Immutable and
* stateless, so a single instance is safe to reuse across requests and threads.
*/
private val EMPTY_BODY: RequestBody = ByteArray(0).toRequestBody(null, 0, 0)
private val EMPTY_BODY: RequestBody = ByteArray(0).toRequestBody()
}
}
Loading