Fixes the multiple user-agent headers
Added a Jersey version agnostic test

Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java b/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java
index 3ede8a9..825a231 100644
--- a/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java
+++ b/connectors/jetty-connector/src/main/java11/org/glassfish/jersey/jetty/connector/JettyConnector.java
@@ -46,6 +46,7 @@
 
 import javax.net.ssl.SSLContext;
 
+import jakarta.ws.rs.ext.RuntimeDelegate;
 import org.eclipse.jetty.client.HttpClientTransport;
 import org.eclipse.jetty.client.HttpRequest;
 import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
@@ -254,7 +255,6 @@
 
     @Override
     public ClientResponse apply(final ClientRequest jerseyRequest) throws ProcessingException {
-        applyUserAgentHeader(jerseyRequest.getHeaders());
         final Request jettyRequest = translateRequest(jerseyRequest);
         final Map<String, String> clientHeadersSnapshot = writeOutBoundHeaders(jerseyRequest.getHeaders(), jettyRequest);
         final ContentProvider entity = getBytesProvider(jerseyRequest);
@@ -338,34 +338,17 @@
         return request;
     }
 
-    /**
-     * Re-write User-agent header set by Jetty; Jersey already sets this in its request (incl. Jetty version)
-     * it shall be propagated to Jetty client before HttpRequest instance is created.
-     * HttpRequest takes User Agent header from client then.
-     *
-     * @param headers - map of Jersey headers
-     */
-    private void applyUserAgentHeader(final MultivaluedMap<String, Object> headers) {
-        if (headers.containsKey(HttpHeaders.USER_AGENT)) {
-            final Map<String, String> stringHeaders =
-                    HeaderUtils.asStringHeadersSingleValue(headers, configuration);
-            client.setUserAgentField(
-                    new HttpField(HttpHeader.USER_AGENT,
-                            HttpHeader.USER_AGENT.name(),
-                            stringHeaders.get(HttpHeaders.USER_AGENT))
-            );
-        }
-    }
-
     private Map<String, String> writeOutBoundHeaders(final MultivaluedMap<String, Object> headers, final Request request) {
         final Map<String, String> stringHeaders = HeaderUtils.asStringHeadersSingleValue(headers, configuration);
 
-         if (request instanceof HttpRequest) {
-             final HttpRequest httpRequest = (HttpRequest) request;
-             for (final Map.Entry<String, String> e : stringHeaders.entrySet()) {
-                 httpRequest.addHeader(new HttpField(e.getKey(), e.getValue()));
-             }
-         }
+        // remove User-agent header set by Jetty; Jersey already sets this in its request (incl. Jetty version)
+        request.headers(httpFields -> httpFields.remove(HttpHeader.USER_AGENT));
+        if (request instanceof HttpRequest) {
+            final HttpRequest httpRequest = (HttpRequest) request;
+            for (final Map.Entry<String, String> e : stringHeaders.entrySet()) {
+                httpRequest.addHeader(new HttpField(e.getKey(), e.getValue()));
+            }
+        }
         return stringHeaders;
     }
 
@@ -422,7 +405,6 @@
 
     @Override
     public Future<?> apply(final ClientRequest jerseyRequest, final AsyncConnectorCallback callback) {
-        applyUserAgentHeader(jerseyRequest.getHeaders());
         final Request jettyRequest = translateRequest(jerseyRequest);
         final Map<String, String> clientHeadersSnapshot = writeOutBoundHeaders(jerseyRequest.getHeaders(), jettyRequest);
         final ContentProvider entity = getStreamProvider(jerseyRequest);
diff --git a/tests/pom.xml b/tests/pom.xml
index 025ebb1..8306acc 100644
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -113,6 +113,7 @@
             </activation>
             <modules>
                 <module>release-test</module>
+                <module>version-agnostic</module>
             </modules>
         </profile>
     </profiles>
diff --git a/tests/version-agnostic/pom.xml b/tests/version-agnostic/pom.xml
new file mode 100644
index 0000000..b711a14
--- /dev/null
+++ b/tests/version-agnostic/pom.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.8</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests</groupId>
+    <artifactId>version-agnostic</artifactId>
+    <packaging>jar</packaging>
+    <name>jersey-agnostic-test</name>
+
+    <description>Jersey post-release validation tests</description>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <hk2.version>3.0.3</hk2.version>
+        <jersey.version>3.0.99-SNAPSHOT</jersey.version>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M7</version>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.glassfish.copyright</groupId>
+                <artifactId>glassfish-copyright-maven-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <excludeFile>etc/config/copyright-exclude</excludeFile>
+                    <!--svn|mercurial|git - defaults to svn-->
+                    <scm>git</scm>
+                    <!-- turn on/off debugging -->
+                    <debug>false</debug>
+                    <!-- skip files not under SCM-->
+                    <scmOnly>true</scmOnly>
+                    <!-- turn off warnings -->
+                    <warn>false</warn>
+                    <!-- for use with repair -->
+                    <update>false</update>
+                    <!-- check that year is correct -->
+                    <ignoreYear>false</ignoreYear>
+                    <templateFile>etc/config/copyright.txt</templateFile>
+                    <bsdTemplateFile>etc/config/edl-copyright.txt</bsdTemplateFile>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.glassfish.jersey</groupId>
+                <artifactId>jersey-bom</artifactId>
+                <version>${jersey.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.glassfish.jersey.test-framework.providers</groupId>
+            <artifactId>jersey-test-framework-provider-bundle</artifactId>
+            <type>pom</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jetty-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.glassfish.jersey.inject</groupId>
+            <artifactId>jersey-hk2</artifactId>
+            <version>${jersey.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.hk2</groupId>
+            <artifactId>hk2-api</artifactId>
+            <version>${hk2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.hk2</groupId>
+            <artifactId>hk2-utils</artifactId>
+            <version>${hk2.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.hk2</groupId>
+            <artifactId>hk2-locator</artifactId>
+            <version>${hk2.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java b/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java
new file mode 100644
index 0000000..01187a2
--- /dev/null
+++ b/tests/version-agnostic/src/test/java/org/glassfish/jersey/test/agnostic/JettyHeaderTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.test.agnostic;
+
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.core.Application;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.HttpHeaders;
+import jakarta.ws.rs.core.Response;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
+import org.glassfish.jersey.logging.LoggingFeature;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.logging.Level;
+
+public class JettyHeaderTest extends JerseyTest {
+    @Path("/")
+    public static class JettyHeaderTestResource {
+        @Context
+        HttpHeaders httpHeaders;
+
+        @GET
+        public String get() {
+            return httpHeaders.getHeaderString(HttpHeaders.USER_AGENT);
+        }
+    }
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(JettyHeaderTestResource.class);
+                //.register(LoggingFeature.builder().level(Level.INFO).verbosity(LoggingFeature.Verbosity.PAYLOAD_ANY).build());
+    }
+
+    @Override
+    protected void configureClient(ClientConfig config) {
+        config.connectorProvider(new JettyConnectorProvider());
+    }
+
+    @Test
+    public void helloWorldTest() {
+        try (Response r = target().request().get()) {
+            Assertions.assertEquals(200, r.getStatus());
+
+            int index = -1;
+            String userAgent = r.readEntity(String.class);
+            index = userAgent.indexOf("Jersey");
+            Assertions.assertTrue(index != -1);
+
+            index = userAgent.indexOf("Jersey", index + 1);
+            Assertions.assertEquals(-1, index);
+        }
+    }
+}