/// <devdoc> /// <para>Gets a <see cref='System.IO.Stream'/> that the application can use to write request data. /// This property returns a stream that the calling application can write on. /// This property is not settable. Getting this property may cause the /// request to be sent, if it wasn't already. Getting this property after /// a request has been sent that doesn't have an entity body causes an /// exception to be thrown. ///</para> /// </devdoc> public Stream GetRequestStream(out TransportContext context) { #if DEBUG using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync)) { #endif GlobalLog.Enter("HttpWebRequest#" + ValidationHelper.HashString(this) + "::GetRequestStream"); if(Logging.On)Logging.Enter(Logging.Web, this, "GetRequestStream", ""); context = null; CheckProtocol(true); // See if we're already submitted a request and have a result cached. if (_WriteAResult == null || !_WriteAResult.InternalPeekCompleted) { lock(this) { if (_WriteAResult != null) { throw new InvalidOperationException(SR.GetString(SR.net_repcall)); } // See if we're already submitted a request (e.g. via GetResponse). if (SetRequestSubmitted()) { // Not completed write stream, this is an application error. throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } // If there's already been a _ReadAResult completed, it better have been with an exception, like an abort. // We need to check within this lock. Before the lock, _WriteAResult didn't exist so won't have been notified. // BeginSubmitRequest() will fail silently if we go ahead and call it after an abort. Since we know this is the // first call to any of the [Begin]GetRe... methods by the above checks, we know ProcessResponse can't have been // called or any other valid _ReadAResult created yet. if (_ReadAResult != null) { GlobalLog.Assert(_ReadAResult.InternalPeekCompleted, "HttpWebRequest#{0}::GetRequestStream()|Incomplete _ReadAResult present on request.", ValidationHelper.HashString(this)); GlobalLog.Assert(_ReadAResult.Result is Exception, "HttpWebRequest#{0}::GetRequestStream()|_ReadAResult with successful completion already present on request.", ValidationHelper.HashString(this)); throw (Exception) _ReadAResult.Result; } // use the AsyncResult to return our Stream _WriteAResult = new LazyAsyncResult(this, null, null); Async = false; } // OK, we haven't submitted the request yet, so do so now // save off verb from origin Verb GlobalLog.Print("HttpWebRequest#" + ValidationHelper.HashString(this) + "GetRequestStream() resetting CurrentMethod to " + _OriginVerb); CurrentMethod = _OriginVerb; // Submit the Request, causes us to queue ourselves to a Connection and may block // It has happened that [....] path uses this loop the Retry memeber for handling resubmissions. while (m_Retry && !_WriteAResult.InternalPeekCompleted) { _OldSubmitWriteStream = null; _SubmitWriteStream = null; BeginSubmitRequest(); } while(Aborted && !_WriteAResult.InternalPeekCompleted) { // spin untill the _CoreResponse is set if (!(_CoreResponse is Exception)) Thread.SpinWait(1); else CheckWriteSideResponseProcessing(); } } ConnectStream connectStream = _WriteAResult.InternalWaitForCompletion() as ConnectStream; _WriteAResult.EndCalled = true; if (connectStream == null) { if (Logging.On) Logging.Exception(Logging.Web, this, "EndGetRequestStream", _WriteAResult.Result as Exception); throw (Exception) _WriteAResult.Result; } context = new ConnectStreamContext(connectStream); if(Logging.On)Logging.Exit(Logging.Web, this, "GetRequestStream", connectStream); GlobalLog.Leave("HttpWebRequest#" + ValidationHelper.HashString(this) + "::GetRequestStream", ValidationHelper.HashString(connectStream)); return connectStream; #if DEBUG } #endif }
/* RequestSubmitDone - Handle submit done callback. This is our submit done handler, called by the underlying connection code when a stream is available for our use. We save the stream for later use and signal the wait event. We also handle the continuation/termination of a BeginGetRequestStream, by saving out the result and calling its callback if needed. Input: SubmitStream - The stream we may write on. Status - The status of the submission. Returns: Nothing. */ internal void SetRequestSubmitDone(ConnectStream submitStream) { GlobalLog.Enter("HttpWebRequest#" + ValidationHelper.HashString(this) + "::SetRequestSubmitDone", ValidationHelper.HashString(submitStream)); GlobalLog.ThreadContract(ThreadKinds.Unknown, "HttpWebRequest#" + ValidationHelper.HashString(this) + "::SetRequestSubmitDone"); if (!Async) { ConnectionAsyncResult.InvokeCallback(); } if (AllowWriteStreamBuffering) { submitStream.EnableWriteBuffering(); } if (submitStream.CanTimeout) { submitStream.ReadTimeout = ReadWriteTimeout; submitStream.WriteTimeout = ReadWriteTimeout; } if(Logging.On)Logging.Associate(Logging.Web, this, submitStream); // The CBT won't actually be valid until we write to the stream for the first time, but we can create // the TransportContext now. We don't query it until later, when we know it's available. TransportContext transportContext = new ConnectStreamContext(submitStream); ServerAuthenticationState.TransportContext = transportContext; ProxyAuthenticationState.TransportContext = transportContext; _SubmitWriteStream = submitStream; // Async RTC requests only. Skip if ApplySetting has not been called so we can test // without IOControl support/workarounds. if (RtcState != null && RtcState.inputData != null && !RtcState.IsAborted) { RtcState.outputData = new byte[sizeof(RtcState.ControlChannelTriggerStatus)]; RtcState.result = _SubmitWriteStream.SetRtcOption(RtcState.inputData, RtcState.outputData); if (!RtcState.IsEnabled()) { // Abort request if we weren't able to enable RTC. Abort(null, AbortState.Public); } RtcState.connectComplete.Set(); } // // This line is needed ONLY if we got a connect failure (Abort can still happen at random time) // CallDone will check for the write side response processing and this is what we want. // Note that [....] case already has a separate path to check for the response // if (Async && _CoreResponse != null && (object)_CoreResponse != (object)DBNull.Value) { GlobalLog.Assert(_CoreResponse is Exception, "SetRequestSubmitDone()|Found offensive response right after getting connection ({0}).", _CoreResponse); submitStream.CallDone(); GlobalLog.Leave("HttpWebRequest#" + ValidationHelper.HashString(this) + "::SetRequestSubmitDone() - already have a core response", _CoreResponse.GetType().FullName); return; } EndSubmitRequest(); GlobalLog.Leave("HttpWebRequest#" + ValidationHelper.HashString(this) + "::SetRequestSubmitDone"); }
/// <devdoc> /// <para>Retreives the Request Stream after an Async operation has completed </para> /// </devdoc> public Stream EndGetRequestStream(IAsyncResult asyncResult, out TransportContext context) { #if DEBUG using (GlobalLog.SetThreadKind(ThreadKinds.User)) { #endif GlobalLog.Enter("HttpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetRequestStream", ValidationHelper.HashString(asyncResult)); if(Logging.On)Logging.Enter(Logging.Web, this, "EndGetRequestStream", ""); context = null; // // parameter validation // if (asyncResult == null) { throw new ArgumentNullException("asyncResult"); } LazyAsyncResult castedAsyncResult = asyncResult as LazyAsyncResult; if (castedAsyncResult==null || castedAsyncResult.AsyncObject!=this) { throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); } if (castedAsyncResult.EndCalled) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetRequestStream")); } ConnectStream connectStream = castedAsyncResult.InternalWaitForCompletion() as ConnectStream; castedAsyncResult.EndCalled = true; if (connectStream == null) { if (Logging.On) Logging.Exception(Logging.Web, this, "EndGetRequestStream", castedAsyncResult.Result as Exception); throw (Exception) castedAsyncResult.Result; } context = new ConnectStreamContext(connectStream); // Otherwise it worked, so return the HttpWebResponse. GlobalLog.Leave("HttpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetRequestStream", ValidationHelper.HashString(connectStream)); if(Logging.On)Logging.Exit(Logging.Web, this, "EndGetRequestStream", connectStream); FrameworkEventSource.Log.EndGetRequestStream(this); return connectStream; #if DEBUG } #endif }