File: System\Net\WebRequest.cs
Web Access
Project: src\src\libraries\System.Net.Requests\src\System.Net.Requests.csproj (System.Net.Requests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net.Cache;
using System.Net.Http;
using System.Net.Security;
using System.Runtime.Serialization;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
namespace System.Net
{
    // NOTE: While this class is not explicitly marked as obsolete,
    // it effectively is by virtue of WebRequest.Create being obsolete.
    public abstract class WebRequest : MarshalByRefObject, ISerializable
    {
        internal sealed class WebRequestPrefixElement
        {
            public readonly string Prefix;
            public readonly IWebRequestCreate Creator;
 
            public WebRequestPrefixElement(string prefix, IWebRequestCreate creator)
            {
                Prefix = prefix;
                Creator = creator;
            }
        }
 
        private static List<WebRequestPrefixElement>? s_prefixList;
        private static object s_internalSyncObject = new object();
 
        internal const int DefaultTimeoutMilliseconds = 100 * 1000;
 
        [Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        protected WebRequest() { }
 
        [Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected WebRequest(SerializationInfo serializationInfo, StreamingContext streamingContext)
        {
            throw new PlatformNotSupportedException();
        }
 
        [Obsolete("Serialization has been deprecated for WebRequest.")]
        void ISerializable.GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
        {
            throw new PlatformNotSupportedException();
        }
 
        [Obsolete("Serialization has been deprecated for WebRequest.")]
        protected virtual void GetObjectData(SerializationInfo serializationInfo, StreamingContext streamingContext)
        {
            throw new PlatformNotSupportedException();
        }
 
        // Create a WebRequest.
        //
        // This is the main creation routine. We take a Uri object, look
        // up the Uri in the prefix match table, and invoke the appropriate
        // handler to create the object. We also have a parameter that
        // tells us whether or not to use the whole Uri or just the
        // scheme portion of it.
        //
        // Input:
        //     requestUri - Uri object for request.
        //     useUriBase - True if we're only to look at the scheme portion of the Uri.
        //
        // Returns:
        //     Newly created WebRequest.
        private static WebRequest Create(Uri requestUri, bool useUriBase)
        {
            string LookupUri;
            WebRequestPrefixElement? Current = null;
            bool Found = false;
 
            if (!useUriBase)
            {
                LookupUri = requestUri.AbsoluteUri;
            }
            else
            {
                // schemes are registered as <schemeName>":", so add the separator
                // to the string returned from the Uri object
                LookupUri = requestUri.Scheme + ':';
            }
 
            int LookupLength = LookupUri.Length;
 
            // Copy the prefix list so that if it is updated it will
            // not affect us on this thread.
 
            List<WebRequestPrefixElement> prefixList = PrefixList;
 
            // Look for the longest matching prefix.
 
            // Walk down the list of prefixes. The prefixes are kept longest
            // first. When we find a prefix that is shorter or the same size
            // as this Uri, we'll do a compare to see if they match. If they
            // do we'll break out of the loop and call the creator.
 
            for (int i = 0; i < prefixList.Count; i++)
            {
                Current = prefixList[i];
 
                // See if this prefix is short enough.
 
                if (LookupLength >= Current.Prefix.Length)
                {
                    // It is. See if these match.
                    if (string.Compare(Current.Prefix,
                                       0,
                                       LookupUri,
                                       0,
                                       Current.Prefix.Length,
                                       StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        // These match. Remember that we found it and break
                        // out.
                        Found = true;
                        break;
                    }
                }
            }
 
            if (Found)
            {
                // We found a match, so just call the creator and return what it does.
                WebRequest webRequest = Current!.Creator.Create(requestUri);
                return webRequest;
            }
 
            // Otherwise no match, throw an exception.
            throw new NotSupportedException(SR.net_unknown_prefix);
        }
 
        // Create - Create a WebRequest.
        //
        // An overloaded utility version of the real Create that takes a
        // string instead of an Uri object.
        //
        // Input:
        //     RequestString       - Uri string to create.
        //
        // Returns:
        //     Newly created WebRequest.
        [Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public static WebRequest Create(string requestUriString)
        {
            ArgumentNullException.ThrowIfNull(requestUriString);
 
            return Create(new Uri(requestUriString), false);
        }
 
        // Create - Create a WebRequest.
        //
        // Another overloaded version of the Create function that doesn't
        // take the UseUriBase parameter.
        //
        // Input:
        //     requestUri - Uri object for request.
        //
        // Returns:
        //     Newly created WebRequest.
        [Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public static WebRequest Create(Uri requestUri)
        {
            ArgumentNullException.ThrowIfNull(requestUri);
 
            return Create(requestUri, false);
        }
 
        // CreateDefault - Create a default WebRequest.
        //
        // This is the creation routine that creates a default WebRequest.
        // We take a Uri object and pass it to the base create routine,
        // setting the useUriBase parameter to true.
        //
        // Input:
        //     RequestUri - Uri object for request.
        //
        // Returns:
        //     Newly created WebRequest.
        [Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public static WebRequest CreateDefault(Uri requestUri)
        {
            ArgumentNullException.ThrowIfNull(requestUri);
 
            return Create(requestUri, true);
        }
 
        [Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public static HttpWebRequest CreateHttp(string requestUriString)
        {
            ArgumentNullException.ThrowIfNull(requestUriString);
 
            return CreateHttp(new Uri(requestUriString));
        }
 
        [Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
        public static HttpWebRequest CreateHttp(Uri requestUri)
        {
            ArgumentNullException.ThrowIfNull(requestUri);
 
            if ((requestUri.Scheme != "http") && (requestUri.Scheme != "https"))
            {
                throw new NotSupportedException(SR.net_unknown_prefix);
            }
            return (HttpWebRequest)CreateDefault(requestUri);
        }
 
        // RegisterPrefix - Register an Uri prefix for creating WebRequests.
        //
        // This function registers a prefix for creating WebRequests. When an
        // user wants to create a WebRequest, we scan a table looking for a
        // longest prefix match for the Uri they're passing. We then invoke
        // the sub creator for that prefix. This function puts entries in
        // that table.
        //
        // We don't allow duplicate entries, so if there is a dup this call
        // will fail.
        //
        // Input:
        //     Prefix  - Represents Uri prefix being registered.
        //     Creator - Interface for sub creator.
        //
        // Returns:
        //     True if the registration worked, false otherwise.
        public static bool RegisterPrefix(string prefix, IWebRequestCreate creator)
        {
            ArgumentNullException.ThrowIfNull(prefix);
            ArgumentNullException.ThrowIfNull(creator);
 
            bool Error = false;
            int i;
            WebRequestPrefixElement Current;
 
            // Lock this object, then walk down PrefixList looking for a place to
            // to insert this prefix.
            lock (s_internalSyncObject)
            {
                // Clone the object and update the clone, thus
                // allowing other threads to still read from the original.
                List<WebRequestPrefixElement> prefixList = new List<WebRequestPrefixElement>(PrefixList);
 
                // As AbsoluteUri is used later for Create, account for formating changes
                // like Unicode escaping, default ports, etc.
                if (Uri.TryCreate(prefix, UriKind.Absolute, out Uri? tempUri))
                {
                    string cookedUri = tempUri.AbsoluteUri;
 
                    // Special case for when a partial host matching is requested, drop the added trailing slash
                    // IE: http://host could match host or host.domain
                    if (!prefix.EndsWith('/')
                        && tempUri.GetComponents(UriComponents.PathAndQuery | UriComponents.Fragment, UriFormat.UriEscaped)
                            .Equals("/"))
                    {
                        cookedUri = cookedUri.Substring(0, cookedUri.Length - 1);
                    }
 
                    prefix = cookedUri;
                }
 
                i = 0;
 
                // The prefix list is sorted with longest entries at the front. We
                // walk down the list until we find a prefix shorter than this
                // one, then we insert in front of it. Along the way we check
                // equal length prefixes to make sure this isn't a dupe.
                while (i < prefixList.Count)
                {
                    Current = prefixList[i];
 
                    // See if the new one is longer than the one we're looking at.
                    if (prefix.Length > Current.Prefix.Length)
                    {
                        // It is. Break out of the loop here.
                        break;
                    }
 
                    // If these are of equal length, compare them.
                    if (prefix.Length == Current.Prefix.Length)
                    {
                        // They're the same length.
                        if (string.Equals(Current.Prefix, prefix, StringComparison.OrdinalIgnoreCase))
                        {
                            // ...and the strings are identical. This is an error.
                            Error = true;
                            break;
                        }
                    }
                    i++;
                }
 
                // When we get here either i contains the index to insert at or
                // we've had an error, in which case Error is true.
                if (!Error)
                {
                    // No error, so insert.
                    prefixList.Insert(i, new WebRequestPrefixElement(prefix, creator));
 
                    // Assign the clone to the static object. Other threads using it
                    // will have copied the original object already.
                    PrefixList = prefixList;
                }
            }
            return !Error;
        }
 
        internal sealed class HttpRequestCreator : IWebRequestCreate
        {
            // Create - Create an HttpWebRequest.
            //
            // This is our method to create an HttpWebRequest. We register
            // for HTTP and HTTPS Uris, and this method is called when a request
            // needs to be created for one of those.
            //
            //
            // Input:
            //     uri - Uri for request being created.
            //
            // Returns:
            //     The newly created HttpWebRequest.
            public WebRequest Create(Uri Uri)
            {
                return new HttpWebRequest(Uri);
            }
        }
 
        // PrefixList - Returns And Initialize our prefix list.
        //
        //
        // This is the method that initializes the prefix list. We create
        // an List for the PrefixList, then each of the request creators,
        // and then we register them with the associated prefixes.
        //
        // Returns:
        //     true
        internal static List<WebRequestPrefixElement> PrefixList
        {
            get
            {
                // GetConfig() might use us, so we have a circular dependency issue
                // that causes us to nest here. We grab the lock only if we haven't
                // initialized.
                return LazyInitializer.EnsureInitialized(ref s_prefixList, ref s_internalSyncObject, () =>
                {
                    var httpRequestCreator = new HttpRequestCreator();
                    var ftpRequestCreator = new FtpWebRequestCreator();
                    var fileRequestCreator = new FileWebRequestCreator();
 
                    const int Count = 4;
                    var prefixList = new List<WebRequestPrefixElement>(Count)
                    {
                        new WebRequestPrefixElement("http:", httpRequestCreator),
                        new WebRequestPrefixElement("https:", httpRequestCreator),
                        new WebRequestPrefixElement("ftp:", ftpRequestCreator),
                        new WebRequestPrefixElement("file:", fileRequestCreator),
                    };
                    Debug.Assert(prefixList.Count == Count, $"Expected {Count}, got {prefixList.Count}");
 
                    return prefixList;
                });
            }
            set
            {
                Volatile.Write(ref s_prefixList, value);
            }
        }
 
        public static RequestCachePolicy? DefaultCachePolicy { get; set; } = new RequestCachePolicy(RequestCacheLevel.BypassCache);
 
        public virtual RequestCachePolicy? CachePolicy { get; set; }
 
        public AuthenticationLevel AuthenticationLevel { get; set; } = AuthenticationLevel.MutualAuthRequested;
 
        public TokenImpersonationLevel ImpersonationLevel { get; set; } = TokenImpersonationLevel.None;
 
        public virtual string? ConnectionGroupName
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        public virtual string Method
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        public virtual Uri RequestUri
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        public virtual WebHeaderCollection Headers
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        public virtual long ContentLength
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        public virtual string? ContentType
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        [DisallowNull]
        public virtual ICredentials? Credentials
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        public virtual int Timeout
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        public virtual bool UseDefaultCredentials
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        public virtual Stream GetRequestStream()
        {
            throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
        }
 
        public virtual WebResponse GetResponse()
        {
            throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
        }
 
        public virtual IAsyncResult BeginGetResponse(AsyncCallback? callback, object? state)
        {
            throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
        }
 
        public virtual WebResponse EndGetResponse(IAsyncResult asyncResult)
        {
            throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
        }
 
        public virtual IAsyncResult BeginGetRequestStream(AsyncCallback? callback, object? state)
        {
            throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
        }
 
        public virtual Stream EndGetRequestStream(IAsyncResult asyncResult)
        {
            throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
        }
 
        public virtual async Task<Stream> GetRequestStreamAsync()
        {
            // Offload to a different thread to avoid blocking the caller during request submission.
            await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
 
            return await Task<Stream>.Factory.FromAsync(
                (callback, state) => ((WebRequest)state!).BeginGetRequestStream(callback, state),
                iar => ((WebRequest)iar.AsyncState!).EndGetRequestStream(iar),
                this).ConfigureAwait(false);
        }
 
        public virtual async Task<WebResponse> GetResponseAsync()
        {
            // Offload to a different thread to avoid blocking the caller during request submission.
            await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
 
            return await Task<WebResponse>.Factory.FromAsync(
                (callback, state) => ((WebRequest)state!).BeginGetResponse(callback, state),
                iar => ((WebRequest)iar.AsyncState!).EndGetResponse(iar),
                this).ConfigureAwait(false);
        }
 
        public virtual void Abort()
        {
            throw NotImplemented.ByDesignWithMessage(SR.net_MethodNotImplementedException);
        }
 
        private static IWebProxy? s_DefaultWebProxy;
        private static bool s_DefaultWebProxyInitialized;
 
        public static IWebProxy GetSystemWebProxy() => HttpClient.DefaultProxy;
 
        public static IWebProxy? DefaultWebProxy
        {
            get => LazyInitializer.EnsureInitialized<IWebProxy>(ref s_DefaultWebProxy, ref s_DefaultWebProxyInitialized, ref s_internalSyncObject, GetSystemWebProxy);
            set
            {
                lock (s_internalSyncObject)
                {
                    s_DefaultWebProxy = value;
                    s_DefaultWebProxyInitialized = true;
                }
            }
        }
 
        public virtual bool PreAuthenticate
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
 
        public virtual IWebProxy? Proxy
        {
            get
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
            set
            {
                throw NotImplemented.ByDesignWithMessage(SR.net_PropertyNotImplementedException);
            }
        }
    }
}