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