BeginGetRequest( AsyncCallback requestCallback, Object stateObject) { // // Validation // if (m_AppPoolHandle == NativeMethods.INVALID_HANDLE_VALUE) { throw new InvalidOperationException("The AppPool handle is invalid"); } // // prepare the ListenerAsyncResult object ( this will have it's own // event that the user can wait on for IO completion - which means we // need to signal it when IO completes ) // GlobalLog.Print("BeginGetRequest() creating ListenerAsyncResult"); ListenerAsyncResult AResult = new ListenerAsyncResult( stateObject, requestCallback); AutoResetEvent m_Event = new AutoResetEvent(false); Marshal.WriteIntPtr( AResult.m_Overlapped, Win32.OverlappedhEventOffset, m_Event.Handle); // // issue unmanaged call until we read enough data: // usually starting with a InitialBufferSize==4096 bytes we should be // able to get all the headers ( and part of the entity body, if any // is present ), if we don't, if the call didn't fail for othe reasons, // we get indication in BytesReturned, on how big the buffer should be // to receive the data available, so usually the second call will // succeed, but we have to consider the case of two competing calls // for the same RequestId, and that's why we need a loop and not just // a try/retry-expecting-success fashion // int result; for (;;) { // // check if we're in a healthy state // if (AResult.m_Retries++ > m_MaxRetries) { throw new InvalidOperationException("UlReceiveHttpRequest() Too many retries"); } result = ComNetOS.IsWinNt ? UlSysApi.UlReceiveHttpRequest( m_AppPoolHandle, AResult.m_RequestId, UlConstants.UL_RECEIVE_REQUEST_FLAG_COPY_BODY, AResult.m_Buffer, AResult.m_BufferSize, ref AResult.m_BytesReturned, AResult.m_Overlapped) : UlVxdApi.UlReceiveHttpRequestHeaders( m_AppPoolHandle, AResult.m_RequestId, 0, AResult.m_Buffer, AResult.m_BufferSize, ref AResult.m_BytesReturned, AResult.m_Overlapped); GlobalLog.Print("UlReceiveHttpRequest() returns:" + Convert.ToString(result)); if (result == NativeMethods.ERROR_SUCCESS || result == NativeMethods.ERROR_IO_PENDING) { // // synchronous success or successfull pending: we are done // break; } if (result == NativeMethods.ERROR_INVALID_PARAMETER) { // // we might get this if somebody stole our RequestId, // set RequestId to null // AResult.m_RequestId = 0; // // and start all over again with the buffer we // just allocated // continue; } if (result == NativeMethods.ERROR_MORE_DATA) { // // the buffer was not big enough to fit the headers, we need // to read the RequestId returned, grow the buffer, keeping // the data already transferred // AResult.m_RequestId = Marshal.ReadInt64(IntPtrHelper.Add(AResult.m_Buffer, m_RequestIdOffset)); // // allocate a new buffer of the required size // IntPtr NewBuffer = Marshal.AllocHGlobal(AResult.m_BytesReturned); // // copy the data already read from the old buffer into the // new one // NativeMethods.CopyMemory(NewBuffer, AResult.m_Buffer, AResult.m_BufferSize); // // free the old buffer // Marshal.FreeHGlobal(AResult.m_Buffer); // // update buffer pointer and size // AResult.m_Buffer = NewBuffer; AResult.m_BufferSize = AResult.m_BytesReturned; AResult.m_BytesReturned = 0; // // and start all over again with the new buffer // continue; } // // someother bad error, possible( ? ) return values are: // // ERROR_INVALID_HANDLE // ERROR_INSUFFICIENT_BUFFER // ERROR_OPERATION_ABORTED // ERROR_IO_PENDING // throw new InvalidOperationException("UlReceiveHttpRequest() failed, err#" + Convert.ToString(result)); } // // we get here only if a break happens, i.e. // 1) syncronous completion // 2) the IO pended // if (result == NativeMethods.ERROR_SUCCESS) { // // set syncronous completion to true // AResult.Complete(true); // // and call the internal callback // WaitCallback(AResult, false); } else { // // create a new delegate // and spin a new thread from the thread pool to wake up when the // event is signaled and call the delegate // ThreadPool.RegisterWaitForSingleObject( m_Event, new WaitOrTimerCallback(WaitCallback), AResult, -1, true); } GlobalLog.Print("returning AResult"); return(AResult); } // StartListen()
GetRequest() { // // Validation // if (m_AppPoolHandle == NativeMethods.INVALID_HANDLE_VALUE) { throw new InvalidOperationException("The AppPool handle is invalid"); } int result; int retries = 0; // // defined in ulcommonapi.cs // int RcvHeadersBufferSize = UlConstants.InitialBufferSize; // // prepare ( allocate/pin ) buffers and data for the first unmanaged call // GCHandle PinnedBuffer; GCHandle NewPinnedBuffer; IntPtr AddrOfPinnedBuffer = IntPtr.Zero; IntPtr NewAddrOfPinnedBuffer = IntPtr.Zero; int BytesReturned = 0; long RequestId = 0; byte[] RcvHeadersBuffer = new byte[RcvHeadersBufferSize]; // // pin buffers and data for the unmanaged call // PinnedBuffer = GCHandle.Alloc(RcvHeadersBuffer, GCHandleType.Pinned); AddrOfPinnedBuffer = PinnedBuffer.AddrOfPinnedObject(); // // issue unmanaged blocking call until we read enough data: // usually starting with a InitialBufferSize==4096 bytes we should be // able to get all the headers ( and part of the entity body, if any // is present ), if we don't, if the call didn't fail for othe reasons, // we get indication in BytesReturned, on how big the buffer should be // to receive the data available, so usually the second call will // succeed, but we have to consider the case of two competing calls // for the same RequestId, and that's why we need a loop and not just // a try/retry-expecting-success fashion // for (;;) { // // check if we're in a healthy state // if (retries++ > m_MaxRetries) { throw new InvalidOperationException("UlReceiveHttpRequest() Too many retries"); } result = ComNetOS.IsWinNt ? UlSysApi.UlReceiveHttpRequest( m_AppPoolHandle, RequestId, UlConstants.UL_RECEIVE_REQUEST_FLAG_COPY_BODY, AddrOfPinnedBuffer, RcvHeadersBufferSize, ref BytesReturned, IntPtr.Zero) : UlVxdApi.UlReceiveHttpRequestHeaders( m_AppPoolHandle, RequestId, 0, AddrOfPinnedBuffer, RcvHeadersBufferSize, ref BytesReturned, IntPtr.Zero); if (result == NativeMethods.ERROR_SUCCESS) { // // success, we are done. // break; } if (result == NativeMethods.ERROR_INVALID_PARAMETER) { // // we might get this if somebody stole our RequestId, // set RequestId to null // RequestId = 0; // // and start all over again with the buffer we // just allocated // continue; } // // let's check if ul is in good shape: // if (BytesReturned < RcvHeadersBufferSize) { throw new InvalidOperationException("UlReceiveHttpRequest() sent bogus BytesReturned: " + Convert.ToString(BytesReturned)); } if (result == NativeMethods.ERROR_MORE_DATA) { // // the buffer was not big enough to fit the headers, we need // to read the RequestId returned, grow the buffer, keeping // the data already transferred // RequestId = Marshal.ReadInt64(IntPtrHelper.Add(AddrOfPinnedBuffer, m_RequestIdOffset)); // // CODEWORK: wait for the answer from LarrySu // // // if the buffer size was too small, grow the buffer // this reallocation dereferences the old buffer, but since // this was previously pinned, it won't be garbage collected // until we unpin it ( which we do below ) // RcvHeadersBuffer = new byte[BytesReturned]; // // pin the new one // NewPinnedBuffer = GCHandle.Alloc(RcvHeadersBuffer, GCHandleType.Pinned); NewAddrOfPinnedBuffer = NewPinnedBuffer.AddrOfPinnedObject(); // // copy the valid data // Marshal.Copy(AddrOfPinnedBuffer, RcvHeadersBuffer, 0, RcvHeadersBufferSize); RcvHeadersBufferSize = BytesReturned; // // unpin the old buffer, reset pinned/unmanaged pointers // PinnedBuffer.Free(); PinnedBuffer = NewPinnedBuffer; AddrOfPinnedBuffer = NewAddrOfPinnedBuffer; // // and start all over again with the new buffer // continue; } // // someother bad error, possible( ? ) return values are: // // ERROR_INVALID_HANDLE // ERROR_INSUFFICIENT_BUFFER // ERROR_OPERATION_ABORTED // ERROR_IO_PENDING // throw new InvalidOperationException("UlReceiveHttpRequest() failed, err#" + Convert.ToString(result)); } GlobalLog.Print("GetRequest RequestId:" + Convert.ToString(RequestId)); // // translate unmanaged results into a new managed object // HttpListenerWebRequest myWebRequest = new HttpListenerWebRequest(AddrOfPinnedBuffer, RcvHeadersBufferSize, m_AppPoolHandle); // // free the unmanaged buffer ( deallocate/unpin ) after unmanaged call // PinnedBuffer.Free(); return(myWebRequest); } // GetRequest()