※ 使うな危険! このアプローチを使うと死亡します。
題名長い。
問題について前回記事をご覧ください。
対策コードはこちらです。
// MIT license
internal sealed class PatchedHttpClientHandler : MessageProcessingHandler
{
public static HttpMessageHandler PatchOrDefault(HttpClientHandler handler)
{
if (handler.MaxAutomaticRedirections != 10) return handler;
return new PatchedHttpClientHandler(handler);
}
private bool _disposed = false;
private HttpClientHandler _innerHandler;
public PatchedHttpClientHandler(HttpClientHandler handler)
: base(handler)
{
this._innerHandler = handler;
}
protected override void Dispose(bool disposing)
{
if (disposing && !this._disposed)
{
this._disposed = true;
if (this._innerHandler != null)
{
this._innerHandler.Dispose();
this._innerHandler = null;
}
}
base.Dispose(disposing);
}
protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken)
{
return request;
}
protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken)
{
this.ProcessResponseCookies(response);
return response;
}
private void ProcessResponseCookies(HttpResponseMessage response)
{
IEnumerable<string> cookies = null;
if (this._innerHandler.UseCookies && response.Headers.TryGetValues("Set-Cookie", out cookies))
{
foreach (var item in cookies)
{
if (!string.IsNullOrWhiteSpace(item))
{
try
{
var requestUri = response.RequestMessage.RequestUri;
var temporaryCookieContainer = new CookieContainer();
temporaryCookieContainer.SetCookies(requestUri, item);
foreach (var cookie in temporaryCookieContainer.GetCookies(requestUri).Cast<Cookie>())
{
if (cookie.HttpOnly)
{
this._innerHandler.CookieContainer.Add(response.RequestMessage.RequestUri, cookie);
}
}
}
catch (Exception) { }
}
}
}
}
}
使い方は
this._clientHandler = new HttpClientHandler()
{
AllowAutoRedirect = false,
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
};
this._Client = new HttpClient(PatchedHttpClientHandler.PatchOrDefault(this._clientHandler));
という感じ。UWP じゃない (ProcessResponseCookies が HttpClientHandler に存在しない) とき、自動で HttpOnly 属性が付いたものを追加する処理になっています。簡単ですね。
二度クッキーをパースしてるのが気になるところですが…
以上です。
追記:
handler.MaxAutomaticRedirections != 10 で patch するかどうか決めてるので仕様変更に弱そう (.NET Native で private methods がとれないからこの方法しか思いつかない… 誰かいい方法あれば)