CASSANDRASC-101 Upgrade Vert.x version in Sidecar to 4.5

Patch by Saranya Krishnakumar; Reviewed by Francisco Guerrero and Yifan Cai for CASSANDRASC-101
diff --git a/CHANGES.txt b/CHANGES.txt
index 85dc0d5..43b7652 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,6 @@
 1.0.0
 -----
+ * Upgrade Vert.x version in Sidecar to 4.5 (CASSANDRASC-101)
  * Break restore job into stage and import phases and persist restore slice status on phase completion (CASSANDRASC-99)
  * Improve logging for traffic shaping / rate limiting configuration (CASSANDRASC-98)
  * Startup Validation Failures when Checking Sidecar Connectivity (CASSANDRASC-86)
diff --git a/gradle.properties b/gradle.properties
index 3606ae9..29f567c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -18,7 +18,7 @@
 
 version=1.0-SNAPSHOT
 junitVersion=5.9.2
-vertxVersion=4.4.6
+vertxVersion=4.5.3
 guavaVersion=27.0.1-jre
 slf4jVersion=1.7.36
 jacksonVersion=2.14.3
diff --git a/src/main/java/org/apache/cassandra/sidecar/server/Server.java b/src/main/java/org/apache/cassandra/sidecar/server/Server.java
index b3fd01d..3c9b0a7 100644
--- a/src/main/java/org/apache/cassandra/sidecar/server/Server.java
+++ b/src/main/java/org/apache/cassandra/sidecar/server/Server.java
@@ -44,6 +44,7 @@
 import io.vertx.core.json.JsonArray;
 import io.vertx.core.json.JsonObject;
 import io.vertx.core.net.SSLOptions;
+import io.vertx.core.net.TrafficShapingOptions;
 import io.vertx.ext.web.Router;
 import org.apache.cassandra.sidecar.cluster.InstancesConfig;
 import org.apache.cassandra.sidecar.cluster.instance.InstanceMetadata;
@@ -190,6 +191,17 @@
     }
 
     /**
+     * Updates the traffic shaping options for all servers in all the deployed verticle instances
+     *
+     * @param options update traffic shaping options
+     */
+    public void updateTrafficShapingOptions(TrafficShapingOptions options)
+    {
+        // Updates the traffic shaping options of all the deployed verticles
+        deployedServerVerticles.forEach(serverVerticle -> serverVerticle.updateTrafficShapingOptions(options));
+    }
+
+    /**
      * Expose the port of the first deployed verticle for testing purposes
      *
      * @return the port where the first verticle is deployed, or -1 if the server has not been deployed
diff --git a/src/main/java/org/apache/cassandra/sidecar/server/ServerVerticle.java b/src/main/java/org/apache/cassandra/sidecar/server/ServerVerticle.java
index d3a01e9..109b369 100644
--- a/src/main/java/org/apache/cassandra/sidecar/server/ServerVerticle.java
+++ b/src/main/java/org/apache/cassandra/sidecar/server/ServerVerticle.java
@@ -33,6 +33,7 @@
 import io.vertx.core.http.HttpServerOptions;
 import io.vertx.core.net.SSLOptions;
 import io.vertx.core.net.SocketAddress;
+import io.vertx.core.net.TrafficShapingOptions;
 import io.vertx.ext.web.Router;
 import org.apache.cassandra.sidecar.config.ServiceConfiguration;
 import org.apache.cassandra.sidecar.config.SidecarConfiguration;
@@ -115,6 +116,21 @@
     }
 
     /**
+     * Updates the {@link TrafficShapingOptions} internally for the deployed server.
+     *
+     * @param options the updated traffic shaping options
+     */
+    void updateTrafficShapingOptions(TrafficShapingOptions options)
+    {
+        List<HttpServer> deployedServers = this.deployedServers;
+        if (deployedServers == null || deployedServers.isEmpty())
+        {
+            throw new IllegalStateException("No servers are running");
+        }
+        deployedServers.forEach(server -> server.updateTrafficShapingOptions(options));
+    }
+
+    /**
      * @return the actual port of the first deployed server, or -1 if no servers are deployed
      */
     @VisibleForTesting
diff --git a/src/test/java/org/apache/cassandra/sidecar/server/ServerTest.java b/src/test/java/org/apache/cassandra/sidecar/server/ServerTest.java
index 608d322..a00f4a7 100644
--- a/src/test/java/org/apache/cassandra/sidecar/server/ServerTest.java
+++ b/src/test/java/org/apache/cassandra/sidecar/server/ServerTest.java
@@ -33,6 +33,7 @@
 import io.netty.handler.codec.http.HttpResponseStatus;
 import io.vertx.core.Future;
 import io.vertx.core.Vertx;
+import io.vertx.core.net.TrafficShapingOptions;
 import io.vertx.ext.web.client.WebClient;
 import io.vertx.junit5.Checkpoint;
 import io.vertx.junit5.VertxExtension;
@@ -41,6 +42,7 @@
 import static org.apache.cassandra.sidecar.common.ResourceUtils.writeResourceToPath;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatException;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 
 /**
  * Unit tests for {@link Server} lifecycle
@@ -136,6 +138,48 @@
               .onFailure(context::failNow);
     }
 
+    @Test
+    @DisplayName("Updating traffic shaping options should succeed")
+    void updatingTrafficShapingOptions(VertxTestContext context)
+    {
+        Checkpoint serverStarted = context.checkpoint();
+        Checkpoint waitUntilUpdate = context.checkpoint();
+
+        vertx.eventBus().localConsumer(SidecarServerEvents.ON_SERVER_START.address(), message -> serverStarted.flag());
+
+        server.start()
+              .compose(this::validateHealthEndpoint)
+              .onFailure(context::failNow)
+              .onSuccess(v -> {
+                  TrafficShapingOptions update = new TrafficShapingOptions()
+                                                 .setOutboundGlobalBandwidth(100 * 1024 * 1024);
+                  server.updateTrafficShapingOptions(update);
+                  waitUntilUpdate.flag();
+                  context.completeNow();
+              });
+    }
+
+    @Test
+    @DisplayName("Update should fail with null options")
+    void updatingTrafficShapingOptionsWithNull(VertxTestContext context)
+    {
+        Checkpoint serverStarted = context.checkpoint();
+        Checkpoint waitUntilUpdate = context.checkpoint();
+
+        vertx.eventBus().localConsumer(SidecarServerEvents.ON_SERVER_START.address(), message -> serverStarted.flag());
+
+        server.start()
+              .compose(this::validateHealthEndpoint)
+              .onFailure(context::failNow)
+              .onSuccess(v -> {
+                  assertThatIllegalArgumentException()
+                  .isThrownBy(() -> server.updateTrafficShapingOptions(null))
+                  .withMessage("Invalid null value passed for traffic shaping options update");
+                  waitUntilUpdate.flag();
+                  context.completeNow();
+              });
+    }
+
     Future<String> validateHealthEndpoint(String deploymentId)
     {
         LOGGER.info("Checking server health 127.0.0.1:{}/api/v1/__health", server.actualPort());