public unsafe void EncryptSessionKey(ref WritableBuffer writer, Span <byte> ticketContent) { var tagLength = 16; var key = _keys.Take(); try { var contentLength = ticketContent.Length + tagLength + sizeof(long) + sizeof(Guid); var nonce = System.Threading.Interlocked.Increment(ref _nounceCounter); writer.WriteBigEndian((ushort)contentLength); writer.Ensure(contentLength); key.IV.Slice(4).Span.Write(nonce); key.Init(KeyMode.Encryption); writer.WriteBigEndian(_keyGuid); writer.WriteBigEndian(_nounceCounter); var amountWritten = key.Finish(ticketContent, writer.Buffer.Span); writer.Advance(amountWritten); key.GetTag(writer.Buffer.Span.Slice(0, tagLength)); writer.Advance(tagLength); } finally { _keys.Add(key); } }
public unsafe void Encrypt(ref WritableBuffer buffer, ReadableBuffer plainText, RecordType recordType) { int outLength; GCHandle inHandle, outHandle; ThrowOnError(EVP_CipherInit_ex(_ctx, _cipherType, IntPtr.Zero, (void *)_keyPointer, (void *)_ivPointer, (int)KeyMode.Encryption)); foreach (var b in plainText) { if (b.Length == 0) { continue; } buffer.Ensure(b.Length); var inPtr = b.GetPointer(out inHandle); var outPtr = buffer.Memory.GetPointer(out outHandle); try { outLength = buffer.Memory.Length; ThrowOnError(EVP_CipherUpdate(_ctx, outPtr, ref outLength, inPtr, b.Length)); buffer.Advance(outLength); } finally { if (inHandle.IsAllocated) { inHandle.Free(); } if (outHandle.IsAllocated) { outHandle.Free(); } } } buffer.Ensure(Overhead + sizeof(RecordType)); var writePtr = buffer.Memory.GetPointer(out outHandle); outLength = buffer.Memory.Length; ThrowOnError(EVP_CipherUpdate(_ctx, writePtr, ref outLength, &recordType, sizeof(RecordType))); buffer.Advance(outLength); if (_paddingSize > 0) { outLength = _paddingSize; writePtr = buffer.Memory.GetPointer(out outHandle); ThrowOnError(EVP_CipherUpdate(_ctx, writePtr, ref outLength, (byte *)s_zeroBuffer, _paddingSize)); buffer.Advance(outLength); } writePtr = buffer.Memory.GetPointer(out outHandle); outLength = 0; ThrowOnError(EVP_CipherFinal_ex(_ctx, null, ref outLength)); ThrowOnError(EVP_CIPHER_CTX_ctrl(_ctx, EVP_CIPHER_CTRL.EVP_CTRL_GCM_GET_TAG, _overhead, writePtr)); buffer.Advance(_overhead); IncrementSequence(); }
private static void WriteAsciiString(ref WritableBuffer buffer, ReadOnlySpan <char> value) { if (value == null || value.Length == 0) { return; } while (value.Length != 0) { buffer.Ensure(); var span = buffer.Buffer.Span; int bytesToWrite = Math.Min(value.Length, span.Length); // todo: Vector.Narrow for (int i = 0; i < bytesToWrite; i++) { span[i] = (byte)value[i]; } buffer.Advance(bytesToWrite); buffer.Commit(); value = value.Slice(bytesToWrite); } }
protected override void WriteRecords(ref ReadableBuffer buffer, ref WritableBuffer writer, RecordType recordType) { ReadableBuffer append; while (buffer.Length > 0) { append = buffer.Slice(0, Math.Min(_maxMessageSize, buffer.Length)); buffer = buffer.Slice(append.End); var recordHeader = new RecordHeader() { RecordType = recordType, Length = (ushort)append.Length, Version = _recordVersion }; writer.Ensure(_minimumMessageSize); if (_connection?.WriteKey != null) { recordHeader.Length += (ushort)(8 + _connection.WriteKey.Overhead); } writer.Buffer.Span.Write(recordHeader); writer.Advance(_minimumMessageSize); if (_connection?.WriteKey != null) { _connection.WriteKey.Encrypt(ref writer, append, recordType, _recordVersion); } else { writer.Append(append); } } }
public void ByteByByteTest() { WritableBuffer writableBuffer = default; for (int i = 1; i <= 1024 * 1024; i++) { writableBuffer = _pipe.Writer.Alloc(100); writableBuffer.Advance(1); writableBuffer.Commit(); Assert.Equal(i, _pipe.Length); } writableBuffer.FlushAsync(); for (int i = 1024 * 1024 - 1; i >= 0; i--) { var result = _pipe.Reader.ReadAsync().GetResult(); var consumed = result.Buffer.Slice(1).Start; Assert.Equal(i + 1, result.Buffer.Length); _pipe.Reader.Advance(consumed, consumed); Assert.Equal(i, _pipe.Length); } }
private static void PingPayloadWriter(WritableBuffer output, Span <byte> maskingKey, int payloadLength, DateTime timestamp) { var payload = output.Memory.Slice(0, payloadLength); // TODO: Don't put this string on the heap? Is there a way to do that without re-implementing ToString? // Ideally we'd like to render the string directly to the output buffer. var str = timestamp.ToString("O", CultureInfo.InvariantCulture); ArraySegment <byte> buffer; if (payload.TryGetArray(out buffer)) { // Fast path - Write the encoded bytes directly out. Encoding.UTF8.GetBytes(str, 0, str.Length, buffer.Array, buffer.Offset); } else { // TODO: Could use TryGetPointer, GetBytes does take a byte*, but it seems like just waiting until we have a version that uses Span is best. // Slow path - Allocate a heap buffer for the encoded bytes before writing them out. payload.Span.Set(Encoding.UTF8.GetBytes(str)); } if (maskingKey.Length > 0) { MaskingUtilities.ApplyMask(payload.Span, maskingKey); } output.Advance(payloadLength); }
public unsafe void WritePublicKey(ref WritableBuffer keyBuffer) { GenerateECKeySet(); var key = EVP_PKEY_get0_EC_KEY(_eKey); var pubKey = EC_KEY_get0_public_key(key); var group = EC_KEY_get0_group(key); IntPtr size = EC_POINT_point2oct(group, pubKey, EC_POINT_CONVERSION.POINT_CONVERSION_UNCOMPRESSED, null, IntPtr.Zero, IntPtr.Zero); var s = (ushort)size.ToInt32(); keyBuffer.Ensure(s); GCHandle handle; var ptr = keyBuffer.Memory.GetPointer(out handle); try { size = EC_POINT_point2oct(group, pubKey, EC_POINT_CONVERSION.POINT_CONVERSION_UNCOMPRESSED, ptr, size, IntPtr.Zero); keyBuffer.Advance(s); } finally { if (handle.IsAllocated) { handle.Free(); } } }
internal static void WriteFrameHeader(ref WritableBuffer output, WebSocketsFrame.FrameFlags flags, WebSocketsFrame.OpCodes opCode, int payloadLength, int mask) { output.Ensure(MaxHeaderLength); int index = 0; var span = output.Buffer.Span; span[index++] = (byte)(((int)flags & 240) | ((int)opCode & 15)); if (payloadLength > ushort.MaxValue) { // write as a 64-bit length span[index++] = (byte)((mask != 0 ? 128 : 0) | 127); span.Slice(index).Write((uint)0); span.Slice(index + 4).Write(ToNetworkByteOrder((uint)payloadLength)); index += 8; } else if (payloadLength > 125) { // write as a 16-bit length span[index++] = (byte)((mask != 0 ? 128 : 0) | 126); span.Slice(index).Write(ToNetworkByteOrder((ushort)payloadLength)); index += 2; } else { // write in the header span[index++] = (byte)((mask != 0 ? 128 : 0) | payloadLength); } if (mask != 0) { span.Slice(index).Write(mask); index += 4; } output.Advance(index); }
public static WritableBuffer CreateNewSessionKey(WritableBuffer buffer, IConnectionStateTls13 state) { var lifetime = TicketLifeTimeInHours * 60 * 60; buffer.WriteBigEndian((uint)lifetime); buffer.Ensure(4); state.CryptoProvider.FillWithRandom(buffer.Memory.Slice(0, 4)); buffer.Advance(4); BufferExtensions.WriteVector <ushort>(ref buffer, (writer, conn) => { state.ResumptionProvider.GenerateSessionTicket(ref writer, conn); return(writer); }, state); BufferExtensions.WriteVector <ushort>(ref buffer, (writer, conn) => { writer.WriteBigEndian(ExtensionType.ticket_early_data_info); writer.WriteBigEndian <ushort>(sizeof(uint)); uint maxData = 1024 * 2; writer.WriteBigEndian(maxData); return(writer); }, state); return(buffer); }
// review: make public? private static unsafe void WriteString(this WritableBuffer buffer, string value, Encoding encoding) { int bytesPerChar = encoding.GetMaxByteCount(1); fixed(char *s = value) { int remainingChars = value.Length, charOffset = 0; while (remainingChars != 0) { buffer.Ensure(bytesPerChar); var memory = buffer.Memory; var charsThisBatch = Math.Min(remainingChars, memory.Length / bytesPerChar); int bytesWritten = 0; void *pointer; ArraySegment <byte> data; if (memory.TryGetPointer(out pointer)) { bytesWritten = encoding.GetBytes(s + charOffset, charsThisBatch, (byte *)pointer, memory.Length); } else if (memory.TryGetArray(out data)) { bytesWritten = encoding.GetBytes(value, charOffset, charsThisBatch, data.Array, data.Offset); } charOffset += charsThisBatch; remainingChars -= charsThisBatch; buffer.Advance(bytesWritten); } } }
public static void WriteCertificateEntry(ref WritableBuffer writer, byte[] certificate) { writer.Ensure(3); writer.Memory.Write24BitNumber(certificate.Length); writer.Advance(3); writer.Write(certificate); }
private unsafe static void FillBuffer(ref WritableBuffer wb, int count) { for (int i = 0; i < count; i++) { wb.Ensure(4); void *pointer; Assert.True(wb.Buffer.TryGetPointer(out pointer)); *(int *)pointer = i; wb.Advance(4); } }
private void OnRead(UvStreamHandle handle, int status) { if (status == 0) { // A zero status does not indicate an error or connection end. It indicates // there is no data to be read right now. // See the note at http://docs.libuv.org/en/v1.x/stream.html#c.uv_read_cb. // We need to clean up whatever was allocated by OnAlloc. _inputBuffer.FlushAsync(); return; } var normalRead = status > 0; var normalDone = status == EOF; var errorDone = !(normalDone || normalRead); var readCount = normalRead ? status : 0; if (!normalRead) { handle.ReadStop(); } IOException error = null; if (errorDone) { Exception uvError; handle.Libuv.Check(status, out uvError); error = new IOException(uvError.Message, uvError); // REVIEW: Should we treat ECONNRESET as an error? // Ignore the error for now _input.CompleteWriter(); } else if (readCount == 0 || _input.Writing.IsCompleted) { _input.CompleteWriter(); } else { _inputBuffer.Advance(readCount); var task = _inputBuffer.FlushAsync(); if (!task.IsCompleted) { // If there's back pressure handle.ReadStop(); // Resume reading when task continues task.ContinueWith((t, state) => ((UvTcpConnection)state).StartReading(), this); } } }
public static void WriteVector24Bit(ref WritableBuffer buffer, Func <WritableBuffer, IConnectionStateTls13, WritableBuffer> writeContent, IConnectionStateTls13 state) { buffer.Ensure(3); var bookmark = buffer.Memory; buffer.Advance(3); int currentSize = buffer.BytesWritten; buffer = writeContent(buffer, state); currentSize = buffer.BytesWritten - currentSize; bookmark.Write24BitNumber(currentSize); }
public static WritableBuffer SendServerHello13(WritableBuffer buffer, IConnectionStateTls13 connectionState) { buffer.Ensure(RandomLength + sizeof(ushort)); buffer.WriteBigEndian(connectionState.Version); var memoryToFill = buffer.Memory.Slice(0, RandomLength); connectionState.CryptoProvider.FillWithRandom(memoryToFill); buffer.Advance(RandomLength); buffer.WriteBigEndian(connectionState.CipherSuite.CipherCode); BufferExtensions.WriteVector <ushort>(ref buffer, ExtensionsWrite.WriteExtensionList, connectionState); return(buffer); }
public void ReceiveBeginComplete(uint bytesTransferred) { if (bytesTransferred == 0 || _input.Writing.IsCompleted) { _input.CompleteWriter(); } else { _buffer.Advance((int)bytesTransferred); _buffer.Commit(); ProcessReceives(); } }
public override void Encrypt(ref WritableBuffer writer, ReadableBuffer plainText, RecordType recordType, TlsVersion tlsVersion) { _key.IV.Span.Slice(4).WriteBigEndian(_sequenceNumber); _key.Init(KeyMode.Encryption); var additionalInfo = new AdditionalInfo() { SequenceNumber = _sequenceNumber, RecordType = recordType, TlsVersion = tlsVersion, PlainTextLength = (ushort)plainText.Length }; _key.AddAdditionalInfo(ref additionalInfo); writer.WriteBigEndian(_sequenceNumber); var totalBytes = plainText.Length; foreach (var b in plainText) { if (b.Length == 0) { continue; } totalBytes -= b.Length; writer.Ensure(b.Length); int bytesWritten; if (totalBytes == 0) { bytesWritten = _key.Finish(b.Span, writer.Buffer.Span); writer.Advance(bytesWritten); break; } bytesWritten = _key.Update(b.Span, writer.Buffer.Span); writer.Advance(bytesWritten); } IncrementSequence(); WriteTag(ref writer); }
/// <summary> /// Writes the source <see cref="Span{Byte}"/> to the <see cref="WritableBuffer"/>. /// </summary> /// <param name="buffer">The <see cref="WritableBuffer"/></param> /// <param name="source">The <see cref="Span{Byte}"/> to write</param> public static void Write(this WritableBuffer buffer, ReadOnlySpan <byte> source) { if (buffer.Memory.IsEmpty) { buffer.Ensure(); } // Fast path, try copying to the available memory directly if (source.Length <= buffer.Memory.Length) { source.CopyTo(buffer.Memory.Span); buffer.Advance(source.Length); return; } var remaining = source.Length; var offset = 0; while (remaining > 0) { var writable = Math.Min(remaining, buffer.Memory.Length); buffer.Ensure(writable); if (writable == 0) { continue; } source.Slice(offset, writable).CopyTo(buffer.Memory.Span); remaining -= writable; offset += writable; buffer.Advance(writable); } }
public static WritableBuffer WriteClientHello(WritableBuffer buffer, IConnectionStateTls13 connectionState) { buffer.WriteBigEndian <ushort>(0x0303); buffer.Ensure(RandomLength); connectionState.CryptoProvider.FillWithRandom(buffer.Memory.Slice(0, RandomLength)); buffer.Advance(RandomLength); //legacy sessionid buffer.WriteBigEndian((byte)0); connectionState.CryptoProvider.WriteCipherSuites(ref buffer); //legacy compression buffer.WriteBigEndian((byte)1); buffer.WriteBigEndian((byte)0); connectionState.KeyShare = connectionState.CryptoProvider.GetDefaultKeyShare(); BufferExtensions.WriteVector <ushort>(ref buffer, ExtensionsWrite.WriteExtensionList, connectionState); return(buffer); }
internal static unsafe void Encrypt <T>(this T context, WritableBuffer outBuffer, ReadableBuffer buffer) where T : ISecureContext { outBuffer.Ensure(context.TrailerSize + context.HeaderSize + buffer.Length); void *outBufferPointer; outBuffer.Memory.TryGetPointer(out outBufferPointer); buffer.CopyTo(outBuffer.Memory.Slice(context.HeaderSize, buffer.Length)); var securityBuff = stackalloc SecurityBuffer[4]; SecurityBufferDescriptor sdcInOut = new SecurityBufferDescriptor(4); securityBuff[0].size = context.HeaderSize; securityBuff[0].type = SecurityBufferType.Header; securityBuff[0].tokenPointer = outBufferPointer; securityBuff[1].size = buffer.Length; securityBuff[1].type = SecurityBufferType.Data; securityBuff[1].tokenPointer = (byte *)outBufferPointer + context.HeaderSize; securityBuff[2].size = context.TrailerSize; securityBuff[2].type = SecurityBufferType.Trailer; securityBuff[2].tokenPointer = (byte *)outBufferPointer + context.HeaderSize + buffer.Length; securityBuff[3].size = 0; securityBuff[3].tokenPointer = null; securityBuff[3].type = SecurityBufferType.Empty; sdcInOut.UnmanagedPointer = securityBuff; var handle = context.ContextHandle; var result = (SecurityStatus)InteropSspi.EncryptMessage(ref handle, 0, sdcInOut, 0); if (result == 0) { outBuffer.Advance(context.HeaderSize + context.TrailerSize + buffer.Length); } else { //Zero out the output buffer before throwing the exception to stop any data being sent in the clear //By a misbehaving underlying channel Span <byte> memoryToClear = new Span <byte>(outBufferPointer, context.HeaderSize + context.TrailerSize + buffer.Length); byte * empty = stackalloc byte[context.HeaderSize + context.TrailerSize + buffer.Length]; memoryToClear.Set(empty, context.HeaderSize + context.TrailerSize + buffer.Length); throw new InvalidOperationException($"There was an issue encrypting the data {result}"); } }
public void WriteBigEndian <T>(T value) where T : struct { var size = Unsafe.SizeOf <T>(); if (_bytesRemaining < size) { _innerBuffer.Ensure(size); _bytesRemaining = _innerBuffer.Buffer.Length; } var s = _innerBuffer.Buffer.Span; size = s.WriteBigEndian(value); _handshakeHash?.HashData(s.Slice(0, size)); _innerBuffer.Advance(size); _bytesWritten += size; _bytesRemaining -= size; }
private static void WriteUtf8String(ref WritableBuffer buffer, ReadOnlySpan <char> value) { if (value == null || value.Length == 0) { return; } var encoder = TextEncoder.Utf8; while (value.Length != 0) { buffer.Ensure(4); // be able to write at least one character (worst case) var span = buffer.Buffer.Span; encoder.TryEncode(value, span, out int charsConsumed, out int bytesWritten); buffer.Advance(bytesWritten); buffer.Commit(); value = value.Slice(charsConsumed); } }
public unsafe void EncryptWithAuthData(ref WritableBuffer buffer, RecordType recordType, ushort tlsVersion, int plaintextLength) { var additionalData = stackalloc byte[13]; var additionalSpan = new Span <byte>(additionalData, 13); additionalSpan.Write64BitNumber(_sequenceNumber); additionalSpan = additionalSpan.Slice(8); additionalSpan.Write(recordType); additionalSpan = additionalSpan.Slice(1); additionalSpan.Write(tlsVersion); additionalSpan = additionalSpan.Slice(2); additionalSpan.Write16BitNumber((ushort)plaintextLength); var plainText = buffer.AsReadableBuffer(); plainText = plainText.Slice(plainText.Length - plaintextLength); ThrowOnError(EVP_CipherInit_ex(_ctx, _cipherType, IntPtr.Zero, (byte *)_keyPointer, (void *)_ivPointer, (int)KeyMode.Encryption)); int outSize = 0; ThrowOnError(EVP_CipherUpdate(_ctx, null, ref outSize, additionalData, 13)); void *inPointer; foreach (var b in plainText) { if (b.Length == 0) { continue; } b.TryGetPointer(out inPointer); outSize = b.Length; ThrowOnError(EVP_CipherUpdate(_ctx, inPointer, ref outSize, inPointer, outSize)); } buffer.Ensure(_overhead); buffer.Memory.TryGetPointer(out inPointer); ThrowOnError(EVP_CipherFinal_ex(_ctx, null, ref outSize)); ThrowOnError(EVP_CIPHER_CTX_ctrl(_ctx, EVP_CIPHER_CTRL.EVP_CTRL_GCM_GET_TAG, _overhead, inPointer)); buffer.Advance(_overhead); _sequenceNumber++; IncrementSequence(); }
public override void Encrypt(ref WritableBuffer writer, Span <byte> plainText, RecordType recordType, TlsVersion tlsVersion) { _key.IV.Span.Slice(4).WriteBigEndian(_sequenceNumber); _key.Init(KeyMode.Encryption); var additionalInfo = new AdditionalInfo() { SequenceNumber = _sequenceNumber, RecordType = recordType, TlsVersion = tlsVersion, PlainTextLength = (ushort)plainText.Length }; _key.AddAdditionalInfo(ref additionalInfo); writer.WriteBigEndian(_sequenceNumber); writer.Ensure(plainText.Length); var bytesWritten = _key.Finish(plainText, writer.Buffer.Span); writer.Advance(bytesWritten); IncrementSequence(); WriteTag(ref writer); }
public unsafe void WritePublicKey(ref WritableBuffer keyBuffer) { if (!_publicPrivateKey.IsValid()) { GenerateKeyset(); } IntPtr ptr; var buffSize = (int)ThrowOnError(EVP_PKEY_get1_tls_encodedpoint(_publicPrivateKey, out ptr)); try { keyBuffer.Ensure(buffSize); var span = new Span <byte>((byte *)ptr, buffSize); span.CopyTo(keyBuffer.Memory.Span); keyBuffer.Advance(span.Length); } finally { CRYPTO_clear_free(ptr, (UIntPtr)buffSize, "ECFunctionInstance.cs", 97); } }
public unsafe void WritePublicKey(ref WritableBuffer keyBuffer) { BIGNUM priv, pub; DH_get0_key(_localKey, out pub, out priv); keyBuffer.Ensure(_keyExchangeSize); GCHandle handle; void * ptr = keyBuffer.Memory.GetPointer(out handle); try { var written = BN_bn2binpad(pub, ptr, _keyExchangeSize); keyBuffer.Advance(written); } finally { if (handle.IsAllocated) { handle.Free(); } } }
// review: make public? private static unsafe void WriteString(this WritableBuffer buffer, string value, Encoding encoding) { int bytesPerChar = encoding.GetMaxByteCount(1); fixed(char *s = value) { int remainingChars = value.Length, charOffset = 0; while (remainingChars != 0) { buffer.Ensure(bytesPerChar); var memory = buffer.Memory; var charsThisBatch = Math.Min(remainingChars, memory.Length / bytesPerChar); int bytesWritten = encoding.GetBytes(s + charOffset, charsThisBatch, (byte *)memory.UnsafePointer, memory.Length); charOffset += charsThisBatch; remainingChars -= charsThisBatch; buffer.Advance(bytesWritten); } } }
public unsafe int SignHash(IHashProvider provider, SignatureScheme scheme, ref WritableBuffer writer, byte *message, int messageLength) { var hash = provider.GetHashInstance(_hashType); hash.HashData(message, messageLength); var digest = new byte[hash.HashSize]; fixed(byte *dPtr = digest) { hash.InterimHash(dPtr, digest.Length); } writer.Ensure(ECDSA_size(_ecKey)); GCHandle handle; var output = writer.Memory.GetPointer(out handle); try { fixed(byte *iPtr = digest) { var sigSize = writer.Memory.Length; ThrowOnError(ECDSA_sign(0, iPtr, digest.Length, output, ref sigSize, _ecKey)); writer.Advance(sigSize); return(sigSize); } } finally { if (handle.IsAllocated) { handle.Free(); } } }
private async Task ReceiveFromSocketAndPushToWriterAsync() { SocketAsyncEventArgs args = null; try { // wait for someone to be interested in data before we // start allocating buffers and probing the socket args = GetOrCreateSocketAsyncEventArgs(); while (!_stopping) { bool haveWriteBuffer = false; WritableBuffer buffer = default(WritableBuffer); var initialSegment = default(ArraySegment <byte>); try { int bytesFromInitialDataBuffer = 0; if (Socket.Available == 0) { // now, this gets a bit messy unfortunately, because support for the ideal option // (zero-length reads) is platform dependent switch (_bufferStyle) { case BufferStyle.Unknown: try { initialSegment = await ReceiveInitialDataUnknownStrategyAsync(args); } catch { initialSegment = default(ArraySegment <byte>); } if (initialSegment.Array == null) { continue; // redo from start } break; case BufferStyle.UseZeroLengthBuffer: // if we already have a buffer, use that (but: zero count); otherwise use a shared // zero-length; this avoids constantly changing the buffer that the args use, which // avoids some overheads args.SetBuffer(args.Buffer ?? _zeroLengthBuffer, 0, 0); // await async for the io work to be completed await Socket.ReceiveSignalAsync(args); break; case BufferStyle.UseSmallBuffer: // We need to do a speculative receive with a *cheap* buffer while we wait for input; it would be *nice* if // we could do a zero-length receive, but this is not supported equally on all platforms (fine on Windows, but // linux hates it). The key aim here is to make sure that we don't tie up an entire block from the memory pool // waiting for input on a socket; fine for 1 socket, not so fine for 100,000 sockets // do a short receive while we wait (async) for data initialSegment = LeaseSmallBuffer(); args.SetBuffer(initialSegment.Array, initialSegment.Offset, initialSegment.Count); // await async for the io work to be completed await Socket.ReceiveSignalAsync(args); break; } if (args.SocketError != SocketError.Success) { throw new SocketException((int)args.SocketError); } // note we can't check BytesTransferred <= 0, as we always // expect 0; but if we returned, we expect data to be // buffered *on the socket*, else EOF if ((bytesFromInitialDataBuffer = args.BytesTransferred) <= 0) { if (ReferenceEquals(initialSegment.Array, _zeroLengthBuffer)) { // sentinel value that means we should just // consume sync (we expect there to be data) initialSegment = default(ArraySegment <byte>); } else { // socket reported EOF RecycleSmallBuffer(ref initialSegment); } if (Socket.Available == 0) { // yup, definitely an EOF break; } } } // note that we will try to coalesce things here to reduce the number of flushes; we // certainly want to coalesce the initial buffer (from the speculative receive) with the initial // data, but we probably don't want to buffer indefinitely; for now, it will buffer up to 4 pages // before flushing (entirely arbitrarily) - might want to make this configurable later buffer = _input.Writer.Alloc(SmallBufferSize * 2); haveWriteBuffer = true; const int FlushInputEveryBytes = 4 * MemoryPool.MaxPooledBlockLength; if (initialSegment.Array != null) { // need to account for anything that we got in the speculative receive if (bytesFromInitialDataBuffer != 0) { buffer.Write(new Span <byte>(initialSegment.Array, initialSegment.Offset, bytesFromInitialDataBuffer)); } // make the small buffer available to other consumers RecycleSmallBuffer(ref initialSegment); } bool isEOF = false; while (Socket.Available != 0 && buffer.BytesWritten < FlushInputEveryBytes) { buffer.Ensure(); // ask for *something*, then use whatever is available (usually much much more) SetBuffer(buffer.Buffer, args); // await async for the io work to be completed await Socket.ReceiveSignalAsync(args); // either way, need to validate if (args.SocketError != SocketError.Success) { throw new SocketException((int)args.SocketError); } int len = args.BytesTransferred; if (len <= 0) { // socket reported EOF isEOF = true; break; } // record what data we filled into the buffer buffer.Advance(len); } if (isEOF) { break; } } finally { RecycleSmallBuffer(ref initialSegment); if (haveWriteBuffer) { _stopping = (await buffer.FlushAsync()).IsCompleted; } } } _input.Writer.Complete(); } catch (Exception ex) { // don't trust signal after an error; someone else could // still have it and invoke Set if (args != null) { args.UserToken = null; } _input?.Writer.Complete(ex); } finally { try { Socket.Shutdown(SocketShutdown.Receive); } catch { } RecycleSocketAsyncEventArgs(args); } }
public void Advance(int bytes) { _writableBuffer.Advance(bytes); }