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; 031import java.net.URI; 032import java.net.URISyntaxException; 033import java.util.ArrayList; 034import java.util.Date; 035import java.util.List; 036 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039import org.apache.http.Header; 040import org.apache.http.HttpException; 041import org.apache.http.HttpHost; 042import org.apache.http.HttpRequest; 043import org.apache.http.HttpRequestInterceptor; 044import org.apache.http.annotation.Contract; 045import org.apache.http.annotation.ThreadingBehavior; 046import org.apache.http.client.CookieStore; 047import org.apache.http.client.config.CookieSpecs; 048import org.apache.http.client.config.RequestConfig; 049import org.apache.http.client.methods.HttpUriRequest; 050import org.apache.http.config.Lookup; 051import org.apache.http.conn.routing.RouteInfo; 052import org.apache.http.cookie.Cookie; 053import org.apache.http.cookie.CookieOrigin; 054import org.apache.http.cookie.CookieSpec; 055import org.apache.http.cookie.CookieSpecProvider; 056import org.apache.http.protocol.HttpContext; 057import org.apache.http.util.Args; 058import org.apache.http.util.TextUtils; 059 060/** 061 * Request interceptor that matches cookies available in the current 062 * {@link CookieStore} to the request being executed and generates 063 * corresponding {@code Cookie} request headers. 064 * 065 * @since 4.0 066 */ 067@Contract(threading = ThreadingBehavior.IMMUTABLE) 068public class RequestAddCookies implements HttpRequestInterceptor { 069 070 private final Log log = LogFactory.getLog(getClass()); 071 072 public RequestAddCookies() { 073 super(); 074 } 075 076 @Override 077 public void process(final HttpRequest request, final HttpContext context) 078 throws HttpException, IOException { 079 Args.notNull(request, "HTTP request"); 080 Args.notNull(context, "HTTP context"); 081 082 final String method = request.getRequestLine().getMethod(); 083 if (method.equalsIgnoreCase("CONNECT")) { 084 return; 085 } 086 087 final HttpClientContext clientContext = HttpClientContext.adapt(context); 088 089 // Obtain cookie store 090 final CookieStore cookieStore = clientContext.getCookieStore(); 091 if (cookieStore == null) { 092 this.log.debug("Cookie store not specified in HTTP context"); 093 return; 094 } 095 096 // Obtain the registry of cookie specs 097 final Lookup<CookieSpecProvider> registry = clientContext.getCookieSpecRegistry(); 098 if (registry == null) { 099 this.log.debug("CookieSpec registry not specified in HTTP context"); 100 return; 101 } 102 103 // Obtain the target host, possibly virtual (required) 104 final HttpHost targetHost = clientContext.getTargetHost(); 105 if (targetHost == null) { 106 this.log.debug("Target host not set in the context"); 107 return; 108 } 109 110 // Obtain the route (required) 111 final RouteInfo route = clientContext.getHttpRoute(); 112 if (route == null) { 113 this.log.debug("Connection route not set in the context"); 114 return; 115 } 116 117 final RequestConfig config = clientContext.getRequestConfig(); 118 String policy = config.getCookieSpec(); 119 if (policy == null) { 120 policy = CookieSpecs.DEFAULT; 121 } 122 if (this.log.isDebugEnabled()) { 123 this.log.debug("CookieSpec selected: " + policy); 124 } 125 126 URI requestURI = null; 127 if (request instanceof HttpUriRequest) { 128 requestURI = ((HttpUriRequest) request).getURI(); 129 } else { 130 try { 131 requestURI = new URI(request.getRequestLine().getUri()); 132 } catch (final URISyntaxException ignore) { 133 } 134 } 135 final String path = requestURI != null ? requestURI.getPath() : null; 136 final String hostName = targetHost.getHostName(); 137 int port = targetHost.getPort(); 138 if (port < 0) { 139 port = route.getTargetHost().getPort(); 140 } 141 142 final CookieOrigin cookieOrigin = new CookieOrigin( 143 hostName, 144 port >= 0 ? port : 0, 145 !TextUtils.isEmpty(path) ? path : "/", 146 route.isSecure()); 147 148 // Get an instance of the selected cookie policy 149 final CookieSpecProvider provider = registry.lookup(policy); 150 if (provider == null) { 151 if (this.log.isDebugEnabled()) { 152 this.log.debug("Unsupported cookie policy: " + policy); 153 } 154 155 return; 156 } 157 final CookieSpec cookieSpec = provider.create(clientContext); 158 // Get all cookies available in the HTTP state 159 final List<Cookie> cookies = cookieStore.getCookies(); 160 // Find cookies matching the given origin 161 final List<Cookie> matchedCookies = new ArrayList<Cookie>(); 162 final Date now = new Date(); 163 boolean expired = false; 164 for (final Cookie cookie : cookies) { 165 if (!cookie.isExpired(now)) { 166 if (cookieSpec.match(cookie, cookieOrigin)) { 167 if (this.log.isDebugEnabled()) { 168 this.log.debug("Cookie " + cookie + " match " + cookieOrigin); 169 } 170 matchedCookies.add(cookie); 171 } 172 } else { 173 if (this.log.isDebugEnabled()) { 174 this.log.debug("Cookie " + cookie + " expired"); 175 } 176 expired = true; 177 } 178 } 179 // Per RFC 6265, 5.3 180 // The user agent must evict all expired cookies if, at any time, an expired cookie 181 // exists in the cookie store 182 if (expired) { 183 cookieStore.clearExpired(now); 184 } 185 // Generate Cookie request headers 186 if (!matchedCookies.isEmpty()) { 187 final List<Header> headers = cookieSpec.formatCookies(matchedCookies); 188 for (final Header header : headers) { 189 request.addHeader(header); 190 } 191 } 192 193 final int ver = cookieSpec.getVersion(); 194 if (ver > 0) { 195 final Header header = cookieSpec.getVersionHeader(); 196 if (header != null) { 197 // Advertise cookie version support 198 request.addHeader(header); 199 } 200 } 201 202 // Stick the CookieSpec and CookieOrigin instances to the HTTP context 203 // so they could be obtained by the response interceptor 204 context.setAttribute(HttpClientContext.COOKIE_SPEC, cookieSpec); 205 context.setAttribute(HttpClientContext.COOKIE_ORIGIN, cookieOrigin); 206 } 207 208}