最近在重写BeetleX中需要实现ReadOnlySequence Stream并支持SslStream的功能,其实在旧版本的BeetleX中就实现过这一功能;现在针对支持ReadOnlySequence进行一个重写!测试的时候发现无论我怎传一个byte[]给SslStream的Read方法时,传到自定义的内部Stream总会是byte[0]....翻阅了一堆SslStream的帮助都没办法知道这问题的原因;想提issues吧感觉很难举证毕竟SslStream要基于网络模块才好处理,也正是这自定义Stream模块代码,而其代码功能也不完善不好说具体问题!最后只能调试.net的SDK的源码了。。。。
[AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))]
private async ValueTask<int> EnsureFullTlsFrameAsync<TIOAdapter>(CancellationToken cancellationToken, int estimatedSize)
where TIOAdapter : IReadWriteAdapter
{
if (HaveFullTlsFrame(out int frameSize))
{
return frameSize;
}
await TIOAdapter.ReadAsync(InnerStream, Memory<byte>.Empty, cancellationToken).ConfigureAwait(false);
// If we don't have enough data to determine the frame size, use the provided estimate
// (e.g. a full TLS frame for reads, and a somewhat shorter frame for handshake / renegotiation).
// If we do know the frame size, ensure we have space for the whole frame.
_buffer.EnsureAvailableSpace(frameSize == UnknownTlsFrameLength ?
estimatedSize :
frameSize - _buffer.EncryptedLength);
while (_buffer.EncryptedLength < frameSize)
{
// there should be space left to read into
Debug.Assert(_buffer.AvailableLength > 0, "_buffer.AvailableBytes > 0");
// We either don't have full frame or we don't have enough data to even determine the size.
int bytesRead = await TIOAdapter.ReadAsync(InnerStream, _buffer.AvailableMemory, cancellationToken).ConfigureAwait(false);
if (bytesRead == 0)
{
if (_buffer.EncryptedLength != 0)
{
// we got EOF in middle of TLS frame. Treat that as error.
throw new IOException(SR.net_io_eof);
}
return 0;
}
_buffer.Commit(bytesRead);
if (frameSize == int.MaxValue && _buffer.EncryptedLength > TlsFrameHelper.HeaderSize)
{
// recalculate frame size if needed e.g. we could not get it before.
frameSize = GetFrameSize(_buffer.EncryptedReadOnlySpan);
_buffer.EnsureAvailableSpace(frameSize - _buffer.EncryptedLength);
}
}
return frameSize;
}
最后跟踪到以上方法时找到了下面一行代码。。。。
await TIOAdapter.ReadAsync(InnerStream, Memory<byte>.Empty, cancellationToken).ConfigureAwait(false);
// If we don't have enough data to determine the frame size, use the provided estimate
// (e.g. a full TLS frame for reads, and a somewhat shorter frame for handshake / renegotiation).
// If we do know the frame size, ensure we have space for the whole frame.
试探性发起一个Read byte[0],虽然注释写了为什么这样做,但我怎么看也看出来这方法执行的代码对后面的逻辑有什么影响。。。。在实现过中只要收到Read byte[0]直接响应读取零长度就好了,之前的实现在当前Stream没有数据时不管传参是什么都返回一个Task awaiter,所以导致功能无法正常使用。。。
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
if (!SslCompleted && IsSsl)
{
return await base.ReadAsync(buffer, cancellationToken);
}
else
{
if (buffer.Length == 0)
return 0;
if (Length > 0)
{
int result = 0;
readCompletionSource = null;
if (_ReadBuffer.Length >= buffer.Length)
{
result = buffer.Length;
_ReadBuffer.Slice(0, buffer.Length).CopyTo(buffer.Span);
ReadAdvance(buffer.Length);
}
else
{
result = (int)_ReadBuffer.Length;
_ReadBuffer.Slice(0, _ReadBuffer.Length).CopyTo(buffer.Span);
ReadAdvance(_ReadBuffer.Length);
}
return result;
}
else
{
readCompletionSource = new ReadTaskCompletionSource();
readCompletionSource.Buffer = buffer;
return await readCompletionSource.Task;
}
}
}
BeetleX
开源跨平台通讯框架(支持TLS)
提供HTTP,Websocket,MQTT,Redis,RPC和服务网关开源组件
个人微信:henryfan128 QQ:28304340
关注公众号
https://github.com/beetlex-io/