public void ProcessRequest(HttpContextBase context, FileInfo file) { var request = context.Request; var response = context.Response; var fileSettingEntity = _fileEntitySettingProvider.GetSetting(file); var fileEntity = new FileEntity(_retryableFileOpener, _mimeTyper, _hasher, MaxFileSizeToServe, BufferSize, file, fileSettingEntity); if (_webServerType == WebServerType.NotSet || _httpResponseHeaderHelper == null || _httpRequestResponder == null) { Initialize(context); } //We don't want to use up all the servers memory keeping a copy of the file, we just want to stream file to client response.BufferOutput = false; try { _httpRequestResponder.ServeRequest(request, response, fileEntity); } catch (HttpException httpException) { //Client disconnected half way through us sending data if (httpException.ErrorCode != ErrorTheRemoteHostClosedTheConnection) return; throw; } }
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); }
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); }