Skip to content

Commit 564b8aa

Browse files
authored
Merge pull request #849 from marci4/Issue847
Fix issue #847
2 parents bfac50c + cb3783b commit 564b8aa

File tree

3 files changed

+270
-6
lines changed

3 files changed

+270
-6
lines changed

src/main/java/org/java_websocket/drafts/Draft_6455.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,9 @@ private Framedata translateSingleFrame( ByteBuffer buffer ) throws IncompleteExc
476476
Opcode optcode = toOpcode( ( byte ) ( b1 & 15 ) );
477477

478478
if( !( payloadlength >= 0 && payloadlength <= 125 ) ) {
479-
payloadlength = translateSingleFramePayloadLength(buffer, optcode, payloadlength ,maxpacketsize, realpacketsize);
479+
TranslatedPayloadMetaData payloadData = translateSingleFramePayloadLength(buffer, optcode, payloadlength ,maxpacketsize, realpacketsize);
480+
payloadlength = payloadData.getPayloadLength();
481+
realpacketsize = payloadData.getRealPackageSize();
480482
}
481483
translateSingleFrameCheckLengthLimit(payloadlength);
482484
realpacketsize += ( mask ? 4 : 0 );
@@ -516,14 +518,15 @@ private Framedata translateSingleFrame( ByteBuffer buffer ) throws IncompleteExc
516518
* @param optcode the decoded optcode
517519
* @param oldPayloadlength the old payload length
518520
* @param maxpacketsize the max packet size allowed
519-
* @param realpacketsize the real packet size
520-
* @return the new payload length
521+
* @param oldRealpacketsize the real packet size
522+
* @return the new payload data containing new payload length and new packet size
521523
* @throws InvalidFrameException thrown if a control frame has an invalid length
522524
* @throws IncompleteException if the maxpacketsize is smaller than the realpackagesize
523525
* @throws LimitExceededException if the payload length is to big
524526
*/
525-
private int translateSingleFramePayloadLength(ByteBuffer buffer, Opcode optcode, int oldPayloadlength, int maxpacketsize, int realpacketsize) throws InvalidFrameException, IncompleteException, LimitExceededException {
526-
int payloadlength = oldPayloadlength;
527+
private TranslatedPayloadMetaData translateSingleFramePayloadLength(ByteBuffer buffer, Opcode optcode, int oldPayloadlength, int maxpacketsize, int oldRealpacketsize) throws InvalidFrameException, IncompleteException, LimitExceededException {
528+
int payloadlength = oldPayloadlength,
529+
realpacketsize = oldRealpacketsize;
527530
if( optcode == Opcode.PING || optcode == Opcode.PONG || optcode == Opcode.CLOSING ) {
528531
log.trace( "Invalid frame: more than 125 octets" );
529532
throw new InvalidFrameException( "more than 125 octets" );
@@ -546,7 +549,7 @@ private int translateSingleFramePayloadLength(ByteBuffer buffer, Opcode optcode,
546549
translateSingleFrameCheckLengthLimit(length);
547550
payloadlength = ( int ) length;
548551
}
549-
return payloadlength;
552+
return new TranslatedPayloadMetaData(payloadlength, realpacketsize);
550553
}
551554

552555
/**
@@ -1035,4 +1038,22 @@ private long getByteBufferListSize() {
10351038
}
10361039
return totalSize;
10371040
}
1041+
1042+
private class TranslatedPayloadMetaData {
1043+
private int payloadLength;
1044+
private int realPackageSize;
1045+
1046+
private int getPayloadLength() {
1047+
return payloadLength;
1048+
}
1049+
1050+
private int getRealPackageSize() {
1051+
return realPackageSize;
1052+
}
1053+
1054+
TranslatedPayloadMetaData(int newPayloadLength, int newRealPackageSize) {
1055+
this.payloadLength = newPayloadLength;
1056+
this.realPackageSize = newRealPackageSize;
1057+
}
1058+
}
10381059
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* Copyright (c) 2010-2019 Nathan Rajlich
3+
*
4+
* Permission is hereby granted, free of charge, to any person
5+
* obtaining a copy of this software and associated documentation
6+
* files (the "Software"), to deal in the Software without
7+
* restriction, including without limitation the rights to use,
8+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the
10+
* Software is furnished to do so, subject to the following
11+
* conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be
14+
* included in all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23+
* OTHER DEALINGS IN THE SOFTWARE.
24+
*
25+
*/
26+
27+
package org.java_websocket.issues;
28+
29+
import org.java_websocket.client.WebSocketClient;
30+
import org.java_websocket.drafts.Draft_6455;
31+
import org.java_websocket.framing.BinaryFrame;
32+
import org.java_websocket.handshake.ServerHandshake;
33+
import org.java_websocket.util.Charsetfunctions;
34+
import org.java_websocket.util.KeyUtils;
35+
import org.java_websocket.util.SocketUtil;
36+
import org.junit.AfterClass;
37+
import org.junit.BeforeClass;
38+
import org.junit.Test;
39+
import org.junit.runner.RunWith;
40+
import org.junit.runners.Parameterized;
41+
42+
import java.io.IOException;
43+
import java.io.OutputStream;
44+
import java.net.ServerSocket;
45+
import java.net.Socket;
46+
import java.net.URI;
47+
import java.nio.ByteBuffer;
48+
import java.util.ArrayList;
49+
import java.util.Collection;
50+
import java.util.List;
51+
import java.util.Scanner;
52+
53+
import static org.junit.Assert.fail;
54+
55+
@RunWith(Parameterized.class)
56+
public class Issue847Test {
57+
58+
private static Thread thread;
59+
private static ServerSocket serverSocket;
60+
61+
private static int port;
62+
private static final int NUMBER_OF_TESTS = 20;
63+
64+
@Parameterized.Parameter
65+
public int size;
66+
67+
@Parameterized.Parameters
68+
public static Collection<Integer[]> data() {
69+
List<Integer[]> ret = new ArrayList<Integer[]>(NUMBER_OF_TESTS);
70+
for (int i = 1; i <= NUMBER_OF_TESTS+1; i++) ret.add(new Integer[]{(int)Math.round(Math.pow(2, i))});
71+
return ret;
72+
}
73+
74+
@BeforeClass
75+
public static void startServer() throws Exception {
76+
port = SocketUtil.getAvailablePort();
77+
thread = new Thread(
78+
new Runnable() {
79+
public void run() {
80+
try {
81+
serverSocket = new ServerSocket( port );
82+
serverSocket.setReuseAddress( true );
83+
while( true ) {
84+
Socket client = null;
85+
try {
86+
client = serverSocket.accept();
87+
Scanner in = new Scanner( client.getInputStream() );
88+
String input;
89+
String seckey = "";
90+
String testCase;
91+
boolean useMask = false;
92+
int size = 0;
93+
OutputStream os = client.getOutputStream();
94+
while( in.hasNext() ) {
95+
input = in.nextLine();
96+
if( input.startsWith( "Sec-WebSocket-Key: " ) ) {
97+
seckey = input.split( " " )[1];
98+
}
99+
//Last
100+
if( input.startsWith( "Upgrade" ) ) {
101+
os.write(Charsetfunctions.asciiBytes("HTTP/1.1 101 Websocket Connection Upgrade\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n" + KeyUtils.getSecKey(seckey) + "\r\n"));
102+
os.flush();
103+
Thread.sleep(10);
104+
Draft_6455 draft_6455 = new Draft_6455();
105+
BinaryFrame binaryFrame = new BinaryFrame();
106+
binaryFrame.setPayload(ByteBuffer.allocate(size));
107+
binaryFrame.setTransferemasked(useMask);
108+
ByteBuffer byteBuffer = draft_6455.createBinaryFrame(binaryFrame);
109+
byte[] bytes = byteBuffer.array();
110+
int first = size/2;
111+
os.write(bytes, 0, first);
112+
os.flush();
113+
Thread.sleep(5);
114+
os.write(bytes, first, bytes.length-first);
115+
os.flush();
116+
break;
117+
}
118+
if( input.startsWith( "GET " ) ) {
119+
testCase = input.split(" ")[1];
120+
String[] strings = testCase.split("/");
121+
useMask = Boolean.valueOf(strings[1]);
122+
size = Integer.valueOf(strings[2]);
123+
}
124+
}
125+
} catch ( IOException e ) {
126+
//
127+
}
128+
}
129+
} catch ( Exception e ) {
130+
e.printStackTrace();
131+
fail( "There should be no exception" );
132+
}
133+
}
134+
} );
135+
thread.start();
136+
}
137+
138+
@AfterClass
139+
public static void successTests() throws IOException {
140+
serverSocket.close();
141+
thread.interrupt();
142+
}
143+
144+
@Test(timeout = 5000)
145+
public void testIncrementalFrameUnmasked() throws Exception {
146+
testIncrementalFrame( false, size );
147+
}
148+
149+
@Test(timeout = 5000)
150+
public void testIncrementalFrameMsked() throws Exception {
151+
testIncrementalFrame( true, size );
152+
}
153+
154+
155+
private void testIncrementalFrame(boolean useMask, int size ) throws Exception {
156+
final boolean[] threadReturned = { false };
157+
final WebSocketClient webSocketClient = new WebSocketClient( new URI( "ws://localhost:"+ port + "/" + useMask + "/" + size ) ) {
158+
@Override
159+
public void onOpen( ServerHandshake handshakedata ) {
160+
}
161+
162+
@Override
163+
public void onMessage( String message ) {
164+
fail( "There should not be a message!" );
165+
}
166+
public void onMessage( ByteBuffer message ) {
167+
threadReturned[0] = true;
168+
close();
169+
}
170+
171+
@Override
172+
public void onClose( int code, String reason, boolean remote ) {
173+
}
174+
175+
@Override
176+
public void onError( Exception ex ) {
177+
ex.printStackTrace();
178+
}
179+
};
180+
Thread finalThread = new Thread( webSocketClient );
181+
finalThread.start();
182+
finalThread.join();
183+
if( !threadReturned[0] ) {
184+
fail( "Error" );
185+
}
186+
}
187+
}
188+
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (c) 2010-2019 Nathan Rajlich
3+
*
4+
* Permission is hereby granted, free of charge, to any person
5+
* obtaining a copy of this software and associated documentation
6+
* files (the "Software"), to deal in the Software without
7+
* restriction, including without limitation the rights to use,
8+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the
10+
* Software is furnished to do so, subject to the following
11+
* conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be
14+
* included in all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23+
* OTHER DEALINGS IN THE SOFTWARE.
24+
*
25+
*/
26+
27+
package org.java_websocket.util;
28+
29+
import java.security.MessageDigest;
30+
import java.security.NoSuchAlgorithmException;
31+
32+
public class KeyUtils {
33+
/**
34+
* Generate a final key from a input string
35+
*
36+
* @param in the input string
37+
* @return a final key
38+
*/
39+
public static String generateFinalKey( String in ) {
40+
String seckey = in.trim();
41+
String acc = seckey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
42+
MessageDigest sh1;
43+
try {
44+
sh1 = MessageDigest.getInstance( "SHA1" );
45+
} catch ( NoSuchAlgorithmException e ) {
46+
throw new IllegalStateException( e );
47+
}
48+
return Base64.encodeBytes( sh1.digest( acc.getBytes() ) );
49+
}
50+
51+
public static String getSecKey( String seckey ) {
52+
return "Sec-WebSocket-Accept: " + KeyUtils.generateFinalKey( seckey ) + "\r\n";
53+
}
54+
55+
}

0 commit comments

Comments
 (0)