/// <summary> /// Begins an asynchronous receive /// </summary> /// <param name="Buffer">Buffer to store received data</param> /// <param name="ReadCallBack">Method to call on receiving data</param> /// <param name="StateObject">State object to be passed to ReadCallBack</param> /// <returns>AsyncResult for the asynchronous operation</returns> public IAsyncResult BeginReceive(int BufferLength, AsyncCallback ReadCallBack, object StateObject) { try { if (recvBuffer == null || recvBuffer.IsDisposed) { recvBuffer = bufferPool.GetBuffer(BufferLength); } else if (recvBuffer.Size < BufferLength) { recvBuffer.Dispose(); recvBuffer = bufferPool.GetBuffer(BufferLength); } lock (syncSocket) { return(socket.BeginReceive(recvBuffer.GetSegments(), SocketFlags.None, ReadCallBack, StateObject)); } } catch (Exception ex) { Diags.LogSocketException(ex); return(null); } }
/// <summary> /// relays the input clientStream to the server at the specified host name and port with the given httpCmd and headers /// as prefix /// Usefull for websocket requests /// Asynchronous Programming Model, which does not throw exceptions when the socket is closed /// </summary> /// <param name="clientStream"></param> /// <param name="serverStream"></param> /// <param name="bufferSize"></param> /// <param name="onDataSend"></param> /// <param name="onDataReceive"></param> /// <param name="cancellationTokenSource"></param> /// <param name="exceptionFunc"></param> /// <returns></returns> internal static async Task SendRawApm(Stream clientStream, Stream serverStream, IBufferPool bufferPool, int bufferSize, Action <byte[], int, int> onDataSend, Action <byte[], int, int> onDataReceive, CancellationTokenSource cancellationTokenSource, ExceptionHandler exceptionFunc) { var taskCompletionSource = new TaskCompletionSource <bool>(); cancellationTokenSource.Token.Register(() => taskCompletionSource.TrySetResult(true)); // Now async relay all server=>client & client=>server data var clientBuffer = bufferPool.GetBuffer(bufferSize); var serverBuffer = bufferPool.GetBuffer(bufferSize); try { beginRead(clientStream, serverStream, clientBuffer, onDataSend, cancellationTokenSource, exceptionFunc); beginRead(serverStream, clientStream, serverBuffer, onDataReceive, cancellationTokenSource, exceptionFunc); await taskCompletionSource.Task; } finally { bufferPool.ReturnBuffer(clientBuffer); bufferPool.ReturnBuffer(serverBuffer); } }
public CopyStream(ICustomStreamReader reader, ICustomStreamWriter writer, IBufferPool bufferPool, int bufferSize) { this.reader = reader; this.writer = writer; BufferSize = bufferSize; buffer = bufferPool.GetBuffer(bufferSize); }
/// <summary> /// Copy streams asynchronously /// </summary> /// <param name="input"></param> /// <param name="output"></param> /// <param name="onCopy"></param> /// <param name="bufferPool"></param> /// <param name="bufferSize"></param> /// <param name="cancellationToken"></param> internal static async Task CopyToAsync(this Stream input, Stream output, Action <byte[], int, int> onCopy, IBufferPool bufferPool, int bufferSize, CancellationToken cancellationToken) { var buffer = bufferPool.GetBuffer(bufferSize); try { while (!cancellationToken.IsCancellationRequested) { // cancellation is not working on Socket ReadAsync // https://github.com/dotnet/corefx/issues/15033 int num = await input.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None) .withCancellation(cancellationToken); int bytesRead; if ((bytesRead = num) != 0 && !cancellationToken.IsCancellationRequested) { await output.WriteAsync(buffer, 0, bytesRead, CancellationToken.None); onCopy?.Invoke(buffer, 0, bytesRead); } else { break; } } } finally { bufferPool.ReturnBuffer(buffer); } }
/// <summary> /// Determines whether the stream starts with the given string. /// </summary> /// <returns> /// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise /// </returns> private static async Task <int> startsWith(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, string expectedStart, CancellationToken cancellationToken = default) { const int lengthToCheck = 10; byte[] buffer = null; try { if (bufferPool.BufferSize < lengthToCheck) { throw new Exception($"Buffer is too small. Minimum size is {lengthToCheck} bytes"); } buffer = bufferPool.GetBuffer(bufferPool.BufferSize); bool isExpected = true; int i = 0; while (i < lengthToCheck) { int peeked = await clientStreamReader.PeekBytesAsync(buffer, i, i, lengthToCheck - i, cancellationToken); if (peeked <= 0) { return(-1); } peeked += i; while (i < peeked) { int b = buffer[i]; if (b == ' ' && i > 2) { return(isExpected ? 1 : 0); } else { char ch = (char)b; if (ch < 'A' || ch > 'z' || (ch > 'Z' && ch < 'a')) // ASCII letter { return(-1); } else if (i >= expectedStart.Length || ch != expectedStart[i]) { isExpected = false; } } i++; } } // only letters return(0); } finally { bufferPool.ReturnBuffer(buffer); } }
/// <summary> /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. /// </summary> /// <param name="value">The byte to write to the stream.</param> public override void WriteByte(byte value) { if (closedWrite) { return; } var buffer = bufferPool.GetBuffer(); try { buffer[0] = value; OnDataWrite(buffer, 0, 1); baseStream.Write(buffer, 0, 1); } catch { closedWrite = true; if (!isNetworkStream) { throw; } } finally { bufferPool.ReturnBuffer(buffer); } }
/// <summary> /// Initializes a new instance of the <see cref="CustomBufferedStream"/> class. /// </summary> /// <param name="baseStream">The base stream.</param> /// <param name="bufferPool">Bufferpool.</param> /// <param name="leaveOpen"><see langword="true" /> to leave the stream open after disposing the <see cref="T:CustomBufferedStream" /> object; otherwise, <see langword="false" />.</param> public CustomBufferedStream(Stream baseStream, IBufferPool bufferPool, bool leaveOpen = false) { BaseStream = baseStream; this.leaveOpen = leaveOpen; streamBuffer = bufferPool.GetBuffer(); this.bufferPool = bufferPool; }
public CopyStream(ICustomStreamReader reader, ICustomStreamWriter writer, IBufferPool bufferPool) { this.reader = reader; this.writer = writer; buffer = bufferPool.GetBuffer(); this.bufferPool = bufferPool; }
/// <summary> /// Initializes a new instance of the <see cref="CustomBufferedStream"/> class. /// </summary> /// <param name="baseStream">The base stream.</param> /// <param name="bufferPool">Bufferpool.</param> /// <param name="bufferSize">Size of the buffer.</param> /// <param name="leaveOpen"><see langword="true" /> to leave the stream open after disposing the <see cref="T:CustomBufferedStream" /> object; otherwise, <see langword="false" />.</param> public CustomBufferedStream(Stream baseStream, IBufferPool bufferPool, int bufferSize, bool leaveOpen = false) { this.baseStream = baseStream; BufferSize = bufferSize; this.leaveOpen = leaveOpen; streamBuffer = bufferPool.GetBuffer(bufferSize); this.bufferPool = bufferPool; }
private Task writeAsyncInternal(string value, bool addNewLine, CancellationToken cancellationToken) { int newLineChars = addNewLine ? newLine.Length : 0; int charCount = value.Length; if (charCount < BufferSize - newLineChars) { value.CopyTo(0, charBuffer, 0, charCount); var buffer = bufferPool.GetBuffer(BufferSize); try { int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true); if (newLineChars > 0) { Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars); idx += newLineChars; } return(stream.WriteAsync(buffer, 0, idx, cancellationToken)); } finally { bufferPool.ReturnBuffer(buffer); } } else { var charBuffer = new char[charCount]; value.CopyTo(0, charBuffer, 0, charCount); var buffer = new byte[charCount + newLineChars + 1]; int idx = encoder.GetBytes(charBuffer, 0, charCount, buffer, 0, true); if (newLineChars > 0) { Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars); idx += newLineChars; } return(stream.WriteAsync(buffer, 0, idx, cancellationToken)); } }
/// <summary> /// Initializes a new instance of the <see cref="HttpStream"/> class. /// </summary> /// <param name="baseStream">The base stream.</param> /// <param name="bufferPool">Bufferpool.</param> /// <param name="leaveOpen"><see langword="true" /> to leave the stream open after disposing the <see cref="T:CustomBufferedStream" /> object; otherwise, <see langword="false" />.</param> internal HttpStream(Stream baseStream, IBufferPool bufferPool, bool leaveOpen = false) { if (baseStream is NetworkStream) { swallowException = true; } this.baseStream = baseStream; this.leaveOpen = leaveOpen; streamBuffer = bufferPool.GetBuffer(); this.bufferPool = bufferPool; }
/// <summary> /// Initializes a new instance of the <see cref="HttpStream"/> class. /// </summary> /// <param name="baseStream">The base stream.</param> /// <param name="bufferPool">Bufferpool.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="leaveOpen"><see langword="true" /> to leave the stream open after disposing the <see cref="T:CustomBufferedStream" /> object; otherwise, <see langword="false" />.</param> internal HttpStream(Stream baseStream, IBufferPool bufferPool, CancellationToken cancellationToken, bool leaveOpen = false) { if (baseStream is NetworkStream) { isNetworkStream = true; } this.baseStream = baseStream; this.leaveOpen = leaveOpen; streamBuffer = bufferPool.GetBuffer(); this.bufferPool = bufferPool; this.cancellationToken = cancellationToken; }
/// <summary> /// Gets the HTTP method from the stream. /// </summary> public static async ValueTask <KnownMethod> GetMethod(IPeekStream httpReader, IBufferPool bufferPool, CancellationToken cancellationToken = default) { const int lengthToCheck = 20; if (bufferPool.BufferSize < lengthToCheck) { throw new Exception($"Buffer is too small. Minimum size is {lengthToCheck} bytes"); } byte[] buffer = bufferPool.GetBuffer(bufferPool.BufferSize); try { int i = 0; while (i < lengthToCheck) { int peeked = await httpReader.PeekBytesAsync(buffer, i, i, lengthToCheck - i, cancellationToken); if (peeked <= 0) { return(KnownMethod.Invalid); } peeked += i; while (i < peeked) { int b = buffer[i]; if (b == ' ' && i > 2) { return(getKnownMethod(buffer.AsSpan(0, i))); } char ch = (char)b; if ((ch < 'A' || ch > 'z' || (ch > 'Z' && ch < 'a')) && (ch != '-')) // ASCII letter { return(KnownMethod.Invalid); } i++; } } // only letters, but no space (or shorter than 3 characters) return(KnownMethod.Invalid); } finally { bufferPool.ReturnBuffer(buffer); } }
private async Task writeAsyncInternal(string value, bool addNewLine, CancellationToken cancellationToken) { int newLineChars = addNewLine ? newLine.Length : 0; int charCount = value.Length; if (charCount < bufferPool.BufferSize - newLineChars) { var buffer = bufferPool.GetBuffer(); try { int idx = encoding.GetBytes(value, 0, charCount, buffer, 0); if (newLineChars > 0) { Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars); idx += newLineChars; } await stream.WriteAsync(buffer, 0, idx, cancellationToken); } finally { bufferPool.ReturnBuffer(buffer); } } else { var buffer = new byte[charCount + newLineChars + 1]; int idx = encoding.GetBytes(value, 0, charCount, buffer, 0); if (newLineChars > 0) { Buffer.BlockCopy(newLine, 0, buffer, idx, newLineChars); idx += newLineChars; } await stream.WriteAsync(buffer, 0, idx, cancellationToken); } }
/// <summary> /// Determines whether the stream starts with the given string. /// </summary> /// <param name="clientStreamReader">The client stream reader.</param> /// <param name="expectedStart">The expected start.</param> /// <returns> /// 1: when starts with the given string, 0: when valid HTTP method, -1: otherwise /// </returns> private static async Task <int> startsWith(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, int bufferSize, string expectedStart, CancellationToken cancellationToken = default) { int iRet = -1; const int lengthToCheck = 10; byte[] buffer = null; try { buffer = bufferPool.GetBuffer(Math.Max(bufferSize, lengthToCheck)); int peeked = await clientStreamReader.PeekBytesAsync(buffer, 0, 0, lengthToCheck, cancellationToken); if (peeked > 0) { bool isExpected = true; for (int i = 0; i < lengthToCheck; i++) { int b = buffer[i]; if (b == ' ' && i > 2) { return(isExpected ? 1 : 0); } else { char ch = (char)b; if (!char.IsLetter(ch)) { return(-1); } else if (i >= expectedStart.Length || ch != expectedStart[i]) { isExpected = false; } } } // only letters iRet = isExpected ? 1 : 0; } } finally { bufferPool.ReturnBuffer(buffer); buffer = null; } return(iRet); }
/// <summary> /// Writes a byte to the current position in the stream and advances the position within the stream by one byte. /// </summary> /// <param name="value">The byte to write to the stream.</param> public override void WriteByte(byte value) { var buffer = bufferPool.GetBuffer(BufferSize); try { buffer[0] = value; OnDataWrite(buffer, 0, 1); baseStream.Write(buffer, 0, 1); } finally { bufferPool.ReturnBuffer(buffer); } }
public async Task Finish() { if (bytesRemaining != -1) { var buffer = bufferPool.GetBuffer(baseStream.BufferSize); try { int res = await ReadAsync(buffer, 0, buffer.Length); if (res != 0) { throw new Exception("Data received after stream end"); } } finally { bufferPool.ReturnBuffer(buffer); } } }