分块流响应不正确
Chunked Stream Responds Incorrectly
对不起,这么模糊的标题,我真的不知道给这个问题起什么标题。
基本上当我得到一个按照 Transfer-Encoding 分块的流时,我会执行以下代码:
private IEnumerable<byte[]> ReceiveMessageBodyChunked() {
readChunk:
#region Read a line from the Stream which should be a Block Length (Chunk Body Length)
string blockLength = _receiverHelper.ReadLine();
#endregion
#region If the end of the block is reached, re-read from the stream
if (blockLength == Http.NewLine) {
goto readChunk;
}
#endregion
#region Trim it so it should end up with JUST the number
blockLength = blockLength.Trim(' ', '\r', '\n');
#endregion
#region If the end of the message body is reached
if (blockLength == string.Empty) {
yield break;
}
#endregion
int blockLengthInt = 0;
#region Convert the Block Length String to an Int32 base16 (hex)
try {
blockLengthInt = Convert.ToInt32(blockLength, 16);
} catch (Exception ex) {
if (ex is FormatException || ex is OverflowException) {
throw new Exception(string.Format(ExceptionValues.HttpException_WrongChunkedBlockLength, blockLength), ex);
}
throw;
}
#endregion
// If the end of the message body is reached.
if (blockLengthInt == 0) {
yield break;
}
byte[] buffer = new byte[blockLengthInt];
int totalBytesRead = 0;
while (totalBytesRead != blockLengthInt) {
int length = blockLengthInt - totalBytesRead;
int bytesRead = _receiverHelper.HasData ? _receiverHelper.Read(buffer, 0, length) : _request.ClientStream.Read(buffer, 0, length);
if (bytesRead == 0) {
WaitData();
continue;
}
totalBytesRead += bytesRead;
System.Windows.Forms.MessageBox.Show("Chunk Length: " + blockLengthInt + "\nBytes Read/Total:" + bytesRead + "/" + totalBytesRead + "\n\n" + Encoding.ASCII.GetString(buffer));
yield return buffer;
}
goto readChunk;
}
这样做是从流中读取 1 行数据,这应该是块的长度,在这里和那里做一些检查,但最终将其转换为 Int32 Radix16 整数。
从那里它基本上创建了一个 int32 的字节缓冲区作为它的长度大小。
然后它一直从流中读取,直到读取的数量与我们转换的 Int32 相同。
这很好用,但是,无论出于何种原因,它在上次读取时响应不正确。
它将读取准确的字节数作为块长度,并且读取了我期望的所有数据。但它也在再次读取另一小块数据,该数据块已经在最后读取,导致可以说从 <!DOCTYPE html>
到 </html>
的所有数据以及来自内部某处的一些数据,例如 <form>
e.t.c
这是一个发生的例子:
如您所见,突出显示的红色文本不应该从阅读中返回!它应该在 </html>
结束。
为什么 Chunk 的长度对我撒谎,我怎样才能找到合适的阅读尺寸?
我不熟悉 C# 但如果我正确理解你的代码和 C# 中 Read
的语义(这似乎类似于 C 中的 read
)那么问题是您一次又一次地使用同一个缓冲区而没有先重置它:
byte[] buffer = new byte[blockLengthInt];
int totalBytesRead = 0;
while (totalBytesRead != blockLengthInt) {
int length = blockLengthInt - totalBytesRead;
int bytesRead = _receiverHelper.HasData ? _receiverHelper.Read(buffer, 0, length) : _request.ClientStream.Read(buffer, 0, length);
...
totalBytesRead += bytesRead;
...
yield return buffer;
}
举例说明这里出了什么问题:假设块大小为 10,您读取的内容是 0123456789
,第一次读取将 return 6 个字节,第二次读取剩余 4 个字节。在这种情况下,您的缓冲区将在第一次读取后为 012345
,在第二次读取后为 567845
。缓冲区末尾的这些 45
保留了上次读取的内容,因为您只替换了缓冲区中的前 4 个字节,但保留了其余字节。
Whats odd AF is that if I hand the request to another TCPStream proxied (127.0.0.1:8888 as a proxy which is fiddler) it works perfectly fine...
Fiddler 是一个代理,可能会更改响应的传输方式。例如,它可能使用 Content-length
而不是分块编码,或者它可能使用较小的块,以便您始终在第一次读取时获得完整的块。
对不起,这么模糊的标题,我真的不知道给这个问题起什么标题。 基本上当我得到一个按照 Transfer-Encoding 分块的流时,我会执行以下代码:
private IEnumerable<byte[]> ReceiveMessageBodyChunked() {
readChunk:
#region Read a line from the Stream which should be a Block Length (Chunk Body Length)
string blockLength = _receiverHelper.ReadLine();
#endregion
#region If the end of the block is reached, re-read from the stream
if (blockLength == Http.NewLine) {
goto readChunk;
}
#endregion
#region Trim it so it should end up with JUST the number
blockLength = blockLength.Trim(' ', '\r', '\n');
#endregion
#region If the end of the message body is reached
if (blockLength == string.Empty) {
yield break;
}
#endregion
int blockLengthInt = 0;
#region Convert the Block Length String to an Int32 base16 (hex)
try {
blockLengthInt = Convert.ToInt32(blockLength, 16);
} catch (Exception ex) {
if (ex is FormatException || ex is OverflowException) {
throw new Exception(string.Format(ExceptionValues.HttpException_WrongChunkedBlockLength, blockLength), ex);
}
throw;
}
#endregion
// If the end of the message body is reached.
if (blockLengthInt == 0) {
yield break;
}
byte[] buffer = new byte[blockLengthInt];
int totalBytesRead = 0;
while (totalBytesRead != blockLengthInt) {
int length = blockLengthInt - totalBytesRead;
int bytesRead = _receiverHelper.HasData ? _receiverHelper.Read(buffer, 0, length) : _request.ClientStream.Read(buffer, 0, length);
if (bytesRead == 0) {
WaitData();
continue;
}
totalBytesRead += bytesRead;
System.Windows.Forms.MessageBox.Show("Chunk Length: " + blockLengthInt + "\nBytes Read/Total:" + bytesRead + "/" + totalBytesRead + "\n\n" + Encoding.ASCII.GetString(buffer));
yield return buffer;
}
goto readChunk;
}
这样做是从流中读取 1 行数据,这应该是块的长度,在这里和那里做一些检查,但最终将其转换为 Int32 Radix16 整数。
从那里它基本上创建了一个 int32 的字节缓冲区作为它的长度大小。
然后它一直从流中读取,直到读取的数量与我们转换的 Int32 相同。
这很好用,但是,无论出于何种原因,它在上次读取时响应不正确。
它将读取准确的字节数作为块长度,并且读取了我期望的所有数据。但它也在再次读取另一小块数据,该数据块已经在最后读取,导致可以说从 <!DOCTYPE html>
到 </html>
的所有数据以及来自内部某处的一些数据,例如 <form>
e.t.c
这是一个发生的例子:
如您所见,突出显示的红色文本不应该从阅读中返回!它应该在 </html>
结束。
为什么 Chunk 的长度对我撒谎,我怎样才能找到合适的阅读尺寸?
我不熟悉 C# 但如果我正确理解你的代码和 C# 中 Read
的语义(这似乎类似于 C 中的 read
)那么问题是您一次又一次地使用同一个缓冲区而没有先重置它:
byte[] buffer = new byte[blockLengthInt];
int totalBytesRead = 0;
while (totalBytesRead != blockLengthInt) {
int length = blockLengthInt - totalBytesRead;
int bytesRead = _receiverHelper.HasData ? _receiverHelper.Read(buffer, 0, length) : _request.ClientStream.Read(buffer, 0, length);
...
totalBytesRead += bytesRead;
...
yield return buffer;
}
举例说明这里出了什么问题:假设块大小为 10,您读取的内容是 0123456789
,第一次读取将 return 6 个字节,第二次读取剩余 4 个字节。在这种情况下,您的缓冲区将在第一次读取后为 012345
,在第二次读取后为 567845
。缓冲区末尾的这些 45
保留了上次读取的内容,因为您只替换了缓冲区中的前 4 个字节,但保留了其余字节。
Whats odd AF is that if I hand the request to another TCPStream proxied (127.0.0.1:8888 as a proxy which is fiddler) it works perfectly fine...
Fiddler 是一个代理,可能会更改响应的传输方式。例如,它可能使用 Content-length
而不是分块编码,或者它可能使用较小的块,以便您始终在第一次读取时获得完整的块。