/// <summary> /// Processes the image. /// </summary> /// <param name="context"> /// the <see cref="T:System.Web.HttpContext">HttpContext</see> object that provides /// references to the intrinsic server objects /// </param> /// <returns> /// The <see cref="T:System.Threading.Tasks.Task"/>. /// </returns> private async Task ProcessImageAsync(HttpContext context) { HttpRequest request = context.Request; bool isRemote = request.Path.Equals(RemotePrefix, StringComparison.OrdinalIgnoreCase); string requestPath = string.Empty; string queryString = string.Empty; if (isRemote) { // We need to split the querystring to get the actual values we want. string urlDecode = HttpUtility.UrlDecode(request.QueryString.ToString()); if (urlDecode != null) { string[] paths = urlDecode.Split('?'); requestPath = paths[0]; if (paths.Length > 1) { queryString = paths[1]; } } } else { requestPath = HostingEnvironment.MapPath(request.Path); queryString = HttpUtility.UrlDecode(request.QueryString.ToString()); } // Only process requests that pass our sanitizing filter. if (ImageUtils.IsValidImageExtension(requestPath) && !string.IsNullOrWhiteSpace(queryString)) { string fullPath = string.Format("{0}?{1}", requestPath, queryString); string imageName = Path.GetFileName(requestPath); // Create a new cache to help process and cache the request. DiskCache cache = new DiskCache(request, requestPath, fullPath, imageName, isRemote); // Is the file new or updated? bool isNewOrUpdated = await cache.IsNewOrUpdatedFileAsync(); // Only process if the file has been updated. if (isNewOrUpdated) { // Process the image. using (ImageFactory imageFactory = new ImageFactory()) { if (isRemote) { Uri uri = new Uri(requestPath); RemoteFile remoteFile = new RemoteFile(uri, false); // Prevent response blocking. WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false); using (MemoryStream memoryStream = new MemoryStream()) { using (WebResponse response = webResponse) { using (Stream responseStream = response.GetResponseStream()) { if (responseStream != null) { // Trim the cache. await cache.TrimCachedFoldersAsync(); responseStream.CopyTo(memoryStream); imageFactory.Load(memoryStream) .AddQueryString(queryString) .Format(ImageUtils.GetImageFormat(imageName)) .AutoProcess().Save(cache.CachedPath); // Ensure that the LastWriteTime property of the source and cached file match. DateTime dateTime = await cache.SetCachedLastWriteTimeAsync(); // Add to the cache. await cache.AddImageToCacheAsync(dateTime); } } } } } else { // Trim the cache. await cache.TrimCachedFoldersAsync(); imageFactory.Load(fullPath).AutoProcess().Save(cache.CachedPath); // Ensure that the LastWriteTime property of the source and cached file match. DateTime dateTime = await cache.SetCachedLastWriteTimeAsync(); // Add to the cache. await cache.AddImageToCacheAsync(dateTime); } } } // Store the response type in the context for later retrieval. context.Items[CachedResponseTypeKey] = ImageUtils.GetResponseType(fullPath).ToDescription(); // The cached file is valid so just rewrite the path. context.RewritePath(cache.GetVirtualCachedPath(), false); } }
private async Task ProcessImageAsync(HttpContext context) { HttpRequest request = context.Request; // Fixes issue 10. bool isRemote = request.Path.EndsWith(remotePrefix, StringComparison.OrdinalIgnoreCase); string requestPath = string.Empty; string queryString = string.Empty; bool validExtensionLessUrl = false; string urlParameters = ""; string extensionLessExtension = ""; if (isRemote) { // We need to split the querystring to get the actual values we want. string urlDecode = HttpUtility.UrlDecode(request.QueryString.ToString()); if (!string.IsNullOrWhiteSpace(urlDecode)) { // UrlDecode seems to mess up in some circumstance. if (urlDecode.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1) { urlDecode = urlDecode.Replace(":/", "://"); } string[] paths = urlDecode.Split('?'); requestPath = paths[0]; // Handle extension-less urls. if (paths.Count() > 2) { queryString = paths[2]; urlParameters = paths[1]; } else if (paths.Length > 1) { queryString = paths[1]; } validExtensionLessUrl = RemoteFile.RemoteFileWhiteListExtensions.Any( x => x.ExtensionLess && requestPath.StartsWith(x.Url.AbsoluteUri)); if (validExtensionLessUrl) { extensionLessExtension = RemoteFile.RemoteFileWhiteListExtensions.First( x => x.ExtensionLess && requestPath.StartsWith(x.Url.AbsoluteUri)).ImageFormat; } } } else { requestPath = HostingEnvironment.MapPath(request.Path); queryString = HttpUtility.UrlDecode(request.QueryString.ToString()); } // Only process requests that pass our sanitizing filter. if ((ImageHelpers.IsValidImageExtension(requestPath) || validExtensionLessUrl) && !string.IsNullOrWhiteSpace(queryString)) { // Replace any presets in the querystring with the actual value. queryString = this.ReplacePresetsInQueryString(queryString); string fullPath = string.Format("{0}?{1}", requestPath, queryString); string imageName = Path.GetFileName(requestPath); if (validExtensionLessUrl && !string.IsNullOrWhiteSpace(extensionLessExtension)) { fullPath = requestPath; if (!string.IsNullOrWhiteSpace(urlParameters)) { string hashedUrlParameters = urlParameters.ToMD5Fingerprint(); // TODO: Add hash for querystring parameters. imageName += hashedUrlParameters; fullPath += hashedUrlParameters; } imageName += "." + extensionLessExtension; fullPath += extensionLessExtension + "?" + queryString; } // Create a new cache to help process and cache the request. DiskCache cache = new DiskCache(request, requestPath, fullPath, imageName, isRemote); // Since we are now rewriting the path we need to check again that the current user has access // to the rewritten path. // Get the user for the current request // If the user is anonymous or authentication doesn't work for this suffix avoid a NullReferenceException // in the UrlAuthorizationModule by creating a generic identity. string virtualCachedPath = cache.GetVirtualCachedPath(); IPrincipal user = context.User ?? new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]); // Do we have permission to call UrlAuthorizationModule.CheckUrlAccessForPrincipal? PermissionSet permission = new PermissionSet(PermissionState.None); permission.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted)); bool hasPermission = permission.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet); bool isAllowed = true; // Run the rewritten path past the auth system again, using the result as the default "AllowAccess" value if (hasPermission && !context.SkipAuthorization) { isAllowed = UrlAuthorizationModule.CheckUrlAccessForPrincipal(virtualCachedPath, user, "GET"); } if (isAllowed) { // Is the file new or updated? bool isNewOrUpdated = await cache.IsNewOrUpdatedFileAsync(); // Only process if the file has been updated. if (isNewOrUpdated) { string cachedPath = cache.CachedPath; // Process the image. using (ImageFactory imageFactory = new ImageFactory(preserveExifMetaData != null && preserveExifMetaData.Value)) { if (isRemote) { Uri uri = new Uri(requestPath + "?" + urlParameters); RemoteFile remoteFile = new RemoteFile(uri, false); // Prevent response blocking. WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false); SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath); try { semaphore.Wait(); using (MemoryStream memoryStream = new MemoryStream()) { using (WebResponse response = webResponse) { using (Stream responseStream = response.GetResponseStream()) { if (responseStream != null) { responseStream.CopyTo(memoryStream); // Process the Image imageFactory.Load(memoryStream) .AddQueryString(queryString) .AutoProcess() .Save(cachedPath); // Store the response type in the context for later retrieval. context.Items[CachedResponseTypeKey] = imageFactory.MimeType; // Add to the cache. cache.AddImageToCache(); // Trim the cache. await cache.TrimCachedFolderAsync(cachedPath); } } } } } finally { semaphore.Release(); } } else { // Check to see if the file exists. // ReSharper disable once AssignNullToNotNullAttribute FileInfo fileInfo = new FileInfo(requestPath); if (!fileInfo.Exists) { throw new HttpException(404, "No image exists at " + fullPath); } SemaphoreSlim semaphore = GetSemaphoreSlim(cachedPath); try { semaphore.Wait(); // Process the Image imageFactory.Load(fullPath).AutoProcess().Save(cachedPath); // Store the response type in the context for later retrieval. context.Items[CachedResponseTypeKey] = imageFactory.MimeType; // Add to the cache. cache.AddImageToCache(); // Trim the cache. await cache.TrimCachedFolderAsync(cachedPath); } finally { semaphore.Release(); } } } } string incomingEtag = context.Request.Headers["If-None-Match"]; if (incomingEtag != null && !isNewOrUpdated) { // Explicitly set the Content-Length header so the client doesn't wait for // content but keeps the connection open for other requests context.Response.AddHeader("Content-Length", "0"); context.Response.StatusCode = (int)HttpStatusCode.NotModified; context.Response.SuppressContent = true; context.Response.AddFileDependency(context.Server.MapPath(cache.GetVirtualCachedPath())); this.SetHeaders(context, (string)context.Items[CachedResponseTypeKey]); if (!isRemote) { return; } } string virtualPath = cache.GetVirtualCachedPath(); // The cached file is valid so just rewrite the path. context.RewritePath(virtualPath, false); } else { throw new HttpException(403, "Access denied"); } } else if (isRemote) { // Just repoint to the external url. HttpContext.Current.Response.Redirect(requestPath); } }
/// <summary> /// Processes the image. /// </summary> /// <param name="context"> /// the <see cref="T:System.Web.HttpContext">HttpContext</see> object that provides /// references to the intrinsic server objects /// </param> /// <returns> /// The <see cref="T:System.Threading.Tasks.Task"/>. /// </returns> private async Task ProcessImageAsync(HttpContext context) { HttpRequest request = context.Request; // Fixes issue 10. bool isRemote = request.Path.EndsWith(RemotePrefix, StringComparison.OrdinalIgnoreCase); string requestPath = string.Empty; string queryString = string.Empty; if (isRemote) { // We need to split the querystring to get the actual values we want. string urlDecode = HttpUtility.UrlDecode(request.QueryString.ToString()); if (!string.IsNullOrWhiteSpace(urlDecode)) { // UrlDecode seems to mess up in some circumstance. if (urlDecode.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1) { urlDecode = urlDecode.Replace(":/", "://"); } string[] paths = urlDecode.Split('?'); requestPath = paths[0]; if (paths.Length > 1) { queryString = paths[1]; } } } else { requestPath = HostingEnvironment.MapPath(request.Path); queryString = HttpUtility.UrlDecode(request.QueryString.ToString()); } // Only process requests that pass our sanitizing filter. if (ImageUtils.IsValidImageExtension(requestPath) && !string.IsNullOrWhiteSpace(queryString)) { string fullPath = string.Format("{0}?{1}", requestPath, queryString); string imageName = Path.GetFileName(requestPath); // Create a new cache to help process and cache the request. DiskCache cache = new DiskCache(request, requestPath, fullPath, imageName, isRemote); // Since we are now rewriting the path we need to check again that the current user has access // to the rewritten path. // Get the user for the current request // If the user is anonymous or authentication doesn't work for this suffix avoid a NullReferenceException // in the UrlAuthorizationModule by creating a generic identity. string virtualCachedPath = cache.GetVirtualCachedPath(); IPrincipal user = context.User ?? new GenericPrincipal(new GenericIdentity(string.Empty, string.Empty), new string[0]); // Do we have permission to call UrlAuthorizationModule.CheckUrlAccessForPrincipal? PermissionSet permission = new PermissionSet(PermissionState.None); permission.AddPermission(new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted)); bool hasPermission = permission.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet); bool isAllowed = true; // Run the rewritten path past the auth system again, using the result as the default "AllowAccess" value if (hasPermission && !context.SkipAuthorization) { isAllowed = UrlAuthorizationModule.CheckUrlAccessForPrincipal(virtualCachedPath, user, "GET"); } if (isAllowed) { // Is the file new or updated? bool isNewOrUpdated = await cache.IsNewOrUpdatedFileAsync(); // Only process if the file has been updated. if (isNewOrUpdated) { // Process the image. using (ImageFactory imageFactory = new ImageFactory()) { if (isRemote) { Uri uri = new Uri(requestPath); RemoteFile remoteFile = new RemoteFile(uri, false); // Prevent response blocking. WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false); using (MemoryStream memoryStream = new MemoryStream()) { using (WebResponse response = webResponse) { using (Stream responseStream = response.GetResponseStream()) { if (responseStream != null) { // Trim the cache. await cache.TrimCachedFoldersAsync(); responseStream.CopyTo(memoryStream); imageFactory.Load(memoryStream) .AddQueryString(queryString) .Format(ImageUtils.GetImageFormat(imageName)) .AutoProcess().Save(cache.CachedPath); // Ensure that the LastWriteTime property of the source and cached file match. DateTime dateTime = await cache.SetCachedLastWriteTimeAsync(); // Add to the cache. await cache.AddImageToCacheAsync(dateTime); } } } } } else { // Trim the cache. await cache.TrimCachedFoldersAsync(); imageFactory.Load(fullPath).AutoProcess().Save(cache.CachedPath); // Ensure that the LastWriteTime property of the source and cached file match. DateTime dateTime = await cache.SetCachedLastWriteTimeAsync(); // Add to the cache. await cache.AddImageToCacheAsync(dateTime); } } } // Store the response type in the context for later retrieval. context.Items[CachedResponseTypeKey] = ImageUtils.GetResponseType(fullPath).ToDescription(); // The cached file is valid so just rewrite the path. context.RewritePath(cache.GetVirtualCachedPath(), false); } else { throw new HttpException(403, "Access denied"); } } }
/// <summary> /// Gets the image using the given identifier. /// </summary> /// <param name="id"> /// The value identifying the image to fetch. /// </param> /// <returns> /// The <see cref="System.Byte"/> array containing the image data. /// </returns> public async Task<byte[]> GetImage(object id) { Uri uri = new Uri(id.ToString()); RemoteFile remoteFile = new RemoteFile(uri) { MaxDownloadSize = int.Parse(this.Settings["MaxBytes"]), TimeoutLength = int.Parse(this.Settings["Timeout"]) }; byte[] buffer; // Prevent response blocking. WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false); using (MemoryStream memoryStream = new MemoryStream()) { using (WebResponse response = webResponse) { using (Stream responseStream = response.GetResponseStream()) { if (responseStream != null) { responseStream.CopyTo(memoryStream); // Reset the position of the stream to ensure we're reading the correct part. memoryStream.Position = 0; buffer = memoryStream.ToArray(); } else { throw new HttpException(404, "No image exists at " + uri); } } } } return buffer; }
/// <summary> /// Gets the image using the given identifier. /// </summary> /// <param name="id"> /// The value identifying the image to fetch. /// </param> /// <returns> /// The <see cref="System.Byte"/> array containing the image data. /// </returns> public async Task<byte[]> GetImage(object id) { string host = this.Settings["Host"]; string container = this.Settings.ContainsKey("Container") ? this.Settings["Container"] : string.Empty; Uri baseUri = new Uri(host); string relativeResourceUrl = id.ToString(); if (!string.IsNullOrEmpty(container)) { container = container.TrimEnd('/') + "/"; if (!relativeResourceUrl.StartsWith(container + "/")) { relativeResourceUrl = container + relativeResourceUrl; } } Uri uri = new Uri(baseUri, relativeResourceUrl); RemoteFile remoteFile = new RemoteFile(uri) { MaxDownloadSize = int.Parse(this.Settings["MaxBytes"]), TimeoutLength = int.Parse(this.Settings["Timeout"]) }; byte[] buffer; // Prevent response blocking. WebResponse webResponse = await remoteFile.GetWebResponseAsync().ConfigureAwait(false); using (MemoryStream memoryStream = new MemoryStream()) { using (WebResponse response = webResponse) { using (Stream responseStream = response.GetResponseStream()) { if (responseStream != null) { responseStream.CopyTo(memoryStream); // Reset the position of the stream to ensure we're reading the correct part. memoryStream.Position = 0; buffer = memoryStream.ToArray(); } else { throw new HttpException((int)HttpStatusCode.NotFound, "No image exists at " + uri); } } } } return buffer; }