private unsafe void UpdateDataChunk() { if (_dataChunks == null) { _dataChunks = new Interop.HttpApi.HTTP_DATA_CHUNK[2]; _dataChunksGCHandle = GCHandle.Alloc(_dataChunks, GCHandleType.Pinned); _dataChunks[0] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[0].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[1] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[1].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; } Debug.Assert(_buffer == null || _bufferList == null, "Either 'm_Buffer' or 'm_BufferList' MUST be NULL."); Debug.Assert(_shouldCloseOutput || _buffer != null || _bufferList != null, "Either 'm_Buffer' or 'm_BufferList' MUST NOT be NULL."); // The underlying byte[] m_Buffer or each m_BufferList[].Array are pinned already if (_buffer != null) { UpdateDataChunk(0, _buffer, _offset, _count); UpdateDataChunk(1, null, 0, 0); _dataChunkCount = 1; } else if (_bufferList != null) { Debug.Assert(_bufferList != null && _bufferList.Count == 2, "'m_BufferList' MUST NOT be NULL and have exactly '2' items at this point."); UpdateDataChunk(0, _bufferList[0].Array, _bufferList[0].Offset, _bufferList[0].Count); UpdateDataChunk(1, _bufferList[1].Array, _bufferList[1].Offset, _bufferList[1].Count); _dataChunkCount = 2; } else { Debug.Assert(_shouldCloseOutput, "'m_ShouldCloseOutput' MUST be 'true' at this point."); _dataChunks = null; } }
private void WriteCore(byte[] buffer, int offset, int size) { Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (size == 0 && _leftToWrite != 0) { return; } if (_leftToWrite >= 0 && size > _leftToWrite) { throw new ProtocolViolationException(SR.net_entitytoobig); } uint statusCode; uint dataToWrite = (uint)size; SafeLocalAllocHandle?bufferAsIntPtr = null; IntPtr pBufferAsIntPtr = IntPtr.Zero; bool sentHeaders = _httpContext.Response.SentHeaders; try { if (size == 0) { statusCode = _httpContext.Response.SendHeaders(null, null, flags, false); } else { fixed(byte *pDataBuffer = buffer) { byte *pBuffer = pDataBuffer; if (_httpContext.Response.BoundaryType == BoundaryType.Chunked) { string chunkHeader = size.ToString("x", CultureInfo.InvariantCulture); dataToWrite = dataToWrite + (uint)(chunkHeader.Length + 4); bufferAsIntPtr = SafeLocalAllocHandle.LocalAlloc((int)dataToWrite); pBufferAsIntPtr = bufferAsIntPtr.DangerousGetHandle(); for (int i = 0; i < chunkHeader.Length; i++) { Marshal.WriteByte(pBufferAsIntPtr, i, (byte)chunkHeader[i]); } Marshal.WriteInt16(pBufferAsIntPtr, chunkHeader.Length, 0x0A0D); Marshal.Copy(buffer, offset, pBufferAsIntPtr + chunkHeader.Length + 2, size); Marshal.WriteInt16(pBufferAsIntPtr, (int)(dataToWrite - 2), 0x0A0D); pBuffer = (byte *)pBufferAsIntPtr; offset = 0; } Interop.HttpApi.HTTP_DATA_CHUNK dataChunk = default; dataChunk.DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; dataChunk.pBuffer = (byte *)(pBuffer + offset); dataChunk.BufferLength = dataToWrite; flags |= _leftToWrite == size ? Interop.HttpApi.HTTP_FLAGS.NONE : Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; if (!sentHeaders) { statusCode = _httpContext.Response.SendHeaders(&dataChunk, null, flags, false); } else { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody"); } statusCode = Interop.HttpApi.HttpSendResponseEntityBody( _httpContext.RequestQueueHandle, _httpContext.RequestId, (uint)flags, 1, &dataChunk, null, SafeLocalAllocHandle.Zero, 0, null, null); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); } if (_httpContext.Listener !.IgnoreWriteExceptions) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "Write() suppressing error"); } statusCode = Interop.HttpApi.ERROR_SUCCESS; } } } } } finally { // free unmanaged buffer bufferAsIntPtr?.Close(); } if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF) { Exception exception = new HttpListenerException((int)statusCode); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(this, exception.ToString()); } _closed = true; _httpContext.Abort(); throw exception; } UpdateAfterWrite(dataToWrite); if (NetEventSource.Log.IsEnabled()) { NetEventSource.DumpBuffer(this, buffer, offset, (int)dataToWrite); } }
protected override void Dispose(bool disposing) { if (NetEventSource.IsEnabled) NetEventSource.Enter(this); try { if (disposing) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, "_closed:" + _closed); if (_closed) { if (NetEventSource.IsEnabled) NetEventSource.Exit(this); return; } _closed = true; Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (_leftToWrite > 0 && !_inOpaqueMode) { throw new InvalidOperationException(SR.net_io_notenoughbyteswritten); } bool sentHeaders = _httpContext.Response.SentHeaders; if (sentHeaders && _leftToWrite == 0) { if (NetEventSource.IsEnabled) NetEventSource.Exit(this); return; } uint statusCode = 0; if ((_httpContext.Response.BoundaryType == BoundaryType.Chunked || _httpContext.Response.BoundaryType == BoundaryType.None) && (String.Compare(_httpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) != 0)) { if (_httpContext.Response.BoundaryType == BoundaryType.None) { flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } fixed (void* pBuffer = ChunkTerminator) { Interop.HttpApi.HTTP_DATA_CHUNK* pDataChunk = null; if (_httpContext.Response.BoundaryType == BoundaryType.Chunked) { Interop.HttpApi.HTTP_DATA_CHUNK dataChunk = new Interop.HttpApi.HTTP_DATA_CHUNK(); dataChunk.DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; dataChunk.pBuffer = (byte*)pBuffer; dataChunk.BufferLength = (uint)ChunkTerminator.Length; pDataChunk = &dataChunk; } if (!sentHeaders) { statusCode = _httpContext.Response.SendHeaders(pDataChunk, null, flags, false); } else { if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody"); statusCode = Interop.HttpApi.HttpSendResponseEntityBody( _httpContext.RequestQueueHandle, _httpContext.RequestId, (uint)flags, pDataChunk != null ? (ushort)1 : (ushort)0, pDataChunk, null, SafeLocalAllocHandle.Zero, 0, null, null); if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); if (_httpContext.Listener.IgnoreWriteExceptions) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Suppressing error"); statusCode = Interop.HttpApi.ERROR_SUCCESS; } } } } else { if (!sentHeaders) { statusCode = _httpContext.Response.SendHeaders(null, null, flags, false); } } if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF) { Exception exception = new HttpListenerException((int)statusCode); if (NetEventSource.IsEnabled) NetEventSource.Error(this, exception.ToString()); _httpContext.Abort(); throw exception; } _leftToWrite = 0; } } finally { base.Dispose(disposing); } if (NetEventSource.IsEnabled) NetEventSource.Exit(this); }
public override void Write(byte[] buffer, int offset, int size) { if (NetEventSource.IsEnabled) { NetEventSource.Enter(this); NetEventSource.Info(this, "buffer.Length:" + buffer.Length + " size:" + size + " offset:" + offset); } if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (offset < 0 || offset > buffer.Length) { throw new ArgumentOutOfRangeException(nameof(offset)); } if (size < 0 || size > buffer.Length - offset) { throw new ArgumentOutOfRangeException(nameof(size)); } Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (_closed || (size == 0 && _leftToWrite != 0)) { if (NetEventSource.IsEnabled) NetEventSource.Exit(this); return; } if (_leftToWrite >= 0 && size > _leftToWrite) { throw new ProtocolViolationException(SR.net_entitytoobig); } uint statusCode; uint dataToWrite = (uint)size; SafeLocalAllocHandle bufferAsIntPtr = null; IntPtr pBufferAsIntPtr = IntPtr.Zero; bool sentHeaders = _httpContext.Response.SentHeaders; try { if (size == 0) { statusCode = _httpContext.Response.SendHeaders(null, null, flags, false); } else { fixed (byte* pDataBuffer = buffer) { byte* pBuffer = pDataBuffer; if (_httpContext.Response.BoundaryType == BoundaryType.Chunked) { string chunkHeader = size.ToString("x", CultureInfo.InvariantCulture); dataToWrite = dataToWrite + (uint)(chunkHeader.Length + 4); bufferAsIntPtr = SafeLocalAllocHandle.LocalAlloc((int)dataToWrite); pBufferAsIntPtr = bufferAsIntPtr.DangerousGetHandle(); for (int i = 0; i < chunkHeader.Length; i++) { Marshal.WriteByte(pBufferAsIntPtr, i, (byte)chunkHeader[i]); } Marshal.WriteInt16(pBufferAsIntPtr, chunkHeader.Length, 0x0A0D); Marshal.Copy(buffer, offset, pBufferAsIntPtr + chunkHeader.Length + 2, size); Marshal.WriteInt16(pBufferAsIntPtr, (int)(dataToWrite - 2), 0x0A0D); pBuffer = (byte*)pBufferAsIntPtr; offset = 0; } Interop.HttpApi.HTTP_DATA_CHUNK dataChunk = new Interop.HttpApi.HTTP_DATA_CHUNK(); dataChunk.DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; dataChunk.pBuffer = (byte*)(pBuffer + offset); dataChunk.BufferLength = dataToWrite; flags |= _leftToWrite == size ? Interop.HttpApi.HTTP_FLAGS.NONE : Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; if (!sentHeaders) { statusCode = _httpContext.Response.SendHeaders(&dataChunk, null, flags, false); } else { if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody"); statusCode = Interop.HttpApi.HttpSendResponseEntityBody( _httpContext.RequestQueueHandle, _httpContext.RequestId, (uint)flags, 1, &dataChunk, null, SafeLocalAllocHandle.Zero, 0, null, null); if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); if (_httpContext.Listener.IgnoreWriteExceptions) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, "Write() suppressing error"); statusCode = Interop.HttpApi.ERROR_SUCCESS; } } } } } finally { // free unmanaged buffer bufferAsIntPtr?.Close(); } if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF) { Exception exception = new HttpListenerException((int)statusCode); if (NetEventSource.IsEnabled) NetEventSource.Error(this, exception.ToString()); _closed = true; _httpContext.Abort(); throw exception; } UpdateAfterWrite(dataToWrite); if (NetEventSource.IsEnabled) NetEventSource.DumpBuffer(this, buffer, offset, (int)dataToWrite); if (NetEventSource.IsEnabled) NetEventSource.Exit(this); }
private void DisposeCore() { Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (_leftToWrite > 0 && !_inOpaqueMode) { throw new InvalidOperationException(SR.net_io_notenoughbyteswritten); } bool sentHeaders = _httpContext.Response.SentHeaders; if (sentHeaders && _leftToWrite == 0) { return; } uint statusCode = 0; if ((_httpContext.Response.BoundaryType == BoundaryType.Chunked || _httpContext.Response.BoundaryType == BoundaryType.None) && !string.Equals(_httpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase)) { if (_httpContext.Response.BoundaryType == BoundaryType.None) { flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } fixed(void *pBuffer = &s_chunkTerminator[0]) { Interop.HttpApi.HTTP_DATA_CHUNK *pDataChunk = null; if (_httpContext.Response.BoundaryType == BoundaryType.Chunked) { Interop.HttpApi.HTTP_DATA_CHUNK dataChunk = default; dataChunk.DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; dataChunk.pBuffer = (byte *)pBuffer; dataChunk.BufferLength = (uint)s_chunkTerminator.Length; pDataChunk = &dataChunk; } if (!sentHeaders) { statusCode = _httpContext.Response.SendHeaders(pDataChunk, null, flags, false); } else { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody"); } statusCode = Interop.HttpApi.HttpSendResponseEntityBody( _httpContext.RequestQueueHandle, _httpContext.RequestId, (uint)flags, pDataChunk != null ? (ushort)1 : (ushort)0, pDataChunk, null, SafeLocalAllocHandle.Zero, 0, null, null); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); } if (_httpContext.Listener.IgnoreWriteExceptions) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "Suppressing error"); } statusCode = Interop.HttpApi.ERROR_SUCCESS; } } } } else { if (!sentHeaders) { statusCode = _httpContext.Response.SendHeaders(null, null, flags, false); } } if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF) { Exception exception = new HttpListenerException((int)statusCode); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(this, exception.ToString()); } _httpContext.Abort(); throw exception; } _leftToWrite = 0; }
internal HttpResponseStreamAsyncResult(object asyncObject, object userState, AsyncCallback callback, byte[] buffer, int offset, int size, bool chunked, bool sentHeaders, ThreadPoolBoundHandle boundHandle) : base(asyncObject, userState, callback) { _boundHandle = boundHandle; _sentHeaders = sentHeaders; if (size == 0) { _dataChunks = null; _pOverlapped = boundHandle.AllocateNativeOverlapped(s_IOCallback, state: this, pinData: null); } else { _dataChunks = new Interop.HttpApi.HTTP_DATA_CHUNK[chunked ? 3 : 1]; if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "m_pOverlapped:0x" + ((IntPtr)_pOverlapped).ToString("x8")); } object[] objectsToPin = new object[1 + _dataChunks.Length]; objectsToPin[_dataChunks.Length] = _dataChunks; int chunkHeaderOffset = 0; byte[] chunkHeaderBuffer = null; if (chunked) { chunkHeaderBuffer = GetChunkHeader(size, out chunkHeaderOffset); _dataChunks[0] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[0].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[0].BufferLength = (uint)(chunkHeaderBuffer.Length - chunkHeaderOffset); objectsToPin[0] = chunkHeaderBuffer; _dataChunks[1] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[1].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[1].BufferLength = (uint)size; objectsToPin[1] = buffer; _dataChunks[2] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[2].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[2].BufferLength = (uint)s_CRLFArray.Length; objectsToPin[2] = s_CRLFArray; } else { _dataChunks[0] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[0].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[0].BufferLength = (uint)size; objectsToPin[0] = buffer; } // This call will pin needed memory _pOverlapped = boundHandle.AllocateNativeOverlapped(s_IOCallback, state: this, pinData: objectsToPin); if (chunked) { _dataChunks[0].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(chunkHeaderBuffer, chunkHeaderOffset)); _dataChunks[1].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); _dataChunks[2].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(s_CRLFArray, 0)); } else { _dataChunks[0].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); } } }
internal HttpResponseStreamAsyncResult(object asyncObject, object userState, AsyncCallback callback, byte[] buffer, int offset, int size, bool chunked, bool sentHeaders, ThreadPoolBoundHandle boundHandle) : base(asyncObject, userState, callback) { _boundHandle = boundHandle; _sentHeaders = sentHeaders; if (size == 0) { _dataChunks = null; _pOverlapped = boundHandle.AllocateNativeOverlapped(s_IOCallback, state: this, pinData: null); } else { _dataChunks = new Interop.HttpApi.HTTP_DATA_CHUNK[chunked ? 3 : 1]; if (NetEventSource.IsEnabled) NetEventSource.Info(this, "m_pOverlapped:0x" + ((IntPtr)_pOverlapped).ToString("x8")); object[] objectsToPin = new object[1 + _dataChunks.Length]; objectsToPin[_dataChunks.Length] = _dataChunks; int chunkHeaderOffset = 0; byte[] chunkHeaderBuffer = null; if (chunked) { chunkHeaderBuffer = GetChunkHeader(size, out chunkHeaderOffset); _dataChunks[0] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[0].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[0].BufferLength = (uint)(chunkHeaderBuffer.Length - chunkHeaderOffset); objectsToPin[0] = chunkHeaderBuffer; _dataChunks[1] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[1].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[1].BufferLength = (uint)size; objectsToPin[1] = buffer; _dataChunks[2] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[2].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[2].BufferLength = (uint)s_CRLFArray.Length; objectsToPin[2] = s_CRLFArray; } else { _dataChunks[0] = new Interop.HttpApi.HTTP_DATA_CHUNK(); _dataChunks[0].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[0].BufferLength = (uint)size; objectsToPin[0] = buffer; } // This call will pin needed memory _pOverlapped = boundHandle.AllocateNativeOverlapped(s_IOCallback, state: this, pinData: objectsToPin); if (chunked) { _dataChunks[0].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(chunkHeaderBuffer, chunkHeaderOffset)); _dataChunks[1].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); _dataChunks[2].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(s_CRLFArray, 0)); } else { _dataChunks[0].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); } } }