Skip to content

Commit a65070d

Browse files
committed
Hacked in absolut minimal Draft10 support. Currently the older drafts do not work.
1 parent 2c10a04 commit a65070d

File tree

6 files changed

+231
-49
lines changed

6 files changed

+231
-49
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*.class
2+
*.svn
23

src/net/tootallnate/websocket/WebSocket.java

Lines changed: 171 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package net.tootallnate.websocket;
22

33
import java.io.IOException;
4-
import java.io.UnsupportedEncodingException;
54
import java.nio.ByteBuffer;
65
import java.nio.channels.NotYetConnectedException;
76
import java.nio.channels.SocketChannel;
7+
import java.nio.charset.Charset;
88
import java.security.NoSuchAlgorithmException;
9+
import java.util.HashMap;
910
import java.util.concurrent.BlockingQueue;
1011

12+
import websync.WebSocketClientTest;
13+
1114
/**
1215
* Represents one end (client or server) of a single WebSocket connection.
1316
* Takes care of the "handshake" phase, then allows for easy sending of
@@ -30,9 +33,9 @@ public final class WebSocket {
3033
public static final int DEFAULT_PORT = 80;
3134
/**
3235
* The WebSocket protocol expects UTF-8 encoded bytes.
33-
*/
34-
public static final String UTF8_CHARSET = "UTF-8";
35-
/**
36+
*/
37+
public final static Charset UTF8_CHARSET = Charset.forName ( "UTF-8" );
38+
/**
3639
* The byte representing CR, or Carriage Return, or \r
3740
*/
3841
public static final byte CR = (byte)0x0D;
@@ -48,6 +51,7 @@ public final class WebSocket {
4851
* The byte representing the end of a WebSocket text frame.
4952
*/
5053
public static final byte END_OF_FRAME = (byte)0xFF;
54+
5155

5256

5357
// INSTANCE PROPERTIES /////////////////////////////////////////////////////
@@ -92,6 +96,9 @@ public final class WebSocket {
9296
private Object bufferQueueMutex = new Object();
9397

9498
private boolean readingState = false;
99+
100+
private WebSocketDraft draft;
101+
95102

96103

97104
// CONSTRUCTOR /////////////////////////////////////////////////////////////
@@ -133,17 +140,28 @@ void handleRead() throws IOException, NoSuchAlgorithmException {
133140
if (bytesRead == -1) {
134141
close();
135142
} else if (bytesRead > 0) {
136-
for(int i = 0; i < bytesRead; i++) {
137-
buffer.rewind();
138-
buffer.put(socketBuffer.get(i));
139-
140-
this.buffer.rewind();
141-
142-
if (!this.handshakeComplete)
143-
recieveHandshake();
144-
else
145-
recieveFrame();
146-
}
143+
System.out.print( "got: {\n" + new String( socketBuffer.array() , 0 , bytesRead ) + "\n}" );
144+
if ( !this.handshakeComplete && recieveHandshake ( bytesRead ) ) {
145+
}
146+
else{
147+
recieveFrame(bytesRead);
148+
}
149+
if(true)
150+
return;
151+
{
152+
for( int i = 0 ; i < bytesRead ; i++ ) {
153+
buffer.rewind();
154+
buffer.put( socketBuffer.get( i ) );
155+
156+
this.buffer.rewind();
157+
158+
if (!this.handshakeComplete)
159+
recieveHandshake( bytesRead );
160+
//recieveHandshake75_76( bytesRead);
161+
else
162+
recieveFrame75_76();
163+
}
164+
}
147165
}
148166
}
149167

@@ -164,7 +182,17 @@ public void close() throws IOException {
164182
*/
165183
public boolean send(String text) throws IOException {
166184
if (!this.handshakeComplete) throw new NotYetConnectedException();
167-
if (text == null) throw new NullPointerException("Cannot send 'null' data to a WebSocket.");
185+
if ( text == null ) throw new NullPointerException( "Cannot send 'null' data to a WebSocket." );
186+
if( draft == WebSocketDraft.DRAFT10 ){
187+
return send10( text);
188+
}
189+
else
190+
return send75_76( text );
191+
}
192+
193+
public boolean send75_76(String text) throws IOException {
194+
if (!this.handshakeComplete) throw new NotYetConnectedException();
195+
if ( text == null ) throw new NullPointerException( "Cannot send 'null' data to a WebSocket." );
168196

169197
// Get 'text' into a WebSocket "frame" of bytes
170198
byte[] textBytes = text.getBytes(UTF8_CHARSET);
@@ -177,7 +205,7 @@ public boolean send(String text) throws IOException {
177205
// See if we have any backlog that needs to be sent first
178206
if (handleWrite()) {
179207
// Write the ByteBuffer to the socket
180-
this.socketChannel.write(b);
208+
channelWrite( b );
181209
}
182210

183211
// If we didn't get it all sent, add it to the buffer of buffers
@@ -192,6 +220,31 @@ public boolean send(String text) throws IOException {
192220
return true;
193221
}
194222

223+
private boolean send10( String text ) throws IOException {
224+
boolean mask=false;
225+
byte[] mes = text.getBytes ( UTF8_CHARSET );
226+
ByteBuffer b = ByteBuffer.allocate ( 2 + mes.length+( mask ? 4 : 0 ) );
227+
ByteBuffer maskkey= ByteBuffer.allocate ( 4 );
228+
byte one = ( byte ) -127;
229+
//if(mask)
230+
// one |= 1;
231+
b.put ( one ); // b1 controll
232+
b.put ( ( byte ) mes.length ); // b2 length
233+
if( mask ){
234+
maskkey.putInt ( Integer.MIN_VALUE );
235+
b.put ( maskkey.array () );
236+
for( int i = 0 ; i < mes.length ; i++){
237+
b.put( ( byte ) ( mes[i] ^ maskkey.get ( i % 4 ) ) );
238+
}
239+
}
240+
else
241+
b.put ( mes );
242+
243+
b.rewind ();
244+
channelWrite ( b );
245+
return true;
246+
}
247+
195248
boolean hasBufferedData() {
196249
return !this.bufferQueue.isEmpty();
197250
}
@@ -204,7 +257,7 @@ boolean handleWrite() throws IOException {
204257
synchronized (this.bufferQueueMutex) {
205258
ByteBuffer buffer = this.bufferQueue.peek();
206259
while (buffer != null) {
207-
this.socketChannel.write(buffer);
260+
channelWrite( buffer );
208261
if (buffer.remaining() > 0) {
209262
return false; // Didn't finish this buffer. There's more to send.
210263
} else {
@@ -221,7 +274,7 @@ public SocketChannel socketChannel() {
221274
}
222275

223276
// PRIVATE INSTANCE METHODS ////////////////////////////////////////////////
224-
private void recieveFrame() {
277+
private void recieveFrame75_76() {
225278
byte newestByte = this.buffer.get();
226279

227280
if (newestByte == START_OF_FRAME && !readingState) { // Beginning of Frame
@@ -234,13 +287,7 @@ private void recieveFrame() {
234287
// currentFrame will be null if END_OF_FRAME was send directly after
235288
// START_OF_FRAME, thus we will send 'null' as the sent message.
236289
if (this.currentFrame != null) {
237-
try {
238-
textFrame = new String(this.currentFrame.array(), UTF8_CHARSET);
239-
} catch (UnsupportedEncodingException ex) {
240-
// TODO: Fire an 'onError' handler here
241-
ex.printStackTrace();
242-
textFrame = "";
243-
}
290+
textFrame = new String( this.currentFrame.array() , UTF8_CHARSET );
244291
}
245292
this.wsl.onMessage(this, textFrame);
246293

@@ -254,15 +301,45 @@ private void recieveFrame() {
254301
this.currentFrame = frame;
255302
}
256303
}
304+
305+
private void recieveFrame( int read) throws IOException {
306+
byte[] b = ByteBuffer.allocate ( read ).put ( socketBuffer.array () , 0 , read ).array ();//socketBuffer.array ()
307+
boolean FIN = b[0] >> 8 != 0;
308+
boolean MASK = ( b[0] &~1 ) != 0;
309+
int payloadlength = (byte)( b[1] & ~(byte)128 );
310+
System.out.println ( "pll: " + payloadlength );
311+
int maskskeytart = payloadlength < 125 ? 1 + 1 : payloadlength == 126 ? 1 + 2 : 1 + 8 ;
312+
int extdatastart = maskskeytart+4; //TODO allow extdata
313+
int payloadstart = extdatastart;
314+
byte[] maskskey = ByteBuffer.allocate ( 4 ).put ( b , maskskeytart , 4 ).array ();
315+
//demasking the payloaddata
316+
ByteBuffer payload=ByteBuffer.allocate ( payloadlength );
317+
for( int i = 0 ; i < payloadlength ; i++ ){
318+
payload.put ( ( byte ) ( (byte)b[payloadstart + i] ^ (byte)maskskey[ i%4 ] ) );
319+
}
320+
321+
/*b[1]&=~(byte)128;
322+
for( int i = 0 ; i < payloadlength ; i++ ){
323+
b[ payloadstart + i - 4 ] = ( byte ) ( (byte)b[payloadstart + i] ^ (byte)maskskey[ i%4 ] );
324+
}
325+
byte[] c = ByteBuffer.allocate ( read-4 ).put ( b , 0 , read-4 ).array ();
326+
channelWrite ( ByteBuffer.wrap ( c ) );*/
327+
this.wsl.onMessage ( this , new String ( payload.array () ,UTF8_CHARSET ) );
328+
329+
}
257330

258-
private void recieveHandshake() throws IOException, NoSuchAlgorithmException {
259-
ByteBuffer ch = ByteBuffer.allocate((this.remoteHandshake != null ? this.remoteHandshake.capacity() : 0) + this.buffer.capacity());
260-
if (this.remoteHandshake != null) {
331+
private boolean recieveHandshake75_76(int bytesRead) throws IOException {
332+
ByteBuffer ch = ByteBuffer.allocate( ( this.remoteHandshake != null ? this.remoteHandshake.capacity() : 0 ) + this.buffer.capacity() );
333+
if ( this.remoteHandshake != null ) {
261334
this.remoteHandshake.rewind();
262-
ch.put(this.remoteHandshake);
335+
ch.put( this.remoteHandshake );
263336
}
264337
ch.put(this.buffer);
265338
this.remoteHandshake = ch;
339+
//byte[] h2 = ByteBuffer.wrap(socketBuffer.array(),0, bytesRead).array();
340+
ByteBuffer b=ByteBuffer.allocate( bytesRead );
341+
b.put( socketBuffer.array() , 0 , bytesRead );
342+
byte[] h2 = b.array();
266343
byte[] h = this.remoteHandshake.array();
267344
// If the ByteBuffer contains 16 random bytes, and ends with
268345
// 0x0D 0x0A 0x0D 0x0A (or two CRLFs), then the client
@@ -289,7 +366,8 @@ private void recieveHandshake() throws IOException, NoSuchAlgorithmException {
289366
h[h.length-2],
290367
h[h.length-1]
291368
});
292-
369+
draft = WebSocketDraft.DRAFT76;
370+
return true;
293371
// If the ByteBuffer contains 8 random bytes,ends with
294372
// 0x0D 0x0A 0x0D 0x0A (or two CRLFs), and the response
295373
// contains Sec-WebSocket-Key1 then the client
@@ -298,6 +376,7 @@ private void recieveHandshake() throws IOException, NoSuchAlgorithmException {
298376
&& h[h.length-11] == LF
299377
&& h[h.length-10] == CR
300378
&& h[h.length-9] == LF) && new String(this.remoteHandshake.array(), UTF8_CHARSET).contains("Sec-WebSocket-Key1")) {
379+
System.out.println("------>Draft 76 Sec-WebSocket-Key1 8<------"+new String(h));
301380
completeHandshake(new byte[] {
302381
h[h.length-8],
303382
h[h.length-7],
@@ -308,19 +387,66 @@ private void recieveHandshake() throws IOException, NoSuchAlgorithmException {
308387
h[h.length-2],
309388
h[h.length-1]
310389
});
311-
390+
draft = WebSocketDraft.DRAFT76;
391+
return true;
392+
312393
// Consider Draft 75, and the Flash Security Policy
313394
// Request edge-case.
314395
} else if ((h.length>=4 && h[h.length-4] == CR
315396
&& h[h.length-3] == LF
316397
&& h[h.length-2] == CR
317398
&& h[h.length-1] == LF) && !(new String(this.remoteHandshake.array(), UTF8_CHARSET).contains("Sec")) ||
318399
(h.length==23 && h[h.length-1] == 0) ) {
400+
System.out.println("------>Draft 75 / flash<------"+new String(h));
319401
completeHandshake(null);
320-
}
402+
draft = WebSocketDraft.DRAFT75;
403+
return true;
404+
}
405+
else
406+
return false;
321407
}
408+
409+
private boolean recieveHandshake( int readcount ) throws IOException {
410+
ByteBuffer message = ByteBuffer.allocate ( readcount );
411+
message.put ( socketBuffer.array () , 0 , readcount );
412+
byte[] lines = message.array ();
413+
int previndex = 0;
414+
int index = findNewLine ( lines , previndex );
415+
if ( index == -1 )
416+
return false;
417+
String line = new String ( lines , previndex , index - previndex );
418+
if ( line.startsWith ( "GET" ) == false )
419+
return false;
420+
previndex = index + 2;
421+
index = findNewLine ( lines , previndex );
422+
423+
HashMap<String,String> elements = new HashMap<String,String> ( 10 );
424+
while ( index != -1 ) {
425+
line = new String ( lines , previndex , index - previndex );
426+
if ( index != previndex ) {
427+
String[] pair = line.split ( ":" , 2 );
428+
if ( pair.length != 2 )
429+
return false;
430+
elements.put ( pair[ 0 ] , pair[ 1 ] );
431+
System.out.println ( "Line: " + line );
432+
}
433+
previndex = index + 2;
434+
index = findNewLine ( lines , previndex );
435+
}
436+
draft = WebSocketDraft.DRAFT10;
437+
return this.handshakeComplete = this.wsl.onHandshakeRecieved ( this , elements );
438+
}
439+
440+
private static int findNewLine( byte[] arr , int offset ) {
441+
int len = arr.length - 1;
442+
for ( int i = offset ; i < len ; i++ )
443+
if( arr[i] == (byte)'\r' && arr[ i + 1 ] == (byte)'\n' )
444+
return i;
445+
return -1;
446+
}
447+
322448

323-
private void completeHandshake(byte[] handShakeBody) throws IOException, NoSuchAlgorithmException {
449+
private void completeHandshake(byte[] handShakeBody) throws IOException {
324450
byte[] handshakeBytes = this.remoteHandshake.array();
325451
String handshake = new String(handshakeBytes, UTF8_CHARSET);
326452
this.handshakeComplete = true;
@@ -330,5 +456,16 @@ private void completeHandshake(byte[] handShakeBody) throws IOException, NoSuchA
330456
close();
331457
}
332458
}
459+
public void channelWrite(ByteBuffer buf) throws IOException{
460+
System.out.println("write: {\n"+new String(buf.array())+"}");
461+
printBytes ( buf , buf.capacity () );
462+
socketChannel.write(buf);
463+
}
464+
public void printBytes(ByteBuffer buf, int len){
465+
for( int i = 0 ; i < 2 && i < len; i++ ){
466+
System.out.println (Integer.toBinaryString ( buf.get ( i ) ));
467+
}
468+
469+
}
333470

334471
}

src/net/tootallnate/websocket/WebSocketClient.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ private void sendHandshake() throws IOException {
276276
}
277277
else
278278
{
279-
conn.socketChannel().write(ByteBuffer.wrap(request.getBytes(WebSocket.UTF8_CHARSET)));
279+
conn.channelWrite(ByteBuffer.wrap(request.getBytes(WebSocket.UTF8_CHARSET)));
280280
}
281281
}
282282

@@ -327,11 +327,11 @@ private String generateKey() {
327327
* @throws IOException When socket related I/O errors occur.
328328
* @throws NoSuchAlgorithmException
329329
*/
330-
public boolean onHandshakeRecieved(WebSocket conn, String handshake, byte[] reply) throws IOException, NoSuchAlgorithmException {
330+
public boolean onHandshakeRecieved(WebSocket conn, String handshake, byte[] reply) throws IOException {
331331
// TODO: Do some parsing of the returned handshake, and close connection
332332
// (return false) if we recieved anything unexpected.
333-
if(this.draft == WebSocketDraft.DRAFT76) {
334-
if (reply == null) {
333+
if( this.draft == WebSocketDraft.DRAFT76 ) {
334+
if ( reply == null ) {
335335
return false;
336336
}
337337
byte[] challenge = new byte[] {
@@ -352,7 +352,12 @@ public boolean onHandshakeRecieved(WebSocket conn, String handshake, byte[] repl
352352
this.key3[6],
353353
this.key3[7]
354354
};
355-
MessageDigest md5 = MessageDigest.getInstance("MD5");
355+
MessageDigest md5;
356+
try {
357+
md5 = MessageDigest.getInstance( "MD5" );
358+
} catch ( NoSuchAlgorithmException e ) {
359+
throw new RuntimeException ( e );//Will never occur on a valid jre.
360+
}
356361
byte[] expected = md5.digest(challenge);
357362
for (int i = 0; i < reply.length; i++) {
358363
if (expected[i] != reply[i]) {

src/net/tootallnate/websocket/WebSocketDraft.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,5 @@
44
* Enum for WebSocket Draft
55
*/
66
public enum WebSocketDraft {
7-
AUTO,
8-
DRAFT75,
9-
DRAFT76
7+
AUTO , DRAFT75 , DRAFT76 , DRAFT10
108
}

0 commit comments

Comments
 (0)