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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"CamelAggregatedCorrelationKey",
"CamelAggregatedSize",
"CamelAsteriskEventName",
"CamelAtmosphereWebsocketEventType",
"CamelExecExitValue",
"CamelFileLength",
"CamelFileName",
Expand Down
5 changes: 5 additions & 0 deletions components/camel-atmosphere-websocket/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
<version>${awaitility-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>

<!-- test infra -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Component><Feature>} 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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public final class ImportantHeaderUtils {
"CamelAggregatedCorrelationKey",
"CamelAggregatedSize",
"CamelAsteriskEventName",
"CamelAtmosphereWebsocketEventType",
"CamelExecExitValue",
"CamelFileLength",
"CamelFileName",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Loading
Loading