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 }
170