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 s = header.getValue();
132                 if (s == null) {
133                     throw new MalformedCookieException("Header value is null");
134                 }
135                 buffer = new CharArrayBuffer(s.length());
136                 buffer.append(s);
137                 cursor = new ParserCursor(0, buffer.length());
138             }
139             helems = new HeaderElement[] { parser.parseHeader(buffer, cursor) };
140             return netscapeDraft.parse(helems, origin);
141         } else {
142             if (SM.SET_COOKIE2.equals(header.getName())) {
143                 return strict.parse(helems, origin);
144             } else {
145                 return obsoleteStrict.parse(helems, origin);
146             }
147         }
148     }
149 
150     @Override
151     public void validate(
152             final Cookie cookie,
153             final CookieOrigin origin) throws MalformedCookieException {
154         Args.notNull(cookie, "Cookie");
155         Args.notNull(origin, "Cookie origin");
156         if (cookie.getVersion() > 0) {
157             if (cookie instanceof SetCookie2) {
158                 strict.validate(cookie, origin);
159             } else {
160                 obsoleteStrict.validate(cookie, origin);
161             }
162         } else {
163             netscapeDraft.validate(cookie, origin);
164         }
165     }
166 
167     @Override
168     public boolean match(final Cookie cookie, final CookieOrigin origin) {
169         Args.notNull(cookie, "Cookie");
170         Args.notNull(origin, "Cookie origin");
171         if (cookie.getVersion() > 0) {
172             if (cookie instanceof SetCookie2) {
173                 return strict.match(cookie, origin);
174             } else {
175                 return obsoleteStrict.match(cookie, origin);
176             }
177         } else {
178             return netscapeDraft.match(cookie, origin);
179         }
180     }
181 
182     @Override
183     public List<Header> formatCookies(final List<Cookie> cookies) {
184         Args.notNull(cookies, "List of cookies");
185         int version = Integer.MAX_VALUE;
186         boolean isSetCookie2 = true;
187         for (final Cookie cookie: cookies) {
188             if (!(cookie instanceof SetCookie2)) {
189                 isSetCookie2 = false;
190             }
191             if (cookie.getVersion() < version) {
192                 version = cookie.getVersion();
193             }
194         }
195         if (version > 0) {
196             if (isSetCookie2) {
197                 return strict.formatCookies(cookies);
198             } else {
199                 return obsoleteStrict.formatCookies(cookies);
200             }
201         } else {
202             return netscapeDraft.formatCookies(cookies);
203         }
204     }
205 
206     @Override
207     public int getVersion() {
208         return strict.getVersion();
209     }
210 
211     @Override
212     public Header getVersionHeader() {
213         return null;
214     }
215 
216     @Override
217     public String toString() {
218         return "default";
219     }
220 
221 }