Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Net / System / Net / FtpWebRequest.cs / 1305376 / FtpWebRequest.cs
// ------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // // ----------------------------------------------------------------------------- // namespace System.Net { using System.Collections; using System.IO; using System.Text; using System.Net.Sockets; using System.Net.Cache; using System.Threading; using System.Security; using System.Security.Cryptography.X509Certificates ; using System.Security.Permissions; using System.Security.Authentication; using System.Globalization; ////// internal enum FtpOperation { DownloadFile = 0, ListDirectory = 1, ListDirectoryDetails = 2, UploadFile = 3, UploadFileUnique = 4, AppendFile = 5, DeleteFile = 6, GetDateTimestamp = 7, GetFileSize = 8, Rename = 9, MakeDirectory = 10, RemoveDirectory = 11, PrintWorkingDirectory = 12, Other = 13, } [Flags] internal enum FtpMethodFlags { None = 0x0, IsDownload = 0x1, IsUpload = 0x2, TakesParameter = 0x4, MayTakeParameter = 0x8, DoesNotTakeParameter = 0x10, ParameterIsDirectory = 0x20, ShouldParseForResponseUri = 0x40, HasHttpCommand = 0x80, MustChangeWorkingDirectoryToPath = 0x100 } internal class FtpMethodInfo { internal string Method; internal FtpOperation Operation; internal FtpMethodFlags Flags; internal string HttpCommand; internal FtpMethodInfo(string method, FtpOperation operation, FtpMethodFlags flags, string httpCommand) { Method = method; Operation = operation; Flags = flags; HttpCommand = httpCommand; } internal bool HasFlag(FtpMethodFlags flags) { return (Flags & flags) != 0; } internal bool IsCommandOnly { get { return (Flags & (FtpMethodFlags.IsDownload | FtpMethodFlags.IsUpload)) == 0; } } internal bool IsUpload { get { return (Flags & FtpMethodFlags.IsUpload) != 0; } } internal bool IsDownload { get { return (Flags & FtpMethodFlags.IsDownload) != 0; } } internal bool HasHttpCommand { get { return (Flags & FtpMethodFlags.HasHttpCommand) != 0; } } ///Allows us to control what the request is used for (based on the type of behavior, /// that the command calls for) ////// internal bool ShouldParseForResponseUri { get { return (Flags & FtpMethodFlags.ShouldParseForResponseUri) != 0; } } internal static FtpMethodInfo GetMethodInfo(string method) { method = method.ToUpper(CultureInfo.InvariantCulture); foreach (FtpMethodInfo methodInfo in KnownMethodInfo) if (method == methodInfo.Method) return methodInfo; // We don't support generic methods throw new ArgumentException(SR.GetString(SR.net_ftp_unsupported_method), "method"); } static readonly FtpMethodInfo[] KnownMethodInfo = { new FtpMethodInfo(WebRequestMethods.Ftp.DownloadFile, FtpOperation.DownloadFile, FtpMethodFlags.IsDownload | FtpMethodFlags.HasHttpCommand | FtpMethodFlags.TakesParameter, "GET"), new FtpMethodInfo(WebRequestMethods.Ftp.ListDirectory, FtpOperation.ListDirectory, FtpMethodFlags.IsDownload | FtpMethodFlags.MustChangeWorkingDirectoryToPath | FtpMethodFlags.HasHttpCommand | FtpMethodFlags.MayTakeParameter, "GET"), new FtpMethodInfo(WebRequestMethods.Ftp.ListDirectoryDetails, FtpOperation.ListDirectoryDetails, FtpMethodFlags.IsDownload | FtpMethodFlags.MustChangeWorkingDirectoryToPath | FtpMethodFlags.HasHttpCommand | FtpMethodFlags.MayTakeParameter, "GET"), new FtpMethodInfo(WebRequestMethods.Ftp.UploadFile, FtpOperation.UploadFile, FtpMethodFlags.IsUpload | FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.UploadFileWithUniqueName, FtpOperation.UploadFileUnique, FtpMethodFlags.IsUpload | FtpMethodFlags.MustChangeWorkingDirectoryToPath | FtpMethodFlags.DoesNotTakeParameter | FtpMethodFlags.ShouldParseForResponseUri, null), new FtpMethodInfo(WebRequestMethods.Ftp.AppendFile, FtpOperation.AppendFile, FtpMethodFlags.IsUpload | FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.DeleteFile, FtpOperation.DeleteFile, FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.GetDateTimestamp, FtpOperation.GetDateTimestamp, FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.GetFileSize, FtpOperation.GetFileSize, FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.Rename, FtpOperation.Rename, FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.MakeDirectory, FtpOperation.MakeDirectory, FtpMethodFlags.TakesParameter | FtpMethodFlags.ParameterIsDirectory, null), new FtpMethodInfo(WebRequestMethods.Ftp.RemoveDirectory, FtpOperation.RemoveDirectory, FtpMethodFlags.TakesParameter | FtpMethodFlags.ParameterIsDirectory, null), new FtpMethodInfo(WebRequestMethods.Ftp.PrintWorkingDirectory, FtpOperation.PrintWorkingDirectory, FtpMethodFlags.DoesNotTakeParameter, null) }; } ///True if we should attempt to get a response uri /// out of a server response ////// public sealed class FtpWebRequest : WebRequest { private object m_SyncObject; private ICredentials m_AuthInfo; private readonly Uri m_Uri; private FtpMethodInfo m_MethodInfo; private string m_RenameTo = null; private bool m_GetRequestStreamStarted; private bool m_GetResponseStarted; private DateTime m_StartTime; private int m_Timeout = s_DefaultTimeout; private int m_RemainingTimeout; private long m_ContentLength = 0; private long m_ContentOffset = 0; private IWebProxy m_Proxy; #if !FEATURE_PAL private X509CertificateCollection m_ClientCertificates; #endif // !FEATURE_PAL private bool m_KeepAlive = true; private bool m_Passive = true; private bool m_Binary = true; private string m_ConnectionGroupName; private ServicePoint m_ServicePoint; private bool m_CacheDone; // Not sure why but the command stream wants to notify the request on every pipiline closure closure by invoking RequestCallback. // m_CacheDone is to facilitate PutConnection decision and to prevent bothering cache protocol when it's all completed. private bool m_Async; private bool m_Aborted; private bool m_TimedOut; private HttpWebRequest m_HttpWebRequest; private Exception m_Exception; private TimerThread.Queue m_TimerQueue = s_DefaultTimerQueue; private TimerThread.Callback m_TimerCallback; private bool m_EnableSsl; private bool m_ProxyUserSet; private ConnectionPool m_ConnectionPool; private FtpControlStream m_Connection; private Stream m_Stream; private RequestStage m_RequestStage; private bool m_OnceFailed; private WebHeaderCollection m_FtpRequestHeaders; private FtpWebResponse m_FtpWebResponse; private int m_ReadWriteTimeout = 5*60*1000; //5 minutes. private ContextAwareResult m_WriteAsyncResult; private LazyAsyncResult m_ReadAsyncResult; private LazyAsyncResult m_RequestCompleteAsyncResult; private static readonly GeneralAsyncDelegate m_AsyncCallback = new GeneralAsyncDelegate(AsyncCallbackWrapper); private static readonly CreateConnectionDelegate m_CreateConnectionCallback = new CreateConnectionDelegate(CreateFtpConnection); private static readonly NetworkCredential DefaultFtpNetworkCredential = new NetworkCredential("anonymous", "anonymous@", String.Empty); private static readonly int s_DefaultTimeout = WebRequest.DefaultTimeout; private static readonly TimerThread.Queue s_DefaultTimerQueue = TimerThread.GetOrCreateQueue(s_DefaultTimeout); // Used by FtpControlStream internal FtpMethodInfo MethodInfo { get { return m_MethodInfo; } } // Used by FtpControlStream internal static NetworkCredential DefaultNetworkCredential{ get { return DefaultFtpNetworkCredential; } } // This is a shortcut that would set the default policy for HTTP/HTTPS. // The default policy is overridden by any prefix-registered policy. // Will demand permission for set{} public static new RequestCachePolicy DefaultCachePolicy { get { RequestCachePolicy policy = RequestCacheManager.GetBinding(Uri.UriSchemeFtp).Policy; if (policy == null) return WebRequest.DefaultCachePolicy; return policy; } set { // This is a replacement of RequestCachePermission demand since we are not including the latest in the product. ExceptionHelper.WebPermissionUnrestricted.Demand(); RequestCacheBinding binding = RequestCacheManager.GetBinding(Uri.UriSchemeFtp); RequestCacheManager.SetBinding(Uri.UriSchemeFtp, new RequestCacheBinding(binding.Cache, binding.Validator, value)); } } ///The FtpWebRequest class implements a basic FTP client /// interface. ////// public override string Method { get { return m_MethodInfo.Method; } set { if (String.IsNullOrEmpty(value)) { throw new ArgumentException(SR.GetString(SR.net_ftp_invalid_method_name), "value"); } if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } try { m_MethodInfo = FtpMethodInfo.GetMethodInfo(value); } catch (ArgumentException) { throw new ArgumentException(SR.GetString(SR.net_ftp_unsupported_method), "value"); } } } ////// Selects upload or download of files. WebRequestMethods.Ftp.DownloadFile is default. /// Not allowed to be changed once request is started. /// ////// public string RenameTo { get { return m_RenameTo; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (String.IsNullOrEmpty(value)) { throw new ArgumentException(SR.GetString(SR.net_ftp_invalid_renameto), "value"); } m_RenameTo = value; } } ////// Sets the target name for the WebRequestMethods.Ftp.Rename command /// Not allowed to be changed once request is started. /// ////// public override ICredentials Credentials { get { return m_AuthInfo; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (value == null) { throw new ArgumentNullException("value"); } if (value is SystemNetworkCredential) { throw new ArgumentException(SR.GetString(SR.net_ftp_no_defaultcreds), "value"); } m_AuthInfo = value; } } ///Used for clear text authentication with FTP server ////// public override Uri RequestUri { get { return m_Uri; } } ///Gets the Uri used to make the request ////// public override int Timeout { get { return m_Timeout; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (value<0 && value!=System.Threading.Timeout.Infinite) { throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_io_timeout_use_ge_zero)); } if (m_Timeout != value) { m_Timeout = value; m_TimerQueue = null; } } } // This can always be calculaed as Remaining = Timeout - (Now - Start) // but we are keeping this for performance reasons (To avoid unnecessary // calculations). This can be removed if the performance gains are // considered negligible and not necessary internal int RemainingTimeout { get { return m_RemainingTimeout; } } ///Timeout of the blocking calls such as GetResponse and GetRequestStream (default 100 secs) ////// public int ReadWriteTimeout { get { return m_ReadWriteTimeout; } set { if (m_GetResponseStarted) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (value<=0 && value!=System.Threading.Timeout.Infinite) { throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_io_timeout_use_gt_zero)); } m_ReadWriteTimeout = value; } } ///Used to control the Timeout when calling Stream.Read (AND) Stream.Write. /// Effects Streams returned from GetResponse().GetResponseStream() (AND) GetRequestStream(). /// Default is 5 mins. /// ////// public long ContentOffset { get { return m_ContentOffset; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (value<0) { throw new ArgumentOutOfRangeException("value"); } m_ContentOffset = value; } } ///Used to specify what offset we will read at ////// public override long ContentLength { get { return m_ContentLength; } set { m_ContentLength = value; } } ///Gets or sets the data size of to-be uploaded data ////// public override IWebProxy Proxy { get { ExceptionHelper.WebPermissionUnrestricted.Demand(); return m_Proxy; } set { ExceptionHelper.WebPermissionUnrestricted.Demand(); if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_ProxyUserSet = true; m_Proxy = value; m_ServicePoint = null; ServicePoint refreshIt = ServicePoint ; } } ///Uses an HTTP proxy if needed to send FTP request ////// public override string ConnectionGroupName { get { return m_ConnectionGroupName; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_ConnectionGroupName = value; } } ///Allows private ConnectionPool(s) to be used ////// public ServicePoint ServicePoint { get { if (m_ServicePoint == null) { IWebProxy proxy = m_Proxy; if (!m_ProxyUserSet) proxy = WebRequest.InternalDefaultWebProxy; ServicePoint servicePoint = ServicePointManager.FindServicePoint(m_Uri, proxy); lock (m_SyncObject) { if (m_ServicePoint == null) { m_ServicePoint = servicePoint; m_Proxy = proxy; } } } return m_ServicePoint; } } internal bool Aborted { get { return m_Aborted; } } ///Generates a service point for this request, and allows setting of Connection settings ////// internal FtpWebRequest(Uri uri) { (new WebPermission(NetworkAccess.Connect, uri)).Demand(); if (Logging.On) Logging.PrintInfo(Logging.Web, this, ".ctor", uri.ToString()); if ((object)uri.Scheme != (object)Uri.UriSchemeFtp) throw new ArgumentOutOfRangeException("uri"); m_TimerCallback = new TimerThread.Callback(TimerCallback); m_SyncObject = new object(); NetworkCredential networkCredential = null; m_Uri = uri; m_MethodInfo = FtpMethodInfo.GetMethodInfo(WebRequestMethods.Ftp.DownloadFile); if (m_Uri.UserInfo != null && m_Uri.UserInfo.Length != 0) { string userInfo = m_Uri.UserInfo; string username = userInfo; string password = ""; int index = userInfo.IndexOf(':'); if (index != -1) { username = Uri.UnescapeDataString(userInfo.Substring(0, index)); index++; // skip ':' password = Uri.UnescapeDataString(userInfo.Substring(index, userInfo.Length - index)); } networkCredential = new NetworkCredential(username, password); } if (networkCredential == null) { networkCredential = DefaultFtpNetworkCredential; } m_AuthInfo = networkCredential; SetupCacheProtocol(m_Uri); } // // Used to query for the Response of an FTP request // public override WebResponse GetResponse() { if(Logging.On)Logging.Enter(Logging.Web, this, "GetResponse", ""); if(Logging.On)Logging.PrintInfo(Logging.Web, this, "GetResponse", SR.GetString(SR.net_log_method_equal, m_MethodInfo.Method)); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetResponse"); try { CheckError(); if (m_FtpWebResponse != null) return m_FtpWebResponse; if (m_GetResponseStarted) { throw new InvalidOperationException(SR.GetString(SR.net_repcall)); } m_GetResponseStarted = true; m_StartTime = DateTime.UtcNow; m_RemainingTimeout = Timeout; // We don't really need this variable, but we just need // to call the property to measure its execution time ServicePoint servicePoint = ServicePoint; if (Timeout != System.Threading.Timeout.Infinite) { m_RemainingTimeout = Timeout - (int)((DateTime.UtcNow - m_StartTime).TotalMilliseconds); if(m_RemainingTimeout <= 0){ throw new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout); } } if (ServicePoint.InternalProxyServicePoint) { if (EnableSsl) { m_GetResponseStarted = false; throw new WebException(SR.GetString(SR.net_ftp_proxy_does_not_support_ssl)); } try { HttpWebRequest httpWebRequest = GetHttpWebRequest(); if (Logging.On) Logging.Associate(Logging.Web, this, httpWebRequest); m_FtpWebResponse = new FtpWebResponse((HttpWebResponse)httpWebRequest.GetResponse()); } catch (WebException webException) { if (webException.Response != null && webException.Response is HttpWebResponse) { webException = new WebException(webException.Message, null, webException.Status, new FtpWebResponse((HttpWebResponse)webException.Response), webException.InternalStatus); } SetException(webException); throw webException; } // Catch added to address Bug # 545645 catch (InvalidOperationException invalidOpException) { SetException(invalidOpException); FinishRequestStage(RequestStage.CheckForError); throw; } } else { RequestStage prev = FinishRequestStage(RequestStage.RequestStarted); if (prev >= RequestStage.RequestStarted) { if (prev < RequestStage.ReadReady) { lock (m_SyncObject) { if (m_RequestStage < RequestStage.ReadReady) m_ReadAsyncResult = new LazyAsyncResult(null, null, null); } // GetRequeststream or BeginGetRequestStream has not finished yet? if (m_ReadAsyncResult != null) m_ReadAsyncResult.InternalWaitForCompletion(); CheckError(); } } else { do { SubmitRequest(false); if (m_MethodInfo.IsUpload) FinishRequestStage(RequestStage.WriteReady); else FinishRequestStage(RequestStage.ReadReady); CheckError(); } while (!CheckCacheRetrieveOnResponse()); EnsureFtpWebResponse(null); // This may update the Stream memeber on m_FtpWebResponse based on the CacheProtocol feedback. CheckCacheUpdateOnResponse(); if (m_FtpWebResponse.IsFromCache) FinishRequestStage(RequestStage.ReleaseConnection); } } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "GetResponse", exception); // if m_Exception == null, we are about to throw an exception to the user // and we haven't saved the exception, which also means we haven't dealt // with it. So just release the connection and log this for investigation if (m_Exception == null) { if(Logging.On)Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_unexpected_exception, "GetResponse()")); if (!NclUtilities.IsFatal(exception)){ GlobalLog.Assert("Find out why we are getting an unexpected exception."); } SetException(exception); FinishRequestStage(RequestStage.CheckForError); } throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetResponse", "returns #"+ValidationHelper.HashString(m_FtpWebResponse)); if(Logging.On)Logging.Exit(Logging.Web, this, "GetResponse", ""); } return m_FtpWebResponse; } ////// Initializes a new instance of the ////// class. /// /// /// [HostProtection(ExternalThreading=true)] public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state) { if(Logging.On)Logging.Enter(Logging.Web, this, "BeginGetResponse", ""); if(Logging.On)Logging.PrintInfo(Logging.Web, this, "BeginGetResponse", SR.GetString(SR.net_log_method_equal, m_MethodInfo.Method)); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::BeginGetResponse"); ContextAwareResult asyncResult; try { if (m_FtpWebResponse != null) { asyncResult = new ContextAwareResult(this, state, callback); asyncResult.InvokeCallback(m_FtpWebResponse); return asyncResult; } if (m_GetResponseStarted) { throw new InvalidOperationException(SR.GetString(SR.net_repcall)); } m_GetResponseStarted = true; CheckError(); if (ServicePoint.InternalProxyServicePoint) { HttpWebRequest httpWebRequest = GetHttpWebRequest(); if (Logging.On) Logging.Associate(Logging.Web, this, httpWebRequest); asyncResult = (ContextAwareResult)httpWebRequest.BeginGetResponse(callback, state); } else { RequestStage prev = FinishRequestStage(RequestStage.RequestStarted); asyncResult = new ContextAwareResult(true, true, this, state, callback); m_ReadAsyncResult = asyncResult; if (prev >= RequestStage.RequestStarted) { // To make sure the context is flowed asyncResult.StartPostingAsyncOp(); asyncResult.FinishPostingAsyncOp(); if (prev >= RequestStage.ReadReady) asyncResult = null; else { lock (m_SyncObject) { if (m_RequestStage >= RequestStage.ReadReady) asyncResult = null;; } } if(asyncResult == null) { // need to complete it now asyncResult = (ContextAwareResult)m_ReadAsyncResult; if (!asyncResult.InternalPeekCompleted) asyncResult.InvokeCallback(); } } else { // Do internal processing in this handler to optimize context flowing. lock (asyncResult.StartPostingAsyncOp()) { SubmitRequest(true); asyncResult.FinishPostingAsyncOp(); } FinishRequestStage(RequestStage.CheckForError); } } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "BeginGetResponse", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::BeginGetResponse"); if(Logging.On)Logging.Exit(Logging.Web, this, "BeginGetResponse", ""); } return asyncResult; } ///Used to query for the Response of an FTP request [async version] ////// public override WebResponse EndGetResponse(IAsyncResult asyncResult) { if(Logging.On)Logging.Enter(Logging.Web, this, "EndGetResponse", ""); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetResponse"); try { // parameter validation if (asyncResult==null) { throw new ArgumentNullException("asyncResult"); } LazyAsyncResult castedAsyncResult = asyncResult as LazyAsyncResult; if (castedAsyncResult==null) { throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); } if (HttpProxyMode?(castedAsyncResult.AsyncObject!=this.GetHttpWebRequest()):castedAsyncResult.AsyncObject!=this) { throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); } if (castedAsyncResult.EndCalled) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetResponse")); } if (HttpProxyMode) { try { CheckError(); if (m_FtpWebResponse == null) { m_FtpWebResponse = new FtpWebResponse((HttpWebResponse)GetHttpWebRequest().EndGetResponse(asyncResult)); } } catch (WebException webException) { if (webException.Response != null && webException.Response is HttpWebResponse) { throw new WebException(webException.Message, null, webException.Status, new FtpWebResponse((HttpWebResponse)webException.Response), webException.InternalStatus); } throw; } } else{ castedAsyncResult.InternalWaitForCompletion(); castedAsyncResult.EndCalled = true; CheckError(); } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "EndGetResponse", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetResponse"); if(Logging.On)Logging.Exit(Logging.Web, this, "EndGetResponse", ""); } return m_FtpWebResponse; } ///Returns result of query for the Response of an FTP request [async version] ////// public override Stream GetRequestStream() { if(Logging.On)Logging.Enter(Logging.Web, this, "GetRequestStream", ""); if(Logging.On)Logging.PrintInfo(Logging.Web, this, "GetRequestStream", SR.GetString(SR.net_log_method_equal, m_MethodInfo.Method)); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetRequestStream"); try { if (m_GetRequestStreamStarted) { throw new InvalidOperationException(SR.GetString(SR.net_repcall)); } m_GetRequestStreamStarted = true; if (!m_MethodInfo.IsUpload) { throw new ProtocolViolationException(SR.GetString(SR.net_nouploadonget)); } CheckError(); m_StartTime = DateTime.UtcNow; m_RemainingTimeout = Timeout; // We don't really need this variable, but we just need // to call the property to measure its execution time ServicePoint servicePoint = ServicePoint; if (Timeout != System.Threading.Timeout.Infinite) { m_RemainingTimeout = Timeout - (int)((DateTime.UtcNow - m_StartTime).TotalMilliseconds); if(m_RemainingTimeout <= 0){ throw new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout); } } if (ServicePoint.InternalProxyServicePoint) { HttpWebRequest httpWebRequest = GetHttpWebRequest(); if (Logging.On) Logging.Associate(Logging.Web, this, httpWebRequest); m_Stream = httpWebRequest.GetRequestStream(); } else { FinishRequestStage(RequestStage.RequestStarted); SubmitRequest(false); FinishRequestStage(RequestStage.WriteReady); CheckError(); } if(m_Stream.CanTimeout) { m_Stream.WriteTimeout = ReadWriteTimeout; m_Stream.ReadTimeout = ReadWriteTimeout; } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "GetRequestStream", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetRequestStream"); if(Logging.On)Logging.Exit(Logging.Web, this, "GetRequestStream", ""); } return m_Stream; } ///Used to query for the Request stream of an FTP Request ////// [HostProtection(ExternalThreading=true)] public override IAsyncResult BeginGetRequestStream(AsyncCallback callback, object state) { if(Logging.On)Logging.Enter(Logging.Web, this, "BeginGetRequestStream", ""); if(Logging.On)Logging.PrintInfo(Logging.Web, this, "BeginGetRequestStream", SR.GetString(SR.net_log_method_equal, m_MethodInfo.Method)); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::BeginGetRequestStream"); ContextAwareResult asyncResult = null; try { if (m_GetRequestStreamStarted) { throw new InvalidOperationException(SR.GetString(SR.net_repcall)); } m_GetRequestStreamStarted = true; if (!m_MethodInfo.IsUpload) { throw new ProtocolViolationException(SR.GetString(SR.net_nouploadonget)); } CheckError(); if (ServicePoint.InternalProxyServicePoint) { HttpWebRequest httpWebRequest = GetHttpWebRequest(); if (Logging.On) Logging.Associate(Logging.Web, this, httpWebRequest); asyncResult = (ContextAwareResult)httpWebRequest.BeginGetRequestStream(callback, state); } else { FinishRequestStage(RequestStage.RequestStarted); asyncResult = new ContextAwareResult(true, true, this, state, callback); lock (asyncResult.StartPostingAsyncOp()) { m_WriteAsyncResult = asyncResult; SubmitRequest(true); asyncResult.FinishPostingAsyncOp(); FinishRequestStage(RequestStage.CheckForError); } } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "BeginGetRequestStream", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::BeginGetRequestStream"); if(Logging.On)Logging.Exit(Logging.Web, this, "BeginGetRequestStream", ""); } return asyncResult; } public override Stream EndGetRequestStream(IAsyncResult asyncResult) { if(Logging.On)Logging.Enter(Logging.Web, this, "EndGetRequestStream", ""); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetRequestStream"); Stream requestStream = null; try { // parameter validation if (asyncResult==null) { throw new ArgumentNullException("asyncResult"); } LazyAsyncResult castedAsyncResult = asyncResult as LazyAsyncResult; if ((castedAsyncResult==null) || (HttpProxyMode?(castedAsyncResult.AsyncObject!=this.GetHttpWebRequest()):castedAsyncResult.AsyncObject!=this)) { throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); } if (castedAsyncResult.EndCalled) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetResponse")); } if (HttpProxyMode) { requestStream = GetHttpWebRequest().EndGetRequestStream(asyncResult); } else { castedAsyncResult.InternalWaitForCompletion(); castedAsyncResult.EndCalled = true; CheckError(); requestStream = m_Stream; castedAsyncResult.EndCalled = true; } if(requestStream.CanTimeout) { requestStream.WriteTimeout = ReadWriteTimeout; requestStream.ReadTimeout = ReadWriteTimeout; } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "EndGetRequestStream", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetRequestStream"); if(Logging.On)Logging.Exit(Logging.Web, this, "EndGetRequestStream", ""); } return requestStream; } // // NOTE1: The caller must synchronize access to SubmitRequest(), only one call is even allowed for a particular request! // NOTE2: This method eats all exceptions so the caller must rethrow them // private void SubmitRequest(bool async) { try { m_Async = async; if (CheckCacheRetrieveBeforeSubmit()) { RequestCallback(null); return; } // This is the only place touching m_ConnectionPool if (m_ConnectionPool == null) m_ConnectionPool = ConnectionPoolManager.GetConnectionPool(ServicePoint, GetConnectionGroupLine(), m_CreateConnectionCallback); // // FYI: Will do 2 attempts max as per AttemptedRecovery // Stream stream; while(true) { FtpControlStream connection = m_Connection; if (connection == null) { connection = QueueOrCreateConnection(); if (connection == null) return; } if(!async){ if (Timeout != System.Threading.Timeout.Infinite) { m_RemainingTimeout = Timeout - (int)((DateTime.UtcNow - m_StartTime).TotalMilliseconds); if(m_RemainingTimeout <= 0){ throw new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout); } } } GlobalLog.Print("Request being submitted"+ValidationHelper.HashString(this)); connection.SetSocketTimeoutOption(SocketShutdown.Both, RemainingTimeout, false); try { stream = TimedSubmitRequestHelper(async); } catch (Exception e) { if (AttemptedRecovery(e)){ if(!async){ if (Timeout != System.Threading.Timeout.Infinite) { m_RemainingTimeout = Timeout - (int)((DateTime.UtcNow - m_StartTime).TotalMilliseconds); if(m_RemainingTimeout <= 0){ throw; } } } continue; } throw; } // no retry needed break; } } catch (WebException webException) { //if this was a timeout, throw a timeout exception IOException ioEx = webException.InnerException as IOException; if(ioEx != null){ SocketException ---- = ioEx.InnerException as SocketException; if(---- != null){ if (----.ErrorCode == (int)SocketError.TimedOut) { SetException(new WebException(SR.GetString(SR.net_timeout), WebExceptionStatus.Timeout)); } } } SetException(webException); } catch (Exception exception) { SetException(exception); } } // // // private FtpControlStream QueueOrCreateConnection() { FtpControlStream connection = (FtpControlStream) m_ConnectionPool.GetConnection((object)this, (m_Async ? m_AsyncCallback : null), (m_Async ? -1: RemainingTimeout)); if (connection == null) { GlobalLog.Assert(m_Async, "QueueOrCreateConnection|m_ConnectionPool.GetConnection() returned null on a [....] Request."); return null; } lock (m_SyncObject) { if (m_Aborted) { if (Logging.On) Logging.PrintInfo(Logging.Web, this, "", SR.GetString(SR.net_log_releasing_connection, ValidationHelper.HashString(connection))); m_ConnectionPool.PutConnection(connection, this, RemainingTimeout); CheckError(); //must throw throw new InternalException(); } m_Connection = connection; if (Logging.On) Logging.Associate(Logging.Web, this, m_Connection); } return connection; } // // private Stream TimedSubmitRequestHelper(bool async) { if(async) { // non-null in the case of re-submit (recovery) if (m_RequestCompleteAsyncResult == null) m_RequestCompleteAsyncResult = new LazyAsyncResult(null, null, null); return m_Connection.SubmitRequest(this, true, true); } Stream stream = null; bool timedOut = false; TimerThread.Timer timer = TimerQueue.CreateTimer(m_TimerCallback, null); try { stream = m_Connection.SubmitRequest(this, false, true); } catch (Exception exception) { if (!(exception is SocketException || exception is ObjectDisposedException) || !timer.HasExpired) { timer.Cancel(); throw; } timedOut = true; } if (timedOut || !timer.Cancel()) { m_TimedOut = true; throw new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout); } if (stream != null) { lock (m_SyncObject) { if (m_Aborted) { ((ICloseEx)stream).CloseEx(CloseExState.Abort|CloseExState.Silent); CheckError(); //must throw throw new InternalException(); //consider replacing this on Assert } m_Stream = stream; } } return stream; } ///Used to query for the Request stream of an FTP Request [async version] ////// private void TimerCallback(TimerThread.Timer timer, int timeNoticed, object context) { GlobalLog.Print("FtpWebRequest#" + ValidationHelper.HashString(this) + "::TimerCallback"); FtpControlStream connection = m_Connection; if (connection != null) { GlobalLog.Print("FtpWebRequest#" + ValidationHelper.HashString(this) + "::TimerCallback aborting connection"); connection.AbortConnect(); } } private TimerThread.Queue TimerQueue { get { if (m_TimerQueue == null) { m_TimerQueue = TimerThread.GetOrCreateQueue(RemainingTimeout); } return m_TimerQueue; } } ///Because this is called from the timer thread, neither it nor any methods it calls can call user code. ////// private bool AttemptedRecovery(Exception e) { // The first 'if' is just checking whether the exception is thrown due to the // relogin failure which is a recoverable error if (!(e is WebException && ((WebException)e).InternalStatus == WebExceptionInternalStatus.Isolated)) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException || m_OnceFailed || m_Aborted || m_TimedOut || m_Connection==null || !m_Connection.RecoverableFailure) { return false; } m_OnceFailed = true; } lock (m_SyncObject) { if (m_ConnectionPool != null && m_Connection != null) { m_Connection.CloseSocket(); if (Logging.On) Logging.PrintInfo(Logging.Web, this, "", SR.GetString(SR.net_log_releasing_connection, ValidationHelper.HashString(m_Connection))); m_ConnectionPool.PutConnection(m_Connection, this, RemainingTimeout); m_Connection = null; } else { return false; } } return true; } ///Returns true if we should restart the request after an error ////// private void SetException(Exception exception) { GlobalLog.Print("FtpWebRequest#" + ValidationHelper.HashString(this) + "::SetException"); if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) { m_Exception = exception; throw exception; } FtpControlStream connection = m_Connection; if (m_Exception == null) { if (exception is WebException) { EnsureFtpWebResponse(exception); m_Exception = new WebException(exception.Message, null, ((WebException)exception).Status, m_FtpWebResponse); } else if (exception is AuthenticationException || exception is SecurityException) { m_Exception = exception; } else if (connection!= null && connection.StatusCode != FtpStatusCode.Undefined) { EnsureFtpWebResponse(exception); m_Exception = new WebException(SR.GetString(SR.net_servererror, connection.StatusLine), exception, WebExceptionStatus.ProtocolError, m_FtpWebResponse); } else { m_Exception = new WebException(exception.Message, exception); } if (connection != null && m_FtpWebResponse != null) m_FtpWebResponse.UpdateStatus(connection.StatusCode, connection.StatusLine, connection.ExitMessage); } } ///Updates and sets our exception to be thrown ////// private void CheckError() { if (m_Exception != null) { throw m_Exception; } } // Return null only on [....] (if we're on the [....] thread). Otherwise throw if no context is available. // // internal override ContextAwareResult GetWritingContext() { if (m_ReadAsyncResult != null && m_ReadAsyncResult is ContextAwareResult) return (ContextAwareResult)m_ReadAsyncResult; else if (m_WriteAsyncResult != null) return m_WriteAsyncResult; // [....]. GlobalLog.ThreadContract(ThreadKinds.User | ThreadKinds.[....], "FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetWritingContext"); return null; } // // Provides an abstract way of having Async code callback into the request (saves a delegate) // // ATTN this method is also called on [....] path when either command or data stream gets closed // Consider: Revisit the design of ftp streams // internal override void RequestCallback(object obj) { if (m_Async) AsyncRequestCallback(obj); else SyncRequestCallback(obj); } // // Only executed for [....] requests when the pipline is completed // private void SyncRequestCallback(object obj) { GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::SyncRequestCallback", "#"+ValidationHelper.HashString(obj)); RequestStage stageMode = RequestStage.CheckForError; try { bool completedRequest = obj == null; Exception exception = obj as Exception; GlobalLog.Print("SyncRequestCallback() exp:"+ValidationHelper.HashString(exception)+" completedRequest:"+ValidationHelper.HashString(completedRequest)); if (exception != null) { SetException(exception); } else if (!completedRequest) { throw new InternalException(); } else { // a signal on current pipeline completion FtpControlStream connection = m_Connection; bool isRevalidatedOrRetried = false; if (connection != null) { EnsureFtpWebResponse(null); // This to update response status and exit message if any // Note that due to a design of FtpControlStream the status 221 "Service closing control connection" is always suppresses. m_FtpWebResponse.UpdateStatus(connection.StatusCode, connection.StatusLine, connection.ExitMessage); isRevalidatedOrRetried =!m_CacheDone && (CacheProtocol.ProtocolStatus == CacheValidationStatus.Continue || CacheProtocol.ProtocolStatus == CacheValidationStatus.RetryResponseFromServer); // This is for [....] Upload commands that do not get chance hit GetResponse loop if (m_MethodInfo.IsUpload) { CheckCacheRetrieveOnResponse(); CheckCacheUpdateOnResponse(); } } if (!isRevalidatedOrRetried) stageMode = RequestStage.ReleaseConnection; } } catch (Exception exception) { SetException(exception); } finally { FinishRequestStage(stageMode); GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::SyncRequestCallback"); CheckError(); //will throw on error } } // // Only executed for Async requests // private void AsyncRequestCallback(object obj) { GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::AsyncRequestCallback", "#"+ValidationHelper.HashString(obj)); RequestStage stageMode = RequestStage.CheckForError; try { FtpControlStream connection; connection = obj as FtpControlStream; FtpDataStream stream = obj as FtpDataStream; Exception exception = obj as Exception; bool completedRequest = (obj == null); GlobalLog.Print("AsyncRequestCallback() stream:"+ValidationHelper.HashString(stream)+" conn:"+ValidationHelper.HashString(connection)+" exp:"+ValidationHelper.HashString(exception)+" completedRequest:"+ValidationHelper.HashString(completedRequest)); while (true) { if (exception != null) { if (AttemptedRecovery(exception)) { connection = QueueOrCreateConnection(); if (connection == null) return; exception = null; } if (exception != null) { SetException(exception); break; } } if (connection != null) { lock(m_SyncObject) { if (m_Aborted) { if (Logging.On) Logging.PrintInfo(Logging.Web, this, "", SR.GetString(SR.net_log_releasing_connection, ValidationHelper.HashString(connection))); m_ConnectionPool.PutConnection(connection, this, Timeout); break; } m_Connection = connection; if (Logging.On) Logging.Associate(Logging.Web, this, m_Connection); } try { stream = (FtpDataStream)TimedSubmitRequestHelper(true); } catch (Exception e) { exception = e; continue; } return; } else if (stream != null) { lock (m_SyncObject) { if (m_Aborted) { ((ICloseEx)stream).CloseEx(CloseExState.Abort|CloseExState.Silent); break; } m_Stream = stream; } stream.SetSocketTimeoutOption(SocketShutdown.Both, Timeout, true); EnsureFtpWebResponse(null); // This one may update the Stream member on m_FtpWebResponse based on the CacheProtocol feedback. CheckCacheRetrieveOnResponse(); CheckCacheUpdateOnResponse(); stageMode = stream.CanRead? RequestStage.ReadReady: RequestStage.WriteReady; } else if (completedRequest) { connection = m_Connection; bool isRevalidatedOrRetried = false; if (connection != null) { EnsureFtpWebResponse(null); // This to update response status and exit message if any // Note that due to a design of FtpControlStream the status 221 "Service closing control connection" is always suppresses. m_FtpWebResponse.UpdateStatus(connection.StatusCode, connection.StatusLine, connection.ExitMessage); isRevalidatedOrRetried =!m_CacheDone && (CacheProtocol.ProtocolStatus == CacheValidationStatus.Continue || CacheProtocol.ProtocolStatus == CacheValidationStatus.RetryResponseFromServer); lock (m_SyncObject) { if(!CheckCacheRetrieveOnResponse()) continue; if (m_FtpWebResponse.IsFromCache) isRevalidatedOrRetried = false; CheckCacheUpdateOnResponse(); } } if (!isRevalidatedOrRetried) stageMode = RequestStage.ReleaseConnection; } else { throw new InternalException(); } break; } } catch (Exception exception) { SetException(exception); } finally { FinishRequestStage(stageMode); GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::AsyncRequestCallback"); } } // // // private enum RequestStage { CheckForError = 0,// Do nothing except if there is an error then auto promote to ReleaseConnection RequestStarted, // Mark this request as started WriteReady, // First half is done, i.e. either writer or response stream. This is always assumed unless Started or CheckForError ReadReady, // Second half is done, i.e. the read stream can be accesses. ReleaseConnection // Release the control connection (request is read i.e. done-done) } // // Returns a previous stage // private RequestStage FinishRequestStage(RequestStage stage) { GlobalLog.Print("FtpWebRequest#" + ValidationHelper.HashString(this) + "::FinishRequestStage : stage="+stage); if (m_Exception != null) stage = RequestStage.ReleaseConnection; RequestStage prev; LazyAsyncResult writeResult; LazyAsyncResult readResult; FtpControlStream connection; lock (m_SyncObject) { prev = m_RequestStage; if (stage == RequestStage.CheckForError) return prev; if (prev == RequestStage.ReleaseConnection && stage == RequestStage.ReleaseConnection) { return RequestStage.ReleaseConnection; } if (stage > prev) m_RequestStage = stage; if (stage <= RequestStage.RequestStarted) return prev; writeResult = m_WriteAsyncResult; readResult = m_ReadAsyncResult; connection = m_Connection; if (stage == RequestStage.ReleaseConnection) { if (m_Exception == null && !m_Aborted && prev != RequestStage.ReadReady && this.m_MethodInfo.IsDownload && !m_FtpWebResponse.IsFromCache) { return prev; } if (m_Exception != null || !(m_FtpWebResponse.IsFromCache && !KeepAlive)) m_Connection = null; } } try { // First check to see on releasing the connection if ((stage == RequestStage.ReleaseConnection || prev == RequestStage.ReleaseConnection) && connection != null) { try { if (m_Exception != null) { connection.Abort(m_Exception); } else if (m_FtpWebResponse.IsFromCache && !KeepAlive) { // This means the response has been revalidated and found as good means the commands pipleline is completed. // Now if this request was NOT KeepAlive we want to be fair and close the control connection. // That becomes unnecessary complicated in the async case to support "QUIT" command semantic, so simply close the socket // and let pool collect the object. connection.Quit(); } } finally { if (Logging.On) Logging.PrintInfo(Logging.Web, this, "", SR.GetString(SR.net_log_releasing_connection, ValidationHelper.HashString(connection))); m_ConnectionPool.PutConnection(connection, this, RemainingTimeout); if (m_Async) if (m_RequestCompleteAsyncResult != null) m_RequestCompleteAsyncResult.InvokeCallback(); } } return prev; } finally { try { // In any case we want to signal the writer if came here if (stage >= RequestStage.WriteReady) { // If writeResult == null and this is an upload request, it means // that the user has called GetResponse() without calling // GetRequestStream() first. So they are not interested in a // stream. Therefore we close the stream so that the // request/pipeline can continue if (m_MethodInfo.IsUpload && !m_GetRequestStreamStarted) { if (m_Stream != null) m_Stream.Close(); } else if (writeResult != null && !writeResult.InternalPeekCompleted) writeResult.InvokeCallback(); } } finally { // The response is ready either with or without a stream if (stage >= RequestStage.ReadReady && readResult != null && !readResult.InternalPeekCompleted) readResult.InvokeCallback(); } } } // // Used only in the async case and only for the initial callback from the pool when connection is established. // private static void AsyncCallbackWrapper(object request, object state) { FtpWebRequest ftpWebRequest = (FtpWebRequest) request; ftpWebRequest.RequestCallback(state); } ///Opposite of SetException, rethrows the exception ////// private static PooledStream CreateFtpConnection(ConnectionPool pool) { return (PooledStream) new FtpControlStream(pool, TimeSpan.MaxValue, false); } ///builds networkStream from Socket ////// public override void Abort() { if (m_Aborted) return; if(Logging.On)Logging.Enter(Logging.Web, this, "Abort", ""); try { GlobalLog.Print("FtpWebRequest#"+ValidationHelper.HashString(this)+"::Abort()"); if (HttpProxyMode) { GetHttpWebRequest().Abort(); return; } if (CacheProtocol != null) CacheProtocol.Abort(); Stream stream; FtpControlStream connection; lock (m_SyncObject) { if (m_RequestStage >= RequestStage.ReleaseConnection) return; m_Aborted = true; stream = m_Stream; connection = m_Connection; m_Exception = new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled) ; } if (stream != null) { GlobalLog.Assert(stream is ICloseEx, "FtpWebRequest.Abort()|The m_Stream member is not CloseEx hence the risk of connection been orphaned."); ((ICloseEx)stream).CloseEx(CloseExState.Abort|CloseExState.Silent); } if (connection != null) connection.Abort(ExceptionHelper.RequestAbortedException); } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "Abort", exception); throw; } finally { if(Logging.On)Logging.Exit(Logging.Web, this, "Abort", ""); } } ///Aborts underlying connection to FTP server (command & data) ////// /// public bool KeepAlive { get { return m_KeepAlive; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_KeepAlive = value; } } ////// If KeepAlive is set to false, then the control connection to the server will be closed when the request completes. /// Default is true /// ////// public bool UseBinary { get { return m_Binary; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_Binary = value; } } ///True by default, false allows transmission using text mode ////// public bool UsePassive { get { return m_Passive; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_Passive = value; } } #if !FEATURE_PAL ///False by default, true enables passive mode communication with server. /// This alters the way the client talks with the server, allowing the client /// to initiate the data connection with the server ////// public X509CertificateCollection ClientCertificates { get { if (m_ClientCertificates == null) { lock (m_SyncObject) { if (m_ClientCertificates == null) { m_ClientCertificates = new X509CertificateCollection(); } } } return m_ClientCertificates; } set { if (value==null) { throw new ArgumentNullException("value"); } m_ClientCertificates = value; } } #endif // !FEATURE_PAL ////// ClientCertificates - sets our certs for our reqest, /// uses a hash of the collection to create a private connection /// group, to prevent us from using the same Connection as /// non-Client Authenticated requests. /// ////// public bool EnableSsl { get { return m_EnableSsl; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_EnableSsl = value; } } ///Set to true if we need SSL ////// public override WebHeaderCollection Headers { get { if (HttpProxyMode) { return GetHttpWebRequest().Headers; } if (m_FtpRequestHeaders == null) { m_FtpRequestHeaders = new WebHeaderCollection(WebHeaderCollectionType.FtpWebRequest); } return m_FtpRequestHeaders; } set { if (HttpProxyMode) { GetHttpWebRequest().Headers = value; } m_FtpRequestHeaders = value; } } // NOT SUPPORTED method public override string ContentType { get { throw ExceptionHelper.PropertyNotSupportedException; } set { throw ExceptionHelper.PropertyNotSupportedException; } } // NOT SUPPORTED method public override bool UseDefaultCredentials { get { throw ExceptionHelper.PropertyNotSupportedException; } set { throw ExceptionHelper.PropertyNotSupportedException; } } // NOT SUPPORTED method public override bool PreAuthenticate { get { throw ExceptionHelper.PropertyNotSupportedException; } set { throw ExceptionHelper.PropertyNotSupportedException; } } ////// A collection of headers, currently nothing is return except an empty collection /// ////// private bool InUse { get { if (m_GetRequestStreamStarted || m_GetResponseStarted) { return true; } else { return false; } } } ///True if a request has been submitted (ie already active) ////// private bool HttpProxyMode { get { return (m_HttpWebRequest != null); } } ///True if request is just wrapping HttpWebRequest ////// private void EnsureFtpWebResponse(Exception exception) { if (m_FtpWebResponse == null || (m_FtpWebResponse.GetResponseStream() is FtpWebResponse.EmptyStream && m_Stream != null)) { lock (m_SyncObject) { if (m_FtpWebResponse == null || (m_FtpWebResponse.GetResponseStream() is FtpWebResponse.EmptyStream && m_Stream != null)) { Stream responseStream = m_Stream; if (m_MethodInfo.IsUpload) { responseStream = null; } if(m_Stream != null && m_Stream.CanRead && m_Stream.CanTimeout) { m_Stream.ReadTimeout = ReadWriteTimeout; m_Stream.WriteTimeout = ReadWriteTimeout; } FtpControlStream connection = m_Connection; long contentLength = connection != null? connection.ContentLength: -1; if (responseStream == null) { // If the last command was SIZE, we set the ContentLength on // the FtpControlStream to be the size of the file returned in the // response. We should propagate that file size to the response so // users can access it. This also maintains the compatibility with // HTTP when returning size instead of content. if (contentLength < 0) contentLength = 0; } if (m_FtpWebResponse != null) { m_FtpWebResponse.SetResponseStream(responseStream); } else { if (connection != null) m_FtpWebResponse = new FtpWebResponse(responseStream, contentLength, connection.ResponseUri, connection.StatusCode, connection.StatusLine, connection.LastModified, connection.BannerMessage, connection.WelcomeMessage, connection.ExitMessage); else m_FtpWebResponse = new FtpWebResponse(responseStream, -1, m_Uri, FtpStatusCode.Undefined, null, DateTime.Now, null, null, null); } } } } GlobalLog.Print("FtpWebRequest#"+ValidationHelper.HashString(this)+"::EnsureFtpWebResponse returns #"+ValidationHelper.HashString(m_FtpWebResponse)+" with stream#"+ValidationHelper.HashString(m_FtpWebResponse.m_ResponseStream)); return; } ///Creates an FTP WebResponse based off the responseStream and our active Connection ////// private HttpWebRequest GetHttpWebRequest() { lock (m_SyncObject) { if (m_HttpWebRequest == null) { if (m_ContentOffset > 0) { throw new InvalidOperationException(SR.GetString(SR.net_ftp_no_offsetforhttp)); } if (!m_MethodInfo.HasHttpCommand) throw new InvalidOperationException(SR.GetString(SR.net_ftp_no_http_cmd)); m_HttpWebRequest = new HttpWebRequest(m_Uri, ServicePoint); m_HttpWebRequest.Credentials = Credentials; m_HttpWebRequest.InternalProxy = m_Proxy; m_HttpWebRequest.KeepAlive = KeepAlive; m_HttpWebRequest.Timeout = Timeout; m_HttpWebRequest.Method = m_MethodInfo.HttpCommand; m_HttpWebRequest.CacheProtocol = CacheProtocol; RequestCacheLevel effectiveLevel; if (CachePolicy == null) effectiveLevel = RequestCacheLevel.BypassCache; else effectiveLevel = CachePolicy.Level; // Cannot support revalidate through the proxy if (effectiveLevel == RequestCacheLevel.Revalidate) effectiveLevel = RequestCacheLevel.Reload; m_HttpWebRequest.CachePolicy = new HttpRequestCachePolicy((HttpRequestCacheLevel)effectiveLevel); //disable cache protocol on that class since we are proxying through HTTP CacheProtocol = null; } } return m_HttpWebRequest; } ///Creates a HttpWebRequest ////// private string GetConnectionGroupLine() { GlobalLog.Print("GetConnectionGroupLine"); return ConnectionGroupName + "_" + GetUserString(); } ///Generates a string that /// allows a Connection to remain unique for user /// this is needed to prevent multiple users from /// using the same sockets after they mess with things ////// internal string GetUserString() { string name = null; if (this.Credentials != null) { NetworkCredential networkCreds = this.Credentials.GetCredential(m_Uri, "basic"); if (networkCreds != null) { name = networkCreds.InternalGetUserName(); string domain = networkCreds.InternalGetDomain(); if (!ValidationHelper.IsBlankString(domain)) { name = domain+"\\"+name; } } } return name == null? null: (String.Compare(name,"anonymous", StringComparison.InvariantCultureIgnoreCase) == 0? null: name); } // // This method may be invoked as part of the request submission but // before the response is received // Return: // - True = Use CacheProtocol properties to create the cached response // - False = Proceed with the request submission private bool CheckCacheRetrieveBeforeSubmit() { if (CacheProtocol == null || m_CacheDone) { m_CacheDone = true; return false; } if (CacheProtocol.ProtocolStatus == CacheValidationStatus.CombineCachedAndServerResponse || CacheProtocol.ProtocolStatus == CacheValidationStatus.DoNotTakeFromCache) { // Re-entry into a new pipeline on failed revalidate or combining cached and live streams return false; } Uri cacheUri = RequestUri; string username = GetUserString(); if(username != null) username = Uri.EscapeDataString(username); if (cacheUri.Fragment.Length != 0 || username != null) { if (username == null) cacheUri = new Uri(cacheUri.GetParts(UriComponents.AbsoluteUri & ~(UriComponents.Fragment|UriComponents.UserInfo), UriFormat.SafeUnescaped)); else { username = cacheUri.GetParts((UriComponents.Scheme | UriComponents.KeepDelimiter), UriFormat.SafeUnescaped) + username + '@'; username += cacheUri.GetParts((UriComponents.Host | UriComponents.Port | UriComponents.Path | UriComponents.Query), UriFormat.SafeUnescaped); cacheUri = new Uri(username); } } CacheProtocol.GetRetrieveStatus(cacheUri, this); if (CacheProtocol.ProtocolStatus == CacheValidationStatus.Fail) { throw CacheProtocol.ProtocolException; } if (CacheProtocol.ProtocolStatus != CacheValidationStatus.ReturnCachedResponse) { return false; } if (m_MethodInfo.Operation != FtpOperation.DownloadFile) { throw new NotSupportedException(SR.GetString(SR.net_cache_not_supported_command)); } if (CacheProtocol.ProtocolStatus == CacheValidationStatus.ReturnCachedResponse) { // If we take it from cache, we have to kick in response processing // The _CacheStream is good to return as the response stream. FtpRequestCacheValidator ctx = (FtpRequestCacheValidator) CacheProtocol.Validator; m_FtpWebResponse = new FtpWebResponse(CacheProtocol.ResponseStream, CacheProtocol.ResponseStreamLength, RequestUri, UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData, (UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData).ToString(), ctx.CacheEntry.LastModifiedUtc == DateTime.MinValue? DateTime.Now: ctx.CacheEntry.LastModifiedUtc.ToLocalTime(), string.Empty, string.Empty, string.Empty); m_FtpWebResponse.InternalSetFromCache = true; m_FtpWebResponse.InternalSetIsCacheFresh = (ctx.CacheFreshnessStatus != CacheFreshnessStatus.Stale); } return true; } // // This method has to be invoked as part of the wire response processing. // The wire response can be replaced on return // // ATTN: If the method returns false, the response is invalid and should be retried // private bool CheckCacheRetrieveOnResponse() { if (CacheProtocol == null || m_CacheDone) { return true; } if (CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue) { // cache has been already revalidated proceed with cache update return true; } if (CacheProtocol.ProtocolStatus == CacheValidationStatus.Fail) { if(Logging.On)Logging.Exception(Logging.Web, this, "CheckCacheRetrieveOnResponse", CacheProtocol.ProtocolException); throw CacheProtocol.ProtocolException; } // At this point we dont have the real data stream, hence passing null and updating it later CacheProtocol.GetRevalidateStatus(m_FtpWebResponse, null); if (CacheProtocol.ProtocolStatus == CacheValidationStatus.RetryResponseFromServer) { if (m_FtpWebResponse != null) m_FtpWebResponse.SetResponseStream(null); //prevent from advancing commands pipeline // Try to resubmit or fail return false; } if (CacheProtocol.ProtocolStatus != CacheValidationStatus.ReturnCachedResponse) { // Proceed with the requesting the real server data stream return false; } if (m_MethodInfo.Operation != FtpOperation.DownloadFile) { // This should never happen in real life throw new NotSupportedException(SR.GetString(SR.net_cache_not_supported_command)); } FtpRequestCacheValidator ctx = (FtpRequestCacheValidator) CacheProtocol.Validator; FtpWebResponse oldResponse = m_FtpWebResponse; m_Stream = CacheProtocol.ResponseStream; m_FtpWebResponse = new FtpWebResponse(CacheProtocol.ResponseStream, CacheProtocol.ResponseStreamLength, RequestUri, UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData, (UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData).ToString(), ctx.CacheEntry.LastModifiedUtc == DateTime.MinValue? DateTime.Now: ctx.CacheEntry.LastModifiedUtc.ToLocalTime(), string.Empty, string.Empty, string.Empty); m_FtpWebResponse.InternalSetFromCache = true; m_FtpWebResponse.InternalSetIsCacheFresh = CacheProtocol.IsCacheFresh; oldResponse.Close(); return true; } // // This will decide on cache update and construct the effective response stream // private void CheckCacheUpdateOnResponse() { if (CacheProtocol == null || m_CacheDone) { return; } m_CacheDone = true; if (m_Connection != null) { m_FtpWebResponse.UpdateStatus(m_Connection.StatusCode, m_Connection.StatusLine, m_Connection.ExitMessage); if (m_Connection.StatusCode == FtpStatusCode.OpeningData && m_FtpWebResponse.ContentLength == 0) m_FtpWebResponse.SetContentLength(m_Connection.ContentLength); } if (CacheProtocol.ProtocolStatus == CacheValidationStatus.CombineCachedAndServerResponse) { // Note we already asked for a file restart // The only problem is that we could not create the combined stream sooner. m_Stream = new CombinedReadStream(CacheProtocol.Validator.CacheStream, m_FtpWebResponse.GetResponseStream()); // // For consistent user experience we always supply DataAlreadyOpen status for a cached response. // FtpStatusCode rightStatus = UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData; m_FtpWebResponse.UpdateStatus(rightStatus, rightStatus.ToString(), string.Empty); m_FtpWebResponse.SetResponseStream(m_Stream); } if (CacheProtocol.GetUpdateStatus(m_FtpWebResponse, m_FtpWebResponse.GetResponseStream()) == CacheValidationStatus.UpdateResponseInformation) { m_Stream = CacheProtocol.ResponseStream; m_FtpWebResponse.SetResponseStream(m_Stream); } else if (CacheProtocol.ProtocolStatus == CacheValidationStatus.Fail) throw CacheProtocol.ProtocolException; } internal void DataStreamClosed(CloseExState closeState) { if ((closeState & CloseExState.Abort) == 0) { if (!m_Async) { if (m_Connection != null) m_Connection.CheckContinuePipeline(); } else { m_RequestCompleteAsyncResult.InternalWaitForCompletion(); CheckError(); } } else { FtpControlStream connection = m_Connection; if (connection != null) connection.Abort(ExceptionHelper.RequestAbortedException); } } } // class FtpWebRequest // // Class used by the WebRequest.Create factory to create FTP requests // internal class FtpWebRequestCreator : IWebRequestCreate { internal FtpWebRequestCreator() { } public WebRequest Create(Uri uri) { return new FtpWebRequest(uri); } } // class FtpWebRequestCreator } //namespace System.Net // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // ------------------------------------------------------------------------------ //Returns username string ///// Copyright (c) Microsoft Corporation. All rights reserved. // // ----------------------------------------------------------------------------- // namespace System.Net { using System.Collections; using System.IO; using System.Text; using System.Net.Sockets; using System.Net.Cache; using System.Threading; using System.Security; using System.Security.Cryptography.X509Certificates ; using System.Security.Permissions; using System.Security.Authentication; using System.Globalization; ////// internal enum FtpOperation { DownloadFile = 0, ListDirectory = 1, ListDirectoryDetails = 2, UploadFile = 3, UploadFileUnique = 4, AppendFile = 5, DeleteFile = 6, GetDateTimestamp = 7, GetFileSize = 8, Rename = 9, MakeDirectory = 10, RemoveDirectory = 11, PrintWorkingDirectory = 12, Other = 13, } [Flags] internal enum FtpMethodFlags { None = 0x0, IsDownload = 0x1, IsUpload = 0x2, TakesParameter = 0x4, MayTakeParameter = 0x8, DoesNotTakeParameter = 0x10, ParameterIsDirectory = 0x20, ShouldParseForResponseUri = 0x40, HasHttpCommand = 0x80, MustChangeWorkingDirectoryToPath = 0x100 } internal class FtpMethodInfo { internal string Method; internal FtpOperation Operation; internal FtpMethodFlags Flags; internal string HttpCommand; internal FtpMethodInfo(string method, FtpOperation operation, FtpMethodFlags flags, string httpCommand) { Method = method; Operation = operation; Flags = flags; HttpCommand = httpCommand; } internal bool HasFlag(FtpMethodFlags flags) { return (Flags & flags) != 0; } internal bool IsCommandOnly { get { return (Flags & (FtpMethodFlags.IsDownload | FtpMethodFlags.IsUpload)) == 0; } } internal bool IsUpload { get { return (Flags & FtpMethodFlags.IsUpload) != 0; } } internal bool IsDownload { get { return (Flags & FtpMethodFlags.IsDownload) != 0; } } internal bool HasHttpCommand { get { return (Flags & FtpMethodFlags.HasHttpCommand) != 0; } } ///Allows us to control what the request is used for (based on the type of behavior, /// that the command calls for) ////// internal bool ShouldParseForResponseUri { get { return (Flags & FtpMethodFlags.ShouldParseForResponseUri) != 0; } } internal static FtpMethodInfo GetMethodInfo(string method) { method = method.ToUpper(CultureInfo.InvariantCulture); foreach (FtpMethodInfo methodInfo in KnownMethodInfo) if (method == methodInfo.Method) return methodInfo; // We don't support generic methods throw new ArgumentException(SR.GetString(SR.net_ftp_unsupported_method), "method"); } static readonly FtpMethodInfo[] KnownMethodInfo = { new FtpMethodInfo(WebRequestMethods.Ftp.DownloadFile, FtpOperation.DownloadFile, FtpMethodFlags.IsDownload | FtpMethodFlags.HasHttpCommand | FtpMethodFlags.TakesParameter, "GET"), new FtpMethodInfo(WebRequestMethods.Ftp.ListDirectory, FtpOperation.ListDirectory, FtpMethodFlags.IsDownload | FtpMethodFlags.MustChangeWorkingDirectoryToPath | FtpMethodFlags.HasHttpCommand | FtpMethodFlags.MayTakeParameter, "GET"), new FtpMethodInfo(WebRequestMethods.Ftp.ListDirectoryDetails, FtpOperation.ListDirectoryDetails, FtpMethodFlags.IsDownload | FtpMethodFlags.MustChangeWorkingDirectoryToPath | FtpMethodFlags.HasHttpCommand | FtpMethodFlags.MayTakeParameter, "GET"), new FtpMethodInfo(WebRequestMethods.Ftp.UploadFile, FtpOperation.UploadFile, FtpMethodFlags.IsUpload | FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.UploadFileWithUniqueName, FtpOperation.UploadFileUnique, FtpMethodFlags.IsUpload | FtpMethodFlags.MustChangeWorkingDirectoryToPath | FtpMethodFlags.DoesNotTakeParameter | FtpMethodFlags.ShouldParseForResponseUri, null), new FtpMethodInfo(WebRequestMethods.Ftp.AppendFile, FtpOperation.AppendFile, FtpMethodFlags.IsUpload | FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.DeleteFile, FtpOperation.DeleteFile, FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.GetDateTimestamp, FtpOperation.GetDateTimestamp, FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.GetFileSize, FtpOperation.GetFileSize, FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.Rename, FtpOperation.Rename, FtpMethodFlags.TakesParameter, null), new FtpMethodInfo(WebRequestMethods.Ftp.MakeDirectory, FtpOperation.MakeDirectory, FtpMethodFlags.TakesParameter | FtpMethodFlags.ParameterIsDirectory, null), new FtpMethodInfo(WebRequestMethods.Ftp.RemoveDirectory, FtpOperation.RemoveDirectory, FtpMethodFlags.TakesParameter | FtpMethodFlags.ParameterIsDirectory, null), new FtpMethodInfo(WebRequestMethods.Ftp.PrintWorkingDirectory, FtpOperation.PrintWorkingDirectory, FtpMethodFlags.DoesNotTakeParameter, null) }; } ///True if we should attempt to get a response uri /// out of a server response ////// public sealed class FtpWebRequest : WebRequest { private object m_SyncObject; private ICredentials m_AuthInfo; private readonly Uri m_Uri; private FtpMethodInfo m_MethodInfo; private string m_RenameTo = null; private bool m_GetRequestStreamStarted; private bool m_GetResponseStarted; private DateTime m_StartTime; private int m_Timeout = s_DefaultTimeout; private int m_RemainingTimeout; private long m_ContentLength = 0; private long m_ContentOffset = 0; private IWebProxy m_Proxy; #if !FEATURE_PAL private X509CertificateCollection m_ClientCertificates; #endif // !FEATURE_PAL private bool m_KeepAlive = true; private bool m_Passive = true; private bool m_Binary = true; private string m_ConnectionGroupName; private ServicePoint m_ServicePoint; private bool m_CacheDone; // Not sure why but the command stream wants to notify the request on every pipiline closure closure by invoking RequestCallback. // m_CacheDone is to facilitate PutConnection decision and to prevent bothering cache protocol when it's all completed. private bool m_Async; private bool m_Aborted; private bool m_TimedOut; private HttpWebRequest m_HttpWebRequest; private Exception m_Exception; private TimerThread.Queue m_TimerQueue = s_DefaultTimerQueue; private TimerThread.Callback m_TimerCallback; private bool m_EnableSsl; private bool m_ProxyUserSet; private ConnectionPool m_ConnectionPool; private FtpControlStream m_Connection; private Stream m_Stream; private RequestStage m_RequestStage; private bool m_OnceFailed; private WebHeaderCollection m_FtpRequestHeaders; private FtpWebResponse m_FtpWebResponse; private int m_ReadWriteTimeout = 5*60*1000; //5 minutes. private ContextAwareResult m_WriteAsyncResult; private LazyAsyncResult m_ReadAsyncResult; private LazyAsyncResult m_RequestCompleteAsyncResult; private static readonly GeneralAsyncDelegate m_AsyncCallback = new GeneralAsyncDelegate(AsyncCallbackWrapper); private static readonly CreateConnectionDelegate m_CreateConnectionCallback = new CreateConnectionDelegate(CreateFtpConnection); private static readonly NetworkCredential DefaultFtpNetworkCredential = new NetworkCredential("anonymous", "anonymous@", String.Empty); private static readonly int s_DefaultTimeout = WebRequest.DefaultTimeout; private static readonly TimerThread.Queue s_DefaultTimerQueue = TimerThread.GetOrCreateQueue(s_DefaultTimeout); // Used by FtpControlStream internal FtpMethodInfo MethodInfo { get { return m_MethodInfo; } } // Used by FtpControlStream internal static NetworkCredential DefaultNetworkCredential{ get { return DefaultFtpNetworkCredential; } } // This is a shortcut that would set the default policy for HTTP/HTTPS. // The default policy is overridden by any prefix-registered policy. // Will demand permission for set{} public static new RequestCachePolicy DefaultCachePolicy { get { RequestCachePolicy policy = RequestCacheManager.GetBinding(Uri.UriSchemeFtp).Policy; if (policy == null) return WebRequest.DefaultCachePolicy; return policy; } set { // This is a replacement of RequestCachePermission demand since we are not including the latest in the product. ExceptionHelper.WebPermissionUnrestricted.Demand(); RequestCacheBinding binding = RequestCacheManager.GetBinding(Uri.UriSchemeFtp); RequestCacheManager.SetBinding(Uri.UriSchemeFtp, new RequestCacheBinding(binding.Cache, binding.Validator, value)); } } ///The FtpWebRequest class implements a basic FTP client /// interface. ////// public override string Method { get { return m_MethodInfo.Method; } set { if (String.IsNullOrEmpty(value)) { throw new ArgumentException(SR.GetString(SR.net_ftp_invalid_method_name), "value"); } if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } try { m_MethodInfo = FtpMethodInfo.GetMethodInfo(value); } catch (ArgumentException) { throw new ArgumentException(SR.GetString(SR.net_ftp_unsupported_method), "value"); } } } ////// Selects upload or download of files. WebRequestMethods.Ftp.DownloadFile is default. /// Not allowed to be changed once request is started. /// ////// public string RenameTo { get { return m_RenameTo; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (String.IsNullOrEmpty(value)) { throw new ArgumentException(SR.GetString(SR.net_ftp_invalid_renameto), "value"); } m_RenameTo = value; } } ////// Sets the target name for the WebRequestMethods.Ftp.Rename command /// Not allowed to be changed once request is started. /// ////// public override ICredentials Credentials { get { return m_AuthInfo; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (value == null) { throw new ArgumentNullException("value"); } if (value is SystemNetworkCredential) { throw new ArgumentException(SR.GetString(SR.net_ftp_no_defaultcreds), "value"); } m_AuthInfo = value; } } ///Used for clear text authentication with FTP server ////// public override Uri RequestUri { get { return m_Uri; } } ///Gets the Uri used to make the request ////// public override int Timeout { get { return m_Timeout; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (value<0 && value!=System.Threading.Timeout.Infinite) { throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_io_timeout_use_ge_zero)); } if (m_Timeout != value) { m_Timeout = value; m_TimerQueue = null; } } } // This can always be calculaed as Remaining = Timeout - (Now - Start) // but we are keeping this for performance reasons (To avoid unnecessary // calculations). This can be removed if the performance gains are // considered negligible and not necessary internal int RemainingTimeout { get { return m_RemainingTimeout; } } ///Timeout of the blocking calls such as GetResponse and GetRequestStream (default 100 secs) ////// public int ReadWriteTimeout { get { return m_ReadWriteTimeout; } set { if (m_GetResponseStarted) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (value<=0 && value!=System.Threading.Timeout.Infinite) { throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_io_timeout_use_gt_zero)); } m_ReadWriteTimeout = value; } } ///Used to control the Timeout when calling Stream.Read (AND) Stream.Write. /// Effects Streams returned from GetResponse().GetResponseStream() (AND) GetRequestStream(). /// Default is 5 mins. /// ////// public long ContentOffset { get { return m_ContentOffset; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } if (value<0) { throw new ArgumentOutOfRangeException("value"); } m_ContentOffset = value; } } ///Used to specify what offset we will read at ////// public override long ContentLength { get { return m_ContentLength; } set { m_ContentLength = value; } } ///Gets or sets the data size of to-be uploaded data ////// public override IWebProxy Proxy { get { ExceptionHelper.WebPermissionUnrestricted.Demand(); return m_Proxy; } set { ExceptionHelper.WebPermissionUnrestricted.Demand(); if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_ProxyUserSet = true; m_Proxy = value; m_ServicePoint = null; ServicePoint refreshIt = ServicePoint ; } } ///Uses an HTTP proxy if needed to send FTP request ////// public override string ConnectionGroupName { get { return m_ConnectionGroupName; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_ConnectionGroupName = value; } } ///Allows private ConnectionPool(s) to be used ////// public ServicePoint ServicePoint { get { if (m_ServicePoint == null) { IWebProxy proxy = m_Proxy; if (!m_ProxyUserSet) proxy = WebRequest.InternalDefaultWebProxy; ServicePoint servicePoint = ServicePointManager.FindServicePoint(m_Uri, proxy); lock (m_SyncObject) { if (m_ServicePoint == null) { m_ServicePoint = servicePoint; m_Proxy = proxy; } } } return m_ServicePoint; } } internal bool Aborted { get { return m_Aborted; } } ///Generates a service point for this request, and allows setting of Connection settings ////// internal FtpWebRequest(Uri uri) { (new WebPermission(NetworkAccess.Connect, uri)).Demand(); if (Logging.On) Logging.PrintInfo(Logging.Web, this, ".ctor", uri.ToString()); if ((object)uri.Scheme != (object)Uri.UriSchemeFtp) throw new ArgumentOutOfRangeException("uri"); m_TimerCallback = new TimerThread.Callback(TimerCallback); m_SyncObject = new object(); NetworkCredential networkCredential = null; m_Uri = uri; m_MethodInfo = FtpMethodInfo.GetMethodInfo(WebRequestMethods.Ftp.DownloadFile); if (m_Uri.UserInfo != null && m_Uri.UserInfo.Length != 0) { string userInfo = m_Uri.UserInfo; string username = userInfo; string password = ""; int index = userInfo.IndexOf(':'); if (index != -1) { username = Uri.UnescapeDataString(userInfo.Substring(0, index)); index++; // skip ':' password = Uri.UnescapeDataString(userInfo.Substring(index, userInfo.Length - index)); } networkCredential = new NetworkCredential(username, password); } if (networkCredential == null) { networkCredential = DefaultFtpNetworkCredential; } m_AuthInfo = networkCredential; SetupCacheProtocol(m_Uri); } // // Used to query for the Response of an FTP request // public override WebResponse GetResponse() { if(Logging.On)Logging.Enter(Logging.Web, this, "GetResponse", ""); if(Logging.On)Logging.PrintInfo(Logging.Web, this, "GetResponse", SR.GetString(SR.net_log_method_equal, m_MethodInfo.Method)); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetResponse"); try { CheckError(); if (m_FtpWebResponse != null) return m_FtpWebResponse; if (m_GetResponseStarted) { throw new InvalidOperationException(SR.GetString(SR.net_repcall)); } m_GetResponseStarted = true; m_StartTime = DateTime.UtcNow; m_RemainingTimeout = Timeout; // We don't really need this variable, but we just need // to call the property to measure its execution time ServicePoint servicePoint = ServicePoint; if (Timeout != System.Threading.Timeout.Infinite) { m_RemainingTimeout = Timeout - (int)((DateTime.UtcNow - m_StartTime).TotalMilliseconds); if(m_RemainingTimeout <= 0){ throw new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout); } } if (ServicePoint.InternalProxyServicePoint) { if (EnableSsl) { m_GetResponseStarted = false; throw new WebException(SR.GetString(SR.net_ftp_proxy_does_not_support_ssl)); } try { HttpWebRequest httpWebRequest = GetHttpWebRequest(); if (Logging.On) Logging.Associate(Logging.Web, this, httpWebRequest); m_FtpWebResponse = new FtpWebResponse((HttpWebResponse)httpWebRequest.GetResponse()); } catch (WebException webException) { if (webException.Response != null && webException.Response is HttpWebResponse) { webException = new WebException(webException.Message, null, webException.Status, new FtpWebResponse((HttpWebResponse)webException.Response), webException.InternalStatus); } SetException(webException); throw webException; } // Catch added to address Bug # 545645 catch (InvalidOperationException invalidOpException) { SetException(invalidOpException); FinishRequestStage(RequestStage.CheckForError); throw; } } else { RequestStage prev = FinishRequestStage(RequestStage.RequestStarted); if (prev >= RequestStage.RequestStarted) { if (prev < RequestStage.ReadReady) { lock (m_SyncObject) { if (m_RequestStage < RequestStage.ReadReady) m_ReadAsyncResult = new LazyAsyncResult(null, null, null); } // GetRequeststream or BeginGetRequestStream has not finished yet? if (m_ReadAsyncResult != null) m_ReadAsyncResult.InternalWaitForCompletion(); CheckError(); } } else { do { SubmitRequest(false); if (m_MethodInfo.IsUpload) FinishRequestStage(RequestStage.WriteReady); else FinishRequestStage(RequestStage.ReadReady); CheckError(); } while (!CheckCacheRetrieveOnResponse()); EnsureFtpWebResponse(null); // This may update the Stream memeber on m_FtpWebResponse based on the CacheProtocol feedback. CheckCacheUpdateOnResponse(); if (m_FtpWebResponse.IsFromCache) FinishRequestStage(RequestStage.ReleaseConnection); } } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "GetResponse", exception); // if m_Exception == null, we are about to throw an exception to the user // and we haven't saved the exception, which also means we haven't dealt // with it. So just release the connection and log this for investigation if (m_Exception == null) { if(Logging.On)Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_unexpected_exception, "GetResponse()")); if (!NclUtilities.IsFatal(exception)){ GlobalLog.Assert("Find out why we are getting an unexpected exception."); } SetException(exception); FinishRequestStage(RequestStage.CheckForError); } throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetResponse", "returns #"+ValidationHelper.HashString(m_FtpWebResponse)); if(Logging.On)Logging.Exit(Logging.Web, this, "GetResponse", ""); } return m_FtpWebResponse; } ////// Initializes a new instance of the ////// class. /// /// /// [HostProtection(ExternalThreading=true)] public override IAsyncResult BeginGetResponse(AsyncCallback callback, object state) { if(Logging.On)Logging.Enter(Logging.Web, this, "BeginGetResponse", ""); if(Logging.On)Logging.PrintInfo(Logging.Web, this, "BeginGetResponse", SR.GetString(SR.net_log_method_equal, m_MethodInfo.Method)); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::BeginGetResponse"); ContextAwareResult asyncResult; try { if (m_FtpWebResponse != null) { asyncResult = new ContextAwareResult(this, state, callback); asyncResult.InvokeCallback(m_FtpWebResponse); return asyncResult; } if (m_GetResponseStarted) { throw new InvalidOperationException(SR.GetString(SR.net_repcall)); } m_GetResponseStarted = true; CheckError(); if (ServicePoint.InternalProxyServicePoint) { HttpWebRequest httpWebRequest = GetHttpWebRequest(); if (Logging.On) Logging.Associate(Logging.Web, this, httpWebRequest); asyncResult = (ContextAwareResult)httpWebRequest.BeginGetResponse(callback, state); } else { RequestStage prev = FinishRequestStage(RequestStage.RequestStarted); asyncResult = new ContextAwareResult(true, true, this, state, callback); m_ReadAsyncResult = asyncResult; if (prev >= RequestStage.RequestStarted) { // To make sure the context is flowed asyncResult.StartPostingAsyncOp(); asyncResult.FinishPostingAsyncOp(); if (prev >= RequestStage.ReadReady) asyncResult = null; else { lock (m_SyncObject) { if (m_RequestStage >= RequestStage.ReadReady) asyncResult = null;; } } if(asyncResult == null) { // need to complete it now asyncResult = (ContextAwareResult)m_ReadAsyncResult; if (!asyncResult.InternalPeekCompleted) asyncResult.InvokeCallback(); } } else { // Do internal processing in this handler to optimize context flowing. lock (asyncResult.StartPostingAsyncOp()) { SubmitRequest(true); asyncResult.FinishPostingAsyncOp(); } FinishRequestStage(RequestStage.CheckForError); } } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "BeginGetResponse", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::BeginGetResponse"); if(Logging.On)Logging.Exit(Logging.Web, this, "BeginGetResponse", ""); } return asyncResult; } ///Used to query for the Response of an FTP request [async version] ////// public override WebResponse EndGetResponse(IAsyncResult asyncResult) { if(Logging.On)Logging.Enter(Logging.Web, this, "EndGetResponse", ""); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetResponse"); try { // parameter validation if (asyncResult==null) { throw new ArgumentNullException("asyncResult"); } LazyAsyncResult castedAsyncResult = asyncResult as LazyAsyncResult; if (castedAsyncResult==null) { throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); } if (HttpProxyMode?(castedAsyncResult.AsyncObject!=this.GetHttpWebRequest()):castedAsyncResult.AsyncObject!=this) { throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); } if (castedAsyncResult.EndCalled) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetResponse")); } if (HttpProxyMode) { try { CheckError(); if (m_FtpWebResponse == null) { m_FtpWebResponse = new FtpWebResponse((HttpWebResponse)GetHttpWebRequest().EndGetResponse(asyncResult)); } } catch (WebException webException) { if (webException.Response != null && webException.Response is HttpWebResponse) { throw new WebException(webException.Message, null, webException.Status, new FtpWebResponse((HttpWebResponse)webException.Response), webException.InternalStatus); } throw; } } else{ castedAsyncResult.InternalWaitForCompletion(); castedAsyncResult.EndCalled = true; CheckError(); } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "EndGetResponse", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetResponse"); if(Logging.On)Logging.Exit(Logging.Web, this, "EndGetResponse", ""); } return m_FtpWebResponse; } ///Returns result of query for the Response of an FTP request [async version] ////// public override Stream GetRequestStream() { if(Logging.On)Logging.Enter(Logging.Web, this, "GetRequestStream", ""); if(Logging.On)Logging.PrintInfo(Logging.Web, this, "GetRequestStream", SR.GetString(SR.net_log_method_equal, m_MethodInfo.Method)); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetRequestStream"); try { if (m_GetRequestStreamStarted) { throw new InvalidOperationException(SR.GetString(SR.net_repcall)); } m_GetRequestStreamStarted = true; if (!m_MethodInfo.IsUpload) { throw new ProtocolViolationException(SR.GetString(SR.net_nouploadonget)); } CheckError(); m_StartTime = DateTime.UtcNow; m_RemainingTimeout = Timeout; // We don't really need this variable, but we just need // to call the property to measure its execution time ServicePoint servicePoint = ServicePoint; if (Timeout != System.Threading.Timeout.Infinite) { m_RemainingTimeout = Timeout - (int)((DateTime.UtcNow - m_StartTime).TotalMilliseconds); if(m_RemainingTimeout <= 0){ throw new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout); } } if (ServicePoint.InternalProxyServicePoint) { HttpWebRequest httpWebRequest = GetHttpWebRequest(); if (Logging.On) Logging.Associate(Logging.Web, this, httpWebRequest); m_Stream = httpWebRequest.GetRequestStream(); } else { FinishRequestStage(RequestStage.RequestStarted); SubmitRequest(false); FinishRequestStage(RequestStage.WriteReady); CheckError(); } if(m_Stream.CanTimeout) { m_Stream.WriteTimeout = ReadWriteTimeout; m_Stream.ReadTimeout = ReadWriteTimeout; } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "GetRequestStream", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetRequestStream"); if(Logging.On)Logging.Exit(Logging.Web, this, "GetRequestStream", ""); } return m_Stream; } ///Used to query for the Request stream of an FTP Request ////// [HostProtection(ExternalThreading=true)] public override IAsyncResult BeginGetRequestStream(AsyncCallback callback, object state) { if(Logging.On)Logging.Enter(Logging.Web, this, "BeginGetRequestStream", ""); if(Logging.On)Logging.PrintInfo(Logging.Web, this, "BeginGetRequestStream", SR.GetString(SR.net_log_method_equal, m_MethodInfo.Method)); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::BeginGetRequestStream"); ContextAwareResult asyncResult = null; try { if (m_GetRequestStreamStarted) { throw new InvalidOperationException(SR.GetString(SR.net_repcall)); } m_GetRequestStreamStarted = true; if (!m_MethodInfo.IsUpload) { throw new ProtocolViolationException(SR.GetString(SR.net_nouploadonget)); } CheckError(); if (ServicePoint.InternalProxyServicePoint) { HttpWebRequest httpWebRequest = GetHttpWebRequest(); if (Logging.On) Logging.Associate(Logging.Web, this, httpWebRequest); asyncResult = (ContextAwareResult)httpWebRequest.BeginGetRequestStream(callback, state); } else { FinishRequestStage(RequestStage.RequestStarted); asyncResult = new ContextAwareResult(true, true, this, state, callback); lock (asyncResult.StartPostingAsyncOp()) { m_WriteAsyncResult = asyncResult; SubmitRequest(true); asyncResult.FinishPostingAsyncOp(); FinishRequestStage(RequestStage.CheckForError); } } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "BeginGetRequestStream", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::BeginGetRequestStream"); if(Logging.On)Logging.Exit(Logging.Web, this, "BeginGetRequestStream", ""); } return asyncResult; } public override Stream EndGetRequestStream(IAsyncResult asyncResult) { if(Logging.On)Logging.Enter(Logging.Web, this, "EndGetRequestStream", ""); GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetRequestStream"); Stream requestStream = null; try { // parameter validation if (asyncResult==null) { throw new ArgumentNullException("asyncResult"); } LazyAsyncResult castedAsyncResult = asyncResult as LazyAsyncResult; if ((castedAsyncResult==null) || (HttpProxyMode?(castedAsyncResult.AsyncObject!=this.GetHttpWebRequest()):castedAsyncResult.AsyncObject!=this)) { throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); } if (castedAsyncResult.EndCalled) { throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetResponse")); } if (HttpProxyMode) { requestStream = GetHttpWebRequest().EndGetRequestStream(asyncResult); } else { castedAsyncResult.InternalWaitForCompletion(); castedAsyncResult.EndCalled = true; CheckError(); requestStream = m_Stream; castedAsyncResult.EndCalled = true; } if(requestStream.CanTimeout) { requestStream.WriteTimeout = ReadWriteTimeout; requestStream.ReadTimeout = ReadWriteTimeout; } } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "EndGetRequestStream", exception); throw; } finally { GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::EndGetRequestStream"); if(Logging.On)Logging.Exit(Logging.Web, this, "EndGetRequestStream", ""); } return requestStream; } // // NOTE1: The caller must synchronize access to SubmitRequest(), only one call is even allowed for a particular request! // NOTE2: This method eats all exceptions so the caller must rethrow them // private void SubmitRequest(bool async) { try { m_Async = async; if (CheckCacheRetrieveBeforeSubmit()) { RequestCallback(null); return; } // This is the only place touching m_ConnectionPool if (m_ConnectionPool == null) m_ConnectionPool = ConnectionPoolManager.GetConnectionPool(ServicePoint, GetConnectionGroupLine(), m_CreateConnectionCallback); // // FYI: Will do 2 attempts max as per AttemptedRecovery // Stream stream; while(true) { FtpControlStream connection = m_Connection; if (connection == null) { connection = QueueOrCreateConnection(); if (connection == null) return; } if(!async){ if (Timeout != System.Threading.Timeout.Infinite) { m_RemainingTimeout = Timeout - (int)((DateTime.UtcNow - m_StartTime).TotalMilliseconds); if(m_RemainingTimeout <= 0){ throw new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout); } } } GlobalLog.Print("Request being submitted"+ValidationHelper.HashString(this)); connection.SetSocketTimeoutOption(SocketShutdown.Both, RemainingTimeout, false); try { stream = TimedSubmitRequestHelper(async); } catch (Exception e) { if (AttemptedRecovery(e)){ if(!async){ if (Timeout != System.Threading.Timeout.Infinite) { m_RemainingTimeout = Timeout - (int)((DateTime.UtcNow - m_StartTime).TotalMilliseconds); if(m_RemainingTimeout <= 0){ throw; } } } continue; } throw; } // no retry needed break; } } catch (WebException webException) { //if this was a timeout, throw a timeout exception IOException ioEx = webException.InnerException as IOException; if(ioEx != null){ SocketException ---- = ioEx.InnerException as SocketException; if(---- != null){ if (----.ErrorCode == (int)SocketError.TimedOut) { SetException(new WebException(SR.GetString(SR.net_timeout), WebExceptionStatus.Timeout)); } } } SetException(webException); } catch (Exception exception) { SetException(exception); } } // // // private FtpControlStream QueueOrCreateConnection() { FtpControlStream connection = (FtpControlStream) m_ConnectionPool.GetConnection((object)this, (m_Async ? m_AsyncCallback : null), (m_Async ? -1: RemainingTimeout)); if (connection == null) { GlobalLog.Assert(m_Async, "QueueOrCreateConnection|m_ConnectionPool.GetConnection() returned null on a [....] Request."); return null; } lock (m_SyncObject) { if (m_Aborted) { if (Logging.On) Logging.PrintInfo(Logging.Web, this, "", SR.GetString(SR.net_log_releasing_connection, ValidationHelper.HashString(connection))); m_ConnectionPool.PutConnection(connection, this, RemainingTimeout); CheckError(); //must throw throw new InternalException(); } m_Connection = connection; if (Logging.On) Logging.Associate(Logging.Web, this, m_Connection); } return connection; } // // private Stream TimedSubmitRequestHelper(bool async) { if(async) { // non-null in the case of re-submit (recovery) if (m_RequestCompleteAsyncResult == null) m_RequestCompleteAsyncResult = new LazyAsyncResult(null, null, null); return m_Connection.SubmitRequest(this, true, true); } Stream stream = null; bool timedOut = false; TimerThread.Timer timer = TimerQueue.CreateTimer(m_TimerCallback, null); try { stream = m_Connection.SubmitRequest(this, false, true); } catch (Exception exception) { if (!(exception is SocketException || exception is ObjectDisposedException) || !timer.HasExpired) { timer.Cancel(); throw; } timedOut = true; } if (timedOut || !timer.Cancel()) { m_TimedOut = true; throw new WebException(NetRes.GetWebStatusString(WebExceptionStatus.Timeout), WebExceptionStatus.Timeout); } if (stream != null) { lock (m_SyncObject) { if (m_Aborted) { ((ICloseEx)stream).CloseEx(CloseExState.Abort|CloseExState.Silent); CheckError(); //must throw throw new InternalException(); //consider replacing this on Assert } m_Stream = stream; } } return stream; } ///Used to query for the Request stream of an FTP Request [async version] ////// private void TimerCallback(TimerThread.Timer timer, int timeNoticed, object context) { GlobalLog.Print("FtpWebRequest#" + ValidationHelper.HashString(this) + "::TimerCallback"); FtpControlStream connection = m_Connection; if (connection != null) { GlobalLog.Print("FtpWebRequest#" + ValidationHelper.HashString(this) + "::TimerCallback aborting connection"); connection.AbortConnect(); } } private TimerThread.Queue TimerQueue { get { if (m_TimerQueue == null) { m_TimerQueue = TimerThread.GetOrCreateQueue(RemainingTimeout); } return m_TimerQueue; } } ///Because this is called from the timer thread, neither it nor any methods it calls can call user code. ////// private bool AttemptedRecovery(Exception e) { // The first 'if' is just checking whether the exception is thrown due to the // relogin failure which is a recoverable error if (!(e is WebException && ((WebException)e).InternalStatus == WebExceptionInternalStatus.Isolated)) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException || m_OnceFailed || m_Aborted || m_TimedOut || m_Connection==null || !m_Connection.RecoverableFailure) { return false; } m_OnceFailed = true; } lock (m_SyncObject) { if (m_ConnectionPool != null && m_Connection != null) { m_Connection.CloseSocket(); if (Logging.On) Logging.PrintInfo(Logging.Web, this, "", SR.GetString(SR.net_log_releasing_connection, ValidationHelper.HashString(m_Connection))); m_ConnectionPool.PutConnection(m_Connection, this, RemainingTimeout); m_Connection = null; } else { return false; } } return true; } ///Returns true if we should restart the request after an error ////// private void SetException(Exception exception) { GlobalLog.Print("FtpWebRequest#" + ValidationHelper.HashString(this) + "::SetException"); if (exception is ThreadAbortException || exception is StackOverflowException || exception is OutOfMemoryException) { m_Exception = exception; throw exception; } FtpControlStream connection = m_Connection; if (m_Exception == null) { if (exception is WebException) { EnsureFtpWebResponse(exception); m_Exception = new WebException(exception.Message, null, ((WebException)exception).Status, m_FtpWebResponse); } else if (exception is AuthenticationException || exception is SecurityException) { m_Exception = exception; } else if (connection!= null && connection.StatusCode != FtpStatusCode.Undefined) { EnsureFtpWebResponse(exception); m_Exception = new WebException(SR.GetString(SR.net_servererror, connection.StatusLine), exception, WebExceptionStatus.ProtocolError, m_FtpWebResponse); } else { m_Exception = new WebException(exception.Message, exception); } if (connection != null && m_FtpWebResponse != null) m_FtpWebResponse.UpdateStatus(connection.StatusCode, connection.StatusLine, connection.ExitMessage); } } ///Updates and sets our exception to be thrown ////// private void CheckError() { if (m_Exception != null) { throw m_Exception; } } // Return null only on [....] (if we're on the [....] thread). Otherwise throw if no context is available. // // internal override ContextAwareResult GetWritingContext() { if (m_ReadAsyncResult != null && m_ReadAsyncResult is ContextAwareResult) return (ContextAwareResult)m_ReadAsyncResult; else if (m_WriteAsyncResult != null) return m_WriteAsyncResult; // [....]. GlobalLog.ThreadContract(ThreadKinds.User | ThreadKinds.[....], "FtpWebRequest#" + ValidationHelper.HashString(this) + "::GetWritingContext"); return null; } // // Provides an abstract way of having Async code callback into the request (saves a delegate) // // ATTN this method is also called on [....] path when either command or data stream gets closed // Consider: Revisit the design of ftp streams // internal override void RequestCallback(object obj) { if (m_Async) AsyncRequestCallback(obj); else SyncRequestCallback(obj); } // // Only executed for [....] requests when the pipline is completed // private void SyncRequestCallback(object obj) { GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::SyncRequestCallback", "#"+ValidationHelper.HashString(obj)); RequestStage stageMode = RequestStage.CheckForError; try { bool completedRequest = obj == null; Exception exception = obj as Exception; GlobalLog.Print("SyncRequestCallback() exp:"+ValidationHelper.HashString(exception)+" completedRequest:"+ValidationHelper.HashString(completedRequest)); if (exception != null) { SetException(exception); } else if (!completedRequest) { throw new InternalException(); } else { // a signal on current pipeline completion FtpControlStream connection = m_Connection; bool isRevalidatedOrRetried = false; if (connection != null) { EnsureFtpWebResponse(null); // This to update response status and exit message if any // Note that due to a design of FtpControlStream the status 221 "Service closing control connection" is always suppresses. m_FtpWebResponse.UpdateStatus(connection.StatusCode, connection.StatusLine, connection.ExitMessage); isRevalidatedOrRetried =!m_CacheDone && (CacheProtocol.ProtocolStatus == CacheValidationStatus.Continue || CacheProtocol.ProtocolStatus == CacheValidationStatus.RetryResponseFromServer); // This is for [....] Upload commands that do not get chance hit GetResponse loop if (m_MethodInfo.IsUpload) { CheckCacheRetrieveOnResponse(); CheckCacheUpdateOnResponse(); } } if (!isRevalidatedOrRetried) stageMode = RequestStage.ReleaseConnection; } } catch (Exception exception) { SetException(exception); } finally { FinishRequestStage(stageMode); GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::SyncRequestCallback"); CheckError(); //will throw on error } } // // Only executed for Async requests // private void AsyncRequestCallback(object obj) { GlobalLog.Enter("FtpWebRequest#" + ValidationHelper.HashString(this) + "::AsyncRequestCallback", "#"+ValidationHelper.HashString(obj)); RequestStage stageMode = RequestStage.CheckForError; try { FtpControlStream connection; connection = obj as FtpControlStream; FtpDataStream stream = obj as FtpDataStream; Exception exception = obj as Exception; bool completedRequest = (obj == null); GlobalLog.Print("AsyncRequestCallback() stream:"+ValidationHelper.HashString(stream)+" conn:"+ValidationHelper.HashString(connection)+" exp:"+ValidationHelper.HashString(exception)+" completedRequest:"+ValidationHelper.HashString(completedRequest)); while (true) { if (exception != null) { if (AttemptedRecovery(exception)) { connection = QueueOrCreateConnection(); if (connection == null) return; exception = null; } if (exception != null) { SetException(exception); break; } } if (connection != null) { lock(m_SyncObject) { if (m_Aborted) { if (Logging.On) Logging.PrintInfo(Logging.Web, this, "", SR.GetString(SR.net_log_releasing_connection, ValidationHelper.HashString(connection))); m_ConnectionPool.PutConnection(connection, this, Timeout); break; } m_Connection = connection; if (Logging.On) Logging.Associate(Logging.Web, this, m_Connection); } try { stream = (FtpDataStream)TimedSubmitRequestHelper(true); } catch (Exception e) { exception = e; continue; } return; } else if (stream != null) { lock (m_SyncObject) { if (m_Aborted) { ((ICloseEx)stream).CloseEx(CloseExState.Abort|CloseExState.Silent); break; } m_Stream = stream; } stream.SetSocketTimeoutOption(SocketShutdown.Both, Timeout, true); EnsureFtpWebResponse(null); // This one may update the Stream member on m_FtpWebResponse based on the CacheProtocol feedback. CheckCacheRetrieveOnResponse(); CheckCacheUpdateOnResponse(); stageMode = stream.CanRead? RequestStage.ReadReady: RequestStage.WriteReady; } else if (completedRequest) { connection = m_Connection; bool isRevalidatedOrRetried = false; if (connection != null) { EnsureFtpWebResponse(null); // This to update response status and exit message if any // Note that due to a design of FtpControlStream the status 221 "Service closing control connection" is always suppresses. m_FtpWebResponse.UpdateStatus(connection.StatusCode, connection.StatusLine, connection.ExitMessage); isRevalidatedOrRetried =!m_CacheDone && (CacheProtocol.ProtocolStatus == CacheValidationStatus.Continue || CacheProtocol.ProtocolStatus == CacheValidationStatus.RetryResponseFromServer); lock (m_SyncObject) { if(!CheckCacheRetrieveOnResponse()) continue; if (m_FtpWebResponse.IsFromCache) isRevalidatedOrRetried = false; CheckCacheUpdateOnResponse(); } } if (!isRevalidatedOrRetried) stageMode = RequestStage.ReleaseConnection; } else { throw new InternalException(); } break; } } catch (Exception exception) { SetException(exception); } finally { FinishRequestStage(stageMode); GlobalLog.Leave("FtpWebRequest#" + ValidationHelper.HashString(this) + "::AsyncRequestCallback"); } } // // // private enum RequestStage { CheckForError = 0,// Do nothing except if there is an error then auto promote to ReleaseConnection RequestStarted, // Mark this request as started WriteReady, // First half is done, i.e. either writer or response stream. This is always assumed unless Started or CheckForError ReadReady, // Second half is done, i.e. the read stream can be accesses. ReleaseConnection // Release the control connection (request is read i.e. done-done) } // // Returns a previous stage // private RequestStage FinishRequestStage(RequestStage stage) { GlobalLog.Print("FtpWebRequest#" + ValidationHelper.HashString(this) + "::FinishRequestStage : stage="+stage); if (m_Exception != null) stage = RequestStage.ReleaseConnection; RequestStage prev; LazyAsyncResult writeResult; LazyAsyncResult readResult; FtpControlStream connection; lock (m_SyncObject) { prev = m_RequestStage; if (stage == RequestStage.CheckForError) return prev; if (prev == RequestStage.ReleaseConnection && stage == RequestStage.ReleaseConnection) { return RequestStage.ReleaseConnection; } if (stage > prev) m_RequestStage = stage; if (stage <= RequestStage.RequestStarted) return prev; writeResult = m_WriteAsyncResult; readResult = m_ReadAsyncResult; connection = m_Connection; if (stage == RequestStage.ReleaseConnection) { if (m_Exception == null && !m_Aborted && prev != RequestStage.ReadReady && this.m_MethodInfo.IsDownload && !m_FtpWebResponse.IsFromCache) { return prev; } if (m_Exception != null || !(m_FtpWebResponse.IsFromCache && !KeepAlive)) m_Connection = null; } } try { // First check to see on releasing the connection if ((stage == RequestStage.ReleaseConnection || prev == RequestStage.ReleaseConnection) && connection != null) { try { if (m_Exception != null) { connection.Abort(m_Exception); } else if (m_FtpWebResponse.IsFromCache && !KeepAlive) { // This means the response has been revalidated and found as good means the commands pipleline is completed. // Now if this request was NOT KeepAlive we want to be fair and close the control connection. // That becomes unnecessary complicated in the async case to support "QUIT" command semantic, so simply close the socket // and let pool collect the object. connection.Quit(); } } finally { if (Logging.On) Logging.PrintInfo(Logging.Web, this, "", SR.GetString(SR.net_log_releasing_connection, ValidationHelper.HashString(connection))); m_ConnectionPool.PutConnection(connection, this, RemainingTimeout); if (m_Async) if (m_RequestCompleteAsyncResult != null) m_RequestCompleteAsyncResult.InvokeCallback(); } } return prev; } finally { try { // In any case we want to signal the writer if came here if (stage >= RequestStage.WriteReady) { // If writeResult == null and this is an upload request, it means // that the user has called GetResponse() without calling // GetRequestStream() first. So they are not interested in a // stream. Therefore we close the stream so that the // request/pipeline can continue if (m_MethodInfo.IsUpload && !m_GetRequestStreamStarted) { if (m_Stream != null) m_Stream.Close(); } else if (writeResult != null && !writeResult.InternalPeekCompleted) writeResult.InvokeCallback(); } } finally { // The response is ready either with or without a stream if (stage >= RequestStage.ReadReady && readResult != null && !readResult.InternalPeekCompleted) readResult.InvokeCallback(); } } } // // Used only in the async case and only for the initial callback from the pool when connection is established. // private static void AsyncCallbackWrapper(object request, object state) { FtpWebRequest ftpWebRequest = (FtpWebRequest) request; ftpWebRequest.RequestCallback(state); } ///Opposite of SetException, rethrows the exception ////// private static PooledStream CreateFtpConnection(ConnectionPool pool) { return (PooledStream) new FtpControlStream(pool, TimeSpan.MaxValue, false); } ///builds networkStream from Socket ////// public override void Abort() { if (m_Aborted) return; if(Logging.On)Logging.Enter(Logging.Web, this, "Abort", ""); try { GlobalLog.Print("FtpWebRequest#"+ValidationHelper.HashString(this)+"::Abort()"); if (HttpProxyMode) { GetHttpWebRequest().Abort(); return; } if (CacheProtocol != null) CacheProtocol.Abort(); Stream stream; FtpControlStream connection; lock (m_SyncObject) { if (m_RequestStage >= RequestStage.ReleaseConnection) return; m_Aborted = true; stream = m_Stream; connection = m_Connection; m_Exception = new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled) ; } if (stream != null) { GlobalLog.Assert(stream is ICloseEx, "FtpWebRequest.Abort()|The m_Stream member is not CloseEx hence the risk of connection been orphaned."); ((ICloseEx)stream).CloseEx(CloseExState.Abort|CloseExState.Silent); } if (connection != null) connection.Abort(ExceptionHelper.RequestAbortedException); } catch (Exception exception) { if(Logging.On)Logging.Exception(Logging.Web, this, "Abort", exception); throw; } finally { if(Logging.On)Logging.Exit(Logging.Web, this, "Abort", ""); } } ///Aborts underlying connection to FTP server (command & data) ////// /// public bool KeepAlive { get { return m_KeepAlive; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_KeepAlive = value; } } ////// If KeepAlive is set to false, then the control connection to the server will be closed when the request completes. /// Default is true /// ////// public bool UseBinary { get { return m_Binary; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_Binary = value; } } ///True by default, false allows transmission using text mode ////// public bool UsePassive { get { return m_Passive; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_Passive = value; } } #if !FEATURE_PAL ///False by default, true enables passive mode communication with server. /// This alters the way the client talks with the server, allowing the client /// to initiate the data connection with the server ////// public X509CertificateCollection ClientCertificates { get { if (m_ClientCertificates == null) { lock (m_SyncObject) { if (m_ClientCertificates == null) { m_ClientCertificates = new X509CertificateCollection(); } } } return m_ClientCertificates; } set { if (value==null) { throw new ArgumentNullException("value"); } m_ClientCertificates = value; } } #endif // !FEATURE_PAL ////// ClientCertificates - sets our certs for our reqest, /// uses a hash of the collection to create a private connection /// group, to prevent us from using the same Connection as /// non-Client Authenticated requests. /// ////// public bool EnableSsl { get { return m_EnableSsl; } set { if (InUse) { throw new InvalidOperationException(SR.GetString(SR.net_reqsubmitted)); } m_EnableSsl = value; } } ///Set to true if we need SSL ////// public override WebHeaderCollection Headers { get { if (HttpProxyMode) { return GetHttpWebRequest().Headers; } if (m_FtpRequestHeaders == null) { m_FtpRequestHeaders = new WebHeaderCollection(WebHeaderCollectionType.FtpWebRequest); } return m_FtpRequestHeaders; } set { if (HttpProxyMode) { GetHttpWebRequest().Headers = value; } m_FtpRequestHeaders = value; } } // NOT SUPPORTED method public override string ContentType { get { throw ExceptionHelper.PropertyNotSupportedException; } set { throw ExceptionHelper.PropertyNotSupportedException; } } // NOT SUPPORTED method public override bool UseDefaultCredentials { get { throw ExceptionHelper.PropertyNotSupportedException; } set { throw ExceptionHelper.PropertyNotSupportedException; } } // NOT SUPPORTED method public override bool PreAuthenticate { get { throw ExceptionHelper.PropertyNotSupportedException; } set { throw ExceptionHelper.PropertyNotSupportedException; } } ////// A collection of headers, currently nothing is return except an empty collection /// ////// private bool InUse { get { if (m_GetRequestStreamStarted || m_GetResponseStarted) { return true; } else { return false; } } } ///True if a request has been submitted (ie already active) ////// private bool HttpProxyMode { get { return (m_HttpWebRequest != null); } } ///True if request is just wrapping HttpWebRequest ////// private void EnsureFtpWebResponse(Exception exception) { if (m_FtpWebResponse == null || (m_FtpWebResponse.GetResponseStream() is FtpWebResponse.EmptyStream && m_Stream != null)) { lock (m_SyncObject) { if (m_FtpWebResponse == null || (m_FtpWebResponse.GetResponseStream() is FtpWebResponse.EmptyStream && m_Stream != null)) { Stream responseStream = m_Stream; if (m_MethodInfo.IsUpload) { responseStream = null; } if(m_Stream != null && m_Stream.CanRead && m_Stream.CanTimeout) { m_Stream.ReadTimeout = ReadWriteTimeout; m_Stream.WriteTimeout = ReadWriteTimeout; } FtpControlStream connection = m_Connection; long contentLength = connection != null? connection.ContentLength: -1; if (responseStream == null) { // If the last command was SIZE, we set the ContentLength on // the FtpControlStream to be the size of the file returned in the // response. We should propagate that file size to the response so // users can access it. This also maintains the compatibility with // HTTP when returning size instead of content. if (contentLength < 0) contentLength = 0; } if (m_FtpWebResponse != null) { m_FtpWebResponse.SetResponseStream(responseStream); } else { if (connection != null) m_FtpWebResponse = new FtpWebResponse(responseStream, contentLength, connection.ResponseUri, connection.StatusCode, connection.StatusLine, connection.LastModified, connection.BannerMessage, connection.WelcomeMessage, connection.ExitMessage); else m_FtpWebResponse = new FtpWebResponse(responseStream, -1, m_Uri, FtpStatusCode.Undefined, null, DateTime.Now, null, null, null); } } } } GlobalLog.Print("FtpWebRequest#"+ValidationHelper.HashString(this)+"::EnsureFtpWebResponse returns #"+ValidationHelper.HashString(m_FtpWebResponse)+" with stream#"+ValidationHelper.HashString(m_FtpWebResponse.m_ResponseStream)); return; } ///Creates an FTP WebResponse based off the responseStream and our active Connection ////// private HttpWebRequest GetHttpWebRequest() { lock (m_SyncObject) { if (m_HttpWebRequest == null) { if (m_ContentOffset > 0) { throw new InvalidOperationException(SR.GetString(SR.net_ftp_no_offsetforhttp)); } if (!m_MethodInfo.HasHttpCommand) throw new InvalidOperationException(SR.GetString(SR.net_ftp_no_http_cmd)); m_HttpWebRequest = new HttpWebRequest(m_Uri, ServicePoint); m_HttpWebRequest.Credentials = Credentials; m_HttpWebRequest.InternalProxy = m_Proxy; m_HttpWebRequest.KeepAlive = KeepAlive; m_HttpWebRequest.Timeout = Timeout; m_HttpWebRequest.Method = m_MethodInfo.HttpCommand; m_HttpWebRequest.CacheProtocol = CacheProtocol; RequestCacheLevel effectiveLevel; if (CachePolicy == null) effectiveLevel = RequestCacheLevel.BypassCache; else effectiveLevel = CachePolicy.Level; // Cannot support revalidate through the proxy if (effectiveLevel == RequestCacheLevel.Revalidate) effectiveLevel = RequestCacheLevel.Reload; m_HttpWebRequest.CachePolicy = new HttpRequestCachePolicy((HttpRequestCacheLevel)effectiveLevel); //disable cache protocol on that class since we are proxying through HTTP CacheProtocol = null; } } return m_HttpWebRequest; } ///Creates a HttpWebRequest ////// private string GetConnectionGroupLine() { GlobalLog.Print("GetConnectionGroupLine"); return ConnectionGroupName + "_" + GetUserString(); } ///Generates a string that /// allows a Connection to remain unique for user /// this is needed to prevent multiple users from /// using the same sockets after they mess with things ////// internal string GetUserString() { string name = null; if (this.Credentials != null) { NetworkCredential networkCreds = this.Credentials.GetCredential(m_Uri, "basic"); if (networkCreds != null) { name = networkCreds.InternalGetUserName(); string domain = networkCreds.InternalGetDomain(); if (!ValidationHelper.IsBlankString(domain)) { name = domain+"\\"+name; } } } return name == null? null: (String.Compare(name,"anonymous", StringComparison.InvariantCultureIgnoreCase) == 0? null: name); } // // This method may be invoked as part of the request submission but // before the response is received // Return: // - True = Use CacheProtocol properties to create the cached response // - False = Proceed with the request submission private bool CheckCacheRetrieveBeforeSubmit() { if (CacheProtocol == null || m_CacheDone) { m_CacheDone = true; return false; } if (CacheProtocol.ProtocolStatus == CacheValidationStatus.CombineCachedAndServerResponse || CacheProtocol.ProtocolStatus == CacheValidationStatus.DoNotTakeFromCache) { // Re-entry into a new pipeline on failed revalidate or combining cached and live streams return false; } Uri cacheUri = RequestUri; string username = GetUserString(); if(username != null) username = Uri.EscapeDataString(username); if (cacheUri.Fragment.Length != 0 || username != null) { if (username == null) cacheUri = new Uri(cacheUri.GetParts(UriComponents.AbsoluteUri & ~(UriComponents.Fragment|UriComponents.UserInfo), UriFormat.SafeUnescaped)); else { username = cacheUri.GetParts((UriComponents.Scheme | UriComponents.KeepDelimiter), UriFormat.SafeUnescaped) + username + '@'; username += cacheUri.GetParts((UriComponents.Host | UriComponents.Port | UriComponents.Path | UriComponents.Query), UriFormat.SafeUnescaped); cacheUri = new Uri(username); } } CacheProtocol.GetRetrieveStatus(cacheUri, this); if (CacheProtocol.ProtocolStatus == CacheValidationStatus.Fail) { throw CacheProtocol.ProtocolException; } if (CacheProtocol.ProtocolStatus != CacheValidationStatus.ReturnCachedResponse) { return false; } if (m_MethodInfo.Operation != FtpOperation.DownloadFile) { throw new NotSupportedException(SR.GetString(SR.net_cache_not_supported_command)); } if (CacheProtocol.ProtocolStatus == CacheValidationStatus.ReturnCachedResponse) { // If we take it from cache, we have to kick in response processing // The _CacheStream is good to return as the response stream. FtpRequestCacheValidator ctx = (FtpRequestCacheValidator) CacheProtocol.Validator; m_FtpWebResponse = new FtpWebResponse(CacheProtocol.ResponseStream, CacheProtocol.ResponseStreamLength, RequestUri, UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData, (UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData).ToString(), ctx.CacheEntry.LastModifiedUtc == DateTime.MinValue? DateTime.Now: ctx.CacheEntry.LastModifiedUtc.ToLocalTime(), string.Empty, string.Empty, string.Empty); m_FtpWebResponse.InternalSetFromCache = true; m_FtpWebResponse.InternalSetIsCacheFresh = (ctx.CacheFreshnessStatus != CacheFreshnessStatus.Stale); } return true; } // // This method has to be invoked as part of the wire response processing. // The wire response can be replaced on return // // ATTN: If the method returns false, the response is invalid and should be retried // private bool CheckCacheRetrieveOnResponse() { if (CacheProtocol == null || m_CacheDone) { return true; } if (CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue) { // cache has been already revalidated proceed with cache update return true; } if (CacheProtocol.ProtocolStatus == CacheValidationStatus.Fail) { if(Logging.On)Logging.Exception(Logging.Web, this, "CheckCacheRetrieveOnResponse", CacheProtocol.ProtocolException); throw CacheProtocol.ProtocolException; } // At this point we dont have the real data stream, hence passing null and updating it later CacheProtocol.GetRevalidateStatus(m_FtpWebResponse, null); if (CacheProtocol.ProtocolStatus == CacheValidationStatus.RetryResponseFromServer) { if (m_FtpWebResponse != null) m_FtpWebResponse.SetResponseStream(null); //prevent from advancing commands pipeline // Try to resubmit or fail return false; } if (CacheProtocol.ProtocolStatus != CacheValidationStatus.ReturnCachedResponse) { // Proceed with the requesting the real server data stream return false; } if (m_MethodInfo.Operation != FtpOperation.DownloadFile) { // This should never happen in real life throw new NotSupportedException(SR.GetString(SR.net_cache_not_supported_command)); } FtpRequestCacheValidator ctx = (FtpRequestCacheValidator) CacheProtocol.Validator; FtpWebResponse oldResponse = m_FtpWebResponse; m_Stream = CacheProtocol.ResponseStream; m_FtpWebResponse = new FtpWebResponse(CacheProtocol.ResponseStream, CacheProtocol.ResponseStreamLength, RequestUri, UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData, (UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData).ToString(), ctx.CacheEntry.LastModifiedUtc == DateTime.MinValue? DateTime.Now: ctx.CacheEntry.LastModifiedUtc.ToLocalTime(), string.Empty, string.Empty, string.Empty); m_FtpWebResponse.InternalSetFromCache = true; m_FtpWebResponse.InternalSetIsCacheFresh = CacheProtocol.IsCacheFresh; oldResponse.Close(); return true; } // // This will decide on cache update and construct the effective response stream // private void CheckCacheUpdateOnResponse() { if (CacheProtocol == null || m_CacheDone) { return; } m_CacheDone = true; if (m_Connection != null) { m_FtpWebResponse.UpdateStatus(m_Connection.StatusCode, m_Connection.StatusLine, m_Connection.ExitMessage); if (m_Connection.StatusCode == FtpStatusCode.OpeningData && m_FtpWebResponse.ContentLength == 0) m_FtpWebResponse.SetContentLength(m_Connection.ContentLength); } if (CacheProtocol.ProtocolStatus == CacheValidationStatus.CombineCachedAndServerResponse) { // Note we already asked for a file restart // The only problem is that we could not create the combined stream sooner. m_Stream = new CombinedReadStream(CacheProtocol.Validator.CacheStream, m_FtpWebResponse.GetResponseStream()); // // For consistent user experience we always supply DataAlreadyOpen status for a cached response. // FtpStatusCode rightStatus = UsePassive? FtpStatusCode.DataAlreadyOpen: FtpStatusCode.OpeningData; m_FtpWebResponse.UpdateStatus(rightStatus, rightStatus.ToString(), string.Empty); m_FtpWebResponse.SetResponseStream(m_Stream); } if (CacheProtocol.GetUpdateStatus(m_FtpWebResponse, m_FtpWebResponse.GetResponseStream()) == CacheValidationStatus.UpdateResponseInformation) { m_Stream = CacheProtocol.ResponseStream; m_FtpWebResponse.SetResponseStream(m_Stream); } else if (CacheProtocol.ProtocolStatus == CacheValidationStatus.Fail) throw CacheProtocol.ProtocolException; } internal void DataStreamClosed(CloseExState closeState) { if ((closeState & CloseExState.Abort) == 0) { if (!m_Async) { if (m_Connection != null) m_Connection.CheckContinuePipeline(); } else { m_RequestCompleteAsyncResult.InternalWaitForCompletion(); CheckError(); } } else { FtpControlStream connection = m_Connection; if (connection != null) connection.Abort(ExceptionHelper.RequestAbortedException); } } } // class FtpWebRequest // // Class used by the WebRequest.Create factory to create FTP requests // internal class FtpWebRequestCreator : IWebRequestCreate { internal FtpWebRequestCreator() { } public WebRequest Create(Uri uri) { return new FtpWebRequest(uri); } } // class FtpWebRequestCreator } //namespace System.Net // File provided for Reference Use Only by Microsoft Corporation (c) 2007.Returns username string ///
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ConstantExpression.cs
- CodeTypeParameterCollection.cs
- ServiceMetadataContractBehavior.cs
- LocalBuilder.cs
- SystemWebSectionGroup.cs
- RecognizerInfo.cs
- BrowserDefinition.cs
- RenderOptions.cs
- AnnotationStore.cs
- EdmScalarPropertyAttribute.cs
- _KerberosClient.cs
- TableItemProviderWrapper.cs
- PageRequestManager.cs
- XmlKeywords.cs
- XmlDictionaryReader.cs
- SBCSCodePageEncoding.cs
- ObjectStateFormatter.cs
- ToolStripComboBox.cs
- XmlTextWriter.cs
- HostSecurityManager.cs
- DataGridTextBox.cs
- X509RawDataKeyIdentifierClause.cs
- Asn1IntegerConverter.cs
- ToolStripItemClickedEventArgs.cs
- ClientTargetCollection.cs
- IdentityNotMappedException.cs
- ConfigXmlReader.cs
- TypeDescriptorFilterService.cs
- Constraint.cs
- _SSPISessionCache.cs
- XsltContext.cs
- XmlAggregates.cs
- FileDialogCustomPlacesCollection.cs
- DictionaryCustomTypeDescriptor.cs
- DataGridCaption.cs
- ReadOnlyCollectionBase.cs
- MainMenu.cs
- LogArchiveSnapshot.cs
- XmlILOptimizerVisitor.cs
- InternalException.cs
- CustomMenuItemCollection.cs
- UpDownEvent.cs
- ServiceDebugBehavior.cs
- GroupItem.cs
- KeyTime.cs
- EventItfInfo.cs
- OdbcParameterCollection.cs
- ImmutableAssemblyCacheEntry.cs
- HttpHandlersSection.cs
- StringFormat.cs
- CookieHandler.cs
- ConstraintStruct.cs
- ResourceReader.cs
- UnmanagedMemoryAccessor.cs
- RowToFieldTransformer.cs
- ViewStateAttachedPropertyFeature.cs
- ToolStripRenderEventArgs.cs
- DesignerWithHeader.cs
- ThrowHelper.cs
- DataGridViewBand.cs
- FixedPage.cs
- WorkflowServiceAttributes.cs
- BinaryCommonClasses.cs
- AdjustableArrowCap.cs
- MarginsConverter.cs
- Point.cs
- SoapClientMessage.cs
- ErrorStyle.cs
- SparseMemoryStream.cs
- FileAuthorizationModule.cs
- ZipIORawDataFileBlock.cs
- StandardToolWindows.cs
- ColorTransform.cs
- RegexCaptureCollection.cs
- WebPartEditorOkVerb.cs
- Content.cs
- Properties.cs
- TreeView.cs
- AutomationProperties.cs
- XmlConverter.cs
- FastEncoder.cs
- FixedSOMGroup.cs
- GridViewUpdateEventArgs.cs
- CfgParser.cs
- HtmlTernaryTree.cs
- SecurityTokenValidationException.cs
- TypeBinaryExpression.cs
- MarkedHighlightComponent.cs
- ControlBuilder.cs
- RemotingException.cs
- CompilationSection.cs
- ExternalCalls.cs
- DataGridViewLayoutData.cs
- PasswordRecovery.cs
- AssemblyInfo.cs
- FindCriteria11.cs
- FixedPageAutomationPeer.cs
- StringConverter.cs
- RichTextBox.cs
- SchemaManager.cs