Merge pull request #2807 from akoeplinger/gchandle
[mono.git] / mcs / class / System / System.Net / WebRequest.cs
1 //
2 // System.Net.WebRequest
3 //
4 // Authors:
5 //  Lawrence Pit (loz@cable.a2000.nl)
6 //      Marek Safar (marek.safar@gmail.com)
7 //
8 // Copyright 2011 Xamarin Inc.
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.Collections;
32 using System.Collections.Specialized;
33 using System.Configuration;
34 using System.IO;
35 using System.Reflection;
36 using System.Runtime.Serialization;
37 using System.Globalization;
38 using System.Net.Configuration;
39 using System.Net.Security;
40 using System.Net.Cache;
41 using System.Security.Principal;
42 using System.Threading.Tasks;
43 using System.Text.RegularExpressions;
44 using Mono.Net;
45
46 #if NET_2_1
47 using ConfigurationException = System.ArgumentException;
48
49 namespace System.Net.Configuration {
50         class Dummy {}
51 }
52 #endif
53
54 namespace System.Net 
55 {
56         [Serializable]
57         public abstract class WebRequest : MarshalByRefObject, ISerializable {
58                 static HybridDictionary prefixes = new HybridDictionary ();
59                 static bool isDefaultWebProxySet;
60                 static IWebProxy defaultWebProxy;
61                 static RequestCachePolicy defaultCachePolicy;
62
63                 static WebRequest ()
64                 {
65 #if MOBILE
66                         IWebRequestCreate http = new HttpRequestCreator ();
67                         RegisterPrefix ("http", http);
68                         RegisterPrefix ("https", http);
69                         RegisterPrefix ("file", new FileWebRequestCreator ());
70                         RegisterPrefix ("ftp", new FtpRequestCreator ());
71 #else
72         #if CONFIGURATION_DEP
73                         object cfg = ConfigurationManager.GetSection ("system.net/webRequestModules");
74                         WebRequestModulesSection s = cfg as WebRequestModulesSection;
75                         if (s != null) {
76                                 foreach (WebRequestModuleElement el in
77                                          s.WebRequestModules)
78                                         AddPrefix (el.Prefix, el.Type);
79                                 return;
80                         }
81         #endif
82                         ConfigurationSettings.GetConfig ("system.net/webRequestModules");
83 #endif
84                 }
85                 
86                 protected WebRequest () 
87                 {
88                 }
89                 
90                 protected WebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext) 
91                 {
92                 }
93
94                 static Exception GetMustImplement ()
95                 {
96                         return new NotImplementedException ("This method must be implemented in derived classes");
97                 }
98                 
99                 // Properties
100
101                 private AuthenticationLevel authentication_level = AuthenticationLevel.MutualAuthRequested;
102                 
103                 public AuthenticationLevel AuthenticationLevel
104                 {
105                         get {
106                                 return(authentication_level);
107                         }
108                         set {
109                                 authentication_level = value;
110                         }
111                 }
112                 
113                 public virtual string ConnectionGroupName {
114                         get { throw GetMustImplement (); }
115                         set { throw GetMustImplement (); }
116                 }
117                 
118                 public virtual long ContentLength { 
119                         get { throw GetMustImplement (); }
120                         set { throw GetMustImplement (); }
121                 }
122                 
123                 public virtual string ContentType { 
124                         get { throw GetMustImplement (); }
125                         set { throw GetMustImplement (); }
126                 }
127                 
128                 public virtual ICredentials Credentials { 
129                         get { throw GetMustImplement (); }
130                         set { throw GetMustImplement (); }
131                 }
132
133                 [MonoTODO ("Implement the caching system. Currently always returns a policy with the NoCacheNoStore level")]
134                 public virtual RequestCachePolicy CachePolicy
135                 {
136                         get { return DefaultCachePolicy; }
137                         set {
138                         }
139                 }
140                 
141                 public static RequestCachePolicy DefaultCachePolicy {
142                         get {
143                                 return defaultCachePolicy ?? (defaultCachePolicy = new HttpRequestCachePolicy (HttpRequestCacheLevel.NoCacheNoStore));
144                         }
145                         set {
146                                 throw GetMustImplement ();
147                         }
148                 }
149                 
150                 public virtual WebHeaderCollection Headers { 
151                         get { throw GetMustImplement (); }
152                         set { throw GetMustImplement (); }
153                 }
154                 
155
156                 public virtual string Method { 
157                         get { throw GetMustImplement (); }
158                         set { throw GetMustImplement (); }
159                 }
160                 
161                 public virtual bool PreAuthenticate { 
162                         get { throw GetMustImplement (); }
163                         set { throw GetMustImplement (); }
164                 }
165                 
166                 public virtual IWebProxy Proxy { 
167                         get { throw GetMustImplement (); }
168                         set { throw GetMustImplement (); }
169                 }
170                 
171                 public virtual Uri RequestUri { 
172                         get { throw GetMustImplement (); }
173                 }
174                 
175                 public virtual int Timeout { 
176                         get { throw GetMustImplement (); }
177                         set { throw GetMustImplement (); }
178                 }
179                 
180                 public virtual bool UseDefaultCredentials
181                 {
182                         get {
183                                 throw GetMustImplement ();
184                         }
185                         set {
186                                 throw GetMustImplement ();
187                         }
188                 }
189
190                 public TokenImpersonationLevel ImpersonationLevel { get; set; }
191
192 //              volatile static IWebProxy proxy;
193                 static readonly object lockobj = new object ();
194                 
195                 public static IWebProxy DefaultWebProxy {
196                         get {
197                                 if (!isDefaultWebProxySet) {
198                                         lock (lockobj) {
199                                                 if (defaultWebProxy == null)
200                                                         defaultWebProxy = GetDefaultWebProxy ();
201                                         }
202                                 }
203                                 return defaultWebProxy;
204                         }
205                         set {
206                                 /* MS documentation states that a null value would cause an ArgumentNullException
207                                  * but that's not the way it behaves:
208                                  * https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=304724
209                                  */
210                                 defaultWebProxy = value;
211                                 isDefaultWebProxySet = true;
212                         }
213                 }
214
215                 internal static IWebProxy InternalDefaultWebProxy {
216                         get {
217                                 return DefaultWebProxy;
218                         }
219                 }
220
221                 
222                 [MonoTODO("Needs to respect Module, Proxy.AutoDetect, and Proxy.ScriptLocation config settings")]
223                 static IWebProxy GetDefaultWebProxy ()
224                 {
225 #if CONFIGURATION_DEP
226                         DefaultProxySection sec = ConfigurationManager.GetSection ("system.net/defaultProxy") as DefaultProxySection;
227                         WebProxy p;
228                         
229                         if (sec == null)
230                                 return GetSystemWebProxy ();
231                         
232                         ProxyElement pe = sec.Proxy;
233                         
234                         if ((pe.UseSystemDefault != ProxyElement.UseSystemDefaultValues.False) && (pe.ProxyAddress == null)) {
235                                 IWebProxy proxy = GetSystemWebProxy ();
236                                 
237                                 if (!(proxy is WebProxy))
238                                         return proxy;
239                                 
240                                 p = (WebProxy) proxy;
241                         } else
242                                 p = new WebProxy ();
243                         
244                         if (pe.ProxyAddress != null)
245                                 p.Address = pe.ProxyAddress;
246                         
247                         if (pe.BypassOnLocal != ProxyElement.BypassOnLocalValues.Unspecified)
248                                 p.BypassProxyOnLocal = (pe.BypassOnLocal == ProxyElement.BypassOnLocalValues.True);
249                                 
250                         foreach(BypassElement elem in sec.BypassList)
251                                 p.BypassArrayList.Add(elem.Address);
252                         
253                         return p;
254 #else
255                         return GetSystemWebProxy ();
256 #endif
257                 }
258
259                 // Methods
260                 
261                 public virtual void Abort()
262                 {
263                         throw GetMustImplement ();
264                 }
265                 
266                 public virtual IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) 
267                 {
268                         throw GetMustImplement ();
269                 }
270                 
271                 public virtual IAsyncResult BeginGetResponse (AsyncCallback callback, object state)
272                 {
273                         throw GetMustImplement ();
274                 }
275
276                 public static WebRequest Create (string requestUriString) 
277                 {
278                         if (requestUriString == null)
279                                 throw new ArgumentNullException ("requestUriString");
280                         return Create (new Uri (requestUriString));
281                 }
282                                 
283                 public static WebRequest Create (Uri requestUri) 
284                 {
285                         if (requestUri == null)
286                                 throw new ArgumentNullException ("requestUri");
287                         return GetCreator (requestUri.AbsoluteUri).Create (requestUri);
288                 }
289                 
290                 public static WebRequest CreateDefault (Uri requestUri)
291                 {
292                         if (requestUri == null)
293                                 throw new ArgumentNullException ("requestUri");
294                         return GetCreator (requestUri.Scheme).Create (requestUri);
295                 }
296                 static HttpWebRequest SharedCreateHttp (Uri uri)
297                 {
298                         if (uri.Scheme != "http" && uri.Scheme != "https")
299                                 throw new NotSupportedException ("The uri should start with http or https");
300
301                         return new HttpWebRequest (uri);
302                 }
303
304                 public static HttpWebRequest CreateHttp (string requestUriString)
305                 {
306                         if (requestUriString == null)
307                                 throw new ArgumentNullException ("requestUriString");
308                         return SharedCreateHttp (new Uri (requestUriString));
309                 }
310                         
311                 public static HttpWebRequest CreateHttp (Uri requestUri)
312                 {
313                         if (requestUri == null)
314                                 throw new ArgumentNullException ("requestUri");
315                         return SharedCreateHttp (requestUri);
316                 }
317                 public virtual Stream EndGetRequestStream (IAsyncResult asyncResult)
318                 {
319                         throw GetMustImplement ();
320                 }
321                 
322                 public virtual WebResponse EndGetResponse (IAsyncResult asyncResult)
323                 {
324                         throw GetMustImplement ();
325                 }
326                 
327                 public virtual Stream GetRequestStream()
328                 {
329                         throw GetMustImplement ();
330                 }
331                 
332                 public virtual WebResponse GetResponse()
333                 {
334                         throw GetMustImplement ();
335                 }
336                 
337                 // Takes an ArrayList of fileglob-formatted strings and returns an array of Regex-formatted strings
338                 private static string[] CreateBypassList (ArrayList al)
339                 {
340                         string[] result = al.ToArray (typeof (string)) as string[];
341                         for (int c = 0; c < result.Length; c++)
342                         {
343                                 result [c] = "^" +
344                                         Regex.Escape (result [c]).Replace (@"\*", ".*").Replace (@"\?", ".") +
345                                         "$";
346                         }
347                         return result;
348                 }
349
350                 [MonoTODO("Look in other places for proxy config info")]
351                 public static IWebProxy GetSystemWebProxy ()
352                 {
353 #if MONOTOUCH
354                         return CFNetwork.GetDefaultProxy ();
355 #else
356 #if MONODROID
357                         // Return the system web proxy.  This only works for ICS+.
358                         var androidProxy = AndroidPlatform.GetDefaultProxy ();
359                         if (androidProxy != null)
360                                 return androidProxy;
361 #endif
362 #if !NET_2_1
363                         if (IsWindows ()) {
364                                 int iProxyEnable = (int)Microsoft.Win32.Registry.GetValue ("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", "ProxyEnable", 0);
365
366                                 if (iProxyEnable > 0) {
367                                         string strHttpProxy = "";                                       
368                                         bool bBypassOnLocal = false;
369                                         ArrayList al = new ArrayList ();
370                                         
371                                         string strProxyServer = (string)Microsoft.Win32.Registry.GetValue ("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", "ProxyServer", null);
372                                         string strProxyOverrride = (string)Microsoft.Win32.Registry.GetValue ("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", "ProxyOverride", null);
373                                         
374                                         if (strProxyServer.Contains ("=")) {
375                                                 foreach (string strEntry in strProxyServer.Split (new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
376                                                         if (strEntry.StartsWith ("http=")) {
377                                                                 strHttpProxy = strEntry.Substring (5);
378                                                                 break;
379                                                         }
380                                         } else strHttpProxy = strProxyServer;
381                                         
382                                         if (strProxyOverrride != null) {                                                
383                                                 string[] bypassList = strProxyOverrride.Split (new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
384                                         
385                                                 foreach (string str in bypassList) {
386                                                         if (str != "<local>")
387                                                                 al.Add (str);
388                                                         else
389                                                                 bBypassOnLocal = true;
390                                                 }
391                                         }
392                                         
393                                         return new WebProxy (strHttpProxy, bBypassOnLocal, CreateBypassList (al));
394                                 }
395                         } else {
396 #endif
397                                 if (Platform.IsMacOS)
398                                         return CFNetwork.GetDefaultProxy ();
399                                 
400                                 string address = Environment.GetEnvironmentVariable ("http_proxy");
401
402                                 if (address == null)
403                                         address = Environment.GetEnvironmentVariable ("HTTP_PROXY");
404                                 
405                                 if (address != null) {
406                                         try {
407                                                 if (!address.StartsWith ("http://"))
408                                                         address = "http://" + address;
409
410                                                 Uri uri = new Uri (address);
411                                                 IPAddress ip;
412                                                 
413                                                 if (IPAddress.TryParse (uri.Host, out ip)) {
414                                                         if (IPAddress.Any.Equals (ip)) {
415                                                                 UriBuilder builder = new UriBuilder (uri);
416                                                                 builder.Host = "127.0.0.1";
417                                                                 uri = builder.Uri;
418                                                         } else if (IPAddress.IPv6Any.Equals (ip)) {
419                                                                 UriBuilder builder = new UriBuilder (uri);
420                                                                 builder.Host = "[::1]";
421                                                                 uri = builder.Uri;
422                                                         }
423                                                 }
424                                                 
425                                                 bool bBypassOnLocal = false;                                            
426                                                 ArrayList al = new ArrayList ();
427                                                 string bypass = Environment.GetEnvironmentVariable ("no_proxy");
428                                                 
429                                                 if (bypass == null)
430                                                         bypass = Environment.GetEnvironmentVariable ("NO_PROXY");
431                                                 
432                                                 if (bypass != null) {
433                                                         string[] bypassList = bypass.Split (new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
434                                                 
435                                                         foreach (string str in bypassList) {
436                                                                 if (str != "*.local")
437                                                                         al.Add (str);
438                                                                 else
439                                                                         bBypassOnLocal = true;
440                                                         }
441                                                 }
442                                                 
443                                                 return new WebProxy (uri, bBypassOnLocal, CreateBypassList (al));
444                                         } catch (UriFormatException) {
445                                         }
446                                 }
447 #if !NET_2_1
448                         }
449 #endif
450                         
451                         return new WebProxy ();
452 #endif // MONOTOUCH
453                 }
454
455                 void ISerializable.GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)
456                 {
457                         throw new NotSupportedException ();
458                 }
459
460                 protected virtual void GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext)
461                 {
462                         throw GetMustImplement ();
463                 }
464
465                 public static bool RegisterPrefix (string prefix, IWebRequestCreate creator)
466                 {
467                         if (prefix == null)
468                                 throw new ArgumentNullException ("prefix");
469                         if (creator == null)
470                                 throw new ArgumentNullException ("creator");
471                         
472                         lock (prefixes.SyncRoot) {
473                                 string lowerCasePrefix = prefix.ToLower (CultureInfo.InvariantCulture);
474                                 if (prefixes.Contains (lowerCasePrefix))
475                                         return false;
476                                 prefixes.Add (lowerCasePrefix, creator);
477                         }
478                         return true;
479                 }
480                 
481                 private static IWebRequestCreate GetCreator (string prefix)
482                 {
483                         int longestPrefix = -1;
484                         IWebRequestCreate creator = null;
485
486                         prefix = prefix.ToLower (CultureInfo.InvariantCulture);
487
488                         IDictionaryEnumerator e = prefixes.GetEnumerator ();
489                         while (e.MoveNext ()) {
490                                 string key = e.Key as string;
491
492                                 if (key.Length <= longestPrefix) 
493                                         continue;
494                                 
495                                 if (!prefix.StartsWith (key))
496                                         continue;
497                                         
498                                 longestPrefix = key.Length;
499                                 creator = (IWebRequestCreate) e.Value;
500                         }
501                         
502                         if (creator == null) 
503                                 throw new NotSupportedException (prefix);
504                                 
505                         return creator;
506                 }
507                 
508                 internal static bool IsWindows ()
509                 {
510                         return (int) Environment.OSVersion.Platform < 4;
511                 }
512
513                 internal static void ClearPrefixes ()
514                 {
515                         prefixes.Clear ();
516                 }
517
518                 internal static void RemovePrefix (string prefix)
519                 {
520                         prefixes.Remove (prefix);
521                 }
522
523                 internal static void AddPrefix (string prefix, string typeName)
524                 {
525                         Type type = Type.GetType (typeName);
526                         if (type == null)
527                                 throw new ConfigurationException (String.Format ("Type {0} not found", typeName));
528                         AddPrefix (prefix, type);
529                 }
530
531                 internal static void AddPrefix (string prefix, Type type)
532                 {
533                         object o = Activator.CreateInstance (type, true);
534                         prefixes [prefix] = o;
535                 }
536
537                 public virtual Task<Stream> GetRequestStreamAsync ()
538                 {
539                         return Task<Stream>.Factory.FromAsync (BeginGetRequestStream, EndGetRequestStream, null);
540                 }
541
542                 public virtual Task<WebResponse> GetResponseAsync ()
543                 {
544                         return Task<WebResponse>.Factory.FromAsync (BeginGetResponse, EndGetResponse, null);
545                 }
546
547         }
548 }