[ https://issues.apache.org/jira/browse/CXF-8936?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Huw Ayling-Miller updated CXF-8936: ----------------------------------- Description: h1. Summary I have been trying to update one of our apps using the Jetty backend to use http2. While doing this I've noticed that the protocol negotiation is in the wrong order which results in http/1.1 always being preferred where the client includes this in the ALPN handshake. This means that both browsers and cURL will never successfully negotiate h2/http2. --- h1. The problem The issue can be shown using curl. {{curl -ikv https://localhost:9000}} I've attached the full output of this command when running curl against the CXF sample {{jax_rs_basic_http2_jetty}}. The important lines are: {code} * ALPN: offers h2,http/1.1 * ALPN: server accepted http/1.1 {code} Compare this with the attached curl output from the CXF sample {{jax_rs_basic_http2_netty}}. The important lines here are: {code} * ALPN: offers h2,http/1.1 * ALPN: server accepted h2 {code} --- h1. The solution The ALPN protocol selection takes place within {{ALPNServerConnection}}, which is part of the {{jetty-alpn-server}} module. {code:java} // RFC 7301 states that the server picks the protocol // that it prefers that is also supported by the client. for (String serverProtocol : serverProtocols) { if (clientProtocols.contains(serverProtocol)) { ConnectionFactory factory = getConnector().getConnectionFactory(serverProtocol); if (factory instanceof CipherDiscriminator && !((CipherDiscriminator)factory).isAcceptable(serverProtocol, tlsProtocol, tlsCipher)) { if (LOG.isDebugEnabled()) LOG.debug("Protocol {} not acceptable to {} for {}/{} on {}", serverProtocol, factory, tlsProtocol, tlsCipher, getEndPoint()); continue; } negotiated = serverProtocol; break; } } {code} As the code states, the server is responsible for picking the protocol and Jetty picks the first match in the server's list. From the log output of the same app the first in the list is http/1.1 {code} INFO: Started ServerConnector@11eadcba\{ssl, (http/1.1, ssl, alpn, h2)}{0.0.0.0:9000}} {code} Therefore the solution is to ensure that when h2 is enabled inside {{JettyHTTPServerEngine}} -> {{createConnectorJetty}}, the HTTP2ServerConnectionFactory should always be added at the start of the list. With this change, curl then successfully negotiates with h2. was: h1. Summary I have been trying to update one of our apps using the Jetty backend to use http2. While doing this I've noticed that the protocol negotiation is in the wrong order which results in http/1.1 always being preferred where the client includes this in the ALPN handshake. This means that both browsers and cURL will never successfully negotiate h2/http2. --- h1. The problem The issue can be shown using curl. {{curl -ikv https://localhost:9000}} I've attached the full output of this command when running curl against the CXF sample `jax_rs_basic_http2_jetty`. The important lines are: {code} * ALPN: offers h2,http/1.1 * ALPN: server accepted http/1.1 {code} Compare this with the attached curl output from the CXF sample {{jax_rs_basic_http2_netty}}. The important lines here are: {code} * ALPN: offers h2,http/1.1 * ALPN: server accepted h2 {code} --- h1. The solution The ALPN protocol selection takes place within {{ALPNServerConnection}}, which is part of the {{jetty-alpn-server}} module. {code:java} // RFC 7301 states that the server picks the protocol // that it prefers that is also supported by the client. for (String serverProtocol : serverProtocols) { if (clientProtocols.contains(serverProtocol)) { ConnectionFactory factory = getConnector().getConnectionFactory(serverProtocol); if (factory instanceof CipherDiscriminator && !((CipherDiscriminator)factory).isAcceptable(serverProtocol, tlsProtocol, tlsCipher)) { if (LOG.isDebugEnabled()) LOG.debug("Protocol {} not acceptable to {} for {}/{} on {}", serverProtocol, factory, tlsProtocol, tlsCipher, getEndPoint()); continue; } negotiated = serverProtocol; break; } } {code} As the code states, the server is responsible for picking the protocol and Jetty picks the first match in the server's list. From the log output of the same app the first in the list is http/1.1 {code} INFO: Started ServerConnector@11eadcba\{ssl, (http/1.1, ssl, alpn, h2)}{0.0.0.0:9000}} {code} Therefore the solution is to ensure that when h2 is enabled inside {{JettyHTTPServerEngine}} -> {{createConnectorJetty}}, the HTTP2ServerConnectionFactory should always be added at the start of the list. With this change, curl then successfully negotiates with h2. > Fix h2 protocol negotiation in Jetty Transport > ---------------------------------------------- > > Key: CXF-8936 > URL: https://issues.apache.org/jira/browse/CXF-8936 > Project: CXF > Issue Type: Bug > Components: Transports > Affects Versions: 4.0.3 > Environment: CXF latest and CXF 4.0.3 on Java 11 and 17 both show > this issue. > Reporter: Huw Ayling-Miller > Priority: Minor > Labels: Jetty > Attachments: output-jetty-original.txt, output-netty.txt > > > h1. Summary > I have been trying to update one of our apps using the Jetty backend to use > http2. While doing this I've noticed that the protocol negotiation is in the > wrong order which results in http/1.1 always being preferred where the client > includes this in the ALPN handshake. This means that both browsers and cURL > will never successfully negotiate h2/http2. > --- > h1. The problem > The issue can be shown using curl. > {{curl -ikv https://localhost:9000}} > I've attached the full output of this command when running curl against the > CXF sample {{jax_rs_basic_http2_jetty}}. > The important lines are: > {code} > * ALPN: offers h2,http/1.1 > * ALPN: server accepted http/1.1 > {code} > Compare this with the attached curl output from the CXF sample > {{jax_rs_basic_http2_netty}}. > The important lines here are: > {code} > * ALPN: offers h2,http/1.1 > * ALPN: server accepted h2 > {code} > --- > h1. The solution > The ALPN protocol selection takes place within {{ALPNServerConnection}}, > which is part of the {{jetty-alpn-server}} module. > {code:java} > // RFC 7301 states that the server picks the protocol > // that it prefers that is also supported by the client. > for (String serverProtocol : serverProtocols) > { > if (clientProtocols.contains(serverProtocol)) > { > ConnectionFactory factory = > getConnector().getConnectionFactory(serverProtocol); > if (factory instanceof CipherDiscriminator && > !((CipherDiscriminator)factory).isAcceptable(serverProtocol, tlsProtocol, > tlsCipher)) > { > if (LOG.isDebugEnabled()) > LOG.debug("Protocol {} not acceptable to {} for {}/{} > on {}", serverProtocol, factory, tlsProtocol, tlsCipher, getEndPoint()); > continue; > } > negotiated = serverProtocol; > break; > } > } > {code} > As the code states, the server is responsible for picking the protocol and > Jetty picks the first match in the server's list. From the log output of the > same app the first in the list is http/1.1 > {code} > INFO: Started ServerConnector@11eadcba\{ssl, (http/1.1, ssl, alpn, > h2)}{0.0.0.0:9000}} > {code} > Therefore the solution is to ensure that when h2 is enabled inside > {{JettyHTTPServerEngine}} -> {{createConnectorJetty}}, the > HTTP2ServerConnectionFactory should always be added at the start of the list. > With this change, curl then successfully negotiates with h2. -- This message was sent by Atlassian Jira (v8.20.10#820010)