本文主要研究一下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

聊聊jdk http的HeaderFilter相关推荐

  1. [case39]聊聊jdk httpclient的executor

    序 本文主要研究一下jdk httpclient的executor HttpClientImpl java.net.http/jdk/internal/net/http/HttpClientImpl. ...

  2. java+connect+time+out_聊聊jdk httpclient的connect timeout异常

    序 本文主要研究一下httpclient的connect timeout异常 实例代码 @Test public void testConnectTimeout() throws IOExceptio ...

  3. 聊聊Jdk中你没听过的关键词-synthetic

    文章目录 前言 什么是synthetic? 作用和原理 产生的问题 什么是NBAC? 前言 为什么要讲讲synthetic和NBAC呢?其实在这之前,对Jdk中这两种机制并不了解,甚至没有听过,主要原 ...

  4. 【拿来吧你】JDK动态代理

    java proxy 因为最近一段时间准备将这几年做的一些业务和技术做个沉淀,也自己造的一些轮子,发现时不时就会需要用到动态代理和反射,所以今天打算先对jdk的动态代理这部分内容做个简单的整理 介绍 ...

  5. 后端JAVA:说说你对【注解】的理解

    本文主要内容如下: 背景 现在已经处于注解盛行时代,注解@Override ,这个注解是再熟悉不过了,还有@Controller.@RequestMapping.@Service..... 注解已经是 ...

  6. 小公司出身的我,是如何拿下知名独角兽公司 Offer?

    作者 | 中华石杉 责编 | 伍杏玲 本文经授权转载石杉的架构笔记(ID:shishan100) [CSDN 编者按]作者是一个不知名的小公司出身,原先年薪不到20万,通过自己的努力不断的提升技术,最 ...

  7. JAVA面试100道必考题

    1.如下代码的执行结果: 4,1,11 @Testvoid demo01(){int i=1;i=i++;int j=i++;// i=2 j=1int k=i+ ++i * i++;//2+3*3= ...

  8. 小公司出身,如何斩获知名独角兽公司Offer?

    正式开始之前,先说一下这位同学的面试成果,从一个不知名的小公司出身,原先年薪不到 20 万,通过自己的努力不断提升技术,最终收获多个知名互联网公司的 Offer,年薪达到 30 多万,几乎翻倍! 本文 ...

  9. 2021秋招高频面经汇总(Java开发岗)

    1.G1为什么高吞吐量 G1多线程并行并发 2.B+树解决什么问题 解决查询遍历太深的问题 3.硬连接和软连接 硬连接:新建的文件是已经存在的文件的一个别名,当原文件删除时,新建的文件仍然可以使用. ...

最新文章

  1. 软件测试之移动应用的压力和性能测试
  2. c++ string分割字符串split_python 字符串和文本处理
  3. java stack list_JAVA自己实现List接口Stack
  4. Material Design入门(三)
  5. php批量新增数据类型,Yii框架批量插入数据扩展类的简单实现方法
  6. 高并发 问题怎么解决
  7. Android中Xml工具类的封装
  8. linux系统怎么启动服务器,Linux操作系统的启动步骤详细说明
  9. 尔雅 科学通史(吴国盛) 个人笔记及课后习题 2018 第七章 实验传统的兴起
  10. 【Golang】家庭收支记账软件
  11. AOP设计与原理(连接点,切入点与代理模式)——Spring AOP(二)
  12. ubuntu 安装咖啡壶-chemex命令详解
  13. 口算加密php怎么使用,从数盲到口算 ——带你玩转RSA加密算法(一)
  14. Hibernate使用详解(一)
  15. camera 添加外挂pmic进行供电的涉及
  16. Tableau数据分析笔记-Chapter08数据分层、数据分组、数据集
  17. 单链表的十三个基本操作(全)
  18. 包青天 - 乞丐王孙(4) 片段 - 堂审河南荥县县令马松友
  19. Android getDimensionPixelSize, 代码中设置字体大小,读xml配置。
  20. ReactiveCocoa Documents 翻译(基于版本V2.5)

热门文章

  1. 实验6 数据查询--高级查询
  2. c语言if不能判断u8变量值,C语言变量名命规则.doc
  3. DS和[address]
  4. 279. 完全平方数 golang BFS
  5. 为github帐号添加SSH keys
  6. Linux安装Ncurses库
  7. 【计算机系统设计】重点 · 学习笔记(0)(数据通路设计思想)
  8. UNIX网络编程笔记(4):简单的回射程序
  9. 中高级工程师Java开发!java生成随机数代码包
  10. php函数网,php函数