The Labs \ Source Viewer \ SSCLI \ System.Runtime.Remoting.Channels.Http \ HttpEncodingHelper

  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: HttpChannelHelper.cs
  17. //
  18. // Summary: Implements helper methods for http client and server channels.
  19. //
  20. //==========================================================================
  21. using System;
  22. using System.Net;
  23. using System.Net.Sockets;
  24. using System.Text;
  25. using System.Globalization;
  26. namespace System.Runtime.Remoting.Channels.Http
  27. {
  28.    
  29.    
  30.     static internal class HttpChannelHelper
  31.     {
  32.         private const string _http = "http://";
  33.        
  34.         private static char[] s_semicolonSeparator = new char[] {';'};
  35.        
  36.        
  37.         // Determine if the url starts with "http://"
  38.         static internal int StartsWithHttp(string url)
  39.         {
  40.             int urlLength = url.Length;
  41.            
  42.             if (StringHelper.StartsWithAsciiIgnoreCasePrefixLower(url, _http))
  43.                 return _http.Length;
  44.             else
  45.                 return -1;
  46.         }
  47.         // StartsWithHttp
  48.        
  49.         // Used by http channels to implement IChannel::Parse.
  50.         // It returns the channel uri and places object uri into out parameter.
  51.         static internal string ParseURL(string url, out string objectURI)
  52.         {
  53.             // Set the out parameters
  54.             objectURI = null;
  55.            
  56.             int separator = StartsWithHttp(url);
  57.             if (separator == -1)
  58.                 return null;
  59.            
  60.             // find next slash (after end of scheme)
  61.             separator = url.IndexOf('/', separator);
  62.             if (-1 == separator) {
  63.                 return url;
  64.                 // means that the url is just "tcp://foo:90" or something like that
  65.             }
  66.            
  67.             // Extract the channel URI which is the prefix
  68.             string channelURI = url.Substring(0, separator);
  69.            
  70.             // Extract the object URI which is the suffix (leave the slash)
  71.             objectURI = url.Substring(separator);
  72.            
  73.             InternalRemotingServices.RemotingTrace("HTTPChannel.Parse URI in: " + url);
  74.             InternalRemotingServices.RemotingTrace("HTTPChannel.Parse channelURI: " + channelURI);
  75.             InternalRemotingServices.RemotingTrace("HTTPChannel.Parse objectURI: " + objectURI);
  76.            
  77.             return channelURI;
  78.         }
  79.         // ParseURL
  80.        
  81.         static internal string GetObjectUriFromRequestUri(string uri)
  82.         {
  83.             // We assume uri may be in one of the following forms
  84.             // http://myhost.com/myobject.rem
  85.             // /myobject.rem
  86.             // /myobject
  87.             // myobject.rem
  88.             // In all cases, myobject is considered to be the object URI (.rem might be absent)
  89.            
  90.             int start;
  91.             int end;
  92.             // range of characters to use
  93.             int index;
  94.             start = 0;
  95.             end = uri.Length;
  96.            
  97.             // first see if uri starts with http://
  98.             // and remove up to next slash if it does
  99.             start = StartsWithHttp(uri);
  100.             if (start != -1) {
  101.                 // remove domain name as well
  102.                 index = uri.IndexOf('/', start);
  103.                 if (index != -1)
  104.                     start = index + 1;
  105.                 else
  106.                     start = end;
  107.                 // uri will end up being ""
  108.             }
  109.             else {
  110.                 // remove "/" if this is an absolute path
  111.                 start = 0;
  112.                 if (uri[start] == '/')
  113.                     start++;
  114.             }
  115.            
  116.             // remove query string if present ('?' and everything past it)
  117.             index = uri.IndexOf('?');
  118.             if (index != -1)
  119.                 end = index;
  120.            
  121.             if (start < end)
  122.                 return CoreChannel.RemoveApplicationNameFromUri(uri.Substring(start, end - start));
  123.             else
  124.                 return "";
  125.         }
  126.         // GetObjectURIFromRequestURI
  127.        
  128.         static internal void ParseContentType(string contentType, out string value, out string charset)
  129.         {
  130.             charset = null;
  131.            
  132.             if (contentType == null) {
  133.                 value = null;
  134.                 return;
  135.             }
  136.            
  137.             string[] parts = contentType.Split(s_semicolonSeparator);
  138.            
  139.             // the actual content-type value is always first
  140.             value = parts[0];
  141.            
  142.             // examine name value pairs and look for charset
  143.             if (parts.Length > 0) {
  144.                 foreach (string part in parts) {
  145.                     int index = part.IndexOf('=');
  146.                     if (index != -1) {
  147.                         string key = part.Substring(0, index).Trim();
  148.                         if (String.Compare(key, "charset", StringComparison.OrdinalIgnoreCase) == 0) {
  149.                             if ((index + 1) < part.Length) {
  150.                                 // we had to make sure there is something after the
  151.                                 // equals sign.
  152.                                 charset = part.Substring(index + 1);
  153.                             }
  154.                             else {
  155.                                 charset = null;
  156.                             }
  157.                             return;
  158.                         }
  159.                     }
  160.                 }
  161.                 // foreach
  162.             }
  163.         }
  164.         // ParseContentType
  165.         static internal string ReplaceChannelUriWithThisString(string url, string channelUri)
  166.         {
  167.             // NOTE: channelUri is assumed to be scheme://machinename:port
  168.             // with NO trailing slash.
  169.            
  170.             string oldChannelUri;
  171.             string objUri;
  172.             oldChannelUri = HttpChannelHelper.ParseURL(url, out objUri);
  173.             InternalRemotingServices.RemotingAssert(oldChannelUri != null, "http url expected.");
  174.             InternalRemotingServices.RemotingAssert(objUri != null, "non-null objUri expected.");
  175.            
  176.             return channelUri + objUri;
  177.         }
  178.         // ReplaceChannelUriWithThisString
  179.        
  180.         // returns url with the machine name replaced with the ip address.
  181.         static internal string ReplaceMachineNameWithThisString(string url, string newMachineName)
  182.         {
  183.             string objectUri;
  184.             string channelUri = ParseURL(url, out objectUri);
  185.            
  186.             // find bounds of machine name
  187.             int index = StartsWithHttp(url);
  188.             if (index == -1)
  189.                 return url;
  190.            
  191.             int colonIndex = channelUri.IndexOf(':', index);
  192.             if (colonIndex == -1)
  193.                 colonIndex = channelUri.Length;
  194.            
  195.             // machine name is between index and up to but not including colonIndex,
  196.             // so we will replace those characters with the ip address.
  197.             string newUrl = url.Substring(0, index) + newMachineName + url.Substring(colonIndex);
  198.             return newUrl;
  199.         }
  200.         // ReplaceMachineNameWithIpAddress
  201.        
  202.         // Decodes a uri while it is in byte array form
  203.         static internal void DecodeUriInPlace(byte[] uriBytes, out int length)
  204.         {
  205.             int percentsFound = 0;
  206.             int count = uriBytes.Length;
  207.             length = count;
  208.             int co = 0;
  209.             while (co < count) {
  210.                 if (uriBytes[co] == (byte)'%') {
  211.                     // determine location to write to (we skip 2 character for each percent)
  212.                     int writePos = co - (percentsFound * 2);
  213.                    
  214.                     // decode in place by collapsing bytes "%XY" (actual byte is 16*Dec(X) + Dec(Y))
  215.                     uriBytes[writePos] = (byte)(16 * CharacterHexDigitToDecimal(uriBytes[co + 1]) + CharacterHexDigitToDecimal(uriBytes[co + 2]));
  216.                    
  217.                     percentsFound++;
  218.                     length -= 2;
  219.                     // we eliminated 2 characters from the length
  220.                     co += 3;
  221.                 }
  222.                 else {
  223.                     if (percentsFound != 0) {
  224.                         // we have to copy characters back into place since we will skip some characters
  225.                        
  226.                         // determine location to write to (we skip 2 character for each percent)
  227.                         int writePos = co - (percentsFound * 2);
  228.                        
  229.                         // copy character back into place
  230.                         uriBytes[writePos] = uriBytes[co];
  231.                     }
  232.                    
  233.                     co++;
  234.                 }
  235.             }
  236.            
  237.         }
  238.         // DecodeUri
  239.        
  240.        
  241.         // reading helper functions
  242.         static internal int CharacterHexDigitToDecimal(byte b)
  243.         {
  244.             switch ((char)b) {
  245.                 case 'F':
  246.                     return 15;
  247.                 case 'E':
  248.                     return 14;
  249.                 case 'D':
  250.                     return 13;
  251.                 case 'C':
  252.                     return 12;
  253.                 case 'B':
  254.                     return 11;
  255.                 case 'A':
  256.                     return 10;
  257.                 default:
  258.                     return b - (byte)'0';
  259.             }
  260.         }
  261.         // CharacterHexDigitToDecimal
  262.        
  263.         static internal char DecimalToCharacterHexDigit(int i)
  264.         {
  265.             switch (i) {
  266.                 case 15:
  267.                     return 'F';
  268.                 case 14:
  269.                     return 'E';
  270.                 case 13:
  271.                     return 'D';
  272.                 case 12:
  273.                     return 'C';
  274.                 case 11:
  275.                     return 'B';
  276.                 case 10:
  277.                     return 'A';
  278.                 default:
  279.                     return (char)(i + (byte)'0');
  280.             }
  281.            
  282.         }
  283.         // DecimalToCharacterHexDigit
  284.        
  285.        
  286.     }
  287.     // class HttpChannelHelper
  288.    
  289.    
  290.     static internal class HttpEncodingHelper
  291.     {
  292.         static internal string EncodeUriAsXLinkHref(string uri)
  293.         {
  294.             if (uri == null)
  295.                 return null;
  296.            
  297.             // uses modified encoding rules from xlink href spec for encoding uri's.
  298.             // http://www.w3.org/TR/2000/PR-xlink-20001220/#link-locators
  299.            
  300.             byte[] uriBytes = Encoding.UTF8.GetBytes(uri);
  301.            
  302.             StringBuilder sb = new StringBuilder(uri.Length);
  303.            
  304.             // iterate over uri bytes and build up an encoded string.
  305.             foreach (byte b in uriBytes) {
  306.                 if (!EscapeInXLinkHref(b)) {
  307.                     sb.Append((char)b);
  308.                 }
  309.                 else {
  310.                     // the character needs to be encoded as %HH
  311.                     sb.Append('%');
  312.                     sb.Append(HttpChannelHelper.DecimalToCharacterHexDigit(b >> 4));
  313.                     sb.Append(HttpChannelHelper.DecimalToCharacterHexDigit(b & 15));
  314.                 }
  315.             }
  316.            
  317.             return sb.ToString();
  318.         }
  319.         // EncodeUriAsXLinkHref
  320.        
  321.         static internal bool EscapeInXLinkHref(byte ch)
  322.         {
  323.             // control characters and space
  324.             // non-ascii characters
  325.             if ((ch <= 32) || (ch >= 128) || (ch == (byte)'<') || (ch == (byte)'>') || (ch == (byte)'"')) {
  326.                 return true;
  327.             }
  328.            
  329.             return false;
  330.         }
  331.         // EscapeInXLinkHref
  332.        
  333.         static internal string DecodeUri(string uri)
  334.         {
  335.             byte[] uriBytes = Encoding.UTF8.GetBytes(uri);
  336.            
  337.             int length;
  338.             HttpChannelHelper.DecodeUriInPlace(uriBytes, out length);
  339.            
  340.             string newUri = Encoding.UTF8.GetString(uriBytes, 0, length);
  341.             return newUri;
  342.         }
  343.         // DecodeUri
  344.     }
  345.     // class HttpEncodingHelper
  346.    
  347.    
  348. }
  349. // namespace System.Runtime.Remoting.Channels.Http

Developer Fusion