private async Task ProcessConnection(WebSocketConnectionAsync connection) { //Fix up the user so it does what we want. connection.User.SetSendPlaceholder((message) => { if(connection.Client != null) connection.AddWrite(connection.Client.WriteMessageAsync(message)); }); connection.User.SetCloseSelfPlaceholder(() => { if (connection.Client != null) { connection.AddClose(connection.Client.WriteRawAsync(WebSocketFrame.GetCloseFrame().GetRawBytes())); connection.Log("This connection was forced to close.", LogLevel.Warning); } }); string error = ""; DataStatus dataStatus; HTTPClientHandshake readHandshake; WebSocketFrame readFrame; byte[] tempBytes; connection.State = WebSocketState.Startup; while (true) { //In the beginning, we wait for a handshake dawg. if (connection.State == WebSocketState.Startup) { Tuple<DataStatus, HTTPClientHandshake, string> handshakeResult = await connection.Client.ReadHandshakeAsync(); dataStatus = handshakeResult.Item1; readHandshake = handshakeResult.Item2; error = handshakeResult.Item3; //Wow, we got a real thing! Let's see if the header is what we need! if (dataStatus == DataStatus.Complete) { if (readHandshake.Service != settings.Service) { connection.AddWrite(connection.Client.WriteHandshakeAsync(HTTPServerHandshake.GetBadRequest())); break; } //Generate a responding handshake, but strip all extensions and protocols. HTTPServerHandshake response = HTTPServerHandshake.GetResponseForClientHandshake(readHandshake); response.AcceptedProtocols.Clear(); response.AcceptedExtensions.Clear(); connection.AddWrite(connection.Client.WriteHandshakeAsync(response)); connection.State = WebSocketState.Connected; connection.LastTest = DateTime.Now; connection.Log("WebSocket handshake complete", LogLevel.Debug); } //Hmm, if it's not complete and we're not waiting, it's an error. Close the connection? else { connection.LogStatus(dataStatus, "Handshake"); if (!string.IsNullOrWhiteSpace(error)) connection.Log("Extra handshake error information: " + error); //Oohhh it was the CLIENT trying to make us do something we don't like! OK then, //let's tell them why they suck! if (dataStatus == DataStatus.DataFormatError) connection.AddWrite(connection.Client.WriteHandshakeAsync(HTTPServerHandshake.GetBadRequest())); break; } } else if (connection.State == WebSocketState.Connected) { Tuple<DataStatus, WebSocketFrame> frameResult = await connection.Client.ReadFrameAsync(); dataStatus = frameResult.Item1; readFrame = frameResult.Item2; //Ah, we got a full frame from the client! Let's see what it is if (dataStatus == DataStatus.Complete) { string frameMessage = ""; bool continueConnection = connection.ProcessFrame(readFrame, out tempBytes, out frameMessage); if (tempBytes != null) connection.AddWrite(connection.Client.WriteRawAsync(tempBytes)); if (!continueConnection) break; if(!string.IsNullOrEmpty(frameMessage)) await Task.Run(() => connection.User.ReceivedMessage(frameMessage)); } //Oh something went wrong. That's OK I guess. else { connection.LogStatus(dataStatus, "Read"); break; } } } //Now that we're ending, try to dump out a bit of the write queue. connection.Log("Connection finished.", LogLevel.Debug); connection.User.ClosedConnection(); RemoveConnection(connection, false); }
public async Task StartAsync() { //OY! Don't call us again! if (server != null) return; server = new TcpListener(System.Net.IPAddress.Any, settings.Port); try { server.Start(); } catch(Exception e) { Log("Couldn't start base server: " + e.Message, LogLevel.FatalError); return; } running = true; Log("Started server on port: " + settings.Port); while (running) { //Accept the client and set it up try { TcpClient client = await server.AcceptTcpClientAsync(); client.ReceiveBufferSize = settings.ReceiveBufferSize; client.SendBufferSize = settings.SendBufferSize; client.SendTimeout = client.ReceiveTimeout = (int)settings.ReadWriteTimeout.TotalMilliseconds; WebSocketClient webClient = new WebSocketClient(client, settings.MaxReceiveSize); Log("Accepted connection from " + client.Client.RemoteEndPoint); //Start up a spinner to handle this new connection. The spinner will take care of headers and all that, //we're just here to intercept new connections. WebSocketConnectionAsync connection = new WebSocketConnectionAsync(webClient, GenerateNewUser(), settings.LogProvider); connection.Awaitable = ProcessConnection(connection); lock (connectionLock) { connections.Add(connection); } } catch(Exception e) { if (e is ObjectDisposedException) Log("Connection accepter has gone away it seems...", LogLevel.Debug); else Log("Encountered exception while accepting: " + e, LogLevel.Error); } } running = false; }
/// <summary> /// Remove and cleanup the given spinner /// </summary> /// <param name="spinner">Spinner.</param> private void RemoveConnection(WebSocketConnectionAsync connection, bool wait = true) { bool removed = false; lock (connectionLock) { removed = connections.Remove(connection); } if (removed) { connection.Client.CancelAsyncOperations(); if (!wait || connection.Awaitable.Wait(settings.ShutdownTimeout)) Log("Removed connection: " + connection.ID, LogLevel.Debug); else Log("Connection " + connection.ID + " didn't shut down properly! You have a dangling connection!", LogLevel.Error); connection.Dispose(); } }