我一直在尝试使用 Web API(Web 主机)作为代理服务器,并且遇到了我的 Web API 代理如何处理带有“Transfer-Encoding: chunked”标头的响应的问题。
绕过代理时,远程资源发送以下响应标头:
Cache-Control:no-cache
Content-Encoding:gzip
Content-Type:text/html
Date:Fri, 24 May 2013 12:42:27 GMT
Expires:-1
Pragma:no-cache
Server:Microsoft-IIS/8.0
Transfer-Encoding:chunked
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-Powered-By:ASP.NET
通过基于 Web API 的代理时,除非我将响应标头上的 TransferEncodingChunked 属性显式重置为 false,否则我的请求会挂起:
response.Headers.TransferEncodingChunked = false;
我承认,我不完全理解设置 TransferEncodingChunked 属性有什么影响,但我觉得很奇怪,为了使代理按预期工作,当传入的响应明显具有“传输编码:分块”标头。我还担心显式设置此属性的副作用。谁能帮助我了解发生了什么以及为什么需要设置此属性?
更新:所以我对通过代理与不通过代理时响应的差异进行了更多挖掘。无论我是否将 TransferEncodingChunked 属性显式设置为 false,通过代理时的响应标头与不通过代理时的响应标头完全相同。但是,响应内容不同。以下是一些示例(我关闭了 gzip 编码):
// With TransferEncodingChunked = false
2d\r\n
This was sent with transfer-encoding: chunked\r\n
0\r\n
// Without explicitly setting TransferEncodingChunked
This was sent with transfer-encoding: chunked
显然,将 TransferEncodingChunked 设置为 false 发送的内容实际上是传输编码的。这实际上是正确的响应,因为它是从代理后面的请求资源接收到的。仍然奇怪的是第二种情况,其中我没有在响应上显式设置 TransferEncodingChunked(但它位于从代理服务接收到的响应标头中)。显然,在这种情况下,响应实际上不是由 IIS 编码的传输,尽管实际响应是。奇怪......这开始感觉像是设计的行为(在这种情况下,我很想知道如何/为什么)或 IIS、ASP.Net 或 Web API 中的错误。
这是我正在运行的代码的简化版本:
代理 Web API 应用程序:
// WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "Proxy",
routeTemplate: "{*path}",
handler: HttpClientFactory.CreatePipeline(
innerHandler: new HttpClientHandler(), // Routes the request to an external resource
handlers: new DelegatingHandler[] { new ProxyHandler() }
),
defaults: new { path = RouteParameter.Optional },
constraints: null
);
// ProxyHandler.cs
public class ProxyHandler : DelegatingHandler
{
protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
// Route the request to my web application
var uri = new Uri("http://localhost:49591" + request.RequestUri.PathAndQuery);
request.RequestUri = uri;
// For GET requests, somewhere upstream, Web API creates an empty stream for the request.Content property
// HttpClientHandler doesn't like this for GET requests, so set it back to null before sending along the request
if (request.Method == HttpMethod.Get)
{
request.Content = null;
}
var response = await base.SendAsync(request, cancellationToken);
// If I comment this out, any response that already has the Transfer-Encoding: chunked header will hang in the browser
response.Headers.TransferEncodingChunked = false;
return response;
}
}
我的 Web 应用程序控制器创建了一个“分块”响应(也是 Web API):
public class ChunkedController : ApiController
{
public HttpResponseMessage Get()
{
var response = Request.CreateResponse(HttpStatusCode.OK);
var content = "This was sent with transfer-encoding: chunked";
var bytes = System.Text.Encoding.ASCII.GetBytes(content);
var stream = new MemoryStream(bytes);
response.Content = new ChunkedStreamContent(stream);
return response;
}
}
public class ChunkedStreamContent : StreamContent
{
public ChunkedStreamContent(Stream stream)
: base(stream) { }
protected override bool TryComputeLength(out long length)
{
length = 0L;
return false;
}
}