Uploaded image for project: 'HttpComponents HttpClient'
  1. HttpComponents HttpClient
  2. HTTPCLIENT-2113

Asynch client fails to set Host header during cross-site redirect

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Resolved
    • Major
    • Resolution: Fixed
    • 5.0.1
    • 5.0.2
    • HttpClient (async)
    • None

    Description

      Run this project:

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <groupId>demo</groupId>
          <artifactId>httpclientbug</artifactId>
          <version>0-SNAPSHOT</version>
          <packaging>jar</packaging>
          <properties>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
              <maven.compiler.source>8</maven.compiler.source>
              <maven.compiler.target>8</maven.compiler.target>
          </properties>
          <dependencies>
              <dependency>
                  <groupId>org.apache.httpcomponents.client5</groupId>
                  <artifactId>httpclient5</artifactId>
                  <version>5.0.1</version>
              </dependency>
              <dependency>
                  <groupId>org.slf4j</groupId>
                  <artifactId>slf4j-jdk14</artifactId>
                  <version>1.7.26</version>
                  <scope>runtime</scope>
              </dependency>
          </dependencies>
      </project>
      
      import java.util.logging.ConsoleHandler;
      import java.util.logging.Level;
      import java.util.logging.Logger;
      import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
      import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
      import org.apache.hc.client5.http.classic.methods.HttpGet;
      import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
      import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
      import org.apache.hc.client5.http.impl.classic.HttpClients;
      import org.apache.hc.core5.concurrent.FutureCallback;
      
      public class Main {
          public static void main(String[] args) throws Exception {
              Logger l = Logger.getLogger("org.apache.hc.client5.http.headers");
              l.setLevel(Level.ALL);
              ConsoleHandler h = new ConsoleHandler();
              h.setLevel(Level.ALL);
              l.addHandler(h);
              String url = "https://updates.jenkins.io/download/plugins/checkmarx/2020.3.3/checkmarx.hpi";
              System.err.println("classic: " + HttpClients.createDefault().execute(new HttpGet(url)).getCode());
              CloseableHttpAsyncClient c = HttpAsyncClients.createDefault();
              c.start();
              System.err.println("asynch: " + c.execute(SimpleHttpRequests.get(url), new FutureCallback<SimpleHttpResponse>() {
                  @Override
                  public void completed(SimpleHttpResponse result) {}
                  @Override
                  public void failed(Exception x) {
                      x.printStackTrace();
                  }
                  @Override
                  public void cancelled() {}
              }).get().getCode());
          }
      }
      

      You will see that the synch client processes the two redirects (first to get.jenkins.io then to some mirror such as ftp.yz.yamagata-u.ac.jp) and successfully returns a 200 code. But the asynch client gets a 400 code after the second redirect, I believe because it is neglecting to send a Host header upon redirects (which apparently get.jenkins.io tolerates but the mirrors do not).

      I had difficulty following the control flow in the code here. AsyncRedirectExec and RequestTargetHost are involved; in a debugger I could confirm that RequestTargetHost is called for all three requests in synch mode, but only for the original request in asynch mode. I suspect the issue is related to the fact that HttpAsyncClientBuilder treats RequestTargetHost specially as part of a DefaultHttpProcessor rather than being included in the HttpProcessorBuilder; for example, User-Agent from RequestUserAgent is sent on all requests.

      TestRedirectExec.testCrossSiteRedirect seems like the most applicable test case but I could not follow what it was doing or where to verify concrete things like headers being set on requests.

      Attachments

        1. httpclient2.log
          149 kB
          Jesse Glick
        2. httpclient.log
          152 kB
          Jesse Glick

        Activity

          People

            Unassigned Unassigned
            jglick@netbeans.org Jesse Glick
            Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: