MTSC  1.0.5
Build TCP servers out of modules with handlers for communication, exception and logging.
WebsocketHandler.cs
1 using MTSC.Common.Http;
4 using System;
5 using System.Collections.Generic;
6 using System.Security.Cryptography;
7 using System.Text;
8 
9 namespace MTSC.Client.Handlers
10 {
14  public class WebsocketHandler : IHandler
15  {
16  private static string WebsocketHeaderAcceptKey = "Sec-WebSocket-Accept";
17  private static string WebsocketHeaderKey = "Sec-WebSocket-Key";
18  private static string WebsocketProtocolKey = "Sec-WebSocket-Protocol";
19  private static string WebsocketProtocolVersionKey = "Sec-WebSocket-Version";
20  private static string GlobalUniqueIdentifier = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
21  private static SHA1 sha1Provider = SHA1.Create();
22  private enum SocketState
23  {
24  Initial,
25  Handshaking,
26  Established,
27  Closed
28  }
29 
30  #region Fields
31  SocketState state = SocketState.Initial;
32  Queue<WebsocketMessage> messageQueue = new Queue<WebsocketMessage>();
33  List<IWebsocketModule> websocketModules = new List<IWebsocketModule>();
34  string expectedguid = string.Empty;
35  #endregion
36  #region Properties
37  public string WebsocketURI { get; set; }
38  #endregion
39  #region Constructors
40  public WebsocketHandler()
41  {
42  WebsocketURI = "/";
43  }
44  #endregion
45  #region Public Methods
46  public WebsocketHandler AddModule(IWebsocketModule websocketModule)
52  {
53  websocketModules.Add(websocketModule);
54  return this;
55  }
60  public void QueueMessage(WebsocketMessage message)
61  {
62  messageQueue.Enqueue(message);
63  }
64  #endregion
65  #region Handler Implementation
66  void IHandler.Disconnected(Client client)
67  {
68 
69  }
70 
71  bool IHandler.HandleReceivedMessage(Client client, Message message)
72  {
73  if(state == SocketState.Handshaking)
74  {
75  HttpMessage response = new HttpMessage();
76  response.ParseResponse(message.MessageBytes);
77  if(response.StatusCode == HttpMessage.StatusCodes.SwitchingProtocols &&
78  response["Upgrade"] == "websocket" &&
79  response[HttpMessage.GeneralHeadersEnum.Connection].ToLower() == "upgrade" &&
80  response[WebsocketHeaderAcceptKey].Trim() == expectedguid)
81  {
82  state = SocketState.Established;
83  foreach (IWebsocketModule websocketModule in websocketModules)
84  {
85  websocketModule.ConnectionInitialized(client, this);
86  }
87  }
88  return true;
89  }
90  else if(state == SocketState.Established)
91  {
92  WebsocketMessage receivedMessage = new WebsocketMessage(message.MessageBytes);
93  if (receivedMessage.Opcode == WebsocketMessage.Opcodes.Close)
94  {
95  foreach (IWebsocketModule websocketModule in websocketModules)
96  {
97  websocketModule.ConnectionClosed(client, this);
98  }
99  }
100  else
101  {
102  foreach (IWebsocketModule websocketModule in websocketModules)
103  {
104  if (websocketModule.HandleReceivedMessage(client, this, receivedMessage))
105  {
106  break;
107  }
108  }
109  }
110  return true;
111  }
112  return false;
113  }
114 
115  bool IHandler.HandleSendMessage(Client client, ref Message message)
116  {
117  return false;
118  }
119 
120  bool IHandler.InitializeConnection(Client client)
121  {
122  state = SocketState.Handshaking;
123  string handshakeGuid = Guid.NewGuid().ToString();
124  string handshakeKey = handshakeGuid+ GlobalUniqueIdentifier;
125  expectedguid = Convert.ToBase64String(sha1Provider.ComputeHash(Encoding.UTF8.GetBytes(handshakeKey)));
126  HttpMessage beginRequest = new HttpMessage();
127  beginRequest.Method = HttpMessage.MethodEnum.Get;
128  beginRequest.RequestURI = WebsocketURI;
129  beginRequest[HttpMessage.RequestHeadersEnum.Host] = client.Address;
130  beginRequest[HttpMessage.GeneralHeadersEnum.Connection] = "Upgrade";
131  beginRequest[WebsocketHeaderKey] = handshakeGuid;
132  beginRequest["Origin"] = client.Address;
133  beginRequest[WebsocketProtocolKey] = "chat";
134  beginRequest[WebsocketProtocolVersionKey] = "13";
135  client.QueueMessage(beginRequest.BuildRequest());
136  return true;
137  }
138 
139  bool IHandler.PreHandleReceivedMessage(Client client, ref Message message)
140  {
141  return false;
142  }
143 
144  void IHandler.Tick(Client client)
145  {
146  while(messageQueue.Count > 0)
147  {
148  WebsocketMessage message = messageQueue.Dequeue();
149  client.QueueMessage(message.GetMessageBytes());
150  if(message.Opcode == WebsocketMessage.Opcodes.Close)
151  {
152  foreach (IWebsocketModule websocketModule in websocketModules)
153  {
154  websocketModule.ConnectionClosed(client, this);
155  }
156  }
157  }
158  }
159  #endregion
160  }
161 }
bool HandleReceivedMessage(Client.Client client, WebsocketHandler handler, WebsocketMessage messageBytes)
Handle received message.
void QueueMessage(WebsocketMessage message)
Add a message to the queue to be sent.
void ConnectionClosed(Client.Client client, WebsocketHandler handler)
Called when an existing websocket connection is being closed.
void ConnectionInitialized(Client.Client client, WebsocketHandler handler)
Called when a new websocket connection has been initialized.
WebsocketHandler AddModule(IWebsocketModule websocketModule)
Add a module to the websocket handler.
Class containing the bytes of a websocket received message.
byte [] BuildRequest()
Build the request bytes based on the message contents.
Definition: HttpMessage.cs:232
void Disconnected(Client client)
Called when the client is disconnected.
byte [] GetMessageBytes()
Get the packed message bytes.
Handler implementing websocket protocol.
Handler interface for client communication.
Definition: IHandler.cs:11
Definition: Client.cs:14
void ParseResponse(byte[] responseBytes)
Parse the received bytes and populate the message contents.
Definition: HttpMessage.cs:406
Base class for TCP Client.
Definition: Client.cs:19