예제 #1
0
        private static bool IsActionableRangeRequest(StreamMetadata metadata, IEnumerable <RangeRequest> rangeRequests, string ifRangeEntityTag)
        {
            bool isRangeRequest = RangeRequestHelpers.IsPartialOrMultipleRangeRequests(rangeRequests, metadata.Size.Value);

            return(isRangeRequest &&
                   (string.IsNullOrWhiteSpace(ifRangeEntityTag) ||
                    string.Equals(metadata.ExpectedMD5, ifRangeEntityTag, StringComparison.OrdinalIgnoreCase)));
        }
예제 #2
0
        private StreamWriteStatus BufferStreamToResponse(StreamLoaderResult fileStreamDetails, IEnumerable <RangeRequest> rangeRequests, string contentType)
        {
            bool isMultipart = RangeRequestHelpers.IsMultipartRequest(rangeRequests);
            int  bytesRead;
            long bytesToRead;
            long remainingBytes = fileStreamDetails.Metadata.Size.Value;

            byte[]     buffer  = new byte[this._bufferSizeBytes];
            TextWriter _output = this._response.Output;

            //stream each requested range to the client
            foreach (RangeRequest rangeRequest in rangeRequests)
            {
                fileStreamDetails.FileStream.Seek(rangeRequest.Start, SeekOrigin.Begin);
                bytesToRead = rangeRequest.End - rangeRequest.Start + 1;
                // If this is a multipart response, we must add certain headers before streaming the content:
                if (isMultipart)
                {
                    _output.Write(rangeRequest.GetMultipartIntermediateHeader(contentType));
                }

                while (bytesToRead > 0)
                {
                    if (!this._response.IsClientConnected)
                    {
                        return(StreamWriteStatus.ClientDisconnected);
                    }

                    //buffer length can only be a max of int32 in length -- so we know that this value will always be less than an int32
                    int toRead = Convert.ToInt32(Math.Min(Convert.ToInt64(buffer.Length), bytesToRead));
                    bytesRead = fileStreamDetails.FileStream.Read(buffer, 0, toRead);
                    this._response.OutputStream.Write(buffer, 0, bytesRead);
                    this._response.Flush();
                    bytesToRead -= bytesRead;
                }

                // In Multipart responses, mark the end of the part
                if (isMultipart)
                {
                    _output.WriteLine();
                }
            }

            if (isMultipart)
            {
                _output.Write(_MultipartFooter);
            }
            return(StreamWriteStatus.ApplicationRestarted);
        }
예제 #3
0
        /// <summary>   Stream file. </summary>
        /// <remarks>   ebrown, 2/15/2011. </remarks>
        /// <exception cref="ArgumentNullException">    Thrown when one or more required arguments are null. </exception>
        /// <exception cref="Exception">                Thrown when exception. </exception>
        /// <param name="requestedResponseType">    Type of the requested response. </param>
        /// <param name="downloadProperties">       The download properties. </param>
        /// <param name="rangeRequests">            The range requests. </param>
        /// <param name="ifRangeEntityTag">         if range entity tag. </param>
        /// <param name="forceDownload">            true to force download. </param>
        /// <returns>   . </returns>
        public StreamWriteStatus StreamFile(HttpResponseType requestedResponseType, StreamLoaderResult downloadProperties, IEnumerable <RangeRequest> rangeRequests, string ifRangeEntityTag, bool forceDownload)
        {
            if (null == downloadProperties)
            {
                throw new ArgumentNullException("downloadProperties");
            }
            if (null == rangeRequests)
            {
                throw new ArgumentNullException("rangeRequests");
            }

            try
            {
                string contentType = !string.IsNullOrEmpty(downloadProperties.ContentType) ?
                                     downloadProperties.ContentType : MimeTypes.GetMimeTypeForFileExtension(Path.GetExtension(downloadProperties.Metadata.FileName));

                if (forceDownload)
                {
                    this._response.AppendHeader("content-disposition", String.Format(CultureInfo.InvariantCulture, "attachment; filename={0}", downloadProperties.Metadata.FileName));
                }

                bool isMultipart = RangeRequestHelpers.IsMultipartRequest(rangeRequests),
                     isActionableRangeRequest = IsActionableRangeRequest(downloadProperties.Metadata, rangeRequests, ifRangeEntityTag);

                //TODO: is this response.Clear() necessary here?
                this._response.Clear();

                long responseContentLength = CalculateContentLength(downloadProperties.Metadata, rangeRequests, ifRangeEntityTag, contentType, isMultipart);
                this._response.StatusCode = isActionableRangeRequest ? (int)HttpStatusCode.PartialContent : (int)HttpStatusCode.OK;

                if (isActionableRangeRequest && !isMultipart)
                {
                    var first = rangeRequests.First();
                    //must indicate the Response Range of in the initial HTTP Header since this isn't multipart
                    this._response.AppendHeader(HttpHeaderFields.ContentRange.ToEnumValueString(),
                                                String.Format(CultureInfo.InvariantCulture, "bytes {0}-{1}/{2}", first.Start, first.End, downloadProperties.Metadata.Size.Value));
                }

                this._response.AppendHeader(HttpHeaderFields.ContentLength.ToEnumValueString(), responseContentLength.ToString(CultureInfo.InvariantCulture));
                //don't go off the DB insert date for last modified b/c the file system file could be older (b/c multiple files records inserted at different dates could share the same physical file)
                if (downloadProperties.Metadata.LastWriteTimeUtc.HasValue)
                {
                    this._response.AppendHeader(HttpHeaderFields.LastModified.ToEnumValueString(), downloadProperties.Metadata.LastWriteTimeUtc.Value.ToString("r", CultureInfo.InvariantCulture));
                }
                this._response.AppendHeader(HttpHeaderFields.AcceptRanges.ToEnumValueString(), "bytes");
                this._response.AppendHeader(HttpHeaderFields.EntityTag.ToEnumValueString(), String.Format(CultureInfo.InvariantCulture, "\"{0}\"", downloadProperties.Metadata.ExpectedMD5));
                //not sure if we should use the Keep-Alive header?
                //this._response.AppendHeader(HttpHeaderFields.HTTP_HEADER_KEEP_ALIVE, "timeout=15, max=30");

                //multipart messages are special -> file's actual mime type written into Response later
                this._response.ContentType = (isMultipart ? MultipartNames.MultipartContentType : contentType);

                //we've dumped our HEAD and can return now
                if (HttpResponseType.HeadOnly == requestedResponseType)
                {
                    // Flush the HEAD information to the client...
                    this._response.Flush();

                    return(StreamWriteStatus.SentHttpHead);
                }

                StreamWriteStatus result = BufferStreamToResponse(downloadProperties, rangeRequests, contentType);
                return((StreamWriteStatus.ClientDisconnected == result) ? result : StreamWriteStatus.SentFile);

                //this causes a ThreadAbortException
                //Response.End();
            }
            catch (IOException ex)
            {
                string userName = string.Empty;
                using (var identity = WindowsIdentity.GetCurrent()) { userName = identity.Name; }

                //, this._request.ToLogString()
                log.Error(String.Format(CultureInfo.InvariantCulture, "File read failure for user {0}", userName), ex);
                return(StreamWriteStatus.StreamReadError);
            }
            catch (Exception ex)
            {
                HttpException httpEx = ex as HttpException;
                //suck up the remote connection failure
                if ((null != httpEx) && (HttpExceptionErrorCodes.ConnectionAborted == httpEx.ErrorCode))
                {
                    return(StreamWriteStatus.ClientDisconnected);
                }

                //bubble up to the caller, and let them log it -- this maintains the identity of our original exception as the innerexception
                throw;
                //log.Error("Unexpected failure for " + WindowsIdentity.GetCurrent().Name, ex);

                //error = "Unexpected File Transfer Error - FileId [" + FileId.ToString() + "] - User Id [" + context.OnyxUser().Identity.OCPUser.Individual.OnyxId.ToString() + "] / Name [" + ((userProperties.Name == null) ? string.Empty : userProperties.Name) + "] / Email [" + ((userProperties.Email == null) ? string.Empty : userProperties.Email) + "]" + Environment.NewLine + "File Path [ " + properties.MappedPath + " ]";
                //return DownloadFileStatus.UnexpectedError;
            }
        }