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.util.List;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.apache.http.Header;
036import org.apache.http.HeaderIterator;
037import org.apache.http.HttpException;
038import org.apache.http.HttpResponse;
039import org.apache.http.HttpResponseInterceptor;
040import org.apache.http.annotation.Contract;
041import org.apache.http.annotation.ThreadingBehavior;
042import org.apache.http.client.CookieStore;
043import org.apache.http.cookie.Cookie;
044import org.apache.http.cookie.CookieOrigin;
045import org.apache.http.cookie.CookieSpec;
046import org.apache.http.cookie.MalformedCookieException;
047import org.apache.http.cookie.SM;
048import org.apache.http.protocol.HttpContext;
049import org.apache.http.util.Args;
050
051/**
052 * Response interceptor that populates the current {@link CookieStore} with data
053 * contained in response cookies received in the given the HTTP response.
054 *
055 * @since 4.0
056 */
057@Contract(threading = ThreadingBehavior.IMMUTABLE)
058public class ResponseProcessCookies implements HttpResponseInterceptor {
059
060    private final Log log = LogFactory.getLog(getClass());
061
062    public ResponseProcessCookies() {
063        super();
064    }
065
066    @Override
067    public void process(final HttpResponse response, final HttpContext context)
068            throws HttpException, IOException {
069        Args.notNull(response, "HTTP request");
070        Args.notNull(context, "HTTP context");
071
072        final HttpClientContext clientContext = HttpClientContext.adapt(context);
073
074        // Obtain actual CookieSpec instance
075        final CookieSpec cookieSpec = clientContext.getCookieSpec();
076        if (cookieSpec == null) {
077            this.log.debug("Cookie spec not specified in HTTP context");
078            return;
079        }
080        // Obtain cookie store
081        final CookieStore cookieStore = clientContext.getCookieStore();
082        if (cookieStore == null) {
083            this.log.debug("Cookie store not specified in HTTP context");
084            return;
085        }
086        // Obtain actual CookieOrigin instance
087        final CookieOrigin cookieOrigin = clientContext.getCookieOrigin();
088        if (cookieOrigin == null) {
089            this.log.debug("Cookie origin not specified in HTTP context");
090            return;
091        }
092        HeaderIterator it = response.headerIterator(SM.SET_COOKIE);
093        processCookies(it, cookieSpec, cookieOrigin, cookieStore);
094
095        // see if the cookie spec supports cookie versioning.
096        if (cookieSpec.getVersion() > 0) {
097            // process set-cookie2 headers.
098            // Cookie2 will replace equivalent Cookie instances
099            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}