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)
     {
     }
 }
Esempio n. 5
0
        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));
        }