Beispiel #1
0
 /// <summary>
 /// The transmit entity strategy to use.
 /// </summary>
 /// <param name="fileEntityCacheItem">The cache item.</param>
 /// <returns></returns>
 public ITransmitEntityStrategy GetTransmitEntityStrategy(FileEntityCacheItem fileEntityCacheItem)
 {
     if (fileEntityCacheItem.EntityData == null)
     {
         //Let IIS send file content with TransmitFile
         return(new TransmitEntityStrategyForIIS(fileEntityCacheItem, FileInfo.FullName));
     }
     else
     {
         //We will serve the in memory file
         return(new TransmitEntityStrategyForByteArray(fileEntityCacheItem, fileEntityCacheItem.EntityData));
     }
 }
Beispiel #2
0
        /// <summary>
        /// Get a fileHanderCacheItem for the requested file.
        /// </summary>
        /// <param name="entityStoredWithCompressionType">The compression type to use for the file.</param>
        /// <param name="fileEntityCacheItem">The fileHandlerCacheItem </param>
        /// <returns>Returns true if a fileHandlerCacheItem can be created; otherwise false.</returns>
        public bool TryGetFileHandlerCacheItem(ResponseCompressionType entityStoredWithCompressionType, out FileEntityCacheItem fileEntityCacheItem)
        {
            fileEntityCacheItem = null;

            // If the response bytes are already cached, then deliver the bytes directly from cache
            var cacheKey = FileInfoEntityType + ":" + entityStoredWithCompressionType + ":" + FileInfo.FullName;

            var cachedValue = HttpRuntime.Cache.Get(cacheKey);
            if (cachedValue != null)
            {
                fileEntityCacheItem = (FileEntityCacheItem)cachedValue;
            }
            else
            {
                //File does not exist
                if (!FileInfo.Exists)
                {
                    return false;
                }

                //File too large to send
                if (FileInfo.Length > MaxFileSizeToServe)
                {
                    return false;
                }

                var etag = string.Empty;
                var lastModifiedFileTime = FileInfo.LastWriteTime.ToUniversalTime();
                //When a browser sets the If-Modified-Since field to 13-1-2010 10:30:58, another DateTime instance is created, but this one has a Ticks value of 633989754580000000
                //But the time from the file system is accurate to a tick. So it might be 633989754586086250.
                var lastModified = new DateTime(lastModifiedFileTime.Year, lastModifiedFileTime.Month,
                                                lastModifiedFileTime.Day, lastModifiedFileTime.Hour,
                                                lastModifiedFileTime.Minute, lastModifiedFileTime.Second);
                var contentType = MimeTyper.GetMimeType(FileInfo.Extension);
                var contentLength = FileInfo.Length;

                //ETAG is always calculated from uncompressed entity data
                switch (FileEntitySetting.EtagMethod)
                {
                    case EtagMethodType.MD5:
                        etag = Hasher.CalculateMd5Etag(FileInfo);
                        break;
                    case EtagMethodType.LastModified:
                        etag = lastModified.ToString();
                        break;
                    default:
                        throw new Exception("Unknown etag method generation");
                }

                fileEntityCacheItem = new FileEntityCacheItem
                {
                    Etag = etag,
                    LastModified = lastModified,
                    ContentLength = contentLength,
                    ContentType = contentType,
                    CompressionType = entityStoredWithCompressionType
                };

                if (FileEntitySetting.ServeFromMemory
                    && (contentLength <= FileEntitySetting.MaxMemorySize))
                {
                    // When not compressed, buffer is the size of the file but when compressed,
                    // initial buffer size is one third of the file size. Assuming, compression
                    // will give us less than 1/3rd of the size
                    using (var memoryStream = new MemoryStream(
                        entityStoredWithCompressionType == ResponseCompressionType.None
                            ?
                                Convert.ToInt32(FileInfo.Length)
                            :
                                Convert.ToInt32((double)FileInfo.Length / 3)))
                    {
                        GetEntityData(entityStoredWithCompressionType, memoryStream);
                        var entityData = memoryStream.ToArray();
                        var entityDataLength = entityData.LongLength;

                        fileEntityCacheItem.EntityData = entityData;
                        fileEntityCacheItem.ContentLength = entityDataLength;
                    }
                }

                //Put fileHandlerCacheItem into cache with 30 min sliding expiration, also if file changes then remove fileHandlerCacheItem from cache
                HttpRuntime.Cache.Insert(
                    cacheKey,
                    fileEntityCacheItem,
                    new CacheDependency(FileInfo.FullName),
                    Cache.NoAbsoluteExpiration,
                    FileEntitySetting.MemorySlidingExpiration,
                    CacheItemPriority.BelowNormal,
                    null);
            }

            return true;
        }
Beispiel #3
0
 /// <summary>
 /// The transmit entity strategy to use.
 /// </summary>
 /// <param name="fileEntityCacheItem">The cache item.</param>
 /// <returns></returns>
 public ITransmitEntityStrategy GetTransmitEntityStrategy(FileEntityCacheItem fileEntityCacheItem)
 {
     if (fileEntityCacheItem.EntityData == null)
     {
         //Let IIS send file content with TransmitFile
         return new TransmitEntityStrategyForIIS(fileEntityCacheItem, FileInfo.FullName);
     }
     else
     {
         //We will serve the in memory file
         return new TransmitEntityStrategyForByteArray(fileEntityCacheItem, fileEntityCacheItem.EntityData);
     }
 }
Beispiel #4
0
        /// <summary>
        /// Get a fileHanderCacheItem for the requested file.
        /// </summary>
        /// <param name="entityStoredWithCompressionType">The compression type to use for the file.</param>
        /// <param name="fileEntityCacheItem">The fileHandlerCacheItem </param>
        /// <returns>Returns true if a fileHandlerCacheItem can be created; otherwise false.</returns>
        public bool TryGetFileHandlerCacheItem(ResponseCompressionType entityStoredWithCompressionType, out FileEntityCacheItem fileEntityCacheItem)
        {
            fileEntityCacheItem = null;

            // If the response bytes are already cached, then deliver the bytes directly from cache
            var cacheKey = FileInfoEntityType + ":" + entityStoredWithCompressionType + ":" + FileInfo.FullName;

            var cachedValue = CacheManager.Get <FileEntityCacheItem>(cacheKey);

            if (cachedValue != null)
            {
                fileEntityCacheItem = cachedValue;
            }
            else
            {
                //File does not exist
                if (!FileInfo.Exists)
                {
                    return(false);
                }

                //File too large to send
                if (FileInfo.Length > MaxFileSizeToServe)
                {
                    return(false);
                }

                var etag = string.Empty;
                var lastModifiedFileTime = FileInfo.LastWriteTime.ToUniversalTime();
                //When a browser sets the If-Modified-Since field to 13-1-2010 10:30:58, another DateTime instance is created, but this one has a Ticks value of 633989754580000000
                //But the time from the file system is accurate to a tick. So it might be 633989754586086250.
                var lastModified = new DateTime(lastModifiedFileTime.Year, lastModifiedFileTime.Month,
                                                lastModifiedFileTime.Day, lastModifiedFileTime.Hour,
                                                lastModifiedFileTime.Minute, lastModifiedFileTime.Second);
                var contentType   = MimeTyper.GetMimeType(FileInfo.Extension);
                var contentLength = FileInfo.Length;

                //ETAG is always calculated from uncompressed entity data
                switch (FileEntitySetting.EtagMethod)
                {
                case EtagMethodType.MD5:
                    etag = Hasher.CalculateMd5Etag(FileInfo);
                    break;

                case EtagMethodType.LastModified:
                    etag = lastModified.ToString();
                    break;

                default:
                    throw new Exception("Unknown etag method generation");
                }

                fileEntityCacheItem = new FileEntityCacheItem
                {
                    Etag            = etag,
                    LastModified    = lastModified,
                    ContentLength   = contentLength,
                    ContentType     = contentType,
                    CompressionType = entityStoredWithCompressionType
                };

                if (FileEntitySetting.ServeFromMemory &&
                    (contentLength <= FileEntitySetting.MaxMemorySize))
                {
                    // When not compressed, buffer is the size of the file but when compressed,
                    // initial buffer size is one third of the file size. Assuming, compression
                    // will give us less than 1/3rd of the size
                    using (var memoryStream = new MemoryStream(
                               entityStoredWithCompressionType == ResponseCompressionType.None
                            ?
                               Convert.ToInt32(FileInfo.Length)
                            :
                               Convert.ToInt32((double)FileInfo.Length / 3)))
                    {
                        GetEntityData(entityStoredWithCompressionType, memoryStream);
                        var entityData       = memoryStream.ToArray();
                        var entityDataLength = entityData.LongLength;

                        fileEntityCacheItem.EntityData    = entityData;
                        fileEntityCacheItem.ContentLength = entityDataLength;
                    }
                }

                //Put fileHandlerCacheItem into cache with 30 min sliding expiration, also if file changes then remove fileHandlerCacheItem from cache
                CacheManager.Insert(
                    cacheKey,
                    fileEntityCacheItem,
                    new CacheDependency(FileInfo.FullName),
                    Cache.NoAbsoluteExpiration,
                    FileEntitySetting.MemorySlidingExpiration,
                    CacheItemPriority.BelowNormal,
                    null);
            }

            return(true);
        }
Beispiel #5
0
        public void ServeRequest(HttpRequestBase request, HttpResponseBase response, FileEntity fileEntity)
        {
            if (!IsHttpMethodAllowed(request))
            {
                //If we are unable to parse url send 405 Method not allowed
                HttpResponseHeaderHelper.SendHttpStatusHeaders(response, HttpStatusCode.MethodNotAllowed);
                return;
            }

            if (!fileEntity.IsAllowedToServeRequestedEntity)
            {
                //If we are unable to parse url send 403 Path Forbidden
                HttpResponseHeaderHelper.SendHttpStatusHeaders(response, HttpStatusCode.Forbidden);
                return;
            }

            var requestHttpMethod = HttpRequestHeaderHelper.GetHttpMethod(request);

            var compressionType = HttpRequestHeaderHelper.GetCompressionMode(request);

            // If this is a binary file like image, then we won't compress it.
            if (!fileEntity.IsCompressable)
            {
                compressionType = ResponseCompressionType.None;
            }

            // If it is a partial request we need to get bytes of orginal entity data, we will compress the byte ranges returned
            var entityStoredWithCompressionType = compressionType;
            var isRangeRequest = HttpRequestHeaderHelper.IsRangeRequest(request);

            if (isRangeRequest)
            {
                entityStoredWithCompressionType = ResponseCompressionType.None;
            }

            FileEntityCacheItem fileEntityCacheItem = null;

            if (!fileEntity.TryGetFileHandlerCacheItem(entityStoredWithCompressionType, out fileEntityCacheItem))
            {
                //File does not exist
                if (!fileEntity.DoesEntityExists)
                {
                    HttpResponseHeaderHelper.SendHttpStatusHeaders(response, HttpStatusCode.NotFound);
                    return;
                }

                //File too large to send
                if (fileEntity.IsEntityLargerThanMaxFileSize)
                {
                    HttpResponseHeaderHelper.SendHttpStatusHeaders(response, HttpStatusCode.RequestEntityTooLarge);
                    return;
                }
            }
            else if (fileEntityCacheItem.EntityData == null && !fileEntity.DoesEntityExists)
            {
                //If we have cached the properties of the file but its to large to serve from memory then we must check that the file exists each time.
                HttpResponseHeaderHelper.SendHttpStatusHeaders(response, HttpStatusCode.NotFound);
                return;
            }

            //Unable to parse request range header
            IEnumerable <RangeItem> ranges = null;
            var requestRange = HttpRequestHeaderHelper.GetRanges(request, fileEntityCacheItem.ContentLength, out ranges);

            if (requestRange.HasValue && !requestRange.Value)
            {
                HttpResponseHeaderHelper.SendHttpStatusHeaders(response, HttpStatusCode.RequestedRangeNotSatisfiable);
                return;
            }

            //Check querystring etag
            var urlEtagHandler = GetUrlEtagHandler(request, response, fileEntity.UrlEtagHandlingMethod, fileEntity.UrlEtagQuerystringName, fileEntityCacheItem.Etag);

            if (urlEtagHandler != null)
            {
                var shouldStopResponse = urlEtagHandler.UpdateEtag(response, request.Url, fileEntityCacheItem.Etag);
                if (shouldStopResponse)
                {
                    return;
                }
            }

            //Check if cached response is valid and if it is send appropriate response headers
            var httpStatus = GetResponseHttpStatus(request, fileEntityCacheItem.LastModified, fileEntityCacheItem.Etag);

            HttpResponseHeaderHelper.SendHttpStatusHeaders(response, httpStatus);

            if (httpStatus == HttpStatusCode.NotModified || httpStatus == HttpStatusCode.PreconditionFailed)
            {
                return;
            }

            //Tell the client it supports resumable requests
            HttpResponseHeaderHelper.SetResponseResumable(response);

            //How the entity should be cached on the client
            HttpResponseHeaderHelper.SetResponseCachable(response, DateTime.Now, fileEntityCacheItem.LastModified, fileEntityCacheItem.Etag, fileEntity.Expires);

            var entityResponseForEntity = GetEntityResponse(response, ranges);

            entityResponseForEntity.SendHeaders(response, compressionType, fileEntityCacheItem);

            var transmitEntity = fileEntity.GetTransmitEntityStrategy(fileEntityCacheItem);

            entityResponseForEntity.SendBody(requestHttpMethod, response, transmitEntity);
        }