Repository: quarkusio/quarkus
Issue: Rest Client sets own User-Agent header even if user specifies a different one

### Describe the bug

This issue is based on the Zulip chat: https://quarkusio.zulipchat.com/#narrow/stream/187030-users/topic/io.2Evertx.2Ecore.2Ehttp.2Eimpl.2ENoStackTraceTimeoutException

We have a reactive rest client where we set a custom `User-Agent` header. The user agent needs to be this specific value for the API to accept our request. 

```
@RegisterRestClient(configKey = "xxxxx")
@Produces(MediaType.APPLICATION_JSON)
interface GatewayApi {
    @GET
    @Path("zzzzz")
    suspend fun getUser(
        @HeaderParam("Accept-Language") acceptLanguage: String,
        @HeaderParam("x-request-tracing-id") traceId: String,
        @HeaderParam("x-api-key") apiKey: String,
        @HeaderParam("User-Agent") userAgent: String
    ): Response
}
```

Unfortunately, it seems that Quarkus versions >= 2.7.0 add an own `User-Agent` header value with the value: `Resteasy Reactive Client`. This value is always added, no matter if we set our own `User-Agent`. So when we make a request the `User-Agent` header has the values: `[Resteasy Reactive Client, Our_Custom_Header_Value]` compared to versions <= 2.6.3 where the `User-Agent` values is `[Our_Custom_Header_Value]`.

A workaround is to add a custom ClientHeadersFactory and register it to above interface. For example:

```
class GatewayHeadersFactory: ClientHeadersFactory {
    override fun update(incomingHeaders: MultivaluedMap<String, String>?, clientOutgoingHeaders: MultivaluedMap<String, String>?): MultivaluedMap<String, String> {
        val clientHeaders = MultivaluedHashMap<String, String>()
        "Our_Custom_Header_Value".addHeader("User-Agent", clientHeaders)
        return clientHeaders
    }
}
```

### Expected behavior

The rest client should only add its own `User-Agent` header when we do not specify our own header. This should apply when the header is set with the `@HeaderParam` annotation in the method as well as when using `@ClientHeaderParam` to set a `User-Agent` header. 

### Actual behavior

The rest client always adds it's own User-Agent header.

### How to Reproduce?

_No response_

### Output of `uname -a` or `ver`

_No response_

### Output of `java -version`

_No response_

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

_No response_

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

_No response_

### Additional information

_No response_

Changed files:
independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InvocationBuilderImpl.java
extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/UserAgentTest.java

diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/UserAgentTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/UserAgentTest.java
index 3e2c924ac9a..d5341d80e33 100644
--- a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/UserAgentTest.java
+++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/headers/UserAgentTest.java
@@ -30,6 +30,12 @@ void testHeadersWithSubresource() {
         assertThat(client.call()).isEqualTo("Resteasy Reactive Client");
     }
 
+    @Test
+    void testHeaderOverride() {
+        Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class);
+        assertThat(client.callWithUserAgent("custom-agent")).isEqualTo("custom-agent");
+    }
+
     @Path("/")
     @ApplicationScoped
     public static class Resource {
@@ -44,6 +50,10 @@ public interface Client {
         @Path("/")
         @GET
         String call();
+
+        @Path("/")
+        @GET
+        String callWithUserAgent(@HeaderParam("User-AgenT") String userAgent);
     }
 
 }
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InvocationBuilderImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InvocationBuilderImpl.java
index 5126b4ab0c4..f9f25cf593c 100644
--- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InvocationBuilderImpl.java
+++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/InvocationBuilderImpl.java
@@ -51,9 +51,6 @@ public InvocationBuilderImpl(URI uri, ClientImpl restClient, HttpClient httpClie
         this.httpClient = httpClient;
         this.target = target;
         this.requestSpec = new RequestSpec(configuration);
-        if (restClient.getUserAgent() != null && !restClient.getUserAgent().isEmpty()) {
-            this.requestSpec.headers.header(HttpHeaders.USER_AGENT, restClient.getUserAgent());
-        }
         this.configuration = configuration;
         this.handlerChain = handlerChain;
         this.requestContext = requestContext;
@@ -67,11 +64,13 @@ public InvocationBuilderImpl(URI uri, ClientImpl restClient, HttpClient httpClie
 
     @Override
     public Invocation build(String method) {
+        setUserAgentIfNotSet();
         return new InvocationImpl(method, async(), null);
     }
 
     @Override
     public Invocation build(String method, Entity<?> entity) {
+        setUserAgentIfNotSet();
         return new InvocationImpl(method, async(), entity);
     }
 
@@ -97,6 +96,7 @@ public Invocation buildPut(Entity<?> entity) {
 
     @Override
     public AsyncInvokerImpl async() {
+        setUserAgentIfNotSet();
         return new AsyncInvokerImpl(restClient, httpClient, uri, requestSpec, configuration,
                 properties, handlerChain, requestContext);
     }
@@ -175,6 +175,7 @@ public CompletionStageRxInvoker rx() {
 
     @Override
     public <T extends RxInvoker> T rx(Class<T> clazz) {
+        setUserAgentIfNotSet();
         if (clazz == MultiInvoker.class) {
             return (T) new MultiInvoker(this);
         } else if (clazz == UniInvoker.class) {
@@ -189,6 +190,13 @@ public <T extends RxInvoker> T rx(Class<T> clazz) {
         return null;
     }
 
+    private void setUserAgentIfNotSet() {
+        if (!requestSpec.headers.getHeaders().containsKey(HttpHeaders.USER_AGENT)
+                && restClient.getUserAgent() != null && !restClient.getUserAgent().isEmpty()) {
+            this.requestSpec.headers.header(HttpHeaders.USER_AGENT, restClient.getUserAgent());
+        }
+    }
+
     @Override
     public Response get() {
         return unwrap(async().get());