[ 
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)

Reply via email to