diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/atmosphere-websocket.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/atmosphere-websocket.json
index 181d8af69752b..8470227392767 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/atmosphere-websocket.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/atmosphere-websocket.json
@@ -40,10 +40,10 @@
"deserializationFilter": { "index": 12, "kind": "property", "displayName": "Deserialization Filter", "group": "security", "label": "advanced,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied when deserializing Java objects from requests or responses with Content-Type application\/x-java-serialized-object (only used when allowJavaSerializedObject or transferException is enabled). When not set, the JVM-wide jdk.serialFilter is used if present; otherwise a conservative default filter denying java.net. and otherwise allowing java., javax. and org.apache.camel. packages is applied." }
},
"headers": {
- "websocket.connectionKey": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The connection key.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY" },
- "websocket.connectionKey.list": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list of connection keys.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY_LIST" },
- "websocket.eventType": { "index": 2, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The type of event received. It can be ONOPEN_EVENT_TYPE, ONERROR_EVENT_TYPE or ONCLOSE_EVENT_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#EVENT_TYPE" },
- "websocket.errorType": { "index": 3, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of error that occurred. It can be MESSAGE_NOT_SENT_ERROR_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#ERROR_TYPE" }
+ "CamelAtmosphereWebsocketConnectionKey": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The connection key.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY" },
+ "CamelAtmosphereWebsocketConnectionKeyList": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list of connection keys.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY_LIST" },
+ "CamelAtmosphereWebsocketEventType": { "index": 2, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The type of event received. It can be ONOPEN_EVENT_TYPE, ONERROR_EVENT_TYPE or ONCLOSE_EVENT_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#EVENT_TYPE" },
+ "CamelAtmosphereWebsocketErrorType": { "index": 3, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of error that occurred. It can be MESSAGE_NOT_SENT_ERROR_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#ERROR_TYPE" }
},
"properties": {
"servicePath": { "index": 0, "kind": "path", "displayName": "Service Path", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of websocket endpoint" },
diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/important-headers.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/important-headers.json
index d5eefe63ecb68..059f30e1b87c3 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/important-headers.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/important-headers.json
@@ -2,6 +2,7 @@
"CamelAggregatedCorrelationKey",
"CamelAggregatedSize",
"CamelAsteriskEventName",
+ "CamelAtmosphereWebsocketEventType",
"CamelExecExitValue",
"CamelFileLength",
"CamelFileName",
diff --git a/components/camel-atmosphere-websocket/pom.xml b/components/camel-atmosphere-websocket/pom.xml
index 19fb80d1ab1d3..384c1f1cbb856 100644
--- a/components/camel-atmosphere-websocket/pom.xml
+++ b/components/camel-atmosphere-websocket/pom.xml
@@ -76,6 +76,11 @@
${awaitility-version}
test
+
+ org.assertj
+ assertj-core
+ test
+
diff --git a/components/camel-atmosphere-websocket/src/generated/resources/META-INF/org/apache/camel/component/atmosphere/websocket/atmosphere-websocket.json b/components/camel-atmosphere-websocket/src/generated/resources/META-INF/org/apache/camel/component/atmosphere/websocket/atmosphere-websocket.json
index 181d8af69752b..8470227392767 100644
--- a/components/camel-atmosphere-websocket/src/generated/resources/META-INF/org/apache/camel/component/atmosphere/websocket/atmosphere-websocket.json
+++ b/components/camel-atmosphere-websocket/src/generated/resources/META-INF/org/apache/camel/component/atmosphere/websocket/atmosphere-websocket.json
@@ -40,10 +40,10 @@
"deserializationFilter": { "index": 12, "kind": "property", "displayName": "Deserialization Filter", "group": "security", "label": "advanced,security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Sets an ObjectInputFilter pattern (jdk.serialFilter syntax) applied when deserializing Java objects from requests or responses with Content-Type application\/x-java-serialized-object (only used when allowJavaSerializedObject or transferException is enabled). When not set, the JVM-wide jdk.serialFilter is used if present; otherwise a conservative default filter denying java.net. and otherwise allowing java., javax. and org.apache.camel. packages is applied." }
},
"headers": {
- "websocket.connectionKey": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The connection key.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY" },
- "websocket.connectionKey.list": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list of connection keys.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY_LIST" },
- "websocket.eventType": { "index": 2, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The type of event received. It can be ONOPEN_EVENT_TYPE, ONERROR_EVENT_TYPE or ONCLOSE_EVENT_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#EVENT_TYPE" },
- "websocket.errorType": { "index": 3, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of error that occurred. It can be MESSAGE_NOT_SENT_ERROR_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#ERROR_TYPE" }
+ "CamelAtmosphereWebsocketConnectionKey": { "index": 0, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The connection key.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY" },
+ "CamelAtmosphereWebsocketConnectionKeyList": { "index": 1, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "java.util.List", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The list of connection keys.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#CONNECTION_KEY_LIST" },
+ "CamelAtmosphereWebsocketEventType": { "index": 2, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The type of event received. It can be ONOPEN_EVENT_TYPE, ONERROR_EVENT_TYPE or ONCLOSE_EVENT_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#EVENT_TYPE" },
+ "CamelAtmosphereWebsocketErrorType": { "index": 3, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "int", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The type of error that occurred. It can be MESSAGE_NOT_SENT_ERROR_TYPE.", "constantName": "org.apache.camel.component.atmosphere.websocket.WebsocketConstants#ERROR_TYPE" }
},
"properties": {
"servicePath": { "index": 0, "kind": "path", "displayName": "Service Path", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of websocket endpoint" },
diff --git a/components/camel-atmosphere-websocket/src/main/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstants.java b/components/camel-atmosphere-websocket/src/main/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstants.java
index c83e7377a73ae..00842a37a30c7 100644
--- a/components/camel-atmosphere-websocket/src/main/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstants.java
+++ b/components/camel-atmosphere-websocket/src/main/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstants.java
@@ -24,17 +24,17 @@
public final class WebsocketConstants {
@Metadata(description = "The connection key.", javaType = "java.lang.String")
- public static final String CONNECTION_KEY = "websocket.connectionKey";
+ public static final String CONNECTION_KEY = "CamelAtmosphereWebsocketConnectionKey";
@Metadata(description = "The list of connection keys.", javaType = "java.util.List")
- public static final String CONNECTION_KEY_LIST = "websocket.connectionKey.list";
- public static final String SEND_TO_ALL = "websocket.sendToAll";
+ public static final String CONNECTION_KEY_LIST = "CamelAtmosphereWebsocketConnectionKeyList";
+ public static final String SEND_TO_ALL = "CamelAtmosphereWebsocketSendToAll";
@Metadata(label = "consumer",
description = "The type of event received. It can be `ONOPEN_EVENT_TYPE`, `ONERROR_EVENT_TYPE` or `ONCLOSE_EVENT_TYPE`.",
javaType = "int", important = true)
- public static final String EVENT_TYPE = "websocket.eventType";
+ public static final String EVENT_TYPE = "CamelAtmosphereWebsocketEventType";
@Metadata(label = "consumer", description = "The type of error that occurred. It can be `MESSAGE_NOT_SENT_ERROR_TYPE`.",
javaType = "int")
- public static final String ERROR_TYPE = "websocket.errorType";
+ public static final String ERROR_TYPE = "CamelAtmosphereWebsocketErrorType";
public static final int ONOPEN_EVENT_TYPE = 1;
public static final int ONCLOSE_EVENT_TYPE = 0;
diff --git a/components/camel-atmosphere-websocket/src/test/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstantsTest.java b/components/camel-atmosphere-websocket/src/test/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstantsTest.java
new file mode 100644
index 0000000000000..cdd27b93792a1
--- /dev/null
+++ b/components/camel-atmosphere-websocket/src/test/java/org/apache/camel/component/atmosphere/websocket/WebsocketConstantsTest.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.atmosphere.websocket;
+
+import org.apache.camel.http.base.HttpHeaderFilterStrategy;
+import org.apache.camel.spi.HeaderFilterStrategy;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Verifies that the Exchange header constants follow the {@code Camel} naming convention, and
+ * therefore sit inside the {@code Camel} namespace that the inherited {@link HttpHeaderFilterStrategy} handles.
+ */
+class WebsocketConstantsTest {
+
+ private static final String[] HEADERS = {
+ WebsocketConstants.CONNECTION_KEY,
+ WebsocketConstants.CONNECTION_KEY_LIST,
+ WebsocketConstants.SEND_TO_ALL,
+ WebsocketConstants.EVENT_TYPE,
+ WebsocketConstants.ERROR_TYPE
+ };
+
+ private final HeaderFilterStrategy strategy = new HttpHeaderFilterStrategy();
+
+ @Test
+ void testHeaderNamesFollowCamelNamingConvention() {
+ assertThat(WebsocketConstants.CONNECTION_KEY).isEqualTo("CamelAtmosphereWebsocketConnectionKey");
+ assertThat(WebsocketConstants.CONNECTION_KEY_LIST).isEqualTo("CamelAtmosphereWebsocketConnectionKeyList");
+ assertThat(WebsocketConstants.SEND_TO_ALL).isEqualTo("CamelAtmosphereWebsocketSendToAll");
+ assertThat(WebsocketConstants.EVENT_TYPE).isEqualTo("CamelAtmosphereWebsocketEventType");
+ assertThat(WebsocketConstants.ERROR_TYPE).isEqualTo("CamelAtmosphereWebsocketErrorType");
+ }
+
+ @Test
+ void testHeadersAreFilteredByInheritedHttpHeaderFilterStrategy() {
+ // WebsocketEndpoint extends ServletEndpoint, so it inherits HttpHeaderFilterStrategy, which filters
+ // the Camel namespace case-insensitively in both directions
+ for (String header : HEADERS) {
+ assertThat(strategy.applyFilterToExternalHeaders(header, "aValue", null))
+ .as("%s should be filtered when mapping external headers in", header)
+ .isTrue();
+ assertThat(strategy.applyFilterToCamelHeaders(header, "aValue", null))
+ .as("%s should be filtered when mapping Camel headers out", header)
+ .isTrue();
+ }
+ }
+}
diff --git a/core/camel-util/src/main/java/org/apache/camel/util/ImportantHeaderUtils.java b/core/camel-util/src/main/java/org/apache/camel/util/ImportantHeaderUtils.java
index 14f22f6bd80eb..ca8c37f5fcfad 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/ImportantHeaderUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/ImportantHeaderUtils.java
@@ -30,6 +30,7 @@ public final class ImportantHeaderUtils {
"CamelAggregatedCorrelationKey",
"CamelAggregatedSize",
"CamelAsteriskEventName",
+ "CamelAtmosphereWebsocketEventType",
"CamelExecExitValue",
"CamelFileLength",
"CamelFileName",
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index 5bb33211e540a..323652455c692 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -1651,3 +1651,62 @@ from("undertow:http://0.0.0.0:8080/foo?headerFilterStrategy=#myStrategy")
Routes that already supply a custom `headerFilterStrategy` or a custom `undertowHttpBinding`
are unaffected.
+
+=== camel-atmosphere-websocket - potential breaking change
+
+The Exchange header constants in `WebsocketConstants` have been renamed to follow the
+Camel naming convention used across the rest of the component catalog. The Java field
+names are unchanged; only the header string values have changed:
+
+[options="header"]
+|===
+| Constant | Previous value | New value
+| `WebsocketConstants.CONNECTION_KEY` | `websocket.connectionKey` | `CamelAtmosphereWebsocketConnectionKey`
+| `WebsocketConstants.CONNECTION_KEY_LIST` | `websocket.connectionKey.list` | `CamelAtmosphereWebsocketConnectionKeyList`
+| `WebsocketConstants.SEND_TO_ALL` | `websocket.sendToAll` | `CamelAtmosphereWebsocketSendToAll`
+| `WebsocketConstants.EVENT_TYPE` | `websocket.eventType` | `CamelAtmosphereWebsocketEventType`
+| `WebsocketConstants.ERROR_TYPE` | `websocket.errorType` | `CamelAtmosphereWebsocketErrorType`
+|===
+
+Routes that reference the constant symbolically (for example
+`setHeader(WebsocketConstants.CONNECTION_KEY, ...)`) continue to work without changes.
+Routes that set the header by its literal string value must be updated to use the new
+value:
+
+._Java-only: updated atmosphere-websocket header names in route_
+[source,java]
+----
+// before
+from("direct:next")
+ .setHeader("websocket.connectionKey", header("myKey"))
+ .to("atmosphere-websocket:///servicepath");
+
+// after
+from("direct:next")
+ .setHeader("CamelAtmosphereWebsocketConnectionKey", header("myKey"))
+ .to("atmosphere-websocket:///servicepath");
+----
+
+The sibling websocket components are not affected by this change: `camel-undertow`
+keeps the `websocket.*` values in `UndertowConstants` (as documented in the 4.18
+upgrade guide), and `camel-vertx-websocket` keeps its `CamelVertxWebsocket.*` values.
+
+Note that `WebsocketConstants.SEND_TO_ALL` is not read by this component. Broadcast is
+selected through the `sendToAll` endpoint option, not through the header; the constant
+is renamed here only to keep the class internally consistent.
+
+==== Behaviour change: cross-transport propagation of the dispatch headers
+
+Because the renamed header values now begin with `Camel`, they are filtered by the
+standard transport `HeaderFilterStrategy` (`HttpHeaderFilterStrategy`,
+`JmsHeaderFilterStrategy`, etc.) when crossing a transport boundary, by design —
+`Camel*` headers are framework-internal and are not propagated over the wire.
+
+Routes that bridge an external transport (HTTP, JMS, ...) into an
+`atmosphere-websocket:` producer and let the sender choose the dispatch target via
+headers must therefore carry those values in non-`Camel`-prefixed application headers
+and map them to the corresponding `WebsocketConstants` value in the route between the
+transport `from` and the `atmosphere-websocket:` `to`. Allowing untrusted senders to
+drive `WebsocketConstants.CONNECTION_KEY_LIST`, which selects the target peers and
+takes precedence over `CONNECTION_KEY`, without such a mapping step is not the intended
+use of the component.
diff --git a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AtmosphereWebsocketEndpointBuilderFactory.java b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AtmosphereWebsocketEndpointBuilderFactory.java
index 11dd4528a31b2..d8885c7ea002e 100644
--- a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AtmosphereWebsocketEndpointBuilderFactory.java
+++ b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AtmosphereWebsocketEndpointBuilderFactory.java
@@ -1606,10 +1606,11 @@ public static class AtmosphereWebsocketHeaderNameBuilder {
*
* Group: common
*
- * @return the name of the header {@code websocket.connectionKey}.
+ * @return the name of the header {@code
+ * AtmosphereWebsocketConnectionKey}.
*/
- public String websocketConnectionkey() {
- return "websocket.connectionKey";
+ public String atmosphereWebsocketConnectionKey() {
+ return "CamelAtmosphereWebsocketConnectionKey";
}
/**
* The list of connection keys.
@@ -1618,10 +1619,11 @@ public String websocketConnectionkey() {
*
* Group: common
*
- * @return the name of the header {@code websocket.connectionKey.list}.
+ * @return the name of the header {@code
+ * AtmosphereWebsocketConnectionKeyList}.
*/
- public String websocketConnectionkeyList() {
- return "websocket.connectionKey.list";
+ public String atmosphereWebsocketConnectionKeyList() {
+ return "CamelAtmosphereWebsocketConnectionKeyList";
}
/**
* The type of event received. It can be ONOPEN_EVENT_TYPE,
@@ -1631,10 +1633,10 @@ public String websocketConnectionkeyList() {
*
* Group: consumer
*
- * @return the name of the header {@code websocket.eventType}.
+ * @return the name of the header {@code AtmosphereWebsocketEventType}.
*/
- public String websocketEventtype() {
- return "websocket.eventType";
+ public String atmosphereWebsocketEventType() {
+ return "CamelAtmosphereWebsocketEventType";
}
/**
* The type of error that occurred. It can be
@@ -1644,10 +1646,10 @@ public String websocketEventtype() {
*
* Group: consumer
*
- * @return the name of the header {@code websocket.errorType}.
+ * @return the name of the header {@code AtmosphereWebsocketErrorType}.
*/
- public String websocketErrortype() {
- return "websocket.errorType";
+ public String atmosphereWebsocketErrorType() {
+ return "CamelAtmosphereWebsocketErrorType";
}
}
static AtmosphereWebsocketEndpointBuilder endpointBuilder(String componentName, String path) {