public CopyStream(ICustomStreamReader reader, ICustomStreamWriter writer, IBufferPool bufferPool) { this.reader = reader; this.writer = writer; buffer = bufferPool.GetBuffer(); this.bufferPool = bufferPool; }
/// <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); } }
public CopyStream(ICustomStreamReader reader, ICustomStreamWriter writer, IBufferPool bufferPool, int bufferSize) { this.reader = reader; this.writer = writer; BufferSize = bufferSize; buffer = bufferPool.GetBuffer(bufferSize); }
/// <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, string expectedStart) { bool isExpected = true; int legthToCheck = 10; for (int i = 0; i < legthToCheck; i++) { int b = await clientStreamReader.PeekByteAsync(i); if (b == -1) { return(-1); } if (b == ' ' && i > 2) { return(isExpected ? 1 : 0); } char ch = (char)b; if (!char.IsLetter(ch)) { return(-1); } if (i >= expectedStart.Length || ch != expectedStart[i]) { isExpected = false; } } // only letters return(isExpected ? 1 : 0); }
/// <summary> /// Copies the streams chunked /// </summary> /// <param name="reader"></param> /// <param name="onCopy"></param> /// <param name="cancellationToken"></param> /// <returns></returns> private async Task copyBodyChunkedAsync(ICustomStreamReader reader, Action <byte[], int, int> onCopy, CancellationToken cancellationToken) { while (true) { string chunkHead = await reader.ReadLineAsync(cancellationToken); int idx = chunkHead.IndexOf(";"); if (idx >= 0) { chunkHead = chunkHead.Substring(0, idx); } int chunkSize = int.Parse(chunkHead, NumberStyles.HexNumber); await WriteLineAsync(chunkHead, cancellationToken); if (chunkSize != 0) { await copyBytesFromStream(reader, chunkSize, onCopy, cancellationToken); } await WriteLineAsync(cancellationToken); // chunk trail await reader.ReadLineAsync(cancellationToken); if (chunkSize == 0) { break; } } }
/// <summary> /// Read a line from the byte stream /// </summary> /// <returns></returns> private async Task <long> readUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, string boundary, CancellationToken cancellationToken) { int bufferDataLength = 0; var buffer = bufferPool.GetBuffer(bufferSize); try { int boundaryLength = boundary.Length + 4; long bytesRead = 0; while (bytesRead < totalBytesToRead && (reader.DataAvailable || await reader.FillBufferAsync(cancellationToken))) { byte newChar = reader.ReadByteFromBuffer(); buffer[bufferDataLength] = newChar; bufferDataLength++; bytesRead++; if (bufferDataLength >= boundaryLength) { int startIdx = bufferDataLength - boundaryLength; if (buffer[startIdx] == '-' && buffer[startIdx + 1] == '-') { startIdx += 2; bool ok = true; for (int i = 0; i < boundary.Length; i++) { if (buffer[startIdx + i] != boundary[i]) { ok = false; break; } } if (ok) { break; } } } if (bufferDataLength == buffer.Length) { // boundary is not longer than 70 bytes according to the specification, so keeping the last 100 (minimum 74) bytes is enough const int bytesToKeep = 100; Buffer.BlockCopy(buffer, buffer.Length - bytesToKeep, buffer, 0, bytesToKeep); bufferDataLength = bytesToKeep; } } return(bytesRead); } finally { bufferPool.ReturnBuffer(buffer); } }
internal LimitedStream(ICustomStreamReader baseStream, bool isChunked, long contentLength) { this.baseStream = baseStream; this.isChunked = isChunked; bytesRemaining = isChunked ? 0 : contentLength == -1 ? long.MaxValue : contentLength; }
internal static async Task ReadHeaders(ICustomStreamReader reader, HeaderCollection headerCollection, CancellationToken cancellationToken) { string tmpLine; while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync(cancellationToken))) { var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); headerCollection.AddHeader(header[0], header[1]); } }
/// <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); }
internal static async Task ReadHeaders(ICustomStreamReader reader, HeaderCollection headerCollection, CancellationToken cancellationToken) { string?tmpLine; while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync(cancellationToken))) { int colonIndex = tmpLine !.IndexOf(':'); if (colonIndex == -1) { throw new Exception("Header line should contain a colon character."); } string headerName = tmpLine.AsSpan(0, colonIndex).ToString(); string headerValue = tmpLine.AsSpan(colonIndex + 1).ToString(); headerCollection.AddHeader(headerName, headerValue); } }
/// <summary> /// Copies the specified content length number of bytes to the output stream from the given inputs stream /// optionally chunked /// </summary> /// <param name="streamReader"></param> /// <param name="isChunked"></param> /// <param name="contentLength"></param> /// <param name="onCopy"></param> /// <param name="cancellationToken"></param> /// <returns></returns> internal Task CopyBodyAsync(ICustomStreamReader streamReader, bool isChunked, long contentLength, Action <byte[], int, int> onCopy, CancellationToken cancellationToken) { // For chunked request we need to read data as they arrive, until we reach a chunk end symbol if (isChunked) { return(copyBodyChunkedAsync(streamReader, onCopy, cancellationToken)); } // http 1.0 or the stream reader limits the stream if (contentLength == -1) { contentLength = long.MaxValue; } // If not chunked then its easy just read the amount of bytes mentioned in content length header return(copyBytesFromStream(streamReader, contentLength, onCopy, cancellationToken)); }
/// <summary> /// Copies the specified bytes to the stream from the input stream /// </summary> /// <param name="reader"></param> /// <param name="count"></param> /// <param name="onCopy"></param> /// <param name="cancellationToken"></param> /// <returns></returns> private async Task copyBytesFromStream(ICustomStreamReader reader, long count, Action <byte[], int, int> onCopy, CancellationToken cancellationToken) { var buffer = bufferPool.GetBuffer(BufferSize); try { long remainingBytes = count; while (remainingBytes > 0) { int bytesToRead = buffer.Length; if (remainingBytes < bytesToRead) { bytesToRead = (int)remainingBytes; } int bytesRead = await reader.ReadAsync(buffer, 0, bytesToRead, cancellationToken); if (bytesRead == 0) { break; } remainingBytes -= bytesRead; await stream.WriteAsync(buffer, 0, bytesRead, cancellationToken); onCopy?.Invoke(buffer, 0, bytesRead); } } finally { bufferPool.ReturnBuffer(buffer); } }
/// <summary> /// Determines whether is connect method. /// </summary> /// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns> internal static Task <int> IsConnectMethod(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, CancellationToken cancellationToken = default) { return(startsWith(clientStreamReader, bufferPool, "CONNECT", cancellationToken)); }
/// <summary> /// Determines whether is connect method. /// </summary> /// <param name="clientStreamReader">The client stream reader.</param> /// <returns>1: when CONNECT, 0: when valid HTTP method, -1: otherwise</returns> internal static Task <int> IsConnectMethod(ICustomStreamReader clientStreamReader) { return(startsWith(clientStreamReader, "CONNECT")); }
/// <summary> /// Determines whether is pri method (HTTP/2). /// </summary> /// <param name="clientStreamReader">The client stream reader.</param> /// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns> internal static Task <int> IsPriMethod(ICustomStreamReader clientStreamReader) { return(startsWith(clientStreamReader, "PRI")); }
public PdfReaderInternal(byte[] bytes) { reader = new CustomStreamReader(bytes); }
/// <summary> /// Determines whether is pri method (HTTP/2). /// </summary> /// <param name="clientStreamReader">The client stream reader.</param> /// <returns>1: when PRI, 0: when valid HTTP method, -1: otherwise</returns> internal static Task <int> IsPriMethod(ICustomStreamReader clientStreamReader, IBufferPool bufferPool, int bufferSize, CancellationToken cancellationToken = default) { return(startsWith(clientStreamReader, bufferPool, bufferSize, "PRI", cancellationToken)); }
internal CustomBufferedPeekStream(ICustomStreamReader baseStream, IBufferPool bufferPool, int startPosition = 0) { this.bufferPool = bufferPool; this.baseStream = baseStream; Position = startPosition; }