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.ArrayList; 031import java.util.Collections; 032import java.util.List; 033 034import org.apache.http.Header; 035import org.apache.http.HeaderElement; 036import org.apache.http.annotation.Contract; 037import org.apache.http.annotation.Obsolete; 038import org.apache.http.annotation.ThreadingBehavior; 039import org.apache.http.client.utils.DateUtils; 040import org.apache.http.cookie.ClientCookie; 041import org.apache.http.cookie.CommonCookieAttributeHandler; 042import org.apache.http.cookie.Cookie; 043import org.apache.http.cookie.CookieOrigin; 044import org.apache.http.cookie.CookiePathComparator; 045import org.apache.http.cookie.CookieRestrictionViolationException; 046import org.apache.http.cookie.MalformedCookieException; 047import org.apache.http.cookie.SM; 048import org.apache.http.message.BufferedHeader; 049import org.apache.http.util.Args; 050import org.apache.http.util.CharArrayBuffer; 051 052/** 053 * RFC 2109 compliant {@link org.apache.http.cookie.CookieSpec} implementation. 054 * <p> 055 * Rendered obsolete by {@link org.apache.http.impl.cookie.RFC6265StrictSpec}. 056 * 057 * @since 4.0 058 * @see org.apache.http.impl.cookie.RFC6265StrictSpec 059 */ 060@Obsolete 061@Contract(threading = ThreadingBehavior.SAFE) 062public class RFC2109Spec extends CookieSpecBase { 063 064 final static String[] DATE_PATTERNS = { 065 DateUtils.PATTERN_RFC1123, 066 DateUtils.PATTERN_RFC1036, 067 DateUtils.PATTERN_ASCTIME 068 }; 069 070 private final boolean oneHeader; 071 072 /** Default constructor */ 073 public RFC2109Spec(final String[] datepatterns, final boolean oneHeader) { 074 super(new RFC2109VersionHandler(), 075 new BasicPathHandler() { 076 077 @Override 078 public void validate( 079 final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException { 080 if (!match(cookie, origin)) { 081 throw new CookieRestrictionViolationException( 082 "Illegal 'path' attribute \"" + cookie.getPath() 083 + "\". Path of origin: \"" + origin.getPath() + "\""); 084 } 085 } 086 087 }, 088 new RFC2109DomainHandler(), 089 new BasicMaxAgeHandler(), 090 new BasicSecureHandler(), 091 new BasicCommentHandler(), 092 new BasicExpiresHandler( 093 datepatterns != null ? datepatterns.clone() : DATE_PATTERNS)); 094 this.oneHeader = oneHeader; 095 } 096 097 /** Default constructor */ 098 public RFC2109Spec() { 099 this(null, false); 100 } 101 102 protected RFC2109Spec(final boolean oneHeader, 103 final CommonCookieAttributeHandler... handlers) { 104 super(handlers); 105 this.oneHeader = oneHeader; 106 } 107 108 @Override 109 public List<Cookie> parse(final Header header, final CookieOrigin origin) 110 throws MalformedCookieException { 111 Args.notNull(header, "Header"); 112 Args.notNull(origin, "Cookie origin"); 113 if (!header.getName().equalsIgnoreCase(SM.SET_COOKIE)) { 114 throw new MalformedCookieException("Unrecognized cookie header '" 115 + header.toString() + "'"); 116 } 117 final HeaderElement[] elems = header.getElements(); 118 return parse(elems, origin); 119 } 120 121 @Override 122 public void validate(final Cookie cookie, final CookieOrigin origin) 123 throws MalformedCookieException { 124 Args.notNull(cookie, "Cookie"); 125 final String name = cookie.getName(); 126 if (name.indexOf(' ') != -1) { 127 throw new CookieRestrictionViolationException("Cookie name may not contain blanks"); 128 } 129 if (name.startsWith("$")) { 130 throw new CookieRestrictionViolationException("Cookie name may not start with $"); 131 } 132 super.validate(cookie, origin); 133 } 134 135 @Override 136 public List<Header> formatCookies(final List<Cookie> cookies) { 137 Args.notEmpty(cookies, "List of cookies"); 138 List<Cookie> cookieList; 139 if (cookies.size() > 1) { 140 // Create a mutable copy and sort the copy. 141 cookieList = new ArrayList<Cookie>(cookies); 142 Collections.sort(cookieList, CookiePathComparator.INSTANCE); 143 } else { 144 cookieList = cookies; 145 } 146 if (this.oneHeader) { 147 return doFormatOneHeader(cookieList); 148 } else { 149 return doFormatManyHeaders(cookieList); 150 } 151 } 152 153 private List<Header> doFormatOneHeader(final List<Cookie> cookies) { 154 int version = Integer.MAX_VALUE; 155 // Pick the lowest common denominator 156 for (final Cookie cookie : cookies) { 157 if (cookie.getVersion() < version) { 158 version = cookie.getVersion(); 159 } 160 } 161 final CharArrayBuffer buffer = new CharArrayBuffer(40 * cookies.size()); 162 buffer.append(SM.COOKIE); 163 buffer.append(": "); 164 buffer.append("$Version="); 165 buffer.append(Integer.toString(version)); 166 for (final Cookie cooky : cookies) { 167 buffer.append("; "); 168 final Cookie cookie = cooky; 169 formatCookieAsVer(buffer, cookie, version); 170 } 171 final List<Header> headers = new ArrayList<Header>(1); 172 headers.add(new BufferedHeader(buffer)); 173 return headers; 174 } 175 176 private List<Header> doFormatManyHeaders(final List<Cookie> cookies) { 177 final List<Header> headers = new ArrayList<Header>(cookies.size()); 178 for (final Cookie cookie : cookies) { 179 final int version = cookie.getVersion(); 180 final CharArrayBuffer buffer = new CharArrayBuffer(40); 181 buffer.append("Cookie: "); 182 buffer.append("$Version="); 183 buffer.append(Integer.toString(version)); 184 buffer.append("; "); 185 formatCookieAsVer(buffer, cookie, version); 186 headers.add(new BufferedHeader(buffer)); 187 } 188 return headers; 189 } 190 191 /** 192 * Return a name/value string suitable for sending in a {@code "Cookie"} 193 * header as defined in RFC 2109 for backward compatibility with cookie 194 * version 0 195 * @param buffer The char array buffer to use for output 196 * @param name The cookie name 197 * @param value The cookie value 198 * @param version The cookie version 199 */ 200 protected void formatParamAsVer(final CharArrayBuffer buffer, 201 final String name, final String value, final int version) { 202 buffer.append(name); 203 buffer.append("="); 204 if (value != null) { 205 if (version > 0) { 206 buffer.append('\"'); 207 buffer.append(value); 208 buffer.append('\"'); 209 } else { 210 buffer.append(value); 211 } 212 } 213 } 214 215 /** 216 * Return a string suitable for sending in a {@code "Cookie"} header 217 * as defined in RFC 2109 for backward compatibility with cookie version 0 218 * @param buffer The char array buffer to use for output 219 * @param cookie The {@link Cookie} to be formatted as string 220 * @param version The version to use. 221 */ 222 protected void formatCookieAsVer(final CharArrayBuffer buffer, 223 final Cookie cookie, final int version) { 224 formatParamAsVer(buffer, cookie.getName(), cookie.getValue(), version); 225 if (cookie.getPath() != null) { 226 if (cookie instanceof ClientCookie 227 && ((ClientCookie) cookie).containsAttribute(ClientCookie.PATH_ATTR)) { 228 buffer.append("; "); 229 formatParamAsVer(buffer, "$Path", cookie.getPath(), version); 230 } 231 } 232 if (cookie.getDomain() != null) { 233 if (cookie instanceof ClientCookie 234 && ((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) { 235 buffer.append("; "); 236 formatParamAsVer(buffer, "$Domain", cookie.getDomain(), version); 237 } 238 } 239 } 240 241 @Override 242 public int getVersion() { 243 return 1; 244 } 245 246 @Override 247 public Header getVersionHeader() { 248 return null; 249 } 250 251 @Override 252 public String toString() { 253 return "rfc2109"; 254 } 255 256}