The Labs \ Source Viewer \ SSCLI \ System.Runtime.Remoting.Channels.Tcp \ TcpSocketHandler

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. //==========================================================================
  16. // File: TcpSocketManager.cs
  17. //
  18. // Summary: Provides a base for the client and server tcp socket
  19. // managers.
  20. //
  21. //==========================================================================
  22. using System;
  23. using System.Collections;
  24. using System.Globalization;
  25. using System.IO;
  26. using System.Net;
  27. using System.Net.Sockets;
  28. using System.Runtime.Remoting.Messaging;
  29. using System.Text;
  30. using System.Threading;
  31. namespace System.Runtime.Remoting.Channels.Tcp
  32. {
  33.    
  34.     // A client socket manager instance should encapsulate the socket
  35.     // for the purpose of reading a response
  36.     internal abstract class TcpSocketHandler : SocketHandler
  37.     {
  38.         private static byte[] s_protocolPreamble = Encoding.ASCII.GetBytes(".NET");
  39.         private static byte[] s_protocolVersion1_0 = new byte[] {1, 0};
  40.        
  41.        
  42.         public TcpSocketHandler(Socket socket, Stream stream) : this(socket, null, stream)
  43.         {
  44.         }
  45.         // TcpSocketHandler
  46.         public TcpSocketHandler(Socket socket, RequestQueue requestQueue, Stream stream) : base(socket, requestQueue, stream)
  47.         {
  48.         }
  49.         // TcpSocketHandler
  50.        
  51.         private void ReadAndMatchPreamble()
  52.         {
  53.             // make sure that the incoming data starts with the preamble
  54.             InternalRemotingServices.RemotingAssert(s_protocolPreamble.Length == 4, "The preamble is supposed to be 4 bytes ('.NET'). Somebody changed it...");
  55.            
  56.             if (ReadAndMatchFourBytes(s_protocolPreamble) == false) {
  57.                 throw new RemotingException(CoreChannel.GetResourceString("Remoting_Tcp_ExpectingPreamble"));
  58.             }
  59.         }
  60.         // ReadAndMatchPreamble
  61.         protected void WritePreambleAndVersion(Stream outputStream)
  62.         {
  63.             outputStream.Write(s_protocolPreamble, 0, s_protocolPreamble.Length);
  64.             outputStream.Write(s_protocolVersion1_0, 0, s_protocolVersion1_0.Length);
  65.         }
  66.         // WritePreamble
  67.        
  68.         protected void ReadVersionAndOperation(out UInt16 operation)
  69.         {
  70.             // check for the preamble
  71.             ReadAndMatchPreamble();
  72.            
  73.             // Check the version number.
  74.             byte majorVersion = (byte)ReadByte();
  75.             byte minorVersion = (byte)ReadByte();
  76.             if ((majorVersion != 1) || (minorVersion != 0)) {
  77.                 throw new RemotingException(String.Format(CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnknownProtocolVersion"), majorVersion.ToString(CultureInfo.CurrentCulture) + "." + minorVersion.ToString(CultureInfo.CurrentCulture)));
  78.             }
  79.            
  80.             // Read the operation
  81.             operation = ReadUInt16();
  82.            
  83.         }
  84.         // ReadVersionAndOperation
  85.        
  86.         protected void ReadContentLength(out bool chunked, out int contentLength)
  87.         {
  88.             contentLength = -1;
  89.            
  90.             UInt16 header = ReadUInt16();
  91.             if (header == TcpContentDelimiter.Chunked) {
  92.                 chunked = true;
  93.             }
  94.             else if (header == TcpContentDelimiter.ContentLength) {
  95.                 chunked = false;
  96.                 contentLength = ReadInt32();
  97.             }
  98.             else {
  99.                 throw new RemotingException(String.Format(CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_ExpectingContentLengthHeader"), header.ToString(CultureInfo.CurrentCulture)));
  100.                
  101.             }
  102.         }
  103.         // ReadContentLength
  104.        
  105.         protected void ReadToEndOfHeaders(BaseTransportHeaders headers)
  106.         {
  107.             bool bError = false;
  108.             string statusPhrase = null;
  109.            
  110.             UInt16 headerType = ReadUInt16();
  111.             while (headerType != TcpHeaders.EndOfHeaders) {
  112.                 if (headerType == TcpHeaders.Custom) {
  113.                     string headerName = ReadCountedString();
  114.                     string headerValue = ReadCountedString();
  115.                    
  116.                     headers[headerName] = headerValue;
  117.                 }
  118.                 else if (headerType == TcpHeaders.RequestUri) {
  119.                     ReadAndVerifyHeaderFormat("RequestUri", TcpHeaderFormat.CountedString);
  120.                    
  121.                     // read uri (and make sure that no channel specific data is present)
  122.                     string uri = ReadCountedString();
  123.                    
  124.                     string channelURI;
  125.                     string objectURI;
  126.                     channelURI = TcpChannelHelper.ParseURL(uri, out objectURI);
  127.                     if (channelURI == null)
  128.                         objectURI = uri;
  129.                    
  130.                     headers.RequestUri = objectURI;
  131.                 }
  132.                 else if (headerType == TcpHeaders.StatusCode) {
  133.                     ReadAndVerifyHeaderFormat("StatusCode", TcpHeaderFormat.UInt16);
  134.                    
  135.                     UInt16 statusCode = ReadUInt16();
  136.                     // We'll throw an exception here if there was an error. If an error
  137.                     // occurs above the transport level, the status code will still be
  138.                     // success here.
  139.                     if (statusCode != TcpStatusCode.Success)
  140.                         bError = true;
  141.                 }
  142.                 else if (headerType == TcpHeaders.StatusPhrase) {
  143.                     ReadAndVerifyHeaderFormat("StatusPhrase", TcpHeaderFormat.CountedString);
  144.                    
  145.                     statusPhrase = ReadCountedString();
  146.                 }
  147.                 else if (headerType == TcpHeaders.ContentType) {
  148.                     ReadAndVerifyHeaderFormat("Content-Type", TcpHeaderFormat.CountedString);
  149.                    
  150.                     string contentType = ReadCountedString();
  151.                     headers.ContentType = contentType;
  152.                 }
  153.                 else {
  154.                     // unknown header: Read header format and ignore rest of data
  155.                     byte headerFormat = (byte)ReadByte();
  156.                    
  157.                     switch (headerFormat) {
  158.                         case TcpHeaderFormat.Void:
  159.                             break;
  160.                         case TcpHeaderFormat.CountedString:
  161.                             ReadCountedString();
  162.                             break;
  163.                         case TcpHeaderFormat.Byte:
  164.                             ReadByte();
  165.                             break;
  166.                         case TcpHeaderFormat.UInt16:
  167.                             ReadUInt16();
  168.                             break;
  169.                         case TcpHeaderFormat.Int32:
  170.                             ReadInt32();
  171.                             break;
  172.                         default:
  173.                            
  174.                            
  175.                             {
  176.                                 // unknown format
  177.                                 throw new RemotingException(String.Format(CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnknownHeaderType"), headerType, headerFormat));
  178.                             }
  179.                             break;
  180.                        
  181.                     }
  182.                     // switch (format)
  183.                 }
  184.                
  185.                 // read next header token
  186.                 headerType = ReadUInt16();
  187.             }
  188.             // loop until end of headers
  189.             // if an error occurred, throw an exception
  190.             if (bError) {
  191.                 if (statusPhrase == null)
  192.                     statusPhrase = "";
  193.                
  194.                 throw new RemotingException(String.Format(CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_GenericServerError"), statusPhrase));
  195.             }
  196.         }
  197.         // ReadToEndOfHeaders
  198.        
  199.         protected void WriteHeaders(ITransportHeaders headers, Stream outputStream)
  200.         {
  201.             IEnumerator it = null;
  202.             BaseTransportHeaders wkHeaders = headers as BaseTransportHeaders;
  203.            
  204.             if (wkHeaders != null) {
  205.                 // write out well known headers
  206.                 // NOTE: RequestUri is written out elsewhere.
  207.                
  208.                 if (wkHeaders.ContentType != null) {
  209.                     WriteContentTypeHeader(wkHeaders.ContentType, outputStream);
  210.                 }
  211.                
  212.                 it = wkHeaders.GetOtherHeadersEnumerator();
  213.             }
  214.             else {
  215.                 it = headers.GetEnumerator();
  216.             }
  217.            
  218.            
  219.             // write custom headers
  220.             if (it != null) {
  221.                 while (it.MoveNext()) {
  222.                     DictionaryEntry header = (DictionaryEntry)it.Current;
  223.                    
  224.                     string headerName = (string)header.Key;
  225.                    
  226.                     // exclude special headers
  227.                     if (!StringHelper.StartsWithDoubleUnderscore(headerName)) {
  228.                         string headerValue = header.Value.ToString();
  229.                        
  230.                         if (wkHeaders == null) {
  231.                             if (String.Compare(headerName, "Content-Type", StringComparison.OrdinalIgnoreCase) == 0) {
  232.                                 WriteContentTypeHeader(headerValue, outputStream);
  233.                                 continue;
  234.                             }
  235.                         }
  236.                        
  237.                         WriteCustomHeader(headerName, headerValue, outputStream);
  238.                     }
  239.                 }
  240.                 // while (it.MoveNext())
  241.             }
  242.            
  243.             // write EndOfHeaders token
  244.             WriteUInt16(TcpHeaders.EndOfHeaders, outputStream);
  245.         }
  246.         // WriteHeaders
  247.        
  248.         private void WriteContentTypeHeader(string value, Stream outputStream)
  249.         {
  250.             WriteUInt16(TcpHeaders.ContentType, outputStream);
  251.             WriteByte(TcpHeaderFormat.CountedString, outputStream);
  252.             WriteCountedString(value, outputStream);
  253.         }
  254.         // WriteContentTypeHeader
  255.         private void WriteCustomHeader(string name, string value, Stream outputStream)
  256.         {
  257.             WriteUInt16(TcpHeaders.Custom, outputStream);
  258.             WriteCountedString(name, outputStream);
  259.             WriteCountedString(value, outputStream);
  260.         }
  261.         // WriteCustomHeader
  262.        
  263.        
  264.         protected string ReadCountedString()
  265.         {
  266.             // strings are formatted as follows
  267.             // [string format (1-byte)][encoded-size (int32)][string value (encoded-size length in bytes)]
  268.            
  269.             byte strFormat = (byte)ReadByte();
  270.             int strDataSize = ReadInt32();
  271.            
  272.             if (strDataSize > 0) {
  273.                 byte[] data = new byte[strDataSize];
  274.                
  275.                 // SocketHander::Read waits until it reads all requested data
  276.                 Read(data, 0, strDataSize);
  277.                
  278.                 switch (strFormat) {
  279.                     case TcpStringFormat.Unicode:
  280.                         return Encoding.Unicode.GetString(data);
  281.                     case TcpStringFormat.UTF8:
  282.                        
  283.                         return Encoding.UTF8.GetString(data);
  284.                     default:
  285.                        
  286.                         throw new RemotingException(String.Format(CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_UnrecognizedStringFormat"), strFormat.ToString(CultureInfo.CurrentCulture)));
  287.                         break;
  288.                 }
  289.             }
  290.             else {
  291.                 return null;
  292.             }
  293.         }
  294.         // ReadCountedString
  295.        
  296.         protected void WriteCountedString(string str, Stream outputStream)
  297.         {
  298.             // strings are formatted as follows [string length (int32)][string value (unicode)]
  299.             int strLength = 0;
  300.             if (str != null)
  301.                 strLength = str.Length;
  302.            
  303.             if (strLength > 0) {
  304.                 byte[] strBytes = Encoding.UTF8.GetBytes(str);
  305.                
  306.                 // write string format
  307.                 WriteByte(TcpStringFormat.UTF8, outputStream);
  308.                
  309.                 // write string data size
  310.                 WriteInt32(strBytes.Length, outputStream);
  311.                
  312.                 // write string data
  313.                 outputStream.Write(strBytes, 0, strBytes.Length);
  314.             }
  315.             else {
  316.                 // write string format
  317.                 // (just call it Unicode (doesn't matter since there is no data))
  318.                 WriteByte(TcpStringFormat.Unicode, outputStream);
  319.                
  320.                 // stream data size is 0.
  321.                 WriteInt32(0, outputStream);
  322.             }
  323.         }
  324.         // WriteCountedString
  325.        
  326.         private void ReadAndVerifyHeaderFormat(string headerName, byte expectedFormat)
  327.         {
  328.             byte headerFormat = (byte)ReadByte();
  329.            
  330.             if (headerFormat != expectedFormat) {
  331.                 throw new RemotingException(String.Format(CultureInfo.CurrentCulture, CoreChannel.GetResourceString("Remoting_Tcp_IncorrectHeaderFormat"), expectedFormat, headerName));
  332.             }
  333.         }
  334.         // ReadAndVerifyHeaderFormat
  335.     }
  336.     // TcpSocketHandler
  337. }
  338. // namespace System.Runtime.Remoting.Channels

Developer Fusion