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