Skip to content

Commit d7a7d9c

Browse files
julianTooTallNate
authored andcommitted
Added Draft 76 to client. Removed uneeded method. Changed server to send challenge as rawbytes
1 parent e9e9991 commit d7a7d9c

6 files changed

Lines changed: 164 additions & 53 deletions

File tree

example/ChatClient.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
public class ChatClient extends WebSocketClient {
2121
private final JTextArea ta;
2222

23-
public ChatClient(URI uri, JTextArea ta) {
24-
super(uri);
23+
public ChatClient(URI uri, JTextArea ta,Draft draft) {
24+
super(uri,Draft.DRAFT75);
2525
this.ta = ta;
2626
}
2727

@@ -109,7 +109,7 @@ public void actionPerformed(ActionEvent e) {
109109
connect.setEnabled(false);
110110
uriField.setEditable(false);
111111
try {
112-
cc = new ChatClient(new URI(uriField.getText()), area);
112+
cc = new ChatClient(new URI(uriField.getText()), area,Draft.DRAFT76);
113113
cc.connect();
114114
} catch (URISyntaxException ex) {
115115
area.append(uriField.getText() + " is not a valid WebSocket URI\n");
@@ -127,10 +127,4 @@ public void run() {
127127
}
128128
});
129129
}
130-
131-
@Override
132-
public Draft getDraft() {
133-
// TODO Auto-generated method stub
134-
return null;
135-
}
136130
}

example/ChatServer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
public class ChatServer extends WebSocketServer {
1010

1111
public ChatServer(int port) {
12-
super(port, Draft.AUTO);
12+
super(port, Draft.DRAFT75);
1313
}
1414

1515
public void onClientOpen(WebSocket conn) {

src/net/tootallnate/websocket/WebSocket.java

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -187,25 +187,51 @@ private void recieveHandshake() throws IOException, NoSuchAlgorithmException {
187187
}
188188
ch.put(this.buffer);
189189
this.remoteHandshake = ch;
190-
191-
// If the ByteBuffer contains 8 random bytes and ends with
192-
// 0x0D 0x0A 0x0D 0x0A (or two CRLFs), then the client
193-
// handshake is complete for Draft 76.
194190
byte[] h = this.remoteHandshake.array();
195-
if ((h.length>=12 && h[h.length-12] == CR
191+
// If the ByteBuffer contains 16 random bytes, and ends with
192+
// 0x0D 0x0A 0x0D 0x0A (or two CRLFs), then the client
193+
// handshake is complete for Draft 76 Client.
194+
if((h.length>=20 && h[h.length-20] == CR
195+
&& h[h.length-19] == LF
196+
&& h[h.length-18] == CR
197+
&& h[h.length-17] == LF)){
198+
byte[] handShakeBody = new byte[16];
199+
handShakeBody[0]=h[h.length-16];
200+
handShakeBody[1]=h[h.length-15];
201+
handShakeBody[2]=h[h.length-14];
202+
handShakeBody[3]=h[h.length-13];
203+
handShakeBody[4]=h[h.length-12];
204+
handShakeBody[5]=h[h.length-11];
205+
handShakeBody[6]=h[h.length-10];
206+
handShakeBody[7]=h[h.length-9];
207+
handShakeBody[8]=h[h.length-8];
208+
handShakeBody[9]=h[h.length-7];
209+
handShakeBody[10]=h[h.length-6];
210+
handShakeBody[11]=h[h.length-5];
211+
handShakeBody[12]=h[h.length-4];
212+
handShakeBody[13]=h[h.length-3];
213+
handShakeBody[14]=h[h.length-2];
214+
handShakeBody[15]=h[h.length-1];
215+
completeHandshake(handShakeBody);
216+
}
217+
// If the ByteBuffer contains 8 random bytes,ends with
218+
// 0x0D 0x0A 0x0D 0x0A (or two CRLFs), and the response
219+
// contains Sec-WebSocket-Key1 then the client
220+
// handshake is complete for Draft 76 Server.
221+
else if ((h.length>=12 && h[h.length-12] == CR
196222
&& h[h.length-11] == LF
197223
&& h[h.length-10] == CR
198-
&& h[h.length-9] == LF)) {
199-
byte[] key3 = new byte[8];
200-
key3[0]=h[h.length-8];
201-
key3[1]=h[h.length-7];
202-
key3[2]=h[h.length-6];
203-
key3[3]=h[h.length-5];
204-
key3[4]=h[h.length-4];
205-
key3[5]=h[h.length-3];
206-
key3[6]=h[h.length-2];
207-
key3[7]=h[h.length-1];
208-
completeHandshake(key3);
224+
&& h[h.length-9] == LF) && new String(this.remoteHandshake.array(), UTF8_CHARSET).contains("Sec-WebSocket-Key1")) {
225+
byte[] handShakeBody = new byte[8];
226+
handShakeBody[0]=h[h.length-8];
227+
handShakeBody[1]=h[h.length-7];
228+
handShakeBody[2]=h[h.length-6];
229+
handShakeBody[3]=h[h.length-5];
230+
handShakeBody[4]=h[h.length-4];
231+
handShakeBody[5]=h[h.length-3];
232+
handShakeBody[6]=h[h.length-2];
233+
handShakeBody[7]=h[h.length-1];
234+
completeHandshake(handShakeBody);
209235

210236
// Consider Draft 75, and the Flash Security Policy
211237
// Request edge-case.
@@ -219,11 +245,11 @@ private void recieveHandshake() throws IOException, NoSuchAlgorithmException {
219245
}
220246
}
221247

222-
private void completeHandshake(byte[] key3) throws IOException, NoSuchAlgorithmException {
248+
private void completeHandshake(byte[] handShakeBody) throws IOException, NoSuchAlgorithmException {
223249
byte[] handshakeBytes = this.remoteHandshake.array();
224250
String handshake = new String(handshakeBytes, UTF8_CHARSET);
225251
this.handshakeComplete = true;
226-
if (this.wsl.onHandshakeRecieved(this, handshake, key3)) {
252+
if (this.wsl.onHandshakeRecieved(this, handshake, handShakeBody)) {
227253
this.wsl.onOpen(this);
228254
} else {
229255
close();

src/net/tootallnate/websocket/WebSocketClient.java

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
import java.nio.channels.SelectionKey;
88
import java.nio.channels.Selector;
99
import java.nio.channels.SocketChannel;
10+
import java.security.MessageDigest;
1011
import java.security.NoSuchAlgorithmException;
1112
import java.util.Iterator;
13+
import java.util.Random;
1214
import java.util.Set;
1315

16+
import net.tootallnate.websocket.WebSocketListener.Draft;
17+
1418
/**
1519
* The <tt>WebSocketClient</tt> is an abstract class that expects a valid
1620
* "ws://" URI to connect to. When connected, an instance recieves important
@@ -32,7 +36,24 @@ public abstract class WebSocketClient implements Runnable, WebSocketListener {
3236
* The WebSocket instance this client object wraps.
3337
*/
3438
private WebSocket conn;
35-
39+
/**
40+
* @author The Websocket mode this client is in.
41+
*/
42+
private Draft draft;
43+
/**
44+
* Number 1 used in handshake
45+
*/
46+
private int number1=0;
47+
/** Number 2 used in handshake
48+
*/
49+
private int number2=0;
50+
/** Key3 used in handshake
51+
*/
52+
private byte[] key3;
53+
public static enum Draft{
54+
DRAFT75,
55+
DRAFT76
56+
}
3657

3758
// CONSTRUCTOR /////////////////////////////////////////////////////////////
3859
/**
@@ -41,8 +62,9 @@ public abstract class WebSocketClient implements Runnable, WebSocketListener {
4162
* must call <var>connect</var> first to initiate the socket connection.
4263
* @param serverUri The <tt>URI</tt> of the WebSocket server to connect to.
4364
*/
44-
public WebSocketClient(URI serverUri) {
65+
public WebSocketClient(URI serverUri,Draft draft) {
4566
this.uri = serverUri;
67+
this.draft = draft;
4668
}
4769

4870
// PUBLIC INSTANCE METHODS /////////////////////////////////////////////////
@@ -104,10 +126,10 @@ public void run() {
104126
while (selector.select(500) > 0) {
105127

106128
Set<SelectionKey> keys = selector.selectedKeys();
107-
Iterator i = keys.iterator();
129+
Iterator<SelectionKey> i = keys.iterator();
108130

109131
while (i.hasNext()) {
110-
SelectionKey key = (SelectionKey)i.next();
132+
SelectionKey key = i.next();
111133
i.remove();
112134

113135
// When 'conn' has connected to the host
@@ -119,17 +141,26 @@ public void run() {
119141
}
120142

121143
// Now send WebSocket client-side handshake
144+
Random r=new Random();
145+
this.key3=new byte[8];
122146
String path = "/" + uri.getPath();
123147
String host = uri.getHost() + (port != WebSocket.DEFAULT_PORT ? ":" + port : "");
124148
String origin = null; // TODO: Make 'origin' configurable
125149
String request = "GET " + path + " HTTP/1.1\r\n" +
126150
"Upgrade: WebSocket\r\n" +
127151
"Connection: Upgrade\r\n" +
128152
"Host: " + host + "\r\n" +
129-
"Origin: " + origin + "\r\n" +
153+
"Origin: " + origin + "\r\n";
154+
if(this.draft==Draft.DRAFT76)
155+
{
156+
request+="Sec-WebSocket-Key1: " + this.generateKey() + "\r\n";
157+
request+="Sec-WebSocket-Key2: " + this.generateKey() + "\r\n";
158+
r.nextBytes(this.key3);
159+
}
130160
//extraHeaders.toString() +
131-
"\r\n";
161+
request+="\r\n";
132162
conn.socketChannel().write(ByteBuffer.wrap(request.getBytes(WebSocket.UTF8_CHARSET)));
163+
conn.socketChannel().write(ByteBuffer.wrap(key3));
133164
}
134165

135166
// When 'conn' has recieved some data
@@ -145,7 +176,39 @@ public void run() {
145176
e.printStackTrace();
146177
}
147178
}
148-
179+
private String generateKey(){
180+
Random r=new Random();
181+
long maxNumber=4294967295L;
182+
long spaces=r.nextInt(12)+1;
183+
int max=new Long(maxNumber/spaces).intValue();
184+
max=Math.abs(max);
185+
int number=r.nextInt(max)+1;
186+
if(this.number1==0){
187+
this.number1=number;
188+
}
189+
else{
190+
this.number2=number;
191+
}
192+
long product=number*spaces;
193+
String key=Long.toString(product);
194+
int numChars=r.nextInt(12);
195+
for (int i=0;i<numChars;i++){
196+
int position=r.nextInt(key.length());
197+
position=Math.abs(position);
198+
char randChar=(char)(r.nextInt(95)+33);
199+
//exclude numbers here
200+
if(randChar >= 48 && randChar <=57){
201+
randChar-=15;
202+
}
203+
key=new StringBuilder(key).insert(position, randChar).toString();
204+
}
205+
for (int i=0;i<spaces;i++){
206+
int position=r.nextInt(key.length()-1)+1;
207+
position=Math.abs(position);
208+
key=new StringBuilder(key).insert(position,"\u0020").toString();
209+
}
210+
return key;
211+
}
149212

150213
// WebSocketListener IMPLEMENTATION ////////////////////////////////////////
151214
/**
@@ -157,10 +220,41 @@ public void run() {
157220
* @return <var>true</var> if <var>handshake</var> is a valid WebSocket server
158221
* handshake, <var>false</var> otherwise.
159222
* @throws IOException When socket related I/O errors occur.
223+
* @throws NoSuchAlgorithmException
160224
*/
161-
public boolean onHandshakeRecieved(WebSocket conn, String handshake,byte[] key3) throws IOException {
225+
public boolean onHandshakeRecieved(WebSocket conn, String handshake,byte[] reply) throws IOException, NoSuchAlgorithmException {
162226
// TODO: Do some parsing of the returned handshake, and close connection
163227
// (return false) if we recieved anything unexpected.
228+
if(this.draft==Draft.DRAFT76){
229+
if(reply==null){
230+
return false;
231+
}
232+
byte[] challenge=new byte[]{
233+
(byte)( this.number1 >> 24 ),
234+
(byte)( (this.number1 << 8) >> 24 ),
235+
(byte)( (this.number1 << 16) >> 24 ),
236+
(byte)( (this.number1 << 24) >> 24 ),
237+
(byte)( this.number2 >> 24 ),
238+
(byte)( (this.number2 << 8) >> 24 ),
239+
(byte)( (this.number2 << 16) >> 24 ),
240+
(byte)( (this.number2 << 24) >> 24 ),
241+
this.key3[0],
242+
this.key3[1],
243+
this.key3[2],
244+
this.key3[3],
245+
this.key3[4],
246+
this.key3[5],
247+
this.key3[6],
248+
this.key3[7]
249+
};
250+
MessageDigest md5=MessageDigest.getInstance("MD5");
251+
byte[] expected=md5.digest(challenge);
252+
for(int i=0;i<reply.length;i++){
253+
if(expected[i]!=reply[i]){
254+
return false;
255+
}
256+
}
257+
}
164258
return true;
165259
}
166260

@@ -188,7 +282,11 @@ public void onOpen(WebSocket conn) {
188282
public void onClose(WebSocket conn) {
189283
onClose();
190284
}
191-
285+
286+
@Override
287+
public net.tootallnate.websocket.WebSocketListener.Draft getDraft() {
288+
return (net.tootallnate.websocket.WebSocketListener.Draft)net.tootallnate.websocket.WebSocketListener.Draft.valueOf(this.draft.name());
289+
}
192290

193291
// ABTRACT METHODS /////////////////////////////////////////////////////////
194292
public abstract void onMessage(String message);

src/net/tootallnate/websocket/WebSocketListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public static enum Draft {
3030
* handshake was invalid, and the connection should be terminated.
3131
* @throws NoSuchAlgorithmException
3232
*/
33-
public boolean onHandshakeRecieved(WebSocket conn, String handshake,byte[] key3) throws IOException, NoSuchAlgorithmException;
33+
public boolean onHandshakeRecieved(WebSocket conn, String handshake,byte[] handShakeBody) throws IOException, NoSuchAlgorithmException;
3434

3535
/**
3636
* Called when an entire text frame has been recieved. Do whatever you want

src/net/tootallnate/websocket/WebSocketServer.java

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,10 @@ public void run() {
187187
while(true) {
188188
selector.select();
189189
Set<SelectionKey> keys = selector.selectedKeys();
190-
Iterator i = keys.iterator();
190+
Iterator<SelectionKey> i = keys.iterator();
191191

192192
while(i.hasNext()) {
193-
SelectionKey key = (SelectionKey) i.next();
193+
SelectionKey key = i.next();
194194

195195
// Remove the current key
196196
i.remove();
@@ -293,7 +293,7 @@ public boolean onHandshakeRecieved(WebSocket conn, String handshake,byte[] key3)
293293
String key1=p.getProperty("Sec-WebSocket-Key1");
294294
String key2=p.getProperty("Sec-WebSocket-Key2");
295295
String headerPrefix="";
296-
String response="";
296+
byte[] responseChallenge=new byte[16];
297297
switch (this.draft){
298298
case DRAFT75:
299299
if (key1 != null || key2 != null || key3 != null) {
@@ -309,7 +309,7 @@ public boolean onHandshakeRecieved(WebSocket conn, String handshake,byte[] key3)
309309
if (isWebSocketRequest) {
310310
if (key1 != null && key2 != null && key3 != null) {
311311
headerPrefix="Sec-";
312-
byte[] part1=this.getPart(key1);
312+
byte[] part1=this.getPart(key1);
313313
byte[] part2=this.getPart(key2);
314314
byte[] challenge=new byte[16];
315315
challenge[0]=part1[0];
@@ -329,7 +329,7 @@ public boolean onHandshakeRecieved(WebSocket conn, String handshake,byte[] key3)
329329
challenge[14]=key3[6];
330330
challenge[15]=key3[7];
331331
MessageDigest md5=MessageDigest.getInstance("MD5");
332-
response = new String(md5.digest(challenge));
332+
responseChallenge=md5.digest(challenge);
333333
}
334334

335335
String responseHandshake = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" +
@@ -345,10 +345,10 @@ public boolean onHandshakeRecieved(WebSocket conn, String handshake,byte[] key3)
345345
responseHandshake += "Cookie: " + p.getProperty("Cookie")+"\r\n";
346346
}
347347
responseHandshake += "\r\n"; // Signifies end of handshake
348-
//only set if its Draft 76
349-
responseHandshake += response;
350348
//Can not use UTF-8 here because we might lose bytes in response during conversion
351349
conn.socketChannel().write(ByteBuffer.wrap(responseHandshake.getBytes()));
350+
//Only set when Draft 76
351+
conn.socketChannel().write(ByteBuffer.wrap(responseChallenge));
352352
return true;
353353
}
354354

@@ -376,21 +376,14 @@ public void onClose(WebSocket conn) {
376376
private byte[] getPart(String key){
377377
long keyNumber=Long.parseLong(key.replaceAll("[^0-9]",""));
378378
long keySpace=key.split("\u0020").length-1;
379-
int part=new Long(keyNumber/keySpace).intValue();
379+
long part=new Long(keyNumber/keySpace);
380380
byte[] bytes=new byte[4];
381381
bytes[0] =(byte)( part >> 24 );
382382
bytes[1] =(byte)( (part << 8) >> 24 );
383383
bytes[2] =(byte)( (part << 16) >> 24 );
384384
bytes[3] =(byte)( (part << 24) >> 24 );
385385
return bytes;
386386
}
387-
388-
private void appendBytesToArray(byte[] sourceArray,byte[] destinationArray){
389-
for(int i=0;i<sourceArray.length;i++){
390-
destinationArray[destinationArray.length-(sourceArray.length+i)]=sourceArray[i];
391-
}
392-
}
393-
394387

395388
// ABTRACT METHODS /////////////////////////////////////////////////////////
396389
public abstract void onClientOpen(WebSocket conn);

0 commit comments

Comments
 (0)