/// <summary> /// Callback for the queued write. /// </summary> /// <param name="value"></param> /// <param name="error"></param> /// <param name="promise"></param> private void QueueWriteCallback(ChunkedBuffer buffer, Exception error, Promise <ChunkedBuffer> promise) { SendQueueState state = (SendQueueState)promise.State; try { SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, (stream is SslStream ? "[SSL] " : "") + "Sent data to [{0}].", RemoteEndpoint); // if the receive buffer was being sent don't close it if (buffer != chunkedBuffer) { buffer.Close(); } } catch (Exception e) { SockNetLogger.Log(SockNetLogger.LogLevel.ERROR, this, "Unable to close the given buffer", e); } finally { Interlocked.CompareExchange(ref sendState, 0, 1); try { state.promise.CreateFulfiller().Fulfill(this); } finally { NotifySendQueue(); } } }
/// <summary> /// A callback the gets invoked after SSL auth completes. /// </summary> /// <param name="result"></param> private void EnableServerSslCallback(IAsyncResult result) { canRead = true; SslStream sslStream = (SslStream)result.AsyncState; sslStream.EndAuthenticateAsServer(result); SockNetLogger.Log(SockNetLogger.LogLevel.INFO, this, "Authenticated SSL with [{0}].", RemoteEndpoint); SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, "[SSL] Reading data from [{0}]...", RemoteEndpoint); this.stream = sslStream; PooledObject <byte[]> buffer = bufferPool.Borrow(); buffer.RefCount.Increment(); receiveState = new ReceiveState() { buffer = buffer, offset = 0, length = buffer.Value.Length }; currentReadResult = stream.BeginRead(buffer.Value, 0, buffer.Value.Length, new AsyncCallback(ReceiveCallback), null); attachPromiseFulfiller.Fulfill(this); }
/// <summary> /// Notifies the send queue to process more data. /// </summary> private void NotifySendQueue() { if (Interlocked.CompareExchange(ref sendState, 1, 0) == 0) { SendQueueState nextState; if (sendQueue.Dequeue(out nextState)) { try { Promise <ChunkedBuffer> promise = nextState.buffer.DrainToStream(stream); promise.State = nextState; promise.OnFulfilled = QueueWriteCallback; } catch (Exception e) { SockNetLogger.Log(SockNetLogger.LogLevel.ERROR, this, "Send data to the socket stream failed.", e); } } else { Interlocked.CompareExchange(ref sendState, 0, 1); } } }
/// <summary> /// Enables SSL on this connection. /// </summary> /// <param name="certificateValidationCallback"></param> public void EnableServerSsl(RemoteCertificateValidationCallback certificateValidationCallback, X509Certificate serverCert) { canRead = false; SockNetLogger.Log(SockNetLogger.LogLevel.INFO, this, "Authenticating SSL with [{0}]...", RemoteEndpoint); if (currentReadResult != null) { stream.EndRead(currentReadResult); } SslStream sslStream = new SslStream(stream, false, certificateValidationCallback); sslStream.BeginAuthenticateAsServer(serverCert, new AsyncCallback(EnableServerSslCallback), sslStream); }
/// <summary> /// Removes a module from this channel. /// </summary> /// <param name="module"></param> /// <returns></returns> public ISockNetChannel RemoveModule(ISockNetChannelModule module) { if (modules.Remove(module.GetType().Name)) { SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, "Uninstalling module: [{0}]", module); module.Uninstall(this); } else { throw new Exception("Module [" + module + "] not installed."); } return(this); }
/// <summary> /// Enables SSL on this connection. /// </summary> /// <param name="certificateValidationCallback"></param> public void EnableClientSsl(RemoteCertificateValidationCallback certificateValidationCallback) { canRead = false; SockNetLogger.Log(SockNetLogger.LogLevel.INFO, this, "Authenticating SSL with [{0}]...", RemoteEndpoint); if (currentReadResult != null) { stream.EndRead(currentReadResult); } SslStream sslStream = new SslStream(stream, false, certificateValidationCallback); X509Certificate2Collection certCollection = new X509Certificate2Collection(); sslStream.BeginAuthenticateAsClient(RemoteEndpoint.Address.ToString(), certCollection, SslProtocols.Tls, false, new AsyncCallback(EnableClientSslCallback), sslStream); }
/// <summary> /// Attaches as a non-ssl channel. /// </summary> protected Promise <ISockNetChannel> Attach() { stream = new NetworkStream(Socket, true); SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, "Reading data from [{0}]...", RemoteEndpoint); PooledObject <byte[]> buffer = bufferPool.Borrow(); buffer.RefCount.Increment(); receiveState = new ReceiveState() { buffer = buffer, offset = 0, length = buffer.Value.Length }; canRead = true; currentReadResult = stream.BeginRead(buffer.Value, 0, buffer.Value.Length, new AsyncCallback(ReceiveCallback), null); return((attachPromiseFulfiller = new Promise <ISockNetChannel>(this).CreateFulfiller()).Promise); }
/// <summary> /// Sets the given message to the IPEndpoint. /// </summary> /// <param name="data"></param> public Promise <ISockNetChannel> Send(object data) { Promise <ISockNetChannel> promise = new Promise <ISockNetChannel>(); object obj = data; try { Pipe.HandleOutgoingData(ref obj); } catch (Exception ex) { SockNetLogger.Log(SockNetLogger.LogLevel.ERROR, this, ex.Message); } finally { if (obj is byte[]) { byte[] rawSendableData = (byte[])obj; SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, (IsConnectionEncrypted ? "[SSL] " : "") + "Sending [{0}] bytes to [{1}]...", rawSendableData.Length, RemoteEndpoint); EnqueueToSendQueueAndNotify(new ChunkedBuffer(bufferPool).OfferRaw(rawSendableData, 0, rawSendableData.Length), promise); } else if (obj is ChunkedBuffer) { ChunkedBuffer sendableStream = ((ChunkedBuffer)obj); SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, (IsConnectionEncrypted ? "[SSL] " : "") + "Sending [{0}] bytes to [{1}]...", sendableStream.AvailableBytesToRead, RemoteEndpoint); EnqueueToSendQueueAndNotify(sendableStream, promise); } else { SockNetLogger.Log(SockNetLogger.LogLevel.ERROR, this, "Unable to send object: {0}", obj); } } return(promise); }
/// <summary> /// Adds a module into this channel. /// </summary> /// <param name="module"></param> /// <returns></returns> public ISockNetChannel AddModule(ISockNetChannelModule module) { ISockNetChannelModule tmp; if (modules.TryAdd(module.GetType().Name, module, out tmp, false)) { SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, "Adding module: [{0}]", module); if (ShouldInstallModule(module)) { SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, "Installing module: [{0}]", module); module.Install(this); } } else { throw new Exception("Module [" + module + "] already installed."); } return(this); }
/// <summary> /// A callback that gets invoked when we have incoming data in the pipe. /// </summary> /// <param name="result"></param> private void ReceiveCallback(IAsyncResult result) { int count = 0; this.currentReadResult = null; try { count = stream.EndRead(result); } catch (Exception) { // this means that the connection closed... Close(); return; } SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, (this.stream is SslStream ? "[SSL] " : "") + "Received [{0}] bytes from [{1}].", count, RemoteEndpoint); if (count > 0) { try { chunkedBuffer.OfferChunk(receiveState.buffer, receiveState.offset, count); while (true) { long startingPosition = chunkedBuffer.ReadPosition; object obj = chunkedBuffer; Pipe.HandleIncomingData(ref obj); if (startingPosition == chunkedBuffer.ReadPosition) { break; } } if (chunkedBuffer.IsClosed) { Close(); return; } } catch (Exception ex) { SockNetLogger.Log(SockNetLogger.LogLevel.ERROR, this, ex.Message, ex); } finally { try { chunkedBuffer.Flush(); if (canRead) { if (receiveState.offset + count >= receiveState.length) { if (receiveState.buffer.RefCount.Decrement() < 1) { if (receiveState.buffer.State == PooledObjectState.USED) { receiveState.buffer.Return(); } else { SockNetLogger.Log(SockNetLogger.LogLevel.WARN, this, "Potential resource leak found."); } } PooledObject <byte[]> buffer = bufferPool.Borrow(); buffer.RefCount.Increment(); if (IsActive) { SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, (this.stream is SslStream ? "[SSL] " : "") + "Reading data from [{0}]...", RemoteEndpoint); receiveState.buffer = buffer; receiveState.offset = 0; receiveState.length = buffer.Value.Length; currentReadResult = stream.BeginRead(buffer.Value, 0, buffer.Value.Length, new AsyncCallback(ReceiveCallback), null); } else { Close(); } } else { if (IsActive) { SockNetLogger.Log(SockNetLogger.LogLevel.DEBUG, this, (this.stream is SslStream ? "[SSL] " : "") + "Reading data from [{0}]...", RemoteEndpoint); receiveState.offset += count; currentReadResult = stream.BeginRead(receiveState.buffer.Value, receiveState.offset, receiveState.length - receiveState.offset, new AsyncCallback(ReceiveCallback), null); } else { Close(); } } } } catch (Exception e) { SockNetLogger.Log(SockNetLogger.LogLevel.ERROR, this, "Unable to begin reading.", e); Close(); } } } else { Close(); } }