1 /* 2 * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/impl/cookie/RFC2109Spec.java $ 3 * $Revision: 677240 $ 4 * $Date: 2008-07-16 04:25:47 -0700 (Wed, 16 Jul 2008) $ 5 * 6 * ==================================================================== 7 * Licensed to the Apache Software Foundation (ASF) under one 8 * or more contributor license agreements. See the NOTICE file 9 * distributed with this work for additional information 10 * regarding copyright ownership. The ASF licenses this file 11 * to you under the Apache License, Version 2.0 (the 12 * "License"); you may not use this file except in compliance 13 * with the License. You may obtain a copy of the License at 14 * 15 * http://www.apache.org/licenses/LICENSE-2.0 16 * 17 * Unless required by applicable law or agreed to in writing, 18 * software distributed under the License is distributed on an 19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 20 * KIND, either express or implied. See the License for the 21 * specific language governing permissions and limitations 22 * under the License. 23 * ==================================================================== 24 * 25 * This software consists of voluntary contributions made by many 26 * individuals on behalf of the Apache Software Foundation. For more 27 * information on the Apache Software Foundation, please see 28 * <http://www.apache.org/>. 29 * 30 */ 31 32 package org.apache.http.impl.cookie; 33 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.List; 37 38 import org.apache.http.Header; 39 import org.apache.http.HeaderElement; 40 import org.apache.http.cookie.ClientCookie; 41 import org.apache.http.cookie.Cookie; 42 import org.apache.http.cookie.CookieOrigin; 43 import org.apache.http.cookie.CookiePathComparator; 44 import org.apache.http.cookie.MalformedCookieException; 45 import org.apache.http.cookie.SM; 46 import org.apache.http.message.BufferedHeader; 47 import org.apache.http.util.CharArrayBuffer; 48 49 /** 50 * RFC 2109 compliant cookie policy 51 * 52 * @author B.C. Holmes 53 * @author <a href="mailto:jericho@thinkfree.com">Park, Sung-Gu</a> 54 * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a> 55 * @author Rod Waldhoff 56 * @author dIon Gillard 57 * @author Sean C. Sullivan 58 * @author <a href="mailto:JEvans@Cyveillance.com">John Evans</a> 59 * @author Marc A. Saegesser 60 * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a> 61 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> 62 * 63 * @since 4.0 64 */ 65 66 public class RFC2109Spec extends CookieSpecBase { 67 68 private final static CookiePathComparator PATH_COMPARATOR = new CookiePathComparator(); 69 70 private final static String[] DATE_PATTERNS = { 71 DateUtils.PATTERN_RFC1123, 72 DateUtils.PATTERN_RFC1036, 73 DateUtils.PATTERN_ASCTIME 74 }; 75 76 private final String[] datepatterns; 77 private final boolean oneHeader; 78 79 /** Default constructor */ RFC2109Spec(final String[] datepatterns, boolean oneHeader)80 public RFC2109Spec(final String[] datepatterns, boolean oneHeader) { 81 super(); 82 if (datepatterns != null) { 83 this.datepatterns = datepatterns.clone(); 84 } else { 85 this.datepatterns = DATE_PATTERNS; 86 } 87 this.oneHeader = oneHeader; 88 registerAttribHandler(ClientCookie.VERSION_ATTR, new RFC2109VersionHandler()); 89 registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler()); 90 registerAttribHandler(ClientCookie.DOMAIN_ATTR, new RFC2109DomainHandler()); 91 registerAttribHandler(ClientCookie.MAX_AGE_ATTR, new BasicMaxAgeHandler()); 92 registerAttribHandler(ClientCookie.SECURE_ATTR, new BasicSecureHandler()); 93 registerAttribHandler(ClientCookie.COMMENT_ATTR, new BasicCommentHandler()); 94 registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler( 95 this.datepatterns)); 96 } 97 98 /** Default constructor */ RFC2109Spec()99 public RFC2109Spec() { 100 this(null, false); 101 } 102 parse(final Header header, final CookieOrigin origin)103 public List<Cookie> parse(final Header header, final CookieOrigin origin) 104 throws MalformedCookieException { 105 if (header == null) { 106 throw new IllegalArgumentException("Header may not be null"); 107 } 108 if (origin == null) { 109 throw new IllegalArgumentException("Cookie origin may not be null"); 110 } 111 HeaderElement[] elems = header.getElements(); 112 return parse(elems, origin); 113 } 114 115 @Override validate(final Cookie cookie, final CookieOrigin origin)116 public void validate(final Cookie cookie, final CookieOrigin origin) 117 throws MalformedCookieException { 118 if (cookie == null) { 119 throw new IllegalArgumentException("Cookie may not be null"); 120 } 121 String name = cookie.getName(); 122 if (name.indexOf(' ') != -1) { 123 throw new MalformedCookieException("Cookie name may not contain blanks"); 124 } 125 if (name.startsWith("$")) { 126 throw new MalformedCookieException("Cookie name may not start with $"); 127 } 128 super.validate(cookie, origin); 129 } 130 formatCookies(List<Cookie> cookies)131 public List<Header> formatCookies(List<Cookie> cookies) { 132 if (cookies == null) { 133 throw new IllegalArgumentException("List of cookies may not be null"); 134 } 135 if (cookies.isEmpty()) { 136 throw new IllegalArgumentException("List of cookies may not be empty"); 137 } 138 if (cookies.size() > 1) { 139 // Create a mutable copy and sort the copy. 140 cookies = new ArrayList<Cookie>(cookies); 141 Collections.sort(cookies, PATH_COMPARATOR); 142 } 143 if (this.oneHeader) { 144 return doFormatOneHeader(cookies); 145 } else { 146 return doFormatManyHeaders(cookies); 147 } 148 } 149 doFormatOneHeader(final List<Cookie> cookies)150 private List<Header> doFormatOneHeader(final List<Cookie> cookies) { 151 int version = Integer.MAX_VALUE; 152 // Pick the lowest common denominator 153 for (Cookie cookie : cookies) { 154 if (cookie.getVersion() < version) { 155 version = cookie.getVersion(); 156 } 157 } 158 CharArrayBuffer buffer = new CharArrayBuffer(40 * cookies.size()); 159 buffer.append(SM.COOKIE); 160 buffer.append(": "); 161 buffer.append("$Version="); 162 buffer.append(Integer.toString(version)); 163 for (Cookie cooky : cookies) { 164 buffer.append("; "); 165 Cookie cookie = cooky; 166 formatCookieAsVer(buffer, cookie, version); 167 } 168 List<Header> headers = new ArrayList<Header>(1); 169 headers.add(new BufferedHeader(buffer)); 170 return headers; 171 } 172 doFormatManyHeaders(final List<Cookie> cookies)173 private List<Header> doFormatManyHeaders(final List<Cookie> cookies) { 174 List<Header> headers = new ArrayList<Header>(cookies.size()); 175 for (Cookie cookie : cookies) { 176 int version = cookie.getVersion(); 177 CharArrayBuffer buffer = new CharArrayBuffer(40); 178 buffer.append("Cookie: "); 179 buffer.append("$Version="); 180 buffer.append(Integer.toString(version)); 181 buffer.append("; "); 182 formatCookieAsVer(buffer, cookie, version); 183 headers.add(new BufferedHeader(buffer)); 184 } 185 return headers; 186 } 187 188 /** 189 * Return a name/value string suitable for sending in a <tt>"Cookie"</tt> 190 * header as defined in RFC 2109 for backward compatibility with cookie 191 * version 0 192 * @param buffer The char array buffer to use for output 193 * @param name The cookie name 194 * @param value The cookie value 195 * @param version The cookie version 196 */ formatParamAsVer(final CharArrayBuffer buffer, final String name, final String value, int version)197 protected void formatParamAsVer(final CharArrayBuffer buffer, 198 final String name, final String value, int version) { 199 buffer.append(name); 200 buffer.append("="); 201 if (value != null) { 202 if (version > 0) { 203 buffer.append('\"'); 204 buffer.append(value); 205 buffer.append('\"'); 206 } else { 207 buffer.append(value); 208 } 209 } 210 } 211 212 /** 213 * Return a string suitable for sending in a <tt>"Cookie"</tt> header 214 * as defined in RFC 2109 for backward compatibility with cookie version 0 215 * @param buffer The char array buffer to use for output 216 * @param cookie The {@link Cookie} to be formatted as string 217 * @param version The version to use. 218 */ formatCookieAsVer(final CharArrayBuffer buffer, final Cookie cookie, int version)219 protected void formatCookieAsVer(final CharArrayBuffer buffer, 220 final Cookie cookie, int version) { 221 formatParamAsVer(buffer, cookie.getName(), cookie.getValue(), version); 222 if (cookie.getPath() != null) { 223 if (cookie instanceof ClientCookie 224 && ((ClientCookie) cookie).containsAttribute(ClientCookie.PATH_ATTR)) { 225 buffer.append("; "); 226 formatParamAsVer(buffer, "$Path", cookie.getPath(), version); 227 } 228 } 229 if (cookie.getDomain() != null) { 230 if (cookie instanceof ClientCookie 231 && ((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) { 232 buffer.append("; "); 233 formatParamAsVer(buffer, "$Domain", cookie.getDomain(), version); 234 } 235 } 236 } 237 getVersion()238 public int getVersion() { 239 return 1; 240 } 241 getVersionHeader()242 public Header getVersionHeader() { 243 return null; 244 } 245 246 } 247