12

我一直在尝试使用 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;
    }
}
4

1 回答 1

7

从 HttpClient 的角度来看,内容分块本质上是传输的一个细节。response.Content 提供的内容总是由 HttpClient 为您分块。

Web API 中似乎存在一个错误,即在 IIS 上运行时,当 response.Headers.TransferEncodingChunked 属性请求时,它不能正确(重新)分块内容。所以问题是代理通过标头告诉客户端内容是分块的,而实际上不是。我在这里提交了错误: https ://aspnetwebstack.codeplex.com/workitem/1124

我认为您的解决方法是目前最好的选择。

另请注意,您在这里有多个层,可能没有针对代理场景进行设计/测试(并且可能不支持它)。在 HttpClient 端,请注意它会自动解压缩并遵循重定向,除非您关闭该行为。至少,您需要设置以下两个属性: http://msdn.microsoft.com/en-us/library/system.net.http.httpclienthandler.allowautoredirect.aspx http://msdn.microsoft.com /en-us/library/system.net.http.httpclienthandler.automaticdecompression.aspx

在 WebApi/IIS 方面,您至少发现了一个错误,而且发现其他错误也不足为奇。请注意,目前可能存在像这样的错误,目前在其主要设计用例之外使用这些技术编写代理。

于 2013-07-12T18:48:47.330 回答