File: State\StateWorkerRequest.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="StateWorkerRequest.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 * StateHttpWorkerRequest
 * 
 * Copyright (c) 1998-1999, Microsoft Corporation
 * 
 */
 
namespace System.Web.SessionState {
 
    using System.Text;
    using System.Configuration.Assemblies;
    using System.Runtime.InteropServices;   
    using System.Collections;    
    using System.Web;
    using System.Web.Util;
    using System.Globalization;
 
    class StateHttpWorkerRequest : HttpWorkerRequest {
 
        /* long enough to hold the string representation of an IPv4 or IPv6 address; keep in sync with tracker.cxx */
        private const int ADDRESS_LENGTH_MAX = 64;
 
        IntPtr                                  _tracker;
        string                                  _uri;              
        UnsafeNativeMethods.StateProtocolExclusive    _exclusive;        
        int                                     _extraFlags;
        int                                     _timeout;           
        int                                     _lockCookie;        
        bool                                    _lockCookieExists;  
        int                                     _contentLength;     
        byte[]                                  _content;           
 
 
        UnsafeNativeMethods.StateProtocolVerb   _methodIndex;
        string                                  _method;         
                                                                 
        string                                  _remoteAddress;  
        int                                     _remotePort;     
        string                                  _localAddress;   
        int                                     _localPort;      
                                                                 
        StringBuilder                           _status;         
        int                                     _statusCode;     
        StringBuilder                           _headers;        
        IntPtr                           _unmanagedState; 
        bool                                    _sent;           
 
        internal StateHttpWorkerRequest(
                   IntPtr tracker,
                   UnsafeNativeMethods.StateProtocolVerb methodIndex,
                   string uri,
                   UnsafeNativeMethods.StateProtocolExclusive exclusive,
                   int extraFlags,
                   int timeout,
                   int lockCookieExists,
                   int lockCookie,
                   int contentLength,
                   IntPtr content
                   ) {
            _tracker = tracker;
            _methodIndex = methodIndex;
            switch (_methodIndex) {
                case UnsafeNativeMethods.StateProtocolVerb.GET:
                    _method = "GET";
                    break;
 
                case UnsafeNativeMethods.StateProtocolVerb.PUT:
                    _method = "PUT";
                    break;
 
                case UnsafeNativeMethods.StateProtocolVerb.HEAD:
                    _method = "HEAD";
                    break;
 
                case UnsafeNativeMethods.StateProtocolVerb.DELETE:
                    _method = "DELETE";
                    break;
 
                default:
                    Debug.Assert(false, "Shouldn't get here!");
                    break;
            }
 
            _uri = uri;
            // Handle the ASP1.1 case which prepends an extra / to the URI
            if (_uri.StartsWith("//", StringComparison.Ordinal)) {
                _uri = _uri.Substring(1);
            }
            _exclusive = exclusive;
            _extraFlags = extraFlags;
            _timeout = timeout;
            _lockCookie = lockCookie;
            _lockCookieExists = lockCookieExists != 0;
            _contentLength = contentLength;
            if (contentLength != 0) {
                Debug.Assert(_contentLength == IntPtr.Size);
                // Need to convert 'content', which is a ptr to native StateItem,
                // into a byte array because that's what GetPreloadedEntityBody
                // must return, and GetPreloadedEntityBody is what the pipeline uses
                // to read the body of the request, which in our case is just a pointer
                // to a native StateItem object.
#if WIN64
                ulong p = (ulong) content; 
                _content = new byte[8] 
                {
                    (byte) ((p & 0x00000000000000ff)),
                    (byte) ((p & 0x000000000000ff00) >> 8),
                    (byte) ((p & 0x0000000000ff0000) >> 16),
                    (byte) ((p & 0x00000000ff000000) >> 24),
                    (byte) ((p & 0x000000ff00000000) >> 32),
                    (byte) ((p & 0x0000ff0000000000) >> 40),
                    (byte) ((p & 0x00ff000000000000) >> 48),
                    (byte) ((p & 0xff00000000000000) >> 56),
                };
#else
                uint p = (uint) content; 
                _content = new byte[4] 
                {
                    (byte) ((p & 0x000000ff)),
                    (byte) ((p & 0x0000ff00) >> 8),
                    (byte) ((p & 0x00ff0000) >> 16),
                    (byte) ((p & 0xff000000) >> 24),
                };
#endif                
            }
 
            _status  = new StringBuilder(256);
            _headers = new StringBuilder(256);
        }
 
        public override string GetUriPath() {
            return HttpUtility.UrlDecode(_uri);
        }
 
        // The file path is used as the path for configuration.
        // This path should always be null, in order to retrieve
        // the machine configuration.
        public override string GetFilePath() {
            return null;
        }
 
        public override string GetQueryString() {
            return null;
        }
 
        public override string GetRawUrl() {
            return _uri;
        }
 
        public override string GetHttpVerbName() {
            return _method;
        }
 
        public override string GetHttpVersion() {
            return "HTTP/1.0";
        }
 
        public override string GetRemoteAddress() {
            StringBuilder   buf;
 
            if (_remoteAddress == null) {
                buf = new StringBuilder(ADDRESS_LENGTH_MAX);
                UnsafeNativeMethods.STWNDGetRemoteAddress(_tracker, buf);
                _remoteAddress = buf.ToString();
            }
 
            return _remoteAddress;
        }
 
        public override int GetRemotePort() {
            if (_remotePort == 0) {
                _remotePort = UnsafeNativeMethods.STWNDGetRemotePort(_tracker);
            }
 
            return _remotePort;
        }
 
        public override string GetLocalAddress() {
            StringBuilder   buf;
 
            if (_localAddress == null) {
                buf = new StringBuilder(ADDRESS_LENGTH_MAX);
                UnsafeNativeMethods.STWNDGetLocalAddress(_tracker, buf);
                _localAddress = buf.ToString();
            }
 
            return _localAddress;
        }
 
        public override int GetLocalPort() {
            if (_localPort == 0) {
                _localPort = UnsafeNativeMethods.STWNDGetLocalPort(_tracker);
            }
 
            return _localPort;
        }
 
        public override byte[] GetPreloadedEntityBody() {
            return _content;
        }
 
 
        public override bool IsEntireEntityBodyIsPreloaded() {
            /* Request is always preloaded */
            return true;
        }
 
 
        public override string MapPath(string virtualPath) {
            /*
             * Physical and virtual are identical to state server.
             */
            return virtualPath;
        }
 
        public override int ReadEntityBody(byte[] buffer, int size) {
            /* pretend everything is preloaded */
            return 0;
        }
 
        public override long GetBytesRead() {
            /* State web doesn't support partial reads */
            throw new NotSupportedException(SR.GetString(SR.Not_supported));
        }
 
        public override string GetKnownRequestHeader(int index) {
            string s = null;
 
            switch (index) {
                /* special case important ones */
                case HeaderContentLength:
                    s = (_contentLength).ToString(CultureInfo.InvariantCulture);
                    break;
            }
 
            return s;
        }
 
        public override string GetUnknownRequestHeader(string name) {
            string s = null;
 
            if (name.Equals(StateHeaders.EXCLUSIVE_NAME)) {
                switch (_exclusive) {
                    case UnsafeNativeMethods.StateProtocolExclusive.ACQUIRE:
                        s = StateHeaders.EXCLUSIVE_VALUE_ACQUIRE;
                        break;
 
                    case UnsafeNativeMethods.StateProtocolExclusive.RELEASE:
                        s = StateHeaders.EXCLUSIVE_VALUE_RELEASE;
                        break;
                }
            }
            else if (name.Equals(StateHeaders.TIMEOUT_NAME)) {
                if (_timeout != -1) {
                    s = (_timeout).ToString(CultureInfo.InvariantCulture);
                }
            }
            else if (name.Equals(StateHeaders.LOCKCOOKIE_NAME)) {
                if (_lockCookieExists) {
                    s = (_lockCookie).ToString(CultureInfo.InvariantCulture);
                }
            }
            else if (name.Equals(StateHeaders.EXTRAFLAGS_NAME)) {
                if (_extraFlags != -1) {
                    s = (_extraFlags).ToString(CultureInfo.InvariantCulture);
                }
            }
 
            return s;
        }
 
        public override string[][] GetUnknownRequestHeaders() {
            string [][] ret;
            int         c, i;
 
            c = 0;
            if (_exclusive != (UnsafeNativeMethods.StateProtocolExclusive) (-1)) {
                c++;
            }
 
            if (_extraFlags != -1) {
                c++;
            }
 
            if (_timeout != -1) {
                c++;
            }
 
            if (_lockCookieExists) {
                c++;
            }
 
            if (c == 0)
                return null;
 
            ret = new string[c][];
            i = 0;
            if (_exclusive != (UnsafeNativeMethods.StateProtocolExclusive) (-1)) {
                ret[0] = new string[2];
                ret[0][0] = StateHeaders.EXCLUSIVE_NAME;
                if (_exclusive == UnsafeNativeMethods.StateProtocolExclusive.ACQUIRE) {
                    ret[0][1] = StateHeaders.EXCLUSIVE_VALUE_ACQUIRE;
                }
                else {
                    Debug.Assert(_exclusive == UnsafeNativeMethods.StateProtocolExclusive.RELEASE, "_exclusive == UnsafeNativeMethods.StateProtocolExclusive.RELEASE");
                    ret[0][1] = StateHeaders.EXCLUSIVE_VALUE_RELEASE;
                }
 
                i++;
            }
 
            if (_timeout != -1) {
                ret[i] = new string[2];
                ret[i][0] = StateHeaders.TIMEOUT_NAME;
                ret[i][1] = (_timeout).ToString(CultureInfo.InvariantCulture);
 
                i++;
            }
 
            if (_lockCookieExists) {
                ret[i] = new string[2];
                ret[i][0] = StateHeaders.LOCKCOOKIE_NAME;
                ret[i][1] = (_lockCookie).ToString(CultureInfo.InvariantCulture);
 
                i++;
            }
 
            if (_extraFlags != -1) {
                ret[i] = new string[2];
                ret[i][0] = StateHeaders.EXTRAFLAGS_NAME;
                ret[i][1] = (_extraFlags).ToString(CultureInfo.InvariantCulture);
 
                i++;
            }
 
            return ret;
        }
 
        public override void SendStatus(int statusCode, string statusDescription) {
            Debug.Assert(!_sent);
            _statusCode = statusCode;
            _status.Append((statusCode).ToString(CultureInfo.InvariantCulture) + " " + statusDescription + "\r\n");
        }
 
        public override void SendKnownResponseHeader(int index, string value) {
            Debug.Assert(!_sent);
            _headers.Append(GetKnownResponseHeaderName(index));
            _headers.Append(": ");
            _headers.Append(value);
            _headers.Append("\r\n");
        }
 
        public override void SendUnknownResponseHeader(string name, string value) {
            Debug.Assert(!_sent);
            _headers.Append(name);
            _headers.Append(": ");
            _headers.Append(value);
            _headers.Append("\r\n");
        }
 
        public override void SendCalculatedContentLength(int contentLength) {
            Debug.Assert(!_sent);
            /*
             * Do nothing - we append the content-length in STWNDSendResponse.
             */
        }
 
        public override bool HeadersSent() {
            return _sent;
        }
 
        public override bool IsClientConnected() {
            return UnsafeNativeMethods.STWNDIsClientConnected(_tracker);
        }
 
        public override void CloseConnection() {
            UnsafeNativeMethods.STWNDCloseConnection(_tracker);
        }
 
        private void SendResponse() {
            if (!_sent) {
                _sent = true;
                UnsafeNativeMethods.STWNDSendResponse(
                                    _tracker, 
                                    _status, 
                                    _status.Length, 
                                    _headers, 
                                    _headers.Length, 
                                    _unmanagedState);
            }
        }
 
        public override void SendResponseFromMemory(byte[] data, int length) {
            /*
             * The only content besides error message text is the pointer
             * to the state item in unmanaged memory.
             */
            if (_statusCode == 200) {
                Debug.Assert(_unmanagedState == IntPtr.Zero, "_unmanagedState == 0");
                Debug.Assert(length == IntPtr.Size, "length == IntPtr.Size");
                Debug.Assert(_methodIndex == UnsafeNativeMethods.StateProtocolVerb.GET, "verb == GET");
                Debug.Assert(_exclusive != UnsafeNativeMethods.StateProtocolExclusive.RELEASE,
                             "correct exclusive method");
 
                if (IntPtr.Size == 4) {
                    _unmanagedState = (IntPtr)
                        (((int)data[0])       |
                         ((int)data[1] << 8)  |
                         ((int)data[2] << 16) |
                         ((int)data[3] << 24));
                }
                else {
                    _unmanagedState = (IntPtr)
                        (((long)data[0])       |
                         ((long)data[1] << 8)  |
                         ((long)data[2] << 16) |
                         ((long)data[3] << 24) |
                         ((long)data[4] << 32) |
                         ((long)data[5] << 40) |
                         ((long)data[6] << 48) |
                         ((long)data[7] << 56));
                }
 
                Debug.Assert(_unmanagedState != IntPtr.Zero, "_unmanagedState != 0");
            }
 
            SendResponse();
        }
 
        public override void SendResponseFromFile(string filename, long offset, long length) {
            /* Not needed by state application */
            throw new NotSupportedException(SR.GetString(SR.Not_supported));
        }
 
        public override void SendResponseFromFile(IntPtr handle, long offset, long length) {
            /* Not needed by state application */
            throw new NotSupportedException(SR.GetString(SR.Not_supported));
        }
 
        public override void FlushResponse(bool finalFlush) {
            SendResponse();
        }
 
        public override void EndOfRequest() {
            SendResponse();
            UnsafeNativeMethods.STWNDEndOfRequest(_tracker);
        }
    }
}