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.StringTokenizer;
31  
32  import org.apache.http.annotation.Contract;
33  import org.apache.http.annotation.ThreadingBehavior;
34  import org.apache.http.cookie.ClientCookie;
35  import org.apache.http.cookie.CommonCookieAttributeHandler;
36  import org.apache.http.cookie.Cookie;
37  import org.apache.http.cookie.CookieOrigin;
38  import org.apache.http.cookie.CookieRestrictionViolationException;
39  import org.apache.http.cookie.MalformedCookieException;
40  import org.apache.http.cookie.SetCookie;
41  import org.apache.http.cookie.SetCookie2;
42  import org.apache.http.util.Args;
43  
44  /**
45   * {@code "Port"} cookie attribute handler for RFC 2965 cookie spec.
46   *
47   * @since 4.0
48   */
49  @Contract(threading = ThreadingBehavior.IMMUTABLE)
50  public class RFC2965PortAttributeHandler implements CommonCookieAttributeHandler {
51  
52      public RFC2965PortAttributeHandler() {
53          super();
54      }
55  
56      /**
57       * Parses the given Port attribute value (e.g. "8000,8001,8002")
58       * into an array of ports.
59       *
60       * @param portValue port attribute value
61       * @return parsed array of ports
62       * @throws MalformedCookieException if there is a problem in
63       *          parsing due to invalid portValue.
64       */
65      private static int[] parsePortAttribute(final String portValue)
66              throws MalformedCookieException {
67          final StringTokenizer st = new StringTokenizer(portValue, ",");
68          final int[] ports = new int[st.countTokens()];
69          try {
70              int i = 0;
71              while(st.hasMoreTokens()) {
72                  ports[i] = Integer.parseInt(st.nextToken().trim());
73                  if (ports[i] < 0) {
74                    throw new MalformedCookieException ("Invalid Port attribute.");
75                  }
76                  ++i;
77              }
78          } catch (final NumberFormatException e) {
79              throw new MalformedCookieException ("Invalid Port "
80                                                  + "attribute: " + e.getMessage());
81          }
82          return ports;
83      }
84  
85      /**
86       * Returns {@code true} if the given port exists in the given
87       * ports list.
88       *
89       * @param port port of host where cookie was received from or being sent to.
90       * @param ports port list
91       * @return true returns {@code true} if the given port exists in
92       *         the given ports list; {@code false} otherwise.
93       */
94      private static boolean portMatch(final int port, final int[] ports) {
95          boolean portInList = false;
96          for (final int port2 : ports) {
97              if (port == port2) {
98                  portInList = true;
99                  break;
100             }
101         }
102         return portInList;
103     }
104 
105     /**
106      * Parse cookie port attribute.
107      */
108     @Override
109     public void parse(final SetCookie cookie, final String portValue)
110             throws MalformedCookieException {
111         Args.notNull(cookie, "Cookie");
112         if (cookie instanceof SetCookie2) {
113             final SetCookie2 cookie2 = (SetCookie2) cookie;
114             if (portValue != null && !portValue.trim().isEmpty()) {
115                 final int[] ports = parsePortAttribute(portValue);
116                 cookie2.setPorts(ports);
117             }
118         }
119     }
120 
121     /**
122      * Validate cookie port attribute. If the Port attribute was specified
123      * in header, the request port must be in cookie's port list.
124      */
125     @Override
126     public void validate(final Cookie cookie, final CookieOrigin origin)
127             throws MalformedCookieException {
128         Args.notNull(cookie, "Cookie");
129         Args.notNull(origin, "Cookie origin");
130         final int port = origin.getPort();
131         if (cookie instanceof ClientCookie
132                 && ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) {
133             if (!portMatch(port, cookie.getPorts())) {
134                 throw new CookieRestrictionViolationException(
135                         "Port attribute violates RFC 2965: "
136                         + "Request port not found in cookie's port list.");
137             }
138         }
139     }
140 
141     /**
142      * Match cookie port attribute. If the Port attribute is not specified
143      * in header, the cookie can be sent to any port. Otherwise, the request port
144      * must be in the cookie's port list.
145      */
146     @Override
147     public boolean match(final Cookie cookie, final CookieOrigin origin) {
148         Args.notNull(cookie, "Cookie");
149         Args.notNull(origin, "Cookie origin");
150         final int port = origin.getPort();
151         if (cookie instanceof ClientCookie
152                 && ((ClientCookie) cookie).containsAttribute(ClientCookie.PORT_ATTR)) {
153             if (cookie.getPorts() == null) {
154                 // Invalid cookie state: port not specified
155                 return false;
156             }
157             if (!portMatch(port, cookie.getPorts())) {
158                 return false;
159             }
160         }
161         return true;
162     }
163 
164     @Override
165     public String getAttributeName() {
166         return ClientCookie.PORT_ATTR;
167     }
168 
169 }