e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
549 lines
15 KiB
C#
549 lines
15 KiB
C#
//
|
|
// System.Net.WebRequest
|
|
//
|
|
// Authors:
|
|
// Lawrence Pit (loz@cable.a2000.nl)
|
|
// Marek Safar (marek.safar@gmail.com)
|
|
//
|
|
// Copyright 2011 Xamarin Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.Configuration;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Runtime.Serialization;
|
|
using System.Globalization;
|
|
using System.Net.Configuration;
|
|
using System.Net.Security;
|
|
using System.Net.Cache;
|
|
using System.Security.Principal;
|
|
using System.Threading.Tasks;
|
|
using System.Text.RegularExpressions;
|
|
using Mono.Net;
|
|
|
|
#if NET_2_1
|
|
using ConfigurationException = System.ArgumentException;
|
|
|
|
namespace System.Net.Configuration {
|
|
class Dummy {}
|
|
}
|
|
#endif
|
|
|
|
namespace System.Net
|
|
{
|
|
[Serializable]
|
|
public abstract class WebRequest : MarshalByRefObject, ISerializable {
|
|
static HybridDictionary prefixes = new HybridDictionary ();
|
|
static bool isDefaultWebProxySet;
|
|
static IWebProxy defaultWebProxy;
|
|
static RequestCachePolicy defaultCachePolicy;
|
|
|
|
internal const int DefaultTimeout = 100000;
|
|
|
|
static WebRequest ()
|
|
{
|
|
#if MOBILE
|
|
IWebRequestCreate http = new HttpRequestCreator ();
|
|
RegisterPrefix ("http", http);
|
|
RegisterPrefix ("https", http);
|
|
RegisterPrefix ("file", new FileWebRequestCreator ());
|
|
RegisterPrefix ("ftp", new FtpRequestCreator ());
|
|
#else
|
|
#if CONFIGURATION_DEP
|
|
object cfg = ConfigurationManager.GetSection ("system.net/webRequestModules");
|
|
WebRequestModulesSection s = cfg as WebRequestModulesSection;
|
|
if (s != null) {
|
|
foreach (WebRequestModuleElement el in
|
|
s.WebRequestModules)
|
|
AddPrefix (el.Prefix, el.Type);
|
|
return;
|
|
}
|
|
#endif
|
|
ConfigurationSettings.GetConfig ("system.net/webRequestModules");
|
|
#endif
|
|
}
|
|
|
|
protected WebRequest ()
|
|
{
|
|
}
|
|
|
|
protected WebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext)
|
|
{
|
|
}
|
|
|
|
static Exception GetMustImplement ()
|
|
{
|
|
return new NotImplementedException ("This method must be implemented in derived classes");
|
|
}
|
|
|
|
// Properties
|
|
|
|
private AuthenticationLevel authentication_level = AuthenticationLevel.MutualAuthRequested;
|
|
|
|
public AuthenticationLevel AuthenticationLevel
|
|
{
|
|
get {
|
|
return(authentication_level);
|
|
}
|
|
set {
|
|
authentication_level = value;
|
|
}
|
|
}
|
|
|
|
public virtual string ConnectionGroupName {
|
|
get { throw GetMustImplement (); }
|
|
set { throw GetMustImplement (); }
|
|
}
|
|
|
|
public virtual long ContentLength {
|
|
get { throw GetMustImplement (); }
|
|
set { throw GetMustImplement (); }
|
|
}
|
|
|
|
public virtual string ContentType {
|
|
get { throw GetMustImplement (); }
|
|
set { throw GetMustImplement (); }
|
|
}
|
|
|
|
public virtual ICredentials Credentials {
|
|
get { throw GetMustImplement (); }
|
|
set { throw GetMustImplement (); }
|
|
}
|
|
|
|
[MonoTODO ("Implement the caching system. Currently always returns a policy with the NoCacheNoStore level")]
|
|
public virtual RequestCachePolicy CachePolicy
|
|
{
|
|
get { return DefaultCachePolicy; }
|
|
set {
|
|
}
|
|
}
|
|
|
|
public static RequestCachePolicy DefaultCachePolicy {
|
|
get {
|
|
return defaultCachePolicy ?? (defaultCachePolicy = new HttpRequestCachePolicy (HttpRequestCacheLevel.NoCacheNoStore));
|
|
}
|
|
set {
|
|
throw GetMustImplement ();
|
|
}
|
|
}
|
|
|
|
public virtual WebHeaderCollection Headers {
|
|
get { throw GetMustImplement (); }
|
|
set { throw GetMustImplement (); }
|
|
}
|
|
|
|
|
|
public virtual string Method {
|
|
get { throw GetMustImplement (); }
|
|
set { throw GetMustImplement (); }
|
|
}
|
|
|
|
public virtual bool PreAuthenticate {
|
|
get { throw GetMustImplement (); }
|
|
set { throw GetMustImplement (); }
|
|
}
|
|
|
|
public virtual IWebProxy Proxy {
|
|
get { throw GetMustImplement (); }
|
|
set { throw GetMustImplement (); }
|
|
}
|
|
|
|
public virtual Uri RequestUri {
|
|
get { throw GetMustImplement (); }
|
|
}
|
|
|
|
public virtual int Timeout {
|
|
get { throw GetMustImplement (); }
|
|
set { throw GetMustImplement (); }
|
|
}
|
|
|
|
public virtual bool UseDefaultCredentials
|
|
{
|
|
get {
|
|
throw GetMustImplement ();
|
|
}
|
|
set {
|
|
throw GetMustImplement ();
|
|
}
|
|
}
|
|
|
|
public TokenImpersonationLevel ImpersonationLevel { get; set; }
|
|
|
|
// volatile static IWebProxy proxy;
|
|
static readonly object lockobj = new object ();
|
|
|
|
public static IWebProxy DefaultWebProxy {
|
|
get {
|
|
if (!isDefaultWebProxySet) {
|
|
lock (lockobj) {
|
|
if (defaultWebProxy == null)
|
|
defaultWebProxy = GetDefaultWebProxy ();
|
|
}
|
|
}
|
|
return defaultWebProxy;
|
|
}
|
|
set {
|
|
/* MS documentation states that a null value would cause an ArgumentNullException
|
|
* but that's not the way it behaves:
|
|
* https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304724
|
|
*/
|
|
defaultWebProxy = value;
|
|
isDefaultWebProxySet = true;
|
|
}
|
|
}
|
|
|
|
internal static IWebProxy InternalDefaultWebProxy {
|
|
get {
|
|
return DefaultWebProxy;
|
|
}
|
|
}
|
|
|
|
|
|
[MonoTODO("Needs to respect Module, Proxy.AutoDetect, and Proxy.ScriptLocation config settings")]
|
|
static IWebProxy GetDefaultWebProxy ()
|
|
{
|
|
#if CONFIGURATION_DEP
|
|
DefaultProxySection sec = ConfigurationManager.GetSection ("system.net/defaultProxy") as DefaultProxySection;
|
|
WebProxy p;
|
|
|
|
if (sec == null)
|
|
return GetSystemWebProxy ();
|
|
|
|
ProxyElement pe = sec.Proxy;
|
|
|
|
if ((pe.UseSystemDefault != ProxyElement.UseSystemDefaultValues.False) && (pe.ProxyAddress == null)) {
|
|
IWebProxy proxy = GetSystemWebProxy ();
|
|
|
|
if (!(proxy is WebProxy))
|
|
return proxy;
|
|
|
|
p = (WebProxy) proxy;
|
|
} else
|
|
p = new WebProxy ();
|
|
|
|
if (pe.ProxyAddress != null)
|
|
p.Address = pe.ProxyAddress;
|
|
|
|
if (pe.BypassOnLocal != ProxyElement.BypassOnLocalValues.Unspecified)
|
|
p.BypassProxyOnLocal = (pe.BypassOnLocal == ProxyElement.BypassOnLocalValues.True);
|
|
|
|
foreach(BypassElement elem in sec.BypassList)
|
|
p.BypassArrayList.Add(elem.Address);
|
|
|
|
return p;
|
|
#else
|
|
return GetSystemWebProxy ();
|
|
#endif
|
|
}
|
|
|
|
// Methods
|
|
|
|
public virtual void Abort()
|
|
{
|
|
throw GetMustImplement ();
|
|
}
|
|
|
|
public virtual IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state)
|
|
{
|
|
throw GetMustImplement ();
|
|
}
|
|
|
|
public virtual IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
|
|
{
|
|
throw GetMustImplement ();
|
|
}
|
|
|
|
public static WebRequest Create (string requestUriString)
|
|
{
|
|
if (requestUriString == null)
|
|
throw new ArgumentNullException ("requestUriString");
|
|
return Create (new Uri (requestUriString));
|
|
}
|
|
|
|
public static WebRequest Create (Uri requestUri)
|
|
{
|
|
if (requestUri == null)
|
|
throw new ArgumentNullException ("requestUri");
|
|
return GetCreator (requestUri.AbsoluteUri).Create (requestUri);
|
|
}
|
|
|
|
public static WebRequest CreateDefault (Uri requestUri)
|
|
{
|
|
if (requestUri == null)
|
|
throw new ArgumentNullException ("requestUri");
|
|
return GetCreator (requestUri.Scheme).Create (requestUri);
|
|
}
|
|
static HttpWebRequest SharedCreateHttp (Uri uri)
|
|
{
|
|
if (uri.Scheme != "http" && uri.Scheme != "https")
|
|
throw new NotSupportedException ("The uri should start with http or https");
|
|
|
|
return new HttpWebRequest (uri);
|
|
}
|
|
|
|
public static HttpWebRequest CreateHttp (string requestUriString)
|
|
{
|
|
if (requestUriString == null)
|
|
throw new ArgumentNullException ("requestUriString");
|
|
return SharedCreateHttp (new Uri (requestUriString));
|
|
}
|
|
|
|
public static HttpWebRequest CreateHttp (Uri requestUri)
|
|
{
|
|
if (requestUri == null)
|
|
throw new ArgumentNullException ("requestUri");
|
|
return SharedCreateHttp (requestUri);
|
|
}
|
|
public virtual Stream EndGetRequestStream (IAsyncResult asyncResult)
|
|
{
|
|
throw GetMustImplement ();
|
|
}
|
|
|
|
public virtual WebResponse EndGetResponse (IAsyncResult asyncResult)
|
|
{
|
|
throw GetMustImplement ();
|
|
}
|
|
|
|
public virtual Stream GetRequestStream()
|
|
{
|
|
throw GetMustImplement ();
|
|
}
|
|
|
|
public virtual WebResponse GetResponse()
|
|
{
|
|
throw GetMustImplement ();
|
|
}
|
|
|
|
// Takes an ArrayList of fileglob-formatted strings and returns an array of Regex-formatted strings
|
|
private static string[] CreateBypassList (ArrayList al)
|
|
{
|
|
string[] result = al.ToArray (typeof (string)) as string[];
|
|
for (int c = 0; c < result.Length; c++)
|
|
{
|
|
result [c] = "^" +
|
|
Regex.Escape (result [c]).Replace (@"\*", ".*").Replace (@"\?", ".") +
|
|
"$";
|
|
}
|
|
return result;
|
|
}
|
|
|
|
[MonoTODO("Look in other places for proxy config info")]
|
|
public static IWebProxy GetSystemWebProxy ()
|
|
{
|
|
#if MONOTOUCH
|
|
return CFNetwork.GetDefaultProxy ();
|
|
#else
|
|
#if MONODROID
|
|
// Return the system web proxy. This only works for ICS+.
|
|
var androidProxy = AndroidPlatform.GetDefaultProxy ();
|
|
if (androidProxy != null)
|
|
return androidProxy;
|
|
#endif
|
|
#if !NET_2_1
|
|
if (IsWindows ()) {
|
|
int iProxyEnable = (int)Microsoft.Win32.Registry.GetValue ("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", "ProxyEnable", 0);
|
|
|
|
if (iProxyEnable > 0) {
|
|
string strHttpProxy = "";
|
|
bool bBypassOnLocal = false;
|
|
ArrayList al = new ArrayList ();
|
|
|
|
string strProxyServer = (string)Microsoft.Win32.Registry.GetValue ("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", "ProxyServer", null);
|
|
string strProxyOverrride = (string)Microsoft.Win32.Registry.GetValue ("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", "ProxyOverride", null);
|
|
|
|
if (strProxyServer.Contains ("=")) {
|
|
foreach (string strEntry in strProxyServer.Split (new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
|
if (strEntry.StartsWith ("http=")) {
|
|
strHttpProxy = strEntry.Substring (5);
|
|
break;
|
|
}
|
|
} else strHttpProxy = strProxyServer;
|
|
|
|
if (strProxyOverrride != null) {
|
|
string[] bypassList = strProxyOverrride.Split (new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
foreach (string str in bypassList) {
|
|
if (str != "<local>")
|
|
al.Add (str);
|
|
else
|
|
bBypassOnLocal = true;
|
|
}
|
|
}
|
|
|
|
return new WebProxy (strHttpProxy, bBypassOnLocal, CreateBypassList (al));
|
|
}
|
|
} else {
|
|
#endif
|
|
if (Platform.IsMacOS)
|
|
return CFNetwork.GetDefaultProxy ();
|
|
|
|
string address = Environment.GetEnvironmentVariable ("http_proxy");
|
|
|
|
if (address == null)
|
|
address = Environment.GetEnvironmentVariable ("HTTP_PROXY");
|
|
|
|
if (address != null) {
|
|
try {
|
|
if (!address.StartsWith ("http://"))
|
|
address = "http://" + address;
|
|
|
|
Uri uri = new Uri (address);
|
|
IPAddress ip;
|
|
|
|
if (IPAddress.TryParse (uri.Host, out ip)) {
|
|
if (IPAddress.Any.Equals (ip)) {
|
|
UriBuilder builder = new UriBuilder (uri);
|
|
builder.Host = "127.0.0.1";
|
|
uri = builder.Uri;
|
|
} else if (IPAddress.IPv6Any.Equals (ip)) {
|
|
UriBuilder builder = new UriBuilder (uri);
|
|
builder.Host = "[::1]";
|
|
uri = builder.Uri;
|
|
}
|
|
}
|
|
|
|
bool bBypassOnLocal = false;
|
|
ArrayList al = new ArrayList ();
|
|
string bypass = Environment.GetEnvironmentVariable ("no_proxy");
|
|
|
|
if (bypass == null)
|
|
bypass = Environment.GetEnvironmentVariable ("NO_PROXY");
|
|
|
|
if (bypass != null) {
|
|
string[] bypassList = bypass.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
foreach (string str in bypassList) {
|
|
if (str != "*.local")
|
|
al.Add (str);
|
|
else
|
|
bBypassOnLocal = true;
|
|
}
|
|
}
|
|
|
|
return new WebProxy (uri, bBypassOnLocal, CreateBypassList (al));
|
|
} catch (UriFormatException) {
|
|
}
|
|
}
|
|
#if !NET_2_1
|
|
}
|
|
#endif
|
|
|
|
return new WebProxy ();
|
|
#endif // MONOTOUCH
|
|
}
|
|
|
|
void ISerializable.GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)
|
|
{
|
|
GetObjectData(serializationInfo, streamingContext);
|
|
}
|
|
|
|
protected virtual void GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)
|
|
{
|
|
}
|
|
|
|
public static bool RegisterPrefix (string prefix, IWebRequestCreate creator)
|
|
{
|
|
if (prefix == null)
|
|
throw new ArgumentNullException ("prefix");
|
|
if (creator == null)
|
|
throw new ArgumentNullException ("creator");
|
|
|
|
lock (prefixes.SyncRoot) {
|
|
string lowerCasePrefix = prefix.ToLower (CultureInfo.InvariantCulture);
|
|
if (prefixes.Contains (lowerCasePrefix))
|
|
return false;
|
|
prefixes.Add (lowerCasePrefix, creator);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static IWebRequestCreate GetCreator (string prefix)
|
|
{
|
|
int longestPrefix = -1;
|
|
IWebRequestCreate creator = null;
|
|
|
|
prefix = prefix.ToLower (CultureInfo.InvariantCulture);
|
|
|
|
IDictionaryEnumerator e = prefixes.GetEnumerator ();
|
|
while (e.MoveNext ()) {
|
|
string key = e.Key as string;
|
|
|
|
if (key.Length <= longestPrefix)
|
|
continue;
|
|
|
|
if (!prefix.StartsWith (key))
|
|
continue;
|
|
|
|
longestPrefix = key.Length;
|
|
creator = (IWebRequestCreate) e.Value;
|
|
}
|
|
|
|
if (creator == null)
|
|
throw new NotSupportedException (prefix);
|
|
|
|
return creator;
|
|
}
|
|
|
|
internal static bool IsWindows ()
|
|
{
|
|
return (int) Environment.OSVersion.Platform < 4;
|
|
}
|
|
|
|
internal static void ClearPrefixes ()
|
|
{
|
|
prefixes.Clear ();
|
|
}
|
|
|
|
internal static void RemovePrefix (string prefix)
|
|
{
|
|
prefixes.Remove (prefix);
|
|
}
|
|
|
|
internal static void AddPrefix (string prefix, string typeName)
|
|
{
|
|
Type type = Type.GetType (typeName);
|
|
if (type == null)
|
|
throw new ConfigurationException (String.Format ("Type {0} not found", typeName));
|
|
AddPrefix (prefix, type);
|
|
}
|
|
|
|
internal static void AddPrefix (string prefix, Type type)
|
|
{
|
|
object o = Activator.CreateInstance (type, true);
|
|
prefixes [prefix] = o;
|
|
}
|
|
|
|
public virtual Task<Stream> GetRequestStreamAsync ()
|
|
{
|
|
return Task<Stream>.Factory.FromAsync (BeginGetRequestStream, EndGetRequestStream, null);
|
|
}
|
|
|
|
public virtual Task<WebResponse> GetResponseAsync ()
|
|
{
|
|
return Task<WebResponse>.Factory.FromAsync (BeginGetResponse, EndGetResponse, null);
|
|
}
|
|
}
|
|
}
|