[System] WebProxy from referencesource
[mono.git] / mcs / class / System / System.Net / HttpWebRequest.cs
index 04baad213dd3ed3f3df9b70d5008dcd06095c6fc..a151f6bb870e2ed992fa5b5639864d82835979ec 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+#if SECURITY_DEP
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+using MonoSecurity::Mono.Security.Interface;
+#else
+using Mono.Security.Interface;
+#endif
+#endif
+
 using System;
 using System.Collections;
 using System.Configuration;
@@ -39,11 +48,13 @@ using System.IO;
 using System.Net;
 using System.Net.Cache;
 using System.Net.Sockets;
+using System.Net.Security;
 using System.Runtime.Remoting.Messaging;
 using System.Runtime.Serialization;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
 using System.Threading;
+using Mono.Net.Security;
 
 namespace System.Net 
 {
@@ -101,6 +112,11 @@ namespace System.Net
                int maxResponseHeadersLength;
                static int defaultMaxResponseHeadersLength;
                int readWriteTimeout = 300000; // ms
+               IMonoTlsProvider tlsProvider;
+#if SECURITY_DEP
+               MonoTlsSettings tlsSettings;
+#endif
+               ServerCertValidationCallback certValidationCallback;
 
                enum NtlmAuthState {
                        None,
@@ -110,6 +126,9 @@ namespace System.Net
                AuthorizationState auth_state, proxy_auth_state;
                string host;
 
+               [NonSerialized]
+               internal Action<Stream> ResendContentFactory;
+
                // Constructors
                static HttpWebRequest ()
                {
@@ -135,11 +154,20 @@ namespace System.Net
                {
                        this.requestUri = uri;
                        this.actualUri = uri;
-                       this.proxy = GlobalProxySelection.Select;
-                       this.webHeaders = new WebHeaderCollection (WebHeaderCollection.HeaderInfo.Request);
+                       this.proxy = InternalDefaultWebProxy;
+                       this.webHeaders = new WebHeaderCollection (WebHeaderCollectionType.HttpWebRequest);
                        ThrowOnError = true;
                        ResetAuthorization ();
                }
+
+#if SECURITY_DEP
+               internal HttpWebRequest (Uri uri, IMonoTlsProvider tlsProvider, MonoTlsSettings settings = null)
+                       : this (uri)
+               {
+                       this.tlsProvider = tlsProvider;
+                       this.tlsSettings = settings;
+               }
+#endif
                
                [Obsolete ("Serialization is obsoleted for this type", false)]
                protected HttpWebRequest (SerializationInfo serializationInfo, StreamingContext streamingContext) 
@@ -177,11 +205,19 @@ namespace System.Net
                
                // Properties
 
+               void SetSpecialHeaders(string HeaderName, string value) {
+                       value = WebHeaderCollection.CheckBadChars(value, true);
+                       webHeaders.RemoveInternal(HeaderName);
+                       if (value.Length != 0) {
+                               webHeaders.AddInternal(HeaderName, value);
+                       }
+               }
+
                public string Accept {
                        get { return webHeaders ["Accept"]; }
                        set {
                                CheckRequestStarted ();
-                               webHeaders.RemoveAndAdd ("Accept", value);
+                               SetSpecialHeaders ("Accept", value);
                        }
                }
                
@@ -226,25 +262,41 @@ namespace System.Net
                
                internal bool InternalAllowBuffering {
                        get {
-                               return (allowBuffering && (method != "HEAD" && method != "GET" &&
-                                                       method != "MKCOL" && method != "CONNECT" &&
-                                                       method != "TRACE"));
+                               return allowBuffering && MethodWithBuffer;
                        }
                }
+
+               bool MethodWithBuffer {
+                       get {
+                               return method != "HEAD" && method != "GET" &&
+                               method != "MKCOL" && method != "CONNECT" &&
+                               method != "TRACE";
+                       }
+               }
+
+               internal IMonoTlsProvider TlsProvider {
+                       get { return tlsProvider; }
+               }
+
+#if SECURITY_DEP
+               internal MonoTlsSettings TlsSettings {
+                       get { return tlsSettings; }
+               }
+#endif
                
                public X509CertificateCollection ClientCertificates {
                        get {
                                if (certificates == null)
                                        certificates = new X509CertificateCollection ();
-
                                return certificates;
                        }
-                       [MonoTODO]
                        set {
-                               throw GetMustImplement ();
+                               if (value == null)
+                                       throw new ArgumentNullException ("value");
+                               certificates = value;
                        }
                }
-               
+
                public string Connection {
                        get { return webHeaders ["Connection"]; }
                        set {
@@ -262,7 +314,7 @@ namespace System.Net
                                if (keepAlive)
                                        value = value + ", Keep-Alive";
                                
-                               webHeaders.RemoveAndAdd ("Connection", value);
+                               webHeaders.CheckUpdate ("Connection", value);
                        }
                }               
                
@@ -292,11 +344,7 @@ namespace System.Net
                public override string ContentType { 
                        get { return webHeaders ["Content-Type"]; }
                        set {
-                               if (value == null || value.Trim().Length == 0) {
-                                       webHeaders.RemoveInternal ("Content-Type");
-                                       return;
-                               }
-                               webHeaders.RemoveAndAdd ("Content-Type", value);
+                               SetSpecialHeaders ("Content-Type", value);
                        }
                }
                
@@ -323,13 +371,17 @@ namespace System.Net
                                return DateTime.ParseExact (date, "r", CultureInfo.InvariantCulture).ToLocalTime ();
                        }
                        set {
-                               if (value.Equals (DateTime.MinValue))
-                                       webHeaders.RemoveInternal ("Date");
-                               else
-                                       webHeaders.RemoveAndAdd ("Date", value.ToUniversalTime ().ToString ("r", CultureInfo.InvariantCulture));
+                               SetDateHeaderHelper ("Date", value);
                        }
                }
 
+               void SetDateHeaderHelper(string headerName, DateTime dateTime) {
+                       if (dateTime == DateTime.MinValue)
+                               SetSpecialHeaders(headerName, null); // remove header
+                       else
+                               SetSpecialHeaders(headerName, HttpProtocolUtils.date2string(dateTime));
+               }
+
 #if !NET_2_1
                [MonoTODO]
                public static new RequestCachePolicy DefaultCachePolicy
@@ -370,7 +422,8 @@ namespace System.Net
                                if (val == "100-continue")
                                        throw new ArgumentException ("100-Continue cannot be set with this property.",
                                                                     "value");
-                               webHeaders.RemoveAndAdd ("Expect", value);
+
+                               webHeaders.CheckUpdate ("Expect", value);
                        }
                }
                
@@ -383,12 +436,21 @@ namespace System.Net
                        get { return webHeaders; }
                        set {
                                CheckRequestStarted ();
-                               WebHeaderCollection newHeaders = new WebHeaderCollection (WebHeaderCollection.HeaderInfo.Request);
-                               int count = value.Count;
-                               for (int i = 0; i < count; i++) 
-                                       newHeaders.Add (value.GetKey (i), value.Get (i));
 
-                               webHeaders = newHeaders;
+                               WebHeaderCollection webHeaders = value;
+                               WebHeaderCollection newWebHeaders = new WebHeaderCollection(WebHeaderCollectionType.HttpWebRequest);
+
+                               // Copy And Validate -
+                               // Handle the case where their object tries to change
+                               //  name, value pairs after they call set, so therefore,
+                               //  we need to clone their headers.
+                               //
+
+                               foreach (String headerName in webHeaders.AllKeys ) {
+                                       newWebHeaders.Add(headerName,webHeaders[headerName]);
+                               }
+
+                               webHeaders = newWebHeaders;
                        }
                }
                
@@ -549,6 +611,7 @@ namespace System.Net
                                CheckRequestStarted ();
                                proxy = value;
                                servicePoint = null; // we may need a new one
+                               GetServicePoint ();
                        }
                }
                
@@ -619,7 +682,7 @@ namespace System.Net
                                if (!sendChunked)
                                        throw new ArgumentException ("SendChunked must be True", "value");
 
-                               webHeaders.RemoveAndAdd ("Transfer-Encoding", value);
+                               webHeaders.CheckUpdate ("Transfer-Encoding", value);
                        }
                }
 
@@ -657,7 +720,26 @@ namespace System.Net
                internal bool ProxyQuery {
                        get { return servicePoint.UsesProxy && !servicePoint.UseConnect; }
                }
-               
+
+               internal ServerCertValidationCallback ServerCertValidationCallback {
+                       get { return certValidationCallback; }
+               }
+
+               public RemoteCertificateValidationCallback ServerCertificateValidationCallback {
+                       get {
+                               if (certValidationCallback == null)
+                                       return null;
+                               return certValidationCallback.ValidationCallback;
+                       }
+                       set
+                       {
+                               if (value == null)
+                                       certValidationCallback = null;
+                               else
+                                       certValidationCallback = new ServerCertValidationCallback (value);
+                       }
+               }
+
                // Methods
                
                internal ServicePoint GetServicePoint ()
@@ -708,7 +790,7 @@ namespace System.Net
                {
                        if (rangeSpecifier == null)
                                throw new ArgumentNullException ("rangeSpecifier");
-                       if (!WebHeaderCollection.IsHeaderValue (rangeSpecifier))
+                       if (!WebHeaderCollection.IsValidToken (rangeSpecifier))
                                throw new ArgumentException ("Invalid range specifier", "rangeSpecifier");
 
                        string r = webHeaders ["Range"];
@@ -726,7 +808,7 @@ namespace System.Net
                                r = r + "0" + n;
                        else
                                r = r + n + "-";
-                       webHeaders.RemoveAndAdd ("Range", r);
+                       webHeaders.ChangeInternal ("Range", r);
                }
 
                public
@@ -734,7 +816,7 @@ namespace System.Net
                {
                        if (rangeSpecifier == null)
                                throw new ArgumentNullException ("rangeSpecifier");
-                       if (!WebHeaderCollection.IsHeaderValue (rangeSpecifier))
+                       if (!WebHeaderCollection.IsValidToken (rangeSpecifier))
                                throw new ArgumentException ("Invalid range specifier", "rangeSpecifier");
                        if (from > to || from < 0)
                                throw new ArgumentOutOfRangeException ("from");
@@ -748,7 +830,7 @@ namespace System.Net
                                r += ",";
 
                        r = String.Format ("{0}{1}-{2}", r, from, to);
-                       webHeaders.RemoveAndAdd ("Range", r);
+                       webHeaders.ChangeInternal ("Range", r);
                }
 
                
@@ -906,11 +988,17 @@ namespace System.Net
                                        }
                                }
 
-                               if (!requestSent) {
+                               if (requestSent)
+                                       return;
+
+                               try {
                                        requestSent = true;
                                        redirects = 0;
                                        servicePoint = GetServicePoint ();
                                        abortHandler = servicePoint.SendRequest (this, connectionGroup);
+                               } catch (Exception ex) {
+                                       aread.SetCompleted (synch, ex);
+                                       aread.DoCallback ();
                                }
                        });
 
@@ -1095,13 +1183,13 @@ namespace System.Net
                                break;
                        }
 
-                       if (method != "GET" && !InternalAllowBuffering && writeStream.WriteBufferLength > 0)
+                       if (method != "GET" && !InternalAllowBuffering && (writeStream.WriteBufferLength > 0 || contentLength > 0))
                                e = new WebException ("The request requires buffering data to succeed.", null, WebExceptionStatus.ProtocolError, webResponse);
 
                        if (e != null)
                                throw e;
 
-                       if (AllowWriteStreamBuffering)
+                       if (AllowWriteStreamBuffering || method == "GET")
                                contentLength = -1;
 
                        uriString = webResponse.Headers ["Location"];
@@ -1128,7 +1216,7 @@ namespace System.Net
                        bool continue100 = false;
                        if (sendChunked) {
                                continue100 = true;
-                               webHeaders.RemoveAndAdd ("Transfer-Encoding", "chunked");
+                               webHeaders.ChangeInternal ("Transfer-Encoding", "chunked");
                                webHeaders.RemoveInternal ("Content-Length");
                        } else if (contentLength != -1) {
                                if (auth_state.NtlmAuthState == NtlmAuthState.Challenge || proxy_auth_state.NtlmAuthState == NtlmAuthState.Challenge) {
@@ -1151,7 +1239,7 @@ namespace System.Net
 
                        if (actualVersion == HttpVersion.Version11 && continue100 &&
                            servicePoint.SendContinue) { // RFC2616 8.2.3
-                               webHeaders.RemoveAndAdd ("Expect" , "100-continue");
+                               webHeaders.ChangeInternal ("Expect" , "100-continue");
                                expectContinue = true;
                        } else {
                                webHeaders.RemoveInternal ("Expect");
@@ -1167,16 +1255,16 @@ namespace System.Net
                        if (keepAlive && (version == HttpVersion.Version10 || spoint10)) {
                                if (webHeaders[connectionHeader] == null
                                    || webHeaders[connectionHeader].IndexOf ("keep-alive", StringComparison.OrdinalIgnoreCase) == -1)
-                                       webHeaders.RemoveAndAdd (connectionHeader, "keep-alive");
+                                       webHeaders.ChangeInternal (connectionHeader, "keep-alive");
                        } else if (!keepAlive && version == HttpVersion.Version11) {
-                               webHeaders.RemoveAndAdd (connectionHeader, "close");
+                               webHeaders.ChangeInternal (connectionHeader, "close");
                        }
 
                        webHeaders.SetInternal ("Host", Host);
                        if (cookieContainer != null) {
                                string cookieHeader = cookieContainer.GetCookieHeader (actualUri);
                                if (cookieHeader != "")
-                                       webHeaders.RemoveAndAdd ("Cookie", cookieHeader);
+                                       webHeaders.ChangeInternal ("Cookie", cookieHeader);
                                else
                                        webHeaders.RemoveInternal ("Cookie");
                        }
@@ -1187,7 +1275,7 @@ namespace System.Net
                        if ((auto_decomp & DecompressionMethods.Deflate) != 0)
                                accept_encoding = accept_encoding != null ? "gzip, deflate" : "deflate";
                        if (accept_encoding != null)
-                               webHeaders.RemoveAndAdd ("Accept-Encoding", accept_encoding);
+                               webHeaders.ChangeInternal ("Accept-Encoding", accept_encoding);
 
                        if (!usedPreAuth && preAuthenticate)
                                DoPreAuthenticate ();
@@ -1227,7 +1315,7 @@ namespace System.Net
                                        wex = new WebException (msg, status);
                                } else {
                                        msg = String.Format ("Error: {0} ({1})", status, exc.Message);
-                                       wex = new WebException (msg, exc, status);
+                                       wex = new WebException (msg, status, WebExceptionInternalStatus.RequestFatal, exc);
                                }
                                r.SetCompleted (false, wex);
                                r.DoCallback ();
@@ -1306,8 +1394,7 @@ namespace System.Net
                                                bodyBuffer = null;
                                                writeStream.Close ();
                                        }
-                               } else if (method != "HEAD" && method != "GET" && method != "MKCOL" && method != "CONNECT" &&
-                                         method != "TRACE") {
+                               } else if (MethodWithBuffer) {
                                        if (getResponseCalled && !writeStream.RequestWritten)
                                                return writeStream.WriteRequestAsync (result);
                                }
@@ -1387,7 +1474,7 @@ namespace System.Net
                        if (wce != null) {
                                WebConnection cnc = wce.Connection;
                                cnc.PriorityRequest = this;
-                               ICredentials creds = !isProxy ? credentials : proxy.Credentials;
+                               ICredentials creds = (!isProxy || proxy == null) ? credentials : proxy.Credentials;
                                if (creds != null) {
                                        cnc.NtlmCredential = creds.GetCredential (requestUri, "NTLM");
                                        cnc.UnsafeAuthenticatedConnectionSharing = unsafe_auth_blah;
@@ -1444,7 +1531,7 @@ namespace System.Net
                                        return;
                                }
 
-                               bool isProxy = ProxyQuery && !proxy.IsBypassed (actualUri);
+                               bool isProxy = ProxyQuery && proxy != null && !proxy.IsBypassed (actualUri);
 
                                bool redirected;
                                try {
@@ -1547,7 +1634,7 @@ namespace System.Net
                                if (isProxy && (request.proxy == null || request.proxy.Credentials == null))
                                        return false;
 
-                               string [] authHeaders = response.Headers.GetValues_internal (isProxy ? "Proxy-Authenticate" : "WWW-Authenticate", false);
+                               string [] authHeaders = response.Headers.GetValues (isProxy ? "Proxy-Authenticate" : "WWW-Authenticate");
                                if (authHeaders == null || authHeaders.Length == 0)
                                        return false;
 
@@ -1562,7 +1649,7 @@ namespace System.Net
                                        return false;
                                request.webHeaders [isProxy ? "Proxy-Authorization" : "Authorization"] = auth.Message;
                                isCompleted = auth.Complete;
-                               bool is_ntlm = (auth.Module.AuthenticationType == "NTLM");
+                               bool is_ntlm = (auth.ModuleAuthenticationType == "NTLM");
                                if (is_ntlm)
                                        ntlm_auth_state = (NtlmAuthState)((int) ntlm_auth_state + 1);
                                return true;
@@ -1606,12 +1693,28 @@ namespace System.Net
                                        (ProxyQuery && !proxy_auth_state.IsCompleted && code == HttpStatusCode.ProxyAuthenticationRequired)) {
                                        if (!usedPreAuth && CheckAuthorization (webResponse, code)) {
                                                // Keep the written body, so it can be rewritten in the retry
-                                               if (InternalAllowBuffering) {
-                                                       if (writeStream.WriteBufferLength > 0) {
-                                                               bodyBuffer = writeStream.WriteBuffer;
-                                                               bodyBufferLength = writeStream.WriteBufferLength;
+                                               if (MethodWithBuffer) {
+                                                       if (AllowWriteStreamBuffering) {
+                                                               if (writeStream.WriteBufferLength > 0) {
+                                                                       bodyBuffer = writeStream.WriteBuffer;
+                                                                       bodyBufferLength = writeStream.WriteBufferLength;
+                                                               }
+
+                                                               return true;
+                                                       }
+
+                                                       //
+                                                       // Buffering is not allowed but we have alternative way to get same content (we
+                                                       // need to resent it due to NTLM Authentication).
+                                                       //
+                                                       if (ResendContentFactory != null) {
+                                                               using (var ms = new MemoryStream ()) {
+                                                                       ResendContentFactory (ms);
+                                                                       bodyBuffer = ms.ToArray ();
+                                                                       bodyBufferLength = bodyBuffer.Length;
+                                                               }
+                                                               return true;
                                                        }
-                                                       return true;
                                                } else if (method != "PUT" && method != "POST") {
                                                        bodyBuffer = null;
                                                        return true;