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
20 import javax.servlet.http.HttpServletRequest;
21 import javax.servlet.http.HttpServletResponse;
22
23 import org.eclipse.jetty.io.AsyncEndPoint;
24 import org.eclipse.jetty.io.Buffer;
25 import org.eclipse.jetty.io.ByteArrayBuffer;
26 import org.eclipse.jetty.io.Connection;
27 import org.eclipse.jetty.io.EndPoint;
28 import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
29 import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
30 import org.eclipse.jetty.util.log.Log;
31
32 public class WebSocketConnectionD00 implements WebSocketConnection
33 {
34 final IdleCheck _idle;
35 final EndPoint _endp;
36 final WebSocketParser _parser;
37 final WebSocketGenerator _generator;
38 final long _timestamp;
39 final WebSocket _websocket;
40 String _key1;
41 String _key2;
42 ByteArrayBuffer _hixieBytes;
43
44 public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint,int draft)
45 throws IOException
46 {
47 this(websocket,endpoint,new WebSocketBuffers(8192),System.currentTimeMillis(),300000,draft);
48 }
49
50 public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, int draft)
51 throws IOException
52 {
53
54 if (endpoint instanceof AsyncEndPoint)
55 ((AsyncEndPoint)endpoint).cancelIdle();
56
57 _endp = endpoint;
58 _endp.setMaxIdleTime(maxIdleTime);
59
60 _timestamp = timestamp;
61 _websocket = websocket;
62
63
64 switch(draft)
65 {
66 case 1:
67 _generator = new WebSocketGeneratorD01(buffers, _endp);
68 _parser = new WebSocketParserD01(buffers, endpoint, new FrameHandlerD1(this,_websocket));
69 break;
70 default:
71 _generator = new WebSocketGeneratorD00(buffers, _endp);
72 _parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD0(_websocket));
73 }
74
75
76 if (_endp instanceof SelectChannelEndPoint)
77 {
78 final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp;
79 scep.cancelIdle();
80 _idle=new IdleCheck()
81 {
82 public void access(EndPoint endp)
83 {
84 scep.scheduleIdle();
85 }
86 };
87 scep.scheduleIdle();
88 }
89 else
90 {
91 _idle = new IdleCheck()
92 {
93 public void access(EndPoint endp)
94 {}
95 };
96 }
97 }
98
99 public void setHixieKeys(String key1,String key2)
100 {
101 _key1=key1;
102 _key2=key2;
103 _hixieBytes=new IndirectNIOBuffer(16);
104 }
105
106 public Connection handle() throws IOException
107 {
108 try
109 {
110
111 if (_hixieBytes!=null)
112 {
113
114
115 Buffer buffer=_parser.getBuffer();
116 if (buffer!=null && buffer.length()>0)
117 {
118 int l=buffer.length();
119 if (l>(8-_hixieBytes.length()))
120 l=8-_hixieBytes.length();
121 _hixieBytes.put(buffer.peek(buffer.getIndex(),l));
122 buffer.skip(l);
123 }
124
125
126 while(_endp.isOpen())
127 {
128
129 if (_hixieBytes.length()==8)
130 {
131
132
133 doTheHixieHixieShake();
134 _endp.flush(_hixieBytes);
135 _hixieBytes=null;
136 _endp.flush();
137 break;
138 }
139
140
141 int filled=_endp.fill(_hixieBytes);
142 if (filled<0)
143 {
144 _endp.close();
145 break;
146 }
147 }
148
149 _websocket.onConnect(this);
150 return this;
151 }
152
153
154 boolean progress=true;
155
156 while (progress)
157 {
158 int flushed=_generator.flush();
159 int filled=_parser.parseNext();
160
161 progress = flushed>0 || filled>0;
162
163 if (filled<0 || flushed<0)
164 {
165 _endp.close();
166 break;
167 }
168 }
169 }
170 catch(IOException e)
171 {
172 try
173 {
174 _endp.close();
175 }
176 catch(IOException e2)
177 {
178 Log.ignore(e2);
179 }
180 throw e;
181 }
182 finally
183 {
184 if (_endp.isOpen())
185 {
186 _idle.access(_endp);
187 checkWriteable();
188 }
189 }
190 return this;
191 }
192
193 private void doTheHixieHixieShake()
194 {
195 byte[] result=WebSocketConnectionD00.doTheHixieHixieShake(
196 WebSocketConnectionD00.hixieCrypt(_key1),
197 WebSocketConnectionD00.hixieCrypt(_key2),
198 _hixieBytes.asArray());
199 _hixieBytes.clear();
200 _hixieBytes.put(result);
201 }
202
203 public boolean isOpen()
204 {
205 return _endp!=null&&_endp.isOpen();
206 }
207
208 public boolean isIdle()
209 {
210 return _parser.isBufferEmpty() && _generator.isBufferEmpty();
211 }
212
213 public boolean isSuspended()
214 {
215 return false;
216 }
217
218 public void closed()
219 {
220 _websocket.onDisconnect();
221 }
222
223 public long getTimeStamp()
224 {
225 return _timestamp;
226 }
227
228
229
230
231
232 public void sendMessage(String content) throws IOException
233 {
234 sendMessage(WebSocket.SENTINEL_FRAME,content);
235 }
236
237
238
239
240
241 public void sendMessage(byte frame, String content) throws IOException
242 {
243 _generator.addFrame(frame,content,_endp.getMaxIdleTime());
244 _generator.flush();
245 checkWriteable();
246 _idle.access(_endp);
247 }
248
249
250
251
252
253 public void sendMessage(byte opcode, byte[] content, int offset, int length) throws IOException
254 {
255 _generator.addFrame(opcode,content,offset,length,_endp.getMaxIdleTime());
256 _generator.flush();
257 checkWriteable();
258 _idle.access(_endp);
259 }
260
261
262
263
264
265 public void sendFragment(boolean more,byte opcode, byte[] content, int offset, int length) throws IOException
266 {
267 _generator.addFragment(more,opcode,content,offset,length,_endp.getMaxIdleTime());
268 _generator.flush();
269 checkWriteable();
270 _idle.access(_endp);
271 }
272
273 public void disconnect()
274 {
275 try
276 {
277 _generator.flush(_endp.getMaxIdleTime());
278 _endp.close();
279 }
280 catch(IOException e)
281 {
282 Log.ignore(e);
283 }
284 }
285
286 public void fillBuffersFrom(Buffer buffer)
287 {
288 _parser.fill(buffer);
289 }
290
291
292 private void checkWriteable()
293 {
294 if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint)
295 ((AsyncEndPoint)_endp).scheduleWrite();
296 }
297
298
299 static long hixieCrypt(String key)
300 {
301
302
303
304 long number=0;
305 int spaces=0;
306 for (char c : key.toCharArray())
307 {
308 if (Character.isDigit(c))
309 number=number*10+(c-'0');
310 else if (c==' ')
311 spaces++;
312 }
313 return number/spaces;
314 }
315
316 public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3)
317 {
318 try
319 {
320 MessageDigest md = MessageDigest.getInstance("MD5");
321 byte [] fodder = new byte[16];
322
323 fodder[0]=(byte)(0xff&(key1>>24));
324 fodder[1]=(byte)(0xff&(key1>>16));
325 fodder[2]=(byte)(0xff&(key1>>8));
326 fodder[3]=(byte)(0xff&key1);
327 fodder[4]=(byte)(0xff&(key2>>24));
328 fodder[5]=(byte)(0xff&(key2>>16));
329 fodder[6]=(byte)(0xff&(key2>>8));
330 fodder[7]=(byte)(0xff&key2);
331 for (int i=0;i<8;i++)
332 fodder[8+i]=key3[i];
333 md.update(fodder);
334 byte[] result=md.digest();
335 return result;
336 }
337 catch (NoSuchAlgorithmException e)
338 {
339 throw new IllegalStateException(e);
340 }
341 }
342
343 private interface IdleCheck
344 {
345 void access(EndPoint endp);
346 }
347
348 public void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException
349 {
350 String uri=request.getRequestURI();
351 String query=request.getQueryString();
352 if (query!=null && query.length()>0)
353 uri+="?"+query;
354 String host=request.getHeader("Host");
355
356 String key1 = request.getHeader("Sec-WebSocket-Key1");
357 if (key1!=null)
358 {
359 String key2 = request.getHeader("Sec-WebSocket-Key2");
360 setHixieKeys(key1,key2);
361
362 response.setHeader("Upgrade","WebSocket");
363 response.addHeader("Connection","Upgrade");
364 response.addHeader("Sec-WebSocket-Origin",origin);
365 response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
366 if (subprotocol!=null)
367 response.addHeader("Sec-WebSocket-Protocol",subprotocol);
368 response.sendError(101,"WebSocket Protocol Handshake");
369 }
370 else
371 {
372 response.setHeader("Upgrade","WebSocket");
373 response.addHeader("Connection","Upgrade");
374 response.addHeader("WebSocket-Origin",origin);
375 response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
376 if (subprotocol!=null)
377 response.addHeader("WebSocket-Protocol",subprotocol);
378 response.sendError(101,"Web Socket Protocol Handshake");
379 response.flushBuffer();
380 _websocket.onConnect(this);
381 }
382 }
383 }