File: channels\http\httpchannelhelper.cs
Project: ndp\clr\src\managedlibraries\remoting\System.Runtime.Remoting.csproj (System.Runtime.Remoting)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
//==========================================================================
//  File:       HttpChannelHelper.cs
//
//  Summary:    Implements helper methods for http client and server channels.
//
//==========================================================================
 
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Globalization;
 
namespace System.Runtime.Remoting.Channels.Http
{
 
    
    internal static class HttpChannelHelper
    {
        private const String _http = "http://";
#if !FEATURE_PAL
        private const String _https = "https://";
#endif
 
        private static char[] s_semicolonSeparator = new char[]{';'};
 
    
        // Determine if the url starts with "http://"
#if !FEATURE_PAL
        // or "https://"
#endif
        internal static int StartsWithHttp(String url)
        {
            int urlLength = url.Length;
            
            if (StringHelper.StartsWithAsciiIgnoreCasePrefixLower(url, _http))
                return _http.Length;
#if !FEATURE_PAL
            else
            if (StringHelper.StartsWithAsciiIgnoreCasePrefixLower(url, _https))
                return _https.Length;
#endif
            else
                return -1;
        } // StartsWithHttp
 
 
        // Used by http channels to implement IChannel::Parse.
        // It returns the channel uri and places object uri into out parameter.
        internal static String ParseURL(String url, out String objectURI)
        {            
            // Set the out parameters
            objectURI = null;
 
            int separator = StartsWithHttp(url);
            if (separator == -1)
                return null;
 
            // find next slash (after end of scheme)
            separator = url.IndexOf('/', separator);
            if (-1 == separator)
            {
                return url;  // means that the url is just "tcp://foo:90" or something like that
            }
 
            // Extract the channel URI which is the prefix
            String channelURI = url.Substring(0, separator);
 
            // Extract the object URI which is the suffix (leave the slash)
            objectURI = url.Substring(separator);
 
            InternalRemotingServices.RemotingTrace("HTTPChannel.Parse URI in: " + url);
            InternalRemotingServices.RemotingTrace("HTTPChannel.Parse channelURI: " + channelURI);
            InternalRemotingServices.RemotingTrace("HTTPChannel.Parse objectURI: " + objectURI);
 
            return channelURI;
        } // ParseURL
 
 
        internal static String GetObjectUriFromRequestUri(String uri)
        {
            // We assume uri may be in one of the following forms
            //   http://myhost.com/myobject.rem
#if !FEATURE_PAL
            //   https://myhost.com/myobject.rem
#endif
            //   /myobject.rem
            //   /myobject
            //   myobject.rem
            // In all cases, myobject is considered to be the object URI (.rem might be absent)
 
            int start, end; // range of characters to use
            int index;
            start = 0;
            end = uri.Length;
 
            // first see if uri starts with http://
#if !FEATURE_PAL
            // or https://
#endif
            // and remove up to next slash if it does
            start = StartsWithHttp(uri);
            if (start != -1)
            {
                // remove domain name as well
                index = uri.IndexOf('/', start);
                if (index != -1)
                    start = index + 1;
                else
                    start = end; // uri will end up being ""
            }
            else
            {
                // remove "/" if this is an absolute path
                start = 0; 
                if (uri[start] == '/')
                   start++;
            }
 
            // remove query string if present ('?' and everything past it)
            index = uri.IndexOf('?');
            if (index != -1)
                end = index;
 
            if (start < end)
                return CoreChannel.RemoveApplicationNameFromUri(uri.Substring(start, end - start));
            else
                return "";
        } // GetObjectURIFromRequestURI
 
        
        internal static void ParseContentType(String contentType,
                                              out String value,
                                              out String charset)
        {
            charset = null;
        
            if (contentType == null)
            {
                value = null;
                return;
            }
        
            String[] parts = contentType.Split(s_semicolonSeparator);
 
            // the actual content-type value is always first
            value = parts[0];
 
            // examine name value pairs and look for charset
            if (parts.Length > 0)
            {
                foreach (String part in parts)
                {
                    int index = part.IndexOf('=');
                    if (index != -1)
                    {
                        String key = part.Substring(0, index).Trim();
                        if (String.Compare(key, "charset", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            if ((index + 1) < part.Length)
                            {
                                // we had to make sure there is something after the 
                                //   equals sign.
                                charset = part.Substring(index + 1);
                            }
                            else
                            {
                                charset = null;
                            }
                            return;
                        }
                    }
                } // foreach
            }
        } // ParseContentType
 
        internal static String ReplaceChannelUriWithThisString(String url, String channelUri) 
        {
            // NOTE: channelUri is assumed to be scheme://machinename:port
            //   with NO trailing slash.
 
            String oldChannelUri;
            String objUri;
            oldChannelUri = HttpChannelHelper.ParseURL(url, out objUri);
            InternalRemotingServices.RemotingAssert(oldChannelUri != null, "http url expected.");
            InternalRemotingServices.RemotingAssert(objUri != null, "non-null objUri expected.");
            
            return channelUri + objUri;
        } // ReplaceChannelUriWithThisString
 
 
        // returns url with the machine name replaced with the ip address.
        internal static String ReplaceMachineNameWithThisString(String url, String newMachineName)
        {
            String objectUri;
            String channelUri = ParseURL(url, out objectUri);    
 
            // find bounds of machine name
            int index = StartsWithHttp(url);
            if (index == -1)
                return url;
   
            int colonIndex = channelUri.IndexOf(':', index);
            if (colonIndex == -1)
                colonIndex = channelUri.Length;
 
            // machine name is between index and up to but not including colonIndex, 
            //   so we will replace those characters with the ip address.
            String newUrl = url.Substring(0, index) + newMachineName + url.Substring(colonIndex);
            return newUrl;
        } // ReplaceMachineNameWithIpAddress
 
 
        // Decodes a uri while it is in byte array form
        internal static void DecodeUriInPlace(byte[] uriBytes, out int length)
        {
            int percentsFound = 0;
            int count = uriBytes.Length;
            length = count;
            int co = 0;
            while (co < count)
            {
                if (uriBytes[co] == (byte)'%')
                {
                    // determine location to write to (we skip 2 character for each percent)
                    int writePos = co - (percentsFound * 2);
 
                    // decode in place by collapsing bytes "%XY" (actual byte is 16*Dec(X) + Dec(Y))
                    uriBytes[writePos] = (byte)
                        (16 * CharacterHexDigitToDecimal(uriBytes[co + 1]) +
                         CharacterHexDigitToDecimal(uriBytes[co + 2]));
 
                    percentsFound++;      
                    length -= 2; // we eliminated 2 characters from the length
                    co += 3;
                }
                else
                {
                    if (percentsFound != 0)
                    {
                        // we have to copy characters back into place since we will skip some characters
 
                        // determine location to write to (we skip 2 character for each percent)
                        int writePos = co - (percentsFound * 2);
 
                        // copy character back into place
                        uriBytes[writePos] = uriBytes[co];
                    }
 
                    co++;
                }
            }
            
        } // DecodeUri
 
 
 
        // reading helper functions
        internal static int CharacterHexDigitToDecimal(byte b)
        {
            switch ((char)b)
            {
            case 'F':
            case 'f': return 15;
            case 'E':
            case 'e': return 14;
            case 'D':
            case 'd': return 13;
            case 'C':
            case 'c': return 12;
            case 'B':
            case 'b': return 11;
            case 'A':
            case 'a': return 10;
            default: return b - (byte)'0';
            }
        } // CharacterHexDigitToDecimal
 
 
        internal static char DecimalToCharacterHexDigit(int i)
        {
            switch (i)
            {
            case 15: return 'F';
            case 14: return 'E';
            case 13: return 'D';
            case 12: return 'C';
            case 11: return 'B';
            case 10: return 'A';
            default: return (char)(i + (byte)'0');
            }
 
        } // DecimalToCharacterHexDigit
                
 
    
    } // class HttpChannelHelper
 
 
 
    internal static class HttpEncodingHelper
    {
        internal static String EncodeUriAsXLinkHref(String uri)
        {
            if (uri == null)
                return null;
        
            // uses modified encoding rules from xlink href spec for encoding uri's.
            // http://www.w3.org/TR/2000/PR-xlink-20001220/#link-locators
 
            byte[] uriBytes = Encoding.UTF8.GetBytes(uri);
 
            StringBuilder sb = new StringBuilder(uri.Length);
 
            // iterate over uri bytes and build up an encoded string.
            foreach (byte b in uriBytes)
            {
                if (!EscapeInXLinkHref(b))
                {
                    sb.Append((char)b);
                }
                else
                {
                    // the character needs to be encoded as %HH
                    sb.Append('%');
                    sb.Append(HttpChannelHelper.DecimalToCharacterHexDigit(b >> 4));
                    sb.Append(HttpChannelHelper.DecimalToCharacterHexDigit(b & 0xF));
                }
            }
 
            return sb.ToString();            
        } // EncodeUriAsXLinkHref
 
 
        internal static bool EscapeInXLinkHref(byte ch)
        {
            if ((ch <= 32) || // control characters and space
                (ch >= 128) ||  // non-ascii characters
                (ch == (byte)'<') ||
                (ch == (byte)'>') ||
                (ch == (byte)'"'))        
            {
                return true;
            }
                   
            return false;
        } // EscapeInXLinkHref
 
 
        internal static String DecodeUri(String uri)
        {
            byte[] uriBytes = Encoding.UTF8.GetBytes(uri);
 
            int length;
            HttpChannelHelper.DecodeUriInPlace(uriBytes, out length);
 
            String newUri = Encoding.UTF8.GetString(uriBytes, 0, length);
            return newUri;
        } // DecodeUri
        
    } // class HttpEncodingHelper
 
 
 
} // namespace System.Runtime.Remoting.Channels.Http