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