1 package org.eclipse.jetty.websocket;
2
3 import java.io.IOException;
4 import java.math.BigInteger;
5 import java.security.MessageDigest;
6 import java.security.NoSuchAlgorithmException;
7
8 import org.eclipse.jetty.io.Buffer;
9 import org.eclipse.jetty.io.EndPoint;
10 import org.eclipse.jetty.io.EofException;
11 import org.eclipse.jetty.util.TypeUtil;
12 import org.eclipse.jetty.util.log.Log;
13
14
15
16
17
18
19
20
21
22 public class WebSocketGenerator
23 {
24 final private WebSocketBuffers _buffers;
25 final private EndPoint _endp;
26 private Buffer _buffer;
27
28 public WebSocketGenerator(WebSocketBuffers buffers, EndPoint endp)
29 {
30 _buffers=buffers;
31 _endp=endp;
32 }
33
34 public synchronized void addFrame(byte frame,byte[] content, int blockFor) throws IOException
35 {
36 addFrame(frame,content,0,content.length,blockFor);
37 }
38
39 public synchronized void addFrame(byte frame,byte[] content, int offset, int length, int blockFor) throws IOException
40 {
41 if (_buffer==null)
42 _buffer=_buffers.getDirectBuffer();
43
44 if (_buffer.space() == 0)
45 expelBuffer(blockFor);
46
47 bufferPut(frame, blockFor);
48
49 if (isLengthFrame(frame))
50 {
51
52
53
54
55
56
57
58 int lengthBytes = new BigInteger(String.valueOf(length)).bitLength() / 7 + 1;
59 for (int i = lengthBytes - 1; i > 0; --i)
60 {
61 byte lengthByte = (byte)(0x80 | (0x7F & (length >> 7 * i)));
62 bufferPut(lengthByte, blockFor);
63 }
64 bufferPut((byte)(0x7F & length), blockFor);
65 }
66
67 int remaining = length;
68 while (remaining > 0)
69 {
70 int chunk = remaining < _buffer.space() ? remaining : _buffer.space();
71 _buffer.put(content, offset + (length - remaining), chunk);
72 remaining -= chunk;
73 if (_buffer.space() > 0)
74 {
75 if (!isLengthFrame(frame))
76 _buffer.put((byte)0xFF);
77
78 flushBuffer();
79 }
80 else
81 {
82
83 expelBuffer(blockFor);
84 if (remaining == 0)
85 {
86 if (!isLengthFrame(frame))
87 _buffer.put((byte)0xFF);
88
89 flushBuffer();
90 }
91 }
92 }
93 }
94
95 private synchronized boolean isLengthFrame(byte frame)
96 {
97 return (frame & WebSocket.LENGTH_FRAME) == WebSocket.LENGTH_FRAME;
98 }
99
100 private synchronized void bufferPut(byte datum, long blockFor) throws IOException
101 {
102 if (_buffer==null)
103 _buffer=_buffers.getDirectBuffer();
104 _buffer.put(datum);
105 if (_buffer.space() == 0)
106 expelBuffer(blockFor);
107 }
108
109 public synchronized void addFrame(byte frame, String content, int blockFor) throws IOException
110 {
111 byte[] bytes = content.getBytes("UTF-8");
112 addFrame(frame, bytes, 0, bytes.length, blockFor);
113 }
114
115 public synchronized int flush(long blockFor) throws IOException
116 {
117 return expelBuffer(blockFor);
118 }
119
120 public synchronized int flush() throws IOException
121 {
122 int flushed = flushBuffer();
123 if (_buffer!=null && _buffer.length()==0)
124 {
125 _buffers.returnBuffer(_buffer);
126 _buffer=null;
127 }
128 return flushed;
129 }
130
131 private synchronized int flushBuffer() throws IOException
132 {
133 if (!_endp.isOpen())
134 throw new EofException();
135
136 if (_buffer!=null)
137 return _endp.flush(_buffer);
138
139 return 0;
140 }
141
142 private synchronized int expelBuffer(long blockFor) throws IOException
143 {
144 if (_buffer==null)
145 return 0;
146 int result = flushBuffer();
147 _buffer.compact();
148 if (!_endp.isBlocking())
149 {
150 while (_buffer.space()==0)
151 {
152
153
154 boolean ready = _endp.blockWritable(blockFor);
155 if (!ready)
156 throw new IOException("Write timeout");
157
158 result += flushBuffer();
159 _buffer.compact();
160 }
161 }
162 return result;
163 }
164
165 public synchronized boolean isBufferEmpty()
166 {
167 return _buffer==null || _buffer.length()==0;
168 }
169
170 public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3)
171 {
172 try
173 {
174 MessageDigest md = MessageDigest.getInstance("MD5");
175 byte [] fodder = new byte[16];
176
177 fodder[0]=(byte)(0xff&(key1>>24));
178 fodder[1]=(byte)(0xff&(key1>>16));
179 fodder[2]=(byte)(0xff&(key1>>8));
180 fodder[3]=(byte)(0xff&key1);
181 fodder[4]=(byte)(0xff&(key2>>24));
182 fodder[5]=(byte)(0xff&(key2>>16));
183 fodder[6]=(byte)(0xff&(key2>>8));
184 fodder[7]=(byte)(0xff&key2);
185 for (int i=0;i<8;i++)
186 fodder[8+i]=key3[i];
187 md.update(fodder);
188 byte[] result=md.digest();
189 return result;
190 }
191 catch (NoSuchAlgorithmException e)
192 {
193 throw new IllegalStateException(e);
194 }
195 }
196
197 }