Code:
/ FXUpdate3074 / FXUpdate3074 / 1.1 / DEVDIV / depot / DevDiv / releases / whidbey / QFE / ndp / fx / src / xsp / System / Web / Hosting / ISAPIWorkerRequest.cs / 12 / ISAPIWorkerRequest.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Web.Hosting { using System.Text; using System.Configuration.Assemblies; using System.Runtime.InteropServices; using System.Collections; using System.Collections.Specialized; using System.IO; using System.Globalization; using System.Threading; using Microsoft.Win32; using System.Web; using System.Web.Management; using System.Web.Util; using System.Web.Configuration; using System.Web.Caching; // // recyclable buffers for IntPtr[] and int[] // to avoid pinning gen0 // internal class RecyclableArrayHelper { private const int ARRAY_SIZE = 128; private const int MAX_FREE_ARRAYS = 64; private static IntegerArrayAllocator s_IntegerArrayAllocator; private static IntPtrArrayAllocator s_IntPtrArrayAllocator; static RecyclableArrayHelper() { s_IntegerArrayAllocator = new IntegerArrayAllocator(ARRAY_SIZE, MAX_FREE_ARRAYS); s_IntPtrArrayAllocator = new IntPtrArrayAllocator(ARRAY_SIZE, MAX_FREE_ARRAYS); } internal static int[] GetIntegerArray(int minimumLength) { if( minimumLength <= ARRAY_SIZE ) return(int[])s_IntegerArrayAllocator.GetBuffer(); else return new int[minimumLength]; } internal static IntPtr[] GetIntPtrArray(int minimumLength) { if( minimumLength <= ARRAY_SIZE ) return(IntPtr[])s_IntPtrArrayAllocator.GetBuffer(); else return new IntPtr[minimumLength]; } internal static void ReuseIntegerArray(int[] array) { if (array != null && array.Length == ARRAY_SIZE) s_IntegerArrayAllocator.ReuseBuffer(array); } internal static void ReuseIntPtrArray(IntPtr[] array) { if (array != null && array.Length == ARRAY_SIZE) s_IntPtrArrayAllocator.ReuseBuffer(array); } } // // char[] appendable buffer. Recyclable up to 1K // Also encapsulates encoding (using utf-8) into recyclable byte[] buffer. // // Usage: // new RecyclableCharBuffer // Append // ... // GetEncodedBytesBuffer // Dispose // internal class RecyclableCharBuffer { private const int BUFFER_SIZE = 1024; private const int MAX_FREE_BUFFERS = 64; private static CharBufferAllocator s_CharBufferAllocator; private static UbyteBufferAllocator s_ByteBufferAllocator; private char[] _charBuffer; private int _size; private int _freePos; private bool _recyclable; private byte[] _byteBuffer; static RecyclableCharBuffer() { s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS); s_ByteBufferAllocator = new UbyteBufferAllocator(Encoding.UTF8.GetMaxByteCount(BUFFER_SIZE), MAX_FREE_BUFFERS); } internal RecyclableCharBuffer() { _charBuffer = (char[])s_CharBufferAllocator.GetBuffer(); _size = _charBuffer.Length; _freePos = 0; _recyclable = true; } internal void Dispose() { if (_recyclable) { if (_charBuffer != null) s_CharBufferAllocator.ReuseBuffer(_charBuffer); if (_byteBuffer != null) s_ByteBufferAllocator.ReuseBuffer(_byteBuffer); } _charBuffer = null; _byteBuffer = null; } private void Grow(int newSize) { if (newSize <= _size) return; if (newSize < _size*2) newSize = _size*2; char[] newBuffer = new char[newSize]; if (_freePos > 0) Array.Copy(_charBuffer, newBuffer, _freePos); _charBuffer = newBuffer; _size = newSize; _recyclable = false; } internal void Append(char ch) { if (_freePos >= _size) Grow(_freePos+1); _charBuffer[_freePos++] = ch; } internal void Append(String s) { int l = s.Length; int newFreePos = _freePos + l; if (newFreePos > _size) Grow(newFreePos); s.CopyTo(0, _charBuffer, _freePos, l); _freePos = newFreePos; } internal byte[] GetEncodedBytesBuffer() { return GetEncodedBytesBuffer(Encoding.UTF8); } internal byte[] GetEncodedBytesBuffer(Encoding encoding) { if (_byteBuffer != null) return _byteBuffer; if (encoding == null) encoding = Encoding.UTF8; // null terminate Append('\0'); // convert to bytes if (_recyclable) { // still using the original recyclable char buffer // -- can use recyclable byte buffer _byteBuffer = (byte[])s_ByteBufferAllocator.GetBuffer(); if (_freePos > 0) encoding.GetBytes(_charBuffer, 0, _freePos, _byteBuffer, 0); } else { _byteBuffer = encoding.GetBytes(_charBuffer, 0, _freePos); } return _byteBuffer; } public override String ToString() { return (_charBuffer != null && _freePos > 0) ? new String(_charBuffer, 0, _freePos) : null; } } // // byte[] buffer of encoded chars bytes. Recyclable up to 4K // Also encapsulates decoding into recyclable char[] buffer. // // Usage: // new RecyclableByteBuffer // fill .Buffer up // GetDecodedTabSeparatedStrings // Dispose // internal class RecyclableByteBuffer { private const int BUFFER_SIZE = 4096; private const int MAX_FREE_BUFFERS = 64; private static UbyteBufferAllocator s_ByteBufferAllocator; private static CharBufferAllocator s_CharBufferAllocator; private int _offset; private byte[] _byteBuffer; private bool _recyclable; private char[] _charBuffer; static RecyclableByteBuffer() { s_ByteBufferAllocator = new UbyteBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS); s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS); } internal RecyclableByteBuffer() { _byteBuffer = (byte[])s_ByteBufferAllocator.GetBuffer(); _recyclable = true; } internal void Dispose() { if (_recyclable) { if (_byteBuffer != null) s_ByteBufferAllocator.ReuseBuffer(_byteBuffer); if (_charBuffer != null) s_CharBufferAllocator.ReuseBuffer(_charBuffer); } _byteBuffer = null; _charBuffer = null; } internal byte[] Buffer { get { return _byteBuffer; } } internal void Resize(int newSize) { _byteBuffer = new byte[newSize]; _recyclable = false; } private void Skip(int count) { if (count <= 0) return; // adjust offset int l = _byteBuffer.Length; int c = 0; for (int i = 0; i < l; i++) { if (_byteBuffer[i] == (byte)'\t') { if (++c == count) { _offset = i+1; return; } } } } private int CalcLength() { // calculate null termitated length if (_byteBuffer != null) { int l = _byteBuffer.Length; for (int i = _offset; i < l; i++) { if (_byteBuffer[i] == 0) return i - _offset; } } return 0; } private char[] GetDecodedCharBuffer(Encoding encoding, ref int len) { if (_charBuffer != null) return _charBuffer; if (len == 0) { _charBuffer = new char[0]; } else if (_recyclable) { _charBuffer = (char[])s_CharBufferAllocator.GetBuffer(); len = encoding.GetChars(_byteBuffer, _offset, len, _charBuffer, 0); } else { _charBuffer = encoding.GetChars(_byteBuffer, _offset, len); len = _charBuffer.Length; } return _charBuffer; } internal string GetDecodedString(Encoding encoding, int len) { return encoding.GetString(_byteBuffer, 0, len); } internal String[] GetDecodedTabSeparatedStrings(Encoding encoding, int numStrings, int numSkipStrings) { if (numSkipStrings > 0) Skip(numSkipStrings); int len = CalcLength(); char[] s = GetDecodedCharBuffer(encoding, ref len); String[] ss = new String[numStrings]; int iStart = 0; int iEnd; int foundStrings = 0; for (int iString = 0; iString < numStrings; iString++) { iEnd = len; for (int i = iStart; i < len; i++) { if (s[i] == '\t') { iEnd = i; break; } } if (iEnd > iStart) ss[iString] = new String(s, iStart, iEnd-iStart); else ss[iString] = String.Empty; foundStrings++; if (iEnd == len) break; iStart = iEnd+1; } if (foundStrings < numStrings) { len = CalcLength(); iStart = _offset; for (int iString = 0; iString < numStrings; iString++) { iEnd = len; for (int i = iStart; i < len; i++) { if (_byteBuffer[i] == (byte)'\t') { iEnd = i; break; } } if (iEnd > iStart) ss[iString] = encoding.GetString(_byteBuffer, iStart, iEnd-iStart); else ss[iString] = String.Empty; if (iEnd == len) break; iStart = iEnd+1; } } return ss; } } // // class to encapsulate writing from byte[], IntPtr (resource or filehandle) // internal enum BufferType: byte { Managed = 0, UnmanagedPool = 1, IISAllocatedRequestMemory = 2, TransmitFile = 3 } internal class MemoryBytes { private int _size; private byte[] _arrayData; private GCHandle _pinnedArrayData; private IntPtr _intptrData; private long _fileSize; private IntPtr _fileHandle; private string _fileName; private long _offset; private BufferType _bufferType; // 0 managed, 1 native pool, 2 IIS allocated request memory, 3 TransmitFile internal MemoryBytes(string fileName, long offset, long fileSize) { _bufferType = BufferType.TransmitFile; _intptrData = IntPtr.Zero; _fileHandle = IntPtr.Zero; _fileSize = fileSize; _fileName = fileName; _offset = offset; // _cachedResponseBodyLength will be wrong if we don't set _size now. _size = IntPtr.Size; } internal MemoryBytes(byte[] data, int size): this(data, size, false, 0) { } internal MemoryBytes(byte[] data, int size, bool useTransmitFile, long fileSize) { _size = size; _arrayData = data; _intptrData = IntPtr.Zero; _fileHandle = IntPtr.Zero; if (useTransmitFile) { _bufferType = BufferType.TransmitFile; } _fileSize = fileSize; } internal MemoryBytes(IntPtr data, int size, BufferType bufferType) { _size = size; _arrayData = null; _intptrData = data; _fileHandle = IntPtr.Zero; _bufferType = bufferType; } internal long FileSize { get { return _fileSize; } } internal bool IsBufferFromUnmanagedPool { get { return _bufferType == BufferType.UnmanagedPool; } } internal BufferType BufferType { get { return _bufferType; } } #if UNUSED_CODE internal long Offset { get { return _offset; } } #endif internal int Size { get { return _size; } } internal bool UseTransmitFile { get { return _bufferType == BufferType.TransmitFile; } } private void CloseHandle() { if (_fileHandle != IntPtr.Zero && _fileHandle != UnsafeNativeMethods.INVALID_HANDLE_VALUE) { UnsafeNativeMethods.CloseHandle(_fileHandle); // don't allow 'this' to be GC'd before CloseHandle returns. _fileHandle = IntPtr.Zero; } } private static byte[] IntPtrToBytes(IntPtr p, long offset, long length) { // This method converts the given pointer and offset to a byte[] representation // of the C struct EcbFileAndOffset (32 and 64-bit specific): // // struct FileAndOffset // { // ULONGLONG cbOffset; // ULONGLONG cbLength; // HANDLE hFile; // } // byte[] bytes = new byte[2 * sizeof(long) + IntPtr.Size]; // Put the offset value first for (int i = 0; i < 8; i++) bytes[i] = (byte)((offset >> 8*i) & 0xFF ); // Put the file value next for (int i = 0; i < 8; i++) bytes[8+i] = (byte)((length >> 8*i) & 0xFF ); if (IntPtr.Size == 4) { int temp = p.ToInt32(); for (int i = 0; i < 4; i++) bytes[16+i] = (byte)((temp >> 8*i) & 0xFF ); } else { long temp = p.ToInt64(); for (int i = 0; i < 8; i++) bytes[16+i] = (byte)((temp >> 8*i) & 0xFF ); } return bytes; } private void SetHandle() { if (_fileName != null) { _fileHandle = UnsafeNativeMethods.GetFileHandleForTransmitFile(_fileName); } if (_fileHandle != IntPtr.Zero) { _arrayData = IntPtrToBytes(_fileHandle, _offset, _fileSize); } } internal IntPtr LockMemory() { SetHandle(); if (_arrayData != null) { _pinnedArrayData = GCHandle.Alloc(_arrayData, GCHandleType.Pinned); return Marshal.UnsafeAddrOfPinnedArrayElement(_arrayData, 0); } else { return _intptrData; } } internal void UnlockMemory() { CloseHandle(); if (_arrayData != null) { _pinnedArrayData.Free(); } } } // // recyclable pinnable char[] buffer to get Unicode server variables // // Usage: // new ServerVarCharBuffer // get PinnedAddress, Length // [Resize] // Dispose // internal class ServerVarCharBuffer { private const int BUFFER_SIZE = 1024; private const int MAX_FREE_BUFFERS = 64; private static CharBufferAllocator s_CharBufferAllocator; private bool _recyclable; private char[] _charBuffer; private bool _pinned; private GCHandle _pinnedCharBufferHandle; private IntPtr _pinnedAddr; static ServerVarCharBuffer() { s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS); } internal ServerVarCharBuffer() { _charBuffer = (char[])s_CharBufferAllocator.GetBuffer(); _recyclable = true; } internal void Dispose() { if (_pinned) { _pinnedCharBufferHandle.Free(); _pinned = false; } if (_recyclable) { if (_charBuffer != null) s_CharBufferAllocator.ReuseBuffer(_charBuffer); } _charBuffer = null; } internal IntPtr PinnedAddress { get { if (!_pinned) { _pinnedCharBufferHandle = GCHandle.Alloc(_charBuffer, GCHandleType.Pinned); _pinnedAddr = Marshal.UnsafeAddrOfPinnedArrayElement(_charBuffer, 0); _pinned = true; } return _pinnedAddr; } } internal int Length { get { return _charBuffer.Length; } } internal void Resize(int newSize) { if (_pinned) { _pinnedCharBufferHandle.Free(); _pinned = false; } _charBuffer = new char[newSize]; _recyclable = false; } } // // Async IO completion callback from IIS // internal delegate void ISAPIAsyncCompletionCallback(IntPtr ecb, int byteCount, int error); // // Implementation of HttpWorkerRequest based on ECB // internal abstract class ISAPIWorkerRequest : HttpWorkerRequest { protected IntPtr _ecb; // ECB as integer protected IntPtr _token; // user token as integer protected Guid _traceId; // ETW traceId // Request data obtained during initialization (basics) protected String _method; protected String _path; protected String _filePath; protected String _pathInfo; protected String _pathTranslated; protected String _appPath; protected String _appPathTranslated; protected int _contentType; protected int _contentTotalLength; protected int _contentAvailLength; protected int _queryStringLength; protected bool _ignoreMinAsyncSize; protected bool _requiresAsyncFlushCallback; // Request data obtained later on private bool _preloadedContentRead; private byte[] _preloadedContent; private bool _requestHeadersAvailable; private String[][] _unknownRequestHeaders; private String[] _knownRequestHeaders; private bool _clientCertFetched; private DateTime _clientCertValidFrom; private DateTime _clientCertValidUntil; private byte [] _clientCert; private int _clientCertEncoding; private byte [] _clientCertPublicKey; private byte [] _clientCertBinaryIssuer; // Outgoing headers storage private bool _headersSent; private Encoding _headerEncoding; private bool _contentLengthSent; private bool _chunked; private RecyclableCharBuffer _headers = new RecyclableCharBuffer(); private RecyclableCharBuffer _status = new RecyclableCharBuffer(); private bool _statusSet = true; // Outgoing data cached for a single FlushCore private byte[] _cachedResponseStatus; private byte[] _cachedResponseHeaders; private int _cachedResponseKeepConnected; private int _cachedResponseBodyLength; private ArrayList _cachedResponseBodyBytes; private int _cachedResponseBodyBytesIoLockCount; // Notification about the end of IO private HttpWorkerRequest.EndOfSendNotification _endOfRequestCallback; private Object _endOfRequestCallbackArg; private int _endOfRequestCallbackLockCount; // Constants for posted content type private const int CONTENT_NONE = 0; private const int CONTENT_FORM = 1; private const int CONTENT_MULTIPART = 2; private const int CONTENT_OTHER = 3; // // ISAPI status constants (for DoneWithSession) // private const int STATUS_SUCCESS = 1; private const int STATUS_SUCCESS_AND_KEEP_CONN = 2; private const int STATUS_PENDING = 3; private const int STATUS_ERROR = 4; // // Private helpers // private String[] ReadBasics(int[] contentInfo) { // call getbasics RecyclableByteBuffer buf = new RecyclableByteBuffer(); int r = GetBasicsCore(buf.Buffer, buf.Buffer.Length, contentInfo); while (r < 0) { buf.Resize(-r); // buffer not big enough r = GetBasicsCore(buf.Buffer, buf.Buffer.Length, contentInfo); } if (r == 0) throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data)); // convert to characters and split the buffer into strings String[] ss = buf.GetDecodedTabSeparatedStrings(Encoding.Default, 6, 0); // recycle buffers buf.Dispose(); return ss; } private static readonly char[] s_ColonOrNL = { ':', '\n' }; private void ReadRequestHeaders() { if (_requestHeadersAvailable) return; _knownRequestHeaders = new String[RequestHeaderMaximum]; // construct unknown headers as array list of name1,value1,... ArrayList headers = new ArrayList(); String s = GetServerVariable("ALL_RAW"); int l = (s != null) ? s.Length : 0; int i = 0; while (i < l) { // find next : int ci = s.IndexOfAny(s_ColonOrNL, i); if (ci < 0) break; if (s[ci] == '\n') { // ignore header without : i = ci+1; continue; } if (ci == i) { i++; continue; } // add extract name String name = s.Substring(i, ci-i).Trim(); // find next \n int ni = s.IndexOf('\n', ci+1); if (ni < 0) ni = l; while (ni < l-1 && s[ni+1] == ' ') { // continuation of header (ASURT 115064) ni = s.IndexOf('\n', ni+1); if (ni < 0) ni = l; } // extract value String value = s.Substring(ci+1, ni-ci-1).Trim(); // remember int knownIndex = GetKnownRequestHeaderIndex(name); if (knownIndex >= 0) { _knownRequestHeaders[knownIndex] = value; } else { headers.Add(name); headers.Add(value); } i = ni+1; } // copy to array unknown headers int n = headers.Count / 2; _unknownRequestHeaders = new String[n][]; int j = 0; for (i = 0; i < n; i++) { _unknownRequestHeaders[i] = new String[2]; _unknownRequestHeaders[i][0] = (String)headers[j++]; _unknownRequestHeaders[i][1] = (String)headers[j++]; } _requestHeadersAvailable = true; } private void SendHeaders() { if (!_headersSent) { if (_statusSet) { _headers.Append("\r\n"); AddHeadersToCachedResponse( _status.GetEncodedBytesBuffer(), _headers.GetEncodedBytesBuffer(_headerEncoding), (_contentLengthSent || _chunked) ? 1 : 0); _headersSent = true; } } } private void SendResponseFromFileStream(FileStream f, long offset, long length) { long fileSize = f.Length; if (length == -1) length = fileSize - offset; if (offset < 0 || length > fileSize - offset) throw new HttpException(SR.GetString(SR.Invalid_range)); if (length > 0) { if (offset > 0) f.Seek(offset, SeekOrigin.Begin); byte[] fileBytes = new byte[(int)length]; int bytesRead = f.Read(fileBytes, 0, (int)length); if (bytesRead > 0) AddBodyToCachedResponse(new MemoryBytes(fileBytes, bytesRead)); } } private void ResetCachedResponse() { _cachedResponseStatus = null; _cachedResponseHeaders = null; _cachedResponseBodyLength = 0; _cachedResponseBodyBytes = null; // DDBugs 162981: ASP.NET leaks requests when page calls TransmitFile and Flush // This happens because FlushCachedResponse may set _requiresAsyncFlushCallback and // _ignoreMinAsyncSize to true and then it "forgets" to reset them after Flush is done. // When the final flush is being executed it uses incorrect values // to determine that it needs an async completion during the final flush. // The fix is to reset async flags after each Flush _requiresAsyncFlushCallback = false; _ignoreMinAsyncSize = false; } private void AddHeadersToCachedResponse(byte[] status, byte[] header, int keepConnected) { _cachedResponseStatus = status; _cachedResponseHeaders = header; _cachedResponseKeepConnected = keepConnected; } private void AddBodyToCachedResponse(MemoryBytes bytes) { if (_cachedResponseBodyBytes == null) _cachedResponseBodyBytes = new ArrayList(); _cachedResponseBodyBytes.Add(bytes); _cachedResponseBodyLength += bytes.Size; } internal void UnlockCachedResponseBytesOnceAfterIoComplete() { if (Interlocked.Decrement(ref _cachedResponseBodyBytesIoLockCount) == 0) { // unlock pinned memory if (_cachedResponseBodyBytes != null) { int numFragments = _cachedResponseBodyBytes.Count; for (int i = 0; i < numFragments; i++) { try { ((MemoryBytes)_cachedResponseBodyBytes[i]).UnlockMemory(); } catch { } } } // don't remember cached data anymore ResetCachedResponse(); } } private void FlushCachedResponse(bool isFinal) { if (_ecb == IntPtr.Zero) return; bool asyncFlush = false; int numFragments = 0; IntPtr[] fragments = null; int[] fragmentLengths = null; long bytesOut = 0; try { // prepare body fragments as IntPtr[] of pointers and int[] of lengths if (_cachedResponseBodyLength > 0) { numFragments = _cachedResponseBodyBytes.Count; fragments = RecyclableArrayHelper.GetIntPtrArray(numFragments); fragmentLengths = RecyclableArrayHelper.GetIntegerArray(numFragments); for (int i = 0; i < numFragments; i++) { MemoryBytes bytes = (MemoryBytes)_cachedResponseBodyBytes[i]; fragments[i] = bytes.LockMemory(); if (!isFinal || !bytes.IsBufferFromUnmanagedPool) _requiresAsyncFlushCallback = true; if (bytes.UseTransmitFile) { fragmentLengths[i] = -bytes.Size; // use negative length for TransmitFile _ignoreMinAsyncSize = true; bytesOut += bytes.FileSize; } else { fragmentLengths[i] = bytes.Size; bytesOut += bytes.Size; } } } // prepare doneWithSession and finalStatus int doneWithSession = isFinal ? 1 : 0; int finalStatus = isFinal ? ((_cachedResponseKeepConnected != 0) ? STATUS_SUCCESS_AND_KEEP_CONN : STATUS_SUCCESS) : 0; // set the count to two - one for return from FlushCore and one for async IO completion // the cleanup should happen on the later of the two _cachedResponseBodyBytesIoLockCount = 2; // increment the lock count controlling end of request callback // so that the callback would be called at the later of EndRequest // and the async IO completion // (doesn't need to be interlocked as only one thread could start the IO) _endOfRequestCallbackLockCount++; if (isFinal) PerfCounters.DecrementCounter(AppPerfCounter.REQUESTS_EXECUTING); // perf counters are DWORDs, so it makes no sense to update REQUEST_BYTES_OUT with a value greater than Int32.MaxValue int delta = (int) bytesOut; if (delta > 0) { PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_OUT, delta); } try { // send to unmanaged code FlushCore( _cachedResponseStatus, _cachedResponseHeaders, _cachedResponseKeepConnected, _cachedResponseBodyLength, numFragments, fragments, fragmentLengths, doneWithSession, finalStatus, out asyncFlush); } finally { if (isFinal) _ecb = IntPtr.Zero; } } finally { // in case of synchronous IO adjust down the lock counts if (!asyncFlush) { _cachedResponseBodyBytesIoLockCount--; _endOfRequestCallbackLockCount--; } // unlock pinned memory UnlockCachedResponseBytesOnceAfterIoComplete(); // recycle buffers RecyclableArrayHelper.ReuseIntPtrArray(fragments); RecyclableArrayHelper.ReuseIntegerArray(fragmentLengths); } } internal void CallEndOfRequestCallbackOnceAfterAllIoComplete() { if (_endOfRequestCallback != null) { // only call the callback on the latest of EndRequest and async IO completion if (Interlocked.Decrement(ref _endOfRequestCallbackLockCount) == 0) { try { _endOfRequestCallback(this, _endOfRequestCallbackArg); } catch { } } } } // // ctor // internal ISAPIWorkerRequest(IntPtr ecb) { _ecb = ecb; PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TOTAL); } public override Guid RequestTraceIdentifier { get { return _traceId; } } internal IntPtr Ecb { get { return _ecb; } } internal void Initialize() { // setup basic values ReadRequestBasics(); if (_appPathTranslated != null && _appPathTranslated.Length > 2 && !StringUtil.StringEndsWith(_appPathTranslated, '\\')) _appPathTranslated += "\\"; // IIS 6.0 doesn't add the trailing '\' // Increment incoming request length PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, _contentTotalLength); } internal virtual void ReadRequestBasics() { // Get requests basics int[] contentInfo = new int[4]; String[] basicStrings = ReadBasics(contentInfo); if (basicStrings == null || basicStrings.Length != 6) throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data)); // Remember content info _contentType = contentInfo[0]; _contentTotalLength = contentInfo[1]; _contentAvailLength = contentInfo[2]; _queryStringLength = contentInfo[3]; // Remember basic strings _method = basicStrings[0]; _filePath = basicStrings[1]; _pathInfo = basicStrings[2]; _path = (_pathInfo.Length > 0) ? (_filePath + _pathInfo) : _filePath; _pathTranslated = basicStrings[3]; _appPath = basicStrings[4]; _appPathTranslated = basicStrings[5]; } // // Public methods // internal static ISAPIWorkerRequest CreateWorkerRequest(IntPtr ecb, bool useOOP) { ISAPIWorkerRequest wr = null; if (useOOP) { EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, false); wr = new ISAPIWorkerRequestOutOfProc(ecb); } else { int version = UnsafeNativeMethods.EcbGetVersion(ecb) >> 16; if (version >= 7) { EtwTrace.TraceEnableCheck(EtwTraceConfigType.IIS7_ISAPI, ecb); } else { EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero); } if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, true); if (version >= 7) { wr = new ISAPIWorkerRequestInProcForIIS7(ecb); } else if (version == 6) { wr = new ISAPIWorkerRequestInProcForIIS6(ecb); } else { wr = new ISAPIWorkerRequestInProc(ecb); } } return wr; } public override String GetUriPath() { return _path; } public override String GetQueryString() { if (_queryStringLength == 0) return String.Empty; int size = _queryStringLength + 2; StringBuilder buf = new StringBuilder(size); int r = GetQueryStringCore(0, buf, size); if (r != 1) throw new HttpException(SR.GetString(SR.Cannot_get_query_string)); return buf.ToString(); } public override byte[] GetQueryStringRawBytes() { if (_queryStringLength == 0) return null; byte[] buf = new byte[_queryStringLength]; int r = GetQueryStringRawBytesCore(buf, _queryStringLength); if (r != 1) throw new HttpException(SR.GetString(SR.Cannot_get_query_string_bytes)); return buf; } public override String GetRawUrl() { String qs = GetQueryString(); if (!String.IsNullOrEmpty(qs)) return _path + "?" + qs; else return _path; } public override String GetHttpVerbName() { return _method; } public override String GetHttpVersion() { return GetServerVariable("SERVER_PROTOCOL"); } public override String GetRemoteAddress() { return GetServerVariable("REMOTE_ADDR"); } public override String GetRemoteName() { return GetServerVariable("REMOTE_HOST"); } public override int GetRemotePort() { return 0; // unknown in ISAPI } public override String GetLocalAddress() { return GetServerVariable("LOCAL_ADDR"); } public override int GetLocalPort() { return Int32.Parse(GetServerVariable("SERVER_PORT")); } internal override String GetLocalPortAsString() { return GetServerVariable("SERVER_PORT"); } public override String GetServerName() { return GetServerVariable("SERVER_NAME"); } public override bool IsSecure() { String https = GetServerVariable("HTTPS"); return (https != null && https.Equals("on")); } public override String GetFilePath() { return _filePath; } public override String GetFilePathTranslated() { return _pathTranslated; } public override String GetPathInfo() { return _pathInfo; } public override String GetAppPath() { return _appPath; } public override String GetAppPathTranslated() { return _appPathTranslated; } public override int GetPreloadedEntityBodyLength() { return _contentAvailLength; } public override int GetPreloadedEntityBody(byte[] buffer, int offset) { if (_contentAvailLength == 0) { return 0; } if (buffer.Length - offset < _contentAvailLength) { throw new ArgumentOutOfRangeException("offset"); } int r = GetPreloadedPostedContentCore(buffer, offset, _contentAvailLength); if (r < 0) throw new HttpException(SR.GetString(SR.Cannot_read_posted_data)); return r; } public override byte[] GetPreloadedEntityBody() { if (!_preloadedContentRead) { if (_contentAvailLength > 0) { _preloadedContent = new byte[_contentAvailLength]; int r = GetPreloadedPostedContentCore(_preloadedContent, 0, _contentAvailLength); if (r < 0) throw new HttpException(SR.GetString(SR.Cannot_read_posted_data)); } _preloadedContentRead = true; } return _preloadedContent; } public override bool IsEntireEntityBodyIsPreloaded() { return (_contentAvailLength == _contentTotalLength); } public override int GetTotalEntityBodyLength() { return _contentTotalLength; } public override int ReadEntityBody(byte[] buffer, int size) { return ReadEntityBody(buffer, 0, size); } public override int ReadEntityBody(byte[] buffer, int offset, int size) { if (buffer.Length - offset < size) { throw new ArgumentOutOfRangeException("offset"); } int r = GetAdditionalPostedContentCore(buffer, offset, size); if (r < 0) { throw new HttpException(SR.GetString(SR.Cannot_read_posted_data)); } return r; } public override long GetBytesRead() { throw new HttpException(SR.GetString(SR.Not_supported)); } public override String GetKnownRequestHeader(int index) { if (!_requestHeadersAvailable) { // special case important ones so that no all headers parsing is required switch (index) { case HeaderContentType: if (_contentType == CONTENT_FORM) return "application/x-www-form-urlencoded"; break; case HeaderContentLength: if (_contentType != CONTENT_NONE) return (_contentTotalLength).ToString(); break; } // parse all headers ReadRequestHeaders(); } return _knownRequestHeaders[index]; } public override String GetUnknownRequestHeader(String name) { if (!_requestHeadersAvailable) ReadRequestHeaders(); int n = _unknownRequestHeaders.Length; for (int i = 0; i < n; i++) { if (StringUtil.EqualsIgnoreCase(name, _unknownRequestHeaders[i][0])) return _unknownRequestHeaders[i][1]; } return null; } public override String[][] GetUnknownRequestHeaders() { if (!_requestHeadersAvailable) ReadRequestHeaders(); return _unknownRequestHeaders; } public override void SendStatus(int statusCode, String statusDescription) { _status.Append(statusCode.ToString()); _status.Append(" "); _status.Append(statusDescription); _statusSet = true; } internal override void SetHeaderEncoding(Encoding encoding) { _headerEncoding = encoding; } public override void SendKnownResponseHeader(int index, String value) { if (_headersSent) throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent)); _headers.Append(GetKnownResponseHeaderName(index)); _headers.Append(": "); _headers.Append(value); _headers.Append("\r\n"); if (index == HeaderContentLength) _contentLengthSent = true; else if (index == HeaderTransferEncoding && (value != null && value.Equals("chunked"))) _chunked = true; } public override void SendUnknownResponseHeader(String name, String value) { if (_headersSent) throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent)); _headers.Append(name); _headers.Append(": "); _headers.Append(value); _headers.Append("\r\n"); } public override void SendCalculatedContentLength(int contentLength) { SendCalculatedContentLength((long)contentLength); } // public override void SendCalculatedContentLength(long contentLength) { if (!_headersSent) { _headers.Append("Content-Length: "); _headers.Append(contentLength.ToString(CultureInfo.InvariantCulture)); _headers.Append("\r\n"); _contentLengthSent = true; } } public override bool HeadersSent() { return _headersSent; } public override bool IsClientConnected() { return (IsClientConnectedCore() == 0) ? false : true; } public override void CloseConnection() { CloseConnectionCore(); } public override void SendResponseFromMemory(byte[] data, int length) { if (!_headersSent) SendHeaders(); if (length > 0) AddBodyToCachedResponse(new MemoryBytes(data, length)); } public override void SendResponseFromMemory(IntPtr data, int length) { SendResponseFromMemory(data, length, false); } internal override void SendResponseFromMemory(IntPtr data, int length, bool isBufferFromUnmanagedPool) { if (!_headersSent) SendHeaders(); if (length > 0) AddBodyToCachedResponse(new MemoryBytes(data, length, isBufferFromUnmanagedPool ? BufferType.UnmanagedPool : BufferType.Managed)); } // PackageFile for in-proc case internal virtual MemoryBytes PackageFile(String filename, long offset64, long length64, bool isImpersonating) { // The offset and length must be less than Int32.MaxValue for in-proc. // This should be true, since HttpFileResponseElement.ctor throws ArgumentOutOfRangeException for in-proc Debug.Assert(offset64 < Int32.MaxValue); Debug.Assert(length64 < Int32.MaxValue); int offset = Convert.ToInt32(offset64); int length = Convert.ToInt32(length64); FileStream f = null; MemoryBytes bytes = null; try { Debug.Assert(offset < length); f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); Debug.Assert((f.Length - offset) == length); int size = (int) (f.Length - offset); byte[] fileBytes = new byte[size]; int bytesRead = f.Read(fileBytes, offset, size); bytes = new MemoryBytes(fileBytes, bytesRead); } finally { if (f != null) f.Close(); } return bytes; } internal override void TransmitFile(string filename, long offset, long length, bool isImpersonating) { if (!_headersSent) SendHeaders(); if (length == 0) return; AddBodyToCachedResponse(PackageFile(filename, offset, length, isImpersonating)); return; } public override void SendResponseFromFile(String filename, long offset, long length) { if (!_headersSent) SendHeaders(); if (length == 0) return; FileStream f = null; try { f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); SendResponseFromFileStream(f, offset, length); } finally { if (f != null) f.Close(); } } public override void SendResponseFromFile(IntPtr handle, long offset, long length) { if (!_headersSent) SendHeaders(); if (length == 0) return; FileStream f = null; try { f = new FileStream(new Microsoft.Win32.SafeHandles.SafeFileHandle(handle,false), FileAccess.Read); SendResponseFromFileStream(f, offset, length); } finally { if (f != null) f.Close(); } } public override void FlushResponse(bool finalFlush) { // only flush headers - the data is write through if (!_headersSent) SendHeaders(); FlushCachedResponse(finalFlush); } public override void EndOfRequest() { FlushCachedResponse(true); // recycle the headers and status buffers if (_headers != null) { _headers.Dispose(); _headers = null; } if (_status != null) { _status.Dispose(); _status = null; } CallEndOfRequestCallbackOnceAfterAllIoComplete(); } public override void SetEndOfSendNotification(HttpWorkerRequest.EndOfSendNotification callback, Object extraData) { if (_endOfRequestCallback != null) throw new InvalidOperationException(); _endOfRequestCallback = callback; _endOfRequestCallbackArg = extraData; _endOfRequestCallbackLockCount = 1; // when goes to 0 the callback is called } public override String MapPath(String path) { return HostingEnvironment.MapPathInternal(path); } public override String MachineConfigPath { get { return HttpConfigurationSystem.MachineConfigurationFilePath; } } public override String RootWebConfigPath { get { return HttpConfigurationSystem.RootWebConfigurationFilePath; } } public override String MachineInstallDirectory { get { return HttpRuntime.AspInstallDirectory; } } public override IntPtr GetUserToken() { return GetUserTokenCore(); } public override IntPtr GetVirtualPathToken() { return GetVirtualPathTokenCore(); } public override byte[] GetClientCertificate() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCert; } public override DateTime GetClientCertificateValidFrom() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertValidFrom; } public override DateTime GetClientCertificateValidUntil() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertValidUntil; } public override byte [] GetClientCertificateBinaryIssuer() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertBinaryIssuer; } public override int GetClientCertificateEncoding() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertEncoding; } public override byte [] GetClientCertificatePublicKey() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertPublicKey; } private void FetchClientCertificate() { if (_clientCertFetched) return; _clientCertFetched = true; byte[] buf = new byte[8192]; int[] pInts = new int[4]; long[] pDates = new long[2]; int iRet = GetClientCertificateCore(buf, pInts, pDates); if (iRet < 0 && (-iRet) > 8192) { iRet = -iRet + 100; buf = new byte[iRet]; iRet = GetClientCertificateCore(buf, pInts, pDates); } if (iRet > 0) { _clientCertEncoding = pInts[0]; if (pInts[1] < buf.Length && pInts[1] > 0) { _clientCert = new byte[pInts[1]]; Array.Copy(buf, _clientCert, pInts[1]); if (pInts[2] + pInts[1] < buf.Length && pInts[2] > 0) { _clientCertBinaryIssuer = new byte[pInts[2]]; Array.Copy(buf, pInts[1], _clientCertBinaryIssuer, 0, pInts[2]); } if (pInts[2] + pInts[1] + pInts[3] < buf.Length && pInts[3] > 0) { _clientCertPublicKey = new byte[pInts[3]]; Array.Copy(buf, pInts[1] + pInts[2], _clientCertPublicKey, 0, pInts[3]); } } } if (iRet > 0 && pDates[0] != 0) _clientCertValidFrom = DateTime.FromFileTime(pDates[0]); else _clientCertValidFrom = DateTime.Now; if (iRet > 0 && pDates[1] != 0) _clientCertValidUntil = DateTime.FromFileTime(pDates[1]); else _clientCertValidUntil = DateTime.Now; } // // internal methods specific to ISAPI // internal void AppendLogParameter(String logParam) { AppendLogParameterCore(logParam); } internal virtual void SendEmptyResponse() { } // // PInvoke callback wrappers -- overridden by the derived classes // internal abstract int GetBasicsCore(byte[] buffer, int size, int[] contentInfo); internal abstract int GetQueryStringCore(int encode, StringBuilder buffer, int size); internal abstract int GetQueryStringRawBytesCore(byte[] buffer, int size); internal abstract int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead); internal abstract int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize); internal abstract void FlushCore(byte[] status, byte[] header, int keepConnected, int totalBodySize, int numBodyFragments, IntPtr[] bodyFragments, int[] bodyFragmentLengths, int doneWithSession, int finalStatus, out bool async); internal abstract int IsClientConnectedCore(); internal abstract int CloseConnectionCore(); internal abstract int MapUrlToPathCore(String url, byte[] buffer, int size); internal abstract IntPtr GetUserTokenCore(); internal abstract IntPtr GetVirtualPathTokenCore(); internal abstract int AppendLogParameterCore(String logParam); internal abstract int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates); internal abstract int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut); } // // In-process ISAPIWorkerRequest // // Does queueing of IO operations. ISAPI only support one async IO at a time. // internal class ISAPIWorkerRequestInProc : ISAPIWorkerRequest { protected const int NUM_SERVER_VARIABLES = 35; // total number of variables that we commonly get protected const int NUM_BASIC_SERVER_VARIABLES = 12; // needed on every request protected const int NUM_ADDITIONAL_SERVER_VARIABLES = 23; // needed when HttpRequest.ServerVariables is populated // These constants must be kept in sync with g_szServerVariables and g_szUnicodeServerVariables in ecbdirect.cxx protected const int LOGON_USER = 0; protected const int AUTH_TYPE = 1; protected const int APPL_PHYSICAL_PATH = 2; protected const int REQUEST_METHOD = 3; protected const int PATH_INFO = 4; protected const int PATH_TRANSLATED = 5; protected const int URL = 6; protected const int CACHE_URL = 7; protected const int SERVER_NAME = 8; protected const int SERVER_PORT = 9; protected const int HTTPS = 10; protected const int ALL_RAW = 11; protected const int REMOTE_ADDR = 12; protected const int AUTH_PASSWORD = 13; protected const int CERT_COOKIE = 14; protected const int CERT_FLAGS = 15; protected const int CERT_ISSUER = 16; protected const int CERT_KEYSIZE = 17; protected const int CERT_SECRETKEYSIZE = 18; protected const int CERT_SERIALNUMBER = 19; protected const int CERT_SERVER_ISSUER = 20; protected const int CERT_SERVER_SUBJECT = 21; protected const int CERT_SUBJECT = 22; protected const int GATEWAY_INTERFACE = 23; protected const int HTTPS_KEYSIZE = 24; protected const int HTTPS_SECRETKEYSIZE = 25; protected const int HTTPS_SERVER_ISSUER = 26; protected const int HTTPS_SERVER_SUBJECT = 27; protected const int INSTANCE_ID = 28; protected const int INSTANCE_META_PATH = 29; protected const int LOCAL_ADDR = 30; protected const int REMOTE_HOST = 31; protected const int REMOTE_PORT = 32; protected const int SERVER_PROTOCOL = 33; protected const int SERVER_SOFTWARE = 34; // storage for common server variables protected string[] _basicServerVars = null; protected string[] _additionalServerVars = null; internal ISAPIWorkerRequestInProc(IntPtr ecb) : base(ecb) { if (ecb == IntPtr.Zero || UnsafeNativeMethods.EcbGetTraceContextId(ecb, out _traceId) != 1) { _traceId = Guid.Empty; } } internal override int GetBasicsCore(byte[] buffer, int size, int[] contentInfo) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbGetBasics(_ecb, buffer, size, contentInfo); } internal override int GetQueryStringCore(int encode, StringBuilder buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbGetQueryString(_ecb, encode, buffer, size); } internal override int GetQueryStringRawBytesCore(byte[] buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbGetQueryStringRawBytes(_ecb, buffer, size); } internal override int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead) { if (_ecb == IntPtr.Zero) return 0; int rc = UnsafeNativeMethods.EcbGetPreloadedPostedContent(_ecb, bytes, offset, numBytesToRead); if (rc > 0) PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc); return rc; } internal override int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize) { if (_ecb == IntPtr.Zero) return 0; int rc = UnsafeNativeMethods.EcbGetAdditionalPostedContent(_ecb, bytes, offset, bufferSize); if (rc > 0) PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc); return rc; } internal override int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbGetClientCertificate(_ecb, buffer, buffer.Length, pInts, pDates); } internal override int IsClientConnectedCore() { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbIsClientConnected(_ecb); } internal override void FlushCore(byte[] status, byte[] header, int keepConnected, int totalBodySize, int numBodyFragments, IntPtr[] bodyFragments, int[] bodyFragmentLengths, int doneWithSession, int finalStatus, out bool async) { async = false; if (_ecb == IntPtr.Zero) return; UnsafeNativeMethods.EcbFlushCore( _ecb, status, header, keepConnected, totalBodySize, numBodyFragments, bodyFragments, bodyFragmentLengths, doneWithSession, finalStatus, 0, 0, null); } internal override int CloseConnectionCore() { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbCloseConnection(_ecb); } internal override int MapUrlToPathCore(String url, byte[] buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbMapUrlToPath(_ecb, url, buffer, size); } internal override IntPtr GetUserTokenCore() { if (_token == IntPtr.Zero && _ecb != IntPtr.Zero) _token = UnsafeNativeMethods.EcbGetImpersonationToken(_ecb, IntPtr.Zero); return _token; } internal override IntPtr GetVirtualPathTokenCore() { if (_token == IntPtr.Zero && _ecb != IntPtr.Zero) _token = UnsafeNativeMethods.EcbGetVirtualPathToken(_ecb, IntPtr.Zero); return _token; } internal override int AppendLogParameterCore(String logParam) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbAppendLogParameter(_ecb, logParam); } // ISAPIWorkerRequestInProc protected virtual String GetServerVariableCore(String name) { if (_ecb == IntPtr.Zero) return null; String value = null; RecyclableByteBuffer buf = new RecyclableByteBuffer(); int retVal = UnsafeNativeMethods.EcbGetServerVariable(_ecb, name, buf.Buffer, buf.Buffer.Length); while (retVal < 0) { buf.Resize(-retVal); // buffer not big enough retVal = UnsafeNativeMethods.EcbGetServerVariable(_ecb, name, buf.Buffer, buf.Buffer.Length); } if (retVal > 0) value = buf.GetDecodedString(Encoding.UTF8, retVal); buf.Dispose(); return value; } // ISAPIWorkerRequestInProc protected virtual void GetAdditionalServerVariables() { if (_ecb == IntPtr.Zero) return; // _additionalServerVars should only be initialized once Debug.Assert(_additionalServerVars == null); if (_additionalServerVars != null) return; _additionalServerVars = new string[NUM_ADDITIONAL_SERVER_VARIABLES]; for(int i = 0; i < _additionalServerVars.Length; i++) { int nameIndex = i + NUM_BASIC_SERVER_VARIABLES; RecyclableByteBuffer buf = new RecyclableByteBuffer(); int retVal = UnsafeNativeMethods.EcbGetServerVariableByIndex(_ecb, nameIndex, buf.Buffer, buf.Buffer.Length); while (retVal < 0) { buf.Resize(-retVal); // buffer not big enough retVal = UnsafeNativeMethods.EcbGetServerVariableByIndex(_ecb, nameIndex, buf.Buffer, buf.Buffer.Length); } if (retVal > 0) _additionalServerVars[i] = buf.GetDecodedString(Encoding.UTF8, retVal); buf.Dispose(); } } private String GetAdditionalServerVar(int index) { if (_additionalServerVars == null) GetAdditionalServerVariables(); return _additionalServerVars[index - NUM_BASIC_SERVER_VARIABLES]; } public override String GetServerVariable(String name) { // this switch statement is a little more than twice as fast as a Hashtable lookup if (name != null) { switch (name.Length) { case 20: if (name == "HTTPS_SERVER_SUBJECT") return GetAdditionalServerVar(HTTPS_SERVER_SUBJECT); break; case 19: if (name == "HTTPS_SECRETKEYSIZE") return GetAdditionalServerVar(HTTPS_SECRETKEYSIZE); else if (name == "CERT_SERVER_SUBJECT") return GetAdditionalServerVar(CERT_SERVER_SUBJECT); else if (name == "HTTPS_SERVER_ISSUER") return GetAdditionalServerVar(HTTPS_SERVER_ISSUER); break; case 18: if (name == "INSTANCE_META_PATH") return GetAdditionalServerVar(INSTANCE_META_PATH); else if (name == "CERT_SECRETKEYSIZE") return GetAdditionalServerVar(CERT_SECRETKEYSIZE); else if (name == "CERT_SERVER_ISSUER") return GetAdditionalServerVar(CERT_SERVER_ISSUER); break; case 17: if (name == "CERT_SERIALNUMBER") return GetAdditionalServerVar(CERT_SERIALNUMBER); else if (name == "GATEWAY_INTERFACE") return GetAdditionalServerVar(GATEWAY_INTERFACE); break; case 15: if (name == "HTTP_USER_AGENT") return GetKnownRequestHeader(HeaderUserAgent); else if (name == "SERVER_PROTOCOL") return GetAdditionalServerVar(SERVER_PROTOCOL); else if (name == "SERVER_SOFTWARE") return GetAdditionalServerVar(SERVER_SOFTWARE); break; case 13: if (name == "AUTH_PASSWORD") return GetAdditionalServerVar(AUTH_PASSWORD); else if (name == "HTTPS_KEYSIZE") return GetAdditionalServerVar(HTTPS_KEYSIZE); break; case 12: if (name == "CERT_KEYSIZE") return GetAdditionalServerVar(CERT_KEYSIZE); else if (name == "CERT_SUBJECT") return GetAdditionalServerVar(CERT_SUBJECT); break; case 11: if (name == "SERVER_NAME") return _basicServerVars[SERVER_NAME]; else if (name == "SERVER_PORT") return _basicServerVars[SERVER_PORT]; else if (name == "REMOTE_HOST") return GetAdditionalServerVar(REMOTE_HOST); else if (name == "REMOTE_PORT") return GetAdditionalServerVar(REMOTE_PORT); else if (name == "REMOTE_ADDR") return GetAdditionalServerVar(REMOTE_ADDR); else if (name == "CERT_COOKIE") return GetAdditionalServerVar(CERT_COOKIE); else if (name == "CERT_ISSUER") return GetAdditionalServerVar(CERT_ISSUER); else if (name == "INSTANCE_ID") return GetAdditionalServerVar(INSTANCE_ID); break; case 10: if (name == "LOGON_USER") return _basicServerVars[LOGON_USER]; else if (name == "LOCAL_ADDR") return GetAdditionalServerVar(LOCAL_ADDR); else if (name == "CERT_FLAGS") return GetAdditionalServerVar(CERT_FLAGS); break; case 9: if (name == "AUTH_TYPE") return _basicServerVars[AUTH_TYPE]; break; case 7: if (name == "ALL_RAW") { return _basicServerVars[ALL_RAW]; } break; case 5: if (name == "HTTPS") return _basicServerVars[HTTPS]; break; } } // this is not a common server variable return GetServerVariableCore(name); } internal override int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbCallISAPI(_ecb, iFunction, bufIn, bufIn.Length, bufOut, bufOut.Length); } } // // In-process ISAPIWorkerRequest specific for IIS7 // // Uses unicode server vars // internal class ISAPIWorkerRequestInProcForIIS7 : ISAPIWorkerRequestInProcForIIS6 { private string _rawUrl; private bool _isRewriteModuleEnabled; internal ISAPIWorkerRequestInProcForIIS7(IntPtr ecb) : base(ecb) { _trySkipIisCustomErrors = true; } internal override bool IsRewriteModuleEnabled { get { // ensure GetRawUrl has been called, since it sets _isRewriteModuleEnabled if (_rawUrl == null) { GetRawUrl(); } return _isRewriteModuleEnabled; } } // If the URL Rewrite module has rewritten the URL, then "IIS_Was_Url_Rewritten" will be "1", // and we will attempt to derive RawUrl from the "CACHE_URL" server variable. If the URL has not been // rewritten, or there's a problem, return null. private string GetRequestUri() { // if it hasn't been rewritten, return null if (GetUnicodeServerVariable("UNICODE_IIS_WasUrlRewritten") != "1") { return null; } // cacheUrl has format "[http|https]://[server]:[port][uri]", including query string and path-info, if they exist. string cacheUrl = GetUnicodeServerVariable(CACHE_URL); // if there's a problem, return null if (cacheUrl == null) { return null; } // the URI begins at the 3rd slash int count = 0; for(int index = 0; index < cacheUrl.Length; index++) { if (cacheUrl[index] == '/') { if (++count == 3) { return cacheUrl.Substring(index); } } } // someone must have modified CACHE_URL, it is not valid return null; } public override String GetRawUrl() { if (_rawUrl == null) { // is the URL Rewrite module enabled? _rawUrl = GetRequestUri(); if (_rawUrl != null) { _isRewriteModuleEnabled = true; return _rawUrl; } _rawUrl = base.GetRawUrl(); } return _rawUrl; } internal override bool TrySkipIisCustomErrors { get { return _trySkipIisCustomErrors; } set { _trySkipIisCustomErrors = value; } } internal override void RaiseTraceEvent(IntegratedTraceType traceType, string eventData) { if (IntPtr.Zero != _ecb) { // the area is derivative of the type, either page or module int areaFlag = (traceType < IntegratedTraceType.DiagCritical) ? EtwTraceFlags.Page : EtwTraceFlags.Module; if (EtwTrace.IsTraceEnabled(EtwTrace.InferVerbosity(traceType), areaFlag)) { string message = String.IsNullOrEmpty(eventData) ? String.Empty : eventData; UnsafeNativeMethods.EcbEmitSimpleTrace(_ecb, (int)traceType, message); } } } internal override void RaiseTraceEvent(WebBaseEvent webEvent) { if (IntPtr.Zero != _ecb) { if (EtwTrace.IsTraceEnabled(webEvent.InferEtwTraceVerbosity(), EtwTraceFlags.Infrastructure)) { int fieldCount; string[] fieldNames; int[] fieldTypes; string[] fieldData; int webEventType; webEvent.DeconstructWebEvent(out webEventType, out fieldCount, out fieldNames, out fieldTypes, out fieldData); UnsafeNativeMethods.EcbEmitWebEventTrace(_ecb, webEventType, fieldCount, fieldNames, fieldTypes, fieldData); } } } } // // In-process ISAPIWorkerRequest specific for IIS6 // // Uses unicode server vars // internal class ISAPIWorkerRequestInProcForIIS6 : ISAPIWorkerRequestInProc { private static int _asyncIoCount; internal ISAPIWorkerRequestInProcForIIS6(IntPtr ecb) : base(ecb) { } internal static void WaitForPendingAsyncIo() { while(_asyncIoCount != 0) { Thread.Sleep(250); } } internal override void SendEmptyResponse() { // facilitate health monitoring for IIS6 -- update last activity timestamp // to avoid deadlock detection UnsafeNativeMethods.UpdateLastActivityTimeForHealthMonitor(); } internal override void ReadRequestBasics() { if (_ecb == IntPtr.Zero) return; // set server variables needed for request basics and Indigo ( GetBasicServerVariables(); // _pathInfo is the difference between UNICODE_PATH_INFO and UNICODE_URL int lengthDiff = _path.Length - _filePath.Length; if (lengthDiff > 0) { _pathInfo = _path.Substring(_filePath.Length); int pathTranslatedLength = _pathTranslated.Length - lengthDiff; if (pathTranslatedLength > 0) { _pathTranslated = _pathTranslated.Substring(0, pathTranslatedLength); } } else { _filePath = _path; _pathInfo = String.Empty; } _appPath = HostingEnvironment.ApplicationVirtualPath; // // other (int) request basics // int[] contentInfo = null; try { contentInfo = RecyclableArrayHelper.GetIntegerArray(4); UnsafeNativeMethods.EcbGetBasicsContentInfo(_ecb, contentInfo); _contentType = contentInfo[0]; _contentTotalLength = contentInfo[1]; _contentAvailLength = contentInfo[2]; _queryStringLength = contentInfo[3]; } finally { RecyclableArrayHelper.ReuseIntegerArray(contentInfo); } } private void GetBasicServerVariables() { if (_ecb == IntPtr.Zero) return; // _basicServerVars should only be initialized once Debug.Assert(_basicServerVars == null); if (_basicServerVars != null) return; _basicServerVars = new string[NUM_BASIC_SERVER_VARIABLES]; ServerVarCharBuffer buffer = new ServerVarCharBuffer(); try { int[] serverVarLengths = new int[NUM_BASIC_SERVER_VARIABLES]; int r = 0; int hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length, serverVarLengths, serverVarLengths.Length, 0, ref r); if (r > buffer.Length) { buffer.Resize(r); hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length, serverVarLengths, serverVarLengths.Length, 0, ref r); } Misc.ThrowIfFailedHr(hresult); IntPtr current = buffer.PinnedAddress; for(int i = 0; i < _basicServerVars.Length; i++) { _basicServerVars[i] = Marshal.PtrToStringUni(current, serverVarLengths[i]); current = new IntPtr((long)current + 2L * (1L + serverVarLengths[i])); } // special case variables _appPathTranslated = _basicServerVars[APPL_PHYSICAL_PATH]; _method = _basicServerVars[REQUEST_METHOD]; _path = _basicServerVars[PATH_INFO]; _pathTranslated = _basicServerVars[PATH_TRANSLATED]; _filePath = _basicServerVars[URL]; } finally { buffer.Dispose(); } } // ISAPIWorkerRequestInProcForIIS6 protected override void GetAdditionalServerVariables() { if (_ecb == IntPtr.Zero) return; // _additionalServerVars should only be initialized once Debug.Assert(_additionalServerVars == null); if (_additionalServerVars != null) return; _additionalServerVars = new string[NUM_ADDITIONAL_SERVER_VARIABLES]; ServerVarCharBuffer buffer = new ServerVarCharBuffer(); try { int[] serverVarLengths = new int[NUM_ADDITIONAL_SERVER_VARIABLES]; int r = 0; int hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length, serverVarLengths, serverVarLengths.Length, NUM_BASIC_SERVER_VARIABLES, ref r); if (r > buffer.Length) { buffer.Resize(r); hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length, serverVarLengths, serverVarLengths.Length, NUM_BASIC_SERVER_VARIABLES, ref r); } if (hresult != 0) Marshal.ThrowExceptionForHR(hresult); IntPtr current = buffer.PinnedAddress; for(int i = 0; i < _additionalServerVars.Length; i++) { _additionalServerVars[i] = Marshal.PtrToStringUni(current, serverVarLengths[i]); current = new IntPtr((long)current + 2L * (1L + serverVarLengths[i])); } } finally { buffer.Dispose(); } } // ISAPIWorkerRequestInProcForIIS6 protected override string GetServerVariableCore(string name) { if (StringUtil.StringStartsWith(name, "HTTP_")) // fall back for headers (IIS6 doesn't support them as UNICODE_XXX) return base.GetServerVariableCore(name); else return GetUnicodeServerVariable("UNICODE_" + name); } protected internal String GetUnicodeServerVariable(String name) { String value = null; ServerVarCharBuffer buf = new ServerVarCharBuffer(); try { value = GetUnicodeServerVariable(name, buf); } finally { buf.Dispose(); } return value; } protected internal String GetUnicodeServerVariable(int nameIndex) { String value = null; ServerVarCharBuffer buf = new ServerVarCharBuffer(); try { value = GetUnicodeServerVariable(nameIndex, buf); } finally { buf.Dispose(); } return value; } private String GetUnicodeServerVariable(String name, ServerVarCharBuffer buffer) { if (_ecb == IntPtr.Zero) return null; int r = UnsafeNativeMethods.EcbGetUnicodeServerVariable(_ecb, name, buffer.PinnedAddress, buffer.Length); if (r < 0) { buffer.Resize(-r); r = UnsafeNativeMethods.EcbGetUnicodeServerVariable(_ecb, name, buffer.PinnedAddress, buffer.Length); } if (r > 0) return Marshal.PtrToStringUni(buffer.PinnedAddress, r); else return null; } private String GetUnicodeServerVariable(int nameIndex, ServerVarCharBuffer buffer) { if (_ecb == IntPtr.Zero) return null; int r = UnsafeNativeMethods.EcbGetUnicodeServerVariableByIndex(_ecb, nameIndex, buffer.PinnedAddress, buffer.Length); if (r < 0) { buffer.Resize(-r); r = UnsafeNativeMethods.EcbGetUnicodeServerVariableByIndex(_ecb, nameIndex, buffer.PinnedAddress, buffer.Length); } if (r > 0) return Marshal.PtrToStringUni(buffer.PinnedAddress, r); else return null; } // // Support for async VectorSend and kernel mode cache on IIS6 // private const int MIN_ASYNC_SIZE = 2048; private GCHandle _rootedThis; // for the duration of async private ISAPIAsyncCompletionCallback _asyncFlushCompletionCallback; private int _asyncFinalStatus; private bool _serverSupportFunctionError = false; private IntPtr _entity; // pointer to HSE_entity private bool _cacheInKernelMode = false; private bool _disableKernelCache = false; protected bool _trySkipIisCustomErrors = false; private const int TRY_SKIP_IIS_CUSTOM_ERRORS = 0x40; // PackageFile for IIS6 internal override MemoryBytes PackageFile(string filename, long offset, long size, bool isImpersonating) { return new MemoryBytes(filename, offset, size); } // internal override bool SupportsLongTransmitFile { get { return true; } } internal override void FlushCore(byte[] status, byte[] header, int keepConnected, int totalBodySize, int numBodyFragments, IntPtr[] bodyFragments, int[] bodyFragmentLengths, int doneWithSession, int finalStatus, out bool async) { async = false; if (_ecb == IntPtr.Zero) return; if (_headersSentFromExecuteUrl) { // don't send headers twice status = null; header = null; } // async only for large responses and only on the last flush // don't do async if shutting down (async IO holds up app domain shutdown) if (doneWithSession != 0 && !HttpRuntime.ShutdownInProgress && (_ignoreMinAsyncSize || (totalBodySize >= MIN_ASYNC_SIZE))) { if (_requiresAsyncFlushCallback) { _asyncFlushCompletionCallback = new ISAPIAsyncCompletionCallback(OnAsyncFlushCompletion); _asyncFinalStatus = finalStatus; // remember to pass to DoneWithSession on completion _rootedThis = GCHandle.Alloc(this); // root for the duration of IO doneWithSession = 0; // will do on completion async = true; Interlocked.Increment(ref _asyncIoCount); // increment async io count } else { // buffers are native, so we don't need to return to managed code _asyncFlushCompletionCallback = null; doneWithSession = 0; // will do on completion async = true; } } // 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 Debug.Assert(0 <= finalStatus && finalStatus <= 2); int flags = _trySkipIisCustomErrors ? finalStatus|TRY_SKIP_IIS_CUSTOM_ERRORS : finalStatus; int rc = UnsafeNativeMethods.EcbFlushCore( _ecb, status, header, keepConnected, totalBodySize, numBodyFragments, bodyFragments, bodyFragmentLengths, doneWithSession, flags, _cacheInKernelMode ? 1 : 0, async ? 1 : 0, _asyncFlushCompletionCallback); if (!_requiresAsyncFlushCallback && rc == 0 && async) { // unlock and reset cached response UnlockCachedResponseBytesOnceAfterIoComplete(); CallEndOfRequestCallbackOnceAfterAllIoComplete(); } else if (rc != 0 && async) { // on async failure default to sync path async = false; // call DoneWithSession UnsafeNativeMethods.EcbFlushCore(_ecb, null, null, 0, 0, 0, null, null, 1, _asyncFinalStatus, 0, 0, null); if (_asyncFlushCompletionCallback != null) { // unroot _rootedThis.Free(); // decrement async io count Interlocked.Decrement(ref _asyncIoCount); } } else if (rc != 0 && !async && doneWithSession == 0 && !_serverSupportFunctionError) { //on non-async failure stop executing the request //only throw once _serverSupportFunctionError = true; string message = SR.Server_Support_Function_Error; //give different error if connection was closed if (rc == HResults.WSAECONNABORTED || rc == HResults.WSAECONNRESET) { message = SR.Server_Support_Function_Error_Disconnect; PerfCounters.IncrementGlobalCounter(GlobalPerfCounter.REQUESTS_DISCONNECTED); } throw new HttpException(SR.GetString(message, rc.ToString("X8", CultureInfo.InvariantCulture)), rc); } } private void OnAsyncFlushCompletion(IntPtr ecb, int byteCount, int error) { try { // unroot _rootedThis.Free(); // call DoneWithSession UnsafeNativeMethods.EcbFlushCore(ecb, null, null, 0, 0, 0, null, null, 1, _asyncFinalStatus, 0, 0, null); // unlock pinned memory (at the latest of this completion and exit from the FlushCore on stack) UnlockCachedResponseBytesOnceAfterIoComplete(); // Revert any impersonation set by IIS UnsafeNativeMethods.RevertToSelf(); // call the HttpRuntime to recycle buffers (at the latest of this completion and EndRequest) CallEndOfRequestCallbackOnceAfterAllIoComplete(); } finally { // decrement async io count Interlocked.Decrement(ref _asyncIoCount); } } // WOS 1555777: kernel cache support // If the response can be kernel cached, return the kernel cache key; // otherwise return null. The kernel cache key is used to invalidate // the entry if a dependency changes or the item is flushed from the // managed cache for any reason. internal override string SetupKernelCaching(int secondsToLive, string originalCacheUrl, bool enableKernelCacheForVaryByStar) { // if someone called DisableKernelCache, don't setup kernel caching if (_ecb == IntPtr.Zero || _disableKernelCache) return null; string cacheUrl = GetUnicodeServerVariable(CACHE_URL); // if we're re-inserting the response into the kernel cache, the original key must match if (originalCacheUrl != null && originalCacheUrl != cacheUrl) { return null; } // If the request contains a query string, don't kernel cache the entry if (String.IsNullOrEmpty(cacheUrl) || (!enableKernelCacheForVaryByStar && cacheUrl.IndexOf('?') != -1)) { return null; } // enable kernel caching (IIS will set the HTTP_CACHE_POLICY when we call VectorSend) _cacheInKernelMode = true; // okay, the response will be kernel cached, here's the key return cacheUrl; } // WOS 1555777: kernel cache support internal override void DisableKernelCache() { _disableKernelCache = true; _cacheInKernelMode = false; } // // ExecuteUrl support // private ISAPIAsyncCompletionCallback _executeUrlCompletionCallback; private HttpAsyncResult _asyncResultOfExecuteUrl; private bool _headersSentFromExecuteUrl; internal override bool SupportsExecuteUrl { get { return true; } } internal override IAsyncResult BeginExecuteUrl( String url, String method, String childHeaders, bool sendHeaders, bool addUserIndo, IntPtr token, String name, String authType, byte[] entity, AsyncCallback cb, Object state) { if (_ecb == IntPtr.Zero || // after done with session _asyncResultOfExecuteUrl != null || // another ExecuteUrl in progress (sendHeaders && HeadersSent())) // asked to send headers, but already sent them { throw new InvalidOperationException(SR.GetString(SR.Cannot_execute_url_in_this_context)); } if (entity != null && entity.Length > 0) { int ret = UnsafeNativeMethods.EcbGetExecUrlEntityInfo(entity.Length, entity, out _entity); if (ret != 1) { throw new HttpException(SR.GetString(SR.Failed_to_execute_url)); } } Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl: url=\"" + url + "\"."); HttpAsyncResult ar = new HttpAsyncResult(cb, state); _asyncResultOfExecuteUrl = ar; _executeUrlCompletionCallback = new ISAPIAsyncCompletionCallback(OnExecuteUrlCompletion); _rootedThis = GCHandle.Alloc(this); // root for the duration of ExecuteUrl int rc = UnsafeNativeMethods.EcbExecuteUrlUnicode(_ecb, url, method, childHeaders, sendHeaders, addUserIndo, token, name, authType, _entity, _executeUrlCompletionCallback); if (rc != 1) { if (_entity != IntPtr.Zero) { UnsafeNativeMethods.EcbFreeExecUrlEntityInfo(_entity); } _rootedThis.Free(); _asyncResultOfExecuteUrl = null; Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl: failed!"); throw new HttpException(SR.GetString(SR.Failed_to_execute_url)); } if (sendHeaders) { // ExecuteUrl will send headers, worker request should not _headersSentFromExecuteUrl = true; } return ar; } internal override void EndExecuteUrl(IAsyncResult result) { Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.EndExecuteUrl"); HttpAsyncResult asyncResult = result as HttpAsyncResult; if (asyncResult != null) { asyncResult.End(); } } private void OnExecuteUrlCompletion(IntPtr ecb, int byteCount, int error) { if (_entity != IntPtr.Zero) { UnsafeNativeMethods.EcbFreeExecUrlEntityInfo(_entity); } _rootedThis.Free(); Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.OnExecuteUrlCompletion"); // signal async caller to resume work HttpAsyncResult asyncResult = _asyncResultOfExecuteUrl; _asyncResultOfExecuteUrl = null; asyncResult.Complete(false, null, null); } } // // Out-of-process worker request // internal class ISAPIWorkerRequestOutOfProc : ISAPIWorkerRequest { // sends chunks separately if the total length exceeds the following // to relieve the memory pressure on named pipes const int PM_FLUSH_THRESHOLD = 31*1024; internal ISAPIWorkerRequestOutOfProc(IntPtr ecb) : base(ecb) { UnsafeNativeMethods.PMGetTraceContextId(ecb, out _traceId); } private bool _useBaseTime = false; private const int _numServerVars = 32; private IDictionary _serverVars; private static String[] _serverVarNames = new String[_numServerVars] { "APPL_MD_PATH", /* this one is not UTF8 so we don't decode it here */ "ALL_RAW", "AUTH_PASSWORD", "AUTH_TYPE", "CERT_COOKIE", "CERT_FLAGS", "CERT_ISSUER", "CERT_KEYSIZE", "CERT_SECRETKEYSIZE", "CERT_SERIALNUMBER", "CERT_SERVER_ISSUER", "CERT_SERVER_SUBJECT", "CERT_SUBJECT", "GATEWAY_INTERFACE", "HTTP_COOKIE", "HTTP_USER_AGENT", "HTTPS", "HTTPS_KEYSIZE", "HTTPS_SECRETKEYSIZE", "HTTPS_SERVER_ISSUER", "HTTPS_SERVER_SUBJECT", "INSTANCE_ID", "INSTANCE_META_PATH", "LOCAL_ADDR", "LOGON_USER", "REMOTE_ADDR", "REMOTE_HOST", "SERVER_NAME", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE", "REMOTE_PORT" }; private void GetAllServerVars() { if (_ecb == IntPtr.Zero) return; RecyclableByteBuffer buf = new RecyclableByteBuffer(); int r = UnsafeNativeMethods.PMGetAllServerVariables(_ecb, buf.Buffer, buf.Buffer.Length); while (r < 0) { buf.Resize(-r); // buffer not big enough r = UnsafeNativeMethods.PMGetAllServerVariables(_ecb, buf.Buffer, buf.Buffer.Length); } if (r == 0) throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data)); // stub out first server var is it could contain non-UTF8 data // convert to characters and split the buffer into strings using default request encoding String[] ss = buf.GetDecodedTabSeparatedStrings(Encoding.Default, _numServerVars-1, 1); // recycle buffers buf.Dispose(); // fill in the hashtable _serverVars = new Hashtable(_numServerVars, StringComparer.OrdinalIgnoreCase); _serverVars.Add("APPL_MD_PATH", HttpRuntime.AppDomainAppIdInternal); for (int i = 1; i < _numServerVars; i++) { // starting with 1 to skip APPL_MD_PATH _serverVars.Add(_serverVarNames[i], ss[i-1]); } } internal override int GetBasicsCore(byte[] buffer, int size, int[] contentInfo) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMGetBasics(_ecb, buffer, size, contentInfo); } internal override int GetQueryStringCore(int encode, StringBuilder buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMGetQueryString(_ecb, encode, buffer, size); } internal override int GetQueryStringRawBytesCore(byte[] buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMGetQueryStringRawBytes(_ecb, buffer, size); } internal override int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead) { if (_ecb == IntPtr.Zero) return 0; int rc = UnsafeNativeMethods.PMGetPreloadedPostedContent(_ecb, bytes, offset, numBytesToRead); if (rc > 0) PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc); return rc; } internal override int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize) { if (_ecb == IntPtr.Zero) return 0; int rc = UnsafeNativeMethods.PMGetAdditionalPostedContent(_ecb, bytes, offset, bufferSize); if (rc > 0) PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc); return rc; } internal override int IsClientConnectedCore() { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMIsClientConnected(_ecb); } // PackageFile for IIS5 internal override MemoryBytes PackageFile(string filename, long offset64, long length64, bool isImpersonating) { // The offset and length must be less than Int32.MaxValue for IIS5. // This should be true, since HttpFileResponseElement.ctor throws ArgumentOutOfRangeException for IIS5. Debug.Assert(offset64 < Int32.MaxValue); Debug.Assert(length64 < Int32.MaxValue); int offset = Convert.ToInt32(offset64); int length = Convert.ToInt32(length64); byte[] offsetBytes = BitConverter.GetBytes(offset); byte[] lengthBytes = BitConverter.GetBytes(length); byte[] nameBytes = Encoding.Unicode.GetBytes(filename); // buffer consists of 1 byte for impersonation flag, 3 bytes for alignment, // 4 bytes for offset, 4 bytes for length, n bytes for file name, 2 bytes for null terminator byte[] buffer = new byte[4 + offsetBytes.Length + lengthBytes.Length + nameBytes.Length + 2]; // first byte indicates whether impersonation is used if (isImpersonating) buffer[0] = 0x31; else buffer[0] = 0x30; // bytes 2 thru 4 are unused for alignment // bytes 5 thru 8 are the offset Buffer.BlockCopy(offsetBytes, 0, buffer, 4, offsetBytes.Length); // bytes 9 thru 12 are the length Buffer.BlockCopy(lengthBytes, 0, buffer, 4 + offsetBytes.Length, lengthBytes.Length); // last two bytes are 0 for null terminator Buffer.BlockCopy(nameBytes, 0, buffer, 4 + offsetBytes.Length + lengthBytes.Length, nameBytes.Length); return new MemoryBytes(buffer, buffer.Length, true, length); } internal override void FlushCore(byte[] status, byte[] header, int keepConnected, int totalBodySize, int numBodyFragments, IntPtr[] bodyFragments, int[] bodyFragmentLengths, int doneWithSession, int finalStatus, out bool async) { async = false; if (_ecb == IntPtr.Zero) return; if (numBodyFragments > 1) { // Don't flush all at once if the length is over the threshold int i = 0; while (i < numBodyFragments) { bool first = (i == 0); int size = bodyFragmentLengths[i]; bool useTransmitFile = (bodyFragmentLengths[i] < 0); int idx = i+1; if (!useTransmitFile) { while (idx < numBodyFragments && size + bodyFragmentLengths[idx] < PM_FLUSH_THRESHOLD && bodyFragmentLengths[idx] >= 0) { size += bodyFragmentLengths[idx]; idx++; } } bool last = (idx == numBodyFragments); // bodyFragmentLength is negative for TransmitFile, but totalBodySize argument must be positive. if (useTransmitFile) size = -size; UnsafeNativeMethods.PMFlushCore( _ecb, first ? status : null, first ? header : null, keepConnected, size, i, idx-i, bodyFragments, bodyFragmentLengths, last ? doneWithSession : 0, last ? finalStatus : 0); i = idx; } } else { // Everything in one chunk UnsafeNativeMethods.PMFlushCore( _ecb, status, header, keepConnected, totalBodySize, 0, numBodyFragments, bodyFragments, bodyFragmentLengths, doneWithSession, finalStatus); } } internal override int CloseConnectionCore() { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMCloseConnection(_ecb); } internal override int MapUrlToPathCore(String url, byte[] buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMMapUrlToPath(_ecb, url, buffer, size); } internal override IntPtr GetUserTokenCore() { if (_token == IntPtr.Zero && _ecb != IntPtr.Zero) _token = UnsafeNativeMethods.PMGetImpersonationToken(_ecb); return _token; } internal override IntPtr GetVirtualPathTokenCore() { if (_token == IntPtr.Zero && _ecb != IntPtr.Zero) _token = UnsafeNativeMethods.PMGetVirtualPathToken(_ecb); return _token; } internal override int AppendLogParameterCore(String logParam) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMAppendLogParameter(_ecb, logParam); } internal override int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMGetClientCertificate(_ecb, buffer, buffer.Length, pInts, pDates); } public override String GetServerVariable(String name) { // PATH_TRANSLATED is mangled -- do not use the original server variable if (name.Equals("PATH_TRANSLATED")) return GetFilePathTranslated(); if (_serverVars == null) GetAllServerVars(); return (String)_serverVars[name]; } internal override int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMCallISAPI(_ecb, iFunction, bufIn, bufIn.Length, bufOut, bufOut.Length); } internal override void SendEmptyResponse() { if (_ecb == IntPtr.Zero) return; UnsafeNativeMethods.PMEmptyResponse(_ecb); } internal override DateTime GetStartTime() { if (_ecb == IntPtr.Zero || _useBaseTime) return base.GetStartTime(); long fileTime = UnsafeNativeMethods.PMGetStartTimeStamp(_ecb); return DateTimeUtil.FromFileTimeToUtc(fileTime); } internal override void ResetStartTime() { base.ResetStartTime(); _useBaseTime = true; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- namespace System.Web.Hosting { using System.Text; using System.Configuration.Assemblies; using System.Runtime.InteropServices; using System.Collections; using System.Collections.Specialized; using System.IO; using System.Globalization; using System.Threading; using Microsoft.Win32; using System.Web; using System.Web.Management; using System.Web.Util; using System.Web.Configuration; using System.Web.Caching; // // recyclable buffers for IntPtr[] and int[] // to avoid pinning gen0 // internal class RecyclableArrayHelper { private const int ARRAY_SIZE = 128; private const int MAX_FREE_ARRAYS = 64; private static IntegerArrayAllocator s_IntegerArrayAllocator; private static IntPtrArrayAllocator s_IntPtrArrayAllocator; static RecyclableArrayHelper() { s_IntegerArrayAllocator = new IntegerArrayAllocator(ARRAY_SIZE, MAX_FREE_ARRAYS); s_IntPtrArrayAllocator = new IntPtrArrayAllocator(ARRAY_SIZE, MAX_FREE_ARRAYS); } internal static int[] GetIntegerArray(int minimumLength) { if( minimumLength <= ARRAY_SIZE ) return(int[])s_IntegerArrayAllocator.GetBuffer(); else return new int[minimumLength]; } internal static IntPtr[] GetIntPtrArray(int minimumLength) { if( minimumLength <= ARRAY_SIZE ) return(IntPtr[])s_IntPtrArrayAllocator.GetBuffer(); else return new IntPtr[minimumLength]; } internal static void ReuseIntegerArray(int[] array) { if (array != null && array.Length == ARRAY_SIZE) s_IntegerArrayAllocator.ReuseBuffer(array); } internal static void ReuseIntPtrArray(IntPtr[] array) { if (array != null && array.Length == ARRAY_SIZE) s_IntPtrArrayAllocator.ReuseBuffer(array); } } // // char[] appendable buffer. Recyclable up to 1K // Also encapsulates encoding (using utf-8) into recyclable byte[] buffer. // // Usage: // new RecyclableCharBuffer // Append // ... // GetEncodedBytesBuffer // Dispose // internal class RecyclableCharBuffer { private const int BUFFER_SIZE = 1024; private const int MAX_FREE_BUFFERS = 64; private static CharBufferAllocator s_CharBufferAllocator; private static UbyteBufferAllocator s_ByteBufferAllocator; private char[] _charBuffer; private int _size; private int _freePos; private bool _recyclable; private byte[] _byteBuffer; static RecyclableCharBuffer() { s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS); s_ByteBufferAllocator = new UbyteBufferAllocator(Encoding.UTF8.GetMaxByteCount(BUFFER_SIZE), MAX_FREE_BUFFERS); } internal RecyclableCharBuffer() { _charBuffer = (char[])s_CharBufferAllocator.GetBuffer(); _size = _charBuffer.Length; _freePos = 0; _recyclable = true; } internal void Dispose() { if (_recyclable) { if (_charBuffer != null) s_CharBufferAllocator.ReuseBuffer(_charBuffer); if (_byteBuffer != null) s_ByteBufferAllocator.ReuseBuffer(_byteBuffer); } _charBuffer = null; _byteBuffer = null; } private void Grow(int newSize) { if (newSize <= _size) return; if (newSize < _size*2) newSize = _size*2; char[] newBuffer = new char[newSize]; if (_freePos > 0) Array.Copy(_charBuffer, newBuffer, _freePos); _charBuffer = newBuffer; _size = newSize; _recyclable = false; } internal void Append(char ch) { if (_freePos >= _size) Grow(_freePos+1); _charBuffer[_freePos++] = ch; } internal void Append(String s) { int l = s.Length; int newFreePos = _freePos + l; if (newFreePos > _size) Grow(newFreePos); s.CopyTo(0, _charBuffer, _freePos, l); _freePos = newFreePos; } internal byte[] GetEncodedBytesBuffer() { return GetEncodedBytesBuffer(Encoding.UTF8); } internal byte[] GetEncodedBytesBuffer(Encoding encoding) { if (_byteBuffer != null) return _byteBuffer; if (encoding == null) encoding = Encoding.UTF8; // null terminate Append('\0'); // convert to bytes if (_recyclable) { // still using the original recyclable char buffer // -- can use recyclable byte buffer _byteBuffer = (byte[])s_ByteBufferAllocator.GetBuffer(); if (_freePos > 0) encoding.GetBytes(_charBuffer, 0, _freePos, _byteBuffer, 0); } else { _byteBuffer = encoding.GetBytes(_charBuffer, 0, _freePos); } return _byteBuffer; } public override String ToString() { return (_charBuffer != null && _freePos > 0) ? new String(_charBuffer, 0, _freePos) : null; } } // // byte[] buffer of encoded chars bytes. Recyclable up to 4K // Also encapsulates decoding into recyclable char[] buffer. // // Usage: // new RecyclableByteBuffer // fill .Buffer up // GetDecodedTabSeparatedStrings // Dispose // internal class RecyclableByteBuffer { private const int BUFFER_SIZE = 4096; private const int MAX_FREE_BUFFERS = 64; private static UbyteBufferAllocator s_ByteBufferAllocator; private static CharBufferAllocator s_CharBufferAllocator; private int _offset; private byte[] _byteBuffer; private bool _recyclable; private char[] _charBuffer; static RecyclableByteBuffer() { s_ByteBufferAllocator = new UbyteBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS); s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS); } internal RecyclableByteBuffer() { _byteBuffer = (byte[])s_ByteBufferAllocator.GetBuffer(); _recyclable = true; } internal void Dispose() { if (_recyclable) { if (_byteBuffer != null) s_ByteBufferAllocator.ReuseBuffer(_byteBuffer); if (_charBuffer != null) s_CharBufferAllocator.ReuseBuffer(_charBuffer); } _byteBuffer = null; _charBuffer = null; } internal byte[] Buffer { get { return _byteBuffer; } } internal void Resize(int newSize) { _byteBuffer = new byte[newSize]; _recyclable = false; } private void Skip(int count) { if (count <= 0) return; // adjust offset int l = _byteBuffer.Length; int c = 0; for (int i = 0; i < l; i++) { if (_byteBuffer[i] == (byte)'\t') { if (++c == count) { _offset = i+1; return; } } } } private int CalcLength() { // calculate null termitated length if (_byteBuffer != null) { int l = _byteBuffer.Length; for (int i = _offset; i < l; i++) { if (_byteBuffer[i] == 0) return i - _offset; } } return 0; } private char[] GetDecodedCharBuffer(Encoding encoding, ref int len) { if (_charBuffer != null) return _charBuffer; if (len == 0) { _charBuffer = new char[0]; } else if (_recyclable) { _charBuffer = (char[])s_CharBufferAllocator.GetBuffer(); len = encoding.GetChars(_byteBuffer, _offset, len, _charBuffer, 0); } else { _charBuffer = encoding.GetChars(_byteBuffer, _offset, len); len = _charBuffer.Length; } return _charBuffer; } internal string GetDecodedString(Encoding encoding, int len) { return encoding.GetString(_byteBuffer, 0, len); } internal String[] GetDecodedTabSeparatedStrings(Encoding encoding, int numStrings, int numSkipStrings) { if (numSkipStrings > 0) Skip(numSkipStrings); int len = CalcLength(); char[] s = GetDecodedCharBuffer(encoding, ref len); String[] ss = new String[numStrings]; int iStart = 0; int iEnd; int foundStrings = 0; for (int iString = 0; iString < numStrings; iString++) { iEnd = len; for (int i = iStart; i < len; i++) { if (s[i] == '\t') { iEnd = i; break; } } if (iEnd > iStart) ss[iString] = new String(s, iStart, iEnd-iStart); else ss[iString] = String.Empty; foundStrings++; if (iEnd == len) break; iStart = iEnd+1; } if (foundStrings < numStrings) { len = CalcLength(); iStart = _offset; for (int iString = 0; iString < numStrings; iString++) { iEnd = len; for (int i = iStart; i < len; i++) { if (_byteBuffer[i] == (byte)'\t') { iEnd = i; break; } } if (iEnd > iStart) ss[iString] = encoding.GetString(_byteBuffer, iStart, iEnd-iStart); else ss[iString] = String.Empty; if (iEnd == len) break; iStart = iEnd+1; } } return ss; } } // // class to encapsulate writing from byte[], IntPtr (resource or filehandle) // internal enum BufferType: byte { Managed = 0, UnmanagedPool = 1, IISAllocatedRequestMemory = 2, TransmitFile = 3 } internal class MemoryBytes { private int _size; private byte[] _arrayData; private GCHandle _pinnedArrayData; private IntPtr _intptrData; private long _fileSize; private IntPtr _fileHandle; private string _fileName; private long _offset; private BufferType _bufferType; // 0 managed, 1 native pool, 2 IIS allocated request memory, 3 TransmitFile internal MemoryBytes(string fileName, long offset, long fileSize) { _bufferType = BufferType.TransmitFile; _intptrData = IntPtr.Zero; _fileHandle = IntPtr.Zero; _fileSize = fileSize; _fileName = fileName; _offset = offset; // _cachedResponseBodyLength will be wrong if we don't set _size now. _size = IntPtr.Size; } internal MemoryBytes(byte[] data, int size): this(data, size, false, 0) { } internal MemoryBytes(byte[] data, int size, bool useTransmitFile, long fileSize) { _size = size; _arrayData = data; _intptrData = IntPtr.Zero; _fileHandle = IntPtr.Zero; if (useTransmitFile) { _bufferType = BufferType.TransmitFile; } _fileSize = fileSize; } internal MemoryBytes(IntPtr data, int size, BufferType bufferType) { _size = size; _arrayData = null; _intptrData = data; _fileHandle = IntPtr.Zero; _bufferType = bufferType; } internal long FileSize { get { return _fileSize; } } internal bool IsBufferFromUnmanagedPool { get { return _bufferType == BufferType.UnmanagedPool; } } internal BufferType BufferType { get { return _bufferType; } } #if UNUSED_CODE internal long Offset { get { return _offset; } } #endif internal int Size { get { return _size; } } internal bool UseTransmitFile { get { return _bufferType == BufferType.TransmitFile; } } private void CloseHandle() { if (_fileHandle != IntPtr.Zero && _fileHandle != UnsafeNativeMethods.INVALID_HANDLE_VALUE) { UnsafeNativeMethods.CloseHandle(_fileHandle); // don't allow 'this' to be GC'd before CloseHandle returns. _fileHandle = IntPtr.Zero; } } private static byte[] IntPtrToBytes(IntPtr p, long offset, long length) { // This method converts the given pointer and offset to a byte[] representation // of the C struct EcbFileAndOffset (32 and 64-bit specific): // // struct FileAndOffset // { // ULONGLONG cbOffset; // ULONGLONG cbLength; // HANDLE hFile; // } // byte[] bytes = new byte[2 * sizeof(long) + IntPtr.Size]; // Put the offset value first for (int i = 0; i < 8; i++) bytes[i] = (byte)((offset >> 8*i) & 0xFF ); // Put the file value next for (int i = 0; i < 8; i++) bytes[8+i] = (byte)((length >> 8*i) & 0xFF ); if (IntPtr.Size == 4) { int temp = p.ToInt32(); for (int i = 0; i < 4; i++) bytes[16+i] = (byte)((temp >> 8*i) & 0xFF ); } else { long temp = p.ToInt64(); for (int i = 0; i < 8; i++) bytes[16+i] = (byte)((temp >> 8*i) & 0xFF ); } return bytes; } private void SetHandle() { if (_fileName != null) { _fileHandle = UnsafeNativeMethods.GetFileHandleForTransmitFile(_fileName); } if (_fileHandle != IntPtr.Zero) { _arrayData = IntPtrToBytes(_fileHandle, _offset, _fileSize); } } internal IntPtr LockMemory() { SetHandle(); if (_arrayData != null) { _pinnedArrayData = GCHandle.Alloc(_arrayData, GCHandleType.Pinned); return Marshal.UnsafeAddrOfPinnedArrayElement(_arrayData, 0); } else { return _intptrData; } } internal void UnlockMemory() { CloseHandle(); if (_arrayData != null) { _pinnedArrayData.Free(); } } } // // recyclable pinnable char[] buffer to get Unicode server variables // // Usage: // new ServerVarCharBuffer // get PinnedAddress, Length // [Resize] // Dispose // internal class ServerVarCharBuffer { private const int BUFFER_SIZE = 1024; private const int MAX_FREE_BUFFERS = 64; private static CharBufferAllocator s_CharBufferAllocator; private bool _recyclable; private char[] _charBuffer; private bool _pinned; private GCHandle _pinnedCharBufferHandle; private IntPtr _pinnedAddr; static ServerVarCharBuffer() { s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS); } internal ServerVarCharBuffer() { _charBuffer = (char[])s_CharBufferAllocator.GetBuffer(); _recyclable = true; } internal void Dispose() { if (_pinned) { _pinnedCharBufferHandle.Free(); _pinned = false; } if (_recyclable) { if (_charBuffer != null) s_CharBufferAllocator.ReuseBuffer(_charBuffer); } _charBuffer = null; } internal IntPtr PinnedAddress { get { if (!_pinned) { _pinnedCharBufferHandle = GCHandle.Alloc(_charBuffer, GCHandleType.Pinned); _pinnedAddr = Marshal.UnsafeAddrOfPinnedArrayElement(_charBuffer, 0); _pinned = true; } return _pinnedAddr; } } internal int Length { get { return _charBuffer.Length; } } internal void Resize(int newSize) { if (_pinned) { _pinnedCharBufferHandle.Free(); _pinned = false; } _charBuffer = new char[newSize]; _recyclable = false; } } // // Async IO completion callback from IIS // internal delegate void ISAPIAsyncCompletionCallback(IntPtr ecb, int byteCount, int error); // // Implementation of HttpWorkerRequest based on ECB // internal abstract class ISAPIWorkerRequest : HttpWorkerRequest { protected IntPtr _ecb; // ECB as integer protected IntPtr _token; // user token as integer protected Guid _traceId; // ETW traceId // Request data obtained during initialization (basics) protected String _method; protected String _path; protected String _filePath; protected String _pathInfo; protected String _pathTranslated; protected String _appPath; protected String _appPathTranslated; protected int _contentType; protected int _contentTotalLength; protected int _contentAvailLength; protected int _queryStringLength; protected bool _ignoreMinAsyncSize; protected bool _requiresAsyncFlushCallback; // Request data obtained later on private bool _preloadedContentRead; private byte[] _preloadedContent; private bool _requestHeadersAvailable; private String[][] _unknownRequestHeaders; private String[] _knownRequestHeaders; private bool _clientCertFetched; private DateTime _clientCertValidFrom; private DateTime _clientCertValidUntil; private byte [] _clientCert; private int _clientCertEncoding; private byte [] _clientCertPublicKey; private byte [] _clientCertBinaryIssuer; // Outgoing headers storage private bool _headersSent; private Encoding _headerEncoding; private bool _contentLengthSent; private bool _chunked; private RecyclableCharBuffer _headers = new RecyclableCharBuffer(); private RecyclableCharBuffer _status = new RecyclableCharBuffer(); private bool _statusSet = true; // Outgoing data cached for a single FlushCore private byte[] _cachedResponseStatus; private byte[] _cachedResponseHeaders; private int _cachedResponseKeepConnected; private int _cachedResponseBodyLength; private ArrayList _cachedResponseBodyBytes; private int _cachedResponseBodyBytesIoLockCount; // Notification about the end of IO private HttpWorkerRequest.EndOfSendNotification _endOfRequestCallback; private Object _endOfRequestCallbackArg; private int _endOfRequestCallbackLockCount; // Constants for posted content type private const int CONTENT_NONE = 0; private const int CONTENT_FORM = 1; private const int CONTENT_MULTIPART = 2; private const int CONTENT_OTHER = 3; // // ISAPI status constants (for DoneWithSession) // private const int STATUS_SUCCESS = 1; private const int STATUS_SUCCESS_AND_KEEP_CONN = 2; private const int STATUS_PENDING = 3; private const int STATUS_ERROR = 4; // // Private helpers // private String[] ReadBasics(int[] contentInfo) { // call getbasics RecyclableByteBuffer buf = new RecyclableByteBuffer(); int r = GetBasicsCore(buf.Buffer, buf.Buffer.Length, contentInfo); while (r < 0) { buf.Resize(-r); // buffer not big enough r = GetBasicsCore(buf.Buffer, buf.Buffer.Length, contentInfo); } if (r == 0) throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data)); // convert to characters and split the buffer into strings String[] ss = buf.GetDecodedTabSeparatedStrings(Encoding.Default, 6, 0); // recycle buffers buf.Dispose(); return ss; } private static readonly char[] s_ColonOrNL = { ':', '\n' }; private void ReadRequestHeaders() { if (_requestHeadersAvailable) return; _knownRequestHeaders = new String[RequestHeaderMaximum]; // construct unknown headers as array list of name1,value1,... ArrayList headers = new ArrayList(); String s = GetServerVariable("ALL_RAW"); int l = (s != null) ? s.Length : 0; int i = 0; while (i < l) { // find next : int ci = s.IndexOfAny(s_ColonOrNL, i); if (ci < 0) break; if (s[ci] == '\n') { // ignore header without : i = ci+1; continue; } if (ci == i) { i++; continue; } // add extract name String name = s.Substring(i, ci-i).Trim(); // find next \n int ni = s.IndexOf('\n', ci+1); if (ni < 0) ni = l; while (ni < l-1 && s[ni+1] == ' ') { // continuation of header (ASURT 115064) ni = s.IndexOf('\n', ni+1); if (ni < 0) ni = l; } // extract value String value = s.Substring(ci+1, ni-ci-1).Trim(); // remember int knownIndex = GetKnownRequestHeaderIndex(name); if (knownIndex >= 0) { _knownRequestHeaders[knownIndex] = value; } else { headers.Add(name); headers.Add(value); } i = ni+1; } // copy to array unknown headers int n = headers.Count / 2; _unknownRequestHeaders = new String[n][]; int j = 0; for (i = 0; i < n; i++) { _unknownRequestHeaders[i] = new String[2]; _unknownRequestHeaders[i][0] = (String)headers[j++]; _unknownRequestHeaders[i][1] = (String)headers[j++]; } _requestHeadersAvailable = true; } private void SendHeaders() { if (!_headersSent) { if (_statusSet) { _headers.Append("\r\n"); AddHeadersToCachedResponse( _status.GetEncodedBytesBuffer(), _headers.GetEncodedBytesBuffer(_headerEncoding), (_contentLengthSent || _chunked) ? 1 : 0); _headersSent = true; } } } private void SendResponseFromFileStream(FileStream f, long offset, long length) { long fileSize = f.Length; if (length == -1) length = fileSize - offset; if (offset < 0 || length > fileSize - offset) throw new HttpException(SR.GetString(SR.Invalid_range)); if (length > 0) { if (offset > 0) f.Seek(offset, SeekOrigin.Begin); byte[] fileBytes = new byte[(int)length]; int bytesRead = f.Read(fileBytes, 0, (int)length); if (bytesRead > 0) AddBodyToCachedResponse(new MemoryBytes(fileBytes, bytesRead)); } } private void ResetCachedResponse() { _cachedResponseStatus = null; _cachedResponseHeaders = null; _cachedResponseBodyLength = 0; _cachedResponseBodyBytes = null; // DDBugs 162981: ASP.NET leaks requests when page calls TransmitFile and Flush // This happens because FlushCachedResponse may set _requiresAsyncFlushCallback and // _ignoreMinAsyncSize to true and then it "forgets" to reset them after Flush is done. // When the final flush is being executed it uses incorrect values // to determine that it needs an async completion during the final flush. // The fix is to reset async flags after each Flush _requiresAsyncFlushCallback = false; _ignoreMinAsyncSize = false; } private void AddHeadersToCachedResponse(byte[] status, byte[] header, int keepConnected) { _cachedResponseStatus = status; _cachedResponseHeaders = header; _cachedResponseKeepConnected = keepConnected; } private void AddBodyToCachedResponse(MemoryBytes bytes) { if (_cachedResponseBodyBytes == null) _cachedResponseBodyBytes = new ArrayList(); _cachedResponseBodyBytes.Add(bytes); _cachedResponseBodyLength += bytes.Size; } internal void UnlockCachedResponseBytesOnceAfterIoComplete() { if (Interlocked.Decrement(ref _cachedResponseBodyBytesIoLockCount) == 0) { // unlock pinned memory if (_cachedResponseBodyBytes != null) { int numFragments = _cachedResponseBodyBytes.Count; for (int i = 0; i < numFragments; i++) { try { ((MemoryBytes)_cachedResponseBodyBytes[i]).UnlockMemory(); } catch { } } } // don't remember cached data anymore ResetCachedResponse(); } } private void FlushCachedResponse(bool isFinal) { if (_ecb == IntPtr.Zero) return; bool asyncFlush = false; int numFragments = 0; IntPtr[] fragments = null; int[] fragmentLengths = null; long bytesOut = 0; try { // prepare body fragments as IntPtr[] of pointers and int[] of lengths if (_cachedResponseBodyLength > 0) { numFragments = _cachedResponseBodyBytes.Count; fragments = RecyclableArrayHelper.GetIntPtrArray(numFragments); fragmentLengths = RecyclableArrayHelper.GetIntegerArray(numFragments); for (int i = 0; i < numFragments; i++) { MemoryBytes bytes = (MemoryBytes)_cachedResponseBodyBytes[i]; fragments[i] = bytes.LockMemory(); if (!isFinal || !bytes.IsBufferFromUnmanagedPool) _requiresAsyncFlushCallback = true; if (bytes.UseTransmitFile) { fragmentLengths[i] = -bytes.Size; // use negative length for TransmitFile _ignoreMinAsyncSize = true; bytesOut += bytes.FileSize; } else { fragmentLengths[i] = bytes.Size; bytesOut += bytes.Size; } } } // prepare doneWithSession and finalStatus int doneWithSession = isFinal ? 1 : 0; int finalStatus = isFinal ? ((_cachedResponseKeepConnected != 0) ? STATUS_SUCCESS_AND_KEEP_CONN : STATUS_SUCCESS) : 0; // set the count to two - one for return from FlushCore and one for async IO completion // the cleanup should happen on the later of the two _cachedResponseBodyBytesIoLockCount = 2; // increment the lock count controlling end of request callback // so that the callback would be called at the later of EndRequest // and the async IO completion // (doesn't need to be interlocked as only one thread could start the IO) _endOfRequestCallbackLockCount++; if (isFinal) PerfCounters.DecrementCounter(AppPerfCounter.REQUESTS_EXECUTING); // perf counters are DWORDs, so it makes no sense to update REQUEST_BYTES_OUT with a value greater than Int32.MaxValue int delta = (int) bytesOut; if (delta > 0) { PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_OUT, delta); } try { // send to unmanaged code FlushCore( _cachedResponseStatus, _cachedResponseHeaders, _cachedResponseKeepConnected, _cachedResponseBodyLength, numFragments, fragments, fragmentLengths, doneWithSession, finalStatus, out asyncFlush); } finally { if (isFinal) _ecb = IntPtr.Zero; } } finally { // in case of synchronous IO adjust down the lock counts if (!asyncFlush) { _cachedResponseBodyBytesIoLockCount--; _endOfRequestCallbackLockCount--; } // unlock pinned memory UnlockCachedResponseBytesOnceAfterIoComplete(); // recycle buffers RecyclableArrayHelper.ReuseIntPtrArray(fragments); RecyclableArrayHelper.ReuseIntegerArray(fragmentLengths); } } internal void CallEndOfRequestCallbackOnceAfterAllIoComplete() { if (_endOfRequestCallback != null) { // only call the callback on the latest of EndRequest and async IO completion if (Interlocked.Decrement(ref _endOfRequestCallbackLockCount) == 0) { try { _endOfRequestCallback(this, _endOfRequestCallbackArg); } catch { } } } } // // ctor // internal ISAPIWorkerRequest(IntPtr ecb) { _ecb = ecb; PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TOTAL); } public override Guid RequestTraceIdentifier { get { return _traceId; } } internal IntPtr Ecb { get { return _ecb; } } internal void Initialize() { // setup basic values ReadRequestBasics(); if (_appPathTranslated != null && _appPathTranslated.Length > 2 && !StringUtil.StringEndsWith(_appPathTranslated, '\\')) _appPathTranslated += "\\"; // IIS 6.0 doesn't add the trailing '\' // Increment incoming request length PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, _contentTotalLength); } internal virtual void ReadRequestBasics() { // Get requests basics int[] contentInfo = new int[4]; String[] basicStrings = ReadBasics(contentInfo); if (basicStrings == null || basicStrings.Length != 6) throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data)); // Remember content info _contentType = contentInfo[0]; _contentTotalLength = contentInfo[1]; _contentAvailLength = contentInfo[2]; _queryStringLength = contentInfo[3]; // Remember basic strings _method = basicStrings[0]; _filePath = basicStrings[1]; _pathInfo = basicStrings[2]; _path = (_pathInfo.Length > 0) ? (_filePath + _pathInfo) : _filePath; _pathTranslated = basicStrings[3]; _appPath = basicStrings[4]; _appPathTranslated = basicStrings[5]; } // // Public methods // internal static ISAPIWorkerRequest CreateWorkerRequest(IntPtr ecb, bool useOOP) { ISAPIWorkerRequest wr = null; if (useOOP) { EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero); if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, false); wr = new ISAPIWorkerRequestOutOfProc(ecb); } else { int version = UnsafeNativeMethods.EcbGetVersion(ecb) >> 16; if (version >= 7) { EtwTrace.TraceEnableCheck(EtwTraceConfigType.IIS7_ISAPI, ecb); } else { EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero); } if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, true); if (version >= 7) { wr = new ISAPIWorkerRequestInProcForIIS7(ecb); } else if (version == 6) { wr = new ISAPIWorkerRequestInProcForIIS6(ecb); } else { wr = new ISAPIWorkerRequestInProc(ecb); } } return wr; } public override String GetUriPath() { return _path; } public override String GetQueryString() { if (_queryStringLength == 0) return String.Empty; int size = _queryStringLength + 2; StringBuilder buf = new StringBuilder(size); int r = GetQueryStringCore(0, buf, size); if (r != 1) throw new HttpException(SR.GetString(SR.Cannot_get_query_string)); return buf.ToString(); } public override byte[] GetQueryStringRawBytes() { if (_queryStringLength == 0) return null; byte[] buf = new byte[_queryStringLength]; int r = GetQueryStringRawBytesCore(buf, _queryStringLength); if (r != 1) throw new HttpException(SR.GetString(SR.Cannot_get_query_string_bytes)); return buf; } public override String GetRawUrl() { String qs = GetQueryString(); if (!String.IsNullOrEmpty(qs)) return _path + "?" + qs; else return _path; } public override String GetHttpVerbName() { return _method; } public override String GetHttpVersion() { return GetServerVariable("SERVER_PROTOCOL"); } public override String GetRemoteAddress() { return GetServerVariable("REMOTE_ADDR"); } public override String GetRemoteName() { return GetServerVariable("REMOTE_HOST"); } public override int GetRemotePort() { return 0; // unknown in ISAPI } public override String GetLocalAddress() { return GetServerVariable("LOCAL_ADDR"); } public override int GetLocalPort() { return Int32.Parse(GetServerVariable("SERVER_PORT")); } internal override String GetLocalPortAsString() { return GetServerVariable("SERVER_PORT"); } public override String GetServerName() { return GetServerVariable("SERVER_NAME"); } public override bool IsSecure() { String https = GetServerVariable("HTTPS"); return (https != null && https.Equals("on")); } public override String GetFilePath() { return _filePath; } public override String GetFilePathTranslated() { return _pathTranslated; } public override String GetPathInfo() { return _pathInfo; } public override String GetAppPath() { return _appPath; } public override String GetAppPathTranslated() { return _appPathTranslated; } public override int GetPreloadedEntityBodyLength() { return _contentAvailLength; } public override int GetPreloadedEntityBody(byte[] buffer, int offset) { if (_contentAvailLength == 0) { return 0; } if (buffer.Length - offset < _contentAvailLength) { throw new ArgumentOutOfRangeException("offset"); } int r = GetPreloadedPostedContentCore(buffer, offset, _contentAvailLength); if (r < 0) throw new HttpException(SR.GetString(SR.Cannot_read_posted_data)); return r; } public override byte[] GetPreloadedEntityBody() { if (!_preloadedContentRead) { if (_contentAvailLength > 0) { _preloadedContent = new byte[_contentAvailLength]; int r = GetPreloadedPostedContentCore(_preloadedContent, 0, _contentAvailLength); if (r < 0) throw new HttpException(SR.GetString(SR.Cannot_read_posted_data)); } _preloadedContentRead = true; } return _preloadedContent; } public override bool IsEntireEntityBodyIsPreloaded() { return (_contentAvailLength == _contentTotalLength); } public override int GetTotalEntityBodyLength() { return _contentTotalLength; } public override int ReadEntityBody(byte[] buffer, int size) { return ReadEntityBody(buffer, 0, size); } public override int ReadEntityBody(byte[] buffer, int offset, int size) { if (buffer.Length - offset < size) { throw new ArgumentOutOfRangeException("offset"); } int r = GetAdditionalPostedContentCore(buffer, offset, size); if (r < 0) { throw new HttpException(SR.GetString(SR.Cannot_read_posted_data)); } return r; } public override long GetBytesRead() { throw new HttpException(SR.GetString(SR.Not_supported)); } public override String GetKnownRequestHeader(int index) { if (!_requestHeadersAvailable) { // special case important ones so that no all headers parsing is required switch (index) { case HeaderContentType: if (_contentType == CONTENT_FORM) return "application/x-www-form-urlencoded"; break; case HeaderContentLength: if (_contentType != CONTENT_NONE) return (_contentTotalLength).ToString(); break; } // parse all headers ReadRequestHeaders(); } return _knownRequestHeaders[index]; } public override String GetUnknownRequestHeader(String name) { if (!_requestHeadersAvailable) ReadRequestHeaders(); int n = _unknownRequestHeaders.Length; for (int i = 0; i < n; i++) { if (StringUtil.EqualsIgnoreCase(name, _unknownRequestHeaders[i][0])) return _unknownRequestHeaders[i][1]; } return null; } public override String[][] GetUnknownRequestHeaders() { if (!_requestHeadersAvailable) ReadRequestHeaders(); return _unknownRequestHeaders; } public override void SendStatus(int statusCode, String statusDescription) { _status.Append(statusCode.ToString()); _status.Append(" "); _status.Append(statusDescription); _statusSet = true; } internal override void SetHeaderEncoding(Encoding encoding) { _headerEncoding = encoding; } public override void SendKnownResponseHeader(int index, String value) { if (_headersSent) throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent)); _headers.Append(GetKnownResponseHeaderName(index)); _headers.Append(": "); _headers.Append(value); _headers.Append("\r\n"); if (index == HeaderContentLength) _contentLengthSent = true; else if (index == HeaderTransferEncoding && (value != null && value.Equals("chunked"))) _chunked = true; } public override void SendUnknownResponseHeader(String name, String value) { if (_headersSent) throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent)); _headers.Append(name); _headers.Append(": "); _headers.Append(value); _headers.Append("\r\n"); } public override void SendCalculatedContentLength(int contentLength) { SendCalculatedContentLength((long)contentLength); } // public override void SendCalculatedContentLength(long contentLength) { if (!_headersSent) { _headers.Append("Content-Length: "); _headers.Append(contentLength.ToString(CultureInfo.InvariantCulture)); _headers.Append("\r\n"); _contentLengthSent = true; } } public override bool HeadersSent() { return _headersSent; } public override bool IsClientConnected() { return (IsClientConnectedCore() == 0) ? false : true; } public override void CloseConnection() { CloseConnectionCore(); } public override void SendResponseFromMemory(byte[] data, int length) { if (!_headersSent) SendHeaders(); if (length > 0) AddBodyToCachedResponse(new MemoryBytes(data, length)); } public override void SendResponseFromMemory(IntPtr data, int length) { SendResponseFromMemory(data, length, false); } internal override void SendResponseFromMemory(IntPtr data, int length, bool isBufferFromUnmanagedPool) { if (!_headersSent) SendHeaders(); if (length > 0) AddBodyToCachedResponse(new MemoryBytes(data, length, isBufferFromUnmanagedPool ? BufferType.UnmanagedPool : BufferType.Managed)); } // PackageFile for in-proc case internal virtual MemoryBytes PackageFile(String filename, long offset64, long length64, bool isImpersonating) { // The offset and length must be less than Int32.MaxValue for in-proc. // This should be true, since HttpFileResponseElement.ctor throws ArgumentOutOfRangeException for in-proc Debug.Assert(offset64 < Int32.MaxValue); Debug.Assert(length64 < Int32.MaxValue); int offset = Convert.ToInt32(offset64); int length = Convert.ToInt32(length64); FileStream f = null; MemoryBytes bytes = null; try { Debug.Assert(offset < length); f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); Debug.Assert((f.Length - offset) == length); int size = (int) (f.Length - offset); byte[] fileBytes = new byte[size]; int bytesRead = f.Read(fileBytes, offset, size); bytes = new MemoryBytes(fileBytes, bytesRead); } finally { if (f != null) f.Close(); } return bytes; } internal override void TransmitFile(string filename, long offset, long length, bool isImpersonating) { if (!_headersSent) SendHeaders(); if (length == 0) return; AddBodyToCachedResponse(PackageFile(filename, offset, length, isImpersonating)); return; } public override void SendResponseFromFile(String filename, long offset, long length) { if (!_headersSent) SendHeaders(); if (length == 0) return; FileStream f = null; try { f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); SendResponseFromFileStream(f, offset, length); } finally { if (f != null) f.Close(); } } public override void SendResponseFromFile(IntPtr handle, long offset, long length) { if (!_headersSent) SendHeaders(); if (length == 0) return; FileStream f = null; try { f = new FileStream(new Microsoft.Win32.SafeHandles.SafeFileHandle(handle,false), FileAccess.Read); SendResponseFromFileStream(f, offset, length); } finally { if (f != null) f.Close(); } } public override void FlushResponse(bool finalFlush) { // only flush headers - the data is write through if (!_headersSent) SendHeaders(); FlushCachedResponse(finalFlush); } public override void EndOfRequest() { FlushCachedResponse(true); // recycle the headers and status buffers if (_headers != null) { _headers.Dispose(); _headers = null; } if (_status != null) { _status.Dispose(); _status = null; } CallEndOfRequestCallbackOnceAfterAllIoComplete(); } public override void SetEndOfSendNotification(HttpWorkerRequest.EndOfSendNotification callback, Object extraData) { if (_endOfRequestCallback != null) throw new InvalidOperationException(); _endOfRequestCallback = callback; _endOfRequestCallbackArg = extraData; _endOfRequestCallbackLockCount = 1; // when goes to 0 the callback is called } public override String MapPath(String path) { return HostingEnvironment.MapPathInternal(path); } public override String MachineConfigPath { get { return HttpConfigurationSystem.MachineConfigurationFilePath; } } public override String RootWebConfigPath { get { return HttpConfigurationSystem.RootWebConfigurationFilePath; } } public override String MachineInstallDirectory { get { return HttpRuntime.AspInstallDirectory; } } public override IntPtr GetUserToken() { return GetUserTokenCore(); } public override IntPtr GetVirtualPathToken() { return GetVirtualPathTokenCore(); } public override byte[] GetClientCertificate() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCert; } public override DateTime GetClientCertificateValidFrom() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertValidFrom; } public override DateTime GetClientCertificateValidUntil() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertValidUntil; } public override byte [] GetClientCertificateBinaryIssuer() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertBinaryIssuer; } public override int GetClientCertificateEncoding() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertEncoding; } public override byte [] GetClientCertificatePublicKey() { if (!_clientCertFetched) FetchClientCertificate(); return _clientCertPublicKey; } private void FetchClientCertificate() { if (_clientCertFetched) return; _clientCertFetched = true; byte[] buf = new byte[8192]; int[] pInts = new int[4]; long[] pDates = new long[2]; int iRet = GetClientCertificateCore(buf, pInts, pDates); if (iRet < 0 && (-iRet) > 8192) { iRet = -iRet + 100; buf = new byte[iRet]; iRet = GetClientCertificateCore(buf, pInts, pDates); } if (iRet > 0) { _clientCertEncoding = pInts[0]; if (pInts[1] < buf.Length && pInts[1] > 0) { _clientCert = new byte[pInts[1]]; Array.Copy(buf, _clientCert, pInts[1]); if (pInts[2] + pInts[1] < buf.Length && pInts[2] > 0) { _clientCertBinaryIssuer = new byte[pInts[2]]; Array.Copy(buf, pInts[1], _clientCertBinaryIssuer, 0, pInts[2]); } if (pInts[2] + pInts[1] + pInts[3] < buf.Length && pInts[3] > 0) { _clientCertPublicKey = new byte[pInts[3]]; Array.Copy(buf, pInts[1] + pInts[2], _clientCertPublicKey, 0, pInts[3]); } } } if (iRet > 0 && pDates[0] != 0) _clientCertValidFrom = DateTime.FromFileTime(pDates[0]); else _clientCertValidFrom = DateTime.Now; if (iRet > 0 && pDates[1] != 0) _clientCertValidUntil = DateTime.FromFileTime(pDates[1]); else _clientCertValidUntil = DateTime.Now; } // // internal methods specific to ISAPI // internal void AppendLogParameter(String logParam) { AppendLogParameterCore(logParam); } internal virtual void SendEmptyResponse() { } // // PInvoke callback wrappers -- overridden by the derived classes // internal abstract int GetBasicsCore(byte[] buffer, int size, int[] contentInfo); internal abstract int GetQueryStringCore(int encode, StringBuilder buffer, int size); internal abstract int GetQueryStringRawBytesCore(byte[] buffer, int size); internal abstract int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead); internal abstract int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize); internal abstract void FlushCore(byte[] status, byte[] header, int keepConnected, int totalBodySize, int numBodyFragments, IntPtr[] bodyFragments, int[] bodyFragmentLengths, int doneWithSession, int finalStatus, out bool async); internal abstract int IsClientConnectedCore(); internal abstract int CloseConnectionCore(); internal abstract int MapUrlToPathCore(String url, byte[] buffer, int size); internal abstract IntPtr GetUserTokenCore(); internal abstract IntPtr GetVirtualPathTokenCore(); internal abstract int AppendLogParameterCore(String logParam); internal abstract int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates); internal abstract int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut); } // // In-process ISAPIWorkerRequest // // Does queueing of IO operations. ISAPI only support one async IO at a time. // internal class ISAPIWorkerRequestInProc : ISAPIWorkerRequest { protected const int NUM_SERVER_VARIABLES = 35; // total number of variables that we commonly get protected const int NUM_BASIC_SERVER_VARIABLES = 12; // needed on every request protected const int NUM_ADDITIONAL_SERVER_VARIABLES = 23; // needed when HttpRequest.ServerVariables is populated // These constants must be kept in sync with g_szServerVariables and g_szUnicodeServerVariables in ecbdirect.cxx protected const int LOGON_USER = 0; protected const int AUTH_TYPE = 1; protected const int APPL_PHYSICAL_PATH = 2; protected const int REQUEST_METHOD = 3; protected const int PATH_INFO = 4; protected const int PATH_TRANSLATED = 5; protected const int URL = 6; protected const int CACHE_URL = 7; protected const int SERVER_NAME = 8; protected const int SERVER_PORT = 9; protected const int HTTPS = 10; protected const int ALL_RAW = 11; protected const int REMOTE_ADDR = 12; protected const int AUTH_PASSWORD = 13; protected const int CERT_COOKIE = 14; protected const int CERT_FLAGS = 15; protected const int CERT_ISSUER = 16; protected const int CERT_KEYSIZE = 17; protected const int CERT_SECRETKEYSIZE = 18; protected const int CERT_SERIALNUMBER = 19; protected const int CERT_SERVER_ISSUER = 20; protected const int CERT_SERVER_SUBJECT = 21; protected const int CERT_SUBJECT = 22; protected const int GATEWAY_INTERFACE = 23; protected const int HTTPS_KEYSIZE = 24; protected const int HTTPS_SECRETKEYSIZE = 25; protected const int HTTPS_SERVER_ISSUER = 26; protected const int HTTPS_SERVER_SUBJECT = 27; protected const int INSTANCE_ID = 28; protected const int INSTANCE_META_PATH = 29; protected const int LOCAL_ADDR = 30; protected const int REMOTE_HOST = 31; protected const int REMOTE_PORT = 32; protected const int SERVER_PROTOCOL = 33; protected const int SERVER_SOFTWARE = 34; // storage for common server variables protected string[] _basicServerVars = null; protected string[] _additionalServerVars = null; internal ISAPIWorkerRequestInProc(IntPtr ecb) : base(ecb) { if (ecb == IntPtr.Zero || UnsafeNativeMethods.EcbGetTraceContextId(ecb, out _traceId) != 1) { _traceId = Guid.Empty; } } internal override int GetBasicsCore(byte[] buffer, int size, int[] contentInfo) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbGetBasics(_ecb, buffer, size, contentInfo); } internal override int GetQueryStringCore(int encode, StringBuilder buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbGetQueryString(_ecb, encode, buffer, size); } internal override int GetQueryStringRawBytesCore(byte[] buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbGetQueryStringRawBytes(_ecb, buffer, size); } internal override int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead) { if (_ecb == IntPtr.Zero) return 0; int rc = UnsafeNativeMethods.EcbGetPreloadedPostedContent(_ecb, bytes, offset, numBytesToRead); if (rc > 0) PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc); return rc; } internal override int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize) { if (_ecb == IntPtr.Zero) return 0; int rc = UnsafeNativeMethods.EcbGetAdditionalPostedContent(_ecb, bytes, offset, bufferSize); if (rc > 0) PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc); return rc; } internal override int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbGetClientCertificate(_ecb, buffer, buffer.Length, pInts, pDates); } internal override int IsClientConnectedCore() { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbIsClientConnected(_ecb); } internal override void FlushCore(byte[] status, byte[] header, int keepConnected, int totalBodySize, int numBodyFragments, IntPtr[] bodyFragments, int[] bodyFragmentLengths, int doneWithSession, int finalStatus, out bool async) { async = false; if (_ecb == IntPtr.Zero) return; UnsafeNativeMethods.EcbFlushCore( _ecb, status, header, keepConnected, totalBodySize, numBodyFragments, bodyFragments, bodyFragmentLengths, doneWithSession, finalStatus, 0, 0, null); } internal override int CloseConnectionCore() { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbCloseConnection(_ecb); } internal override int MapUrlToPathCore(String url, byte[] buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbMapUrlToPath(_ecb, url, buffer, size); } internal override IntPtr GetUserTokenCore() { if (_token == IntPtr.Zero && _ecb != IntPtr.Zero) _token = UnsafeNativeMethods.EcbGetImpersonationToken(_ecb, IntPtr.Zero); return _token; } internal override IntPtr GetVirtualPathTokenCore() { if (_token == IntPtr.Zero && _ecb != IntPtr.Zero) _token = UnsafeNativeMethods.EcbGetVirtualPathToken(_ecb, IntPtr.Zero); return _token; } internal override int AppendLogParameterCore(String logParam) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbAppendLogParameter(_ecb, logParam); } // ISAPIWorkerRequestInProc protected virtual String GetServerVariableCore(String name) { if (_ecb == IntPtr.Zero) return null; String value = null; RecyclableByteBuffer buf = new RecyclableByteBuffer(); int retVal = UnsafeNativeMethods.EcbGetServerVariable(_ecb, name, buf.Buffer, buf.Buffer.Length); while (retVal < 0) { buf.Resize(-retVal); // buffer not big enough retVal = UnsafeNativeMethods.EcbGetServerVariable(_ecb, name, buf.Buffer, buf.Buffer.Length); } if (retVal > 0) value = buf.GetDecodedString(Encoding.UTF8, retVal); buf.Dispose(); return value; } // ISAPIWorkerRequestInProc protected virtual void GetAdditionalServerVariables() { if (_ecb == IntPtr.Zero) return; // _additionalServerVars should only be initialized once Debug.Assert(_additionalServerVars == null); if (_additionalServerVars != null) return; _additionalServerVars = new string[NUM_ADDITIONAL_SERVER_VARIABLES]; for(int i = 0; i < _additionalServerVars.Length; i++) { int nameIndex = i + NUM_BASIC_SERVER_VARIABLES; RecyclableByteBuffer buf = new RecyclableByteBuffer(); int retVal = UnsafeNativeMethods.EcbGetServerVariableByIndex(_ecb, nameIndex, buf.Buffer, buf.Buffer.Length); while (retVal < 0) { buf.Resize(-retVal); // buffer not big enough retVal = UnsafeNativeMethods.EcbGetServerVariableByIndex(_ecb, nameIndex, buf.Buffer, buf.Buffer.Length); } if (retVal > 0) _additionalServerVars[i] = buf.GetDecodedString(Encoding.UTF8, retVal); buf.Dispose(); } } private String GetAdditionalServerVar(int index) { if (_additionalServerVars == null) GetAdditionalServerVariables(); return _additionalServerVars[index - NUM_BASIC_SERVER_VARIABLES]; } public override String GetServerVariable(String name) { // this switch statement is a little more than twice as fast as a Hashtable lookup if (name != null) { switch (name.Length) { case 20: if (name == "HTTPS_SERVER_SUBJECT") return GetAdditionalServerVar(HTTPS_SERVER_SUBJECT); break; case 19: if (name == "HTTPS_SECRETKEYSIZE") return GetAdditionalServerVar(HTTPS_SECRETKEYSIZE); else if (name == "CERT_SERVER_SUBJECT") return GetAdditionalServerVar(CERT_SERVER_SUBJECT); else if (name == "HTTPS_SERVER_ISSUER") return GetAdditionalServerVar(HTTPS_SERVER_ISSUER); break; case 18: if (name == "INSTANCE_META_PATH") return GetAdditionalServerVar(INSTANCE_META_PATH); else if (name == "CERT_SECRETKEYSIZE") return GetAdditionalServerVar(CERT_SECRETKEYSIZE); else if (name == "CERT_SERVER_ISSUER") return GetAdditionalServerVar(CERT_SERVER_ISSUER); break; case 17: if (name == "CERT_SERIALNUMBER") return GetAdditionalServerVar(CERT_SERIALNUMBER); else if (name == "GATEWAY_INTERFACE") return GetAdditionalServerVar(GATEWAY_INTERFACE); break; case 15: if (name == "HTTP_USER_AGENT") return GetKnownRequestHeader(HeaderUserAgent); else if (name == "SERVER_PROTOCOL") return GetAdditionalServerVar(SERVER_PROTOCOL); else if (name == "SERVER_SOFTWARE") return GetAdditionalServerVar(SERVER_SOFTWARE); break; case 13: if (name == "AUTH_PASSWORD") return GetAdditionalServerVar(AUTH_PASSWORD); else if (name == "HTTPS_KEYSIZE") return GetAdditionalServerVar(HTTPS_KEYSIZE); break; case 12: if (name == "CERT_KEYSIZE") return GetAdditionalServerVar(CERT_KEYSIZE); else if (name == "CERT_SUBJECT") return GetAdditionalServerVar(CERT_SUBJECT); break; case 11: if (name == "SERVER_NAME") return _basicServerVars[SERVER_NAME]; else if (name == "SERVER_PORT") return _basicServerVars[SERVER_PORT]; else if (name == "REMOTE_HOST") return GetAdditionalServerVar(REMOTE_HOST); else if (name == "REMOTE_PORT") return GetAdditionalServerVar(REMOTE_PORT); else if (name == "REMOTE_ADDR") return GetAdditionalServerVar(REMOTE_ADDR); else if (name == "CERT_COOKIE") return GetAdditionalServerVar(CERT_COOKIE); else if (name == "CERT_ISSUER") return GetAdditionalServerVar(CERT_ISSUER); else if (name == "INSTANCE_ID") return GetAdditionalServerVar(INSTANCE_ID); break; case 10: if (name == "LOGON_USER") return _basicServerVars[LOGON_USER]; else if (name == "LOCAL_ADDR") return GetAdditionalServerVar(LOCAL_ADDR); else if (name == "CERT_FLAGS") return GetAdditionalServerVar(CERT_FLAGS); break; case 9: if (name == "AUTH_TYPE") return _basicServerVars[AUTH_TYPE]; break; case 7: if (name == "ALL_RAW") { return _basicServerVars[ALL_RAW]; } break; case 5: if (name == "HTTPS") return _basicServerVars[HTTPS]; break; } } // this is not a common server variable return GetServerVariableCore(name); } internal override int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.EcbCallISAPI(_ecb, iFunction, bufIn, bufIn.Length, bufOut, bufOut.Length); } } // // In-process ISAPIWorkerRequest specific for IIS7 // // Uses unicode server vars // internal class ISAPIWorkerRequestInProcForIIS7 : ISAPIWorkerRequestInProcForIIS6 { private string _rawUrl; private bool _isRewriteModuleEnabled; internal ISAPIWorkerRequestInProcForIIS7(IntPtr ecb) : base(ecb) { _trySkipIisCustomErrors = true; } internal override bool IsRewriteModuleEnabled { get { // ensure GetRawUrl has been called, since it sets _isRewriteModuleEnabled if (_rawUrl == null) { GetRawUrl(); } return _isRewriteModuleEnabled; } } // If the URL Rewrite module has rewritten the URL, then "IIS_Was_Url_Rewritten" will be "1", // and we will attempt to derive RawUrl from the "CACHE_URL" server variable. If the URL has not been // rewritten, or there's a problem, return null. private string GetRequestUri() { // if it hasn't been rewritten, return null if (GetUnicodeServerVariable("UNICODE_IIS_WasUrlRewritten") != "1") { return null; } // cacheUrl has format "[http|https]://[server]:[port][uri]", including query string and path-info, if they exist. string cacheUrl = GetUnicodeServerVariable(CACHE_URL); // if there's a problem, return null if (cacheUrl == null) { return null; } // the URI begins at the 3rd slash int count = 0; for(int index = 0; index < cacheUrl.Length; index++) { if (cacheUrl[index] == '/') { if (++count == 3) { return cacheUrl.Substring(index); } } } // someone must have modified CACHE_URL, it is not valid return null; } public override String GetRawUrl() { if (_rawUrl == null) { // is the URL Rewrite module enabled? _rawUrl = GetRequestUri(); if (_rawUrl != null) { _isRewriteModuleEnabled = true; return _rawUrl; } _rawUrl = base.GetRawUrl(); } return _rawUrl; } internal override bool TrySkipIisCustomErrors { get { return _trySkipIisCustomErrors; } set { _trySkipIisCustomErrors = value; } } internal override void RaiseTraceEvent(IntegratedTraceType traceType, string eventData) { if (IntPtr.Zero != _ecb) { // the area is derivative of the type, either page or module int areaFlag = (traceType < IntegratedTraceType.DiagCritical) ? EtwTraceFlags.Page : EtwTraceFlags.Module; if (EtwTrace.IsTraceEnabled(EtwTrace.InferVerbosity(traceType), areaFlag)) { string message = String.IsNullOrEmpty(eventData) ? String.Empty : eventData; UnsafeNativeMethods.EcbEmitSimpleTrace(_ecb, (int)traceType, message); } } } internal override void RaiseTraceEvent(WebBaseEvent webEvent) { if (IntPtr.Zero != _ecb) { if (EtwTrace.IsTraceEnabled(webEvent.InferEtwTraceVerbosity(), EtwTraceFlags.Infrastructure)) { int fieldCount; string[] fieldNames; int[] fieldTypes; string[] fieldData; int webEventType; webEvent.DeconstructWebEvent(out webEventType, out fieldCount, out fieldNames, out fieldTypes, out fieldData); UnsafeNativeMethods.EcbEmitWebEventTrace(_ecb, webEventType, fieldCount, fieldNames, fieldTypes, fieldData); } } } } // // In-process ISAPIWorkerRequest specific for IIS6 // // Uses unicode server vars // internal class ISAPIWorkerRequestInProcForIIS6 : ISAPIWorkerRequestInProc { private static int _asyncIoCount; internal ISAPIWorkerRequestInProcForIIS6(IntPtr ecb) : base(ecb) { } internal static void WaitForPendingAsyncIo() { while(_asyncIoCount != 0) { Thread.Sleep(250); } } internal override void SendEmptyResponse() { // facilitate health monitoring for IIS6 -- update last activity timestamp // to avoid deadlock detection UnsafeNativeMethods.UpdateLastActivityTimeForHealthMonitor(); } internal override void ReadRequestBasics() { if (_ecb == IntPtr.Zero) return; // set server variables needed for request basics and Indigo ( GetBasicServerVariables(); // _pathInfo is the difference between UNICODE_PATH_INFO and UNICODE_URL int lengthDiff = _path.Length - _filePath.Length; if (lengthDiff > 0) { _pathInfo = _path.Substring(_filePath.Length); int pathTranslatedLength = _pathTranslated.Length - lengthDiff; if (pathTranslatedLength > 0) { _pathTranslated = _pathTranslated.Substring(0, pathTranslatedLength); } } else { _filePath = _path; _pathInfo = String.Empty; } _appPath = HostingEnvironment.ApplicationVirtualPath; // // other (int) request basics // int[] contentInfo = null; try { contentInfo = RecyclableArrayHelper.GetIntegerArray(4); UnsafeNativeMethods.EcbGetBasicsContentInfo(_ecb, contentInfo); _contentType = contentInfo[0]; _contentTotalLength = contentInfo[1]; _contentAvailLength = contentInfo[2]; _queryStringLength = contentInfo[3]; } finally { RecyclableArrayHelper.ReuseIntegerArray(contentInfo); } } private void GetBasicServerVariables() { if (_ecb == IntPtr.Zero) return; // _basicServerVars should only be initialized once Debug.Assert(_basicServerVars == null); if (_basicServerVars != null) return; _basicServerVars = new string[NUM_BASIC_SERVER_VARIABLES]; ServerVarCharBuffer buffer = new ServerVarCharBuffer(); try { int[] serverVarLengths = new int[NUM_BASIC_SERVER_VARIABLES]; int r = 0; int hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length, serverVarLengths, serverVarLengths.Length, 0, ref r); if (r > buffer.Length) { buffer.Resize(r); hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length, serverVarLengths, serverVarLengths.Length, 0, ref r); } Misc.ThrowIfFailedHr(hresult); IntPtr current = buffer.PinnedAddress; for(int i = 0; i < _basicServerVars.Length; i++) { _basicServerVars[i] = Marshal.PtrToStringUni(current, serverVarLengths[i]); current = new IntPtr((long)current + 2L * (1L + serverVarLengths[i])); } // special case variables _appPathTranslated = _basicServerVars[APPL_PHYSICAL_PATH]; _method = _basicServerVars[REQUEST_METHOD]; _path = _basicServerVars[PATH_INFO]; _pathTranslated = _basicServerVars[PATH_TRANSLATED]; _filePath = _basicServerVars[URL]; } finally { buffer.Dispose(); } } // ISAPIWorkerRequestInProcForIIS6 protected override void GetAdditionalServerVariables() { if (_ecb == IntPtr.Zero) return; // _additionalServerVars should only be initialized once Debug.Assert(_additionalServerVars == null); if (_additionalServerVars != null) return; _additionalServerVars = new string[NUM_ADDITIONAL_SERVER_VARIABLES]; ServerVarCharBuffer buffer = new ServerVarCharBuffer(); try { int[] serverVarLengths = new int[NUM_ADDITIONAL_SERVER_VARIABLES]; int r = 0; int hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length, serverVarLengths, serverVarLengths.Length, NUM_BASIC_SERVER_VARIABLES, ref r); if (r > buffer.Length) { buffer.Resize(r); hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length, serverVarLengths, serverVarLengths.Length, NUM_BASIC_SERVER_VARIABLES, ref r); } if (hresult != 0) Marshal.ThrowExceptionForHR(hresult); IntPtr current = buffer.PinnedAddress; for(int i = 0; i < _additionalServerVars.Length; i++) { _additionalServerVars[i] = Marshal.PtrToStringUni(current, serverVarLengths[i]); current = new IntPtr((long)current + 2L * (1L + serverVarLengths[i])); } } finally { buffer.Dispose(); } } // ISAPIWorkerRequestInProcForIIS6 protected override string GetServerVariableCore(string name) { if (StringUtil.StringStartsWith(name, "HTTP_")) // fall back for headers (IIS6 doesn't support them as UNICODE_XXX) return base.GetServerVariableCore(name); else return GetUnicodeServerVariable("UNICODE_" + name); } protected internal String GetUnicodeServerVariable(String name) { String value = null; ServerVarCharBuffer buf = new ServerVarCharBuffer(); try { value = GetUnicodeServerVariable(name, buf); } finally { buf.Dispose(); } return value; } protected internal String GetUnicodeServerVariable(int nameIndex) { String value = null; ServerVarCharBuffer buf = new ServerVarCharBuffer(); try { value = GetUnicodeServerVariable(nameIndex, buf); } finally { buf.Dispose(); } return value; } private String GetUnicodeServerVariable(String name, ServerVarCharBuffer buffer) { if (_ecb == IntPtr.Zero) return null; int r = UnsafeNativeMethods.EcbGetUnicodeServerVariable(_ecb, name, buffer.PinnedAddress, buffer.Length); if (r < 0) { buffer.Resize(-r); r = UnsafeNativeMethods.EcbGetUnicodeServerVariable(_ecb, name, buffer.PinnedAddress, buffer.Length); } if (r > 0) return Marshal.PtrToStringUni(buffer.PinnedAddress, r); else return null; } private String GetUnicodeServerVariable(int nameIndex, ServerVarCharBuffer buffer) { if (_ecb == IntPtr.Zero) return null; int r = UnsafeNativeMethods.EcbGetUnicodeServerVariableByIndex(_ecb, nameIndex, buffer.PinnedAddress, buffer.Length); if (r < 0) { buffer.Resize(-r); r = UnsafeNativeMethods.EcbGetUnicodeServerVariableByIndex(_ecb, nameIndex, buffer.PinnedAddress, buffer.Length); } if (r > 0) return Marshal.PtrToStringUni(buffer.PinnedAddress, r); else return null; } // // Support for async VectorSend and kernel mode cache on IIS6 // private const int MIN_ASYNC_SIZE = 2048; private GCHandle _rootedThis; // for the duration of async private ISAPIAsyncCompletionCallback _asyncFlushCompletionCallback; private int _asyncFinalStatus; private bool _serverSupportFunctionError = false; private IntPtr _entity; // pointer to HSE_entity private bool _cacheInKernelMode = false; private bool _disableKernelCache = false; protected bool _trySkipIisCustomErrors = false; private const int TRY_SKIP_IIS_CUSTOM_ERRORS = 0x40; // PackageFile for IIS6 internal override MemoryBytes PackageFile(string filename, long offset, long size, bool isImpersonating) { return new MemoryBytes(filename, offset, size); } // internal override bool SupportsLongTransmitFile { get { return true; } } internal override void FlushCore(byte[] status, byte[] header, int keepConnected, int totalBodySize, int numBodyFragments, IntPtr[] bodyFragments, int[] bodyFragmentLengths, int doneWithSession, int finalStatus, out bool async) { async = false; if (_ecb == IntPtr.Zero) return; if (_headersSentFromExecuteUrl) { // don't send headers twice status = null; header = null; } // async only for large responses and only on the last flush // don't do async if shutting down (async IO holds up app domain shutdown) if (doneWithSession != 0 && !HttpRuntime.ShutdownInProgress && (_ignoreMinAsyncSize || (totalBodySize >= MIN_ASYNC_SIZE))) { if (_requiresAsyncFlushCallback) { _asyncFlushCompletionCallback = new ISAPIAsyncCompletionCallback(OnAsyncFlushCompletion); _asyncFinalStatus = finalStatus; // remember to pass to DoneWithSession on completion _rootedThis = GCHandle.Alloc(this); // root for the duration of IO doneWithSession = 0; // will do on completion async = true; Interlocked.Increment(ref _asyncIoCount); // increment async io count } else { // buffers are native, so we don't need to return to managed code _asyncFlushCompletionCallback = null; doneWithSession = 0; // will do on completion async = true; } } // 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 Debug.Assert(0 <= finalStatus && finalStatus <= 2); int flags = _trySkipIisCustomErrors ? finalStatus|TRY_SKIP_IIS_CUSTOM_ERRORS : finalStatus; int rc = UnsafeNativeMethods.EcbFlushCore( _ecb, status, header, keepConnected, totalBodySize, numBodyFragments, bodyFragments, bodyFragmentLengths, doneWithSession, flags, _cacheInKernelMode ? 1 : 0, async ? 1 : 0, _asyncFlushCompletionCallback); if (!_requiresAsyncFlushCallback && rc == 0 && async) { // unlock and reset cached response UnlockCachedResponseBytesOnceAfterIoComplete(); CallEndOfRequestCallbackOnceAfterAllIoComplete(); } else if (rc != 0 && async) { // on async failure default to sync path async = false; // call DoneWithSession UnsafeNativeMethods.EcbFlushCore(_ecb, null, null, 0, 0, 0, null, null, 1, _asyncFinalStatus, 0, 0, null); if (_asyncFlushCompletionCallback != null) { // unroot _rootedThis.Free(); // decrement async io count Interlocked.Decrement(ref _asyncIoCount); } } else if (rc != 0 && !async && doneWithSession == 0 && !_serverSupportFunctionError) { //on non-async failure stop executing the request //only throw once _serverSupportFunctionError = true; string message = SR.Server_Support_Function_Error; //give different error if connection was closed if (rc == HResults.WSAECONNABORTED || rc == HResults.WSAECONNRESET) { message = SR.Server_Support_Function_Error_Disconnect; PerfCounters.IncrementGlobalCounter(GlobalPerfCounter.REQUESTS_DISCONNECTED); } throw new HttpException(SR.GetString(message, rc.ToString("X8", CultureInfo.InvariantCulture)), rc); } } private void OnAsyncFlushCompletion(IntPtr ecb, int byteCount, int error) { try { // unroot _rootedThis.Free(); // call DoneWithSession UnsafeNativeMethods.EcbFlushCore(ecb, null, null, 0, 0, 0, null, null, 1, _asyncFinalStatus, 0, 0, null); // unlock pinned memory (at the latest of this completion and exit from the FlushCore on stack) UnlockCachedResponseBytesOnceAfterIoComplete(); // Revert any impersonation set by IIS UnsafeNativeMethods.RevertToSelf(); // call the HttpRuntime to recycle buffers (at the latest of this completion and EndRequest) CallEndOfRequestCallbackOnceAfterAllIoComplete(); } finally { // decrement async io count Interlocked.Decrement(ref _asyncIoCount); } } // WOS 1555777: kernel cache support // If the response can be kernel cached, return the kernel cache key; // otherwise return null. The kernel cache key is used to invalidate // the entry if a dependency changes or the item is flushed from the // managed cache for any reason. internal override string SetupKernelCaching(int secondsToLive, string originalCacheUrl, bool enableKernelCacheForVaryByStar) { // if someone called DisableKernelCache, don't setup kernel caching if (_ecb == IntPtr.Zero || _disableKernelCache) return null; string cacheUrl = GetUnicodeServerVariable(CACHE_URL); // if we're re-inserting the response into the kernel cache, the original key must match if (originalCacheUrl != null && originalCacheUrl != cacheUrl) { return null; } // If the request contains a query string, don't kernel cache the entry if (String.IsNullOrEmpty(cacheUrl) || (!enableKernelCacheForVaryByStar && cacheUrl.IndexOf('?') != -1)) { return null; } // enable kernel caching (IIS will set the HTTP_CACHE_POLICY when we call VectorSend) _cacheInKernelMode = true; // okay, the response will be kernel cached, here's the key return cacheUrl; } // WOS 1555777: kernel cache support internal override void DisableKernelCache() { _disableKernelCache = true; _cacheInKernelMode = false; } // // ExecuteUrl support // private ISAPIAsyncCompletionCallback _executeUrlCompletionCallback; private HttpAsyncResult _asyncResultOfExecuteUrl; private bool _headersSentFromExecuteUrl; internal override bool SupportsExecuteUrl { get { return true; } } internal override IAsyncResult BeginExecuteUrl( String url, String method, String childHeaders, bool sendHeaders, bool addUserIndo, IntPtr token, String name, String authType, byte[] entity, AsyncCallback cb, Object state) { if (_ecb == IntPtr.Zero || // after done with session _asyncResultOfExecuteUrl != null || // another ExecuteUrl in progress (sendHeaders && HeadersSent())) // asked to send headers, but already sent them { throw new InvalidOperationException(SR.GetString(SR.Cannot_execute_url_in_this_context)); } if (entity != null && entity.Length > 0) { int ret = UnsafeNativeMethods.EcbGetExecUrlEntityInfo(entity.Length, entity, out _entity); if (ret != 1) { throw new HttpException(SR.GetString(SR.Failed_to_execute_url)); } } Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl: url=\"" + url + "\"."); HttpAsyncResult ar = new HttpAsyncResult(cb, state); _asyncResultOfExecuteUrl = ar; _executeUrlCompletionCallback = new ISAPIAsyncCompletionCallback(OnExecuteUrlCompletion); _rootedThis = GCHandle.Alloc(this); // root for the duration of ExecuteUrl int rc = UnsafeNativeMethods.EcbExecuteUrlUnicode(_ecb, url, method, childHeaders, sendHeaders, addUserIndo, token, name, authType, _entity, _executeUrlCompletionCallback); if (rc != 1) { if (_entity != IntPtr.Zero) { UnsafeNativeMethods.EcbFreeExecUrlEntityInfo(_entity); } _rootedThis.Free(); _asyncResultOfExecuteUrl = null; Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl: failed!"); throw new HttpException(SR.GetString(SR.Failed_to_execute_url)); } if (sendHeaders) { // ExecuteUrl will send headers, worker request should not _headersSentFromExecuteUrl = true; } return ar; } internal override void EndExecuteUrl(IAsyncResult result) { Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.EndExecuteUrl"); HttpAsyncResult asyncResult = result as HttpAsyncResult; if (asyncResult != null) { asyncResult.End(); } } private void OnExecuteUrlCompletion(IntPtr ecb, int byteCount, int error) { if (_entity != IntPtr.Zero) { UnsafeNativeMethods.EcbFreeExecUrlEntityInfo(_entity); } _rootedThis.Free(); Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.OnExecuteUrlCompletion"); // signal async caller to resume work HttpAsyncResult asyncResult = _asyncResultOfExecuteUrl; _asyncResultOfExecuteUrl = null; asyncResult.Complete(false, null, null); } } // // Out-of-process worker request // internal class ISAPIWorkerRequestOutOfProc : ISAPIWorkerRequest { // sends chunks separately if the total length exceeds the following // to relieve the memory pressure on named pipes const int PM_FLUSH_THRESHOLD = 31*1024; internal ISAPIWorkerRequestOutOfProc(IntPtr ecb) : base(ecb) { UnsafeNativeMethods.PMGetTraceContextId(ecb, out _traceId); } private bool _useBaseTime = false; private const int _numServerVars = 32; private IDictionary _serverVars; private static String[] _serverVarNames = new String[_numServerVars] { "APPL_MD_PATH", /* this one is not UTF8 so we don't decode it here */ "ALL_RAW", "AUTH_PASSWORD", "AUTH_TYPE", "CERT_COOKIE", "CERT_FLAGS", "CERT_ISSUER", "CERT_KEYSIZE", "CERT_SECRETKEYSIZE", "CERT_SERIALNUMBER", "CERT_SERVER_ISSUER", "CERT_SERVER_SUBJECT", "CERT_SUBJECT", "GATEWAY_INTERFACE", "HTTP_COOKIE", "HTTP_USER_AGENT", "HTTPS", "HTTPS_KEYSIZE", "HTTPS_SECRETKEYSIZE", "HTTPS_SERVER_ISSUER", "HTTPS_SERVER_SUBJECT", "INSTANCE_ID", "INSTANCE_META_PATH", "LOCAL_ADDR", "LOGON_USER", "REMOTE_ADDR", "REMOTE_HOST", "SERVER_NAME", "SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SOFTWARE", "REMOTE_PORT" }; private void GetAllServerVars() { if (_ecb == IntPtr.Zero) return; RecyclableByteBuffer buf = new RecyclableByteBuffer(); int r = UnsafeNativeMethods.PMGetAllServerVariables(_ecb, buf.Buffer, buf.Buffer.Length); while (r < 0) { buf.Resize(-r); // buffer not big enough r = UnsafeNativeMethods.PMGetAllServerVariables(_ecb, buf.Buffer, buf.Buffer.Length); } if (r == 0) throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data)); // stub out first server var is it could contain non-UTF8 data // convert to characters and split the buffer into strings using default request encoding String[] ss = buf.GetDecodedTabSeparatedStrings(Encoding.Default, _numServerVars-1, 1); // recycle buffers buf.Dispose(); // fill in the hashtable _serverVars = new Hashtable(_numServerVars, StringComparer.OrdinalIgnoreCase); _serverVars.Add("APPL_MD_PATH", HttpRuntime.AppDomainAppIdInternal); for (int i = 1; i < _numServerVars; i++) { // starting with 1 to skip APPL_MD_PATH _serverVars.Add(_serverVarNames[i], ss[i-1]); } } internal override int GetBasicsCore(byte[] buffer, int size, int[] contentInfo) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMGetBasics(_ecb, buffer, size, contentInfo); } internal override int GetQueryStringCore(int encode, StringBuilder buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMGetQueryString(_ecb, encode, buffer, size); } internal override int GetQueryStringRawBytesCore(byte[] buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMGetQueryStringRawBytes(_ecb, buffer, size); } internal override int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead) { if (_ecb == IntPtr.Zero) return 0; int rc = UnsafeNativeMethods.PMGetPreloadedPostedContent(_ecb, bytes, offset, numBytesToRead); if (rc > 0) PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc); return rc; } internal override int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize) { if (_ecb == IntPtr.Zero) return 0; int rc = UnsafeNativeMethods.PMGetAdditionalPostedContent(_ecb, bytes, offset, bufferSize); if (rc > 0) PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc); return rc; } internal override int IsClientConnectedCore() { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMIsClientConnected(_ecb); } // PackageFile for IIS5 internal override MemoryBytes PackageFile(string filename, long offset64, long length64, bool isImpersonating) { // The offset and length must be less than Int32.MaxValue for IIS5. // This should be true, since HttpFileResponseElement.ctor throws ArgumentOutOfRangeException for IIS5. Debug.Assert(offset64 < Int32.MaxValue); Debug.Assert(length64 < Int32.MaxValue); int offset = Convert.ToInt32(offset64); int length = Convert.ToInt32(length64); byte[] offsetBytes = BitConverter.GetBytes(offset); byte[] lengthBytes = BitConverter.GetBytes(length); byte[] nameBytes = Encoding.Unicode.GetBytes(filename); // buffer consists of 1 byte for impersonation flag, 3 bytes for alignment, // 4 bytes for offset, 4 bytes for length, n bytes for file name, 2 bytes for null terminator byte[] buffer = new byte[4 + offsetBytes.Length + lengthBytes.Length + nameBytes.Length + 2]; // first byte indicates whether impersonation is used if (isImpersonating) buffer[0] = 0x31; else buffer[0] = 0x30; // bytes 2 thru 4 are unused for alignment // bytes 5 thru 8 are the offset Buffer.BlockCopy(offsetBytes, 0, buffer, 4, offsetBytes.Length); // bytes 9 thru 12 are the length Buffer.BlockCopy(lengthBytes, 0, buffer, 4 + offsetBytes.Length, lengthBytes.Length); // last two bytes are 0 for null terminator Buffer.BlockCopy(nameBytes, 0, buffer, 4 + offsetBytes.Length + lengthBytes.Length, nameBytes.Length); return new MemoryBytes(buffer, buffer.Length, true, length); } internal override void FlushCore(byte[] status, byte[] header, int keepConnected, int totalBodySize, int numBodyFragments, IntPtr[] bodyFragments, int[] bodyFragmentLengths, int doneWithSession, int finalStatus, out bool async) { async = false; if (_ecb == IntPtr.Zero) return; if (numBodyFragments > 1) { // Don't flush all at once if the length is over the threshold int i = 0; while (i < numBodyFragments) { bool first = (i == 0); int size = bodyFragmentLengths[i]; bool useTransmitFile = (bodyFragmentLengths[i] < 0); int idx = i+1; if (!useTransmitFile) { while (idx < numBodyFragments && size + bodyFragmentLengths[idx] < PM_FLUSH_THRESHOLD && bodyFragmentLengths[idx] >= 0) { size += bodyFragmentLengths[idx]; idx++; } } bool last = (idx == numBodyFragments); // bodyFragmentLength is negative for TransmitFile, but totalBodySize argument must be positive. if (useTransmitFile) size = -size; UnsafeNativeMethods.PMFlushCore( _ecb, first ? status : null, first ? header : null, keepConnected, size, i, idx-i, bodyFragments, bodyFragmentLengths, last ? doneWithSession : 0, last ? finalStatus : 0); i = idx; } } else { // Everything in one chunk UnsafeNativeMethods.PMFlushCore( _ecb, status, header, keepConnected, totalBodySize, 0, numBodyFragments, bodyFragments, bodyFragmentLengths, doneWithSession, finalStatus); } } internal override int CloseConnectionCore() { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMCloseConnection(_ecb); } internal override int MapUrlToPathCore(String url, byte[] buffer, int size) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMMapUrlToPath(_ecb, url, buffer, size); } internal override IntPtr GetUserTokenCore() { if (_token == IntPtr.Zero && _ecb != IntPtr.Zero) _token = UnsafeNativeMethods.PMGetImpersonationToken(_ecb); return _token; } internal override IntPtr GetVirtualPathTokenCore() { if (_token == IntPtr.Zero && _ecb != IntPtr.Zero) _token = UnsafeNativeMethods.PMGetVirtualPathToken(_ecb); return _token; } internal override int AppendLogParameterCore(String logParam) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMAppendLogParameter(_ecb, logParam); } internal override int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMGetClientCertificate(_ecb, buffer, buffer.Length, pInts, pDates); } public override String GetServerVariable(String name) { // PATH_TRANSLATED is mangled -- do not use the original server variable if (name.Equals("PATH_TRANSLATED")) return GetFilePathTranslated(); if (_serverVars == null) GetAllServerVars(); return (String)_serverVars[name]; } internal override int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut) { if (_ecb == IntPtr.Zero) return 0; return UnsafeNativeMethods.PMCallISAPI(_ecb, iFunction, bufIn, bufIn.Length, bufOut, bufOut.Length); } internal override void SendEmptyResponse() { if (_ecb == IntPtr.Zero) return; UnsafeNativeMethods.PMEmptyResponse(_ecb); } internal override DateTime GetStartTime() { if (_ecb == IntPtr.Zero || _useBaseTime) return base.GetStartTime(); long fileTime = UnsafeNativeMethods.PMGetStartTimeStamp(_ecb); return DateTimeUtil.FromFileTimeToUtc(fileTime); } internal override void ResetStartTime() { base.ResetStartTime(); _useBaseTime = true; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SortDescriptionCollection.cs
- XmlQueryTypeFactory.cs
- InternalPermissions.cs
- WindowsContainer.cs
- InfoCardAsymmetricCrypto.cs
- SynchronizingStream.cs
- ValidationResult.cs
- wgx_exports.cs
- ViewSimplifier.cs
- PaintEvent.cs
- TrustLevelCollection.cs
- XsltQilFactory.cs
- DesignTimeParseData.cs
- XmlSchemaGroup.cs
- VerticalAlignConverter.cs
- NavigateEvent.cs
- ZipPackagePart.cs
- DiscoveryServiceExtension.cs
- HiddenField.cs
- EUCJPEncoding.cs
- precedingquery.cs
- TypeGeneratedEventArgs.cs
- ConnectionModeReader.cs
- SqlDataReaderSmi.cs
- DropDownList.cs
- LoginView.cs
- SettingsPropertyCollection.cs
- ValueExpressions.cs
- SurrogateEncoder.cs
- Identity.cs
- DataBoundControlAdapter.cs
- WinFormsUtils.cs
- VariantWrapper.cs
- IApplicationTrustManager.cs
- MenuTracker.cs
- DataGridViewColumn.cs
- Transform.cs
- FormatterConverter.cs
- SafeLibraryHandle.cs
- FreezableCollection.cs
- TaskHelper.cs
- Cloud.cs
- ToolStripPanel.cs
- ImageListStreamer.cs
- SqlExpander.cs
- mansign.cs
- PasswordRecovery.cs
- _NtlmClient.cs
- graph.cs
- PartBasedPackageProperties.cs
- XmlDocumentFragment.cs
- EntityModelSchemaGenerator.cs
- DataGridViewAccessibleObject.cs
- AssemblyBuilderData.cs
- BreakRecordTable.cs
- NotCondition.cs
- DataGridViewRowsRemovedEventArgs.cs
- ExpandCollapsePattern.cs
- relpropertyhelper.cs
- CollectionDataContractAttribute.cs
- NativeMethodsCLR.cs
- HeaderedContentControl.cs
- PersonalizationDictionary.cs
- sqlstateclientmanager.cs
- ExpressionVisitor.cs
- NamespaceQuery.cs
- LabelExpression.cs
- ClientSettingsProvider.cs
- WebPartTransformerAttribute.cs
- InvalidWMPVersionException.cs
- MarshalDirectiveException.cs
- PageAdapter.cs
- RemoteWebConfigurationHost.cs
- InstanceNotReadyException.cs
- WindowsToolbar.cs
- propertyentry.cs
- EditorPartCollection.cs
- KeyValueConfigurationCollection.cs
- KnownIds.cs
- DefaultCommandConverter.cs
- SafeBitVector32.cs
- OdbcConnectionOpen.cs
- ToolStripGripRenderEventArgs.cs
- shaperfactoryquerycacheentry.cs
- XmlSchemaChoice.cs
- TextAction.cs
- AssemblyHelper.cs
- HuffmanTree.cs
- UxThemeWrapper.cs
- PageContentAsyncResult.cs
- RawContentTypeMapper.cs
- XmlQueryRuntime.cs
- _FixedSizeReader.cs
- Pointer.cs
- ProfileSection.cs
- SelectorAutomationPeer.cs
- PackageFilter.cs
- DoubleConverter.cs
- SplineQuaternionKeyFrame.cs
- TimeSpanHelper.cs