2

メソッドを介して取得する HttpWebRequest クラスを使用してリモート Web アドレスを呼び出すアプリケーションがありますWebRequest.Create

これが実際のコードです。

var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "HEAD";
request.Timeout = this.connectionTimeout;

if (this.usePipelinedConnection)
{
     request.KeepAlive = true;
     request.Pipelined = true;
}

request.BeginGetResponse(cb => logService.EndGetRequestStream(cb), null);

現在、非決定論的な方法で (再現するパターンが見つからない)、次のエラーが発生します。

System.InvalidCastException

タイプ 'System.Net.HttpWebResponse' のオブジェクトをタイプ 'System.Exception' にキャストできません。

このスタック トレースで:

System.Net.HttpWebRequest.EndGetRequestStream (IAsyncResult asyncResult、TransportContext& コンテキスト) で

System.Net.HttpWebRequest.EndGetRequestStream (IAsyncResult asyncResult) で

System.Net.LazyAsyncResult.Complete (IntPtr userToken) で

System.Net.ContextAwareResult.CaptureOrComplete (ExecutionContext& cachedContext、ブール値の returnContext) で

System.Net.ContextAwareResult.FinishPostingAsyncOp() で

System.Net.HttpWebRequest.BeginGetResponse (AsyncCallback コールバック、オブジェクトの状態) で

このメソッドに関するドキュメントでは、スローされる可能性のあるいくつかの例外が報告されていますがInvalidCastException、それらの 1 つではありません。つまり、Microsoft メソッドでは処理されません。.Net ソースを掘り下げ始めたところ、犯人が見つかったと思います。HttpWebResponse.EndGetResponseStream メソッドには、次の行があります。

throw (Exception) lazyAsyncResult.Result;

これが、このメソッドに存在する Exception への唯一のキャストであるため、これに違いありません。メソッドは、接続ストリームが null の場合にのみこの行に到達するように実装されているため、lazyasyncresult.Resultプロパティには例外が含まれている必要があります。ただし、私の場合、ブランチに到達しましたが、がlazyasyncresult.Result含まれているHttpWebResponseため、ボクシングが失敗し、そのエラーが発生します。今、私は2つの考慮事項があります:

  • の内容lazyasyncresult.Resultが正しければ、とにかくスローされます (行がスローで始まるため) が、意味のあるエラーになります。
  • 前のポイントに関連して、HttpWebResponse がある場合、スローするコード ブランチにとにかく到達するべきではないと思います

私の質問は非常に単純です。どうすればこれが起こらないようにできますか? コードで何か間違ったことをしているのですか、それとも MS メソッドの単純なバグですか?

参考までに、メソッドの MS ソースを以下に示します。非難された行にいくつかのコメントを追加しました。

お時間をいただきありがとうございます。

public Stream EndGetRequestStream(IAsyncResult asyncResult, out TransportContext context)
    {
      if (Logging.On)
        Logging.Enter(Logging.Web, (object) this, "EndGetRequestStream", "");
      context = (TransportContext) null;
      if (asyncResult == null)
        throw new ArgumentNullException("asyncResult");
      LazyAsyncResult lazyAsyncResult = asyncResult as LazyAsyncResult;
      if (lazyAsyncResult == null || lazyAsyncResult.AsyncObject != this)
        throw new ArgumentException(SR.GetString("net_io_invalidasyncresult"), "asyncResult");
      if (lazyAsyncResult.EndCalled)
      {
        throw new InvalidOperationException(SR.GetString("net_io_invalidendcall", new object[1]
        {
          (object) "EndGetRequestStream"
        }));
      }
      else
      {
        ConnectStream connectStream = lazyAsyncResult.InternalWaitForCompletion() as ConnectStream;
        lazyAsyncResult.EndCalled = true;
        if (connectStream == null)
        {
          if (Logging.On)
            Logging.Exception(Logging.Web, (object) this, "EndGetRequestStream", lazyAsyncResult.Result as Exception);

// Here result contains HttpWebResponse so the cast to Exception fails. 
// It would throw anyway (since there' a throw) but I think, since result contains a response
// that the code shouldn't be hitting this if branch.
          throw (Exception) lazyAsyncResult.Result;
        }
        else
        {
          context = (TransportContext) new ConnectStreamContext(connectStream);
          if (Logging.On)
            Logging.Exit(Logging.Web, (object) this, "EndGetRequestStream", (object) connectStream);
          return (Stream) connectStream;
        }
      }
    }
4

1 に答える 1