View Javadoc
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   */
27  
28  package org.apache.http.impl.cookie;
29  
30  import java.util.ArrayList;
31  import java.util.Collections;
32  import java.util.List;
33  
34  import org.apache.http.Header;
35  import org.apache.http.HeaderElement;
36  import org.apache.http.annotation.Contract;
37  import org.apache.http.annotation.Obsolete;
38  import org.apache.http.annotation.ThreadingBehavior;
39  import org.apache.http.client.utils.DateUtils;
40  import org.apache.http.cookie.ClientCookie;
41  import org.apache.http.cookie.CommonCookieAttributeHandler;
42  import org.apache.http.cookie.Cookie;
43  import org.apache.http.cookie.CookieOrigin;
44  import org.apache.http.cookie.CookiePathComparator;
45  import org.apache.http.cookie.CookieRestrictionViolationException;
46  import org.apache.http.cookie.MalformedCookieException;
47  import org.apache.http.cookie.SM;
48  import org.apache.http.message.BufferedHeader;
49  import org.apache.http.util.Args;
50  import org.apache.http.util.CharArrayBuffer;
51  
52  /**
53   * RFC 2109 compliant {@link org.apache.http.cookie.CookieSpec} implementation.
54   * <p>
55   * Rendered obsolete by {@link org.apache.http.impl.cookie.RFC6265StrictSpec}.
56   *
57   * @since 4.0
58   * @see org.apache.http.impl.cookie.RFC6265StrictSpec
59   */
60  @Obsolete
61  @Contract(threading = ThreadingBehavior.SAFE)
62  public class RFC2109Spec extends CookieSpecBase {
63  
64      final static String[] DATE_PATTERNS = {
65          DateUtils.PATTERN_RFC1123,
66          DateUtils.PATTERN_RFC1036,
67          DateUtils.PATTERN_ASCTIME
68      };
69  
70      private final boolean oneHeader;
71  
72      /** Default constructor */
73      public RFC2109Spec(final String[] datepatterns, final boolean oneHeader) {
74          super(new RFC2109VersionHandler(),
75                  new BasicPathHandler() {
76  
77                      @Override
78                      public void validate(
79                              final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException {
80                          if (!match(cookie, origin)) {
81                              throw new CookieRestrictionViolationException(
82                                      "Illegal 'path' attribute \"" + cookie.getPath()
83                                              + "\". Path of origin: \"" + origin.getPath() + "\"");
84                          }
85                      }
86  
87                  },
88                  new RFC2109DomainHandler(),
89                  new BasicMaxAgeHandler(),
90                  new BasicSecureHandler(),
91                  new BasicCommentHandler(),
92                  new BasicExpiresHandler(
93                          datepatterns != null ? datepatterns.clone() : DATE_PATTERNS));
94          this.oneHeader = oneHeader;
95      }
96  
97      /** Default constructor */
98      public RFC2109Spec() {
99          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 }