Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Web / Hosting / ISAPIWorkerRequest.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ISAPIWorkerRequest.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Web.Hosting {
8     using System.Text;
9     using System.Configuration.Assemblies;
10     using System.Runtime.InteropServices;
11     using System.Collections;
12     using System.Collections.Specialized;
13     using System.Security.Authentication.ExtendedProtection;
14     using System.IO;
15     using System.Globalization;
16     using System.Threading;
17     using Microsoft.Win32;
18     using System.Web;
19     using System.Web.Management;
20     using System.Web.Util;
21     using System.Web.Configuration;
22     using System.Web.Caching;
23
24 //
25 // recyclable buffers for IntPtr[] and int[]
26 // to avoid pinning gen0
27 //
28
29 internal class RecyclableArrayHelper {
30     private const int ARRAY_SIZE = 128;
31     private const int MAX_FREE_ARRAYS = 64;
32     private static IntegerArrayAllocator s_IntegerArrayAllocator;
33     private static IntPtrArrayAllocator s_IntPtrArrayAllocator;
34
35     static RecyclableArrayHelper() {
36         s_IntegerArrayAllocator = new IntegerArrayAllocator(ARRAY_SIZE, MAX_FREE_ARRAYS);
37         s_IntPtrArrayAllocator  = new IntPtrArrayAllocator(ARRAY_SIZE, MAX_FREE_ARRAYS);
38     }
39
40     internal static int[] GetIntegerArray(int minimumLength) {
41         if( minimumLength <= ARRAY_SIZE )
42             return(int[])s_IntegerArrayAllocator.GetBuffer();
43         else
44             return new int[minimumLength];
45     }
46
47     internal static IntPtr[] GetIntPtrArray(int minimumLength) {
48         if( minimumLength <= ARRAY_SIZE )
49             return(IntPtr[])s_IntPtrArrayAllocator.GetBuffer();
50         else
51             return new IntPtr[minimumLength];
52     }
53
54     internal static void ReuseIntegerArray(int[] array) {
55         if (array != null && array.Length == ARRAY_SIZE)
56             s_IntegerArrayAllocator.ReuseBuffer(array);
57     }
58
59     internal static void ReuseIntPtrArray(IntPtr[] array) {
60         if (array != null && array.Length == ARRAY_SIZE)
61             s_IntPtrArrayAllocator.ReuseBuffer(array);
62     }
63 }
64
65 //
66 // char[] appendable buffer. Recyclable up to 1K
67 // Also encapsulates encoding (using utf-8) into recyclable byte[] buffer.
68 //
69 // Usage:
70 //      new RecyclableCharBuffer
71 //      Append
72 //      ...
73 //      GetEncodedBytesBuffer
74 //      Dispose
75 //
76
77 internal class RecyclableCharBuffer {
78     private const int BUFFER_SIZE       = 1024;
79     private const int MAX_FREE_BUFFERS  = 64;
80     private static CharBufferAllocator  s_CharBufferAllocator;
81     private static UbyteBufferAllocator s_ByteBufferAllocator;
82
83     private char[]  _charBuffer;
84     private int     _size;
85     private int     _freePos;
86     private bool    _recyclable;
87
88
89     private byte[]  _byteBuffer;
90
91     static RecyclableCharBuffer() {
92         s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS);
93         s_ByteBufferAllocator = new UbyteBufferAllocator(Encoding.UTF8.GetMaxByteCount(BUFFER_SIZE), MAX_FREE_BUFFERS);
94     }
95
96     internal RecyclableCharBuffer() {
97         _charBuffer = (char[])s_CharBufferAllocator.GetBuffer();
98         _size = _charBuffer.Length;
99         _freePos = 0;
100         _recyclable = true;
101     }
102
103     internal void Dispose() {
104         if (_recyclable) {
105             if (_charBuffer != null)
106                 s_CharBufferAllocator.ReuseBuffer(_charBuffer);
107
108             if (_byteBuffer != null)
109                 s_ByteBufferAllocator.ReuseBuffer(_byteBuffer);
110         }
111
112         _charBuffer = null;
113         _byteBuffer = null;
114     }
115
116     private void Grow(int newSize) {
117         if (newSize <= _size)
118             return;
119
120         if (newSize < _size*2)
121             newSize = _size*2;
122
123         char[] newBuffer = new char[newSize];
124
125         if (_freePos > 0)
126             Array.Copy(_charBuffer, newBuffer, _freePos);
127
128         _charBuffer = newBuffer;
129         _size = newSize;
130         _recyclable = false;
131     }
132
133     internal void Append(char ch) {
134         if (_freePos >= _size)
135             Grow(_freePos+1);
136
137         _charBuffer[_freePos++] = ch;
138     }
139
140     internal void Append(String s) {
141         int l = s.Length;
142         int newFreePos = _freePos + l;
143
144         if (newFreePos > _size)
145             Grow(newFreePos);
146
147         s.CopyTo(0, _charBuffer, _freePos, l);
148         _freePos = newFreePos;
149     }
150
151     internal byte[] GetEncodedBytesBuffer() {
152         return GetEncodedBytesBuffer(Encoding.UTF8);
153     }
154
155     internal byte[] GetEncodedBytesBuffer(Encoding encoding) {
156         if (_byteBuffer != null)
157             return _byteBuffer;
158
159         if (encoding == null)
160             encoding = Encoding.UTF8;
161
162         // null terminate
163
164         Append('\0');
165
166         // convert to bytes
167
168         if (_recyclable) {
169             // still using the original recyclable char buffer
170             // -- can use recyclable byte buffer
171
172             _byteBuffer = (byte[])s_ByteBufferAllocator.GetBuffer();
173
174             if (_freePos > 0)
175                 encoding.GetBytes(_charBuffer, 0, _freePos, _byteBuffer, 0);
176         }
177         else {
178             _byteBuffer = encoding.GetBytes(_charBuffer, 0, _freePos);
179         }
180
181         return _byteBuffer;
182     }
183
184     public override String ToString() {
185         return (_charBuffer != null && _freePos > 0) ? new String(_charBuffer, 0, _freePos) : null;
186     }
187 }
188
189 //
190 // byte[] buffer of encoded chars bytes. Recyclable up to 4K
191 // Also encapsulates decoding into recyclable char[] buffer.
192 //
193 // Usage:
194 //      new RecyclableByteBuffer
195 //      fill .Buffer up
196 //      GetDecodedTabSeparatedStrings
197 //      Dispose
198 //
199
200 internal class RecyclableByteBuffer {
201     private const int BUFFER_SIZE       = 4096;
202     private const int MAX_FREE_BUFFERS  = 64;
203     private static UbyteBufferAllocator s_ByteBufferAllocator;
204     private static CharBufferAllocator  s_CharBufferAllocator;
205
206     private int     _offset;
207     private byte[]  _byteBuffer;
208     private bool    _recyclable;
209
210     private char[]  _charBuffer;
211
212     static RecyclableByteBuffer() {
213         s_ByteBufferAllocator = new UbyteBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS);
214         s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS);
215     }
216
217     internal RecyclableByteBuffer() {
218         _byteBuffer = (byte[])s_ByteBufferAllocator.GetBuffer();
219         _recyclable = true;
220     }
221
222     internal void Dispose() {
223         if (_recyclable) {
224             if (_byteBuffer != null)
225                 s_ByteBufferAllocator.ReuseBuffer(_byteBuffer);
226
227             if (_charBuffer != null)
228                 s_CharBufferAllocator.ReuseBuffer(_charBuffer);
229         }
230
231         _byteBuffer = null;
232         _charBuffer = null;
233     }
234
235     internal byte[] Buffer {
236         get { return _byteBuffer; }
237     }
238
239     internal void Resize(int newSize) {
240         _byteBuffer = new byte[newSize];
241         _recyclable = false;
242     }
243
244     private void Skip(int count) {
245         if (count <= 0)
246             return;
247
248         // adjust offset
249         int l = _byteBuffer.Length;
250         int c = 0;
251
252         for (int i = 0; i < l; i++) {
253             if (_byteBuffer[i] == (byte)'\t') {
254                 if (++c == count) {
255                     _offset = i+1;
256                     return;
257                 }
258             }
259         }
260     }
261
262
263     private int CalcLength()
264     {
265         // calculate null termitated length
266
267         if (_byteBuffer != null) {
268             int l = _byteBuffer.Length;
269
270             for (int i = _offset; i < l; i++) {
271                 if (_byteBuffer[i] == 0)
272                     return i - _offset;
273             }
274         }
275
276         return 0;
277     }
278
279     private char[] GetDecodedCharBuffer(Encoding encoding, ref int len) {
280         if (_charBuffer != null)
281             return _charBuffer;
282
283         if (len == 0) {
284             _charBuffer = new char[0];
285         }
286         else if (_recyclable) {
287             _charBuffer = (char[])s_CharBufferAllocator.GetBuffer();
288             len = encoding.GetChars(_byteBuffer, _offset, len, _charBuffer, 0);
289         }
290         else {
291             _charBuffer = encoding.GetChars(_byteBuffer, _offset, len);
292             len = _charBuffer.Length;
293         }
294
295         return _charBuffer;
296     }
297
298     internal string GetDecodedString(Encoding encoding, int len) {
299         return encoding.GetString(_byteBuffer, 0, len);
300     }
301
302     internal String[] GetDecodedTabSeparatedStrings(Encoding encoding, int numStrings, int numSkipStrings) {
303         if (numSkipStrings > 0)
304             Skip(numSkipStrings);
305
306         int len = CalcLength();
307         char[] s = GetDecodedCharBuffer(encoding, ref len);
308
309         String[] ss = new String[numStrings];
310
311         int iStart = 0;
312         int iEnd;
313         int foundStrings = 0;
314
315         for (int iString = 0; iString < numStrings; iString++) {
316             iEnd = len;
317
318             for (int i = iStart; i < len; i++) {
319                 if (s[i] == '\t') {
320                     iEnd = i;
321                     break;
322                 }
323             }
324
325             if (iEnd > iStart)
326                 ss[iString] = new String(s, iStart, iEnd-iStart);
327             else
328                 ss[iString] = String.Empty;
329
330             foundStrings++;
331
332             if (iEnd == len)
333                 break;
334
335             iStart = iEnd+1;
336         }
337
338         if (foundStrings < numStrings) {
339             len = CalcLength();
340             iStart = _offset;
341
342             for (int iString = 0; iString < numStrings; iString++) {
343                 iEnd = len;
344
345                 for (int i = iStart; i < len; i++) {
346                     if (_byteBuffer[i] == (byte)'\t') {
347                         iEnd = i;
348                         break;
349                     }
350                 }
351
352                 if (iEnd > iStart)
353                     ss[iString] = encoding.GetString(_byteBuffer, iStart, iEnd-iStart);
354                 else
355                     ss[iString] = String.Empty;
356
357                 if (iEnd == len)
358                     break;
359
360                 iStart = iEnd+1;
361             }
362
363         }
364
365         return ss;
366     }
367 }
368
369
370 //
371 // class to encapsulate writing from byte[], IntPtr (resource or filehandle)
372 //
373
374 internal enum BufferType: byte {
375     Managed = 0,
376     UnmanagedPool = 1,
377     IISAllocatedRequestMemory = 2,
378     TransmitFile = 3
379 }
380
381 internal class MemoryBytes {
382     private int         _size;
383     private byte[]      _arrayData;
384     private GCHandle    _pinnedArrayData;
385     private IntPtr      _intptrData;
386     private long        _fileSize;
387     private IntPtr      _fileHandle;
388     private string      _fileName;
389     private long        _offset;
390     private BufferType  _bufferType; // 0 managed, 1 native pool, 2 IIS allocated request memory, 3 TransmitFile
391
392     internal MemoryBytes(string fileName, long offset, long fileSize) {
393         _bufferType = BufferType.TransmitFile;
394         _intptrData = IntPtr.Zero;
395         _fileHandle = IntPtr.Zero;
396         _fileSize = fileSize;
397         _fileName = fileName;
398         _offset = offset;
399         // _cachedResponseBodyLength will be wrong if we don't set _size now.
400         _size = IntPtr.Size;
401     }
402
403     internal MemoryBytes(byte[] data, int size): this(data, size, false, 0) {
404     }
405
406     internal MemoryBytes(byte[] data, int size, bool useTransmitFile, long fileSize) {
407         _size = size;
408         _arrayData = data;
409         _intptrData = IntPtr.Zero;
410         _fileHandle = IntPtr.Zero;
411         if (useTransmitFile) {
412             _bufferType = BufferType.TransmitFile;
413         }
414         _fileSize = fileSize;
415     }
416
417     internal MemoryBytes(IntPtr data, int size, BufferType bufferType) {
418         _size = size;
419         _arrayData = null;
420         _intptrData = data;
421         _fileHandle = IntPtr.Zero;
422         _bufferType = bufferType;
423     }
424
425     internal long FileSize {
426         get { return _fileSize; }
427     }
428
429     internal bool IsBufferFromUnmanagedPool {
430         get { return _bufferType == BufferType.UnmanagedPool; }
431     }
432
433     internal BufferType BufferType {
434         get { return _bufferType; }
435     }
436
437     internal int Size {
438         get { return _size; }
439     }
440
441     internal bool UseTransmitFile {
442         get { return _bufferType == BufferType.TransmitFile; }
443     }
444
445     private void CloseHandle() {
446         if (_fileHandle != IntPtr.Zero && _fileHandle != UnsafeNativeMethods.INVALID_HANDLE_VALUE) {
447             UnsafeNativeMethods.CloseHandle(_fileHandle);
448             // don't allow 'this' to be GC'd before CloseHandle returns.
449             _fileHandle = IntPtr.Zero;
450         }
451     }
452
453     private static byte[] IntPtrToBytes(IntPtr p, long offset, long length) {
454         // This method converts the given pointer and offset to a byte[] representation
455         // of the C struct EcbFileAndOffset (32 and 64-bit specific):
456         //
457         // struct FileAndOffset
458         // {
459         //     ULONGLONG cbOffset;
460         //     ULONGLONG cbLength;
461         //     HANDLE hFile;
462         // }
463         //
464
465         byte[] bytes = new byte[2 * sizeof(long) + IntPtr.Size];
466
467         // Put the offset value first
468         for (int i = 0; i < 8; i++)
469             bytes[i] = (byte)((offset >> 8*i) & 0xFF );
470
471         // Put the file value next
472         for (int i = 0; i < 8; i++)
473             bytes[8+i] = (byte)((length >> 8*i) & 0xFF );
474         
475         if (IntPtr.Size == 4) {
476             int temp = p.ToInt32();
477             for (int i = 0; i < 4; i++)
478                 bytes[16+i] = (byte)((temp >> 8*i) & 0xFF );
479         }  else {
480             long temp = p.ToInt64();
481             for (int i = 0; i < 8; i++)
482                 bytes[16+i] = (byte)((temp >> 8*i) & 0xFF );
483         }
484         return bytes;
485     }
486
487     private void SetHandle() {
488         if (_fileName != null) {
489             _fileHandle = UnsafeNativeMethods.GetFileHandleForTransmitFile(_fileName);
490         }
491         if (_fileHandle != IntPtr.Zero) {
492             _arrayData = IntPtrToBytes(_fileHandle, _offset, _fileSize);
493         }
494     }
495
496     internal IntPtr LockMemory() {
497         SetHandle();
498         if (_arrayData != null) {
499             _pinnedArrayData = GCHandle.Alloc(_arrayData, GCHandleType.Pinned);
500             return Marshal.UnsafeAddrOfPinnedArrayElement(_arrayData, 0);
501         }
502         else {
503             return _intptrData;
504         }
505     }
506
507     internal void UnlockMemory() {
508         CloseHandle();
509         if (_arrayData != null) {
510             _pinnedArrayData.Free();
511         }
512     }
513 }
514
515 //
516 // recyclable pinnable char[] buffer to get Unicode server variables
517 //
518 // Usage:
519 //      new ServerVarCharBuffer
520 //      get PinnedAddress, Length
521 //      [Resize]
522 //      Dispose
523 //
524
525 internal class ServerVarCharBuffer {
526     private const int BUFFER_SIZE       = 1024;
527     private const int MAX_FREE_BUFFERS  = 64;
528     private static CharBufferAllocator  s_CharBufferAllocator;
529
530     private bool        _recyclable;
531     private char[]      _charBuffer;
532     private bool        _pinned;
533     private GCHandle    _pinnedCharBufferHandle;
534     private IntPtr      _pinnedAddr;
535
536     static ServerVarCharBuffer() {
537         s_CharBufferAllocator = new CharBufferAllocator(BUFFER_SIZE, MAX_FREE_BUFFERS);
538     }
539
540     internal ServerVarCharBuffer() {
541         _charBuffer = (char[])s_CharBufferAllocator.GetBuffer();
542         _recyclable = true;
543     }
544
545     internal void Dispose() {
546         if (_pinned) {
547             _pinnedCharBufferHandle.Free();
548             _pinned = false;
549         }
550
551         if (_recyclable) {
552             if (_charBuffer != null)
553                 s_CharBufferAllocator.ReuseBuffer(_charBuffer);
554         }
555
556         _charBuffer = null;
557     }
558
559     internal IntPtr PinnedAddress {
560         get {
561             if (!_pinned) {
562                 _pinnedCharBufferHandle = GCHandle.Alloc(_charBuffer, GCHandleType.Pinned);
563                 _pinnedAddr = Marshal.UnsafeAddrOfPinnedArrayElement(_charBuffer, 0);
564                 _pinned = true;
565             }
566
567             return _pinnedAddr;
568         }
569     }
570
571     internal int Length {
572         get {
573             return _charBuffer.Length;
574         }
575     }
576
577     internal void Resize(int newSize) {
578         if (_pinned) {
579             _pinnedCharBufferHandle.Free();
580             _pinned = false;
581         }
582
583         _charBuffer = new char[newSize];
584         _recyclable = false;
585     }
586 }
587
588 //
589 // Async IO completion callback from IIS
590 //
591 internal delegate void ISAPIAsyncCompletionCallback(IntPtr ecb, int byteCount, int error);
592
593 internal delegate void AsyncCompletionCallback(int bytesCompleted, int hresult, IntPtr pbAsyncReceiveBuffer);
594
595 //
596 // Implementation of HttpWorkerRequest based on ECB
597 //
598 internal abstract class ISAPIWorkerRequest : HttpWorkerRequest {
599
600     protected IntPtr _ecb;     // ECB as integer
601     protected IntPtr _token;   // user token as integer
602     protected Guid _traceId;   // ETW traceId
603     protected AsyncResultBase _asyncResultBase;
604     protected AsyncCompletionCallback _asyncCompletionCallback;
605
606     // Request data obtained during initialization (basics)
607
608     protected String _method;
609     protected String _path;
610     protected String _filePath;
611     protected String _pathInfo;
612     protected String _pathTranslated;
613     protected String _appPath;
614     protected String _appPathTranslated;
615
616     protected int _contentType;
617     protected int _contentTotalLength;
618     protected int _contentAvailLength;
619
620     protected int _queryStringLength;
621
622     protected bool _ignoreMinAsyncSize;
623     protected bool _requiresAsyncFlushCallback;
624
625     // Request data obtained later on
626
627     private bool _preloadedContentRead;
628     private byte[] _preloadedContent;
629
630     private bool _requestHeadersAvailable;
631     private String[][] _unknownRequestHeaders;
632     private String[] _knownRequestHeaders;
633
634     private bool      _clientCertFetched;
635     private DateTime  _clientCertValidFrom;
636     private DateTime  _clientCertValidUntil;
637     private byte []   _clientCert;
638     private int       _clientCertEncoding;
639     private byte []   _clientCertPublicKey;
640     private byte []   _clientCertBinaryIssuer;
641
642     // Outgoing headers storage
643
644     private bool _headersSent;
645     private Encoding _headerEncoding;
646     private bool _contentLengthSent;
647     private bool _chunked;
648     private RecyclableCharBuffer _headers = new RecyclableCharBuffer();
649     private RecyclableCharBuffer _status  = new RecyclableCharBuffer();
650     private bool _statusSet = true;
651
652     // Outgoing data cached for a single FlushCore
653
654     private byte[]      _cachedResponseStatus;
655     private byte[]      _cachedResponseHeaders;
656     private int         _cachedResponseKeepConnected;
657     private int         _cachedResponseBodyLength;
658     private ArrayList   _cachedResponseBodyBytes;
659     private int         _cachedResponseBodyBytesIoLockCount;
660
661     // Notification about the end of IO
662
663     private HttpWorkerRequest.EndOfSendNotification _endOfRequestCallback;
664     private Object                                  _endOfRequestCallbackArg;
665     private int                                     _endOfRequestCallbackLockCount;
666
667     //  Constants for posted content type
668
669     private const int CONTENT_NONE = 0;
670     private const int CONTENT_FORM = 1;
671     private const int CONTENT_MULTIPART = 2;
672     private const int CONTENT_OTHER = 3;
673
674     //
675     // ISAPI status constants (for DoneWithSession)
676     //
677
678     private const int STATUS_SUCCESS = 1;
679     private const int STATUS_SUCCESS_AND_KEEP_CONN = 2;
680     private const int STATUS_PENDING = 3;
681     private const int STATUS_ERROR = 4;
682
683     //
684     // Private helpers
685     //
686
687     private String[] ReadBasics(int[] contentInfo) {
688         // call getbasics
689
690         RecyclableByteBuffer buf = new RecyclableByteBuffer();
691
692         int r = GetBasicsCore(buf.Buffer, buf.Buffer.Length, contentInfo);
693
694         while (r < 0) {
695             buf.Resize(-r);     // buffer not big enough
696             r = GetBasicsCore(buf.Buffer, buf.Buffer.Length, contentInfo);
697         }
698
699         if (r == 0)
700             throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data));
701
702         // convert to characters and split the buffer into strings
703
704         String[] ss = buf.GetDecodedTabSeparatedStrings(Encoding.Default, 6, 0);
705
706         // recycle buffers
707
708         buf.Dispose();
709
710         return ss;
711     }
712
713     private static readonly char[] s_ColonOrNL = { ':', '\n' };
714
715     private void ReadRequestHeaders() {
716         if (_requestHeadersAvailable)
717             return;
718
719         _knownRequestHeaders = new String[RequestHeaderMaximum];
720
721         // construct unknown headers as array list of name1,value1,...
722
723         ArrayList headers = new ArrayList();
724
725         String s = GetServerVariable("ALL_RAW");
726         int l = (s != null) ? s.Length : 0;
727         int i = 0;
728
729         while (i < l)
730         {
731             //  find next :
732
733             int ci = s.IndexOfAny(s_ColonOrNL, i);
734
735             if (ci < 0)
736                 break;
737
738             if (s[ci] == '\n') {
739                 // ignore header without :
740                 i = ci+1;
741                 continue;
742             }
743
744             if (ci == i) {
745                 i++;
746                 continue;
747             }
748
749             // add extract name
750             String name = s.Substring(i, ci-i).Trim();
751
752             //  find next \n
753             int ni = s.IndexOf('\n', ci+1);
754             if (ni < 0)
755                 ni = l;
756
757             while (ni < l-1 && s[ni+1] == ' ')  {   // continuation of header (ASURT 115064)
758                 ni = s.IndexOf('\n', ni+1);
759                 if (ni < 0)
760                     ni = l;
761             }
762
763             // extract value
764             String value = s.Substring(ci+1, ni-ci-1).Trim();
765
766             // remember
767             int knownIndex = GetKnownRequestHeaderIndex(name);
768             if (knownIndex >= 0) {
769                 _knownRequestHeaders[knownIndex] = value;
770             }
771             else {
772                 headers.Add(name);
773                 headers.Add(value);
774             }
775
776             i = ni+1;
777         }
778
779         // copy to array unknown headers
780
781         int n = headers.Count / 2;
782         _unknownRequestHeaders = new String[n][];
783         int j = 0;
784
785         for (i = 0; i < n; i++) {
786             _unknownRequestHeaders[i] = new String[2];
787             _unknownRequestHeaders[i][0] = (String)headers[j++];
788             _unknownRequestHeaders[i][1] = (String)headers[j++];
789         }
790
791         _requestHeadersAvailable = true;
792     }
793
794     private void SendHeaders() {
795         if (!_headersSent) {
796             if (_statusSet) {
797                 _headers.Append("\r\n");
798
799                 AddHeadersToCachedResponse(
800                     _status.GetEncodedBytesBuffer(),
801                     _headers.GetEncodedBytesBuffer(_headerEncoding),
802                     (_contentLengthSent || _chunked) ? 1 : 0);
803
804                 _headersSent = true;
805             }
806         }
807     }
808
809     private void SendResponseFromFileStream(FileStream f, long offset, long length)  {
810         long fileSize = f.Length;
811
812         if (length == -1)
813             length = fileSize - offset;
814
815         if (offset < 0 || length > fileSize - offset)
816             throw new HttpException(SR.GetString(SR.Invalid_range));
817
818         if (length > 0) {
819             if (offset > 0)
820                 f.Seek(offset, SeekOrigin.Begin);
821
822             byte[] fileBytes = new byte[(int)length];
823             int bytesRead = f.Read(fileBytes, 0, (int)length);
824             if (bytesRead > 0)
825                 AddBodyToCachedResponse(new MemoryBytes(fileBytes, bytesRead));
826         }
827     }
828
829     private void ResetCachedResponse() {
830         _cachedResponseStatus = null;
831         _cachedResponseHeaders = null;
832         _cachedResponseBodyLength = 0;
833         _cachedResponseBodyBytes = null;
834
835         // DDBugs 162981: ASP.NET leaks requests when page calls TransmitFile and Flush
836         // This happens because FlushCachedResponse may set _requiresAsyncFlushCallback and 
837         // _ignoreMinAsyncSize to true and then it "forgets" to reset them after Flush is done. 
838         // When the final flush is being executed it uses incorrect values 
839         // to determine that it needs an async completion during the final flush.
840         // The fix is to reset async flags after each Flush
841         _requiresAsyncFlushCallback = false;
842         _ignoreMinAsyncSize = false;
843     }
844
845     private void AddHeadersToCachedResponse(byte[] status, byte[] header, int keepConnected) {
846         _cachedResponseStatus = status;
847         _cachedResponseHeaders = header;
848         _cachedResponseKeepConnected = keepConnected;
849     }
850
851     private void AddBodyToCachedResponse(MemoryBytes bytes) {
852         if (_cachedResponseBodyBytes == null)
853             _cachedResponseBodyBytes = new ArrayList();
854         _cachedResponseBodyBytes.Add(bytes);
855         _cachedResponseBodyLength += bytes.Size;
856     }
857
858     internal void UnlockCachedResponseBytesOnceAfterIoComplete() {
859         if (Interlocked.Decrement(ref _cachedResponseBodyBytesIoLockCount) == 0) {
860             // unlock pinned memory
861             if (_cachedResponseBodyBytes != null) {
862                 int numFragments = _cachedResponseBodyBytes.Count;
863                 for (int i = 0; i < numFragments; i++) {
864                     try {
865                         ((MemoryBytes)_cachedResponseBodyBytes[i]).UnlockMemory();
866                     }
867                     catch {
868                     }
869                 }
870             }
871
872             // don't remember cached data anymore
873             ResetCachedResponse();
874
875             FlushAsyncResult flushAsyncResult = _asyncResultBase as FlushAsyncResult;
876             if (flushAsyncResult != null) {
877                 _endOfRequestCallbackLockCount--;
878                 _asyncCompletionCallback(0, flushAsyncResult.HResult, IntPtr.Zero);
879             }
880         }
881     }
882
883     // ISAPIWorkerRequest
884     private void FlushCachedResponse(bool isFinal) {
885         if (_ecb == IntPtr.Zero)
886             return;
887
888         bool        asyncFlush = false;
889         int         numFragments = 0;
890         IntPtr[]    fragments = null;
891         int[]       fragmentLengths = null;
892         long        bytesOut = 0;
893
894         try {
895             // prepare body fragments as IntPtr[] of pointers and int[] of lengths
896             if (_cachedResponseBodyLength > 0) {
897                 numFragments = _cachedResponseBodyBytes.Count;
898                 fragments = RecyclableArrayHelper.GetIntPtrArray(numFragments);
899                 fragmentLengths = RecyclableArrayHelper.GetIntegerArray(numFragments);
900
901                 for (int i = 0; i < numFragments; i++) {
902                     MemoryBytes bytes = (MemoryBytes)_cachedResponseBodyBytes[i];
903                     fragments[i] = bytes.LockMemory();
904
905                     if (!isFinal || !bytes.IsBufferFromUnmanagedPool)
906                         _requiresAsyncFlushCallback = true;
907
908                     if (bytes.UseTransmitFile) {
909                         fragmentLengths[i] = -bytes.Size; // use negative length for TransmitFile
910                         _ignoreMinAsyncSize = true;
911                         bytesOut += bytes.FileSize;
912                     }
913                     else {
914                         fragmentLengths[i] = bytes.Size;
915                         bytesOut += bytes.Size;
916                     }
917                 }
918             }
919
920             // prepare doneWithSession and finalStatus
921             int doneWithSession = isFinal ? 1 : 0;
922             int finalStatus = isFinal ? ((_cachedResponseKeepConnected != 0) ? STATUS_SUCCESS_AND_KEEP_CONN : STATUS_SUCCESS) : 0;
923
924             // set the count to two - one for return from FlushCore and one for async IO completion
925             // the cleanup should happen on the later of the two
926             _cachedResponseBodyBytesIoLockCount = 2;
927
928             // increment the lock count controlling end of request callback
929             // so that the callback would be called at the later of EndRequest
930             // and the async IO completion
931             // (doesn't need to be interlocked as only one thread could start the IO)
932             _endOfRequestCallbackLockCount++;
933
934             if (isFinal)
935                 PerfCounters.DecrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
936
937             // perf counters are DWORDs, so it makes no sense to update REQUEST_BYTES_OUT with a value greater than Int32.MaxValue
938             int delta = (int) bytesOut;
939             if (delta > 0) {
940                 PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_OUT, delta);
941             }
942
943             try {
944                 // send to unmanaged code
945                 FlushCore(
946                     _cachedResponseStatus, _cachedResponseHeaders, _cachedResponseKeepConnected,
947                     _cachedResponseBodyLength, numFragments, fragments, fragmentLengths,
948                     doneWithSession, finalStatus, out asyncFlush);
949             }
950             finally {
951                 if (isFinal) {
952                     Close();
953                     _ecb = IntPtr.Zero;
954                 }
955             }
956         }
957         finally {
958             // in case of synchronous IO adjust down the lock counts
959             if (!asyncFlush) {
960                 _cachedResponseBodyBytesIoLockCount--;
961                 _endOfRequestCallbackLockCount--;
962             }
963
964             // unlock pinned memory
965             UnlockCachedResponseBytesOnceAfterIoComplete();
966
967             // recycle buffers
968             RecyclableArrayHelper.ReuseIntPtrArray(fragments);
969             RecyclableArrayHelper.ReuseIntegerArray(fragmentLengths);
970         }
971     }
972
973     internal void CallEndOfRequestCallbackOnceAfterAllIoComplete() {
974         if (_endOfRequestCallback != null) {
975             // only call the callback on the latest of EndRequest and async IO completion
976             if (Interlocked.Decrement(ref _endOfRequestCallbackLockCount) == 0) {
977                 try {
978                     _endOfRequestCallback(this, _endOfRequestCallbackArg);
979                 }
980                 catch {
981                 }
982             }
983         }
984     }
985
986     //
987     // ctor
988     //
989
990     internal ISAPIWorkerRequest(IntPtr ecb) {
991         _ecb = ecb;
992         PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_TOTAL);
993     }
994
995     public override Guid RequestTraceIdentifier {
996         get { return _traceId; }
997     }
998
999     internal IntPtr Ecb {
1000         get {
1001             return _ecb;
1002         }
1003     }
1004
1005     internal void Initialize() {
1006         // setup basic values
1007
1008         ReadRequestBasics();
1009
1010         if (_appPathTranslated != null && _appPathTranslated.Length > 2 && !StringUtil.StringEndsWith(_appPathTranslated, '\\'))
1011             _appPathTranslated += "\\";  // IIS 6.0 doesn't add the trailing '\'
1012
1013         // Increment incoming request length
1014         PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, _contentTotalLength);
1015     }
1016
1017     internal virtual void ReadRequestBasics() {
1018
1019         // Get requests basics
1020
1021         int[] contentInfo = new int[4];
1022         String[] basicStrings = ReadBasics(contentInfo);
1023
1024         if (basicStrings == null || basicStrings.Length != 6)
1025             throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data));
1026
1027         // Remember content info
1028
1029         _contentType        = contentInfo[0];
1030         _contentTotalLength = contentInfo[1];
1031         _contentAvailLength = contentInfo[2];
1032         _queryStringLength  = contentInfo[3];
1033
1034         // Remember basic strings
1035
1036         _method             = basicStrings[0];
1037         _filePath           = basicStrings[1];
1038         _pathInfo           = basicStrings[2];
1039         _path = (_pathInfo.Length > 0) ? (_filePath + _pathInfo) : _filePath;
1040         _pathTranslated     = basicStrings[3];
1041         _appPath            = basicStrings[4];
1042         _appPathTranslated  = basicStrings[5];
1043     }
1044
1045     //
1046     // Public methods
1047     //
1048
1049     internal static ISAPIWorkerRequest CreateWorkerRequest(IntPtr ecb, bool useOOP) {
1050
1051         ISAPIWorkerRequest wr = null;
1052         if (useOOP) {
1053             EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero);
1054
1055             if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, false);
1056
1057             wr = new ISAPIWorkerRequestOutOfProc(ecb);
1058         }
1059         else {
1060             int version = UnsafeNativeMethods.EcbGetVersion(ecb) >> 16;
1061             
1062             if (version >= 7) {
1063                 EtwTrace.TraceEnableCheck(EtwTraceConfigType.IIS7_ISAPI, ecb);
1064             }
1065             else {
1066                 EtwTrace.TraceEnableCheck(EtwTraceConfigType.DOWNLEVEL, IntPtr.Zero);
1067             }
1068
1069             if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_APPDOMAIN_ENTER, ecb, Thread.GetDomain().FriendlyName, null, true);
1070
1071             if (version >= 7) {
1072                 wr = new ISAPIWorkerRequestInProcForIIS7(ecb);
1073             }
1074             else if (version == 6) {
1075                 wr = new ISAPIWorkerRequestInProcForIIS6(ecb);
1076             }
1077             else {
1078                 wr = new ISAPIWorkerRequestInProc(ecb);
1079             }
1080         }
1081         return wr;
1082     }
1083
1084     public override String GetUriPath() {
1085         return _path;
1086     }
1087
1088     public override String GetQueryString() {
1089         if (_queryStringLength == 0)
1090             return String.Empty;
1091
1092         int size = _queryStringLength + 2;
1093         StringBuilder buf = new StringBuilder(size);
1094
1095         int r = GetQueryStringCore(0, buf, size);
1096
1097         if (r != 1)
1098             throw new HttpException(SR.GetString(SR.Cannot_get_query_string));
1099
1100         return buf.ToString();
1101     }
1102
1103     public override byte[] GetQueryStringRawBytes() {
1104         if (_queryStringLength == 0)
1105             return null;
1106
1107         byte[] buf = new byte[_queryStringLength];
1108         int r = GetQueryStringRawBytesCore(buf, _queryStringLength);
1109         if (r != 1)
1110             throw new HttpException(SR.GetString(SR.Cannot_get_query_string_bytes));
1111
1112         return buf;
1113     }
1114
1115
1116     public override String GetRawUrl() {
1117         String qs = GetQueryString();
1118
1119         if (!String.IsNullOrEmpty(qs))
1120             return _path + "?" + qs;
1121         else
1122             return _path;
1123     }
1124
1125     public override String GetHttpVerbName() {
1126         return _method;
1127     }
1128
1129     public override String GetHttpVersion() {
1130         return GetServerVariable("SERVER_PROTOCOL");
1131     }
1132
1133     public override String GetRemoteAddress() {
1134         return GetServerVariable("REMOTE_ADDR");
1135     }
1136
1137     public override String GetRemoteName() {
1138         return GetServerVariable("REMOTE_HOST");
1139     }
1140
1141     public override int GetRemotePort() {
1142         return 0;   // unknown in ISAPI
1143     }
1144
1145     public override String GetLocalAddress() {
1146         return GetServerVariable("LOCAL_ADDR");
1147     }
1148
1149     public override int GetLocalPort() {
1150         return Int32.Parse(GetServerVariable("SERVER_PORT"));
1151     }
1152
1153     internal override String GetLocalPortAsString() {
1154         return GetServerVariable("SERVER_PORT");
1155     }
1156
1157     public override String GetServerName() {
1158         return GetServerVariable("SERVER_NAME");
1159     }
1160
1161     public override bool IsSecure() {
1162         String https = GetServerVariable("HTTPS");
1163         return (https != null && https.Equals("on"));
1164     }
1165
1166     public override String GetFilePath() {
1167         return _filePath;
1168     }
1169
1170     public override String GetFilePathTranslated() {
1171         return _pathTranslated;
1172     }
1173
1174     public override String GetPathInfo() {
1175         return _pathInfo;
1176     }
1177
1178     public override String GetAppPath() {
1179         return _appPath;
1180     }
1181
1182     public override String GetAppPathTranslated() {
1183         return _appPathTranslated;
1184     }
1185
1186     public override int GetPreloadedEntityBodyLength() {
1187         return _contentAvailLength;
1188     }
1189
1190     public override byte[] GetPreloadedEntityBody() {
1191         if (!_preloadedContentRead) {
1192             if (_contentAvailLength > 0) {
1193                 _preloadedContent = new byte[_contentAvailLength];
1194
1195                 int r = GetPreloadedPostedContentCore(_preloadedContent, 0, _contentAvailLength);
1196
1197                 if (r < 0)
1198                     throw new HttpException(SR.GetString(SR.Cannot_read_posted_data));
1199             }
1200
1201             _preloadedContentRead = true;
1202         }
1203
1204         return _preloadedContent;
1205     }
1206
1207     public override bool IsEntireEntityBodyIsPreloaded() {
1208         return (_contentAvailLength == _contentTotalLength);
1209     }
1210
1211     public override int GetTotalEntityBodyLength() {
1212         return _contentTotalLength;
1213     }
1214
1215     public override int ReadEntityBody(byte[] buffer, int size)  {
1216         return ReadEntityBody(buffer, 0, size);
1217     }
1218
1219     public override int ReadEntityBody(byte[] buffer, int offset, int size) {
1220         if (buffer.Length - offset < size) {
1221             throw new ArgumentOutOfRangeException("offset");
1222         }
1223
1224         int r = GetAdditionalPostedContentCore(buffer, offset, size);
1225
1226         if (r < 0) {
1227             throw new HttpException(SR.GetString(SR.Cannot_read_posted_data));
1228         }
1229
1230         return r;
1231     }
1232
1233     public override long GetBytesRead() {
1234         throw new HttpException(SR.GetString(SR.Not_supported));
1235     }
1236
1237     public override String GetKnownRequestHeader(int index)  {
1238         if (!_requestHeadersAvailable) {
1239             // special case important ones so that no all headers parsing is required
1240
1241             switch (index) {
1242                 case HeaderContentType:
1243                     if (_contentType == CONTENT_FORM)
1244                         return "application/x-www-form-urlencoded";
1245                     break;
1246
1247                 case HeaderContentLength:
1248                     if (_contentType != CONTENT_NONE)
1249                         return (_contentTotalLength).ToString();
1250                     break;
1251             }
1252
1253             // parse all headers
1254             ReadRequestHeaders();
1255         }
1256
1257         return _knownRequestHeaders[index];
1258     }
1259
1260     public override String GetUnknownRequestHeader(String name) {
1261         if (!_requestHeadersAvailable)
1262             ReadRequestHeaders();
1263
1264         int n = _unknownRequestHeaders.Length;
1265
1266         for (int i = 0; i < n; i++) {
1267             if (StringUtil.EqualsIgnoreCase(name, _unknownRequestHeaders[i][0]))
1268                 return _unknownRequestHeaders[i][1];
1269         }
1270
1271         return null;
1272     }
1273
1274     public override String[][] GetUnknownRequestHeaders() {
1275         if (!_requestHeadersAvailable)
1276             ReadRequestHeaders();
1277
1278         return _unknownRequestHeaders;
1279     }
1280
1281     public override void SendStatus(int statusCode, String statusDescription) {
1282         _status.Append(statusCode.ToString());
1283         _status.Append(" ");
1284         _status.Append(statusDescription);
1285         _statusSet = true;
1286     }
1287
1288     internal override void SetHeaderEncoding(Encoding encoding) {
1289         _headerEncoding = encoding;
1290     }
1291
1292     public override void SendKnownResponseHeader(int index, String value) {
1293         if (_headersSent)
1294             throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1295
1296         if (index == HttpWorkerRequest.HeaderSetCookie) {
1297             DisableKernelCache();
1298         }
1299
1300         _headers.Append(GetKnownResponseHeaderName(index));
1301         _headers.Append(": ");
1302         _headers.Append(value);
1303         _headers.Append("\r\n");
1304
1305         if (index == HeaderContentLength)
1306             _contentLengthSent = true;
1307         else if (index == HeaderTransferEncoding && (value != null && value.Equals("chunked")))
1308             _chunked = true;
1309     }
1310
1311     public override void SendUnknownResponseHeader(String name, String value) {
1312         if (_headersSent)
1313             throw new HttpException(SR.GetString(SR.Cannot_append_header_after_headers_sent));
1314
1315         if (StringUtil.EqualsIgnoreCase(name, "Set-Cookie")) {
1316             DisableKernelCache();
1317         }
1318
1319         _headers.Append(name);
1320         _headers.Append(": ");
1321         _headers.Append(value);
1322         _headers.Append("\r\n");
1323     }
1324
1325     public override void SendCalculatedContentLength(int contentLength) {
1326         SendCalculatedContentLength((long)contentLength);
1327     }
1328
1329     // VSWhidbey 559473: need to support Content-Length response header values > 2GB
1330     public override void SendCalculatedContentLength(long contentLength) {
1331         if (!_headersSent)
1332         {
1333             _headers.Append("Content-Length: ");
1334             _headers.Append(contentLength.ToString(CultureInfo.InvariantCulture));
1335             _headers.Append("\r\n");
1336             _contentLengthSent = true;
1337         }
1338     }
1339
1340     public override bool HeadersSent() {
1341         return _headersSent;
1342     }
1343
1344     public override bool IsClientConnected() {
1345         return (IsClientConnectedCore() == 0) ? false : true;
1346     }
1347
1348     public override void CloseConnection() {
1349         CloseConnectionCore();
1350     }
1351
1352     public override void SendResponseFromMemory(byte[] data, int length) {
1353         if (!_headersSent)
1354             SendHeaders();
1355
1356         if (length > 0)
1357             AddBodyToCachedResponse(new MemoryBytes(data, length));
1358     }
1359
1360     public override void SendResponseFromMemory(IntPtr data, int length) {
1361         SendResponseFromMemory(data, length, false);
1362     }
1363
1364     internal override void SendResponseFromMemory(IntPtr data, int length, bool isBufferFromUnmanagedPool) {
1365         if (!_headersSent)
1366             SendHeaders();
1367
1368         if (length > 0)
1369             AddBodyToCachedResponse(new MemoryBytes(data, length, isBufferFromUnmanagedPool ? BufferType.UnmanagedPool : BufferType.Managed));
1370     }
1371
1372     // PackageFile for in-proc case
1373     internal virtual MemoryBytes PackageFile(String filename, long offset64, long length64, bool isImpersonating) {
1374         // The offset and length must be less than Int32.MaxValue for in-proc. 
1375         // This should be true, since HttpFileResponseElement.ctor throws ArgumentOutOfRangeException for in-proc
1376         Debug.Assert(offset64 < Int32.MaxValue);
1377         Debug.Assert(length64 < Int32.MaxValue);
1378         int offset = Convert.ToInt32(offset64);
1379         int length = Convert.ToInt32(length64);
1380
1381         FileStream f = null;
1382         MemoryBytes bytes = null;
1383         try {
1384             Debug.Assert(offset < length);
1385             f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
1386             Debug.Assert((f.Length - offset) == length);
1387             int size = (int) (f.Length - offset);
1388             byte[] fileBytes = new byte[size];
1389             int bytesRead = f.Read(fileBytes, offset, size);
1390             bytes = new MemoryBytes(fileBytes, bytesRead);
1391         }
1392         finally {
1393             if (f != null)
1394                 f.Close();
1395         }
1396
1397         return bytes;
1398     }
1399
1400     internal override void TransmitFile(string filename, long offset, long length, bool isImpersonating) {
1401         if (!_headersSent)
1402             SendHeaders();
1403
1404         if (length == 0)
1405             return;
1406
1407         AddBodyToCachedResponse(PackageFile(filename, offset, length, isImpersonating));
1408         return;
1409     }
1410
1411     public override void SendResponseFromFile(String filename, long offset, long length) {
1412         if (!_headersSent)
1413             SendHeaders();
1414
1415         if (length == 0)
1416             return;
1417
1418         FileStream f = null;
1419
1420         try {
1421             f = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
1422             SendResponseFromFileStream(f, offset, length);
1423         }
1424         finally {
1425             if (f != null)
1426                 f.Close();
1427         }
1428     }
1429
1430     public override void SendResponseFromFile(IntPtr handle, long offset, long length) {
1431         if (!_headersSent)
1432             SendHeaders();
1433
1434         if (length == 0)
1435             return;
1436
1437         FileStream f = null;
1438
1439         try {
1440             f = new FileStream(new Microsoft.Win32.SafeHandles.SafeFileHandle(handle,false), FileAccess.Read);
1441             SendResponseFromFileStream(f, offset, length);
1442         }
1443         finally {
1444             if (f != null)
1445                 f.Close();
1446         }
1447     }
1448
1449     // ISAPIWorkerRequest
1450     public override void FlushResponse(bool finalFlush) {
1451         // only flush headers - the data is write through
1452
1453         if (!_headersSent)
1454             SendHeaders();
1455
1456         FlushCachedResponse(finalFlush);
1457     }
1458
1459     public override void EndOfRequest() {
1460         FlushCachedResponse(true);
1461
1462         // recycle the headers and status buffers
1463         if (_headers != null) {
1464             _headers.Dispose();
1465             _headers = null;
1466         }
1467
1468         if (_status != null) {
1469             _status.Dispose();
1470             _status = null;
1471         }
1472         CallEndOfRequestCallbackOnceAfterAllIoComplete();
1473     }
1474
1475     public override void SetEndOfSendNotification(HttpWorkerRequest.EndOfSendNotification callback, Object extraData) {
1476         if (_endOfRequestCallback != null)
1477             throw new InvalidOperationException();
1478         _endOfRequestCallback = callback;
1479         _endOfRequestCallbackArg = extraData;
1480         _endOfRequestCallbackLockCount = 1;   // when goes to 0 the callback is called
1481     }
1482
1483     public override String MapPath(String path) {
1484         return HostingEnvironment.MapPathInternal(path);
1485     }
1486
1487     public override String MachineConfigPath {
1488         get {
1489             return HttpConfigurationSystem.MachineConfigurationFilePath;
1490         }
1491     }
1492
1493     public override String RootWebConfigPath {
1494         get {
1495             return HttpConfigurationSystem.RootWebConfigurationFilePath;
1496         }
1497     }
1498
1499     public override String MachineInstallDirectory {
1500         get {
1501             return HttpRuntime.AspInstallDirectory;
1502         }
1503     }
1504
1505     public override IntPtr GetUserToken() {
1506         return GetUserTokenCore();
1507     }
1508
1509     public override IntPtr GetVirtualPathToken() {
1510         return GetVirtualPathTokenCore();
1511     }
1512
1513     public override byte[] GetClientCertificate() {
1514         if (!_clientCertFetched)
1515             FetchClientCertificate();
1516
1517         return _clientCert;
1518     }
1519
1520     public override DateTime GetClientCertificateValidFrom() {
1521         if (!_clientCertFetched)
1522             FetchClientCertificate();
1523
1524         return _clientCertValidFrom;
1525     }
1526
1527     public override DateTime GetClientCertificateValidUntil() {
1528         if (!_clientCertFetched)
1529             FetchClientCertificate();
1530
1531         return _clientCertValidUntil;
1532     }
1533
1534     public override byte [] GetClientCertificateBinaryIssuer() {
1535         if (!_clientCertFetched)
1536             FetchClientCertificate();
1537         return _clientCertBinaryIssuer;
1538     }
1539
1540     public override int GetClientCertificateEncoding() {
1541         if (!_clientCertFetched)
1542             FetchClientCertificate();
1543         return _clientCertEncoding;
1544     }
1545
1546     public override byte [] GetClientCertificatePublicKey() {
1547         if (!_clientCertFetched)
1548             FetchClientCertificate();
1549         return _clientCertPublicKey;
1550     }
1551
1552     private void FetchClientCertificate() {
1553         if (_clientCertFetched)
1554             return;
1555
1556         _clientCertFetched = true;
1557
1558         byte[]         buf        = new byte[8192];
1559         int[]          pInts      = new int[4];
1560         long[]         pDates     = new long[2];
1561         int            iRet       = GetClientCertificateCore(buf, pInts, pDates);
1562
1563         if (iRet < 0 && (-iRet) > 8192) {
1564             iRet = -iRet + 100;
1565             buf  = new byte[iRet];
1566             iRet = GetClientCertificateCore(buf, pInts, pDates);
1567         }
1568         if (iRet > 0) {
1569             _clientCertEncoding = pInts[0];
1570
1571             if (pInts[1] < buf.Length && pInts[1] > 0) {
1572                 _clientCert = new byte[pInts[1]];
1573                 Array.Copy(buf, _clientCert, pInts[1]);
1574
1575                 if (pInts[2] + pInts[1] < buf.Length && pInts[2] > 0) {
1576                     _clientCertBinaryIssuer = new byte[pInts[2]];
1577                     Array.Copy(buf, pInts[1], _clientCertBinaryIssuer, 0, pInts[2]);
1578                 }
1579
1580                 if (pInts[2] + pInts[1] + pInts[3] < buf.Length && pInts[3] > 0) {
1581                     _clientCertPublicKey = new byte[pInts[3]];
1582                     Array.Copy(buf, pInts[1] + pInts[2], _clientCertPublicKey, 0, pInts[3]);
1583                 }
1584             }
1585         }
1586
1587         if (iRet > 0 && pDates[0] != 0)
1588             _clientCertValidFrom = DateTime.FromFileTime(pDates[0]);
1589         else
1590             _clientCertValidFrom = DateTime.Now;
1591
1592         if (iRet > 0 && pDates[1] != 0)
1593             _clientCertValidUntil = DateTime.FromFileTime(pDates[1]);
1594         else
1595             _clientCertValidUntil = DateTime.Now;
1596     }
1597
1598     //
1599     // internal methods specific to ISAPI
1600     //
1601
1602     internal void AppendLogParameter(String logParam) {
1603         AppendLogParameterCore(logParam);
1604     }
1605
1606     internal virtual void SendEmptyResponse() {
1607     }
1608
1609     //
1610     // PInvoke callback wrappers -- overridden by the derived classes
1611     //
1612
1613     internal abstract int GetBasicsCore(byte[] buffer, int size, int[] contentInfo);
1614     internal abstract int GetQueryStringCore(int encode, StringBuilder buffer, int size);
1615     internal abstract int GetQueryStringRawBytesCore(byte[] buffer, int size);
1616     internal abstract int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead);
1617     internal abstract int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize);
1618     // ISAPIWorkerRequest
1619     internal abstract void FlushCore(byte[]     status,
1620                                      byte[]     header,
1621                                      int        keepConnected,
1622                                      int        totalBodySize,
1623                                      int        numBodyFragments,
1624                                      IntPtr[]   bodyFragments,
1625                                      int[]      bodyFragmentLengths,
1626                                      int        doneWithSession,
1627                                      int        finalStatus,
1628                                      out bool   async);
1629     internal abstract int IsClientConnectedCore();
1630     internal abstract int CloseConnectionCore();
1631     internal abstract int MapUrlToPathCore(String url, byte[] buffer, int size);
1632     internal abstract IntPtr GetUserTokenCore();
1633     internal abstract IntPtr GetVirtualPathTokenCore();
1634     internal abstract int AppendLogParameterCore(String logParam);
1635     internal abstract int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates);
1636     internal abstract int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut);
1637     internal virtual void Close() {}
1638 }
1639
1640 //
1641 // In-process ISAPIWorkerRequest
1642 //
1643 // Does queueing of IO operations. ISAPI only support one async IO at a time.
1644 //
1645
1646 internal class ISAPIWorkerRequestInProc : ISAPIWorkerRequest {
1647
1648     protected const int NUM_SERVER_VARIABLES = 35; // total number of variables that we commonly get
1649     protected const int NUM_BASIC_SERVER_VARIABLES = 12; // needed on every request
1650     protected const int NUM_ADDITIONAL_SERVER_VARIABLES = 23; // needed when HttpRequest.ServerVariables is populated
1651
1652     // These constants must be kept in [....] with g_szServerVariables and g_szUnicodeServerVariables in ecbdirect.cxx
1653
1654     protected const int LOGON_USER = 0;
1655     protected const int AUTH_TYPE = 1;
1656     protected const int APPL_PHYSICAL_PATH = 2;
1657     protected const int REQUEST_METHOD = 3;
1658     protected const int PATH_INFO = 4;
1659     protected const int PATH_TRANSLATED = 5;
1660     protected const int URL = 6;
1661     protected const int CACHE_URL = 7;
1662     protected const int SERVER_NAME = 8;
1663     protected const int SERVER_PORT = 9;
1664     protected const int HTTPS = 10;
1665     protected const int ALL_RAW = 11;
1666     protected const int REMOTE_ADDR = 12;
1667     protected const int AUTH_PASSWORD = 13;
1668     protected const int CERT_COOKIE = 14;
1669     protected const int CERT_FLAGS = 15;
1670     protected const int CERT_ISSUER = 16;
1671     protected const int CERT_KEYSIZE = 17;
1672     protected const int CERT_SECRETKEYSIZE = 18;
1673     protected const int CERT_SERIALNUMBER = 19;
1674     protected const int CERT_SERVER_ISSUER = 20;
1675     protected const int CERT_SERVER_SUBJECT = 21;
1676     protected const int CERT_SUBJECT = 22;
1677     protected const int GATEWAY_INTERFACE = 23;
1678     protected const int HTTPS_KEYSIZE = 24;
1679     protected const int HTTPS_SECRETKEYSIZE = 25;
1680     protected const int HTTPS_SERVER_ISSUER = 26;
1681     protected const int HTTPS_SERVER_SUBJECT = 27;
1682     protected const int INSTANCE_ID = 28;
1683     protected const int INSTANCE_META_PATH = 29;
1684     protected const int LOCAL_ADDR = 30;
1685     protected const int REMOTE_HOST = 31;
1686     protected const int REMOTE_PORT = 32;
1687     protected const int SERVER_PROTOCOL = 33;
1688     protected const int SERVER_SOFTWARE = 34;
1689
1690     // storage for common server variables
1691     protected string[] _basicServerVars = null;
1692     protected string[] _additionalServerVars = null;
1693     private ChannelBinding   _channelBindingToken;
1694
1695     internal ISAPIWorkerRequestInProc(IntPtr ecb) : base(ecb) {
1696         if (ecb == IntPtr.Zero || UnsafeNativeMethods.EcbGetTraceContextId(ecb, out _traceId) != 1) {
1697             _traceId = Guid.Empty;
1698         }
1699     }
1700
1701     internal override int GetBasicsCore(byte[] buffer, int size, int[] contentInfo) {
1702         if (_ecb == IntPtr.Zero)
1703             return 0;
1704
1705         return UnsafeNativeMethods.EcbGetBasics(_ecb, buffer, size, contentInfo);
1706     }
1707
1708     internal override int GetQueryStringCore(int encode, StringBuilder buffer, int size) {
1709         if (_ecb == IntPtr.Zero)
1710             return 0;
1711
1712         return UnsafeNativeMethods.EcbGetQueryString(_ecb, encode, buffer, size);
1713     }
1714
1715     internal override int GetQueryStringRawBytesCore(byte[] buffer, int size) {
1716         if (_ecb == IntPtr.Zero)
1717             return 0;
1718
1719         return UnsafeNativeMethods.EcbGetQueryStringRawBytes(_ecb, buffer, size);
1720     }
1721
1722     internal override int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead) {
1723         if (_ecb == IntPtr.Zero)
1724             return 0;
1725
1726         int rc = UnsafeNativeMethods.EcbGetPreloadedPostedContent(_ecb, bytes, offset, numBytesToRead);
1727         if (rc > 0)
1728             PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc);
1729         return rc;
1730     }
1731
1732     internal override int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize) {
1733         if (_ecb == IntPtr.Zero)
1734             return 0;
1735
1736         int rc = 0;
1737
1738         try {
1739             // Acquire blocking call
1740             IsInReadEntitySync = true;
1741   
1742             rc = UnsafeNativeMethods.EcbGetAdditionalPostedContent(_ecb, bytes, offset, bufferSize);
1743         }
1744         finally {
1745             // Release blocking call
1746             IsInReadEntitySync = false;
1747         }
1748
1749         if (rc > 0)
1750             PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc);
1751         return rc;
1752     }
1753
1754     internal override int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates) {
1755         if (_ecb == IntPtr.Zero)
1756             return 0;
1757
1758         return UnsafeNativeMethods.EcbGetClientCertificate(_ecb, buffer, buffer.Length, pInts, pDates);
1759     }
1760
1761     internal override int IsClientConnectedCore()
1762     {
1763         if (_ecb == IntPtr.Zero)
1764             return 0;
1765
1766         return UnsafeNativeMethods.EcbIsClientConnected(_ecb);
1767     }
1768
1769     // ISAPIWorkerRequestInProc
1770     internal override void FlushCore(byte[]     status,
1771                                      byte[]     header,
1772                                      int        keepConnected,
1773                                      int        totalBodySize,
1774                                      int        numBodyFragments,
1775                                      IntPtr[]   bodyFragments,
1776                                      int[]      bodyFragmentLengths,
1777                                      int        doneWithSession,
1778                                      int        finalStatus,
1779                                      out bool   async) {
1780         async = false;
1781
1782         if (_ecb == IntPtr.Zero)
1783             return;
1784
1785         UnsafeNativeMethods.EcbFlushCore(
1786                         _ecb,
1787                         status,
1788                         header,
1789                         keepConnected,
1790                         totalBodySize,
1791                         numBodyFragments,
1792                         bodyFragments,
1793                         bodyFragmentLengths,
1794                         doneWithSession,
1795                         finalStatus,
1796                         0,
1797                         0,
1798                         null);
1799     }
1800
1801     internal override int CloseConnectionCore() {
1802         if (_ecb == IntPtr.Zero)
1803             return 0;
1804
1805         return UnsafeNativeMethods.EcbCloseConnection(_ecb);
1806     }
1807
1808     internal override int MapUrlToPathCore(String url, byte[] buffer, int size) {
1809         if (_ecb == IntPtr.Zero)
1810             return 0;
1811
1812         return UnsafeNativeMethods.EcbMapUrlToPath(_ecb, url, buffer, size);
1813     }
1814
1815     internal override IntPtr GetUserTokenCore() {
1816         if (_token == IntPtr.Zero && _ecb != IntPtr.Zero)
1817             _token = UnsafeNativeMethods.EcbGetImpersonationToken(_ecb, IntPtr.Zero);
1818         return _token;
1819     }
1820
1821     internal override IntPtr GetVirtualPathTokenCore() {
1822         if (_token == IntPtr.Zero && _ecb != IntPtr.Zero)
1823             _token = UnsafeNativeMethods.EcbGetVirtualPathToken(_ecb, IntPtr.Zero);
1824
1825         return _token;
1826     }
1827
1828     internal override int AppendLogParameterCore(String logParam) {
1829         if (_ecb == IntPtr.Zero)
1830             return 0;
1831
1832         return UnsafeNativeMethods.EcbAppendLogParameter(_ecb, logParam);
1833     }
1834
1835     // ISAPIWorkerRequestInProc
1836     protected virtual String GetServerVariableCore(String name) {
1837         if (_ecb == IntPtr.Zero)
1838             return null;
1839
1840         String value = null;
1841
1842         RecyclableByteBuffer buf = new RecyclableByteBuffer();
1843
1844         int retVal = UnsafeNativeMethods.EcbGetServerVariable(_ecb, name, buf.Buffer, buf.Buffer.Length);
1845
1846         while (retVal < 0) {
1847             buf.Resize(-retVal);     // buffer not big enough
1848             retVal = UnsafeNativeMethods.EcbGetServerVariable(_ecb, name, buf.Buffer, buf.Buffer.Length);
1849         }
1850
1851         if (retVal > 0)
1852             value = buf.GetDecodedString(Encoding.UTF8, retVal);
1853
1854         buf.Dispose();
1855
1856         return value;
1857     }
1858
1859     // ISAPIWorkerRequestInProc
1860     protected virtual void GetAdditionalServerVariables() {
1861         if (_ecb == IntPtr.Zero)
1862             return;
1863
1864         // _additionalServerVars should only be initialized once
1865         Debug.Assert(_additionalServerVars == null);
1866         if (_additionalServerVars != null)
1867             return;
1868
1869         _additionalServerVars = new string[NUM_ADDITIONAL_SERVER_VARIABLES];
1870
1871         for(int i = 0; i < _additionalServerVars.Length; i++) {
1872             int nameIndex = i + NUM_BASIC_SERVER_VARIABLES;
1873
1874             RecyclableByteBuffer buf = new RecyclableByteBuffer();
1875
1876             int retVal = UnsafeNativeMethods.EcbGetServerVariableByIndex(_ecb, nameIndex, buf.Buffer, buf.Buffer.Length);
1877
1878             while (retVal < 0) {
1879                 buf.Resize(-retVal);     // buffer not big enough
1880                 retVal = UnsafeNativeMethods.EcbGetServerVariableByIndex(_ecb, nameIndex, buf.Buffer, buf.Buffer.Length);
1881             }
1882
1883             if (retVal > 0)
1884                 _additionalServerVars[i] = buf.GetDecodedString(Encoding.UTF8, retVal);
1885
1886             buf.Dispose();
1887         }
1888     }
1889
1890     private String GetAdditionalServerVar(int index) {
1891         if (_additionalServerVars == null)
1892             GetAdditionalServerVariables();
1893
1894         return _additionalServerVars[index - NUM_BASIC_SERVER_VARIABLES];
1895     }
1896
1897     public override String GetServerVariable(String name) {
1898         // this switch statement is a little more than twice as fast as a Hashtable lookup
1899         if (name != null) {
1900             switch (name.Length) {
1901                 case 20:
1902                     if (name == "HTTPS_SERVER_SUBJECT")
1903                         return GetAdditionalServerVar(HTTPS_SERVER_SUBJECT);
1904                     break;
1905
1906                 case 19:
1907                     if (name == "HTTPS_SECRETKEYSIZE")
1908                         return GetAdditionalServerVar(HTTPS_SECRETKEYSIZE);
1909                     else if (name == "CERT_SERVER_SUBJECT")
1910                         return GetAdditionalServerVar(CERT_SERVER_SUBJECT);
1911                     else if (name == "HTTPS_SERVER_ISSUER")
1912                         return GetAdditionalServerVar(HTTPS_SERVER_ISSUER);
1913                     break;
1914
1915                 case 18:
1916                     if (name == "INSTANCE_META_PATH")
1917                         return GetAdditionalServerVar(INSTANCE_META_PATH);
1918                     else if (name == "CERT_SECRETKEYSIZE")
1919                         return GetAdditionalServerVar(CERT_SECRETKEYSIZE);
1920                     else if (name == "CERT_SERVER_ISSUER")
1921                         return GetAdditionalServerVar(CERT_SERVER_ISSUER);
1922                     break;
1923
1924                 case 17:
1925                     if (name == "CERT_SERIALNUMBER")
1926                         return GetAdditionalServerVar(CERT_SERIALNUMBER);
1927                     else if (name == "GATEWAY_INTERFACE")
1928                         return GetAdditionalServerVar(GATEWAY_INTERFACE);
1929                     break;
1930
1931                 case 15:
1932                     if (name == "HTTP_USER_AGENT")
1933                         return GetKnownRequestHeader(HeaderUserAgent);
1934                     else if (name == "SERVER_PROTOCOL")
1935                         return GetAdditionalServerVar(SERVER_PROTOCOL);
1936                     else if (name == "SERVER_SOFTWARE")
1937                         return GetAdditionalServerVar(SERVER_SOFTWARE);
1938                     break;
1939
1940                 case 13:
1941                     if (name == "AUTH_PASSWORD")
1942                         return GetAdditionalServerVar(AUTH_PASSWORD);
1943                     else if (name == "HTTPS_KEYSIZE")
1944                         return GetAdditionalServerVar(HTTPS_KEYSIZE);
1945                     break;
1946
1947                 case 12:
1948                     if (name == "CERT_KEYSIZE")
1949                         return GetAdditionalServerVar(CERT_KEYSIZE);
1950                     else if (name == "CERT_SUBJECT")
1951                         return GetAdditionalServerVar(CERT_SUBJECT);
1952                     break;
1953
1954                 case 11:
1955                     if (name == "SERVER_NAME")
1956                         return _basicServerVars[SERVER_NAME];
1957                     else if (name == "SERVER_PORT")
1958                         return _basicServerVars[SERVER_PORT];
1959                     else if (name == "REMOTE_HOST")
1960                         return GetAdditionalServerVar(REMOTE_HOST);
1961                     else if (name == "REMOTE_PORT")
1962                         return GetAdditionalServerVar(REMOTE_PORT);
1963                     else if (name == "REMOTE_ADDR")
1964                         return GetAdditionalServerVar(REMOTE_ADDR);
1965                     else if (name == "CERT_COOKIE")
1966                         return GetAdditionalServerVar(CERT_COOKIE);
1967                     else if (name == "CERT_ISSUER")
1968                         return GetAdditionalServerVar(CERT_ISSUER);
1969                     else if (name == "INSTANCE_ID")
1970                         return GetAdditionalServerVar(INSTANCE_ID);
1971                     break;
1972
1973                 case 10:
1974                     if (name == "LOGON_USER")
1975                         return _basicServerVars[LOGON_USER];
1976                     else if (name == "LOCAL_ADDR")
1977                         return GetAdditionalServerVar(LOCAL_ADDR);
1978                     else if (name == "CERT_FLAGS")
1979                         return GetAdditionalServerVar(CERT_FLAGS);
1980                     break;
1981
1982                 case 9:
1983                     if (name == "AUTH_TYPE")
1984                         return _basicServerVars[AUTH_TYPE];
1985                     break;
1986
1987                 case 7:
1988                     if (name == "ALL_RAW") {
1989                         return _basicServerVars[ALL_RAW];
1990                     }
1991                     break;
1992
1993                 case 5:
1994                     if (name == "HTTPS")
1995                         return _basicServerVars[HTTPS];
1996                     break;
1997             }
1998         }
1999
2000         // this is not a common server variable
2001         return GetServerVariableCore(name);
2002     }
2003
2004     internal override int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut) {
2005         if (_ecb == IntPtr.Zero)
2006             return 0;
2007
2008         return UnsafeNativeMethods.EcbCallISAPI(_ecb, iFunction, bufIn, bufIn.Length, bufOut, bufOut.Length);
2009     }
2010     internal override void Close() {
2011         if (_channelBindingToken != null && !_channelBindingToken.IsInvalid)
2012             _channelBindingToken.Dispose();
2013     }
2014     internal ChannelBinding HttpChannelBindingToken {
2015         get {
2016             if (_channelBindingToken == null) {
2017                 IntPtr token       = IntPtr.Zero;
2018                 int    tokenSize   = 0;
2019                 int    hr          = HResults.S_OK;
2020
2021                 hr = UnsafeNativeMethods.EcbGetChannelBindingToken(_ecb, out token, out tokenSize);
2022                 if (hr == HResults.E_NOTIMPL)
2023                     throw new PlatformNotSupportedException();
2024                 Misc.ThrowIfFailedHr(hr);
2025                 _channelBindingToken = new HttpChannelBindingToken(token, tokenSize);
2026             }
2027             return _channelBindingToken;
2028         }
2029     }
2030 }
2031
2032 //
2033 // In-process ISAPIWorkerRequest specific for IIS7
2034 //
2035 // Uses unicode server vars
2036 //
2037
2038 internal class ISAPIWorkerRequestInProcForIIS7 : ISAPIWorkerRequestInProcForIIS6 {
2039
2040     internal ISAPIWorkerRequestInProcForIIS7(IntPtr ecb) : base(ecb) {
2041         _trySkipIisCustomErrors = true;
2042     }
2043
2044     internal override bool TrySkipIisCustomErrors {
2045         get { return _trySkipIisCustomErrors;  }
2046         set { _trySkipIisCustomErrors = value; }
2047     }
2048
2049     internal override void RaiseTraceEvent(IntegratedTraceType traceType, string eventData) {
2050         if (IntPtr.Zero != _ecb) {
2051             // the area is derivative of the type, either page or module
2052             int areaFlag = (traceType < IntegratedTraceType.DiagCritical) ? EtwTraceFlags.Page : EtwTraceFlags.Module;
2053             if (EtwTrace.IsTraceEnabled(EtwTrace.InferVerbosity(traceType), areaFlag)) {
2054                 string message = String.IsNullOrEmpty(eventData) ? String.Empty : eventData;
2055                 UnsafeNativeMethods.EcbEmitSimpleTrace(_ecb, (int)traceType, message);
2056             }
2057         }
2058     }
2059
2060     internal override void RaiseTraceEvent(WebBaseEvent webEvent) {
2061         if (IntPtr.Zero != _ecb) {
2062             if (EtwTrace.IsTraceEnabled(webEvent.InferEtwTraceVerbosity(), EtwTraceFlags.Infrastructure)) {
2063                 int fieldCount;
2064                 string[] fieldNames;
2065                 int[] fieldTypes;
2066                 string[] fieldData;
2067                 int webEventType;
2068                 webEvent.DeconstructWebEvent(out webEventType, out fieldCount, out fieldNames, out fieldTypes, out fieldData);
2069             UnsafeNativeMethods.EcbEmitWebEventTrace(_ecb, webEventType, fieldCount, fieldNames, fieldTypes, fieldData);
2070             }
2071         }
2072     }
2073 }
2074
2075 //
2076 // In-process ISAPIWorkerRequest specific for IIS6
2077 //
2078 // Uses unicode server vars
2079 //
2080
2081 internal class ISAPIWorkerRequestInProcForIIS6 : ISAPIWorkerRequestInProc {
2082
2083     private static int _asyncIoCount;
2084     private bool _disconnected;
2085
2086     internal ISAPIWorkerRequestInProcForIIS6(IntPtr ecb) : base(ecb) {
2087     }
2088
2089     internal static void WaitForPendingAsyncIo() {
2090         while(_asyncIoCount != 0) {
2091             Thread.Sleep(250);
2092         }
2093     }
2094
2095     internal override void SendEmptyResponse() {
2096         // facilitate health monitoring for IIS6 -- update last activity timestamp
2097         // to avoid deadlock detection
2098         UnsafeNativeMethods.UpdateLastActivityTimeForHealthMonitor();
2099     }
2100
2101     public override String GetRawUrl() {
2102         // CACHE_URL is the original URI, unaffected by any rewriting or routing
2103         // that may have occurred on the server
2104         string rawUrl = GetRawUrlHelper(GetUnicodeServerVariable(CACHE_URL));
2105         Debug.Trace("ClientUrl", "*** GetRawUrl --> " + rawUrl + " ***");
2106         return rawUrl;
2107     }
2108
2109     internal override void ReadRequestBasics() {
2110         if (_ecb == IntPtr.Zero)
2111             return;
2112         // set server variables needed for request basics and Indigo (VSWhidbey 352117,344580)
2113         GetBasicServerVariables();
2114
2115         // _pathInfo is the difference between UNICODE_PATH_INFO and UNICODE_URL
2116         int lengthDiff = _path.Length - _filePath.Length;
2117         if (lengthDiff > 0) {
2118             _pathInfo = _path.Substring(_filePath.Length);
2119             int pathTranslatedLength = _pathTranslated.Length - lengthDiff;
2120             if (pathTranslatedLength > 0) {
2121                 _pathTranslated = _pathTranslated.Substring(0, pathTranslatedLength);
2122             }
2123         }
2124         else {
2125             _filePath = _path;
2126             _pathInfo = String.Empty;
2127         }
2128
2129         _appPath = HostingEnvironment.ApplicationVirtualPath;
2130
2131         //
2132         // other (int) request basics
2133         //
2134
2135         int[] contentInfo = null;
2136
2137         try {
2138             contentInfo = RecyclableArrayHelper.GetIntegerArray(4);
2139             UnsafeNativeMethods.EcbGetBasicsContentInfo(_ecb, contentInfo);
2140
2141             _contentType        = contentInfo[0];
2142             _contentTotalLength = contentInfo[1];
2143             _contentAvailLength = contentInfo[2];
2144             _queryStringLength  = contentInfo[3];
2145         }
2146         finally {
2147             RecyclableArrayHelper.ReuseIntegerArray(contentInfo);
2148         }
2149     }
2150
2151     private void GetBasicServerVariables() {
2152
2153         if (_ecb == IntPtr.Zero)
2154             return;
2155         // _basicServerVars should only be initialized once
2156         Debug.Assert(_basicServerVars == null);
2157         if (_basicServerVars != null)
2158             return;
2159
2160         _basicServerVars = new string[NUM_BASIC_SERVER_VARIABLES];
2161
2162         ServerVarCharBuffer buffer = new ServerVarCharBuffer();
2163
2164         try {
2165             int[] serverVarLengths = new int[NUM_BASIC_SERVER_VARIABLES];
2166             int r = 0;
2167             int hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length,
2168                                                                      serverVarLengths, serverVarLengths.Length, 0, ref r);
2169             if (r > buffer.Length)
2170             {
2171                 buffer.Resize(r);
2172                 hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length,
2173                                                                      serverVarLengths, serverVarLengths.Length, 0, ref r);
2174             }
2175
2176             Misc.ThrowIfFailedHr(hresult);
2177
2178             IntPtr current = buffer.PinnedAddress;
2179
2180             for(int i = 0; i < _basicServerVars.Length; i++) {
2181                 _basicServerVars[i] = Marshal.PtrToStringUni(current, serverVarLengths[i]);
2182                 current = new IntPtr((long)current + 2L * (1L + serverVarLengths[i]));
2183             }
2184             // special case variables
2185             _appPathTranslated = _basicServerVars[APPL_PHYSICAL_PATH];
2186             _method = _basicServerVars[REQUEST_METHOD];
2187             _path = _basicServerVars[PATH_INFO];
2188             _pathTranslated = _basicServerVars[PATH_TRANSLATED];
2189             _filePath = _basicServerVars[URL];
2190         }
2191         finally {
2192             buffer.Dispose();
2193         }
2194     }
2195
2196     // ISAPIWorkerRequestInProcForIIS6
2197     protected override void GetAdditionalServerVariables() {
2198         if (_ecb == IntPtr.Zero)
2199             return;
2200
2201         // _additionalServerVars should only be initialized once
2202         Debug.Assert(_additionalServerVars == null);
2203         if (_additionalServerVars != null)
2204             return;
2205
2206         _additionalServerVars = new string[NUM_ADDITIONAL_SERVER_VARIABLES];
2207
2208         ServerVarCharBuffer buffer = new ServerVarCharBuffer();
2209
2210         try {
2211             int[] serverVarLengths = new int[NUM_ADDITIONAL_SERVER_VARIABLES];
2212             int r = 0;
2213             int hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length,
2214                                                                       serverVarLengths, serverVarLengths.Length, NUM_BASIC_SERVER_VARIABLES, ref r);
2215             if (r > buffer.Length) {
2216                 buffer.Resize(r);
2217                 hresult = UnsafeNativeMethods.EcbGetUnicodeServerVariables(_ecb, buffer.PinnedAddress, buffer.Length,
2218                                                                      serverVarLengths, serverVarLengths.Length, NUM_BASIC_SERVER_VARIABLES, ref r);
2219             }
2220             if (hresult != 0)
2221                 Marshal.ThrowExceptionForHR(hresult);
2222             IntPtr current = buffer.PinnedAddress;
2223
2224             for(int i = 0; i < _additionalServerVars.Length; i++) {
2225                 _additionalServerVars[i] = Marshal.PtrToStringUni(current, serverVarLengths[i]);
2226                 current = new IntPtr((long)current + 2L * (1L + serverVarLengths[i]));
2227             }
2228         }
2229         finally {
2230             buffer.Dispose();
2231         }
2232     }
2233
2234     // ISAPIWorkerRequestInProcForIIS6
2235     protected override string GetServerVariableCore(string name) {
2236         if (StringUtil.StringStartsWith(name, "HTTP_"))
2237             // fall back for headers (IIS6 doesn't support them as UNICODE_XXX)
2238             return base.GetServerVariableCore(name);
2239         else
2240             return GetUnicodeServerVariable("UNICODE_" + name);
2241     }
2242
2243     private String GetUnicodeServerVariable(String name) {
2244         String value = null;
2245         ServerVarCharBuffer buf = new ServerVarCharBuffer();
2246
2247         try {
2248             value = GetUnicodeServerVariable(name, buf);
2249         }
2250         finally {
2251             buf.Dispose();
2252         }
2253
2254         return value;
2255     }
2256
2257     private String GetUnicodeServerVariable(int nameIndex) {
2258         String value = null;
2259         ServerVarCharBuffer buf = new ServerVarCharBuffer();
2260
2261         try {
2262             value = GetUnicodeServerVariable(nameIndex, buf);
2263         }
2264         finally {
2265             buf.Dispose();
2266         }
2267
2268         return value;
2269     }
2270
2271     private String GetUnicodeServerVariable(String name, ServerVarCharBuffer buffer) {
2272         if (_ecb == IntPtr.Zero)
2273             return null;
2274         int r = UnsafeNativeMethods.EcbGetUnicodeServerVariable(_ecb, name, buffer.PinnedAddress, buffer.Length);
2275
2276         if (r < 0) {
2277             buffer.Resize(-r);
2278             r = UnsafeNativeMethods.EcbGetUnicodeServerVariable(_ecb, name, buffer.PinnedAddress, buffer.Length);
2279         }
2280
2281         if (r > 0)
2282             return Marshal.PtrToStringUni(buffer.PinnedAddress, r);
2283         else
2284             return null;
2285     }
2286
2287     private String GetUnicodeServerVariable(int nameIndex, ServerVarCharBuffer buffer) {
2288         if (_ecb == IntPtr.Zero)
2289             return null;
2290         int r = UnsafeNativeMethods.EcbGetUnicodeServerVariableByIndex(_ecb, nameIndex, buffer.PinnedAddress, buffer.Length);
2291
2292         if (r < 0) {
2293             buffer.Resize(-r);
2294             r = UnsafeNativeMethods.EcbGetUnicodeServerVariableByIndex(_ecb, nameIndex, buffer.PinnedAddress, buffer.Length);
2295         }
2296
2297         if (r > 0)
2298             return Marshal.PtrToStringUni(buffer.PinnedAddress, r);
2299         else
2300             return null;
2301     }
2302
2303     //
2304     // Support for async VectorSend and kernel mode cache on IIS6
2305     //
2306
2307     private const int MIN_ASYNC_SIZE = 2048;
2308     private GCHandle _rootedThis;      // for the duration of async
2309     private ISAPIAsyncCompletionCallback _asyncFlushCompletionCallback;
2310     private int _asyncFinalStatus;
2311     private bool _serverSupportFunctionError = false;
2312     private IntPtr _entity;  // pointer to HSE_entity
2313
2314     private bool _cacheInKernelMode = false;
2315     private bool _disableKernelCache = false;
2316     protected bool _trySkipIisCustomErrors = false;
2317     private const int TRY_SKIP_IIS_CUSTOM_ERRORS = 0x40;
2318
2319     // PackageFile for IIS6
2320     internal override MemoryBytes PackageFile(string filename, long offset, long size, bool isImpersonating) {
2321         return new MemoryBytes(filename, offset, size);
2322     }
2323
2324     // VSWhidbey 555203: support 64-bit file sizes for TransmitFile on IIS6
2325     internal override bool SupportsLongTransmitFile {
2326         get { return true; }
2327     }
2328
2329     // ISAPIWorkerRequestInProcForIIS6
2330     internal override void FlushCore(byte[]     status,
2331                                      byte[]     header,
2332                                      int        keepConnected,
2333                                      int        totalBodySize,
2334                                      int        numBodyFragments,
2335                                      IntPtr[]   bodyFragments,
2336                                      int[]      bodyFragmentLengths,
2337                                      int        doneWithSession,
2338                                      int        finalStatus,
2339                                      out bool   async) {
2340         async = false;
2341
2342         if (_ecb == IntPtr.Zero)
2343             return;
2344
2345         if (_headersSentFromExecuteUrl) {
2346             // don't send headers twice
2347             status = null;
2348             header = null;
2349         }
2350
2351         bool inAsyncFlush = false;
2352
2353         // async only for large responses and only on the last flush or if inAsyncFlush is true
2354         // don't do async if shutting down (async IO holds up app domain shutdown)
2355         if (doneWithSession != 0 && !HttpRuntime.ShutdownInProgress && (_ignoreMinAsyncSize || (totalBodySize >= MIN_ASYNC_SIZE))) {
2356             if (_requiresAsyncFlushCallback) {
2357                 _asyncFlushCompletionCallback = new ISAPIAsyncCompletionCallback(OnAsyncFlushCompletion);
2358                 _asyncFinalStatus = finalStatus;    // remember to pass to DoneWithSession on completion
2359                 _rootedThis = GCHandle.Alloc(this); // root for the duration of IO
2360                 doneWithSession = 0;                // will do on completion
2361                 async = true;
2362                 Interlocked.Increment(ref _asyncIoCount);  // increment async io count
2363             }
2364             else {
2365                 // buffers are native, so we don't need to return to managed code
2366                 _asyncFlushCompletionCallback = null;
2367                 doneWithSession = 0;                // will do on completion
2368                 async = true;
2369             }
2370         }
2371         else {
2372             inAsyncFlush = (_asyncResultBase is FlushAsyncResult);
2373             if (inAsyncFlush) {
2374                 _requiresAsyncFlushCallback = true;
2375                 _asyncFlushCompletionCallback = new ISAPIAsyncCompletionCallback(OnAsyncFlushCompletion);
2376                 _asyncFinalStatus = finalStatus;    // remember to pass to DoneWithSession on completion
2377                 _rootedThis = GCHandle.Alloc(this); // root for the duration of IO
2378                 async = true;
2379                 Interlocked.Increment(ref _asyncIoCount);  // increment async io count
2380             }
2381         }
2382
2383         // finalStatus is either 0 to force for a flush, 1 to indicate HSE_STATUS_SUCCESS, or 2 to indicate HSE_STATUS_SUCCESS_AND_KEEP_CONN
2384         Debug.Assert(0 <= finalStatus && finalStatus <= 2);
2385         int flags = _trySkipIisCustomErrors ? finalStatus|TRY_SKIP_IIS_CUSTOM_ERRORS : finalStatus;
2386
2387         int rc = UnsafeNativeMethods.EcbFlushCore(
2388                         _ecb,
2389                         status,
2390                         header,
2391                         keepConnected,
2392                         totalBodySize,
2393                         numBodyFragments,
2394                         bodyFragments,
2395                         bodyFragmentLengths,
2396                         doneWithSession,
2397                         flags,
2398                         _cacheInKernelMode ? 1 : 0,
2399                         async ? 1 : 0,
2400                         _asyncFlushCompletionCallback);
2401
2402         if (!_requiresAsyncFlushCallback && rc == 0 && async) {
2403
2404             // unlock and reset cached response
2405             UnlockCachedResponseBytesOnceAfterIoComplete();
2406
2407             CallEndOfRequestCallbackOnceAfterAllIoComplete();
2408         }
2409         else if (rc != 0 && async) {
2410             // on async failure default to [....] path
2411             async = false;
2412             
2413             if (!inAsyncFlush) {
2414                 // call DoneWithSession
2415                 UnsafeNativeMethods.EcbFlushCore(_ecb, null, null, 0, 0, 0, null, null, 1, _asyncFinalStatus, 0, 0, null);
2416             }
2417             
2418             if (_asyncFlushCompletionCallback != null) {
2419                 // unroot
2420                 _rootedThis.Free();
2421
2422                 // decrement async io count
2423                 Interlocked.Decrement(ref _asyncIoCount);
2424             }
2425
2426             if (inAsyncFlush) {
2427                 _asyncResultBase = null;
2428                 // treat every error as if the client disconnected
2429                 IncrementRequestsDisconnected();
2430                 throw new HttpException(SR.GetString(SR.ClientDisconnected), rc);
2431             }
2432         }
2433         else if (rc != 0 && !async && doneWithSession == 0 && !_serverSupportFunctionError) {
2434             //on non-async failure stop executing the request
2435
2436             //only throw once
2437             _serverSupportFunctionError = true;
2438
2439             string message = SR.Server_Support_Function_Error;
2440
2441             //give different error if connection was closed
2442             if (rc == HResults.WSAECONNABORTED || rc == HResults.WSAECONNRESET) {
2443                 message = SR.Server_Support_Function_Error_Disconnect;
2444                 IncrementRequestsDisconnected();
2445             }
2446
2447             throw new HttpException(SR.GetString(message, rc.ToString("X8", CultureInfo.InvariantCulture)), rc);
2448         }
2449     }
2450
2451     private void OnAsyncFlushCompletion(IntPtr ecb, int byteCount, int error) {
2452         try {
2453             FlushAsyncResult flushAsyncResult = _asyncResultBase as FlushAsyncResult;
2454             
2455             // unroot
2456             _rootedThis.Free();            
2457
2458             if (flushAsyncResult == null) {
2459                 // call DoneWithSession
2460                 UnsafeNativeMethods.EcbFlushCore(ecb, null, null, 0, 0, 0, null, null, 1, _asyncFinalStatus, 0, 0, null);
2461             }
2462             else {
2463                 flushAsyncResult.HResult = error;
2464             }
2465
2466             // unlock pinned memory (at the latest of this completion and exit from the FlushCore on stack)
2467             UnlockCachedResponseBytesOnceAfterIoComplete();
2468
2469             // Revert any impersonation set by IIS
2470             UnsafeNativeMethods.RevertToSelf();
2471
2472             if (flushAsyncResult == null) {
2473                 // call the HttpRuntime to recycle buffers (at the latest of this completion and EndRequest)
2474                 CallEndOfRequestCallbackOnceAfterAllIoComplete();
2475             }
2476         }
2477         finally {
2478             // decrement async io count
2479             Interlocked.Decrement(ref _asyncIoCount);
2480         }
2481     }
2482
2483     // WOS 1555777: kernel cache support
2484     // If the response can be kernel cached, return the kernel cache key;
2485     // otherwise return null.  The kernel cache key is used to invalidate
2486     // the entry if a dependency changes or the item is flushed from the
2487     // managed cache for any reason.
2488     internal override string SetupKernelCaching(int secondsToLive, string originalCacheUrl, bool enableKernelCacheForVaryByStar) {
2489         // if someone called DisableKernelCache, don't setup kernel caching
2490         if (_ecb == IntPtr.Zero || _disableKernelCache)
2491             return null;
2492
2493         string cacheUrl = GetUnicodeServerVariable(CACHE_URL);
2494
2495         // if we're re-inserting the response into the kernel cache, the original key must match
2496         if (originalCacheUrl != null && originalCacheUrl != cacheUrl) {
2497             return null;
2498         }
2499
2500         // If the request contains a query string, don't kernel cache the entry
2501         if (String.IsNullOrEmpty(cacheUrl) || (!enableKernelCacheForVaryByStar && cacheUrl.IndexOf('?') != -1)) {
2502             return null;
2503         }
2504
2505         // enable kernel caching (IIS will set the HTTP_CACHE_POLICY when we call VectorSend)
2506         _cacheInKernelMode = true;
2507         
2508         // okay, the response will be kernel cached, here's the key
2509         return cacheUrl;
2510     }
2511
2512     // WOS 1555777: kernel cache support
2513     internal override void DisableKernelCache() {
2514         _disableKernelCache = true;
2515         _cacheInKernelMode = false;
2516     }
2517
2518     //
2519     // Async Flush support
2520     //
2521
2522     public override bool SupportsAsyncFlush { get { return true; } }
2523
2524     // Begin an asynchronous flush of the response.  To support this, the worker request buffers
2525     // the status, headers, and resonse body until an asynchronous flush operation is initiated.
2526     public override IAsyncResult BeginFlush(AsyncCallback callback, Object state) {
2527         if (_ecb == IntPtr.Zero) {
2528             throw new InvalidOperationException();
2529         }
2530         
2531         FlushAsyncResult ar = new FlushAsyncResult(callback, state);
2532         
2533         // we only allow one async operation at a time
2534         if (Interlocked.CompareExchange(ref _asyncResultBase, ar, null) != null)
2535             throw new InvalidOperationException(SR.GetString(SR.Async_operation_pending));
2536
2537         // initiate async operation here
2538         if (_asyncCompletionCallback == null) {
2539             _asyncCompletionCallback = new AsyncCompletionCallback(OnAsyncCompletion);
2540         }
2541
2542         try {
2543             ar.MarkCallToBeginMethodStarted();
2544             FlushResponse(finalFlush: false);
2545         }
2546         finally {
2547             ar.MarkCallToBeginMethodCompleted();
2548         }
2549
2550         return ar;
2551     }
2552
2553     // Finish an asynchronous flush.
2554     public override void EndFlush(IAsyncResult asyncResult) {
2555         if (asyncResult == null)
2556             throw new ArgumentNullException("asyncResult");
2557         FlushAsyncResult ar = asyncResult as FlushAsyncResult;
2558         if (ar == null)
2559             throw new ArgumentException(null, "asyncResult");
2560
2561         ar.ReleaseWaitHandleWhenSignaled();
2562         if (ar.HResult < 0) {
2563             // treat every error as if the client disconnected
2564             IncrementRequestsDisconnected();
2565             throw new HttpException(SR.GetString(SR.ClientDisconnected), ar.HResult);
2566         }
2567     }
2568
2569
2570     //
2571     // Async Read support
2572     //
2573     
2574     public override bool SupportsAsyncRead { get { return true; } }
2575
2576     internal void OnAsyncCompletion(int bytesCompleted, int hresult, IntPtr pAsyncCompletionContext) {
2577         if (_asyncResultBase is ReadAsyncResult) 
2578             _rootedThis.Free();
2579         // clear _asyncResultBase because when we call Complete the user's callback
2580         // may begin another async operation
2581         AsyncResultBase ar = _asyncResultBase;
2582         _asyncResultBase = null;
2583         ar.Complete(bytesCompleted, hresult, pAsyncCompletionContext, synchronous: false);
2584     }
2585
2586     // Begin an asynchronous read of the request entity body.  To read the entire entity, invoke
2587     // repeatedly until total bytes read is equal to Request.ContentLength or EndRead indicates
2588     // that zero bytes were read.  If Request.ContentLength is zero and the request is chunked,
2589     // then invoke repeatedly until EndRead indicates that zero bytes were read.
2590     //
2591     // If an error occurs and the client is no longer connected, an HttpException will be thrown.
2592     //
2593     // This implements Stream.BeginRead, and as such, should throw
2594     // exceptions as described on MSDN when errors occur.
2595     public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, Object state) {
2596         // Do as Stream does.
2597         if (buffer == null)
2598             throw new ArgumentNullException("buffer");
2599         if (offset < 0)
2600             throw new ArgumentOutOfRangeException("offset");
2601         if (count < 0)
2602             throw new ArgumentOutOfRangeException("count");
2603         if (buffer.Length - offset < count)
2604             throw new ArgumentException(SR.GetString(SR.InvalidOffsetOrCount, "offset", "count"));
2605         if (_ecb == IntPtr.Zero)
2606             throw new InvalidOperationException();
2607
2608         ReadAsyncResult ar = new ReadAsyncResult(callback, state, buffer, offset, count, updatePerfCounter: false);
2609
2610         if (count == 0) {
2611             ar.Complete(0, HResults.S_OK, IntPtr.Zero, synchronous: true);
2612             return ar;
2613         }
2614
2615         // we only allow one async operation at a time
2616         if (Interlocked.CompareExchange(ref _asyncResultBase, ar, null) != null)
2617             throw new InvalidOperationException(SR.GetString(SR.Async_operation_pending));
2618         
2619         // initiate async operation here
2620         if (_asyncCompletionCallback == null) {
2621             _asyncCompletionCallback = new AsyncCompletionCallback(OnAsyncCompletion);
2622         }
2623         _rootedThis = GCHandle.Alloc(this); // root for duration of async operation
2624
2625         int hresult;
2626         try {
2627             ar.MarkCallToBeginMethodStarted();
2628             hresult = UnsafeNativeMethods.EcbReadClientAsync(_ecb,
2629                                                              count,
2630                                                              _asyncCompletionCallback);
2631         }
2632         finally {
2633             ar.MarkCallToBeginMethodCompleted();
2634         }
2635
2636         if (hresult < 0) {
2637             _rootedThis.Free();
2638             _asyncResultBase = null;
2639             // treat every error as if the client disconnected
2640             IncrementRequestsDisconnected();
2641             throw new HttpException(SR.GetString(SR.ClientDisconnected), hresult);
2642         }
2643         else {
2644             return ar;
2645         }
2646     }
2647
2648     // Finish an asynchronous read.  When this returns zero there is no more to be read.  If Request.ContentLength is non-zero,
2649     // do not read more bytes then specified by ContentLength, or an error will occur.
2650     // This implements Stream.EndRead on HttpBufferlessInputStream, and as such, should throw
2651     // exceptions as described on MSDN when errors occur.
2652     public override int EndRead(IAsyncResult asyncResult) {
2653         if (asyncResult == null)
2654             throw new ArgumentNullException("asyncResult");
2655         ReadAsyncResult ar = asyncResult as ReadAsyncResult;
2656         if (ar == null)
2657             throw new ArgumentException(null, "asyncResult");
2658         ar.ReleaseWaitHandleWhenSignaled();
2659         if (ar.HResult < 0) {
2660             // treat every error as if the client disconnected
2661             IncrementRequestsDisconnected();
2662             throw new HttpException(SR.GetString(SR.ClientDisconnected), ar.HResult);
2663         }
2664         else {
2665             return ar.BytesRead;
2666         }
2667     }
2668
2669     private void IncrementRequestsDisconnected() {
2670         if (!_disconnected) {
2671             PerfCounters.IncrementGlobalCounter(GlobalPerfCounter.REQUESTS_DISCONNECTED);
2672             _disconnected = true;
2673         }
2674     }
2675
2676     //
2677     // ExecuteUrl support
2678     //
2679
2680     private ISAPIAsyncCompletionCallback _executeUrlCompletionCallback;
2681     private HttpAsyncResult _asyncResultOfExecuteUrl;
2682     private bool _headersSentFromExecuteUrl;
2683
2684     internal override bool SupportsExecuteUrl {
2685         get { return true; }
2686     }
2687
2688     internal override IAsyncResult BeginExecuteUrl(
2689                                         String url, String method, String childHeaders,
2690                                         bool sendHeaders,
2691                                         bool addUserIndo, IntPtr token, String name, String authType,
2692                                         byte[] entity,
2693                                         AsyncCallback cb, Object state) {
2694
2695         if (_ecb == IntPtr.Zero ||              // after done with session
2696             _asyncResultOfExecuteUrl != null || // another ExecuteUrl in progress
2697             (sendHeaders && HeadersSent()))     // asked to send headers, but already sent them
2698         {
2699             throw new InvalidOperationException(SR.GetString(SR.Cannot_execute_url_in_this_context));
2700         }
2701
2702         if (entity != null && entity.Length > 0) {
2703             int ret = UnsafeNativeMethods.EcbGetExecUrlEntityInfo(entity.Length, entity, out _entity);
2704             if (ret != 1) {
2705                 throw new HttpException(SR.GetString(SR.Failed_to_execute_url));
2706             }
2707         }
2708
2709         Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl: url=\"" + url + "\".");
2710
2711
2712         HttpAsyncResult ar = new HttpAsyncResult(cb, state);
2713         _asyncResultOfExecuteUrl = ar;
2714
2715         _executeUrlCompletionCallback = new ISAPIAsyncCompletionCallback(OnExecuteUrlCompletion);
2716         _rootedThis = GCHandle.Alloc(this); // root for the duration of ExecuteUrl
2717
2718         int rc;
2719         try {
2720             ar.MarkCallToBeginMethodStarted();
2721             rc = UnsafeNativeMethods.EcbExecuteUrlUnicode(_ecb,
2722                                         url, method, childHeaders,
2723                                         sendHeaders,
2724                                         addUserIndo, token, name, authType,
2725                                         _entity,
2726                                         _executeUrlCompletionCallback);
2727         }
2728         finally {
2729             ar.MarkCallToBeginMethodCompleted();
2730         }
2731
2732         if (rc != 1) {
2733             if (_entity != IntPtr.Zero) {
2734                 UnsafeNativeMethods.EcbFreeExecUrlEntityInfo(_entity);
2735             }
2736             _rootedThis.Free();
2737             _asyncResultOfExecuteUrl = null;
2738
2739             Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.BeginExecuteUrl: failed!");
2740
2741             throw new HttpException(SR.GetString(SR.Failed_to_execute_url));
2742         }
2743
2744         if (sendHeaders) {
2745             // ExecuteUrl will send headers, worker request should not
2746             _headersSentFromExecuteUrl = true;
2747         }
2748
2749         return ar;
2750     }
2751
2752     internal override void EndExecuteUrl(IAsyncResult result) {
2753
2754         Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.EndExecuteUrl");
2755
2756         HttpAsyncResult asyncResult = result as HttpAsyncResult;
2757         if (asyncResult != null) {
2758             asyncResult.End();
2759         }
2760     }
2761
2762     private void OnExecuteUrlCompletion(IntPtr ecb, int byteCount, int error) {
2763         if (_entity != IntPtr.Zero) {
2764             UnsafeNativeMethods.EcbFreeExecUrlEntityInfo(_entity);
2765         }
2766
2767         _rootedThis.Free();
2768
2769         Debug.Trace("ExecuteUrl", "ISAPIWorkerRequestInProcForIIS6.OnExecuteUrlCompletion");
2770
2771         // signal async caller to resume work
2772         HttpAsyncResult asyncResult = _asyncResultOfExecuteUrl;
2773         _asyncResultOfExecuteUrl = null;
2774         asyncResult.Complete(false, null, null);
2775     }
2776 }
2777
2778 //
2779 // Out-of-process worker request
2780 //
2781
2782 internal class ISAPIWorkerRequestOutOfProc : ISAPIWorkerRequest {
2783
2784     // sends chunks separately if the total length exceeds the following
2785     // to relieve the memory pressure on named pipes
2786     const int PM_FLUSH_THRESHOLD = 31*1024;
2787
2788     internal ISAPIWorkerRequestOutOfProc(IntPtr ecb) : base(ecb) {
2789         UnsafeNativeMethods.PMGetTraceContextId(ecb, out _traceId);
2790     }
2791
2792     private bool _useBaseTime = false;
2793     private const int _numServerVars = 32;
2794     private IDictionary _serverVars;
2795
2796     private static String[] _serverVarNames =
2797         new String[_numServerVars] {
2798             "APPL_MD_PATH", /* this one is not UTF8 so we don't decode it here */
2799             "ALL_RAW",
2800             "AUTH_PASSWORD",
2801             "AUTH_TYPE",
2802             "CERT_COOKIE",
2803             "CERT_FLAGS",
2804             "CERT_ISSUER",
2805             "CERT_KEYSIZE",
2806             "CERT_SECRETKEYSIZE",
2807             "CERT_SERIALNUMBER",
2808             "CERT_SERVER_ISSUER",
2809             "CERT_SERVER_SUBJECT",
2810             "CERT_SUBJECT",
2811             "GATEWAY_INTERFACE",
2812             "HTTP_COOKIE",
2813             "HTTP_USER_AGENT",
2814             "HTTPS",
2815             "HTTPS_KEYSIZE",
2816             "HTTPS_SECRETKEYSIZE",
2817             "HTTPS_SERVER_ISSUER",
2818             "HTTPS_SERVER_SUBJECT",
2819             "INSTANCE_ID",
2820             "INSTANCE_META_PATH",
2821             "LOCAL_ADDR",
2822             "LOGON_USER",
2823             "REMOTE_ADDR",
2824             "REMOTE_HOST",
2825             "SERVER_NAME",
2826             "SERVER_PORT",
2827             "SERVER_PROTOCOL",
2828             "SERVER_SOFTWARE",
2829             "REMOTE_PORT"
2830         };
2831
2832
2833     private void GetAllServerVars() {
2834         if (_ecb == IntPtr.Zero)
2835             return;
2836         RecyclableByteBuffer buf = new RecyclableByteBuffer();
2837
2838         int r = UnsafeNativeMethods.PMGetAllServerVariables(_ecb, buf.Buffer, buf.Buffer.Length);
2839
2840         while (r < 0) {
2841             buf.Resize(-r);     // buffer not big enough
2842             r = UnsafeNativeMethods.PMGetAllServerVariables(_ecb, buf.Buffer, buf.Buffer.Length);
2843         }
2844
2845         if (r == 0)
2846             throw new HttpException(SR.GetString(SR.Cannot_retrieve_request_data));
2847
2848         // stub out first server var is it could contain non-UTF8 data
2849         // convert to characters and split the buffer into strings using default request encoding
2850
2851         String[] ss = buf.GetDecodedTabSeparatedStrings(Encoding.Default, _numServerVars-1, 1);
2852
2853         // recycle buffers
2854
2855         buf.Dispose();
2856
2857         // fill in the hashtable
2858
2859         _serverVars = new Hashtable(_numServerVars, StringComparer.OrdinalIgnoreCase);
2860
2861         _serverVars.Add("APPL_MD_PATH", HttpRuntime.AppDomainAppId);
2862
2863         for (int i = 1; i < _numServerVars; i++) {       // starting with 1 to skip APPL_MD_PATH
2864             _serverVars.Add(_serverVarNames[i], ss[i-1]);
2865         }
2866     }
2867
2868
2869     internal override int GetBasicsCore(byte[] buffer, int size, int[] contentInfo) {
2870         if (_ecb == IntPtr.Zero)
2871             return 0;
2872
2873         return UnsafeNativeMethods.PMGetBasics(_ecb, buffer, size, contentInfo);
2874     }
2875
2876     internal override int GetQueryStringCore(int encode, StringBuilder buffer, int size) {
2877         if (_ecb == IntPtr.Zero)
2878             return 0;
2879
2880         return UnsafeNativeMethods.PMGetQueryString(_ecb, encode, buffer, size);
2881     }
2882
2883     internal override int GetQueryStringRawBytesCore(byte[] buffer, int size) {
2884         if (_ecb == IntPtr.Zero)
2885             return 0;
2886
2887         return UnsafeNativeMethods.PMGetQueryStringRawBytes(_ecb, buffer, size);
2888     }
2889
2890     internal override int GetPreloadedPostedContentCore(byte[] bytes, int offset, int numBytesToRead) {
2891         if (_ecb == IntPtr.Zero)
2892             return 0;
2893         int rc = UnsafeNativeMethods.PMGetPreloadedPostedContent(_ecb, bytes, offset, numBytesToRead);
2894         if (rc > 0)
2895             PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc);
2896         return rc;
2897     }
2898
2899     internal override int GetAdditionalPostedContentCore(byte[] bytes, int offset, int bufferSize) {
2900         if (_ecb == IntPtr.Zero)
2901             return 0;
2902         int rc = UnsafeNativeMethods.PMGetAdditionalPostedContent(_ecb, bytes, offset, bufferSize);
2903         if (rc > 0)
2904             PerfCounters.IncrementCounterEx(AppPerfCounter.REQUEST_BYTES_IN, rc);
2905         return rc;
2906     }
2907
2908     internal override int IsClientConnectedCore() {
2909         if (_ecb == IntPtr.Zero)
2910             return 0;
2911
2912         return UnsafeNativeMethods.PMIsClientConnected(_ecb);
2913     }
2914
2915     // PackageFile for IIS5
2916     internal override MemoryBytes PackageFile(string filename, long offset64, long length64, bool isImpersonating) {
2917         // The offset and length must be less than Int32.MaxValue for IIS5. 
2918         // This should be true, since HttpFileResponseElement.ctor throws ArgumentOutOfRangeException for IIS5.
2919         Debug.Assert(offset64 < Int32.MaxValue);
2920         Debug.Assert(length64 < Int32.MaxValue);
2921         int offset = Convert.ToInt32(offset64);
2922         int length = Convert.ToInt32(length64);
2923
2924         byte[] offsetBytes = BitConverter.GetBytes(offset);
2925         byte[] lengthBytes = BitConverter.GetBytes(length);
2926         byte[] nameBytes = Encoding.Unicode.GetBytes(filename);
2927         // buffer consists of 1 byte for impersonation flag, 3 bytes for alignment, 
2928         // 4 bytes for offset, 4 bytes for length, n bytes for file name, 2 bytes for null terminator
2929         byte[] buffer = new byte[4 + offsetBytes.Length + lengthBytes.Length + nameBytes.Length + 2];
2930
2931         // first byte indicates whether impersonation is used
2932         if (isImpersonating)
2933             buffer[0] = 0x31;
2934         else
2935             buffer[0] = 0x30;
2936
2937         // bytes 2 thru 4 are unused for alignment
2938         // bytes 5 thru 8 are the offset
2939         Buffer.BlockCopy(offsetBytes, 0, buffer, 4, offsetBytes.Length);
2940         // bytes 9 thru 12 are the length
2941         Buffer.BlockCopy(lengthBytes, 0, buffer, 4 + offsetBytes.Length, lengthBytes.Length);
2942         // last two bytes are 0 for null terminator
2943         Buffer.BlockCopy(nameBytes, 0, buffer, 4 + offsetBytes.Length + lengthBytes.Length, nameBytes.Length);
2944
2945         return new MemoryBytes(buffer, buffer.Length, true, length);
2946     }
2947
2948     // ISAPIWorkerRequestOutOfProc
2949     internal override void FlushCore(byte[]     status,
2950                                      byte[]     header,
2951                                      int        keepConnected,
2952                                      int        totalBodySize,
2953                                      int        numBodyFragments,
2954                                      IntPtr[]   bodyFragments,
2955                                      int[]      bodyFragmentLengths,
2956                                      int        doneWithSession,
2957                                      int        finalStatus,
2958                                      out bool   async) {
2959         async = false;
2960
2961         if (_ecb == IntPtr.Zero)
2962             return;
2963
2964
2965         if (numBodyFragments > 1) {
2966             // Don't flush all at once if the length is over the threshold
2967
2968             int i = 0;
2969             while (i < numBodyFragments) {
2970                 bool first = (i == 0);
2971
2972                 int size = bodyFragmentLengths[i];
2973                 bool useTransmitFile = (bodyFragmentLengths[i] < 0);
2974                 int idx = i+1;
2975                 if (!useTransmitFile) {
2976                     while (idx < numBodyFragments
2977                            && size + bodyFragmentLengths[idx] < PM_FLUSH_THRESHOLD
2978                            && bodyFragmentLengths[idx] >= 0) {
2979                         size += bodyFragmentLengths[idx];
2980                         idx++;
2981                     }
2982                 }
2983
2984                 bool last = (idx == numBodyFragments);
2985
2986                 // bodyFragmentLength is negative for TransmitFile, but totalBodySize argument must be positive.
2987                 if (useTransmitFile)
2988                     size = -size;
2989
2990                 UnsafeNativeMethods.PMFlushCore(
2991                                         _ecb,
2992                                         first ? status : null,
2993                                         first ? header : null,
2994                                         keepConnected,
2995                                         size,
2996                                         i,
2997                                         idx-i,
2998                                         bodyFragments,
2999                                         bodyFragmentLengths,
3000                                         last ? doneWithSession : 0,
3001                                         last ? finalStatus : 0);
3002
3003                 i = idx;
3004             }
3005         }
3006         else {
3007             // Everything in one chunk
3008             UnsafeNativeMethods.PMFlushCore(
3009                                     _ecb,
3010                                     status,
3011                                     header,
3012                                     keepConnected,
3013                                     totalBodySize,
3014                                     0,
3015                                     numBodyFragments,
3016                                     bodyFragments,
3017                                     bodyFragmentLengths,
3018                                     doneWithSession,
3019                                     finalStatus);
3020         }
3021     }
3022
3023     internal override int CloseConnectionCore() {
3024         if (_ecb == IntPtr.Zero)
3025             return 0;
3026
3027         return UnsafeNativeMethods.PMCloseConnection(_ecb);
3028     }
3029
3030     internal override int MapUrlToPathCore(String url, byte[] buffer, int size) {
3031         if (_ecb == IntPtr.Zero)
3032             return 0;
3033
3034         return UnsafeNativeMethods.PMMapUrlToPath(_ecb, url, buffer, size);
3035     }
3036
3037     internal override IntPtr GetUserTokenCore() {
3038         if (_token == IntPtr.Zero && _ecb != IntPtr.Zero)
3039             _token = UnsafeNativeMethods.PMGetImpersonationToken(_ecb);
3040
3041         return _token;
3042     }
3043
3044     internal override IntPtr GetVirtualPathTokenCore() {
3045         if (_token == IntPtr.Zero && _ecb != IntPtr.Zero)
3046             _token = UnsafeNativeMethods.PMGetVirtualPathToken(_ecb);
3047
3048         return _token;
3049     }
3050
3051     internal override int AppendLogParameterCore(String logParam) {
3052         if (_ecb == IntPtr.Zero)
3053             return 0;
3054
3055         return UnsafeNativeMethods.PMAppendLogParameter(_ecb, logParam);
3056     }
3057
3058     internal override int GetClientCertificateCore(byte[] buffer, int [] pInts, long [] pDates) {
3059         if (_ecb == IntPtr.Zero)
3060             return 0;
3061
3062         return UnsafeNativeMethods.PMGetClientCertificate(_ecb, buffer, buffer.Length, pInts, pDates);
3063     }
3064
3065     public override String GetServerVariable(String name) {
3066         // PATH_TRANSLATED is mangled -- do not use the original server variable
3067         if (name.Equals("PATH_TRANSLATED"))
3068             return GetFilePathTranslated();
3069
3070         if (_serverVars == null)
3071             GetAllServerVars();
3072
3073         return (String)_serverVars[name];
3074     }
3075
3076     internal override int CallISAPI(UnsafeNativeMethods.CallISAPIFunc iFunction, byte [] bufIn, byte [] bufOut) {
3077         if (_ecb == IntPtr.Zero)
3078             return 0;
3079
3080         return UnsafeNativeMethods.PMCallISAPI(_ecb, iFunction, bufIn, bufIn.Length, bufOut, bufOut.Length);
3081     }
3082
3083     internal override void SendEmptyResponse() {
3084         if (_ecb == IntPtr.Zero)
3085             return;
3086
3087         UnsafeNativeMethods.PMEmptyResponse(_ecb);
3088     }
3089
3090     internal override DateTime GetStartTime() {
3091         if (_ecb == IntPtr.Zero || _useBaseTime)
3092             return base.GetStartTime();
3093
3094         long fileTime = UnsafeNativeMethods.PMGetStartTimeStamp(_ecb);
3095
3096         return DateTimeUtil.FromFileTimeToUtc(fileTime);
3097     }
3098
3099     internal override void ResetStartTime() {
3100         base.ResetStartTime();
3101         _useBaseTime = true;
3102     }
3103
3104 }
3105
3106 }