AsyncHttpClient对Cookie的控制太不灵活了

业务上遇到一个坑,java服务代理了一个接口到upstream,原样转发请求数据和头部。但是代理之后的结果总是莫名其妙的多了一个Cookie,比如是Set-Cookie: ticket=t1

业务上用一个静态的AsyncHttpClient来做代理,也没有做特殊处理,基本上就是如下的代码逻辑:

import org.asynchttpclient.*;

import java.io.IOException;
import java.util.concurrent.ExecutionException;

class Main {
    private static AsyncHttpClient httpClient;

    static {
        DefaultAsyncHttpClientConfig.Builder builder = new DefaultAsyncHttpClientConfig.Builder();

        httpClient = new DefaultAsyncHttpClient(builder.build());
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException, IOException {
        BoundRequestBuilder builder = httpClient.prepareGet(
                "https://httpbin.org/cookies/set/ticket/val1"
        );
        builder.resetCookies();
        builder.execute().get();

        BoundRequestBuilder builder2 = httpClient.prepareGet(
                "https://httpbin.org/cookies"
        );
        builder2.resetCookies();
        Response res2 = builder2.execute().get();
        System.out.println(res2.getResponseBody());
    }
}

当时为了防止Cookie问题,特意加上了resetCookies。

首先是查看ticket Cookie的来源,发现upstream在客户端请求带上ticket Cookie的时候,会返回Set-Cookie: ticket=<val> 这个应该就是多余Cookie的来源了。

但是,即使客户端不带Cookie,java服务这边也会返回Set-Cookie字段。这个问题,排查之后发现问题在于resetCookies只能reset本次请求的Cookie,而客户端的Cookie,则不能清除。

即,某次请求,upstream返回了Set-Cookie: ticket=val,那么,以后的代理请求中,都会带上这个Cookie,那么最终用户也会拿到Set-Cookie字段……

从上述代码的运行结果也可以看出:

{
  "cookies": {
    "ticket": "val1"
  }
}

即,async-http-client没有一个request级别的Cookie控制,只能全局控制Cookie存储。这个问题也有人反馈给了async-http-client

在官方给出解决方案之前,我们只能通过自定义CookieStore的方式来绕过这个问题了:

builder.setCookieStore(new CookieStore() {
    @Override
    public void add(Uri uri, Cookie cookie) {
    }

    @Override
    public List<Cookie> get(Uri uri) {
        return new ArrayList<>();
    }

    @Override
    public List<Cookie> getAll() {
        return new ArrayList<>();
    }

    @Override
    public boolean remove(Predicate<Cookie> predicate) {
        return false;
    }

    @Override
    public boolean clear() {
        return true;
    }
});

这个问题,归根结底还是在于我们不了解async-http-client导致的。所以说采用第三方库,还是得了解下这些库,以免出问题。

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.