GitHub user nobodyiam opened a pull request:

    https://github.com/apache/httpasyncclient/pull/8

    Fix the issue when redirecting a post request, original 'Host' header is 
carried

    ## Issue description
    
    We have a situation that allows 307 redirects for POST methods, which works 
well when using HttpClient. 
    
    However, when using HttpAsyncClient, we found the redirected request 
carried the wrong 'Host' header, which caused the requests failed with error 
responses like '421 Misdirected Request'.
    
    ## Demo to duplicate the issue
    
    Attached is a simple demo to dupliate the issue: 
[issue-duplication-demo.zip](https://github.com/apache/httpasyncclient/files/1851502/issue-duplication-demo.zip)
    
    In this demo, we have a web controller that returns 307 redirect to 
`https://hc.apache.org`:
    
    ```java
    @RestController
    public class Test307Controller {
    
      @PostMapping("/test307")
      @RequestMapping(value = "/test307", method = {RequestMethod.GET, 
RequestMethod.POST})
      public ModelAndView redirectPostToPost(HttpServletRequest request) {
        request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, 
HttpStatus.TEMPORARY_REDIRECT);
        return new ModelAndView("redirect:https://hc.apache.org";);
      }
    }
    ```
    
    And we have 2 tests that send a http post to this controller using 
HttpClient and HttpAsyncClient respectively, which both expect a 200 response.
    
    ```Java
      @Test
      public void testSyncPost() throws Exception {
        CloseableHttpClient httpclient = createSyncClient();
    
        try {
          HttpPost request = new HttpPost("http://localhost:8080/test307";);
          CloseableHttpResponse response = httpclient.execute(request);
    
          System.out.println("Response status line: " + 
response.getStatusLine());
    
          assertEquals(200, response.getStatusLine().getStatusCode());
        } finally {
          httpclient.close();
        }
      }
    
      @Test
      public void testAsyncPost() throws Exception {
        CloseableHttpAsyncClient httpclient = createAsyncClient();
    
        try {
          httpclient.start();
          HttpPost request = new HttpPost("http://localhost:8080/test307";);
          Future<HttpResponse> future = httpclient.execute(request, null);
          HttpResponse response = future.get();
    
          System.out.println("Response status line: " + 
response.getStatusLine());
    
          assertEquals(200, response.getStatusLine().getStatusCode());
        } finally {
          httpclient.close();
        }
      }
    ```
    
    However, only the HttpClient case will succeed and the HttpAsyncClient will 
always fail with a http 421 response.
    
    After downloading the demo to your computer, you may run `mvn clean test` 
directly and shall see the `testSyncPost` case succeeds and the `testAsyncPost` 
case fails.
    
    ## Locating the issue
    
    After debugging both the HttpClient and HttpAsyncClient codes, we found the 
difference is 
[HttpClient](https://github.com/apache/httpcomponents-client/blob/4.5.x/httpclient/src/main/java/org/apache/http/impl/execchain/RedirectExec.java#L122)
 passed `currentRequest.getOriginal()` to `redirectStrategy.getRedirect` 
method, while 
[HttpAsyncClient](https://github.com/apache/httpasyncclient/blob/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MainClientExec.java#L591)
 passed `currentRequest` directly to `redirectStrategy.getRedirect` method.
    
    [HttpClient 
logic](https://github.com/apache/httpcomponents-client/blob/4.5.x/httpclient/src/main/java/org/apache/http/impl/execchain/RedirectExec.java#L122):
    ```java
    final HttpRequest redirect = 
this.redirectStrategy.getRedirect(currentRequest.getOriginal(), response, 
context);
    ```
    
    [HttpAsyncClient 
logic](https://github.com/apache/httpasyncclient/blob/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MainClientExec.java#L591):
    ```java
    final HttpUriRequest redirect = 
this.redirectStrategy.getRedirect(currentRequest, currentResponse, 
localContext);
    ```
    
    Since the current request contains the 'Host' header, the redirected 
request will fail.
    
    For the demo case, the redirected request to `hc.apache.org` looks like the 
following, which is obviously wrong since the `Host` header is still 
`localhost:8080`:
    
    ```
    POST / HTTP/1.1
    Host: localhost:8080
    ...
    User-Agent: Apache-HttpAsyncClient/4.1.3 (Java/1.8.0_74)
    ```
    
    ## Fixing the issue
    
    The fix to this issue is simple: passing `currentRequest.getOriginal()` to 
`redirectStrategy.getRedirect` method, just the same logic as what 
[HttpClient](https://github.com/apache/httpcomponents-client/blob/4.5.x/httpclient/src/main/java/org/apache/http/impl/execchain/RedirectExec.java#L122)
 does.
    
    So please help to review this change, we do need this change available ASAP.
    
    Thanks!

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/nobodyiam/httpasyncclient 4.1.x

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/httpasyncclient/pull/8.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #8
    
----
commit e47ed00e488a6b2d84ad8043fdb197f8a7b2716c
Author: Jason Song <nobodyiam@...>
Date:   2018-03-27T10:07:17Z

    fix the issue when redirecting a post request, the original 'Host' header 
is carried, which will cause errors like '421 Misdirected Request'

----


---

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to