モノトーンの伝説日記

Apex Legends, Splatoon, Programming, and so on...

<mini> UWP 版 System.Net.Http.HttpClient で Cookie に HttpOnly 属性がついている場合クッキーが取得できないという問題に対する対策コード。 → 使うのやめてください

※ 使うな危険! このアプローチを使うと死亡します。

 題名長い。

 問題について前回記事をご覧ください。

mntone.hateblo.jp

 対策コードはこちらです。

// 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 じゃない (ProcessResponseCookiesHttpClientHandler に存在しない) とき、自動で HttpOnly 属性が付いたものを追加する処理になっています。簡単ですね。

 二度クッキーをパースしてるのが気になるところですが…

 以上です。

追記:

 handler.MaxAutomaticRedirections != 10 で patch するかどうか決めてるので仕様変更に弱そう (.NET Native で private methods がとれないからこの方法しか思いつかない… 誰かいい方法あれば)