Author: markt Date: Tue Jul 9 21:01:37 2013 New Revision: 1501548 URL: http://svn.apache.org/r1501548 Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=55221 Truncate excessively long clean reasons before sending as control messages are strictly limited in length.
Added: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java (with props) Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java?rev=1501548&r1=1501547&r2=1501548&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java Tue Jul 9 21:01:37 2013 @@ -48,6 +48,12 @@ import org.apache.tomcat.util.res.String public class WsSession implements Session { private static final Charset UTF8 = Charset.forName("UTF8"); + // An ellipsis is a single character that looks like three periods in a row + // and is used to indicate a continuation. + private static final byte[] ELLIPSIS_BYTES = "\u2026".getBytes(UTF8); + // An ellipsis is three bytes in UTF-8 + private static final int ELLIPSIS_BYTES_LEN = ELLIPSIS_BYTES.length; + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); private static AtomicLong ids = new AtomicLong(0); @@ -450,9 +456,10 @@ public class WsSession implements Sessio // 125 is maximum size for the payload of a control message ByteBuffer msg = ByteBuffer.allocate(125); msg.putShort((short) closeReason.getCloseCode().getCode()); + String reason = closeReason.getReasonPhrase(); if (reason != null && reason.length() > 0) { - msg.put(reason.getBytes(UTF8)); + appendCloseReasonWithTruncation(msg, reason); } msg.flip(); try { @@ -470,6 +477,35 @@ public class WsSession implements Sessio } + /** + * Use protected so unit tests can access this method directly. + */ + protected static void appendCloseReasonWithTruncation(ByteBuffer msg, + String reason) { + // Once the close code has been added there are a maximum of 123 bytes + // left for the reason phrase. If it is truncated then care needs to be + // taken to ensure the bytes are not truncated in the middle of a + // multi-byte UTF-8 character. + byte[] reasonBytes = reason.getBytes(UTF8); + + if (reasonBytes.length <= 123) { + // No need to truncate + msg.put(reasonBytes); + } else { + // Need to truncate + int remaining = 123 - ELLIPSIS_BYTES_LEN; + int pos = 0; + byte[] bytesNext = reason.substring(pos, pos + 1).getBytes(UTF8); + while (remaining >= bytesNext.length) { + msg.put(bytesNext); + remaining -= bytesNext.length; + pos++; + bytesNext = reason.substring(pos, pos + 1).getBytes(UTF8); + } + msg.put(ELLIPSIS_BYTES); + } + } + @Override public URI getRequestURI() { checkState(); Added: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java?rev=1501548&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java (added) +++ tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java Tue Jul 9 21:01:37 2013 @@ -0,0 +1,107 @@ +/* + * 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.websocket; + +import java.nio.ByteBuffer; + +import org.junit.Assert; +import org.junit.Test; + +public class TestWsSession { + + @Test + public void testAppendCloseReasonWithTruncation01() { + doTestAppendCloseReasonWithTruncation(100); + } + + + @Test + public void testAppendCloseReasonWithTruncation02() { + doTestAppendCloseReasonWithTruncation(119); + } + + + @Test + public void testAppendCloseReasonWithTruncation03() { + doTestAppendCloseReasonWithTruncation(120); + } + + + @Test + public void testAppendCloseReasonWithTruncation04() { + doTestAppendCloseReasonWithTruncation(121); + } + + + @Test + public void testAppendCloseReasonWithTruncation05() { + doTestAppendCloseReasonWithTruncation(122); + } + + + @Test + public void testAppendCloseReasonWithTruncation06() { + doTestAppendCloseReasonWithTruncation(123); + } + + + @Test + public void testAppendCloseReasonWithTruncation07() { + doTestAppendCloseReasonWithTruncation(124); + } + + + @Test + public void testAppendCloseReasonWithTruncation08() { + doTestAppendCloseReasonWithTruncation(125); + } + + + @Test + public void testAppendCloseReasonWithTruncation09() { + doTestAppendCloseReasonWithTruncation(150); + } + + + private void doTestAppendCloseReasonWithTruncation(int reasonLength) { + StringBuilder reason = new StringBuilder(reasonLength); + for (int i = 0; i < reasonLength; i++) { + reason.append('a'); + } + + ByteBuffer buf = ByteBuffer.allocate(256); + + WsSession.appendCloseReasonWithTruncation(buf, reason.toString()); + + // Check the position and contents + if (reasonLength <= 123) { + Assert.assertEquals(reasonLength, buf.position()); + for (int i = 0; i < reasonLength; i++) { + Assert.assertEquals('a', buf.get(i)); + } + } else { + // Must have been truncated + Assert.assertEquals(123, buf.position()); + for (int i = 0; i < 120; i++) { + Assert.assertEquals('a', buf.get(i)); + } + Assert.assertEquals(0xE2, buf.get(120) & 0xFF); + Assert.assertEquals(0x80, buf.get(121) & 0xFF); + Assert.assertEquals(0xA6, buf.get(122) & 0xFF); + } + } +} Propchange: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsSession.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org