View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec.http;
17  
18  import java.util.Collections;
19  import java.util.Set;
20  import java.util.TreeSet;
21  
22  
23  
24  /**
25   * The default {@link Cookie} implementation.
26   */
27  public class DefaultCookie implements Cookie {
28  
29      private final String name;
30      private String value;
31      private String rawValue;
32      private String domain;
33      private String path;
34      private String comment;
35      private String commentUrl;
36      private boolean discard;
37      private Set<Integer> ports = Collections.emptySet();
38      private Set<Integer> unmodifiablePorts = ports;
39      private long maxAge = Long.MIN_VALUE;
40      private int version;
41      private boolean secure;
42      private boolean httpOnly;
43  
44      /**
45       * Creates a new cookie with the specified name and value.
46       */
47      public DefaultCookie(String name, String value) {
48          if (name == null) {
49              throw new NullPointerException("name");
50          }
51          name = name.trim();
52          if (name.isEmpty()) {
53              throw new IllegalArgumentException("empty name");
54          }
55  
56          for (int i = 0; i < name.length(); i ++) {
57              char c = name.charAt(i);
58              if (c > 127) {
59                  throw new IllegalArgumentException(
60                          "name contains non-ascii character: " + name);
61              }
62  
63              // Check prohibited characters.
64              switch (c) {
65              case '\t': case '\n': case 0x0b: case '\f': case '\r':
66              case ' ':  case ',':  case ';':  case '=':
67                  throw new IllegalArgumentException(
68                          "name contains one of the following prohibited characters: " +
69                          "=,; \\t\\r\\n\\v\\f: " + name);
70              }
71          }
72  
73          if (name.charAt(0) == '$') {
74              throw new IllegalArgumentException("name starting with '$' not allowed: " + name);
75          }
76  
77          this.name = name;
78          setValue(value);
79      }
80  
81      @Override
82      public String name() {
83          return name;
84      }
85  
86      @Override
87      public String value() {
88          return value;
89      }
90  
91      @Override
92      public void setValue(String value) {
93          if (value == null) {
94              throw new NullPointerException("value");
95          }
96          this.value = value;
97      }
98  
99      @Override
100     public String rawValue() {
101         return rawValue;
102     }
103 
104     @Override
105     public void setRawValue(String rawValue) {
106         if (value == null) {
107             throw new NullPointerException("rawValue");
108         }
109         this.rawValue = rawValue;
110     }
111 
112     @Override
113     public String domain() {
114         return domain;
115     }
116 
117     @Override
118     public void setDomain(String domain) {
119         this.domain = validateValue("domain", domain);
120     }
121 
122     @Override
123     public String path() {
124         return path;
125     }
126 
127     @Override
128     public void setPath(String path) {
129         this.path = validateValue("path", path);
130     }
131 
132     @Override
133     public String comment() {
134         return comment;
135     }
136 
137     @Override
138     public void setComment(String comment) {
139         this.comment = validateValue("comment", comment);
140     }
141 
142     @Override
143     public String commentUrl() {
144         return commentUrl;
145     }
146 
147     @Override
148     public void setCommentUrl(String commentUrl) {
149         this.commentUrl = validateValue("commentUrl", commentUrl);
150     }
151 
152     @Override
153     public boolean isDiscard() {
154         return discard;
155     }
156 
157     @Override
158     public void setDiscard(boolean discard) {
159         this.discard = discard;
160     }
161 
162     @Override
163     public Set<Integer> ports() {
164         if (unmodifiablePorts == null) {
165             unmodifiablePorts = Collections.unmodifiableSet(ports);
166         }
167         return unmodifiablePorts;
168     }
169 
170     @Override
171     public void setPorts(int... ports) {
172         if (ports == null) {
173             throw new NullPointerException("ports");
174         }
175 
176         int[] portsCopy = ports.clone();
177         if (portsCopy.length == 0) {
178             unmodifiablePorts = this.ports = Collections.emptySet();
179         } else {
180             Set<Integer> newPorts = new TreeSet<Integer>();
181             for (int p: portsCopy) {
182                 if (p <= 0 || p > 65535) {
183                     throw new IllegalArgumentException("port out of range: " + p);
184                 }
185                 newPorts.add(Integer.valueOf(p));
186             }
187             this.ports = newPorts;
188             unmodifiablePorts = null;
189         }
190     }
191 
192     @Override
193     public void setPorts(Iterable<Integer> ports) {
194         Set<Integer> newPorts = new TreeSet<Integer>();
195         for (int p: ports) {
196             if (p <= 0 || p > 65535) {
197                 throw new IllegalArgumentException("port out of range: " + p);
198             }
199             newPorts.add(Integer.valueOf(p));
200         }
201         if (newPorts.isEmpty()) {
202             unmodifiablePorts = this.ports = Collections.emptySet();
203         } else {
204             this.ports = newPorts;
205             unmodifiablePorts = null;
206         }
207     }
208 
209     @Override
210     public long maxAge() {
211         return maxAge;
212     }
213 
214     @Override
215     public void setMaxAge(long maxAge) {
216         this.maxAge = maxAge;
217     }
218 
219     @Override
220     public int version() {
221         return version;
222     }
223 
224     @Override
225     public void setVersion(int version) {
226         this.version = version;
227     }
228 
229     @Override
230     public boolean isSecure() {
231         return secure;
232     }
233 
234     @Override
235     public void setSecure(boolean secure) {
236         this.secure = secure;
237     }
238 
239     @Override
240     public boolean isHttpOnly() {
241         return httpOnly;
242     }
243 
244     @Override
245     public void setHttpOnly(boolean httpOnly) {
246         this.httpOnly = httpOnly;
247     }
248 
249     @Override
250     public int hashCode() {
251         return name().hashCode();
252     }
253 
254     @Override
255     public boolean equals(Object o) {
256         if (!(o instanceof Cookie)) {
257             return false;
258         }
259 
260         Cookie that = (Cookie) o;
261         if (!name().equalsIgnoreCase(that.name())) {
262             return false;
263         }
264 
265         if (path() == null) {
266             if (that.path() != null) {
267                 return false;
268             }
269         } else if (that.path() == null) {
270             return false;
271         } else if (!path().equals(that.path())) {
272             return false;
273         }
274 
275         if (domain() == null) {
276             if (that.domain() != null) {
277                 return false;
278             }
279         } else if (that.domain() == null) {
280             return false;
281         } else {
282             return domain().equalsIgnoreCase(that.domain());
283         }
284 
285         return true;
286     }
287 
288     @Override
289     public int compareTo(Cookie c) {
290         int v;
291         v = name().compareToIgnoreCase(c.name());
292         if (v != 0) {
293             return v;
294         }
295 
296         if (path() == null) {
297             if (c.path() != null) {
298                 return -1;
299             }
300         } else if (c.path() == null) {
301             return 1;
302         } else {
303             v = path().compareTo(c.path());
304             if (v != 0) {
305                 return v;
306             }
307         }
308 
309         if (domain() == null) {
310             if (c.domain() != null) {
311                 return -1;
312             }
313         } else if (c.domain() == null) {
314             return 1;
315         } else {
316             v = domain().compareToIgnoreCase(c.domain());
317             return v;
318         }
319 
320         return 0;
321     }
322 
323     @Override
324     public String toString() {
325         StringBuilder buf = new StringBuilder()
326             .append(name())
327             .append('=')
328             .append(value());
329         if (domain() != null) {
330             buf.append(", domain=")
331                .append(domain());
332         }
333         if (path() != null) {
334             buf.append(", path=")
335                .append(path());
336         }
337         if (comment() != null) {
338             buf.append(", comment=")
339                .append(comment());
340         }
341         if (maxAge() >= 0) {
342             buf.append(", maxAge=")
343                .append(maxAge())
344                .append('s');
345         }
346         if (isSecure()) {
347             buf.append(", secure");
348         }
349         if (isHttpOnly()) {
350             buf.append(", HTTPOnly");
351         }
352         return buf.toString();
353     }
354 
355     private static String validateValue(String name, String value) {
356         if (value == null) {
357             return null;
358         }
359         value = value.trim();
360         if (value.isEmpty()) {
361             return null;
362         }
363         for (int i = 0; i < value.length(); i ++) {
364             char c = value.charAt(i);
365             switch (c) {
366             case '\r': case '\n': case '\f': case 0x0b: case ';':
367                 throw new IllegalArgumentException(
368                         name + " contains one of the following prohibited characters: " +
369                         ";\\r\\n\\f\\v (" + value + ')');
370             }
371         }
372         return value;
373     }
374 }