Skip to content

5.1.x breaking change in ALPN #6162

Description

@NilsRenaud

Version

5.1.1 -> 5.1.3

Context

When Client and Server are configured with ALPN enabled and the same list of protocol, in the same order (HTTP/2, HTTP/1.1), They end up agreeing on HTTP/1.1 instead of HTTP/2 (as it used to be in 5.0.x).

Reproducer

This test pass in 5.0.x, but breaks on 5.1.x.
It simply creates a HTTPs server returning the request protocol version as HTTP response body. Checking that when both client/server have HTTP/2 as preferred protocol, they should agree to use it.

import java.util.List;

import org.junit.jupiter.api.Test;

import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClientAgent;
import io.vertx.core.http.HttpClientOptions;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.http.RequestOptions;
import io.vertx.core.net.PemKeyCertOptions;

public class VertxMigrationTests {

    @Test
    void shouldAccessServerWithHttp2IfRequestIsHttpsAndAlpnUsed() {
        Vertx vertx = Vertx.vertx();
        HttpServer server = createHttpServerReturningVersionUsed(vertx);
        HttpClientAgent client = createHttpClientWithAlpn(vertx);
        try {
            RequestOptions requestOptions = new RequestOptions().setHost("localhost")
                                                                .setPort(server.actualPort())
                                                                .setSsl(true)
                                                                .setURI("/")
                                                                .setMethod(HttpMethod.GET);
            String responseContent = client.request(requestOptions).await()
                         .send().await()
                         .body().await()
                         .toString();
            assert "HTTP_2".equals(responseContent);
        } finally {
            server.close();
            client.close();
        }
    }

    private static HttpClientAgent createHttpClientWithAlpn(final Vertx vertx) {
        HttpClientOptions clientOptions = new HttpClientOptions()
                .setUseAlpn(true)
                .setTrustAll(true)
                .setAlpnVersions(List.of(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1));
        return vertx.createHttpClient(clientOptions);
    }

    private static HttpServer createHttpServerReturningVersionUsed(final Vertx vertx) {
        HttpServerOptions serverOptions = new HttpServerOptions()
                .setSsl(true)
                .setKeyCertOptions(new PemKeyCertOptions()
                                           .setCertPath("<SELF_SIGNED_CERT>")
                                           .setKeyPath("<SELF_SIGNED_CERT_KEY>"))
                .setUseAlpn(true)
                .setAlpnVersions(List.of(HttpVersion.HTTP_2, HttpVersion.HTTP_1_1));
        return vertx.createHttpServer(serverOptions)
                                 .requestHandler(request -> request.response().end(request.version().toString()))
                                 .listen(0)
                                 .await();
    }
}

What does the spec says

From RFC 7301:

It is expected that a server will have a list of protocols that it
supports, in preference order, and will only select a protocol if the
client supports it. In that case, the server SHOULD select the most
highly preferred protocol that it supports and that is also
advertised by the client.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions