序
本文主要研究一下jdk http的HeaderFilter。
FilterFactory
java.net.http/jdk/internal/net/http/FilterFactory.java
class FilterFactory {// Strictly-ordered list of filters.final LinkedList<Class<? extends HeaderFilter>> filterClasses = new LinkedList<>();public void addFilter(Class<? extends HeaderFilter> type) {filterClasses.add(type);}LinkedList<HeaderFilter> getFilterChain() {LinkedList<HeaderFilter> l = new LinkedList<>();for (Class<? extends HeaderFilter> clazz : filterClasses) {try {// Requires a public no arg constructor.HeaderFilter headerFilter = clazz.getConstructor().newInstance();l.add(headerFilter);} catch (ReflectiveOperationException e) {throw new InternalError(e);}}return l;}
}
复制代码
- 提供了addFilter及getFilterChain方法,前者添加filter class,后者使用反射实例化filter。
HttpClientImpl
java.net.http/jdk/internal/net/http/HttpClientImpl.java
private HttpClientImpl(HttpClientBuilderImpl builder,SingleFacadeFactory facadeFactory) {id = CLIENT_IDS.incrementAndGet();dbgTag = "HttpClientImpl(" + id +")";if (builder.sslContext == null) {try {sslContext = SSLContext.getDefault();} catch (NoSuchAlgorithmException ex) {throw new InternalError(ex);}} else {sslContext = builder.sslContext;}Executor ex = builder.executor;if (ex == null) {ex = Executors.newCachedThreadPool(new DefaultThreadFactory(id));isDefaultExecutor = true;} else {isDefaultExecutor = false;}delegatingExecutor = new DelegatingExecutor(this::isSelectorThread, ex);facadeRef = new WeakReference<>(facadeFactory.createFacade(this));client2 = new Http2ClientImpl(this);cookieHandler = builder.cookieHandler;connectTimeout = builder.connectTimeout;followRedirects = builder.followRedirects == null ?Redirect.NEVER : builder.followRedirects;this.userProxySelector = Optional.ofNullable(builder.proxy);this.proxySelector = userProxySelector.orElseGet(HttpClientImpl::getDefaultProxySelector);if (debug.on())debug.log("proxySelector is %s (user-supplied=%s)",this.proxySelector, userProxySelector.isPresent());authenticator = builder.authenticator;if (builder.version == null) {version = HttpClient.Version.HTTP_2;} else {version = builder.version;}if (builder.sslParams == null) {sslParams = getDefaultParams(sslContext);} else {sslParams = builder.sslParams;}connections = new ConnectionPool(id);connections.start();timeouts = new TreeSet<>();try {selmgr = new SelectorManager(this);} catch (IOException e) {// unlikelythrow new InternalError(e);}selmgr.setDaemon(true);filters = new FilterFactory();initFilters();assert facadeRef.get() != null;}private void initFilters() {addFilter(AuthenticationFilter.class);addFilter(RedirectFilter.class);if (this.cookieHandler != null) {addFilter(CookieFilter.class);}}private void addFilter(Class<? extends HeaderFilter> f) {filters.addFilter(f);}final LinkedList<HeaderFilter> filterChain() {return filters.getFilterChain();}
复制代码
- HttpClientImpl的构造器创建了FilterFactory,并调用addFilter添加默认的filter
- filterChain方法则调用了FilterFactory的getFilterChain()方法,使用反射实例化这些filter
MultiExchange
java.net.http/jdk/internal/net/http/MultiExchange.java
/*** MultiExchange with one final response.*/MultiExchange(HttpRequest userRequest,HttpRequestImpl requestImpl,HttpClientImpl client,HttpResponse.BodyHandler<T> responseHandler,PushPromiseHandler<T> pushPromiseHandler,AccessControlContext acc) {this.previous = null;this.userRequest = userRequest;this.request = requestImpl;this.currentreq = request;this.previousreq = null;this.client = client;this.filters = client.filterChain();this.acc = acc;this.executor = client.theExecutor();this.responseHandler = responseHandler;if (pushPromiseHandler != null) {Executor executor = acc == null? this.executor.delegate(): new PrivilegedExecutor(this.executor.delegate(), acc);this.pushGroup = new PushGroup<>(pushPromiseHandler, request, executor);} else {pushGroup = null;}this.exchange = new Exchange<>(request, this);}private CompletableFuture<Response> responseAsyncImpl() {CompletableFuture<Response> cf;if (attempts.incrementAndGet() > max_attempts) {cf = failedFuture(new IOException("Too many retries", retryCause));} else {if (currentreq.timeout().isPresent()) {responseTimerEvent = ResponseTimerEvent.of(this);client.registerTimer(responseTimerEvent);}try {// 1. apply request filters// if currentreq == previousreq the filters have already// been applied once. Applying them a second time might// cause some headers values to be added twice: for// instance, the same cookie might be added again.if (currentreq != previousreq) {requestFilters(currentreq);}} catch (IOException e) {return failedFuture(e);}Exchange<T> exch = getExchange();// 2. get responsecf = exch.responseAsync().thenCompose((Response response) -> {HttpRequestImpl newrequest;try {// 3. apply response filtersnewrequest = responseFilters(response);} catch (IOException e) {return failedFuture(e);}// 4. check filter result and repeat or continueif (newrequest == null) {if (attempts.get() > 1) {Log.logError("Succeeded on attempt: " + attempts);}return completedFuture(response);} else {this.response =new HttpResponseImpl<>(currentreq, response, this.response, null, exch);Exchange<T> oldExch = exch;return exch.ignoreBody().handle((r,t) -> {previousreq = currentreq;currentreq = newrequest;expiredOnce = false;setExchange(new Exchange<>(currentreq, this, acc));return responseAsyncImpl();}).thenCompose(Function.identity());} }).handle((response, ex) -> {// 5. handle errors and cancel any timer setcancelTimer();if (ex == null) {assert response != null;return completedFuture(response);}// all exceptions thrown are handled hereCompletableFuture<Response> errorCF = getExceptionalCF(ex);if (errorCF == null) {return responseAsyncImpl();} else {return errorCF;} }).thenCompose(Function.identity());}return cf;}private void requestFilters(HttpRequestImpl r) throws IOException {Log.logTrace("Applying request filters");for (HeaderFilter filter : filters) {Log.logTrace("Applying {0}", filter);filter.request(r, this);}Log.logTrace("All filters applied");}private HttpRequestImpl responseFilters(Response response) throws IOException{Log.logTrace("Applying response filters");Iterator<HeaderFilter> reverseItr = filters.descendingIterator();while (reverseItr.hasNext()) {HeaderFilter filter = reverseItr.next();Log.logTrace("Applying {0}", filter);HttpRequestImpl newreq = filter.response(response);if (newreq != null) {Log.logTrace("New request: stopping filters");return newreq;}}Log.logTrace("All filters applied");return null;}
复制代码
- MultiExchange在构造器里头调用了client.filterChain(),完成filters的初始化
- 在responseAsyncImpl方法里头,执行请求之前调用requestFilters,得到response之后调用responseFilters
- requestFilters是按顺序执行,而responseFilters则取的是descendingIterator,逆序执行
HeaderFilter
java.net.http/jdk/internal/net/http/HeaderFilter.java
/*** A header filter that can examine or modify, typically system headers for* requests before they are sent, and responses before they are returned to the* user. Some ability to resend requests is provided.*/
interface HeaderFilter {void request(HttpRequestImpl r, MultiExchange<?> e) throws IOException;/*** Returns null if response ok to be given to user. Non null is a request* that must be resent and its response given to user. If impl throws an* exception that is returned to user instead.*/HttpRequestImpl response(Response r) throws IOException;
}
复制代码
- 可以看到HeaderFilter接口定义了request以及response方法
- 对于response方法,如果对header处理没问题就返回null,有异常抛异常,需要重新发送的则会返回HttpRequestImpl
- HeaderFilter有三个实现类,分别是AuthenticationFilter、RedirectFilter、CookieFilter
AuthenticationFilter
java.net.http/jdk/internal/net/http/AuthenticationFilter.java
@Overridepublic void request(HttpRequestImpl r, MultiExchange<?> e) throws IOException {// use preemptive authentication if an entry exists.Cache cache = getCache(e);this.exchange = e;// Proxyif (exchange.proxyauth == null) {URI proxyURI = getProxyURI(r);if (proxyURI != null) {CacheEntry ca = cache.get(proxyURI, true);if (ca != null) {exchange.proxyauth = new AuthInfo(true, ca.scheme, null, ca);addBasicCredentials(r, true, ca.value);}}}// Serverif (exchange.serverauth == null) {CacheEntry ca = cache.get(r.uri(), false);if (ca != null) {exchange.serverauth = new AuthInfo(true, ca.scheme, null, ca);addBasicCredentials(r, false, ca.value);}}}// TODO: refactor into per auth scheme classprivate static void addBasicCredentials(HttpRequestImpl r,boolean proxy,PasswordAuthentication pw) {String hdrname = proxy ? "Proxy-Authorization" : "Authorization";StringBuilder sb = new StringBuilder(128);sb.append(pw.getUserName()).append(':').append(pw.getPassword());String s = encoder.encodeToString(sb.toString().getBytes(ISO_8859_1));String value = "Basic " + s;if (proxy) {if (r.isConnect()) {if (!Utils.PROXY_TUNNEL_FILTER.test(hdrname, value)) {Log.logError("{0} disabled", hdrname);return;}} else if (r.proxy() != null) {if (!Utils.PROXY_FILTER.test(hdrname, value)) {Log.logError("{0} disabled", hdrname);return;}}}r.setSystemHeader(hdrname, value);}@Overridepublic HttpRequestImpl response(Response r) throws IOException {Cache cache = getCache(exchange);int status = r.statusCode();HttpHeaders hdrs = r.headers();HttpRequestImpl req = r.request();if (status != UNAUTHORIZED && status != PROXY_UNAUTHORIZED) {// check if any authentication succeeded for first timeif (exchange.serverauth != null && !exchange.serverauth.fromcache) {AuthInfo au = exchange.serverauth;cache.store(au.scheme, req.uri(), false, au.credentials);}if (exchange.proxyauth != null && !exchange.proxyauth.fromcache) {AuthInfo au = exchange.proxyauth;URI proxyURI = getProxyURI(req);if (proxyURI != null) {cache.store(au.scheme, proxyURI, true, au.credentials);}}return null;}//......}
复制代码
- 可以用于添加basic authentication的header
RedirectFilter
java.net.http/jdk/internal/net/http/RedirectFilter.java
@Overridepublic synchronized void request(HttpRequestImpl r, MultiExchange<?> e) throws IOException {this.request = r;this.client = e.client();this.policy = client.followRedirects();this.method = r.method();this.uri = r.uri();this.exchange = e;}@Overridepublic synchronized HttpRequestImpl response(Response r) throws IOException {return handleResponse(r);}/*** Checks to see if a new request is needed and returns it.* Null means response is ok to return to user.*/private HttpRequestImpl handleResponse(Response r) {int rcode = r.statusCode();if (rcode == 200 || policy == HttpClient.Redirect.NEVER) {return null;}if (rcode == HTTP_NOT_MODIFIED)return null;if (rcode >= 300 && rcode <= 399) {URI redir = getRedirectedURI(r.headers());String newMethod = redirectedMethod(rcode, method);Log.logTrace("response code: {0}, redirected URI: {1}", rcode, redir);if (canRedirect(redir) && ++exchange.numberOfRedirects < max_redirects) {Log.logTrace("redirect to: {0} with method: {1}", redir, newMethod);return HttpRequestImpl.newInstanceForRedirection(redir, newMethod, request);} else {Log.logTrace("not redirecting");return null;}}return null;}
复制代码
- 主要用于处理3xx跳转,这个时候满足条件的话会返回新的HttpRequestImpl实例
CookieFilter
java.net.http/jdk/internal/net/http/CookieFilter.java
@Overridepublic void request(HttpRequestImpl r, MultiExchange<?> e) throws IOException {HttpClientImpl client = e.client();Optional<CookieHandler> cookieHandlerOpt = client.cookieHandler();if (cookieHandlerOpt.isPresent()) {CookieHandler cookieHandler = cookieHandlerOpt.get();Map<String,List<String>> userheaders = r.getUserHeaders().map();Map<String,List<String>> cookies = cookieHandler.get(r.uri(), userheaders);// add the returned cookiesHttpHeadersBuilder systemHeadersBuilder = r.getSystemHeadersBuilder();if (cookies.isEmpty()) {Log.logTrace("Request: no cookie to add for {0}", r.uri());} else {Log.logTrace("Request: adding cookies for {0}", r.uri());}for (Map.Entry<String,List<String>> entry : cookies.entrySet()) {final String hdrname = entry.getKey();if (!hdrname.equalsIgnoreCase("Cookie")&& !hdrname.equalsIgnoreCase("Cookie2"))continue;List<String> values = entry.getValue();if (values == null || values.isEmpty()) continue;for (String val : values) {if (Utils.isValidValue(val)) {systemHeadersBuilder.addHeader(hdrname, val);}}}} else {Log.logTrace("Request: No cookie manager found for {0}", r.uri());}}@Overridepublic HttpRequestImpl response(Response r) throws IOException {HttpHeaders hdrs = r.headers();HttpRequestImpl request = r.request();Exchange<?> e = r.exchange;Log.logTrace("Response: processing cookies for {0}", request.uri());Optional<CookieHandler> cookieHandlerOpt = e.client().cookieHandler();if (cookieHandlerOpt.isPresent()) {CookieHandler cookieHandler = cookieHandlerOpt.get();Log.logTrace("Response: parsing cookies from {0}", hdrs.map());cookieHandler.put(request.uri(), hdrs.map());} else {Log.logTrace("Response: No cookie manager found for {0}",request.uri());}return null;}
复制代码
- 用于请求以及响应的cookie相关的处理
小结
- FilterFactory使用了简单的责任链模式,getFilterChain方法使用反射实例化各种filter
- HeaderFilter定义了request及response两个方法,分别作用于请求前及获得响应之后
- HeaderFilter有三个实现类,分别是AuthenticationFilter、RedirectFilter、CookieFilter
- MultiExchange在responseAsyncImpl方法里头,执行请求之前调用requestFilters,得到response之后调用responseFilters。其中requestFilters是按顺序执行,而responseFilters则取的是descendingIterator,逆序执行
doc
- java.net.http javadoc