internal void ReportFileCompleted(long contentPosition, byte[] hash) { #if (TRACE) ThreadLog.WriteLine("File completed " + contentPosition.ToString(CultureInfo.InvariantCulture)); #endif // Create a new Stringbuilder to collect the bytes and create a string. StringBuilder objStringBuilder = new StringBuilder(); // Loop through each byte of the hashed data // and format each one as a hexadecimal string. for (int i = 0; i < hash.Length; i++) { objStringBuilder.Append(hash[i].ToString("x2", CultureInfo.InvariantCulture)); } _ContentPosition = contentPosition; this.UploadFileInProgress.HashValue = objStringBuilder.ToString(); this.UploadFileInProgress.IsComplete = true; if (HttpContext.Current != null) //Support for unit tests { System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ContentPosition == contentPosition); System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ProgressStatus == UploadProgressStatus.InProgress); } }
/// <summary> /// Gets upload data from context cache /// </summary> /// <param name="context"></param> /// <param name="uploadId"></param> /// <returns></returns> public static UploadData GetUploadData(HttpContext context, string uploadId) { #if (TRACE) ThreadLog.WriteLine("Getting upload data for " + uploadId); #endif #if (DEBUG) //DEBUG mode should support unit tests if ((context == null) || (String.IsNullOrEmpty(uploadId))) { return(new UploadData(String.Empty, String.Empty, false)); } #endif if (context == null) { throw new ArgumentNullException("context", Resources.ExceptionNullContext); } if (String.IsNullOrEmpty(uploadId)) { throw new ArgumentNullException("uploadId", Resources.ExceptionNullOrEmptyUploadId); } //Try first to retrieve upload context data from context cache UploadData objUploadDataRet = HttpRuntime.Cache.Get(UPLOAD_DATA_PREFIX + uploadId) as UploadData; //If upload context data is available in application state/context cache, check user provided download has started if (objUploadDataRet != null) { #if (TRACE) ThreadLog.WriteLine("Upload status is " + objUploadDataRet.ProgressStatus.ToString()); #endif //We need to test for Flash applets, because the Flash applet works in anonymous mode //but passes cookies which may contain the authentication from previous sessions on the web site //implying a failure of the test below if (!context.Request.UrlReferrer.AbsolutePath.ToLowerInvariant().EndsWith(".swf")) { if ((objUploadDataRet.IsAuthenticated != context.User.Identity.IsAuthenticated) || (objUploadDataRet.UserName != context.User.Identity.Name)) { throw new System.Security.SecurityException(Resources.ExceptionUnauthorizedAccessToUploadData); } } } else { //Upload context data could be null, for example when the uploadId requested does not exist #if (TRACE) ThreadLog.WriteLine("Upload data is null"); #endif } return(objUploadDataRet); }
internal void ReportPosition(long contentPosition) { #if (TRACE) ThreadLog.WriteLine("Content position = " + contentPosition.ToString(CultureInfo.InvariantCulture)); #endif _ContentPosition = contentPosition; if (HttpContext.Current != null) //Support for unit tests { System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ContentPosition == contentPosition); System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ProgressStatus == UploadProgressStatus.InProgress); } }
internal void ReportFailed(Exception exception) { #if (TRACE) ThreadLog.WriteLine("Parsing failed: " + exception.Message); #endif _EndDate = DateTime.UtcNow; _ProgressStatus = UploadProgressStatus.Failed; _Exception = exception; if (HttpContext.Current != null) //Support for unit tests { //System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ContentPosition == contentPosition); System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ProgressStatus == UploadProgressStatus.Failed); } }
internal void ReportFileCreated(long contentPosition, string htmlInputID, string fileName, string contentType, object providerFileKey) { #if (TRACE) ThreadLog.WriteLine("New file = " + fileName); #endif _ContentPosition = contentPosition; UploadFile objUploadFile = new UploadFile(htmlInputID, fileName, contentType, providerFileKey); _UploadFiles.Add(objUploadFile); if (HttpContext.Current != null) //Support for unit tests { System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ContentPosition == contentPosition); System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ProgressStatus == UploadProgressStatus.InProgress); } }
internal void ReportStart(long contentLength) { #if (TRACE) ThreadLog.WriteLine("Upload started with Content-Length = " + contentLength.ToString(CultureInfo.InvariantCulture)); #endif _ContentLength = contentLength; _ContentPosition = 0; _StartDate = DateTime.UtcNow; _ProgressStatus = UploadProgressStatus.InProgress; if (HttpContext.Current != null) //Support for unit tests { System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ContentLength == contentLength); System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ProgressStatus == UploadProgressStatus.InProgress); } }
internal void ReportCanceled() { #if (TRACE) ThreadLog.WriteLine("Upload canceled"); #endif _EndDate = DateTime.UtcNow; _ProgressStatus = UploadProgressStatus.Canceled; if (HttpContext.Current != null) //Support for unit tests { //System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ContentPosition == contentPosition); System.Diagnostics.Debug.Assert(((UploadData)HttpContext.Current.Cache.Get(UploadMonitor.UPLOAD_DATA_PREFIX + _UploadId)).ProgressStatus == UploadProgressStatus.Canceled); } HealthMonitoringManager.LogErrorEvent( Resources.ExceptionRequestCanceled, this, UploadRequestErrorEvent.RuntimeErrorRequestAbort, null); }
private void OnPreRequestHandlerExecute(object sender, EventArgs e) { HttpApplication objHttpApplication = sender as HttpApplication; System.Diagnostics.Debug.Assert(objHttpApplication != null); HttpRequest objHttpRequest = objHttpApplication.Request; System.Diagnostics.Debug.Assert(objHttpRequest != null); if (objHttpRequest != null && UploadHttpModule.IsUploadRequest(objHttpRequest)) { #if (TRACE) ThreadLog.WriteLine("Entered OnPreRequestHandlerExecute for an upload request"); #endif HttpContext objHttpContext = objHttpApplication.Context; System.Diagnostics.Debug.Assert(objHttpContext != null); HttpWorkerRequest objHttpWorkerRequest = UploadHttpModule.GetWorkerRequest(objHttpContext); System.Diagnostics.Debug.Assert(objHttpWorkerRequest != null); if (objHttpWorkerRequest != null) { //long lContentLength = objHttpRequest.ContentLength; long lContentLength = long.Parse(objHttpWorkerRequest.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength), CultureInfo.InvariantCulture); if (lContentLength <= 0) //This is for Flash 8 FileReference which tests an empty post before sending a large upload { #if (TRACE) ThreadLog.WriteLine("No content, maybe Flash"); #endif HealthMonitoringManager.LogSucessEvent( Resources.ExceptionZeroContentLength, null, //<- no upload data yet UploadRequestSuccessEvent.RuntimeUploadSuccessZeroContentLength); objHttpApplication.Response.StatusCode = 200; //OK objHttpApplication.CompleteRequest(); //See: http://support.microsoft.com/kb/312629 //objHttpApplication.Response.End(); return; } //Initialize an upload monitor string sUploadID = UploadHttpModule.GetUploadID(objHttpRequest); if (String.IsNullOrEmpty(sUploadID)) { #if (TRACE) ThreadLog.WriteLine("No upload ID"); #endif HttpException objHttpException = new HttpException(400, Resources.ExceptionNullOrEmptyUploadId); HealthMonitoringManager.LogErrorEvent( Resources.ExceptionNullOrEmptyUploadId, null, //<- no upload data yet UploadRequestErrorEvent.RuntimeErrorMissingUploadID, objHttpException); UploadHttpModule.CloseConnectionAfterError(objHttpApplication.Response); throw objHttpException; //See comment in relation with MaxRequestLength here below //objHttpApplication.Response.StatusCode = 400; //Bad request //objHttpApplication.Response.StatusDescription = Resources.ExceptionNullOrEmptyUploadId; //objHttpApplication.Response.Write(String.Format(Resources.Culture, Resources.ErrorPage, 400, Resources.ExceptionNullOrEmptyUploadId)); //objHttpApplication.CompleteRequest(); //See: http://support.microsoft.com/kb/312629 //objHttpApplication.Response.End(); } UploadData objUploadData = UploadMonitor.SetUploadData(objHttpContext, sUploadID); System.Diagnostics.Debug.Assert(objUploadData != null); //Check whether we should read MaxRequestLength from httpRuntime section of web.config (true) bool bMaximizeRequestLength = UploadHttpModule.GetForceHttpMaxRequestLength(objHttpRequest); //Check the upload size and end request if file is too big long lMaxRequestLength = UploadHttpModule.GetMaxRequestLengthBytes(objHttpContext, bMaximizeRequestLength); if ((lMaxRequestLength >= 0) && (lContentLength > lMaxRequestLength)) { #if (TRACE) ThreadLog.WriteLine("Post request is too large"); #endif HttpException objHttpException = new HttpException(413, Resources.ExceptionPostTooLarge); HealthMonitoringManager.LogErrorEvent( Resources.ExceptionPostTooLarge, objUploadData, UploadRequestErrorEvent.RuntimeErrorPostTooLarge, objHttpException); objUploadData.ReportFailed(objHttpException); UploadHttpModule.CloseConnectionAfterError(objHttpApplication.Response); throw objHttpException; //There are 3 possible options //1) Do nothing and let httpRuntime/maxRequestlength do its job //2) Do something like // objHttpApplication.Response.StatusCode = 413; // objHttpApplication.Response.StatusDescription = Resources.ExceptionPostTooLarge; // objHttpApplication.Response.Write(String.Format(Resources.Culture, Resources.ErrorPage, 413, Resources.ExceptionPostTooLarge)); // objHttpApplication.CompleteRequest(); // See: http://support.microsoft.com/kb/312629 // //objHttpApplication.Response.End(); // return; //3) Raise an HttpException //Option 1 is no more an option since we have implemented uploadRuntime //Option 2 sometimes aborts and closes the connection with an IE error page, //sometimes displays a blank page. When the IE page appears, we get //an ERROR_INTERNET_CONNECTION_ABORTED, when the blank page is displayed //the post returns a 413 status code. To get some content we would need //to write to the response _Input using something like objHttpApplication.Response.Write //HttpRequest.GetEntireRawContent implements option 3). Actually it triggers //throw new HttpException(SR.GetString("Max_request_length_exceeded"), null, 0xbbc); //after calling HttpResponse.CloseConnectionAfterError(). In this case an unhdandled //exception is thrown abd we can rely on Application_Error and Custom Errors which //is the best option. } #if (TRACE) ThreadLog.WriteLine("Start parsing upload _Input"); #endif Encoding objEncoding = objHttpRequest.ContentEncoding; string sContentType = objHttpRequest.ContentType; int iPos = sContentType.ToLowerInvariant().IndexOf(Constants.MultiPartBoundary); if (iPos < 0) { #if (TRACE) ThreadLog.WriteLine("Bad request"); #endif HttpException objHttpException = new HttpException(400, Resources.ExceptionMalformedContentType); HealthMonitoringManager.LogErrorEvent( Resources.ExceptionMalformedContentType, objUploadData, UploadRequestErrorEvent.RuntimeErrorMalformedContentType, objHttpException ); objUploadData.ReportFailed(objHttpException); UploadHttpModule.CloseConnectionAfterError(objHttpApplication.Response); throw objHttpException; //See comment in relation with MaxRequestLength here above //objHttpApplication.Response.StatusCode = 400; //Bad request //objHttpApplication.Response.StatusDescription = Resources.ExceptionMultipartBoundaryNotFound; //objHttpApplication.Response.Write(String.Format(Resources.Culture, Resources.ErrorPage, 400, Resources.ExceptionMultipartBoundaryNotFound)); //objHttpApplication.CompleteRequest(); //See: http://support.microsoft.com/kb/312629 //objHttpApplication.Response.End(); //return; } string sMultiPartBoundary = Constants.BoundaryPrefix + sContentType.Substring(iPos + Constants.MultiPartBoundary.Length); #if (TRACE) ThreadLog.WriteLine("Identified boundary = " + sMultiPartBoundary); #endif RequestStream objRequestStream = null; RequestFilter objRequestFilter = null; try { HashAlgorithm objHashAlgorithm = CryptoConfig.CreateFromName(Constants.HashAlgorithmName) as HashAlgorithm; //objHashAlgorithm.Initialize(); Done in RequestFilter objRequestStream = new RequestStream(objHttpWorkerRequest); objRequestFilter = new RequestFilter(objRequestStream, objHashAlgorithm, lContentLength, objEncoding, sMultiPartBoundary, objUploadData); #if (TRACE) ThreadLog.WriteLine("Started parsing"); #endif //Parse the request to filter input files MimeParser objMimeParser = new MimeParser(objRequestFilter); objMimeParser.Parse(); //Get the filtered request byte[] arrFilteredRequest = objRequestFilter.Encoding.GetBytes(objRequestFilter.FilteredRequest); //Redirect the filtered request RedirectFilteredRequest(objHttpApplication, objHttpWorkerRequest, arrFilteredRequest); #if (TRACE) ThreadLog.WriteLine("Filtered request redirected"); #endif HealthMonitoringManager.LogSucessEvent( Resources.MessageUploadCompleted, objUploadData, UploadRequestSuccessEvent.RuntimeUploadSuccessCompleted ); } catch (Exception Ex) { #if (TRACE) ThreadLog.WriteLine("Parsing error"); #endif HealthMonitoringManager.LogErrorEvent( Resources.ExceptionUnhandled + "\r\n" + objHttpWorkerRequest.GetKnownRequestHeader(HttpWorkerRequest.HeaderUserAgent), objUploadData, UploadRequestErrorEvent.RuntimeErrorExceptionUnhandled, Ex); objUploadData.ReportFailed(Ex); UploadHttpModule.CloseConnectionAfterError(objHttpApplication.Response); if ((Ex is HttpException) || (Ex is System.Net.WebException)) { throw; } else { throw new HttpException(500, Resources.ExceptionUnhandled, Ex); } //objHttpApplication.Response.StatusCode = 500; //Error //objHttpApplication.Response.StatusDescription = Resources.ExceptionUnhandled; //objHttpApplication.Response.Write(String.Format(Resources.Culture, Resources.ErrorPage, 500, Ex.Message)); //objHttpApplication.CompleteRequest(); //See: http://support.microsoft.com/kb/312629 //objHttpApplication.Response.End(); } finally { #if (TRACE) ThreadLog.WriteLine("Disposing of resources"); #endif if (objRequestFilter != null) { objRequestFilter.Dispose(); } if (objRequestStream != null) { objRequestStream.Dispose(); } if (objUploadData != null) { if (objUploadData.ProgressStatus != UploadProgressStatus.Completed) { this.DeleteUploadFiles(objUploadData.UploadFiles); } //Too soon to release here: let sliding expiration work for us //UploadMonitor.Release(objHttpContext, objUploadData.UploadId); } } } #if (TRACE) ThreadLog.WriteLine("Exit OnPreRequestHandlerExecute"); #endif } }
public override int Read(byte[] buffer, int offset, int count) { #if (TRACE) ThreadLog.WriteLine(String.Format(CultureInfo.InvariantCulture, "Start reading {0} bytes", count)); #endif if (!_IsOpen) { throw new ObjectDisposedException(null); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset"); } if (count < 0) { throw new ArgumentOutOfRangeException("count"); } if (offset + count > buffer.Length) { throw new ArgumentException(Resources.ExceptionEndOfBufferReached); } int iBytesRead = 0; //bool bRetry = true; //LABEL_RETRY: if (this._IsEntireEntityBodyIsPreloaded) { iBytesRead = this.ReadPreloadedEntityBody(buffer, offset, count); #if (TRACE) ThreadLog.WriteLine(String.Format(CultureInfo.InvariantCulture, "Just read {0} preloaded bytes", iBytesRead)); #endif if (iBytesRead < count) { if (this._HttpWorkerRequest.IsClientConnected() && !this._HttpWorkerRequest.IsEntireEntityBodyIsPreloaded()) { iBytesRead += this.ReadEntityBody(buffer, offset + iBytesRead, count - iBytesRead); #if (TRACE) ThreadLog.WriteLine(String.Format(CultureInfo.InvariantCulture, "Just read {0} more bytes", iBytesRead)); #endif } this._IsEntireEntityBodyIsPreloaded = false; } } else if (this._HttpWorkerRequest.IsClientConnected() && !this._HttpWorkerRequest.IsEntireEntityBodyIsPreloaded()) { iBytesRead = this.ReadEntityBody(buffer, offset, count); #if (TRACE) ThreadLog.WriteLine(String.Format(CultureInfo.InvariantCulture, "Just read {0} bytes", iBytesRead)); #endif } this._Position += iBytesRead; if (iBytesRead == 0) { #if (TRACE) ThreadLog.WriteLine("Houston, we have a problem: no bytes to read ..................."); #endif //if (bRetry) //{ // bRetry = false; // goto LABEL_RETRY; //} //else throw new System.Net.WebException(Resources.ExceptionConnectionClosed, System.Net.WebExceptionStatus.ConnectionClosed); } return(iBytesRead); }
private const int CACHE_SLIDINGEXPIRATION = 1; //in Days #region Other Members #region SetUploadData /// <summary> /// Sets upload data in context cache /// </summary> /// <param name="context"></param> /// <param name="uploadId"></param> /// <returns></returns> public static UploadData SetUploadData(HttpContext context, string uploadId) { #if (TRACE) ThreadLog.WriteLine("Setting upload data with " + uploadId); #endif //Note: //Adding UploadData to HttpContext.Items is not a possible option considering HttpContext.Items are only available during the lifetime and scope of a single request //Adding UploadData to HttpSessionState is a reasonable choice to share data between requests and prevent other users to access uploaded files but the server hangs the connection //So we are left with two options: // 1) storing UploadData in HttpApplicationState. // 2) storing UploadData in HttpRuntime.Cache //Note that HttpApplicationState is a legacy of ASP, and the use of HttpRuntime.Cache is recommended in ASP.NET //Also note: //Because HttpRuntime.Cache is global, we need to prevent unauthorized access and check the context user each time UploadData is requested. #if (DEBUG) //DEBUG mode should support unit tests if ((context == null) || (String.IsNullOrEmpty(uploadId))) { return(new UploadData((String.IsNullOrEmpty(uploadId) ? String.Empty : uploadId), String.Empty, false)); } #endif if (context == null) { throw new ArgumentNullException("context", Resources.ExceptionNullContext); } if (String.IsNullOrEmpty(uploadId)) { throw new ArgumentNullException("uploadId", Resources.ExceptionNullOrEmptyUploadId); } //Needs to be removed before it can be added if (context.Items.Contains(UploadMonitor.UploadIdParam)) { context.Items.Remove(UploadMonitor.UploadIdParam); } context.Items.Add(UploadMonitor.UploadIdParam, uploadId); UploadData objUploadDataRet = new UploadData(uploadId, context.User.Identity.Name, context.User.Identity.IsAuthenticated); if (HttpRuntime.Cache.Get(UPLOAD_DATA_PREFIX + uploadId) != null) { HttpRuntime.Cache.Remove(UPLOAD_DATA_PREFIX + uploadId); } //TODO: Not sure we could create an event callback because we need teh data after the module has completed its part of the request //See: http://blogs.msdn.com/tess/archive/2006/08/11/asp-net-quiz-answers-does-page-cache-leak-memory.aspx HttpRuntime.Cache.Add( UPLOAD_DATA_PREFIX + uploadId, objUploadDataRet, null, Cache.NoAbsoluteExpiration, new TimeSpan(CACHE_SLIDINGEXPIRATION, 0, 0, 0), CacheItemPriority.AboveNormal, //Make it above normal just in case scavenging is triggered because some caching space is needed null ); return(objUploadDataRet); }