1

I have a small sample servlet deployed to a Tomcat that will echo back any received parameters. The Tomcat is configured to support HTTP/1.1 and HTTP/2. I can use curl to make GET requests to the servlet demonstrating it works over both HTTP/1.1 and HTTP/2.

Using the Apache HTTP 5.0 client I have a unit test to hit the servlet and POST a request with some parameters (request content) that works fine - the server receives the parameters and returns them back to the client where I can read them. In this test I call the client to use HTTP/1.1 or HTTP/2 using the CloseableHttpAsyncClient.setVersionPolicy() call. However, if I change to use HTTP/2 then the client does not send the request content to the server.

Below is the example code for a successful HTTP/1.1 call - you can see I have setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1). If I change that to FORCE_HTTP_2 then the call is made over HTTP/2.0 but the request content is not sent.

Can anyone suggest what I am doing wrong or what extra I need to do? I have not found a lot of documentation on the use of the new Apache 5.0 client libraries, and the examples do not show sending and POST content (or at least those I can find).

    public void testWithHTTP1() throws Exception {

        final String content = "Code=99&Message=Hello";
        final String contentType = "application/text";

        final CloseableHttpAsyncClient client = HttpAsyncClients.custom()
                .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1)
                .build();

        client.start();

        final HttpHost target = new HttpHost("http","localhost",8002);
        final String requestUri = "/VSFTestMT_Test/Test/Test_EchoHttpGet";

        final HttpClientContext clientContext = HttpClientContext.create();

        final SimpleHttpRequest httppost = SimpleHttpRequests.post(target, requestUri);     
        httppost.setBody(content, ContentType.create("application.x_www_form_urlencoded"));
        
        final Future<SimpleHttpResponse> future = client.execute(
                httppost,
                clientContext,
                new FutureCallback<SimpleHttpResponse>() {

                    @Override
                    public void completed(final SimpleHttpResponse response) {
                        System.out.println(requestUri + "->" + response.getCode());
                        System.out.println(response.getBody());
                    }

                    @Override
                    public void failed(final Exception ex) {
                        System.out.println(requestUri + "->" + ex);
                    }

                    @Override
                    public void cancelled() {
                        System.out.println(requestUri + " cancelled");
                    }

                });


        System.out.println("Shutting down");
        client.close(CloseMode.GRACEFUL);
    }
JohnXF
  • 972
  • 9
  • 22

2 Answers2

2

HTTP/1 can be implemented via HTTP and HTTPS, but for HTTP/2 its implemented in HTTPS only.

From what I can see you are calling http url only, might be that is the reason why servlet is not able to fetch payload content

Please see this more detail

Wang Du
  • 101
  • 2
  • Thank you, that lead me to do some more testing with CURL. I had thought my tomcat server was OK with HTTP/2 on both HTTP and HTTPS. However the use of HTTP/2 is not so clear cut. I can use CURL to do HTTP/2 POST requests on the non-SSL connections but only if there is no payload. I can also do HTTP/2 GET requests with or without URL parameters on both HTTP and HTTPS. So it seems that adding body content to a non-SSL HTTP/2 request will downgrade to HTTP/1. In my tests I can add body content to the HTTP/2 request successfully so as you point out I need to make my requests to the HTTPS server. – JohnXF Mar 02 '21 at 18:06
0

It looks like they have a completly different builder for Http/2 looking at their sourcecode:

/**
 * Creates builder object for construction of custom HTTP/2
 * {@link CloseableHttpAsyncClient} instances optimized for HTTP/2 protocol
 * and message multiplexing
 */
public static H2AsyncClientBuilder customHttp2() {
    return H2AsyncClientBuilder.create();
}

/**
 * Creates HTTP/2 {@link CloseableHttpAsyncClient} instance with default configuration and
 * system properties optimized for HTTP/2 protocol and message multiplexing.
 */
public static CloseableHttpAsyncClient createHttp2System() {
    return H2AsyncClientBuilder.create().useSystemProperties().build();
}

Therefore I suggest you try the actual customHttp2(), instead of custom().

https://github.com/apache/httpcomponents-client/blob/master/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClients.java

JCompetence
  • 6,997
  • 3
  • 19
  • 26
  • Thanks Susan, but no joy there. Using customHttp2() will give me a ClientBuilder from which I get another CloseableHttpAsyncClient, but proceeding as before I still then get the same result. – JohnXF Mar 02 '21 at 14:57
  • @Susan Mustafa This builder is intended for those users who would like to use HTTP/2 protocol only and take full advantage of message exchange multiplexing. – ok2c Mar 07 '21 at 16:29