Skip to content
Open
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
93 changes: 76 additions & 17 deletions core-context-propagation-quarkus/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,27 +133,89 @@ Propagates and allows to get `X-Channel-Request-Id` value. If an incoming reques

**Default behavior:** `X-Channel-Request-Id` is NOT propagated to outgoing requests.

**Enabling propagation:** To allow `X-Channel-Request-Id` to be propagated to outgoing requests, remove it from the
blacklist using one of the following methods:
#### Restricted contexts

The framework owns a hard-coded list of restricted contexts — headers that are not propagated
to outgoing requests by default. It currently contains:

- `X-Channel-Request-Id`

The list itself cannot be changed from configuration. The only externally visible knob is the
`quarkus.context.propagation.headers.enable.optional` property, which names headers from that list
that should be enabled for propagation.

#### `quarkus.context.propagation.headers.enable.optional` property

The property carries a comma-separated list of header names. Every name that matches a restricted
context is dropped from the effective restricted list (and thus becomes eligible for propagation).
Names that are not in the restricted list have no effect.

Examples:

| Property value | Effect |
|---|---|
| not set / empty | Restricted list applies in full. `X-Channel-Request-Id` is not propagated. |
| `X-Channel-Request-Id` | `X-Channel-Request-Id` is propagated to outgoing requests. |
| `Some-Other-Header` | No effect — the header is not in the restricted list. |
| `X-Channel-Request-Id, Some-Other-Header` | `X-Channel-Request-Id` is propagated; the second entry is ignored. |

Comparison is case-insensitive. Whitespace around comma-separated entries is trimmed.

#### How to set the property

**Via `application.properties`:**

```properties
quarkus.context.propagation.headers.enable.optional=X-Channel-Request-Id
```

**Via `application.yaml`:**

```yaml
quarkus:
context:
propagation:
headers:
enable:
optional: X-Channel-Request-Id
```

**Via JVM system property:**

1. **Via environment variable:**
```text
HEADERS_BLOCKED=
-Dquarkus.context.propagation.headers.enable.optional=X-Channel-Request-Id
```

2. **Via application.properties (Quarkus):**
**Sourcing the value from an environment variable**

Instead of hard-coding the list of enabled headers in `application.properties` / `application.yaml`,
the value can be sourced from an environment variable using a standard `${ENV_VAR:default}` placeholder.
This way the file structure stays the same across environments and the actual list is controlled
externally — through an ENV variable that is set somewhere outside Quarkus (a container manifest,
a Helm chart, `systemd` unit, CI variable, local shell, etc.).

```properties
quarkus.headers.blocked=
quarkus.context.propagation.headers.enable.optional=${CONTEXT_PROPAGATION_HEADERS_ENABLE_OPTIONAL:}
```

**`quarkus.headers.blocked` rules and limitations**
or, equivalently, in `application.yaml`:

- Source priority: system property `quarkus.headers.blocked` overrides environment variable `HEADERS_BLOCKED`.
- Default when not configured at all: `X-Channel-Request-Id` is blocked.
- Explicit empty value (`quarkus.headers.blocked=` / `HEADERS_BLOCKED=`): blacklist is empty (nothing is blocked).
- Explicit non-empty value with valid headers (for example `quarkus.headers.blocked=Some-Header`): only listed headers are blocked.
- `X-Request-Id` is non-blockable: if it is listed in `quarkus.headers.blocked`/`HEADERS_BLOCKED`, it is ignored.
- If configured value contains only non-blockable entries (for example only `X-Request-Id`), default block is applied and `X-Channel-Request-Id` remains blocked.
```yaml
quarkus:
context:
propagation:
headers:
enable:
optional: ${CONTEXT_PROPAGATION_HEADERS_ENABLE_OPTIONAL:}
```

The trailing `:` (empty default) lets the property gracefully resolve to an empty value when the ENV
variable is absent — under our model that is equivalent to "not configured" and the restricted list
applies in full. Setting `CONTEXT_PROPAGATION_HEADERS_ENABLE_OPTIONAL=X-Channel-Request-Id` in the runtime
environment enables that header for propagation without touching the YAML.

The ENV variable name in the placeholder is just a contract between the application file and the runtime
environment — it can be any name, as long as both sides agree.

**MDC Integration:** The channel request ID is automatically stored in MDC under the key `x_channel_request_id` for use in
logging.
Expand Down Expand Up @@ -481,7 +543,4 @@ executorService.submit(contextPropagationCallable).get();
Sometimes, you may use `CompletableFuture` class and this way it would be convenient to use `ContextPropagationSupplier` delegator. This class takes delegate and context snapshot.
If you want to perform a task in a current context then you can perform the following code:

```java
ContextPropagationSupplier contextPropagationSupplier = new ContextPropagationSupplier(ContextManager.createContextSnapshot(), delegate);
```

`
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.netcracker.cloud.quarkus</groupId>
<artifactId>cloud-core-context-propagation-bom-aggregator</artifactId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>cloud-core-context-propagation-bom-internal</artifactId>
Expand All @@ -15,7 +15,7 @@
<packaging>pom</packaging>

<properties>
<cloud-core.core-context-propagation.version>8.2.0-SNAPSHOT</cloud-core.core-context-propagation.version>
<cloud-core.core-context-propagation.version>8.2.1-SNAPSHOT</cloud-core.core-context-propagation.version>

<wagon-provider-api.version>3.5.3</wagon-provider-api.version>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.netcracker.cloud.quarkus</groupId>
<artifactId>cloud-core-context-propagation-bom</artifactId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>

<name>Cloud-Core Context Propagation BOM</name>
<packaging>pom</packaging>
Expand Down
2 changes: 1 addition & 1 deletion core-context-propagation-quarkus/bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.netcracker.cloud.quarkus</groupId>
<artifactId>cloud-core-context-propagation</artifactId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion core-context-propagation-quarkus/build-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.netcracker.cloud.quarkus</groupId>
<artifactId>cloud-core-context-propagation</artifactId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.netcracker.cloud.quarkus</groupId>
<artifactId>context-propagation-parent</artifactId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.netcracker.cloud.quarkus</groupId>
<artifactId>cloud-core-context-propagation-build-parent</artifactId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
<relativePath>../build-parent/pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>context-propagation-parent</artifactId>
<groupId>com.netcracker.cloud.quarkus</groupId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.netcracker.cloud.context.propagation.quarkus.runtime.filter;

import com.netcracker.cloud.context.propagation.core.ContextManager;
import com.netcracker.cloud.framework.contexts.xchannelrequestid.XChannelRequestIdContextProvider;
import com.netcracker.cloud.headerstracking.filters.context.ChannelRequestIdContext;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpServerRequest;
Expand Down Expand Up @@ -32,7 +33,7 @@ void testDoFilterWithXChannelRequestIdHeader() {
HttpServerRequest httpServerRequest = mock(HttpServerRequest.class);
when(routingContext.request()).thenReturn(httpServerRequest);

String xChannelRequestId = "X-Channel-Request-Id";
String xChannelRequestId = XChannelRequestIdContextProvider.X_CHANNEL_REQUEST_ID_CONTEXT_NAME;
String xChannelRequestIdValue = "channel-123";

MultiMap multiMap = new HeadersMultiMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<artifactId>framework-contexts-parent</artifactId>
<groupId>com.netcracker.cloud.quarkus</groupId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.netcracker.cloud.framework.quarkus.contexts.allowedheaders.HeadersAllowedConfig;
import com.netcracker.cloud.framework.quarkus.contexts.allowedheaders.HeadersAllowedRecorder;
import com.netcracker.cloud.framework.quarkus.contexts.xchannelrequestid.HeadersOptionalConfig;
import com.netcracker.cloud.framework.quarkus.contexts.xchannelrequestid.HeadersOptionalRecorder;

import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
Expand All @@ -20,14 +23,22 @@ FeatureBuildItem feature() {

@BuildStep
UnremovableBeanBuildItem unremovableBeans() {
return UnremovableBeanBuildItem.beanTypes(HeadersAllowedConfig.class);
return UnremovableBeanBuildItem.beanTypes(
HeadersAllowedConfig.class,
HeadersOptionalConfig.class);
}

@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
ServiceStartBuildItem fillSystemProperty(HeadersAllowedRecorder headersAllowedRecorder) {
headersAllowedRecorder.setAllowedHeadersToSystemProperty();

ServiceStartBuildItem fillAllowedHeadersSystemProperty(HeadersAllowedRecorder recorder) {
recorder.setAllowedHeadersToSystemProperty();
return new ServiceStartBuildItem("allowedHeadersRecord");
}

@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
ServiceStartBuildItem fillOptionalHeadersSystemProperty(HeadersOptionalRecorder recorder) {
recorder.setEnableOptionalToSystemProperty();
return new ServiceStartBuildItem("optionalHeadersRecord");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>com.netcracker.cloud.quarkus</groupId>
<artifactId>cloud-core-context-propagation-build-parent</artifactId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
<relativePath>../build-parent/pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<artifactId>framework-contexts-parent</artifactId>
<groupId>com.netcracker.cloud.quarkus</groupId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,10 @@
@ConfigMapping(prefix = "quarkus")
@ConfigRoot(phase = ConfigPhase.RUN_TIME)
public interface HeadersAllowedConfig {

/**
* Allowed headers to propagate in contexts
* Allowed headers to propagate in contexts.
*/
@WithName("headers.allowed")
Optional<String> allowedHeaders();

/**
* Blocked headers for context propagation. X-Channel-Request-Id is blocked by default.
*/
@WithName("headers.blocked")
Optional<String> blockedHeaders();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ public class HeadersAllowedRecorder {

public void setAllowedHeadersToSystemProperty() {
HeadersAllowedConfig allowedConfig = Arc.container().instance(HeadersAllowedConfig.class).get();
allowedConfig.allowedHeaders().ifPresent(allowedHeaders -> System.setProperty("headers.allowed", allowedHeaders));
allowedConfig.blockedHeaders().ifPresent(blockedHeaders -> System.setProperty("headers.blocked", blockedHeaders));

allowedConfig.allowedHeaders()
.ifPresent(allowedHeaders -> System.setProperty("headers.allowed", allowedHeaders));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.netcracker.cloud.framework.quarkus.contexts.xchannelrequestid;

import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithName;

import java.util.List;
import java.util.Optional;

/**
* Configuration for headers that are restricted from propagation by default and may be
* optionally enabled via {@code quarkus.context.propagation.headers.enable.optional}.
*
* <p>The framework owns a hard-coded list of restricted headers (currently {@code X-Channel-Request-Id})
* that are not propagated to outgoing requests by default. This configuration lets users opt in
* to propagating one or more of those restricted headers by listing them, comma-separated, in the
* {@code quarkus.context.propagation.headers.enable.optional} property.</p>
*
*/
@ConfigMapping(prefix = "quarkus.context.propagation.headers")
@ConfigRoot(phase = ConfigPhase.RUN_TIME)
public interface HeadersOptionalConfig {

/**
* Comma-separated list of restricted headers to enable for propagation. Header names that
* are not part of the framework's restricted list are ignored.
*
* <p>When the value is absent or empty, the restricted list applies in full.</p>
*/
@WithName("enable.optional")
Optional<List<String>> enableOptional();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.netcracker.cloud.framework.quarkus.contexts.xchannelrequestid;

import com.netcracker.cloud.framework.contexts.xchannelrequestid.HeaderPropagationConfiguration;

import io.quarkus.arc.Arc;
import io.quarkus.runtime.annotations.Recorder;

/**
* Bridges {@link HeadersOptionalConfig} to the JVM system property
* {@link HeaderPropagationConfiguration#ENABLE_OPTIONAL_PROPERTY} which the non-Quarkus
* framework reads to compute the effective restricted list.
*/
@Recorder
public class HeadersOptionalRecorder {

public void setEnableOptionalToSystemProperty() {
HeadersOptionalConfig config = Arc.container().instance(HeadersOptionalConfig.class).get();

config.enableOptional()
.filter(list -> !list.isEmpty())
.ifPresent(list -> System.setProperty(
HeaderPropagationConfiguration.ENABLE_OPTIONAL_PROPERTY,
String.join(",", list)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>com.netcracker.cloud.quarkus</groupId>
<artifactId>integration-tests</artifactId>
<version>9.1.0-SNAPSHOT</version>
<version>9.1.1-SNAPSHOT</version>
</parent>

<artifactId>context-propagation-reactive-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

import java.util.Optional;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@QuarkusTest
class HeadersAllowedConfigTest {

@Inject
HeadersAllowedConfig headersAllowedConfig;

Expand All @@ -20,6 +22,4 @@ void shouldReadHeadersAllowedFromProperty() {
assertTrue(value.isPresent(), "quarkus.headers.allowed must be present");
assertEquals("test-quarkus.headers.allowed", value.get());
}

}

Loading
Loading