Author: jboynes Date: Tue Jan 7 19:21:20 2014 New Revision: 1556328 URL: http://svn.apache.org/r1556328 Log: Refactor ServerCookie to separate cookie state from the helper code used to generate the header. This change is purely a movement of code to improve readability.
ServerCookie is now a pure data object holding MessageBytes, typically resulting from the parse of the Cookie header done by Cookies. The appendCookieValue static helper method is moved to a new class, SetCookieSupport, that Response uses when addCookie is called. Added: tomcat/trunk/java/org/apache/tomcat/util/http/SetCookieSupport.java (with props) Modified: tomcat/trunk/java/org/apache/catalina/connector/Response.java tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java Modified: tomcat/trunk/java/org/apache/catalina/connector/Response.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/Response.java?rev=1556328&r1=1556327&r2=1556328&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/connector/Response.java (original) +++ tomcat/trunk/java/org/apache/catalina/connector/Response.java Tue Jan 7 19:21:20 2014 @@ -49,7 +49,7 @@ import org.apache.tomcat.util.buf.CharCh import org.apache.tomcat.util.buf.UEncoder; import org.apache.tomcat.util.http.FastHttpDateFormat; import org.apache.tomcat.util.http.MimeHeaders; -import org.apache.tomcat.util.http.ServerCookie; +import org.apache.tomcat.util.http.SetCookieSupport; import org.apache.tomcat.util.http.parser.MediaTypeCache; import org.apache.tomcat.util.net.URL; import org.apache.tomcat.util.res.StringManager; @@ -913,21 +913,21 @@ public class Response AccessController.doPrivileged(new PrivilegedAction<Void>() { @Override public Void run(){ - ServerCookie.appendCookieValue - (sb, cookie.getVersion(), cookie.getName(), - cookie.getValue(), cookie.getPath(), - cookie.getDomain(), cookie.getComment(), - cookie.getMaxAge(), cookie.getSecure(), - cookie.isHttpOnly()); + SetCookieSupport.appendCookieValue + (sb, cookie.getVersion(), cookie.getName(), + cookie.getValue(), cookie.getPath(), + cookie.getDomain(), cookie.getComment(), + cookie.getMaxAge(), cookie.getSecure(), + cookie.isHttpOnly()); return null; } }); } else { - ServerCookie.appendCookieValue - (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(), - cookie.getPath(), cookie.getDomain(), cookie.getComment(), - cookie.getMaxAge(), cookie.getSecure(), - cookie.isHttpOnly()); + SetCookieSupport.appendCookieValue + (sb, cookie.getVersion(), cookie.getName(), cookie.getValue(), + cookie.getPath(), cookie.getDomain(), cookie.getComment(), + cookie.getMaxAge(), cookie.getSecure(), + cookie.isHttpOnly()); } return sb; } Modified: tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java?rev=1556328&r1=1556327&r2=1556328&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java (original) +++ tomcat/trunk/java/org/apache/tomcat/util/http/ServerCookie.java Tue Jan 7 19:21:20 2014 @@ -17,12 +17,6 @@ package org.apache.tomcat.util.http; import java.io.Serializable; -import java.text.DateFormat; -import java.text.FieldPosition; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; import org.apache.tomcat.util.buf.MessageBytes; @@ -51,30 +45,9 @@ public class ServerCookie implements Ser private final MessageBytes comment=MessageBytes.newInstance(); private int version = 0; - // Other fields - private static final String OLD_COOKIE_PATTERN = - "EEE, dd-MMM-yyyy HH:mm:ss z"; - private static final ThreadLocal<DateFormat> OLD_COOKIE_FORMAT = - new ThreadLocal<DateFormat>() { - @Override - protected DateFormat initialValue() { - DateFormat df = - new SimpleDateFormat(OLD_COOKIE_PATTERN, Locale.US); - df.setTimeZone(TimeZone.getTimeZone("GMT")); - return df; - } - }; - private static final String ancientDate; + // Note: Servlet Spec =< 3.0 only refers to Netscape and RFC2109, not RFC2965 - static { - ancientDate = OLD_COOKIE_FORMAT.get().format(new Date(10000)); - } - - // Note: Servlet Spec =< 3.0 only refers to Netscape and RFC2109, - // not RFC2965 - - // Version 2 (RFC2965) attributes that would need to be added to support - // v2 cookies + // Version 2 (RFC2965) attributes that would need to be added to support v2 cookies // CommentURL // Discard - implied by maxAge <0 // Port @@ -129,192 +102,5 @@ public class ServerCookie implements Ser return "Cookie " + getName() + "=" + getValue() + " ; " + getVersion() + " " + getPath() + " " + getDomain(); } - - // -------------------- Cookie parsing tools - - - public static void appendCookieValue( StringBuffer headerBuf, - int version, - String name, - String value, - String path, - String domain, - String comment, - int maxAge, - boolean isSecure, - boolean isHttpOnly) - { - StringBuffer buf = new StringBuffer(); - // Servlet implementation checks name - buf.append( name ); - buf.append("="); - // Servlet implementation does not check anything else - - /* - * The spec allows some latitude on when to send the version attribute - * with a Set-Cookie header. To be nice to clients, we'll make sure the - * version attribute is first. That means checking the various things - * that can cause us to switch to a v1 cookie first. - * - * Note that by checking for tokens we will also throw an exception if a - * control character is encountered. - */ - // Start by using the version we were asked for - int newVersion = version; - - // If it is v0, check if we need to switch - if (newVersion == 0 && - (!CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && - CookieSupport.isHttpToken(value) || - CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && - CookieSupport.isV0Token(value))) { - // HTTP token in value - need to use v1 - newVersion = 1; - } - - if (newVersion == 0 && comment != null) { - // Using a comment makes it a v1 cookie - newVersion = 1; - } - - if (newVersion == 0 && - (!CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && - CookieSupport.isHttpToken(path) || - CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && - CookieSupport.isV0Token(path))) { - // HTTP token in path - need to use v1 - newVersion = 1; - } - - if (newVersion == 0 && - (!CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && - CookieSupport.isHttpToken(domain) || - CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && - CookieSupport.isV0Token(domain))) { - // HTTP token in domain - need to use v1 - newVersion = 1; - } - - // Now build the cookie header - // Value - maybeQuote(buf, value); - // Add version 1 specific information - if (newVersion == 1) { - // Version=1 ... required - buf.append ("; Version=1"); - - // Comment=comment - if ( comment!=null ) { - buf.append ("; Comment="); - maybeQuote(buf, comment); - } - } - - // Add domain information, if present - if (domain!=null) { - buf.append("; Domain="); - maybeQuote(buf, domain); - } - - // Max-Age=secs ... or use old "Expires" format - if (maxAge >= 0) { - if (newVersion > 0) { - buf.append ("; Max-Age="); - buf.append (maxAge); - } - // IE6, IE7 and possibly other browsers don't understand Max-Age. - // They do understand Expires, even with V1 cookies! - if (newVersion == 0 || CookieSupport.ALWAYS_ADD_EXPIRES) { - // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format ) - buf.append ("; Expires="); - // To expire immediately we need to set the time in past - if (maxAge == 0) { - buf.append( ancientDate ); - } else { - OLD_COOKIE_FORMAT.get().format( - new Date(System.currentTimeMillis() + - maxAge*1000L), - buf, new FieldPosition(0)); - } - } - } - - // Path=path - if (path!=null) { - buf.append ("; Path="); - maybeQuote(buf, path); - } - - // Secure - if (isSecure) { - buf.append ("; Secure"); - } - - // HttpOnly - if (isHttpOnly) { - buf.append("; HttpOnly"); - } - headerBuf.append(buf); - } - - /** - * Quotes values if required. - * @param buf - * @param value - */ - private static void maybeQuote (StringBuffer buf, String value) { - if (value==null || value.length()==0) { - buf.append("\"\""); - } else if (CookieSupport.alreadyQuoted(value)) { - buf.append('"'); - buf.append(escapeDoubleQuotes(value,1,value.length()-1)); - buf.append('"'); - } else if (CookieSupport.isHttpToken(value) && - !CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 || - CookieSupport.isV0Token(value) && - CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0) { - buf.append('"'); - buf.append(escapeDoubleQuotes(value,0,value.length())); - buf.append('"'); - } else { - buf.append(value); - } - } - - - /** - * Escapes any double quotes in the given string. - * - * @param s the input string - * @param beginIndex start index inclusive - * @param endIndex exclusive - * @return The (possibly) escaped string - */ - private static String escapeDoubleQuotes(String s, int beginIndex, int endIndex) { - - if (s == null || s.length() == 0 || s.indexOf('"') == -1) { - return s; - } - - StringBuffer b = new StringBuffer(); - for (int i = beginIndex; i < endIndex; i++) { - char c = s.charAt(i); - if (c == '\\' ) { - b.append(c); - //ignore the character after an escape, just append it - if (++i>=endIndex) { - throw new IllegalArgumentException("Invalid escape character in cookie value."); - } - b.append(s.charAt(i)); - } else if (c == '"') { - b.append('\\').append('"'); - } else { - b.append(c); - } - } - - return b.toString(); - } - } Added: tomcat/trunk/java/org/apache/tomcat/util/http/SetCookieSupport.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/http/SetCookieSupport.java?rev=1556328&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/util/http/SetCookieSupport.java (added) +++ tomcat/trunk/java/org/apache/tomcat/util/http/SetCookieSupport.java Tue Jan 7 19:21:20 2014 @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.http; + +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +/** + * Support class for generating Set-Cookie header values. + */ +public class SetCookieSupport { + // Other fields + private static final String OLD_COOKIE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z"; + private static final ThreadLocal<DateFormat> OLD_COOKIE_FORMAT = + new ThreadLocal<DateFormat>() { + @Override + protected DateFormat initialValue() { + DateFormat df = + new SimpleDateFormat(OLD_COOKIE_PATTERN, Locale.US); + df.setTimeZone(TimeZone.getTimeZone("GMT")); + return df; + } + }; + private static final String ancientDate; + + static { + ancientDate = OLD_COOKIE_FORMAT.get().format(new Date(10000)); + } + + public static void appendCookieValue( StringBuffer headerBuf, + int version, + String name, + String value, + String path, + String domain, + String comment, + int maxAge, + boolean isSecure, + boolean isHttpOnly) + { + StringBuffer buf = new StringBuffer(); + // Servlet implementation checks name + buf.append( name ); + buf.append("="); + // Servlet implementation does not check anything else + + /* + * The spec allows some latitude on when to send the version attribute + * with a Set-Cookie header. To be nice to clients, we'll make sure the + * version attribute is first. That means checking the various things + * that can cause us to switch to a v1 cookie first. + * + * Note that by checking for tokens we will also throw an exception if a + * control character is encountered. + */ + // Start by using the version we were asked for + int newVersion = version; + + // If it is v0, check if we need to switch + if (newVersion == 0 && + (!CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && + CookieSupport.isHttpToken(value) || + CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && + CookieSupport.isV0Token(value))) { + // HTTP token in value - need to use v1 + newVersion = 1; + } + + if (newVersion == 0 && comment != null) { + // Using a comment makes it a v1 cookie + newVersion = 1; + } + + if (newVersion == 0 && + (!CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && + CookieSupport.isHttpToken(path) || + CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && + CookieSupport.isV0Token(path))) { + // HTTP token in path - need to use v1 + newVersion = 1; + } + + if (newVersion == 0 && + (!CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && + CookieSupport.isHttpToken(domain) || + CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 && + CookieSupport.isV0Token(domain))) { + // HTTP token in domain - need to use v1 + newVersion = 1; + } + + // Now build the cookie header + // Value + maybeQuote(buf, value); + // Add version 1 specific information + if (newVersion == 1) { + // Version=1 ... required + buf.append ("; Version=1"); + + // Comment=comment + if ( comment!=null ) { + buf.append ("; Comment="); + maybeQuote(buf, comment); + } + } + + // Add domain information, if present + if (domain!=null) { + buf.append("; Domain="); + maybeQuote(buf, domain); + } + + // Max-Age=secs ... or use old "Expires" format + if (maxAge >= 0) { + if (newVersion > 0) { + buf.append ("; Max-Age="); + buf.append (maxAge); + } + // IE6, IE7 and possibly other browsers don't understand Max-Age. + // They do understand Expires, even with V1 cookies! + if (newVersion == 0 || CookieSupport.ALWAYS_ADD_EXPIRES) { + // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format ) + buf.append ("; Expires="); + // To expire immediately we need to set the time in past + if (maxAge == 0) { + buf.append( ancientDate ); + } else { + OLD_COOKIE_FORMAT.get().format( + new Date(System.currentTimeMillis() + + maxAge*1000L), + buf, new FieldPosition(0)); + } + } + } + + // Path=path + if (path!=null) { + buf.append ("; Path="); + maybeQuote(buf, path); + } + + // Secure + if (isSecure) { + buf.append ("; Secure"); + } + + // HttpOnly + if (isHttpOnly) { + buf.append("; HttpOnly"); + } + headerBuf.append(buf); + } + + /** + * Quotes values if required. + * @param buf + * @param value + */ + private static void maybeQuote (StringBuffer buf, String value) { + if (value==null || value.length()==0) { + buf.append("\"\""); + } else if (CookieSupport.alreadyQuoted(value)) { + buf.append('"'); + buf.append(escapeDoubleQuotes(value,1,value.length()-1)); + buf.append('"'); + } else if (CookieSupport.isHttpToken(value) && + !CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0 || + CookieSupport.isV0Token(value) && + CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0) { + buf.append('"'); + buf.append(escapeDoubleQuotes(value,0,value.length())); + buf.append('"'); + } else { + buf.append(value); + } + } + + /** + * Escapes any double quotes in the given string. + * + * @param s the input string + * @param beginIndex start index inclusive + * @param endIndex exclusive + * @return The (possibly) escaped string + */ + private static String escapeDoubleQuotes(String s, int beginIndex, int endIndex) { + + if (s == null || s.length() == 0 || s.indexOf('"') == -1) { + return s; + } + + StringBuffer b = new StringBuffer(); + for (int i = beginIndex; i < endIndex; i++) { + char c = s.charAt(i); + if (c == '\\' ) { + b.append(c); + //ignore the character after an escape, just append it + if (++i>=endIndex) { + throw new IllegalArgumentException("Invalid escape character in cookie value."); + } + b.append(s.charAt(i)); + } else if (c == '"') { + b.append('\\').append('"'); + } else { + b.append(c); + } + } + + return b.toString(); + } +} Propchange: tomcat/trunk/java/org/apache/tomcat/util/http/SetCookieSupport.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org