1
1
package net .tootallnate .websocket ;
2
2
3
3
import java .io .IOException ;
4
- import java .io .UnsupportedEncodingException ;
5
4
import java .nio .ByteBuffer ;
6
5
import java .nio .channels .NotYetConnectedException ;
7
6
import java .nio .channels .SocketChannel ;
7
+ import java .nio .charset .Charset ;
8
8
import java .security .NoSuchAlgorithmException ;
9
+ import java .util .HashMap ;
9
10
import java .util .concurrent .BlockingQueue ;
10
11
12
+ import websync .WebSocketClientTest ;
13
+
11
14
/**
12
15
* Represents one end (client or server) of a single WebSocket connection.
13
16
* Takes care of the "handshake" phase, then allows for easy sending of
@@ -30,9 +33,9 @@ public final class WebSocket {
30
33
public static final int DEFAULT_PORT = 80 ;
31
34
/**
32
35
* 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
+ /**
36
39
* The byte representing CR, or Carriage Return, or \r
37
40
*/
38
41
public static final byte CR = (byte )0x0D ;
@@ -48,6 +51,7 @@ public final class WebSocket {
48
51
* The byte representing the end of a WebSocket text frame.
49
52
*/
50
53
public static final byte END_OF_FRAME = (byte )0xFF ;
54
+
51
55
52
56
53
57
// INSTANCE PROPERTIES /////////////////////////////////////////////////////
@@ -92,6 +96,9 @@ public final class WebSocket {
92
96
private Object bufferQueueMutex = new Object ();
93
97
94
98
private boolean readingState = false ;
99
+
100
+ private WebSocketDraft draft ;
101
+
95
102
96
103
97
104
// CONSTRUCTOR /////////////////////////////////////////////////////////////
@@ -133,17 +140,28 @@ void handleRead() throws IOException, NoSuchAlgorithmException {
133
140
if (bytesRead == -1 ) {
134
141
close ();
135
142
} 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
+ }
147
165
}
148
166
}
149
167
@@ -164,7 +182,17 @@ public void close() throws IOException {
164
182
*/
165
183
public boolean send (String text ) throws IOException {
166
184
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." );
168
196
169
197
// Get 'text' into a WebSocket "frame" of bytes
170
198
byte [] textBytes = text .getBytes (UTF8_CHARSET );
@@ -177,7 +205,7 @@ public boolean send(String text) throws IOException {
177
205
// See if we have any backlog that needs to be sent first
178
206
if (handleWrite ()) {
179
207
// Write the ByteBuffer to the socket
180
- this . socketChannel . write ( b );
208
+ channelWrite ( b );
181
209
}
182
210
183
211
// 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 {
192
220
return true ;
193
221
}
194
222
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
+
195
248
boolean hasBufferedData () {
196
249
return !this .bufferQueue .isEmpty ();
197
250
}
@@ -204,7 +257,7 @@ boolean handleWrite() throws IOException {
204
257
synchronized (this .bufferQueueMutex ) {
205
258
ByteBuffer buffer = this .bufferQueue .peek ();
206
259
while (buffer != null ) {
207
- this . socketChannel . write ( buffer );
260
+ channelWrite ( buffer );
208
261
if (buffer .remaining () > 0 ) {
209
262
return false ; // Didn't finish this buffer. There's more to send.
210
263
} else {
@@ -221,7 +274,7 @@ public SocketChannel socketChannel() {
221
274
}
222
275
223
276
// PRIVATE INSTANCE METHODS ////////////////////////////////////////////////
224
- private void recieveFrame () {
277
+ private void recieveFrame75_76 () {
225
278
byte newestByte = this .buffer .get ();
226
279
227
280
if (newestByte == START_OF_FRAME && !readingState ) { // Beginning of Frame
@@ -234,13 +287,7 @@ private void recieveFrame() {
234
287
// currentFrame will be null if END_OF_FRAME was send directly after
235
288
// START_OF_FRAME, thus we will send 'null' as the sent message.
236
289
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 );
244
291
}
245
292
this .wsl .onMessage (this , textFrame );
246
293
@@ -254,15 +301,45 @@ private void recieveFrame() {
254
301
this .currentFrame = frame ;
255
302
}
256
303
}
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
+ }
257
330
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 ) {
261
334
this .remoteHandshake .rewind ();
262
- ch .put (this .remoteHandshake );
335
+ ch .put ( this .remoteHandshake );
263
336
}
264
337
ch .put (this .buffer );
265
338
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 ();
266
343
byte [] h = this .remoteHandshake .array ();
267
344
// If the ByteBuffer contains 16 random bytes, and ends with
268
345
// 0x0D 0x0A 0x0D 0x0A (or two CRLFs), then the client
@@ -289,7 +366,8 @@ private void recieveHandshake() throws IOException, NoSuchAlgorithmException {
289
366
h [h .length -2 ],
290
367
h [h .length -1 ]
291
368
});
292
-
369
+ draft = WebSocketDraft .DRAFT76 ;
370
+ return true ;
293
371
// If the ByteBuffer contains 8 random bytes,ends with
294
372
// 0x0D 0x0A 0x0D 0x0A (or two CRLFs), and the response
295
373
// contains Sec-WebSocket-Key1 then the client
@@ -298,6 +376,7 @@ private void recieveHandshake() throws IOException, NoSuchAlgorithmException {
298
376
&& h [h .length -11 ] == LF
299
377
&& h [h .length -10 ] == CR
300
378
&& 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 ));
301
380
completeHandshake (new byte [] {
302
381
h [h .length -8 ],
303
382
h [h .length -7 ],
@@ -308,19 +387,66 @@ private void recieveHandshake() throws IOException, NoSuchAlgorithmException {
308
387
h [h .length -2 ],
309
388
h [h .length -1 ]
310
389
});
311
-
390
+ draft = WebSocketDraft .DRAFT76 ;
391
+ return true ;
392
+
312
393
// Consider Draft 75, and the Flash Security Policy
313
394
// Request edge-case.
314
395
} else if ((h .length >=4 && h [h .length -4 ] == CR
315
396
&& h [h .length -3 ] == LF
316
397
&& h [h .length -2 ] == CR
317
398
&& h [h .length -1 ] == LF ) && !(new String (this .remoteHandshake .array (), UTF8_CHARSET ).contains ("Sec" )) ||
318
399
(h .length ==23 && h [h .length -1 ] == 0 ) ) {
400
+ System .out .println ("------>Draft 75 / flash<------" +new String (h ));
319
401
completeHandshake (null );
320
- }
402
+ draft = WebSocketDraft .DRAFT75 ;
403
+ return true ;
404
+ }
405
+ else
406
+ return false ;
321
407
}
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
+
322
448
323
- private void completeHandshake (byte [] handShakeBody ) throws IOException , NoSuchAlgorithmException {
449
+ private void completeHandshake (byte [] handShakeBody ) throws IOException {
324
450
byte [] handshakeBytes = this .remoteHandshake .array ();
325
451
String handshake = new String (handshakeBytes , UTF8_CHARSET );
326
452
this .handshakeComplete = true ;
@@ -330,5 +456,16 @@ private void completeHandshake(byte[] handShakeBody) throws IOException, NoSuchA
330
456
close ();
331
457
}
332
458
}
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
+ }
333
470
334
471
}
0 commit comments