001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.client.protocol; 029 030import java.io.IOException; 031 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.apache.http.HttpException; 035import org.apache.http.HttpHost; 036import org.apache.http.HttpRequest; 037import org.apache.http.HttpRequestInterceptor; 038import org.apache.http.annotation.Contract; 039import org.apache.http.annotation.ThreadingBehavior; 040import org.apache.http.auth.AuthProtocolState; 041import org.apache.http.auth.AuthScheme; 042import org.apache.http.auth.AuthScope; 043import org.apache.http.auth.AuthState; 044import org.apache.http.auth.Credentials; 045import org.apache.http.client.AuthCache; 046import org.apache.http.client.CredentialsProvider; 047import org.apache.http.conn.routing.RouteInfo; 048import org.apache.http.protocol.HttpContext; 049import org.apache.http.util.Args; 050 051/** 052 * Request interceptor that can preemptively authenticate against known hosts, 053 * if there is a cached {@link AuthScheme} instance in the local 054 * {@link AuthCache} associated with the given target or proxy host. 055 * 056 * @since 4.1 057 */ 058@Contract(threading = ThreadingBehavior.IMMUTABLE) 059public class RequestAuthCache implements HttpRequestInterceptor { 060 061 private final Log log = LogFactory.getLog(getClass()); 062 063 public RequestAuthCache() { 064 super(); 065 } 066 067 @Override 068 public void process(final HttpRequest request, final HttpContext context) 069 throws HttpException, IOException { 070 Args.notNull(request, "HTTP request"); 071 Args.notNull(context, "HTTP context"); 072 073 final HttpClientContext clientContext = HttpClientContext.adapt(context); 074 075 final AuthCache authCache = clientContext.getAuthCache(); 076 if (authCache == null) { 077 this.log.debug("Auth cache not set in the context"); 078 return; 079 } 080 081 final CredentialsProvider credsProvider = clientContext.getCredentialsProvider(); 082 if (credsProvider == null) { 083 this.log.debug("Credentials provider not set in the context"); 084 return; 085 } 086 087 final RouteInfo route = clientContext.getHttpRoute(); 088 if (route == null) { 089 this.log.debug("Route info not set in the context"); 090 return; 091 } 092 093 HttpHost target = clientContext.getTargetHost(); 094 if (target == null) { 095 this.log.debug("Target host not set in the context"); 096 return; 097 } 098 099 if (target.getPort() < 0) { 100 target = new HttpHost( 101 target.getHostName(), 102 route.getTargetHost().getPort(), 103 target.getSchemeName()); 104 } 105 106 final AuthState targetState = clientContext.getTargetAuthState(); 107 if (targetState != null && targetState.getState() == AuthProtocolState.UNCHALLENGED) { 108 final AuthScheme authScheme = authCache.get(target); 109 if (authScheme != null) { 110 doPreemptiveAuth(target, authScheme, targetState, credsProvider); 111 } 112 } 113 114 final HttpHost proxy = route.getProxyHost(); 115 final AuthState proxyState = clientContext.getProxyAuthState(); 116 if (proxy != null && proxyState != null && proxyState.getState() == AuthProtocolState.UNCHALLENGED) { 117 final AuthScheme authScheme = authCache.get(proxy); 118 if (authScheme != null) { 119 doPreemptiveAuth(proxy, authScheme, proxyState, credsProvider); 120 } 121 } 122 } 123 124 private void doPreemptiveAuth( 125 final HttpHost host, 126 final AuthScheme authScheme, 127 final AuthState authState, 128 final CredentialsProvider credsProvider) { 129 final String schemeName = authScheme.getSchemeName(); 130 if (this.log.isDebugEnabled()) { 131 this.log.debug("Re-using cached '" + schemeName + "' auth scheme for " + host); 132 } 133 134 final AuthScope authScope = new AuthScope(host, AuthScope.ANY_REALM, schemeName); 135 final Credentials creds = credsProvider.getCredentials(authScope); 136 137 if (creds != null) { 138 if ("BASIC".equalsIgnoreCase(authScheme.getSchemeName())) { 139 authState.setState(AuthProtocolState.CHALLENGED); 140 } else { 141 authState.setState(AuthProtocolState.SUCCESS); 142 } 143 authState.update(authScheme, creds); 144 } else { 145 this.log.debug("No credentials for preemptive authentication"); 146 } 147 } 148 149}