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  package org.apache.http.impl.cookie;
28  
29  import java.util.ArrayList;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.Map;
34  
35  import org.apache.http.Header;
36  import org.apache.http.HeaderElement;
37  import org.apache.http.NameValuePair;
38  import org.apache.http.annotation.Contract;
39  import org.apache.http.annotation.Obsolete;
40  import org.apache.http.annotation.ThreadingBehavior;
41  import org.apache.http.cookie.ClientCookie;
42  import org.apache.http.cookie.CommonCookieAttributeHandler;
43  import org.apache.http.cookie.Cookie;
44  import org.apache.http.cookie.CookieAttributeHandler;
45  import org.apache.http.cookie.CookieOrigin;
46  import org.apache.http.cookie.CookieRestrictionViolationException;
47  import org.apache.http.cookie.MalformedCookieException;
48  import org.apache.http.cookie.SM;
49  import org.apache.http.message.BufferedHeader;
50  import org.apache.http.util.Args;
51  import org.apache.http.util.CharArrayBuffer;
52  
53  /**
54   * RFC 2965 compliant {@link org.apache.http.cookie.CookieSpec} implementation.
55   * <p>
56   * Rendered obsolete by {@link org.apache.http.impl.cookie.RFC6265StrictSpec}.
57   *
58   * @since 4.0
59   * @see org.apache.http.impl.cookie.RFC6265StrictSpec
60   */
61  @Obsolete
62  @Contract(threading = ThreadingBehavior.SAFE)
63  public class RFC2965Spec extends RFC2109Spec {
64  
65      /**
66       * Default constructor
67       */
68      public RFC2965Spec() {
69          this(null, false);
70      }
71  
72      public RFC2965Spec(final String[] datepatterns, final boolean oneHeader) {
73          super(oneHeader,
74                  new RFC2965VersionAttributeHandler(),
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 RFC2965DomainAttributeHandler(),
89                  new RFC2965PortAttributeHandler(),
90                  new BasicMaxAgeHandler(),
91                  new BasicSecureHandler(),
92                  new BasicCommentHandler(),
93                  new BasicExpiresHandler(
94                          datepatterns != null ? datepatterns.clone() : DATE_PATTERNS),
95                  new RFC2965CommentUrlAttributeHandler(),
96                  new RFC2965DiscardAttributeHandler());
97      }
98  
99      RFC2965Spec(final boolean oneHeader,
100                 final CommonCookieAttributeHandler... handlers) {
101         super(oneHeader, handlers);
102     }
103 
104     @Override
105     public List<Cookie> parse(
106             final Header header,
107             final CookieOrigin origin) throws MalformedCookieException {
108         Args.notNull(header, "Header");
109         Args.notNull(origin, "Cookie origin");
110         if (!header.getName().equalsIgnoreCase(SM.SET_COOKIE2)) {
111             throw new MalformedCookieException("Unrecognized cookie header '"
112                     + header.toString() + "'");
113         }
114         final HeaderElement[] elems = header.getElements();
115         return createCookies(elems, adjustEffectiveHost(origin));
116     }
117 
118     @Override
119     protected List<Cookie> parse(
120             final HeaderElement[] elems,
121             final CookieOrigin origin) throws MalformedCookieException {
122         return createCookies(elems, adjustEffectiveHost(origin));
123     }
124 
125     private List<Cookie> createCookies(
126             final HeaderElement[] elems,
127             final CookieOrigin origin) throws MalformedCookieException {
128         final List<Cookie> cookies = new ArrayList<Cookie>(elems.length);
129         for (final HeaderElement headerelement : elems) {
130             final String name = headerelement.getName();
131             final String value = headerelement.getValue();
132             if (name == null || name.isEmpty()) {
133                 throw new MalformedCookieException("Cookie name may not be empty");
134             }
135 
136             final BasicClientCookie2ntCookie2.html#BasicClientCookie2">BasicClientCookie2 cookie = new BasicClientCookie2(name, value);
137             cookie.setPath(getDefaultPath(origin));
138             cookie.setDomain(getDefaultDomain(origin));
139             cookie.setPorts(new int [] { origin.getPort() });
140             // cycle through the parameters
141             final NameValuePair[] attribs = headerelement.getParameters();
142 
143             // Eliminate duplicate attributes. The first occurrence takes precedence
144             // See RFC2965: 3.2  Origin Server Role
145             final Map<String, NameValuePair> attribmap =
146                     new HashMap<String, NameValuePair>(attribs.length);
147             for (int j = attribs.length - 1; j >= 0; j--) {
148                 final NameValuePair param = attribs[j];
149                 attribmap.put(param.getName().toLowerCase(Locale.ROOT), param);
150             }
151             for (final Map.Entry<String, NameValuePair> entry : attribmap.entrySet()) {
152                 final NameValuePair attrib = entry.getValue();
153                 final String s = attrib.getName().toLowerCase(Locale.ROOT);
154 
155                 cookie.setAttribute(s, attrib.getValue());
156 
157                 final CookieAttributeHandler handler = findAttribHandler(s);
158                 if (handler != null) {
159                     handler.parse(cookie, attrib.getValue());
160                 }
161             }
162             cookies.add(cookie);
163         }
164         return cookies;
165     }
166 
167     @Override
168     public void validate(
169             final Cookie cookie, final CookieOrigin origin) throws MalformedCookieException {
170         Args.notNull(cookie, "Cookie");
171         Args.notNull(origin, "Cookie origin");
172         super.validate(cookie, adjustEffectiveHost(origin));
173     }
174 
175     @Override
176     public boolean match(final Cookie cookie, final CookieOrigin origin) {
177         Args.notNull(cookie, "Cookie");
178         Args.notNull(origin, "Cookie origin");
179         return super.match(cookie, adjustEffectiveHost(origin));
180     }
181 
182     /**
183      * Adds valid Port attribute value, e.g. "8000,8001,8002"
184      */
185     @Override
186     protected void formatCookieAsVer(final CharArrayBuffer buffer,
187             final Cookie cookie, final int version) {
188         super.formatCookieAsVer(buffer, cookie, version);
189         // format port attribute
190         if (cookie instanceof ClientCookie) {
191             // Test if the port attribute as set by the origin server is not blank
192             final String s = ((ClientCookie) cookie).getAttribute(ClientCookie.PORT_ATTR);
193             if (s != null) {
194                 buffer.append("; $Port");
195                 buffer.append("=\"");
196                 if (!s.trim().isEmpty()) {
197                     final int[] ports = cookie.getPorts();
198                     if (ports != null) {
199                         final int len = ports.length;
200                         for (int i = 0; i < len; i++) {
201                             if (i > 0) {
202                                 buffer.append(",");
203                             }
204                             buffer.append(Integer.toString(ports[i]));
205                         }
206                     }
207                 }
208                 buffer.append("\"");
209             }
210         }
211     }
212 
213     /**
214      * Set 'effective host name' as defined in RFC 2965.
215      * <p>
216      * If a host name contains no dots, the effective host name is
217      * that name with the string .local appended to it.  Otherwise
218      * the effective host name is the same as the host name.  Note
219      * that all effective host names contain at least one dot.
220      *
221      * @param origin origin where cookie is received from or being sent to.
222      */
223     private static CookieOriginkieOrigin">CookieOrigin adjustEffectiveHost(final CookieOrigin origin) {
224         final String host = origin.getHost();
225 
226         // Test if the host name appears to be a fully qualified DNS name,
227         // IPv4 address or IPv6 address
228         boolean isLocalHost = true;
229         for (int i = 0; i < host.length(); i++) {
230             final char ch = host.charAt(i);
231             if (ch == '.' || ch == ':') {
232                 isLocalHost = false;
233                 break;
234             }
235         }
236         return isLocalHost
237                         ? new CookieOrigin(
238                                         host + ".local",
239                                         origin.getPort(),
240                                         origin.getPath(),
241                                         origin.isSecure())
242                         : origin;
243     }
244 
245     @Override
246     public int getVersion() {
247         return 1;
248     }
249 
250     @Override
251     public Header getVersionHeader() {
252         final CharArrayBuffer buffer = new CharArrayBuffer(40);
253         buffer.append(SM.COOKIE2);
254         buffer.append(": ");
255         buffer.append("$Version=");
256         buffer.append(Integer.toString(getVersion()));
257         return new BufferedHeader(buffer);
258     }
259 
260     @Override
261     public String toString() {
262         return "rfc2965";
263     }
264 
265 }
266