public void ShouldThrow_BadFilter() { try { UrlParser.ParseUrl("/process/filterfilter/1,2,3,4"); Assert.Fail(); } catch (InvalidOperationException e) { } }
public void ShouldNotParse_BadParamThreshold() { try { UrlParser.ParseUrl("/process/threshold(-20)/1,1,2,2"); Assert.Fail(); } catch (InvalidOperationException e) { } }
public void ShouldThrow_BadCoords() { try { UrlParser.ParseUrl("/process/grayscale/bla,bla,bla"); Assert.Fail(); } catch (InvalidOperationException e) { } }
public void ShouldThrow_BadUrl() { try { UrlParser.ParseUrl("/hello/world"); Assert.Fail(); } catch (InvalidOperationException e) { } }
public Request(HttpListenerRequest req, string MountUrl) { this.Context = req; this.Method = req.HttpMethod; UrlParser urlParser = new UrlParser(MountUrl); UrlSpec urlSpec = urlParser.ParseUrl(this.Context); this.RawUrl = urlSpec.RawUrl; this.OriginalUrl = urlSpec.OriginalUrl; this.Query = urlSpec.Query; this.ReadInputStream(); }
/// <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; string rawUrl = this.GetRequestUrl(request); // Should we ignore this request? if (string.IsNullOrWhiteSpace(rawUrl) || rawUrl.IndexOf("IPIGNORE=TRUE", StringComparison.InvariantCultureIgnoreCase) >= 0) { return; } // Sometimes the request is url encoded // See https://github.com/JimBobSquarePants/ImageProcessor/issues/478 // This causes a bit of a nightmare as the incoming request is corrupted and cannot be used for splitting // out each url part. This becomes a manual job. string url = rawUrl; string applicationPath = request.ApplicationPath; IImageService currentService = this.GetImageServiceForRequest(url, applicationPath); if (currentService == null) { return; } // Parse url UrlParser.ParseUrl(url, currentService.Prefix, out string requestPath, out string queryString); string originalQueryString = queryString; // Replace any presets in the querystring with the actual value. queryString = this.ReplacePresetsInQueryString(queryString); var httpContextBase = new HttpContextWrapper(context); // Execute the handler which can change the querystring var validatingArgs = new ValidatingRequestEventArgs(httpContextBase, queryString); this.OnValidatingRequest(validatingArgs); // If the validation has failed based on events, return if (validatingArgs.Cancel) { ImageProcessorBootstrapper.Instance.Logger.Log <ImageProcessingModule>($"Image processing for {url} has been cancelled by an event"); return; } // Re-assign based on event handlers queryString = validatingArgs.QueryString; if (string.IsNullOrWhiteSpace(originalQueryString) && !string.IsNullOrWhiteSpace(queryString)) { url = url + "?" + queryString; } else if (!string.IsNullOrWhiteSpace(queryString)) { url = Regex.Replace(url, originalQueryString, queryString, RegexOptions.IgnoreCase); } // Map the request path if file local. bool isFileLocal = currentService.IsFileLocalService; if (currentService.IsFileLocalService) { requestPath = HostingEnvironment.MapPath(requestPath); } if (string.IsNullOrWhiteSpace(requestPath)) { return; } // Parse any protocol values from settings if no protocol is present. if (currentService.Settings.ContainsKey("Protocol") && (ProtocolRegex.Matches(requestPath).Count == 0 || ProtocolRegex.Matches(requestPath)[0].Index > 0)) { // ReSharper disable once PossibleNullReferenceException requestPath = currentService.Settings["Protocol"] + "://" + requestPath.TrimStart('/'); } // Break out if we don't meet critera. // First check that the request path is valid and whether we are intercepting all requests or the querystring is valid. bool interceptAll = interceptAllRequests != null && interceptAllRequests.Value; if (string.IsNullOrWhiteSpace(requestPath) || (!interceptAll && string.IsNullOrWhiteSpace(queryString))) { return; } // Check whether the path is valid for other requests. // We've already checked the unprefixed requests in GetImageServiceForRequest(). if (!string.IsNullOrWhiteSpace(currentService.Prefix) && !currentService.IsValidRequest(requestPath)) { return; } bool isNewOrUpdated = false; string cachedPath = string.Empty; bool processing = false; IWebGraphicsProcessor[] processors = null; AnimationProcessMode mode = AnimationProcessMode.First; using (await Locker.ReaderLockAsync(rawUrl).ConfigureAwait(false)) { // Parse the url to see whether we should be doing any work. // If we're not intercepting all requests and we don't have valid instructions we shoul break here. if (!string.IsNullOrWhiteSpace(queryString)) { // Attempt to match querystring and processors. processors = ImageFactoryExtensions.GetMatchingProcessors(queryString); // Animation is not a processor but can be a specific request so we should allow it. mode = this.ParseAnimationMode(queryString, out bool processAnimation); // Are we processing or cache busting? processing = processors != null && (processors.Length > 0 || processAnimation); bool cacheBusting = ParseCacheBuster(queryString); if (!processing && !cacheBusting) { // No? Someone is either attacking the server or hasn't read the instructions. string message = $"The request {request.Unvalidated.RawUrl} could not be understood by the server due to malformed syntax."; ImageProcessorBootstrapper.Instance.Logger.Log <ImageProcessingModule>(message); return; } } // Create a new cache to help process and cache the request. this.imageCache = (IImageCache)ImageProcessorConfiguration.Instance .ImageCache.GetInstance(requestPath, url, queryString); // Is the file new or updated? isNewOrUpdated = await this.imageCache.IsNewOrUpdatedAsync().ConfigureAwait(false); cachedPath = this.imageCache.CachedPath; if (!isNewOrUpdated) { // The cached file is valid so just rewrite the path. this.imageCache.RewritePath(context); // Redirect if not a locally store file. if (!new Uri(cachedPath).IsFile) { context.ApplicationInstance.CompleteRequest(); } return; } } // Only process if the file has been updated. using (await Locker.WriterLockAsync(rawUrl).ConfigureAwait(false)) { // Ok let's get the image byte[] imageBuffer = null; string mimeType; try { if (currentService is IImageService2 imageService2) { imageBuffer = await imageService2.GetImage(requestPath, context).ConfigureAwait(false); } else { imageBuffer = await currentService.GetImage(requestPath).ConfigureAwait(false); } } catch (HttpException ex) { // We want 404's to be handled by IIS so that other handlers/modules can still run. if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound) { ImageProcessorBootstrapper.Instance.Logger.Log <ImageProcessingModule>(ex.Message); return; } } if (imageBuffer == null) { return; } // Using recyclable streams here should dramatically reduce the overhead required using (MemoryStream inStream = MemoryStreamPool.Shared.GetStream("inStream", imageBuffer, 0, imageBuffer.Length)) { // Process the Image. Use a recyclable stream here to reduce the allocations MemoryStream outStream = MemoryStreamPool.Shared.GetStream(); if (!string.IsNullOrWhiteSpace(queryString)) { if (processing) { // Process the image. bool exif = preserveExifMetaData != null && preserveExifMetaData.Value; MetaDataMode metaMode = !exif ? MetaDataMode.None : metaDataMode.Value; bool gamma = fixGamma != null && fixGamma.Value; try { using (var imageFactory = new ImageFactory(metaMode, gamma) { AnimationProcessMode = mode }) { imageFactory.Load(inStream).AutoProcess(processors).Save(outStream); mimeType = imageFactory.CurrentImageFormat.MimeType; } } catch (ImageFormatException) { ImageProcessorBootstrapper.Instance.Logger.Log <ImageProcessingModule>($"Request {url} is not a valid image."); return; } } else { // We're cache-busting. Allow the value to be cached await inStream.CopyToAsync(outStream).ConfigureAwait(false); mimeType = FormatUtilities.GetFormat(outStream).MimeType; } } else { // We're capturing all requests. await inStream.CopyToAsync(outStream).ConfigureAwait(false); mimeType = FormatUtilities.GetFormat(outStream).MimeType; } // Fire the post processing event. EventHandler <PostProcessingEventArgs> handler = OnPostProcessing; if (handler != null) { string extension = Path.GetExtension(cachedPath); var args = new PostProcessingEventArgs { Context = context, ImageStream = outStream, ImageExtension = extension }; handler(this, args); outStream = args.ImageStream; } // Add to the cache. await this.imageCache.AddImageToCacheAsync(outStream, mimeType).ConfigureAwait(false); // Cleanup outStream.Dispose(); } // Store the response type and cache dependency in the context for later retrieval. context.Items[CachedResponseTypeKey] = mimeType; bool isFileCached = new Uri(cachedPath).IsFile; if (isFileLocal) { if (isFileCached) { // Some services might only provide filename so we can't monitor for the browser. context.Items[CachedResponseFileDependency] = Path.GetFileName(requestPath) == requestPath ? new[] { cachedPath } : new[] { requestPath, cachedPath }; } else { context.Items[CachedResponseFileDependency] = Path.GetFileName(requestPath) == requestPath ? null : new[] { requestPath }; } } else if (isFileCached) { context.Items[CachedResponseFileDependency] = new[] { cachedPath }; } // The cached file has been saved so now rewrite the path. this.imageCache.RewritePath(context); // Redirect if not a locally store file. if (!new Uri(cachedPath).IsFile) { context.ApplicationInstance.CompleteRequest(); } // Trim the cache. await this.imageCache.TrimCacheAsync().ConfigureAwait(false); } }
public void ShouldParseCorrectly_ThresholdParam() { var requestParams = UrlParser.ParseUrl("/process/threshold(20)/1,1,2,2"); Assert.That(requestParams.Filter.FilterParam, Is.EqualTo(20)); }
public void ShouldParse_GoodUrlThreshold() { var requestParams = UrlParser.ParseUrl("/process/threshold(20)/1,1,2,2"); Assert.That(requestParams.Filter.FilterType, Is.EqualTo(FilterType.Threshold)); }
public void ShouldParse_GoodUrl() { var requestParams = UrlParser.ParseUrl("/process/grayscale/1,1,2,2"); Assert.That(requestParams.Filter.FilterType, Is.EqualTo(FilterType.Grayscale)); }