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.impl.cookie; 029 030import java.util.List; 031 032import org.apache.http.FormattedHeader; 033import org.apache.http.Header; 034import org.apache.http.HeaderElement; 035import org.apache.http.annotation.Contract; 036import org.apache.http.annotation.ThreadingBehavior; 037import org.apache.http.cookie.Cookie; 038import org.apache.http.cookie.CookieOrigin; 039import org.apache.http.cookie.CookieSpec; 040import org.apache.http.cookie.MalformedCookieException; 041import org.apache.http.cookie.SM; 042import org.apache.http.cookie.SetCookie2; 043import org.apache.http.message.ParserCursor; 044import org.apache.http.util.Args; 045import org.apache.http.util.CharArrayBuffer; 046 047/** 048 * Default cookie specification that picks up the best matching cookie policy based on 049 * the format of cookies sent with the HTTP response. 050 * 051 * @since 4.4 052 */ 053@Contract(threading = ThreadingBehavior.SAFE) 054public class DefaultCookieSpec implements CookieSpec { 055 056 private final RFC2965Spec strict; 057 private final RFC2109Spec obsoleteStrict; 058 private final NetscapeDraftSpec netscapeDraft; 059 060 DefaultCookieSpec( 061 final RFC2965Spec strict, 062 final RFC2109Spec obsoleteStrict, 063 final NetscapeDraftSpec netscapeDraft) { 064 this.strict = strict; 065 this.obsoleteStrict = obsoleteStrict; 066 this.netscapeDraft = netscapeDraft; 067 } 068 069 public DefaultCookieSpec( 070 final String[] datepatterns, 071 final boolean oneHeader) { 072 this.strict = new RFC2965Spec(oneHeader, 073 new RFC2965VersionAttributeHandler(), 074 new BasicPathHandler(), 075 new RFC2965DomainAttributeHandler(), 076 new RFC2965PortAttributeHandler(), 077 new BasicMaxAgeHandler(), 078 new BasicSecureHandler(), 079 new BasicCommentHandler(), 080 new RFC2965CommentUrlAttributeHandler(), 081 new RFC2965DiscardAttributeHandler()); 082 this.obsoleteStrict = new RFC2109Spec(oneHeader, 083 new RFC2109VersionHandler(), 084 new BasicPathHandler(), 085 new RFC2109DomainHandler(), 086 new BasicMaxAgeHandler(), 087 new BasicSecureHandler(), 088 new BasicCommentHandler()); 089 this.netscapeDraft = new NetscapeDraftSpec( 090 new BasicDomainHandler(), 091 new BasicPathHandler(), 092 new BasicSecureHandler(), 093 new BasicCommentHandler(), 094 new BasicExpiresHandler( 095 datepatterns != null ? datepatterns.clone() : new String[]{NetscapeDraftSpec.EXPIRES_PATTERN})); 096 } 097 098 public DefaultCookieSpec() { 099 this(null, false); 100 } 101 102 @Override 103 public List<Cookie> parse( 104 final Header header, 105 final CookieOrigin origin) throws MalformedCookieException { 106 Args.notNull(header, "Header"); 107 Args.notNull(origin, "Cookie origin"); 108 HeaderElement[] helems = header.getElements(); 109 boolean versioned = false; 110 boolean netscape = false; 111 for (final HeaderElement helem: helems) { 112 if (helem.getParameterByName("version") != null) { 113 versioned = true; 114 } 115 if (helem.getParameterByName("expires") != null) { 116 netscape = true; 117 } 118 } 119 if (netscape || !versioned) { 120 // Need to parse the header again, because Netscape style cookies do not correctly 121 // support multiple header elements (comma cannot be treated as an element separator) 122 final NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT; 123 final CharArrayBuffer buffer; 124 final ParserCursor cursor; 125 if (header instanceof FormattedHeader) { 126 buffer = ((FormattedHeader) header).getBuffer(); 127 cursor = new ParserCursor( 128 ((FormattedHeader) header).getValuePos(), 129 buffer.length()); 130 } else { 131 final String s = header.getValue(); 132 if (s == null) { 133 throw new MalformedCookieException("Header value is null"); 134 } 135 buffer = new CharArrayBuffer(s.length()); 136 buffer.append(s); 137 cursor = new ParserCursor(0, buffer.length()); 138 } 139 helems = new HeaderElement[] { parser.parseHeader(buffer, cursor) }; 140 return netscapeDraft.parse(helems, origin); 141 } else { 142 if (SM.SET_COOKIE2.equals(header.getName())) { 143 return strict.parse(helems, origin); 144 } else { 145 return obsoleteStrict.parse(helems, origin); 146 } 147 } 148 } 149 150 @Override 151 public void validate( 152 final Cookie cookie, 153 final CookieOrigin origin) throws MalformedCookieException { 154 Args.notNull(cookie, "Cookie"); 155 Args.notNull(origin, "Cookie origin"); 156 if (cookie.getVersion() > 0) { 157 if (cookie instanceof SetCookie2) { 158 strict.validate(cookie, origin); 159 } else { 160 obsoleteStrict.validate(cookie, origin); 161 } 162 } else { 163 netscapeDraft.validate(cookie, origin); 164 } 165 } 166 167 @Override 168 public boolean match(final Cookie cookie, final CookieOrigin origin) { 169 Args.notNull(cookie, "Cookie"); 170 Args.notNull(origin, "Cookie origin"); 171 if (cookie.getVersion() > 0) { 172 if (cookie instanceof SetCookie2) { 173 return strict.match(cookie, origin); 174 } else { 175 return obsoleteStrict.match(cookie, origin); 176 } 177 } else { 178 return netscapeDraft.match(cookie, origin); 179 } 180 } 181 182 @Override 183 public List<Header> formatCookies(final List<Cookie> cookies) { 184 Args.notNull(cookies, "List of cookies"); 185 int version = Integer.MAX_VALUE; 186 boolean isSetCookie2 = true; 187 for (final Cookie cookie: cookies) { 188 if (!(cookie instanceof SetCookie2)) { 189 isSetCookie2 = false; 190 } 191 if (cookie.getVersion() < version) { 192 version = cookie.getVersion(); 193 } 194 } 195 if (version > 0) { 196 if (isSetCookie2) { 197 return strict.formatCookies(cookies); 198 } else { 199 return obsoleteStrict.formatCookies(cookies); 200 } 201 } else { 202 return netscapeDraft.formatCookies(cookies); 203 } 204 } 205 206 @Override 207 public int getVersion() { 208 return strict.getVersion(); 209 } 210 211 @Override 212 public Header getVersionHeader() { 213 return null; 214 } 215 216 @Override 217 public String toString() { 218 return "default"; 219 } 220 221}