From 3b3ddb1317644c1f7cc0cf51a2ee6fbaa6f99795 Mon Sep 17 00:00:00 2001
From: Ksiona
Date: Thu, 14 May 2026 14:03:28 +0400
Subject: [PATCH 1/6] fix: documentation mismatch
---
core-context-propagation-quarkus/README.md | 22 +++++++++-------------
core-context-propagation/README.md | 2 +-
2 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/core-context-propagation-quarkus/README.md b/core-context-propagation-quarkus/README.md
index 198fa4944..7d2a2f34c 100644
--- a/core-context-propagation-quarkus/README.md
+++ b/core-context-propagation-quarkus/README.md
@@ -29,6 +29,7 @@ Design overview: [context-propagation diagram](./design.png)
- [Allowed headers](#allowed-headers)
- [API version](#api-version)
- [X-Request-Id](#x-request-id)
+ - [X-Channel-Request-Id](#x-channel-request-id)
- [X-Version](#x-version)
- [X-Version-Name](#x-version-name)
- [X-Nc-Client-Ip](#x-nc-client-ip)
@@ -130,7 +131,7 @@ String xRequestId = xRequestIdContextObject.getRequestId();
Propagates and allows to get `X-Channel-Request-Id` value. If an incoming request does not contain the `X-Channel-Request-Id` header then a random value is not generated and the value defaults to placeholder "-". This context is **blocked by default** and will not be propagated to outgoing requests.
-**Default behavior:** `X-Channel-Request-Id` is NOT propagated to outgoing responses.
+**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:
@@ -140,23 +141,18 @@ blacklist using one of the following methods:
HEADERS_BLOCKED=
```
-2. **Via system property:**
-```text
--Dheaders.blocked=
-```
-
-3. **Via application.properties (Quarkus):**
+2. **Via application.properties (Quarkus):**
```properties
-headers.blocked=
+quarkus.headers.blocked
```
-**`headers.blocked` rules and limitations**
+**`quarkus.headers.blocked` rules and limitations**
-- Source priority: system property `headers.blocked` overrides environment variable `HEADERS_BLOCKED`.
+- 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 (`headers.blocked=` / `HEADERS_BLOCKED=`): blacklist is empty (nothing is blocked).
-- Explicit non-empty value with valid headers (for example `headers.blocked=Some-Header`): only listed headers are blocked.
-- `X-Request-Id` is non-blockable: if it is listed in `headers.blocked`/`HEADERS_BLOCKED`, it is ignored.
+- 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.
**MDC Integration:** The channel request ID is automatically stored in MDC under the key `x_channel_request_id` for use in
diff --git a/core-context-propagation/README.md b/core-context-propagation/README.md
index c7a8c6301..40f550846 100644
--- a/core-context-propagation/README.md
+++ b/core-context-propagation/README.md
@@ -145,7 +145,7 @@ Access:
Propagates and allows to get `X-Channel-Request-Id` value. If an incoming request does not contain the `X-Channel-Request-Id` header then a random value is not generated and the value defaults to placeholder "-". This context is **blocked by default** and will not be propagated to outgoing requests.
-**Default behavior:** `X-Channel-Request-Id` is NOT propagated to outgoing responses.
+**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:
From 3bba46267d00c0ac668e057e16e9026d0f1cdb75 Mon Sep 17 00:00:00 2001
From: Ksiona
Date: Thu, 14 May 2026 16:00:46 +0400
Subject: [PATCH 2/6] fix: documentation mismatch
---
core-context-propagation-quarkus/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core-context-propagation-quarkus/README.md b/core-context-propagation-quarkus/README.md
index 7d2a2f34c..d156f5320 100644
--- a/core-context-propagation-quarkus/README.md
+++ b/core-context-propagation-quarkus/README.md
@@ -143,7 +143,7 @@ HEADERS_BLOCKED=
2. **Via application.properties (Quarkus):**
```properties
-quarkus.headers.blocked
+quarkus.headers.blocked=
```
**`quarkus.headers.blocked` rules and limitations**
From 39c9234130cd30172a850d26eb31527fa3157d93 Mon Sep 17 00:00:00 2001
From: Ksiona
Date: Thu, 14 May 2026 16:29:47 +0400
Subject: [PATCH 3/6] fix: documentation mismatch
---
core-context-propagation-quarkus/README.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/core-context-propagation-quarkus/README.md b/core-context-propagation-quarkus/README.md
index d156f5320..6ab77d25c 100644
--- a/core-context-propagation-quarkus/README.md
+++ b/core-context-propagation-quarkus/README.md
@@ -84,7 +84,7 @@ String acceptLanguage = acceptLanguageContextObject.getAcceptedLanguages();
#### Allowed headers
Allows propagating any specified headers. To set a list of headers you should put either
-`HEADERS_ALLOWED` environment or set the `headers.allowed` property. Property has more precedence than env.
+`HEADERS_ALLOWED` environment or set the `quarkus.headers.allowed` property. Property has more precedence than env.
Access:
@@ -94,10 +94,10 @@ Map allowedHeaders = allowedHeadersContextObject.getHeaders();
```
You just need to specify a list of headers in `application.properties`
-in the `headers.allowed` property. For example:
+in the `quarkus.headers.allowed` property. For example:
```properties
-headers.allowed=myheader1,myheader2,...
+quarkus.headers.allowed=myheader1,myheader2,...
```
Otherwise, you need to take care that this parameter is in System#property or environment.
From e384b69ec8d85d2066fd67f541942a3ed1603ef3 Mon Sep 17 00:00:00 2001
From: Ksiona
Date: Fri, 15 May 2026 23:54:41 +0400
Subject: [PATCH 4/6] fix: empty value is equal to "not set" for quarkus.
---
core-context-propagation-quarkus/README.md | 4 +-
.../allowedheaders/HeadersAllowedConfig.java | 32 ++++++++-
.../HeadersAllowedRecorder.java | 5 +-
.../allowedheaders/RawStringConverter.java | 10 +++
.../HeadersAllowedConfigTest.java | 9 ++-
.../src/test/resources/application.properties | 2 +
core-context-propagation/README.md | 4 +-
.../HeaderPropagationConfiguration.java | 24 ++++---
.../HeaderPropagationConfigurationTest.java | 34 ++++++++--
.../context-propagation-spring-common/pom.xml | 6 ++
...ContextProviderConfigurationEmptyTest.java | 52 +++++++++++++++
...xtProviderConfigurationFromEnvVarTest.java | 66 +++++++++++++++++++
...roviderConfigurationNotConfiguredTest.java | 48 ++++++++++++++
...iderConfigurationOnlyNonBlockableTest.java | 54 +++++++++++++++
...extProviderConfigurationWithValueTest.java | 52 +++++++++++++++
15 files changed, 378 insertions(+), 24 deletions(-)
create mode 100644 core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/RawStringConverter.java
create mode 100644 core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java
create mode 100644 core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java
create mode 100644 core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java
create mode 100644 core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java
create mode 100644 core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java
diff --git a/core-context-propagation-quarkus/README.md b/core-context-propagation-quarkus/README.md
index 6ab77d25c..4be422ff0 100644
--- a/core-context-propagation-quarkus/README.md
+++ b/core-context-propagation-quarkus/README.md
@@ -152,8 +152,8 @@ quarkus.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.
+- `X-Request-Id` is non-blockable: if it is listed in `quarkus.headers.blocked`/`HEADERS_BLOCKED`, it is silently dropped from the configured list.
+- If the configured value contains only non-blockable entries (for example only `X-Request-Id`), the resulting blocked list is **empty** — the default is **not** restored. Any explicit configuration (even one that effectively blocks nothing) is treated as the user's deliberate override of the default.
**MDC Integration:** The channel request ID is automatically stored in MDC under the key `x_channel_request_id` for use in
logging.
diff --git a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfig.java b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfig.java
index ff956700c..c59ddce43 100644
--- a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfig.java
+++ b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfig.java
@@ -3,6 +3,8 @@
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
+import io.smallrye.config.WithConverter;
+import io.smallrye.config.WithDefault;
import io.smallrye.config.WithName;
import java.util.Optional;
@@ -10,15 +12,41 @@
@ConfigMapping(prefix = "quarkus")
@ConfigRoot(phase = ConfigPhase.RUN_TIME)
public interface HeadersAllowedConfig {
+
+ /**
+ * Sentinel value used to distinguish "property not set" from "property explicitly set to empty".
+ * Starts with a NUL character so users cannot accidentally type it.
+ */
+ String UNSET = "\0__unset__";
+
/**
- * Allowed headers to propagate in contexts
+ * Allowed headers to propagate in contexts.
*/
@WithName("headers.allowed")
Optional allowedHeaders();
/**
* Blocked headers for context propagation. X-Channel-Request-Id is blocked by default.
+ *
+ * Three distinct states are supported:
+ *
+ * - property not set — value equals {@link #UNSET}; the default blocked list applies.
+ * - property set to empty — value equals "" (empty string); the default is erased.
+ * - property set to a value — value is the raw configured string.
+ *
+ * The custom {@link RawStringConverter} is required so that an empty value is preserved
+ * instead of being collapsed to {@code null} by SmallRye's default String converter.
*/
@WithName("headers.blocked")
- Optional blockedHeaders();
+ @WithConverter(RawStringConverter.class)
+ @WithDefault(UNSET)
+ String blockedHeaders();
+
+ /**
+ * @return {@code true} if the user explicitly configured {@code quarkus.headers.blocked}
+ * (including to an empty value); {@code false} if the property was not set at all.
+ */
+ default boolean isBlockedHeadersSet() {
+ return !UNSET.equals(blockedHeaders());
+ }
}
diff --git a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedRecorder.java b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedRecorder.java
index e795f9919..99cec85df 100644
--- a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedRecorder.java
+++ b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedRecorder.java
@@ -9,6 +9,9 @@ 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));
+
+ if (allowedConfig.isBlockedHeadersSet()) {
+ System.setProperty("headers.blocked", allowedConfig.blockedHeaders());
+ }
}
}
diff --git a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/RawStringConverter.java b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/RawStringConverter.java
new file mode 100644
index 000000000..12ac744ff
--- /dev/null
+++ b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/RawStringConverter.java
@@ -0,0 +1,10 @@
+package com.netcracker.cloud.framework.quarkus.contexts.allowedheaders;
+
+import org.eclipse.microprofile.config.spi.Converter;
+
+public class RawStringConverter implements Converter {
+ @Override
+ public String convert(String value) {
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigTest.java b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigTest.java
index 0d5618251..fcece6b33 100644
--- a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigTest.java
+++ b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigTest.java
@@ -10,6 +10,7 @@
@QuarkusTest
class HeadersAllowedConfigTest {
+
@Inject
HeadersAllowedConfig headersAllowedConfig;
@@ -21,5 +22,11 @@ void shouldReadHeadersAllowedFromProperty() {
assertEquals("test-quarkus.headers.allowed", value.get());
}
+ @Test
+ void shouldReadHeadersBlockedAsExplicitlyEmpty() {
+ assertTrue(headersAllowedConfig.isBlockedHeadersSet(),
+ "quarkus.headers.blocked must be considered explicitly set");
+ assertEquals("", headersAllowedConfig.blockedHeaders(),
+ "quarkus.headers.blocked must be preserved as an empty string");
+ }
}
-
diff --git a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/resources/application.properties b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/resources/application.properties
index 6fecc5866..209ea103f 100644
--- a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/resources/application.properties
+++ b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/resources/application.properties
@@ -4,4 +4,6 @@ quarkus.devservices.enabled=false
quarkus.rest-client.my-client.url=http://localhost:${quarkus.http.test-port}
quarkus.headers.allowed=test-quarkus.headers.allowed
+quarkus.headers.blocked=
headers.allowed=test-headers.allowed
+headers.blocked=test-headers.blocked
diff --git a/core-context-propagation/README.md b/core-context-propagation/README.md
index 40f550846..2e968cb92 100644
--- a/core-context-propagation/README.md
+++ b/core-context-propagation/README.md
@@ -171,8 +171,8 @@ headers.blocked=
- Default when not configured at all: `X-Channel-Request-Id` is blocked.
- Explicit empty value (`headers.blocked=` / `HEADERS_BLOCKED=`): blacklist is empty (nothing is blocked).
- Explicit non-empty value with valid headers (for example `headers.blocked=Some-Header`): only listed headers are blocked.
-- `X-Request-Id` is non-blockable: if it is listed in `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.
+- `X-Request-Id` is non-blockable: if it is listed in `headers.blocked`/`HEADERS_BLOCKED`, it is silently dropped from the configured list.
+- If the configured value contains only non-blockable entries (for example only `X-Request-Id`), the resulting blocked list is **empty** — the default is **not** restored. Any explicit configuration (even one that effectively blocks nothing) is treated as the user's deliberate override of the default.
**MDC Integration:**
The `X-Channel-Request-Id` is automatically integrated with SLF4J's Mapped Diagnostic Context (MDC) for seamless logging.
diff --git a/core-context-propagation/framework-contexts/src/main/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfiguration.java b/core-context-propagation/framework-contexts/src/main/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfiguration.java
index aa9de6028..e3d9a301a 100644
--- a/core-context-propagation/framework-contexts/src/main/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfiguration.java
+++ b/core-context-propagation/framework-contexts/src/main/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfiguration.java
@@ -69,23 +69,29 @@ private static List readBlockedHeaders() {
String envValue = System.getenv(HEADERS_BLOCKED_ENV);
boolean envSpecified = envValue != null;
- String blockedHeaders = propertySpecified
+ // No source set at all → fall back to the built-in default.
+ if (!propertySpecified && !envSpecified) {
+ return DEFAULT_BLOCKED_HEADERS;
+ }
+
+ // Property wins over env when both are set.
+ String raw = propertySpecified
? System.getProperty(HEADERS_BLOCKED_PROPERTY)
: envValue;
- boolean anySourceSpecified = propertySpecified || envSpecified;
-
- if (blockedHeaders == null || blockedHeaders.isBlank()) {
- return anySourceSpecified ? Collections.emptyList() : DEFAULT_BLOCKED_HEADERS;
+ // Source is set but empty/blank → explicit "erase the default".
+ if (raw == null || raw.isBlank()) {
+ return Collections.emptyList();
}
-
- List configured = Arrays.stream(blockedHeaders.split(","))
+
+ // Source is set with a value → parse, trim, drop non-blockable entries.
+ // If everything filters out, we still respect the user's explicit override
+ // and return an empty list — we do NOT silently restore the default.
+ return Arrays.stream(raw.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.filter(s -> NON_BLOCKABLE_HEADERS.stream()
.noneMatch(s::equalsIgnoreCase))
.toList();
-
- return configured.isEmpty() ? DEFAULT_BLOCKED_HEADERS : configured;
}
}
diff --git a/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java b/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java
index cf5944adf..fed20c41a 100644
--- a/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java
+++ b/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java
@@ -67,31 +67,51 @@ void shouldNotBlacklistXChannelRequestIdWhenBlockedHeadersExplicitlyEmpty() {
@Test
void shouldNotBlacklistXChannelRequestIdWhenOtherHeadersExplicitlyBlocked() {
+ // The property is explicitly set to a non-blockable header; the default
+ // blocked list must NOT silently kick in.
System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, "X-Request-Id");
HeaderPropagationConfiguration.resetCache();
Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
- Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
+ Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
}
@Test
- void shouldApplyDefaultBlacklistWhenOnlyNonBlockableHeadersConfigured() {
+ void shouldReturnEmptyListWhenOnlyNonBlockableHeadersConfigured() {
System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, "X-Request-Id, x-request-id");
HeaderPropagationConfiguration.resetCache();
Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
- Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
- Assertions.assertEquals(HeaderPropagationConfiguration.DEFAULT_BLOCKED_HEADERS,
- HeaderPropagationConfiguration.blockedHeaders());
+ Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
+ Assertions.assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty());
}
@Test
- void shouldBlacklistHeaderByEnvWhenPropertyNotSet() throws Exception {
+ void shouldReadEnvWhenPropertyNotSet() throws Exception {
+ // Env value is read when no system property is set. The configured value
+ // is only X-Request-Id (non-blockable), so the resulting list must be empty —
+ // we deliberately do NOT fall back to the default blocked list here.
environmentVariables.set(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV, "X-Request-Id");
try {
HeaderPropagationConfiguration.resetCache();
Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
- Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
+ Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
+ Assertions.assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty());
+ } finally {
+ environmentVariables.remove(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV);
+ HeaderPropagationConfiguration.resetCache();
+ }
+ }
+
+ @Test
+ void shouldBlacklistHeaderByEnvWhenPropertyNotSet() throws Exception {
+ // Sanity check that env-sourced configuration actually drives the blocked list
+ // when the system property is absent.
+ environmentVariables.set(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV, "Custom-Header");
+ try {
+ HeaderPropagationConfiguration.resetCache();
+ Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("Custom-Header"));
+ Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
} finally {
environmentVariables.remove(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV);
HeaderPropagationConfiguration.resetCache();
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/pom.xml b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/pom.xml
index 3694ec174..c208c3358 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/pom.xml
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/pom.xml
@@ -56,6 +56,12 @@
spring-test
test
+
+ uk.org.webcompere
+ system-stubs-jupiter
+ 2.1.8
+ test
+
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java
new file mode 100644
index 000000000..ce5a5721b
--- /dev/null
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java
@@ -0,0 +1,52 @@
+package com.netcracker.cloud.context.propagation.spring.common.configuration;
+
+import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Spring integration scenario: {@code headers.blocked=} (set explicitly to empty).
+ * Verifies that {@link SpringContextProviderConfiguration#init()} propagates the
+ * empty value to the system property, which downstream code interprets as
+ * "erase the default blocked list".
+ */
+@SpringJUnitConfig(classes = SpringContextProviderConfiguration.class)
+@TestPropertySource(properties = {
+ "headers.allowed=custom-header",
+ "headers.blocked="
+})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
+class SpringContextProviderConfigurationEmptyTest {
+
+ @BeforeAll
+ static void setup() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @AfterAll
+ static void teardown() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @Test
+ void shouldSetEmptySystemPropertyAndEraseDefaultBlockedList() {
+ assertEquals("", System.getProperty("headers.blocked"),
+ "Spring init() must propagate explicit empty value to the system property");
+
+ HeaderPropagationConfiguration.resetCache();
+ assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty(),
+ "explicit empty value must erase the default blocked list");
+ assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
+ "X-Channel-Request-Id must no longer be blocked");
+ }
+}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java
new file mode 100644
index 000000000..96851f99c
--- /dev/null
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java
@@ -0,0 +1,66 @@
+package com.netcracker.cloud.context.propagation.spring.common.configuration;
+
+import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
+import uk.org.webcompere.systemstubs.jupiter.SystemStub;
+import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Spring integration scenario: {@code headers.blocked} is configured only via the
+ * {@code HEADERS_BLOCKED} environment variable. Verifies that Spring's relaxed
+ * binding picks up the env var as the {@code headers.blocked} property, that
+ * {@link SpringContextProviderConfiguration#init()} propagates it to the system
+ * property, and that the downstream blocked list reflects the env-sourced value.
+ *
+ * {@link SystemStubsExtension} must be registered before {@link SpringExtension}
+ * so that the env var is set before Spring's {@code SystemEnvironmentPropertySource}
+ * is consulted during context initialization.
+ */
+@ExtendWith({SystemStubsExtension.class, SpringExtension.class})
+@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
+@TestPropertySource(properties = {
+ // headers.blocked deliberately not declared here — it must come from the env var
+ "headers.allowed=custom-header"
+})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
+class SpringContextProviderConfigurationFromEnvVarTest {
+
+ @SystemStub
+ static EnvironmentVariables envVars = new EnvironmentVariables("HEADERS_BLOCKED", "Custom-Header");
+
+ @BeforeAll
+ static void setup() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @AfterAll
+ static void teardown() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @Test
+ void shouldReadHeadersBlockedFromEnvVar() {
+ assertEquals("Custom-Header", System.getProperty("headers.blocked"),
+ "Spring init() must propagate env-sourced value to the system property");
+
+ HeaderPropagationConfiguration.resetCache();
+ assertTrue(HeaderPropagationConfiguration.isBlacklisted("Custom-Header"),
+ "env-sourced header must be blocked");
+ assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
+ "explicit env-sourced configuration overrides the default");
+ }
+}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java
new file mode 100644
index 000000000..664859f6a
--- /dev/null
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java
@@ -0,0 +1,48 @@
+package com.netcracker.cloud.context.propagation.spring.common.configuration;
+
+import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Spring integration scenario: {@code headers.blocked} is not set anywhere.
+ * Verifies that {@link SpringContextProviderConfiguration#init()} does NOT touch
+ * the {@code headers.blocked} system property, so downstream code falls back to
+ * the built-in default blocked list (which contains {@code X-Channel-Request-Id}).
+ */
+@SpringJUnitConfig(classes = SpringContextProviderConfiguration.class)
+@TestPropertySource(properties = {
+ "headers.allowed=custom-header"
+})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
+class SpringContextProviderConfigurationNotConfiguredTest {
+
+ @BeforeAll
+ static void setup() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @AfterAll
+ static void teardown() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @Test
+ void shouldNotSetSystemPropertyAndApplyDefaultBlockedList() {
+ assertNull(System.getProperty("headers.blocked"),
+ "headers.blocked must remain unset when no source configures it");
+
+ HeaderPropagationConfiguration.resetCache();
+ assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
+ "default blocked list must apply when nothing is configured");
+ }
+}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java
new file mode 100644
index 000000000..e97020668
--- /dev/null
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java
@@ -0,0 +1,54 @@
+package com.netcracker.cloud.context.propagation.spring.common.configuration;
+
+import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Spring integration scenario: {@code headers.blocked=X-Request-Id} — the configured
+ * value consists exclusively of a non-blockable header. The resulting blocked list
+ * must be empty, NOT the built-in default. This locks in the behavior change made
+ * to {@link HeaderPropagationConfiguration} (no silent fallback to default when the
+ * user explicitly configured something).
+ */
+@SpringJUnitConfig(classes = SpringContextProviderConfiguration.class)
+@TestPropertySource(properties = {
+ "headers.allowed=custom-header",
+ "headers.blocked=X-Request-Id"
+})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
+class SpringContextProviderConfigurationOnlyNonBlockableTest {
+
+ @BeforeAll
+ static void setup() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @AfterAll
+ static void teardown() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @Test
+ void shouldRespectExplicitOverrideEvenWhenItFiltersToEmpty() {
+ assertEquals("X-Request-Id", System.getProperty("headers.blocked"));
+
+ HeaderPropagationConfiguration.resetCache();
+ assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty(),
+ "the resulting blocked list must be empty — the default must NOT be restored");
+ assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"),
+ "X-Request-Id is non-blockable and must never be blocked");
+ assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
+ "explicit (even if filter-emptied) configuration overrides the default");
+ }
+}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java
new file mode 100644
index 000000000..45480ca94
--- /dev/null
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java
@@ -0,0 +1,52 @@
+package com.netcracker.cloud.context.propagation.spring.common.configuration;
+
+import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Spring integration scenario: {@code headers.blocked} is set to a concrete blockable header.
+ * Verifies that the listed header is blocked and the default's
+ * {@code X-Channel-Request-Id} entry no longer applies.
+ */
+@SpringJUnitConfig(classes = SpringContextProviderConfiguration.class)
+@TestPropertySource(properties = {
+ "headers.allowed=custom-header",
+ "headers.blocked=Custom-Header"
+})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
+class SpringContextProviderConfigurationWithValueTest {
+
+ @BeforeAll
+ static void setup() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @AfterAll
+ static void teardown() {
+ System.clearProperty("headers.blocked");
+ HeaderPropagationConfiguration.resetCache();
+ }
+
+ @Test
+ void shouldSetSystemPropertyAndBlockConfiguredHeader() {
+ assertEquals("Custom-Header", System.getProperty("headers.blocked"));
+
+ HeaderPropagationConfiguration.resetCache();
+ assertTrue(HeaderPropagationConfiguration.isBlacklisted("Custom-Header"),
+ "configured header must be blocked");
+ assertTrue(HeaderPropagationConfiguration.isBlacklisted("custom-header"),
+ "blocking must be case-insensitive");
+ assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
+ "explicit configuration overrides the default blocked list");
+ }
+}
From 934ad6392675eb5d6e2de3742563e0e85b8aa1fc Mon Sep 17 00:00:00 2001
From: Ksiona
Date: Mon, 18 May 2026 10:40:55 +0400
Subject: [PATCH 5/6] chore: review improvements
---
.../HeaderPropagationConfigurationTest.java | 2 +-
.../HeaderPropagationStateReset.java | 18 ++++++++++++++++
...ContextProviderConfigurationEmptyTest.java | 21 +++++--------------
...xtProviderConfigurationFromEnvVarTest.java | 16 +-------------
...roviderConfigurationNotConfiguredTest.java | 21 +++++--------------
...iderConfigurationOnlyNonBlockableTest.java | 21 +++++--------------
...extProviderConfigurationWithValueTest.java | 21 +++++--------------
7 files changed, 40 insertions(+), 80 deletions(-)
create mode 100644 core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/HeaderPropagationStateReset.java
diff --git a/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java b/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java
index fed20c41a..68247fa37 100644
--- a/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java
+++ b/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java
@@ -87,7 +87,7 @@ void shouldReturnEmptyListWhenOnlyNonBlockableHeadersConfigured() {
}
@Test
- void shouldReadEnvWhenPropertyNotSet() throws Exception {
+ void shouldReadEnvWhenPropertyNotSet() {
// Env value is read when no system property is set. The configured value
// is only X-Request-Id (non-blockable), so the resulting list must be empty —
// we deliberately do NOT fall back to the default blocked list here.
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/HeaderPropagationStateReset.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/HeaderPropagationStateReset.java
new file mode 100644
index 000000000..8c0a30297
--- /dev/null
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/HeaderPropagationStateReset.java
@@ -0,0 +1,18 @@
+package com.netcracker.cloud.context.propagation.spring.common.configuration;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
+
+public class HeaderPropagationStateReset implements BeforeAllCallback, AfterAllCallback {
+ @Override public void beforeAll(ExtensionContext ctx) { reset(); }
+ @Override public void afterAll(ExtensionContext ctx) { reset(); }
+
+ private static void reset() {
+ System.clearProperty("headers.blocked");
+ System.clearProperty("headers.allowed");
+ HeaderPropagationConfiguration.resetCache();
+ }
+}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java
index ce5a5721b..f9c0905ad 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java
@@ -1,12 +1,12 @@
package com.netcracker.cloud.context.propagation.spring.common.configuration;
import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -18,7 +18,8 @@
* empty value to the system property, which downstream code interprets as
* "erase the default blocked list".
*/
-@SpringJUnitConfig(classes = SpringContextProviderConfiguration.class)
+@ExtendWith({HeaderPropagationStateReset.class, SpringExtension.class})
+@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
@TestPropertySource(properties = {
"headers.allowed=custom-header",
"headers.blocked="
@@ -26,18 +27,6 @@
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class SpringContextProviderConfigurationEmptyTest {
- @BeforeAll
- static void setup() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
- @AfterAll
- static void teardown() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
@Test
void shouldSetEmptySystemPropertyAndEraseDefaultBlockedList() {
assertEquals("", System.getProperty("headers.blocked"),
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java
index 96851f99c..4b56a696f 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java
@@ -1,8 +1,6 @@
package com.netcracker.cloud.context.propagation.spring.common.configuration;
import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.annotation.DirtiesContext;
@@ -28,7 +26,7 @@
* so that the env var is set before Spring's {@code SystemEnvironmentPropertySource}
* is consulted during context initialization.
*/
-@ExtendWith({SystemStubsExtension.class, SpringExtension.class})
+@ExtendWith({HeaderPropagationStateReset.class, SystemStubsExtension.class, SpringExtension.class})
@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
@TestPropertySource(properties = {
// headers.blocked deliberately not declared here — it must come from the env var
@@ -40,18 +38,6 @@ class SpringContextProviderConfigurationFromEnvVarTest {
@SystemStub
static EnvironmentVariables envVars = new EnvironmentVariables("HEADERS_BLOCKED", "Custom-Header");
- @BeforeAll
- static void setup() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
- @AfterAll
- static void teardown() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
@Test
void shouldReadHeadersBlockedFromEnvVar() {
assertEquals("Custom-Header", System.getProperty("headers.blocked"),
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java
index 664859f6a..1d4ba15fa 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java
@@ -1,12 +1,12 @@
package com.netcracker.cloud.context.propagation.spring.common.configuration;
import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -17,25 +17,14 @@
* the {@code headers.blocked} system property, so downstream code falls back to
* the built-in default blocked list (which contains {@code X-Channel-Request-Id}).
*/
-@SpringJUnitConfig(classes = SpringContextProviderConfiguration.class)
+@ExtendWith({HeaderPropagationStateReset.class, SpringExtension.class})
+@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
@TestPropertySource(properties = {
"headers.allowed=custom-header"
})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class SpringContextProviderConfigurationNotConfiguredTest {
- @BeforeAll
- static void setup() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
- @AfterAll
- static void teardown() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
@Test
void shouldNotSetSystemPropertyAndApplyDefaultBlockedList() {
assertNull(System.getProperty("headers.blocked"),
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java
index e97020668..b6649f58b 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java
@@ -1,12 +1,12 @@
package com.netcracker.cloud.context.propagation.spring.common.configuration;
import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -19,7 +19,8 @@
* to {@link HeaderPropagationConfiguration} (no silent fallback to default when the
* user explicitly configured something).
*/
-@SpringJUnitConfig(classes = SpringContextProviderConfiguration.class)
+@ExtendWith({HeaderPropagationStateReset.class, SpringExtension.class})
+@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
@TestPropertySource(properties = {
"headers.allowed=custom-header",
"headers.blocked=X-Request-Id"
@@ -27,18 +28,6 @@
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class SpringContextProviderConfigurationOnlyNonBlockableTest {
- @BeforeAll
- static void setup() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
- @AfterAll
- static void teardown() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
@Test
void shouldRespectExplicitOverrideEvenWhenItFiltersToEmpty() {
assertEquals("X-Request-Id", System.getProperty("headers.blocked"));
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java
index 45480ca94..88e48709d 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java
@@ -1,12 +1,12 @@
package com.netcracker.cloud.context.propagation.spring.common.configuration;
import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -17,7 +17,8 @@
* Verifies that the listed header is blocked and the default's
* {@code X-Channel-Request-Id} entry no longer applies.
*/
-@SpringJUnitConfig(classes = SpringContextProviderConfiguration.class)
+@ExtendWith({HeaderPropagationStateReset.class, SpringExtension.class})
+@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
@TestPropertySource(properties = {
"headers.allowed=custom-header",
"headers.blocked=Custom-Header"
@@ -25,18 +26,6 @@
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class SpringContextProviderConfigurationWithValueTest {
- @BeforeAll
- static void setup() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
- @AfterAll
- static void teardown() {
- System.clearProperty("headers.blocked");
- HeaderPropagationConfiguration.resetCache();
- }
-
@Test
void shouldSetSystemPropertyAndBlockConfiguredHeader() {
assertEquals("Custom-Header", System.getProperty("headers.blocked"));
From 85ae957b7daba24e26a394503c5538a3008f044c Mon Sep 17 00:00:00 2001
From: Ksiona
Date: Mon, 18 May 2026 19:38:30 +0400
Subject: [PATCH 6/6] fix: x-channel-request-id feature redesign
---
core-context-propagation-quarkus/README.md | 89 ++++++++++--
.../allowedheaders/HeadersAllowedConfig.java | 51 +++----
.../HeadersAllowedRecorder.java | 10 +-
.../allowedheaders/RawStringConverter.java | 10 --
...HeadersAllowBlockedRecorderEffectTest.java | 33 +++++
.../HeadersAllowedConfigNotSetTest.java | 34 +++++
.../HeadersAllowedConfigTest.java | 16 ++-
.../src/test/resources/application.properties | 4 +-
core-context-propagation/README.md | 84 ++++++++---
.../HeaderPropagationConfiguration.java | 73 ++++++----
.../HeaderPropagationConfigurationTest.java | 133 ++++--------------
...RequestIdContextObjectPropagationTest.java | 23 ++-
.../RequestPropagationSpringCommonTest.java | 2 +-
.../requests/RequestPropagationTest.java | 10 +-
...opagationXChannelRequestIdAllowedTest.java | 6 +-
...pagationXChannelRequestIdResponseTest.java | 6 +-
.../SpringContextProviderConfiguration.java | 14 +-
.../HeaderPropagationStateReset.java | 14 +-
...ContextProviderConfigurationEmptyTest.java | 20 +--
...xtProviderConfigurationFromEnvVarTest.java | 25 +---
...roviderConfigurationNotConfiguredTest.java | 15 +-
...iderConfigurationOnlyNonBlockableTest.java | 43 ------
...iderConfigurationUnknownExemptionTest.java | 34 +++++
...extProviderConfigurationWithValueTest.java | 19 +--
.../spring/kafka/ContextPropagationTest.java | 10 +-
.../spring/rabbit/PropagationTest.java | 24 +++-
.../kafka/KafkaContextPropagationTest.java | 6 +-
27 files changed, 447 insertions(+), 361 deletions(-)
delete mode 100644 core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/RawStringConverter.java
create mode 100644 core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowBlockedRecorderEffectTest.java
create mode 100644 core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigNotSetTest.java
delete mode 100644 core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java
create mode 100644 core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationUnknownExemptionTest.java
diff --git a/core-context-propagation-quarkus/README.md b/core-context-propagation-quarkus/README.md
index 4be422ff0..28a01dcf2 100644
--- a/core-context-propagation-quarkus/README.md
+++ b/core-context-propagation-quarkus/README.md
@@ -133,27 +133,86 @@ 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:
+#### Internal blocklist
-1. **Via environment variable:**
-```text
-HEADERS_BLOCKED=
-```
+The framework owns a hard-coded internal blocklist of headers that are not propagated to outgoing
+requests. It currently contains:
+
+- `X-Channel-Request-Id`
+
+The blocklist itself cannot be changed from configuration. The only externally visible knob is the
+`quarkus.context.propagation.allow-blocked-headers` property, which lists header names that should be **exempted** from
+this blocklist (i.e. allowed to propagate).
+
+#### `quarkus.context.propagation.allow-blocked-headers` property
+
+The property carries a comma-separated list of header names. Every name that matches an entry of
+the internal blocklist is removed from the effective blocklist. Names that are not in the
+internal blocklist have no effect.
+
+Examples:
+
+| Property value | Effect |
+|---|---|
+| not set / empty | Internal blocklist 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 internal blocklist. |
+| `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`:**
-2. **Via application.properties (Quarkus):**
```properties
-quarkus.headers.blocked=
+quarkus.context.propagation.allow-blocked-headers=X-Channel-Request-Id
+```
+
+**Via `application.yaml`:**
+
+```yaml
+quarkus:
+ context:
+ propagation:
+ allow-blocked-headers: X-Channel-Request-Id
+```
+
+**Via JVM system property:**
+
+```text
+-Dquarkus.context.propagation.allow-blocked-headers=X-Channel-Request-Id
```
-**`quarkus.headers.blocked` rules and limitations**
+**Via container ENV (recommended, Qubership pattern)**
+
+In Qubership deployments configuration values are typically wired from container ENV variables
+through `application.yaml` placeholders. The full chain looks like this:
+
+1. The container declares an ENV variable, for example in the deployment manifest:
+
+ ```yaml
+ env:
+ - name: CONTEXT_PROPAGATION_ALLOW_BLOCKED_HEADERS
+ value: "X-Channel-Request-Id"
+ ```
+
+2. The application's `application.yaml` reads it via a `${VAR}` placeholder:
+
+ ```yaml
+ quarkus:
+ context:
+ propagation:
+ allow-blocked-headers: ${CONTEXT_PROPAGATION_ALLOW_BLOCKED_HEADERS:}
+ ```
+
+ The trailing `:` (empty default) lets the property gracefully fall through to "not set" when
+ the ENV variable is absent — the internal blocklist then applies in full.
-- 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 silently dropped from the configured list.
-- If the configured value contains only non-blockable entries (for example only `X-Request-Id`), the resulting blocked list is **empty** — the default is **not** restored. Any explicit configuration (even one that effectively blocks nothing) is treated as the user's deliberate override of the default.
+3. Quarkus also recognises the ENV variable directly under the canonical name
+ `CONTEXT_PROPAGATION_ALLOW_BLOCKED_HEADERS` (via MicroProfile Config relaxed env-to-property mapping), so
+ the same ENV variable works even without the `application.yaml` indirection. The placeholder
+ form is preferred because it makes the dependency explicit in source control.
**MDC Integration:** The channel request ID is automatically stored in MDC under the key `x_channel_request_id` for use in
logging.
diff --git a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfig.java b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfig.java
index c59ddce43..68e2a77b6 100644
--- a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfig.java
+++ b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfig.java
@@ -3,22 +3,15 @@
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
-import io.smallrye.config.WithConverter;
-import io.smallrye.config.WithDefault;
import io.smallrye.config.WithName;
+import java.util.List;
import java.util.Optional;
@ConfigMapping(prefix = "quarkus")
@ConfigRoot(phase = ConfigPhase.RUN_TIME)
public interface HeadersAllowedConfig {
- /**
- * Sentinel value used to distinguish "property not set" from "property explicitly set to empty".
- * Starts with a NUL character so users cannot accidentally type it.
- */
- String UNSET = "\0__unset__";
-
/**
* Allowed headers to propagate in contexts.
*/
@@ -26,27 +19,29 @@ public interface HeadersAllowedConfig {
Optional allowedHeaders();
/**
- * Blocked headers for context propagation. X-Channel-Request-Id is blocked by default.
- *
- * Three distinct states are supported:
+ * Headers that should be allowed to propagate even though they appear in the framework's
+ * internal blocklist. The blocklist itself is hard-coded and cannot be changed from
+ * configuration.
+ *
+ *
Semantics:
*
- * - property not set — value equals {@link #UNSET}; the default blocked list applies.
- * - property set to empty — value equals "" (empty string); the default is erased.
- * - property set to a value — value is the raw configured string.
+ * - Property not set or empty: the internal blocklist is applied as-is. Any header
+ * in the blocklist (for example {@code X-Channel-Request-Id}) is not propagated.
+ * - Property contains a comma-separated list of header names: each listed name is
+ * removed from the effective blocklist and is allowed to propagate. Names that
+ * are not in the internal blocklist have no effect.
*
- * The custom {@link RawStringConverter} is required so that an empty value is preserved
- * instead of being collapsed to {@code null} by SmallRye's default String converter.
- */
- @WithName("headers.blocked")
- @WithConverter(RawStringConverter.class)
- @WithDefault(UNSET)
- String blockedHeaders();
-
- /**
- * @return {@code true} if the user explicitly configured {@code quarkus.headers.blocked}
- * (including to an empty value); {@code false} if the property was not set at all.
+ *
+ * Example {@code application.yaml} with a container ENV indirection:
+ *
+ * quarkus:
+ * context:
+ * propagation:
+ * allow-blocked-headers: ${CONTEXT_PROPAGATION_ALLOW_BLOCKED_HEADERS:}
+ *
+ * The container then sets {@code CONTEXT_PROPAGATION_ALLOW_BLOCKED_HEADERS=X-Channel-Request-Id} when it needs
+ * that header to be propagated.
*/
- default boolean isBlockedHeadersSet() {
- return !UNSET.equals(blockedHeaders());
- }
+ @WithName("context.propagation.allow-blocked-headers")
+ Optional> allowedHeadersFromBlocklist();
}
diff --git a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedRecorder.java b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedRecorder.java
index 99cec85df..c0dabdb9e 100644
--- a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedRecorder.java
+++ b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedRecorder.java
@@ -8,10 +8,12 @@ public class HeadersAllowedRecorder {
public void setAllowedHeadersToSystemProperty() {
HeadersAllowedConfig allowedConfig = Arc.container().instance(HeadersAllowedConfig.class).get();
- allowedConfig.allowedHeaders().ifPresent(allowedHeaders -> System.setProperty("headers.allowed", allowedHeaders));
- if (allowedConfig.isBlockedHeadersSet()) {
- System.setProperty("headers.blocked", allowedConfig.blockedHeaders());
- }
+ allowedConfig.allowedHeaders()
+ .ifPresent(allowedHeaders -> System.setProperty("headers.allowed", allowedHeaders));
+
+ allowedConfig.allowedHeadersFromBlocklist()
+ .filter(list -> !list.isEmpty())
+ .ifPresent(list -> System.setProperty("context.propagation.allow-blocked-headers", String.join(",", list)));
}
}
diff --git a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/RawStringConverter.java b/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/RawStringConverter.java
deleted file mode 100644
index 12ac744ff..000000000
--- a/core-context-propagation-quarkus/framework-contexts/runtime/src/main/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/RawStringConverter.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.netcracker.cloud.framework.quarkus.contexts.allowedheaders;
-
-import org.eclipse.microprofile.config.spi.Converter;
-
-public class RawStringConverter implements Converter {
- @Override
- public String convert(String value) {
- return value;
- }
-}
\ No newline at end of file
diff --git a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowBlockedRecorderEffectTest.java b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowBlockedRecorderEffectTest.java
new file mode 100644
index 000000000..0d6f17fb1
--- /dev/null
+++ b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowBlockedRecorderEffectTest.java
@@ -0,0 +1,33 @@
+package com.netcracker.cloud.framework.quarkus.contexts.allowedheaders;
+
+import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
+import io.quarkus.test.junit.QuarkusTest;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@QuarkusTest
+class HeadersAllowBlockedRecorderEffectTest {
+
+ @Test
+ void shouldExposeAllowBlockedValueAsSystemProperty() {
+ assertEquals("X-Channel-Request-Id",
+ System.getProperty("context.propagation.allow-blocked-headers"),
+ "Recorder must propagate the quarkus.context.propagation.allow-blocked-headers value " +
+ "to the context.propagation.allow-blocked-headers system property.");
+ }
+
+ @Test
+ void shouldRemoveExemptedHeaderFromInternalBlocklist() {
+ // The blocked list is cached on first access; ensure we read the post-recorder state.
+ HeaderPropagationConfiguration.resetCache();
+
+ assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty(),
+ "The only entry of the internal blocklist (X-Channel-Request-Id) must be removed " +
+ "by the exemption configured in application.properties.");
+ assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
+ "X-Channel-Request-Id must not be blocked when explicitly exempted.");
+ }
+}
diff --git a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigNotSetTest.java b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigNotSetTest.java
new file mode 100644
index 000000000..61f016c4d
--- /dev/null
+++ b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigNotSetTest.java
@@ -0,0 +1,34 @@
+package com.netcracker.cloud.framework.quarkus.contexts.allowedheaders;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.QuarkusTestProfile;
+import io.quarkus.test.junit.TestProfile;
+import jakarta.inject.Inject;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+@QuarkusTest
+@TestProfile(HeadersAllowedConfigNotSetTest.NotSetProfile.class)
+class HeadersAllowedConfigNotSetTest {
+
+ @Inject
+ HeadersAllowedConfig headersAllowedConfig;
+
+ @Test
+ void shouldReportAllowedFromBlocklistAsEmptyWhenNotConfigured() {
+ assertFalse(headersAllowedConfig.allowedHeadersFromBlocklist().isPresent(),
+ "quarkus.context.propagation.allow-blocked-headers must resolve to Optional.empty() " +
+ "when no exemption value is configured");
+ }
+
+ public static class NotSetProfile implements QuarkusTestProfile {
+
+ @Override
+ public Map getConfigOverrides() {
+ return Map.of("quarkus.context.propagation.allow-blocked-headers", "");
+ }
+ }
+}
diff --git a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigTest.java b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigTest.java
index fcece6b33..cf03260d5 100644
--- a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigTest.java
+++ b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/java/com/netcracker/cloud/framework/quarkus/contexts/allowedheaders/HeadersAllowedConfigTest.java
@@ -4,9 +4,11 @@
import jakarta.inject.Inject;
import org.junit.jupiter.api.Test;
+import java.util.List;
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 {
@@ -23,10 +25,12 @@ void shouldReadHeadersAllowedFromProperty() {
}
@Test
- void shouldReadHeadersBlockedAsExplicitlyEmpty() {
- assertTrue(headersAllowedConfig.isBlockedHeadersSet(),
- "quarkus.headers.blocked must be considered explicitly set");
- assertEquals("", headersAllowedConfig.blockedHeaders(),
- "quarkus.headers.blocked must be preserved as an empty string");
+ void shouldReadAllowedHeadersFromBlocklist() {
+ Optional> value = headersAllowedConfig.allowedHeadersFromBlocklist();
+
+ assertTrue(value.isPresent(),
+ "quarkus.context.propagation.allow-blocked-headers must be present when configured");
+ assertEquals(List.of("X-Channel-Request-Id"), value.get(),
+ "SmallRye must parse the comma-separated value into a list");
}
}
diff --git a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/resources/application.properties b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/resources/application.properties
index 209ea103f..544d085e0 100644
--- a/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/resources/application.properties
+++ b/core-context-propagation-quarkus/integration-tests/context-propagation-reactive-test/src/test/resources/application.properties
@@ -4,6 +4,6 @@ quarkus.devservices.enabled=false
quarkus.rest-client.my-client.url=http://localhost:${quarkus.http.test-port}
quarkus.headers.allowed=test-quarkus.headers.allowed
-quarkus.headers.blocked=
+
+quarkus.context.propagation.allow-blocked-headers=X-Channel-Request-Id
headers.allowed=test-headers.allowed
-headers.blocked=test-headers.blocked
diff --git a/core-context-propagation/README.md b/core-context-propagation/README.md
index 2e968cb92..ff5d3e453 100644
--- a/core-context-propagation/README.md
+++ b/core-context-propagation/README.md
@@ -147,32 +147,82 @@ 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:
+#### Internal blocklist
-1. **Via environment variable:**
-```text
-HEADERS_BLOCKED=
+The framework owns a hard-coded internal blocklist of headers that are not propagated to outgoing
+requests. It currently contains:
+
+- `X-Channel-Request-Id`
+
+The blocklist itself cannot be changed from configuration. The only externally visible knob is the
+`context.propagation.allow-blocked-headers` property, which lists header names that should be **exempted** from this
+blocklist (i.e. allowed to propagate).
+
+#### `context.propagation.allow-blocked-headers` property
+
+The property carries a comma-separated list of header names. Every name that matches an entry of
+the internal blocklist is removed from the effective blocklist. Names that are not in the
+internal blocklist have no effect.
+
+Examples:
+
+| Property value | Effect |
+|---|---|
+| not set / empty | Internal blocklist 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 internal blocklist. |
+| `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
+
+**Spring (`application.properties` / `application.yml`):**
+
+```properties
+context.propagation.allow-blocked-headers=X-Channel-Request-Id
```
-2. **Via system property:**
-```text
--Dheaders.blocked=
+```yaml
+context:
+ propagation:
+ allow-blocked-headers: X-Channel-Request-Id
```
-3. **Via application.properties (Spring):**
+**Via JVM system property:**
+
```text
-headers.blocked=
+-Dcontext.propagation.allow-blocked-headers=X-Channel-Request-Id
```
-**`headers.blocked` rules and limitations**
+**Via container ENV (recommended, Qubership pattern)**
+
+In Qubership deployments configuration values are typically wired from container ENV variables
+through `application.yaml` placeholders. The full chain looks like this:
+
+1. The container declares an ENV variable, for example in the deployment manifest:
+
+ ```yaml
+ env:
+ - name: CONTEXT_PROPAGATION_ALLOW_BLOCKED_HEADERS
+ value: "X-Channel-Request-Id"
+ ```
+
+2. The application's `application.yaml` reads it via a `${VAR}` placeholder:
+
+ ```yaml
+ context:
+ propagation:
+ allow-blocked-headers: ${CONTEXT_PROPAGATION_ALLOW_BLOCKED_HEADERS:}
+ ```
+
+ The trailing `:` (empty default) lets the property gracefully fall through to "not set" when
+ the ENV variable is absent — the internal blocklist then applies in full.
-- Source priority: system property `headers.blocked` overrides environment variable `HEADERS_BLOCKED`.
-- Default when not configured at all: `X-Channel-Request-Id` is blocked.
-- Explicit empty value (`headers.blocked=` / `HEADERS_BLOCKED=`): blacklist is empty (nothing is blocked).
-- Explicit non-empty value with valid headers (for example `headers.blocked=Some-Header`): only listed headers are blocked.
-- `X-Request-Id` is non-blockable: if it is listed in `headers.blocked`/`HEADERS_BLOCKED`, it is silently dropped from the configured list.
-- If the configured value contains only non-blockable entries (for example only `X-Request-Id`), the resulting blocked list is **empty** — the default is **not** restored. Any explicit configuration (even one that effectively blocks nothing) is treated as the user's deliberate override of the default.
+3. Spring's relaxed binding also recognises the ENV variable directly under the property name
+ `context.propagation.allow-blocked-headers` (i.e. without an explicit placeholder), so the same ENV variable works
+ even without the `application.yaml` indirection. The placeholder form is preferred because it
+ makes the dependency explicit in source control.
**MDC Integration:**
The `X-Channel-Request-Id` is automatically integrated with SLF4J's Mapped Diagnostic Context (MDC) for seamless logging.
diff --git a/core-context-propagation/framework-contexts/src/main/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfiguration.java b/core-context-propagation/framework-contexts/src/main/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfiguration.java
index e3d9a301a..6e2291cb5 100644
--- a/core-context-propagation/framework-contexts/src/main/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfiguration.java
+++ b/core-context-propagation/framework-contexts/src/main/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfiguration.java
@@ -1,21 +1,43 @@
package com.netcracker.cloud.framework.contexts.allowedheaders;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
+/**
+ * Computes the effective set of headers that must not be propagated to outgoing requests.
+ *
+ * Model
+ *
+ * - The framework owns an internal blocklist ({@link #INTERNAL_BLOCKED_HEADERS}). It is
+ * hard-coded and cannot be changed from configuration. By default it blocks
+ * {@code X-Channel-Request-Id}.
+ * - A user-facing system property {@value #ALLOW_BLOCKED_PROPERTY} carries a
+ * comma-separated list of header names that should be exempted from the internal
+ * blocklist (i.e. allowed to propagate). Names that are not in the internal
+ * blocklist have no effect.
+ *
+ *
+ * How the property is supplied
+ * The Quarkus extension copies {@code quarkus.context.propagation.allow-blocked-headers} into this system property
+ * at {@code RUNTIME_INIT}; the Spring configuration does the same for the Spring-style
+ * {@code context.propagation.allow-blocked-headers} property. In both cases standard env-to-property mapping
+ * applies, so a container ENV variable like {@code CONTEXT_PROPAGATION_ALLOW_BLOCKED_HEADERS=X-Channel-Request-Id}
+ * propagates all the way through to this class.
+ */
public final class HeaderPropagationConfiguration {
- public static final String HEADERS_BLOCKED_PROPERTY = "headers.blocked";
- public static final String HEADERS_BLOCKED_ENV = "HEADERS_BLOCKED";
- public static final List DEFAULT_BLOCKED_HEADERS =
- List.of("X-Channel-Request-Id");
- public static final List NON_BLOCKABLE_HEADERS =
- List.of("X-Request-Id");
+ /** System property carrying exempted-from-blocklist header names, comma-separated. */
+ public static final String ALLOW_BLOCKED_PROPERTY = "context.propagation.allow-blocked-headers";
+
+ /**
+ * Hard-coded internal blocklist of headers that the framework refuses to propagate
+ * unless explicitly exempted via {@link #ALLOW_BLOCKED_PROPERTY}.
+ */
+ public static final List INTERNAL_BLOCKED_HEADERS = List.of("X-Channel-Request-Id");
private static final AtomicReference cachedHeaders = new AtomicReference<>(null);
@@ -41,7 +63,7 @@ private static CachedHeaders getOrInit() {
synchronized (HeaderPropagationConfiguration.class) {
local = cachedHeaders.get();
if (local == null) {
- local = new CachedHeaders(readBlockedHeaders());
+ local = new CachedHeaders(computeEffectiveBlocklist());
cachedHeaders.set(local);
}
}
@@ -64,34 +86,25 @@ public static boolean isBlacklisted(String headerName) {
return getOrInit().lowerSet.contains(headerName.toLowerCase(Locale.ROOT));
}
- private static List readBlockedHeaders() {
- boolean propertySpecified = System.getProperties().containsKey(HEADERS_BLOCKED_PROPERTY);
- String envValue = System.getenv(HEADERS_BLOCKED_ENV);
- boolean envSpecified = envValue != null;
-
- // No source set at all → fall back to the built-in default.
- if (!propertySpecified && !envSpecified) {
- return DEFAULT_BLOCKED_HEADERS;
+ private static List computeEffectiveBlocklist() {
+ Set exemptions = readExemptions();
+ if (exemptions.isEmpty()) {
+ return INTERNAL_BLOCKED_HEADERS;
}
+ return INTERNAL_BLOCKED_HEADERS.stream()
+ .filter(h -> !exemptions.contains(h.toLowerCase(Locale.ROOT)))
+ .toList();
+ }
- // Property wins over env when both are set.
- String raw = propertySpecified
- ? System.getProperty(HEADERS_BLOCKED_PROPERTY)
- : envValue;
-
- // Source is set but empty/blank → explicit "erase the default".
+ private static Set readExemptions() {
+ String raw = System.getProperty(ALLOW_BLOCKED_PROPERTY);
if (raw == null || raw.isBlank()) {
- return Collections.emptyList();
+ return Set.of();
}
-
- // Source is set with a value → parse, trim, drop non-blockable entries.
- // If everything filters out, we still respect the user's explicit override
- // and return an empty list — we do NOT silently restore the default.
return Arrays.stream(raw.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
- .filter(s -> NON_BLOCKABLE_HEADERS.stream()
- .noneMatch(s::equalsIgnoreCase))
- .toList();
+ .map(s -> s.toLowerCase(Locale.ROOT))
+ .collect(Collectors.toUnmodifiableSet());
}
}
diff --git a/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java b/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java
index 68247fa37..de57edc09 100644
--- a/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java
+++ b/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/allowedheaders/HeaderPropagationConfigurationTest.java
@@ -1,157 +1,84 @@
package com.netcracker.cloud.framework.contexts.allowedheaders;
-import java.util.List;
-
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
-import uk.org.webcompere.systemstubs.jupiter.SystemStub;
-import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;
-@ExtendWith(SystemStubsExtension.class)
class HeaderPropagationConfigurationTest {
- @SystemStub
- private EnvironmentVariables environmentVariables = new EnvironmentVariables("TEST_PROP_FOR_ENV_SETUP", "1");
@BeforeEach
void setup() {
- System.clearProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY);
+ System.clearProperty(HeaderPropagationConfiguration.ALLOW_BLOCKED_PROPERTY);
HeaderPropagationConfiguration.resetCache();
}
@AfterEach
void cleanup() {
- System.clearProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY);
- HeaderPropagationConfiguration.resetCache();
- }
-
- @Test
- void shouldBlacklistHeaderByPropertyName() {
- System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, "X-Channel-Request-Id");
+ System.clearProperty(HeaderPropagationConfiguration.ALLOW_BLOCKED_PROPERTY);
HeaderPropagationConfiguration.resetCache();
-
- Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
- Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("x-channel-request-id"));
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
- }
-
- @Test
- void shouldParseCommaSeparatedBlacklistedHeaders() {
- System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, "X-Channel-Request-Id, X-Request-Id");
- HeaderPropagationConfiguration.resetCache();
-
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
- Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("x-channel-request-id"));
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("Custom-Header"));
}
@Test
- void shouldBlacklistXChannelRequestIdByDefault() {
+ void shouldBlockXChannelRequestIdByDefault() {
Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("x-channel-request-id"));
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
+ Assertions.assertEquals(HeaderPropagationConfiguration.INTERNAL_BLOCKED_HEADERS,
+ HeaderPropagationConfiguration.blockedHeaders());
}
@Test
- void shouldNotBlacklistXChannelRequestIdWhenBlockedHeadersExplicitlyEmpty() {
- System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, "");
+ void shouldNotBlockXChannelRequestIdWhenExempted() {
+ System.setProperty(HeaderPropagationConfiguration.ALLOW_BLOCKED_PROPERTY, "X-Channel-Request-Id");
HeaderPropagationConfiguration.resetCache();
Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("x-channel-request-id"));
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
+ Assertions.assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty());
}
@Test
- void shouldNotBlacklistXChannelRequestIdWhenOtherHeadersExplicitlyBlocked() {
- // The property is explicitly set to a non-blockable header; the default
- // blocked list must NOT silently kick in.
- System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, "X-Request-Id");
+ void shouldApplyExemptionsCaseInsensitively() {
+ System.setProperty(HeaderPropagationConfiguration.ALLOW_BLOCKED_PROPERTY, "x-channel-request-id");
HeaderPropagationConfiguration.resetCache();
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
}
@Test
- void shouldReturnEmptyListWhenOnlyNonBlockableHeadersConfigured() {
- System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, "X-Request-Id, x-request-id");
+ void shouldIgnoreUnknownExemptionEntries() {
+ // Names that are not in the internal blocklist must not change anything.
+ System.setProperty(HeaderPropagationConfiguration.ALLOW_BLOCKED_PROPERTY, "Custom-Header, X-Request-Id");
HeaderPropagationConfiguration.resetCache();
+ Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
+ "Internal blocklist must remain unchanged when no exemption matches it");
+ Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("Custom-Header"));
Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
- Assertions.assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty());
}
@Test
- void shouldReadEnvWhenPropertyNotSet() {
- // Env value is read when no system property is set. The configured value
- // is only X-Request-Id (non-blockable), so the resulting list must be empty —
- // we deliberately do NOT fall back to the default blocked list here.
- environmentVariables.set(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV, "X-Request-Id");
- try {
- HeaderPropagationConfiguration.resetCache();
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
- Assertions.assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty());
- } finally {
- environmentVariables.remove(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV);
- HeaderPropagationConfiguration.resetCache();
- }
- }
+ void shouldTreatEmptyExemptionPropertyAsNoExemption() {
+ System.setProperty(HeaderPropagationConfiguration.ALLOW_BLOCKED_PROPERTY, "");
+ HeaderPropagationConfiguration.resetCache();
- @Test
- void shouldBlacklistHeaderByEnvWhenPropertyNotSet() throws Exception {
- // Sanity check that env-sourced configuration actually drives the blocked list
- // when the system property is absent.
- environmentVariables.set(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV, "Custom-Header");
- try {
- HeaderPropagationConfiguration.resetCache();
- Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("Custom-Header"));
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
- } finally {
- environmentVariables.remove(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV);
- HeaderPropagationConfiguration.resetCache();
- }
+ Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
+ Assertions.assertEquals(HeaderPropagationConfiguration.INTERNAL_BLOCKED_HEADERS,
+ HeaderPropagationConfiguration.blockedHeaders());
}
@Test
- void shouldNotBlockXRequestIdEvenWhenExplicitlyListed() {
- System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, "X-Channel-Request-Id, X-Request-Id");
+ void shouldTreatBlankAndCommaOnlyExemptionPropertyAsNoExemption() {
+ System.setProperty(HeaderPropagationConfiguration.ALLOW_BLOCKED_PROPERTY, " , ,, ");
HeaderPropagationConfiguration.resetCache();
-
- // X-Request-Id is never blocked
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"));
- // X-Channel-Request-Id blocked
+
Assertions.assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
- // there is no X-Request-Id in a list
- Assertions.assertFalse(HeaderPropagationConfiguration.blockedHeaders()
- .stream().anyMatch(h -> h.equalsIgnoreCase("X-Request-Id")));
}
-
- @Test
- void shouldReturnEmptyListWhenEnvExplicitlyEmpty() throws Exception {
- environmentVariables.set(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV, "");
- try {
- HeaderPropagationConfiguration.resetCache();
- Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"));
- Assertions.assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty());
- } finally {
- environmentVariables.remove(HeaderPropagationConfiguration.HEADERS_BLOCKED_ENV);
- HeaderPropagationConfiguration.resetCache();
- }
- }
-
+
@Test
- void shouldBlockedHeadersReturnCorrectList() {
- System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, "Custom-Header, X-Request-Id");
- HeaderPropagationConfiguration.resetCache();
-
- List blocked = HeaderPropagationConfiguration.blockedHeaders();
- Assertions.assertTrue(blocked.contains("Custom-Header"));
- Assertions.assertFalse(blocked.stream().anyMatch(h -> h.equalsIgnoreCase("X-Request-Id")));
+ void isBlacklistedShouldReturnFalseForNullAndBlank() {
+ Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted(null));
+ Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted(""));
+ Assertions.assertFalse(HeaderPropagationConfiguration.isBlacklisted(" "));
}
}
diff --git a/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/xchannelrequestid/XChannelRequestIdContextObjectPropagationTest.java b/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/xchannelrequestid/XChannelRequestIdContextObjectPropagationTest.java
index e806c6573..c26e5c2a3 100644
--- a/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/xchannelrequestid/XChannelRequestIdContextObjectPropagationTest.java
+++ b/core-context-propagation/framework-contexts/src/test/java/com/netcracker/cloud/framework/contexts/xchannelrequestid/XChannelRequestIdContextObjectPropagationTest.java
@@ -69,20 +69,17 @@ void testXChannelRequestIdPropagationWithResponsePropagatableData() {
}
@Test
- void testXChannelRequestIdPropagationIsBlockedWhenConfigured() {
- System.setProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY, X_CHANNEL_REQUEST_ID);
+ void testXChannelRequestIdPropagationIsBlockedByInternalBlocklist() {
+ System.clearProperty(HeaderPropagationConfiguration.ALLOW_BLOCKED_PROPERTY);
HeaderPropagationConfiguration.resetCache();
- try {
- RequestContextPropagation.initRequestContext(new ContextDataRequest()); // filter
- ContextDataResponse responseContextData = new ContextDataResponse();
- RequestContextPropagation.setResponsePropagatableData(responseContextData);
- Assertions.assertEquals("-", responseContextData.getResponseHeaders().get(X_CHANNEL_REQUEST_ID));
-
- Map> serializableContextData = ContextManager.getSerializableContextData();
- Assertions.assertTrue(serializableContextData.getOrDefault(X_CHANNEL_REQUEST_ID_CONTEXT_NAME, Collections.emptyMap()).isEmpty());
- } finally {
- System.clearProperty(HeaderPropagationConfiguration.HEADERS_BLOCKED_PROPERTY);
- }
+
+ RequestContextPropagation.initRequestContext(new ContextDataRequest()); // filter
+ ContextDataResponse responseContextData = new ContextDataResponse();
+ RequestContextPropagation.setResponsePropagatableData(responseContextData);
+ Assertions.assertEquals("-", responseContextData.getResponseHeaders().get(X_CHANNEL_REQUEST_ID));
+
+ Map> serializableContextData = ContextManager.getSerializableContextData();
+ Assertions.assertTrue(serializableContextData.getOrDefault(X_CHANNEL_REQUEST_ID_CONTEXT_NAME, Collections.emptyMap()).isEmpty());
}
@Test
diff --git a/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationSpringCommonTest.java b/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationSpringCommonTest.java
index 50cfdc264..01bbb10e9 100644
--- a/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationSpringCommonTest.java
+++ b/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationSpringCommonTest.java
@@ -30,7 +30,7 @@ class RequestPropagationSpringCommonTest {
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).addFilter(filter).build();
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
}
@Test
diff --git a/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationTest.java b/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationTest.java
index a9f9fb421..e62da4eba 100644
--- a/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationTest.java
+++ b/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationTest.java
@@ -34,7 +34,8 @@
TestController.class, RequestPropagationTestConfig.class})
@TestPropertySource(properties = {
"headers.allowed=custom-header",
- "headers.blocked=",
+ // context.propagation.allow-blocked-headers deliberately not set — the internal blocklist applies and
+ // X-Channel-Request-Id should not propagate to outgoing requests.
"cloud-core.context-propagation.url=/test_url/v111/test"
})
class RequestPropagationTest {
@@ -64,7 +65,7 @@ class RequestPropagationTest {
@BeforeEach
void setUp() {
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
HeaderPropagationConfiguration.resetCache();
ContextManager.reinitialize();
mockMvc = MockMvcBuilders.webAppContextSetup(context).addFilters(preAuthnFilter, postAuthnFilter).build();
@@ -107,15 +108,14 @@ void testRequestPropagation() throws Exception {
}
@Test
- void testXRequestIdStillPropagatesWhenAddedToBlockedList() throws Exception {
- System.setProperty("headers.blocked", X_REQUEST_ID_NAME);
+ void testXRequestIdNotAffectedByExemptionConfig() throws Exception {
+ System.setProperty("context.propagation.allow-blocked-headers", X_REQUEST_ID_NAME);
HeaderPropagationConfiguration.resetCache();
ContextManager.reinitialize();
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(requestTo("/chain_request"))
.andExpect(header(HttpHeaders.ACCEPT_LANGUAGE, ACCEPT_LANGUAGE_VALUE))
- // X-Request-Id is non-blockable and must still be propagated.
.andExpect(header(X_REQUEST_ID_NAME, X_REQUEST_ID_VALUE))
.andExpect(header(CUSTOM_NAME, CUSTOM_VALUE))
.andRespond(withSuccess());
diff --git a/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationXChannelRequestIdAllowedTest.java b/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationXChannelRequestIdAllowedTest.java
index fa47551a4..3c567f1fb 100644
--- a/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationXChannelRequestIdAllowedTest.java
+++ b/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationXChannelRequestIdAllowedTest.java
@@ -33,7 +33,7 @@
TestController.class, RequestPropagationTestConfig.class})
@TestPropertySource(properties = {
"headers.allowed=custom-header",
- "headers.blocked=",
+ "context.propagation.allow-blocked-headers=X-Channel-Request-Id",
"cloud-core.context-propagation.url=/test_url/v111/test"
})
class RequestPropagationXChannelRequestIdAllowedTest {
@@ -64,7 +64,7 @@ class RequestPropagationXChannelRequestIdAllowedTest {
@BeforeAll
static void beforeAll() {
System.setProperty("headers.allowed", "custom-header");
- System.setProperty("headers.blocked", "");
+ System.setProperty("context.propagation.allow-blocked-headers", "X-Channel-Request-Id");
HeaderPropagationConfiguration.resetCache();
ContextManager.reinitialize();
}
@@ -72,7 +72,7 @@ static void beforeAll() {
@AfterAll
static void afterAll() {
System.clearProperty("headers.allowed");
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
HeaderPropagationConfiguration.resetCache();
ContextManager.reinitialize();
}
diff --git a/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationXChannelRequestIdResponseTest.java b/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationXChannelRequestIdResponseTest.java
index d60295141..19d649a2c 100644
--- a/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationXChannelRequestIdResponseTest.java
+++ b/core-context-propagation/sample-context-tests/src/test/java/com/netcracker/cloud/context/propagation/sample/requests/RequestPropagationXChannelRequestIdResponseTest.java
@@ -31,7 +31,7 @@
@TestPropertySource(properties = {
"headers.allowed=custom-header",
"cloud-core.context-propagation.url=/test_url/v111/test"
- // headers.blocked is not set, X-Channel-Request-Id blocked for outgoing requests
+ // context.propagation.allow-blocked-headers is not set, internal blocklist applies → X-Channel-Request-Id blocked for outgoing requests
})
class RequestPropagationXChannelRequestIdResponseTest {
@@ -55,14 +55,14 @@ class RequestPropagationXChannelRequestIdResponseTest {
@BeforeAll
static void beforeAll() {
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
HeaderPropagationConfiguration.resetCache();
ContextManager.reinitialize();
}
@AfterAll
static void afterAll() {
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
HeaderPropagationConfiguration.resetCache();
ContextManager.reinitialize();
}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/main/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfiguration.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/main/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfiguration.java
index 3166e2397..b06fc1b31 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/main/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfiguration.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/main/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfiguration.java
@@ -13,12 +13,14 @@
@Configuration
public class SpringContextProviderConfiguration {
+
@Bean
- public SpringPostAuthnContextProviderFilter springPostAuthnContextProviderFilter(){
+ public SpringPostAuthnContextProviderFilter springPostAuthnContextProviderFilter() {
return new SpringPostAuthnContextProviderFilter();
}
+
@Bean
- public SpringPreAuthnContextProviderFilter springPreAuthnContextProviderFilter(){
+ public SpringPreAuthnContextProviderFilter springPreAuthnContextProviderFilter() {
return new SpringPreAuthnContextProviderFilter();
}
@@ -29,14 +31,14 @@ public SpringPreAuthnContextProviderFilter springPreAuthnContextProviderFilter()
@Value("${headers.allowed:}")
private String allowedHeaders;
- @Value("${headers.blocked:}")
- private String blockedHeaders;
+ @Value("${context.propagation.allow-blocked-headers:}")
+ private String allowedFromBlocklist;
@PostConstruct
public void init() {
System.setProperty("headers.allowed", allowedHeaders);
- if (environment.containsProperty("headers.blocked")) {
- System.setProperty("headers.blocked", blockedHeaders);
+ if (environment.containsProperty("context.propagation.allow-blocked-headers")) {
+ System.setProperty("context.propagation.allow-blocked-headers", allowedFromBlocklist);
}
}
}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/HeaderPropagationStateReset.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/HeaderPropagationStateReset.java
index 8c0a30297..3f92991b4 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/HeaderPropagationStateReset.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/HeaderPropagationStateReset.java
@@ -7,11 +7,19 @@
import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
public class HeaderPropagationStateReset implements BeforeAllCallback, AfterAllCallback {
- @Override public void beforeAll(ExtensionContext ctx) { reset(); }
- @Override public void afterAll(ExtensionContext ctx) { reset(); }
+
+ @Override
+ public void beforeAll(ExtensionContext ctx) {
+ reset();
+ }
+
+ @Override
+ public void afterAll(ExtensionContext ctx) {
+ reset();
+ }
private static void reset() {
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
System.clearProperty("headers.allowed");
HeaderPropagationConfiguration.resetCache();
}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java
index f9c0905ad..b5fe96476 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationEmptyTest.java
@@ -1,6 +1,7 @@
package com.netcracker.cloud.context.propagation.spring.common.configuration;
import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
+
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.annotation.DirtiesContext;
@@ -9,33 +10,24 @@
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Spring integration scenario: {@code headers.blocked=} (set explicitly to empty).
- * Verifies that {@link SpringContextProviderConfiguration#init()} propagates the
- * empty value to the system property, which downstream code interprets as
- * "erase the default blocked list".
- */
@ExtendWith({HeaderPropagationStateReset.class, SpringExtension.class})
@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
@TestPropertySource(properties = {
"headers.allowed=custom-header",
- "headers.blocked="
+ "context.propagation.allow-blocked-headers="
})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class SpringContextProviderConfigurationEmptyTest {
@Test
- void shouldSetEmptySystemPropertyAndEraseDefaultBlockedList() {
- assertEquals("", System.getProperty("headers.blocked"),
+ void shouldSetEmptySystemPropertyAndStillApplyInternalBlocklist() {
+ assertEquals("", System.getProperty("context.propagation.allow-blocked-headers"),
"Spring init() must propagate explicit empty value to the system property");
HeaderPropagationConfiguration.resetCache();
- assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty(),
- "explicit empty value must erase the default blocked list");
- assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
- "X-Channel-Request-Id must no longer be blocked");
+ assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
+ "Internal blocklist must still apply when the exemption property is blank");
}
}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java
index 4b56a696f..121acc73b 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationFromEnvVarTest.java
@@ -15,38 +15,27 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Spring integration scenario: {@code headers.blocked} is configured only via the
- * {@code HEADERS_BLOCKED} environment variable. Verifies that Spring's relaxed
- * binding picks up the env var as the {@code headers.blocked} property, that
- * {@link SpringContextProviderConfiguration#init()} propagates it to the system
- * property, and that the downstream blocked list reflects the env-sourced value.
- *
- * {@link SystemStubsExtension} must be registered before {@link SpringExtension}
- * so that the env var is set before Spring's {@code SystemEnvironmentPropertySource}
- * is consulted during context initialization.
- */
@ExtendWith({HeaderPropagationStateReset.class, SystemStubsExtension.class, SpringExtension.class})
@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
@TestPropertySource(properties = {
- // headers.blocked deliberately not declared here — it must come from the env var
+ // context.propagation.allow-blocked-headers deliberately NOT declared here — it must come from the env var
"headers.allowed=custom-header"
})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class SpringContextProviderConfigurationFromEnvVarTest {
@SystemStub
- static EnvironmentVariables envVars = new EnvironmentVariables("HEADERS_BLOCKED", "Custom-Header");
+ static EnvironmentVariables envVars = new EnvironmentVariables("CONTEXT_PROPAGATION_ALLOW_BLOCKED_HEADERS", "X-Channel-Request-Id");
@Test
- void shouldReadHeadersBlockedFromEnvVar() {
- assertEquals("Custom-Header", System.getProperty("headers.blocked"),
+ void shouldReadHeadersAllowBlockedFromEnvVar() {
+ assertEquals("X-Channel-Request-Id", System.getProperty("context.propagation.allow-blocked-headers"),
"Spring init() must propagate env-sourced value to the system property");
HeaderPropagationConfiguration.resetCache();
- assertTrue(HeaderPropagationConfiguration.isBlacklisted("Custom-Header"),
- "env-sourced header must be blocked");
assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
- "explicit env-sourced configuration overrides the default");
+ "Env-sourced exemption must take effect");
+ assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty(),
+ "The only entry of the internal blocklist must be removed by the exemption");
}
}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java
index 1d4ba15fa..a9da90895 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationNotConfiguredTest.java
@@ -11,27 +11,22 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Spring integration scenario: {@code headers.blocked} is not set anywhere.
- * Verifies that {@link SpringContextProviderConfiguration#init()} does NOT touch
- * the {@code headers.blocked} system property, so downstream code falls back to
- * the built-in default blocked list (which contains {@code X-Channel-Request-Id}).
- */
@ExtendWith({HeaderPropagationStateReset.class, SpringExtension.class})
@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
@TestPropertySource(properties = {
+ // context.propagation.allow-blocked-headers intentionally absent
"headers.allowed=custom-header"
})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class SpringContextProviderConfigurationNotConfiguredTest {
@Test
- void shouldNotSetSystemPropertyAndApplyDefaultBlockedList() {
- assertNull(System.getProperty("headers.blocked"),
- "headers.blocked must remain unset when no source configures it");
+ void shouldNotTouchSystemPropertyAndKeepInternalBlocklist() {
+ assertNull(System.getProperty("context.propagation.allow-blocked-headers"),
+ "context.propagation.allow-blocked-headers must remain unset when no source configures it");
HeaderPropagationConfiguration.resetCache();
assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
- "default blocked list must apply when nothing is configured");
+ "Internal blocklist must apply when no exemption is configured");
}
}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java
deleted file mode 100644
index b6649f58b..000000000
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationOnlyNonBlockableTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.netcracker.cloud.context.propagation.spring.common.configuration;
-
-import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.TestPropertySource;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * Spring integration scenario: {@code headers.blocked=X-Request-Id} — the configured
- * value consists exclusively of a non-blockable header. The resulting blocked list
- * must be empty, NOT the built-in default. This locks in the behavior change made
- * to {@link HeaderPropagationConfiguration} (no silent fallback to default when the
- * user explicitly configured something).
- */
-@ExtendWith({HeaderPropagationStateReset.class, SpringExtension.class})
-@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
-@TestPropertySource(properties = {
- "headers.allowed=custom-header",
- "headers.blocked=X-Request-Id"
-})
-@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
-class SpringContextProviderConfigurationOnlyNonBlockableTest {
-
- @Test
- void shouldRespectExplicitOverrideEvenWhenItFiltersToEmpty() {
- assertEquals("X-Request-Id", System.getProperty("headers.blocked"));
-
- HeaderPropagationConfiguration.resetCache();
- assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty(),
- "the resulting blocked list must be empty — the default must NOT be restored");
- assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Request-Id"),
- "X-Request-Id is non-blockable and must never be blocked");
- assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
- "explicit (even if filter-emptied) configuration overrides the default");
- }
-}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationUnknownExemptionTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationUnknownExemptionTest.java
new file mode 100644
index 000000000..1a8a766c1
--- /dev/null
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationUnknownExemptionTest.java
@@ -0,0 +1,34 @@
+package com.netcracker.cloud.context.propagation.spring.common.configuration;
+
+import com.netcracker.cloud.framework.contexts.allowedheaders.HeaderPropagationConfiguration;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@ExtendWith({HeaderPropagationStateReset.class, SpringExtension.class})
+@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
+@TestPropertySource(properties = {
+ "headers.allowed=custom-header",
+ "context.propagation.allow-blocked-headers=Custom-Header, X-Some-Other-Header"
+})
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
+class SpringContextProviderConfigurationUnknownExemptionTest {
+
+ @Test
+ void shouldLeaveInternalBlocklistIntactWhenExemptionsDontMatch() {
+ assertEquals("Custom-Header, X-Some-Other-Header",
+ System.getProperty("context.propagation.allow-blocked-headers"));
+
+ HeaderPropagationConfiguration.resetCache();
+ assertTrue(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
+ "Internal blocklist must remain intact when no exemption matches it");
+ assertEquals(HeaderPropagationConfiguration.INTERNAL_BLOCKED_HEADERS,
+ HeaderPropagationConfiguration.blockedHeaders());
+ }
+}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java
index 88e48709d..b496265a1 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-common/src/test/java/com/netcracker/cloud/context/propagation/spring/common/configuration/SpringContextProviderConfigurationWithValueTest.java
@@ -12,30 +12,23 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Spring integration scenario: {@code headers.blocked} is set to a concrete blockable header.
- * Verifies that the listed header is blocked and the default's
- * {@code X-Channel-Request-Id} entry no longer applies.
- */
@ExtendWith({HeaderPropagationStateReset.class, SpringExtension.class})
@ContextConfiguration(classes = SpringContextProviderConfiguration.class)
@TestPropertySource(properties = {
"headers.allowed=custom-header",
- "headers.blocked=Custom-Header"
+ "context.propagation.allow-blocked-headers=X-Channel-Request-Id"
})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
class SpringContextProviderConfigurationWithValueTest {
@Test
- void shouldSetSystemPropertyAndBlockConfiguredHeader() {
- assertEquals("Custom-Header", System.getProperty("headers.blocked"));
+ void shouldExemptListedHeaderFromInternalBlocklist() {
+ assertEquals("X-Channel-Request-Id", System.getProperty("context.propagation.allow-blocked-headers"));
HeaderPropagationConfiguration.resetCache();
- assertTrue(HeaderPropagationConfiguration.isBlacklisted("Custom-Header"),
- "configured header must be blocked");
- assertTrue(HeaderPropagationConfiguration.isBlacklisted("custom-header"),
- "blocking must be case-insensitive");
assertFalse(HeaderPropagationConfiguration.isBlacklisted("X-Channel-Request-Id"),
- "explicit configuration overrides the default blocked list");
+ "Exempted header must not be blocked");
+ assertTrue(HeaderPropagationConfiguration.blockedHeaders().isEmpty(),
+ "The only entry of the internal blocklist (X-Channel-Request-Id) must be removed");
}
}
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-kafka/src/test/java/com/netcracker/cloud/context/propagation/spring/kafka/ContextPropagationTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-kafka/src/test/java/com/netcracker/cloud/context/propagation/spring/kafka/ContextPropagationTest.java
index afe9ff138..62a134218 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-kafka/src/test/java/com/netcracker/cloud/context/propagation/spring/kafka/ContextPropagationTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-kafka/src/test/java/com/netcracker/cloud/context/propagation/spring/kafka/ContextPropagationTest.java
@@ -61,7 +61,7 @@ public class ContextPropagationTest {
@BeforeAll
static void setup() {
System.setProperty("headers.allowed", CUSTOM_HEADER.toLowerCase());
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
HeaderPropagationConfiguration.resetCache();
}
@@ -73,7 +73,7 @@ void beforeEach() {
@AfterEach
void afterEach() {
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
HeaderPropagationConfiguration.resetCache();
}
@@ -100,8 +100,10 @@ void testContextPropagationBlocksXChannelRequestIdByDefault() throws Exception {
@Test
@Timeout(30)
- public void testContextPropagationAllowsXChannelRequestIdWhenHeadersBlockedEmpty() throws Exception {
- System.setProperty("headers.blocked", "");
+ public void testContextPropagationAllowsXChannelRequestIdWhenExempted() throws Exception {
+ System.setProperty("context.propagation.allow-blocked-headers", X_CHANNEL_REQUEST_ID_NAME);
+ HeaderPropagationConfiguration.resetCache();
+
ChannelRequestIdContext.set(X_CHANNEL_REQUEST_ID_VALUE);
AcceptLanguageContext.set(TEST_LANG);
AllowedHeadersContext.set(Map.of(CUSTOM_HEADER, CUSTOM_HEADER_VALUE));
diff --git a/core-context-propagation/spring-context-aggregator/context-propagation-spring-rabbit/src/test/java/com/netcracker/cloud/context/propagation/spring/rabbit/PropagationTest.java b/core-context-propagation/spring-context-aggregator/context-propagation-spring-rabbit/src/test/java/com/netcracker/cloud/context/propagation/spring/rabbit/PropagationTest.java
index bc539c6e5..581b06fd8 100644
--- a/core-context-propagation/spring-context-aggregator/context-propagation-spring-rabbit/src/test/java/com/netcracker/cloud/context/propagation/spring/rabbit/PropagationTest.java
+++ b/core-context-propagation/spring-context-aggregator/context-propagation-spring-rabbit/src/test/java/com/netcracker/cloud/context/propagation/spring/rabbit/PropagationTest.java
@@ -87,7 +87,7 @@ public static void setup() throws Exception {
channel.queueBind("orders", "orders", "invoice");
}
System.setProperty("headers.allowed", CUSTOM_HEADER.toLowerCase());
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
}
@AfterAll
@@ -103,7 +103,7 @@ void beforeEach() {
@AfterEach
void afterEach() {
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
HeaderPropagationConfiguration.resetCache();
}
@@ -125,8 +125,11 @@ public void testXChannelRequestIdBlockedByDefault() throws InterruptedException
@Test
@Timeout(value = 20, unit = TimeUnit.SECONDS)
- public void testXChannelRequestIdAllowedWhenHeadersBlockedEmpty() throws InterruptedException {
- System.setProperty("headers.blocked", "");
+ public void testXChannelRequestIdAllowedWhenExempted() throws InterruptedException {
+ // Exempt X-Channel-Request-Id from the internal blocklist — it must propagate.
+ System.setProperty("context.propagation.allow-blocked-headers", X_CHANNEL_REQUEST_ID_NAME);
+ HeaderPropagationConfiguration.resetCache();
+
AcceptLanguageContext.set("ZULU");
AllowedHeadersContext.set(Map.of(CUSTOM_HEADER, CUSTOM_HEADER_VALUE));
ChannelRequestIdContext.set(X_CHANNEL_REQUEST_ID_VALUE);
@@ -142,12 +145,17 @@ public void testXChannelRequestIdAllowedWhenHeadersBlockedEmpty() throws Interru
@Test
@Timeout(value = 20, unit = TimeUnit.SECONDS)
- public void testCustomHeaderBlockedWhenConfiguredByProperty() throws InterruptedException {
- System.setProperty("headers.blocked", ANOTHER_HEADER);
+ public void testUnknownExemptionDoesNotAffectInternalBlocklist() throws InterruptedException {
+ // Listing a header that is NOT in the internal blocklist has no effect — the
+ // internal blocklist (containing X-Channel-Request-Id) still applies.
+ System.setProperty("context.propagation.allow-blocked-headers", ANOTHER_HEADER);
+ HeaderPropagationConfiguration.resetCache();
+
AcceptLanguageContext.set("ZULU");
AllowedHeadersContext.set(Map.of(
CUSTOM_HEADER, CUSTOM_HEADER_VALUE,
ANOTHER_HEADER, ANOTHER_HEADER_VALUE));
+ ChannelRequestIdContext.set(X_CHANNEL_REQUEST_ID_VALUE);
template.convertAndSend("orders", "invoice", "rye wheat");
ContextManager.clearAll();
@@ -155,8 +163,10 @@ public void testCustomHeaderBlockedWhenConfiguredByProperty() throws Interrupted
fail("Message listener failed or message doesn't even arrived in 10 seconds");
}
- assertNull(getHeaderIgnoreCase(receivedHeaders.get(), ANOTHER_HEADER));
+ assertNull(getHeaderIgnoreCase(receivedHeaders.get(), X_CHANNEL_REQUEST_ID_NAME),
+ "Internal blocklist must remain intact when no exemption matches it");
assertEquals(CUSTOM_HEADER_VALUE, getHeaderIgnoreCase(receivedHeaders.get(), CUSTOM_HEADER));
+ assertEquals(ANOTHER_HEADER_VALUE, getHeaderIgnoreCase(receivedHeaders.get(), ANOTHER_HEADER));
}
diff --git a/maas-client/kafka-context-propagation/src/test/java/com/netcracker/cloud/maas/client/context/kafka/KafkaContextPropagationTest.java b/maas-client/kafka-context-propagation/src/test/java/com/netcracker/cloud/maas/client/context/kafka/KafkaContextPropagationTest.java
index addb62f3b..a5ee8744c 100644
--- a/maas-client/kafka-context-propagation/src/test/java/com/netcracker/cloud/maas/client/context/kafka/KafkaContextPropagationTest.java
+++ b/maas-client/kafka-context-propagation/src/test/java/com/netcracker/cloud/maas/client/context/kafka/KafkaContextPropagationTest.java
@@ -103,8 +103,8 @@ void testDumpDoesNotContainXChannelRequestIdByDefault() {
}
@Test
- void testDumpContainsXChannelRequestIdWhenNotBlocked() {
- System.setProperty("headers.blocked", "");
+ void testDumpContainsXChannelRequestIdWhenExempted() {
+ System.setProperty("context.propagation.allow-blocked-headers", X_CHANNEL_REQUEST_ID);
HeaderPropagationConfiguration.resetCache();
ContextManager.reinitialize();
@@ -117,7 +117,7 @@ void testDumpContainsXChannelRequestIdWhenNotBlocked() {
assertEquals("ch-456", dumped.get(X_CHANNEL_REQUEST_ID));
} finally {
- System.clearProperty("headers.blocked");
+ System.clearProperty("context.propagation.allow-blocked-headers");
HeaderPropagationConfiguration.resetCache();
ContextManager.reinitialize();
}