1 //------------------------------------------------------------------------------
2 // <copyright file="ISAPIWorkerRequest.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System.Web.Hosting {
9 using System.Configuration.Assemblies;
10 using System.Runtime.InteropServices;
11 using System.Collections;
12 using System.Collections.Specialized;
13 using System.Security.Authentication.ExtendedProtection;
15 using System.Globalization;
16 using System.Threading;
17 using Microsoft.Win32;
19 using System.Web.Management;
20 using System.Web.Util;
21 using System.Web.Configuration;
22 using System.Web.Caching;
25 // recyclable buffers for IntPtr[] and int[]
26 // to avoid pinning gen0
29 internal class RecyclableArrayHelper {
30 private const int ARRAY_SIZE = 128;
31 private const int MAX_FREE_ARRAYS = 64;
32 private static IntegerArrayAllocator s_IntegerArrayAllocator;
33 private static IntPtrArrayAllocator s_IntPtrArrayAllocator;
35 static RecyclableArrayHelper() {
36 s_IntegerArrayAllocator = new IntegerArrayAllocator(ARRAY_SIZE, MAX_FREE_ARRAYS);
37 s_IntPtrArrayAllocator = new IntPtrArrayAllocator(ARRAY_SIZE, MAX_FREE_ARRAYS);
40 internal static int[] GetIntegerArray(int minimumLength) {
41 if( minimumLength <= ARRAY_SIZE )
42 return(int[])s_IntegerArrayAllocator.GetBuffer();
44 return new int[minimumLength];
47 internal static IntPtr[] GetIntPtrArray(int minimumLength) {
48 if( minimumLength <= ARRAY_SIZE )
49 return(IntPtr[])s_IntPtrArrayAllocator.GetBuffer();
51 return new IntPtr[minimumLength];
54 internal static void ReuseIntegerArray(int[] array) {
55 if (array != null && array.Length == ARRAY_SIZE)
56 s_IntegerArrayAllocator.ReuseBuffer(array);
59 internal static void ReuseIntPtrArray(IntPtr[] array) {
60 if (array != null && array.Length == ARRAY_SIZE)
61 s_IntPtrArrayAllocator.ReuseBuffer(array);
66 // char[] appendable buffer. Recyclable up to 1K
67 // Also encapsulates encoding (using utf-8) into recyclable byte[] buffer.
70 // new RecyclableCharBuffer
73 // GetEncodedBytesBuffer
77 internal class RecyclableCharBuffer {
78 private const int BUFFER_SIZE = 1024;
79 private const int MAX_FREE_BUFFERS = 64;
80 private static CharBufferAllocator s_CharBufferAllocator;
81 private static UbyteBufferAllocator s_ByteBufferAllocator;
83 private char[] _charBuffer;
86 private bool _recyclable;
89 private byte[] _byteBuffer;
91 static RecyclableCharBuffer() {
92 s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS);
93 s_ByteBufferAllocator = new UbyteBufferAllocator(Encoding.UTF8.GetMaxByteCount(BUFFER_SIZE), MAX_FREE_BUFFERS);
96 internal RecyclableCharBuffer() {
97 _charBuffer = (char[])s_CharBufferAllocator.GetBuffer();
98 _size = _charBuffer.Length;
103 internal void Dispose() {
105 if (_charBuffer != null)
106 s_CharBufferAllocator.ReuseBuffer(_charBuffer);
108 if (_byteBuffer != null)
109 s_ByteBufferAllocator.ReuseBuffer(_byteBuffer);
116 private void Grow(int newSize) {
117 if (newSize <= _size)
120 if (newSize < _size*2)
123 char[] newBuffer = new char[newSize];
126 Array.Copy(_charBuffer, newBuffer, _freePos);
128 _charBuffer = newBuffer;
133 internal void Append(char ch) {
134 if (_freePos >= _size)
137 _charBuffer[_freePos++] = ch;
140 internal void Append(String s) {
142 int newFreePos = _freePos + l;
144 if (newFreePos > _size)
147 s.CopyTo(0, _charBuffer, _freePos, l);
148 _freePos = newFreePos;
151 internal byte[] GetEncodedBytesBuffer() {
152 return GetEncodedBytesBuffer(Encoding.UTF8);
155 internal byte[] GetEncodedBytesBuffer(Encoding encoding) {
156 if (_byteBuffer != null)
159 if (encoding == null)
160 encoding = Encoding.UTF8;
169 // still using the original recyclable char buffer
170 // -- can use recyclable byte buffer
172 _byteBuffer = (byte[])s_ByteBufferAllocator.GetBuffer();
175 encoding.GetBytes(_charBuffer, 0, _freePos, _byteBuffer, 0);
178 _byteBuffer = encoding.GetBytes(_charBuffer, 0, _freePos);
184 public override String ToString() {
185 return (_charBuffer != null && _freePos > 0) ? new String(_charBuffer, 0, _freePos) : null;
190 // byte[] buffer of encoded chars bytes. Recyclable up to 4K
191 // Also encapsulates decoding into recyclable char[] buffer.
194 // new RecyclableByteBuffer
196 // GetDecodedTabSeparatedStrings
200 internal class RecyclableByteBuffer {
201 private const int BUFFER_SIZE = 4096;
202 private const int MAX_FREE_BUFFERS = 64;
203 private static UbyteBufferAllocator s_ByteBufferAllocator;
204 private static CharBufferAllocator s_CharBufferAllocator;
207 private byte[] _byteBuffer;
208 private bool _recyclable;
210 private char[] _charBuffer;
212 static RecyclableByteBuffer() {
213 s_ByteBufferAllocator = new UbyteBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS);
214 s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS);
217 internal RecyclableByteBuffer() {
218 _byteBuffer = (byte[])s_ByteBufferAllocator.GetBuffer();
222 internal void Dispose() {
224 if (_byteBuffer != null)
225 s_ByteBufferAllocator.ReuseBuffer(_byteBuffer);
227 if (_charBuffer != null)
228 s_CharBufferAllocator.ReuseBuffer(_charBuffer);
235 internal byte[] Buffer {
236 get { return _byteBuffer; }
239 internal void Resize(int newSize) {
240 _byteBuffer = new byte[newSize];
244 private void Skip(int count) {
249 int l = _byteBuffer.Length;
252 for (int i = 0; i < l; i++) {
253 if (_byteBuffer[i] == (byte)'\t') {
263 private int CalcLength()
265 // calculate null termitated length
267 if (_byteBuffer != null) {
268 int l = _byteBuffer.Length;
270 for (int i = _offset; i < l; i++) {
271 if (_byteBuffer[i] == 0)
279 private char[] GetDecodedCharBuffer(Encoding encoding, ref int len) {
280 if (_charBuffer != null)
284 _charBuffer = new char[0];
286 else if (_recyclable) {
287 _charBuffer = (char[])s_CharBufferAllocator.GetBuffer();
288 len = encoding.GetChars(_byteBuffer, _offset, len, _charBuffer, 0);
291 _charBuffer = encoding.GetChars(_byteBuffer, _offset, len);
292 len = _charBuffer.Length;
298 internal string GetDecodedString(Encoding encoding, int len) {
299 return encoding.GetString(_byteBuffer, 0, len);
302 internal String[] GetDecodedTabSeparatedStrings(Encoding encoding, int numStrings, int numSkipStrings) {
303 if (numSkipStrings > 0)
304 Skip(numSkipStrings);
306 int len = CalcLength();
307 char[] s = GetDecodedCharBuffer(encoding, ref len);
309 String[] ss = new String[numStrings];
313 int foundStrings = 0;
315 for (int iString = 0; iString < numStrings; iString++) {
318 for (int i = iStart; i < len; i++) {
326 ss[iString] = new String(s, iStart, iEnd-iStart);
328 ss[iString] = String.Empty;
338 if (foundStrings < numStrings) {
342 for (int iString = 0; iString < numStrings; iString++) {
345 for (int i = iStart; i < len; i++) {
346 if (_byteBuffer[i] == (byte)'\t') {
353 ss[iString] = encoding.GetString(_byteBuffer, iStart, iEnd-iStart);
355 ss[iString] = String.Empty;
371 // class to encapsulate writing from byte[], IntPtr (resource or filehandle)
374 internal enum BufferType: byte {
377 IISAllocatedRequestMemory = 2,
381 internal class MemoryBytes {
383 private byte[] _arrayData;
384 private GCHandle _pinnedArrayData;
385 private IntPtr _intptrData;
386 private long _fileSize;
387 private IntPtr _fileHandle;
388 private string _fileName;
389 private long _offset;
390 private BufferType _bufferType; // 0 managed, 1 native pool, 2 IIS allocated request memory, 3 TransmitFile
392 internal MemoryBytes(string fileName, long offset, long fileSize) {
393 _bufferType = BufferType.TransmitFile;
394 _intptrData = IntPtr.Zero;
395 _fileHandle = IntPtr.Zero;
396 _fileSize = fileSize;
397 _fileName = fileName;
399 // _cachedResponseBodyLength will be wrong if we don't set _size now.
403 internal MemoryBytes(byte[] data, int size): this(data, size, false, 0) {
406 internal MemoryBytes(byte[] data, int size, bool useTransmitFile, long fileSize) {
409 _intptrData = IntPtr.Zero;
410 _fileHandle = IntPtr.Zero;
411 if (useTransmitFile) {
412 _bufferType = BufferType.TransmitFile;
414 _fileSize = fileSize;
417 internal MemoryBytes(IntPtr data, int size, BufferType bufferType) {
421 _fileHandle = IntPtr.Zero;
422 _bufferType = bufferType;
425 internal long FileSize {
426 get { return _fileSize; }
429 internal bool IsBufferFromUnmanagedPool {
430 get { return _bufferType == BufferType.UnmanagedPool; }
433 internal BufferType BufferType {
434 get { return _bufferType; }
438 get { return _size; }
441 internal bool UseTransmitFile {
442 get { return _bufferType == BufferType.TransmitFile; }
445 private void CloseHandle() {
446 if (_fileHandle != IntPtr.Zero && _fileHandle != UnsafeNativeMethods.INVALID_HANDLE_VALUE) {
447 UnsafeNativeMethods.CloseHandle(_fileHandle);
448 // don't allow 'this' to be GC'd before CloseHandle returns.
449 _fileHandle = IntPtr.Zero;
453 private static byte[] IntPtrToBytes(IntPtr p, long offset, long length) {
454 // This method converts the given pointer and offset to a byte[] representation
455 // of the C struct EcbFileAndOffset (32 and 64-bit specific):
457 // struct FileAndOffset
459 // ULONGLONG cbOffset;
460 // ULONGLONG cbLength;
465 byte[] bytes = new byte[2 * sizeof(long) + IntPtr.Size];
467 // Put the offset value first
468 for (int i = 0; i < 8; i++)
469 bytes[i] = (byte)((offset >> 8*i) & 0xFF );
471 // Put the file value next
472 for (int i = 0; i < 8; i++)
473 bytes[8+i] = (byte)((length >> 8*i) & 0xFF );
475 if (IntPtr.Size == 4) {
476 int temp = p.ToInt32();
477 for (int i = 0; i < 4; i++)
478 bytes[16+i] = (byte)((temp >> 8*i) & 0xFF );
480 long temp = p.ToInt64();
481 for (int i = 0; i < 8; i++)
482 bytes[16+i] = (byte)((temp >> 8*i) & 0xFF );
487 private void SetHandle() {
488 if (_fileName != null) {
489 _fileHandle = UnsafeNativeMethods.GetFileHandleForTransmitFile(_fileName);
491 if (_fileHandle != IntPtr.Zero) {
492 _arrayData = IntPtrToBytes(_fileHandle, _offset, _fileSize);
496 internal IntPtr LockMemory() {
498 if (_arrayData != null) {
499 _pinnedArrayData = GCHandle.Alloc(_arrayData, GCHandleType.Pinned);
500 return Marshal.UnsafeAddrOfPinnedArrayElement(_arrayData, 0);
507 internal void UnlockMemory() {
509 if (_arrayData != null) {
510 _pinnedArrayData.Free();
516 // recyclable pinnable char[] buffer to get Unicode server variables
519 // new ServerVarCharBuffer
520 // get PinnedAddress, Length
525 internal class ServerVarCharBuffer {
526 private const int BUFFER_SIZE = 1024;
527 private const int MAX_FREE_BUFFERS = 64;
528 private static CharBufferAllocator s_CharBufferAllocator;
530 private bool _recyclable;
531 private char[] _charBuffer;
532 private bool _pinned;
533 private GCHandle _pinnedCharBufferHandle;
534 private IntPtr _pinnedAddr;
536 static ServerVarCharBuffer() {
537 s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS);
540 internal ServerVarCharBuffer() {
541 _charBuffer = (char[])s_CharBufferAllocator.GetBuffer();
545 internal void Dispose() {
547 _pinnedCharBufferHandle.Free();
552 if (_charBuffer != null)
553 s_CharBufferAllocator.ReuseBuffer(_charBuffer);
559 internal IntPtr PinnedAddress {
562 _pinnedCharBufferHandle = GCHandle.Alloc(_charBuffer, GCHandleType.Pinned);
563 _pinnedAddr = Marshal.UnsafeAddrOfPinnedArrayElement(_charBuffer, 0);
571 internal int Length {
573 return _charBuffer.Length;
577 internal void Resize(int newSize) {
579 _pinnedCharBufferHandle.Free();
583 _charBuffer = new char[newSize];
589 // Async IO completion callback from IIS
591 internal delegate void ISAPIAsyncCompletionCallback(IntPtr ecb, int byteCount, int error);
593 internal delegate void AsyncCompletionCallback(int bytesCompleted, int hresult, IntPtr pbAsyncReceiveBuffer);
596 // Implementation of HttpWorkerRequest based on ECB
598 internal abstract class ISAPIWorkerRequest : HttpWorkerRequest {
600 protected IntPtr _ecb; // ECB as integer
601 protected IntPtr _token; // user token as integer
602 protected Guid _traceId; // ETW traceId
603 protected AsyncResultBase _asyncResultBase;
604 protected AsyncCompletionCallback _asyncCompletionCallback;
606 // Request data obtained during initialization (basics)
608 protected String _method;
609 protected String _path;
610 protected String _filePath;
611 protected String _pathInfo;
612 protected String _pathTranslated;
613 protected String _appPath;
614 protected String _appPathTranslated;
616 protected int _contentType;
617 protected int _contentTotalLength;
618 protected int _contentAvailLength;
620 protected int _queryStringLength;
622 protected bool _ignoreMinAsyncSize;
623 protected bool _requiresAsyncFlushCallback;
625 // Request data obtained later on
627 private bool _preloadedContentRead;
628 private byte[] _preloadedContent;
630 private bool _requestHeadersAvailable;
631 private String[][] _unknownRequestHeaders;
632 private String[] _knownRequestHeaders;
634 private bool _clientCertFetched;
635 private DateTime _clientCertValidFrom;
636 private DateTime _clientCertValidUntil;
637 private byte [] _clientCert;
638 private int _clientCertEncoding;
639 private byte [] _clientCertPublicKey;
640 private byte [] _clientCertBinaryIssuer;
642 // Outgoing headers storage
644 private bool _headersSent;
645 private Encoding _headerEncoding;
646 private bool _contentLengthSent;
647 private bool _chunked;
648 private RecyclableCharBuffer _headers = new RecyclableCharBuffer();
649 private RecyclableCharBuffer _status = new RecyclableCharBuffer();
650 private bool _statusSet = true;
652 // Outgoing data cached for a single FlushCore
654 private byte[] _cachedResponseStatus;
655 private byte[] _cachedResponseHeaders;
656 private int _cachedResponseKeepConnected;
657 private int _cachedResponseBodyLength;
658 private ArrayList _cachedResponseBodyBytes;
659 private int _cachedResponseBodyBytesIoLockCount;
661 // Notification about the end of IO
663 private HttpWorkerRequest.EndOfSendNotification _endOfRequestCallback;
664 private Object _endOfRequestCallbackArg;
665 private int _endOfRequestCallbackLockCount;
667 // Constants for posted content type
669 private const int CONTENT_NONE = 0;
670 private const int CONTENT_FORM = 1;
671 private const int CONTENT_MULTIPART = 2;
672 private const int CONTENT_OTHER = 3;
675 // ISAPI status constants (for DoneWithSession)
678 private const int STATUS_SUCCESS = 1;
679 private const int STATUS_SUCCESS_AND_KEEP_CONN = 2;
680 private const int STATUS_PENDING = 3;
681 private const int STATUS_ERROR = 4;
687 private String[] ReadBasics(int[] contentInfo) {
690 RecyclableByteBuffer buf = new RecyclableByteBuffer();
692 int r = GetBasicsCore(buf.Buffer, buf.Buffer.Length, contentInfo);
695 buf.Resize(-r); // buffer not big enough
696 r = GetBasicsCore(buf.Buffer, buf.Buffer.Length, contentInfo);
700 throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data));
702 // convert to characters and split the buffer into strings
704 String[] ss = buf.GetDecodedTabSeparatedStrings(Encoding.Default, 6, 0);
713 private static readonly char[] s_ColonOrNL = { ':', '\n' };
715 private void ReadRequestHeaders() {
716 if (_requestHeadersAvailable)
719 _knownRequestHeaders = new String[RequestHeaderMaximum];
721 // construct unknown headers as array list of name1,value1,...
723 ArrayList headers = new ArrayList();
725 String s = GetServerVariable("ALL_RAW");
726 int l = (s != null) ? s.Length : 0;
733 int ci = s.IndexOfAny(s_ColonOrNL, i);
739 // ignore header without :
750 String name = s.Substring(i, ci-i).Trim();
753 int ni = s.IndexOf('\n', ci+1);
757 while (ni < l-1 && s[ni+1] == ' ') { // continuation of header (ASURT 115064)
758 ni = s.IndexOf('\n', ni+1);
764 String value = s.Substring(ci+1, ni-ci-1).Trim();
767 int knownIndex = GetKnownRequestHeaderIndex(name);
768 if (knownIndex >= 0) {
769 _knownRequestHeaders[knownIndex] = value;
779 // copy to array unknown headers
781 int n = headers.Count / 2;
782 _unknownRequestHeaders = new String[n][];
785 for (i = 0; i < n; i++) {
786 _unknownRequestHeaders[i] = new String[2];
787 _unknownRequestHeaders[i][0] = (String)headers[j++];
788 _unknownRequestHeaders[i][1] = (String)headers[j++];
791 _requestHeadersAvailable = true;
794 private void SendHeaders() {
797 _headers.Append("\r\n");
799 AddHeadersToCachedResponse(
800 _status.GetEncodedBytesBuffer(),
801 _headers.GetEncodedBytesBuffer(_headerEncoding),
802 (_contentLengthSent || _chunked) ? 1 : 0);
809 private void SendResponseFromFileStream(FileStream f, long offset, long length) {
810 long fileSize = f.Length;
813 length = fileSize - offset;
815 if (offset < 0 || length > fileSize - offset)
816 throw new HttpException(SR.GetString(SR.Invalid_range));
820 f.Seek(offset, SeekOrigin.Begin);
822 byte[] fileBytes = new byte[(int)length];
823 int bytesRead = f.Read(fileBytes, 0, (int)length);
825 AddBodyToCachedResponse(new MemoryBytes(fileBytes, bytesRead));
829 private void ResetCachedResponse() {
830 _cachedResponseStatus = null;
831 _cachedResponseHeaders = null;
832 _cachedResponseBodyLength = 0;
833 _cachedResponseBodyBytes = null;
835 // DDBugs 162981: ASP.NET leaks requests when page calls TransmitFile and Flush
836 // This happens because FlushCachedResponse may set _requiresAsyncFlushCallback and
837 // _ignoreMinAsyncSize to true and then it "forgets" to reset them after Flush is done.
838 // When the final flush is being executed it uses incorrect values
839 // to determine that it needs an async completion during the final flush.
840 // The fix is to reset async flags after each Flush
841 _requiresAsyncFlushCallback = false;
842 _ignoreMinAsyncSize = false;
845 private void AddHeadersToCachedResponse(byte[] status, byte[] header, int keepConnected) {
846 _cachedResponseStatus = status;
847 _cachedResponseHeaders = header;
848 _cachedResponseKeepConnected = keepConnected;
851 private void AddBodyToCachedResponse(MemoryBytes bytes) {
852 if (_cachedResponseBodyBytes == null)
853 _cachedResponseBodyBytes = new ArrayList();
854 _cachedResponseBodyBytes.Add(bytes);
855 _cachedResponseBodyLength += bytes.Size;
858 internal void UnlockCachedResponseBytesOnceAfterIoComplete() {
859 if (Interlocked.Decrement(ref _cachedResponseBodyBytesIoLockCount) == 0) {
860 // unlock pinned memory
861 if (_cachedResponseBodyBytes != null) {
862 int numFragments = _cachedResponseBodyBytes.Count;
863 for (int i = 0; i < numFragments; i++) {
865 ((MemoryBytes)_cachedResponseBodyBytes[i]).UnlockMemory();
872 // don't remember cached data anymore
873 ResetCachedResponse();
875 FlushAsyncResult flushAsyncResult = _asyncResultBase as FlushAsyncResult;
876 if (flushAsyncResult != null) {
877 _endOfRequestCallbackLockCount--;
878 _asyncCompletionCallback(0, flushAsyncResult.HResult, IntPtr.Zero);
883 // ISAPIWorkerRequest
884 private void FlushCachedResponse(bool isFinal) {
885 if (_ecb == IntPtr.Zero)
888 bool asyncFlush = false;
889 int numFragments = 0;
890 IntPtr[] fragments = null;
891 int[] fragmentLengths = null;
895 // prepare body fragments as IntPtr[] of pointers and int[] of lengths
896 if (_cachedResponseBodyLength > 0) {
897 numFragments = _cachedResponseBodyBytes.Count;
898 fragments = RecyclableArrayHelper.GetIntPtrArray(numFragments);
899 fragmentLengths = RecyclableArrayHelper.GetIntegerArray(numFragments);
901 for (int i = 0; i < numFragments; i++) {
902 MemoryBytes bytes = (MemoryBytes)_cachedResponseBodyBytes[i];
903 fragments[i] = bytes.LockMemory();
905 if (!isFinal || !bytes.IsBufferFromUnmanagedPool)
906 _requiresAsyncFlushCallback = true;
908 if (bytes.UseTransmitFile) {
909 fragmentLengths[i] = -bytes.Size; // use negative length for TransmitFile
910 _ignoreMinAsyncSize = true;
911 bytesOut += bytes.FileSize;
914 fragmentLengths[i] = bytes.Size;
915 bytesOut += bytes.Size;
920 // prepare doneWithSession and finalStatus
921 int doneWithSession = isFinal ? 1 : 0;
922 int finalStatus = isFinal ? ((_cachedResponseKeepConnected != 0) ? STATUS_SUCCESS_AND_KEEP_CONN : STATUS_SUCCESS) : 0;
924 // set the count to two - one for return from FlushCore and one for async IO completion
925 // the cleanup should happen on the later of the two
926 _cachedResponseBodyBytesIoLockCount = 2;
928 // increment the lock count controlling end of request callback
929 // so that the callback would be called at the later of EndRequest
930 // and the async IO completion
931 // (doesn't need to be interlocked as only one thread could start the IO)
932 _endOfRequestCallbackLockCount++;
935 PerfCounters.DecrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
937 // perf counters are DWORDs, so it makes no sense to update REQUEST_BYTES_OUT with a value greater than Int32.MaxValue
938 int delta = (int) bytesOut;
940 PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_OUT, delta);
944 // send to unmanaged code
946 _cachedResponseStatus, _cachedResponseHeaders, _cachedResponseKeepConnected,
947 _cachedResponseBodyLength, numFragments, fragments, fragmentLengths,
948 doneWithSession, finalStatus, out asyncFlush);
958 // in case of synchronous IO adjust down the lock counts
960 _cachedResponseBodyBytesIoLockCount--;
961 _endOfRequestCallbackLockCount--;
964 // unlock pinned memory
965 UnlockCachedResponseBytesOnceAfterIoComplete();
968 RecyclableArrayHelper.ReuseIntPtrArray(fragments);
969 RecyclableArrayHelper.ReuseIntegerArray(fragmentLengths);
973 internal void CallEndOfRequestCallbackOnceAfterAllIoComplete() {
974 if (_endOfRequestCallback != null) {
975 // only call the callback on the latest of EndRequest and async IO completion
976 if (Interlocked.Decrement(ref _endOfRequestCallbackLockCount) == 0) {
978 _endOfRequestCallback(this, _endOfRequestCallbackArg);
990 internal ISAPIWorkerRequest(IntPtr ecb) {
992 PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TOTAL);
995 public override Guid RequestTraceIdentifier {
996 get { return _traceId; }
999 internal IntPtr Ecb {
1005 internal void Initialize() {
1006 // setup basic values
1008 ReadRequestBasics();
1010 if (_appPathTranslated != null && _appPathTranslated.Length > 2 && !StringUtil.StringEndsWith(_appPathTranslated, '\\'))
1011 _appPathTranslated += "\\"; // IIS 6.0 doesn't add the trailing '\'
1013 // Increment incoming request length
1014 PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, _contentTotalLength);
1017 internal virtual void ReadRequestBasics() {
1019 // Get requests basics
1021 int[] contentInfo = new int[4];
1022 String[] basicStrings = ReadBasics(contentInfo);
1024 if (basicStrings == null || basicStrings.Length != 6)
1025 throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data));
1027 // Remember content info
1029 _contentType = contentInfo[0];
1030 _contentTotalLength = contentInfo[1];
1031 _contentAvailLength = contentInfo[2];
1032 _queryStringLength = contentInfo[3];
1034 // Remember basic strings
1036 _method = basicStrings[0];
1037 _filePath = basicStrings[1];
1038 _pathInfo = basicStrings[2];
1039 _path = (_pathInfo.Length > 0) ? (_filePath + _pathInfo) : _filePath;
1040 _pathTranslated = basicStrings[3];
1041 _appPath = basicStrings[4];
1042 _appPathTranslated = basicStrings[5];
1049 internal static ISAPIWorkerRequest CreateWorkerRequest(IntPtr ecb, bool useOOP) {
1051 ISAPIWorkerRequest wr = null;
1053 EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero);
1055 if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, false);
1057 wr = new ISAPIWorkerRequestOutOfProc(ecb);
1060 int version = UnsafeNativeMethods.EcbGetVersion(ecb) >> 16;
1063 EtwTrace.TraceEnableCheck(EtwTraceConfigType.IIS7_ISAPI, ecb);
1066 EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero);
1069 if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, true);
1072 wr = new ISAPIWorkerRequestInProcForIIS7(ecb);
1074 else if (version == 6) {
1075 wr = new ISAPIWorkerRequestInProcForIIS6(ecb);
1078 wr = new ISAPIWorkerRequestInProc(ecb);
1084 public override String GetUriPath() {
1088 public override String GetQueryString() {
1089 if (_queryStringLength == 0)
1090 return String.Empty;
1092 int size = _queryStringLength + 2;
1093 StringBuilder buf = new StringBuilder(size);
1095 int r = GetQueryStringCore(0, buf, size);
1098 throw new HttpException(SR.GetString(SR.Cannot_get_query_string));
1100 return buf.ToString();
1103 public override byte[] GetQueryStringRawBytes() {
1104 if (_queryStringLength == 0)
1107 byte[] buf = new byte[_queryStringLength];
1108 int r = GetQueryStringRawBytesCore(buf, _queryStringLength);
1110 throw new HttpException(SR.GetString(SR.Cannot_get_query_string_bytes));
1116 public override String GetRawUrl() {
1117 String qs = GetQueryString();
1119 if (!String.IsNullOrEmpty(qs))
1120 return _path + "?" + qs;
1125 public override String GetHttpVerbName() {
1129 public override String GetHttpVersion() {
1130 return GetServerVariable("SERVER_PROTOCOL");
1133 public override String GetRemoteAddress() {
1134 return GetServerVariable("REMOTE_ADDR");
1137 public override String GetRemoteName() {
1138 return GetServerVariable("REMOTE_HOST");
1141 public override int GetRemotePort() {
1142 return 0; // unknown in ISAPI
1145 public override String GetLocalAddress() {
1146 return GetServerVariable("LOCAL_ADDR");
1149 public override int GetLocalPort() {
1150 return Int32.Parse(GetServerVariable("SERVER_PORT"));
1153 internal override String GetLocalPortAsString() {
1154 return GetServerVariable("SERVER_PORT");
1157 public override String GetServerName() {
1158 return GetServerVariable("SERVER_NAME");
1161 public override bool IsSecure() {
1162 String https = GetServerVariable("HTTPS");
1163 return (https != null && https.Equals("on"));
1166 public override String GetFilePath() {
1170 public override String GetFilePathTranslated() {
1171 return _pathTranslated;
1174 public override String GetPathInfo() {
1178 public override String GetAppPath() {
1182 public override String GetAppPathTranslated() {
1183 return _appPathTranslated;
1186 public override int GetPreloadedEntityBodyLength() {
1187 return _contentAvailLength;
1190 public override byte[] GetPreloadedEntityBody() {
1191 if (!_preloadedContentRead) {
1192 if (_contentAvailLength > 0) {
1193 _preloadedContent = new byte[_contentAvailLength];
1195 int r = GetPreloadedPostedContentCore(_preloadedContent, 0, _contentAvailLength);
1198 throw new HttpException(SR.GetString(SR.Cannot_read_posted_data));
1201 _preloadedContentRead = true;
1204 return _preloadedContent;
1207 public override bool IsEntireEntityBodyIsPreloaded() {
1208 return (_contentAvailLength == _contentTotalLength);
1211 public override int GetTotalEntityBodyLength() {
1212 return _contentTotalLength;
1215 public override int ReadEntityBody(byte[] buffer, int size) {
1216 return ReadEntityBody(buffer, 0, size);
1219 public override int ReadEntityBody(byte[] buffer, int offset, int size) {
1220 if (buffer.Length - offset < size) {
1221 throw new ArgumentOutOfRangeException("offset");
1224 int r = GetAdditionalPostedContentCore(buffer, offset, size);
1227 throw new HttpException(SR.GetString(SR.Cannot_read_posted_data));
1233 public override long GetBytesRead() {
1234 throw new HttpException(SR.GetString(SR.Not_supported));
1237 public override String GetKnownRequestHeader(int index) {
1238 if (!_requestHeadersAvailable) {
1239 // special case important ones so that no all headers parsing is required
1242 case HeaderContentType:
1243 if (_contentType == CONTENT_FORM)
1244 return "application/x-www-form-urlencoded";
1247 case HeaderContentLength:
1248 if (_contentType != CONTENT_NONE)
1249 return (_contentTotalLength).ToString();
1253 // parse all headers
1254 ReadRequestHeaders();
1257 return _knownRequestHeaders[index];
1260 public override String GetUnknownRequestHeader(String name) {
1261 if (!_requestHeadersAvailable)
1262 ReadRequestHeaders();
1264 int n = _unknownRequestHeaders.Length;
1266 for (int i = 0; i < n; i++) {
1267 if (StringUtil.EqualsIgnoreCase(name, _unknownRequestHeaders[i][0]))
1268 return _unknownRequestHeaders[i][1];
1274 public override String[][] GetUnknownRequestHeaders() {
1275 if (!_requestHeadersAvailable)
1276 ReadRequestHeaders();
1278 return _unknownRequestHeaders;
1281 public override void SendStatus(int statusCode, String statusDescription) {
1282 _status.Append(statusCode.ToString());
1283 _status.Append(" ");
1284 _status.Append(statusDescription);
1288 internal override void SetHeaderEncoding(Encoding encoding) {
1289 _headerEncoding = encoding;
1292 public override void SendKnownResponseHeader(int index, String value) {
1294 throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1296 if (index == HttpWorkerRequest.HeaderSetCookie) {
1297 DisableKernelCache();
1300 _headers.Append(GetKnownResponseHeaderName(index));
1301 _headers.Append(": ");
1302 _headers.Append(value);
1303 _headers.Append("\r\n");
1305 if (index == HeaderContentLength)
1306 _contentLengthSent = true;
1307 else if (index == HeaderTransferEncoding && (value != null && value.Equals("chunked")))
1311 public override void SendUnknownResponseHeader(String name, String value) {
1313 throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1315 if (StringUtil.EqualsIgnoreCase(name, "Set-Cookie")) {
1316 DisableKernelCache();
1319 _headers.Append(name);
1320 _headers.Append(": ");
1321 _headers.Append(value);
1322 _headers.Append("\r\n");
1325 public override void SendCalculatedContentLength(int contentLength) {
1326 SendCalculatedContentLength((long)contentLength);
1329 // VSWhidbey 559473: need to support Content-Length response header values > 2GB
1330 public override void SendCalculatedContentLength(long contentLength) {
1333 _headers.Append("Content-Length: ");
1334 _headers.Append(contentLength.ToString(CultureInfo.InvariantCulture));
1335 _headers.Append("\r\n");
1336 _contentLengthSent = true;
1340 public override bool HeadersSent() {
1341 return _headersSent;
1344 public override bool IsClientConnected() {
1345 return (IsClientConnectedCore() == 0) ? false : true;
1348 public override void CloseConnection() {
1349 CloseConnectionCore();
1352 public override void SendResponseFromMemory(byte[] data, int length) {
1357 AddBodyToCachedResponse(new MemoryBytes(data, length));
1360 public override void SendResponseFromMemory(IntPtr data, int length) {
1361 SendResponseFromMemory(data, length, false);
1364 internal override void SendResponseFromMemory(IntPtr data, int length, bool isBufferFromUnmanagedPool) {
1369 AddBodyToCachedResponse(new MemoryBytes(data, length, isBufferFromUnmanagedPool ? BufferType.UnmanagedPool : BufferType.Managed));
1372 // PackageFile for in-proc case
1373 internal virtual MemoryBytes PackageFile(String filename, long offset64, long length64, bool isImpersonating) {
1374 // The offset and length must be less than Int32.MaxValue for in-proc.
1375 // This should be true, since HttpFileResponseElement.ctor throws ArgumentOutOfRangeException for in-proc
1376 Debug.Assert(offset64 < Int32.MaxValue);
1377 Debug.Assert(length64 < Int32.MaxValue);
1378 int offset = Convert.ToInt32(offset64);
1379 int length = Convert.ToInt32(length64);
1381 FileStream f = null;
1382 MemoryBytes bytes = null;
1384 Debug.Assert(offset < length);
1385 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
1386 Debug.Assert((f.Length - offset) == length);
1387 int size = (int) (f.Length - offset);
1388 byte[] fileBytes = new byte[size];
1389 int bytesRead = f.Read(fileBytes, offset, size);
1390 bytes = new MemoryBytes(fileBytes, bytesRead);
1400 internal override void TransmitFile(string filename, long offset, long length, bool isImpersonating) {
1407 AddBodyToCachedResponse(PackageFile(filename, offset, length, isImpersonating));
1411 public override void SendResponseFromFile(String filename, long offset, long length) {
1418 FileStream f = null;
1421 f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
1422 SendResponseFromFileStream(f, offset, length);
1430 public override void SendResponseFromFile(IntPtr handle, long offset, long length) {
1437 FileStream f = null;
1440 f = new FileStream(new Microsoft.Win32.SafeHandles.SafeFileHandle(handle,false), FileAccess.Read);
1441 SendResponseFromFileStream(f, offset, length);
1449 // ISAPIWorkerRequest
1450 public override void FlushResponse(bool finalFlush) {
1451 // only flush headers - the data is write through
1456 FlushCachedResponse(finalFlush);
1459 public override void EndOfRequest() {
1460 FlushCachedResponse(true);
1462 // recycle the headers and status buffers
1463 if (_headers != null) {
1468 if (_status != null) {
1472 CallEndOfRequestCallbackOnceAfterAllIoComplete();
1475 public override void SetEndOfSendNotification(HttpWorkerRequest.EndOfSendNotification callback, Object extraData) {
1476 if (_endOfRequestCallback != null)
1477 throw new InvalidOperationException();
1478 _endOfRequestCallback = callback;
1479 _endOfRequestCallbackArg = extraData;
1480 _endOfRequestCallbackLockCount = 1; // when goes to 0 the callback is called
1483 public override String MapPath(String path) {
1484 return HostingEnvironment.MapPathInternal(path);
1487 public override String MachineConfigPath {
1489 return HttpConfigurationSystem.MachineConfigurationFilePath;
1493 public override String RootWebConfigPath {
1495 return HttpConfigurationSystem.RootWebConfigurationFilePath;
1499 public override String MachineInstallDirectory {
1501 return HttpRuntime.AspInstallDirectory;
1505 public override IntPtr GetUserToken() {
1506 return GetUserTokenCore();
1509 public override IntPtr GetVirtualPathToken() {
1510 return GetVirtualPathTokenCore();
1513 public override byte[] GetClientCertificate() {
1514 if (!_clientCertFetched)
1515 FetchClientCertificate();
1520 public override DateTime GetClientCertificateValidFrom() {
1521 if (!_clientCertFetched)
1522 FetchClientCertificate();
1524 return _clientCertValidFrom;
1527 public override DateTime GetClientCertificateValidUntil() {
1528 if (!_clientCertFetched)
1529 FetchClientCertificate();
1531 return _clientCertValidUntil;
1534 public override byte [] GetClientCertificateBinaryIssuer() {
1535 if (!_clientCertFetched)
1536 FetchClientCertificate();
1537 return _clientCertBinaryIssuer;
1540 public override int GetClientCertificateEncoding() {
1541 if (!_clientCertFetched)
1542 FetchClientCertificate();
1543 return _clientCertEncoding;
1546 public override byte [] GetClientCertificatePublicKey() {
1547 if (!_clientCertFetched)
1548 FetchClientCertificate();
1549 return _clientCertPublicKey;
1552 private void FetchClientCertificate() {
1553 if (_clientCertFetched)
1556 _clientCertFetched = true;
1558 byte[] buf = new byte[8192];
1559 int[] pInts = new int[4];
1560 long[] pDates = new long[2];
1561 int iRet = GetClientCertificateCore(buf, pInts, pDates);
1563 if (iRet < 0 && (-iRet) > 8192) {
1565 buf = new byte[iRet];
1566 iRet = GetClientCertificateCore(buf, pInts, pDates);
1569 _clientCertEncoding = pInts[0];
1571 if (pInts[1] < buf.Length && pInts[1] > 0) {
1572 _clientCert = new byte[pInts[1]];
1573 Array.Copy(buf, _clientCert, pInts[1]);
1575 if (pInts[2] + pInts[1] < buf.Length && pInts[2] > 0) {
1576 _clientCertBinaryIssuer = new byte[pInts[2]];
1577 Array.Copy(buf, pInts[1], _clientCertBinaryIssuer, 0, pInts[2]);
1580 if (pInts[2] + pInts[1] + pInts[3] < buf.Length && pInts[3] > 0) {
1581 _clientCertPublicKey = new byte[pInts[3]];
1582 Array.Copy(buf, pInts[1] + pInts[2], _clientCertPublicKey, 0, pInts[3]);
1587 if (iRet > 0 && pDates[0] != 0)
1588 _clientCertValidFrom = DateTime.FromFileTime(pDates[0]);
1590 _clientCertValidFrom = DateTime.Now;
1592 if (iRet > 0 && pDates[1] != 0)
1593 _clientCertValidUntil = DateTime.FromFileTime(pDates[1]);
1595 _clientCertValidUntil = DateTime.Now;
1599 // internal methods specific to ISAPI
1602 internal void AppendLogParameter(String logParam) {
1603 AppendLogParameterCore(logParam);
1606 internal virtual void SendEmptyResponse() {
1610 // PInvoke callback wrappers -- overridden by the derived classes
1613 internal abstract int GetBasicsCore(byte[] buffer, int size, int[] contentInfo);
1614 internal abstract int GetQueryStringCore(int encode, StringBuilder buffer, int size);
1615 internal abstract int GetQueryStringRawBytesCore(byte[] buffer, int size);
1616 internal abstract int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead);
1617 internal abstract int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize);
1618 // ISAPIWorkerRequest
1619 internal abstract void FlushCore(byte[] status,
1623 int numBodyFragments,
1624 IntPtr[] bodyFragments,
1625 int[] bodyFragmentLengths,
1626 int doneWithSession,
1629 internal abstract int IsClientConnectedCore();
1630 internal abstract int CloseConnectionCore();
1631 internal abstract int MapUrlToPathCore(String url, byte[] buffer, int size);
1632 internal abstract IntPtr GetUserTokenCore();
1633 internal abstract IntPtr GetVirtualPathTokenCore();
1634 internal abstract int AppendLogParameterCore(String logParam);
1635 internal abstract int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates);
1636 internal abstract int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut);
1637 internal virtual void Close() {}
1641 // In-process ISAPIWorkerRequest
1643 // Does queueing of IO operations. ISAPI only support one async IO at a time.
1646 internal class ISAPIWorkerRequestInProc : ISAPIWorkerRequest {
1648 protected const int NUM_SERVER_VARIABLES = 35; // total number of variables that we commonly get
1649 protected const int NUM_BASIC_SERVER_VARIABLES = 12; // needed on every request
1650 protected const int NUM_ADDITIONAL_SERVER_VARIABLES = 23; // needed when HttpRequest.ServerVariables is populated
1652 // These constants must be kept in [....] with g_szServerVariables and g_szUnicodeServerVariables in ecbdirect.cxx
1654 protected const int LOGON_USER = 0;
1655 protected const int AUTH_TYPE = 1;
1656 protected const int APPL_PHYSICAL_PATH = 2;
1657 protected const int REQUEST_METHOD = 3;
1658 protected const int PATH_INFO = 4;
1659 protected const int PATH_TRANSLATED = 5;
1660 protected const int URL = 6;
1661 protected const int CACHE_URL = 7;
1662 protected const int SERVER_NAME = 8;
1663 protected const int SERVER_PORT = 9;
1664 protected const int HTTPS = 10;
1665 protected const int ALL_RAW = 11;
1666 protected const int REMOTE_ADDR = 12;
1667 protected const int AUTH_PASSWORD = 13;
1668 protected const int CERT_COOKIE = 14;
1669 protected const int CERT_FLAGS = 15;
1670 protected const int CERT_ISSUER = 16;
1671 protected const int CERT_KEYSIZE = 17;
1672 protected const int CERT_SECRETKEYSIZE = 18;
1673 protected const int CERT_SERIALNUMBER = 19;
1674 protected const int CERT_SERVER_ISSUER = 20;
1675 protected const int CERT_SERVER_SUBJECT = 21;
1676 protected const int CERT_SUBJECT = 22;
1677 protected const int GATEWAY_INTERFACE = 23;
1678 protected const int HTTPS_KEYSIZE = 24;
1679 protected const int HTTPS_SECRETKEYSIZE = 25;
1680 protected const int HTTPS_SERVER_ISSUER = 26;
1681 protected const int HTTPS_SERVER_SUBJECT = 27;
1682 protected const int INSTANCE_ID = 28;
1683 protected const int INSTANCE_META_PATH = 29;
1684 protected const int LOCAL_ADDR = 30;
1685 protected const int REMOTE_HOST = 31;
1686 protected const int REMOTE_PORT = 32;
1687 protected const int SERVER_PROTOCOL = 33;
1688 protected const int SERVER_SOFTWARE = 34;
1690 // storage for common server variables
1691 protected string[] _basicServerVars = null;
1692 protected string[] _additionalServerVars = null;
1693 private ChannelBinding _channelBindingToken;
1695 internal ISAPIWorkerRequestInProc(IntPtr ecb) : base(ecb) {
1696 if (ecb == IntPtr.Zero || UnsafeNativeMethods.EcbGetTraceContextId(ecb, out _traceId) != 1) {
1697 _traceId = Guid.Empty;
1701 internal override int GetBasicsCore(byte[] buffer, int size, int[] contentInfo) {
1702 if (_ecb == IntPtr.Zero)
1705 return UnsafeNativeMethods.EcbGetBasics(_ecb, buffer, size, contentInfo);
1708 internal override int GetQueryStringCore(int encode, StringBuilder buffer, int size) {
1709 if (_ecb == IntPtr.Zero)
1712 return UnsafeNativeMethods.EcbGetQueryString(_ecb, encode, buffer, size);
1715 internal override int GetQueryStringRawBytesCore(byte[] buffer, int size) {
1716 if (_ecb == IntPtr.Zero)
1719 return UnsafeNativeMethods.EcbGetQueryStringRawBytes(_ecb, buffer, size);
1722 internal override int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead) {
1723 if (_ecb == IntPtr.Zero)
1726 int rc = UnsafeNativeMethods.EcbGetPreloadedPostedContent(_ecb, bytes, offset, numBytesToRead);
1728 PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc);
1732 internal override int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize) {
1733 if (_ecb == IntPtr.Zero)
1739 // Acquire blocking call
1740 IsInReadEntitySync = true;
1742 rc = UnsafeNativeMethods.EcbGetAdditionalPostedContent(_ecb, bytes, offset, bufferSize);
1745 // Release blocking call
1746 IsInReadEntitySync = false;
1750 PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc);
1754 internal override int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates) {
1755 if (_ecb == IntPtr.Zero)
1758 return UnsafeNativeMethods.EcbGetClientCertificate(_ecb, buffer, buffer.Length, pInts, pDates);
1761 internal override int IsClientConnectedCore()
1763 if (_ecb == IntPtr.Zero)
1766 return UnsafeNativeMethods.EcbIsClientConnected(_ecb);
1769 // ISAPIWorkerRequestInProc
1770 internal override void FlushCore(byte[] status,
1774 int numBodyFragments,
1775 IntPtr[] bodyFragments,
1776 int[] bodyFragmentLengths,
1777 int doneWithSession,
1782 if (_ecb == IntPtr.Zero)
1785 UnsafeNativeMethods.EcbFlushCore(
1793 bodyFragmentLengths,
1801 internal override int CloseConnectionCore() {
1802 if (_ecb == IntPtr.Zero)
1805 return UnsafeNativeMethods.EcbCloseConnection(_ecb);
1808 internal override int MapUrlToPathCore(String url, byte[] buffer, int size) {
1809 if (_ecb == IntPtr.Zero)
1812 return UnsafeNativeMethods.EcbMapUrlToPath(_ecb, url, buffer, size);
1815 internal override IntPtr GetUserTokenCore() {
1816 if (_token == IntPtr.Zero && _ecb != IntPtr.Zero)
1817 _token = UnsafeNativeMethods.EcbGetImpersonationToken(_ecb, IntPtr.Zero);
1821 internal override IntPtr GetVirtualPathTokenCore() {
1822 if (_token == IntPtr.Zero && _ecb != IntPtr.Zero)
1823 _token = UnsafeNativeMethods.EcbGetVirtualPathToken(_ecb, IntPtr.Zero);
1828 internal override int AppendLogParameterCore(String logParam) {
1829 if (_ecb == IntPtr.Zero)
1832 return UnsafeNativeMethods.EcbAppendLogParameter(_ecb, logParam);
1835 // ISAPIWorkerRequestInProc
1836 protected virtual String GetServerVariableCore(String name) {
1837 if (_ecb == IntPtr.Zero)
1840 String value = null;
1842 RecyclableByteBuffer buf = new RecyclableByteBuffer();
1844 int retVal = UnsafeNativeMethods.EcbGetServerVariable(_ecb, name, buf.Buffer, buf.Buffer.Length);
1846 while (retVal < 0) {
1847 buf.Resize(-retVal); // buffer not big enough
1848 retVal = UnsafeNativeMethods.EcbGetServerVariable(_ecb, name, buf.Buffer, buf.Buffer.Length);
1852 value = buf.GetDecodedString(Encoding.UTF8, retVal);
1859 // ISAPIWorkerRequestInProc
1860 protected virtual void GetAdditionalServerVariables() {
1861 if (_ecb == IntPtr.Zero)
1864 // _additionalServerVars should only be initialized once
1865 Debug.Assert(_additionalServerVars == null);
1866 if (_additionalServerVars != null)
1869 _additionalServerVars = new string[NUM_ADDITIONAL_SERVER_VARIABLES];
1871 for(int i = 0; i < _additionalServerVars.Length; i++) {
1872 int nameIndex = i + NUM_BASIC_SERVER_VARIABLES;
1874 RecyclableByteBuffer buf = new RecyclableByteBuffer();
1876 int retVal = UnsafeNativeMethods.EcbGetServerVariableByIndex(_ecb, nameIndex, buf.Buffer, buf.Buffer.Length);
1878 while (retVal < 0) {
1879 buf.Resize(-retVal); // buffer not big enough
1880 retVal = UnsafeNativeMethods.EcbGetServerVariableByIndex(_ecb, nameIndex, buf.Buffer, buf.Buffer.Length);
1884 _additionalServerVars[i] = buf.GetDecodedString(Encoding.UTF8, retVal);
1890 private String GetAdditionalServerVar(int index) {
1891 if (_additionalServerVars == null)
1892 GetAdditionalServerVariables();
1894 return _additionalServerVars[index - NUM_BASIC_SERVER_VARIABLES];
1897 public override String GetServerVariable(String name) {
1898 // this switch statement is a little more than twice as fast as a Hashtable lookup
1900 switch (name.Length) {
1902 if (name == "HTTPS_SERVER_SUBJECT")
1903 return GetAdditionalServerVar(HTTPS_SERVER_SUBJECT);
1907 if (name == "HTTPS_SECRETKEYSIZE")
1908 return GetAdditionalServerVar(HTTPS_SECRETKEYSIZE);
1909 else if (name == "CERT_SERVER_SUBJECT")
1910 return GetAdditionalServerVar(CERT_SERVER_SUBJECT);
1911 else if (name == "HTTPS_SERVER_ISSUER")
1912 return GetAdditionalServerVar(HTTPS_SERVER_ISSUER);
1916 if (name == "INSTANCE_META_PATH")
1917 return GetAdditionalServerVar(INSTANCE_META_PATH);
1918 else if (name == "CERT_SECRETKEYSIZE")
1919 return GetAdditionalServerVar(CERT_SECRETKEYSIZE);
1920 else if (name == "CERT_SERVER_ISSUER")
1921 return GetAdditionalServerVar(CERT_SERVER_ISSUER);
1925 if (name == "CERT_SERIALNUMBER")
1926 return GetAdditionalServerVar(CERT_SERIALNUMBER);
1927 else if (name == "GATEWAY_INTERFACE")
1928 return GetAdditionalServerVar(GATEWAY_INTERFACE);
1932 if (name == "HTTP_USER_AGENT")
1933 return GetKnownRequestHeader(HeaderUserAgent);
1934 else if (name == "SERVER_PROTOCOL")
1935 return GetAdditionalServerVar(SERVER_PROTOCOL);
1936 else if (name == "SERVER_SOFTWARE")
1937 return GetAdditionalServerVar(SERVER_SOFTWARE);
1941 if (name == "AUTH_PASSWORD")
1942 return GetAdditionalServerVar(AUTH_PASSWORD);
1943 else if (name == "HTTPS_KEYSIZE")
1944 return GetAdditionalServerVar(HTTPS_KEYSIZE);
1948 if (name == "CERT_KEYSIZE")
1949 return GetAdditionalServerVar(CERT_KEYSIZE);
1950 else if (name == "CERT_SUBJECT")
1951 return GetAdditionalServerVar(CERT_SUBJECT);
1955 if (name == "SERVER_NAME")
1956 return _basicServerVars[SERVER_NAME];
1957 else if (name == "SERVER_PORT")
1958 return _basicServerVars[SERVER_PORT];
1959 else if (name == "REMOTE_HOST")
1960 return GetAdditionalServerVar(REMOTE_HOST);
1961 else if (name == "REMOTE_PORT")
1962 return GetAdditionalServerVar(REMOTE_PORT);
1963 else if (name == "REMOTE_ADDR")
1964 return GetAdditionalServerVar(REMOTE_ADDR);
1965 else if (name == "CERT_COOKIE")
1966 return GetAdditionalServerVar(CERT_COOKIE);
1967 else if (name == "CERT_ISSUER")
1968 return GetAdditionalServerVar(CERT_ISSUER);
1969 else if (name == "INSTANCE_ID")
1970 return GetAdditionalServerVar(INSTANCE_ID);
1974 if (name == "LOGON_USER")
1975 return _basicServerVars[LOGON_USER];
1976 else if (name == "LOCAL_ADDR")
1977 return GetAdditionalServerVar(LOCAL_ADDR);
1978 else if (name == "CERT_FLAGS")
1979 return GetAdditionalServerVar(CERT_FLAGS);
1983 if (name == "AUTH_TYPE")
1984 return _basicServerVars[AUTH_TYPE];
1988 if (name == "ALL_RAW") {
1989 return _basicServerVars[ALL_RAW];
1994 if (name == "HTTPS")
1995 return _basicServerVars[HTTPS];
2000 // this is not a common server variable
2001 return GetServerVariableCore(name);
2004 internal override int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut) {
2005 if (_ecb == IntPtr.Zero)
2008 return UnsafeNativeMethods.EcbCallISAPI(_ecb, iFunction, bufIn, bufIn.Length, bufOut, bufOut.Length);
2010 internal override void Close() {
2011 if (_channelBindingToken != null && !_channelBindingToken.IsInvalid)
2012 _channelBindingToken.Dispose();
2014 internal ChannelBinding HttpChannelBindingToken {
2016 if (_channelBindingToken == null) {
2017 IntPtr token = IntPtr.Zero;
2019 int hr = HResults.S_OK;
2021 hr = UnsafeNativeMethods.EcbGetChannelBindingToken(_ecb, out token, out tokenSize);
2022 if (hr == HResults.E_NOTIMPL)
2023 throw new PlatformNotSupportedException();
2024 Misc.ThrowIfFailedHr(hr);
2025 _channelBindingToken = new HttpChannelBindingToken(token, tokenSize);
2027 return _channelBindingToken;
2033 // In-process ISAPIWorkerRequest specific for IIS7
2035 // Uses unicode server vars
2038 internal class ISAPIWorkerRequestInProcForIIS7 : ISAPIWorkerRequestInProcForIIS6 {
2040 internal ISAPIWorkerRequestInProcForIIS7(IntPtr ecb) : base(ecb) {
2041 _trySkipIisCustomErrors = true;
2044 internal override bool TrySkipIisCustomErrors {
2045 get { return _trySkipIisCustomErrors; }
2046 set { _trySkipIisCustomErrors = value; }
2049 internal override void RaiseTraceEvent(IntegratedTraceType traceType, string eventData) {
2050 if (IntPtr.Zero != _ecb) {
2051 // the area is derivative of the type, either page or module
2052 int areaFlag = (traceType < IntegratedTraceType.DiagCritical) ? EtwTraceFlags.Page : EtwTraceFlags.Module;
2053 if (EtwTrace.IsTraceEnabled(EtwTrace.InferVerbosity(traceType), areaFlag)) {
2054 string message = String.IsNullOrEmpty(eventData) ? String.Empty : eventData;
2055 UnsafeNativeMethods.EcbEmitSimpleTrace(_ecb, (int)traceType, message);
2060 internal override void RaiseTraceEvent(WebBaseEvent webEvent) {
2061 if (IntPtr.Zero != _ecb) {
2062 if (EtwTrace.IsTraceEnabled(webEvent.InferEtwTraceVerbosity(), EtwTraceFlags.Infrastructure)) {
2064 string[] fieldNames;
2068 webEvent.DeconstructWebEvent(out webEventType, out fieldCount, out fieldNames, out fieldTypes, out fieldData);
2069 UnsafeNativeMethods.EcbEmitWebEventTrace(_ecb, webEventType, fieldCount, fieldNames, fieldTypes, fieldData);
2076 // In-process ISAPIWorkerRequest specific for IIS6
2078 // Uses unicode server vars
2081 internal class ISAPIWorkerRequestInProcForIIS6 : ISAPIWorkerRequestInProc {
2083 private static int _asyncIoCount;
2084 private bool _disconnected;
2086 internal ISAPIWorkerRequestInProcForIIS6(IntPtr ecb) : base(ecb) {
2089 internal static void WaitForPendingAsyncIo() {
2090 while(_asyncIoCount != 0) {
2095 internal override void SendEmptyResponse() {
2096 // facilitate health monitoring for IIS6 -- update last activity timestamp
2097 // to avoid deadlock detection
2098 UnsafeNativeMethods.UpdateLastActivityTimeForHealthMonitor();
2101 public override String GetRawUrl() {
2102 // CACHE_URL is the original URI, unaffected by any rewriting or routing
2103 // that may have occurred on the server
2104 string rawUrl = GetRawUrlHelper(GetUnicodeServerVariable(CACHE_URL));
2105 Debug.Trace("ClientUrl", "*** GetRawUrl --> " + rawUrl + " ***");
2109 internal override void ReadRequestBasics() {
2110 if (_ecb == IntPtr.Zero)
2112 // set server variables needed for request basics and Indigo (VSWhidbey 352117,344580)
2113 GetBasicServerVariables();
2115 // _pathInfo is the difference between UNICODE_PATH_INFO and UNICODE_URL
2116 int lengthDiff = _path.Length - _filePath.Length;
2117 if (lengthDiff > 0) {
2118 _pathInfo = _path.Substring(_filePath.Length);
2119 int pathTranslatedLength = _pathTranslated.Length - lengthDiff;
2120 if (pathTranslatedLength > 0) {
2121 _pathTranslated = _pathTranslated.Substring(0, pathTranslatedLength);
2126 _pathInfo = String.Empty;
2129 _appPath = HostingEnvironment.ApplicationVirtualPath;
2132 // other (int) request basics
2135 int[] contentInfo = null;
2138 contentInfo = RecyclableArrayHelper.GetIntegerArray(4);
2139 UnsafeNativeMethods.EcbGetBasicsContentInfo(_ecb, contentInfo);
2141 _contentType = contentInfo[0];
2142 _contentTotalLength = contentInfo[1];
2143 _contentAvailLength = contentInfo[2];
2144 _queryStringLength = contentInfo[3];
2147 RecyclableArrayHelper.ReuseIntegerArray(contentInfo);
2151 private void GetBasicServerVariables() {
2153 if (_ecb == IntPtr.Zero)
2155 // _basicServerVars should only be initialized once
2156 Debug.Assert(_basicServerVars == null);
2157 if (_basicServerVars != null)
2160 _basicServerVars = new string[NUM_BASIC_SERVER_VARIABLES];
2162 ServerVarCharBuffer buffer = new ServerVarCharBuffer();
2165 int[] serverVarLengths = new int[NUM_BASIC_SERVER_VARIABLES];
2167 int hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length,
2168 serverVarLengths, serverVarLengths.Length, 0, ref r);
2169 if (r > buffer.Length)
2172 hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length,
2173 serverVarLengths, serverVarLengths.Length, 0, ref r);
2176 Misc.ThrowIfFailedHr(hresult);
2178 IntPtr current = buffer.PinnedAddress;
2180 for(int i = 0; i < _basicServerVars.Length; i++) {
2181 _basicServerVars[i] = Marshal.PtrToStringUni(current, serverVarLengths[i]);
2182 current = new IntPtr((long)current + 2L * (1L + serverVarLengths[i]));
2184 // special case variables
2185 _appPathTranslated = _basicServerVars[APPL_PHYSICAL_PATH];
2186 _method = _basicServerVars[REQUEST_METHOD];
2187 _path = _basicServerVars[PATH_INFO];
2188 _pathTranslated = _basicServerVars[PATH_TRANSLATED];
2189 _filePath = _basicServerVars[URL];
2196 // ISAPIWorkerRequestInProcForIIS6
2197 protected override void GetAdditionalServerVariables() {
2198 if (_ecb == IntPtr.Zero)
2201 // _additionalServerVars should only be initialized once
2202 Debug.Assert(_additionalServerVars == null);
2203 if (_additionalServerVars != null)
2206 _additionalServerVars = new string[NUM_ADDITIONAL_SERVER_VARIABLES];
2208 ServerVarCharBuffer buffer = new ServerVarCharBuffer();
2211 int[] serverVarLengths = new int[NUM_ADDITIONAL_SERVER_VARIABLES];
2213 int hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length,
2214 serverVarLengths, serverVarLengths.Length, NUM_BASIC_SERVER_VARIABLES, ref r);
2215 if (r > buffer.Length) {
2217 hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length,
2218 serverVarLengths, serverVarLengths.Length, NUM_BASIC_SERVER_VARIABLES, ref r);
2221 Marshal.ThrowExceptionForHR(hresult);
2222 IntPtr current = buffer.PinnedAddress;
2224 for(int i = 0; i < _additionalServerVars.Length; i++) {
2225 _additionalServerVars[i] = Marshal.PtrToStringUni(current, serverVarLengths[i]);
2226 current = new IntPtr((long)current + 2L * (1L + serverVarLengths[i]));
2234 // ISAPIWorkerRequestInProcForIIS6
2235 protected override string GetServerVariableCore(string name) {
2236 if (StringUtil.StringStartsWith(name, "HTTP_"))
2237 // fall back for headers (IIS6 doesn't support them as UNICODE_XXX)
2238 return base.GetServerVariableCore(name);
2240 return GetUnicodeServerVariable("UNICODE_" + name);
2243 private String GetUnicodeServerVariable(String name) {
2244 String value = null;
2245 ServerVarCharBuffer buf = new ServerVarCharBuffer();
2248 value = GetUnicodeServerVariable(name, buf);
2257 private String GetUnicodeServerVariable(int nameIndex) {
2258 String value = null;
2259 ServerVarCharBuffer buf = new ServerVarCharBuffer();
2262 value = GetUnicodeServerVariable(nameIndex, buf);
2271 private String GetUnicodeServerVariable(String name, ServerVarCharBuffer buffer) {
2272 if (_ecb == IntPtr.Zero)
2274 int r = UnsafeNativeMethods.EcbGetUnicodeServerVariable(_ecb, name, buffer.PinnedAddress, buffer.Length);
2278 r = UnsafeNativeMethods.EcbGetUnicodeServerVariable(_ecb, name, buffer.PinnedAddress, buffer.Length);
2282 return Marshal.PtrToStringUni(buffer.PinnedAddress, r);
2287 private String GetUnicodeServerVariable(int nameIndex, ServerVarCharBuffer buffer) {
2288 if (_ecb == IntPtr.Zero)
2290 int r = UnsafeNativeMethods.EcbGetUnicodeServerVariableByIndex(_ecb, nameIndex, buffer.PinnedAddress, buffer.Length);
2294 r = UnsafeNativeMethods.EcbGetUnicodeServerVariableByIndex(_ecb, nameIndex, buffer.PinnedAddress, buffer.Length);
2298 return Marshal.PtrToStringUni(buffer.PinnedAddress, r);
2304 // Support for async VectorSend and kernel mode cache on IIS6
2307 private const int MIN_ASYNC_SIZE = 2048;
2308 private GCHandle _rootedThis; // for the duration of async
2309 private ISAPIAsyncCompletionCallback _asyncFlushCompletionCallback;
2310 private int _asyncFinalStatus;
2311 private bool _serverSupportFunctionError = false;
2312 private IntPtr _entity; // pointer to HSE_entity
2314 private bool _cacheInKernelMode = false;
2315 private bool _disableKernelCache = false;
2316 protected bool _trySkipIisCustomErrors = false;
2317 private const int TRY_SKIP_IIS_CUSTOM_ERRORS = 0x40;
2319 // PackageFile for IIS6
2320 internal override MemoryBytes PackageFile(string filename, long offset, long size, bool isImpersonating) {
2321 return new MemoryBytes(filename, offset, size);
2324 // VSWhidbey 555203: support 64-bit file sizes for TransmitFile on IIS6
2325 internal override bool SupportsLongTransmitFile {
2326 get { return true; }
2329 // ISAPIWorkerRequestInProcForIIS6
2330 internal override void FlushCore(byte[] status,
2334 int numBodyFragments,
2335 IntPtr[] bodyFragments,
2336 int[] bodyFragmentLengths,
2337 int doneWithSession,
2342 if (_ecb == IntPtr.Zero)
2345 if (_headersSentFromExecuteUrl) {
2346 // don't send headers twice
2351 bool inAsyncFlush = false;
2353 // async only for large responses and only on the last flush or if inAsyncFlush is true
2354 // don't do async if shutting down (async IO holds up app domain shutdown)
2355 if (doneWithSession != 0 && !HttpRuntime.ShutdownInProgress && (_ignoreMinAsyncSize || (totalBodySize >= MIN_ASYNC_SIZE))) {
2356 if (_requiresAsyncFlushCallback) {
2357 _asyncFlushCompletionCallback = new ISAPIAsyncCompletionCallback(OnAsyncFlushCompletion);
2358 _asyncFinalStatus = finalStatus; // remember to pass to DoneWithSession on completion
2359 _rootedThis = GCHandle.Alloc(this); // root for the duration of IO
2360 doneWithSession = 0; // will do on completion
2362 Interlocked.Increment(ref _asyncIoCount); // increment async io count
2365 // buffers are native, so we don't need to return to managed code
2366 _asyncFlushCompletionCallback = null;
2367 doneWithSession = 0; // will do on completion
2372 inAsyncFlush = (_asyncResultBase is FlushAsyncResult);
2374 _requiresAsyncFlushCallback = true;
2375 _asyncFlushCompletionCallback = new ISAPIAsyncCompletionCallback(OnAsyncFlushCompletion);
2376 _asyncFinalStatus = finalStatus; // remember to pass to DoneWithSession on completion
2377 _rootedThis = GCHandle.Alloc(this); // root for the duration of IO
2379 Interlocked.Increment(ref _asyncIoCount); // increment async io count
2383 // finalStatus is either 0 to force for a flush, 1 to indicate HSE_STATUS_SUCCESS, or 2 to indicate HSE_STATUS_SUCCESS_AND_KEEP_CONN
2384 Debug.Assert(0 <= finalStatus && finalStatus <= 2);
2385 int flags = _trySkipIisCustomErrors ? finalStatus|TRY_SKIP_IIS_CUSTOM_ERRORS : finalStatus;
2387 int rc = UnsafeNativeMethods.EcbFlushCore(
2395 bodyFragmentLengths,
2398 _cacheInKernelMode ? 1 : 0,
2400 _asyncFlushCompletionCallback);
2402 if (!_requiresAsyncFlushCallback && rc == 0 && async) {
2404 // unlock and reset cached response
2405 UnlockCachedResponseBytesOnceAfterIoComplete();
2407 CallEndOfRequestCallbackOnceAfterAllIoComplete();
2409 else if (rc != 0 && async) {
2410 // on async failure default to [....] path
2413 if (!inAsyncFlush) {
2414 // call DoneWithSession
2415 UnsafeNativeMethods.EcbFlushCore(_ecb, null, null, 0, 0, 0, null, null, 1, _asyncFinalStatus, 0, 0, null);
2418 if (_asyncFlushCompletionCallback != null) {
2422 // decrement async io count
2423 Interlocked.Decrement(ref _asyncIoCount);
2427 _asyncResultBase = null;
2428 // treat every error as if the client disconnected
2429 IncrementRequestsDisconnected();
2430 throw new HttpException(SR.GetString(SR.ClientDisconnected), rc);
2433 else if (rc != 0 && !async && doneWithSession == 0 && !_serverSupportFunctionError) {
2434 //on non-async failure stop executing the request
2437 _serverSupportFunctionError = true;
2439 string message = SR.Server_Support_Function_Error;
2441 //give different error if connection was closed
2442 if (rc == HResults.WSAECONNABORTED || rc == HResults.WSAECONNRESET) {
2443 message = SR.Server_Support_Function_Error_Disconnect;
2444 IncrementRequestsDisconnected();
2447 throw new HttpException(SR.GetString(message, rc.ToString("X8", CultureInfo.InvariantCulture)), rc);
2451 private void OnAsyncFlushCompletion(IntPtr ecb, int byteCount, int error) {
2453 FlushAsyncResult flushAsyncResult = _asyncResultBase as FlushAsyncResult;
2458 if (flushAsyncResult == null) {
2459 // call DoneWithSession
2460 UnsafeNativeMethods.EcbFlushCore(ecb, null, null, 0, 0, 0, null, null, 1, _asyncFinalStatus, 0, 0, null);
2463 flushAsyncResult.HResult = error;
2466 // unlock pinned memory (at the latest of this completion and exit from the FlushCore on stack)
2467 UnlockCachedResponseBytesOnceAfterIoComplete();
2469 // Revert any impersonation set by IIS
2470 UnsafeNativeMethods.RevertToSelf();
2472 if (flushAsyncResult == null) {
2473 // call the HttpRuntime to recycle buffers (at the latest of this completion and EndRequest)
2474 CallEndOfRequestCallbackOnceAfterAllIoComplete();
2478 // decrement async io count
2479 Interlocked.Decrement(ref _asyncIoCount);
2483 // WOS 1555777: kernel cache support
2484 // If the response can be kernel cached, return the kernel cache key;
2485 // otherwise return null. The kernel cache key is used to invalidate
2486 // the entry if a dependency changes or the item is flushed from the
2487 // managed cache for any reason.
2488 internal override string SetupKernelCaching(int secondsToLive, string originalCacheUrl, bool enableKernelCacheForVaryByStar) {
2489 // if someone called DisableKernelCache, don't setup kernel caching
2490 if (_ecb == IntPtr.Zero || _disableKernelCache)
2493 string cacheUrl = GetUnicodeServerVariable(CACHE_URL);
2495 // if we're re-inserting the response into the kernel cache, the original key must match
2496 if (originalCacheUrl != null && originalCacheUrl != cacheUrl) {
2500 // If the request contains a query string, don't kernel cache the entry
2501 if (String.IsNullOrEmpty(cacheUrl) || (!enableKernelCacheForVaryByStar && cacheUrl.IndexOf('?') != -1)) {
2505 // enable kernel caching (IIS will set the HTTP_CACHE_POLICY when we call VectorSend)
2506 _cacheInKernelMode = true;
2508 // okay, the response will be kernel cached, here's the key
2512 // WOS 1555777: kernel cache support
2513 internal override void DisableKernelCache() {
2514 _disableKernelCache = true;
2515 _cacheInKernelMode = false;
2519 // Async Flush support
2522 public override bool SupportsAsyncFlush { get { return true; } }
2524 // Begin an asynchronous flush of the response. To support this, the worker request buffers
2525 // the status, headers, and resonse body until an asynchronous flush operation is initiated.
2526 public override IAsyncResult BeginFlush(AsyncCallback callback, Object state) {
2527 if (_ecb == IntPtr.Zero) {
2528 throw new InvalidOperationException();
2531 FlushAsyncResult ar = new FlushAsyncResult(callback, state);
2533 // we only allow one async operation at a time
2534 if (Interlocked.CompareExchange(ref _asyncResultBase, ar, null) != null)
2535 throw new InvalidOperationException(SR.GetString(SR.Async_operation_pending));
2537 // initiate async operation here
2538 if (_asyncCompletionCallback == null) {
2539 _asyncCompletionCallback = new AsyncCompletionCallback(OnAsyncCompletion);
2543 ar.MarkCallToBeginMethodStarted();
2544 FlushResponse(finalFlush: false);
2547 ar.MarkCallToBeginMethodCompleted();
2553 // Finish an asynchronous flush.
2554 public override void EndFlush(IAsyncResult asyncResult) {
2555 if (asyncResult == null)
2556 throw new ArgumentNullException("asyncResult");
2557 FlushAsyncResult ar = asyncResult as FlushAsyncResult;
2559 throw new ArgumentException(null, "asyncResult");
2561 ar.ReleaseWaitHandleWhenSignaled();
2562 if (ar.HResult < 0) {
2563 // treat every error as if the client disconnected
2564 IncrementRequestsDisconnected();
2565 throw new HttpException(SR.GetString(SR.ClientDisconnected), ar.HResult);
2571 // Async Read support
2574 public override bool SupportsAsyncRead { get { return true; } }
2576 internal void OnAsyncCompletion(int bytesCompleted, int hresult, IntPtr pAsyncCompletionContext) {
2577 if (_asyncResultBase is ReadAsyncResult)
2579 // clear _asyncResultBase because when we call Complete the user's callback
2580 // may begin another async operation
2581 AsyncResultBase ar = _asyncResultBase;
2582 _asyncResultBase = null;
2583 ar.Complete(bytesCompleted, hresult, pAsyncCompletionContext, synchronous: false);
2586 // Begin an asynchronous read of the request entity body. To read the entire entity, invoke
2587 // repeatedly until total bytes read is equal to Request.ContentLength or EndRead indicates
2588 // that zero bytes were read. If Request.ContentLength is zero and the request is chunked,
2589 // then invoke repeatedly until EndRead indicates that zero bytes were read.
2591 // If an error occurs and the client is no longer connected, an HttpException will be thrown.
2593 // This implements Stream.BeginRead, and as such, should throw
2594 // exceptions as described on MSDN when errors occur.
2595 public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) {
2596 // Do as Stream does.
2598 throw new ArgumentNullException("buffer");
2600 throw new ArgumentOutOfRangeException("offset");
2602 throw new ArgumentOutOfRangeException("count");
2603 if (buffer.Length - offset < count)
2604 throw new ArgumentException(SR.GetString(SR.InvalidOffsetOrCount, "offset", "count"));
2605 if (_ecb == IntPtr.Zero)
2606 throw new InvalidOperationException();
2608 ReadAsyncResult ar = new ReadAsyncResult(callback, state, buffer, offset, count, updatePerfCounter: false);
2611 ar.Complete(0, HResults.S_OK, IntPtr.Zero, synchronous: true);
2615 // we only allow one async operation at a time
2616 if (Interlocked.CompareExchange(ref _asyncResultBase, ar, null) != null)
2617 throw new InvalidOperationException(SR.GetString(SR.Async_operation_pending));
2619 // initiate async operation here
2620 if (_asyncCompletionCallback == null) {
2621 _asyncCompletionCallback = new AsyncCompletionCallback(OnAsyncCompletion);
2623 _rootedThis = GCHandle.Alloc(this); // root for duration of async operation
2627 ar.MarkCallToBeginMethodStarted();
2628 hresult = UnsafeNativeMethods.EcbReadClientAsync(_ecb,
2630 _asyncCompletionCallback);
2633 ar.MarkCallToBeginMethodCompleted();
2638 _asyncResultBase = null;
2639 // treat every error as if the client disconnected
2640 IncrementRequestsDisconnected();
2641 throw new HttpException(SR.GetString(SR.ClientDisconnected), hresult);
2648 // Finish an asynchronous read. When this returns zero there is no more to be read. If Request.ContentLength is non-zero,
2649 // do not read more bytes then specified by ContentLength, or an error will occur.
2650 // This implements Stream.EndRead on HttpBufferlessInputStream, and as such, should throw
2651 // exceptions as described on MSDN when errors occur.
2652 public override int EndRead(IAsyncResult asyncResult) {
2653 if (asyncResult == null)
2654 throw new ArgumentNullException("asyncResult");
2655 ReadAsyncResult ar = asyncResult as ReadAsyncResult;
2657 throw new ArgumentException(null, "asyncResult");
2658 ar.ReleaseWaitHandleWhenSignaled();
2659 if (ar.HResult < 0) {
2660 // treat every error as if the client disconnected
2661 IncrementRequestsDisconnected();
2662 throw new HttpException(SR.GetString(SR.ClientDisconnected), ar.HResult);
2665 return ar.BytesRead;
2669 private void IncrementRequestsDisconnected() {
2670 if (!_disconnected) {
2671 PerfCounters.IncrementGlobalCounter(GlobalPerfCounter.REQUESTS_DISCONNECTED);
2672 _disconnected = true;
2677 // ExecuteUrl support
2680 private ISAPIAsyncCompletionCallback _executeUrlCompletionCallback;
2681 private HttpAsyncResult _asyncResultOfExecuteUrl;
2682 private bool _headersSentFromExecuteUrl;
2684 internal override bool SupportsExecuteUrl {
2685 get { return true; }
2688 internal override IAsyncResult BeginExecuteUrl(
2689 String url, String method, String childHeaders,
2691 bool addUserIndo, IntPtr token, String name, String authType,
2693 AsyncCallback cb, Object state) {
2695 if (_ecb == IntPtr.Zero || // after done with session
2696 _asyncResultOfExecuteUrl != null || // another ExecuteUrl in progress
2697 (sendHeaders && HeadersSent())) // asked to send headers, but already sent them
2699 throw new InvalidOperationException(SR.GetString(SR.Cannot_execute_url_in_this_context));
2702 if (entity != null && entity.Length > 0) {
2703 int ret = UnsafeNativeMethods.EcbGetExecUrlEntityInfo(entity.Length, entity, out _entity);
2705 throw new HttpException(SR.GetString(SR.Failed_to_execute_url));
2709 Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl: url=\"" + url + "\".");
2712 HttpAsyncResult ar = new HttpAsyncResult(cb, state);
2713 _asyncResultOfExecuteUrl = ar;
2715 _executeUrlCompletionCallback = new ISAPIAsyncCompletionCallback(OnExecuteUrlCompletion);
2716 _rootedThis = GCHandle.Alloc(this); // root for the duration of ExecuteUrl
2720 ar.MarkCallToBeginMethodStarted();
2721 rc = UnsafeNativeMethods.EcbExecuteUrlUnicode(_ecb,
2722 url, method, childHeaders,
2724 addUserIndo, token, name, authType,
2726 _executeUrlCompletionCallback);
2729 ar.MarkCallToBeginMethodCompleted();
2733 if (_entity != IntPtr.Zero) {
2734 UnsafeNativeMethods.EcbFreeExecUrlEntityInfo(_entity);
2737 _asyncResultOfExecuteUrl = null;
2739 Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl: failed!");
2741 throw new HttpException(SR.GetString(SR.Failed_to_execute_url));
2745 // ExecuteUrl will send headers, worker request should not
2746 _headersSentFromExecuteUrl = true;
2752 internal override void EndExecuteUrl(IAsyncResult result) {
2754 Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.EndExecuteUrl");
2756 HttpAsyncResult asyncResult = result as HttpAsyncResult;
2757 if (asyncResult != null) {
2762 private void OnExecuteUrlCompletion(IntPtr ecb, int byteCount, int error) {
2763 if (_entity != IntPtr.Zero) {
2764 UnsafeNativeMethods.EcbFreeExecUrlEntityInfo(_entity);
2769 Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.OnExecuteUrlCompletion");
2771 // signal async caller to resume work
2772 HttpAsyncResult asyncResult = _asyncResultOfExecuteUrl;
2773 _asyncResultOfExecuteUrl = null;
2774 asyncResult.Complete(false, null, null);
2779 // Out-of-process worker request
2782 internal class ISAPIWorkerRequestOutOfProc : ISAPIWorkerRequest {
2784 // sends chunks separately if the total length exceeds the following
2785 // to relieve the memory pressure on named pipes
2786 const int PM_FLUSH_THRESHOLD = 31*1024;
2788 internal ISAPIWorkerRequestOutOfProc(IntPtr ecb) : base(ecb) {
2789 UnsafeNativeMethods.PMGetTraceContextId(ecb, out _traceId);
2792 private bool _useBaseTime = false;
2793 private const int _numServerVars = 32;
2794 private IDictionary _serverVars;
2796 private static String[] _serverVarNames =
2797 new String[_numServerVars] {
2798 "APPL_MD_PATH", /* this one is not UTF8 so we don't decode it here */
2806 "CERT_SECRETKEYSIZE",
2807 "CERT_SERIALNUMBER",
2808 "CERT_SERVER_ISSUER",
2809 "CERT_SERVER_SUBJECT",
2811 "GATEWAY_INTERFACE",
2816 "HTTPS_SECRETKEYSIZE",
2817 "HTTPS_SERVER_ISSUER",
2818 "HTTPS_SERVER_SUBJECT",
2820 "INSTANCE_META_PATH",
2833 private void GetAllServerVars() {
2834 if (_ecb == IntPtr.Zero)
2836 RecyclableByteBuffer buf = new RecyclableByteBuffer();
2838 int r = UnsafeNativeMethods.PMGetAllServerVariables(_ecb, buf.Buffer, buf.Buffer.Length);
2841 buf.Resize(-r); // buffer not big enough
2842 r = UnsafeNativeMethods.PMGetAllServerVariables(_ecb, buf.Buffer, buf.Buffer.Length);
2846 throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data));
2848 // stub out first server var is it could contain non-UTF8 data
2849 // convert to characters and split the buffer into strings using default request encoding
2851 String[] ss = buf.GetDecodedTabSeparatedStrings(Encoding.Default, _numServerVars-1, 1);
2857 // fill in the hashtable
2859 _serverVars = new Hashtable(_numServerVars, StringComparer.OrdinalIgnoreCase);
2861 _serverVars.Add("APPL_MD_PATH", HttpRuntime.AppDomainAppId);
2863 for (int i = 1; i < _numServerVars; i++) { // starting with 1 to skip APPL_MD_PATH
2864 _serverVars.Add(_serverVarNames[i], ss[i-1]);
2869 internal override int GetBasicsCore(byte[] buffer, int size, int[] contentInfo) {
2870 if (_ecb == IntPtr.Zero)
2873 return UnsafeNativeMethods.PMGetBasics(_ecb, buffer, size, contentInfo);
2876 internal override int GetQueryStringCore(int encode, StringBuilder buffer, int size) {
2877 if (_ecb == IntPtr.Zero)
2880 return UnsafeNativeMethods.PMGetQueryString(_ecb, encode, buffer, size);
2883 internal override int GetQueryStringRawBytesCore(byte[] buffer, int size) {
2884 if (_ecb == IntPtr.Zero)
2887 return UnsafeNativeMethods.PMGetQueryStringRawBytes(_ecb, buffer, size);
2890 internal override int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead) {
2891 if (_ecb == IntPtr.Zero)
2893 int rc = UnsafeNativeMethods.PMGetPreloadedPostedContent(_ecb, bytes, offset, numBytesToRead);
2895 PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc);
2899 internal override int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize) {
2900 if (_ecb == IntPtr.Zero)
2902 int rc = UnsafeNativeMethods.PMGetAdditionalPostedContent(_ecb, bytes, offset, bufferSize);
2904 PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc);
2908 internal override int IsClientConnectedCore() {
2909 if (_ecb == IntPtr.Zero)
2912 return UnsafeNativeMethods.PMIsClientConnected(_ecb);
2915 // PackageFile for IIS5
2916 internal override MemoryBytes PackageFile(string filename, long offset64, long length64, bool isImpersonating) {
2917 // The offset and length must be less than Int32.MaxValue for IIS5.
2918 // This should be true, since HttpFileResponseElement.ctor throws ArgumentOutOfRangeException for IIS5.
2919 Debug.Assert(offset64 < Int32.MaxValue);
2920 Debug.Assert(length64 < Int32.MaxValue);
2921 int offset = Convert.ToInt32(offset64);
2922 int length = Convert.ToInt32(length64);
2924 byte[] offsetBytes = BitConverter.GetBytes(offset);
2925 byte[] lengthBytes = BitConverter.GetBytes(length);
2926 byte[] nameBytes = Encoding.Unicode.GetBytes(filename);
2927 // buffer consists of 1 byte for impersonation flag, 3 bytes for alignment,
2928 // 4 bytes for offset, 4 bytes for length, n bytes for file name, 2 bytes for null terminator
2929 byte[] buffer = new byte[4 + offsetBytes.Length + lengthBytes.Length + nameBytes.Length + 2];
2931 // first byte indicates whether impersonation is used
2932 if (isImpersonating)
2937 // bytes 2 thru 4 are unused for alignment
2938 // bytes 5 thru 8 are the offset
2939 Buffer.BlockCopy(offsetBytes, 0, buffer, 4, offsetBytes.Length);
2940 // bytes 9 thru 12 are the length
2941 Buffer.BlockCopy(lengthBytes, 0, buffer, 4 + offsetBytes.Length, lengthBytes.Length);
2942 // last two bytes are 0 for null terminator
2943 Buffer.BlockCopy(nameBytes, 0, buffer, 4 + offsetBytes.Length + lengthBytes.Length, nameBytes.Length);
2945 return new MemoryBytes(buffer, buffer.Length, true, length);
2948 // ISAPIWorkerRequestOutOfProc
2949 internal override void FlushCore(byte[] status,
2953 int numBodyFragments,
2954 IntPtr[] bodyFragments,
2955 int[] bodyFragmentLengths,
2956 int doneWithSession,
2961 if (_ecb == IntPtr.Zero)
2965 if (numBodyFragments > 1) {
2966 // Don't flush all at once if the length is over the threshold
2969 while (i < numBodyFragments) {
2970 bool first = (i == 0);
2972 int size = bodyFragmentLengths[i];
2973 bool useTransmitFile = (bodyFragmentLengths[i] < 0);
2975 if (!useTransmitFile) {
2976 while (idx < numBodyFragments
2977 && size + bodyFragmentLengths[idx] < PM_FLUSH_THRESHOLD
2978 && bodyFragmentLengths[idx] >= 0) {
2979 size += bodyFragmentLengths[idx];
2984 bool last = (idx == numBodyFragments);
2986 // bodyFragmentLength is negative for TransmitFile, but totalBodySize argument must be positive.
2987 if (useTransmitFile)
2990 UnsafeNativeMethods.PMFlushCore(
2992 first ? status : null,
2993 first ? header : null,
2999 bodyFragmentLengths,
3000 last ? doneWithSession : 0,
3001 last ? finalStatus : 0);
3007 // Everything in one chunk
3008 UnsafeNativeMethods.PMFlushCore(
3017 bodyFragmentLengths,
3023 internal override int CloseConnectionCore() {
3024 if (_ecb == IntPtr.Zero)
3027 return UnsafeNativeMethods.PMCloseConnection(_ecb);
3030 internal override int MapUrlToPathCore(String url, byte[] buffer, int size) {
3031 if (_ecb == IntPtr.Zero)
3034 return UnsafeNativeMethods.PMMapUrlToPath(_ecb, url, buffer, size);
3037 internal override IntPtr GetUserTokenCore() {
3038 if (_token == IntPtr.Zero && _ecb != IntPtr.Zero)
3039 _token = UnsafeNativeMethods.PMGetImpersonationToken(_ecb);
3044 internal override IntPtr GetVirtualPathTokenCore() {
3045 if (_token == IntPtr.Zero && _ecb != IntPtr.Zero)
3046 _token = UnsafeNativeMethods.PMGetVirtualPathToken(_ecb);
3051 internal override int AppendLogParameterCore(String logParam) {
3052 if (_ecb == IntPtr.Zero)
3055 return UnsafeNativeMethods.PMAppendLogParameter(_ecb, logParam);
3058 internal override int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates) {
3059 if (_ecb == IntPtr.Zero)
3062 return UnsafeNativeMethods.PMGetClientCertificate(_ecb, buffer, buffer.Length, pInts, pDates);
3065 public override String GetServerVariable(String name) {
3066 // PATH_TRANSLATED is mangled -- do not use the original server variable
3067 if (name.Equals("PATH_TRANSLATED"))
3068 return GetFilePathTranslated();
3070 if (_serverVars == null)
3073 return (String)_serverVars[name];
3076 internal override int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut) {
3077 if (_ecb == IntPtr.Zero)
3080 return UnsafeNativeMethods.PMCallISAPI(_ecb, iFunction, bufIn, bufIn.Length, bufOut, bufOut.Length);
3083 internal override void SendEmptyResponse() {
3084 if (_ecb == IntPtr.Zero)
3087 UnsafeNativeMethods.PMEmptyResponse(_ecb);
3090 internal override DateTime GetStartTime() {
3091 if (_ecb == IntPtr.Zero || _useBaseTime)
3092 return base.GetStartTime();
3094 long fileTime = UnsafeNativeMethods.PMGetStartTimeStamp(_ecb);
3096 return DateTimeUtil.FromFileTimeToUtc(fileTime);
3099 internal override void ResetStartTime() {
3100 base.ResetStartTime();
3101 _useBaseTime = true;