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