1 /*
2  * ====================================================================
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  * ====================================================================
20  *
21  * This software consists of voluntary contributions made by many
22  * individuals on behalf of the Apache Software Foundation.  For more
23  * information on the Apache Software Foundation, please see
24  * <http://www.apache.org/>.
25  *
26  */

27
28 package org.apache.http.client.protocol;
29
30 import java.io.IOException;
31 import java.util.List;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35 import org.apache.http.Header;
36 import org.apache.http.HeaderIterator;
37 import org.apache.http.HttpException;
38 import org.apache.http.HttpResponse;
39 import org.apache.http.HttpResponseInterceptor;
40 import org.apache.http.annotation.Contract;
41 import org.apache.http.annotation.ThreadingBehavior;
42 import org.apache.http.client.CookieStore;
43 import org.apache.http.cookie.Cookie;
44 import org.apache.http.cookie.CookieOrigin;
45 import org.apache.http.cookie.CookieSpec;
46 import org.apache.http.cookie.MalformedCookieException;
47 import org.apache.http.cookie.SM;
48 import org.apache.http.protocol.HttpContext;
49 import org.apache.http.util.Args;
50
51 /**
52  * Response interceptor that populates the current {@link CookieStore} with data
53  * contained in response cookies received in the given the HTTP response.
54  *
55  * @since 4.0
56  */

57 @Contract(threading = ThreadingBehavior.IMMUTABLE)
58 public class ResponseProcessCookies implements HttpResponseInterceptor {
59
60     private final Log log = LogFactory.getLog(getClass());
61
62     public ResponseProcessCookies() {
63         super();
64     }
65
66     @Override
67     public void process(final HttpResponse response, final HttpContext context)
68             throws HttpException, IOException {
69         Args.notNull(response, "HTTP request");
70         Args.notNull(context, "HTTP context");
71
72         final HttpClientContext clientContext = HttpClientContext.adapt(context);
73
74         // Obtain actual CookieSpec instance
75         final CookieSpec cookieSpec = clientContext.getCookieSpec();
76         if (cookieSpec == null) {
77             this.log.debug("Cookie spec not specified in HTTP context");
78             return;
79         }
80         // Obtain cookie store
81         final CookieStore cookieStore = clientContext.getCookieStore();
82         if (cookieStore == null) {
83             this.log.debug("Cookie store not specified in HTTP context");
84             return;
85         }
86         // Obtain actual CookieOrigin instance
87         final CookieOrigin cookieOrigin = clientContext.getCookieOrigin();
88         if (cookieOrigin == null) {
89             this.log.debug("Cookie origin not specified in HTTP context");
90             return;
91         }
92         HeaderIterator it = response.headerIterator(SM.SET_COOKIE);
93         processCookies(it, cookieSpec, cookieOrigin, cookieStore);
94
95         // see if the cookie spec supports cookie versioning.
96         if (cookieSpec.getVersion() > 0) {
97             // process set-cookie2 headers.
98             // Cookie2 will replace equivalent Cookie instances
99             it = response.headerIterator(SM.SET_COOKIE2);
100             processCookies(it, cookieSpec, cookieOrigin, cookieStore);
101         }
102     }
103
104     private void processCookies(
105             final HeaderIterator iterator,
106             final CookieSpec cookieSpec,
107             final CookieOrigin cookieOrigin,
108             final CookieStore cookieStore) {
109         while (iterator.hasNext()) {
110             final Header header = iterator.nextHeader();
111             try {
112                 final List<Cookie> cookies = cookieSpec.parse(header, cookieOrigin);
113                 for (final Cookie cookie : cookies) {
114                     try {
115                         cookieSpec.validate(cookie, cookieOrigin);
116                         cookieStore.addCookie(cookie);
117
118                         if (this.log.isDebugEnabled()) {
119                             this.log.debug("Cookie accepted [" + formatCooke(cookie) + "]");
120                         }
121                     } catch (final MalformedCookieException ex) {
122                         if (this.log.isWarnEnabled()) {
123                             this.log.warn("Cookie rejected [" + formatCooke(cookie) + "] "
124                                     + ex.getMessage());
125                         }
126                     }
127                 }
128             } catch (final MalformedCookieException ex) {
129                 if (this.log.isWarnEnabled()) {
130                     this.log.warn("Invalid cookie header: \""
131                             + header + "\". " + ex.getMessage());
132                 }
133             }
134         }
135     }
136
137     private static String formatCooke(final Cookie cookie) {
138         final StringBuilder buf = new StringBuilder();
139         buf.append(cookie.getName());
140         buf.append("=\"");
141         String v = cookie.getValue();
142         if (v != null) {
143             if (v.length() > 100) {
144                 v = v.substring(0, 100) + "...";
145             }
146             buf.append(v);
147         }
148         buf.append("\"");
149         buf.append(", version:");
150         buf.append(Integer.toString(cookie.getVersion()));
151         buf.append(", domain:");
152         buf.append(cookie.getDomain());
153         buf.append(", path:");
154         buf.append(cookie.getPath());
155         buf.append(", expiry:");
156         buf.append(cookie.getExpiryDate());
157         return buf.toString();
158     }
159
160 }
161