Skip to content

Commit da2c30c

Browse files
committed
TextMessage.toString() does not throw StringIndexOutOfBoundsException for payload with multibyte characters
Issue: SPR-12307 (cherry picked from commit da14aee)
1 parent a7eeeda commit da2c30c

File tree

7 files changed

+71
-28
lines changed

7 files changed

+71
-28
lines changed

spring-websocket/src/main/java/org/springframework/web/socket/AbstractWebSocketMessage.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,6 @@ public boolean isLast() {
6969
return this.last;
7070
}
7171

72-
@Override
73-
public int hashCode() {
74-
return AbstractWebSocketMessage.class.hashCode() * 13 + ObjectUtils.nullSafeHashCode(this.payload);
75-
}
7672

7773
@Override
7874
public boolean equals(Object other) {
@@ -86,10 +82,15 @@ public boolean equals(Object other) {
8682
return ObjectUtils.nullSafeEquals(this.payload, otherMessage.payload);
8783
}
8884

85+
@Override
86+
public int hashCode() {
87+
return ObjectUtils.nullSafeHashCode(this.payload);
88+
}
89+
8990
@Override
9091
public String toString() {
91-
return getClass().getSimpleName() + " payload= " + toStringPayload()
92-
+ ", byteCount=" + getPayloadLength() + ", last=" + isLast() + "]";
92+
return getClass().getSimpleName() + " payload=[" + toStringPayload() +
93+
"], byteCount=" + getPayloadLength() + ", last=" + isLast() + "]";
9394
}
9495

9596
protected abstract String toStringPayload();

spring-websocket/src/main/java/org/springframework/web/socket/BinaryMessage.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
*/
2727
public final class BinaryMessage extends AbstractWebSocketMessage<ByteBuffer> {
2828

29-
3029
/**
3130
* Create a new binary WebSocket message with the given ByteBuffer payload.
3231
* @param payload the non-null payload
@@ -78,7 +77,7 @@ public BinaryMessage(byte[] payload, boolean isLast) {
7877
* @param isLast if the message is the last of a series of partial messages
7978
*/
8079
public BinaryMessage(byte[] payload, int offset, int length, boolean isLast) {
81-
super((payload != null) ? ByteBuffer.wrap(payload, offset, length) : null, isLast);
80+
super(payload != null ? ByteBuffer.wrap(payload, offset, length) : null, isLast);
8281
}
8382

8483

spring-websocket/src/main/java/org/springframework/web/socket/PingMessage.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
*/
2727
public final class PingMessage extends AbstractWebSocketMessage<ByteBuffer> {
2828

29-
3029
/**
3130
* Create a new ping message with an empty payload.
3231
*/
@@ -45,12 +44,12 @@ public PingMessage(ByteBuffer payload) {
4544

4645
@Override
4746
public int getPayloadLength() {
48-
return (getPayload() != null) ? getPayload().remaining() : 0;
47+
return (getPayload() != null ? getPayload().remaining() : 0);
4948
}
5049

5150
@Override
5251
protected String toStringPayload() {
53-
return (getPayload() != null) ? getPayload().toString() : null;
52+
return (getPayload() != null ? getPayload().toString() : null);
5453
}
5554

5655
}

spring-websocket/src/main/java/org/springframework/web/socket/PongMessage.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
*/
2727
public final class PongMessage extends AbstractWebSocketMessage<ByteBuffer> {
2828

29-
3029
/**
3130
* Create a new pong message with an empty payload.
3231
*/
@@ -45,12 +44,12 @@ public PongMessage(ByteBuffer payload) {
4544

4645
@Override
4746
public int getPayloadLength() {
48-
return (getPayload() != null) ? getPayload().remaining() : 0;
47+
return (getPayload() != null ? getPayload().remaining() : 0);
4948
}
5049

5150
@Override
5251
protected String toStringPayload() {
53-
return (getPayload() != null) ? getPayload().toString() : null;
52+
return (getPayload() != null ? getPayload().toString() : null);
5453
}
5554

5655
}

spring-websocket/src/main/java/org/springframework/web/socket/TextMessage.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,9 +41,8 @@ public TextMessage(CharSequence payload) {
4141
}
4242

4343
/**
44-
* Create a new text WebSocket message from the given byte[]. It is assumed the
45-
* byte array can be encoded into an UTF-8 String.
46-
*
44+
* Create a new text WebSocket message from the given byte[]. It is assumed
45+
* the byte array can be encoded into an UTF-8 String.
4746
* @param payload the non-null payload
4847
*/
4948
public TextMessage(byte[] payload) {
@@ -76,7 +75,8 @@ public byte[] asBytes() {
7675

7776
@Override
7877
protected String toStringPayload() {
79-
return (getPayloadLength() > 10) ? getPayload().substring(0, 10) + ".." : getPayload();
78+
String payload = getPayload();
79+
return (payload.length() > 10 ? payload.substring(0, 10) + ".." : payload);
8080
}
8181

8282
}

spring-websocket/src/main/java/org/springframework/web/socket/WebSocketSession.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -51,12 +51,10 @@ public interface WebSocketSession {
5151

5252
/**
5353
* Return the map with attributes associated with the WebSocket session.
54-
*
5554
* <p>When the WebSocketSession is created, on the server side, the map can be
5655
* through a {@link org.springframework.web.socket.server.HandshakeInterceptor}.
5756
* On the client side, the map can be populated by passing attributes to the
58-
* {@link org.springframework.web.socket.client.WebSocketClient} handshake
59-
* methods.
57+
* {@link org.springframework.web.socket.client.WebSocketClient} handshake methods.
6058
*/
6159
Map<String, Object> getAttributes();
6260

@@ -110,15 +108,14 @@ public interface WebSocketSession {
110108
List<WebSocketExtension> getExtensions();
111109

112110
/**
113-
* Return whether the connection is still open.
111+
* Send a WebSocket message: either {@link TextMessage} or {@link BinaryMessage}.
114112
*/
115-
boolean isOpen();
113+
void sendMessage(WebSocketMessage<?> message) throws IOException;
116114

117115
/**
118-
* Send a WebSocket message either {@link TextMessage} or
119-
* {@link BinaryMessage}.
116+
* Return whether the connection is still open.
120117
*/
121-
void sendMessage(WebSocketMessage<?> message) throws IOException;
118+
boolean isOpen();
122119

123120
/**
124121
* Close the WebSocket connection with status 1000, i.e. equivalent to:
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.socket;
18+
19+
import org.hamcrest.Matchers;
20+
import org.junit.Test;
21+
22+
import static org.junit.Assert.*;
23+
24+
/**
25+
* Test fixture for {@link TextMessage}.
26+
*
27+
* @author Shinobu Aoki
28+
* @author Juergen Hoeller
29+
*/
30+
public class TextMessageTests {
31+
32+
@Test
33+
public void toStringWithAscii() {
34+
String expected = "foo,bar";
35+
TextMessage actual = new TextMessage(expected);
36+
assertThat(actual.getPayload(), Matchers.is(expected));
37+
assertThat(actual.toString(), Matchers.containsString(expected));
38+
}
39+
40+
@Test
41+
public void toStringWithMultibyteString() {
42+
String expected = "\u3042\u3044\u3046\u3048\u304a";
43+
TextMessage actual = new TextMessage(expected);
44+
assertThat(actual.getPayload(), Matchers.is(expected));
45+
assertThat(actual.toString(), Matchers.containsString(expected));
46+
}
47+
48+
}

0 commit comments

Comments
 (0)