This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new da0124046a4 Add handshakeHeaders uri param in vertx-websocket 
component (#13960)
da0124046a4 is described below

commit da0124046a4c861e6e02fd2f0711965acca6925e
Author: Alexis SEGURA <alex.segur...@gmail.com>
AuthorDate: Fri Apr 26 12:43:48 2024 +0200

    Add handshakeHeaders uri param in vertx-websocket component (#13960)
    
    Some websocket apis require authentication and various header during the 
handshake process in order to upgrade to websocket. It was not supported by the 
vertx-component. So I added an uri param to set a map of headers that will be 
passed as header in the HTTP handshake request.
    It is important to note that it only works when the endpoint is a producer, 
or a consumer of a remote host (consumeAsClient true).
    Use multiValue mecanism on UriParam.
    
    Co-authored-by: Alexis SEGURA <alexis.seg...@akt.io>
---
 .../camel/catalog/components/vertx-websocket.json  |  29 +--
 .../VertxWebsocketEndpointConfigurer.java          |  15 ++
 .../VertxWebsocketEndpointUriFactory.java          |   7 +-
 .../component/vertx/websocket/vertx-websocket.json |  29 +--
 .../vertx/websocket/VertxWebsocketComponent.java   |   4 +
 .../websocket/VertxWebsocketConfiguration.java     |  15 ++
 .../vertx/websocket/VertxWebsocketEndpoint.java    |   5 +
 .../VertxWebsocketEndpointConfigurationTest.java   |  18 ++
 .../VertxWebsocketHandshakeHeadersTest.java        | 226 +++++++++++++++++++++
 .../kotlin/components/VertxWebsocketUriDsl.kt      |   8 +
 10 files changed, 326 insertions(+), 30 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/vertx-websocket.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/vertx-websocket.json
index efe52316058..1841d9b9b2e 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/vertx-websocket.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/vertx-websocket.json
@@ -49,19 +49,20 @@
     "allowOriginHeader": { "index": 4, "kind": "parameter", "displayName": 
"Allow Origin Header", "group": "consumer", "label": "producer,consumer", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Whether the WebSocket 
client should add the Origin  [...]
     "consumeAsClient": { "index": 5, "kind": "parameter", "displayName": 
"Consume As Client", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When set to true, the 
consumer acts as a WebSocket client, crea [...]
     "fireWebSocketConnectionEvents": { "index": 6, "kind": "parameter", 
"displayName": "Fire Web Socket Connection Events", "group": "consumer", 
"label": "consumer", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Whether the server 
consumer will  [...]
-    "maxReconnectAttempts": { "index": 7, "kind": "parameter", "displayName": 
"Max Reconnect Attempts", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the maximum num [...]
-    "originHeaderUrl": { "index": 8, "kind": "parameter", "displayName": 
"Origin Header Url", "group": "consumer", "label": "producer,consumer", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "The value of the Origin 
header that the WebSocket client should use o [...]
-    "reconnectInitialDelay": { "index": 9, "kind": "parameter", "displayName": 
"Reconnect Initial Delay", "group": "consumer", "label": "consumer", 
"required": false, "type": "integer", "javaType": "int", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the initial d [...]
-    "reconnectInterval": { "index": 10, "kind": "parameter", "displayName": 
"Reconnect Interval", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 1000, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the interval in mi [...]
-    "router": { "index": 11, "kind": "parameter", "displayName": "Router", 
"group": "consumer", "label": "consumer", "required": false, "type": "object", 
"javaType": "io.vertx.ext.web.Router", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To use an existing vertx 
router for the HTTP server" },
-    "serverOptions": { "index": 12, "kind": "parameter", "displayName": 
"Server Options", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "io.vertx.core.http.HttpServerOptions", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Sets customized options 
for configuring the HTTP server hostin [...]
-    "bridgeErrorHandler": { "index": 13, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming [...]
-    "exceptionHandler": { "index": 14, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By de [...]
-    "exchangePattern": { "index": 15, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
-    "clientOptions": { "index": 16, "kind": "parameter", "displayName": 
"Client Options", "group": "producer", "label": "producer", "required": false, 
"type": "object", "javaType": "io.vertx.core.http.HttpClientOptions", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Sets customized options 
for configuring the WebSocket client u [...]
-    "clientSubProtocols": { "index": 17, "kind": "parameter", "displayName": 
"Client Sub Protocols", "group": "producer", "label": "producer", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Comma separated list of 
WebSocket subprotocols that the client should u [...]
-    "sendToAll": { "index": 18, "kind": "parameter", "displayName": "Send To 
All", "group": "producer", "label": "producer", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To send to all websocket 
subscribers. Can be used to configure at the endp [...]
-    "lazyStartProducer": { "index": 19, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
-    "sslContextParameters": { "index": 20, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To configure security 
using SSLCo [...]
+    "handshakeHeaders": { "index": 7, "kind": "parameter", "displayName": 
"Handshake Headers", "group": "consumer", "label": "producer,consumer", 
"required": false, "type": "object", "javaType": 
"java.util.Map<java.lang.String, java.lang.Object>", "prefix": "handshake.", 
"multiValue": true, "deprecated": false, "autowired": false, "secret": false, 
"configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "descri [...]
+    "maxReconnectAttempts": { "index": 8, "kind": "parameter", "displayName": 
"Max Reconnect Attempts", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the maximum num [...]
+    "originHeaderUrl": { "index": 9, "kind": "parameter", "displayName": 
"Origin Header Url", "group": "consumer", "label": "producer,consumer", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "The value of the Origin 
header that the WebSocket client should use o [...]
+    "reconnectInitialDelay": { "index": 10, "kind": "parameter", 
"displayName": "Reconnect Initial Delay", "group": "consumer", "label": 
"consumer", "required": false, "type": "integer", "javaType": "int", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, 
"configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the initial  [...]
+    "reconnectInterval": { "index": 11, "kind": "parameter", "displayName": 
"Reconnect Interval", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 1000, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the interval in mi [...]
+    "router": { "index": 12, "kind": "parameter", "displayName": "Router", 
"group": "consumer", "label": "consumer", "required": false, "type": "object", 
"javaType": "io.vertx.ext.web.Router", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To use an existing vertx 
router for the HTTP server" },
+    "serverOptions": { "index": 13, "kind": "parameter", "displayName": 
"Server Options", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "io.vertx.core.http.HttpServerOptions", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Sets customized options 
for configuring the HTTP server hostin [...]
+    "bridgeErrorHandler": { "index": 14, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming [...]
+    "exceptionHandler": { "index": 15, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By de [...]
+    "exchangePattern": { "index": 16, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
+    "clientOptions": { "index": 17, "kind": "parameter", "displayName": 
"Client Options", "group": "producer", "label": "producer", "required": false, 
"type": "object", "javaType": "io.vertx.core.http.HttpClientOptions", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Sets customized options 
for configuring the WebSocket client u [...]
+    "clientSubProtocols": { "index": 18, "kind": "parameter", "displayName": 
"Client Sub Protocols", "group": "producer", "label": "producer", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Comma separated list of 
WebSocket subprotocols that the client should u [...]
+    "sendToAll": { "index": 19, "kind": "parameter", "displayName": "Send To 
All", "group": "producer", "label": "producer", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To send to all websocket 
subscribers. Can be used to configure at the endp [...]
+    "lazyStartProducer": { "index": 20, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
+    "sslContextParameters": { "index": 21, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To configure security 
using SSLCo [...]
   }
 }
diff --git 
a/components/camel-vertx/camel-vertx-websocket/src/generated/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointConfigurer.java
 
b/components/camel-vertx/camel-vertx-websocket/src/generated/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointConfigurer.java
index 9d90229200d..1fea496c4e8 100644
--- 
a/components/camel-vertx/camel-vertx-websocket/src/generated/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointConfigurer.java
+++ 
b/components/camel-vertx/camel-vertx-websocket/src/generated/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointConfigurer.java
@@ -41,6 +41,8 @@ public class VertxWebsocketEndpointConfigurer extends 
PropertyConfigurerSupport
         case "exchangePattern": 
target.setExchangePattern(property(camelContext, 
org.apache.camel.ExchangePattern.class, value)); return true;
         case "firewebsocketconnectionevents":
         case "fireWebSocketConnectionEvents": 
target.getConfiguration().setFireWebSocketConnectionEvents(property(camelContext,
 boolean.class, value)); return true;
+        case "handshakeheaders":
+        case "handshakeHeaders": 
target.getConfiguration().setHandshakeHeaders(property(camelContext, 
java.util.Map.class, value)); return true;
         case "lazystartproducer":
         case "lazyStartProducer": 
target.setLazyStartProducer(property(camelContext, boolean.class, value)); 
return true;
         case "maxreconnectattempts":
@@ -83,6 +85,8 @@ public class VertxWebsocketEndpointConfigurer extends 
PropertyConfigurerSupport
         case "exchangePattern": return org.apache.camel.ExchangePattern.class;
         case "firewebsocketconnectionevents":
         case "fireWebSocketConnectionEvents": return boolean.class;
+        case "handshakeheaders":
+        case "handshakeHeaders": return java.util.Map.class;
         case "lazystartproducer":
         case "lazyStartProducer": return boolean.class;
         case "maxreconnectattempts":
@@ -126,6 +130,8 @@ public class VertxWebsocketEndpointConfigurer extends 
PropertyConfigurerSupport
         case "exchangePattern": return target.getExchangePattern();
         case "firewebsocketconnectionevents":
         case "fireWebSocketConnectionEvents": return 
target.getConfiguration().isFireWebSocketConnectionEvents();
+        case "handshakeheaders":
+        case "handshakeHeaders": return 
target.getConfiguration().getHandshakeHeaders();
         case "lazystartproducer":
         case "lazyStartProducer": return target.isLazyStartProducer();
         case "maxreconnectattempts":
@@ -146,5 +152,14 @@ public class VertxWebsocketEndpointConfigurer extends 
PropertyConfigurerSupport
         default: return null;
         }
     }
+
+    @Override
+    public Object getCollectionValueType(Object target, String name, boolean 
ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "handshakeheaders":
+        case "handshakeHeaders": return java.lang.Object.class;
+        default: return null;
+        }
+    }
 }
 
diff --git 
a/components/camel-vertx/camel-vertx-websocket/src/generated/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointUriFactory.java
 
b/components/camel-vertx/camel-vertx-websocket/src/generated/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointUriFactory.java
index 49d740af4ed..47f7fac1be1 100644
--- 
a/components/camel-vertx/camel-vertx-websocket/src/generated/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointUriFactory.java
+++ 
b/components/camel-vertx/camel-vertx-websocket/src/generated/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointUriFactory.java
@@ -23,7 +23,7 @@ public class VertxWebsocketEndpointUriFactory extends 
org.apache.camel.support.c
     private static final Set<String> SECRET_PROPERTY_NAMES;
     private static final Set<String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(21);
+        Set<String> props = new HashSet<>(22);
         props.add("allowOriginHeader");
         props.add("allowedOriginPattern");
         props.add("bridgeErrorHandler");
@@ -33,6 +33,7 @@ public class VertxWebsocketEndpointUriFactory extends 
org.apache.camel.support.c
         props.add("exceptionHandler");
         props.add("exchangePattern");
         props.add("fireWebSocketConnectionEvents");
+        props.add("handshakeHeaders");
         props.add("host");
         props.add("lazyStartProducer");
         props.add("maxReconnectAttempts");
@@ -47,7 +48,9 @@ public class VertxWebsocketEndpointUriFactory extends 
org.apache.camel.support.c
         props.add("sslContextParameters");
         PROPERTY_NAMES = Collections.unmodifiableSet(props);
         SECRET_PROPERTY_NAMES = Collections.emptySet();
-        MULTI_VALUE_PREFIXES = Collections.emptySet();
+        Set<String> prefixes = new HashSet<>(1);
+        prefixes.add("handshake.");
+        MULTI_VALUE_PREFIXES = Collections.unmodifiableSet(prefixes);
     }
 
     @Override
diff --git 
a/components/camel-vertx/camel-vertx-websocket/src/generated/resources/META-INF/org/apache/camel/component/vertx/websocket/vertx-websocket.json
 
b/components/camel-vertx/camel-vertx-websocket/src/generated/resources/META-INF/org/apache/camel/component/vertx/websocket/vertx-websocket.json
index efe52316058..1841d9b9b2e 100644
--- 
a/components/camel-vertx/camel-vertx-websocket/src/generated/resources/META-INF/org/apache/camel/component/vertx/websocket/vertx-websocket.json
+++ 
b/components/camel-vertx/camel-vertx-websocket/src/generated/resources/META-INF/org/apache/camel/component/vertx/websocket/vertx-websocket.json
@@ -49,19 +49,20 @@
     "allowOriginHeader": { "index": 4, "kind": "parameter", "displayName": 
"Allow Origin Header", "group": "consumer", "label": "producer,consumer", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Whether the WebSocket 
client should add the Origin  [...]
     "consumeAsClient": { "index": 5, "kind": "parameter", "displayName": 
"Consume As Client", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When set to true, the 
consumer acts as a WebSocket client, crea [...]
     "fireWebSocketConnectionEvents": { "index": 6, "kind": "parameter", 
"displayName": "Fire Web Socket Connection Events", "group": "consumer", 
"label": "consumer", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Whether the server 
consumer will  [...]
-    "maxReconnectAttempts": { "index": 7, "kind": "parameter", "displayName": 
"Max Reconnect Attempts", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the maximum num [...]
-    "originHeaderUrl": { "index": 8, "kind": "parameter", "displayName": 
"Origin Header Url", "group": "consumer", "label": "producer,consumer", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "The value of the Origin 
header that the WebSocket client should use o [...]
-    "reconnectInitialDelay": { "index": 9, "kind": "parameter", "displayName": 
"Reconnect Initial Delay", "group": "consumer", "label": "consumer", 
"required": false, "type": "integer", "javaType": "int", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the initial d [...]
-    "reconnectInterval": { "index": 10, "kind": "parameter", "displayName": 
"Reconnect Interval", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 1000, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the interval in mi [...]
-    "router": { "index": 11, "kind": "parameter", "displayName": "Router", 
"group": "consumer", "label": "consumer", "required": false, "type": "object", 
"javaType": "io.vertx.ext.web.Router", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To use an existing vertx 
router for the HTTP server" },
-    "serverOptions": { "index": 12, "kind": "parameter", "displayName": 
"Server Options", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "io.vertx.core.http.HttpServerOptions", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Sets customized options 
for configuring the HTTP server hostin [...]
-    "bridgeErrorHandler": { "index": 13, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming [...]
-    "exceptionHandler": { "index": 14, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By de [...]
-    "exchangePattern": { "index": 15, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
-    "clientOptions": { "index": 16, "kind": "parameter", "displayName": 
"Client Options", "group": "producer", "label": "producer", "required": false, 
"type": "object", "javaType": "io.vertx.core.http.HttpClientOptions", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Sets customized options 
for configuring the WebSocket client u [...]
-    "clientSubProtocols": { "index": 17, "kind": "parameter", "displayName": 
"Client Sub Protocols", "group": "producer", "label": "producer", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Comma separated list of 
WebSocket subprotocols that the client should u [...]
-    "sendToAll": { "index": 18, "kind": "parameter", "displayName": "Send To 
All", "group": "producer", "label": "producer", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To send to all websocket 
subscribers. Can be used to configure at the endp [...]
-    "lazyStartProducer": { "index": 19, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
-    "sslContextParameters": { "index": 20, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To configure security 
using SSLCo [...]
+    "handshakeHeaders": { "index": 7, "kind": "parameter", "displayName": 
"Handshake Headers", "group": "consumer", "label": "producer,consumer", 
"required": false, "type": "object", "javaType": 
"java.util.Map<java.lang.String, java.lang.Object>", "prefix": "handshake.", 
"multiValue": true, "deprecated": false, "autowired": false, "secret": false, 
"configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "descri [...]
+    "maxReconnectAttempts": { "index": 8, "kind": "parameter", "displayName": 
"Max Reconnect Attempts", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 0, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the maximum num [...]
+    "originHeaderUrl": { "index": 9, "kind": "parameter", "displayName": 
"Origin Header Url", "group": "consumer", "label": "producer,consumer", 
"required": false, "type": "string", "javaType": "java.lang.String", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "The value of the Origin 
header that the WebSocket client should use o [...]
+    "reconnectInitialDelay": { "index": 10, "kind": "parameter", 
"displayName": "Reconnect Initial Delay", "group": "consumer", "label": 
"consumer", "required": false, "type": "integer", "javaType": "int", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 0, 
"configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the initial  [...]
+    "reconnectInterval": { "index": 11, "kind": "parameter", "displayName": 
"Reconnect Interval", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": 1000, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "When consumeAsClient is 
set to true this sets the interval in mi [...]
+    "router": { "index": 12, "kind": "parameter", "displayName": "Router", 
"group": "consumer", "label": "consumer", "required": false, "type": "object", 
"javaType": "io.vertx.ext.web.Router", "deprecated": false, "autowired": false, 
"secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To use an existing vertx 
router for the HTTP server" },
+    "serverOptions": { "index": 13, "kind": "parameter", "displayName": 
"Server Options", "group": "consumer", "label": "consumer", "required": false, 
"type": "object", "javaType": "io.vertx.core.http.HttpServerOptions", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Sets customized options 
for configuring the HTTP server hostin [...]
+    "bridgeErrorHandler": { "index": 14, "kind": "parameter", "displayName": 
"Bridge Error Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Allows for bridging the consumer to the 
Camel routing Error Handler, which mean any exceptions (if possible) occurred 
while the Camel consumer is trying to pickup incoming [...]
+    "exceptionHandler": { "index": 15, "kind": "parameter", "displayName": 
"Exception Handler", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.", 
"deprecated": false, "autowired": false, "secret": false, "description": "To 
let the consumer use a custom ExceptionHandler. Notice if the option 
bridgeErrorHandler is enabled then this option is not in use. By de [...]
+    "exchangePattern": { "index": 16, "kind": "parameter", "displayName": 
"Exchange Pattern", "group": "consumer (advanced)", "label": 
"consumer,advanced", "required": false, "type": "object", "javaType": 
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ], 
"deprecated": false, "autowired": false, "secret": false, "description": "Sets 
the exchange pattern when the consumer creates an exchange." },
+    "clientOptions": { "index": 17, "kind": "parameter", "displayName": 
"Client Options", "group": "producer", "label": "producer", "required": false, 
"type": "object", "javaType": "io.vertx.core.http.HttpClientOptions", 
"deprecated": false, "autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Sets customized options 
for configuring the WebSocket client u [...]
+    "clientSubProtocols": { "index": 18, "kind": "parameter", "displayName": 
"Client Sub Protocols", "group": "producer", "label": "producer", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "Comma separated list of 
WebSocket subprotocols that the client should u [...]
+    "sendToAll": { "index": 19, "kind": "parameter", "displayName": "Send To 
All", "group": "producer", "label": "producer", "required": false, "type": 
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To send to all websocket 
subscribers. Can be used to configure at the endp [...]
+    "lazyStartProducer": { "index": 20, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produ [...]
+    "sslContextParameters": { "index": 21, "kind": "parameter", "displayName": 
"Ssl Context Parameters", "group": "security", "label": "security", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.support.jsse.SSLContextParameters", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.vertx.websocket.VertxWebsocketConfiguration", 
"configurationField": "configuration", "description": "To configure security 
using SSLCo [...]
   }
 }
diff --git 
a/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketComponent.java
 
b/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketComponent.java
index f29295371c7..9b057cb8c8a 100644
--- 
a/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketComponent.java
+++ 
b/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketComponent.java
@@ -30,6 +30,7 @@ import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.annotations.Component;
 import org.apache.camel.support.DefaultComponent;
 import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.PropertiesHelper;
 import org.apache.camel.util.URISupport;
 import org.apache.camel.util.UnsafeUriCharactersEncoder;
 
@@ -76,9 +77,12 @@ public class VertxWebsocketComponent extends 
DefaultComponent implements SSLCont
             }
         }
 
+        Map<String, Object> handshakeHeaders = 
PropertiesHelper.extractProperties(parameters, "handshake.");
+
         VertxWebsocketConfiguration configuration = new 
VertxWebsocketConfiguration();
         configuration.setAllowOriginHeader(isAllowOriginHeader());
         configuration.setOriginHeaderUrl(getOriginHeaderUrl());
+        configuration.setHandshakeHeaders(handshakeHeaders);
 
         VertxWebsocketEndpoint endpoint = createEndpointInstance(uri, 
configuration);
         setProperties(endpoint, parameters);
diff --git 
a/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketConfiguration.java
 
b/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketConfiguration.java
index f91631fe0a8..a474089902c 100644
--- 
a/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketConfiguration.java
+++ 
b/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketConfiguration.java
@@ -17,6 +17,7 @@
 package org.apache.camel.component.vertx.websocket;
 
 import java.net.URI;
+import java.util.Map;
 
 import io.vertx.core.http.HttpClientOptions;
 import io.vertx.core.http.HttpServerOptions;
@@ -68,6 +69,8 @@ public class VertxWebsocketConfiguration {
     private boolean allowOriginHeader = true;
     @UriParam(label = "producer,consumer")
     private String originHeaderUrl;
+    @UriParam(label = "producer,consumer", prefix = "handshake.", multiValue = 
true)
+    private Map<String, Object> handshakeHeaders;
 
     /**
      * The WebSocket URI address to use.
@@ -277,4 +280,16 @@ public class VertxWebsocketConfiguration {
     public void setOriginHeaderUrl(String originHeaderUrl) {
         this.originHeaderUrl = originHeaderUrl;
     }
+
+    public Map<String, Object> getHandshakeHeaders() {
+        return handshakeHeaders;
+    }
+
+    /**
+     * Headers to send in the HTTP handshake request. When the endpoint is a 
consumer, it only works when it consumes a
+     * remote host as a client (i.e. consumeAsClient is true).
+     */
+    public void setHandshakeHeaders(final Map<String, Object> 
handshakeHeaders) {
+        this.handshakeHeaders = handshakeHeaders;
+    }
 }
diff --git 
a/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpoint.java
 
b/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpoint.java
index e160b4204b4..4c162867bce 100644
--- 
a/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpoint.java
+++ 
b/components/camel-vertx/camel-vertx-websocket/src/main/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpoint.java
@@ -189,6 +189,11 @@ public class VertxWebsocketEndpoint extends 
DefaultEndpoint {
             connectOptions.addHeader(ORIGIN_HTTP_HEADER_NAME, 
defaultOriginHeader);
         }
 
+        if (ObjectHelper.isNotEmpty(configuration.getHandshakeHeaders())) {
+            configuration.getHandshakeHeaders()
+                    .forEach((headerName, headerValue) -> 
connectOptions.addHeader(headerName, headerValue.toString()));
+        }
+
         return connectOptions;
     }
 
diff --git 
a/components/camel-vertx/camel-vertx-websocket/src/test/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointConfigurationTest.java
 
b/components/camel-vertx/camel-vertx-websocket/src/test/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointConfigurationTest.java
index 810a5edfdf5..cd467566de1 100644
--- 
a/components/camel-vertx/camel-vertx-websocket/src/test/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointConfigurationTest.java
+++ 
b/components/camel-vertx/camel-vertx-websocket/src/test/java/org/apache/camel/component/vertx/websocket/VertxWebsocketEndpointConfigurationTest.java
@@ -114,6 +114,24 @@ public class VertxWebsocketEndpointConfigurationTest 
extends VertxWebSocketTestS
         assertEquals(testQueryParam, websocketURI.getQuery(), "Query 
parameters are not correctly set in the in websocketURI.");
     }
 
+    @Test
+    void testHandshakeHeaders() {
+        String handshakeHeaders
+                = "handshake.Authorization=Bearer 
token&handshake.ApiSign=-u-4tjFSE=&handshake.Timestamp=2024-04-23T15:22:16.000000Z";
+        String endpointParams = "consumeAsClient=true";
+
+        VertxWebsocketEndpoint endpoint
+                = context.getEndpoint("vertx-websocket:foo.bar.com/test?" + 
endpointParams + "&" + handshakeHeaders,
+                        VertxWebsocketEndpoint.class);
+        
assertNotNull(endpoint.getConfiguration().getHandshakeHeaders().get("Authorization"),
+                "Handshake headers Authorization is not correctly 
configured.");
+        
assertNotNull(endpoint.getConfiguration().getHandshakeHeaders().get("ApiSign"),
+                "Handshake headers ApiSign is not correctly configured.");
+        
assertNotNull(endpoint.getConfiguration().getHandshakeHeaders().get("Timestamp"),
+                "Handshake headers Timestamp is not correctly configured.");
+
+    }
+
     @Override
     protected RoutesBuilder createRouteBuilder() {
         return new RouteBuilder() {
diff --git 
a/components/camel-vertx/camel-vertx-websocket/src/test/java/org/apache/camel/component/vertx/websocket/VertxWebsocketHandshakeHeadersTest.java
 
b/components/camel-vertx/camel-vertx-websocket/src/test/java/org/apache/camel/component/vertx/websocket/VertxWebsocketHandshakeHeadersTest.java
new file mode 100644
index 00000000000..6bf034c0cb1
--- /dev/null
+++ 
b/components/camel-vertx/camel-vertx-websocket/src/test/java/org/apache/camel/component/vertx/websocket/VertxWebsocketHandshakeHeadersTest.java
@@ -0,0 +1,226 @@
+/*
+ * 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.vertx.websocket;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import io.vertx.core.Handler;
+import io.vertx.core.Vertx;
+import io.vertx.core.http.HttpHeaders;
+import io.vertx.core.http.HttpServerOptions;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.ServerWebSocket;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class VertxWebsocketHandshakeHeadersTest extends 
VertxWebSocketTestSupport {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(VertxWebsocketHandshakeHeadersTest.class);
+
+    @Test
+    public void testHandshakeHeadersAsProducer() throws Exception {
+        CountDownLatch latch = new CountDownLatch(2);
+        Vertx vertx = Vertx.vertx();
+        Router router = Router.router(vertx);
+        Route route = router.route("/ws");
+        route.handler(new Handler<RoutingContext>() {
+            @Override
+            public void handle(RoutingContext context) {
+                HttpServerRequest request = context.request();
+
+                String authorizationHeader = 
request.getHeader("Authorization");
+                assertNotNull(authorizationHeader, "Authorization header is 
not passed in the request.");
+                assertFalse(authorizationHeader.isBlank(), "Authorization 
header is blank.");
+
+                String apiSignHeader = request.getHeader("ApiSign");
+                assertNotNull(apiSignHeader, "ApiSign header is not passed in 
the request.");
+                assertFalse(apiSignHeader.isBlank(), "ApiSign header is 
blank.");
+
+                String connectionHeader = 
request.headers().get(HttpHeaders.CONNECTION);
+                if (connectionHeader == null || 
!connectionHeader.toLowerCase().contains("upgrade")) {
+                    context.response().setStatusCode(400);
+                    context.response().end("Can \"Upgrade\" only to 
\"WebSocket\".");
+                } else {
+                    // we're about to upgrade the connection, which means an 
asynchronous
+                    // operation. We have to pause the request otherwise we 
will loose the
+                    // body of the request once the upgrade completes
+                    final boolean parseEnded = request.isEnded();
+                    if (!parseEnded) {
+                        request.pause();
+                    }
+                    // upgrade
+                    request.toWebSocket(toWebSocket -> {
+                        if (toWebSocket.succeeded()) {
+                            // resume the parsing
+                            if (!parseEnded) {
+                                request.resume();
+                            }
+                            // handle the websocket session as usual
+                            ServerWebSocket webSocket = toWebSocket.result();
+                            webSocket.textMessageHandler(new Handler<String>() 
{
+                                @Override
+                                public void handle(String message) {
+                                    latch.countDown();
+                                }
+                            });
+                        } else {
+                            // the upgrade failed
+                            context.fail(toWebSocket.cause());
+                        }
+                    });
+                }
+            }
+        });
+
+        HttpServerOptions options = new HttpServerOptions();
+
+        VertxWebsocketHostConfiguration configuration = new 
VertxWebsocketHostConfiguration(vertx, router, options, null);
+        VertxWebsocketHostKey key = new VertxWebsocketHostKey("localhost", 0);
+        VertxWebsocketHost host = new VertxWebsocketHost(context, 
configuration, key);
+        host.start();
+
+        String handshakeHeaders = "handshake.Authorization=Bearer 
token&handshake.ApiSign=-u-4tjFSE=";
+
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() {
+                from("direct:start")
+                        .toD("vertx-websocket:localhost:${header.port}/ws?" + 
handshakeHeaders);
+
+            }
+        });
+
+        context.start();
+        try {
+            ProducerTemplate template = context.createProducerTemplate();
+            template.sendBodyAndHeader("direct:start", "Hello world", "port", 
host.getPort());
+            template.sendBodyAndHeader("direct:start", "Hello world after 
handshake", "port", host.getPort());
+
+            assertTrue(latch.await(10, TimeUnit.SECONDS));
+        } finally {
+            try {
+                host.stop();
+            } catch (Exception e) {
+                LOG.warn("Failed to stop Vert.x server", e);
+            }
+            context.stop();
+        }
+    }
+
+    @Test
+    public void testHandshakeHeadersAsConsumer() throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        Vertx vertx = Vertx.vertx();
+        Router router = Router.router(vertx);
+        Route route = router.route("/ws");
+        route.handler(new Handler<RoutingContext>() {
+            @Override
+            public void handle(RoutingContext context) {
+                HttpServerRequest request = context.request();
+
+                String authorizationHeader = 
request.getHeader("Authorization");
+                assertNotNull(authorizationHeader, "Authorization header is 
not passed in the request.");
+                assertFalse(authorizationHeader.isBlank(), "Authorization 
header is blank.");
+
+                String apiSignHeader = request.getHeader("ApiSign");
+                assertNotNull(apiSignHeader, "ApiSign header is not passed in 
the request.");
+                assertFalse(apiSignHeader.isBlank(), "ApiSign header is 
blank.");
+
+                String connectionHeader = 
request.headers().get(HttpHeaders.CONNECTION);
+                if (connectionHeader == null || 
!connectionHeader.toLowerCase().contains("upgrade")) {
+                    context.response().setStatusCode(400);
+                    context.response().end("Can \"Upgrade\" only to 
\"WebSocket\".");
+                } else {
+                    // we're about to upgrade the connection, which means an 
asynchronous
+                    // operation. We have to pause the request otherwise we 
will loose the
+                    // body of the request once the upgrade completes
+                    final boolean parseEnded = request.isEnded();
+                    if (!parseEnded) {
+                        request.pause();
+                    }
+
+                    // upgrade
+                    request.toWebSocket(toWebSocket -> {
+                        if (toWebSocket.succeeded()) {
+                            // resume the parsing
+                            if (!parseEnded) {
+                                request.resume();
+                            }
+                            // Send a text message to consumer
+                            ServerWebSocket webSocket = toWebSocket.result();
+                            webSocket.writeTextMessage("Hello World");
+                            
webSocket.writeTextMessage("Ping").onComplete(event -> latch.countDown());
+                        } else {
+                            // the upgrade failed
+                            context.fail(toWebSocket.cause());
+                        }
+                    });
+                }
+            }
+        });
+
+        HttpServerOptions options = new HttpServerOptions();
+
+        VertxWebsocketHostConfiguration configuration = new 
VertxWebsocketHostConfiguration(vertx, router, options, null);
+        VertxWebsocketHostKey key = new VertxWebsocketHostKey("localhost", 0);
+        VertxWebsocketHost host = new VertxWebsocketHost(context, 
configuration, key);
+        host.start();
+
+        String handshakeHeaders = "handshake.Authorization=Bearer 
token&handshake.ApiSign=-u-4tjFSE=";
+
+        context.addRoutes(new RouteBuilder() {
+            @Override
+            public void configure() {
+                fromF("vertx-websocket:localhost:%d/ws?consumeAsClient=true&" 
+ handshakeHeaders, host.getPort())
+                        .log("Consume websocket message ${body}")
+                        .to("mock:result");
+            }
+        });
+
+        context.start();
+        try {
+            assertTrue(latch.await(10, TimeUnit.SECONDS));
+
+            MockEndpoint mockEndpoint = context.getEndpoint("mock:result", 
MockEndpoint.class);
+            mockEndpoint.expectedBodiesReceivedInAnyOrder("Hello World", 
"Ping");
+            mockEndpoint.assertIsSatisfied();
+        } finally {
+            try {
+                host.stop();
+            } catch (Exception e) {
+                LOG.warn("Failed to stop Vert.x server", e);
+            }
+            context.stop();
+        }
+    }
+
+    @Override
+    protected void startCamelContext() {
+    }
+}
diff --git 
a/dsl/camel-kotlin-api/src/generated/kotlin/org/apache/camel/kotlin/components/VertxWebsocketUriDsl.kt
 
b/dsl/camel-kotlin-api/src/generated/kotlin/org/apache/camel/kotlin/components/VertxWebsocketUriDsl.kt
index 7d283d96e1e..44b06090c03 100644
--- 
a/dsl/camel-kotlin-api/src/generated/kotlin/org/apache/camel/kotlin/components/VertxWebsocketUriDsl.kt
+++ 
b/dsl/camel-kotlin-api/src/generated/kotlin/org/apache/camel/kotlin/components/VertxWebsocketUriDsl.kt
@@ -132,6 +132,14 @@ public class VertxWebsocketUriDsl(
     it.property("fireWebSocketConnectionEvents", 
fireWebSocketConnectionEvents.toString())
   }
 
+  /**
+   * Headers to send in the HTTP handshake request. When the endpoint is a 
consumer, it only works
+   * when it consumes a remote host as a client (i.e. consumeAsClient is true).
+   */
+  public fun handshakeHeaders(handshakeHeaders: String) {
+    it.property("handshakeHeaders", handshakeHeaders)
+  }
+
   /**
    * When consumeAsClient is set to true this sets the maximum number of 
allowed reconnection
    * attempts to a previously closed WebSocket. A value of 0 (the default) 
will attempt to reconnect

Reply via email to