1 // ------------------------------------------------------------------------------ 2 // <copyright file="_HttpResponseStream.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // ------------------------------------------------------------------------------ 6 7 namespace System.Net { 8 using System.IO; 9 using System.Runtime.InteropServices; 10 using System.Threading; 11 using System.Globalization; 12 using System.Security.Permissions; 13 using System.Threading.Tasks; 14 using System.Diagnostics.CodeAnalysis; 15 16 unsafe class HttpResponseStream : Stream { 17 private HttpListenerContext m_HttpContext; 18 private long m_LeftToWrite = long.MinValue; 19 private bool m_Closed; 20 private bool m_InOpaqueMode; 21 // The last write needs special handling to cancel. 22 private HttpResponseStreamAsyncResult m_LastWrite; 23 HttpResponseStream(HttpListenerContext httpContext)24 internal HttpResponseStream(HttpListenerContext httpContext) { 25 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::.ctor() HttpListenerContext##" + ValidationHelper.HashString(httpContext)); 26 m_HttpContext = httpContext; 27 } 28 ComputeLeftToWrite()29 internal UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS ComputeLeftToWrite() { 30 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::ComputeLeftToWrite() on entry m_LeftToWrite:" + m_LeftToWrite); 31 UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE; 32 if (!m_HttpContext.Response.ComputedHeaders) { 33 flags = m_HttpContext.Response.ComputeHeaders(); 34 } 35 if (m_LeftToWrite==long.MinValue) { 36 UnsafeNclNativeMethods.HttpApi.HTTP_VERB method = m_HttpContext.GetKnownMethod(); 37 m_LeftToWrite = method != UnsafeNclNativeMethods.HttpApi.HTTP_VERB.HttpVerbHEAD ? m_HttpContext.Response.ContentLength64 : 0; 38 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::ComputeLeftToWrite() computed m_LeftToWrite:" + m_LeftToWrite); 39 } 40 return flags; 41 } 42 43 public override bool CanSeek { 44 get { 45 return false; 46 } 47 } 48 49 public override bool CanWrite { 50 get { 51 return true; 52 } 53 } 54 55 public override bool CanRead { 56 get { 57 return false; 58 } 59 } 60 61 internal bool Closed 62 { 63 get 64 { 65 return m_Closed; 66 } 67 } 68 69 internal HttpListenerContext InternalHttpContext 70 { 71 get 72 { 73 return m_HttpContext; 74 } 75 } 76 SetClosedFlag()77 internal void SetClosedFlag() 78 { 79 m_Closed = true; 80 } 81 Flush()82 public override void Flush() { 83 } 84 FlushAsync(CancellationToken cancellationToken)85 public override Task FlushAsync(CancellationToken cancellationToken) 86 { 87 return Task.CompletedTask; 88 } 89 90 public override long Length { 91 get { 92 throw new NotSupportedException(SR.GetString(SR.net_noseek)); 93 } 94 95 } 96 97 public override long Position { 98 get { 99 throw new NotSupportedException(SR.GetString(SR.net_noseek)); 100 } 101 set { 102 throw new NotSupportedException(SR.GetString(SR.net_noseek)); 103 } 104 } 105 Seek(long offset, SeekOrigin origin)106 public override long Seek(long offset, SeekOrigin origin) { 107 throw new NotSupportedException(SR.GetString(SR.net_noseek)); 108 } 109 SetLength(long value)110 public override void SetLength(long value) { 111 throw new NotSupportedException(SR.GetString(SR.net_noseek)); 112 } 113 Read([In, Out] byte[] buffer, int offset, int size)114 public override int Read([In, Out] byte[] buffer, int offset, int size) { 115 throw new InvalidOperationException(SR.GetString(SR.net_writeonlystream)); 116 } 117 118 [HostProtection(ExternalThreading=true)] BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state)119 public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state) { 120 throw new InvalidOperationException(SR.GetString(SR.net_writeonlystream)); 121 } 122 EndRead(IAsyncResult asyncResult)123 public override int EndRead(IAsyncResult asyncResult) { 124 throw new InvalidOperationException(SR.GetString(SR.net_writeonlystream)); 125 } 126 Write(byte[] buffer, int offset, int size)127 public override void Write(byte[] buffer, int offset, int size) { 128 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Write", ""); 129 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() buffer.Length:" + buffer.Length + " size:" + size + " offset:" + offset); 130 if (buffer==null) { 131 throw new ArgumentNullException("buffer"); 132 } 133 if (offset<0 || offset>buffer.Length) { 134 throw new ArgumentOutOfRangeException("offset"); 135 } 136 if (size<0 || size>buffer.Length-offset) { 137 throw new ArgumentOutOfRangeException("size"); 138 } 139 UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); 140 if (m_Closed || (size == 0 && m_LeftToWrite != 0)) { 141 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Write", ""); 142 return; 143 } 144 if (m_LeftToWrite>=0 && size>m_LeftToWrite) { 145 throw new ProtocolViolationException(SR.GetString(SR.net_entitytoobig)); 146 } 147 148 uint statusCode; 149 uint dataToWrite = (uint)size; 150 SafeLocalFree bufferAsIntPtr = null; 151 IntPtr pBufferAsIntPtr = IntPtr.Zero; 152 bool sentHeaders = m_HttpContext.Response.SentHeaders; 153 try { 154 if (size == 0) 155 { 156 statusCode = m_HttpContext.Response.SendHeaders(null, null, flags, false); 157 } 158 else 159 { 160 fixed (byte* pDataBuffer = buffer) { 161 byte* pBuffer = pDataBuffer; 162 if (m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) { 163 // 164 165 166 string chunkHeader = size.ToString("x", CultureInfo.InvariantCulture); 167 dataToWrite = dataToWrite + (uint)(chunkHeader.Length + 4); 168 bufferAsIntPtr = SafeLocalFree.LocalAlloc((int)dataToWrite); 169 pBufferAsIntPtr = bufferAsIntPtr.DangerousGetHandle(); 170 for (int i = 0; i < chunkHeader.Length; i++) { 171 Marshal.WriteByte(pBufferAsIntPtr, i, (byte)chunkHeader[i]); 172 } 173 Marshal.WriteInt16(pBufferAsIntPtr, chunkHeader.Length, 0x0A0D); 174 Marshal.Copy(buffer, offset, IntPtrHelper.Add(pBufferAsIntPtr, chunkHeader.Length + 2), size); 175 Marshal.WriteInt16(pBufferAsIntPtr, (int)(dataToWrite - 2), 0x0A0D); 176 pBuffer = (byte*)pBufferAsIntPtr; 177 offset = 0; 178 } 179 UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK dataChunk = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); 180 dataChunk.DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; 181 dataChunk.pBuffer = (byte*)(pBuffer + offset); 182 dataChunk.BufferLength = dataToWrite; 183 184 flags |= m_LeftToWrite == size ? UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE : UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; 185 if (!sentHeaders) { 186 statusCode = m_HttpContext.Response.SendHeaders(&dataChunk, null, flags, false); 187 } 188 else { 189 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() calling UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody"); 190 191 statusCode = 192 UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody( 193 m_HttpContext.RequestQueueHandle, 194 m_HttpContext.RequestId, 195 (uint)flags, 196 1, 197 &dataChunk, 198 null, 199 SafeLocalFree.Zero, 200 0, 201 null, 202 null); 203 204 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() call to UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); 205 if (m_HttpContext.Listener.IgnoreWriteExceptions) { 206 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Write() suppressing error"); 207 statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; 208 } 209 } 210 } 211 } 212 } 213 finally { 214 if (bufferAsIntPtr != null) { 215 // free unmanaged buffer 216 bufferAsIntPtr.Close(); 217 } 218 } 219 220 if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { 221 Exception exception = new HttpListenerException((int)statusCode); 222 if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Write", exception); 223 m_Closed = true; 224 m_HttpContext.Abort(); 225 throw exception; 226 } 227 UpdateAfterWrite(dataToWrite); 228 if(Logging.On)Logging.Dump(Logging.HttpListener, this, "Write", buffer, offset, (int)dataToWrite); 229 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Write", ""); 230 } 231 232 233 [HostProtection(ExternalThreading=true)] BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state)234 public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state) { 235 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() buffer.Length:" + buffer.Length + " size:" + size + " offset:" + offset); 236 if (buffer==null) { 237 throw new ArgumentNullException("buffer"); 238 } 239 if (offset<0 || offset>buffer.Length) { 240 throw new ArgumentOutOfRangeException("offset"); 241 } 242 if (size<0 || size>buffer.Length-offset) { 243 throw new ArgumentOutOfRangeException("size"); 244 } 245 UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); 246 if (m_Closed || (size == 0 && m_LeftToWrite != 0)) { 247 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "BeginWrite", ""); 248 HttpResponseStreamAsyncResult result = new HttpResponseStreamAsyncResult(this, state, callback); 249 result.InvokeCallback((uint) 0); 250 return result; 251 } 252 if (m_LeftToWrite>=0 && size>m_LeftToWrite) { 253 throw new ProtocolViolationException(SR.GetString(SR.net_entitytoobig)); 254 } 255 256 uint statusCode; 257 uint bytesSent = 0; 258 flags |= m_LeftToWrite==size ? UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE : UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; 259 bool sentHeaders = m_HttpContext.Response.SentHeaders; 260 HttpResponseStreamAsyncResult asyncResult = new HttpResponseStreamAsyncResult(this, state, callback, buffer, offset, size, m_HttpContext.Response.BoundaryType==BoundaryType.Chunked, sentHeaders); 261 262 // Update m_LeftToWrite now so we can queue up additional BeginWrite's without waiting for EndWrite. 263 UpdateAfterWrite((uint)((m_HttpContext.Response.BoundaryType == BoundaryType.Chunked) ? 0 : size)); 264 265 try { 266 if (!sentHeaders) { 267 statusCode = m_HttpContext.Response.SendHeaders(null, asyncResult, flags, false); 268 } 269 else { 270 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() calling UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody"); 271 272 m_HttpContext.EnsureBoundHandle(); 273 statusCode = 274 UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody( 275 m_HttpContext.RequestQueueHandle, 276 m_HttpContext.RequestId, 277 (uint)flags, 278 asyncResult.dataChunkCount, 279 asyncResult.pDataChunks, 280 &bytesSent, 281 SafeLocalFree.Zero, 282 0, 283 asyncResult.m_pOverlapped, 284 null); 285 286 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() call to UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); 287 } 288 } 289 catch (Exception e) { 290 if (Logging.On) Logging.Exception(Logging.HttpListener, this, "BeginWrite", e); 291 asyncResult.InternalCleanup(); 292 m_Closed = true; 293 m_HttpContext.Abort(); 294 throw; 295 } 296 297 if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { 298 asyncResult.InternalCleanup(); 299 if (m_HttpContext.Listener.IgnoreWriteExceptions && sentHeaders) { 300 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::BeginWrite() suppressing error"); 301 } 302 else { 303 Exception exception = new HttpListenerException((int)statusCode); 304 if (Logging.On) Logging.Exception(Logging.HttpListener, this, "BeginWrite", exception); 305 m_Closed = true; 306 m_HttpContext.Abort(); 307 throw exception; 308 } 309 } 310 311 if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && HttpListener.SkipIOCPCallbackOnSuccess) 312 { 313 // IO operation completed synchronously - callback won't be called to signal completion. 314 asyncResult.IOCompleted(statusCode, bytesSent); 315 } 316 317 // Last write, cache it for special cancelation handling. 318 if ((flags & UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) 319 { 320 m_LastWrite = asyncResult; 321 } 322 323 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "BeginWrite", ""); 324 return asyncResult; 325 } 326 EndWrite(IAsyncResult asyncResult)327 public override void EndWrite(IAsyncResult asyncResult) { 328 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "EndWrite", ""); 329 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::EndWrite() asyncResult#" + ValidationHelper.HashString(asyncResult)); 330 if (asyncResult==null) { 331 throw new ArgumentNullException("asyncResult"); 332 } 333 HttpResponseStreamAsyncResult castedAsyncResult = asyncResult as HttpResponseStreamAsyncResult; 334 if (castedAsyncResult==null || castedAsyncResult.AsyncObject!=this) { 335 throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); 336 } 337 if (castedAsyncResult.EndCalled) { 338 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndWrite")); 339 } 340 castedAsyncResult.EndCalled = true; 341 // wait & then check for errors 342 object returnValue = castedAsyncResult.InternalWaitForCompletion(); 343 344 Exception exception = returnValue as Exception; 345 if (exception!=null) { 346 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::EndWrite() rethrowing exception:" + exception); 347 if(Logging.On)Logging.Exception(Logging.HttpListener, this, "EndWrite", exception); 348 m_Closed = true; 349 m_HttpContext.Abort(); 350 throw exception; 351 } 352 // 353 354 355 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::EndWrite()"); 356 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "EndWrite", ""); 357 } 358 UpdateAfterWrite(uint dataWritten)359 void UpdateAfterWrite(uint dataWritten) { 360 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::UpdateAfterWrite() dataWritten:" + dataWritten + " m_LeftToWrite:" + m_LeftToWrite + " m_Closed:" + m_Closed); 361 if (!m_InOpaqueMode) 362 { 363 if (m_LeftToWrite>0) { 364 // keep track of the data transferred 365 m_LeftToWrite -= dataWritten; 366 } 367 if (m_LeftToWrite==0) { 368 // in this case we already passed 0 as the flag, so we don't need to call HttpSendResponseEntityBody() when we Close() 369 m_Closed = true; 370 } 371 } 372 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::UpdateAfterWrite() dataWritten:" + dataWritten + " m_LeftToWrite:" + m_LeftToWrite + " m_Closed:" + m_Closed); 373 } 374 Dispose(bool disposing)375 protected override void Dispose(bool disposing) { 376 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Close", ""); 377 378 try { 379 if(disposing){ 380 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() m_Closed:" + m_Closed); 381 if (m_Closed) { 382 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", ""); 383 return; 384 } 385 m_Closed = true; 386 UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite(); 387 if (m_LeftToWrite>0 && !m_InOpaqueMode) { 388 throw new InvalidOperationException(SR.GetString(SR.net_io_notenoughbyteswritten)); 389 } 390 bool sentHeaders = m_HttpContext.Response.SentHeaders; 391 if (sentHeaders && m_LeftToWrite==0) { 392 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", ""); 393 return; 394 } 395 396 uint statusCode = 0; 397 if ((m_HttpContext.Response.BoundaryType==BoundaryType.Chunked || m_HttpContext.Response.BoundaryType==BoundaryType.None) && (String.Compare(m_HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase)!=0)) { 398 if (m_HttpContext.Response.BoundaryType==BoundaryType.None) { 399 flags |= UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; 400 } 401 fixed (void* pBuffer = NclConstants.ChunkTerminator) 402 { 403 UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK* pDataChunk = null; 404 if (m_HttpContext.Response.BoundaryType==BoundaryType.Chunked) { 405 UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK dataChunk = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); 406 dataChunk.DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; 407 dataChunk.pBuffer = (byte *)pBuffer; 408 dataChunk.BufferLength = (uint) NclConstants.ChunkTerminator.Length; 409 pDataChunk = &dataChunk; 410 } 411 if (!sentHeaders) { 412 statusCode = m_HttpContext.Response.SendHeaders(pDataChunk, null, flags, false); 413 } 414 else { 415 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() calling UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody"); 416 417 statusCode = 418 UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody( 419 m_HttpContext.RequestQueueHandle, 420 m_HttpContext.RequestId, 421 (uint)flags, 422 pDataChunk!=null ? (ushort)1 : (ushort)0, 423 pDataChunk, 424 null, 425 SafeLocalFree.Zero, 426 0, 427 null, 428 null); 429 430 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() call to UnsafeNclNativeMethods.HttpApi.HttpSendResponseEntityBody returned:" + statusCode); 431 if (m_HttpContext.Listener.IgnoreWriteExceptions) { 432 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::Close() suppressing error"); 433 statusCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; 434 } 435 } 436 } 437 } 438 else { 439 if (!sentHeaders) { 440 statusCode = m_HttpContext.Response.SendHeaders(null, null, flags, false); 441 } 442 } 443 if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { 444 Exception exception = new HttpListenerException((int)statusCode); 445 if(Logging.On)Logging.Exception(Logging.HttpListener, this, "Close", exception); 446 m_HttpContext.Abort(); 447 throw exception; 448 } 449 m_LeftToWrite = 0; 450 } 451 } 452 finally { 453 base.Dispose(disposing); 454 } 455 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Dispose", ""); 456 } 457 SwitchToOpaqueMode()458 internal void SwitchToOpaqueMode() 459 { 460 GlobalLog.Print("HttpResponseStream#" + ValidationHelper.HashString(this) + "::SwitchToOpaqueMode()"); 461 m_InOpaqueMode = true; 462 m_LeftToWrite = long.MaxValue; 463 } 464 465 // The final Content-Length async write can only be cancelled by CancelIoEx. 466 // Sync can only be cancelled by CancelSynchronousIo, but we don't attempt this right now. 467 [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = 468 "It is safe to ignore the return value on a cancel operation because the connection is being closed")] CancelLastWrite(CriticalHandle requestQueueHandle)469 internal void CancelLastWrite(CriticalHandle requestQueueHandle) 470 { 471 HttpResponseStreamAsyncResult asyncState = m_LastWrite; 472 if (asyncState != null && !asyncState.IsCompleted) 473 { 474 UnsafeNclNativeMethods.CancelIoEx(requestQueueHandle, asyncState.m_pOverlapped); 475 } 476 } 477 } 478 479 unsafe class HttpResponseStreamAsyncResult : LazyAsyncResult { 480 internal NativeOverlapped* m_pOverlapped; 481 private UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK[] m_DataChunks; 482 internal bool m_SentHeaders; 483 484 private static readonly IOCompletionCallback s_IOCallback = new IOCompletionCallback(Callback); 485 486 internal ushort dataChunkCount { 487 get { 488 if (m_DataChunks == null) { 489 return 0; 490 } 491 else { 492 return (ushort)m_DataChunks.Length; 493 } 494 } 495 } 496 497 internal UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK* pDataChunks { 498 get { 499 if (m_DataChunks == null) { 500 return null; 501 } 502 else { 503 return (UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK*)(Marshal.UnsafeAddrOfPinnedArrayElement(m_DataChunks, 0)); 504 } 505 } 506 } 507 HttpResponseStreamAsyncResult(object asyncObject, object userState, AsyncCallback callback)508 internal HttpResponseStreamAsyncResult(object asyncObject, object userState, AsyncCallback callback) : base(asyncObject, userState, callback) { 509 } 510 511 HttpResponseStreamAsyncResult(object asyncObject, object userState, AsyncCallback callback, byte[] buffer, int offset, int size, bool chunked, bool sentHeaders)512 internal HttpResponseStreamAsyncResult(object asyncObject, object userState, AsyncCallback callback, byte[] buffer, int offset, int size, bool chunked, bool sentHeaders): base(asyncObject, userState, callback){ 513 m_SentHeaders = sentHeaders; 514 Overlapped overlapped = new Overlapped(); 515 overlapped.AsyncResult = this; 516 517 if (size == 0) { 518 m_DataChunks = null; 519 m_pOverlapped = overlapped.Pack(s_IOCallback, null); 520 } 521 else { 522 m_DataChunks = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK[chunked ? 3 : 1]; 523 524 GlobalLog.Print("HttpResponseStreamAsyncResult#" + ValidationHelper.HashString(this) + "::.ctor() m_pOverlapped:0x" + ((IntPtr)m_pOverlapped).ToString("x8")); 525 526 object[] objectsToPin = new object[1 + m_DataChunks.Length]; 527 objectsToPin[m_DataChunks.Length] = m_DataChunks; 528 529 530 int chunkHeaderOffset = 0; 531 byte[] chunkHeaderBuffer = null; 532 if (chunked) { 533 chunkHeaderBuffer = ConnectStream.GetChunkHeader(size, out chunkHeaderOffset); 534 535 m_DataChunks[0] = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); 536 m_DataChunks[0].DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; 537 m_DataChunks[0].BufferLength = (uint)(chunkHeaderBuffer.Length - chunkHeaderOffset); 538 539 objectsToPin[0] = chunkHeaderBuffer; 540 541 m_DataChunks[1] = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); 542 m_DataChunks[1].DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; 543 m_DataChunks[1].BufferLength = (uint)size; 544 545 objectsToPin[1] = buffer; 546 547 m_DataChunks[2] = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); 548 m_DataChunks[2].DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; 549 m_DataChunks[2].BufferLength = (uint)NclConstants.CRLF.Length; 550 551 objectsToPin[2] = NclConstants.CRLF; 552 553 } 554 else { 555 m_DataChunks[0] = new UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK(); 556 m_DataChunks[0].DataChunkType = UnsafeNclNativeMethods.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; 557 m_DataChunks[0].BufferLength = (uint)size; 558 559 objectsToPin[0] = buffer; 560 } 561 562 // This call will pin needed memory 563 m_pOverlapped = overlapped.Pack(s_IOCallback, objectsToPin); 564 565 if (chunked) 566 { 567 m_DataChunks[0].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(chunkHeaderBuffer, chunkHeaderOffset)); 568 m_DataChunks[1].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); 569 m_DataChunks[2].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(NclConstants.CRLF, 0)); 570 } 571 else 572 { 573 m_DataChunks[0].pBuffer = (byte*)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); 574 } 575 576 } 577 } 578 IOCompleted(uint errorCode, uint numBytes)579 internal void IOCompleted(uint errorCode, uint numBytes) 580 { 581 IOCompleted(this, errorCode, numBytes); 582 } 583 IOCompleted(HttpResponseStreamAsyncResult asyncResult, uint errorCode, uint numBytes)584 private static void IOCompleted(HttpResponseStreamAsyncResult asyncResult, uint errorCode, uint numBytes) 585 { 586 GlobalLog.Print("HttpResponseStreamAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::Callback() errorCode:0x" + errorCode.ToString("x8") + " numBytes:" + numBytes); 587 object result = null; 588 try { 589 if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { 590 asyncResult.ErrorCode = (int)errorCode; 591 result = new HttpListenerException((int)errorCode); 592 } 593 else { 594 // if we sent headers and body together, numBytes will be the total, but we need to only account for the data 595 // result = numBytes; 596 if (asyncResult.m_DataChunks == null) { 597 result = (uint) 0; 598 if (Logging.On) { Logging.Dump(Logging.HttpListener, asyncResult, "Callback", IntPtr.Zero, 0); } 599 } 600 else { 601 result = asyncResult.m_DataChunks.Length == 1 ? asyncResult.m_DataChunks[0].BufferLength : 0; 602 if (Logging.On) { for (int i = 0; i < asyncResult.m_DataChunks.Length; i++) { Logging.Dump(Logging.HttpListener, asyncResult, "Callback", (IntPtr)asyncResult.m_DataChunks[0].pBuffer, (int)asyncResult.m_DataChunks[0].BufferLength); } } 603 } 604 } 605 GlobalLog.Print("HttpResponseStreamAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::Callback() calling Complete()"); 606 } 607 catch (Exception e) { 608 result = e; 609 } 610 asyncResult.InvokeCallback(result); 611 } 612 Callback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped)613 private static unsafe void Callback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) { 614 Overlapped callbackOverlapped = Overlapped.Unpack(nativeOverlapped); 615 HttpResponseStreamAsyncResult asyncResult = callbackOverlapped.AsyncResult as HttpResponseStreamAsyncResult; 616 GlobalLog.Print("HttpResponseStreamAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::Callback() errorCode:0x" + errorCode.ToString("x8") + " numBytes:" + numBytes + " nativeOverlapped:0x" + ((IntPtr)nativeOverlapped).ToString("x8")); 617 618 IOCompleted(asyncResult, errorCode, numBytes); 619 } 620 621 // Will be called from the base class upon InvokeCallback() Cleanup()622 protected override void Cleanup() { 623 base.Cleanup(); 624 if (m_pOverlapped != null) { 625 Overlapped.Free(m_pOverlapped); 626 } 627 } 628 629 } 630 631 } 632