View Javadoc

    1   /*
    2    * Copyright 2009 Red Hat, Inc.
    3    *
    4    * Red Hat licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the
    6    * 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 org.jboss.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   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
    28   * @author Andy Taylor (andy.taylor@jboss.org)
    29   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
    30   * @version $Rev: 2080 $, $Date: 2010-01-26 18:04:19 +0900 (Tue, 26 Jan 2010) $
    31   */
    32  public class DefaultCookie implements Cookie {
    33
    34      private static final Set<String> RESERVED_NAMES = new TreeSet<String>(CaseIgnoringComparator.INSTANCE);
    35
    36      static {
    37          RESERVED_NAMES.add("Domain");
    38          RESERVED_NAMES.add("Path");
    39          RESERVED_NAMES.add("Comment");
    40          RESERVED_NAMES.add("CommentURL");
    41          RESERVED_NAMES.add("Discard");
    42          RESERVED_NAMES.add("Port");
    43          RESERVED_NAMES.add("Max-Age");
    44          RESERVED_NAMES.add("Expires");
    45          RESERVED_NAMES.add("Version");
    46          RESERVED_NAMES.add("Secure");
    47          RESERVED_NAMES.add("HTTPOnly");
    48      }
    49
    50      private final String name;
    51      private String value;
    52      private String domain;
    53      private String path;
    54      private String comment;
    55      private String commentUrl;
    56      private boolean discard;
    57      private Set<Integer> ports = Collections.emptySet();
    58      private Set<Integer> unmodifiablePorts = ports;
    59      private int maxAge = -1;
    60      private int version;
    61      private boolean secure;
    62      private boolean httpOnly;
    63
    64      /**
    65       * Creates a new cookie with the specified name and value.
    66       */
    67      public DefaultCookie(String name, String value) {
    68          if (name == null) {
    69              throw new NullPointerException("name");
    70          }
    71          name = name.trim();
    72          if (name.length() == 0) {
    73              throw new IllegalArgumentException("empty name");
    74          }
    75
    76          for (int i = 0; i < name.length(); i ++) {
    77              char c = name.charAt(i);
    78              if (c > 127) {
    79                  throw new IllegalArgumentException(
    80                          "name contains non-ascii character: " + name);
    81              }
    82
    83              // Check prohibited characters.
    84              switch (c) {
    85              case '\t': case '\n': case 0x0b: case '\f': case '\r':
    86              case ' ':  case ',':  case ';':  case '=':
    87                  throw new IllegalArgumentException(
    88                          "name contains one of the following prohibited characters: " +
    89                          "=,; \\t\\r\\n\\v\\f: " + name);
    90              }
    91          }
    92
    93          if (RESERVED_NAMES.contains(name)) {
    94              throw new IllegalArgumentException("reserved name: " + name);
    95          }
    96
    97          this.name = name;
    98          setValue(value);
    99      }
    100
    101     public String getName() {
    102         return name;
    103     }
    104
    105     public String getValue() {
    106         return value;
    107     }
    108
    109     public void setValue(String value) {
    110         if (value == null) {
    111             throw new NullPointerException("value");
    112         }
    113         this.value = value;
    114     }
    115
    116     public String getDomain() {
    117         return domain;
    118     }
    119
    120     public void setDomain(String domain) {
    121         this.domain = validateValue("domain", domain);
    122     }
    123
    124     public String getPath() {
    125         return path;
    126     }
    127
    128     public void setPath(String path) {
    129         this.path = validateValue("path", path);
    130     }
    131
    132     public String getComment() {
    133         return comment;
    134     }
    135
    136     public void setComment(String comment) {
    137         this.comment = validateValue("comment", comment);
    138     }
    139
    140     public String getCommentUrl() {
    141         return commentUrl;
    142     }
    143
    144     public void setCommentUrl(String commentUrl) {
    145         this.commentUrl = validateValue("commentUrl", commentUrl);
    146     }
    147
    148     public boolean isDiscard() {
    149         return discard;
    150     }
    151
    152     public void setDiscard(boolean discard) {
    153         this.discard = discard;
    154     }
    155
    156     public Set<Integer> getPorts() {
    157         if (unmodifiablePorts == null) {
    158             unmodifiablePorts = Collections.unmodifiableSet(ports);
    159         }
    160         return unmodifiablePorts;
    161     }
    162
    163     public void setPorts(int... ports) {
    164         if (ports == null) {
    165             throw new NullPointerException("ports");
    166         }
    167
    168         int[] portsCopy = ports.clone();
    169         if (portsCopy.length == 0) {
    170             unmodifiablePorts = this.ports = Collections.emptySet();
    171         } else {
    172             Set<Integer> newPorts = new TreeSet<Integer>();
    173             for (int p: portsCopy) {
    174                 if (p <= 0 || p > 65535) {
    175                     throw new IllegalArgumentException("port out of range: " + p);
    176                 }
    177                 newPorts.add(Integer.valueOf(p));
    178             }
    179             this.ports = newPorts;
    180             unmodifiablePorts = null;
    181         }
    182     }
    183
    184     public void setPorts(Iterable<Integer> ports) {
    185         Set<Integer> newPorts = new TreeSet<Integer>();
    186         for (int p: ports) {
    187             if (p <= 0 || p > 65535) {
    188                 throw new IllegalArgumentException("port out of range: " + p);
    189             }
    190             newPorts.add(Integer.valueOf(p));
    191         }
    192         if (newPorts.isEmpty()) {
    193             unmodifiablePorts = this.ports = Collections.emptySet();
    194         } else {
    195             this.ports = newPorts;
    196             unmodifiablePorts = null;
    197         }
    198     }
    199
    200     public int getMaxAge() {
    201         return maxAge;
    202     }
    203
    204     public void setMaxAge(int maxAge) {
    205         if (maxAge < -1) {
    206             throw new IllegalArgumentException(
    207                     "maxAge must be either -1, 0, or a positive integer: " +
    208                     maxAge);
    209         }
    210         this.maxAge = maxAge;
    211     }
    212
    213     public int getVersion() {
    214         return version;
    215     }
    216
    217     public void setVersion(int version) {
    218         this.version = version;
    219     }
    220
    221     public boolean isSecure() {
    222         return secure;
    223     }
    224
    225     public void setSecure(boolean secure) {
    226         this.secure = secure;
    227     }
    228
    229     public boolean isHttpOnly() {
    230         return httpOnly;
    231     }
    232
    233     public void setHttpOnly(boolean httpOnly) {
    234         this.httpOnly = httpOnly;
    235     }
    236
    237     @Override
    238     public int hashCode() {
    239         return getName().hashCode();
    240     }
    241
    242     @Override
    243     public boolean equals(Object o) {
    244         if (!(o instanceof Cookie)) {
    245             return false;
    246         }
    247
    248         Cookie that = (Cookie) o;
    249         if (!getName().equalsIgnoreCase(that.getName())) {
    250             return false;
    251         }
    252
    253         if (getPath() == null && that.getPath() != null) {
    254             return false;
    255         } else if (that.getPath() == null) {
    256             return false;
    257         }
    258         if (!getPath().equals(that.getPath())) {
    259             return false;
    260         }
    261
    262         if (getDomain() == null && that.getDomain() != null) {
    263             return false;
    264         } else if (that.getDomain() == null) {
    265             return false;
    266         }
    267         if (!getDomain().equalsIgnoreCase(that.getDomain())) {
    268             return false;
    269         }
    270
    271         return true;
    272     }
    273
    274     public int compareTo(Cookie c) {
    275         int v;
    276         v = getName().compareToIgnoreCase(c.getName());
    277         if (v != 0) {
    278             return v;
    279         }
    280
    281         if (getPath() == null && c.getPath() != null) {
    282             return -1;
    283         } else if (c.getPath() == null) {
    284             return 1;
    285         }
    286         v = getPath().compareTo(c.getPath());
    287         if (v != 0) {
    288             return v;
    289         }
    290
    291         if (getDomain() == null && c.getDomain() != null) {
    292             return -1;
    293         } else if (c.getDomain() == null) {
    294             return 1;
    295         }
    296         v = getDomain().compareToIgnoreCase(c.getDomain());
    297         return v;
    298     }
    299
    300     @Override
    301     public String toString() {
    302         StringBuilder buf = new StringBuilder();
    303         buf.append(getName());
    304         buf.append('=');
    305         buf.append(getValue());
    306         if (getDomain() != null) {
    307             buf.append(", domain=");
    308             buf.append(getDomain());
    309         }
    310         if (getPath() != null) {
    311             buf.append(", path=");
    312             buf.append(getPath());
    313         }
    314         if (getComment() != null) {
    315             buf.append(", comment=");
    316             buf.append(getComment());
    317         }
    318         if (getMaxAge() >= 0) {
    319             buf.append(", maxAge=");
    320             buf.append(getMaxAge());
    321             buf.append('s');
    322         }
    323         if (isSecure()) {
    324             buf.append(", secure");
    325         }
    326         if (isHttpOnly()) {
    327             buf.append(", HTTPOnly");
    328         }
    329         return buf.toString();
    330     }
    331
    332     private static String validateValue(String name, String value) {
    333         if (value == null) {
    334             return null;
    335         }
    336         value = value.trim();
    337         if (value.length() == 0) {
    338             return null;
    339         }
    340         for (int i = 0; i < value.length(); i ++) {
    341             char c = value.charAt(i);
    342             switch (c) {
    343             case '\r': case '\n': case '\f': case 0x0b: case ';':
    344                 throw new IllegalArgumentException(
    345                         name + " contains one of the following prohibited characters: " +
    346                         ";\\r\\n\\f\\v (" + value + ')');
    347             }
    348         }
    349         return value;
    350     }
    351 }