001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.impl.cookie; 029 030import java.util.StringTokenizer; 031 032import org.apache.http.annotation.Contract; 033import org.apache.http.annotation.ThreadingBehavior; 034import org.apache.http.cookie.ClientCookie; 035import org.apache.http.cookie.CommonCookieAttributeHandler; 036import org.apache.http.cookie.Cookie; 037import org.apache.http.cookie.CookieOrigin; 038import org.apache.http.cookie.CookieRestrictionViolationException; 039import org.apache.http.cookie.MalformedCookieException; 040import org.apache.http.cookie.SetCookie; 041import org.apache.http.cookie.SetCookie2; 042import org.apache.http.util.Args; 043 044/** 045 * {@code "Port"} cookie attribute handler for RFC 2965 cookie spec. 046 * 047 * @since 4.0 048 */ 049@Contract(threading = ThreadingBehavior.IMMUTABLE) 050public class RFC2965PortAttributeHandler implements CommonCookieAttributeHandler { 051 052 public RFC2965PortAttributeHandler() { 053 super(); 054 } 055 056 /** 057 * Parses the given Port attribute value (e.g. "8000,8001,8002") 058 * into an array of ports. 059 * 060 * @param portValue port attribute value 061 * @return parsed array of ports 062 * @throws MalformedCookieException if there is a problem in 063 * parsing due to invalid portValue. 064 */ 065 private static int[] parsePortAttribute(final String portValue) 066 throws MalformedCookieException { 067 final StringTokenizer st = new StringTokenizer(portValue, ","); 068 final int[] ports = new int[st.countTokens()]; 069 try { 070 int i = 0; 071 while(st.hasMoreTokens()) { 072 ports[i] = Integer.parseInt(st.nextToken().trim()); 073 if (ports[i] < 0) { 074 throw new MalformedCookieException ("Invalid Port attribute."); 075 } 076 ++i; 077 } 078 } catch (final NumberFormatException e) { 079 throw new MalformedCookieException ("Invalid Port " 080 + "attribute: " + e.getMessage()); 081 } 082 return ports; 083 } 084 085 /** 086 * Returns {@code true} if the given port exists in the given 087 * ports list. 088 * 089 * @param port port of host where cookie was received from or being sent to. 090 * @param ports port list 091 * @return true returns {@code true} if the given port exists in 092 * the given ports list; {@code false} otherwise. 093 */ 094 private static boolean portMatch(final int port, final int[] ports) { 095 boolean portInList = false; 096 for (final int port2 : ports) { 097 if (port == port2) { 098 portInList = true; 099 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}