• 聊聊jdk http的HeaderFilter


    本文主要研究一下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) {
                // unlikely
                throw 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 response
                cf = exch.responseAsync()
                         .thenCompose((Response response) -> {
                            HttpRequestImpl newrequest;
                            try {
                                // 3. apply response filters
                                newrequest = responseFilters(response);
                            } catch (IOException e) {
                                return failedFuture(e);
                            }
                            // 4. check filter result and repeat or continue
                            if (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 set
                            cancelTimer();
                            if (ex == null) {
                                assert response != null;
                                return completedFuture(response);
                            }
                            // all exceptions thrown are handled here
                            CompletableFuture<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

    
        @Override
        public void request(HttpRequestImpl r, MultiExchange<?> e) throws IOException {
            // use preemptive authentication if an entry exists.
            Cache cache = getCache(e);
            this.exchange = e;
    
            // Proxy
            if (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);
                    }
                }
            }
    
            // Server
            if (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 class
        private 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);
        }
    
       @Override
        public 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 time
                if (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

    
        @Override
        public 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;
        }
    
        @Override
        public 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

    
        @Override
        public 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 cookies
                HttpHeadersBuilder 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());
            }
        }
    
        @Override
        public 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

  • 相关阅读:
    ASP.NET Core新书终于上市,完成今年一个目标,赠书活动
    .NET 5.0 RC2 发布,正式版即将在 11 月 .NET Conf 大会上发布
    .NET Standard 来日苦短去日长
    [C#.NET 拾遗补漏]09:数据标注与数据校验
    几个超级实用但很少人知道的 VS 技巧[更新]
    .NET 5 中 Target Framework 详解
    .NET 5.0 RC1 发布,离正式版发布仅剩两个版本
    Git 实用操作:撤销 Commit 提交(动图讲解)
    Git 实用操作:重写 Commit 历史
    [C#.NET 拾遗补漏]08:强大的LINQ
  • 原文地址:https://www.cnblogs.com/lovellll/p/10097659.html
Copyright © 2020-2023  润新知