/// <summary> /// Disposes the socket dependencies. /// </summary> public virtual void Dispose() { Socket?.Close(); SslStream?.Close(); CancellationTokenSourceManager?.Dispose(); IsDisposed = true; }
private static async Task DecodeContentLengthAsync(this SslStream sslStream, StringBuilder placeHolder, Memory <byte> buffer, string bufferString, int readTimeout, CancellationTokenSourceManager timeoutCancellationTokenSourceWrapper) { var(contentLength, totalBytesRead) = ExtractContentLength(placeHolder, bufferString); while (totalBytesRead < contentLength) { var innerBytesRead = await sslStream.ReadAsync(buffer, readTimeout, timeoutCancellationTokenSourceWrapper); if (innerBytesRead == 0) { throw new ProxyException("Stream ended without reaching expected limit."); } totalBytesRead += innerBytesRead; placeHolder.Append(Encoding.ASCII.GetString(buffer.Span.Slice(0, innerBytesRead))); } }
private static async Task DecodeChunkedAsync(this SslStream sslStream, StringBuilder placeHolder, Memory <byte> buffer, string bufferString, int readTimeout, CancellationTokenSourceManager timeoutCancellationTokenSourceWrapper) { var splitBuffer = bufferString.Split(new[] { RequestConstants.CONTENT_SEPERATOR }, 2, StringSplitOptions.RemoveEmptyEntries); if (splitBuffer.Length == 1) { var peekBytesRead = await sslStream.ReadAsync(buffer.Slice(0, PeekBufferSize), readTimeout, timeoutCancellationTokenSourceWrapper); bufferString = Encoding.ASCII.GetString(buffer.Span.Slice(0, peekBytesRead)); placeHolder.Append(bufferString); } else { bufferString = splitBuffer[1]; } var(chunkSize, totalBytesRead) = ExtractChunkSize(bufferString); while (chunkSize != 0) { while (totalBytesRead < chunkSize) { var remainingReadSize = chunkSize - totalBytesRead; var readSize = remainingReadSize > BufferSize ? BufferSize : remainingReadSize; var innerBytesRead = await sslStream.ReadAsync(buffer.Slice(0, readSize), readTimeout, timeoutCancellationTokenSourceWrapper); if (innerBytesRead == 0) { throw new ProxyException("Stream ended without reaching expected limit."); } totalBytesRead += innerBytesRead; placeHolder.Append(Encoding.ASCII.GetString(buffer.Span.Slice(0, innerBytesRead))); } var peekBytesRead = await sslStream.ReadAsync(buffer.Slice(0, PeekBufferSize), readTimeout, timeoutCancellationTokenSourceWrapper); bufferString = Encoding.ASCII.GetString(buffer.Span.Slice(0, peekBytesRead)); (chunkSize, totalBytesRead) = ExtractChunkSize(bufferString); } }
/// <summary> /// Asynchronously receives the response from the destination server. /// </summary> /// <param name="socket">Underlying socket.</param> /// <param name="readTimeout">Socket Read Timeout.</param> /// <param name="cancellationTokenSourceManager">Cancellation Token Source manager.</param> /// <returns>The raw response and the time to first byte</returns> public static async Task <(string response, float firstByteTime)> ReceiveAllAsync(this Socket socket, int readTimeout, CancellationTokenSourceManager cancellationTokenSourceManager) { var buffer = new byte[BufferSize].AsMemory(); var placeHolder = new StringBuilder(); var bytesRead = 0; var firstByteTime = await TimingHelper.MeasureAsync(async() => { bytesRead = await socket.ReceiveAsync(buffer, readTimeout, cancellationTokenSourceManager); }); if (bytesRead == 0) { throw new ProxyException("Destination Server has no data to send."); } var bufferString = Encoding.ASCII.GetString(buffer.Span.Slice(0, bytesRead)); placeHolder.Append(bufferString); while (!bufferString.Contains(RequestConstants.CONTENT_SEPERATOR)) { bytesRead = await socket.ReceiveAsync(buffer, readTimeout, cancellationTokenSourceManager); if (bytesRead == 0) { return(placeHolder.ToString(), firstByteTime); } bufferString = Encoding.ASCII.GetString(buffer.Span.Slice(0, bytesRead)); placeHolder.Append(bufferString); } var readString = placeHolder.ToString(); if (readString.Contains(RequestConstants.CONTENT_LENGTH_HEADER)) { await socket.DecodeContentLengthAsync(placeHolder, buffer, bufferString, readTimeout, cancellationTokenSourceManager); } else if (readString.Contains(RequestConstants.TRANSFER_ENCODING_CHUNKED_HEADER)) { await socket.DecodeChunkedAsync(placeHolder, buffer, bufferString, readTimeout, cancellationTokenSourceManager); } else { throw new ProxyException("Unknown Transfer Encoding provided by Destination Server."); } return(placeHolder.ToString(), firstByteTime); }
/// <summary> /// Asynchronously reads data from the Destination Server through an SSL connection given a read timeout. /// </summary> /// <param name="sslStream">Underlying SSL Stream.</param> /// <param name="buffer">Read byte buffer.</param> /// <param name="readTimeout">Socket Read Timeout.</param> /// <param name="cancellationTokenSourceManager">Cancellation Token Source manager.</param> /// <returns>Number of read bytes</returns> public static async ValueTask <int> ReadAsync(this SslStream sslStream, Memory <byte> buffer, int readTimeout, CancellationTokenSourceManager cancellationTokenSourceManager) { cancellationTokenSourceManager.Start(readTimeout); var readBytes = await sslStream.ReadAsync(buffer, cancellationTokenSourceManager.Token); cancellationTokenSourceManager.Stop(); return(readBytes); }
/// <summary> /// Asynchronously sends data to the Destination Server through an SSL connection given a write timeout. /// </summary> /// <param name="sslStream">Underlying SSL Stream.</param> /// <param name="buffer">Send request byte buffer.</param> /// <param name="writeTimeout">Socket Write Timeout.</param> /// <param name="cancellationTokenSourceManager">Cancellation Token Source manager.</param> public static async ValueTask WriteAsync(this SslStream sslStream, ReadOnlyMemory <byte> buffer, int writeTimeout, CancellationTokenSourceManager cancellationTokenSourceManager) { cancellationTokenSourceManager.Start(writeTimeout); await sslStream.WriteAsync(buffer, cancellationTokenSourceManager.Token); cancellationTokenSourceManager.Stop(); }
/// <summary> /// Asynchronously reads data from the Destination Server given a read timeout. /// </summary> /// <param name="socket">Underlying socket.</param> /// <param name="buffer">Read byte buffer.</param> /// <param name="readTimeout">Socket Read Timeout.</param> /// <param name="cancellationTokenSourceManager">Cancellation Token Source manager.</param> /// <returns>Number of read bytes</returns> public static async ValueTask <int> ReceiveAsync(this Socket socket, Memory <byte> buffer, int readTimeout, CancellationTokenSourceManager cancellationTokenSourceManager) { cancellationTokenSourceManager.Start(readTimeout); var readBytes = await socket.ReceiveAsync(buffer, SocketFlags.None, cancellationTokenSourceManager.Token); cancellationTokenSourceManager.Stop(); return(readBytes); }
/// <summary> /// Asynchronously sends data to the Destination Server given a write timeout. /// </summary> /// <param name="socket">Underlying socket.</param> /// <param name="buffer">Send request byte buffer.</param> /// <param name="writeTimeout">Socket Write Timeout.</param> /// <param name="cancellationTokenSourceManager">Cancellation Token Source manager.</param> /// <returns>Number of sent bytes</returns> public static async ValueTask <int> SendAsync(this Socket socket, ReadOnlyMemory <byte> buffer, int writeTimeout, CancellationTokenSourceManager cancellationTokenSourceManager) { cancellationTokenSourceManager.Start(writeTimeout); var sentBytes = await socket.SendAsync(buffer, SocketFlags.None, cancellationTokenSourceManager.Token); cancellationTokenSourceManager.Stop(); return(sentBytes); }
/// <summary> /// Asynchronously Connects to the Destination Server given a connect timeout. /// </summary> /// <param name="socket">Underlying socket.</param> /// <param name="host">Destination Host.</param> /// <param name="port">Destination Port.</param> /// <param name="connectTimeout">Socket Connect Timeout.</param> /// <param name="cancellationTokenSourceManager">Cancellation Token Source manager.</param> public static async ValueTask ConnectAsync(this Socket socket, string host, int port, int connectTimeout, CancellationTokenSourceManager cancellationTokenSourceManager) { cancellationTokenSourceManager.Start(connectTimeout); await socket.ConnectAsync(host, port, cancellationTokenSourceManager.Token); cancellationTokenSourceManager.Stop(); }
/// <summary> /// Executes the task provided with a specific timeout value. /// </summary> /// <typeparam name="T">Task Type.</typeparam> /// <param name="task">The task to apply timeout on.</param> /// <param name="client">The Proxy Client firing the task.</param> /// <param name="totalTimeout">Total Request Timeout in ms.</param> /// <param name="cancellationTokenSourceManager">Cancellation Token Source manager.</param> /// <returns>Current task result if it completes within the specified timeout, or TimeoutException if it times out.</returns> public static async Task <T> ExecuteTaskWithTimeout <T>(this Task <T> task, BaseProxyClient client, int totalTimeout, CancellationTokenSourceManager cancellationTokenSourceManager) { if (await Task.WhenAny(task, Task.Delay(totalTimeout)) == task) { if (task.IsFaulted) { throw task.Exception.InnerException; } return(task.Result); } else { cancellationTokenSourceManager?.Cancel(); client.IsFaulted = true; throw new TimeoutException(); } }