Example #1
0
        private async Task ResizeSourceImage(byte[] sourceBytes, UrlParserResult urlParserResult)
        {
            string preProcessedFile     = null;
            string postProcessorCommand = GetPostProcessorCommand(urlParserResult);

            if (!string.IsNullOrEmpty(postProcessorCommand))
            {
                preProcessedFile = Regex.Replace(urlParserResult.Destination, @"\.(gif|jpe?g|png)$", new MatchEvaluator((match) => { return(".tmp" + match.Value); }), RegexOptions.IgnoreCase);
            }

            ImageFit.IEncoder encoder = GetEncoder(urlParserResult);

            Action <byte[], string, int, int, ImageFit.IEncoder> action;
            ResizeMethod resizeMethod = urlParserResult.ResizeMethod ?? ResizeMethod.ScaleDown;

            switch (resizeMethod)
            {
            case ResizeMethod.Contain: action = ImageFit.Image.Contain; break;

            case ResizeMethod.Cover: action = ImageFit.Image.Cover; break;

            case ResizeMethod.Fill: action = ImageFit.Image.Fill; break;

            case ResizeMethod.ScaleDown: action = ImageFit.Image.ScaleDown; break;

            default: throw new NotSupportedException();
            }
            action(sourceBytes, preProcessedFile ?? urlParserResult.Destination, urlParserResult.Width, urlParserResult.Height, encoder);


            if (!string.IsNullOrEmpty(postProcessorCommand))
            {
                string command = postProcessorCommand
                                 .Replace("{inputfile}", preProcessedFile)
                                 .Replace("{outputfile}", urlParserResult.Destination);

                SimpleShell.ICommandResult commandResult = await SimpleShell.Command.Run(command);

                File.Delete(preProcessedFile);

                File.Delete(urlParserResult.Destination + ".bak"); // OptiPng likes creating .bak files...

                if (!string.IsNullOrEmpty(commandResult.StandardOutput))
                {
                    _logger.LogInfo(commandResult.StandardOutput);
                }

                if (!string.IsNullOrEmpty(commandResult.StandardError))
                {
                    _logger.LogInfo(commandResult.StandardError);
                }

                if (commandResult.ErrorType != SimpleShell.CommandErrorType.None)
                {
                    File.Delete(urlParserResult.Destination);
                    throw new Exception($"Error running post-processor command: {postProcessorCommand}");
                }
            }
        }
Example #2
0
        public async Task <IRequestHandlerResult> HandleRequest(Uri url, string ipAddress)
        {
            if (_rateLimiter.IsBlacklisted(ipAddress))
            {
                _logger.LogInfo($"Denied request from blacklisted ip: {ipAddress}");
                return(new RequestHandlerResult(ResultStatus.Forbidden));
            }

            if (_rateLimiter.IsLimitExceeded(ipAddress))
            {
                _logger.LogInfo($"Denied request because limit was exceeded for ip: {ipAddress}");
                return(new RequestHandlerResult(ResultStatus.TooManyRequests));
            }

            try
            {
                UrlParserResult urlParserResult = _urlParser.Parse(url);
                byte[]          sourceBytes     = await GetSourceImage(urlParserResult.Source);

                _rateLimiter.RegisterRequest(ipAddress, sourceBytes.LongLength);

                await FileAccessor.RunAction(urlParserResult.Destination, () => ResizeSourceImage(sourceBytes, urlParserResult));

                _logger.LogInfo($"Created image at: {urlParserResult.Destination}");

                string   contentType = "image/" + urlParserResult.ImageType.ToString().ToLower();
                FileInfo fileInfo    = new FileInfo(urlParserResult.Destination);
                return(new RequestHandlerResult(ResultStatus.OK, new ImageInfo(fileInfo.Length, contentType, urlParserResult.Destination)));
            }
            catch (UrlParserException)
            {
                _logger.LogWarning($"Could not parse url: {url}");
                return(new RequestHandlerResult(ResultStatus.BadRequest));
            }
            catch (FileNotFoundException e)
            {
                _logger.LogWarning($"Could not find source: {e.FileName}");
                return(new RequestHandlerResult(ResultStatus.FileNotFound));
            }
            catch (HttpRequestException e)
            {
                _logger.LogError($"Exception was thrown trying to fetch remote source: {e.ToString()}");
                return(new RequestHandlerResult(ResultStatus.BadGateway));
            }
            catch (Exception e)
            {
                _logger.LogError($"An unhandled exception occurred: {e.ToString()}");
                return(new RequestHandlerResult(ResultStatus.ServerError));
            }
        }
Example #3
0
        private string GetPostProcessorCommand(UrlParserResult urlParserResult)
        {
            bool forcedOn  = urlParserResult.PostProcess.HasValue && urlParserResult.PostProcess.Value == true;
            bool forcedOff = urlParserResult.PostProcess.HasValue && urlParserResult.PostProcess.Value == false;

            Func <IPostProcessorSettings, string> GetCommand = (settings) =>
            {
                string cmd = string.Empty;

                if (forcedOn || (settings.PostProcessorEnabled && !forcedOff))
                {
                    cmd = settings.PostProcessorCommand;
                    if (string.IsNullOrEmpty(cmd))
                    {
                        _logger.LogWarning($"Post-processing requested but no post-processor command specified.");
                    }
                }

                return(cmd);
            };


            string command = string.Empty;

            if (!forcedOff)
            {
                switch (urlParserResult.ImageType)
                {
                case ImageType.Gif:
                    command = GetCommand(_settings.GifSettings);
                    break;

                case ImageType.Jpeg:
                    command = GetCommand(_settings.JpegSettings);
                    break;

                case ImageType.Png:
                    command = GetCommand(_settings.PngSettings);
                    break;
                }
            }

            return(command);
        }
Example #4
0
        private ImageFit.IEncoder GetEncoder(UrlParserResult urlParserResult)
        {
            switch (urlParserResult.ImageType)
            {
            case ImageType.Gif:
                return(new ImageFit.GifEncoder());

            case ImageType.Jpeg:
                int quality = urlParserResult.Quality.HasValue ? urlParserResult.Quality.Value : _settings.JpegSettings.Quality;
                return(new ImageFit.JpegEncoder(quality));

            case ImageType.Png:
                int compressionLevel = urlParserResult.Compression.HasValue ? urlParserResult.Compression.Value : _settings.PngSettings.CompressionLevel;
                return(new ImageFit.PngEncoder(compressionLevel));

            default:
                throw new NotSupportedException();
            }
        }
Example #5
0
        public UrlParserResult Parse(Uri url)
        {
            string pattern = @"^
                                (?<location>.*)
                                /
                                (?<modifier>
                                    (?<width>\d+)x(?<height>\d+)
                                    (
                                        -(?<resize>contain|cover|fill|scaledown)
                                        |
                                        -(q(?<quality>\d+))
                                        |
                                        -(c(?<compression>\d+))
                                        |
                                        -(?<postprocess>n?pp)
                                    )*
                                )
                                /
                                (?<file>[^/]+\.(?<extension>gif|jpe?g|png))
                                $";


            Match match = Regex.Match(url.GetLeftPart(UriPartial.Path), pattern, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);

            if (!match.Success)
            {
                throw new UrlParserException($"The URL {url.GetLeftPart(UriPartial.Path)} is not a valid.");
            }

            string location  = match.Groups["location"].Value;
            string modifier  = match.Groups["modifier"].Value;
            string file      = match.Groups["file"].Value;
            string extension = match.Groups["extension"].Value;

            string root = _destinationRoot.AbsoluteUrl.OriginalString;
            string dirs = string.Empty;

            if (location.StartsWith(root, StringComparison.InvariantCultureIgnoreCase))
            {
                dirs = location.Replace(root, "").Trim('/');
            }
            else
            {
                throw new UrlParserException($"The requested location {location} is not below the root location {root}.");
            }

            UrlParserResult result = new UrlParserResult()
            {
                Source       = GetSource(dirs, file),
                Destination  = GetDestination(dirs, modifier, file),
                ImageType    = GetImageType(extension),
                Width        = Convert.ToInt32(match.Groups["width"].Value),
                Height       = Convert.ToInt32(match.Groups["height"].Value),
                ResizeMethod = GetValueOrNull <ResizeMethod>(match.Groups["resize"].Value),
                Quality      = GetValueOrNull <int>(match.Groups["quality"].Value),
                Compression  = GetValueOrNull <int>(match.Groups["compression"].Value)
            };

            string postProcessValue = match.Groups["postprocess"].Value;

            if (!string.IsNullOrEmpty(postProcessValue))
            {
                result.PostProcess = postProcessValue == "pp";
            }

            return(result);
        }