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