Uploaded image for project: 'CXF'
  1. CXF
  2. CXF-8936

Fix h2 protocol negotiation in Jetty Transport

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Minor
    • Resolution: Fixed
    • 4.0.3
    • 3.6.3, 4.0.4
    • Transports
    • CXF latest and CXF 4.0.3 on Java 11 and 17 both show this issue.

    • Unknown

    Description

      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.

      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:

      * ALPN: offers h2,http/1.1
      * ALPN: server accepted http/1.1
      

      Compare this with the attached curl output from the CXF sample jax_rs_basic_http2_netty.

      The important lines here are:

      * ALPN: offers h2,http/1.1
      * ALPN: server accepted h2
      

      The solution

      The ALPN takes place within ALPNServerConnection, which is part of the jetty-alpn-server module.

              // 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;
                  }
              }
      

      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

      INFO: Started ServerConnector@11eadcba\{ssl, (http/1.1, ssl, alpn, h2)}{0.0.0.0:9000}}
      

      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.

      Attachments

        1. output-jetty-original.txt
          1 kB
          Huw Ayling-Miller
        2. output-netty.txt
          2 kB
          Huw Ayling-Miller

        Issue Links

          Activity

            People

              Unassigned Unassigned
              huw0 Huw Ayling-Miller
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: