File: channels\ipc\ipcmanager.cs
Project: ndp\clr\src\managedlibraries\remoting\System.Runtime.Remoting.csproj (System.Runtime.Remoting)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
//==========================================================================
//  File:       IpcHandler.cs
//  Author:   Microsoft@Microsoft.com
//  Summary:    Class for managing a socket connection.
//
//==========================================================================
 
using System;
using System.IO;
using System.Runtime.Remoting.Messaging;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Runtime.Remoting.Channels.Tcp;
using System.Globalization;
 
 
namespace System.Runtime.Remoting.Channels.Ipc
{
 
    internal class IpcServerHandler : TcpSocketHandler
    {
        // The stream to manage incoming request data
        private Stream _stream = null;
        protected Stream _requestStream = null;
        // NamePipe associated with this manager
        protected IpcPort _port;
        private RequestQueue _requestQueue;
        bool _bOneWayRequest;
        int _contentLength;
 
        internal IpcServerHandler(IpcPort port, RequestQueue requestQueue, Stream stream) : base (null, requestQueue, stream)
        {
            _requestQueue = requestQueue;
            _port = port;
            _stream = stream;
        }
 
        internal Stream GetRequestStream()
        {
                _requestStream =  new TcpFixedLengthReadingStream(this, _contentLength);
                return _requestStream;
        }
 
        internal IpcPort Port {
            get {
                return _port;
            }
        }
 
        internal ITransportHeaders ReadHeaders()
        {
            BaseTransportHeaders headers = new BaseTransportHeaders();
 
            UInt16 operation;
            ReadVersionAndOperation(out operation);
 
            if (operation == TcpOperations.OneWayRequest)
            {
                _bOneWayRequest = true;
            }
 
            bool bChunked = false;
            // content length must come next (may be chunked or a specific length)
            ReadContentLength(out bChunked, out _contentLength);
 
            // read to end of headers  
            ReadToEndOfHeaders(headers);   
                           
            return headers;
        }
 
        protected new void ReadToEndOfHeaders(BaseTransportHeaders headers)
        {
            bool bError = false;
            String statusPhrase = null; 
        
            UInt16 headerType = ReadUInt16();
            while (headerType != TcpHeaders.EndOfHeaders)
            {
                if (headerType == TcpHeaders.Custom)
                {
                    String headerName = ReadCountedString();
                    String headerValue = ReadCountedString();
 
                    headers[headerName] = headerValue;
                }
                else
                if (headerType == TcpHeaders.RequestUri)
                {         
                    ReadAndVerifyHeaderFormat("RequestUri", TcpHeaderFormat.CountedString);
                
                    // read uri (and make sure that no channel specific data is present)
                    String uri = ReadCountedString();
                    
                    String channelURI;
                    String objectURI;
                    channelURI = IpcChannelHelper.ParseURL(uri, out objectURI);
                    if (channelURI == null)
                        objectURI = uri;              
            
                    headers.RequestUri = objectURI;
                }
                else
                if (headerType == TcpHeaders.StatusCode)
                {
                    ReadAndVerifyHeaderFormat("StatusCode", TcpHeaderFormat.UInt16);
                    
                    UInt16 statusCode = ReadUInt16();
                    // We'll throw an exception here if there was an error. If an error
                    //   occurs above the transport level, the status code will still be
                    //   success here.
                    if (statusCode != TcpStatusCode.Success)
                        bError = true;
                }
                else
                if (headerType == TcpHeaders.StatusPhrase)
                {
                    ReadAndVerifyHeaderFormat("StatusPhrase", TcpHeaderFormat.CountedString);
                
                    statusPhrase = ReadCountedString();
                }
                else
                if (headerType == TcpHeaders.ContentType)
                {
                    ReadAndVerifyHeaderFormat("Content-Type", TcpHeaderFormat.CountedString);
                
                    String contentType = ReadCountedString();
                    headers.ContentType = contentType;
                }
                else
                {
                    // unknown header: Read header format and ignore rest of data
                    byte headerFormat = (byte)ReadByte();
 
                    switch (headerFormat)
                    {
                    case TcpHeaderFormat.Void: break;
                    case TcpHeaderFormat.CountedString: ReadCountedString(); break;
                    case TcpHeaderFormat.Byte: ReadByte(); break;
                    case TcpHeaderFormat.UInt16: ReadUInt16(); break;
                    case TcpHeaderFormat.Int32: ReadInt32(); break;
 
                    default:
                    {
                        // unknown format
                        throw new RemotingException(
                            String.Format(
                                CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnknownHeaderType"),
                                headerType, headerFormat));
                    }
                    
                    } // switch (format)
                
                }
 
                // read next header token
                headerType = ReadUInt16();
            } // loop until end of headers         
 
            // if an error occurred, throw an exception
            if (bError)
            {
                if (statusPhrase == null)
                    statusPhrase = "";
                    
                throw new RemotingException(
                    String.Format(
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_GenericServerError"),
                        statusPhrase));
            }
        } // ReadToEndOfHeaders
 
        private void ReadAndVerifyHeaderFormat(String headerName, byte expectedFormat)
        {
            byte headerFormat = (byte)ReadByte();
 
            if (headerFormat != expectedFormat)
            {
                throw new RemotingException(
                    String.Format(
                        CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_IncorrectHeaderFormat"),
                        expectedFormat, headerName));
            }
        } // ReadAndVerifyHeaderFormat
 
        // Prepare for reading a new request off of the same socket
        protected override void PrepareForNewMessage()
        {
        } // PrepareForNewRequest
 
        protected override void SendErrorMessageIfPossible(Exception e)
        {
            // bail out if the original request was OneWay (means the client doesn't even
            //   want or expect to receive responses or error messages)
            if (_bOneWayRequest)
                return;
        
            // build up headers and send      
            ChunkedMemoryStream headerStream = new ChunkedMemoryStream(CoreChannel.BufferPool);
 
            // output preamble and version
            WritePreambleAndVersion(headerStream);
            // output opcode
            WriteUInt16(TcpOperations.Reply, headerStream);
            // output content length delimiter (0-length stream)
            WriteUInt16(TcpContentDelimiter.ContentLength, headerStream);
            WriteInt32(0, headerStream);
 
            // output status code and reason
            WriteUInt16(TcpHeaders.StatusCode, headerStream);
            WriteByte(TcpHeaderFormat.UInt16, headerStream);
            WriteUInt16(TcpStatusCode.GenericError, headerStream);
            // we purposely don't include the stack trace to avoid giving
            //   out too much information for security purposes.
            WriteUInt16(TcpHeaders.StatusPhrase, headerStream);
            WriteByte(TcpHeaderFormat.CountedString, headerStream);
            WriteCountedString(e.ToString(), headerStream);
 
            // indicate that we are about to close the connection
            WriteUInt16(TcpHeaders.CloseConnection, headerStream);
            WriteByte(TcpHeaderFormat.Void, headerStream);
 
            // end of headers
            WriteUInt16(TcpHeaders.EndOfHeaders, headerStream);
            
            headerStream.WriteTo(NetStream);
            headerStream.Close();
        }     
 
        internal void SendResponse(ITransportHeaders headers, Stream contentStream)
        {
            // bail out if the original request was OneWay (means the client doesn't even
            //   want or expect to receive responses or error messages)
            if (_bOneWayRequest)
                return;            
        
            // build up headers and send      
            ChunkedMemoryStream headerStream = new ChunkedMemoryStream(CoreChannel.BufferPool);
 
            // output preamble and version
            WritePreambleAndVersion(headerStream);
            // output opcode
            WriteUInt16(TcpOperations.Reply, headerStream);
            // output content length delimiter
            WriteUInt16(TcpContentDelimiter.ContentLength, headerStream);
            WriteInt32((int)contentStream.Length, headerStream);
 
            // No status code header is needed because if we're in this code path
            //   the data transfer succeeded as far as the transport protocol is
            //   concerned (and the success status code is optional).
 
            WriteHeaders(headers, headerStream);
            
            headerStream.WriteTo(NetStream);
            headerStream.Close();
 
            StreamHelper.CopyStream(contentStream, NetStream);          
                         
            contentStream.Close();            
        }
    }
 
}