12
|
1 ???using System; |
|
2 using System.IO; |
|
3 using System.Security.Cryptography; |
|
4 using System.Text; |
|
5 using System.Threading; |
|
6 using UCIS.Util; |
|
7 |
|
8 namespace UCIS.Net.HTTP { |
|
9 public class WebSocketPacketStream : PacketStream { |
|
10 Stream baseStream; |
|
11 Boolean negotiationDone = false; |
|
12 Boolean closed = false; |
|
13 ManualResetEvent negotiationEvent = new ManualResetEvent(false); |
|
14 Boolean binaryProtocol = false; |
|
15 int wsProtocol = -1; |
|
16 |
|
17 public WebSocketPacketStream(HTTPContext context) { |
|
18 try { |
|
19 String ConnectionHeader = context.GetRequestHeader("Connection"); //can be comma-separated list |
|
20 Boolean ConnectionUpgrade = ConnectionHeader != null && ConnectionHeader.Contains("Upgrade"); |
|
21 Boolean UpgradeWebsocket = "WebSocket".Equals(context.GetRequestHeader("Upgrade"), StringComparison.OrdinalIgnoreCase); |
|
22 String SecWebSocketKey = context.GetRequestHeader("Sec-WebSocket-Key"); |
|
23 String SecWebSocketKey1 = context.GetRequestHeader("Sec-WebSocket-Key1"); |
|
24 String SecWebSocketKey2 = context.GetRequestHeader("Sec-WebSocket-Key2"); |
|
25 String SecWebSocketProtocol = context.GetRequestHeader("Sec-WebSocket-Protocol"); |
|
26 String[] SecWebSocketProtocols = SecWebSocketProtocol == null ? null : SecWebSocketProtocol.Split(new String[] { ", " }, StringSplitOptions.None); |
|
27 if (!ConnectionUpgrade || !UpgradeWebsocket || SecWebSocketProtocols == null || SecWebSocketProtocols.Length == 0) goto Failure; |
|
28 binaryProtocol = SecWebSocketProtocol != null && Array.IndexOf(SecWebSocketProtocols, "binary") != -1; |
|
29 if (SecWebSocketKey != null) { |
|
30 wsProtocol = 13; |
|
31 String hashedKey; |
|
32 using (SHA1 sha1 = new SHA1Managed()) { |
|
33 Byte[] hashable = Encoding.ASCII.GetBytes(SecWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); |
|
34 Byte[] hash = sha1.ComputeHash(hashable); |
|
35 hashedKey = Convert.ToBase64String(hash, Base64FormattingOptions.None); |
|
36 } |
|
37 context.SuppressStandardHeaders = true; |
|
38 context.SendStatus(101); |
|
39 context.SendHeader("Connection", "Upgrade"); |
|
40 context.SendHeader("Upgrade", "websocket"); |
|
41 context.SendHeader("Sec-WebSocket-Accept", hashedKey); |
|
42 context.SendHeader("Sec-WebSocket-Protocol", binaryProtocol ? "binary" : "base64"); |
|
43 baseStream = context.GetResponseStream(); |
|
44 } else if (SecWebSocketKey1 != null && SecWebSocketKey2 != null) { |
|
45 wsProtocol = 100; |
|
46 Byte[] key = new Byte[4 + 4 + 8]; |
|
47 CalculateHybi00MagicNumber(SecWebSocketKey1, key, 0); |
|
48 CalculateHybi00MagicNumber(SecWebSocketKey2, key, 4); |
|
49 context.SuppressStandardHeaders = true; |
|
50 context.SendStatus(101); |
|
51 context.SendHeader("Connection", "Upgrade"); |
|
52 context.SendHeader("Upgrade", "websocket"); |
|
53 context.SendHeader("Sec-WebSocket-Protocol", binaryProtocol ? "binary" : "base64"); |
|
54 context.SendHeader("Sec-WebSocket-Origin", context.GetRequestHeader("Origin")); |
|
55 context.SendHeader("Sec-WebSocket-Location", "ws://" + context.GetRequestHeader("Host") + context.RequestPath); |
|
56 baseStream = context.GetResponseStream(); |
|
57 ReadAllBytes(key, 8, 8); |
|
58 using (MD5 md5 = new MD5CryptoServiceProvider()) key = md5.ComputeHash(key); |
|
59 baseStream.Write(key, 0, key.Length); |
|
60 } else { |
|
61 goto Failure; |
|
62 } |
|
63 if (closed) baseStream.Close(); |
37
|
64 } catch (Exception) { |
12
|
65 closed = true; |
|
66 context.Close(); |
|
67 } finally { |
|
68 negotiationDone = true; |
|
69 negotiationEvent.Set(); |
|
70 } |
|
71 return; |
|
72 Failure: |
|
73 closed = true; |
|
74 context.SendErrorAndClose(400); |
|
75 return; |
|
76 } |
|
77 |
|
78 private void CalculateHybi00MagicNumber(String s, Byte[] obuf, int opos) { |
|
79 long number = 0; |
|
80 long spaces = 0; |
|
81 foreach (Char c in s) { |
|
82 if (c == ' ') { |
|
83 spaces++; |
|
84 } else if (c >= '0' && c <= '9') { |
|
85 number = number * 10 + (c - '0'); |
|
86 } |
|
87 } |
|
88 number /= spaces; |
|
89 obuf[opos++] = (Byte)(number >> 24); |
|
90 obuf[opos++] = (Byte)(number >> 16); |
|
91 obuf[opos++] = (Byte)(number >> 8); |
|
92 obuf[opos++] = (Byte)(number >> 0); |
|
93 } |
|
94 |
|
95 public override bool CanRead { get { return negotiationDone && !closed; } } |
|
96 public override bool CanSeek { get { return false; } } |
|
97 public override bool CanWrite { get { return negotiationDone && !closed; } } |
|
98 public override void Flush() { } |
|
99 |
|
100 public override void Close() { |
|
101 closed = true; |
|
102 base.Close(); |
|
103 if (baseStream != null) baseStream.Close(); |
|
104 } |
|
105 public override bool CanTimeout { get { return baseStream.CanTimeout; } } |
|
106 public override int ReadTimeout { |
|
107 get { return baseStream.ReadTimeout; } |
|
108 set { baseStream.ReadTimeout = value; } |
|
109 } |
|
110 public override int WriteTimeout { |
|
111 get { return baseStream.WriteTimeout; } |
|
112 set { baseStream.WriteTimeout = value; } |
|
113 } |
|
114 |
|
115 public override long Length { get { throw new NotSupportedException(); } } |
|
116 public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } |
|
117 public override void SetLength(long value) { throw new NotSupportedException(); } |
|
118 public override long Position { |
|
119 get { throw new NotSupportedException(); } |
|
120 set { throw new NotSupportedException(); } |
|
121 } |
|
122 |
|
123 private void ReadAllBytes(Byte[] buffer, int offset, int count) { |
|
124 while (count > 0) { |
|
125 int l = baseStream.Read(buffer, offset, count); |
|
126 if (l <= 0) throw new EndOfStreamException(); |
|
127 offset += l; |
|
128 count -= l; |
|
129 } |
|
130 } |
|
131 |
|
132 Byte[] leftOver = null; |
|
133 public override int Read(byte[] buffer, int offset, int count) { |
|
134 Byte[] packet = leftOver; |
|
135 leftOver = null; |
|
136 if (packet == null) packet = ReadPacket(); |
|
137 if (packet == null) return 0; |
|
138 if (count > packet.Length) count = packet.Length; |
|
139 Buffer.BlockCopy(packet, 0, buffer, offset, count); |
|
140 if (packet.Length > count) leftOver = ArrayUtil.Slice(packet, count); |
|
141 return count; |
|
142 } |
|
143 public override byte[] ReadPacket() { |
|
144 if (leftOver != null) throw new InvalidOperationException("There is remaining data from a partial read"); |
|
145 negotiationEvent.WaitOne(); |
|
146 if (closed) throw new ObjectDisposedException("WebSocketPacketStream"); |
|
147 try { |
|
148 if (wsProtocol == 13) { |
|
149 Byte[] multipartbuffer = null; |
|
150 int multipartopcode = -1; |
|
151 while (true) { |
|
152 int flags = baseStream.ReadByte(); |
|
153 if (flags == -1) throw new EndOfStreamException(); |
|
154 UInt64 pllen = (byte)baseStream.ReadByte(); |
|
155 Boolean masked = (pllen & 128) != 0; |
|
156 pllen &= 127; |
|
157 if (pllen == 126) { |
|
158 pllen = (uint)baseStream.ReadByte() << 8; |
|
159 pllen |= (uint)baseStream.ReadByte(); |
|
160 } else if (pllen == 127) { |
|
161 pllen = (ulong)baseStream.ReadByte() << 56; |
|
162 pllen |= (ulong)baseStream.ReadByte() << 48; |
|
163 pllen |= (ulong)baseStream.ReadByte() << 40; |
|
164 pllen |= (ulong)baseStream.ReadByte() << 32; |
|
165 pllen |= (uint)baseStream.ReadByte() << 24; |
|
166 pllen |= (uint)baseStream.ReadByte() << 16; |
|
167 pllen |= (uint)baseStream.ReadByte() << 8; |
|
168 pllen |= (uint)baseStream.ReadByte(); |
|
169 } |
|
170 Byte[] mask = new Byte[4]; |
|
171 if (masked) ReadAllBytes(mask, 0, mask.Length); |
|
172 //Console.WriteLine("Read flags={0} masked={1} mask={2} len={3}", flags, masked, mask, pllen); |
|
173 Byte[] payload = new Byte[pllen]; // + (4 - (pllen % 4))]; |
|
174 ReadAllBytes(payload, 0, (int)pllen); |
|
175 if (masked) for (int i = 0; i < (int)pllen; i++) payload[i] ^= mask[i % 4]; |
|
176 int opcode = flags & 0x0f; |
|
177 Boolean fin = (flags & 0x80) != 0; |
|
178 if (opcode == 0) { |
|
179 //Console.WriteLine("WebSocket received continuation frame type {0}!", multipartopcode); |
|
180 Array.Resize(ref multipartbuffer, multipartbuffer.Length + payload.Length); |
|
181 payload.CopyTo(multipartbuffer, multipartbuffer.Length - payload.Length); |
|
182 opcode = -1; |
|
183 if (fin) { |
|
184 payload = multipartbuffer; |
|
185 opcode = multipartopcode; |
|
186 multipartbuffer = null; |
|
187 } |
|
188 } else if (!fin) { |
|
189 //Console.WriteLine("WebSocket received non-fin frame type {0}!", opcode); |
|
190 multipartbuffer = payload; |
|
191 multipartopcode = opcode; |
|
192 opcode = -1; |
|
193 } |
|
194 if (opcode == -1) { |
|
195 } else if (opcode == 0) { |
|
196 throw new NotSupportedException("WebSocket opcode 0 is not supported"); |
|
197 } else if (opcode == 1) { |
|
198 String text = Encoding.UTF8.GetString(payload); //, 0, pllen); |
|
199 return Convert.FromBase64String(text); |
|
200 } else if (opcode == 2) { |
|
201 return payload; // ArrayUtil.Slice(payload, 0, pllen); |
|
202 } else if (opcode == 8) { |
|
203 return null; |
|
204 } else if (opcode == 9) { |
|
205 //Console.WriteLine("WebSocket PING"); |
|
206 WriteFrame(10, payload, 0, (int)pllen); |
|
207 } else if (opcode == 10) { //PONG |
|
208 } else { |
|
209 //Console.WriteLine("WebSocket UNKNOWN OPCODE {0}", opcode); |
|
210 } |
|
211 } |
|
212 } else if (wsProtocol == 100) { |
|
213 int frameType = baseStream.ReadByte(); |
|
214 if (frameType == -1) throw new EndOfStreamException(); |
|
215 if ((frameType & 0x80) != 0) { |
|
216 int length = 0; |
|
217 while (true) { |
|
218 int b = baseStream.ReadByte(); |
|
219 if (b == -1) throw new EndOfStreamException(); |
|
220 length = (length << 7) | (b & 0x7f); |
|
221 if ((b & 0x80) == 0) break; |
|
222 } |
|
223 Byte[] buffer = new Byte[length]; |
|
224 ReadAllBytes(buffer, 0, length); |
|
225 if (frameType == 0xff && length == 0) { |
|
226 return null; |
|
227 } else { |
|
228 throw new InvalidOperationException(); |
|
229 } |
|
230 } else { |
|
231 using (MemoryStream ms = new MemoryStream()) { |
|
232 while (true) { |
|
233 int b = baseStream.ReadByte(); |
|
234 if (b == -1) throw new EndOfStreamException(); |
|
235 if (b == 0xff) break; |
|
236 ms.WriteByte((Byte)b); |
|
237 } |
|
238 if (frameType == 0x00) { |
|
239 ms.Seek(0, SeekOrigin.Begin); |
|
240 StreamReader reader = new StreamReader(ms, Encoding.UTF8, false); |
|
241 return Convert.FromBase64String(reader.ReadToEnd()); |
|
242 } else { |
|
243 throw new InvalidOperationException(); |
|
244 } |
|
245 } |
|
246 } |
|
247 } else { |
|
248 throw new InvalidOperationException(); |
|
249 } |
|
250 } catch (Exception ex) { |
|
251 Console.WriteLine(ex); |
|
252 throw; |
|
253 } |
|
254 } |
|
255 private delegate Byte[] ReadPacketDelegate(); |
|
256 ReadPacketDelegate readPacketDelegate; |
|
257 public override IAsyncResult BeginReadPacket(AsyncCallback callback, object state) { |
|
258 if (readPacketDelegate == null) readPacketDelegate = ReadPacket; |
|
259 return readPacketDelegate.BeginInvoke(callback, state); |
|
260 } |
|
261 public override byte[] EndReadPacket(IAsyncResult asyncResult) { |
|
262 return readPacketDelegate.EndInvoke(asyncResult); |
|
263 } |
|
264 public override void Write(byte[] buffer, int offset, int count) { |
|
265 negotiationEvent.WaitOne(); |
|
266 if (closed) throw new ObjectDisposedException("WebSocketPacketStream"); |
|
267 if (!binaryProtocol) { |
|
268 String encoded = Convert.ToBase64String(buffer, offset, count, Base64FormattingOptions.None); |
|
269 buffer = Encoding.ASCII.GetBytes(encoded); |
|
270 offset = 0; |
|
271 count = buffer.Length; |
|
272 } |
|
273 if (wsProtocol == 13) { |
|
274 WriteFrame(binaryProtocol ? (Byte)0x2 : (Byte)0x1, buffer, offset, count); |
|
275 } else if (wsProtocol == 100) { |
|
276 Byte[] bytes = new Byte[2 + count]; |
|
277 bytes[0] = 0x00; |
|
278 Buffer.BlockCopy(buffer, offset, bytes, 1, count); |
|
279 bytes[1 + count] = 0xff; |
|
280 baseStream.Write(bytes, 0, bytes.Length); |
|
281 } else { |
|
282 throw new InvalidOperationException(); |
|
283 } |
|
284 } |
|
285 private void WriteFrame(Byte opcode, Byte[] buffer, int offset, int count) { |
|
286 int pllen = count; |
|
287 int hlen = 2; |
|
288 if (pllen > 0xffff) hlen += 8; |
|
289 else if (pllen > 125) hlen += 2; |
|
290 Byte[] wbuf = new Byte[count + hlen]; |
|
291 wbuf[0] = (Byte)(opcode | 0x80); |
|
292 if (pllen > 0xffff) { |
|
293 wbuf[1] = 127; |
|
294 wbuf[2] = 0; |
|
295 wbuf[3] = 0; |
|
296 wbuf[4] = 0; |
|
297 wbuf[5] = 0; |
|
298 wbuf[6] = (Byte)(pllen >> 24); |
|
299 wbuf[7] = (Byte)(pllen >> 16); |
|
300 wbuf[8] = (Byte)(pllen >> 8); |
|
301 wbuf[9] = (Byte)(pllen >> 0); |
|
302 } else if (pllen > 125) { |
|
303 wbuf[1] = 126; |
|
304 wbuf[2] = (Byte)(pllen >> 8); |
|
305 wbuf[3] = (Byte)(pllen >> 0); |
|
306 } else { |
|
307 wbuf[1] = (Byte)pllen; |
|
308 } |
|
309 Buffer.BlockCopy(buffer, offset, wbuf, hlen, count); |
|
310 baseStream.Write(wbuf, 0, wbuf.Length); |
|
311 } |
|
312 } |
|
313 } |