websocket-server – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Linq;
5 using System.Net.Sockets;
6 using System.Security.Cryptography;
7 using System.Text;
8 using System.IO;
9 using WebSockets.Events;
10  
11 namespace WebSockets.Common
12 {
13 public abstract class WebSocketBase
14 {
15 private readonly IWebSocketLogger _logger;
16 private readonly object _sendLocker;
17 private Stream _stream;
18 private WebSocketFrameWriter _writer;
19 private WebSocketOpCode _multiFrameOpcode;
20 private Socket _socket;
21 protected bool _isOpen;
22  
23 public event EventHandler ConnectionOpened;
24 public event EventHandler<ConnectionCloseEventArgs> ConnectionClose;
25 public event EventHandler<PingEventArgs> Ping;
26 public event EventHandler<PingEventArgs> Pong;
27 public event EventHandler<TextFrameEventArgs> TextFrame;
28 public event EventHandler<TextMultiFrameEventArgs> TextMultiFrame;
29 public event EventHandler<BinaryFrameEventArgs> BinaryFrame;
30 public event EventHandler<BinaryMultiFrameEventArgs> BinaryMultiFrame;
31  
32 public WebSocketBase(IWebSocketLogger logger)
33 {
34 _logger = logger;
35 _sendLocker = new object();
36 _isOpen = false;
37 }
38  
39 protected void OpenBlocking(Stream stream, Socket socket)
40 {
41 _socket = socket;
42 _stream = stream;
43 _writer = new WebSocketFrameWriter(stream);
44 PerformHandshake(stream);
45 _isOpen = true;
46 MainReadLoop();
47 }
48  
49 protected virtual void Send(WebSocketOpCode opCode, byte[] toSend, bool isLastFrame)
50 {
51 if (_isOpen)
52 {
53 lock (_sendLocker)
54 {
55 if (_isOpen)
56 {
57 _writer.Write(opCode, toSend, isLastFrame);
58 }
59 }
60 }
61 }
62  
63 protected virtual void Send(WebSocketOpCode opCode, byte[] toSend)
64 {
65 Send(opCode, toSend, true);
66 }
67  
68 protected virtual void Send(byte[] toSend)
69 {
70 Send(WebSocketOpCode.BinaryFrame, toSend, true);
71 }
72  
73 protected virtual void Send(string text)
74 {
75 byte[] bytes = Encoding.UTF8.GetBytes(text);
76 Send(WebSocketOpCode.TextFrame, bytes, true);
77 }
78  
79 protected virtual void OnConnectionOpened()
80 {
81 if (ConnectionOpened != null)
82 {
83 ConnectionOpened(this, new EventArgs());
84 }
85 }
86  
87 protected virtual void OnPing(byte[] payload)
88 {
89 Send(WebSocketOpCode.Pong, payload);
90  
91 if (Ping != null)
92 {
93 Ping(this, new PingEventArgs(payload));
94 }
95 }
96  
97 protected virtual void OnPong(byte[] payload)
98 {
99 if (Pong != null)
100 {
101 Pong(this, new PingEventArgs(payload));
102 }
103 }
104  
105 protected virtual void OnTextFrame(string text)
106 {
107 if (TextFrame != null)
108 {
109 TextFrame(this, new TextFrameEventArgs(text));
110 }
111 }
112  
113 protected virtual void OnTextMultiFrame(string text, bool isLastFrame)
114 {
115 if (TextMultiFrame != null)
116 {
117 TextMultiFrame(this, new TextMultiFrameEventArgs(text, isLastFrame));
118 }
119 }
120  
121 protected virtual void OnBinaryFrame(byte[] payload)
122 {
123 if (BinaryFrame != null)
124 {
125 BinaryFrame(this, new BinaryFrameEventArgs(payload));
126 }
127 }
128  
129 protected virtual void OnBinaryMultiFrame(byte[] payload, bool isLastFrame)
130 {
131 if (BinaryMultiFrame != null)
132 {
133 BinaryMultiFrame(this, new BinaryMultiFrameEventArgs(payload, isLastFrame));
134 }
135 }
136  
137 protected virtual void OnConnectionClose(byte[] payload)
138 {
139 ConnectionCloseEventArgs args = GetConnectionCloseEventArgsFromPayload(payload);
140  
141 if (args.Reason == null)
142 {
143 _logger.Information(this.GetType(), "Received web socket close message: {0}", args.Code);
144 }
145 else
146 {
147 _logger.Information(this.GetType(), "Received web socket close message: Code '{0}' Reason '{1}'", args.Code, args.Reason);
148 }
149  
150 if (ConnectionClose != null)
151 {
152 ConnectionClose(this, args);
153 }
154 }
155  
156 protected abstract void PerformHandshake(Stream stream);
157  
158 /// <summary>
159 /// Combines the key supplied by the client with a guid and returns the sha1 hash of the combination
160 /// </summary>
161 protected string ComputeSocketAcceptString(string secWebSocketKey)
162 {
163 // this is a guid as per the web socket spec
164 const string webSocketGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
165  
166 string concatenated = secWebSocketKey + webSocketGuid;
167 byte[] concatenatedAsBytes = Encoding.UTF8.GetBytes(concatenated);
168 byte[] sha1Hash = SHA1.Create().ComputeHash(concatenatedAsBytes);
169 string secWebSocketAccept = Convert.ToBase64String(sha1Hash);
170 return secWebSocketAccept;
171 }
172  
173 protected ConnectionCloseEventArgs GetConnectionCloseEventArgsFromPayload(byte[] payload)
174 {
175 if (payload.Length >= 2)
176 {
177 using (MemoryStream stream = new MemoryStream(payload))
178 {
179 ushort code = BinaryReaderWriter.ReadUShortExactly(stream, false);
180  
181 try
182 {
183 WebSocketCloseCode closeCode = (WebSocketCloseCode)code;
184  
185 if (payload.Length > 2)
186 {
187 string reason = Encoding.UTF8.GetString(payload, 2, payload.Length - 2);
188 return new ConnectionCloseEventArgs(closeCode, reason);
189 }
190 else
191 {
192 return new ConnectionCloseEventArgs(closeCode, null);
193 }
194 }
195 catch (InvalidCastException)
196 {
197 _logger.Warning(this.GetType(), "Close code {0} not recognised", code);
198 return new ConnectionCloseEventArgs(WebSocketCloseCode.Normal, null);
199 }
200 }
201 }
202  
203 return new ConnectionCloseEventArgs(WebSocketCloseCode.Normal, null);
204 }
205  
206 private void MainReadLoop()
207 {
208 Stream stream = _stream;
209 OnConnectionOpened();
210 WebSocketFrameReader reader = new WebSocketFrameReader();
211 List<WebSocketFrame> fragmentedFrames = new List<WebSocketFrame>();
212  
213 while (true)
214 {
215 WebSocketFrame frame;
216  
217 try
218 {
219 frame = reader.Read(stream, _socket);
220 if (frame == null)
221 {
222 return;
223 }
224 }
225 catch (ObjectDisposedException)
226 {
227 return;
228 }
229  
230 // if we have received unexpected data
231 if (!frame.IsValid)
232 {
233 return;
234 }
235  
236 if (frame.OpCode == WebSocketOpCode.ContinuationFrame)
237 {
238 switch (_multiFrameOpcode)
239 {
240 case WebSocketOpCode.TextFrame:
241 String data = Encoding.UTF8.GetString(frame.DecodedPayload, 0, frame.DecodedPayload.Length);
242 OnTextMultiFrame(data, frame.IsFinBitSet);
243 break;
244 case WebSocketOpCode.BinaryFrame:
245 OnBinaryMultiFrame(frame.DecodedPayload, frame.IsFinBitSet);
246 break;
247 }
248 }
249 else
250 {
251 switch (frame.OpCode)
252 {
253 case WebSocketOpCode.ConnectionClose:
254 OnConnectionClose(frame.DecodedPayload);
255 return;
256 case WebSocketOpCode.Ping:
257 OnPing(frame.DecodedPayload);
258 break;
259 case WebSocketOpCode.Pong:
260 OnPong(frame.DecodedPayload);
261 break;
262 case WebSocketOpCode.TextFrame:
263 String data = Encoding.UTF8.GetString(frame.DecodedPayload, 0, frame.DecodedPayload.Length);
264 if (frame.IsFinBitSet)
265 {
266 OnTextFrame(data);
267 }
268 else
269 {
270 _multiFrameOpcode = frame.OpCode;
271 OnTextMultiFrame(data, frame.IsFinBitSet);
272 }
273 break;
274 case WebSocketOpCode.BinaryFrame:
275 if (frame.IsFinBitSet)
276 {
277 OnBinaryFrame(frame.DecodedPayload);
278 }
279 else
280 {
281 _multiFrameOpcode = frame.OpCode;
282 OnBinaryMultiFrame(frame.DecodedPayload, frame.IsFinBitSet);
283 }
284 break;
285 }
286 }
287 }
288 }
289 }
290 }