如何将循环中的字节存储到 var 中,以便可以解压缩 Web 响应?
How do you store bytes to a var from a loop so that web response can be decompressed?
我一直在使用 HTTPWebRequest/HTTPWebResponse 构建自己的 HTTP/1.0 代理服务器。部分原始代码将远程服务器的响应直接流式传输到客户端,如下所示:
if (response != null)
{
List<Tuple<String, String>> responseHeaders = ProcessResponse(response);
StreamWriter myResponseWriter = new StreamWriter(outStream);
Stream responseStream = response.GetResponseStream();
HttpStatusCode statusCode = response.StatusCode;
string statusDesc = response.StatusDescription;
Byte[] buffer;
if (response.ContentLength > 0)
{
buffer = new Byte[response.ContentLength];
}
else
{
buffer = new Byte[BUFFER_SIZE];
}
int bytesRead;
//send the response status and response headers
WriteResponseStatus(statusCode, statusDesc, myResponseWriter);
WriteResponseHeaders(myResponseWriter, responseHeaders);
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
// this is the response body being streamed directly to the client browser
outStream.Write(buffer, 0, bytesRead);
}
}
我一直在尝试拦截响应正文以便我可以修改内容,但在我这样做之前我需要解压缩内容(如果它已被远程服务器gzip/br/deflate)。到目前为止,这是我的想法,但是正如您从我的评论中看到的那样,我无法弄清楚如何将字节流存储到一个 var 中,以便我可以将其发送以进行解压缩:
Byte[] buffer;
if (response.ContentLength > 0)
buffer = new Byte[response.ContentLength];
else
buffer = new Byte[BUFFER_SIZE];
int bytesRead;
var res = "";
// if the url and content type matches the criteria, then we want to edit it
if (hostPathMatch.Count > 0 && contentTypeMatch.Count > 0)
{
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
// how to we send this response stream to a var so that the entire contents can be sent to decompress
//res += UTF8Encoding.UTF8.GetString(buffer, 0, bytesRead); // this doesnt work as it mangles gzipped contents
}
//was the page compressed? check the content-encoding header.
if (responseHeaders.Any(p => p.Item1.ToLower() == "content-encoding" && p.Item2.ToLower() == "gzip"))
{
Output._Log.Output_Log("CONTENT IS GZIPPED");
res = Tools.Unzip(res); // expects byte[], returns UTF8
}
// THIS IS WHERE WE WILL MODIFY THE BODY CONTENTS
res = res.Replace("Foo", "Bar Bar");
// then we will re-compress
// update the response headers with the correct content length after modification
responseHeaders.RemoveAll(p => p.Item1 == "Content-Length");
responseHeaders.Add(new Tuple<string, string>("Content-Length", res.Length));
//send the response status and response headers
WriteResponseStatus(statusCode, statusDesc, myResponseWriter);
WriteResponseHeaders(myResponseWriter, responseHeaders);
}
else // we didnt want to modify this file, so just stream it out directly to the browser
{
//send the response status and response headers
WriteResponseStatus(statusCode, statusDesc, myResponseWriter);
WriteResponseHeaders(myResponseWriter, responseHeaders);
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
outStream.Write(buffer, 0, bytesRead);
}
}
对于事先不知道总大小的情况,可以做如下操作
[...]
List<byte> data = new List<byte>();
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
for(int i = 0; i < bytesRead; ++i)
data.Add(buffer[i]);
}
var bytes = data.ToArray();
[...]
如果你事先知道,你可以使用类似
的东西
[...]
int offset = 0;
byte[] data = new byte[TOTAL_SIZE];
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
buffer.CopyTo(data, offset);
offset += bytesRead;
}
[...]
请注意,以上代码段均未经过测试,必须添加一些检查(例如超出范围等)。此外,根据用例,它可能不符合您的需求(例如,对于海量数据)。
PS:不要忘记 dispose/cleanup 所有 IDisposable
。
我一直在使用 HTTPWebRequest/HTTPWebResponse 构建自己的 HTTP/1.0 代理服务器。部分原始代码将远程服务器的响应直接流式传输到客户端,如下所示:
if (response != null)
{
List<Tuple<String, String>> responseHeaders = ProcessResponse(response);
StreamWriter myResponseWriter = new StreamWriter(outStream);
Stream responseStream = response.GetResponseStream();
HttpStatusCode statusCode = response.StatusCode;
string statusDesc = response.StatusDescription;
Byte[] buffer;
if (response.ContentLength > 0)
{
buffer = new Byte[response.ContentLength];
}
else
{
buffer = new Byte[BUFFER_SIZE];
}
int bytesRead;
//send the response status and response headers
WriteResponseStatus(statusCode, statusDesc, myResponseWriter);
WriteResponseHeaders(myResponseWriter, responseHeaders);
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
// this is the response body being streamed directly to the client browser
outStream.Write(buffer, 0, bytesRead);
}
}
我一直在尝试拦截响应正文以便我可以修改内容,但在我这样做之前我需要解压缩内容(如果它已被远程服务器gzip/br/deflate)。到目前为止,这是我的想法,但是正如您从我的评论中看到的那样,我无法弄清楚如何将字节流存储到一个 var 中,以便我可以将其发送以进行解压缩:
Byte[] buffer;
if (response.ContentLength > 0)
buffer = new Byte[response.ContentLength];
else
buffer = new Byte[BUFFER_SIZE];
int bytesRead;
var res = "";
// if the url and content type matches the criteria, then we want to edit it
if (hostPathMatch.Count > 0 && contentTypeMatch.Count > 0)
{
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
// how to we send this response stream to a var so that the entire contents can be sent to decompress
//res += UTF8Encoding.UTF8.GetString(buffer, 0, bytesRead); // this doesnt work as it mangles gzipped contents
}
//was the page compressed? check the content-encoding header.
if (responseHeaders.Any(p => p.Item1.ToLower() == "content-encoding" && p.Item2.ToLower() == "gzip"))
{
Output._Log.Output_Log("CONTENT IS GZIPPED");
res = Tools.Unzip(res); // expects byte[], returns UTF8
}
// THIS IS WHERE WE WILL MODIFY THE BODY CONTENTS
res = res.Replace("Foo", "Bar Bar");
// then we will re-compress
// update the response headers with the correct content length after modification
responseHeaders.RemoveAll(p => p.Item1 == "Content-Length");
responseHeaders.Add(new Tuple<string, string>("Content-Length", res.Length));
//send the response status and response headers
WriteResponseStatus(statusCode, statusDesc, myResponseWriter);
WriteResponseHeaders(myResponseWriter, responseHeaders);
}
else // we didnt want to modify this file, so just stream it out directly to the browser
{
//send the response status and response headers
WriteResponseStatus(statusCode, statusDesc, myResponseWriter);
WriteResponseHeaders(myResponseWriter, responseHeaders);
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
outStream.Write(buffer, 0, bytesRead);
}
}
对于事先不知道总大小的情况,可以做如下操作
[...]
List<byte> data = new List<byte>();
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
for(int i = 0; i < bytesRead; ++i)
data.Add(buffer[i]);
}
var bytes = data.ToArray();
[...]
如果你事先知道,你可以使用类似
的东西[...]
int offset = 0;
byte[] data = new byte[TOTAL_SIZE];
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
buffer.CopyTo(data, offset);
offset += bytesRead;
}
[...]
请注意,以上代码段均未经过测试,必须添加一些检查(例如超出范围等)。此外,根据用例,它可能不符合您的需求(例如,对于海量数据)。
PS:不要忘记 dispose/cleanup 所有 IDisposable
。