业务上遇到一个坑,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导致的。所以说采用第三方库,还是得了解下这些库,以免出问题。