private UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS ComputeLeftToWrite(bool endOfRequest = false) { UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; if (!_requestContext.Response.ComputedHeaders) { flags = _requestContext.Response.ComputeHeaders(endOfRequest, _buffer.TotalBytes); } if (_leftToWrite == long.MinValue) { UnsafeNclNativeMethods.HttpApi.HTTP_VERB method = _requestContext.Request.GetKnownMethod(); if (method == UnsafeNclNativeMethods.HttpApi.HTTP_VERB.HttpVerbHEAD) { _leftToWrite = 0; } else if (_requestContext.Response.BoundaryType == BoundaryType.ContentLength) { _leftToWrite = _requestContext.Response.ExpectedBodyLength; } else { _leftToWrite = -1; // unlimited } } return(flags); }
internal UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS ComputeLeftToWrite() { UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS nONE = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; if (!this.m_HttpContext.Response.ComputedHeaders) { nONE = this.m_HttpContext.Response.ComputeHeaders(); } if (this.m_LeftToWrite == -9223372036854775808L) { UnsafeNclNativeMethods.HttpApi.HTTP_VERB knownMethod = this.m_HttpContext.GetKnownMethod(); this.m_LeftToWrite = (knownMethod != UnsafeNclNativeMethods.HttpApi.HTTP_VERB.HttpVerbHEAD) ? this.m_HttpContext.Response.ContentLength64 : 0L; } return(nONE); }
internal UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS ComputeLeftToWrite() { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::ComputeLeftToWrite() on entry m_LeftToWrite:" + m_LeftToWrite); UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; if (!m_HttpContext.Response.ComputedHeaders) { flags = m_HttpContext.Response.ComputeHeaders(); } if (m_LeftToWrite == long.MinValue) { UnsafeNclNativeMethods.HttpApi.HTTP_VERB method = m_HttpContext.GetKnownMethod(); m_LeftToWrite = method != UnsafeNclNativeMethods.HttpApi.HTTP_VERB.HttpVerbHEAD ? m_HttpContext.Response.ContentLength64 : 0; GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::ComputeLeftToWrite() computed m_LeftToWrite:" + m_LeftToWrite); } return(flags); }
protected override void Dispose(bool disposing) { if (Logging.On) { Logging.Enter(Logging.HttpListener, this, "Close", ""); } try { if (disposing) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() m_Closed:" + m_Closed); if (m_Closed) { if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Close", ""); } return; } m_Closed = true; UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (m_LeftToWrite > 0 && !m_InOpaqueMode) { throw new InvalidOperationException(SR.GetString(SR.net_io_notenoughbyteswritten)); } bool sentHeaders = m_HttpContext.Response.SentHeaders; if (sentHeaders && m_LeftToWrite == 0) { if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Close", ""); } return; } uint statusCode = 0; if ((m_HttpContext.Response.BoundaryType == BoundaryType.Chunked || m_HttpContext.Response.BoundaryType == BoundaryType.None) && (String.Compare(m_HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) != 0)) { if (m_HttpContext.Response.BoundaryType == BoundaryType.None) { flags |= UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } fixed(void *pBuffer = NclConstants.ChunkTerminator) { UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK *pDataChunk = null; if (m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) { UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK dataChunk = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); dataChunk.DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; dataChunk.pBuffer = (byte *)pBuffer; dataChunk.BufferLength = (uint)NclConstants.ChunkTerminator.Length; pDataChunk = &dataChunk; } if (!sentHeaders) { statusCode = m_HttpContext.Response.SendHeaders(pDataChunk, null, flags, false); } else { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() calling UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody"); statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody( m_HttpContext.RequestQueueHandle, m_HttpContext.RequestId, (uint)flags, pDataChunk != null ? (ushort)1 : (ushort)0, pDataChunk, null, SafeLocalFree.Zero, 0, null, null); GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() call to UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); if (m_HttpContext.Listener.IgnoreWriteExceptions) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() suppressing error"); statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; } } } } else { if (!sentHeaders) { statusCode = m_HttpContext.Response.SendHeaders(null, null, flags, false); } } if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { Exception exception = new HttpListenerException((int)statusCode); if (Logging.On) { Logging.Exception(Logging.HttpListener, this, "Close", exception); } m_HttpContext.Abort(); throw exception; } m_LeftToWrite = 0; } } finally { base.Dispose(disposing); } if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Dispose", ""); } }
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() buffer.Length:" + buffer.Length + " size:" + size + " offset:" + offset); if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0 || offset > buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } if (size < 0 || size > buffer.Length - offset) { throw new ArgumentOutOfRangeException("size"); } UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (m_Closed || (size == 0 && m_LeftToWrite != 0)) { if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "BeginWrite", ""); } HttpResponseStreamAsyncResult result = new HttpResponseStreamAsyncResult(this, state, callback); result.InvokeCallback((uint)0); return(result); } if (m_LeftToWrite >= 0 && size > m_LeftToWrite) { throw new ProtocolViolationException(SR.GetString(SR.net_entitytoobig)); } uint statusCode; uint bytesSent = 0; flags |= m_LeftToWrite == size ? UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE : UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; bool sentHeaders = m_HttpContext.Response.SentHeaders; HttpResponseStreamAsyncResult asyncResult = new HttpResponseStreamAsyncResult(this, state, callback, buffer, offset, size, m_HttpContext.Response.BoundaryType == BoundaryType.Chunked, sentHeaders); // Update m_LeftToWrite now so we can queue up additional BeginWrite's without waiting for EndWrite. UpdateAfterWrite((uint)((m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) ? 0 : size)); try { if (!sentHeaders) { statusCode = m_HttpContext.Response.SendHeaders(null, asyncResult, flags, false); } else { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() calling UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody"); m_HttpContext.EnsureBoundHandle(); statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody( m_HttpContext.RequestQueueHandle, m_HttpContext.RequestId, (uint)flags, asyncResult.dataChunkCount, asyncResult.pDataChunks, &bytesSent, SafeLocalFree.Zero, 0, asyncResult.m_pOverlapped, null); GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() call to UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); } } catch (Exception e) { if (Logging.On) { Logging.Exception(Logging.HttpListener, this, "BeginWrite", e); } asyncResult.InternalCleanup(); m_Closed = true; m_HttpContext.Abort(); throw; } if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { asyncResult.InternalCleanup(); if (m_HttpContext.Listener.IgnoreWriteExceptions && sentHeaders) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() suppressing error"); } else { Exception exception = new HttpListenerException((int)statusCode); if (Logging.On) { Logging.Exception(Logging.HttpListener, this, "BeginWrite", exception); } m_Closed = true; m_HttpContext.Abort(); throw exception; } } if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) { // IO operation completed synchronously - callback won't be called to signal completion. asyncResult.IOCompleted(statusCode, bytesSent); } // Last write, cache it for special cancelation handling. if ((flags & UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { m_LastWrite = asyncResult; } if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "BeginWrite", ""); } return(asyncResult); }
public override void Write(byte[] buffer, int offset, int size) { if (Logging.On) { Logging.Enter(Logging.HttpListener, this, "Write", ""); } GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() buffer.Length:" + buffer.Length + " size:" + size + " offset:" + offset); if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0 || offset > buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } if (size < 0 || size > buffer.Length - offset) { throw new ArgumentOutOfRangeException("size"); } UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (m_Closed || (size == 0 && m_LeftToWrite != 0)) { if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Write", ""); } return; } if (m_LeftToWrite >= 0 && size > m_LeftToWrite) { throw new ProtocolViolationException(SR.GetString(SR.net_entitytoobig)); } uint statusCode; uint dataToWrite = (uint)size; SafeLocalFree bufferAsIntPtr = null; IntPtr pBufferAsIntPtr = IntPtr.Zero; bool sentHeaders = m_HttpContext.Response.SentHeaders; try { if (size == 0) { statusCode = m_HttpContext.Response.SendHeaders(null, null, flags, false); } else { fixed(byte *pDataBuffer = buffer) { byte *pBuffer = pDataBuffer; if (m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) { // string chunkHeader = size.ToString("x", CultureInfo.InvariantCulture); dataToWrite = dataToWrite + (uint)(chunkHeader.Length + 4); bufferAsIntPtr = SafeLocalFree.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, IntPtrHelper.Add(pBufferAsIntPtr, chunkHeader.Length + 2), size); Marshal.WriteInt16(pBufferAsIntPtr, (int)(dataToWrite - 2), 0x0A0D); pBuffer = (byte *)pBufferAsIntPtr; offset = 0; } UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK dataChunk = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); dataChunk.DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; dataChunk.pBuffer = (byte *)(pBuffer + offset); dataChunk.BufferLength = dataToWrite; flags |= m_LeftToWrite == size ? UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE : UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; if (!sentHeaders) { statusCode = m_HttpContext.Response.SendHeaders(&dataChunk, null, flags, false); } else { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() calling UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody"); statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody( m_HttpContext.RequestQueueHandle, m_HttpContext.RequestId, (uint)flags, 1, &dataChunk, null, SafeLocalFree.Zero, 0, null, null); GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() call to UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); if (m_HttpContext.Listener.IgnoreWriteExceptions) { GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() suppressing error"); statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; } } } } } finally { if (bufferAsIntPtr != null) { // free unmanaged buffer bufferAsIntPtr.Close(); } } if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { Exception exception = new HttpListenerException((int)statusCode); if (Logging.On) { Logging.Exception(Logging.HttpListener, this, "Write", exception); } m_Closed = true; m_HttpContext.Abort(); throw exception; } UpdateAfterWrite(dataToWrite); if (Logging.On) { Logging.Dump(Logging.HttpListener, this, "Write", buffer, offset, (int)dataToWrite); } if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Write", ""); } }
internal UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS ComputeHeaders() { UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::ComputeHeaders()"); GlobalLog.Assert(!ComputedHeaders, "HttpListenerResponse#{0}::ComputeHeaders()|ComputedHeaders is true.", ValidationHelper.HashString(this)); m_ResponseState = ResponseState.ComputedHeaders; /* * // here we would check for BoundaryType.Raw, in this case we wouldn't need to do anything * if (m_BoundaryType==BoundaryType.Raw) { * return flags; * } */ ComputeCoreHeaders(); GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::ComputeHeaders() flags:" + flags + " m_BoundaryType:" + m_BoundaryType + " m_ContentLength:" + m_ContentLength + " m_KeepAlive:" + m_KeepAlive); if (m_BoundaryType == BoundaryType.None) { if (HttpListenerRequest.ProtocolVersion.Minor == 0) { // m_KeepAlive = false; } else { m_BoundaryType = BoundaryType.Chunked; } if (CanSendResponseBody(m_HttpContext.Response.StatusCode)) { m_ContentLength = -1; } else { ContentLength64 = 0; } } GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::ComputeHeaders() flags:" + flags + " m_BoundaryType:" + m_BoundaryType + " m_ContentLength:" + m_ContentLength + " m_KeepAlive:" + m_KeepAlive); if (m_BoundaryType == BoundaryType.ContentLength) { Headers.SetInternal(HttpResponseHeader.ContentLength, m_ContentLength.ToString("D", NumberFormatInfo.InvariantInfo)); if (m_ContentLength == 0) { flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; } } else if (m_BoundaryType == BoundaryType.Chunked) { Headers.SetInternal(HttpResponseHeader.TransferEncoding, HttpWebRequest.ChunkedHeader); } else if (m_BoundaryType == BoundaryType.None) { flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; // seems like HTTP_SEND_RESPONSE_FLAG_MORE_DATA but this hangs the app; } else { m_KeepAlive = false; } if (!m_KeepAlive) { Headers.Add(HttpResponseHeader.Connection, "close"); if (flags == UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE) { flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } } else { if (HttpListenerRequest.ProtocolVersion.Minor == 0) { Headers.SetInternal(HttpResponseHeader.KeepAlive, "true"); } } GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::ComputeHeaders() flags:" + flags + " m_BoundaryType:" + m_BoundaryType + " m_ContentLength:" + m_ContentLength + " m_KeepAlive:" + m_KeepAlive); return(flags); }
/* * 12.3 * HttpSendHttpResponse() and HttpSendResponseEntityBody() Flag Values. * The following flags can be used on calls to HttpSendHttpResponse() and HttpSendResponseEntityBody() API calls: * #define HTTP_SEND_RESPONSE_FLAG_DISCONNECT 0x00000001 #define HTTP_SEND_RESPONSE_FLAG_MORE_DATA 0x00000002 #define HTTP_SEND_RESPONSE_FLAG_RAW_HEADER 0x00000004 #define HTTP_SEND_RESPONSE_FLAG_VALID 0x00000007 * * HTTP_SEND_RESPONSE_FLAG_DISCONNECT: * specifies that the network connection should be disconnected immediately after * sending the response, overriding the HTTP protocol's persistent connection features. * HTTP_SEND_RESPONSE_FLAG_MORE_DATA: * specifies that additional entity body data will be sent by the caller. Thus, * the last call HttpSendResponseEntityBody for a RequestId, will have this flag reset. * HTTP_SEND_RESPONSE_RAW_HEADER: * specifies that a caller of HttpSendResponseEntityBody() is intentionally omitting * a call to HttpSendHttpResponse() in order to bypass normal header processing. The * actual HTTP header will be generated by the application and sent as entity body. * This flag should be passed on the first call to HttpSendResponseEntityBody, and * not after. Thus, flag is not applicable to HttpSendHttpResponse. */ internal unsafe uint SendHeaders(UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK *pDataChunk, HttpResponseStreamAsyncResult asyncResult, UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags, bool isWebSocketHandshake) { GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SendHeaders() pDataChunk:" + ValidationHelper.ToString((IntPtr)pDataChunk) + " asyncResult:" + ValidationHelper.ToString(asyncResult)); GlobalLog.Assert(!SentHeaders, "HttpListenerResponse#{0}::SendHeaders()|SentHeaders is true.", ValidationHelper.HashString(this)); if (StatusCode == (int)HttpStatusCode.Unauthorized) // User set 401 // Using the configured Auth schemes, populate the auth challenge headers. This is for scenarios where // Anonymous access is allowed for some resources, but the server later determines that authorization // is required for this request. { HttpListenerContext.SetAuthenticationHeaders(); } // Log headers if (Logging.On) { StringBuilder sb = new StringBuilder("HttpListenerResponse Headers:\n"); for (int i = 0; i < Headers.Count; i++) { sb.Append("\t"); sb.Append(Headers.GetKey(i)); sb.Append(" : "); sb.Append(Headers.Get(i)); sb.Append("\n"); } Logging.PrintInfo(Logging.HttpListener, this, ".ctor", sb.ToString()); } m_ResponseState = ResponseState.SentHeaders; /* * if (m_BoundaryType==BoundaryType.Raw) { * use HTTP_SEND_RESPONSE_FLAG_RAW_HEADER; * } */ uint statusCode; uint bytesSent; List <GCHandle> pinnedHeaders = SerializeHeaders(ref m_NativeResponse.Headers, isWebSocketHandshake); try { if (pDataChunk != null) { m_NativeResponse.EntityChunkCount = 1; m_NativeResponse.pEntityChunks = pDataChunk; } else if (asyncResult != null && asyncResult.pDataChunks != null) { m_NativeResponse.EntityChunkCount = asyncResult.dataChunkCount; m_NativeResponse.pEntityChunks = asyncResult.pDataChunks; } else { m_NativeResponse.EntityChunkCount = 0; m_NativeResponse.pEntityChunks = null; } GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SendHeaders() calling UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse flags:" + flags); if (StatusDescription.Length > 0) { byte[] statusDescriptionBytes = new byte[WebHeaderCollection.HeaderEncoding.GetByteCount(StatusDescription)]; fixed(byte *pStatusDescription = statusDescriptionBytes) { m_NativeResponse.ReasonLength = (ushort)statusDescriptionBytes.Length; WebHeaderCollection.HeaderEncoding.GetBytes(StatusDescription, 0, statusDescriptionBytes.Length, statusDescriptionBytes, 0); m_NativeResponse.pReason = (sbyte *)pStatusDescription; fixed(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE *pResponse = &m_NativeResponse) { if (asyncResult != null) { HttpListenerContext.EnsureBoundHandle(); } statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse( HttpListenerContext.RequestQueueHandle, HttpListenerRequest.RequestId, (uint)flags, pResponse, null, &bytesSent, SafeLocalFree.Zero, 0, asyncResult == null ? null : asyncResult.m_pOverlapped, null); if (asyncResult != null && statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) { asyncResult.IOCompleted(statusCode, bytesSent); // IO operation completed synchronously - callback won't be called to signal completion. } } } } else { fixed(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE *pResponse = &m_NativeResponse) { if (asyncResult != null) { HttpListenerContext.EnsureBoundHandle(); } statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse( HttpListenerContext.RequestQueueHandle, HttpListenerRequest.RequestId, (uint)flags, pResponse, null, &bytesSent, SafeLocalFree.Zero, 0, asyncResult == null ? null : asyncResult.m_pOverlapped, null); if (asyncResult != null && statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) { asyncResult.IOCompleted(statusCode, bytesSent); // IO operation completed synchronously - callback won't be called to signal completion. } } } GlobalLog.Print("HttpListenerResponse#" + ValidationHelper.HashString(this) + "::SendHeaders() call to UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse returned:" + statusCode); } finally { FreePinnedHeaders(pinnedHeaders); } return(statusCode); }
// return value indicates [....] vs async completion // false: [....] completion // true: async completion private bool WriteAsyncFast(HttpListenerAsyncEventArgs eventArgs) { if (WebSocketBase.LoggingEnabled) { Logging.Enter(Logging.WebSockets, this, Methods.WriteAsyncFast, string.Empty); } UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; eventArgs.StartOperationCommon(this); eventArgs.StartOperationSend(); uint statusCode; bool completedAsynchronously = false; try { if (m_OutputStream.Closed || (eventArgs.Buffer != null && eventArgs.Count == 0)) { eventArgs.FinishOperationSuccess(eventArgs.Count, true); return(false); } if (eventArgs.ShouldCloseOutput) { flags |= UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } else { flags |= UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; // When using HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA HTTP.SYS will copy the payload to // kernel memory (Non-Paged Pool). Http.Sys will buffer up to // Math.Min(16 MB, current TCP window size) flags |= UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA; } m_OutputStream.InternalHttpContext.EnsureBoundHandle(); uint bytesSent; statusCode = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody2( m_OutputStream.InternalHttpContext.RequestQueueHandle, m_OutputStream.InternalHttpContext.RequestId, (uint)flags, eventArgs.EntityChunkCount, eventArgs.EntityChunks, out bytesSent, SafeLocalFree.Zero, 0, eventArgs.NativeOverlapped, IntPtr.Zero); if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { throw new HttpListenerException((int)statusCode); } else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) { // IO operation completed synchronously - callback won't be called to signal completion. eventArgs.FinishOperationSuccess((int)bytesSent, true); completedAsynchronously = false; } else { completedAsynchronously = true; } } catch (Exception e) { m_WriteEventArgs.FinishOperationFailure(e, true); m_OutputStream.SetClosedFlag(); m_OutputStream.InternalHttpContext.Abort(); throw; } finally { if (WebSocketBase.LoggingEnabled) { Logging.Exit(Logging.WebSockets, this, Methods.WriteAsyncFast, completedAsynchronously); } } return(completedAsynchronously); }
internal unsafe uint SendHeaders(UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK *pDataChunk, HttpResponseStreamAsyncResult asyncResult, UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags) { uint num2; if (Logging.On) { StringBuilder builder = new StringBuilder("HttpListenerResponse Headers:\n"); for (int i = 0; i < this.Headers.Count; i++) { builder.Append("\t"); builder.Append(this.Headers.GetKey(i)); builder.Append(" : "); builder.Append(this.Headers.Get(i)); builder.Append("\n"); } Logging.PrintInfo(Logging.HttpListener, this, ".ctor", builder.ToString()); } this.m_ResponseState = ResponseState.SentHeaders; List <GCHandle> pinnedHeaders = this.SerializeHeaders(ref this.m_NativeResponse.Headers); try { if (pDataChunk != null) { this.m_NativeResponse.EntityChunkCount = 1; this.m_NativeResponse.pEntityChunks = pDataChunk; } else if ((asyncResult != null) && (asyncResult.pDataChunks != null)) { this.m_NativeResponse.EntityChunkCount = asyncResult.dataChunkCount; this.m_NativeResponse.pEntityChunks = asyncResult.pDataChunks; } else { this.m_NativeResponse.EntityChunkCount = 0; this.m_NativeResponse.pEntityChunks = null; } if (this.StatusDescription.Length > 0) { ref byte pinned numRef; byte[] bytes = new byte[WebHeaderCollection.HeaderEncoding.GetByteCount(this.StatusDescription)]; try { byte[] buffer2; if (((buffer2 = bytes) == null) || (buffer2.Length == 0)) { numRef = null; } else { numRef = buffer2; } this.m_NativeResponse.ReasonLength = (ushort)bytes.Length; WebHeaderCollection.HeaderEncoding.GetBytes(this.StatusDescription, 0, bytes.Length, bytes, 0); this.m_NativeResponse.pReason = (sbyte *)numRef; fixed(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE *http_responseRef = &this.m_NativeResponse) { if (asyncResult != null) { this.HttpListenerContext.EnsureBoundHandle(); } num2 = UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse(this.HttpListenerContext.RequestQueueHandle, this.HttpListenerRequest.RequestId, (uint)flags, http_responseRef, null, null, SafeLocalFree.Zero, 0, (asyncResult == null) ? null : asyncResult.m_pOverlapped, null); } return(num2); } finally { numRef = null; } } fixed(UnsafeNclNativeMethods.HttpApi.HTTP_RESPONSE *http_responseRef2 = &this.m_NativeResponse) { if (asyncResult != null) { this.HttpListenerContext.EnsureBoundHandle(); } num2 = UnsafeNclNativeMethods.HttpApi.HttpSendHttpResponse(this.HttpListenerContext.RequestQueueHandle, this.HttpListenerRequest.RequestId, (uint)flags, http_responseRef2, null, null, SafeLocalFree.Zero, 0, (asyncResult == null) ? null : asyncResult.m_pOverlapped, null); } }
internal UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS ComputeHeaders() { UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS nONE = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; this.m_ResponseState = ResponseState.ComputedHeaders; if ((this.HttpListenerContext.MutualAuthentication != null) && (this.HttpListenerContext.MutualAuthentication.Length > 0)) { this.Headers.SetInternal(HttpResponseHeader.WwwAuthenticate, this.HttpListenerContext.MutualAuthentication); } this.ComputeCookies(); if (this.m_BoundaryType == System.Net.BoundaryType.None) { if (this.HttpListenerRequest.ProtocolVersion.Minor == 0) { this.m_KeepAlive = false; } else { this.m_BoundaryType = System.Net.BoundaryType.Chunked; } if (this.CanSendResponseBody(this.m_HttpContext.Response.StatusCode)) { this.m_ContentLength = -1L; } else { this.ContentLength64 = 0L; } } if (this.m_BoundaryType == System.Net.BoundaryType.ContentLength) { this.Headers.SetInternal(HttpResponseHeader.ContentLength, this.m_ContentLength.ToString("D", NumberFormatInfo.InvariantInfo)); if (this.m_ContentLength == 0L) { nONE = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; } } else if (this.m_BoundaryType == System.Net.BoundaryType.Chunked) { this.Headers.SetInternal(HttpResponseHeader.TransferEncoding, "chunked"); } else if (this.m_BoundaryType == System.Net.BoundaryType.None) { nONE = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; } else { this.m_KeepAlive = false; } if (!this.m_KeepAlive) { this.Headers.Add(HttpResponseHeader.Connection, "close"); if (nONE == UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE) { nONE = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_INITIALIZE_SERVER; } return(nONE); } if (this.HttpListenerRequest.ProtocolVersion.Minor == 0) { this.Headers.SetInternal(HttpResponseHeader.KeepAlive, "true"); } return(nONE); }
public override unsafe void Write(byte[] buffer, int offset, int size) { if (Logging.On) { Logging.Enter(Logging.HttpListener, this, "Write", ""); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if ((offset < 0) || (offset > buffer.Length)) { throw new ArgumentOutOfRangeException("offset"); } if ((size < 0) || (size > (buffer.Length - offset))) { throw new ArgumentOutOfRangeException("size"); } UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = this.ComputeLeftToWrite(); if (this.m_Closed || ((size == 0) && (this.m_LeftToWrite != 0L))) { if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Write", ""); } } else { uint num; if ((this.m_LeftToWrite >= 0L) && (size > this.m_LeftToWrite)) { throw new ProtocolViolationException(SR.GetString("net_entitytoobig")); } uint dataWritten = (uint)size; SafeLocalFree free = null; IntPtr zero = IntPtr.Zero; bool sentHeaders = this.m_HttpContext.Response.SentHeaders; try { if (size == 0) { num = this.m_HttpContext.Response.SendHeaders(null, null, flags); } else { try { byte[] buffer2; if (((buffer2 = buffer) == null) || (buffer2.Length == 0)) { numRef = null; goto Label_0109; } fixed(byte *numRef = buffer2) { byte *numPtr; Label_0109: numPtr = numRef; if (this.m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) { string str = size.ToString("x", CultureInfo.InvariantCulture); dataWritten += (uint)(str.Length + 4); free = SafeLocalFree.LocalAlloc((int)dataWritten); zero = free.DangerousGetHandle(); for (int i = 0; i < str.Length; i++) { Marshal.WriteByte(zero, i, (byte)str[i]); } Marshal.WriteInt16(zero, str.Length, (short)0xa0d); Marshal.Copy(buffer, offset, IntPtrHelper.Add(zero, str.Length + 2), size); Marshal.WriteInt16(zero, ((int)dataWritten) - 2, (short)0xa0d); numPtr = (byte *)zero; offset = 0; } UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK pDataChunk = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK { DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory, pBuffer = numPtr + offset, BufferLength = dataWritten }; flags |= (this.m_LeftToWrite == size) ? UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE : UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; if (!sentHeaders) { num = this.m_HttpContext.Response.SendHeaders(&pDataChunk, null, flags); } else { num = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody(this.m_HttpContext.RequestQueueHandle, this.m_HttpContext.RequestId, (uint)flags, 1, &pDataChunk, null, SafeLocalFree.Zero, 0, null, null); if (this.m_HttpContext.Listener.IgnoreWriteExceptions) { num = 0; } } } } finally { numRef = null; } } } finally { if (free != null) { free.Close(); } } switch (num) { case 0: case 0x26: this.UpdateAfterWrite(dataWritten); if (Logging.On) { Logging.Dump(Logging.HttpListener, this, "Write", buffer, offset, (int)dataWritten); } if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Write", ""); } return; } Exception e = new HttpListenerException((int)num); if (Logging.On) { Logging.Exception(Logging.HttpListener, this, "Write", e); } this.m_HttpContext.Abort(); throw e; } }
public override unsafe IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state) { uint num; if (buffer == null) { throw new ArgumentNullException("buffer"); } if ((offset < 0) || (offset > buffer.Length)) { throw new ArgumentOutOfRangeException("offset"); } if ((size < 0) || (size > (buffer.Length - offset))) { throw new ArgumentOutOfRangeException("size"); } UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = this.ComputeLeftToWrite(); if (this.m_Closed || ((size == 0) && (this.m_LeftToWrite != 0L))) { if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "BeginWrite", ""); } HttpResponseStreamAsyncResult result = new HttpResponseStreamAsyncResult(this, state, callback); result.InvokeCallback(0); return(result); } if ((this.m_LeftToWrite >= 0L) && (size > this.m_LeftToWrite)) { throw new ProtocolViolationException(SR.GetString("net_entitytoobig")); } flags |= (this.m_LeftToWrite == size) ? UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE : UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; bool sentHeaders = this.m_HttpContext.Response.SentHeaders; HttpResponseStreamAsyncResult asyncResult = new HttpResponseStreamAsyncResult(this, state, callback, buffer, offset, size, this.m_HttpContext.Response.BoundaryType == BoundaryType.Chunked, sentHeaders); try { if (!sentHeaders) { num = this.m_HttpContext.Response.SendHeaders(null, asyncResult, flags); } else { this.m_HttpContext.EnsureBoundHandle(); num = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody(this.m_HttpContext.RequestQueueHandle, this.m_HttpContext.RequestId, (uint)flags, asyncResult.dataChunkCount, asyncResult.pDataChunks, null, SafeLocalFree.Zero, 0, asyncResult.m_pOverlapped, null); } } catch (Exception exception) { if (Logging.On) { Logging.Exception(Logging.HttpListener, this, "BeginWrite", exception); } asyncResult.InternalCleanup(); this.m_HttpContext.Abort(); throw; } switch (num) { case 0: case 0x3e5: break; default: asyncResult.InternalCleanup(); if (!this.m_HttpContext.Listener.IgnoreWriteExceptions || !sentHeaders) { Exception e = new HttpListenerException((int)num); if (Logging.On) { Logging.Exception(Logging.HttpListener, this, "BeginWrite", e); } this.m_HttpContext.Abort(); throw e; } break; } if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "BeginWrite", ""); } return(asyncResult); }
protected override unsafe void Dispose(bool disposing) { if (Logging.On) { Logging.Enter(Logging.HttpListener, this, "Close", ""); } try { if (!disposing) { goto Label_023F; } if (this.m_Closed) { if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Close", ""); } return; } this.m_Closed = true; UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = this.ComputeLeftToWrite(); if (this.m_LeftToWrite > 0L) { throw new InvalidOperationException(SR.GetString("net_io_notenoughbyteswritten")); } bool sentHeaders = this.m_HttpContext.Response.SentHeaders; if (sentHeaders && (this.m_LeftToWrite == 0L)) { if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Close", ""); } return; } uint num = 0; if (((this.m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) || (this.m_HttpContext.Response.BoundaryType == BoundaryType.None)) && (string.Compare(this.m_HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) != 0)) { if (this.m_HttpContext.Response.BoundaryType == BoundaryType.None) { flags |= UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_INITIALIZE_SERVER; } try { byte[] buffer; if (((buffer = NclConstants.ChunkTerminator) == null) || (buffer.Length == 0)) { ptrRef = null; goto Label_0132; } fixed(IntPtr *ptrRef = buffer) { UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK *http_data_chunkPtr; Label_0132: http_data_chunkPtr = null; if (this.m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) { UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK http_data_chunk = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK { DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory, pBuffer = (byte *)ptrRef, BufferLength = (uint)NclConstants.ChunkTerminator.Length }; http_data_chunkPtr = &http_data_chunk; } if (!sentHeaders) { num = this.m_HttpContext.Response.SendHeaders(http_data_chunkPtr, null, flags); } else { num = UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody(this.m_HttpContext.RequestQueueHandle, this.m_HttpContext.RequestId, (uint)flags, (http_data_chunkPtr != null) ? ((ushort)1) : ((ushort)0), http_data_chunkPtr, null, SafeLocalFree.Zero, 0, null, null); if (this.m_HttpContext.Listener.IgnoreWriteExceptions) { num = 0; } } goto Label_01F6; } } finally { ptrRef = null; } } if (!sentHeaders) { num = this.m_HttpContext.Response.SendHeaders(null, null, flags); } Label_01F6: switch (num) { case 0: case 0x26: break; default: { Exception e = new HttpListenerException((int)num); if (Logging.On) { Logging.Exception(Logging.HttpListener, this, "Close", e); } this.m_HttpContext.Abort(); throw e; } } this.m_LeftToWrite = 0L; } finally { base.Dispose(disposing); } Label_023F: if (Logging.On) { Logging.Exit(Logging.HttpListener, this, "Dispose", ""); } }
internal unsafe Task SendFileAsyncCore(string fileName, long offset, long?count, CancellationToken cancellationToken) { _requestContext.Response.Start(); UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); if (count == 0 && _leftToWrite != 0) { return(Helpers.CompletedTask()); } if (_leftToWrite >= 0 && count > _leftToWrite) { throw new InvalidOperationException(Resources.Exception_TooMuchWritten); } // TODO: Verbose log if (cancellationToken.IsCancellationRequested) { return(Helpers.CanceledTask <int>()); } var cancellationRegistration = default(CancellationTokenRegistration); if (cancellationToken.CanBeCanceled) { cancellationRegistration = cancellationToken.Register(RequestContext.AbortDelegate, _requestContext); } uint statusCode; uint bytesSent = 0; bool startedSending = _requestContext.Response.HasStartedSending; var chunked = _requestContext.Response.BoundaryType == BoundaryType.Chunked; ResponseStreamAsyncResult asyncResult = new ResponseStreamAsyncResult(this, fileName, offset, count, chunked, cancellationRegistration); long bytesWritten; if (chunked) { bytesWritten = 0; } else if (count.HasValue) { bytesWritten = count.Value; } else { bytesWritten = asyncResult.FileLength - offset; } // Update _leftToWrite now so we can queue up additional calls to SendFileAsync. flags |= _leftToWrite == bytesWritten ? HttpApi.HTTP_FLAGS.NONE : HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; UpdateWritenCount((uint)bytesWritten); try { if (!startedSending) { statusCode = _requestContext.Response.SendHeaders(null, asyncResult, flags, false); bytesSent = asyncResult.BytesSent; } else { // TODO: If opaque then include the buffer data flag. statusCode = HttpApi.HttpSendResponseEntityBody( _requestContext.RequestQueueHandle, _requestContext.RequestId, (uint)flags, asyncResult.DataChunkCount, asyncResult.DataChunks, &bytesSent, SafeLocalFree.Zero, 0, asyncResult.NativeOverlapped, IntPtr.Zero); } } catch (Exception e) { LogHelper.LogException(_requestContext.Logger, "SendFileAsync", e); asyncResult.Dispose(); Abort(); throw; } if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { asyncResult.Dispose(); if (_requestContext.Server.IgnoreWriteExceptions && startedSending) { asyncResult.Complete(); } else { Exception exception = new IOException(string.Empty, new WebListenerException((int)statusCode)); LogHelper.LogException(_requestContext.Logger, "SendFileAsync", exception); Abort(); throw exception; } } if (statusCode == ErrorCodes.ERROR_SUCCESS && WebListener.SkipIOCPCallbackOnSuccess) { // IO operation completed synchronously - callback won't be called to signal completion. asyncResult.IOCompleted(statusCode, bytesSent); } // Last write, cache it for special cancellation handling. if ((flags & HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { _lastWrite = asyncResult; } return(asyncResult.Task); }