Web API (Web Host) をプロキシ サーバーとして使用して遊んでいて、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 プロパティの設定がどのような影響を与えるかは完全には理解していませんが、プロキシを期待どおりに動作させるには、着信応答に明らかに " Transfer-Encoding: chunked" ヘッダー。このプロパティを明示的に設定することによる副作用についても心配しています。何が起こっているのか、なぜこのプロパティの設定が必要なのかを理解するのを手伝ってくれる人はいますか?
更新: そこで、プロキシを通過する場合としない場合の応答の違いをもう少し掘り下げました。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 を明示的に設定しない 2 番目のシナリオです (ただし、プロキシされたサービスから受信した応答ヘッダーにあります)。明らかに、この場合、実際の応答が 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 APIも)を作成する私のWebアプリケーションコントローラー:
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;
}
}