Beispiel #1
0
            /// <summary>
            /// Adds the compression headers
            /// </summary>
            /// <param name="context"></param>
            public void OnActionExecuted(ActionExecutedContext context)
            {
                //get the model from the items
                if (!context.HttpContext.Items.ContainsKey(nameof(AddCompressionHeaderAttribute)))
                {
                    return;
                }
                var file = context.HttpContext.Items[nameof(AddCompressionHeaderAttribute)] as RequestModel;

                if (file == null)
                {
                    return;
                }

                var enableCompression = true;

                //check if it's a bundle (not composite file)
                var bundleRequest = file as BundleRequestModel;

                if (bundleRequest != null)
                {
                    Bundle b;
                    if (_bundleManager.TryGetValue(bundleRequest.FileKey, out b))
                    {
                        var bundleOptions = b.GetBundleOptions(_bundleManager, bundleRequest.Debug);
                        enableCompression = bundleOptions.CompressResult;
                    }
                }

                if (enableCompression)
                {
                    context.HttpContext.Response.AddCompressionResponseHeader(
                        _requestHelper.GetClientCompression(context.HttpContext.Request.Headers));
                }
            }
Beispiel #2
0
        public BundleRequestModel(IUrlManager urlManager, IActionContextAccessor accessor, IRequestHelper requestHelper, IBundleManager bundleManager, CacheBusterResolver cacheBusterResolver)
            : base("bundle", urlManager, accessor, requestHelper)
        {
            //TODO: Pretty sure if we want to control the caching of the file, we'll have to retrieve the bundle definition here
            // In reality we'll need to do that anyways if we want to support load balancing!
            // https://github.com/Shazwazza/Smidge/issues/17


            if (!ParsedPath.Names.Any())
            {
                throw new InvalidOperationException("The bundle route value does not contain a bundle name");
            }

            FileKey = ParsedPath.Names.Single();

            Bundle bundle;

            if (!bundleManager.TryGetValue(FileKey, out bundle))
            {
                throw new InvalidOperationException("No bundle found with key " + FileKey);
            }
            Bundle = bundle;

            CacheBuster = cacheBusterResolver.GetCacheBuster(bundle.GetBundleOptions(bundleManager, Debug).GetCacheBusterType());
        }
Beispiel #3
0
            /// <summary>
            /// Adds the expiry headers
            /// </summary>
            /// <param name="context"></param>
            public void OnActionExecuted(ActionExecutedContext context)
            {
                if (context.Exception != null)
                {
                    return;
                }

                //get the model from the items
                if (!context.HttpContext.Items.ContainsKey(nameof(AddExpiryHeadersAttribute)))
                {
                    return;
                }
                var file = context.HttpContext.Items[nameof(AddExpiryHeadersAttribute)] as RequestModel;

                if (file == null)
                {
                    return;
                }

                var enableETag         = true;
                var cacheControlMaxAge = 10 * 24; //10 days

                //check if it's a bundle (not composite file)
                var bundleRequest = file as BundleRequestModel;

                if (bundleRequest != null)
                {
                    Bundle b;
                    if (_bundleManager.TryGetValue(bundleRequest.FileKey, out b))
                    {
                        var bundleOptions = b.GetBundleOptions(_bundleManager, bundleRequest.Debug);
                        enableETag         = bundleOptions.CacheControlOptions.EnableETag;
                        cacheControlMaxAge = bundleOptions.CacheControlOptions.CacheControlMaxAge;
                    }
                }

                if (enableETag)
                {
                    var etag = _hasher.Hash(file.FileKey + file.Compression + file.Mime);
                    context.HttpContext.Response.AddETagResponseHeader(etag);
                }

                if (cacheControlMaxAge > 0)
                {
                    context.HttpContext.Response.AddCacheControlResponseHeader(cacheControlMaxAge);
                    context.HttpContext.Response.AddLastModifiedResponseHeader(file);
                    context.HttpContext.Response.AddExpiresResponseHeader(cacheControlMaxAge);
                }
            }
Beispiel #4
0
        public FileResult SourceMap([FromServices] BundleRequestModel bundle)
        {
            Bundle foundBundle;

            if (!_bundleManager.TryGetValue(bundle.FileKey, out foundBundle))
            {
                //TODO: Throw an exception, this will result in an exception anyways
                return(null);
            }

            //now we need to determine if this bundle has already been created
            var compositeFilePath = new FileInfo(_fileSystemHelper.GetCurrentCompositeFilePath(bundle.CacheBuster, bundle.Compression, bundle.FileKey));
            //we need to go one level above the composite path into the non-compression named folder since the map request will always be 'none' compression
            var mapPath = new FileInfo(Path.Combine(compositeFilePath.Directory.Parent.FullName, compositeFilePath.Name + ".map"));

            if (mapPath.Exists)
            {
                //this should already be processed if this is being requested!
                return(File(mapPath.OpenRead(), "application/json"));
            }

            //TODO: Throw an exception, this will result in an exception anyways
            return(null);
        }
        /// <summary>
        /// Handles requests for named bundles
        /// </summary>
        /// <param name="bundle">The bundle model</param>
        /// <returns></returns>
        public async Task <IActionResult> Bundle(
            [FromServices] BundleRequestModel bundle)
        {
            if (!_bundleManager.TryGetValue(bundle.FileKey, out Bundle foundBundle))
            {
                return(NotFound());
            }

            var bundleOptions = foundBundle.GetBundleOptions(_bundleManager, bundle.Debug);

            //now we need to determine if this bundle has already been created
            var compositeFilePath = new FileInfo(_fileSystemHelper.GetCurrentCompositeFilePath(bundle.CacheBuster, bundle.Compression, bundle.FileKey));

            if (compositeFilePath.Exists)
            {
                _logger.LogDebug($"Returning bundle '{bundle.FileKey}' from cache");

                //this is already processed, return it
                return(File(compositeFilePath.OpenRead(), bundle.Mime));
            }

            //the bundle doesn't exist so we'll go get the files, process them and create the bundle
            //TODO: We should probably lock here right?! we don't want multiple threads trying to do this at the same time, we'll need a dictionary of locks to do this effectively

            //get the files for the bundle
            var files = _fileSetGenerator.GetOrderedFileSet(foundBundle,
                                                            _processorFactory.CreateDefault(
                                                                //the file type in the bundle will always be the same
                                                                foundBundle.Files[0].DependencyType))
                        .ToArray();

            if (files.Length == 0)
            {
                return(NotFound());
            }

            using (var bundleContext = new BundleContext(bundle, compositeFilePath))
            {
                var watch = new Stopwatch();
                watch.Start();
                _logger.LogDebug($"Processing bundle '{bundle.FileKey}', debug? {bundle.Debug} ...");

                //we need to do the minify on the original files
                foreach (var file in files)
                {
                    await _preProcessManager.ProcessAndCacheFileAsync(file, bundleOptions, bundleContext);
                }

                //Get each file path to it's hashed location since that is what the pre-processed file will be saved as
                Lazy <IFileInfo> fi;
                var filePaths = files.Select(
                    x => _fileSystemHelper.GetCacheFilePath(x, bundleOptions.FileWatchOptions.Enabled, Path.GetExtension(x.FilePath), bundle.CacheBuster, out fi));

                using (var resultStream = await GetCombinedStreamAsync(filePaths, bundleContext))
                {
                    //compress the response (if enabled)
                    var compressedStream = await Compressor.CompressAsync(
                        //do not compress anything if it's not enabled in the bundle options
                        bundleOptions.CompressResult?bundle.Compression : CompressionType.none,
                        resultStream);

                    //save the resulting compressed file, if compression is not enabled it will just save the non compressed format
                    // this persisted file will be used in the CheckNotModifiedAttribute which will short circuit the request and return
                    // the raw file if it exists for further requests to this path
                    await CacheCompositeFileAsync(compositeFilePath, compressedStream);

                    _logger.LogDebug($"Processed bundle '{bundle.FileKey}' in {watch.ElapsedMilliseconds}ms");

                    //return the stream
                    return(File(compressedStream, bundle.Mime));
                }
            }
        }