Skip to content

Commit 0bc85a3

Browse files
committed
-added usable draft76 implementation(some missing validation )
-generalized Handshakedata handling
1 parent 5891f03 commit 0bc85a3

7 files changed

Lines changed: 108 additions & 56 deletions

File tree

src/net/tootallnate/websocket/HandshakeBuilder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ public interface HandshakeBuilder extends Handshakedata{
55
public abstract void setContent( byte[] content );
66

77
public abstract void setResourceDescriptor( String resourcedescriptor );
8+
9+
public abstract void setHttpStatusMessage( String message );
810

911
public abstract void put( String name , String value );
1012

src/net/tootallnate/websocket/Handshakedata.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
import java.util.Iterator;
55

66
public interface Handshakedata {
7+
public String getHttpStatusMessage();
78
public String getResourceDescriptor();
89
public Iterator<String> iterateHttpFields();
910
public String getFieldValue( String name );
11+
public boolean hasFieldValue( String name );
1012
public byte[] getContent();
1113
//public boolean isComplete();
1214
}

src/net/tootallnate/websocket/HandshakedataImpl1.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22

33
import java.util.HashMap;
44
import java.util.Iterator;
5+
import java.util.LinkedHashMap;
56
import java.util.Map;
67

78
public class HandshakedataImpl1 implements HandshakeBuilder {
89

10+
private String httpstatusmessage;
911
private String resourcedescriptor;
1012
private byte[] content;
11-
private Map<String,String> map;
13+
private LinkedHashMap<String,String> map;
1214

1315
public HandshakedataImpl1() {
14-
map = new HashMap<String,String>();
16+
map = new LinkedHashMap<String,String>();
1517
}
1618

1719
@Override
@@ -53,4 +55,20 @@ public void put( String name, String value ){
5355
map.put ( name , value );
5456
}
5557

58+
@Override
59+
public boolean hasFieldValue( String name ) {
60+
return map.containsKey( name );
61+
}
62+
63+
@Override
64+
public String getHttpStatusMessage( ) {
65+
return httpstatusmessage;
66+
}
67+
68+
@Override
69+
public void setHttpStatusMessage( String message ) {
70+
this.httpstatusmessage=message;
71+
72+
}
73+
5674
}

src/net/tootallnate/websocket/WebSocketClient.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,13 +201,14 @@ public void run() {
201201
running = tryToConnect(new InetSocketAddress(uri.getHost(), getPort()));
202202

203203
while (this.running) {
204+
SelectionKey key = null;
204205
try {
205206
selector.select();
206207
Set<SelectionKey> keys = selector.selectedKeys();
207208
Iterator<SelectionKey> i = keys.iterator();
208209

209210
while (i.hasNext()) {
210-
SelectionKey key = i.next();
211+
key = i.next();
211212
i.remove();
212213

213214
if (key.isConnectable()) {
@@ -219,6 +220,8 @@ public void run() {
219220
}
220221
}
221222
} catch (IOException ex) {
223+
if( key != null )
224+
key.cancel();
222225
onIOError(conn, ex);
223226
} catch (Exception ex) {
224227
// NullPointerException is the most common error that can happen here
@@ -254,12 +257,10 @@ private void sendHandshake() throws IOException {
254257
}
255258
int port = getPort();
256259
String host = uri.getHost() + (port != WebSocket.DEFAULT_PORT ? ":" + port : "");
257-
String origin = null; // TODO: Make 'origin' configurable
260+
String origin = "x"; // TODO: Make 'origin' configurable
258261

259262
HandshakedataImpl1 handshake = new HandshakedataImpl1();
260263
handshake.setResourceDescriptor ( path );
261-
handshake.put ( "Upgrade" , "WebSocket" );
262-
handshake.put ( "Connection" , "Upgrade" );
263264
handshake.put ( "Host" , host );
264265
handshake.put ( "Origin" , origin );
265266
conn.startHandshake ( handshake );

src/net/tootallnate/websocket/WebSocketServer.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,9 @@ public void run() {
250250
}
251251
}
252252
} catch (IOException ex) {
253-
if( key != null )
254-
key.cancel();
255-
onError(ex);
253+
if( key != null )
254+
key.cancel();
255+
onError(ex);
256256
} catch (RuntimeException ex) {
257257
ex.printStackTrace();
258258
} catch (NoSuchAlgorithmException ex) {

src/net/tootallnate/websocket/drafts/Draft_75.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import net.tootallnate.websocket.HandshakeBuilder;
1717
import net.tootallnate.websocket.Handshakedata;
1818
import net.tootallnate.websocket.FramedataImpl1;
19+
import net.tootallnate.websocket.exeptions.InvalidHandshakeException;
1920

2021
public class Draft_75 extends Draft {
2122

@@ -102,6 +103,7 @@ public ByteBuffer createBinaryFrame( Framedata framedata ) {
102103
b.put(START_OF_FRAME);
103104
b.put(pay);
104105
b.put(END_OF_FRAME);
106+
b.rewind();
105107
return b;
106108
}
107109

@@ -122,7 +124,7 @@ public List<Framedata> createFrames( byte[] binary , boolean mask ) {
122124

123125
@Override
124126
public boolean acceptHandshakeAsClient( Handshakedata request , Handshakedata response ) {
125-
throw new RuntimeException ( "not yet implemented" );
127+
return true;//TODO validate the handshake
126128
}
127129

128130
@Override
@@ -131,8 +133,15 @@ public HandshakeBuilder postProcessHandshakeRequestAsClient( HandshakeBuilder re
131133
}
132134

133135
@Override
134-
public HandshakeBuilder postProcessHandshakeResponseAsServer( Handshakedata request , HandshakeBuilder response ) {
135-
throw new RuntimeException ( "not yet implemented" );
136+
public HandshakeBuilder postProcessHandshakeResponseAsServer( Handshakedata request , HandshakeBuilder response ) throws InvalidHandshakeException {
137+
super.postProcessHandshakeResponseAsServer( request , response );
138+
response.setHttpStatusMessage( "Web Socket Protocol Handshake" );
139+
response.put ( "Upgrade" , "WebSocket" );
140+
response.put( "Sec-WebSocket-Origin" , request.getFieldValue( "Origin" ) );
141+
String location = "ws://" + request.getFieldValue("Host") + request.getResourceDescriptor();
142+
response.put( "Sec-WebSocket-Location" , location );
143+
//TODO handle Sec-WebSocket-Protocol and Set-Cookie
144+
return response;
136145
}
137146

138147
}

src/net/tootallnate/websocket/drafts/Draft_76.java

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,56 +13,21 @@
1313
import net.tootallnate.websocket.Handshakedata;
1414
import net.tootallnate.websocket.HandshakedataImpl1;
1515
import net.tootallnate.websocket.WebSocket;
16+
import net.tootallnate.websocket.exeptions.InvalidHandshakeException;
1617

1718
public class Draft_76 extends Draft_75 {
1819

1920
@Override
2021
public boolean acceptHandshakeAsClient( Handshakedata request , Handshakedata response ) {
21-
if( response.getContent ().length >= 20 && new String ( response.getContent () ).endsWith ( "\r\n\r\n" )){
22-
/*{ //TODO finish function
23-
if ( reply == null ) {
24-
return false;
25-
}
26-
byte[] challenge = new byte[] {
27-
(byte)( this.number1 >> 24 ),
28-
(byte)( (this.number1 << 8) >> 24 ),
29-
(byte)( (this.number1 << 16) >> 24 ),
30-
(byte)( (this.number1 << 24) >> 24 ),
31-
(byte)( this.number2 >> 24 ),
32-
(byte)( (this.number2 << 8) >> 24 ),
33-
(byte)( (this.number2 << 16) >> 24 ),
34-
(byte)( (this.number2 << 24) >> 24 ),
35-
this.key3[0],
36-
this.key3[1],
37-
this.key3[2],
38-
this.key3[3],
39-
this.key3[4],
40-
this.key3[5],
41-
this.key3[6],
42-
this.key3[7]
43-
};
44-
MessageDigest md5;
45-
try {
46-
md5 = MessageDigest.getInstance( "MD5" );
47-
} catch ( NoSuchAlgorithmException e ) {
48-
throw new RuntimeException ( e );//Will never occur on a valid jre.
49-
}
50-
byte[] expected = md5.digest(challenge);
51-
for (int i = 0; i < reply.length; i++) {
52-
if (expected[i] != reply[i]) {
53-
return false;
54-
}
55-
}
56-
}*/
57-
return true;
58-
}
59-
return false;
22+
return super.acceptHandshakeAsClient( request , response );//TODO validate the handshake
6023
}
6124

6225
@Override
6326
public boolean acceptHandshakeAsServer( Handshakedata handshakedata ) {
6427

65-
if( handshakedata.getFieldValue ( "Sec-WebSocket-Key1" ).equals ( "8" ) && new String ( handshakedata.getContent () ).endsWith ( "\r\n\r\n" ))
28+
if( !handshakedata.getFieldValue ( "Sec-WebSocket-Key1" ).isEmpty()
29+
&& !handshakedata.getFieldValue ( "Sec-WebSocket-Key1" ).isEmpty()
30+
/*new String ( handshakedata.getContent () ).endsWith ( "\r\n\r\n" )*/)
6631
return true;
6732
return false;
6833
}
@@ -101,14 +66,69 @@ private String generateKey() {
10166
public HandshakeBuilder postProcessHandshakeRequestAsClient( HandshakeBuilder request ) {
10267
request.put ( "Sec-WebSocket-Key1" , this.generateKey() );
10368
request.put ( "Sec-WebSocket-Key2" , this.generateKey() );
69+
byte[] key3 = new byte[8];
70+
(new Random()).nextBytes( key3 );
71+
request.setContent( key3 );
10472
return request;
10573

10674
}
107-
108-
10975

11076
@Override
111-
public HandshakeBuilder postProcessHandshakeResponseAsServer( Handshakedata response , HandshakeBuilder request ) {
112-
throw new RuntimeException ( "not yet implemented" );
77+
public HandshakeBuilder postProcessHandshakeResponseAsServer( Handshakedata request , HandshakeBuilder response ) throws InvalidHandshakeException {
78+
super.postProcessHandshakeResponseAsServer( request , response );
79+
String key1 = request.getFieldValue("Sec-WebSocket-Key1");
80+
String key2 = request.getFieldValue("Sec-WebSocket-Key2");
81+
byte[] key3 = request.getContent();
82+
if (key1 == null || key2 == null || key3 == null || key3.length != 8) {
83+
throw new InvalidHandshakeException("Bad keys");
84+
}
85+
response.setContent( createChallenge( key1 , key2 , key3 ) );
86+
return response;
87+
}
88+
89+
private static byte[] getPart(String key) throws InvalidHandshakeException {
90+
try {
91+
long keyNumber = Long.parseLong(key.replaceAll("[^0-9]",""));
92+
long keySpace = key.split("\u0020").length - 1;
93+
long part = new Long(keyNumber / keySpace);
94+
return new byte[] {
95+
(byte)( part >> 24 ),
96+
(byte)( (part << 8) >> 24 ),
97+
(byte)( (part << 16) >> 24 ),
98+
(byte)( (part << 24) >> 24 )
99+
};
100+
} catch ( NumberFormatException e ) {
101+
throw new InvalidHandshakeException("invalid Sec-WebSocket-Key (/key2/ or /key3/)");
102+
}
103+
}
104+
105+
106+
public static byte[] createChallenge( String key1 , String key2, byte[] key3 ) throws InvalidHandshakeException{
107+
byte[] part1 = getPart(key1);
108+
byte[] part2 = getPart(key2);
109+
byte[] challenge = new byte[16];
110+
challenge[0] = part1[0];
111+
challenge[1] = part1[1];
112+
challenge[2] = part1[2];
113+
challenge[3] = part1[3];
114+
challenge[4] = part2[0];
115+
challenge[5] = part2[1];
116+
challenge[6] = part2[2];
117+
challenge[7] = part2[3];
118+
challenge[8] = key3[0];
119+
challenge[9] = key3[1];
120+
challenge[10] = key3[2];
121+
challenge[11] = key3[3];
122+
challenge[12] = key3[4];
123+
challenge[13] = key3[5];
124+
challenge[14] = key3[6];
125+
challenge[15] = key3[7];
126+
MessageDigest md5;
127+
try {
128+
md5 = MessageDigest.getInstance("MD5");
129+
} catch ( NoSuchAlgorithmException e ) {
130+
throw new RuntimeException(e);
131+
}
132+
return md5.digest(challenge);
113133
}
114134
}

0 commit comments

Comments
 (0)