Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
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 */
28 package ch.boye.httpclientandroidlib.impl.cookie;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
34 import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
36 import ch.boye.httpclientandroidlib.Header;
37 import ch.boye.httpclientandroidlib.HeaderElement;
38 import ch.boye.httpclientandroidlib.cookie.ClientCookie;
39 import ch.boye.httpclientandroidlib.cookie.Cookie;
40 import ch.boye.httpclientandroidlib.cookie.CookieOrigin;
41 import ch.boye.httpclientandroidlib.cookie.CookiePathComparator;
42 import ch.boye.httpclientandroidlib.cookie.CookieRestrictionViolationException;
43 import ch.boye.httpclientandroidlib.cookie.CookieSpec;
44 import ch.boye.httpclientandroidlib.cookie.MalformedCookieException;
45 import ch.boye.httpclientandroidlib.cookie.SM;
46 import ch.boye.httpclientandroidlib.message.BufferedHeader;
47 import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
49 /**
50 * RFC 2109 compliant {@link CookieSpec} implementation. This is an older
51 * version of the official HTTP state management specification superseded
52 * by RFC 2965.
53 *
54 * @see RFC2965Spec
55 *
56 * @since 4.0
57 */
58 @NotThreadSafe // superclass is @NotThreadSafe
59 public class RFC2109Spec extends CookieSpecBase {
61 private final static CookiePathComparator PATH_COMPARATOR = new CookiePathComparator();
63 private final static String[] DATE_PATTERNS = {
64 DateUtils.PATTERN_RFC1123,
65 DateUtils.PATTERN_RFC1036,
66 DateUtils.PATTERN_ASCTIME
67 };
69 private final String[] datepatterns;
70 private final boolean oneHeader;
72 /** Default constructor */
73 public RFC2109Spec(final String[] datepatterns, boolean oneHeader) {
74 super();
75 if (datepatterns != null) {
76 this.datepatterns = datepatterns.clone();
77 } else {
78 this.datepatterns = DATE_PATTERNS;
79 }
80 this.oneHeader = oneHeader;
81 registerAttribHandler(ClientCookie.VERSION_ATTR, new RFC2109VersionHandler());
82 registerAttribHandler(ClientCookie.PATH_ATTR, new BasicPathHandler());
83 registerAttribHandler(ClientCookie.DOMAIN_ATTR, new RFC2109DomainHandler());
84 registerAttribHandler(ClientCookie.MAX_AGE_ATTR, new BasicMaxAgeHandler());
85 registerAttribHandler(ClientCookie.SECURE_ATTR, new BasicSecureHandler());
86 registerAttribHandler(ClientCookie.COMMENT_ATTR, new BasicCommentHandler());
87 registerAttribHandler(ClientCookie.EXPIRES_ATTR, new BasicExpiresHandler(
88 this.datepatterns));
89 }
91 /** Default constructor */
92 public RFC2109Spec() {
93 this(null, false);
94 }
96 public List<Cookie> parse(final Header header, final CookieOrigin origin)
97 throws MalformedCookieException {
98 if (header == null) {
99 throw new IllegalArgumentException("Header may not be null");
100 }
101 if (origin == null) {
102 throw new IllegalArgumentException("Cookie origin may not be null");
103 }
104 if (!header.getName().equalsIgnoreCase(SM.SET_COOKIE)) {
105 throw new MalformedCookieException("Unrecognized cookie header '"
106 + header.toString() + "'");
107 }
108 HeaderElement[] elems = header.getElements();
109 return parse(elems, origin);
110 }
112 @Override
113 public void validate(final Cookie cookie, final CookieOrigin origin)
114 throws MalformedCookieException {
115 if (cookie == null) {
116 throw new IllegalArgumentException("Cookie may not be null");
117 }
118 String name = cookie.getName();
119 if (name.indexOf(' ') != -1) {
120 throw new CookieRestrictionViolationException("Cookie name may not contain blanks");
121 }
122 if (name.startsWith("$")) {
123 throw new CookieRestrictionViolationException("Cookie name may not start with $");
124 }
125 super.validate(cookie, origin);
126 }
128 public List<Header> formatCookies(List<Cookie> cookies) {
129 if (cookies == null) {
130 throw new IllegalArgumentException("List of cookies may not be null");
131 }
132 if (cookies.isEmpty()) {
133 throw new IllegalArgumentException("List of cookies may not be empty");
134 }
135 if (cookies.size() > 1) {
136 // Create a mutable copy and sort the copy.
137 cookies = new ArrayList<Cookie>(cookies);
138 Collections.sort(cookies, PATH_COMPARATOR);
139 }
140 if (this.oneHeader) {
141 return doFormatOneHeader(cookies);
142 } else {
143 return doFormatManyHeaders(cookies);
144 }
145 }
147 private List<Header> doFormatOneHeader(final List<Cookie> cookies) {
148 int version = Integer.MAX_VALUE;
149 // Pick the lowest common denominator
150 for (Cookie cookie : cookies) {
151 if (cookie.getVersion() < version) {
152 version = cookie.getVersion();
153 }
154 }
155 CharArrayBuffer buffer = new CharArrayBuffer(40 * cookies.size());
156 buffer.append(SM.COOKIE);
157 buffer.append(": ");
158 buffer.append("$Version=");
159 buffer.append(Integer.toString(version));
160 for (Cookie cooky : cookies) {
161 buffer.append("; ");
162 Cookie cookie = cooky;
163 formatCookieAsVer(buffer, cookie, version);
164 }
165 List<Header> headers = new ArrayList<Header>(1);
166 headers.add(new BufferedHeader(buffer));
167 return headers;
168 }
170 private List<Header> doFormatManyHeaders(final List<Cookie> cookies) {
171 List<Header> headers = new ArrayList<Header>(cookies.size());
172 for (Cookie cookie : cookies) {
173 int version = cookie.getVersion();
174 CharArrayBuffer buffer = new CharArrayBuffer(40);
175 buffer.append("Cookie: ");
176 buffer.append("$Version=");
177 buffer.append(Integer.toString(version));
178 buffer.append("; ");
179 formatCookieAsVer(buffer, cookie, version);
180 headers.add(new BufferedHeader(buffer));
181 }
182 return headers;
183 }
185 /**
186 * Return a name/value string suitable for sending in a <tt>"Cookie"</tt>
187 * header as defined in RFC 2109 for backward compatibility with cookie
188 * version 0
189 * @param buffer The char array buffer to use for output
190 * @param name The cookie name
191 * @param value The cookie value
192 * @param version The cookie version
193 */
194 protected void formatParamAsVer(final CharArrayBuffer buffer,
195 final String name, final String value, int version) {
196 buffer.append(name);
197 buffer.append("=");
198 if (value != null) {
199 if (version > 0) {
200 buffer.append('\"');
201 buffer.append(value);
202 buffer.append('\"');
203 } else {
204 buffer.append(value);
205 }
206 }
207 }
209 /**
210 * Return a string suitable for sending in a <tt>"Cookie"</tt> header
211 * as defined in RFC 2109 for backward compatibility with cookie version 0
212 * @param buffer The char array buffer to use for output
213 * @param cookie The {@link Cookie} to be formatted as string
214 * @param version The version to use.
215 */
216 protected void formatCookieAsVer(final CharArrayBuffer buffer,
217 final Cookie cookie, int version) {
218 formatParamAsVer(buffer, cookie.getName(), cookie.getValue(), version);
219 if (cookie.getPath() != null) {
220 if (cookie instanceof ClientCookie
221 && ((ClientCookie) cookie).containsAttribute(ClientCookie.PATH_ATTR)) {
222 buffer.append("; ");
223 formatParamAsVer(buffer, "$Path", cookie.getPath(), version);
224 }
225 }
226 if (cookie.getDomain() != null) {
227 if (cookie instanceof ClientCookie
228 && ((ClientCookie) cookie).containsAttribute(ClientCookie.DOMAIN_ATTR)) {
229 buffer.append("; ");
230 formatParamAsVer(buffer, "$Domain", cookie.getDomain(), version);
231 }
232 }
233 }
235 public int getVersion() {
236 return 1;
237 }
239 public Header getVersionHeader() {
240 return null;
241 }
243 @Override
244 public String toString() {
245 return "rfc2109";
246 }
248 }