1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.websocket;
15
16 import java.io.IOException;
17 import java.security.MessageDigest;
18 import java.security.NoSuchAlgorithmException;
19 import java.util.Collections;
20 import java.util.List;
21
22 import javax.servlet.http.HttpServletRequest;
23 import javax.servlet.http.HttpServletResponse;
24
25 import org.eclipse.jetty.http.HttpURI;
26 import org.eclipse.jetty.io.AbstractConnection;
27 import org.eclipse.jetty.io.AsyncEndPoint;
28 import org.eclipse.jetty.io.Buffer;
29 import org.eclipse.jetty.io.ByteArrayBuffer;
30 import org.eclipse.jetty.io.Connection;
31 import org.eclipse.jetty.io.EndPoint;
32 import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
33 import org.eclipse.jetty.util.QuotedStringTokenizer;
34 import org.eclipse.jetty.util.StringUtil;
35 import org.eclipse.jetty.util.log.Log;
36 import org.eclipse.jetty.util.log.Logger;
37 import org.eclipse.jetty.websocket.WebSocket.OnFrame;
38
39 public class WebSocketConnectionD00 extends AbstractConnection implements WebSocketConnection, WebSocket.FrameConnection
40 {
41 private static final Logger LOG = Log.getLogger(WebSocketConnectionD00.class);
42
43 public final static byte LENGTH_FRAME=(byte)0x80;
44 public final static byte SENTINEL_FRAME=(byte)0x00;
45
46 final WebSocketParser _parser;
47 final WebSocketGenerator _generator;
48 final WebSocket _websocket;
49 final String _protocol;
50 String _key1;
51 String _key2;
52 ByteArrayBuffer _hixieBytes;
53
54 public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
55 throws IOException
56 {
57 super(endpoint,timestamp);
58
59 _endp.setMaxIdleTime(maxIdleTime);
60
61 _websocket = websocket;
62 _protocol=protocol;
63
64 _generator = new WebSocketGeneratorD00(buffers, _endp);
65 _parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD00(_websocket));
66 }
67
68
69 public org.eclipse.jetty.websocket.WebSocket.Connection getConnection()
70 {
71 return this;
72 }
73
74
75
76 public void setHixieKeys(String key1,String key2)
77 {
78 _key1=key1;
79 _key2=key2;
80 _hixieBytes=new IndirectNIOBuffer(16);
81 }
82
83
84 public Connection handle() throws IOException
85 {
86 try
87 {
88
89 if (_hixieBytes!=null)
90 {
91
92
93 Buffer buffer=_parser.getBuffer();
94 if (buffer!=null && buffer.length()>0)
95 {
96 int l=buffer.length();
97 if (l>(8-_hixieBytes.length()))
98 l=8-_hixieBytes.length();
99 _hixieBytes.put(buffer.peek(buffer.getIndex(),l));
100 buffer.skip(l);
101 }
102
103
104 while(_endp.isOpen())
105 {
106
107 if (_hixieBytes.length()==8)
108 {
109
110
111 doTheHixieHixieShake();
112 _endp.flush(_hixieBytes);
113 _hixieBytes=null;
114 _endp.flush();
115 break;
116 }
117
118
119 int filled=_endp.fill(_hixieBytes);
120 if (filled<0)
121 {
122 _endp.close();
123 break;
124 }
125 }
126
127 if (_websocket instanceof OnFrame)
128 ((OnFrame)_websocket).onHandshake(this);
129 _websocket.onOpen(this);
130 return this;
131 }
132
133
134 boolean progress=true;
135
136 while (progress)
137 {
138 int flushed=_generator.flush();
139 int filled=_parser.parseNext();
140
141 progress = flushed>0 || filled>0;
142
143 if (filled<0 || flushed<0)
144 {
145 _endp.close();
146 break;
147 }
148 }
149 }
150 catch(IOException e)
151 {
152 LOG.debug(e);
153 try
154 {
155 _endp.close();
156 }
157 catch(IOException e2)
158 {
159 LOG.ignore(e2);
160 }
161 throw e;
162 }
163 finally
164 {
165 if (_endp.isOpen())
166 {
167 if (_endp.isInputShutdown() && _generator.isBufferEmpty())
168 _endp.close();
169 else
170 checkWriteable();
171
172 checkWriteable();
173 }
174 }
175 return this;
176 }
177
178
179 public void onInputShutdown() throws IOException
180 {
181
182 }
183
184
185 private void doTheHixieHixieShake()
186 {
187 byte[] result=WebSocketConnectionD00.doTheHixieHixieShake(
188 WebSocketConnectionD00.hixieCrypt(_key1),
189 WebSocketConnectionD00.hixieCrypt(_key2),
190 _hixieBytes.asArray());
191 _hixieBytes.clear();
192 _hixieBytes.put(result);
193 }
194
195
196 public boolean isOpen()
197 {
198 return _endp!=null&&_endp.isOpen();
199 }
200
201
202 public boolean isIdle()
203 {
204 return _parser.isBufferEmpty() && _generator.isBufferEmpty();
205 }
206
207
208 public boolean isSuspended()
209 {
210 return false;
211 }
212
213
214 public void onClose()
215 {
216 _websocket.onClose(WebSocketConnectionD06.CLOSE_NORMAL,"");
217 }
218
219
220
221
222 public void sendMessage(String content) throws IOException
223 {
224 byte[] data = content.getBytes(StringUtil.__UTF8);
225 _generator.addFrame((byte)0,SENTINEL_FRAME,data,0,data.length);
226 _generator.flush();
227 checkWriteable();
228 }
229
230
231 public void sendMessage(byte[] data, int offset, int length) throws IOException
232 {
233 _generator.addFrame((byte)0,LENGTH_FRAME,data,offset,length);
234 _generator.flush();
235 checkWriteable();
236 }
237
238
239 public boolean isMore(byte flags)
240 {
241 return (flags&0x8) != 0;
242 }
243
244
245
246
247
248 public void sendControl(byte code, byte[] content, int offset, int length) throws IOException
249 {
250 }
251
252
253 public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException
254 {
255 _generator.addFrame((byte)0,opcode,content,offset,length);
256 _generator.flush();
257 checkWriteable();
258 }
259
260
261 public void close(int code, String message)
262 {
263 throw new UnsupportedOperationException();
264 }
265
266
267 public void disconnect()
268 {
269 close();
270 }
271
272
273 public void close()
274 {
275 try
276 {
277 _generator.flush();
278 _endp.close();
279 }
280 catch(IOException e)
281 {
282 LOG.ignore(e);
283 }
284 }
285
286
287 public void fillBuffersFrom(Buffer buffer)
288 {
289 _parser.fill(buffer);
290 }
291
292
293
294 private void checkWriteable()
295 {
296 if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint)
297 ((AsyncEndPoint)_endp).scheduleWrite();
298 }
299
300
301 static long hixieCrypt(String key)
302 {
303
304
305
306 long number=0;
307 int spaces=0;
308 for (char c : key.toCharArray())
309 {
310 if (Character.isDigit(c))
311 number=number*10+(c-'0');
312 else if (c==' ')
313 spaces++;
314 }
315 return number/spaces;
316 }
317
318 public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3)
319 {
320 try
321 {
322 MessageDigest md = MessageDigest.getInstance("MD5");
323 byte [] fodder = new byte[16];
324
325 fodder[0]=(byte)(0xff&(key1>>24));
326 fodder[1]=(byte)(0xff&(key1>>16));
327 fodder[2]=(byte)(0xff&(key1>>8));
328 fodder[3]=(byte)(0xff&key1);
329 fodder[4]=(byte)(0xff&(key2>>24));
330 fodder[5]=(byte)(0xff&(key2>>16));
331 fodder[6]=(byte)(0xff&(key2>>8));
332 fodder[7]=(byte)(0xff&key2);
333 System.arraycopy(key3, 0, fodder, 8, 8);
334 md.update(fodder);
335 return md.digest();
336 }
337 catch (NoSuchAlgorithmException e)
338 {
339 throw new IllegalStateException(e);
340 }
341 }
342
343 public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
344 {
345 String uri=request.getRequestURI();
346 String query=request.getQueryString();
347 if (query!=null && query.length()>0)
348 uri+="?"+query;
349 uri=new HttpURI(uri).toString();
350 String host=request.getHeader("Host");
351
352 String origin=request.getHeader("Sec-WebSocket-Origin");
353 if (origin==null)
354 origin=request.getHeader("Origin");
355 if (origin!=null)
356 origin= QuotedStringTokenizer.quoteIfNeeded(origin, "\r\n");
357
358
359 String key1 = request.getHeader("Sec-WebSocket-Key1");
360
361 if (key1!=null)
362 {
363 String key2 = request.getHeader("Sec-WebSocket-Key2");
364 setHixieKeys(key1,key2);
365
366 response.setHeader("Upgrade","WebSocket");
367 response.addHeader("Connection","Upgrade");
368 if (origin!=null)
369 response.addHeader("Sec-WebSocket-Origin",origin);
370 response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
371 if (subprotocol!=null)
372 response.addHeader("Sec-WebSocket-Protocol",subprotocol);
373 response.sendError(101,"WebSocket Protocol Handshake");
374 }
375 else
376 {
377 response.setHeader("Upgrade","WebSocket");
378 response.addHeader("Connection","Upgrade");
379 response.addHeader("WebSocket-Origin",origin);
380 response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
381 if (subprotocol!=null)
382 response.addHeader("WebSocket-Protocol",subprotocol);
383 response.sendError(101,"Web Socket Protocol Handshake");
384 response.flushBuffer();
385 if (_websocket instanceof OnFrame)
386 ((OnFrame)_websocket).onHandshake(this);
387 _websocket.onOpen(this);
388 }
389 }
390
391 public void setMaxTextMessageSize(int size)
392 {
393 }
394
395 public void setMaxIdleTime(int ms)
396 {
397 try
398 {
399 _endp.setMaxIdleTime(ms);
400 }
401 catch(IOException e)
402 {
403 LOG.warn(e);
404 }
405 }
406
407 public void setMaxBinaryMessageSize(int size)
408 {
409 }
410
411 public int getMaxTextMessageSize()
412 {
413 return -1;
414 }
415
416 public int getMaxIdleTime()
417 {
418 return _endp.getMaxIdleTime();
419 }
420
421 public int getMaxBinaryMessageSize()
422 {
423 return -1;
424 }
425
426 public String getProtocol()
427 {
428 return _protocol;
429 }
430
431 static class FrameHandlerD00 implements WebSocketParser.FrameHandler
432 {
433 final WebSocket _websocket;
434
435 FrameHandlerD00(WebSocket websocket)
436 {
437 _websocket=websocket;
438 }
439
440 public void onFrame(byte flags, byte opcode, Buffer buffer)
441 {
442 try
443 {
444 byte[] array=buffer.array();
445
446 if (opcode==0)
447 {
448 if (_websocket instanceof WebSocket.OnTextMessage)
449 ((WebSocket.OnTextMessage)_websocket).onMessage(buffer.toString(StringUtil.__UTF8));
450 }
451 else
452 {
453 if (_websocket instanceof WebSocket.OnBinaryMessage)
454 ((WebSocket.OnBinaryMessage)_websocket).onMessage(array,buffer.getIndex(),buffer.length());
455 }
456 }
457 catch(Throwable th)
458 {
459 LOG.warn(th);
460 }
461 }
462
463 public void close(int code,String message)
464 {
465 }
466 }
467
468 public boolean isMessageComplete(byte flags)
469 {
470 return true;
471 }
472
473 public byte binaryOpcode()
474 {
475 return LENGTH_FRAME;
476 }
477
478 public byte textOpcode()
479 {
480 return SENTINEL_FRAME;
481 }
482
483 public boolean isControl(byte opcode)
484 {
485 return false;
486 }
487
488 public boolean isText(byte opcode)
489 {
490 return (opcode&LENGTH_FRAME)==0;
491 }
492
493 public boolean isBinary(byte opcode)
494 {
495 return (opcode&LENGTH_FRAME)!=0;
496 }
497
498 public boolean isContinuation(byte opcode)
499 {
500 return false;
501 }
502
503 public boolean isClose(byte opcode)
504 {
505 return false;
506 }
507
508 public boolean isPing(byte opcode)
509 {
510 return false;
511 }
512
513 public boolean isPong(byte opcode)
514 {
515 return false;
516 }
517
518 public List<Extension> getExtensions()
519 {
520 return Collections.emptyList();
521 }
522
523 public byte continuationOpcode()
524 {
525 return 0;
526 }
527
528 public byte finMask()
529 {
530 return 0;
531 }
532
533 public void setAllowFrameFragmentation(boolean allowFragmentation)
534 {
535 }
536
537 public boolean isAllowFrameFragmentation()
538 {
539 return false;
540 }
541 }