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.List;
31  
32  import org.apache.http.FormattedHeader;
33  import org.apache.http.Header;
34  import org.apache.http.HeaderElement;
35  import org.apache.http.annotation.Contract;
36  import org.apache.http.annotation.ThreadingBehavior;
37  import org.apache.http.cookie.Cookie;
38  import org.apache.http.cookie.CookieOrigin;
39  import org.apache.http.cookie.CookieSpec;
40  import org.apache.http.cookie.MalformedCookieException;
41  import org.apache.http.cookie.SM;
42  import org.apache.http.cookie.SetCookie2;
43  import org.apache.http.message.ParserCursor;
44  import org.apache.http.util.Args;
45  import org.apache.http.util.CharArrayBuffer;
46  
47  /**
48   * Default cookie specification that picks up the best matching cookie policy based on
49   * the format of cookies sent with the HTTP response.
50   *
51   * @since 4.4
52   */
53  @Contract(threading = ThreadingBehavior.SAFE)
54  public class DefaultCookieSpec implements CookieSpec {
55  
56      private final RFC2965Spec strict;
57      private final RFC2109Spec obsoleteStrict;
58      private final NetscapeDraftSpec netscapeDraft;
59  
60      DefaultCookieSpec(
61              final RFC2965Spec strict,
62              final RFC2109Spec obsoleteStrict,
63              final NetscapeDraftSpec netscapeDraft) {
64          this.strict = strict;
65          this.obsoleteStrict = obsoleteStrict;
66          this.netscapeDraft = netscapeDraft;
67      }
68  
69      public DefaultCookieSpec(
70              final String[] datepatterns,
71              final boolean oneHeader) {
72          this.strict = new RFC2965Spec(oneHeader,
73                  new RFC2965VersionAttributeHandler(),
74                  new BasicPathHandler(),
75                  new RFC2965DomainAttributeHandler(),
76                  new RFC2965PortAttributeHandler(),
77                  new BasicMaxAgeHandler(),
78                  new BasicSecureHandler(),
79                  new BasicCommentHandler(),
80                  new RFC2965CommentUrlAttributeHandler(),
81                  new RFC2965DiscardAttributeHandler());
82          this.obsoleteStrict = new RFC2109Spec(oneHeader,
83                  new RFC2109VersionHandler(),
84                  new BasicPathHandler(),
85                  new RFC2109DomainHandler(),
86                  new BasicMaxAgeHandler(),
87                  new BasicSecureHandler(),
88                  new BasicCommentHandler());
89          this.netscapeDraft = new NetscapeDraftSpec(
90                  new BasicDomainHandler(),
91                  new BasicPathHandler(),
92                  new BasicSecureHandler(),
93                  new BasicCommentHandler(),
94                  new BasicExpiresHandler(
95                          datepatterns != null ? datepatterns.clone() : new String[]{NetscapeDraftSpec.EXPIRES_PATTERN}));
96      }
97  
98      public DefaultCookieSpec() {
99          this(null, false);
100     }
101 
102     @Override
103     public List<Cookie> parse(
104             final Header header,
105             final CookieOrigin origin) throws MalformedCookieException {
106         Args.notNull(header, "Header");
107         Args.notNull(origin, "Cookie origin");
108         HeaderElement[] hElems = header.getElements();
109         boolean versioned = false;
110         boolean netscape = false;
111         for (final HeaderElement hElem: hElems) {
112             if (hElem.getParameterByName("version") != null) {
113                 versioned = true;
114             }
115             if (hElem.getParameterByName("expires") != null) {
116                netscape = true;
117             }
118         }
119         if (netscape || !versioned) {
120             // Need to parse the header again, because Netscape style cookies do not correctly
121             // support multiple header elements (comma cannot be treated as an element separator)
122             final NetscapeDraftHeaderParser parser = NetscapeDraftHeaderParser.DEFAULT;
123             final CharArrayBuffer buffer;
124             final ParserCursor cursor;
125             if (header instanceof FormattedHeader) {
126                 buffer = ((FormattedHeader) header).getBuffer();
127                 cursor = new ParserCursor(
128                         ((FormattedHeader) header).getValuePos(),
129                         buffer.length());
130             } else {
131                 final String hValue = header.getValue();
132                 if (hValue == null) {
133                     throw new MalformedCookieException("Header value is null");
134                 }
135                 buffer = new CharArrayBuffer(hValue.length());
136                 buffer.append(hValue);
137                 cursor = new ParserCursor(0, buffer.length());
138             }
139             hElems = new HeaderElement[] { parser.parseHeader(buffer, cursor) };
140             return netscapeDraft.parse(hElems, origin);
141         }
142         return SM.SET_COOKIE2.equals(header.getName())
143                         ? strict.parse(hElems, origin)
144                         : obsoleteStrict.parse(hElems, origin);
145     }
146 
147     @Override
148     public void validate(
149             final Cookie cookie,
150             final CookieOrigin origin) throws MalformedCookieException {
151         Args.notNull(cookie, "Cookie");
152         Args.notNull(origin, "Cookie origin");
153         if (cookie.getVersion() > 0) {
154             if (cookie instanceof SetCookie2) {
155                 strict.validate(cookie, origin);
156             } else {
157                 obsoleteStrict.validate(cookie, origin);
158             }
159         } else {
160             netscapeDraft.validate(cookie, origin);
161         }
162     }
163 
164     @Override
165     public boolean match(final Cookie cookie, final CookieOrigin origin) {
166         Args.notNull(cookie, "Cookie");
167         Args.notNull(origin, "Cookie origin");
168         if (cookie.getVersion() > 0) {
169             return cookie instanceof SetCookie2
170                             ? strict.match(cookie, origin)
171                             : obsoleteStrict.match(cookie, origin);
172         }
173         return netscapeDraft.match(cookie, origin);
174     }
175 
176     @Override
177     public List<Header> formatCookies(final List<Cookie> cookies) {
178         Args.notNull(cookies, "List of cookies");
179         int version = Integer.MAX_VALUE;
180         boolean isSetCookie2 = true;
181         for (final Cookie cookie: cookies) {
182             if (!(cookie instanceof SetCookie2)) {
183                 isSetCookie2 = false;
184             }
185             if (cookie.getVersion() < version) {
186                 version = cookie.getVersion();
187             }
188         }
189         if (version > 0) {
190             return isSetCookie2
191                             ? strict.formatCookies(cookies)
192                             : obsoleteStrict.formatCookies(cookies);
193         }
194         return netscapeDraft.formatCookies(cookies);
195     }
196 
197     @Override
198     public int getVersion() {
199         return strict.getVersion();
200     }
201 
202     @Override
203     public Header getVersionHeader() {
204         return null;
205     }
206 
207     @Override
208     public String toString() {
209         return "default";
210     }
211 
212 }