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
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,10 @@ public fun AsyncHttpClient.executeMono(
): Mono<Response> = deferMono { executeAsync(request, options) }

/**
* Pipeline-level [Mono] facade — see [executeMono].
*
* ## MDC propagation
*
* The SLF4J MDC is captured **per subscription**, not at assembly time: the [Mono.defer]
* wrapper snapshots MDC the moment a subscriber subscribes, so each subscription reinstates
* the *subscriber's* `trace.id` / `span.id` — both in the [AsyncHttpPipeline.sendAsync]
* supplier and in the adapter's own `.doOnSubscribe` / `.doOnCancel` hooks, even when those
* fire on a different scheduler. A `Mono` assembled once and re-subscribed therefore picks
* up the MDC live at each subscribe, not a stale snapshot from the assembling thread.
* To extend MDC propagation through user-supplied downstream operators, enable
* `Hooks.enableAutomaticContextPropagation()` at the application level.
* Pipeline-level [Mono] facade — see [executeMono]. Same cold-publisher and
* per-subscription MDC semantics: each subscription reinstates the subscriber's
* `trace.id` / `span.id` for the [AsyncHttpPipeline.sendAsync] supplier and the
* `.doOnSubscribe` / `.doOnCancel` hooks.
*/
public fun AsyncHttpPipeline.sendMono(request: Request): Mono<Response> = deferMono { sendAsync(request) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ import java.util.Locale
* delimiters. Escape sequences (`\"`, `\\`) inside quoted strings are unescaped on
* read; surrounding quotes are stripped from the stored value.
*
* Malformed challenges are skipped — the parser does not throw on bad input. This
* matches the [edge-case guardrails][to-implement.md] for Rank 12.
* Malformed challenges are skipped — the parser does not throw on bad input.
*/
public object AuthChallengeParser {
// Initial capacity for the per-challenge auth-param map. Auth challenges typically
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import org.dexpace.sdk.core.generics.Builder as GenericBuilder

/**
* A single auth alternative an operation accepts: one [AuthScheme] plus any OAuth-specific
* parameters ([oauthScopes] / [oauthParams]) the auth step should forward to the token
* provider when the chosen scheme is [AuthScheme.OAUTH2].
* parameters ([oauthScopes] / [oauthParams], e.g. `claims`) the auth step should forward to
* the token provider when the chosen scheme is [AuthScheme.OAUTH2].
*
* Each requirement pairs a scheme with its *own* OAuth parameters, so an operation that
* accepts OAuth with `read` scopes **or** a static API key is two requirements, only one of
Expand All @@ -27,10 +27,6 @@ import org.dexpace.sdk.core.generics.Builder as GenericBuilder
* copy), so a caller that retains and later mutates the argument collection cannot mutate this
* instance.
* Instances are obtained via [of] or [Builder], never a public constructor.
*
* @param scheme the auth scheme this alternative selects.
* @param oauthScopes OAuth scopes to forward to the token provider; ignored for non-OAuth schemes.
* @param oauthParams extra OAuth params to forward (e.g. `claims`); ignored for non-OAuth schemes.
*/
public class AuthRequirement private constructor(
scheme: AuthScheme,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import java.time.Instant
* pre-emptively refresh by passing a `marginBefore` to [isExpiredAt] — useful for avoiding
* mid-flight expiries during long requests.
*
* Value semantics via `data class` so equal tokens compare equal and can be diffed by
* tests cheaply.
* Value semantics via `data class` gives value-based [equals]/[hashCode], so tokens diff
* cheaply in tests.
*
* @param token the bearer token string; must not be blank.
* @param expiresAt the instant the token becomes invalid, or null for a non-expiring token.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,9 @@ public class DigestChallengeHandler
* absent (legacy RFC 2069 style — we'll still handle it),
* - it carries a `realm` and `nonce`.
*
* Ordering: we scan [preferredAlgorithms] first, then within that scan the
* candidates. This ensures SHA-256 wins over MD5 even when MD5 appears first
* in the header.
*
* Per-challenge validation is delegated to [toCandidate]; this scan only applies
* the algorithm-priority ordering, independent of the order challenges arrived in.
* Per-challenge validation is delegated to [toCandidate]. This scan then applies
* the [preferredAlgorithms] priority ordering — independent of the order challenges
* arrived in — so SHA-256 wins over MD5 even when MD5 appears first in the header.
*/
private fun pickChallenge(
challenges: List<AuthenticateChallenge>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,9 @@ public fun interface HttpClient : AutoCloseable {
): Response = execute(request)

/**
* Releases any resources held by this transport. The default implementation is a no-op so
* SAM literals and lightweight client wrappers do not need to implement [AutoCloseable.close]
* explicitly. Transport implementations that own background threads, connection pools, or
* executors must override this method; see the class-level "Lifecycle" KDoc for the contract.
* Releases any resources held by this transport. The default is a no-op (so SAM literals and
* lightweight wrappers need not implement it); transports that own threads, pools, or executors
* must override it — see the class-level "Lifecycle" KDoc for the contract.
*/
public override fun close() {
// No-op default. Transports with owned resources override this.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ package org.dexpace.sdk.core.generics
* across builders with this helper keeps the missing-field diagnostics identical, which
* matters most once builders are emitted by codegen.
*
* @param name The field name to report in the failure message.
* @param value The field value to validate.
* @return [value], guaranteed non-null.
* @throws IllegalStateException If [value] is `null`.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public data class Headers private constructor(
* Returns the first header value for the given name, or null if none.
*
* @param name the header name (case-insensitive)
* @return the first header value, or null if not found
*/
public fun get(name: String): String? = headersMap[sanitizeName(name)]?.firstOrNull()

Expand Down Expand Up @@ -71,24 +70,19 @@ public data class Headers private constructor(
public fun contains(name: HttpHeaderName): Boolean = headersMap.containsKey(name.caseInsensitiveName)

/**
* Returns a point-in-time snapshot of all header names at the time of the call.
* Returns a snapshot of all header names.
*
* Returns a defensive copy (`Set<String>`) so that callers cannot observe later
* mutations to this [Headers] instance through the returned set. The set is exposed
* through Kotlin's read-only [Set] type.
*
* @return a snapshot of header names
* A defensive copy (`Set<String>`) exposed through Kotlin's read-only [Set] type, so
* callers cannot observe later mutations to this [Headers] instance through it.
*/
public fun names(): Set<String> = headersMap.keys.toSet()

/**
* Returns a point-in-time snapshot of all header entries at the time of the call.
* Returns a snapshot of all header entries.
*
* Each entry is copied into a fresh map so that callers cannot observe later mutations
* to this [Headers] instance through the returned set. The set and its per-name value
* lists are exposed through Kotlin's read-only [Set] / [List] types.
*
* @return a snapshot of header entries as [Map.Entry]
*/
public fun entries(): Set<Map.Entry<String, List<String>>> {
val snapshot = LinkedHashMap<String, List<String>>(headersMap.size)
Expand All @@ -98,11 +92,7 @@ public data class Headers private constructor(
return snapshot.entries
}

/**
* Returns a new [Builder] initialized with the existing headers.
*
* @return a new [Builder]
*/
/** Returns a new [Builder] initialized with the existing headers. */
public fun newBuilder(): Builder = Builder(this)

override fun toString(): String = headersMap.toString()
Expand All @@ -120,8 +110,6 @@ public data class Headers private constructor(

/**
* Creates a new builder initialized with the headers from [headers].
*
* @param headers the headers to initialize from
*/
public constructor(headers: Headers) : this() {
headers.headersMap.forEach { (key, values) ->
Expand All @@ -132,10 +120,6 @@ public data class Headers private constructor(
/**
* Adds a header with the specified name and value.
* Multiple headers with the same name are allowed.
*
* @param name the header name
* @param value the header value
* @return this builder
*/
public fun add(
name: String,
Expand All @@ -144,10 +128,6 @@ public data class Headers private constructor(

/**
* Adds all header values for the specified name.
*
* @param name the header name
* @param values the list of header values
* @return this builder
*/
public fun add(
name: String,
Expand Down Expand Up @@ -175,7 +155,6 @@ public data class Headers private constructor(
*
* @param name the header name
* @param value the header value, which may contain non-ASCII bytes
* @return this builder
* @throws IllegalArgumentException if [name] is blank or contains a control/non-ASCII byte,
* or [value] contains a prohibited control character
*/
Expand Down Expand Up @@ -213,10 +192,6 @@ public data class Headers private constructor(
* Sets the header with the specified name to the single value provided.
* If headers with this name already exist, they are removed. Passing a `null`
* [value] removes the header entirely (Azure / OkHttp semantics).
*
* @param name the header name
* @param value the header value, or `null` to remove
* @return this builder
*/
public fun set(
name: String,
Expand All @@ -233,10 +208,6 @@ public data class Headers private constructor(
/**
* Sets the header with the specified name to the values list provided.
* If headers with this name already exist, they are removed.
*
* @param name the header name
* @param values the header values
* @return this builder
*/
public fun set(
name: String,
Expand Down Expand Up @@ -278,9 +249,6 @@ public data class Headers private constructor(

/**
* Removes any header with the specified name.
*
* @param name the header name
* @return this builder
*/
public fun remove(name: String): Builder =
apply {
Expand Down Expand Up @@ -309,11 +277,7 @@ public data class Headers private constructor(
}
}

/**
* Builds an immutable [Headers] instance.
*
* @return the built [Headers]
*/
/** Builds an immutable [Headers] instance. */
public fun build(): Headers {
// Deep, defensive copy: snapshot each value list into its own fresh copy so
// that later builder mutations cannot reach into the built instance. The
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,8 @@ public class HttpRange private constructor(private val raw: String) {
}

/**
* Parses a `Range` header value into an [HttpRange]. The accepted value is stored
* verbatim and round-trips through [toHeaderValue] unchanged, so its exact casing is
* preserved on the wire.
*
* The value must begin with the `bytes=` unit token (matched case-insensitively, with no
* leading whitespace); only the `bytes` unit is accepted, and multi-range values are
* rejected.
* Parses a `Range` header value into an [HttpRange], storing it verbatim so its casing
* round-trips through [toHeaderValue] (see the class KDoc for the accepted-form contract).
*
* @throws IllegalArgumentException if the value does not begin with the `bytes=` unit
* token or contains a comma (multi-range).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ public data class MediaType private constructor(
* in a try/catch.
*
* The lookup is case-insensitive: `UTF-8`, `utf-8`, and `Utf-8` all resolve to the same
* [Charset]. The parameter value is lower-cased before being passed to [Charset.forName],
* normalising the cache key. The JDK accepts any case, but lowercasing here ensures a
* consistent canonical form for comparison and logging purposes.
* [Charset]. The value is lower-cased before [Charset.forName] — the JDK accepts any case,
* but a canonical lower-case form normalises the cache key and keeps comparison and logging
* consistent.
*/
val charset: Charset?
get() =
Expand Down Expand Up @@ -222,7 +222,6 @@ public data class MediaType private constructor(
* Parses a media type string into a [MediaType].
*
* @param mediaType the wire-form value (e.g. `application/json;charset=utf-8`).
* @return the parsed [MediaType].
* @throws IllegalArgumentException if the media type cannot be parsed.
*/
@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ import org.dexpace.sdk.core.instrumentation.InstrumentationContext
* promotes this into an [ExchangeContext]. Inherits the chain's [callKey] from the
* [DispatchContext] it was promoted from.
*
* In the normal flow the [callKey] is supplied by [DispatchContext.toRequestContext] so the
* whole chain shares one store slot. When this context is constructed directly off-chain it
* defaults to a freshly generated, call-unique key (`traceId:spanId:n`) via
* [DispatchContext.generateCallKey] — see [DispatchContext] for why the trace/span pair alone is not
* a collision-safe store key. One consequence of the generated default: two default-constructed
* instances are not structurally equal, since each generates a distinct key — pin an explicit
* In the normal flow the [callKey] is supplied by [DispatchContext.toRequestContext] so the whole
* chain shares one store slot. When constructed directly off-chain, [callKey] defaults to a fresh
* call-unique key (`traceId:spanId:n`) via [DispatchContext.generateCallKey] — see [DispatchContext]
* for why the trace/span pair alone is not a collision-safe key. Because each default generates a
* distinct key, two default-constructed instances are not structurally equal; pin an explicit
* [callKey] if you need equality.
*
* @property operationName Optional schema-defined operation id (e.g. `"GetUser"`) for this call,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,10 @@ public class AsyncHttpPipeline internal constructor(

/**
* Runs [request] through the pipeline with per-call [options] applied, then maps the response
* with [handler], returning the typed result. Combines the per-call [options] threading of
* [sendAsync]`(request, options)` with the terminal mapping of [sendAsync]`(request, handler)`;
* equivalent to `sendAsync(request, options).handleWith(handler)`. On success the handler's value
* completes the future and the response is closed; on failure the future is failed with the
* unwrapped cause. See [handleWith] for the exact semantics.
* with [handler]. Combines the per-call [options] threading of [sendAsync]`(request, options)`
* with the terminal mapping of [sendAsync]`(request, handler)`; equivalent to
* `sendAsync(request, options).handleWith(handler)`. See [handleWith] for the success/failure
* and response-close semantics.
*/
public fun <T> sendAsync(
request: Request,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,9 @@ public class AsyncHttpPipelineBuilder(private val httpClient: AsyncHttpClient) {

/**
* Seeds a new builder from [pipeline] by **flattening** it: [pipeline]'s steps become this
* builder's steps and its transport becomes this builder's terminal transport (identical to
* [from]). Prefer this over the generic [AsyncHttpClient] transport constructor whenever the
* argument is an [AsyncHttpPipeline] — the transport constructor would instead **nest** the
* pipeline as an opaque transport, running the outer steps outside the inner pipeline's
* retry / auth loops. Because [AsyncHttpPipeline] is more specific than [AsyncHttpClient],
* `AsyncHttpPipelineBuilder(pipeline)` resolves to this flattening constructor.
* builder's steps and its transport its terminal transport (identical to [from]). Overload
* resolution already picks this constructor for a statically-typed [AsyncHttpPipeline]; see the
* primary constructor KDoc for the flatten-vs-nest distinction.
*/
public constructor(pipeline: AsyncHttpPipeline) : this(pipeline.httpClient) {
steps.reload(pipeline.steps)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,10 @@ public class HttpPipelineBuilder(private val httpClient: HttpClient) {
/**
* Seeds a new builder from [pipeline] by **flattening** it: [pipeline]'s steps become this
* builder's steps and its transport becomes this builder's terminal transport (identical to
* [from]). Prefer this over the generic [HttpClient] transport constructor whenever the argument
* is an [HttpPipeline] — the transport constructor would instead **nest** the pipeline as an
* opaque transport, running the outer steps outside the inner pipeline's redirect / retry / auth
* loops. Because [HttpPipeline] is more specific than [HttpClient], `HttpPipelineBuilder(pipeline)`
* resolves to this flattening constructor.
* [from]). Prefer this over the generic [HttpClient] transport constructor when the argument
* is an [HttpPipeline]; that constructor would instead **nest** the pipeline (see the primary
* `@constructor` KDoc for the nesting hazard). Because [HttpPipeline] is more specific than
* [HttpClient], `HttpPipelineBuilder(pipeline)` resolves to this flattening constructor.
*/
public constructor(pipeline: HttpPipeline) : this(pipeline.httpClient) {
steps.reload(pipeline.steps)
Expand All @@ -63,9 +62,9 @@ public class HttpPipelineBuilder(private val httpClient: HttpClient) {
public fun appendAll(batch: Iterable<HttpStep>): HttpPipelineBuilder = apply { batch.forEach(::append) }

/**
* Prepends every step in [batch] via [prepend], preserving iteration order. Note that
* because each call uses [prepend], the final relative ordering inside each stage is the
* reverse of [batch] — the last item ends up at the head.
* Prepends every step in [batch] via [prepend], preserving iteration order. Because each
* call uses [prepend], the final relative ordering inside each stage is the reverse of
* [batch] — the last item ends up at the head.
*/
public fun prependAll(batch: Iterable<HttpStep>): HttpPipelineBuilder = apply { batch.forEach(::prepend) }

Expand Down
Loading
Loading